Here is what I was able to accomplish:
The fan icon button calls the fan.toggle
service and the three buttons on the right side call the fan.set_preset_mode
service providing their respective mode.
Here is the code for this card
# Cealing fan/light card
- type: custom:swiss-army-knife-card
aspectratio: 5/1
disable_card: false
entities:
- entity: light.sonoff_1001589996_1
name: "Ceiling Fan Light"
icon: mdi:ceiling-fan-light
- entity: fan.sonoff_1001589996
name: "Ceiling Fan"
icon: mdi:fan
- entity: fan.sonoff_1001589996
name: "Preset Mode"
icon: mdi:fan
attribute: preset_mode
layout:
toolsets:
- toolset: half-circle
position:
cx: 0
cy: 50
tools:
- type: circle
position:
cx: 50
cy: 50
radius: 48
styles:
circle:
fill: none
stroke: var(--theme-sys-color-secondary)
stroke-width: 3em
- type: icon
position:
cx: 72
cy: 50
align: center
icon_size: 40
icon: mdi:ceiling-fan
styles:
icon:
fill: var(--theme-sys-color-secondary)
opacity: 0.9
- toolset: card-label
position:
cx: 125
cy: 50
tools:
- type: text
position:
cx: 0
cy: 50
text: "Ceiling Fan"
styles:
text:
text-anchor: start
font-size: 20em
font-weight: 700
opacity: 1
overflow: hidden
- toolset: vertical-line-1
position:
cx: 200
cy: 50
tools:
- type: line
position:
cx: 50
cy: 50
orientation: vertical
length: 75
styles:
line:
fill: var(--theme-sys-color-secondary)
opacity: 0.7
- toolset: light-state-button
position:
cx: 250
cy: 50
tools:
- type: circle
position:
cx: 50
cy: 50
radius: 35
entity_index: 0
animations:
- state: "on"
styles:
circle:
stroke: orange
opacity: 1
filter: url(#is-1)
- state: "off"
styles:
circle:
stroke: var(--theme-sys-color-secondary)
opacity: 1
styles:
circle:
fill: none
stroke-width: 4em
transition: 'all 0.8s ease'
- type: icon
position:
cx: 50
cy: 50
align: center
icon_size: 50
entity_index: 0
animations:
- state: "on"
styles:
icon:
color: orange
fill: orange
opacity: 1
- state: "off"
styles:
icon:
color: var(--theme-sys-color-secondary)
fill: var(--theme-sys-color-secondary)
opacity: 1
styles:
icon:
transition: 'all 0.8s ease'
- type: circle # transparent shape that covers the whole button to act as the link
position:
cx: 50
cy: 50
radius: 35
entity_index: 0
styles:
circle:
fill: rgba(0,0,0,0)
stroke: none
user_actions:
tap_action:
haptic: success
actions:
- action: call-service
service: light.toggle
- toolset: fan-state-button
position:
cx: 350
cy: 50
tools:
- type: circle
position:
cx: 50
cy: 50
radius: 35
entity_index: 1
animations:
- state: "on"
styles:
circle:
stroke: orange
opacity: 1
filter: url(#is-1)
- state: "off"
styles:
circle:
stroke: var(--theme-sys-color-secondary)
opacity: 1
styles:
circle:
fill: none
stroke-width: 4em
transition: 'all 0.8s ease'
- type: icon
position:
cx: 50
cy: 50
align: center
icon_size: 50
entity_index: 1
animations:
- state: "on"
styles:
icon:
color: orange
fill: orange
opacity: 1
- state: "off"
styles:
icon:
color: var(--theme-sys-color-secondary)
fill: var(--theme-sys-color-secondary)
opacity: 1
styles:
icon:
transition: 'all 0.8s ease'
- type: circle # transparent shape that covers the whole button to act as the link
position:
cx: 50
cy: 50
radius: 35
entity_index: 1
styles:
circle:
fill: rgba(0,0,0,0)
stroke: none
user_actions:
tap_action:
haptic: success
actions:
- action: call-service
service: fan.toggle
- toolset: preset-mode-buttons
position:
cx: 450
cy: 50
tools:
# Preset Mode: Low
- type: rectangle
position:
cx: 50
cy: 25
height: 20
width: 70
rx: 10
entity_index: 2
animations:
- state: "low"
styles:
rectangle:
stroke: orange
opacity: 1
- state: "medium"
styles:
rectangle:
stroke: var(--theme-sys-color-secondary)
opacity: 1
- state: "high"
styles:
rectangle:
stroke: var(--theme-sys-color-secondary)
opacity: 1
- state: "off" # this state value is never recorded.
styles:
rectangle:
stroke: var(--theme-sys-color-secondary)
opacity: 1
styles:
rectangle:
fill: none
stroke-width: 2em
transition: 'all 0.8s ease'
- type: text
position:
cx: 50
cy: 25
text: "Low"
styles:
text:
text-anchor: middle
font-size: 12em
font-weight: 700
opacity: 1
- type: rectangle # transparent shape that covers the whole button to act as the link
position:
cx: 50
cy: 25
height: 20
width: 70
rx: 10
entity_index: 2
styles:
rectangle:
fill: rgba(0,0,0,0)
stroke: none
user_actions:
tap_action:
haptic: success
actions:
- action: call-service
service: fan.set_preset_mode
service_data:
preset_mode: "low"
# Preset Mode: Medium
- type: rectangle
position:
cx: 50
cy: 50
height: 20
width: 70
rx: 10
entity_index: 2
animations:
- state: "low"
styles:
rectangle:
stroke: var(--theme-sys-color-secondary)
opacity: 1
- state: "medium"
styles:
rectangle:
stroke: orange
opacity: 1
- state: "high"
styles:
rectangle:
stroke: var(--theme-sys-color-secondary)
opacity: 1
- state: "off"
styles:
rectangle:
stroke: var(--theme-sys-color-secondary)
opacity: 1
styles:
rectangle:
fill: none
stroke-width: 2em
transition: 'all 0.8s ease'
- type: text
position:
cx: 50
cy: 50
text: "Medium"
styles:
text:
text-anchor: middle
font-size: 12em
font-weight: 700
opacity: 1
- type: rectangle # transparent shape that covers the whole button to act as the link
position:
cx: 50
cy: 50
height: 20
width: 70
rx: 10
entity_index: 2
styles:
rectangle:
fill: rgba(0,0,0,0)
stroke: none
user_actions:
tap_action:
haptic: success
actions:
- action: call-service
service: fan.set_preset_mode
service_data:
preset_mode: "medium"
# Preset Mode: High
- type: rectangle
position:
cx: 50
cy: 75
height: 20
width: 70
rx: 10
entity_index: 2
animations:
- state: "low"
styles:
rectangle:
stroke: var(--theme-sys-color-secondary)
opacity: 1
- state: "medium"
styles:
rectangle:
stroke: var(--theme-sys-color-secondary)
opacity: 1
- state: "high"
styles:
rectangle:
stroke: orange
opacity: 1
- state: "off"
styles:
rectangle:
stroke: var(--theme-sys-color-secondary)
opacity: 1
styles:
rectangle:
fill: none
stroke-width: 2em
transition: 'all 0.8s ease'
- type: text
position:
cx: 50
cy: 75
text: "High"
styles:
text:
text-anchor: middle
font-size: 12em
font-weight: 700
opacity: 1
- type: rectangle # transparent shape that covers the whole button to act as the link
position:
cx: 50
cy: 75
height: 20
width: 70
rx: 10
entity_index: 2
styles:
rectangle:
fill: rgba(0,0,0,0)
stroke: none
user_actions:
tap_action:
haptic: success
actions:
- action: call-service
service: fan.set_preset_mode
service_data:
preset_mode: "high"
There is still one issue outstanding on this setup. When the fan is turned off the preset_mode:
is set to null
according to the details in the state developer tools. I have not yet figured out how to capture that value in the automation section of the tools. I tried checking for the following state values:
- state: "null"
- state: null
- state: "none"
- state: none
- state: "" # Double quotes, no space
- state: " " # Double quotes with a space
- state: '' # Single quote, no space
- state: ' ' # Single quote with a space
- state: # just a blank element
None of these captured the null state of the preset_mode when the fan entity is turned off. The result of this is that when the fan is turned off the last preset mode remains active. If the page is refreshed the style of the buttons reverts to a black stroke color. When the fan is turned on again the styles for each button are re-established.
If anyone has any suggestions on how to handle this null state, please share!
@AmoebeLabs any suggestions? Would there be a way to set a default if none of the - state
values match?
Happy Automating!
UPDATE: State issue solved
animations:
- state: 'low'
styles:
rectangle:
stroke: orange
opacity: 1
- state: 'low'
operator: '!=' # <-- This was the key to the solution. Sometimes it helps to re-read the docs (and read the whole document)!
styles:
rectangle:
stroke: var(--theme-sys-color-secondary)
opacity: 1