Work with HomeAssistant pyscript automation files. Use when asked to add, modify, debug, or refactor automations in the pyscript/ directory. Also use when asked to sync or patch stubs from HA.
Work with pyscript automation files in pyscript/. Each file controls one physical/logical
system and is deployed to HA.
| File | System |
|---|---|
kitchen.py | Kitchen lights: level dispatch, ZHA buttons, motion |
dining.py | Dining area lights |
loft.py | Loft/upstairs lights |
mb_bathroom.py | Master bathroom |
mb_bedroom.py | Master bedroom lights + nightlight |
gaurav.py, gautam.py | Person-specific rooms |
storeroom.py | Storeroom lights |
kitchen_bathroom.py | Kitchen bathroom |
water_heater.py | Water heater |
lighting_mode.py| Global lighting mode (daytime/evening/night) |
monitoring.py | Synology backup and error alerts |
Stubs in modules/stubs/ provide IDE autocomplete and enable method syntax. Each pyscript
file imports from them at the top:
from stubs.pyscript_builtins import event_trigger, state_trigger, task, time_trigger
from stubs.pyscript_generated import light, timer, input_number, input_select
These imports are ignored at runtime by pyscript. Check modules/stubs/pyscript_generated.py
for exact entity names before writing service calls.
Method syntax (preferred for single known entities):
light.kitchen_front.turn_on(brightness_pct=100) # method — single entity only
light.turn_on(entity_id="light.kitchen_front", ...) # service — always valid
light.turn_on(entity_id=["light.a", "light.b"], ...) # lists always use service form
When stubs are stale or missing:
pyscript.generate_stubsrsync -rv ha:config/pyscript/modules/stubs/ modules/stubs
uv run python .claude/skills/ha-pyscript/scripts/patch_stubs.py modules/stubs/pyscript_generated.py
rsync -rv ./pyscript/ "ha:/root/config/pyscript"
Reload after adding a new file: Developer Tools → Actions
→ pyscript.reload
See sources/pyscript_tutorial.md for the full language reference.
@state_trigger("entity == 'on'")
@state_trigger("float(input_number.x) > 2.9 and float(input_number.x) < 3.1")
@state_trigger("binary_sensor.door") # any state change
@state_trigger("entity.old == 'off' and entity == 'on'") # from/to
@time_trigger("once(7:00:00)")
@time_trigger("cron(30 22 * * *)") # 22:30 daily
@time_trigger("once(sunset - 30 min)")
@event_trigger("timer.finished", "entity_id == 'timer.my_timer'")
@event_trigger("zha_event", "device_id == '...' and command == 'toggle'")
Multiple triggers on one function — any one fires it:
@event_trigger("zha_event", "device_id == 'device_a' and command == 'toggle'")
@event_trigger("zha_event", "device_id == 'device_b' and command == 'toggle'")
def handle_button(**kwargs):
...
def my_func(**kwargs):
if input_select.lighting_mode not in ['evening', 'night']: return
if not (float(sensor.luminance) < 15): return
if light.kitchen_front != 'on': return
# Service calls
light.turn_on(entity_id="light.kitchen_front", brightness_pct=100, color_temp_kelvin=5400)
light.turn_on(entity_id=["light.a", "light.b"], brightness_pct=50)
light.turn_off(entity_id="light.kitchen_front")
input_number.kitchen_light_level.set_value(value=2)
# Delays
task.sleep(30) # sleep N seconds
# HA timers
timer.my_timer.start(duration="900") # start (seconds as string)
timer.my_timer.start(duration="0:15:0") # start (H:M:S)
timer.my_timer.cancel()
def my_func(**kwargs):
task.unique('my_func', kill_me=True) # mode: single — new call kills old
task.unique('my_func') # mode: restart — old kills new
# omit task.unique for mode: parallel or queued
All HA state values are strings; cast explicitly for comparisons:
float(input_number.kitchen_light_level) # numeric state
int(sensor.some_count)
state.get("timer.water_heater") # dynamic entity name
state.names("input_text") # list all entities in a domain
Trigger not firing? Check HA logs for:
trigger file.module.func got event trigger, but not active
This means the event filter expression has a syntax error or mismatched event type.
To find ZHA event command strings: Developer Tools → Events
→ listen to zha_event → press the button.