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:
alfrix 2023-07-30 18:31:51 -03:00 committed by Alfredo Monclus
parent 77a24ec809
commit 8053e97d10
6 changed files with 65 additions and 40 deletions

View File

@ -173,7 +173,7 @@ class KlipperScreenConfig:
strs = (
'moonraker_api_key', 'moonraker_host', 'titlebar_name_type',
'screw_positions', 'power_devices', 'titlebar_items', 'z_babystep_values',
'extrude_distances', "extrude_speeds", "camera_url",
'extrude_distances', "extrude_speeds",
)
numbers = (
'moonraker_port', 'move_speed_xy', 'move_speed_z',
@ -199,6 +199,9 @@ class KlipperScreenConfig:
for key in config[section]:
if key not in bools and key not in strs and key not in numbers:
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:
# This should only be called for the auto-generated section
self.config.remove_option(section, key)

View File

@ -147,7 +147,7 @@ enable: {{ printer.power_devices.count > 0 }}
name: {{ gettext('Camera') }}
icon: camera
panel: camera
enable: {{ camera_configured }}
enable: {{ printer.cameras.count > 0 }}
[menu __main more console]
name: {{ gettext('Console') }}

View File

@ -24,6 +24,7 @@ class Printer:
self.busy_cb = busy_cb
self.busy = False
self.tempstore_size = 1200
self.cameras = []
def reinit(self, printer_info, data):
self.config = data['configfile']['config']
@ -160,6 +161,10 @@ class Printer:
}
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=""):
if self.config is not None:
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(),
"pause_resume": {"is_paused": self.state == "paused"},
"power_devices": {"count": len(self.get_power_devices())},
"cameras": {"count": len(self.cameras)},
}
}

View File

@ -12,35 +12,53 @@ class Panel(ScreenPanel):
def __init__(self, screen, title):
super().__init__(screen, title)
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.add(self.da)
box.add(fs)
self.content.add(box)
for i, cam in enumerate(self._printer.cameras):
if not cam["enabled"]:
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.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):
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):
if self.mpv:
self.mpv.terminate()
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:
self.mpv.terminate()
self.mpv = None
# 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 = mpv.MPV(fullscreen=True, log_handler=self.log, vo='gpu,wlshm,xv,x11')
self.mpv.vf = vf
with suppress(Exception):
self.mpv.profile = 'sw-fast'
@ -51,22 +69,13 @@ class Panel(ScreenPanel):
self.mpv.untimed = True
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')
def clicked():
self.mpv.quit(0)
# else:
# self.mpv.wid = f'{self.da.get_property("window").get_xid()}'
#
# @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:
logging.debug(f"Camera URL: {url}")
self.mpv.play(url)
try:
self.mpv.wait_for_playback()
except mpv.ShutdownError:
@ -75,8 +84,10 @@ class Panel(ScreenPanel):
logging.exception(e)
self.mpv.terminate()
self.mpv = None
self._screen._menu_go_back()
if len(self._printer.cameras) == 1:
self._screen._menu_go_back()
@staticmethod
def log(loglevel, component, message):
def log(self, loglevel, component, message):
logging.debug(f'[{loglevel}] {component}: {message}')
if loglevel == 'error':
self._screen.show_popup_message(f'{message}')

View File

@ -103,8 +103,6 @@ class Panel(ScreenPanel):
if enable == "{{ moonraker_connected }}":
logging.info(f"moonraker connected {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()
try:
j2_temp = Template(enable, autoescape=True)

View File

@ -832,9 +832,16 @@ class KlipperScreen(Gtk.Window):
# Moonraker is ready, set a loop to init the printer
self.reinit_count += 1
powerdevs = self.apiclient.send_request("machine/device_power/devices")
if powerdevs is not False:
self.printer.configure_power_devices(powerdevs['result'])
server_info = self.apiclient.get_server_info()["result"]
logging.info(f"Moonraker info {server_info}")
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:
logging.info("Klipper not connected")