#!/bin/bash ################################################################################ # Home Assistant Legacy Template Migration (Bash Version) # Safely migrates legacy template syntax to modern template: syntax ################################################################################ set -e # Exit on error # Configuration CONFIG_DIR="/config" BACKUP_TIMESTAMP=$(date +%Y%m%d_%H%M%S) BACKUP_DIR="$CONFIG_DIR/backups/template_migration_$BACKUP_TIMESTAMP" TEMP_DIR="/tmp/template_migration_$$" # Colors GREEN='\033[0;32m' BLUE='\033[0;34m' YELLOW='\033[1;33m' RED='\033[0;31m' NC='\033[0m' # Counters TOTAL_SENSORS=0 TOTAL_BINARY_SENSORS=0 FILES_MODIFIED=0 echo "======================================================================" echo "Home Assistant Legacy Template Migration (Bash)" echo "======================================================================" echo "" # Create temp directory mkdir -p "$TEMP_DIR" trap "rm -rf $TEMP_DIR" EXIT # Create backup directory mkdir -p "$BACKUP_DIR" echo -e "${GREEN}✓${NC} Created backup: $BACKUP_DIR" echo "" ################################################################################ # Extract legacy template sensors from a file ################################################################################ extract_legacy_templates() { local file="$1" local type="$2" # "sensor" or "binary_sensor" local output_file="$TEMP_DIR/${type}_$(basename "$file").extracted" # Check if file has legacy templates if ! grep -q "platform: template" "$file" 2>/dev/null; then return fi echo -e "${BLUE}ℹ${NC} Processing $(basename "$file") for ${type}..." # Use awk to extract legacy template blocks awk -v type="$type" ' BEGIN { in_sensor_section = 0 in_template_platform = 0 in_sensors_block = 0 indent_level = 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 match($0, /^[[:space:]]*/) platform_indent = RLENGTH next } # Look for sensors: block within template platform in_template_platform && /^[[:space:]]*sensors:/ { in_sensors_block = 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 next } # Print sensor lines print } ' "$file" > "$output_file" if [ -s "$output_file" ]; then local count=$(grep -c "^[[:space:]]*[a-zA-Z_].*:" "$output_file" 2>/dev/null || echo 0) echo " ${GREEN}✓${NC} Found $count ${type}(s)" if [ "$type" = "sensor" ]; then TOTAL_SENSORS=$((TOTAL_SENSORS + count)) else TOTAL_BINARY_SENSORS=$((TOTAL_BINARY_SENSORS + count)) fi # Backup original file cp "$file" "$BACKUP_DIR/$(basename "$file")" echo " ${GREEN}✓${NC} Backed up: $(basename "$file")" FILES_MODIFIED=$((FILES_MODIFIED + 1)) else rm -f "$output_file" fi } ################################################################################ # Remove legacy template definitions from a file ################################################################################ remove_legacy_templates() { local file="$1" if [ ! -f "$file" ]; then return fi # Use awk to remove legacy template platform blocks awk ' BEGIN { in_template_platform = 0 platform_indent = 0 skip_block = 0 } # Detect platform: template /^[[:space:]]*-[[:space:]]*platform:[[:space:]]*template/ { in_template_platform = 1 match($0, /^[[:space:]]*/) platform_indent = RLENGTH skip_block = 1 next } # Skip lines in template platform block skip_block { # Check if we hit next item at same or lower indent if (/^[[:space:]]*-[[:space:]]/ || /^[[:alpha:]]/) { match($0, /^[[:space:]]*/) current_indent = RLENGTH # If at same or lower indent, end of block if (current_indent <= platform_indent || /^[[:alpha:]]/) { skip_block = 0 in_template_platform = 0 print next } } # Skip this line next } # Print non-skipped lines { print } ' "$file" > "$file.tmp" mv "$file.tmp" "$file" } ################################################################################ # Convert extracted templates to modern syntax ################################################################################ convert_to_modern_syntax() { local type="$1" # "sensor" or "binary_sensor" local output_file="$TEMP_DIR/modern_${type}.yaml" echo "- ${type}:" > "$output_file" # Process all extracted files for extracted in "$TEMP_DIR/${type}_"*.extracted; do [ -f "$extracted" ] || continue # Parse each sensor and convert to modern syntax awk -v type="$type" ' BEGIN { sensor_name = "" in_sensor = 0 indent = " " } # Detect sensor name (e.g., "router_authenticated:") /^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*:/ { if (sensor_name != "") { # End previous sensor print "" } # Start new sensor match($0, /[a-zA-Z_][a-zA-Z0-9_]*/) sensor_name = substr($0, RSTART, RLENGTH) print indent "- unique_id: " sensor_name in_sensor = 1 next } in_sensor { # Convert field names if (/friendly_name:/) { sub(/friendly_name:/, "name:") print indent indent $0 } else if (/value_template:/) { sub(/value_template:/, "state:") print indent indent $0 } else if (/icon_template:/) { sub(/icon_template:/, "icon:") print indent indent $0 } else if (/entity_picture_template:/) { sub(/entity_picture_template:/, "picture:") print indent indent $0 } else if (/availability_template:/) { sub(/availability_template:/, "availability:") print indent indent $0 } else if (/attribute_templates:/) { sub(/attribute_templates:/, "attributes:") print indent indent $0 } else if (/^[[:space:]]+[a-zA-Z_]/) { # Keep other fields (unit_of_measurement, device_class, etc) print indent indent $0 } } ' "$extracted" >> "$output_file" done } ################################################################################ # Add modern templates to configuration.yaml ################################################################################ add_to_configuration() { local config_file="$CONFIG_DIR/configuration.yaml" echo -e "\n${BLUE}ℹ${NC} Adding modern template definitions to configuration.yaml..." # Backup configuration.yaml cp "$config_file" "$BACKUP_DIR/configuration.yaml" # Check if template: section already exists 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" echo "template:" >> "$config_file" [ -f "$TEMP_DIR/modern_sensor.yaml" ] && cat "$TEMP_DIR/modern_sensor.yaml" >> "$config_file" [ -f "$TEMP_DIR/modern_binary_sensor.yaml" ] && cat "$TEMP_DIR/modern_binary_sensor.yaml" >> "$config_file" echo -e " ${YELLOW}⚠${NC} Manual merge required - see end of configuration.yaml" else echo -e " ${GREEN}✓${NC} Adding new template: section..." echo "" >> "$config_file" echo "# Modern template syntax (migrated $BACKUP_TIMESTAMP)" >> "$config_file" echo "template:" >> "$config_file" [ -f "$TEMP_DIR/modern_sensor.yaml" ] && cat "$TEMP_DIR/modern_sensor.yaml" >> "$config_file" [ -f "$TEMP_DIR/modern_binary_sensor.yaml" ] && cat "$TEMP_DIR/modern_binary_sensor.yaml" >> "$config_file" fi echo -e " ${GREEN}✓${NC} Updated configuration.yaml" } ################################################################################ # Create restore script ################################################################################ create_restore_script() { local restore_script="$BACKUP_DIR/restore.sh" cat > "$restore_script" << 'EOF' #!/bin/bash echo "Restoring from template migration backup..." SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" CONFIG_DIR="/config" # Restore all backed up files for file in "$SCRIPT_DIR"/*.yaml; do [ -f "$file" ] || continue filename=$(basename "$file") cp "$file" "$CONFIG_DIR/$filename" echo "✓ Restored: $filename" done echo "" echo "Files restored!" echo "Restart Home Assistant: ha core restart" EOF chmod +x "$restore_script" echo -e "\n${GREEN}✓${NC} Restore script created: $restore_script" } ################################################################################ # Main migration process ################################################################################ main() { echo -e "${BLUE}ℹ${NC} Scanning for legacy template definitions..." echo "" # Find all YAML files 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) # 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) fi # Extract templates from all files for file in "${yaml_files[@]}"; do extract_legacy_templates "$file" "sensor" extract_legacy_templates "$file" "binary_sensor" done # Check if we found anything if [ $TOTAL_SENSORS -eq 0 ] && [ $TOTAL_BINARY_SENSORS -eq 0 ]; then echo -e "\n${GREEN}✓${NC} No legacy template definitions found!" echo "All your templates are already using modern syntax!" rm -rf "$BACKUP_DIR" exit 0 fi TOTAL=$((TOTAL_SENSORS + TOTAL_BINARY_SENSORS)) echo "" echo -e "${BLUE}ℹ${NC} Found $TOTAL legacy template entities:" echo " - $TOTAL_SENSORS sensor(s)" echo " - $TOTAL_BINARY_SENSORS binary_sensor(s)" echo "" # Convert to modern syntax echo -e "${BLUE}ℹ${NC} Converting to modern template: syntax..." [ $TOTAL_SENSORS -gt 0 ] && convert_to_modern_syntax "sensor" [ $TOTAL_BINARY_SENSORS -gt 0 ] && convert_to_modern_syntax "binary_sensor" echo -e " ${GREEN}✓${NC} Conversion complete" 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 "" # Add modern templates to configuration.yaml add_to_configuration # Create restore script create_restore_script echo "" echo "======================================================================" echo "MIGRATION COMPLETE" echo "======================================================================" echo "" echo "Backup: $BACKUP_DIR" echo "Migrated: $TOTAL template entities" echo "Files modified: $FILES_MODIFIED" echo "" echo "NEXT STEPS:" echo "1. Check config: ha core check" echo "2. Review configuration.yaml template: section" echo "3. Restart HA: ha core restart" echo "4. If issues: bash $BACKUP_DIR/restore.sh" echo "" echo "======================================================================" } # Run main function main