Fix FullTopic parameter to prevent extra equals sign at beginning of value

This commit is contained in:
Mike Geppert 2025-08-06 04:30:31 -05:00
parent ecef7bc50f
commit cced5a76cc
4 changed files with 403 additions and 4 deletions

View File

@ -539,9 +539,9 @@ class TasmotaDiscovery:
} }
for setting, value in mqtt_fields.items(): for setting, value in mqtt_fields.items():
# For FullTopic, we need to avoid adding a space (%20) between the command and value # For FullTopic, we need to avoid adding a space (%20) or equals sign between the command and value
if setting == "FullTopic": if setting == "FullTopic":
url = f"http://{ip}/cm?cmnd={setting}={value}" url = f"http://{ip}/cm?cmnd={setting}{value}"
else: else:
url = f"http://{ip}/cm?cmnd={setting}%20{value}" url = f"http://{ip}/cm?cmnd={setting}%20{value}"
response = requests.get(url, timeout=5) response = requests.get(url, timeout=5)
@ -1022,9 +1022,9 @@ class TasmotaDiscovery:
# Apply changes if needed # Apply changes if needed
for setting, value in changes_needed: for setting, value in changes_needed:
try: try:
# For FullTopic, we need to avoid adding a space (%20) between the command and value # For FullTopic, we need to avoid adding a space (%20) or equals sign between the command and value
if setting == "FullTopic": if setting == "FullTopic":
url = f"http://{ip}/cm?cmnd={setting}={value}" url = f"http://{ip}/cm?cmnd={setting}{value}"
else: else:
url = f"http://{ip}/cm?cmnd={setting}%20{value}" url = f"http://{ip}/cm?cmnd={setting}%20{value}"
response = requests.get(url, timeout=5) response = requests.get(url, timeout=5)

View File

@ -0,0 +1,65 @@
# FullTopic Equals Sign Fix Summary
## Issue Description
When setting the MQTT FullTopic parameter, an extra equals sign ('=') was being added to the beginning of the value. For example, instead of setting the FullTopic to `%prefix%/%topic%/`, it was being set to `=%prefix%/%topic%/`.
## Root Cause
The issue was related to how the Tasmota device interprets the command when an equals sign is used as a separator between the command and the value. When using the format:
```
http://{ip}/cm?cmnd=FullTopic={value}
```
The Tasmota device was interpreting this as a command to set the FullTopic to `={value}` rather than just `{value}`.
## Investigation
A test script was created to reproduce the issue and test different approaches for setting the FullTopic parameter. The script tested several methods:
1. Current approach (setting=value): `http://{ip}/cm?cmnd=FullTopic={full_topic}`
2. URL encoded value: `http://{ip}/cm?cmnd=FullTopic={urllib.parse.quote(full_topic)}`
3. Using space (%20) instead of equals: `http://{ip}/cm?cmnd=FullTopic%20{full_topic}`
4. Backslash before equals: `http://{ip}/cm?cmnd=FullTopic\={full_topic}`
5. Double equals: `http://{ip}/cm?cmnd=FullTopic=={full_topic}`
6. No separator (direct value): `http://{ip}/cm?cmnd=FullTopic{full_topic}`
The testing revealed that three approaches worked correctly:
1. Using space (%20) instead of equals
2. Backslash before equals
3. No separator (direct value)
## Solution
The "no separator" approach was chosen as the simplest and most reliable solution. The code was modified in two places:
1. In the `configure_unknown_device` method:
```python
# For FullTopic, we need to avoid adding a space (%20) or equals sign between the command and value
if setting == "FullTopic":
url = f"http://{ip}/cm?cmnd={setting}{value}"
else:
url = f"http://{ip}/cm?cmnd={setting}%20{value}"
```
2. In the `check_mqtt_settings` function:
```python
# For FullTopic, we need to avoid adding a space (%20) or equals sign between the command and value
if setting == "FullTopic":
url = f"http://{ip}/cm?cmnd={setting}{value}"
else:
url = f"http://{ip}/cm?cmnd={setting}%20{value}"
```
## Testing
The fix was tested by running the TasmotaManager.py script with the --Device parameter and verifying that the FullTopic parameter was set correctly without the extra '=' at the beginning of the value.
Before the fix:
```json
{"FullTopic":"=%prefix%/%topic%/"}
```
After the fix:
```json
{"FullTopic":"%prefix%/%topic%/"}
```
## Conclusion
The issue has been resolved by changing how the FullTopic parameter is set. Instead of using an equals sign as a separator between the command and value, the fix uses no separator at all, which prevents the Tasmota device from adding an extra equals sign to the beginning of the value.

180
test_fulltopic_approaches.py Executable file
View File

@ -0,0 +1,180 @@
#!/usr/bin/env python3
"""
Test script to try different approaches for setting the FullTopic parameter
to find a solution that avoids the extra '=' being added to the beginning of the value.
"""
import sys
import logging
import requests
import json
import argparse
import time
import urllib.parse
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("FullTopicApproachesTest")
def load_config():
"""Load the network configuration."""
try:
with open('network_configuration.json', 'r') as f:
return json.load(f)
except Exception as e:
logger.error(f"Error loading configuration: {str(e)}")
sys.exit(1)
def get_current_fulltopic(ip_address):
"""Get the current FullTopic value from the device."""
try:
status_url = f"http://{ip_address}/cm?cmnd=FullTopic"
response = requests.get(status_url, timeout=5)
if response.status_code == 200:
try:
# Try to parse as JSON
data = response.json()
if isinstance(data, dict) and "FullTopic" in data:
current_value = data["FullTopic"]
else:
current_value = response.text
except:
# If not JSON, use the raw text
current_value = response.text
logger.info(f"Current FullTopic value: {current_value}")
return current_value
else:
logger.error(f"Failed to get current FullTopic value: {response.status_code}")
return None
except requests.exceptions.RequestException as e:
logger.error(f"Error connecting to device: {str(e)}")
return None
def test_approach(ip_address, approach_name, url):
"""Test a specific approach for setting the FullTopic parameter."""
logger.info(f"Testing approach: {approach_name}")
logger.info(f"URL: {url}")
try:
response = requests.get(url, timeout=5)
# Log the raw response for debugging
logger.info(f"Raw response: {response.text}")
if response.status_code == 200:
try:
# Try to parse as JSON
data = response.json()
logger.info(f"Response JSON: {data}")
except:
logger.info(f"Response is not JSON: {response.text}")
else:
logger.error(f"Failed to set FullTopic: {response.status_code}")
return False
except requests.exceptions.RequestException as e:
logger.error(f"Error setting FullTopic: {str(e)}")
return False
# Wait a moment for the change to take effect
time.sleep(1)
# Verify the FullTopic was set correctly
new_value = get_current_fulltopic(ip_address)
if new_value is None:
return False
# Check if the value has an extra '=' at the beginning
if new_value.startswith('='):
logger.error(f"ISSUE DETECTED: FullTopic still has an extra '=' at the beginning: {new_value}")
return False
else:
logger.info(f"SUCCESS: FullTopic set correctly without an extra '=': {new_value}")
return True
def main():
"""Main function to test different approaches for setting the FullTopic parameter."""
parser = argparse.ArgumentParser(description='Test different approaches for setting the FullTopic parameter')
parser.add_argument('ip_address', help='IP address of the Tasmota device to test')
args = parser.parse_args()
if not args.ip_address:
print("Usage: python test_fulltopic_approaches.py <ip_address>")
sys.exit(1)
# Load configuration
config = load_config()
mqtt_config = config.get('mqtt', {})
if not mqtt_config:
logger.error("No MQTT configuration found")
sys.exit(1)
# Get the FullTopic value from configuration
full_topic = mqtt_config.get('FullTopic', '%prefix%/%topic%/')
logger.info(f"FullTopic from configuration: {full_topic}")
# Get the current FullTopic value
current_value = get_current_fulltopic(args.ip_address)
if current_value is None:
sys.exit(1)
# Try different approaches
approaches = [
# Current approach in TasmotaManager.py
{
"name": "Current approach (setting=value)",
"url": f"http://{args.ip_address}/cm?cmnd=FullTopic={full_topic}"
},
# Try with URL encoding the value
{
"name": "URL encoded value",
"url": f"http://{args.ip_address}/cm?cmnd=FullTopic={urllib.parse.quote(full_topic)}"
},
# Try with a space (%20) instead of equals
{
"name": "Using space (%20) instead of equals",
"url": f"http://{args.ip_address}/cm?cmnd=FullTopic%20{full_topic}"
},
# Try with backslash before equals
{
"name": "Backslash before equals",
"url": f"http://{args.ip_address}/cm?cmnd=FullTopic\\={full_topic}"
},
# Try with double equals
{
"name": "Double equals",
"url": f"http://{args.ip_address}/cm?cmnd=FullTopic=={full_topic}"
},
# Try with no separator (direct value)
{
"name": "No separator (direct value)",
"url": f"http://{args.ip_address}/cm?cmnd=FullTopic{full_topic}"
}
]
# Test each approach
successful_approaches = []
for approach in approaches:
success = test_approach(args.ip_address, approach["name"], approach["url"])
if success:
successful_approaches.append(approach["name"])
# Print summary
print("\n=== SUMMARY ===")
if successful_approaches:
print(f"Successful approaches: {len(successful_approaches)}/{len(approaches)}")
for i, approach in enumerate(successful_approaches, 1):
print(f"{i}. {approach}")
else:
print("No successful approaches found.")
# Exit with success if at least one approach worked
sys.exit(0 if successful_approaches else 1)
if __name__ == "__main__":
main()

154
test_fulltopic_equals_issue.py Executable file
View File

@ -0,0 +1,154 @@
#!/usr/bin/env python3
"""
Test script to verify the issue with an extra '=' being added to the beginning of the FullTopic value.
This script will:
1. Connect to a Tasmota device
2. Check the current FullTopic value
3. Set the FullTopic parameter using the current code
4. Verify if an extra '=' is being added to the beginning of the value
"""
import sys
import logging
import requests
import json
import argparse
import time
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("FullTopicEqualsTest")
def load_config():
"""Load the network configuration."""
try:
with open('network_configuration.json', 'r') as f:
return json.load(f)
except Exception as e:
logger.error(f"Error loading configuration: {str(e)}")
sys.exit(1)
def test_fulltopic_equals_issue(ip_address):
"""Test if an extra '=' is being added to the beginning of the FullTopic value."""
logger.info(f"Testing FullTopic equals issue on device at {ip_address}")
# Load configuration
config = load_config()
mqtt_config = config.get('mqtt', {})
if not mqtt_config:
logger.error("No MQTT configuration found")
return False
# Get the FullTopic value from configuration
full_topic = mqtt_config.get('FullTopic', '%prefix%/%topic%/')
logger.info(f"FullTopic from configuration: {full_topic}")
# First, check the current FullTopic value
try:
status_url = f"http://{ip_address}/cm?cmnd=FullTopic"
response = requests.get(status_url, timeout=5)
if response.status_code == 200:
try:
# Try to parse as JSON
data = response.json()
if isinstance(data, dict) and "FullTopic" in data:
current_value = data["FullTopic"]
else:
current_value = response.text
except:
# If not JSON, use the raw text
current_value = response.text
logger.info(f"Current FullTopic value: {current_value}")
else:
logger.error(f"Failed to get current FullTopic value: {response.status_code}")
return False
except requests.exceptions.RequestException as e:
logger.error(f"Error connecting to device: {str(e)}")
return False
# Set the FullTopic using the current code method
try:
# This is how it's done in TasmotaManager.py
set_url = f"http://{ip_address}/cm?cmnd=FullTopic={full_topic}"
logger.info(f"Setting FullTopic with URL: {set_url}")
response = requests.get(set_url, timeout=5)
# Log the raw response for debugging
logger.info(f"Raw response: {response.text}")
if response.status_code == 200:
try:
# Try to parse as JSON
data = response.json()
logger.info(f"Response JSON: {data}")
except:
logger.info(f"Response is not JSON: {response.text}")
else:
logger.error(f"Failed to set FullTopic: {response.status_code}")
return False
except requests.exceptions.RequestException as e:
logger.error(f"Error setting FullTopic: {str(e)}")
return False
# Wait a moment for the change to take effect
time.sleep(1)
# Verify the FullTopic was set correctly
try:
verify_url = f"http://{ip_address}/cm?cmnd=FullTopic"
response = requests.get(verify_url, timeout=5)
if response.status_code == 200:
try:
# Try to parse as JSON
data = response.json()
if isinstance(data, dict) and "FullTopic" in data:
new_value = data["FullTopic"]
else:
new_value = response.text
except:
# If not JSON, use the raw text
new_value = response.text
logger.info(f"New FullTopic value: {new_value}")
# Check if the value has an extra '=' at the beginning
if new_value.startswith('='):
logger.error(f"ISSUE DETECTED: FullTopic has an extra '=' at the beginning: {new_value}")
return True # Return True to indicate the issue was found
else:
logger.info("FullTopic does not have an extra '=' at the beginning")
return False # Return False to indicate the issue was not found
else:
logger.error(f"Failed to verify FullTopic: {response.status_code}")
return False
except requests.exceptions.RequestException as e:
logger.error(f"Error verifying FullTopic: {str(e)}")
return False
def main():
"""Main function to test the FullTopic equals issue."""
parser = argparse.ArgumentParser(description='Test FullTopic equals issue')
parser.add_argument('ip_address', help='IP address of the Tasmota device to test')
args = parser.parse_args()
if not args.ip_address:
print("Usage: python test_fulltopic_equals_issue.py <ip_address>")
sys.exit(1)
issue_found = test_fulltopic_equals_issue(args.ip_address)
if issue_found:
print("ISSUE CONFIRMED: An extra '=' is being added to the beginning of the FullTopic value")
sys.exit(0)
else:
print("ISSUE NOT FOUND: No extra '=' is being added to the beginning of the FullTopic value")
sys.exit(0)
if __name__ == "__main__":
main()