Classify multiple low-confidence devices in a SINGLE GPT-5 Nano call. Mutates nodes in-place for the given indices.
(nodes, indices, ai_service, labels, valid_types)
| 5869 | def _start_service_with_monitor(service_name: str, timeout: int = 30) -> tuple[bool, str]: |
| 5870 | start_proc = _run_systemctl(['start', service_name]) |
| 5871 | if start_proc.returncode != 0: |
| 5872 | detail = start_proc.stderr.strip() or start_proc.stdout.strip() or f"systemctl start {service_name} failed with {start_proc.returncode}" |
| 5873 | status_excerpt = _collect_service_status(service_name) |
| 5874 | summary = status_excerpt or detail |
| 5875 | return False, summary |
| 5876 | return _wait_for_service_active(service_name, timeout=timeout) |
| 5877 | |
| 5878 | |
| 5879 | def _stop_service(service_name: str) -> tuple[bool, str]: |
| 5880 | stop_proc = _run_systemctl(['stop', service_name]) |
| 5881 | if stop_proc.returncode != 0: |
| 5882 | detail = stop_proc.stderr.strip() or stop_proc.stdout.strip() or f"systemctl stop {service_name} failed with {stop_proc.returncode}" |
| 5883 | logger.warning(detail) |
| 5884 | return False, detail |
| 5885 | return True, 'stopped' |
| 5886 | |
| 5887 | |
| 5888 | KIOSK_SERVICE = 'ragnar-kiosk.service' |
| 5889 | KIOSK_SERVICE_FILE = '/etc/systemd/system/ragnar-kiosk.service' |
| 5890 | KIOSK_INSTALL_SCRIPT = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'scripts', 'install_kiosk.sh') |
| 5891 | KIOSK_UNINSTALL_SCRIPT = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'scripts', 'uninstall_kiosk.sh') |
| 5892 | KIOSK_AUTOSTART_REL = '.config/autostart/ragnar-kiosk.desktop' |
| 5893 | |
| 5894 | |
| 5895 | def _kiosk_capability() -> tuple[bool, str]: |
| 5896 | """(capable, reason) - may the on-screen kiosk run on this hardware? |
| 5897 | |
| 5898 | The kiosk drives a full Chromium (~1GB resident), which a 512MB Pi Zero 2 W |
| 5899 | cannot host without swapping itself to a standstill and taking Ragnar's |
| 5900 | scanning down with it. It is a Ragnar Pi server feature; everything below |
| 5901 | that bar is refused here rather than half-installed and left lagging. |
| 5902 | Fails closed - if capability cannot be determined, the kiosk stays off. |
| 5903 | """ |
| 5904 | try: |
| 5905 | from server_capabilities import kiosk_capability |
| 5906 | return kiosk_capability(shared_data) |
| 5907 | except Exception as exc: |
| 5908 | logger.error(f"[kiosk] capability check unavailable: {exc}") |
| 5909 | return False, ('Could not verify this system meets the kiosk hardware requirements ' |
| 5910 | f'({exc}). The kiosk needs a Ragnar Pi server (2GB+ RAM).') |
| 5911 | |
| 5912 | |
| 5913 | def _kiosk_autostart_paths() -> list[str]: |
| 5914 | """Return every existing per-user autostart .desktop path.""" |
| 5915 | import pwd |
| 5916 | paths = [] |
| 5917 | try: |
| 5918 | for entry in pwd.getpwall(): |
| 5919 | if 1000 <= entry.pw_uid < 65534: |
| 5920 | p = os.path.join(entry.pw_dir, KIOSK_AUTOSTART_REL) |
| 5921 | if os.path.exists(p): |
| 5922 | paths.append(p) |
| 5923 | except Exception: |
| 5924 | pass |
| 5925 | return paths |
| 5926 | |
| 5927 | |
| 5928 | # Kiosk install/teardown runs on a background thread and can take many minutes |
no test coverage detected