Add Pico dashboard view model
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
def format_value(value, suffix="", decimals=1, missing="--"):
|
||||
if value is None:
|
||||
return missing
|
||||
|
||||
if isinstance(value, int):
|
||||
return f"{value}{suffix}"
|
||||
|
||||
if isinstance(value, float):
|
||||
return f"{value:.{decimals}f}{suffix}"
|
||||
|
||||
return f"{value}{suffix}"
|
||||
|
||||
|
||||
def format_bool(value, true_text="ON", false_text="OFF"):
|
||||
return true_text if value else false_text
|
||||
|
||||
|
||||
class DashboardViewModel:
|
||||
def __init__(self, state, alarms=None):
|
||||
self.state = state
|
||||
self.alarms = alarms or []
|
||||
|
||||
def top_bar(self):
|
||||
soc = format_value(self.state.battery.get("soc"), "%", decimals=0)
|
||||
uart = "UART OK" if self.state.network.get("uart_connected") else "UART LOST"
|
||||
alarm_count = len(self.alarms)
|
||||
|
||||
return {
|
||||
"soc": soc,
|
||||
"comms": uart,
|
||||
"alarms": alarm_count,
|
||||
}
|
||||
|
||||
def battery_summary(self):
|
||||
return {
|
||||
"voltage": format_value(self.state.battery.get("voltage"), "V", decimals=1),
|
||||
"current": format_value(self.state.battery.get("current"), "A", decimals=1),
|
||||
"runtime": format_value(self.state.battery.get("runtime_hours"), " hr", decimals=1),
|
||||
}
|
||||
|
||||
def fridge_summary(self):
|
||||
return {
|
||||
"zone_1": format_value(self.state.temps.get("fridge_zone_1"), "°F", decimals=1),
|
||||
"zone_2": format_value(self.state.temps.get("fridge_zone_2"), "°F", decimals=1),
|
||||
}
|
||||
|
||||
def power_summary(self):
|
||||
return {
|
||||
"starlink": format_bool(self.state.relays.get("starlink")),
|
||||
"fridge": format_bool(self.state.relays.get("fridge")),
|
||||
}
|
||||
|
||||
def as_dict(self):
|
||||
return {
|
||||
"top_bar": self.top_bar(),
|
||||
"battery": self.battery_summary(),
|
||||
"fridge": self.fridge_summary(),
|
||||
"power": self.power_summary(),
|
||||
}
|
||||
@@ -426,3 +426,59 @@ def test_communication_service_auto_selects_http_fallback_when_uart_down():
|
||||
|
||||
assert service.auto_select_transport() is True
|
||||
assert service.use_http_fallback is True
|
||||
|
||||
|
||||
def test_dashboard_view_model_formats_dashboard_summary():
|
||||
from ui.dashboard_view_model import DashboardViewModel
|
||||
|
||||
state = AppState()
|
||||
state.update_from_status({
|
||||
"battery": {
|
||||
"soc": 82,
|
||||
"voltage": 13.2,
|
||||
"current": -6.4,
|
||||
"runtime_hours": 12.0,
|
||||
},
|
||||
"temps": {
|
||||
"fridge_zone_1": 34.5,
|
||||
"fridge_zone_2": 36.0,
|
||||
},
|
||||
"relays": {
|
||||
"starlink": False,
|
||||
"fridge": True,
|
||||
},
|
||||
"network": {
|
||||
"uart_connected": True,
|
||||
},
|
||||
})
|
||||
|
||||
vm = DashboardViewModel(state, alarms=[]).as_dict()
|
||||
|
||||
assert vm["top_bar"]["soc"] == "82%"
|
||||
assert vm["top_bar"]["comms"] == "UART OK"
|
||||
assert vm["top_bar"]["alarms"] == 0
|
||||
|
||||
assert vm["battery"]["voltage"] == "13.2V"
|
||||
assert vm["battery"]["current"] == "-6.4A"
|
||||
assert vm["battery"]["runtime"] == "12.0 hr"
|
||||
|
||||
assert vm["fridge"]["zone_1"] == "34.5°F"
|
||||
assert vm["fridge"]["zone_2"] == "36.0°F"
|
||||
|
||||
assert vm["power"]["starlink"] == "OFF"
|
||||
assert vm["power"]["fridge"] == "ON"
|
||||
|
||||
|
||||
def test_dashboard_view_model_handles_missing_values():
|
||||
from ui.dashboard_view_model import DashboardViewModel
|
||||
|
||||
state = AppState()
|
||||
|
||||
vm = DashboardViewModel(state, alarms=["communication_lost"]).as_dict()
|
||||
|
||||
assert vm["top_bar"]["soc"] == "--"
|
||||
assert vm["top_bar"]["comms"] == "UART LOST"
|
||||
assert vm["top_bar"]["alarms"] == 1
|
||||
|
||||
assert vm["battery"]["voltage"] == "--"
|
||||
assert vm["fridge"]["zone_1"] == "--"
|
||||
|
||||
Reference in New Issue
Block a user