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