| name | ha-pyscript-converter |
| description | Converts Home Assistant YAML automations to HA-Pyscript Python files. Use when asked to convert, migrate, or rewrite HA automations from YAML/JSON format to pyscript. Groups related automations into the same Python file, merges complementary automations into single functions with multiple triggers, and replaces delays with task.sleep(). The source automations are in originals/ and the output goes to the pyscript/ directory. |
HA Pyscript Converter
Converts HA YAML automations (from originals/) into pyscript Python files.
Conversion Workflow
Step 0: Generate stubs and UUID lookup (strongly recommended)
Stubs enable method syntax and UUID resolution. Before analyzing, check if modules/stubs/pyscript_generated.py exists and is up to date, and if originals/entity_ids.csv exists. If either is missing, prompt the user:
Before converting, prepare two lookup files from your HA instance:
Stubs (enables method syntax and entity candidates):
- Go to Developer Tools → Actions and run the
pyscript.generate_stubs action.
- Sync to this machine:
rsync -rv ha:config/pyscript/modules/stubs/ modules/stubs
UUID lookup (resolves opaque device/entity UUIDs to human-readable IDs):
- Go to Developer Tools → Template and render this template:
{% for entity in states %}
{{ entity.entity_id }}, {{ device_id(entity.entity_id) }}
{%- endfor %}
- Copy the output and save it as
originals/entity_ids.csv.
Once stubs are present, the analyzer will use method syntax and provide UUID candidates automatically.
Step 1-4: Analyze, plan, convert, review
- Analyze - run
analyze_automations.py to cluster automations by shared entities
- Plan groupings - decide which automations belong in the same file (review clusters, merge small ones)
- Convert - write each group as a
.py file; merge related automations into single functions
- Review TODOs - resolve device UUIDs, ZHA command strings, template conditions
uv run python .claude/skills/ha-pyscript-converter/scripts/analyze_automations.py \
originals/automations.yaml --output-dir tmp/
Output goes to tmp/ for review. Final files go to pyscript/.
The script auto-detects modules/stubs/pyscript_generated.py if present. When stubs are loaded:
- Known entities use method syntax:
light.kitchen_front.turn_on(brightness_pct=100)
- UUID
device_id/entity_id TODOs include candidate entity lists from the registry
- Output header reads
(with stubs) vs (no stubs; resolve UUIDs manually)
Key Conversion Rules
Triggers → decorators (see trigger-mapping.md):
trigger: state, to: 'on' → @state_trigger("entity == 'on'")
trigger: numeric_state, above: X, below: Y → @state_trigger("X < float(e) < Y")
trigger: time, at: HH:MM → @time_trigger("once(HH:MM)")
trigger: time_pattern → @time_trigger("cron(min hour * * *)")
trigger: sun, event: sunset → @time_trigger("once(sunset)")
trigger: event, timer.finished → @event_trigger("timer.finished", "entity_id == '...'")
trigger: device, domain: zha → @event_trigger("zha_event", "device_id == '...' and command == '...'") ⚠️ verify command string
Conditions → early returns:
def my_func(**kwargs):
if input_select.lighting_mode not in ['evening', 'night']: return
if not (float(sensor.luminance) < 15): return
Actions (prefer method syntax when entity is known from stubs):
delay: → task.sleep(seconds)
action: light.turn_on, target: entity_id: X → light.X_name.turn_on(brightness_pct=...) (method) or light.turn_on(entity_id="light.X_name", ...) (service)
action: light.turn_on, target: entity_id: [X, Y] → light.turn_on(entity_id=["light.X", "light.Y"], ...) (lists always use service form)
choose: / if:/then:/else: → Python if/elif/else
repeat: count: N → for _i in range(N):
action: automation.trigger → direct function call (convert target to plain helper function)
Mode:
mode: single → task.unique('fn_name', kill_me=True) as first line
Grouping & Merging
See patterns.md for detailed before/after examples.
Merge into one function when:
- Same trigger entity with complementary conditions (on/off toggle pairs)
- Same ZHA button with different press types (use multiple
@event_trigger decorators)
- Light level setter automations (Set 0/1/2/3) → one function dispatching on value
File organization (one file per physical/logical system):
kitchen.py - all kitchen light automations (motion, buttons, level sets)
dining.py - dining area lights
loft.py - loft/upstairs lights
mb_bathroom.py - master bathroom
mb_bedroom.py - master bedroom lights + nightlight
water_heater.py - water heater automations
lighting_mode.py - global lighting mode (evening/night/day)
monitoring.py - Synology backup, error alerts
Important Caveats
Device UUIDs: YAML uses opaque UUIDs for device_id and sometimes entity_id. Resolve to human-readable entity IDs:
- Check
originals/entity_ids.csv first (generated in Step 0) — grep for the UUID to find the matching entity IDs
- HA UI: Settings → Devices → click device, or Developer Tools → States
- Template:
{{ device_entities('UUID') }} in Developer Tools → Template
ZHA commands: The analyze_automations.py script marks ZHA triggers with # TODO: narrow command. To find the correct string:
- Developer Tools → Events → listen to
zha_event
- Press the button, note the
command field
Timer entities: Keep existing HA timer entities (timer.start, timer.cancel, @event_trigger("timer.finished", ...)) unless simplifying with task.sleep. Mixing approaches within a system is fine.
automation.trigger calls: Convert the target automation into a plain Python helper function (no decorators), then call it directly.
Reference Files
- trigger-mapping.md - complete YAML→pyscript mapping tables for all trigger/condition/action types
- patterns.md - before/after examples for complex patterns: toggle pairs, motion-activated lights, ZHA buttons, light level dispatch, disabled triggers, template conversion