Ok, here is the solution (so far) that does not (yet) use the toggle function. The plan I have is to allow the button to also lock/unlock the door - IF the door is closed.
First, you need the following for each of your doors to build the combined sensor:
- platform: template
sensors:
front_door_status_sc:
friendly_name: "Front Door Status Test"
unique_id: front-door-status-9ff7b07a-9d76-48aa-99f8-931126715014
value_template: >-
{% if ( (is_state('lock.front_door','unlocked')) and (is_state('binary_sensor.door_front_entry_window_door_is_open','off')) ) %}
Unlocked
{% elif ( (is_state('binary_sensor.door_front_entry_window_door_is_open','on')) and (is_state('lock.front_door','locked')) ) %}
Locked Open
{% elif ( (is_state('binary_sensor.door_front_entry_window_door_is_open','off')) and (is_state('lock.front_door','locked')) ) %}
Locked
{% elif ( (is_state('binary_sensor.door_front_entry_window_door_is_open','on')) and (is_state('lock.front_door','unlocked')) ) %}
Open
{% else %}
Unknown
{% endif %}
icon_template: >-
{% if ( (is_state('lock.front_door','unlocked')) and (is_state('binary_sensor.door_front_entry_window_door_is_open','off')) ) %}
mdi:lock-open
{% elif ( (is_state('binary_sensor.door_front_entry_window_door_is_open','on')) and (is_state('lock.front_door','locked')) ) %}
mdi:door-closed-cancel
{% elif ( (is_state('binary_sensor.door_front_entry_window_door_is_open','off')) and (is_state('lock.front_door','locked')) ) %}
mdi:lock
{% elif ( (is_state('binary_sensor.door_front_entry_window_door_is_open','on')) and (is_state('lock.front_door','unlocked')) ) %}
mdi:door-open
{% else %}
mdi:lock-question
{% endif %}
Then you need an entities card in the GUI (I am using the css mod from above):
entities:
- card_type: horizontal-stack
cards:
- entity: sensor.front_door_status_sc_2
card_mod:
style: |
ha-card {
{% if states('sensor.front_door_status_sc_2') == 'Locked' %}
--card-mod-icon-color: green;
{% elif states('sensor.front_door_status_sc_2') == 'Unlocked' %}
--card-mod-icon-color: red;
{% elif states('sensor.front_door_status_sc_2') == 'Open' %}
--card-mod-icon-color: blue;
{% elif states('sensor.front_door_status_sc_2') == 'Closed' %}
--card-mod-icon-color: yellow;
{% else %}
--card-mod-icon-color: orange;
{% endif %}
}
tap_action:
service: lock.front_door
name: Front
template: menu_button
type: custom:button-card
If you do all your doors like this, you should see:
And each door shows the corresponding state and colours!
Next up… the toggle.
TOGGLE WORKING:
Here is the code for the GUI - no change to the sensor.
IMPORTANT PREREQUISITES
HACS: Add Ons Needed:
- button-card
- hui-element
- card-mod
You also need to build the Multi-States for your sensors (see above).
With this, you can click on the front door icon and lock, or unlock it with a press. The only drawback is that you can lock it while the door is open - the advantage is that you can actually see that state in the icons, so you can see you messed up:
entities:
- card_type: horizontal-stack
cards:
- entity: sensor.front_door_status_sc_2
card_mod:
style: |
ha-card {
{% if states('sensor.front_door_status_sc_2') == 'Locked' %}
--card-mod-icon-color: green;
{% elif states('sensor.front_door_status_sc_2') == 'Unlocked' %}
--card-mod-icon-color: red;
{% elif states('sensor.front_door_status_sc_2') == 'Open' %}
--card-mod-icon-color: blue;
{% elif states('sensor.front_door_status_sc_2') == 'Closed' %}
--card-mod-icon-color: yellow;
{% else %}
--card-mod-icon-color: orange;
{% endif %}
}
tap_action:
action: call-service
service: |
[[[
return (entity.state === 'Locked') ? 'lock.unlock' : 'lock.lock';
]]]
service_data:
entity_id: lock.front_door
name: Front
template: menu_button
type: custom:button-card
- entity: sensor.porch_door_status_sc
card_mod:
style: |
ha-card {
{% if states('sensor.porch_door_status_sc') == 'Locked' %}
--card-mod-icon-color: green;
{% elif states('sensor.porch_door_status_sc') == 'Unlocked' %}
--card-mod-icon-color: red;
{% elif states('sensor.porch_door_status_sc') == 'Open' %}
--card-mod-icon-color: blue;
{% elif states('sensor.porch_door_status_sc') == 'Closed' %}
--card-mod-icon-color: yellow;
{% else %}
--card-mod-icon-color: orange;
{% endif %}
}
tap_action:
action: call-service
service: |
[[[
return (entity.state === 'Locked') ? 'lock.unlock' : 'lock.lock';
]]]
service_data:
entity_id: lock.deck
name: Porch
template: menu_button
type: custom:button-card
- entity: sensor.breezeway_door_status_sc
card_mod:
style: |
ha-card {
{% if states('sensor.breezeway_door_status_sc') == 'Locked' %}
--card-mod-icon-color: green;
{% elif states('sensor.breezeway_door_status_sc') == 'Unlocked' %}
--card-mod-icon-color: red;
{% elif states('sensor.breezeway_door_status_sc') == 'Open' %}
--card-mod-icon-color: blue;
{% elif states('sensor.breezeway_door_status_sc') == 'Closed' %}
--card-mod-icon-color: yellow;
{% else %}
--card-mod-icon-color: orange;
{% endif %}
}
name: Breezeway
tap_action:
action: call-service
service: |
[[[
return (entity.state === 'Locked') ? 'lock.unlock' : 'lock.lock';
]]]
service_data:
entity_id: lock.breezeway
template: menu_button
type: custom:button-card
- entity: sensor.basement_door_status_sc
card_mod:
style: |
ha-card {
{% if states('sensor.basement_door_status_sc') == 'Locked' %}
--card-mod-icon-color: green;
{% elif states('sensor.basement_door_status_sc') == 'Unlocked' %}
--card-mod-icon-color: red;
{% elif states('sensor.basement_door_status_sc') == 'Open' %}
--card-mod-icon-color: blue;
{% elif states('sensor.basement_door_status_sc') == 'Closed' %}
--card-mod-icon-color: yellow;
{% else %}
--card-mod-icon-color: orange;
{% endif %}
}
name: Basement
tap_action:
action: call-service
service: |
[[[
return (entity.state === 'Locked') ? 'lock.unlock' : 'lock.lock';
]]]
service_data:
entity_id: lock.basement
template: menu_button
type: custom:button-card
- entity: sensor.garage_door_status_sc
card_mod:
style: |
ha-card {
{% if states('sensor.garage_door_status_sc') == 'Locked' %}
--card-mod-icon-color: green;
{% elif states('sensor.garage_door_status_sc') == 'Unlocked' %}
--card-mod-icon-color: red;
{% elif states('sensor.garage_door_status_sc') == 'Open' %}
--card-mod-icon-color: blue;
{% elif states('sensor.garage_door_status_sc') == 'Closed' %}
--card-mod-icon-color: yellow;
{% else %}
--card-mod-icon-color: orange;
{% endif %}
}
tap_action:
action: call-service
service: |
[[[
return (entity.state === 'Locked') ? 'lock.unlock' : 'lock.lock';
]]]
service_data:
entity_id: lock.garage
template: menu_button
name: Garage
type: custom:button-card
type: custom:hui-element
show_header_toggle: false
title: All Doors
type: entities
I was working on some zwave configs, and because of that, I was also able to verify the “unknown” state for these icons while the zwave was down:
Ok, I think I have what can be considered the solution. First, here is what the card looks like, I have it stacked with two rows. Also, each button will toggle and lock/unlock locks or open/close garages. The color and icon of each will change according to the state:
Here is the GUI side code to make that work:
entities:
- card_type: horizontal-stack
cards:
- entity: sensor.front_door_status_sc_2
card_mod:
style: |
ha-card {
{% if states('sensor.front_door_status_sc_2') == 'Locked' %}
--card-mod-icon-color: green;
{% elif states('sensor.front_door_status_sc_2') == 'Unlocked' %}
--card-mod-icon-color: red;
{% elif states('sensor.front_door_status_sc_2') == 'Open' %}
--card-mod-icon-color: blue;
{% elif states('sensor.front_door_status_sc_2') == 'Closed' %}
--card-mod-icon-color: yellow;
{% else %}
--card-mod-icon-color: orange;
{% endif %}
}
tap_action:
action: call-service
service: |
[[[
return (entity.state === 'Locked') ? 'lock.unlock' : 'lock.lock';
]]]
service_data:
entity_id: lock.front_door
name: Front
template: menu_button
type: custom:button-card
- entity: sensor.porch_door_status_sc
card_mod:
style: |
ha-card {
{% if states('sensor.porch_door_status_sc') == 'Locked' %}
--card-mod-icon-color: green;
{% elif states('sensor.porch_door_status_sc') == 'Unlocked' %}
--card-mod-icon-color: red;
{% elif states('sensor.porch_door_status_sc') == 'Open' %}
--card-mod-icon-color: blue;
{% elif states('sensor.porch_door_status_sc') == 'Closed' %}
--card-mod-icon-color: yellow;
{% else %}
--card-mod-icon-color: orange;
{% endif %}
}
tap_action:
action: call-service
service: |
[[[
return (entity.state === 'Locked') ? 'lock.unlock' : 'lock.lock';
]]]
service_data:
entity_id: lock.deck
name: Porch
template: menu_button
type: custom:button-card
- entity: sensor.breezeway_door_status_sc
card_mod:
style: |
ha-card {
{% if states('sensor.breezeway_door_status_sc') == 'Locked' %}
--card-mod-icon-color: green;
{% elif states('sensor.breezeway_door_status_sc') == 'Unlocked' %}
--card-mod-icon-color: red;
{% elif states('sensor.breezeway_door_status_sc') == 'Open' %}
--card-mod-icon-color: blue;
{% elif states('sensor.breezeway_door_status_sc') == 'Closed' %}
--card-mod-icon-color: yellow;
{% else %}
--card-mod-icon-color: orange;
{% endif %}
}
tap_action:
action: call-service
service: |
[[[
return (entity.state === 'Locked') ? 'lock.unlock' : 'lock.lock';
]]]
service_data:
entity_id: lock.breezeway
name: Breezeway
template: menu_button
type: custom:button-card
- entity: sensor.basement_door_status_sc
card_mod:
style: |
ha-card {
{% if states('sensor.basement_door_status_sc') == 'Locked' %}
--card-mod-icon-color: green;
{% elif states('sensor.basement_door_status_sc') == 'Unlocked' %}
--card-mod-icon-color: red;
{% elif states('sensor.basement_door_status_sc') == 'Open' %}
--card-mod-icon-color: blue;
{% elif states('sensor.basement_door_status_sc') == 'Closed' %}
--card-mod-icon-color: yellow;
{% else %}
--card-mod-icon-color: orange;
{% endif %}
}
name: Basement
tap_action:
action: call-service
service: |
[[[
return (entity.state === 'Locked') ? 'lock.unlock' : 'lock.lock';
]]]
service_data:
entity_id: lock.basement
template: menu_button
type: custom:button-card
type: custom:hui-element
- card_type: horizontal-stack
cards:
- entity: sensor.garage_door_status_sc
card_mod:
style: |
ha-card {
{% if states('sensor.garage_door_status_sc') == 'Locked' %}
--card-mod-icon-color: green;
{% elif states('sensor.garage_door_status_sc') == 'Unlocked' %}
--card-mod-icon-color: red;
{% elif states('sensor.garage_door_status_sc') == 'Open' %}
--card-mod-icon-color: blue;
{% elif states('sensor.garage_door_status_sc') == 'Closed' %}
--card-mod-icon-color: yellow;
{% else %}
--card-mod-icon-color: orange;
{% endif %}
}
tap_action:
action: call-service
service: |
[[[
return (entity.state === 'Locked') ? 'lock.unlock' : 'lock.lock';
]]]
service_data:
entity_id: lock.garage
template: menu_button
name: Garage
type: custom:button-card
- entity: sensor.garage_east_door_status_sc
card_mod:
style: |
ha-card {
{% if states('cover.garage_door_2') == 'open' %}
--card-mod-icon-color: red;
{% elif states('cover.garage_door_2') == 'closed' %}
--card-mod-icon-color: green;
{% else %}
--card-mod-icon-color: orange;
{% endif %}
}
tap_action:
action: call-service
service: cover.toggle
target:
entity_id: cover.garage_door_2
name: East
template: menu_button
type: custom:button-card
- entity: sensor.garage_west_door_status_sc
card_mod:
style: |
ha-card {
{% if states('cover.garage_door') == 'open' %}
--card-mod-icon-color: red;
{% elif states('cover.garage_door') == 'closed' %}
--card-mod-icon-color: green;
{% else %}
--card-mod-icon-color: orange;
{% endif %}
}
tap_action:
action: call-service
service: cover.toggle
target:
entity_id: cover.garage_door
name: East
template: menu_button
type: custom:button-card
- entity: sensor.garage_shop_door_status_sc
card_mod:
style: |
ha-card {
{% if states('cover.sgarage') == 'open' %}
--card-mod-icon-color: red;
{% elif states('cover.sgarage') == 'closed' %}
--card-mod-icon-color: green;
{% else %}
--card-mod-icon-color: orange;
{% endif %}
}
tap_action:
action: call-service
service: cover.toggle
target:
entity_id: cover.sgarage
name: Shop
template: menu_button
type: custom:button-card
type: custom:hui-element
show_header_toggle: false
title: All Doors
type: entities
Within my sensor.yaml I have created some entities that change to power the GUI component:
###############################################
# TEMPLATES For Door States and Garage States #
###############################################
- platform: template
sensors:
front_door_status_sc:
friendly_name: "Front Door Status"
unique_id: front-door-status-9ff7b07a-9d76-48aa-99f8-931126715014
value_template: >-
{% if ( (is_state('lock.front_door','unlocked')) and (is_state('binary_sensor.door_front_entry_window_door_is_open','off')) ) %}
Unlocked
{% elif ( (is_state('binary_sensor.door_front_entry_window_door_is_open','on')) and (is_state('lock.front_door','locked')) ) %}
Locked Open
{% elif ( (is_state('binary_sensor.door_front_entry_window_door_is_open','off')) and (is_state('lock.front_door','locked')) ) %}
Locked
{% elif ( (is_state('binary_sensor.door_front_entry_window_door_is_open','on')) and (is_state('lock.front_door','unlocked')) ) %}
Open
{% else %}
Unknown
{% endif %}
icon_template: >-
{% if ( (is_state('lock.front_door','unlocked')) and (is_state('binary_sensor.door_front_entry_window_door_is_open','off')) ) %}
mdi:lock-open
{% elif ( (is_state('binary_sensor.door_front_entry_window_door_is_open','on')) and (is_state('lock.front_door','locked')) ) %}
mdi:door-closed-cancel
{% elif ( (is_state('binary_sensor.door_front_entry_window_door_is_open','off')) and (is_state('lock.front_door','locked')) ) %}
mdi:lock
{% elif ( (is_state('binary_sensor.door_front_entry_window_door_is_open','on')) and (is_state('lock.front_door','unlocked')) ) %}
mdi:door-open
{% else %}
mdi:lock-question
{% endif %}
- platform: template
sensors:
porch_door_status_sc:
friendly_name: "Porch Door Status"
unique_id: porch-door-status-8ac9a06a-2d77-48aa-99f8-941226715014
value_template: >-
{% if ( (is_state('lock.deck','unlocked')) and (is_state('binary_sensor.door_porch_window_door_is_open','off')) ) %}
Unlocked
{% elif ( (is_state('binary_sensor.door_porch_window_door_is_open','on')) and (is_state('lock.deck','locked')) ) %}
Locked Open
{% elif ( (is_state('binary_sensor.door_porch_window_door_is_open','off')) and (is_state('lock.deck','locked')) ) %}
Locked
{% elif ( (is_state('binary_sensor.door_porch_window_door_is_open','on')) and (is_state('lock.deck','unlocked')) ) %}
Open
{% else %}
Unknown
{% endif %}
icon_template: >-
{% if ( (is_state('lock.deck','unlocked')) and (is_state('binary_sensor.door_porch_window_door_is_open','off')) ) %}
mdi:lock-open
{% elif ( (is_state('binary_sensor.door_porch_window_door_is_open','on')) and (is_state('lock.deck','locked')) ) %}
mdi:door-closed-cancel
{% elif ( (is_state('binary_sensor.door_porch_window_door_is_open','off')) and (is_state('lock.deck','locked')) ) %}
mdi:lock
{% elif ( (is_state('binary_sensor.door_porch_window_door_is_open','on')) and (is_state('lock.deck','unlocked')) ) %}
mdi:door-open
{% else %}
mdi:lock-question
{% endif %}
- platform: template
sensors:
breezeway_door_status_sc:
friendly_name: "Breezeway Door Status"
unique_id: breezeway-door-status-2ad4a16a-3d68-48bb-11f8-491226726111
value_template: >-
{% if ( (is_state('lock.breezeway','unlocked')) and (is_state('binary_sensor.door_breezeway_window_door_is_open','off')) ) %}
Unlocked
{% elif ( (is_state('binary_sensor.door_breezeway_window_door_is_open','on')) and (is_state('lock.breezeway','locked')) ) %}
Locked Open
{% elif ( (is_state('binary_sensor.door_breezeway_window_door_is_open','off')) and (is_state('lock.breezeway','locked')) ) %}
Locked
{% elif ( (is_state('binary_sensor.door_breezeway_window_door_is_open','on')) and (is_state('lock.breezeway','unlocked')) ) %}
Open
{% else %}
Unknown
{% endif %}
icon_template: >-
{% if ( (is_state('lock.breezeway','unlocked')) and (is_state('binary_sensor.door_breezeway_window_door_is_open','off')) ) %}
mdi:lock-open
{% elif ( (is_state('binary_sensor.door_breezeway_window_door_is_open','on')) and (is_state('lock.breezeway','locked')) ) %}
mdi:door-closed-cancel
{% elif ( (is_state('binary_sensor.door_breezeway_window_door_is_open','off')) and (is_state('lock.breezeway','locked')) ) %}
mdi:lock
{% elif ( (is_state('binary_sensor.door_breezeway_window_door_is_open','on')) and (is_state('lock.breezeway','unlocked')) ) %}
mdi:door-open
{% else %}
mdi:lock-question
{% endif %}
- platform: template
sensors:
basement_door_status_sc:
friendly_name: "Basement Door Status"
unique_id: breezeway-door-status-2bb6c26a-3a72-44bc-42f5-495256726115
value_template: >-
{% if ( (is_state('lock.basement','unlocked')) and (is_state('binary_sensor.door_basement_window_door_is_open','off')) ) %}
Unlocked
{% elif ( (is_state('binary_sensor.door_basement_window_door_is_open','on')) and (is_state('lock.basement','locked')) ) %}
Locked Open
{% elif ( (is_state('binary_sensor.door_basement_window_door_is_open','off')) and (is_state('lock.basement','locked')) ) %}
Locked
{% elif ( (is_state('binary_sensor.door_basement_window_door_is_open','on')) and (is_state('lock.basement','unlocked')) ) %}
Open
{% else %}
Unknown
{% endif %}
icon_template: >-
{% if ( (is_state('lock.basement','unlocked')) and (is_state('binary_sensor.door_basement_window_door_is_open','off')) ) %}
mdi:lock-open
{% elif ( (is_state('binary_sensor.door_basement_window_door_is_open','on')) and (is_state('lock.basement','locked')) ) %}
mdi:door-closed-cancel
{% elif ( (is_state('binary_sensor.door_basement_window_door_is_open','off')) and (is_state('lock.basement','locked')) ) %}
mdi:lock
{% elif ( (is_state('binary_sensor.door_basement_window_door_is_open','on')) and (is_state('lock.basement','unlocked')) ) %}
mdi:door-open
{% else %}
mdi:lock-question
{% endif %}
- platform: template
sensors:
garage_door_status_sc:
friendly_name: "Garage Door Status"
unique_id: garage-door-status-1bbbc85a-2cf2-444f-42f6-405056726105
value_template: >-
{% if ( (is_state('lock.garage','unlocked')) and (is_state('binary_sensor.door_garage_window_door_is_open','off')) ) %}
Unlocked
{% elif ( (is_state('binary_sensor.door_garage_window_door_is_open','on')) and (is_state('lock.garage','locked')) ) %}
Locked Open
{% elif ( (is_state('binary_sensor.door_garage_window_door_is_open','off')) and (is_state('lock.garage','locked')) ) %}
Locked
{% elif ( (is_state('binary_sensor.door_garage_window_door_is_open','on')) and (is_state('lock.garage','unlocked')) ) %}
Open
{% else %}
Unknown
{% endif %}
icon_template: >-
{% if ( (is_state('lock.garage','unlocked')) and (is_state('binary_sensor.door_garage_window_door_is_open','off')) ) %}
mdi:lock-open
{% elif ( (is_state('binary_sensor.door_garage_window_door_is_open','on')) and (is_state('lock.garage','locked')) ) %}
mdi:door-closed-cancel
{% elif ( (is_state('binary_sensor.door_garage_window_door_is_open','off')) and (is_state('lock.garage','locked')) ) %}
mdi:lock
{% elif ( (is_state('binary_sensor.door_garage_window_door_is_open','on')) and (is_state('lock.garage','unlocked')) ) %}
mdi:door-open
{% else %}
mdi:lock-question
{% endif %}
- platform: template
sensors:
garage_east_door_status_sc:
friendly_name: "Garage Door East Status"
unique_id: garage-door-east-status-1ffac45a-2bf2-d44e-32f6-411156726102
value_template: >-
{% if ( (is_state('cover.garage_door_2','open')) ) %}
Open
{% elif ( (is_state('cover.garage_door_2','closed')) ) %}
Closed
{% elif ( (is_state('cover.garage_door_2','closing')) ) %}
Closing
{% elif ( (is_state('cover.garage_door_2','opening')) ) %}
Opening
{% else %}
Unknown
{% endif %}
icon_template: >-
{% if ( (is_state('cover.garage_door_2','open')) ) %}
mdi:garage-open-variant
{% elif ( (is_state('cover.garage_door_2','closed')) ) %}
mdi:garage-variant
{% elif ( (is_state('cover.garage_door_2','closing')) ) %}
mdi:arrow-down-box
{% elif ( (is_state('cover.garage_door_2','opening')) ) %}
mdi:arrow-up-box
{% else %}
mdi:garage-alert-variant
{% endif %}
- platform: template
sensors:
garage_west_door_status_sc:
friendly_name: "Garage Door West Status"
unique_id: garage-door-west-status-2baac34a-5aa3-d44b-3bf7-400151726002
value_template: >-
{% if ( (is_state('cover.garage_door','open')) ) %}
Open
{% elif ( (is_state('cover.garage_door','closed')) ) %}
Closed
{% else %}
Unknown
{% endif %}
icon_template: >-
{% if ( (is_state('cover.garage_door','open')) ) %}
mdi:garage-open-variant
{% elif ( (is_state('cover.garage_door','closed')) ) %}
mdi:garage-variant
{% elif ( (is_state('cover.garage_door','closing')) ) %}
mdi:arrow-down-box
{% elif ( (is_state('cover.garage_door','opening')) ) %}
mdi:arrow-up-box
{% else %}
mdi:garage-alert-variant
{% endif %}
- platform: template
sensors:
garage_shop_door_status_sc:
friendly_name: "Garage Door Shop Status"
unique_id: garage-door-shop-status-3abbd45b-6bc5-b37a-2bf7-400151726002
value_template: >-
{% if ( (is_state('cover.sgarage','open')) ) %}
Open
{% elif ( (is_state('cover.sgarage','closed')) ) %}
Closed
{% else %}
Unknown
{% endif %}
icon_template: >-
{% if ( (is_state('cover.sgarage','open')) ) %}
mdi:garage-open-variant
{% elif ( (is_state('cover.sgarage','closed')) ) %}
mdi:garage-variant
{% elif ( (is_state('cover.sgarage','closing')) ) %}
mdi:arrow-down-box
{% elif ( (is_state('cover.sgarage','opening')) ) %}
mdi:arrow-up-box
{% else %}
mdi:garage-alert-variant
{% endif %}
You need both of these components for it to function. As I mentioned above:
HACS: Add Ons Needed:
- button-card
- hui-element
- card-mod
Some things that I have not done, but you could do:
- add in a hold-action to do something like lock all doors
- add logic to not lock a door if it is open
- maybe fix the closed status in my templates that never gets called