Fix v2: Handle included files and list-item template format

Critical fixes for real-world HA configurations:
- Scans ALL .yaml files in /config/ and /config/packages/
- Handles "- platform: template" (list item with dash)
- Properly extracts sensors from "sensors:" blocks
- Handles multiline templates with > operator
- Correct indentation for modern template: format
- Counts sensors correctly from included files

This version will find all 67+ legacy template entities in typical HA setups.
This commit is contained in:
2025-12-23 12:53:00 +11:00
parent e486f0efc9
commit ec2062872c

View File

@@ -1,7 +1,7 @@
#!/bin/bash
################################################################################
# Home Assistant Legacy Template Migration (Bash Version)
# Safely migrates legacy template syntax to modern template: syntax
# Home Assistant Legacy Template Migration v2 (Bash)
# Handles templates in included files (sensors.yaml, binary_sensors.yaml)
################################################################################
set -e # Exit on error
@@ -25,7 +25,7 @@ TOTAL_BINARY_SENSORS=0
FILES_MODIFIED=0
echo "======================================================================"
echo "Home Assistant Legacy Template Migration (Bash)"
echo "Home Assistant Legacy Template Migration v2 (Bash)"
echo "======================================================================"
echo ""
@@ -53,53 +53,47 @@ extract_legacy_templates() {
echo -e "${BLUE}${NC} Processing $(basename "$file") for ${type}..."
# Use awk to extract legacy template blocks
awk -v type="$type" '
# Extract all sensor definitions from platform: template blocks
awk '
BEGIN {
in_sensor_section = 0
in_template_platform = 0
in_sensors_block = 0
indent_level = 0
in_platform = 0
in_sensors = 0
platform_indent = 0
}
# Detect sensor: or binary_sensor: section
/^[[:space:]]*'"$type"':/ {
in_sensor_section = 1
next
}
# If in sensor section, look for platform: template
in_sensor_section && /^[[:space:]]*-[[:space:]]*platform:[[:space:]]*template/ {
in_template_platform = 1
# Detect "- platform: template" or "platform: template"
/^[[:space:]]*-?[[:space:]]*platform:[[:space:]]*template/ {
in_platform = 1
match($0, /^[[:space:]]*/)
platform_indent = RLENGTH
next
}
# Look for sensors: block within template platform
in_template_platform && /^[[:space:]]*sensors:/ {
in_sensors_block = 1
# Look for "sensors:" within platform block
in_platform && /^[[:space:]]*sensors:/ {
in_sensors = 1
next
}
# Extract sensor definitions
in_sensors_block {
# Check if we hit next platform or end of section
if (/^[[:space:]]*-[[:space:]]*platform:/ || /^[[:alpha:]]/) {
in_sensor_section = 0
in_template_platform = 0
in_sensors_block = 0
in_sensors {
# Check if we hit next platform or top-level item
if (/^[[:space:]]*-[[:space:]]*platform:/ || /^[[:space:]]*-[[:space:]]*$/ || /^[a-zA-Z]/) {
in_platform = 0
in_sensors = 0
next
}
# Print sensor lines
# Print sensor lines (indented under sensors:)
if (/^[[:space:]]+[a-zA-Z_]/) {
print
}
}
' "$file" > "$output_file"
if [ -s "$output_file" ]; then
local count=$(grep -c "^[[:space:]]*[a-zA-Z_].*:" "$output_file" 2>/dev/null || echo 0)
# Count unique sensor names (lines that start a sensor definition)
local count=$(grep -c "^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*:" "$output_file" 2>/dev/null || echo 0)
echo " ${GREEN}${NC} Found $count ${type}(s)"
if [ "$type" = "sensor" ]; then
@@ -108,17 +102,19 @@ extract_legacy_templates() {
TOTAL_BINARY_SENSORS=$((TOTAL_BINARY_SENSORS + count))
fi
# Backup original file
# Backup original file (only once)
if [ ! -f "$BACKUP_DIR/$(basename "$file")" ]; then
cp "$file" "$BACKUP_DIR/$(basename "$file")"
echo " ${GREEN}${NC} Backed up: $(basename "$file")"
FILES_MODIFIED=$((FILES_MODIFIED + 1))
fi
else
rm -f "$output_file"
fi
}
################################################################################
# Remove legacy template definitions from a file
# Remove legacy template platform blocks from a file
################################################################################
remove_legacy_templates() {
local file="$1"
@@ -127,7 +123,9 @@ remove_legacy_templates() {
return
fi
# Use awk to remove legacy template platform blocks
echo -e "${BLUE}${NC} Removing legacy templates from $(basename "$file")..."
# Use awk to remove entire platform: template blocks
awk '
BEGIN {
in_template_platform = 0
@@ -135,7 +133,7 @@ remove_legacy_templates() {
skip_block = 0
}
# Detect platform: template
# Detect "- platform: template" (list item)
/^[[:space:]]*-[[:space:]]*platform:[[:space:]]*template/ {
in_template_platform = 1
match($0, /^[[:space:]]*/)
@@ -146,20 +144,20 @@ remove_legacy_templates() {
# Skip lines in template platform block
skip_block {
# Check if we hit next item at same or lower indent
if (/^[[:space:]]*-[[:space:]]/ || /^[[:alpha:]]/) {
# Check if we hit next list item at same or lower indent
if (/^[[:space:]]*-[[:space:]]/ || /^[a-zA-Z]/) {
match($0, /^[[:space:]]*/)
current_indent = RLENGTH
# If at same or lower indent, end of block
if (current_indent <= platform_indent || /^[[:alpha:]]/) {
# If at same or lower indent level, end of block
if (current_indent <= platform_indent || /^[a-zA-Z]/) {
skip_block = 0
in_template_platform = 0
print
next
}
}
# Skip this line
# Skip this line (its part of the template block)
next
}
@@ -168,6 +166,7 @@ remove_legacy_templates() {
' "$file" > "$file.tmp"
mv "$file.tmp" "$file"
echo " ${GREEN}${NC} Removed legacy template blocks"
}
################################################################################
@@ -177,9 +176,9 @@ convert_to_modern_syntax() {
local type="$1" # "sensor" or "binary_sensor"
local output_file="$TEMP_DIR/modern_${type}.yaml"
echo "- ${type}:" > "$output_file"
echo " - ${type}:" > "$output_file"
# Process all extracted files
# Process all extracted files for this type
for extracted in "$TEMP_DIR/${type}_"*.extracted; do
[ -f "$extracted" ] || continue
@@ -188,11 +187,14 @@ convert_to_modern_syntax() {
BEGIN {
sensor_name = ""
in_sensor = 0
indent = " "
indent = " " # 4 spaces for items under - binary_sensor:/sensor:
field_indent = " " # 6 spaces for fields
in_multiline = 0
multiline_field = ""
}
# Detect sensor name (e.g., "router_authenticated:")
/^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*:/ {
# Detect sensor name (start of new sensor)
/^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*:[[:space:]]*$/ {
if (sensor_name != "") {
# End previous sensor
print ""
@@ -203,38 +205,66 @@ convert_to_modern_syntax() {
sensor_name = substr($0, RSTART, RLENGTH)
print indent "- unique_id: " sensor_name
in_sensor = 1
in_multiline = 0
next
}
in_sensor {
# Handle multiline templates (lines starting with more indent after >)
if (in_multiline && /^[[:space:]]+[^a-zA-Z_]/) {
# Continue multiline value
print
next
} else if (in_multiline) {
in_multiline = 0
}
# Convert field names
if (/friendly_name:/) {
sub(/friendly_name:/, "name:")
print indent indent $0
sub(/^[[:space:]]*friendly_name:/, field_indent "name:")
print
}
else if (/value_template:/) {
sub(/value_template:/, "state:")
print indent indent $0
sub(/^[[:space:]]*value_template:/, field_indent "state:")
print
if (/>[[:space:]]*$/) {
in_multiline = 1
}
}
else if (/icon_template:/) {
sub(/icon_template:/, "icon:")
print indent indent $0
sub(/^[[:space:]]*icon_template:/, field_indent "icon:")
print
if (/>[[:space:]]*$/) {
in_multiline = 1
}
}
else if (/entity_picture_template:/) {
sub(/entity_picture_template:/, "picture:")
print indent indent $0
sub(/^[[:space:]]*entity_picture_template:/, field_indent "picture:")
print
if (/>[[:space:]]*$/) {
in_multiline = 1
}
}
else if (/availability_template:/) {
sub(/availability_template:/, "availability:")
print indent indent $0
sub(/^[[:space:]]*availability_template:/, field_indent "availability:")
print
if (/>[[:space:]]*$/) {
in_multiline = 1
}
}
else if (/attribute_templates:/) {
sub(/attribute_templates:/, "attributes:")
print indent indent $0
sub(/^[[:space:]]*attribute_templates:/, field_indent "attributes:")
print
}
else if (/^[[:space:]]+[a-zA-Z_]/) {
# Keep other fields (unit_of_measurement, device_class, etc)
print indent indent $0
# Adjust indentation to match field_indent
sub(/^[[:space:]]+/, field_indent)
print
}
else if (/^[[:space:]]+/) {
# Indented content (like multiline template continuation)
print
}
}
' "$extracted" >> "$output_file"
@@ -256,8 +286,6 @@ add_to_configuration() {
if grep -q "^template:" "$config_file"; then
echo -e " ${YELLOW}${NC} Existing template: section found, appending..."
# Append to existing template section
# This is complex, so we'll add at the end and let user merge manually
echo "" >> "$config_file"
echo "# Migrated templates from legacy syntax ($BACKUP_TIMESTAMP)" >> "$config_file"
echo "# NOTE: Merge these into your existing template: section" >> "$config_file"
@@ -318,17 +346,17 @@ main() {
echo -e "${BLUE}${NC} Scanning for legacy template definitions..."
echo ""
# Find all YAML files
# Find all YAML files in config directory
yaml_files=()
while IFS= read -r -d '' file; do
yaml_files+=("$file")
done < <(find "$CONFIG_DIR" -maxdepth 1 -name "*.yaml" -type f -print0 2>/dev/null)
for file in "$CONFIG_DIR"/*.yaml; do
[ -f "$file" ] && yaml_files+=("$file")
done
# Also check packages directory
if [ -d "$CONFIG_DIR/packages" ]; then
while IFS= read -r -d '' file; do
yaml_files+=("$file")
done < <(find "$CONFIG_DIR/packages" -name "*.yaml" -type f -print0 2>/dev/null)
for file in "$CONFIG_DIR/packages"/*.yaml; do
[ -f "$file" ] && yaml_files+=("$file")
done
fi
# Extract templates from all files
@@ -361,11 +389,9 @@ main() {
echo ""
# Remove legacy definitions from original files
echo -e "${BLUE}${NC} Removing legacy template definitions..."
for file in "${yaml_files[@]}"; do
if grep -q "platform: template" "$file" 2>/dev/null; then
remove_legacy_templates "$file"
echo " ${GREEN}${NC} Updated: $(basename "$file")"
fi
done
echo ""
@@ -383,11 +409,11 @@ main() {
echo ""
echo "Backup: $BACKUP_DIR"
echo "Migrated: $TOTAL template entities"
echo "Files modified: $FILES_MODIFIED"
echo "Files modified: $FILES_MODIFIED (+ configuration.yaml)"
echo ""
echo "NEXT STEPS:"
echo "1. Check config: ha core check"
echo "2. Review configuration.yaml template: section"
echo "1. Review configuration.yaml template: section"
echo "2. Check config: ha core check"
echo "3. Restart HA: ha core restart"
echo "4. If issues: bash $BACKUP_DIR/restore.sh"
echo ""