Send microwave states to lora
This commit is contained in:
@@ -2,6 +2,7 @@ import time
|
|||||||
from shared.cookingState import CookingState, CookingStates
|
from shared.cookingState import CookingState, CookingStates
|
||||||
from shared import config
|
from shared import config
|
||||||
import framebuf
|
import framebuf
|
||||||
|
from shared.microwave_state import MicrowaveState
|
||||||
|
|
||||||
# Icon and bitmaps
|
# Icon and bitmaps
|
||||||
image_weather_frost_bits = bytearray(b'\x01\x00\x13\x901\x18s\x9c\x09 \x05@S\x94\xfe\xfeS\x94\x05@\x09 s\x9c1\x18\x13\x90\x01\x00\x00\x00')
|
image_weather_frost_bits = bytearray(b'\x01\x00\x13\x901\x18s\x9c\x09 \x05@S\x94\xfe\xfeS\x94\x05@\x09 s\x9c1\x18\x13\x90\x01\x00\x00\x00')
|
||||||
@@ -10,12 +11,16 @@ image_CoolHiSmall_bits = bytearray(b'\x1f\xff\xc0?\xff\xe0\x7f\xff\xf0\xff\xdf\x
|
|||||||
image_CoolLoSmall_bits = bytearray(b'\x1f\xff\xc0 \x00 @\x00\x10\x80 \x08\x80\xa8\x08\x82r\x08\x86#\x08\x81$\x08\x88\xa8\x88\x84q\x08\x9f\xff\xc8\x84q\x08\x88\xa8\x88\x81$\x08\x86#\x08\x82r\x08\x80\xa8\x08\x80 \x08@\x00\x10 \x00 \x1f\xff\xc0')
|
image_CoolLoSmall_bits = bytearray(b'\x1f\xff\xc0 \x00 @\x00\x10\x80 \x08\x80\xa8\x08\x82r\x08\x86#\x08\x81$\x08\x88\xa8\x88\x84q\x08\x9f\xff\xc8\x84q\x08\x88\xa8\x88\x81$\x08\x86#\x08\x82r\x08\x80\xa8\x08\x80 \x08@\x00\x10 \x00 \x1f\xff\xc0')
|
||||||
image_music_sound_wave_bits = bytearray(b'\x00\x00\x00\x02\x00\x00\x02 \x00\x02 \x00\x02 \x00\x0a(\x00\x0a\xa8\x00*\xa8\x00\xaa\xaa\x80\x0a\xaa\x00\x0a\xa8\x00\x0a \x00\x02 \x00\x02 \x00\x02\x00\x00\x00\x00\x00')
|
image_music_sound_wave_bits = bytearray(b'\x00\x00\x00\x02\x00\x00\x02 \x00\x02 \x00\x02 \x00\x0a(\x00\x0a\xa8\x00*\xa8\x00\xaa\xaa\x80\x0a\xaa\x00\x0a\xa8\x00\x0a \x00\x02 \x00\x02 \x00\x02\x00\x00\x00\x00\x00')
|
||||||
image_weather_temperature_bits = bytearray(b'\x1c\x00\x22\x00+\x00*\x00+\x00*\x00+\x00*\x00*\x00I\x00\x9c\x80\xae\x80\xbe\x80\x9c\x80A\x00>\x00')
|
image_weather_temperature_bits = bytearray(b'\x1c\x00\x22\x00+\x00*\x00+\x00*\x00+\x00*\x00*\x00I\x00\x9c\x80\xae\x80\xbe\x80\x9c\x80A\x00>\x00')
|
||||||
|
image_camera_bits = bytearray(b'\x00x\x000\x84\x00\x7f{\x00\xc0\x00\x80\x91\xc6\x80\x860\x80\x85\xd0\x80\x8a(\x80\x8a(\x80\x8a(\x80\x85\xd0\x80\x860\x80\x91\xc6\x80\xc0\x00\x80\x7f\xff\x00\x00\x00\x00')
|
||||||
|
image_cloud_sync_bits = bytearray(b'\x00\x00\x00\x07\xc0\x00\x08 \x00\x10\x10\x000\x08\x00@\x0e\x00\x80\x01\x00\x82 \x80G \x80/\xaf\x00\x02 \x00\x02\xf8\x00\x02p\x00\x02 \x00\x00\x00\x00\x00\x00\x00')
|
||||||
fb_image_weather_frost_bits = framebuf.FrameBuffer(image_weather_frost_bits, 15, 16, framebuf.MONO_HLSB)
|
fb_image_weather_frost_bits = framebuf.FrameBuffer(image_weather_frost_bits, 15, 16, framebuf.MONO_HLSB)
|
||||||
fb_image_CoolHiSmall_bits = framebuf.FrameBuffer(image_CoolHiSmall_bits, 21, 21, framebuf.MONO_HLSB)
|
fb_image_CoolHiSmall_bits = framebuf.FrameBuffer(image_CoolHiSmall_bits, 21, 21, framebuf.MONO_HLSB)
|
||||||
fb_image_weather_temperature_bits = framebuf.FrameBuffer(image_weather_temperature_bits, 9, 16, framebuf.MONO_HLSB)
|
fb_image_weather_temperature_bits = framebuf.FrameBuffer(image_weather_temperature_bits, 9, 16, framebuf.MONO_HLSB)
|
||||||
fb_image_Alert_bits = framebuf.FrameBuffer(image_Alert_bits, 9, 8, framebuf.MONO_HLSB)
|
fb_image_Alert_bits = framebuf.FrameBuffer(image_Alert_bits, 9, 8, framebuf.MONO_HLSB)
|
||||||
fb_image_music_sound_wave_bits = framebuf.FrameBuffer(image_music_sound_wave_bits, 17, 16, framebuf.MONO_HLSB)
|
fb_image_music_sound_wave_bits = framebuf.FrameBuffer(image_music_sound_wave_bits, 17, 16, framebuf.MONO_HLSB)
|
||||||
fb_image_CoolLoSmall_bits = framebuf.FrameBuffer(image_CoolLoSmall_bits, 21, 21, framebuf.MONO_HLSB)
|
fb_image_CoolLoSmall_bits = framebuf.FrameBuffer(image_CoolLoSmall_bits, 21, 21, framebuf.MONO_HLSB)
|
||||||
|
fb_image_camera_bits = framebuf.FrameBuffer(image_camera_bits, 17, 16, framebuf.MONO_HLSB)
|
||||||
|
fb_image_cloud_sync_bits = framebuf.FrameBuffer(image_cloud_sync_bits, 17, 16, framebuf.MONO_HLSB)
|
||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
CHARACTER_WIDTH = 6
|
CHARACTER_WIDTH = 6
|
||||||
@@ -70,44 +75,50 @@ class MicrowaveScreen:
|
|||||||
|
|
||||||
self.display.fill_rect(x, y, fill_width, height, color)
|
self.display.fill_rect(x, y, fill_width, height, color)
|
||||||
|
|
||||||
def update(self, cooking_state: CookingState | None, defrost_mode: bool = False):
|
def update(self, cooking_state: CookingState | None, defrost_mode: bool = False, microwave_state: str | None = None):
|
||||||
self.flick()
|
self.flick()
|
||||||
|
|
||||||
# self.lastUpdateTime = time.time() # Not Used right now
|
# self.lastUpdateTime = time.time() # Not Used right now
|
||||||
|
|
||||||
if not cooking_state or cooking_state.state == CookingStates.IDLE: # Is not currently cooking
|
if (not microwave_state or microwave_state == MicrowaveState.DONE) and (not cooking_state or cooking_state.state == CookingStates.IDLE): # Is not currently cooking
|
||||||
self.h_centered_text("Ready !", 28, 1)
|
self.h_centered_text("Ready !", 28, 1)
|
||||||
else:
|
else:
|
||||||
# Screen center
|
# Screen center
|
||||||
if cooking_state.state == CookingStates.STIRRING_REQUIRED:
|
if cooking_state and cooking_state.state == CookingStates.STIRRING_REQUIRED:
|
||||||
self._message("Pls Stir", True)
|
self._message("Pls Stir", True)
|
||||||
elif cooking_state.state == CookingStates.ALERT:
|
elif cooking_state and cooking_state.state == CookingStates.ALERT:
|
||||||
self._message("Alert !", True)
|
self._message("Alert !", True)
|
||||||
elif cooking_state.state == CookingStates.DONE:
|
elif cooking_state and cooking_state.state == CookingStates.DONE:
|
||||||
self._message("Done !", False)
|
self._message("Done !", False)
|
||||||
|
elif microwave_state and microwave_state == MicrowaveState.ANALYZING:
|
||||||
|
self.display.blit(fb_image_camera_bits, 56, 24)
|
||||||
|
self._secondary_message("Analyzing...", False)
|
||||||
|
elif microwave_state and microwave_state == MicrowaveState.WAITING_FOR_CLOUD:
|
||||||
|
self.display.blit(fb_image_cloud_sync_bits, 55, 24)
|
||||||
|
self._secondary_message("Receiving...", False)
|
||||||
else: # Progress bar
|
else: # Progress bar
|
||||||
elapsed_time = cooking_state.get_elapsed_time()
|
elapsed_time = cooking_state.get_elapsed_time()
|
||||||
estimated_progress = elapsed_time / (elapsed_time + cooking_state.estimated_remaining_time)
|
estimated_progress = elapsed_time / (elapsed_time + cooking_state.estimated_remaining_time)
|
||||||
self._progressBar(estimated_progress, 21, 25)
|
self._progressBar(estimated_progress, 21, 25)
|
||||||
|
|
||||||
# Estimated remaining time
|
# Estimated remaining time
|
||||||
if cooking_state.paused:
|
if cooking_state and cooking_state.paused:
|
||||||
self.h_centered_text("Paused", 40, 1)
|
self.h_centered_text("Paused", 40, 1)
|
||||||
elif cooking_state.state != CookingStates.DONE:
|
elif cooking_state and cooking_state.state != CookingStates.DONE:
|
||||||
remaining_time = int(cooking_state.estimated_remaining_time)
|
remaining_time = int(cooking_state.estimated_remaining_time)
|
||||||
minutes = remaining_time // 60
|
minutes = remaining_time // 60
|
||||||
seconds = remaining_time % 60
|
seconds = remaining_time % 60
|
||||||
self.h_centered_text(f"{minutes:02}:{seconds:02}", 40, 1)
|
self.h_centered_text(f"{minutes:02}:{seconds:02}", 40, 1)
|
||||||
|
|
||||||
# Current dish temperature
|
# Current dish temperature
|
||||||
if cooking_state.current_dish_temp is not None and cooking_state.current_dish_temp is not 0.0:
|
if cooking_state and cooking_state.current_dish_temp is not None and cooking_state.current_dish_temp is not 0.0:
|
||||||
self.display.blit(fb_image_weather_temperature_bits, 27, 2)
|
self.display.blit(fb_image_weather_temperature_bits, 27, 2)
|
||||||
self.display.text(str(int(cooking_state.current_dish_temp)), 37, 6, 1)
|
self.display.text(str(int(cooking_state.current_dish_temp)), 37, 6, 1)
|
||||||
self.display.ellipse(54, 6, 1, 1, 1, False)
|
self.display.ellipse(54, 6, 1, 1, 1, False)
|
||||||
self.display.text("C", 56, 6, 1)
|
self.display.text("C", 56, 6, 1)
|
||||||
|
|
||||||
# Power level
|
# Power level
|
||||||
if cooking_state.power_level is not None:
|
if cooking_state and cooking_state.power_level is not None:
|
||||||
self.display.blit(fb_image_music_sound_wave_bits, 74, 2)
|
self.display.blit(fb_image_music_sound_wave_bits, 74, 2)
|
||||||
self.display.text(f"{cooking_state.power_level}W", 92, 6, 1)
|
self.display.text(f"{cooking_state.power_level}W", 92, 6, 1)
|
||||||
if defrost_mode:
|
if defrost_mode:
|
||||||
@@ -118,12 +129,18 @@ class MicrowaveScreen:
|
|||||||
self.show()
|
self.show()
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _message(self, text, alert):
|
def _message_at(self, y, text, alert):
|
||||||
self.display.fill_rect(0, 27, self.width, CHARACTER_HEIGHT + 4, 1)
|
self.display.fill_rect(0, y, self.width, CHARACTER_HEIGHT + 4, 1)
|
||||||
if alert:
|
if alert:
|
||||||
self.display.blit(fb_image_Alert_bits, 10, 29)
|
self.display.blit(fb_image_Alert_bits, 10, y+2)
|
||||||
self.display.blit(fb_image_Alert_bits, self.width - 10, 29)
|
self.display.blit(fb_image_Alert_bits, self.width - 10, y+2)
|
||||||
self.h_centered_text(text, 29, 0)
|
self.h_centered_text(text, y+2, 0)
|
||||||
|
|
||||||
|
def _message(self, text, alert):
|
||||||
|
self._message_at(27, text, alert)
|
||||||
|
|
||||||
|
def _secondary_message(self, text, alert):
|
||||||
|
self._message_at(40, text, alert)
|
||||||
|
|
||||||
def message(self, text, alert):
|
def message(self, text, alert):
|
||||||
# self.flick()
|
# self.flick()
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from shared.sensors import RGBLED
|
|||||||
from shared.logging import log
|
from shared.logging import log
|
||||||
from shared.lora_device import LoraCommands
|
from shared.lora_device import LoraCommands
|
||||||
from shared.alerts import Alert, AlertType, AlertManager
|
from shared.alerts import Alert, AlertType, AlertManager
|
||||||
|
from shared.microwave_state import MicrowaveState
|
||||||
|
|
||||||
# --- READ DEVICE ID ---
|
# --- READ DEVICE ID ---
|
||||||
try:
|
try:
|
||||||
@@ -29,6 +30,7 @@ except Exception:
|
|||||||
# --- GLOBAL VARIABLES ---
|
# --- GLOBAL VARIABLES ---
|
||||||
cooking_state = None
|
cooking_state = None
|
||||||
cooking_start_time = None # Track start timestamp
|
cooking_start_time = None # Track start timestamp
|
||||||
|
microwave_state: str = None
|
||||||
data_queue = SafeQueue()
|
data_queue = SafeQueue()
|
||||||
lora = None
|
lora = None
|
||||||
uart_device = None
|
uart_device = None
|
||||||
@@ -196,12 +198,11 @@ def cooking_state_on_state_change(state):
|
|||||||
paused=state.paused
|
paused=state.paused
|
||||||
)
|
)
|
||||||
|
|
||||||
microwave_screen.update(cooking_state, defrost_mode)
|
update_screen()
|
||||||
|
|
||||||
def cooking_state_on_refresh(state):
|
def cooking_state_on_refresh(state):
|
||||||
# Update OLED display
|
# Update OLED display
|
||||||
if microwave_screen:
|
update_screen()
|
||||||
microwave_screen.update(state, defrost_mode)
|
|
||||||
|
|
||||||
def cooking_state_on_pause(state):
|
def cooking_state_on_pause(state):
|
||||||
# If the cooking is unpaused and was in STIRRING_REQUIRED or ALERT state, we set the state back to COOKING.
|
# If the cooking is unpaused and was in STIRRING_REQUIRED or ALERT state, we set the state back to COOKING.
|
||||||
@@ -215,6 +216,13 @@ def cooking_state_on_pause(state):
|
|||||||
estimated_remaining_time=state.get_remaining_time()
|
estimated_remaining_time=state.get_remaining_time()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# --- SCREEN UPDATE ---
|
||||||
|
def update_screen():
|
||||||
|
global microwave_screen, cooking_state, defrost_mode, microwave_state
|
||||||
|
|
||||||
|
if microwave_screen:
|
||||||
|
microwave_screen.update(cooking_state, defrost_mode, microwave_state)
|
||||||
|
|
||||||
|
|
||||||
# --- ASYNC TASKS ---
|
# --- ASYNC TASKS ---
|
||||||
|
|
||||||
@@ -266,7 +274,7 @@ async def uart_polling_task():
|
|||||||
|
|
||||||
async def lora_process_task():
|
async def lora_process_task():
|
||||||
"""Consumes packets pushed to data_queue by the LoRa hardware thread."""
|
"""Consumes packets pushed to data_queue by the LoRa hardware thread."""
|
||||||
global cooking_state, defrost_mode, microwave_screen
|
global cooking_state, defrost_mode, microwave_screen, microwave_state
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
while not data_queue.empty():
|
while not data_queue.empty():
|
||||||
@@ -293,12 +301,21 @@ async def lora_process_task():
|
|||||||
elif data["action"] == LoraCommands.TOGGLE_DEFROST:
|
elif data["action"] == LoraCommands.TOGGLE_DEFROST:
|
||||||
print("[LoRa Process] Toggling defrost mode via orchestrator command.")
|
print("[LoRa Process] Toggling defrost mode via orchestrator command.")
|
||||||
defrost_mode = data["defrost_state"]
|
defrost_mode = data["defrost_state"]
|
||||||
microwave_screen.update(cooking_state, defrost_mode)
|
update_screen()
|
||||||
elif data["action"] == LoraCommands.NEW_ALERT:
|
elif data["action"] == LoraCommands.NEW_ALERT:
|
||||||
alert = Alert(data.get("alert_type"), data.get("message", "Unsafe area"))
|
alert = Alert(data.get("alert_type"), data.get("message", "Unsafe area"))
|
||||||
alert.timestamp = data.get("timestamp", time.time())
|
alert.timestamp = data.get("timestamp", time.time())
|
||||||
print(f"[LoRa Process] New alert received via orchestrator: {alert.to_dict()}")
|
print(f"[LoRa Process] New alert received via orchestrator: {alert.to_dict()}")
|
||||||
alert_manager.add_alert(alert)
|
alert_manager.add_alert(alert)
|
||||||
|
elif data["action"] == LoraCommands.MICROVAVE_STATE_UPDATE:
|
||||||
|
new_state = data.get("new_microwave_state")
|
||||||
|
if new_state:
|
||||||
|
print(f"[LoRa Process] Microwave state update received: {new_state}")
|
||||||
|
microwave_state = new_state
|
||||||
|
update_screen()
|
||||||
|
else:
|
||||||
|
print("[LoRa Process] Received microwave state update with no state specified.")
|
||||||
|
|
||||||
|
|
||||||
await asyncio.sleep_ms(100)
|
await asyncio.sleep_ms(100)
|
||||||
|
|
||||||
@@ -358,8 +375,7 @@ async def main():
|
|||||||
|
|
||||||
print("[Main] All async tasks running concurrently!")
|
print("[Main] All async tasks running concurrently!")
|
||||||
|
|
||||||
microwave_screen.update(None, defrost_mode)
|
update_screen()
|
||||||
|
|
||||||
# Keep main task alive indefinitely
|
# Keep main task alive indefinitely
|
||||||
while True:
|
while True:
|
||||||
await asyncio.sleep(3600)
|
await asyncio.sleep(3600)
|
||||||
|
|||||||
+16
-4
@@ -81,6 +81,15 @@ def notify_display_update():
|
|||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def on_microwave_change(state: MicrowaveState, field: MicrowaveStateFields):
|
||||||
|
if field != MicrowaveStateFields.STATE: # No external screen or lcd uses this field, so we can skip updates for it
|
||||||
|
notify_display_update()
|
||||||
|
|
||||||
|
if field == MicrowaveStateFields.STATE and state.state in (MicrowaveState.DONE, MicrowaveState.ANALYZING, MicrowaveState.WAITING_FOR_CLOUD):
|
||||||
|
# Send to LoRa device if state changed
|
||||||
|
lora.send_reliable(payloads.lora_microwave_state(state.state), max_retries=5)
|
||||||
|
|
||||||
|
|
||||||
def set_cloud_alert(state: bool):
|
def set_cloud_alert(state: bool):
|
||||||
"""Setter for cloud_alert that triggers a display update when changed."""
|
"""Setter for cloud_alert that triggers a display update when changed."""
|
||||||
global cloud_alert
|
global cloud_alert
|
||||||
@@ -91,7 +100,7 @@ def set_cloud_alert(state: bool):
|
|||||||
update_lcd_cloud_alert()
|
update_lcd_cloud_alert()
|
||||||
|
|
||||||
# Global state trackers
|
# Global state trackers
|
||||||
microwave_states = {"2": MicrowaveState("2", on_change_callback=notify_display_update)}
|
microwave_states = {"2": MicrowaveState("2", on_change_callback=on_microwave_change)}
|
||||||
button_state = False
|
button_state = False
|
||||||
cloud_alert = None # Global status flag for screen / UI display
|
cloud_alert = None # Global status flag for screen / UI display
|
||||||
async_event_queue = None
|
async_event_queue = None
|
||||||
@@ -434,7 +443,7 @@ async def request_cloud_cooking_plan(microwave_id, sensors_data):
|
|||||||
|
|
||||||
if c_time is None or c_power is None or c_temp is None:
|
if c_time is None or c_power is None or c_temp is None:
|
||||||
print(f"[{microwave_id}] Invalid plan received: {res_json}")
|
print(f"[{microwave_id}] Invalid plan received: {res_json}")
|
||||||
microwave_states[microwave_id].set_state(MicrowaveState.IDLE)
|
microwave_states[microwave_id].set_state(MicrowaveState.DONE)
|
||||||
return
|
return
|
||||||
|
|
||||||
print(f"[{microwave_id}] Cloud Plan Received! Starting microwave: {c_time}s @ {c_power}W")
|
print(f"[{microwave_id}] Cloud Plan Received! Starting microwave: {c_time}s @ {c_power}W")
|
||||||
@@ -530,7 +539,7 @@ async def process_messages_task():
|
|||||||
# Pass change callback to newly connected microwave
|
# Pass change callback to newly connected microwave
|
||||||
microwave_states[component_id] = MicrowaveState(
|
microwave_states[component_id] = MicrowaveState(
|
||||||
component_id,
|
component_id,
|
||||||
on_change_callback=notify_display_update
|
on_change_callback=on_microwave_change
|
||||||
)
|
)
|
||||||
notify_display_update() # Update the external screens
|
notify_display_update() # Update the external screens
|
||||||
update_lcd_microwave_count() # Update the LCD with new microwave count
|
update_lcd_microwave_count() # Update the LCD with new microwave count
|
||||||
@@ -641,7 +650,10 @@ async def monitor_dish_height_task():
|
|||||||
|
|
||||||
while True:
|
while True:
|
||||||
dist = await get_filtered_dish_height()
|
dist = await get_filtered_dish_height()
|
||||||
current_state = microwave_states.get(mw_id, MicrowaveState.IDLE).state
|
try:
|
||||||
|
current_state = microwave_states.get(mw_id).state
|
||||||
|
except AttributeError:
|
||||||
|
current_state = MicrowaveState.IDLE
|
||||||
|
|
||||||
if dist is not None:
|
if dist is not None:
|
||||||
# Hysteresis Thresholds:
|
# Hysteresis Thresholds:
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import shared.payloads as payloads
|
|||||||
import shared.cookingState as cookingState
|
import shared.cookingState as cookingState
|
||||||
import shared.safeQueue as safeQueue
|
import shared.safeQueue as safeQueue
|
||||||
import shared.alerts as alerts
|
import shared.alerts as alerts
|
||||||
|
import shared.microwave_state as microwave_state
|
||||||
try:
|
try:
|
||||||
import shared.lora_device as lora_device
|
import shared.lora_device as lora_device
|
||||||
except (ImportError, SyntaxError):
|
except (ImportError, SyntaxError):
|
||||||
|
|||||||
@@ -79,3 +79,14 @@ def lora_new_alert(alert: Alert):
|
|||||||
"action": LoraCommands.NEW_ALERT,
|
"action": LoraCommands.NEW_ALERT,
|
||||||
"alert_type": alert.alertType
|
"alert_type": alert.alertType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def lora_microwave_state(state: str):
|
||||||
|
try:
|
||||||
|
from shared.lora_device import LoraCommands
|
||||||
|
except ImportError:
|
||||||
|
pass # No need
|
||||||
|
|
||||||
|
return {
|
||||||
|
"action": LoraCommands.MICROVAVE_STATE_UPDATE,
|
||||||
|
"new_microwave_state": state
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user