PyScript version 1.0.0 Released!

Seems like the whole script is a 1 value sensor with other information. It’s definitely written procedurally and an writing an integration is most likely not an option based on coding skill level.

I don’t think the complexity lies in Object Oriented nature of it. A large part of the complexity, at least for me, was in the lack of easily readable documentation.

When I added “switch” support to pyscript (in the above mentioned PR) as well as “proper” support for “sensor” and “binary_sensor”, it took quite a bit of hunting (and, even more useful, just reading the code of other integrations) to figure out exactly what needed to go where. Even then, I’m not 100% sure that I did it right, only that it works for me. There may be some piece that I did in way that was harder than necessary because I couldn’t find docs on the “right” way.

When I was trying to write code to get all the entities in an area, the documentation didn’t help enough, so I had to pick through the Home Assistant source code to figure it out. Turns out it was incredibly easy, but it took a lot of digging to get those 10 or so lines of code.

Toward the end of the documentation we learn about async_setup_entry (a critical piece in the puzzle), yet, no where in the documentation can I find an example of what actually goes inside async_setup_entry or how to use the 3rd parameter (async_add_devices in the one example of this). Again, one must read the code of other integrations to figure this out. Again, it turns out to be pretty simple, but figuring out how simple it was was complicated, if that makes sense.

I much prefer the way Config Flows are setup (extending a class) than the way platforms are handled. But even then, there are parts of the documentation that don’t apply to custom_components, like the bit about running “hassfest”.

I don’t mean to cast a bad light on Home Assistant at all. It’s a great piece of software that does a lot of great things. But the barrier to entry in writing a custom_component is much higher than it needs to be, in my opinion. Things like pyscript point this out by making it incredibly easy to do many of the tasks an integration can do. Better documentation aimed at custom_component developers would help a lot. I’d write it myself if I was even a little bit confident that I was “doing it right”, but I’m not. Some effort could be made to abstract away a lot of the boilerplate needed and make the whole processes much simpler.

There’s no ‘right way’. There’s many different options, it just depends on which direction you want to go. The only way you’ll understand the API is if you understand the hierarchy, which is complex. But the API documentation is quite good, if you’re used to normal python api documentation.

That’s the overview to help you understand home assistant. The api is documented here.

again, you’re looking at the overview, not the actual api. But examples are always used. Whenever I code, I always look up the api and then examples. This is normal procedure. Did you start just writing pyscripts without examples? I doubt it, you probably just have it memorized at this point so it falsely seems easy. Not to mention, the bar is probably a tad lower because it can only handle a few things opposed to an entire api that can do everything.

I’m sorry but I disagree. I’m guessing you haven’t written code for other open source packages? Home assistant is by far the easiest to write for, largely because the documentation is worlds better than most open source packages. Hell, it’s even better than non-open source.

Well… I guess the first problem is that I have, somehow, never seen this. So, I’ve got some reading to do. If this is as well written as you say it is, this will make the entire process much easier.

Like you, I tend to start with the API documentation, then look for examples. I just, wrongly, believed that the link I offered was the ONLY documentation available. Using a backlinks checker, I can see why this is the case.

Thanks for the conversation and the link!

I spent some time digging through the docs you provided. The link was very helpful.

I wrote a simple integration that allows two entities to “follow the leader”. If the leader turns on, the follower also turns on. And if the leader turns off, the follower does too. It includes a binary_sensor that basically tracks the state of the leader.

It’s trivial, however, it provides all of the boilerplate I’ll need for a more complicated integration (those larger tasks being load into Home Assistant, listen for state changes, make service calls, create binary_sensors).

For this test case, the integration was 221 Lines of Code spread over 5 files. It took me about two hours to hunt down all the bits and pieces I needed. The docs weren’t enough so I used existing integrations as examples.

In Pyscript, this took 25 Lines of Code in 1 file. All of the examples I needed were already in the documentation, so I didn’t have to seek out any other code. It took less than 10 minutes, but, part of that is likely my familiarity with pyscript.

There are, of course, some differences.

If I change the configuration of the integration, I have to press the “reload” button on the “Server Controls” page. If I change the pyscript configuration, it detects this and reloads for me automatically.

With the integration, the binary sensor is a real, registered entity. I can change the entity_id in the UI, for instance. It’s listed on the entities page of “Integrations” as being part of my integration. With the pyscript version, it shows as belonging to the “Binary Sensor” integration, and I can’t change the entity_id. This entity_id renaming shortcoming is addressed in an unmerged PR I’ve written for pyscript.

With the integration, to write it, I had to spin up a separate instance of Home Assistant (to keep from having to restart my REAL instance over and over as I developed and tested). I used a VSCode devcontainer for this, which made it fairly simple, but still an extra step. With Pyscript, I just developed it in my REAL instance and any errors or typos were confined to this functionality; the rest of my Home Assistant integrations and even my other pyscripts continued to function normally.

With the Pyscript version it’s also extends of all of Pyscript. This means, if I need this same functionality in another something I’m writing, I can call the methods from this pyscript to do the work. With the integration, to my knowledge, this just isn’t possible aside from generating configuration YAML automatically and then calling reload afterwards, which is messy.

The configuration.yaml needed to use this integration and pyscript is also the same. However, with the real integration, it’s a top level config, where, with pyscript, it’s configured under the “pyscript” top level.

The really interesting thing is that the bits of these two test cases that actually do the “work” are roughly the same. They use different method calls, of course, but you can follow them almost line by line with one another. Which leads me to believe this will be the case for my more complicated integration as well.

I’ve already written the more complicated version in Pyscript. It’s 154 Lines of Code. Which means the equivalent integration will be 300 lines of code or so. In my opinion, the pyscript code is easier to follow and understand than the integration as well.

This isn’t a case against Integrations. Pyscript, itself, is an integration. But Pyscript is also a baby compared to Home Assistant. In time, the places Pyscript fell short will likely be improved.

But, more importantly, there’s no reason something like Pyscript couldn’t be included IN Home Assistant. Done this way, integration authors could choose which way to write their integrations favoring the “real integration” way in very complicated cases where more control is needed. But, additionally, this would allow pyscript integrations to exist in Home Assistant as their own integrations, instead of as a part of “pyscript” (which is really the only thing I’ve found to be “impossible” thus far).

I don’t know if I’ll finish the more complicated integration. The biggest compelling reason to do so is that it’ll make it easier for others to install and use (install this custom_component vs install pyscript and THEN install this module into it). But it was a good exercise in learning the Home Assistant way to do these same tasks, even if I didn’t do it in the most efficient way.

Apples and oranges. A follow the leader automation should not be an integration, it should be an automation.

Again, I’m simply pointing out that when you use sockets you should be creating an integration. Integrations have all the building blocks for safely using, storing, and reusing metadata (like a socket). I don’t think we will ever agree on this because you’ve already drank the cool aid. Yes writing an integration will be harder than a pyscript. But I can guarantee you that the integration will be more stable than pyscript with a socket. All because you remove the middle man of pyscript thus the reliance on global variables that a socket would require in the pyscript environment.

Point taken. But, so you’re aware, a global variable isn’t REQUIRED to use a socket in the pyscript environment. That might be the way most people would write it, sure, but it’s not REQUIRED. Also, global variables in pyscript, are less bad than you’d otherwise expect since each pyscript is run in its own context which means those globals are limited to the scope of that particular pyscript file. The only “global” requirement is that the trigger functions must be “global”. In most cases, this is as simple as def whatever():. However, in cases where trigger functions are generated in a closure or class, they must be registered in the global space, often done like this:

TRIGGERS = []
def register_trigger(thing):
  TRIGGERS.append(thing)

Then a trigger can be defined like this:

def make_a_trigger():
  
  @state_trigger('some trigger')
  def the_trigger():
    log.info('do something')

  register_trigger(the_trigger)

make_a_trigger()

Also, you’ve inspired me to “stop drinking the cool aid”.

I’ve been a Home Assistant user for at least 6 years (maybe longer, I can’t quite remember when I first started but I know I was using it in 2015). I was initially turned off by the formatting of YAML automations and Jinja templates. For simple automations, it’s fine. But for anything complex, I don’t like juggling multiple automations and the ugliness of Jinja templates. Since then, Automations have become more capable (like choose) but I still find it verbose and less readable compared to just Python. And writing such an automation once isn’t an issue, but, often, the logic is repeated in other areas of my home, requiring cut and paste of the logic, and then altering it in every spot if I improve on it later. Blueprints have helped with this, somewhat, but, most of my more complex automations require more than one, which a blueprint doesn’t cover (multiple blueprints, maybe? it gets weird).

As a result, I initially turned to Node-Red. However, the GUI based programming wasn’t my favorite and, as my collection of automations grew, it became a nightmare to manage.

Then I turned to AppDaemon. It was (and still is) great. But as Home Assistant grew with features, they weren’t always supported by AppDaemon. And the nature of AppDaemon NOT being an integration made some things not possible.

So I turned to pyscript.

Writing a REAL integration always seemed awful. But this exercise has made me realize that, while it may be a bit verbose, it’s not hard.

This has encouraged me to rewrite my more involved pyscripts into Integrations. In every case, they are just simplifying what COULD be done with 5 or 6 automations and a handful of input_* helpers. But having it neatly packaged, reusable, and configurable via YAML is what I’m going for.

These are the Integrations:

  • Wasp in a Box binary_sensor. This is essentially a way to avoid the common problem of, when motion stops in a room but someone is still in there, don’t turn off the lights.
  • Armable Template Binary Sensors. This is basically a regular Template binary_sensor. However, there are a second set of conditions that have to met for state to change from off to on, however, they don’t have to continue to be met once the sensor is already on. So the Template binary_sensor has to be able to refer to its own state. And, in a reusable way, since the entity ID can be changed, I don’t know of a way to reference it inside of the template for the binary_sensor. But an integration (or currently, a Pyscript) can certianly handle this. Automations can handle this too, but, to have the result be a binary_sensor, one must resort to MQTT trick or the python_script.set_state trick, or other such things that get messy. This would be all included and reusable and configurable via YAML.
  • A handful of integrations to make certain ZWave and Zigbee devices easier to use. Remembering the various event codes to trigger on in an automation is annoying and also difficult to reread later. These integrations will consume those events and present them as “sensors” to make writing automations easier. This can be done with the new trigger templates, but, the logic has to be repeated for each device and it becomes a cut and paste extravaganza.
  • A conditional average sensor. We already have the min/max sensor. This allows some other condition to determine if that sensor should be included in the average. I use this mostly for my HVAC system as I prefer to let my central HVAC to only target rooms people are actually in. So it gives me a way to get an average of temperatures but only for rooms that are occupied.

I just have one more thing to figure out on the Integration front to make this possible. My efforts so far have been focused on making it work. But I’m not doing a good job of cleaning up after myself. Any event listeners I’ve created, for instance, still exist even after a reload. This just makes a mess in the long run, especially if a lot of changes/reloads happen without restarting Home Assistant.

But I’m inspired to figure it out. So “Thanks” to you for that. The API docs you linked to have helped a lot. I still don’t know how in 6+ years of using Home Assistant I’ve never found those until now.

I followed the exact same path as you but skipped pyscript because it wasn’t a thing when I made this change. I do like the idea of pyscript. I probably would have used it for one of my really complex automations, but I don’t want to make it over… again.

So… do you write Integrations now for your more complex automations? Or do you just write it as a bunch of automations and copy/paste it to reuse it?

How do you handle cases where a “binary_sensor” would be best, but can’t be encapsulated in a single template binary_sensor? Do you just use input_* helpers, or are you setting state some other way?

I’m not sure I follow you. But a rule of thumb that I have: If it creates bulk entities, it’s going to be an integration.

1 Like

Would you mind sharing the code for this?
I am trying to get this right with Appdaemon forever without success. It works almost 99% but the last 1% is not possible.

Here is the code as an integration. I haven’t finished documenting it, so, please look in .devcontainer/configuration.yaml for an example of how to use it.

Or did you mean share the pyscript code? I can do that too, it’s just not packaged up on github yet, but I can certainly do that if you’d prefer it.

Anyone know how to pass data to the notify function in pyscript ? I can’t find it documented anywhere…

For example
notify.mobile_app_xxxx(title=“notification title”, message=“bla bla”)

How to add
TTL: 0
Priority: high
Image: path…
Thx

Can you be more specific? I’m not sure what notify function you mean.

Edited. Thanks

I’m not familiar with this service call. How do you pass those parameters if you call the service from the UI or yaml?

I’m guessing that they are in an additional parameter called data, which is likely a dict. So try something like this:

notify.mobile_app_xxxx(title=“notification title”, message=“bla bla”, data={"TTL": 0, "Priority": "high"})
1 Like

Yep that works. I found out at the same time by trying different parameters^^
Thanks !

I had to comment out the state_active check for this to work again. Has something changed lately regarding script states?

@state_trigger("binary_sensor.hoveddor == 'off' and binary_sensor.hoveddor.old == 'on'")
#@state_active("script.skann_etter_telefoner == 'off'")
def maindoor1():
    script.skann_etter_telefoner.turn_on()
    log.info("Skann etter telefoner")

Nothing has changed in pyscript regarding this. And, as best as I can tell, nothing has changed in Home Assistant regarding the states of script entities.