External Screen working
This commit is contained in:
@@ -0,0 +1,54 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "esphome.h"
|
||||||
|
#include "esphome/components/web_server_base/web_server_base.h"
|
||||||
|
#include "esphome/components/json/json_util.h"
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Callback signature: void(device_id, cloud_alert, defrost_mode)
|
||||||
|
using DisplayUpdateCallback = std::function<void(const std::string &device_id, bool cloud_alert, bool defrost_mode)>;
|
||||||
|
|
||||||
|
static DisplayUpdateCallback g_display_callback = nullptr;
|
||||||
|
|
||||||
|
class DisplayApiHandler : public esphome::web_server_idf::AsyncWebHandler {
|
||||||
|
public:
|
||||||
|
bool canHandle(esphome::web_server_idf::AsyncWebServerRequest *request) const override {
|
||||||
|
return request->url_to() == "/api/display";
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleRequest(esphome::web_server_idf::AsyncWebServerRequest *request) override {
|
||||||
|
request->send(200, "application/json", "{\"status\":\"ok\"}");
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleBody(esphome::web_server_idf::AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override {
|
||||||
|
std::string body((char *)data, len);
|
||||||
|
|
||||||
|
esphome::json::parse_json(body, [](JsonObject root) -> bool {
|
||||||
|
std::string device_id = root["device_id"] | "unknown";
|
||||||
|
bool cloud_alert = root["cloud_alert"] | false;
|
||||||
|
bool defrost_mode = root["defrost_mode"] | false;
|
||||||
|
|
||||||
|
ESP_LOGI("custom_api", "Received update for %s (Alert: %d, Defrost: %d)",
|
||||||
|
device_id.c_str(), cloud_alert, defrost_mode);
|
||||||
|
|
||||||
|
// Execute the callback passed from YAML
|
||||||
|
if (g_display_callback != nullptr) {
|
||||||
|
g_display_callback(device_id, cloud_alert, defrost_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
request->send(200, "application/json", "{\"status\":\"ok\"}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void setup_display_api(DisplayUpdateCallback callback) {
|
||||||
|
g_display_callback = callback;
|
||||||
|
if (esphome::web_server_base::global_web_server_base != nullptr) {
|
||||||
|
esphome::web_server_base::global_web_server_base->add_handler(new DisplayApiHandler());
|
||||||
|
ESP_LOGI("custom_api", "Successfully registered /api/display POST handler");
|
||||||
|
} else {
|
||||||
|
ESP_LOGE("custom_api", "Failed to register endpoint: global_web_server_base is null");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,18 +2,57 @@ substitutions:
|
|||||||
device_id: !include device_id.txt
|
device_id: !include device_id.txt
|
||||||
|
|
||||||
esphome:
|
esphome:
|
||||||
name: epaper-node
|
name: "smartwave-epaper-${device_id}"
|
||||||
friendly_name: "Microwave Thread Display Node"
|
friendly_name: "SmartWave ePaper ExternalScreen ${device_id}"
|
||||||
|
includes:
|
||||||
|
- display_api.h
|
||||||
on_boot:
|
on_boot:
|
||||||
priority: -100.0
|
priority: -100.0
|
||||||
then:
|
then:
|
||||||
- component.update: my_display
|
- lambda: |-
|
||||||
|
setup_display_api([](const std::string &dev_id, bool alert, bool defrost) {
|
||||||
|
// Update global state
|
||||||
|
id(cloud_alert) = alert;
|
||||||
|
id(defrost_mode) = defrost;
|
||||||
|
|
||||||
|
// Set status message text
|
||||||
|
std::string msg = "Microwave " + dev_id;
|
||||||
|
id(status_message).publish_state(msg.c_str());
|
||||||
|
|
||||||
|
// Force immediate e-Paper refresh!
|
||||||
|
id(my_display).update();
|
||||||
|
});
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------
|
||||||
|
# State Globals
|
||||||
|
# -------------------------------------------------------------------
|
||||||
|
globals:
|
||||||
|
- id: cloud_alert
|
||||||
|
type: bool
|
||||||
|
initial_value: 'false'
|
||||||
|
- id: defrost_mode
|
||||||
|
type: bool
|
||||||
|
initial_value: 'false'
|
||||||
|
|
||||||
|
web_server:
|
||||||
|
port: 5000
|
||||||
|
|
||||||
|
mdns:
|
||||||
|
disabled: false
|
||||||
|
services:
|
||||||
|
- service: "_displaytcp"
|
||||||
|
protocol: "_tcp"
|
||||||
|
port: 5000
|
||||||
|
txt:
|
||||||
|
version: "1.0"
|
||||||
|
type: "epaper_2.13"
|
||||||
|
|
||||||
esp32:
|
esp32:
|
||||||
board: esp32-h2-devkitm-1
|
board: epaper-node-display
|
||||||
variant: esp32h2
|
variant: esp32h2
|
||||||
framework:
|
framework:
|
||||||
type: esp-idf
|
type: esp-idf
|
||||||
|
log_level: INFO
|
||||||
advanced:
|
advanced:
|
||||||
loop_task_stack_size: 32768
|
loop_task_stack_size: 32768
|
||||||
sdkconfig_options:
|
sdkconfig_options:
|
||||||
@@ -25,43 +64,39 @@ esp32:
|
|||||||
CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA: "y"
|
CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA: "y"
|
||||||
|
|
||||||
logger:
|
logger:
|
||||||
level: INFO
|
level: VERBOSE
|
||||||
hardware_uart: UART0
|
hardware_uart: UART0
|
||||||
|
logs:
|
||||||
|
openthread: INFO
|
||||||
|
|
||||||
network:
|
network:
|
||||||
enable_ipv6: true
|
enable_ipv6: true
|
||||||
|
|
||||||
# -------------------------------------------------------------------
|
|
||||||
# OpenThread Network Settings
|
|
||||||
# -------------------------------------------------------------------
|
|
||||||
openthread:
|
openthread:
|
||||||
network_name: "OpenThread-7f41"
|
|
||||||
pan_id: 0x7f41
|
|
||||||
channel: 24
|
|
||||||
network_key: "0x3f543281275a135e38982974aa71e987"
|
|
||||||
force_dataset: true
|
force_dataset: true
|
||||||
|
tlv: 00030000184a0300000b35060004001fffe00208f7754c0c4a4dea9a0708fd1adbbe7435e95405103f543281275a135e38982974aa71e987030f4f70656e5468726561642d3766343101027f410410a500c86abfe7ef17888c2232feac2dc60c0402a0f7f80e080000000000010000
|
||||||
|
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
# MQTT Connection over Thread IPv6
|
# MQTT Connection over Thread IPv6
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
mqtt:
|
# mqtt:
|
||||||
broker: "fd1a:dbbe:7435:e954:4455:28c5:b6cf:e990"
|
# broker: "mqtt.thread.smartwave.matthiasg.dev"
|
||||||
port: 8884
|
# # broker: "mqtt://[fd36:5102:8d0:1:f1cd:3499:e546:dbec]"
|
||||||
skip_cert_cn_check: true
|
# # port: 1883
|
||||||
topic_prefix: microwave/display
|
# topic_prefix: "microwave/display"
|
||||||
keepalive: 60s
|
# keepalive: 60s
|
||||||
on_connect:
|
# on_connect:
|
||||||
- mqtt.publish:
|
# - mqtt.publish:
|
||||||
topic: smartwave/hello
|
# topic: smartwave/hello
|
||||||
payload: !lambda |-
|
# payload: !lambda |-
|
||||||
return std::string("{\"id_microwave\":\"") + "${device_id}" + "\",\"type\":\"externalDisplay\"}";
|
# return std::string("{\"id_microwave\":\"") + "${device_id}" + "\",\"type\":\"externalDisplay\"}";
|
||||||
on_message:
|
# on_message:
|
||||||
- topic: microwave/display/status
|
# - topic: microwave/display/status
|
||||||
then:
|
# then:
|
||||||
- text_sensor.template.publish:
|
# - text_sensor.template.publish:
|
||||||
id: status_message
|
# id: status_message
|
||||||
state: !lambda 'return x;'
|
# state: !lambda 'return x;'
|
||||||
- component.update: my_display
|
# - component.update: my_display
|
||||||
|
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
# Template Text Sensor
|
# Template Text Sensor
|
||||||
@@ -70,6 +105,19 @@ text_sensor:
|
|||||||
- platform: template
|
- platform: template
|
||||||
name: "Display Status Message"
|
name: "Display Status Message"
|
||||||
id: status_message
|
id: status_message
|
||||||
|
- platform: openthread_info
|
||||||
|
ip_address:
|
||||||
|
name: "Thread IP"
|
||||||
|
id: thread_ip
|
||||||
|
role:
|
||||||
|
name: "Thread Role"
|
||||||
|
id: thread_role
|
||||||
|
channel:
|
||||||
|
name: "Thread Channel"
|
||||||
|
id: thread_channel
|
||||||
|
rloc16:
|
||||||
|
name: "Thread RLOC16"
|
||||||
|
id: thread_rloc16
|
||||||
|
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
# Hardware SPI Pin Mapping (ESP32-H2)
|
# Hardware SPI Pin Mapping (ESP32-H2)
|
||||||
@@ -82,12 +130,18 @@ spi:
|
|||||||
# Fonts
|
# Fonts
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
font:
|
font:
|
||||||
- file: "gfonts://Roboto"
|
- file:
|
||||||
|
type: gfonts
|
||||||
|
family: Open+Sans
|
||||||
|
weight: 700
|
||||||
|
size: 20
|
||||||
id: font_header
|
id: font_header
|
||||||
size: 18
|
- file:
|
||||||
- file: "gfonts://Roboto"
|
type: gfonts
|
||||||
|
family: Overpass
|
||||||
|
weight: 400
|
||||||
id: font_body
|
id: font_body
|
||||||
size: 12
|
size: 14
|
||||||
|
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
# Waveshare 2.13-inch e-Paper Display (V4 panel = 2.13inv3 in ESPHome)
|
# Waveshare 2.13-inch e-Paper Display (V4 panel = 2.13inv3 in ESPHome)
|
||||||
@@ -100,16 +154,38 @@ display:
|
|||||||
busy_pin: GPIO3
|
busy_pin: GPIO3
|
||||||
model: 2.13inv3
|
model: 2.13inv3
|
||||||
rotation: 90°
|
rotation: 90°
|
||||||
update_interval: 30s
|
update_interval: 30min
|
||||||
id: my_display
|
id: my_display
|
||||||
lambda: |-
|
lambda: |-
|
||||||
if (id(status_message).has_state()) {
|
if (id(status_message).has_state()) {
|
||||||
it.print(5, 3, id(font_header), "MICROWAVE STATUS");
|
// Header
|
||||||
it.line(0, 20, it.get_width(), 20);
|
it.print(5, 3, id(font_header), id(status_message).state.c_str());
|
||||||
it.print(5, 25, id(font_body), id(status_message).state.c_str());
|
it.line(0, 26, it.get_width(), 26);
|
||||||
|
|
||||||
|
// Mode Status
|
||||||
|
if (id(defrost_mode)) {
|
||||||
|
it.print(5, 32, id(font_header), "MODE: DEFROST");
|
||||||
|
} else {
|
||||||
|
it.print(5, 32, id(font_header), "MODE: NORMAL");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cloud Alert Status Box
|
||||||
|
if (id(cloud_alert)) {
|
||||||
|
it.filled_rectangle(5, 60, 240, 24, COLOR_OFF); // Inverse background if supported, or frame box
|
||||||
|
it.print(10, 62, id(font_body), "CLOUD ALERT ACTIVE");
|
||||||
|
} else {
|
||||||
|
it.print(10, 62, id(font_body), "System Normal");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Footer: Thread Info
|
||||||
|
it.line(0, 95, it.get_width(), 95);
|
||||||
|
it.printf(5, 100, id(font_body), "Role: %s | Ch: %s",
|
||||||
|
id(thread_role).state.c_str(),
|
||||||
|
id(thread_channel).state.c_str());
|
||||||
} else {
|
} else {
|
||||||
int center_x = it.get_width() / 2;
|
int center_x = it.get_width() / 2;
|
||||||
int center_y = it.get_height() / 2;
|
int center_y = it.get_height() / 2;
|
||||||
|
|
||||||
it.printf(center_x, center_y, id(font_header), TextAlign::CENTER, "SmartWave");
|
it.printf(center_x, center_y - 10, id(font_header), TextAlign::CENTER, "SmartWave");
|
||||||
|
it.printf(center_x, center_y + 15, id(font_body), TextAlign::CENTER, "Waiting for Pi updates...");
|
||||||
}
|
}
|
||||||
@@ -1,26 +1,32 @@
|
|||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
otbr:
|
otbr:
|
||||||
image: openthread/otbr:latest
|
image: openthread/border-router:latest
|
||||||
container_name: otbr
|
container_name: otbr
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
privileged: true
|
privileged: true
|
||||||
network_mode: host
|
network_mode: host
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
environment:
|
||||||
|
- OT_RCP_DEVICE=spinel+hdlc+uart:///dev/ttyUSB1?uart-baudrate=460800
|
||||||
|
- OT_INFRA_IF=wlan0
|
||||||
|
- OT_THREAD_IF=wpan0
|
||||||
|
- OT_LOG_LEVEL=6
|
||||||
|
- FIREWALL=0
|
||||||
|
- NAT64=1
|
||||||
devices:
|
devices:
|
||||||
# Change /dev/ttyACM0 if your ESP32-H2 RCP enumerates as /dev/ttyUSB0
|
- /dev/serial/by-id/usb-Silicon_Labs_CP2102N_USB_to_UART_Bridge_Controller_ba78c1181566ee11b42a8d6293cd958c-if00-port0:/dev/ttyUSB1
|
||||||
- /dev/ttyACM0:/dev/ttyACM0
|
- /dev/net/tun:/dev/net/tun
|
||||||
command: --radio-url "spinel+hdlc+uart:///dev/ttyACM0?uart-baudrate=460800"
|
volumes:
|
||||||
|
- otbr-data:/data
|
||||||
|
|
||||||
mqtt-broker:
|
mqtt-broker:
|
||||||
image: eclipse-mosquitto:2.0
|
image: eclipse-mosquitto:2.0
|
||||||
container_name: mqtt-broker
|
container_name: mqtt-broker
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
network_mode: host
|
||||||
environment:
|
environment:
|
||||||
MQTT_TLS_ENABLED: ${MQTT_TLS_ENABLED:-true}
|
MQTT_TLS_ENABLED: ${MQTT_TLS_ENABLED:-true}
|
||||||
ports:
|
|
||||||
# Listens on all interfaces (IPv4 and IPv6) for Thread compatibility
|
|
||||||
- "8884:8884"
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./mqtt/mosquitto-tls.conf:/mosquitto/config/mosquitto-tls.conf:ro
|
- ./mqtt/mosquitto-tls.conf:/mosquitto/config/mosquitto-tls.conf:ro
|
||||||
- ./mqtt/mosquitto-plain.conf:/mosquitto/config/mosquitto-plain.conf:ro
|
- ./mqtt/mosquitto-plain.conf:/mosquitto/config/mosquitto-plain.conf:ro
|
||||||
@@ -31,5 +37,6 @@ services:
|
|||||||
command: ["/bin/sh", "/scripts/start-broker.sh"]
|
command: ["/bin/sh", "/scripts/start-broker.sh"]
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
otbr-data:
|
||||||
mqtt-data:
|
mqtt-data:
|
||||||
mqtt-log:
|
mqtt-log:
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import asyncio
|
||||||
|
import time
|
||||||
|
import requests
|
||||||
|
import ipaddress
|
||||||
|
from typing import Dict
|
||||||
|
from zeroconf import ServiceStateChange
|
||||||
|
from zeroconf.asyncio import AsyncZeroconf, AsyncServiceBrowser, AsyncServiceInfo
|
||||||
|
|
||||||
|
SERVICE_TYPE = "_displaytcp._tcp.local."
|
||||||
|
|
||||||
|
class DisplayManager:
|
||||||
|
def __init__(self, endpoint_path: str = "/api/display"):
|
||||||
|
self.endpoint_path = endpoint_path
|
||||||
|
self.screens: Dict[str, str] = {} # Map: service_name -> URL
|
||||||
|
self.aiozc: AsyncZeroconf | None = None
|
||||||
|
self.browser: AsyncServiceBrowser | None = None
|
||||||
|
|
||||||
|
async def start(self):
|
||||||
|
"""Starts dynamic mDNS discovery for e-Paper screens."""
|
||||||
|
self.aiozc = AsyncZeroconf()
|
||||||
|
self.browser = AsyncServiceBrowser(
|
||||||
|
self.aiozc.zeroconf,
|
||||||
|
SERVICE_TYPE,
|
||||||
|
handlers=[self._on_service_state_change]
|
||||||
|
)
|
||||||
|
print(f"[DisplayManager] 🔍 Listening for mDNS screens ({SERVICE_TYPE})...")
|
||||||
|
|
||||||
|
def _on_service_state_change(self, zeroconf, service_type, name, state_change):
|
||||||
|
asyncio.create_task(self._update_service(name, state_change))
|
||||||
|
|
||||||
|
async def _update_service(self, name: str, state_change: ServiceStateChange):
|
||||||
|
if state_change in (ServiceStateChange.Added, ServiceStateChange.Updated):
|
||||||
|
info = AsyncServiceInfo(SERVICE_TYPE, name)
|
||||||
|
if await info.async_request(self.aiozc.zeroconf, 3000):
|
||||||
|
addresses = info.parsed_addresses()
|
||||||
|
if addresses:
|
||||||
|
# Prefer IPv4 if available, fallback to IPv6 (Thread)
|
||||||
|
ipv4_addrs = [a for a in addresses if ":" not in a]
|
||||||
|
|
||||||
|
if ipv4_addrs:
|
||||||
|
ip_str = ipv4_addrs[0]
|
||||||
|
else:
|
||||||
|
raw_ip = addresses[0]
|
||||||
|
# Wrap IPv6 addresses in square brackets for valid HTTP URLs
|
||||||
|
ip_str = f"[{raw_ip}]" if not raw_ip.startswith("[") else raw_ip
|
||||||
|
|
||||||
|
port = info.port or 5000
|
||||||
|
url = f"http://{ip_str}:{port}{self.endpoint_path}"
|
||||||
|
|
||||||
|
self.screens[name] = url
|
||||||
|
print(f"[DisplayManager] 📺 Screen registered: {name} -> {url}")
|
||||||
|
|
||||||
|
elif state_change == ServiceStateChange.Removed:
|
||||||
|
if name in self.screens:
|
||||||
|
print(f"[DisplayManager] ❌ Screen disconnected: {name}")
|
||||||
|
self.screens.pop(name, None)
|
||||||
|
|
||||||
|
async def broadcast_state(self, device_id: str, microwave_states: dict, button_state: bool, cloud_alert: bool):
|
||||||
|
"""Broadcasts the system state JSON to all discovered screens concurrently."""
|
||||||
|
if not self.screens:
|
||||||
|
return
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"device_id": device_id,
|
||||||
|
"timestamp": int(time.time()),
|
||||||
|
"cloud_alert": cloud_alert,
|
||||||
|
"defrost_mode": button_state,
|
||||||
|
"microwaves": [
|
||||||
|
{"id": mw_id, "state": state}
|
||||||
|
for mw_id, state in microwave_states.items()
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Dispatch POST requests in parallel without blocking the main loop
|
||||||
|
tasks = [
|
||||||
|
self._post_to_screen(name, url, payload)
|
||||||
|
for name, url in list(self.screens.items())
|
||||||
|
]
|
||||||
|
await asyncio.gather(*tasks, return_exceptions=True)
|
||||||
|
|
||||||
|
async def _post_to_screen(self, name: str, url: str, payload: dict):
|
||||||
|
try:
|
||||||
|
response = await asyncio.to_thread(
|
||||||
|
requests.post,
|
||||||
|
url,
|
||||||
|
json=payload,
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
timeout=3.0
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
print(f"[DisplayManager] ✅ Updated {name}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[DisplayManager] ⚠️ Error pushing state to {name}: {e}")
|
||||||
|
|
||||||
|
async def stop(self):
|
||||||
|
"""Clean up mDNS browser and Zeroconf instance."""
|
||||||
|
if self.browser:
|
||||||
|
await self.browser.async_cancel()
|
||||||
|
if self.aiozc:
|
||||||
|
await self.aiozc.async_close()
|
||||||
|
print("[DisplayManager] Stopped service browser.")
|
||||||
+25
-1
@@ -12,6 +12,7 @@ from shared.lora_device import LoraCommands
|
|||||||
from shared.alerts import AlertManager, Alert, AlertType
|
from shared.alerts import AlertManager, Alert, AlertType
|
||||||
from sensors import ultrasonicRanger, temp_hum, button, camera, buzzer
|
from sensors import ultrasonicRanger, temp_hum, button, camera, buzzer
|
||||||
from lib.systemd_logs import get_systemd_logs
|
from lib.systemd_logs import get_systemd_logs
|
||||||
|
from external_display_manager import DisplayManager # <-- Import display manager
|
||||||
|
|
||||||
# --- DB SETUP ---
|
# --- DB SETUP ---
|
||||||
DB_PATH = "orchestrateur/db.sqlite"
|
DB_PATH = "orchestrateur/db.sqlite"
|
||||||
@@ -73,6 +74,9 @@ button_state = False
|
|||||||
cloud_alert = False # Global status flag for screen / UI display
|
cloud_alert = False # Global status flag for screen / UI display
|
||||||
async_event_queue = None
|
async_event_queue = None
|
||||||
|
|
||||||
|
# Display Manager instance
|
||||||
|
display_manager = DisplayManager(endpoint_path="/api/display")
|
||||||
|
|
||||||
# Async synchronization trackers for MQTT IR sensors responses
|
# Async synchronization trackers for MQTT IR sensors responses
|
||||||
ir_data_cache = {} # mw_id -> dict of IR readings
|
ir_data_cache = {} # mw_id -> dict of IR readings
|
||||||
ir_data_events = {} # mw_id -> asyncio.Event()
|
ir_data_events = {} # mw_id -> asyncio.Event()
|
||||||
@@ -174,6 +178,21 @@ async def cloud_mqtt_listener_task():
|
|||||||
})
|
})
|
||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1)
|
||||||
|
|
||||||
|
async def display_broadcast_task():
|
||||||
|
"""Monitors state changes and broadcasts updates to all connected screens."""
|
||||||
|
last_state = None
|
||||||
|
while True:
|
||||||
|
current_state = (dict(microwave_states), button_state, cloud_alert)
|
||||||
|
if current_state != last_state:
|
||||||
|
last_state = current_state
|
||||||
|
await display_manager.broadcast_state(
|
||||||
|
device_id=DEVICE_ID,
|
||||||
|
microwave_states=microwave_states,
|
||||||
|
button_state=button_state,
|
||||||
|
cloud_alert=cloud_alert
|
||||||
|
)
|
||||||
|
await asyncio.sleep(3.0)
|
||||||
|
|
||||||
def button_callback():
|
def button_callback():
|
||||||
"""Button physical interrupt callback."""
|
"""Button physical interrupt callback."""
|
||||||
global button_state
|
global button_state
|
||||||
@@ -587,6 +606,9 @@ async def main():
|
|||||||
# Initialize SQLite database table
|
# Initialize SQLite database table
|
||||||
init_db()
|
init_db()
|
||||||
|
|
||||||
|
# Start mDNS Display discovery
|
||||||
|
await display_manager.start()
|
||||||
|
|
||||||
async_event_queue = asyncio.Queue()
|
async_event_queue = asyncio.Queue()
|
||||||
|
|
||||||
await asyncio.gather(
|
await asyncio.gather(
|
||||||
@@ -594,7 +616,8 @@ async def main():
|
|||||||
mqtt_listener_task(),
|
mqtt_listener_task(),
|
||||||
cloud_mqtt_listener_task(),
|
cloud_mqtt_listener_task(),
|
||||||
process_messages_task(),
|
process_messages_task(),
|
||||||
monitor_dish_height_task()
|
monitor_dish_height_task(),
|
||||||
|
display_broadcast_task()
|
||||||
)
|
)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -603,6 +626,7 @@ if __name__ == "__main__":
|
|||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\nArrêt manuel.")
|
print("\nArrêt manuel.")
|
||||||
finally:
|
finally:
|
||||||
|
asyncio.run(display_manager.stop())
|
||||||
if hasattr(mqtt_client._client, "loop_stop"):
|
if hasattr(mqtt_client._client, "loop_stop"):
|
||||||
mqtt_client._client.loop_stop()
|
mqtt_client._client.loop_stop()
|
||||||
if hasattr(cloud_mqtt_client._client, "loop_stop"):
|
if hasattr(cloud_mqtt_client._client, "loop_stop"):
|
||||||
|
|||||||
@@ -8,5 +8,11 @@ log_type notice
|
|||||||
log_type information
|
log_type information
|
||||||
allow_anonymous true
|
allow_anonymous true
|
||||||
|
|
||||||
listener 8884 ::
|
listener 8884
|
||||||
protocol mqtt
|
protocol mqtt
|
||||||
|
|
||||||
|
# Unencrypted listener for local Thread nodes
|
||||||
|
listener 1883
|
||||||
|
protocol mqtt
|
||||||
|
socket_domain ipv6
|
||||||
|
allow_anonymous true
|
||||||
@@ -8,10 +8,17 @@ log_type notice
|
|||||||
log_type information
|
log_type information
|
||||||
allow_anonymous true
|
allow_anonymous true
|
||||||
|
|
||||||
listener 8884 ::
|
listener 8884
|
||||||
protocol mqtt
|
protocol mqtt
|
||||||
cafile /mosquitto/certs/ca.crt
|
cafile /mosquitto/certs/ca.crt
|
||||||
certfile /mosquitto/certs/server.crt
|
certfile /mosquitto/certs/server.crt
|
||||||
keyfile /mosquitto/certs/server.key
|
keyfile /mosquitto/certs/server.key
|
||||||
require_certificate false
|
require_certificate false
|
||||||
tls_version tlsv1.2
|
tls_version tlsv1.2
|
||||||
|
|
||||||
|
# Unencrypted listener for local Thread nodes
|
||||||
|
# Thread is already encrypted, so we don't need to use TLS for this listener
|
||||||
|
listener 1883 ::
|
||||||
|
protocol mqtt
|
||||||
|
socket_domain ipv6
|
||||||
|
allow_anonymous true
|
||||||
@@ -4,3 +4,4 @@ pyserial>=3.5,<4
|
|||||||
# OpenCV
|
# OpenCV
|
||||||
# sudo apt install -y python3-opencv
|
# sudo apt install -y python3-opencv
|
||||||
# sudo apt install -y opencv-data
|
# sudo apt install -y opencv-data
|
||||||
|
zeroconf>=0.131.0
|
||||||
Reference in New Issue
Block a user