Automation 'integrations' or types

I’m not sure if I’m just struggling with this, but I find home assistant’s approach to automation cumbersome and it took me quite long to get used to. As most people it seems, I’ve resorted to alternatives like node-red (ditched it in the end, too complex for many simple applications) and currently appdaemon. I would really like to have as much as possible everything done within home assistant, preferably using the interface.

After thinking about it for a while I think I have a proposal that could solve these issues while still keeping with the home assistant core concept: Create/allow different generic types of automations, just like we have different integrations. The current automation would than just be the ‘generic’ type.

A simple example would be a button automation. In the automation editor you’d select the type as button, select the entity used as button and what should happen when it fires. It mostly narrows the scope of what the automation is supposed to do and might enable a better interface. For instance more focussed on multiple effects for the same button depending on different states. It basically tells you something about what the user is trying to achieve in broad terms. I have something like this currently working in appdaemon (obviously only for my use cases). In general it took me way too long to figure out how to get a wireless button working…

What I’m trying to get accross is that I now have several common automations in appdaemon that take quite a bit of effort imo to implement for each automation in hass, but only takes 2 entity_ids or so to implement in appdaemon using my generic code.
There currently is no way for me to implement the common approach in hass and give back to the community what I think is valuable code.

To give another example, I have some generic code in appdaemon that has entities mirror each other states, be it a switch, light or binary sensor. It can also take certain conditions and it can be either both way or one way. It is used to switch on the lights inside a cabinet, but only if noone is in bed, and switch them off again if the cabinet closes. In home assistant this would be 2 seperate automations and would miss critical logic (sometimes the signal is ignored/delayed, so you want to a period where you recheck and retry).
I used the same automation to have two lights mirror each other under certain conditions.

Hopefully this isn’t too rambling. I’ve seen the automations finally get some love in recent updates and the device page seems a step in the right direction, so I figured it was time for my 2 cents.

There are possibilities to share your code either through a custom component or even as an AppDaemon app through HACS.

Yeah, I guess I was unclear. I didn’t mean to imply there is no way to do it, just that doing so isn’t native to home assistant and therefore not for all users (and often a bit awkward).

I think it’s easier and more likely that your ideas get integrated into core, if you create a custom component first. Then you’ll see the demand for this and in case the demand is really high and your custom component gets used by a lot of people, it will be more likely that it will be integrated into core at some point.

Would you mind sharing your AppDaemon apps? I’d love to take a look at these.

I agree with that, but unless I completely misunderstand how things work, there isn’t really any way for me to make a custom automation component is there?

I think the most interestting one is the mirror state (everything is in dutch I’m afraid):

import appdaemon.plugins.hass.hassapi as hass

class MirrorSource(hass.Hass):
    def initialize(self):
        if 'target_lamp' in self.args and 'source_switch' in self.args:
            self.listen_state(self.spiegel_switch_lamp, self.args['source_switch'])
        elif 'target_switch' in self.args and 'source_switch' in self.args:
            self.listen_state(self.spiegel_switch_switch, self.args['source_switch'])
        elif 'target_lamp' in self.args and 'source_lamp' in self.args:
            self.listen_state(self.spiegel_lamp_lamp, self.args['source_lamp'])
        elif 'target_switch' in self.args and 'source_lamp' in self.args:
            self.listen_state(self.spiegel_lamp_switch, self.args['source_lamp'])

    def spiegel_switch_lamp(self, entity, attribute, old, new, kwargs):
        if self.get_state(self.args['target_lamp']) != new: 
            self.toggle(self.args["target_lamp"])
        
    def spiegel_switch_switch(self, entity, attribute, old, new, kwargs):
        if self.get_state(self.args['target_switch']) != new:
            self.toggle(self.args["target_switch"])
            
    def spiegel_lamp_lamp(self, entity, attribute, old, new, kwargs):
        self.spiegel_switch_lamp(entity, attribute, old, new, kwargs)
            
    def spiegel_lamp_switch(self, entity, attribute, old, new, kwargs):
        self.spiegel_switch_switch(entity, attribute, old, new, kwargs)
            
class MirrorBoth(hass.Hass):
    def initialize(self):
        if 'target_lamp' in self.args and 'source_switch' in self.args:
            self.listen_state(self.spiegel_switch_lamp, self.args['source_switch'])
            self.listen_state(self.spiegel_switch_lamp, self.args['target_lamp'])
        elif 'target_switch' in self.args and 'source_switch' in self.args:
            self.listen_state(self.spiegel_switch_switch, self.args['source_switch'])
            self.listen_state(self.spiegel_switch_switch, self.args['target_switch'])
        elif 'target_lamp' in self.args and 'source_lamp' in self.args:
            self.listen_state(self.spiegel_lamp_lamp, self.args['source_lamp'])
            self.listen_state(self.spiegel_lamp_lamp, self.args['target_lamp'])
        elif 'target_switch' in self.args and 'source_lamp' in self.args:
            self.listen_state(self.spiegel_lamp_switch, self.args['source_lamp'])
            self.listen_state(self.spiegel_lamp_switch, self.args['target_switch'])

    def spiegel_switch_lamp(self, entity, attribute, old, new, kwargs):
        if entity == self.args['source_switch']:
            if self.get_state(self.args['target_lamp']) != new: 
                self.toggle(self.args["target_lamp"])
        if entity == self.args['target_lamp']:
            if self.get_state(self.args['source_switch']) != new: 
                self.toggle(self.args["source_switch"])
           
    def spiegel_switch_switch(self, entity, attribute, old, new, kwargs):
        if entity == self.args['source_switch']:
            if self.get_state(self.args['target_switch']) != new: 
                self.toggle(self.args["target_switch"])
        if entity == self.args['target_switch']:
            if self.get_state(self.args['source_switch']) != new: 
                self.toggle(self.args["source_switch"])
                
    def spiegel_lamp_switch(self, entity, attribute, old, new, kwargs):
        if entity == self.args['source_lamp']:
            if self.get_state(self.args['target_switch']) != new: 
                self.toggle(self.args["target_switch"])
        if entity == self.args['target_switch']:
            if self.get_state(self.args['source_lamp']) != new: 
                self.toggle(self.args["source_lamp"])
                
    def spiegel_lamp_lamp(self, entity, attribute, old, new, kwargs):
        if entity == self.args['source_lamp']:
            if self.get_state(self.args['target_lamp']) != new: 
                self.toggle(self.args["target_lamp"])
        if entity == self.args['target_lamp']:
            if self.get_state(self.args['source_lamp']) != new: 
                self.toggle(self.args["source_lamp"])

And it is used like this:

garderobekastlicht:
  module: mirror_state
  class: MirrorSource
  source_switch: binary_sensor.garderobekast_deur
  target_switch: switch.plug_garderobekast_switch
  constrain_input_boolean: binary_sensor.time_bed,off

The mirrorBoth is imo the most interesting, but I’m not using it anymore at the moment. It was used to sync a switch that was tied to the cabinetlights. If the lights would come on/off by some automation, the switch would also change state (it was a touch sensor with an LED for the state). And obviously, flipping the switch would change the light.

And an example of a simple automation is buttons exposed by deconz:

import appdaemon.plugins.hass.hassapi as hass

class DeconzButton(hass.Hass):

    def initialize(self):
        if 'multiple_service' in self.args:
            self.listen_event(self.handle_event_multiple, 'deconz_event', id = self.args['id'])
        else:
            self.listen_event(self.handle_event, 'deconz_event', id = self.args['id'])

    def handle_event(self, event_name, data, kwargs):
        if data['event'] == 1002: # Single press
            self.call_service(self.args['single_service'], **self.args['single_data'])
        elif data['event'] == 1004: # Double
            self.call_service(self.args['double_service'], **self.args['double_data'])
        elif data['event'] == 1005: # Triple
            self.call_service(self.args['triple_service'], **self.args['triple_data'])
                
    def handle_event_multiple(self, event_name, data, kwargs):
        if data['event'] == 1002: # Single press
            self.call_service(self.args['single_service'], **self.args['single_data'])
        elif data['event'] == 1004: # Double
            self.call_service(self.args['multiple_service'], **self.args['multiple_data'])
            
class DeconzButton_double(hass.Hass):

    def initialize(self):
        self.listen_event(self.handle_event, 'deconz_event', id = self.args['id'])

    def handle_event(self, event_name, data, kwargs):
        if data['event'] == 1002: # Left press
            self.call_service(self.args['left_service'], **self.args['left_data'])
        elif data['event'] == 2002: # Right press
            self.call_service(self.args['right_service'], **self.args['right_data'])
knop_slaapkamer:
  module: deconz_button
  class: DeconzButton
  id: slaapkamer_knop
  single_service: scene/turn_on
  single_data: {"entity_id": "scene.alles_uit_slaapkamer"}
  multiple_service: scene/turn_on
  multiple_data: {"entity_id": "scene.alles_aan_slaapkamer"}

dubbele_knop_bank:
  module: deconz_button
  class: DeconzButton_double
  id: aqara_switch2
  left_service: light/toggle
  left_data: {"entity_id": "light.stalamp_bank"}
  right_service: light/toggle
  right_data: {"entity_id": "light.tv_dimmer_level"}
  
knop_eettafel:
  module: deconz_button
  class: DeconzButton
  id: woonkamer_knop
  single_service: light/toggle
  single_data: {"entity_id": "light.eettafel_dimmer_level"}
  double_service: light/turn_on
  double_data: {"entity_id": "light.eettafel_dimmer_level", "brightness_pct" : 100}
  triple_service: light/turn_on
  triple_data: {"entity_id": "light.eettafel_dimmer_level", "brightness_pct" : 1}

Obviously this is also not hard per se in home assistant itself, but I think this abstracts it better for the user. I think buttons might be solved by the device implementation though.

For your button automations, I think you are right that it will probably will be obsolete really soon with the progress they make in the device implementation.

You can make your own custom component to implement the light mirror app or any other app. To be honest I don’t understand your feature request and what you mean with different type of automations. You can create all the automations you think are useful for other people with custom components, a good example is Schedy

Looking at your link, schedy is anothet appdaemon app, isn’t it? Basically what I’m proposing is a way to improve automations in home assistant itself.

I think I might be able to build it as a component (I’ve only got limited experience working on the nzbget component), but to me that seems like a hack and not very intuitive. Searching around I also can’t find any component, custom or otherwise, that really tackles this issue. And that is kinda where my point comes from, I have the impression that the current implementation is preventing many possible solutions due to a high barrier of entry.

Basically what I’m suggesting is an abstraction like we have with integrations/components, but for automations. Keeping the concept of entities and automations strictly separate. We have abstracted binary sensors in their own category, I think we should be able to do the same for automations (which is why I picked buttons as my example).

Ohhh you are right, I was thinking of another app that was an appdaemon app first and then the developer turned it into a custom component, but I can’t remember the name, sorry.

The thing is, I don’t think the developers will completely change the automation core, but maybe if you’re lucky you’ll find someone that will implement it, but for this the feature request has to be way more specific in my opinion.

Your idea doesn’t sound so bad in general, I just don’t know how much effort it is to implement this into core.

I can’t disagree that it would be a pretty big change and that it isn’t very specific. I primarily wanted to have people start thinking about this differently, since I didn’t see any inclination that people even think there’s something wrong with the current implementation (it does everything you’d want in theory).

1 Like

I don’t know if a feature request can be closed, but I think blueprints capture this issue better than I could’ve imagined :+1: