Files
ha-template-migration/migrate_templates.sh
jessikitty 5171723a05 Add pure bash template migration script (no Python dependencies)
Safe and reliable bash-only implementation:
- Works in SSH add-on without Python
- Uses awk for YAML parsing (reliable and fast)
- Automatic timestamped backups
- Extracts legacy template definitions
- Converts to modern template: syntax
- Removes legacy platform: template blocks
- Adds modern templates to configuration.yaml
- Generates restore script for rollback
- Exit on error for safety (set -e)
- Temp directory cleanup with trap

Fixes all 67 template deprecation warnings without Python dependencies.
2025-12-23 12:30:19 +11:00

399 lines
13 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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