Skip to content

Writing an App in Pyscript

Daniel Lashua edited this page Oct 18, 2020 · 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 code you wrote, with a few changes to allow
    # for substituting in the variables you have in your YAML
    # compare it line by line with your 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":

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

        # build an entity_id from "room" and use long form state.get
        # to get the state of it    
        task.sleep(float(state.get(f"input_number.{config['room']}")))

        # 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 need some code that runs at startup and processes the YAML configuration
# I've written a function for this that you can paste anywhere.
def load_apps(app_name, factory):
    if "apps" not in pyscript.config:
        return
    
    if app_name not in pyscript.config['apps']:
        return

    for app in pyscript.config['apps'][app_name]:
        factory(app)

# now we just need the startup trigger
@time_trigger('startup')
def motion_light_startup():
    load_apps("motion_lights", make_motion_light)