Skip to content

Instantly share code, notes, and snippets.

@nwithan8
Created November 21, 2023 06:49
Show Gist options
  • Save nwithan8/5cdc27dff031914ac8cf054a2be8a732 to your computer and use it in GitHub Desktop.
Save nwithan8/5cdc27dff031914ac8cf054a2be8a732 to your computer and use it in GitHub Desktop.
blueprint:
name: "HASPone p[x].b[y] displays a fan with a toggle on/off icon"
description: |
## Blueprint Version: `1.05.00`
# Description
A HASPone button displays a fan control on page 4 and 5 with a toggle on/off icon to the left.
![Preview](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/hasp_Display_Fan_with_Icon.png)
## HASPone Page and Button reference
<details>
This automation is designed to work with the sliders and buttons found on pages 4 and 5
| Pages 4-5 |
|-----------|
| ![Pages 4-5](https://github.com/aderusha/HASwitchPlate/raw/dev/Documentation/Images/NextionUI_p4-p5_3sliders.png) |
</details>
domain: automation
input:
haspdevice:
name: "HASPone Device"
description: "Select the HASPone device"
selector:
device:
integration: mqtt
manufacturer: "HASwitchPlate"
model: "HASPone v1.0.0"
hasppage:
name: "HASPone Page"
description: "Select the HASPone page (4 or 5) for the fan. Refer to the HASPone Page and Button reference above."
default: 4
selector:
number:
min: 4
max: 5
mode: slider
unit_of_measurement: page
haspbutton:
name: "HASPone Button"
description: "Select the HASPone button (7-9) for the fan. Refer to the HASPone Page and Button reference above."
default: 7
selector:
number:
min: 7
max: 9
mode: slider
unit_of_measurement: button
fan:
name: "Light to control"
description: "Select a fan device which supports multiple speeds"
selector:
entity:
domain: fan
text_on:
name: "HASPone Button Text On"
description: "Enter text to appear on the button above the fan when the selected device is ON."
default: "Fan"
selector:
text:
text_off:
name: "HASPone Button Text Off"
description: "Enter text to appear on the button when the selected device is OFF. The default value of {{text_on}} will leave the text unchanged when the device turns on/off"
default: "{{text_on}}"
selector:
text:
font_select:
name: "HASPone Button Font"
description: "Select the text font for this button label. Refer to the HASPone Font reference above."
default: "6 - Noto Sans 32"
selector:
select:
options:
- "0 - Consolas 24"
- "1 - Consolas 32"
- "2 - Consolas 48"
- "3 - Consolas 80"
- "4 - Webdings 56"
- "5 - Noto Sans 24"
- "6 - Noto Sans 32"
- "7 - Noto Sans 48"
- "8 - Noto Sans 64"
- "9 - Noto Sans 80"
- "10 - Noto Sans Bold 80"
xcen_select:
name: "Text horizontal alignment"
description: "Horizontal text alignment: 0=Left 1=Center 2=Right"
default: "1 - Centered"
selector:
select:
options:
- "0 - Left aligned"
- "1 - Centered"
- "2 - Right aligned"
ycen_select:
name: "Text vertical alignment"
description: "Vertical text alignment: 0=Top 1=Center 2=Bottom"
default: "0 - Top aligned"
selector:
select:
options:
- "0 - Top aligned"
- "1 - Centered"
- "2 - Bottom aligned"
wrap:
name: "Text wrap"
description: "Enable line-wrapping text if too long to fit in the button."
default: false
selector:
boolean:
icon_on:
name: '"On" state icon'
description: 'Enter the icon to be shown when the selected entity is "on"'
default: ""
selector:
text:
icon_off:
name: '"Off" state icon'
description: 'Enter the icon to be shown when the selected entity is "off"'
default: ""
selector:
text:
mode: parallel
max_exceeded: silent
variables:
haspdevice: !input haspdevice
haspname: >-
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^sensor\..+_sensor(?:_\d+|)$") -%}
{{- entity|regex_replace(find="^sensor\.", replace="", ignorecase=true)|regex_replace(find="_sensor(?:_\d+|)$", replace="", ignorecase=true) -}}
{%- endif -%}
{%- endfor -%}
hasppage: !input hasppage
haspbutton: !input haspbutton
fan: !input fan
text_on: !input text_on
text_off: !input text_off
font_select: !input font_select
font: '{{ font_select.split(" - ")[0] | int }}'
xcen_select: !input xcen_select
xcen: '{{ xcen_select.split(" - ")[0] | int }}'
ycen_select: !input ycen_select
ycen: '{{ ycen_select.split(" - ")[0] | int }}'
wrap: !input wrap
icon_on: !input icon_on
icon_off: !input icon_off
fanbutton: "{{haspbutton|int}}"
togglebutton: "{{fanbutton-3}}"
fanobject: '{{ "p[" ~ hasppage ~ "].b[" ~ haspbutton ~ "]" }}'
toggleobject: '{{ "p[" ~ hasppage ~ "].b[" ~ togglebutton ~ "]" }}'
commandtopic: '{{ "hasp/" ~ haspname ~ "/command/" ~ fanobject }}'
togglecommandtopic: '{{ "hasp/" ~ haspname ~ "/command/" ~ toggleobject }}'
jsontopic: '{{ "hasp/" ~ haspname ~ "/state/json" }}'
jsoncommandtopic: '{{ "hasp/" ~ haspname ~ "/command/json" }}'
isbr: "{% if wrap == true %}1{% else %}0{% endif %}"
activepage: >-
{%- set activepage = namespace() -%}
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^number\..*_active_page(?:_\d+|)$") -%}
{%- set activepage.entity=entity -%}
{%- endif -%}
{%- endfor -%}
{{ states(activepage.entity) | int(default=-1) }}
selectedfgtopic: '{{ "hasp/" ~ haspname ~ "/light/selectedforegroundcolor/rgb" }}'
selectedbgtopic: '{{ "hasp/" ~ haspname ~ "/light/selectedbackgroundcolor/rgb" }}'
unselectedfgtopic: '{{ "hasp/" ~ haspname ~ "/light/unselectedforegroundcolor/rgb" }}'
unselectedbgtopic: '{{ "hasp/" ~ haspname ~ "/light/unselectedbackgroundcolor/rgb" }}'
selectedfg: >-
{%- set color = namespace() -%}
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^light\..*_selected_foreground_color(?:_\d+|)$") -%}
{%- set color.source=entity -%}
{%- endif -%}
{%- endfor -%}
{%- set brightness = state_attr(color.source, "brightness")|int(default=255) / 255 -%}
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{{ (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int }}
selectedbg: >-
{%- set color = namespace() -%}
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^light\..*_selected_background_color(?:_\d+|)$") -%}
{%- set color.source=entity -%}
{%- endif -%}
{%- endfor -%}
{%- set brightness = state_attr(color.source, "brightness")|int(default=255) / 255 -%}
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{{ (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int }}
unselectedfg: >-
{%- set color = namespace() -%}
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^light\..*_unselected_foreground_color(?:_\d+|)$") -%}
{%- set color.source=entity -%}
{%- endif -%}
{%- endfor -%}
{%- set brightness = state_attr(color.source, "brightness")|int(default=255) / 255 -%}
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{{ (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int }}
unselectedbg: >-
{%- set color = namespace() -%}
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^light\..*_unselected_background_color(?:_\d+|)$") -%}
{%- set color.source=entity -%}
{%- endif -%}
{%- endfor -%}
{%- set brightness = state_attr(color.source, "brightness")|int(default=255) / 255 -%}
{%- set red=(state_attr(color.source, "rgb_color")[0] * brightness)|int(default=0) -%}
{%- set green=(state_attr(color.source, "rgb_color")[1] * brightness)|int(default=0) -%}
{%- set blue=(state_attr(color.source, "rgb_color")[2] * brightness)|int(default=0) -%}
{{ (red|bitwise_and(248)*256) + (green|bitwise_and(252)*8) + (blue|bitwise_and(248)/8)|int }}
ypos: "{{(haspbutton|int - 7) * 90 + 32}}" # calculate the top pixel position based on the selected button
xpos: 0
icon: '{% if states(fan) == "on" %}{{icon_on}}{% else %}{{icon_off}}{% endif %}'
text: '{% if states(fan) == "on" %}{{text_on}}{% else %}{{text_off}}{% endif %}'
iconwidth: 60
iconheight: 65
iconfont: 8
speed: '{{state_attr(fan, "percentage")|int(default=0)}}'
trigger_variables:
haspdevice: !input haspdevice
haspname: >-
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^sensor\..+_sensor(?:_\d+|)$") -%}
{{- entity|regex_replace(find="^sensor\.", replace="", ignorecase=true)|regex_replace(find="_sensor(?:_\d+|)$", replace="", ignorecase=true) -}}
{%- endif -%}
{%- endfor -%}
haspsensor: >-
{%- for entity in device_entities(haspdevice) -%}
{%- if entity|regex_search("^sensor\..+_sensor(?:_\d+|)$") -%}
{{ entity }}
{%- endif -%}
{%- endfor -%}
jsontopic: '{{ "hasp/" ~ haspname ~ "/state/json" }}'
selectedfgtopic: '{{ "hasp/" ~ haspname ~ "/light/selectedforegroundcolor/rgb" }}'
selectedbgtopic: '{{ "hasp/" ~ haspname ~ "/light/selectedbackgroundcolor/rgb" }}'
unselectedfgtopic: '{{ "hasp/" ~ haspname ~ "/light/unselectedforegroundcolor/rgb" }}'
unselectedbgtopic: '{{ "hasp/" ~ haspname ~ "/light/unselectedbackgroundcolor/rgb" }}'
trigger:
- platform: state
entity_id: !input fan
- platform: template
value_template: "{{ is_state(haspsensor, 'ON') }}"
- platform: homeassistant
event: start
- platform: mqtt
topic: "{{jsontopic}}"
- platform: mqtt
topic: "{{selectedfgtopic}}"
- platform: mqtt
topic: "{{selectedbgtopic}}"
- platform: mqtt
topic: "{{unselectedfgtopic}}"
- platform: mqtt
topic: "{{unselectedbgtopic}}"
condition:
- condition: template
value_template: "{{ is_state(haspsensor, 'ON') }}"
action:
- choose:
#########################################################################
# RUN ACTIONS or Home Assistant Startup or HASPone Connect
# Apply styles, place text, and then place icon if our target page is currently active
- conditions:
- condition: template
value_template: >-
{{-
(trigger is not defined)
or
(trigger.platform is none)
or
((trigger.platform == 'homeassistant') and (trigger.event == 'start'))
or
((trigger.platform == 'template') and (trigger.entity_id == haspsensor) and (trigger.to_state.state == 'ON'))
-}}
sequence:
- service: mqtt.publish
data:
topic: "{{jsoncommandtopic}}"
payload: >-
["{{toggleobject}}.txt=\"{{text}}\"",
"{{toggleobject}}.font={{font}}",
"{{toggleobject}}.xcen={{xcen}}",
"{{toggleobject}}.ycen={{ycen}}",
"{{toggleobject}}.pco={{selectedfg}}",
"{{toggleobject}}.bco={{selectedbg}}",
"{{toggleobject}}.pco2={{unselectedfg}}",
"{{toggleobject}}.bco2={{unselectedbg}}",
"{{fanobject}}.pco={{unselectedfg}}",
"{{fanobject}}.bco={{unselectedbg}}",
"{{fanobject}}.val={{speed}}"
{%- if activepage|int == hasppage|int -%}
,"delay=1","xstr {{xpos}},{{ypos}},{{iconwidth}},{{iconheight}},{{iconfont}},{{selectedfg}},0,1,1,3,\"{{icon}}\"","vis {{fanbutton}},1"
{%- endif -%}]
#########################################################################
# Update display if our entity has changed state
- conditions: # fan has changed value, on/off state has also changed, and we're currently on the selected page
- condition: template
value_template: '{{ (trigger.platform == "state") and (trigger.entity_id == fan) and (trigger.from_state.state != trigger.to_state.state) and (activepage|int == hasppage|int) }}'
sequence:
- service: mqtt.publish
data:
topic: "{{jsoncommandtopic}}"
payload: '["{{toggleobject}}.txt=\"{{text}}\"","{{fanobject}}.val={{speed}}","delay=1","xstr {{xpos}},{{ypos}},{{iconwidth}},{{iconheight}},{{iconfont}},{{selectedfg}},0,1,1,3,\"{{icon}}\""]'
- conditions: # fan has changed value, on/off state has also changed
- condition: template
value_template: '{{ (trigger.platform == "state") and (trigger.entity_id == fan) and (trigger.from_state.state != trigger.to_state.state) }}'
sequence:
- service: mqtt.publish
data:
topic: "{{jsoncommandtopic}}"
payload: '["{{toggleobject}}.txt=\"{{text}}\"","{{fanobject}}.val={{speed}}"]'
- conditions: # fan has changed value
- condition: template
value_template: '{{ (trigger.platform == "state") and (trigger.entity_id == fan) }}'
sequence:
- service: mqtt.publish
data:
topic: "{{jsoncommandtopic}}"
payload: '["{{fanobject}}.val={{speed}}"]'
#########################################################################
# Handle MQTT message triggers
- conditions:
- condition: template
value_template: '{{ trigger.platform == "mqtt" }}'
sequence:
- choose:
#########################################################################
# Catch incoming JSON messages
- conditions:
- condition: template
value_template: "{{ (trigger.topic == jsontopic) and trigger.payload_json is defined }}"
sequence:
- choose:
#########################################################################
# Primary function: Toggle a light when we press the button
- conditions:
- condition: template
value_template: '{{ (trigger.topic == jsontopic) and (trigger.payload_json.event == toggleobject) and (trigger.payload_json.value == "OFF") }}'
sequence:
- service: homeassistant.toggle
entity_id: !input fan
#########################################################################
# Primary function: Set the fan value when the HASPone slider has moved
- conditions:
- condition: template
value_template: '{{ (trigger.topic == jsontopic) and (trigger.payload_json.event == fanobject ~ ".val") }}'
sequence:
- service: fan.set_percentage
entity_id: !input fan
data:
percentage: "{{trigger.payload_json.value}}"
#########################################################################
# Icon overlay
- conditions: # Page changed to our page, so place the icon on the screen.
- condition: template
value_template: '{{ (trigger.topic == jsontopic ) and (trigger.payload_json.event == "page" ) and (trigger.payload_json.value == hasppage|int) }}'
sequence:
- service: mqtt.publish
data:
topic: "{{jsoncommandtopic}}"
payload: '["ref {{togglebutton}}","delay=1","xstr {{xpos}},{{ypos}},{{iconwidth}},{{iconheight}},{{iconfont}},{{selectedfg}},0,1,1,3,\"{{icon}}\"","delay=1","vis {{fanbutton}},1"]'
#########################################################################
# Theme: Apply selected foreground color when it changes.
# Any change to the button will remove the overlaid icon.
- conditions:
- condition: template
value_template: "{{ trigger.topic == selectedfgtopic }}"
sequence:
- delay: "00:00:00.5"
- service: mqtt.publish
data:
topic: "{{togglecommandtopic}}.pco"
payload: "{{trigger.payload}}"
- condition: template
value_template: "{{ activepage|int == hasppage|int }}"
- delay: "00:00:00.5"
- service: mqtt.publish
data:
topic: "{{jsoncommandtopic}}"
payload: '["delay=1","xstr {{xpos}},{{ypos}},{{iconwidth}},{{iconheight}},{{iconfont}},{{trigger.payload}},0,1,1,3,\"{{icon}}\"","delay=1","vis {{fanbutton}},1"]'
#########################################################################
# Theme: Apply selected background color on change
- conditions:
- condition: template
value_template: "{{ trigger.topic == selectedbgtopic }}"
sequence:
- service: mqtt.publish
data:
topic: "{{togglecommandtopic}}.bco"
payload: "{{trigger.payload}}"
- condition: template
value_template: "{{ activepage|int == hasppage|int }}"
- delay: "00:00:00.5"
- service: mqtt.publish
data:
topic: "{{jsoncommandtopic}}"
payload: '["delay=1","xstr {{xpos}},{{ypos}},{{iconwidth}},{{iconheight}},{{iconfont}},{{fgcolor}},0,1,1,3,\"{{icon}}\"","delay=1","vis {{fanbutton}},1"]'
#########################################################################
# Theme: Apply unselected foreground color on change
- conditions:
- condition: template
value_template: "{{ trigger.topic == unselectedfgtopic }}"
sequence:
- service: mqtt.publish
data:
topic: "{{jsoncommandtopic}}"
payload: '["{{toggleobject}}.pco2={{trigger.payload}}","{{fanobject}}.pco={{trigger.payload}}"]'
- condition: template
value_template: "{{ activepage|int == hasppage|int }}"
- delay: "00:00:00.5"
- service: mqtt.publish
data:
topic: "{{jsoncommandtopic}}"
payload: '["delay=1","xstr {{xpos}},{{ypos}},{{iconwidth}},{{iconheight}},{{iconfont}},{{fgcolor}},0,1,1,3,\"{{icon}}\"","delay=1","vis {{fanbutton}},1"]'
#########################################################################
# Theme: Apply unselected background color on change
- conditions:
- condition: template
value_template: "{{ trigger.topic == unselectedbgtopic }}"
sequence:
- service: mqtt.publish
data:
topic: "{{jsoncommandtopic}}"
payload: '["{{toggleobject}}.bco2={{trigger.payload}}","{{fanobject}}.bco={{trigger.payload}}"]'
- condition: template
value_template: "{{ activepage|int == hasppage|int }}"
- delay: "00:00:00.5"
- service: mqtt.publish
data:
topic: "{{jsoncommandtopic}}"
payload: '["delay=1","xstr {{xpos}},{{ypos}},{{iconwidth}},{{iconheight}},{{iconfont}},{{fgcolor}},0,1,1,3,\"{{icon}}\"","delay=1","vis {{fanbutton}},1"]'
@roopesh
Copy link

roopesh commented Jun 19, 2024

Thanks for this! It looks like lights require a 255 brightness for the slider to work whereas fans work with 100 percentage as the max. Do you know where I'd make the changes to make the slider work correctly?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment