diff --git a/TasmotaManager.py b/TasmotaManager.py index d8ec9ad..e5114a0 100755 --- a/TasmotaManager.py +++ b/TasmotaManager.py @@ -1458,8 +1458,15 @@ class TasmotaDiscovery: response = requests.get(url, timeout=5) if response.status_code == 200: self.logger.debug(f"{name}: Set {param} to {final_value} (step 2 of 2 to update MQTT broker retain settings)") - console_updated = True - success = True + # Verify the change took effect + verified = self._verify_console_param_value(ip, name, param, final_value) + if verified: + console_updated = True + success = True + else: + self.logger.warning(f"{name}: Verification failed for {param} after update; retrying (attempt {attempts}/{max_attempts})") + if attempts < max_attempts: + time.sleep(1) else: self.logger.warning(f"{name}: Failed to set {param} to {final_value} (attempt {attempts}/{max_attempts})") last_error = f"HTTP {response.status_code}" @@ -1596,8 +1603,14 @@ class TasmotaDiscovery: self.logger.info(f"{name}: Set rule {param} to '{value}'") else: self.logger.debug(f"{name}: Set console parameter {param} to {value}") - console_updated = True - success = True + # Verify the change took effect before marking success + if self._verify_console_param_value(ip, name, param, value): + console_updated = True + success = True + else: + self.logger.warning(f"{name}: Verification failed for {param} after update; retrying (attempt {attempts}/{max_attempts})") + if attempts < max_attempts: + time.sleep(1) else: self.logger.warning(f"{name}: Failed to set console parameter {param} (attempt {attempts}/{max_attempts})") last_error = f"HTTP {response.status_code}" @@ -1685,8 +1698,13 @@ class TasmotaDiscovery: response = requests.get(url, timeout=5) if response.status_code == 200: self.logger.info(f"{name}: Auto-enabled {rule_enable_param}") - console_updated = True - success = True + if self._verify_console_param_value(ip, name, rule_enable_param, "1"): + console_updated = True + success = True + else: + self.logger.warning(f"{name}: Verification failed for {rule_enable_param} after update; retrying (attempt {attempts}/{max_attempts})") + if attempts < max_attempts: + time.sleep(1) else: self.logger.warning(f"{name}: Failed to auto-enable {rule_enable_param} (attempt {attempts}/{max_attempts})") last_error = f"HTTP {response.status_code}" @@ -1724,6 +1742,50 @@ class TasmotaDiscovery: return console_updated + def _verify_console_param_value(self, ip, name, param, expected): + """Post-update verification; returns True if device value equals expected. + Normalizes values: + - ruleN (lowercase) compare rules text (case/whitespace-insensitive) + - RuleN (uppercase) consider ON/1/enabled as True when expected is truthy ('1','on') + - Generic: normalize 'on'/'off' vs '1'/'0' + """ + try: + cur, ok = self._get_console_param_value(ip, name, param) + if not ok: + return False + # Rule definition verify + if param.lower().startswith('rule') and param.islower() and param[-1].isdigit(): + import re as _re + def _norm(s): + s = str(s or '') + s = s.strip().lower() + s = _re.sub(r"\s+", " ", s) + return s + return _norm(cur) == _norm(expected) + # Rule enable verify (RuleN) + if param.lower().startswith('rule') and not param.islower() and param[-1].isdigit(): + val = str(cur or '').strip().lower() + if val in ('on','enabled','active'): + val = '1' + if val in ('off','disabled','inactive'): + val = '0' + exp = str(expected or '').strip().lower() + if exp in ('on','enabled','active'): + exp = '1' + if exp in ('off','disabled','inactive'): + exp = '0' + return val == exp or val == '1' + # Generic verify + val = str(cur or '').strip().lower() + exp = str(expected or '').strip().lower() + if val in ('on','off') and exp in ('1','0'): + val = '1' if val=='on' else '0' + if val in ('1','0') and exp in ('on','off'): + val = 'on' if val=='1' else 'off' + return val == exp + except Exception: + return False + def _get_console_param_value(self, ip, name, param): """Query the device for the current value of a console parameter. Returns (value, True) on success, (None, False) on failure.