Skip to content

Writing an App in Pyscript

Daniel Lashua edited this page Feb 18, 2021 · 4 revisions

Why Write an App?

So you've got a pyscript doing a thing, and you realize you want to do that same thing, but with different inputs and outputs. You can just copy the code and change all the entity_ids, over and over each time you need it. For really simple scripts, sometimes, this is the best route.

But, another option: turn it into an app!

Here's an example with heavily commented code to show you what the conversion might look like.

Original Pyscript Code

@state_trigger("binary_sensor.room1 == 'on'")
def motion_light():
    task.unique("motion_light")
    if light.room1 != "on":
        light.turn_on(entity_id="light.room1")
    task.sleep(float(input_number.room1))
    light.turn_off(entity_id="light.room1")

YAML to use this same automation multiple times

pyscript:
  apps:
    motion_lights:
      - motion_id: binary_sensor.room1
        light_id: light.room1
        time_id: input_number.room1
      - motion_id: binary_sensor.room2
        light_id: light.room2
        time_id: input_number.room1

Converted code to turn this in to an App, with comments!

# we'll use this later
registered_triggers = []

# First, we define a function that makes more functions.
def make_motion_light(config):

    # this is the original code modified to 
    # substituting in the variables from YAML.
    # compare it line by line with the original code

    # replace the hardcoded entity ID with the value from config
    @state_trigger(f"{config['motion_id']} == 'on'")
    def motion_light():

        # we want a unique task for each separate app, so lets use
        # a unique name based on some config data
        task.unique(f"motion_light_{config['motion_id']}")

        # because our light entity is in a variable we'll have to
        # use the longer form to get the state
        if state.get(config['light_id']) != "on":

            # substitute in the value from config
            light.turn_on(entity_id=config['light_id'])

        # substitute from config    
        task.sleep(float(state.get(config['time_id'])))

        # substitute from config
        light.turn_off(entity_id=config['light_id'])


    # now that we've made a function specifically for this config item
    # we need to register it in the global scope so pyscript sees it.
    # the easiest way to do that is add it to a global list.
    registered_triggers.append(motion_light)


# now we just need the startup trigger
@time_trigger('startup')
def motion_light_startup():
    for app in pyscript.app_config:
        make_motion_light(app)