I consider that smart light switches have an amazing untapped potential, but maybe I’m just crazy…
- Switches are connected to mains electricity, so they are always powered
- They are ‘strategically’ placed all around our homes (so I’m thinking - access points / control nodes)
- Most important, everybody has them, so everybody knows how to use them (thinking about family or guests that are not used to control everything with their voice or from a computer/tablet/phone but already have the ‘muscle memory’ to use a light switch)
… So I wanted to do more than just toggle the lights from the light switches…absurd, right?
What I wanted?
Every time I write a script in HomeAssistant (that I consider it should be manually / physically triggered), it will be dynamically (due to a naming convention) added to a list/dropdown linked to a physical button of a light switch anywhere in the house, so pressing the button activates the script (since every appliance or function can be controlled by a script in HA, this gives the light switches the possibility to control anything in the house, software or hardware)
I was looking for a way to control my appliances and to be able to trigger my automations / scripts from physical access points - and this is what I came up with:
You should probably know that I’m using Xiaomi Aqara QBKG12LM, double gang, Zigbee switches running on local network only, with Zigbee2MQTT and CC2531 antennas, but you can use this with any smart switch (it will only have less options)
I chose those switches not just for their aspect, but for the fact they support double press events, both buttons pressed event and I believe they can also send a signal via MQTT for long press, but that also start pairing mode, so I will ignore that for now. They also have power and temperature integrated sensors.
Another great thing about those, is that you can decouple the relay on them so you can run them with smart bulbs without anyone being able to power off the bulb by mistake, but I won’t go into details about that now since this will probably be a huge post anyway.
First thing I did in order to get more from my switches was to install switches with more buttons than needed electrical connections.
For example, if I had a regular 1 gang switch, I replaced it with a 2 gang smart switch, so every one has a free button than can be programmed independently (work as a remote) and connected to homeassistant. So if I press a button (that has no electrical connection in the wall) the switch still sends that event to homeassistant and something happens (if HA is waiting for that certain event)
I’m using LEFT switch to toggle the light (electrical connection) and the RIGHT button to activate a smart plug at DOUBLE click and the jack bottle at Single click…nothing special yet.
This already gives me a way to trigger scripts and automations from physical interaction…but it is not enough; I want to be able to dynamically change what every button from every switch does.
Oonly using the right switch, from a 2 gang switch
There is one more thing I need to solve before I’m satisfied with this implementation; since I have around 10 light switches, I don’t want to manually link scripts to every one of them, every time I make a new script - I want the scripts that can / should be triggered by switches to be automatically exposed and attributed to the certain switch.
So, ideally, when I write a new script, if I decide it should be triggered by a light switch, all I have to do is to make it ‘special’ somehow (start it’s name with “enhanced_”, in my case) and then it will be automatically added to a dropdown of actions / scripts attributed to a certain switch.
Most of the work was done to make the interface clean, while having the option to chose from dozens of scripts for every switch.
I managed to have the main interface simple and clean, with cards that only have 2 buttons plus the homeplan area control, so if this is displayed on a tablet on the wall it looks simple enough, but hidden behind a long press is this absurd implementation, so no one can play with it or change it…i hope…i think…maybe…
Ok, so for those of you interested in this madness, here is what you need to do:
- First you will need to generate a Long-Lived Access Token. Go to http://your.local.homeassistant.ip:8123/profile, scroll down to the bottom, and under “Long-Lived Access Tokens” you will be able to “CREATE TOKEN”. (copy it somewhere, I keep it in secrets.yaml)
This will be required, because, in order to populate an input_select, we will need to make an API call from local homeassistant to…local homeassistant, otherwise yaml / jinja will return a string instead of a list and the input_select will only have one element - the list as a string.
In your configuration, you will have:
- rest_command:
enhanced_master:
url: "http://localhost:8123/api/services/input_select/set_options"
method: POST
headers:
authorization: !secret enhanced_authorization
content-type: application/json
payload: >
{
"entity_id": "input_select.master_enhanced",
"options":
[
{%- for state in states.script %}
{%- if state.entity_id.startswith("script.enhanced") %}
"{{ state.entity_id}}"
{%- if not loop.last %},{%- endif %}
{%- endif %}
{%- endfor %}
"INACTIVE"
]
}
The enhanced_master (rest command) is going through the entire sensor domain and populates a single input_select with all the scripts that match the naming convention, the scripts that are named enhanced_something.
also under rest command:
enhanced_update:
url: "http://localhost:8123/api/services/input_select/set_options"
method: POST
headers:
authorization: !secret enhanced_authorization
content-type: application/json
payload: >-
{
"entity_id":
{%- if is_state("input_select.aqara_office_right_single", "MASTER") %}
"input_select.aqara_office_right_single",
{%- elif is_state("input_select.aqara_office_right_double", "MASTER") %}
"input_select.aqara_office_right_double",
{%- elif is_state("input_select.aqara_office_left_double", "MASTER") %}
"input_select.aqara_office_left_double",
{%- elif is_state("input_select.aqara_office_both", "MASTER") %}
"input_select.aqara_office_both",
{%- endif %}
"options":
[
{%- for option in states.input_select.master_enhanced.attributes.options%}
"{{states.input_select.master_enhanced.attributes.options[(loop.index0)] |replace('script.enhanced_', '') |replace('_', ' ') |title}}"
{%- if not loop.last %},{%- endif %}
{%- endfor %}
]
}
Note that I’m only showcasing a single switch here (the one in the office), so under enhanced_update rest command, you will need to continue the “entity_id” template for EVERY smart switch in your home.
The reason I have 4 different “if_state” conditions is simply because my switches are double gang supporting double click actions plus both buttons pressed at once. I don’t have a “_left_single” in the example because the single press of the left button is connected to the lights (the electrical connection), so that doesn’t need to do anything else.
The bottom part of the command will populate the dropdown of the selected switch with entities that are readable by human eyes, so something that is named “script.enhanced_christmas_tree” will appear as “Christmas Tree” in the dropdown.
The REST commands will only make available the usable scripts for our switches; to get some action we need to move to
- automations.yaml:
- id: enhanced_aqara_master
alias: ENHANCED AQARA MASTER
initial_state: on
trigger:
- platform: homeassistant
event: start
action:
- service: rest_command.enhanced_master
This first automation is only here to populate the main list with usable scripts at the start of homeassistant; it is not crucial.
- id: enhanced_aqara_selector_close
alias: Aqara Selector Auto-Close
initial_state: on
trigger:
- platform: state
entity_id:
- input_boolean.aqara_office_right
- input_boolean.aqara_office_left
to: 'on'
for: "00:00:10"
action:
- service: input_boolean.turn_off
data_template:
entity_id: "{{ trigger.entity_id }}"
This second automation is only here as a ‘quality of life’ thingie…it will automatically hide the dropdown 10 seconds after it was opened so the UI returns to it’s clean, simple state. This is not crucial and is only useful if you want to implement this just like me.
- id: enhanced_aqara_office
alias: "AQARA office ENHANCED"
initial_state: on
trigger:
- platform: mqtt
topic: 'zigbee2mqtt_ac/aqara_office'
action:
- service: script.turn_on
data_template:
entity_id: >-
{% if ("right_single" == trigger.payload_json.click) %}
{{ 'script.enhanced_' + states.input_select.aqara_office_right_single.state | replace (' ', '_') |lower}}
{% elif ("right_double" == trigger.payload_json.click) %}
{{ 'script.enhanced_' + states.input_select.aqara_office_right_double.state | replace (' ', '_') |lower}}
{% elif ("left_double" == trigger.payload_json.click) %}
{{ 'script.enhanced_' + states.input_select.aqara_office_left_double.state | replace (' ', '_') |lower}}
{% elif ("both_single" == trigger.payload_json.click) %}
{{ 'script.enhanced_' + states.input_select.aqara_office_both.state | replace (' ', '_') |lower}}
{% else %}
script.enhanced_inactive
{% endif %}
This (3rd one) is the important one. This will trigger the selected script when you push the light switch button and need to be copy-paste-adapted for EVERY switch in the home.
I apologize for wasting the time of those who don’t need redundant explanation of the code, but I’m trying to make this as clear as I can for everybody and since I’m not a programmer myself I know how big chunks of unexplained code can be discuraging.
I started with the rest commands because this is the logic of this implementation and since probably most people reading this already know where I’m going with it, this should be enough to get it working.
For the rest of us, this goes on…
In order for the previous blocks of code to work we need to declare the (already used above) input_select entities; so we are moving to
- input_select:
master_enhanced:
name: MASTER
options:
- INACTIVE
icon: mdi:light-switch
This is the main input_select (dropdown) that will be populated by the rest commands, so you can leave it as it is.
Now, continuing under input_select, this needs to be copy-pasted-adapted for EVERY switch in the house; in this example, I’m only showcasing the office switch:
aqara_office_right_single: # RIGHT SIDE - SINGLE CLICK
name: SINGLE CLICK - office
options:
- INACTIVE
- MASTER
icon: mdi:light-switch
aqara_office_right_double: # RIGHT SIDE - DOUBLE CLICK
name: DOUBLE CLICK - office
options:
- INACTIVE
- MASTER
icon: mdi:light-switch
# RIGHT SIDE - SINGLE CLICK - POWERED
aqara_office_left_double: # LEFT SIDE - DOUBLE CLICK
name: DOUBLE CLICK - office
options:
- INACTIVE
- MASTER
icon: mdi:light-switch
aqara_office_both: # BOTH SIDES - SINGLE CLICK
name: BOTH CLICKED - office
options:
- INACTIVE
- Christmas Tree
- MASTER
icon: mdi:light-switch
The options you add here will be selectable without calling the REST command. It’s basically a default mode for every switch, considering that in certain cases you may only want to be able to trigger one or two scripts and not see a full list of dozens of available scripts.
For example the “- Christmas Tree” option presented here will trigger (if selected) and be equivalent to a script called “script.enhanced_christmas_tree”
At this point the implementation is pretty much operational, so the following is mostly related to the way I wanted to build the UI for it; the next parts are required for the visual interface.
- input_boolean:
aqara_office_right:
name: office Right
initial: off
aqara_office_left:
name: office Left
initial: off
This is required for EVERY switch in the house. This is responsible for displaying/hiding the menu for this implementation since it will be linked as a conditional in the lovelace.yaml
- script:
enhanced_reset:
sequence:
- service: input_select.reload
- service: rest_command.enhanced_master
This script first service sill clear every input_select and reset it to default options. The second one will populate enhanced_MASTER after the reset, to have the ‘MASTER’ option working again on all input_select
# office
office_right_selector:
sequence:
- service: input_boolean.toggle
entity_id: input_boolean.aqara_office_right
- service: input_boolean.turn_off
entity_id: input_boolean.aqara_office_left
office_left_selector:
sequence:
- service: input_boolean.toggle
entity_id: input_boolean.aqara_office_left
- service: input_boolean.turn_off
entity_id: input_boolean.aqara_office_right
Those 2 scripts will be used by lovelace.yaml to display the dropdown menus and you will need to copy-pase-adapt them for EVERY switch in the house.
Bonus (which means completely useless) scripts to test your integration:
# TEST
enhanced_test_one:
sequence:
- service: tts.cloud_say
entity_id: media_player.office_assistant
data:
message: 'Test 1'
# TEST
enhanced_test_two:
sequence:
- service: tts.cloud_say
entity_id: media_player.office_assistant
data:
message: 'Test 2'
# TEST
enhanced_test_three:
sequence:
- service: tts.cloud_say
entity_id: media_player.office_assistant
data:
message: 'Test 3'
# TEST
enhanced_test_four:
sequence:
- service: tts.cloud_say
entity_id: media_player.office_assistant
data:
message: 'Test 4'
And if for some reason you are still here and want to check the lovelace…here you go
- lovelace.yaml:
# ________________________________ AQARA office
- type: horizontal-stack
cards:
- type: conditional # LEFT SIDE SELECTOR
conditions:
- entity: input_boolean.aqara_office_left
state: "on"
card:
type: entities
show_header_toggle: false
entities:
- entity: input_select.aqara_office_left_double
- entity: input_select.aqara_office_both
- type: glance ### AQARA office SWITCH ###
show_state: false
show_name: true
theme: shadesofgrey_dimmed
entities:
- entity: switch.aqara_office_left # LEFT Switch
icon: mdi:light-switch
tap_action:
action: toggle
double_tap_action:
action: call-service
service: rest_command.enhanced_update
hold_action:
action: call-service
service: script.office_left_selector
name: office
- entity: switch.aqara_office_right # RIGHT Switch
icon: mdi:light-switch
tap_action:
action: toggle
hold_action:
action: call-service
service: script.office_right_selector
double_tap_action:
action: call-service
service: rest_command.enhanced_update
name:
- type: conditional # RIGHT SIDE SELECTOR
conditions:
- entity: input_boolean.aqara_office_right
state: "on"
card:
type: entities
show_header_toggle: false
entities:
- entity: input_select.aqara_office_right_single
- entity: input_select.aqara_office_right_double
- type: conditional # AQARA office STATS
conditions:
- entity: sensor.aqara_office_power
state_not: "unknown"
- entity: sensor.aqara_office_power
state_not: "0"
# - entity: switch.aqara_office_left
# state_not: "off"
card:
type: horizontal-stack
cards:
- type: horizontal-stack
cards:
- type: gauge
name: " "
theme: split_theme
entity: sensor.aqara_office_temperature
severity:
green: 0
yellow: 45
red: 60
- type: gauge
name: " "
theme: split_theme
entity: sensor.aqara_office_power
severity:
green: 0
yellow: 60
red: 100
- type: sensor
entity: sensor.aqara_office_consumption
name: Energy consumption
theme: split_theme
icon: mdi:power-plug
graph: line
Needless to say, this needs to be adapted and used for EVERY switch in the house.
I implemented this in my home almost 1 year ago and I wanted to share it ever since but I knew it will be a very long post and I never had the time or will do start doing it, but I used this since then and it actually makes the entire home feel a lot smarter (…and it gives a purpose to smart switches).
I hope you can enjoy this at least as much as I do. Have fun!