diff --git a/can_sniffer/src/flipper/pages/can_stats.py b/can_sniffer/src/flipper/pages/can_stats.py index ccff3ea..e2f65da 100644 --- a/can_sniffer/src/flipper/pages/can_stats.py +++ b/can_sniffer/src/flipper/pages/can_stats.py @@ -29,6 +29,8 @@ class CANStatsPage(InfoPage): def get_lines(self) -> list[str]: """Get statistics lines for display.""" + # Force refresh to get fresh data + self._provider.refresh() data = self._provider.get_data() lines = [ diff --git a/can_sniffer/src/flipper/pages/system_info.py b/can_sniffer/src/flipper/pages/system_info.py index b554175..d0c422a 100644 --- a/can_sniffer/src/flipper/pages/system_info.py +++ b/can_sniffer/src/flipper/pages/system_info.py @@ -29,10 +29,12 @@ class SystemInfoPage(InfoPage): def get_lines(self) -> list[str]: """Get system info lines for display.""" + # Force refresh to get fresh data + self._provider.refresh() data = self._provider.get_data() lines = [ - f"CPU Temp: {data.cpu_temp:.1f}C {self._get_temp_indicator(data.cpu_temp)}", + f"CPU: {data.cpu_temp:.1f}C {self._get_temp_indicator(data.cpu_temp)}", f"Power: {data.power_watts:.2f}W", ] diff --git a/can_sniffer/src/flipper/pages/ups_status.py b/can_sniffer/src/flipper/pages/ups_status.py index 196ef11..3ad3eb0 100644 --- a/can_sniffer/src/flipper/pages/ups_status.py +++ b/can_sniffer/src/flipper/pages/ups_status.py @@ -35,6 +35,8 @@ class UPSStatusPage(InfoPage): self._provider.get_last_error() or "Check I2C connection" ] + # Force refresh to get fresh data + self._provider.refresh() data = self._provider.get_data() # Battery bar visualization diff --git a/can_sniffer/src/flipper/providers/system_provider.py b/can_sniffer/src/flipper/providers/system_provider.py index 0b969c0..e7d0525 100644 --- a/can_sniffer/src/flipper/providers/system_provider.py +++ b/can_sniffer/src/flipper/providers/system_provider.py @@ -12,6 +12,9 @@ from dataclasses import dataclass from pathlib import Path from flipper.providers.base import BaseProvider +from logger import get_logger + +logger = get_logger(__name__) @dataclass @@ -50,16 +53,31 @@ class SystemProvider(BaseProvider): Float value or None on error """ try: - output = subprocess.check_output(args).decode("utf-8") + output = subprocess.check_output(args, stderr=subprocess.DEVNULL).decode("utf-8") value_str = output.split("=")[1].strip().rstrip(strip_chars) return float(value_str) - except Exception: + except Exception as e: + logger.debug(f"Failed to read metric {args}: {e}") return None def _read_cpu_temp(self) -> float: """Read CPU temperature in Celsius.""" + # Try vcgencmd first result = self._read_metric(["vcgencmd", "measure_temp"], "'C") - return result if result is not None else 0.0 + if result is not None: + return result + + # Fallback to sysfs thermal zone + try: + thermal_path = Path("/sys/class/thermal/thermal_zone0/temp") + if thermal_path.exists(): + with open(thermal_path, "r") as f: + temp_millicelsius = int(f.read().strip()) + return temp_millicelsius / 1000.0 + except Exception as e: + logger.debug(f"Failed to read sysfs temp: {e}") + + return 0.0 def _read_cpu_volts(self) -> float: """Read CPU core voltage.""" @@ -149,19 +167,32 @@ class SystemProvider(BaseProvider): def refresh(self) -> bool: """Refresh system metrics.""" try: + cpu_temp = self._read_cpu_temp() + cpu_volts = self._read_cpu_volts() + cpu_amps = self._read_cpu_amps() + power_watts = self._read_power_watts() + fan_rpm = self._read_fan_rpm() + input_voltage = self._read_input_voltage() + self._data = SystemData( - cpu_temp=self._read_cpu_temp(), - cpu_volts=self._read_cpu_volts(), - cpu_amps=self._read_cpu_amps(), - power_watts=self._read_power_watts(), - fan_rpm=self._read_fan_rpm(), - input_voltage=self._read_input_voltage() + cpu_temp=cpu_temp, + cpu_volts=cpu_volts, + cpu_amps=cpu_amps, + power_watts=power_watts, + fan_rpm=fan_rpm, + input_voltage=input_voltage + ) + + logger.debug( + f"System metrics: temp={cpu_temp}, power={power_watts}W, " + f"fan={fan_rpm}, input={input_voltage}V" ) self._set_cached("data", self._data) return True except Exception as e: + logger.error(f"Failed to refresh system metrics: {e}") self._last_error = str(e) return False diff --git a/can_sniffer/src/flipper/providers/ups_provider.py b/can_sniffer/src/flipper/providers/ups_provider.py index 780da4d..11cbbdd 100644 --- a/can_sniffer/src/flipper/providers/ups_provider.py +++ b/can_sniffer/src/flipper/providers/ups_provider.py @@ -10,6 +10,9 @@ from typing import Optional, Tuple from dataclasses import dataclass from flipper.providers.base import BaseProvider +from logger import get_logger + +logger = get_logger(__name__) @dataclass diff --git a/flip_monitor/can_monitor.c b/flip_monitor/can_monitor.c index 752c273..08485d2 100644 --- a/flip_monitor/can_monitor.c +++ b/flip_monitor/can_monitor.c @@ -171,7 +171,8 @@ static uint8_t parse_items(const char* str, char items[][MAX_LINE_LENGTH], uint8 // Parse PAGE message // Format: PAGE:/|||<lines>|<actions>|<selected> -static bool parse_page(const char* line, PageContent* page) { +// Returns true if page changed (different page_index or page_type) +static bool parse_page(const char* line, PageContent* page, bool* page_changed) { if(strncmp(line, "PAGE:", 5) != 0) { return false; } @@ -179,6 +180,11 @@ static bool parse_page(const char* line, PageContent* page) { const char* p = line + 5; char* end; + // Save old values to detect page change + uint8_t old_page_index = page->page_index; + PageType old_page_type = page->page_type; + bool was_valid = page->data_valid; + // Parse page index and total page->page_index = (uint8_t)strtoul(p, &end, 10); if(*end != '/') return false; @@ -192,17 +198,27 @@ static bool parse_page(const char* line, PageContent* page) { const char* type_end = strchr(p, '|'); if(!type_end) return false; + PageType new_page_type; if(strncmp(p, "info", type_end - p) == 0) { - page->page_type = PageTypeInfo; + new_page_type = PageTypeInfo; } else if(strncmp(p, "menu", type_end - p) == 0) { - page->page_type = PageTypeMenu; + new_page_type = PageTypeMenu; } else if(strncmp(p, "confirm", type_end - p) == 0) { - page->page_type = PageTypeConfirm; + new_page_type = PageTypeConfirm; } else { - page->page_type = PageTypeInfo; + new_page_type = PageTypeInfo; } + page->page_type = new_page_type; p = type_end + 1; + // Detect if page changed (index OR type changed) + bool is_page_change = !was_valid || + (page->page_index != old_page_index) || + (new_page_type != old_page_type); + if(page_changed) { + *page_changed = is_page_change; + } + // Parse title const char* title_end = strchr(p, '|'); if(!title_end) return false; @@ -243,8 +259,18 @@ static bool parse_page(const char* line, PageContent* page) { page->action_count = parse_items(actions_buf, page->actions, MAX_ACTIONS); p = actions_end + 1; - // Parse selected index - page->selected_index = (uint8_t)strtoul(p, NULL, 10); + // Parse selected index from server + uint8_t server_selected = (uint8_t)strtoul(p, NULL, 10); + + // IMPORTANT: Only update selected_index on page change + // For menu/confirm pages, preserve local selection during updates + if(is_page_change) { + page->selected_index = server_selected; + } else if(page->page_type == PageTypeInfo) { + // Info pages don't have selection, always use server value + page->selected_index = server_selected; + } + // For menu/confirm on same page: keep old_selected (local user choice) page->data_valid = true; page->last_update_tick = furi_get_tick(); @@ -322,7 +348,11 @@ static void process_line(CanMonitorApp* app, const char* line) { } else if(app->conn_state == StateConnected) { // Parse PAGE or RESULT if(line[0] == 'P') { - parse_page(line, &app->page); + bool page_changed = false; + parse_page(line, &app->page, &page_changed); + if(page_changed) { + FURI_LOG_I(TAG, "Page changed to %d", app->page.page_index); + } } else if(line[0] == 'R') { parse_result(line, &app->result); }