Unleashing Custom Logic with Home Assistant's python_script Integration

Avatar picture of NGC 224

NGC 224

DIY Smart Home Creator
0
0
Represent Unleashing Custom Logic with Home Assistant's python_script Integration article
5m read

Home Assistant offers incredible flexibility for automating your home. While standard automations using YAML, the UI editor, or even Node-RED and AppDaemon cover most use cases, sometimes you need truly custom logic that's best handled by a full programming language. That's where the built-in python_script integration comes in.

What is python_script?

The python_script integration allows you to write small Python scripts that can interact with your Home Assistant instance. These scripts have access to Home Assistant's state machine, can call services, and log information. They offer a powerful way to implement logic that might be too complex or cumbersome for template sensors, Jinja2 templates in automations, or even graphical automation tools.

Think of it as a lightweight way to run custom Python code directly within your Home Assistant environment, specifically designed for automation tasks.

Why Use python_script?

  • Complex Logic: Handle conditional branching, loops, calculations, or string manipulation that's difficult in standard templates.
  • Custom Service Calls: Build sequences of service calls based on dynamic conditions.
  • Dynamic State Updates: Create or modify states based on complex inputs or logic.
  • Data Processing: Perform light data analysis or transformation before triggering actions.
  • Integration with Python Libraries: (Limited, typically standard library) Access Python's standard library for tasks like time manipulation, math, etc.
  • Built-in & Lightweight: No external add-ons like AppDaemon or Node-RED required, making it simpler for smaller scripting needs.

Setting Up python_script

Enabling the python_script integration is straightforward. Add the following lines to your configuration.yaml file:

python_script:

After adding this, restart your Home Assistant instance. The integration will create a python_scripts folder in your Home Assistant configuration directory (the same place as configuration.yaml).

Writing Your First Script

All your Python scripts must reside within the python_scripts folder. The name of the file (without the .py extension) becomes the script's entity ID. For example, a file named set_light_brightness.py will correspond to the service python_script.set_light_brightness.

Let's create a simple script to log a message. Create a file named hello_world.py inside the python_scripts folder and add the following content:

logger.info("Hello from my first python_script!")

Save the file. You don't need to restart Home Assistant when adding or modifying scripts in this folder; simply reload the python_script integration via Developer Tools -> YAML or by calling the homeassistant.reload_config_entry service for the python_script integration.

Calling Your Script

Once the script is saved and the integration reloaded, it becomes available as a service under the python_script domain. You can call it from Developer Tools -> Services, from automations, or from other scripts.

In Developer Tools -> Services, select python_script.hello_world and click "CALL SERVICE". Check your Home Assistant logs (Settings -> System -> Logs) and you should see the "Hello from my first python_script!" message.

Interacting with Home Assistant

Python scripts have access to several built-in variables and functions:

  • hass: The Home Assistant instance. Use this to access states, call services, etc.
  • logger: A logger object for writing messages to the Home Assistant log.
  • data: A dictionary containing any data passed to the script when called (useful for automations).

Accessing States

You can get the state of an entity using hass.states.get('entity_id'). This returns a state object with attributes like state, name, attributes, etc.

light_state = hass.states.get('light.my_kitchen_light')

if light_state:
    logger.info(f"Kitchen light state: {light_state.state}")
    if light_state.state == 'on' and 'brightness' in light_state.attributes:
        logger.info(f"Brightness: {light_state.attributes['brightness']}")
else:
    logger.warning("Kitchen light entity not found!")

Calling Services

Use hass.services.call('domain', 'service', service_data) to call any Home Assistant service.

# Turn on a light
hass.services.call('light', 'turn_on', {'entity_id': 'light.living_room_lamp'}) 

# Set light brightness using passed data
entity_id = data.get('entity_id')
brightness = data.get('brightness')

if entity_id and brightness is not None:
    hass.services.call('light', 'turn_on', {'entity_id': entity_id, 'brightness': brightness}, False)
else:
    logger.warning("Missing entity_id or brightness data.")

Note the False parameter in the second call example. This is `blocking`. By default, service calls are blocking. Setting it to False makes it non-blocking, which can be useful if your script needs to continue executing immediately.

Passing Data to Scripts

Scripts become much more versatile when you pass data to them from the automation or service call that triggers them. This data is available in the script via the data dictionary.

Let's create a script set_brightness_dynamic.py:

entity_id = data.get('entity_id')
brightness_level = data.get('brightness', 128) # Default to 128 if not provided

if entity_id:
    logger.info(f"Setting {entity_id} brightness to {brightness_level}")
    hass.services.call('light', 'turn_on', {'entity_id': entity_id, 'brightness': brightness_level}, False)
else:
    logger.error("No entity_id provided to set_brightness_dynamic script.")

Now, call this script from an automation (using YAML example):

automation:
  - alias: 'Set Lamp Brightness from Python'
    trigger:
      - platform: state
        entity_id: input_boolean.trigger_brightness_script
        to: 'on'
    action:
      - service: python_script.set_brightness_dynamic
        data:
          entity_id: light.my_desk_lamp
          brightness: 200
      - service: input_boolean.turn_off
        entity_id: input_boolean.trigger_brightness_script # Reset the trigger

Or via the UI Automation editor, selecting the 'Call service' action, choosing python_script.set_brightness_dynamic, and providing the data in the service data field:

entity_id: light.my_desk_lamp
brightness: 200

Advanced Concepts & Examples

Conditional Logic Based on Multiple States

A script can easily check the state of multiple entities before performing an action.

weather_state = hass.states.get('weather.forecast_home')
window_state = hass.states.get('binary_sensor.living_room_window')
thermostat_temp = hass.states.get('climate.main_thermostat')

if weather_state and window_state and thermostat_temp:
    current_temp = float(thermostat_temp.attributes.get('current_temperature', 0))

    if weather_state.state == 'rainy' and window_state.state == 'on':
        logger.warning("It's raining and a window is open! Consider closing.")
        # Optionally call a notification service here

    if weather_state.state == 'sunny' and current_temp > 25:
        logger.info("It's hot and sunny. Consider closing blinds.")
        # Optionally call a service to close blinds/shades

Using Time and Dates

Access Python's standard library for time-based logic.

from datetime import datetime

now = datetime.now()
hour = now.hour

if 6 <= hour < 18:
    logger.info("It's daytime.")
    hass.services.call('homeassistant', 'turn_on', {'entity_id': 'light.garden_lights'}) # Example action
else:
    logger.info("It's nighttime.")
    hass.services.call('homeassistant', 'turn_off', {'entity_id': 'light.garden_lights'}) # Example action

Best Practices

  • Keep Scripts Focused: Write small, single-purpose scripts. Complex logic can sometimes be broken down or handled better by other tools.
  • Use Logging Extensively: Use logger.info(), logger.warning(), logger.error() to understand what your script is doing and diagnose issues.
  • Handle Missing Data/Entities: Always check if entities exist or if required data was passed using data.get() with a default value or an if check.
  • Reload, Don't Restart: When developing scripts, use the "Reload Python Scripts" service or the YAML reload option instead of a full Home Assistant restart.
  • Avoid Long-Running Tasks: Python scripts are synchronous. Avoid operations that take a long time as they can block Home Assistant's event loop. For long-running tasks, AppDaemon is a better fit.
  • Error Handling: Use try...except blocks for operations that might fail, like type conversions or accessing potentially missing dictionary keys.

Limitations

Compared to AppDaemon:

  • No direct access to the event bus (you react to being called as a service).
  • Synchronous execution (can block Home Assistant if script is too slow).
  • Limited external library support (primarily standard library).

Conclusion

The python_script integration is a valuable tool in the Home Assistant ecosystem. It provides a simple, built-in way to execute custom Python logic for your automations when standard methods fall short. By leveraging its access to states, services, and logging, you can implement sophisticated control flows and data-driven decisions to make your smart home even smarter and more responsive. Start experimenting with small scripts and gradually tackle more complex automation challenges!

Avatar picture of NGC 224
Written by:

NGC 224

Author bio: DIY Smart Home Creator

There are no comments yet
loading...