-
Notifications
You must be signed in to change notification settings - Fork 55
entities platforms (sensor, binary_sensor, switch) #129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Syntax for sensor and binary_sensor:
|
Syntax for switch: This is a very simple switch that just turns on when you ask it to turn on, and off when you ask it to turn off (basically, it's an input_boolean):
ast_ctx is being passed around to handle the service calls. maybe there is a better way? |
The following pyscript does 3 things:
|
On Device Management... Adding an entity to a device is quite simple. However, if that entity goes away, the device still stays in Home Assistant with no easy way to remove it. Device cleanup is already cumbersome in Home Assistant. Add to that that we don't know which devices are still in use until after all pyscripts have been evaluated and it gets really hard. We either need to grab a list of devices from Home Assistant, wait some time after startup for all entities to register, and then iterate over the device list and remove those no longer in use or, we need to be more explicit about which devices and entities we create (some kind of manifest alongside the pyscript that gets read first and handled before the pyscript loads). So, I'm going to hold off on handling devices for now. I'll add some attributes to the entity so that, even if a user changes the entity id, they'll always be able to find the pyscript that is creating it. |
Just starting to look at this. This is definitely outside my understanding of HASS, so I need to learn some more before I can offer useful input. Does this address #120 too? |
It does not address #120 directly. But it provides the features pyscript would need to address it (specifically the "switch" platform). To fully support #120, we'd then need a decorator ( With this PR, though, someone could implement automation_switch = em.get('switch','my_automation')
@automation_switch.on_turn_on
def automation_on(entity, **kwargs):
entity.set('on')
@automation_switch.on_turn_off
def automation_off(entity, **kwargs):
# Kill it if it's running already
task.unique('doit')
entity.set('off')
@state_trigger('input_boolean.test_1 == "on"')
@task_unique('doit')
def doit():
if automation_switch.state == "off":
log.info('not doing it')
return
#alternate implementation to show entity_ids at work
automation_switch_entity_id = automation_switch.entity_id
if state.get(automation_switch_entity_id) == "off":
log.info('not doing it')
return
log.info('doing it')
task.sleep(15)
log.info('i did it') Outside of making switches, or anything that can have a service_call made against it (since they simply don't work correctly with The biggest Pro is that the entity_id is now managed by the user. If I make a pyscript App that introduces a new weather information source, for example, and I use state.set, I have to pick an entity_id to set -- The biggest Con to using this method, outside of jumping through the hoops to do it correctly, is that you don't know what the entity_id of something is unless you ask for it, and it can change. So, if your pyscript app creates an entity that is also then used by your pyscript app somewhere else, you're going to have some trouble and more hoops to jump through. With This whole situation has to do with the way Home Assistant was designed and, if starting from scratch, I think I'd do it differently, but, I digress. For But, when it comes to entities that can have a service call against them ( The code is simple, once the pattern is understood: the objects have to expose certain methods that Home Assistant uses to do what it does (you can see these objects and methods in It should also be noted that Entities made this way CAN indicate their entity_id (and not use a unique id at all). Home Assistant will automatically append "_2", "_3", "_x" as needed if the entity_id already exists. This is the "old way" that everything in Home Assistant used to work, and it's still supported. But I figure, if we're going to do it, we may as well do it in the currently recommended way. Though, Native Automations even, that don't have an "id" set in them, work this way. So it's still used by the core in some places. |
Before you understand the HASS pieces of this, you can still provide input on the Python API for this. Right now, I've done it "simple", but it's not very "pyscript". It has one entry point:
To make it more
I am intentionally avoiding using Also, this API pattern breaks the ability to use
For instance, you can't do a |
This is all a bit confusing to me (speaking as someone who does have a decent understanding of how entities work) so I may be overstepping or not understanding correctly, but it seems like the problem you are trying to solve here is to be able to create an entity without all of the boilerplate code that HA requires in order for you to create a new integration. If I am understanding the problem correctly, the cleanest way to achieve this, IMO, is to:
Thoughts? |
in re-reading what you wrote, I think your |
@raman325 extending the more specific HomeAssistant Entity classes and using We could, instead, provide a It also makes things a bit more difficult to manage. I will also, likely, have to translate between the native python class that Home Assistant expects, and the 'pyscript' class that user defined classes actually look like internally. It might just be easier to write classes for the user to extend, and have the classes we send to Home Assistant just proxy to that user defined class. That way, the class Home Assistant sees never changes. So I guess it boils down to what is easier for the user. Do we make a "flask-style" decorator based interface like I have now? Or, do we made a class-based interface and proxy them through? Here's a simple "virtual switch" (basically, an input_boolean) implemented in the "flask-style" way: my_switch = em.get('switch', 'my_virtual_switch')
@my_switch.on_turn_on
def turn_on(entity):
entity.set('on')
@my_switch.on_turn_off
def turn_off(entity):
entity.set('off') And here's that same virtual switch implemented in what I imagine the end result will look like in a class-based way: class VirtualSwitch(PyscriptSwitch):
def __init__(self, unique_id):
self._unique_id = unique_id
def turn_on(self):
self.set('on')
def turn_off(self):
self.set('off')
my_switch = VirtualSwitch('my_virtual_switch')
em.register(my_switch) we could, of course, support both interfaces. It's just more work during initial code, documentation, support, and with future changes thoughts? |
I realize now, if we go the "class" route, we can save the user from having to deal with unique_id by providing a second stage class VirtualSwitch(PyscriptSwitch):
def turn_on(self):
self.set('on')
def turn_off(self):
self.set('off')
my_switch = VirtualSwitch('my_virtual_switch')
em.register(my_switch) |
Investigating further on using, for instance, It can be done. However, I think the resulting code is more complicated and with no benefit. Take, for example, one small piece of the whole puzzle: the There's nothing wrong with this, it's just more verbose than just extending |
As a Mixin, it was just too verbose. However, after looking over many other Home Assistant components, many of them simply inherit from both classes (the Home Assistant Entity that inherits from Entity, and their own Entity which also inherits from Entity). So, I did the same. Far less repetitive code that way, and everything still works as it did before. We still need to decide if we want a flask-like decorator interface, or a class-based interface inside of pyscript. And we need a variable name other than |
After a lot of thought on this, I think sticking with the decorator-style interface is the way to go. It's not as "native" feeling to seasoned Python users, but, it's more inline with the rest of pyscript, easier for beginners, and still fully usable in a class if you prefer to use classes. If it becomes cumbersome for advanced usage, we can always add a class-based interface later on. While it's a little verbose, I think "entity_get" is the best variable name to bind this to in pyscript: It indicates exactly what it does, and is unlikely to conflict with existing variables. |
I'm so pleased to have happened across this! I was going to drop an issue asking about something just like this. I've done some work with pyscript lately, enabled by the MQTT triggers that were recently added. Just some code to look for some MQTT messages with temperature and humidity data in them, dropping duplicate messages and creating humidity, temperature and battery sensors for each device. This is working pretty well! I show an earlier version of my code here https://community.home-assistant.io/t/a-new-approach-for-xiaomi-ble-temperature-sensors-with-esphome-mqtt-and-pyscript/267321 What was lacking is the ability to persist the entities over a Home Assistant restart, and also since the entities don't have an underlying "device", I can't assign them to areas (I think?) which might be useful. Right now, I recreate the entities on restart, though not until the first message arrives since I don't have the previous value lying around anywhere.. in the mean time, the Lovelace UI might look a little ugly until the entities are re-created. Not a huge problem in a practical sense.. It seems like this work might open the door to solving these problems, and that would be great! Using pyscript to do the development of an integration without having to restart Home Assistant during development would be amazing! FWIW, after having actually done my first "real" thing with pyscript, I think the decorator approach matches the feel and style of pyscript pretty well. It's compact and doesn't need you to drag in what looks like a bunch of boilerplate that the class-based approach feels like. The ability to do iterative and interactive development with pyscript is really wonderful, especially using the Jupyter Notebook tool to do so. It's just about magical! The ability to do interactive ad-hoc inspection of the code is very powerful, and I don't think people realize how powerful until they've done it once. The pyscript work is quite amazing and wonderful; thanks very much! |
@lmamakos thank you for the feedback, it's greatly appreciated. This PR has stalled a bit. For Until this is ready, look into pyscript's |
Another thought: when |
Yup I have not had much time to work on the automation switches:
I am with you that two methods is not the right path forward. I am OK with deferring to your approach. I also retract my vote against decorators - to @lmamakos 's point, they provide a good mechanism to iterate on entity development. You could achieve the same via a class (only implement the methods you care about, the rest should raise NotImplementedError) but given that this pattern already exists it makes sense that it should continue to be a pattern available to users. I still think there will be a need for a class based approach as well. The decorator approach is fine for one off entities, but if someone wants to publish a pyscript app as an alternative to a direct HA integration, they will need a way to dynamically create entities. You can achieve this via decorators and closures, but at some point you are doing a lot of work under the hood that is made significantly easier by just exposing a superclass and letting the user inherit from it and create instances of it. To the point about all of the boilerplate code, you could abstract a substantial amount of that away from most users, letting advanced users overload the built in logic where necessary. |
Last point on the subject and then I will drop it. I don't think we should go for achieving both right off the bat. But just keep that in mind when you design the |
@dlashua what all is needed to take this over the finish line? I may have some time coming up and can help with closing some of the gaps if you would be open to contributions |
@raman325 the last time I looked at it is was complete and working. The only outstanding issues were in regard to the design of the API which I was happy with, but wanted to be sure @craigbarratt and others who might use these features agreed with. At this point, it might need some cleanup since the pyscript core has likely changed, though, github seems to think there are no conflicts. |
Hi @dlashua, did you give up on this project? Is it possible to publish it as a pyscript app? |
@jrlacharnay It can't be a pyscript app, as it requires Home Assistant Setup functionality for each platform (switch, sensor, binary_sensor, climate, etc). It is still functionality I want to see in pyscript. The last I checked (over a year ago) it was working just fine. I'm not sure if the PR is still valid since pyscript has changed quite a bit since then. But you're welcome to look at the branch and make whatever changes are needed to get it in your local copy of pyscript. The only things left to do were to get agreement that that API I designed is what was desired, as there were several ways to go with it. |
This is a feature I would love to see. I am so happy to see other people think along the same lines as me. My development skills are probably outside the scope of creating this sadly. But looking through the thread, and seeing what you are discussing, I would still like to give my 5 cents, Hopefully it can perhaps trigger some new ideas or intrest in this feature. This is how i envision how this feature could look and work. I think it would fit well with the style of pyscript and how pyscript works because its a nice and clean approach with minimal code for the user, yet still very versitile and powerful. That is, if it could even be implemented this way. Example of a "custom pyscript" binary sensor, mimicing the suns position in the sky:
Doing it this way I think you would only have to create one "custom module" for the entire entity functionallity, one size fits all (except a little special with switches) where you as a user just choose type. The creation and the updating of the entities themself you let pyscript handle internally just like any other intigration. Just defining the diffrent platforms and how they binds with the "entity module". In most cases wouldnt that just be calling the decorated function and passing along the current state for updating? Then handling the retriving of the updated values in one of the ways discussed above. The only thing the user would do is define the function for the updating/execution behavior. Maybe this approach makes it more managable to implement and fits better with how intigrations works in home assistant? I am not sure. One would have to create the "entity module" and a few surrounding function for binding the updating and intializing of the entities. And then the decorator, exposing the function to the internal workings of the intigration. The entity type platforms could be done one at a time after that . No need to make it work for every single entity type all at once. I think one can get extremly far just having binary_sensor, sensor and switch working. Some final thoughts tho. As said earlier, the internal workings of pyscript is I think beyond the scope of my abillity and understanding as a devveloper. With that said, even I can see how this implementation would raise some security concernes. It could for instance treathen the HA server stability, if say an "entity factory" style of app got stuck in an infinite loop and wouldnt stop producing entities. Functionallity like this could be dangerous for inexperience and new users. And even pros can make error like this. To medigate that I had some final ideas for security features, to make this approach more safe :
Anyway. Sorry for a wall of text. If you would like to ball some ideas back and forth I am all ears. I can try to contribute in what ways I can aswell, if anyone is interested in reopening this. |
Implement Native Home Assistant Entities (through the Entity Registry) in Pyscript.
In comparison to a simple
state.set('domain.entity', value)
it offers several features:switch.turn_on
for a switch)If you'd prefer to not have this in pyscript, I can build it as a separate custom_component designed to integrate well with pyscript (though useful without it as well).
Needed:
em
during testing)EntityManager.wait_for_platform_register()
)Missing: