TasmotaManager/exclude_patterns_analysis.md
Mike Geppert 126cd39555 Major code improvements and bug fixes:
1. Restructured configuration: Moved config_other and console to top level
2. Added common _match_pattern function for regex pattern matching
3. Implemented Unifi Hostname bug fix in is_hostname_unknown
4. Created common get_device_hostname function to eliminate code duplication
5. Added comprehensive test scripts for all new functionality
6. Added detailed documentation for all changes
2025-08-08 19:04:33 -05:00

126 lines
5.9 KiB
Markdown

# Analysis of exclude_patterns Checks in TasmotaManager.py
## Summary
The script performs checks against `exclude_patterns` in 3 distinct places:
1. In the `is_tasmota_device` function (lines 165-185)
2. In the `save_tasmota_config` function via a local `is_device_excluded` function (lines 423-431)
3. In the `process_single_device` function (lines 1589-1610)
## Detailed Analysis
### 1. In `is_tasmota_device` function (lines 165-185)
**Purpose**: During device discovery, this function checks if devices should be excluded based on their name or hostname.
**Context**: This is part of the initial device discovery process when scanning the network. The function returns `False` (excluding the device) if the device's name or hostname matches any exclude pattern.
**Code snippet**:
```python
# Check exclusion patterns
exclude_patterns = network.get('exclude_patterns', [])
for pattern in exclude_patterns:
pattern = pattern.lower()
# Convert glob pattern to regex pattern
pattern = pattern.replace('.', r'\.').replace('*', '.*')
# Check if pattern already starts with ^
if pattern.startswith('^'):
regex_pattern = f"{pattern}$"
# Special case for patterns like ^.*something.* which should match anywhere in the string
if pattern.startswith('^.*'):
if re.search(regex_pattern, name) or re.search(regex_pattern, hostname):
self.logger.debug(f"Excluding device due to pattern '{pattern}': {name} ({hostname})")
return False
continue
else:
regex_pattern = f"^{pattern}$"
# For normal patterns, use re.match which anchors at the beginning of the string
if re.match(regex_pattern, name) or re.match(regex_pattern, hostname):
self.logger.debug(f"Excluding device due to pattern '{pattern}': {name} ({hostname})")
return False
```
### 2. In `save_tasmota_config` function (lines 423-431)
**Purpose**: When saving device information to a JSON file, this function checks if devices should be excluded from the current or deprecated lists.
**Context**: This function is used during the device tracking process to determine which devices should be included in the output files. It defines a local helper function `is_device_excluded` that checks if a device name or hostname matches any exclude pattern.
**Code snippet**:
```python
# Function to check if device is excluded
def is_device_excluded(device_name: str, hostname: str = '') -> bool:
name = device_name.lower()
hostname = hostname.lower()
for pattern in exclude_patterns:
pattern = pattern.lower()
pattern = pattern.replace('.', r'\.').replace('*', '.*')
if re.match(f"^{pattern}$", name) or re.match(f"^{pattern}$", hostname):
return True
return False
```
### 3. In `process_single_device` function (lines 1589-1610)
**Purpose**: When processing a single device by IP or hostname, this function checks if the device should be excluded based on its name or hostname.
**Context**: This function is used in Device mode (triggered by the `--Device` command-line argument) to determine if a specific device should be processed. It returns `False` (skipping the device) if the device's name or hostname matches any exclude pattern.
**Code snippet**:
```python
# Check if device is excluded
exclude_patterns = target_network.get('exclude_patterns', [])
for pattern in exclude_patterns:
pattern_lower = pattern.lower()
pattern_regex = pattern_lower.replace('.', r'\.').replace('*', '.*')
# Check if pattern already starts with ^
if pattern_regex.startswith('^'):
regex_pattern = f"{pattern_regex}$"
# Special case for patterns like ^.*something.* which should match anywhere in the string
if pattern_regex.startswith('^.*'):
if (re.search(regex_pattern, device_name.lower()) or
re.search(regex_pattern, device_hostname.lower())):
self.logger.error(f"Device {device_name} is excluded by pattern: {pattern}")
return False
continue
else:
regex_pattern = f"^{pattern_regex}$"
# For normal patterns, use re.match which anchors at the beginning of the string
if (re.match(regex_pattern, device_name.lower()) or
re.match(regex_pattern, device_hostname.lower())):
self.logger.error(f"Device {device_name} is excluded by pattern: {pattern}")
return False
```
## Pattern Matching Logic Comparison
The pattern matching logic is similar across these locations, but there are some differences:
1. **Common elements**:
- All implementations convert patterns to lowercase for case-insensitive matching
- All implementations convert glob patterns (with *) to regex patterns
- All implementations check if the device name or hostname matches any exclude pattern
2. **Differences**:
- `is_tasmota_device` and `process_single_device` have special handling for patterns that start with `^` and patterns like `^.*something.*`
- `save_tasmota_config` has a simpler implementation without these special cases
- `is_tasmota_device` uses `self.logger.debug` for logging, while `process_single_device` uses `self.logger.error`
- `save_tasmota_config` doesn't include any logging
## Recommendation
Based on this analysis, a common function for exclude_patterns checks would be beneficial to ensure consistent pattern matching behavior across the codebase. This function should:
1. Take a device name and hostname as input
2. Check if either matches any exclude pattern
3. Support case-insensitive matching
4. Handle glob patterns (with *)
5. Handle patterns that already start with `^`
6. Have special handling for patterns like `^.*something.*`
7. Include appropriate logging
8. Return a boolean indicating if the device should be excluded
This would be similar to the `is_hostname_unknown` function that was implemented for unknown_device_patterns, but with the opposite return value logic (return `True` if the device should be excluded, `False` otherwise).