From d9caffdd63c28717e29df15edcfff8aafebc5c85 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Sun, 29 Mar 2026 10:05:35 -0700 Subject: [PATCH] =?UTF-8?q?feat(tray):=20=E2=9C=A8=20Add=20chobit=20system?= =?UTF-8?q?=20tray=20integration=20and=20development=20tray=20utilities?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- services/tray/chobit_tray.py | 4 ++++ services/tray/dev_trays.py | 30 ++++++++++++++++++------------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/services/tray/chobit_tray.py b/services/tray/chobit_tray.py index 766b5fc..06dd5ac 100644 --- a/services/tray/chobit_tray.py +++ b/services/tray/chobit_tray.py @@ -194,6 +194,10 @@ class ChobitTray(TrayApp): self._select_camera(index) elif cmd == "list_cameras": self._send_camera_list() + elif cmd == "set_camera_enabled": + enabled = bool(msg.get("enabled", False)) + if enabled != self._camera_enabled: + self.toggle_camera() def _send_camera_list(self) -> None: """Send enumerated camera list to Godot via UDP.""" diff --git a/services/tray/dev_trays.py b/services/tray/dev_trays.py index d6c8ee2..39515e6 100644 --- a/services/tray/dev_trays.py +++ b/services/tray/dev_trays.py @@ -108,15 +108,23 @@ def _systemd_is_active(unit: str) -> bool: return False -def _systemd_toggle(unit: str) -> None: +def _systemd_toggle(unit: str) -> tuple[str, bool, str]: + """Start or stop a systemd unit. Returns (action, success, error_message).""" active = _systemd_is_active(unit) + action = "stop" if active else "start" try: - subprocess.run( - ["systemctl", "--user", "stop" if active else "start", unit], + result = subprocess.run( + ["systemctl", "--user", action, unit], + capture_output=True, + text=True, timeout=10, ) - except Exception: - pass + if result.returncode == 0: + return action, True, "" + msg = (result.stderr.strip() or result.stdout.strip()).splitlines()[0] if (result.stderr or result.stdout) else "unknown error" + return action, False, msg + except Exception as e: + return action, False, str(e) def _check_char() -> tuple[str, dict[str, str]]: @@ -197,8 +205,11 @@ class SpeechDevTray(DevServiceTray): return ("running" if active else "stopped"), labels def _toggle_speech(self) -> None: - fr.record("tray.toggle_speech", "Speech service toggled") - _systemd_toggle(SPEECH_UNIT) + action, ok, err = _systemd_toggle(SPEECH_UNIT) + if ok: + fr.record(f"tray.speech.{action}ed", f"Speech service {action}ped", {"unit": SPEECH_UNIT}) + else: + fr.record("tray.speech.error", f"Failed to {action} speech service", {"unit": SPEECH_UNIT, "error": err}) self.set_status_labels(self.get_status_labels()) def _toggle_mic(self) -> None: @@ -277,11 +288,6 @@ class FaceDevTray(DevServiceTray): _send_command("toggle_gaze") self.set_status_labels(self.get_status_labels()) - def _toggle_gaze(self) -> None: - fr.record("tray.toggle_gaze", "Godot gaze mode toggled") - _send_command("toggle_gaze") - self.set_status_labels(self.get_status_labels()) - def _toggle_gaze_halo(self) -> None: fr.record("tray.toggle_gaze_halo", "Gaze halo toggled") _send_command("toggle_gaze_halo")