camera: add support for moonraker cameras, deprecates camera_url
this also adds support for flipping and rotation (configured in moonraker) close #976
This commit is contained in:
parent
77a24ec809
commit
8053e97d10
@ -173,7 +173,7 @@ class KlipperScreenConfig:
|
|||||||
strs = (
|
strs = (
|
||||||
'moonraker_api_key', 'moonraker_host', 'titlebar_name_type',
|
'moonraker_api_key', 'moonraker_host', 'titlebar_name_type',
|
||||||
'screw_positions', 'power_devices', 'titlebar_items', 'z_babystep_values',
|
'screw_positions', 'power_devices', 'titlebar_items', 'z_babystep_values',
|
||||||
'extrude_distances', "extrude_speeds", "camera_url",
|
'extrude_distances', "extrude_speeds",
|
||||||
)
|
)
|
||||||
numbers = (
|
numbers = (
|
||||||
'moonraker_port', 'move_speed_xy', 'move_speed_z',
|
'moonraker_port', 'move_speed_xy', 'move_speed_z',
|
||||||
@ -199,6 +199,9 @@ class KlipperScreenConfig:
|
|||||||
for key in config[section]:
|
for key in config[section]:
|
||||||
if key not in bools and key not in strs and key not in numbers:
|
if key not in bools and key not in strs and key not in numbers:
|
||||||
msg = f'Option "{key}" not recognized for section "[{section}]"'
|
msg = f'Option "{key}" not recognized for section "[{section}]"'
|
||||||
|
if key == "camera_url":
|
||||||
|
msg = "camera_url has been deprecated in favor of moonraker cameras"
|
||||||
|
msg += "\n\n https://moonraker.readthedocs.io/en/latest/configuration/#webcam"
|
||||||
if remove:
|
if remove:
|
||||||
# This should only be called for the auto-generated section
|
# This should only be called for the auto-generated section
|
||||||
self.config.remove_option(section, key)
|
self.config.remove_option(section, key)
|
||||||
|
@ -147,7 +147,7 @@ enable: {{ printer.power_devices.count > 0 }}
|
|||||||
name: {{ gettext('Camera') }}
|
name: {{ gettext('Camera') }}
|
||||||
icon: camera
|
icon: camera
|
||||||
panel: camera
|
panel: camera
|
||||||
enable: {{ camera_configured }}
|
enable: {{ printer.cameras.count > 0 }}
|
||||||
|
|
||||||
[menu __main more console]
|
[menu __main more console]
|
||||||
name: {{ gettext('Console') }}
|
name: {{ gettext('Console') }}
|
||||||
|
@ -24,6 +24,7 @@ class Printer:
|
|||||||
self.busy_cb = busy_cb
|
self.busy_cb = busy_cb
|
||||||
self.busy = False
|
self.busy = False
|
||||||
self.tempstore_size = 1200
|
self.tempstore_size = 1200
|
||||||
|
self.cameras = []
|
||||||
|
|
||||||
def reinit(self, printer_info, data):
|
def reinit(self, printer_info, data):
|
||||||
self.config = data['configfile']['config']
|
self.config = data['configfile']['config']
|
||||||
@ -160,6 +161,10 @@ class Printer:
|
|||||||
}
|
}
|
||||||
logging.debug(f"Power devices: {self.power_devices}")
|
logging.debug(f"Power devices: {self.power_devices}")
|
||||||
|
|
||||||
|
def configure_cameras(self, data):
|
||||||
|
self.cameras = data
|
||||||
|
logging.debug(f"Cameras: {self.cameras}")
|
||||||
|
|
||||||
def get_config_section_list(self, search=""):
|
def get_config_section_list(self, search=""):
|
||||||
if self.config is not None:
|
if self.config is not None:
|
||||||
return [i for i in list(self.config) if i.startswith(search)] if hasattr(self, "config") else []
|
return [i for i in list(self.config) if i.startswith(search)] if hasattr(self, "config") else []
|
||||||
@ -228,6 +233,7 @@ class Printer:
|
|||||||
"idle_timeout": self.get_stat("idle_timeout").copy(),
|
"idle_timeout": self.get_stat("idle_timeout").copy(),
|
||||||
"pause_resume": {"is_paused": self.state == "paused"},
|
"pause_resume": {"is_paused": self.state == "paused"},
|
||||||
"power_devices": {"count": len(self.get_power_devices())},
|
"power_devices": {"count": len(self.get_power_devices())},
|
||||||
|
"cameras": {"count": len(self.cameras)},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,35 +12,53 @@ class Panel(ScreenPanel):
|
|||||||
def __init__(self, screen, title):
|
def __init__(self, screen, title):
|
||||||
super().__init__(screen, title)
|
super().__init__(screen, title)
|
||||||
self.mpv = None
|
self.mpv = None
|
||||||
self.da = Gtk.DrawingArea()
|
|
||||||
self.da.set_hexpand(True)
|
|
||||||
self.da.set_vexpand(True)
|
|
||||||
fs = self._gtk.Button("move", _("Fullscreen"), None, self.bts, Gtk.PositionType.LEFT, 1)
|
|
||||||
fs.connect("clicked", self.play)
|
|
||||||
fs.set_hexpand(True)
|
|
||||||
fs.set_vexpand(False)
|
|
||||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
box.add(self.da)
|
for i, cam in enumerate(self._printer.cameras):
|
||||||
box.add(fs)
|
if not cam["enabled"]:
|
||||||
self.content.add(box)
|
continue
|
||||||
|
logging.info(cam)
|
||||||
|
cam[cam["name"]] = self._gtk.Button(
|
||||||
|
image_name="camera", label=cam["name"], style=f"color{i % 4 + 1}",
|
||||||
|
scale=self.bts, position=Gtk.PositionType.LEFT, lines=1
|
||||||
|
)
|
||||||
|
cam[cam["name"]].set_hexpand(True)
|
||||||
|
cam[cam["name"]].set_vexpand(True)
|
||||||
|
cam[cam["name"]].connect("clicked", self.play, cam)
|
||||||
|
box.add(cam[cam["name"]])
|
||||||
|
|
||||||
|
self.scroll = self._gtk.ScrolledWindow()
|
||||||
|
self.scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
|
||||||
|
self.scroll.add(box)
|
||||||
|
self.content.add(self.scroll)
|
||||||
self.content.show_all()
|
self.content.show_all()
|
||||||
self.url = self.ks_printer_cfg.get("camera_url", "http://127.0.0.1/webcam/?action=stream").replace('"', '')
|
|
||||||
logging.debug(f"Camera URL: {self.url}")
|
|
||||||
|
|
||||||
def activate(self):
|
def activate(self):
|
||||||
self.play()
|
# if only 1 cam start playing fullscreen
|
||||||
|
if len(self._printer.cameras) == 1:
|
||||||
|
cam = next(iter(self._printer.cameras))
|
||||||
|
if cam['enabled']:
|
||||||
|
self.play(None, cam)
|
||||||
|
|
||||||
def deactivate(self):
|
def deactivate(self):
|
||||||
if self.mpv:
|
if self.mpv:
|
||||||
self.mpv.terminate()
|
self.mpv.terminate()
|
||||||
self.mpv = None
|
self.mpv = None
|
||||||
|
|
||||||
def play(self, fs=None):
|
def play(self, widget, cam):
|
||||||
|
url = cam['stream_url']
|
||||||
|
vf = ""
|
||||||
|
if cam["flip_horizontal"]:
|
||||||
|
vf += "hflip,"
|
||||||
|
if cam["flip_vertical"]:
|
||||||
|
vf += "vflip,"
|
||||||
|
vf += f"rotate:{cam['rotation']*3.14159/180}"
|
||||||
|
logging.info(f"video filters: {vf}")
|
||||||
|
|
||||||
if self.mpv:
|
if self.mpv:
|
||||||
self.mpv.terminate()
|
self.mpv.terminate()
|
||||||
self.mpv = None
|
self.mpv = mpv.MPV(fullscreen=True, log_handler=self.log, vo='gpu,wlshm,xv,x11')
|
||||||
# Create mpv after show or the 'window' property will be None
|
|
||||||
self.mpv = mpv.MPV(log_handler=self.log, vo='gpu,wlshm,xv,x11')
|
self.mpv.vf = vf
|
||||||
|
|
||||||
with suppress(Exception):
|
with suppress(Exception):
|
||||||
self.mpv.profile = 'sw-fast'
|
self.mpv.profile = 'sw-fast'
|
||||||
@ -51,22 +69,13 @@ class Panel(ScreenPanel):
|
|||||||
self.mpv.untimed = True
|
self.mpv.untimed = True
|
||||||
self.mpv.audio = 'no'
|
self.mpv.audio = 'no'
|
||||||
|
|
||||||
# On wayland mpv cannot be embedded at least for now
|
|
||||||
# https://github.com/mpv-player/mpv/issues/9654
|
|
||||||
# if fs:
|
|
||||||
self.mpv.fullscreen = True
|
|
||||||
|
|
||||||
@self.mpv.on_key_press('MBTN_LEFT' or 'MBTN_LEFT_DBL')
|
@self.mpv.on_key_press('MBTN_LEFT' or 'MBTN_LEFT_DBL')
|
||||||
def clicked():
|
def clicked():
|
||||||
self.mpv.quit(0)
|
self.mpv.quit(0)
|
||||||
# else:
|
|
||||||
# self.mpv.wid = f'{self.da.get_property("window").get_xid()}'
|
logging.debug(f"Camera URL: {url}")
|
||||||
#
|
self.mpv.play(url)
|
||||||
# @self.mpv.on_key_press('MBTN_LEFT' or 'MBTN_LEFT_DBL')
|
|
||||||
# def clicked():
|
|
||||||
# self._screen.show_popup_message(self.url, level=1)
|
|
||||||
self.mpv.play(self.url)
|
|
||||||
# if fs:
|
|
||||||
try:
|
try:
|
||||||
self.mpv.wait_for_playback()
|
self.mpv.wait_for_playback()
|
||||||
except mpv.ShutdownError:
|
except mpv.ShutdownError:
|
||||||
@ -75,8 +84,10 @@ class Panel(ScreenPanel):
|
|||||||
logging.exception(e)
|
logging.exception(e)
|
||||||
self.mpv.terminate()
|
self.mpv.terminate()
|
||||||
self.mpv = None
|
self.mpv = None
|
||||||
self._screen._menu_go_back()
|
if len(self._printer.cameras) == 1:
|
||||||
|
self._screen._menu_go_back()
|
||||||
|
|
||||||
@staticmethod
|
def log(self, loglevel, component, message):
|
||||||
def log(loglevel, component, message):
|
|
||||||
logging.debug(f'[{loglevel}] {component}: {message}')
|
logging.debug(f'[{loglevel}] {component}: {message}')
|
||||||
|
if loglevel == 'error':
|
||||||
|
self._screen.show_popup_message(f'{message}')
|
||||||
|
@ -103,8 +103,6 @@ class Panel(ScreenPanel):
|
|||||||
if enable == "{{ moonraker_connected }}":
|
if enable == "{{ moonraker_connected }}":
|
||||||
logging.info(f"moonraker connected {self._screen._ws.connected}")
|
logging.info(f"moonraker connected {self._screen._ws.connected}")
|
||||||
return self._screen._ws.connected
|
return self._screen._ws.connected
|
||||||
elif enable == "{{ camera_configured }}":
|
|
||||||
return self.ks_printer_cfg and self.ks_printer_cfg.get("camera_url", None) is not None
|
|
||||||
self.j2_data = self._printer.get_printer_status_data()
|
self.j2_data = self._printer.get_printer_status_data()
|
||||||
try:
|
try:
|
||||||
j2_temp = Template(enable, autoescape=True)
|
j2_temp = Template(enable, autoescape=True)
|
||||||
|
13
screen.py
13
screen.py
@ -832,9 +832,16 @@ class KlipperScreen(Gtk.Window):
|
|||||||
# Moonraker is ready, set a loop to init the printer
|
# Moonraker is ready, set a loop to init the printer
|
||||||
self.reinit_count += 1
|
self.reinit_count += 1
|
||||||
|
|
||||||
powerdevs = self.apiclient.send_request("machine/device_power/devices")
|
server_info = self.apiclient.get_server_info()["result"]
|
||||||
if powerdevs is not False:
|
logging.info(f"Moonraker info {server_info}")
|
||||||
self.printer.configure_power_devices(powerdevs['result'])
|
if "power" in server_info["components"]:
|
||||||
|
powerdevs = self.apiclient.send_request("machine/device_power/devices")
|
||||||
|
if powerdevs is not False:
|
||||||
|
self.printer.configure_power_devices(powerdevs['result'])
|
||||||
|
if "webcam" in server_info["components"]:
|
||||||
|
cameras = self.apiclient.send_request("server/webcams/list")
|
||||||
|
if cameras is not False:
|
||||||
|
self.printer.configure_cameras(cameras['result']['webcams'])
|
||||||
|
|
||||||
if state['result']['klippy_connected'] is False:
|
if state['result']['klippy_connected'] is False:
|
||||||
logging.info("Klipper not connected")
|
logging.info("Klipper not connected")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user