Fix FullTopic parameter to prevent extra equals sign at beginning of value
This commit is contained in:
parent
ecef7bc50f
commit
cced5a76cc
@ -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)
|
||||||
|
|||||||
65
fulltopic_equals_fix_summary.md
Normal file
65
fulltopic_equals_fix_summary.md
Normal 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
180
test_fulltopic_approaches.py
Executable 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
154
test_fulltopic_equals_issue.py
Executable 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()
|
||||||
Loading…
Reference in New Issue
Block a user