Add Pico display rendering abstraction
This commit is contained in:
@@ -0,0 +1,31 @@
|
|||||||
|
class Display:
|
||||||
|
def __init__(self, driver=None):
|
||||||
|
self.driver = driver
|
||||||
|
self.commands = []
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.commands.append(("clear",))
|
||||||
|
|
||||||
|
def text(self, x, y, value, size=1):
|
||||||
|
self.commands.append(("text", x, y, str(value), size))
|
||||||
|
|
||||||
|
def rect(self, x, y, w, h, filled=False):
|
||||||
|
self.commands.append(("rect", x, y, w, h, filled))
|
||||||
|
|
||||||
|
def flush(self):
|
||||||
|
if not self.driver:
|
||||||
|
return
|
||||||
|
|
||||||
|
for command in self.commands:
|
||||||
|
name = command[0]
|
||||||
|
|
||||||
|
if name == "clear":
|
||||||
|
self.driver.clear()
|
||||||
|
elif name == "text":
|
||||||
|
_, x, y, value, size = command
|
||||||
|
self.driver.text(x, y, value, size)
|
||||||
|
elif name == "rect":
|
||||||
|
_, x, y, w, h, filled = command
|
||||||
|
self.driver.rect(x, y, w, h, filled)
|
||||||
|
|
||||||
|
self.commands = []
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
class DashboardRenderer:
|
||||||
|
def __init__(self, display):
|
||||||
|
self.display = display
|
||||||
|
|
||||||
|
def render(self, view_model):
|
||||||
|
vm = view_model.as_dict()
|
||||||
|
|
||||||
|
self.display.clear()
|
||||||
|
|
||||||
|
top = vm["top_bar"]
|
||||||
|
self.display.text(0, 0, f'SOC {top["soc"]}')
|
||||||
|
self.display.text(120, 0, top["comms"])
|
||||||
|
self.display.text(260, 0, f'ALM {top["alarms"]}')
|
||||||
|
|
||||||
|
battery = vm["battery"]
|
||||||
|
self.display.text(0, 48, "Battery")
|
||||||
|
self.display.text(
|
||||||
|
0,
|
||||||
|
72,
|
||||||
|
f'{battery["voltage"]} {battery["current"]} {battery["runtime"]}',
|
||||||
|
)
|
||||||
|
|
||||||
|
fridge = vm["fridge"]
|
||||||
|
self.display.text(0, 128, "Fridge")
|
||||||
|
self.display.text(0, 152, f'Zone 1: {fridge["zone_1"]}')
|
||||||
|
self.display.text(0, 176, f'Zone 2: {fridge["zone_2"]}')
|
||||||
|
|
||||||
|
power = vm["power"]
|
||||||
|
self.display.text(0, 232, "Power")
|
||||||
|
self.display.text(
|
||||||
|
0,
|
||||||
|
256,
|
||||||
|
f'Starlink {power["starlink"]} Fridge {power["fridge"]}',
|
||||||
|
)
|
||||||
|
|
||||||
|
self.render_nav("dashboard")
|
||||||
|
|
||||||
|
def render_nav(self, active_screen):
|
||||||
|
labels = [
|
||||||
|
("dashboard", "Dash"),
|
||||||
|
("battery", "Bat"),
|
||||||
|
("temps", "Temps"),
|
||||||
|
("power", "Power"),
|
||||||
|
("system", "System"),
|
||||||
|
]
|
||||||
|
|
||||||
|
x = 0
|
||||||
|
for screen, label in labels:
|
||||||
|
text = f"[{label}]" if screen == active_screen else label
|
||||||
|
self.display.text(x, 440, text)
|
||||||
|
x += 64
|
||||||
@@ -722,3 +722,59 @@ def test_touch_router_ignores_unpressed_event():
|
|||||||
|
|
||||||
assert handled is False
|
assert handled is False
|
||||||
assert screens.current_screen == "dashboard"
|
assert screens.current_screen == "dashboard"
|
||||||
|
|
||||||
|
|
||||||
|
def test_display_records_commands():
|
||||||
|
from hardware.display import Display
|
||||||
|
|
||||||
|
display = Display()
|
||||||
|
|
||||||
|
display.clear()
|
||||||
|
display.text(1, 2, "hello", size=2)
|
||||||
|
display.rect(3, 4, 5, 6, filled=True)
|
||||||
|
|
||||||
|
assert display.commands == [
|
||||||
|
("clear",),
|
||||||
|
("text", 1, 2, "hello", 2),
|
||||||
|
("rect", 3, 4, 5, 6, True),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_dashboard_renderer_creates_draw_commands():
|
||||||
|
from hardware.display import Display
|
||||||
|
from ui.dashboard_view_model import DashboardViewModel
|
||||||
|
from ui.renderers import DashboardRenderer
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
display = Display()
|
||||||
|
renderer = DashboardRenderer(display)
|
||||||
|
|
||||||
|
renderer.render(DashboardViewModel(state))
|
||||||
|
|
||||||
|
assert display.commands[0] == ("clear",)
|
||||||
|
assert ("text", 0, 0, "SOC 82%", 1) in display.commands
|
||||||
|
assert ("text", 120, 0, "UART OK", 1) in display.commands
|
||||||
|
assert ("text", 0, 48, "Battery", 1) in display.commands
|
||||||
|
assert ("text", 0, 128, "Fridge", 1) in display.commands
|
||||||
|
assert ("text", 0, 232, "Power", 1) in display.commands
|
||||||
|
assert ("text", 0, 440, "[Dash]", 1) in display.commands
|
||||||
|
|||||||
Reference in New Issue
Block a user