Add Pico communication service
This commit is contained in:
@@ -0,0 +1,48 @@
|
|||||||
|
from .protocol import (
|
||||||
|
make_status_request,
|
||||||
|
make_set_relay,
|
||||||
|
is_status_response,
|
||||||
|
is_relay_response,
|
||||||
|
is_error,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CommunicationService:
|
||||||
|
def __init__(self, uart_client, app_state):
|
||||||
|
self.uart_client = uart_client
|
||||||
|
self.app_state = app_state
|
||||||
|
self.last_messages = []
|
||||||
|
|
||||||
|
def request_status(self):
|
||||||
|
self.uart_client.send_message(make_status_request())
|
||||||
|
|
||||||
|
def set_relay(self, relay, enabled):
|
||||||
|
self.uart_client.send_message(make_set_relay(relay, enabled))
|
||||||
|
|
||||||
|
def poll(self):
|
||||||
|
messages = self.uart_client.read_available_messages()
|
||||||
|
self.last_messages = messages
|
||||||
|
|
||||||
|
for message in messages:
|
||||||
|
self.handle_message(message)
|
||||||
|
|
||||||
|
return messages
|
||||||
|
|
||||||
|
def handle_message(self, message):
|
||||||
|
if is_status_response(message):
|
||||||
|
self.app_state.update_from_status(message)
|
||||||
|
return
|
||||||
|
|
||||||
|
if is_relay_response(message):
|
||||||
|
self.app_state.update_from_relay_response(message)
|
||||||
|
return
|
||||||
|
|
||||||
|
if is_error(message):
|
||||||
|
self.app_state.set_error(message)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.app_state.set_error({
|
||||||
|
"type": "error",
|
||||||
|
"message": "unknown_message_type",
|
||||||
|
"raw": message,
|
||||||
|
})
|
||||||
@@ -150,3 +150,81 @@ def test_uart_client_handles_invalid_json():
|
|||||||
|
|
||||||
assert messages[0]["type"] == "error"
|
assert messages[0]["type"] == "error"
|
||||||
assert messages[0]["message"] == "invalid_json"
|
assert messages[0]["message"] == "invalid_json"
|
||||||
|
|
||||||
|
|
||||||
|
def test_communication_service_requests_status():
|
||||||
|
from comms.uart_client import UartClient
|
||||||
|
from comms.communication_service import CommunicationService
|
||||||
|
|
||||||
|
fake = FakeUart()
|
||||||
|
state = AppState()
|
||||||
|
service = CommunicationService(UartClient(fake), state)
|
||||||
|
|
||||||
|
service.request_status()
|
||||||
|
|
||||||
|
assert fake.writes == [b'{"type":"status_request"}\n']
|
||||||
|
|
||||||
|
|
||||||
|
def test_communication_service_sends_relay_command():
|
||||||
|
from comms.uart_client import UartClient
|
||||||
|
from comms.communication_service import CommunicationService
|
||||||
|
|
||||||
|
fake = FakeUart()
|
||||||
|
state = AppState()
|
||||||
|
service = CommunicationService(UartClient(fake), state)
|
||||||
|
|
||||||
|
service.set_relay("fridge", False)
|
||||||
|
|
||||||
|
assert fake.writes == [b'{"type":"set_relay","relay":"fridge","enabled":false}\n']
|
||||||
|
|
||||||
|
|
||||||
|
def test_communication_service_updates_state_from_status():
|
||||||
|
from comms.uart_client import UartClient
|
||||||
|
from comms.communication_service import CommunicationService
|
||||||
|
|
||||||
|
fake = FakeUart()
|
||||||
|
fake.read_chunks = [
|
||||||
|
b'{"type":"status_response","timestamp":99,"battery":{"soc":75},"network":{"uart_connected":true}}\n'
|
||||||
|
]
|
||||||
|
|
||||||
|
state = AppState()
|
||||||
|
service = CommunicationService(UartClient(fake), state)
|
||||||
|
|
||||||
|
messages = service.poll()
|
||||||
|
|
||||||
|
assert messages[0]["type"] == "status_response"
|
||||||
|
assert state.last_status_timestamp == 99
|
||||||
|
assert state.battery["soc"] == 75
|
||||||
|
assert state.network["uart_connected"] is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_communication_service_updates_state_from_relay_response():
|
||||||
|
from comms.uart_client import UartClient
|
||||||
|
from comms.communication_service import CommunicationService
|
||||||
|
|
||||||
|
fake = FakeUart()
|
||||||
|
fake.read_chunks = [
|
||||||
|
b'{"type":"relay_response","relay":"starlink","enabled":true,"ok":true}\n'
|
||||||
|
]
|
||||||
|
|
||||||
|
state = AppState()
|
||||||
|
service = CommunicationService(UartClient(fake), state)
|
||||||
|
|
||||||
|
service.poll()
|
||||||
|
|
||||||
|
assert state.relays["starlink"] is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_communication_service_records_errors():
|
||||||
|
from comms.uart_client import UartClient
|
||||||
|
from comms.communication_service import CommunicationService
|
||||||
|
|
||||||
|
fake = FakeUart()
|
||||||
|
fake.read_chunks = [b'{"type":"error","message":"invalid_json"}\n']
|
||||||
|
|
||||||
|
state = AppState()
|
||||||
|
service = CommunicationService(UartClient(fake), state)
|
||||||
|
|
||||||
|
service.poll()
|
||||||
|
|
||||||
|
assert state.last_error["message"] == "invalid_json"
|
||||||
|
|||||||
Reference in New Issue
Block a user