From 8403df531da837aa046e2a0f83d9bdff41ee9476 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Thu, 28 May 2026 00:38:09 +1200 Subject: [PATCH] feat(espnow): improve bridge transport and driver sync Co-authored-by: Cursor --- bulk.sh | 8 ++++++++ src/controller_messages.py | 29 ++++++++++++++++++++++------- src/device_groups.py | 16 ++++++++++++++-- src/espnow_transport.py | 2 +- src/main.py | 2 ++ src/settings.py | 3 ++- 6 files changed, 49 insertions(+), 11 deletions(-) create mode 100755 bulk.sh diff --git a/bulk.sh b/bulk.sh new file mode 100755 index 0000000..cc7c0d8 --- /dev/null +++ b/bulk.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +PORT="${1:-/dev/ttyACM0}" + +while true; do + ls "$PORT" && led-cli -p "$PORT" --erase --src --patterns && led-cli -p "$PORT" --reset -f + sleep 0.5 +done \ No newline at end of file diff --git a/src/controller_messages.py b/src/controller_messages.py index e04b44c..3790408 100644 --- a/src/controller_messages.py +++ b/src/controller_messages.py @@ -68,7 +68,7 @@ def process_data(payload, settings, presets, controller_ip=None, save=False): set_groups = bool(data.get("set_groups")) groups = data.get("groups") if set_groups and isinstance(groups, list): - dg.groups_replace(groups) + dg.groups_replace(groups, settings) print("groups set", dg.list_groups()) elif isinstance(groups, list) and groups: if not any(dg.in_group(str(g)) for g in groups): @@ -96,6 +96,7 @@ def process_data(payload, settings, presets, controller_ip=None, save=False): settings.save() if "save" in data and "device_config" in data: settings.save() + _flush_pending_select(settings, presets) _VALID_DEVICE_COLOR_ORDERS = frozenset({"rgb", "rbg", "grb", "gbr", "brg", "bgr"}) @@ -196,6 +197,18 @@ def _run_select(presets, settings, preset_name, step=None): return False +def _flush_pending_select(settings, presets): + global _pending_select + if _pending_select is None: + return + preset_name, step = _pending_select + if preset_name not in presets.presets and preset_name not in ("on", "off"): + return + _pending_select = None + if not _run_select(presets, settings, preset_name, step): + print("select failed (pending):", preset_name) + + def apply_presets(data, settings, presets): global _pending_select presets_map = data["presets"] @@ -219,13 +232,10 @@ def apply_presets(data, settings, presets): pass presets.edit(id, preset_data) # Same message often carries select; apply now while presets are loaded. - if "select" in data: + if "select" in data or "s" in data: apply_select(data, settings, presets) - elif _pending_select is not None: - preset_name, step = _pending_select - _pending_select = None - if preset_name in presets.presets or preset_name in ("on", "off"): - _run_select(presets, settings, preset_name, step) + else: + _flush_pending_select(settings, presets) def _select_list_for_this_device(select_val, settings): @@ -276,6 +286,11 @@ def apply_select(data, settings, presets): if not preset_name: return step = select_list[1] if len(select_list) > 1 else None + if preset_name not in presets.presets and preset_name not in ("on", "off"): + try: + presets.load(settings) + except Exception: + pass if preset_name not in presets.presets and preset_name not in ("on", "off"): _pending_select = (preset_name, step) print("select deferred (preset not loaded yet):", preset_name) diff --git a/src/device_groups.py b/src/device_groups.py index 3f5a975..0bc4b78 100644 --- a/src/device_groups.py +++ b/src/device_groups.py @@ -1,11 +1,23 @@ -"""In-memory group membership for GROUP_CMD filtering.""" +"""Group membership for GROUP_CMD filtering; persisted in settings.json.""" _groups = [] -def groups_replace(group_ids): +def load_from_settings(settings): + global _groups + g = settings.get("groups") if settings is not None else None + if isinstance(g, list): + _groups = [str(x) for x in g if str(x).strip()] + else: + _groups = [] + + +def groups_replace(group_ids, settings=None, *, persist=True): global _groups _groups = [str(g) for g in group_ids] + if persist and settings is not None: + settings["groups"] = list(_groups) + settings.save() def in_group(group_id): diff --git a/src/espnow_transport.py b/src/espnow_transport.py index e4730ec..78d7944 100644 --- a/src/espnow_transport.py +++ b/src/espnow_transport.py @@ -112,7 +112,7 @@ def _handle_packet(host, pkt, settings, presets): if mt == MSG_GROUPS: ids = parse_groups(pkt) if ids is not None: - dg.groups_replace(ids) + dg.groups_replace(ids, settings) _groups_received = True print("groups", ids) return diff --git a/src/main.py b/src/main.py index 7c3373b..4a895a8 100644 --- a/src/main.py +++ b/src/main.py @@ -6,6 +6,7 @@ import gc import json import network import espnow +import device_groups as dg from presets import Presets from controller_messages import apply_startup_pattern, process_data from espnow_transport import _handle_packet, init_espnow @@ -17,6 +18,7 @@ wdt.feed() machine.freq(160000000) settings = Settings() +dg.load_from_settings(settings) print(settings) gc.collect() diff --git a/src/settings.py b/src/settings.py index d760ed2..91b47d4 100644 --- a/src/settings.py +++ b/src/settings.py @@ -35,7 +35,8 @@ class Settings(dict): self["brightness"] = 32 self["wifi_channel"] = WIFI_CHANNEL_DEFAULT - + self["groups"] = [] + def save(self): try: