From 213c9f47a3275d5cafbef283d9ab41964c9f5e71 Mon Sep 17 00:00:00 2001 From: Alejandro Sirgo Rica Date: Wed, 5 Feb 2025 11:35:28 +0100 Subject: [PATCH] kiosk: obtain os probe data from ogClient Define a kiosk event "refresh" to send configuration data from ogClient to Kiosk. Send the OS list after an ogClient refresh operation. Reload widgets in Kiosk when a refresh event is received. --- src/kiosk/boot.py | 6 +++--- src/kiosk/config.py | 34 +++------------------------------- src/kiosk/kiosk.py | 18 ++++++++++-------- src/kiosk/sidepanel.py | 15 +++++++++------ src/live/kiosk.py | 27 +++++++++++++++++++++++++++ src/live/ogOperations.py | 6 ++++++ src/ogClient.py | 7 +++++++ src/ogRest.py | 3 +++ 8 files changed, 68 insertions(+), 48 deletions(-) create mode 100644 src/live/kiosk.py diff --git a/src/kiosk/boot.py b/src/kiosk/boot.py index 1ab5306..55710e3 100644 --- a/src/kiosk/boot.py +++ b/src/kiosk/boot.py @@ -10,6 +10,7 @@ from PyQt6.QtWidgets import ( ) from PyQt6.QtCore import Qt, QSize, pyqtSignal from PyQt6.QtGui import QPixmap, QIcon +from src.utils.probe import OSFamily from src.kiosk.config import * from src.kiosk.theme import * @@ -25,7 +26,6 @@ class BootView(QWidget): super().__init__() self.checked_os = None - probe_partitions() self.layout = QVBoxLayout() self.layout.setSpacing(10) @@ -67,9 +67,9 @@ class BootView(QWidget): button.setStyleSheet(f'height: {BUTTON_HEIGHT}px; width: {BUTTON_WIDTH}px;') button.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextUnderIcon) button.setCheckable(True) - if part_data['os'] == OSFamily.WINDOWS: + if part_data['os'] == OSFamily.WINDOWS.name: button.setIcon(win_icon) - elif part_data['os'] == OSFamily.LINUX: + elif part_data['os'] == OSFamily.LINUX.name: button.setIcon(linux_icon) button.setIconSize(QSize(BUTTON_ICON_SIZE, BUTTON_ICON_SIZE)) button.toggled.connect(lambda checked, b=button: self.on_part_button_checked(checked, b)) diff --git a/src/kiosk/config.py b/src/kiosk/config.py index aa80b5b..bee540a 100644 --- a/src/kiosk/config.py +++ b/src/kiosk/config.py @@ -5,8 +5,6 @@ # Free Software Foundation; either version 3 of the License, or # (at your option) any later version. -from src.utils.probe import os_probe, get_os_family, OSFamily -from src.utils.fs import mount_mkdir, umount from PyQt6.QtGui import QColor from src.utils.disk import * from src.utils.net import * @@ -16,39 +14,13 @@ import os config_data = {} +def update_config(new_config): + for k in new_config: + config_data[k] = new_config[k] def get_boot_os_list(): return config_data.get('os_list', []) -def probe_partitions(): - os_list = [] - for disknum, disk in enumerate(get_disks(), start=1): - try: - partitions = get_partition_data(device=f'/dev/{disk}') - except Exception as e: - continue - - for pa in partitions: - partnum = pa.partno + 1 - mountpoint = pa.padev.replace('dev', 'mnt') - if mount_mkdir(pa.padev, mountpoint): - try: - os_family = get_os_family(mountpoint) - if os_family == OSFamily.UNKNOWN: - continue - os_name = os_probe(mountpoint) - os_data = { - 'name': os_name, - 'partition': partnum, - 'disk': disknum, - 'os': os_family, - 'image': '' - } - os_list.append(os_data) - finally: - umount(mountpoint) - config_data['os_list'] = os_list - def get_main_color_theme(): color_values = config_data.get('color_theme', [49, 69, 106]) return QColor(*color_values) diff --git a/src/kiosk/kiosk.py b/src/kiosk/kiosk.py index 60a187e..b6b8fe1 100644 --- a/src/kiosk/kiosk.py +++ b/src/kiosk/kiosk.py @@ -51,7 +51,8 @@ class Kiosk(QMainWindow): self.main_content.setLayout(QVBoxLayout()) layout.addWidget(self.main_content) - self.reload_theme() + self.instance_sidepanel() + self.side_panel.request_widget_change(ViewType.BOOT_OS) def closeEvent(self, event): if self.socket_notifier: @@ -82,6 +83,8 @@ class Kiosk(QMainWindow): status = payload.get('status', 'idle') self.apply_busy_mode_configuration(status) elif payload.get('command') == 'refresh': + new_configuration = payload.get('config', {}) + update_config(new_configuration) self.reload_theme() elif payload.get('command') == 'close': self.close() @@ -90,24 +93,23 @@ class Kiosk(QMainWindow): except json.JSONDecodeError as e: logging.error(f'Failed to decode JSON: {e}') - def reload_theme(self): - # Floating side panel - if self.side_panel: - self.side_panel.setParent(None) - self.side_panel.deleteLater() + def instance_sidepanel(self): self.side_panel = SidePanel() self.side_panel.setParent(self) self.side_panel.setFixedWidth(SIDE_PANEL_WIDTH) self.side_panel.panel_hidden = True self.side_panel.setGeometry(-SIDE_PANEL_WIDTH, 0, SIDE_PANEL_WIDTH, self.height()) self.side_panel.widget_change_requested.connect(self.update_view) - self.side_panel.emit_default_widget() self.side_panel.setVisible(True) self.side_panel.raise_() - self.animation = QPropertyAnimation(self.side_panel, b'geometry') self.animation.setDuration(200) + def reload_theme(self): + self.side_panel.reload_theme() + if self.side_panel.last_widget == ViewType.BOOT_OS: + self.side_panel.request_widget_change(ViewType.BOOT_OS) + def apply_busy_mode_configuration(self, status): if get_status() == status: return diff --git a/src/kiosk/sidepanel.py b/src/kiosk/sidepanel.py index fe9b389..dbf8c5d 100644 --- a/src/kiosk/sidepanel.py +++ b/src/kiosk/sidepanel.py @@ -56,6 +56,8 @@ class SidePanel(QFrame): super().__init__() self.panel_hidden = False + self.last_widget = ViewType.BOOT_OS + self.button_map = {} self.setFrameStyle(QFrame.Shape.StyledPanel | QFrame.Shadow.Plain) @@ -71,11 +73,12 @@ class SidePanel(QFrame): layout.addWidget(button) button.clicked.connect(lambda _, view=view_type: self.request_widget_change(view)) + self.button_map[view_type] = button + + def reload_theme(self): + for view_type, button in self.button_map.items(): + button.setIcon(view_type.get_view_icon()) + def request_widget_change(self, view_type): + self.last_widget = view_type self.widget_change_requested.emit(view_type.get_widget_instance()) - - def emit_default_widget(self): - self.request_widget_change(ViewType.BOOT_OS) - - def emit_monitor_widget(self): - self.request_widget_change(ViewType.SYSTEM_MONITOR) diff --git a/src/live/kiosk.py b/src/live/kiosk.py new file mode 100644 index 0000000..e0c7c1a --- /dev/null +++ b/src/live/kiosk.py @@ -0,0 +1,27 @@ +# +# Copyright (C) 2020-2025 Soleta Networks +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Affero General Public License as published by the +# Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. + +kiosk_config = {} + +def update_kiosk_config(refresh_payload): + from src.utils.probe import OSFamily + os_list = [] + for part_setup in refresh_payload['partition_setup']: + if part_setup['os_family'] == OSFamily.UNKNOWN.name: + del part_setup['os_family'] + continue + os_data = { + 'name': part_setup['os'], + 'partition': int(part_setup['partition']), + 'disk': int(part_setup['disk']), + 'os': part_setup['os_family'], + 'image': '' + } + del part_setup['os_family'] + os_list.append(os_data) + kiosk_config['os_list'] = os_list \ No newline at end of file diff --git a/src/live/ogOperations.py b/src/live/ogOperations.py index 3dbbad7..6b65442 100644 --- a/src/live/ogOperations.py +++ b/src/live/ogOperations.py @@ -23,6 +23,7 @@ from src.live.partcodes import GUID_MAP from src.live.parttypes import get_parttype from src.utils.image import * +from src.live.kiosk import * from src.utils.postinstall import configure_os from src.utils.net import * from src.utils.menu import generate_menu @@ -83,9 +84,12 @@ class OgLiveOperations: if mount_mkdir(pa.padev, target): part_setup['os'] = '' + part_setup['os_family'] = OSFamily.UNKNOWN.name if part_setup['disk'] == '1': probe_result = os_probe(target) part_setup['os'] = probe_result + os_family = get_os_family(target) + part_setup['os_family'] = os_family.name total, used, free = shutil.disk_usage(target) part_setup['used_size'] = used @@ -93,6 +97,7 @@ class OgLiveOperations: umount(target) else: part_setup['os'] = '' + part_setup['os_family'] = OSFamily.UNKNOWN.name part_setup['used_size'] = 0 part_setup['free_size'] = 0 @@ -861,6 +866,7 @@ class OgLiveOperations: if boot_entry_data: json_body['efi'] = boot_entry_data + update_kiosk_config(json_body) generate_menu(json_body['partition_setup']) generate_cache_txt() self._restartBrowser(self._url) diff --git a/src/ogClient.py b/src/ogClient.py index 915a91e..6f0d458 100644 --- a/src/ogClient.py +++ b/src/ogClient.py @@ -16,6 +16,7 @@ import logging from io import StringIO from src.restRequest import * +from src.live.kiosk import * from src.ogRest import * from src.log import OgError from enum import Enum @@ -159,6 +160,12 @@ class ogClient: except Exception as e: logging.error(f"Unexpected error in send_kiosk_event: {e}") + def send_kiosk_refresh(self): + self.send_kiosk_event({ + 'command': 'refresh', + 'config': kiosk_config + }) + def cleanup(self): self.data = "" self.content_len = 0 diff --git a/src/ogRest.py b/src/ogRest.py index 586d393..186145c 100644 --- a/src/ogRest.py +++ b/src/ogRest.py @@ -176,6 +176,7 @@ class ogThread(): response = restResponse(ogResponses.OK, json_body, seq=client.seq) client.send(response.get()) + client.send_kiosk_refresh() ogRest.set_state(ThreadState.IDLE, client) def image_restore_local(client, request, ogRest): @@ -200,6 +201,7 @@ class ogThread(): response = restResponse(ogResponses.OK, json_body, seq=client.seq) client.send(response.get()) + client.send_kiosk_refresh() ogRest.set_state(ThreadState.IDLE, client) def image_create(client, request, ogRest): @@ -271,6 +273,7 @@ class ogThread(): response = restResponse(ogResponses.OK, json_body, seq=client.seq) client.send(response.get()) + client.send_kiosk_refresh() ogRest.set_state(ThreadState.IDLE, client, update_kiosk=False) class ogResponses(Enum):