From 6c02220719d1d0939a0070753d7f7c09d3fc28fe Mon Sep 17 00:00:00 2001 From: MarekWo Date: Mon, 6 Apr 2026 11:08:38 +0200 Subject: [PATCH] fix: skip empty channel slots during sync, clean up stale DB channels Empty device channel slots have all-zero secrets (32 hex chars) which passed the length check and got persisted to DB as "Channel N". This caused ghost channels (e.g. Channel 14) to appear in unread counts while the sidebar correctly showed only real channels. Co-Authored-By: Claude Opus 4.6 --- app/device_manager.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/device_manager.py b/app/device_manager.py index 721fdbb..641984f 100644 --- a/app/device_manager.py +++ b/app/device_manager.py @@ -341,7 +341,9 @@ class DeviceManager: async def _load_channel_secrets(self): """Load channel secrets from device for pkt_payload computation and persist to DB.""" + EMPTY_SECRET = '0' * 32 # all-zero secret means empty channel slot consecutive_empty = 0 + valid_channel_indices = set() try: for idx in range(self._max_channels): try: @@ -359,14 +361,16 @@ class DeviceManager: name = data.get('channel_name', data.get('name', '')) if isinstance(name, str): name = name.strip('\x00').strip() - if secret and len(secret) == 32: + if secret and len(secret) == 32 and secret != EMPTY_SECRET: self._channel_secrets[idx] = secret # Persist to DB so API endpoints can read without device calls self.db.upsert_channel(idx, name or f'Channel {idx}', secret) + valid_channel_indices.add(idx) consecutive_empty = 0 elif name: # Channel exists but has no secret (e.g. Public) self.db.upsert_channel(idx, name, None) + valid_channel_indices.add(idx) consecutive_empty = 0 else: consecutive_empty += 1 @@ -374,6 +378,15 @@ class DeviceManager: consecutive_empty += 1 if consecutive_empty >= 3: break # stop after 3 consecutive empty channels + + # Remove stale channels from DB that no longer exist on the device + if valid_channel_indices: + db_channels = self.db.get_channels() + for ch in db_channels: + if ch['idx'] not in valid_channel_indices: + self.db.delete_channel(ch['idx']) + logger.debug(f"Removed stale channel {ch['idx']} ({ch['name']}) from DB") + logger.info(f"Cached {len(self._channel_secrets)} channel secrets") except Exception as e: logger.error(f"Failed to load channel secrets: {e}")