I have a wireless wall panel switch with 3 buttons. I want to use this to control lights in Home Assistant. Each button toggles a light through an automation.
light instances work fine out of the box, because you can use
light.toggle service on them. This allows you to use a single button to both turn on and turn off the light. Unfortunately,
mqtt_json lights do not support the toggle feature. The solution is to either use two buttons (one for “turn on” one for “turn off”), which would waste a button mapping, or we can write automations that handle the state of the
mqtt_json light and imitate a
toggle service. I had considered this, however, it would create a set of really bad automations whose sole purpose is to imitate a toggle service.
Toggle LED strip (MQTT JSON light)
So I did just that… for sake of simplicity and just getting it to do what I want it to do. The following are all scripts related to
tv_led which is the
mqtt_json light associated with the LED strip behind my TV unit. There are 5 automations:
- Dim light
- undim light
- quick fade in
- quick fade out
- toggle between 3 and 4
fade_out_strip: alias: fade out strip sequence: - service: light.turn_on data: entity_id: light.tv_led white_value: 255 brightness: 55 rgb_color: [0,0,0] transition: 5 fade_in_strip: alias: fade in strip sequence: - service: light.turn_on data: entity_id: light.tv_led white_value: 255 brightness: 200 rgb_color: [0,255,0] transition: 2 tv_fade_in_quick: alias: tv fade in quick sequence: - service: light.turn_on data_template: entity_id: light.tv_led white_value: 255 brightness: 255 rgb_color: [255,255,255] transition: 1 tv_fade_out_quick: alias: tv fade out quick sequence: - service: light.turn_on data: entity_id: light.tv_led white_value: 0 brightness: 255 rgb_color: [0,0,0] transition: 1 - delay: "00:00:05" - service: light.turn_off entity_id: light.tv_led tv_toggle_quick: sequence: - service_template: > script.tv_fade_in_quick
Why I dislike this solution
I have a few problems with this setup:
- the solution does not scale, add a second
mqtt_jsonlight (like I have on my bed’s headboard) and you have to copy all 5 automations (or come up with ludacrous templates that take parameters, and add that same parameter filth to all automations that use them)
- the service parameters (and yaml keys) are duplicated across all 4 automations. The YAML format, by its key-value nature, can cause a lot of overhead in the way automations must be defined. Each automation above duplicates the
light.turn_onservice call. The only thing that diffes is the actual values passed in.
I wish there was a way to define a function that takes these values such as brightness and entity_id as a parameter and calls the
light.turn_on service using the supplied parameters. We could then define light-weight wrapper functions for automations number 1, 2, 3 and 4 and pass in the entity_id to be controlled. Similarly, automation 5 could control any light using one of the afforementioned functions in its if-else blocks and taking a parameter itself to pass along. That would be infinitely scalable as those functions can be reused for all led strips, supplying a different parameter each time.
I suppose this is the tradeoff we have to live with given the relative of ease of use of the YAML format compared to typed programming languages.
Define Programming Interfaces for components
We have a number of components that act like “switches” such as
binary_sensors and regular
switches. The problem with those is that they contain state, and need to be reset to off in order to trigger automations1 This creates a problem: When are they to be reset to off? You might say at the end of your automation that uses them.
I think that is really bad design because it means every single automation that uses this switch has to remember to set it back to off. It should not be the automations responsibility to main the state of the switch.
The feature I am proposing is an “
interface” component (or whatever the final name may be). I am talking about programming interfaces and, for the love of all that compiles, not GUI interfaces.
interface does not have a state like
off. All it represents is an abstraction of another component. Think of it like a variable that stores a more complex entity.
For example Say you have a complex MQTT trigger for motion sensors, which contains a specific topic, some more configuration and lots of detail specific to how the trigger works. Now let’s say you need to use that trigger in 2 or 3 different automations. This means you have to copy and paste the trigger into all automations, meaning you now have the same trigger defined in 3 different locations.
This is not good. Any time you find yourself copy and pasting in programming means you are probably violating the DRY principle.2
Rather than copying that trigger to X different automations (and duplicating information such as MQTT topic, payload etc), this proposed
interface component would enable you to create a single
interface containing the specific trigger information. And then you may duplicate the relatively simple interface into multiple locations.
You would then be able to use a reference to this
interface as a trigger in other automations. In other words, all your X automations refer to the same
interface which in turn contains the trigger.
We can extend this idea to automation actions and conditions as well. Have a set of really complex conditions that is better maintained in a central location? Create an
interface for each and refer to that named
interface in your automations.
Or consider the
interface below, which abstracts the
light.turn_on srvice from the previous section.
interfaces: set_strip_color: parameters: - name: colour default: [150,0,0] - name: transition_time default: 2 - name: entity required: true reference: service: light.turn_on data: entity_id: white_value: 255 brightness: 200 rgb_color: transition:
We would then be able to call this
interface in other automations, passing in the parameters.
# left out other automation parts. actions: - interface: set_strip_color paramaters: - entity: light.tv_led - colour: [100,100,0]
Note that this is not a Home Assistant feature. I am proposing this functinality. Don’t break your configuration by attempting to try this out :P
In summary, an
interface is a way to store a complex
condition, etc and reuse it easily. It might help to rename
function depending on the use case.
I learned a few months after wwriting this post about AppDaemon. I highly recommend you check it out if you find yourself restricted by HA’s YAML format. It provides a Python based way to define automations.