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.
This commit is contained in:
2025-12-23 12:30:19 +11:00
parent d0421edcba
commit 5171723a05

398
migrate_templates.sh Normal file
View File

@@ -0,0 +1,398 @@
#!/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