From 269789cdd70cc1475bad3401752531e12d1903ac Mon Sep 17 00:00:00 2001 From: aderusha Date: Mon, 8 Nov 2021 13:21:18 -0500 Subject: [PATCH] Add Cycle Automations blueprint --- Home_Assistant/blueprints/README.md | 12 + .../blueprints/hasp_Cycle_Automations.yaml | 319 ++++++++++++++++++ images/hasp_Cycle_Automations.gif | Bin 0 -> 9890 bytes 3 files changed, 331 insertions(+) create mode 100644 Home_Assistant/blueprints/hasp_Cycle_Automations.yaml create mode 100644 images/hasp_Cycle_Automations.gif diff --git a/Home_Assistant/blueprints/README.md b/Home_Assistant/blueprints/README.md index 5b58eff..ebd221c 100644 --- a/Home_Assistant/blueprints/README.md +++ b/Home_Assistant/blueprints/README.md @@ -194,6 +194,18 @@ A button on the HASP will perform an action when pressed. Can be combined on a b --- +### Cycle Automations + +[![Cycle Automations](https://my.home-assistant.io/badges/blueprint_import.svg)](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgithub.com%2FHASwitchPlate%2FHASPone%2Fblob%2Fmain%2FHome_Assistant%2Fblueprints%2Fhasp_Cycle_Automations.yaml) + +![Preview](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/hasp_Cycle_Automations.gif) + +A button on the HASP will toggle through as many as 10 selected automations. This allows the user to assign multiple blueprints to the same button on the HASPone device, and to cycle between them by pressing the selected button. + +Optionally, a timeout can be set to cycle back to a "default" automation after a specified interval, or to continuously cycle through selected automations. + +--- + ### Dim Screen with Sun [![Dim Screen with Sun](https://my.home-assistant.io/badges/blueprint_import.svg)](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgithub.com%2FHASwitchPlate%2FHASPone%2Fblob%2Fmain%2FHome_Assistant%2Fblueprints%2Fhasp_Dim_Screen_with_Sun.yaml) diff --git a/Home_Assistant/blueprints/hasp_Cycle_Automations.yaml b/Home_Assistant/blueprints/hasp_Cycle_Automations.yaml new file mode 100644 index 0000000..c19129d --- /dev/null +++ b/Home_Assistant/blueprints/hasp_Cycle_Automations.yaml @@ -0,0 +1,319 @@ +blueprint: + name: "HASP p[x].b[y] cycles through multiple automations" + description: | + + ## Description + + A button on the HASP will toggle through as many as 10 selected automations. This allows the user to assign multiple blueprints to the same button on the HASPone device, and to cycle between them by pressing the selected button. + + Optionally, a timeout can be set to cycle back to a "default" automation after a specified interval, or to continuously cycle through selected automations. + + ## Example + + 1. Deploy blueprint `hasp_Display_Clock_with_Icon.yaml` to button `p[1].b[4]` + 2. Deploy blueprint `hasp_Display_Calendar_with_Icon.yaml` to the same button `p[1].b[4]` + 3. Deploy this blueprint, selecting page 1 button 4 (same as above), and then select each of the previous deployed automations as the "first" and "second" automations + 4. Press `p[1].b[4]` to show the calendar and disable the clock automation on `p[1].b[4]` + 5. Press `p[1].b[4]` again to show the clock and disable the calendar automation on `p[1].b[4]` + + ![Preview](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/hasp_Cycle_Automations.gif) + + ## HASP Page and Button Reference + + The images below show each available HASP page along with the layout of available button objects. + +
+ + | Page 0 | Pages 1-3 | Pages 4-5 | + |--------|-----------|-----------| + | ![Page 0](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p0_Init_Screen.png) | ![Pages 1-3](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p1-p3_4buttons.png) | ![Pages 4-5](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p4-p5_3sliders.png) | + + | Page 6 | Page 7 | Page 8 | + |--------|--------|--------| + | ![Page 6](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p6_8buttons.png) | ![Page 7](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p7_12buttons.png) | ![Page 8](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p8_5buttons+1slider.png) | + + | Page 9 | Page 10 | Page 11 | + |--------|---------|---------| + | ![Page 9](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p9_9buttons.png) | ![Page 10](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p10_5buttons.png) | ![Page 11](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p11_1button+1slider.png) + +
+ + domain: automation + input: + haspdevice: + name: "HASP Device" + description: "Select the HASP device" + selector: + device: + integration: mqtt + manufacturer: "HASwitchPlate" + model: "HASPone v1.0.0" + hasppage: + name: "HASP Page" + description: "Select the HASP page (1-11) for the button to be cycled. Refer to the HASP Page and Button reference above." + default: 1 + selector: + number: + min: 1 + max: 11 + mode: slider + unit_of_measurement: page + haspbutton: + name: "HASP Button" + description: "Select the HASP button (4-15) to be cycled. Refer to the HASP Page and Button reference above." + default: 4 + selector: + number: + min: 4 + max: 15 + mode: slider + unit_of_measurement: button + automation01: + name: "Cycle automation 1" + description: "Select the first automation to cycle. This entry is required." + selector: + entity: + domain: automation + automation02: + name: "Cycle automation 2" + description: "Select the second automation to cycle. This entry is required." + selector: + entity: + domain: automation + automation03: + name: "Cycle automation 3" + description: "Select the third automation to cycle. This entry is optional (but cannot be blank), enter 'none' to skip this slot." + default: "none" + selector: + entity: + domain: automation + automation04: + name: "Cycle automation 4" + description: "Select the fourth automation to cycle. This entry is optional (but cannot be blank), enter 'none' to skip this slot." + default: "none" + selector: + entity: + domain: automation + automation05: + name: "Cycle automation 5" + description: "Select the fifth automation to cycle. This entry is optional (but cannot be blank), enter 'none' to skip this slot." + default: "none" + selector: + entity: + domain: automation + automation06: + name: "Cycle automation 6" + description: "Select the sixth automation to cycle. This entry is optional (but cannot be blank), enter 'none' to skip this slot." + default: "none" + selector: + entity: + domain: automation + automation07: + name: "Cycle automation 7" + description: "Select the seventh automation to cycle. This entry is optional (but cannot be blank), enter 'none' to skip this slot." + default: "none" + selector: + entity: + domain: automation + automation08: + name: "Cycle automation 8" + description: "Select the eighth automation to cycle. This entry is optional (but cannot be blank), enter 'none' to skip this slot." + default: "none" + selector: + entity: + domain: automation + automation09: + name: "Cycle automation 9" + description: "Select the nineth automation to cycle. This entry is optional (but cannot be blank), enter 'none' to skip this slot." + default: "none" + selector: + entity: + domain: automation + automation10: + name: "Cycle automation 10" + description: "Select the tenth automation to cycle. This entry is optional (but cannot be blank), enter 'none' to skip this slot." + default: "none" + selector: + entity: + domain: automation + cycleselect: + name: "Automatic cycling" + description: "Optional: enable automation periodic cycling of the selected automations" + default: "Disabled" + selector: + select: + options: + - "Disabled" + - "Default back to automation 1" + - "Continuously cycle automations" + cycletime: + name: "Cycle time" + description: "Idle time in seconds for automatic cycling (if enabled)" + default: 15 + selector: + number: + min: 1 + max: 600 + step: 1 + mode: box + unit_of_measurement: seconds + +mode: restart + +variables: + haspdevice: !input haspdevice + haspname: >- + {%- for entity in device_entities(haspdevice) -%} + {%- if entity|regex_search("^sensor\.") -%} + {{- entity|regex_replace(find="^sensor\.", replace="", ignorecase=true)|regex_replace(find="_sensor(?:_\d+|)$", replace="", ignorecase=true) -}} + {%- endif -%} + {%- endfor -%} + hasppage: !input hasppage + haspbutton: !input haspbutton + haspobject: '{{ "p[" ~ hasppage ~ "].b[" ~ haspbutton ~ "]" }}' + commandtopic: '{{ "hasp/" ~ haspname ~ "/command/" ~ haspobject }}' + jsoncommandtopic: '{{ "hasp/" ~ haspname ~ "/command/json" }}' + automation01: !input automation01 + automation02: !input automation02 + automation03: !input automation03 + automation04: !input automation04 + automation05: !input automation05 + automation06: !input automation06 + automation07: !input automation07 + automation08: !input automation08 + automation09: !input automation09 + automation10: !input automation10 + cycleselect: !input cycleselect + cycletime: !input cycletime + automation_list: >- + {%- set return_list = [] -%} + {%- if automation01|default("none") != "none" and automation01.startswith("automation.") -%} + {%- set return_list = return_list + [automation01] -%} + {%- endif -%} + {%- if automation02|default("none") != "none" and automation02.startswith("automation.") and automation02 not in return_list -%} + {%- set return_list = return_list + [automation02] -%} + {%- endif -%} + {%- if automation03|default("none") != "none" and automation03.startswith("automation.") and automation03 not in return_list -%} + {%- set return_list = return_list + [automation03] -%} + {%- endif -%} + {%- if automation04|default("none") != "none" and automation04.startswith("automation.") and automation04 not in return_list -%} + {%- set return_list = return_list + [automation04] -%} + {%- endif -%} + {%- if automation05|default("none") != "none" and automation05.startswith("automation.") and automation05 not in return_list -%} + {%- set return_list = return_list + [automation05] -%} + {%- endif -%} + {%- if automation06|default("none") != "none" and automation06.startswith("automation.") and automation06 not in return_list -%} + {%- set return_list = return_list + [automation06] -%} + {%- endif -%} + {%- if automation07|default("none") != "none" and automation07.startswith("automation.") and automation07 not in return_list -%} + {%- set return_list = return_list + [automation07] -%} + {%- endif -%} + {%- if automation08|default("none") != "none" and automation08.startswith("automation.") and automation08 not in return_list -%} + {%- set return_list = return_list + [automation08] -%} + {%- endif -%} + {%- if automation09|default("none") != "none" and automation09.startswith("automation.") and automation09 not in return_list -%} + {%- set return_list = return_list + [automation09] -%} + {%- endif -%} + {%- if automation10|default("none") != "none" and automation10.startswith("automation.") and automation10 not in return_list -%} + {%- set return_list = return_list + [automation10] -%} + {%- endif -%}{{ return_list }} + automation_enable_index: >- + {%- set automations = namespace() -%} + {%- set automations.next = 0 -%} + {%- for automation in automation_list -%} + {%- if states(automation_list[loop.index-1]) == "on" and automations.next == 0 and not loop.last -%} + {%- set automations.next = loop.index -%} + {%- endif -%} + {%- endfor -%}{{automations.next}} + +trigger_variables: + haspdevice: !input haspdevice + haspname: >- + {%- for entity in device_entities(haspdevice) -%} + {%- if entity|regex_search("^sensor\.") -%} + {{- 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" }}' + hasppage: !input hasppage + haspbutton: !input haspbutton + haspobject: '{{ "p[" ~ hasppage ~ "].b[" ~ haspbutton ~ "]" }}' + buttonjsonpayload: '{"event_type":"button_short_press","event":"{{haspobject}}","value":"ON"}' + +trigger: + - platform: template + value_template: "{{ is_state(haspsensor, 'ON') }}" + - platform: homeassistant + event: start + - platform: event + event_type: automation_reloaded + - platform: mqtt + topic: "{{jsontopic}}" + payload: "{{buttonjsonpayload}}" + +condition: + - condition: template + value_template: "{{ is_state(haspsensor, 'ON') }}" + +action: + - choose: + ######################################################################### + # Run cycle on button press or when "RUN ACTIONS" is pressed by the user + # This prevents cycling on device connect, home assistant start, or automation reloads + - conditions: "{{ (trigger.platform is none) or (trigger.platform == 'mqtt') }}" + sequence: + ######################################################################### + # Cycle through automation list and disable everything except the "next" + # automation to be enabled (automation_list[automation_enable_index]) + - repeat: + count: "{{automation_list|length}}" + sequence: + - choose: + - conditions: "{{ repeat.index - 1 != automation_enable_index }}" + sequence: + - service: automation.turn_off + data: + stop_actions: true + target: + entity_id: "{{automation_list[repeat.index - 1]}}" + - service: automation.turn_on + target: + entity_id: "{{automation_list[automation_enable_index]}}" + - delay: "00:00:00.2" + - service: automation.trigger + target: + entity_id: "{{automation_list[automation_enable_index]}}" + - choose: + ######################################################################### + # Default back to automation 1 if that option is selected + - conditions: "{{ cycleselect == 'Default back to automation 1' }}" + sequence: + - delay: + seconds: "{{cycletime|int}}" + - service: automation.turn_off + data: + stop_actions: true + target: + entity_id: "{{automation_list[automation_enable_index]}}" + - service: automation.turn_on + target: + entity_id: "{{automation_list[0]}}" + - delay: "00:00:00.2" + - service: automation.trigger + target: + entity_id: "{{automation_list[0]}}" + ######################################################################### + # Continuously cycle automations if that option is selected + - conditions: "{{ cycleselect == 'Continuously cycle automations' }}" + sequence: + - delay: + seconds: "{{cycletime|int}}" + - service: automation.trigger + target: + entity_id: "{{this.entity_id}}" diff --git a/images/hasp_Cycle_Automations.gif b/images/hasp_Cycle_Automations.gif new file mode 100644 index 0000000000000000000000000000000000000000..058412f102b8fa7929e21c916e8ca656e82ccf95 GIT binary patch literal 9890 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