Mastering Home Assistant Scripting with pyscript: Bridging Python's Power with Smart Home Automations
NGC 224
DIY Smart Home Creator
The Challenge: Beyond YAML's Limits
Home Assistant's YAML automations are excellent for many tasks, but complex logic, intricate data manipulation, or the need for advanced computation can quickly make them unwieldy. This is where pyscript shines, allowing you to embed full Python scripting directly within Home Assistant. With pyscript, you can leverage Python's extensive capabilities to create sophisticated, data-driven smart home automations.
Step-by-Step Setup: Integrating pyscript
1. Installation and Configuration
First, ensure HACS (Home Assistant Community Store) is installed. Then, go to HACS > Integrations, search for "pyscript", download it, and restart Home Assistant.
Add the following to your configuration.yaml:
# configuration.yaml
pyscript:
allow_code_remote: true # Enable script reloads without HA restarts (dev)
log_level: info # Adjust for debugging (debug, info, warning, error)
allow_code_remote: true allows you to reload scripts via the pyscript.reload service (Developer Tools > Services), speeding up development. After changes, restart Home Assistant or call pyscript.reload.
2. Your First pyscript
Create a pyscript/ directory in your Home Assistant configuration folder. Inside, create hello_world.py:
# pyscript/hello_world.py
from datetime import datetime
@time_trigger('startup')
def hello_world_startup():
log.info("pyscript: Hello from startup!")
@state_trigger("binary_sensor.front_door == 'on'")
def front_door_opened():
log.info(f"Front door opened at {datetime.now().strftime('%H:%M:%S')}!")
hass.services.call('persistent_notification', 'create', {
'message': 'Front door just opened!',
'title': 'Security Alert'
})
This demonstrates @time_trigger for startup and @state_trigger for entity state changes. Save, reload pyscript, and check your Home Assistant logs (Settings > System > Logs) for output.
Troubleshooting Common Issues
- Script Not Running: Verify correct file path (
<config_dir>/pyscript/), YAML configuration, and ensurepyscriptwas reloaded/HA restarted. Check HA logs for errors. - Python Errors: Standard Python tracebacks are logged. Use an IDE for linting.
- Service Call Issues: Confirm service domain, name, and payload in Developer Tools > Services before using in
pyscript. - Debugging: Use
log.info("Message")liberally. Setlog_level: debuginconfiguration.yamlfor verbose output.
Advanced Configuration and Optimization
Entity & Service Interaction
Use global objects state (e.g., state.get('sensor.temp')) to read entity states/attributes and hass.services.call('domain', 'service', {{'data': 'value'}}) to call services.
Code Organization
For modularity, split logic into multiple .py files and use standard Python import statements within the pyscript/ directory. Example:
# pyscript/my_utils.py
def adaptive_brightness(lux, min_lux):
return int(200 * (min_lux / lux)) if lux < min_lux else 0
# pyscript/main.py
from my_utils import adaptive_brightness
@state_trigger('sensor.room_lux')
def control_light(new_value):
brightness = adaptive_brightness(float(new_value), 50)
if brightness > 0:
hass.services.call('light', 'turn_on', {'entity_id': 'light.room', 'brightness': brightness})
Performance & State
- Event-Driven: Always prefer
@state_trigger,@time_trigger,@event_triggerover polling loops. - Persistent State: For state that persists across
pyscriptreloads, use Home Assistant'sinput_boolean,input_number, orinput_texthelpers instead of global Python variables.
Real-World Example: Dynamic Ambiance Control
Here's a pyscript for intelligent living room lighting, adjusting brightness and color temperature based on occupancy, ambient light, and time of day, respecting a manual override toggle.
Assumptions: binary_sensor.living_room_occupancy, sensor.living_room_lux_level, light.living_room_main, input_boolean.living_room_light_manual_mode.
# pyscript/dynamic_living_room_light.py
from datetime import datetime, time
@state_trigger(
'binary_sensor.living_room_occupancy',
'sensor.living_room_lux_level',
'time',
'input_boolean.living_room_light_manual_mode'
)
def manage_living_room_lights():
# Manual override check
if state.get('input_boolean.living_room_light_manual_mode') == 'on':
log.info("Light in manual mode. Skipping automation.")
return
is_occupied = state.get('binary_sensor.living_room_occupancy') == 'on'
current_lux = float(state.get('sensor.living_room_lux_level', default=0))
light_entity = 'light.living_room_main'
# Turn off if unoccupied
if not is_occupied:
if state.get(light_entity) == 'on':
hass.services.call('light', 'turn_off', {'entity_id': light_entity, 'transition': 5})
return
# Occupied: calculate brightness and color temp
now = datetime.now().time()
# Time-based kelvin and base brightness
if time(22, 0) <= now or now < time(6, 0): # Late night/early morning
desired_kelvin, base_brightness_pct = 2200, 25
elif time(6, 0) <= now < time(9, 0): # Morning
desired_kelvin, base_brightness_pct = 2700, 50
elif time(9, 0) <= now < time(17, 0): # Daytime
desired_kelvin, base_brightness_pct = 4000, 75
else: # Evening
desired_kelvin, base_brightness_pct = 3000, 40
# Lux-based brightness adjustment
lux_reduction_factor = max(0, 1 - (min(current_lux, 200) / 200)) # Reduce if ambient light exists
final_brightness_pct = max(10, min(100, int(base_brightness_pct * lux_reduction_factor)))
# Update light if needed
current_brightness_pct_ha = int(round((state.attr(light_entity, 'brightness') / 255) * 100)) if state.attr(light_entity, 'brightness') else 0
current_kelvin = state.attr(light_entity, 'color_temp_kelvin')
if final_brightness_pct > 0 and (
state.get(light_entity) == 'off' or
abs(current_brightness_pct_ha - final_brightness_pct) > 5 or
abs(current_kelvin - desired_kelvin) > 100
):
hass.services.call('light', 'turn_on', {
'entity_id': light_entity,
'brightness_pct': final_brightness_pct,
'kelvin': desired_kelvin,
'transition': 3
})
elif final_brightness_pct == 0 and state.get(light_entity) == 'on':
hass.services.call('light', 'turn_off', {'entity_id': light_entity, 'transition': 3})
This script exemplifies how pyscript streamlines complex, multi-factor automations, offering superior control over brightness, color temperature, and state management.
Best Practices and Wrap-up
- Security:
pyscripthas deep HA access. Only run trusted code. - Backup: Include your
pyscript/directory in HA backups (e.g., Google Drive Backup). - Version Control: Use Git for managing scripts, enabling easy tracking and rollbacks.
- Code Clarity: Write readable code with comments and functions.
- Testing: Develop and test in a non-production environment.
- Error Handling: Use
try-exceptblocks for robust scripts.
pyscript empowers Home Assistant users to move beyond declarative YAML, leveraging Python's full power for truly custom, intelligent, and responsive smart home automations.
NGC 224
Author bio: DIY Smart Home Creator
