diff --git a/configuration.py b/configuration.py index 172798e..dfddde9 100644 --- a/configuration.py +++ b/configuration.py @@ -225,18 +225,24 @@ class ConfigurationManager: current_full_topic = result.get('FullTopic', '') if success and result else '' self.logger.debug(f"{device_name}: Raw FullTopic from device: '{current_full_topic}'") - + + # Check if device has URL-encoded spaces at the beginning (broken state) + has_leading_encoded_space = current_full_topic.startswith('%20') + # Normalize: remove any URL-encoded spaces from the beginning of current value # This handles the case where the device returns '%20%prefix%' instead of '%prefix%' while current_full_topic.startswith('%20'): current_full_topic = current_full_topic[3:] - + # Also normalize expected value in case config has leading spaces mqtt_full_topic_normalized = mqtt_full_topic.lstrip() - + self.logger.debug(f"{device_name}: Comparing FullTopic: current='{current_full_topic}' vs expected='{mqtt_full_topic_normalized}'") - - if current_full_topic != mqtt_full_topic_normalized: + + # Update if values don't match OR if device had leading encoded space (needs fixing) + if current_full_topic != mqtt_full_topic_normalized or has_leading_encoded_space: + if has_leading_encoded_space: + self.logger.info(f"{device_name}: FullTopic has invalid leading space, will fix") updates_needed.append(('FullTopic', mqtt_full_topic_normalized)) # Handle NoRetain (SetOption62) @@ -255,8 +261,14 @@ class ConfigurationManager: failed_updates = [] for setting_name, setting_value in updates_needed: - command = f"{setting_name}%20{setting_value}" - + # URL encode the value, especially important for FullTopic which contains % and / + from urllib.parse import quote + # Convert value to string and URL encode it + encoded_value = quote(str(setting_value), safe='') + command = f"{setting_name}%20{encoded_value}" + + self.logger.debug(f"{device_name}: Sending command: {command}") + result, success = retry_command( lambda: send_tasmota_command(device_ip, command, timeout=5, logger=self.logger), max_attempts=3,