feat(controller): migrate wifi drivers from tcp to websocket clients
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
"""Deliver driver JSON messages over serial (ESP-NOW) and/or TCP (Wi-Fi clients)."""
|
||||
"""Deliver driver JSON messages over serial (ESP-NOW) and/or WebSocket (Wi-Fi drivers)."""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
from models.device import normalize_mac
|
||||
from models.tcp_clients import send_json_line_to_ip
|
||||
from models.wifi_ws_clients import send_json_line_to_ip
|
||||
|
||||
# Serial bridge (ESP32): broadcast MAC + this envelope → firmware unicasts ``body`` to each peer.
|
||||
_SPLIT_MODE = "split"
|
||||
@@ -20,7 +20,7 @@ def _split_serial_envelope(inner_json_str, peer_hex_list):
|
||||
|
||||
def _wifi_message_for_device(msg, device_name):
|
||||
"""
|
||||
For Wi-Fi TCP fanout, narrow a v1 select map to a single device name.
|
||||
For Wi-Fi WebSocket fanout, narrow a v1 select map to a single device name.
|
||||
Returns the original message when no narrowing applies.
|
||||
"""
|
||||
if not device_name:
|
||||
@@ -40,6 +40,33 @@ def _wifi_message_for_device(msg, device_name):
|
||||
return json.dumps(body, separators=(",", ":"))
|
||||
|
||||
|
||||
def _combine_preset_chunks_for_wifi(chunk_messages):
|
||||
"""Merge chunked v1 preset messages into one v1 JSON string for Wi-Fi."""
|
||||
merged_presets = {}
|
||||
save_flag = False
|
||||
default_id = None
|
||||
for msg in chunk_messages:
|
||||
try:
|
||||
body = json.loads(msg)
|
||||
except Exception:
|
||||
continue
|
||||
if not isinstance(body, dict):
|
||||
continue
|
||||
presets = body.get("presets")
|
||||
if isinstance(presets, dict):
|
||||
merged_presets.update(presets)
|
||||
if body.get("save"):
|
||||
save_flag = True
|
||||
if body.get("default") is not None:
|
||||
default_id = body.get("default")
|
||||
out = {"v": "1", "presets": merged_presets}
|
||||
if save_flag:
|
||||
out["save"] = True
|
||||
if default_id is not None:
|
||||
out["default"] = default_id
|
||||
return json.dumps(out, separators=(",", ":"))
|
||||
|
||||
|
||||
async def deliver_preset_broadcast_then_per_device(
|
||||
sender,
|
||||
chunk_messages,
|
||||
@@ -50,8 +77,8 @@ async def deliver_preset_broadcast_then_per_device(
|
||||
):
|
||||
"""
|
||||
Send preset definition chunks: ESP-NOW broadcast once per chunk; same chunk to each
|
||||
Wi-Fi driver over TCP. If default_id is set, send a per-target default message
|
||||
(unicast serial or TCP) with targets=[device name] for each registry entry.
|
||||
Wi-Fi driver over WebSocket. If default_id is set, send a per-target default message
|
||||
(unicast serial or WebSocket) with targets=[device name] for each registry entry.
|
||||
"""
|
||||
if not chunk_messages:
|
||||
return 0
|
||||
@@ -72,17 +99,22 @@ async def deliver_preset_broadcast_then_per_device(
|
||||
wifi_ips.append(str(doc["address"]).strip())
|
||||
|
||||
deliveries = 0
|
||||
wifi_combined_msg = _combine_preset_chunks_for_wifi(chunk_messages)
|
||||
for msg in chunk_messages:
|
||||
tasks = [sender.send(msg, addr=_BROADCAST_MAC_HEX)]
|
||||
for ip in wifi_ips:
|
||||
if ip:
|
||||
tasks.append(send_json_line_to_ip(ip, msg))
|
||||
results = await asyncio.gather(*tasks, return_exceptions=True)
|
||||
if results and results[0] is True:
|
||||
deliveries += 1
|
||||
for r in results[1:]:
|
||||
if r is True:
|
||||
await asyncio.sleep(delay_s)
|
||||
|
||||
for ip in wifi_ips:
|
||||
if not ip:
|
||||
continue
|
||||
try:
|
||||
if await send_json_line_to_ip(ip, wifi_combined_msg):
|
||||
deliveries += 1
|
||||
except Exception as e:
|
||||
print(f"[driver_delivery] Wi-Fi preset send failed: {e!r}")
|
||||
await asyncio.sleep(delay_s)
|
||||
|
||||
if default_id:
|
||||
@@ -98,7 +130,7 @@ async def deliver_preset_broadcast_then_per_device(
|
||||
if await send_json_line_to_ip(ip, out):
|
||||
deliveries += 1
|
||||
except Exception as e:
|
||||
print(f"[driver_delivery] default TCP failed: {e!r}")
|
||||
print(f"[driver_delivery] default Wi-Fi send failed: {e!r}")
|
||||
else:
|
||||
try:
|
||||
await sender.send(out, addr=mac)
|
||||
@@ -112,10 +144,10 @@ async def deliver_preset_broadcast_then_per_device(
|
||||
|
||||
async def deliver_json_messages(sender, messages, target_macs, devices_model, delay_s=0.1):
|
||||
"""
|
||||
Send each message string to the bridge and/or TCP clients.
|
||||
Send each message string to the bridge and/or Wi-Fi WebSocket clients.
|
||||
|
||||
If target_macs is None or empty: one serial send per message (default/broadcast address).
|
||||
Otherwise: Wi-Fi uses TCP in parallel. Multiple ESP-NOW peers are sent in **one** serial
|
||||
Otherwise: Wi-Fi uses WebSocket in parallel. Multiple ESP-NOW peers are sent in **one** serial
|
||||
write to the ESP32 (broadcast + split envelope); the bridge unicasts ``body`` to each
|
||||
peer. A single ESP-NOW peer still uses one unicast serial frame. Wi-Fi and serial
|
||||
tasks run together in one asyncio.gather.
|
||||
|
||||
Reference in New Issue
Block a user