From e293de2a766d528238a65dc06b0528dfbbf3efa6 Mon Sep 17 00:00:00 2001 From: MarekWo Date: Fri, 24 Apr 2026 11:54:01 +0200 Subject: [PATCH] fix(regions): rename tab to Regions and soften v1.14 firmware error Two small follow-ups after initial deployment. - Rename the Settings tab 'Channels' -> 'Regions' (id now tabSettingsRegions). The tab manages the region registry, not channels; the old label was confusing. The per-channel picker still lives under Manage Channels as before. - Graceful handling of firmware rejection: CMD_SET_DEFAULT_FLOOD_SCOPE (63) and CMD_GET_DEFAULT_FLOOD_SCOPE (64) were introduced in firmware v1.15.0; on v1.14.x the device replies with a generic ERR frame and our toast showed the unhelpful 'Firmware error: unknown'. Now the device_manager translates the empty/timeout reason into a concrete message naming the v1.15 requirement, and the api handler appends 'Your choice is saved locally' so the user knows the local state still persists. Same treatment for the delete-default-region clear path. --- app/device_manager.py | 11 +++++++++-- app/routes/api.py | 11 ++++++----- app/static/js/app.js | 4 ++-- app/templates/base.html | 4 ++-- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/app/device_manager.py b/app/device_manager.py index 603c23e..16c9979 100644 --- a/app/device_manager.py +++ b/app/device_manager.py @@ -3005,8 +3005,15 @@ class DeviceManager: timeout=5, ) if event and getattr(event, 'type', None) == EventType.ERROR: - reason = (getattr(event, 'payload', {}) or {}).get('reason', 'unknown') - return {'success': False, 'error': f'Firmware error: {reason}'} + reason = (getattr(event, 'payload', {}) or {}).get('reason', '') + # CMD_SET_DEFAULT_FLOOD_SCOPE (63) was introduced in firmware v1.15.0. + # Older firmware replies with a generic ERR frame (no specific reason) + # or times out — both funnel into this branch. + if not reason or reason in ('timeout', 'no_event_received'): + friendly = "Device did not accept the default scope — this requires firmware v1.15 or newer." + else: + friendly = f"Device rejected the default scope (reason: {reason})." + return {'success': False, 'error': friendly} return {'success': True, 'message': f'Default scope set to: {name or "(cleared)"}'} except Exception as e: logger.error(f"set_default_flood_scope failed: {e}") diff --git a/app/routes/api.py b/app/routes/api.py index a674cc2..9c51d0d 100644 --- a/app/routes/api.py +++ b/app/routes/api.py @@ -4003,9 +4003,9 @@ def delete_region_api(region_id): try: res = dm.set_default_flood_scope('', '') if not res.get('success'): - warning = f"Firmware default not cleared: {res.get('error')}" + warning = f"{res.get('error')} Firmware default left as-is." except Exception as e: - warning = f"Firmware default not cleared: {e}" + warning = f"Could not clear firmware default: {e}" out = {'success': True} if warning: @@ -4094,11 +4094,12 @@ def set_default_region_api(region_id): try: res = dm.set_default_flood_scope(region['name'], region['key_hex']) if not res.get('success'): - warning = f"Firmware push failed: {res.get('error')}" + # device_manager already returns a user-friendly message here. + warning = f"{res.get('error')} Your choice is saved locally." except Exception as e: - warning = f"Firmware push failed: {e}" + warning = f"Could not push to firmware: {e}. Your choice is saved locally." else: - warning = 'Device disconnected; firmware default not updated' + warning = 'Device disconnected — choice saved locally; firmware default not updated.' out = {'success': True, 'region': db.get_region(region_id)} if warning: diff --git a/app/static/js/app.js b/app/static/js/app.js index 4e3e6b2..c9d2840 100644 --- a/app/static/js/app.js +++ b/app/static/js/app.js @@ -2439,7 +2439,7 @@ document.addEventListener('DOMContentLoaded', () => { // Initial load so suppress flag is available before user opens Settings loadContactsSettings(); - // Channels tab: region registry + // Regions tab: region registry const addRegionForm = document.getElementById('addRegionForm'); if (addRegionForm) { addRegionForm.addEventListener('submit', (e) => { @@ -2998,7 +2998,7 @@ function renderRegionPickerList() { // Activate the Channels tab after the modal is shown. settingsModal.addEventListener('shown.bs.modal', function onceShown() { settingsModal.removeEventListener('shown.bs.modal', onceShown); - const btn = document.querySelector('[data-bs-target="#tabSettingsChannels"]'); + const btn = document.querySelector('[data-bs-target="#tabSettingsRegions"]'); if (btn) bootstrap.Tab.getOrCreateInstance(btn).show(); }); }); diff --git a/app/templates/base.html b/app/templates/base.html index 8f6197a..6037873 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -420,7 +420,7 @@
@@ -768,7 +768,7 @@
-
+
Only repeaters allowing a region will forward messages tagged with it. Find standardised region names at