corrige temperature
This commit is contained in:
461
gnome-pilot-extension/ui/pilotWindow.js
Normal file
461
gnome-pilot-extension/ui/pilotWindow.js
Normal file
@@ -0,0 +1,461 @@
|
||||
// ui/pilotWindow.js - Fenêtre principale de l'extension Pilot Control
|
||||
|
||||
import GObject from 'gi://GObject';
|
||||
import Gtk from 'gi://Gtk';
|
||||
import Adw from 'gi://Adw';
|
||||
import GLib from 'gi://GLib';
|
||||
|
||||
import {MetricEditDialog} from './metricEditDialog.js';
|
||||
import {CommandEditDialog} from './commandEditDialog.js';
|
||||
|
||||
/**
|
||||
* Fenêtre principale avec les sections Services, Telemetry, Commands
|
||||
*/
|
||||
export const PilotWindow = GObject.registerClass(
|
||||
class PilotWindow extends Adw.Window {
|
||||
_init(extension, yamlConfig, serviceManager) {
|
||||
super._init({
|
||||
title: 'Pilot Control Panel',
|
||||
default_width: 800,
|
||||
default_height: 600,
|
||||
});
|
||||
|
||||
this._extension = extension;
|
||||
this._yamlConfig = yamlConfig;
|
||||
this._serviceManager = serviceManager;
|
||||
|
||||
this._buildUI();
|
||||
this._loadData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit l'interface utilisateur
|
||||
*/
|
||||
_buildUI() {
|
||||
// Header bar
|
||||
const headerBar = new Adw.HeaderBar();
|
||||
|
||||
// Bouton refresh
|
||||
const refreshButton = new Gtk.Button({
|
||||
icon_name: 'view-refresh-symbolic',
|
||||
tooltip_text: 'Reload configuration',
|
||||
});
|
||||
refreshButton.connect('clicked', () => {
|
||||
this._loadData();
|
||||
});
|
||||
headerBar.pack_end(refreshButton);
|
||||
|
||||
// Bouton save
|
||||
const saveButton = new Gtk.Button({
|
||||
icon_name: 'document-save-symbolic',
|
||||
tooltip_text: 'Save configuration',
|
||||
});
|
||||
saveButton.connect('clicked', () => {
|
||||
this._saveConfig();
|
||||
});
|
||||
headerBar.pack_end(saveButton);
|
||||
|
||||
// Toolbar view (GNOME 45+)
|
||||
const toolbarView = new Adw.ToolbarView();
|
||||
toolbarView.add_top_bar(headerBar);
|
||||
|
||||
// Main content box
|
||||
const mainBox = new Gtk.Box({
|
||||
orientation: Gtk.Orientation.VERTICAL,
|
||||
margin_top: 12,
|
||||
margin_bottom: 12,
|
||||
margin_start: 12,
|
||||
margin_end: 12,
|
||||
spacing: 12,
|
||||
});
|
||||
|
||||
// Scrolled window
|
||||
const scrolledWindow = new Gtk.ScrolledWindow({
|
||||
vexpand: true,
|
||||
hscrollbar_policy: Gtk.PolicyType.NEVER,
|
||||
});
|
||||
scrolledWindow.set_child(mainBox);
|
||||
|
||||
toolbarView.set_content(scrolledWindow);
|
||||
this.set_content(toolbarView);
|
||||
|
||||
// Section: Service Control
|
||||
mainBox.append(this._buildServiceSection());
|
||||
|
||||
// Section: Telemetry Metrics
|
||||
mainBox.append(this._buildTelemetrySection());
|
||||
|
||||
// Section: Commands
|
||||
mainBox.append(this._buildCommandsSection());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit la section Service Control
|
||||
*/
|
||||
_buildServiceSection() {
|
||||
const group = new Adw.PreferencesGroup({
|
||||
title: 'Service Control',
|
||||
description: 'Manage the Pilot systemd service',
|
||||
});
|
||||
|
||||
// Service status row
|
||||
this._serviceStatusRow = new Adw.ActionRow({
|
||||
title: 'Service Status',
|
||||
subtitle: 'Unknown',
|
||||
});
|
||||
|
||||
const serviceSwitch = new Gtk.Switch({
|
||||
valign: Gtk.Align.CENTER,
|
||||
});
|
||||
serviceSwitch.connect('notify::active', (sw) => {
|
||||
if (sw.active) {
|
||||
this._serviceManager.startService();
|
||||
} else {
|
||||
this._serviceManager.stopService();
|
||||
}
|
||||
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => {
|
||||
this._updateServiceStatus();
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
});
|
||||
this._serviceSwitch = serviceSwitch;
|
||||
|
||||
this._serviceStatusRow.add_suffix(serviceSwitch);
|
||||
this._serviceStatusRow.activatable_widget = serviceSwitch;
|
||||
|
||||
group.add(this._serviceStatusRow);
|
||||
|
||||
// Service auto-start row
|
||||
this._serviceEnableRow = new Adw.ActionRow({
|
||||
title: 'Auto-start Service',
|
||||
subtitle: 'Enable service at system startup',
|
||||
});
|
||||
|
||||
const enableSwitch = new Gtk.Switch({
|
||||
valign: Gtk.Align.CENTER,
|
||||
});
|
||||
enableSwitch.connect('notify::active', (sw) => {
|
||||
if (sw.active) {
|
||||
this._serviceManager.enableService();
|
||||
} else {
|
||||
this._serviceManager.disableService();
|
||||
}
|
||||
});
|
||||
this._serviceEnableSwitch = enableSwitch;
|
||||
|
||||
this._serviceEnableRow.add_suffix(enableSwitch);
|
||||
this._serviceEnableRow.activatable_widget = enableSwitch;
|
||||
|
||||
group.add(this._serviceEnableRow);
|
||||
|
||||
// Restart button row
|
||||
const restartRow = new Adw.ActionRow({
|
||||
title: 'Restart Service',
|
||||
subtitle: 'Apply configuration changes',
|
||||
});
|
||||
|
||||
const restartButton = new Gtk.Button({
|
||||
label: 'Restart',
|
||||
valign: Gtk.Align.CENTER,
|
||||
});
|
||||
restartButton.connect('clicked', () => {
|
||||
this._serviceManager.restartService();
|
||||
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => {
|
||||
this._updateServiceStatus();
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
});
|
||||
|
||||
restartRow.add_suffix(restartButton);
|
||||
group.add(restartRow);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit la section Telemetry
|
||||
*/
|
||||
_buildTelemetrySection() {
|
||||
const group = new Adw.PreferencesGroup({
|
||||
title: 'Telemetry Metrics',
|
||||
description: 'Configure system monitoring metrics',
|
||||
});
|
||||
|
||||
// Global telemetry switch
|
||||
this._telemetryGlobalRow = new Adw.ActionRow({
|
||||
title: 'Enable Telemetry',
|
||||
subtitle: 'Master switch for all metrics',
|
||||
});
|
||||
|
||||
const telemetrySwitch = new Gtk.Switch({
|
||||
valign: Gtk.Align.CENTER,
|
||||
});
|
||||
telemetrySwitch.connect('notify::active', (sw) => {
|
||||
this._yamlConfig.setTelemetryEnabled(sw.active);
|
||||
this._markDirty();
|
||||
});
|
||||
this._telemetrySwitch = telemetrySwitch;
|
||||
|
||||
this._telemetryGlobalRow.add_suffix(telemetrySwitch);
|
||||
this._telemetryGlobalRow.activatable_widget = telemetrySwitch;
|
||||
|
||||
group.add(this._telemetryGlobalRow);
|
||||
|
||||
// Container pour les métriques individuelles
|
||||
this._telemetryMetricsBox = new Gtk.Box({
|
||||
orientation: Gtk.Orientation.VERTICAL,
|
||||
spacing: 0,
|
||||
});
|
||||
|
||||
group.add(this._telemetryMetricsBox);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit la section Commands
|
||||
*/
|
||||
_buildCommandsSection() {
|
||||
const group = new Adw.PreferencesGroup({
|
||||
title: 'Commands',
|
||||
description: 'Configure allowed system commands',
|
||||
});
|
||||
|
||||
// Global commands switch
|
||||
this._commandsGlobalRow = new Adw.ActionRow({
|
||||
title: 'Enable Commands',
|
||||
subtitle: 'Master switch for all commands',
|
||||
});
|
||||
|
||||
const commandsSwitch = new Gtk.Switch({
|
||||
valign: Gtk.Align.CENTER,
|
||||
});
|
||||
commandsSwitch.connect('notify::active', (sw) => {
|
||||
this._yamlConfig.setCommandsEnabled(sw.active);
|
||||
this._markDirty();
|
||||
});
|
||||
this._commandsSwitch = commandsSwitch;
|
||||
|
||||
this._commandsGlobalRow.add_suffix(commandsSwitch);
|
||||
this._commandsGlobalRow.activatable_widget = commandsSwitch;
|
||||
|
||||
group.add(this._commandsGlobalRow);
|
||||
|
||||
// Allowlist editor row
|
||||
this._commandsAllowlistRow = new Adw.ActionRow({
|
||||
title: 'Allowed Commands',
|
||||
subtitle: 'Click to edit the allowlist',
|
||||
});
|
||||
|
||||
const editButton = new Gtk.Button({
|
||||
icon_name: 'document-edit-symbolic',
|
||||
valign: Gtk.Align.CENTER,
|
||||
});
|
||||
editButton.connect('clicked', () => {
|
||||
this._editCommandsAllowlist();
|
||||
});
|
||||
|
||||
this._commandsAllowlistRow.add_suffix(editButton);
|
||||
this._commandsAllowlistRow.set_activatable(true);
|
||||
this._commandsAllowlistRow.connect('activated', () => {
|
||||
this._editCommandsAllowlist();
|
||||
});
|
||||
|
||||
group.add(this._commandsAllowlistRow);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Charge les données depuis la config YAML
|
||||
*/
|
||||
_loadData() {
|
||||
const config = this._yamlConfig.load();
|
||||
|
||||
if (!config) {
|
||||
this._showError('Failed to load configuration');
|
||||
return;
|
||||
}
|
||||
|
||||
// Update service status
|
||||
this._updateServiceStatus();
|
||||
|
||||
// Update telemetry section
|
||||
const telemetryEnabled = config.features?.telemetry?.enabled || false;
|
||||
this._telemetrySwitch.active = telemetryEnabled;
|
||||
|
||||
// Clear existing metrics
|
||||
let child = this._telemetryMetricsBox.get_first_child();
|
||||
while (child) {
|
||||
const next = child.get_next_sibling();
|
||||
this._telemetryMetricsBox.remove(child);
|
||||
child = next;
|
||||
}
|
||||
|
||||
// Add metrics
|
||||
const metrics = this._yamlConfig.getTelemetryMetrics();
|
||||
for (const [name, metricConfig] of Object.entries(metrics)) {
|
||||
this._addMetricRow(name, metricConfig);
|
||||
}
|
||||
|
||||
// Update commands section
|
||||
const commandsEnabled = config.features?.commands?.enabled || false;
|
||||
this._commandsSwitch.active = commandsEnabled;
|
||||
|
||||
const allowlist = this._yamlConfig.getCommandsAllowlist();
|
||||
this._commandsAllowlistRow.subtitle = `${allowlist.length} commands allowed`;
|
||||
|
||||
this._dirtyConfig = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute une ligne pour une métrique
|
||||
*/
|
||||
_addMetricRow(name, metricConfig) {
|
||||
const row = new Adw.ActionRow({
|
||||
title: metricConfig.name || name,
|
||||
subtitle: `Interval: ${metricConfig.interval_s || 'N/A'}s`,
|
||||
});
|
||||
|
||||
// Switch pour enable/disable
|
||||
const metricSwitch = new Gtk.Switch({
|
||||
active: metricConfig.enabled || false,
|
||||
valign: Gtk.Align.CENTER,
|
||||
});
|
||||
metricSwitch.connect('notify::active', (sw) => {
|
||||
this._yamlConfig.updateTelemetryMetric(name, {enabled: sw.active});
|
||||
this._markDirty();
|
||||
});
|
||||
|
||||
// Bouton edit
|
||||
const editButton = new Gtk.Button({
|
||||
icon_name: 'document-edit-symbolic',
|
||||
valign: Gtk.Align.CENTER,
|
||||
});
|
||||
editButton.connect('clicked', () => {
|
||||
this._editMetric(name, metricConfig);
|
||||
});
|
||||
|
||||
row.add_suffix(metricSwitch);
|
||||
row.add_suffix(editButton);
|
||||
|
||||
this._telemetryMetricsBox.append(row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Édite une métrique
|
||||
*/
|
||||
_editMetric(name, currentConfig) {
|
||||
const dialog = new MetricEditDialog(this, name, currentConfig);
|
||||
|
||||
dialog.connect('response', (dlg, responseId) => {
|
||||
if (responseId === Gtk.ResponseType.OK) {
|
||||
const updates = dialog.getUpdates();
|
||||
this._yamlConfig.updateTelemetryMetric(name, updates);
|
||||
this._markDirty();
|
||||
this._loadData();
|
||||
}
|
||||
dialog.destroy();
|
||||
});
|
||||
|
||||
dialog.present();
|
||||
}
|
||||
|
||||
/**
|
||||
* Édite la allowlist des commandes
|
||||
*/
|
||||
_editCommandsAllowlist() {
|
||||
const currentAllowlist = this._yamlConfig.getCommandsAllowlist();
|
||||
const dialog = new CommandEditDialog(this, currentAllowlist);
|
||||
|
||||
dialog.connect('response', (dlg, responseId) => {
|
||||
if (responseId === Gtk.ResponseType.OK) {
|
||||
const newAllowlist = dialog.getAllowlist();
|
||||
this._yamlConfig.updateCommandsAllowlist(newAllowlist);
|
||||
this._markDirty();
|
||||
this._loadData();
|
||||
}
|
||||
dialog.destroy();
|
||||
});
|
||||
|
||||
dialog.present();
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le status du service
|
||||
*/
|
||||
_updateServiceStatus() {
|
||||
const isActive = this._serviceManager.isServiceActive();
|
||||
const isEnabled = this._serviceManager.isServiceEnabled();
|
||||
|
||||
this._serviceSwitch.active = isActive;
|
||||
this._serviceEnableSwitch.active = isEnabled;
|
||||
|
||||
const statusText = isActive ? '🟢 Running' : '🔴 Stopped';
|
||||
this._serviceStatusRow.subtitle = statusText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque la config comme modifiée
|
||||
*/
|
||||
_markDirty() {
|
||||
this._dirtyConfig = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sauvegarde la configuration
|
||||
*/
|
||||
_saveConfig() {
|
||||
if (!this._dirtyConfig) {
|
||||
this._showInfo('No changes to save');
|
||||
return;
|
||||
}
|
||||
|
||||
const success = this._yamlConfig.save();
|
||||
|
||||
if (success) {
|
||||
this._dirtyConfig = false;
|
||||
this._showInfo('Configuration saved successfully');
|
||||
|
||||
// Recharger le service
|
||||
this._serviceManager.reloadService();
|
||||
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => {
|
||||
this._updateServiceStatus();
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
} else {
|
||||
this._showError('Failed to save configuration');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche un message d'information
|
||||
*/
|
||||
_showInfo(message) {
|
||||
const toast = new Adw.Toast({
|
||||
title: message,
|
||||
timeout: 2,
|
||||
});
|
||||
|
||||
// Note: Toast overlay nécessite Adw.ToastOverlay
|
||||
// Pour l'instant, on utilise console.log
|
||||
console.log(`Info: ${message}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche un message d'erreur
|
||||
*/
|
||||
_showError(message) {
|
||||
const dialog = new Adw.MessageDialog({
|
||||
transient_for: this,
|
||||
heading: 'Error',
|
||||
body: message,
|
||||
});
|
||||
|
||||
dialog.add_response('ok', 'OK');
|
||||
dialog.set_default_response('ok');
|
||||
dialog.set_close_response('ok');
|
||||
|
||||
dialog.present();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user