Wait, is this peak ?
Build, push image, and notify Watchtower / build-image (push) Successful in 3m32s
Build, push image, and notify Watchtower / notify (push) Successful in 13s

This commit is contained in:
2026-08-04 18:21:20 +02:00
parent caf81d4bbb
commit 5c60017e8d
13 changed files with 249 additions and 89 deletions
+5
View File
@@ -5,6 +5,11 @@ import shared.deviceTypes as deviceTypes
import shared.config as config
import shared.payloads as payloads
import shared.cookingState as cookingState
import shared.safeQueue as safeQueue
try:
import shared.lora_device as lora_device
except ImportError:
pass # No need
try:
import shared.uart_comm as uart_comm
except ImportError:
+1 -1
View File
@@ -1,7 +1,7 @@
DEBUG=True
# LoRa
HEARTBEAT_INTERVAL = 30
LORA_HEARTBEAT_INTERVAL = 30
# MQTT
MQTT_BROKER_HOST = "192.168.50.1"
+27 -9
View File
@@ -13,6 +13,7 @@ class CookingState:
self.temperature_provider = temperature_provider
self.on_state_change = on_state_change
self.on_refresh = on_refresh
self.on_pause = None
self.state = CookingStates.COOKING
self.paused = False
@@ -24,6 +25,7 @@ class CookingState:
self.estimated_remaining_time = float(cook_time)
self._last_temperature_sample = None
self._last_refresh_signature = None
self._stirred = False
def set_temperature_provider(self, temperature_provider):
self.temperature_provider = temperature_provider
@@ -33,6 +35,9 @@ class CookingState:
def set_refresh_callback(self, callback):
self.on_refresh = callback
def set_pause_callback(self, callback):
self.on_pause = callback
def pause(self):
if self.paused:
@@ -40,8 +45,9 @@ class CookingState:
self.paused = True
self._pause_started_at = time.time()
self._notify_refresh(force=True)
# self._notify_refresh(force=True)
if self.on_pause:
self.on_pause(self)
def unpause(self):
if not self.paused:
return
@@ -50,15 +56,16 @@ class CookingState:
if self._pause_started_at is not None:
self._paused_duration += now - self._pause_started_at
self._pause_started_at = None
# self._pause_started_at = None
self.paused = False
self._notify_refresh(force=True)
# self._notify_refresh(force=True)
def toggle_pause(self):
if self.paused:
self.unpause()
else:
self.pause()
self.on_pause(self)
def set_state(self, state):
if self.state == state:
@@ -154,6 +161,8 @@ class CookingState:
self.on_refresh(self)
def update_tick(self):
if self.state == CookingStates.IDLE:
return self.state
if self.paused:
self._notify_refresh()
return self.state
@@ -169,18 +178,20 @@ class CookingState:
now = time.time()
elapsed_time = self.get_elapsed_time()
self.estimated_remaining_time = self.get_remaining_time_estimation()
print(elapsed_time, self.cook_time, self.current_dish_temp, self.target_temp, self._paused_duration, self._pause_started_at, now)
if self.current_dish_temp is not None:
if elapsed_time < (self.cook_time / 2.0) and self.current_dish_temp >= self.target_temp:
if elapsed_time < (self.cook_time / 2.0) and self.current_dish_temp >= self.target_temp and (self._paused_duration == None or self._paused_duration < 5): # If the dish is heating too fast, we require stirring
self.state = CookingStates.STIRRING_REQUIRED
self.pause()
elif elapsed_time >= self.cook_time and self.current_dish_temp >= (self.target_temp - self.TEMPERATURE_TOLERANCE):
self.state = CookingStates.DONE
elif self.state == CookingStates.DONE and self.current_dish_temp < (self.target_temp - self.TEMPERATURE_TOLERANCE):
self.state = CookingStates.COOKING
elif elapsed_time >= self.cook_time * 1.25: # If the dish is not heating up
elif elapsed_time >= self.cook_time * 1.25 and (self._paused_duration == None or self._paused_duration < 5): # If the dish is not heating up
self.state = CookingStates.STIRRING_REQUIRED
self.pause()
elif self._pause_started_at != None and (self._pause_started_at + self._paused_duration) < (now - (self.cook_time * 0.75)): # If the dish had to be pause and it's been a long time, we stop the cooking
self.state = CookingStates.DONE
self._last_temperature_sample = (now, self.current_dish_temp)
@@ -196,4 +207,11 @@ class CookingStates:
STIRRING_REQUIRED = 1
DONE = 2
ALERT = 3 # Microwave is too hot internally or other alerts
IDLE = 4 # Waiting for cooking parameters to be set, or after cooking is done
IDLE = 4 # Waiting for cooking parameters to be set, or after cooking is done
@staticmethod
def get_state_name(state_val):
for key, value in CookingStates.__dict__.items():
if value == state_val and not key.startswith('__'):
return key
return "UNKNOWN"
+1 -1
View File
@@ -344,7 +344,7 @@ else:
print(f"[RPi LoRa Serial] Transmitting HEX payload: {hex_payload}")
cmd = f"AT+PSEND={hex_payload}"
resp = self._send_at_cmd(cmd, wait_time=0.25) # Wait for RF TX to finish
print(f"[RPi LoRa Serial] AT+PSEND response: {resp.strip().replace(chr(10), ' | ')}")
print(f"[RPi LoRa Serial] AT+PSEND response: {resp}")
# Re-enable continuous receive mode after transmission completes
self._send_at_cmd("AT+PRECV=65535", wait_time=0.05)
+11
View File
@@ -303,6 +303,17 @@ class BrokerClient:
pass
finally:
self._client = None
def ping(self):
"""Thread-safe PINGREQ wrapper for MicroPython."""
if self._client is None:
return
if IS_MICROPYTHON:
with self._lock:
return self._client.ping()
else:
# Paho handles keepalives automatically via loop_start/loop
pass
def __enter__(self):
self.connect()
+37
View File
@@ -0,0 +1,37 @@
import _thread
class SafeQueue:
"""A lightweight, thread-safe FIFO queue for MicroPython."""
def __init__(self, maxsize=20):
self._queue = []
self._lock = _thread.allocate_lock()
self.maxsize = maxsize
def put(self, item) -> bool:
"""Push an item to the end of the queue. Returns False if queue is full."""
with self._lock:
if len(self._queue) < self.maxsize:
self._queue.append(item)
return True
else:
print("[Queue Warning] Buffer full, dropping oldest message.")
self._queue.pop(0) # Drop oldest to make room
self._queue.append(item)
return False
def get(self):
"""Pop and return the oldest item from the queue, or None if empty."""
with self._lock:
if self._queue:
return self._queue.pop(0)
return None
def empty(self) -> bool:
"""Check if the queue has no items."""
with self._lock:
return len(self._queue) == 0
def size(self) -> int:
"""Return current number of queued items."""
with self._lock:
return len(self._queue)