diff --git a/esphome/esp-bedrm1airfilter.yaml b/esphome/esp-bedrm1airfilter.yaml index c8ce93c..d1f11ae 100644 --- a/esphome/esp-bedrm1airfilter.yaml +++ b/esphome/esp-bedrm1airfilter.yaml @@ -12,6 +12,8 @@ # https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-bedrm1airfilter.yaml #:########################################################################################:# # VERSIONS: +# V1.3 2026-04-29 Added restored fan speed so the selected fan speed is remembered +# and re-applied after reboot. # V1.2 2026-04-27 Converted filter replacement logic from pulse-count based to kRevs based. # Added editable Replacement Threshold number in kRevs. # Added Reset Replacement Threshold button to restore default 19000 kRevs. @@ -81,7 +83,7 @@ substitutions: # Project Naming project_name: "Generic.D1 Mini" # Project Details - project_version: "v1.2" # Project V denotes release of yaml file, allowing checking of deployed vs latest version + project_version: "v1.3" # Project V denotes release of yaml file, allowing checking of deployed vs latest version # Passwords and Secrets api_key: !secret esp-api_key @@ -177,6 +179,18 @@ esphome: # Restore Clean/Dirty state and warning LEDs. - script.execute: update_filter_state + # Re-apply the restored fan speed after boot. + # The template number restores its value, but this forces the actual + # MOSFET/PWM output state to match the restored Home Assistant value. + - delay: 1s + - number.set: + id: fan_speed + value: !lambda |- + if (isnan(id(fan_speed).state)) { + return 0.0f; + } + + return id(fan_speed).state; #:########################################################################################:# # ESP PLATFORM AND FRAMEWORK: @@ -297,11 +311,12 @@ button: number: # Slider for Fan Speed 0-6, 0 = Off, 6 = Max - platform: template - name: "${friendly_name} Fan Speed" + name: "${friendly_name} Speed Level" id: fan_speed icon: mdi:air-filter update_interval: never optimistic: true + restore_value: true min_value: 0 max_value: 6 initial_value: 0 diff --git a/esphome/esp-gasheater.yaml b/esphome/esp-gasheater.yaml index 9bb1df4..2d045e7 100644 --- a/esphome/esp-gasheater.yaml +++ b/esphome/esp-gasheater.yaml @@ -264,7 +264,7 @@ preferences: flash_write_interval: 5min mdns: - disabled: false + disabled: true ########################################################################################## # GLOBAL VARIABLES @@ -696,7 +696,7 @@ text_sensor: return std::string("At Temperature"); } if (id(led_standby_red).state) return std::string("Standby"); - return std::string("Off"); + return std::string("Standby"); on_value: - script.execute: publish_status diff --git a/packages/bedroom_1_ikea_air_filter.yaml b/packages/bedroom_1_ikea_air_filter.yaml new file mode 100644 index 0000000..e78cf22 --- /dev/null +++ b/packages/bedroom_1_ikea_air_filter.yaml @@ -0,0 +1,227 @@ +############################################################################### +# Bedroom 1 Ikea Air Filter Package +# File: /config/packages/bedroom_1_ikea_air_filter.yaml +# Version: 1.0 +# Date: 2026-04-29 +# +# Purpose: +# - Creates a dropdown helper called "Bedroom 1 Ikea Fan". +# - Auto mode controls the Bedroom 1 Ikea air filter speed by priority. +# - Manual dropdown modes set a fixed fan speed and disable auto control. +# +# Auto priority order: +# 1. Quiet time on: fan speed 2 +# 2. Bedside light on: fan speed 2 +# 3. Free power time, 21:00 to midnight: fan speed 5 +# 4. Daytime, 09:00 to 15:00: fan speed 4 +# 5. Default: fan speed 2 +############################################################################### + +input_select: + bedroom_1_ikea_fan: + name: "Bedroom 1 Ikea Fan" + icon: mdi:fan + options: + - "Auto" + - "Off" + - "High" + - "Medium" + - "Low" + - "Quiet" + +script: + bedroom_1_ikea_air_filter_set_speed_if_needed: + alias: "Bedroom 1 Ikea Air Filter - Set Speed If Needed" + description: > + Sets the Bedroom 1 Ikea air filter speed only when the requested speed + is different from the current speed. + mode: queued + fields: + target_speed: + name: "Target speed" + description: "Fan speed level from 0 to 6." + example: 2 + sequence: + - variables: + fan_speed_entity: number.bedroom_1_air_filter_bedroom_1_air_filter_speed_level + fan_current_speed: "{{ states(fan_speed_entity) | float(-1) }}" + fan_target_speed: "{{ target_speed | float }}" + + - condition: template + value_template: "{{ fan_current_speed != fan_target_speed }}" + + - action: number.set_value + target: + entity_id: number.bedroom_1_air_filter_bedroom_1_air_filter_speed_level + data: + value: "{{ fan_target_speed | int }}" + +automation: + - id: bedroom_1_ikea_air_filter_auto_control + alias: "Bedroom 1 Ikea Air Filter - Auto Control" + description: > + Automatically controls the Bedroom 1 Ikea air filter speed when the + Bedroom 1 Ikea Fan dropdown is set to Auto. + mode: restart + triggers: + # Re-apply the correct speed after Home Assistant restarts. + - trigger: homeassistant + event: start + + # Highest priority condition. + - trigger: state + entity_id: input_boolean.quiet_time + + # Bedside lamp should force low speed while on. + - trigger: state + entity_id: light.master_bedroom_bedside_lamp_arlec_bulb_master_bedroom_bedside_lamp_arlec_bulb + + # Main timing boundaries. + - trigger: time + at: + - "00:00:00" + - "09:00:00" + - "15:00:00" + - "21:00:00" + + # Safety/re-check trigger, especially for the bedside lamp rule. + - trigger: time_pattern + minutes: "/10" + + conditions: + # Manual dropdown modes disable automatic control. + - condition: state + entity_id: input_select.bedroom_1_ikea_fan + state: "Auto" + + actions: + - variables: + auto_target_speed: >- + {% if is_state('input_boolean.quiet_time', 'on') %} + 2 + {% elif is_state('light.master_bedroom_bedside_lamp_arlec_bulb_master_bedroom_bedside_lamp_arlec_bulb', 'on') %} + 2 + {% elif now().hour >= 21 %} + 5 + {% elif now().hour >= 9 and now().hour < 15 %} + 4 + {% else %} + 2 + {% endif %} + + - action: script.bedroom_1_ikea_air_filter_set_speed_if_needed + data: + target_speed: "{{ auto_target_speed | int }}" + + - id: bedroom_1_ikea_air_filter_dropdown_control + alias: "Bedroom 1 Ikea Air Filter - Dropdown Control" + description: > + Handles the Bedroom 1 Ikea Fan dropdown. Auto enables the automatic + control automation. Manual modes disable automatic control and set + the requested fixed speed. + mode: restart + triggers: + # Apply the selected dropdown mode when it changes. + - trigger: state + entity_id: input_select.bedroom_1_ikea_fan + + # Re-apply the selected dropdown mode after Home Assistant restarts. + - trigger: homeassistant + event: start + + actions: + - choose: + - alias: "Auto mode" + conditions: + - condition: state + entity_id: input_select.bedroom_1_ikea_fan + state: "Auto" + sequence: + - action: automation.turn_on + target: + entity_id: automation.bedroom_1_ikea_air_filter_auto_control + + # Auto starts at speed 2, then immediately evaluates the + # current priority rules. + - action: script.bedroom_1_ikea_air_filter_set_speed_if_needed + data: + target_speed: 2 + + - delay: + seconds: 1 + + - action: automation.trigger + target: + entity_id: automation.bedroom_1_ikea_air_filter_auto_control + data: + skip_condition: false + + - alias: "Off mode" + conditions: + - condition: state + entity_id: input_select.bedroom_1_ikea_fan + state: "Off" + sequence: + - action: automation.turn_off + target: + entity_id: automation.bedroom_1_ikea_air_filter_auto_control + + - action: script.bedroom_1_ikea_air_filter_set_speed_if_needed + data: + target_speed: 0 + + - alias: "High mode" + conditions: + - condition: state + entity_id: input_select.bedroom_1_ikea_fan + state: "High" + sequence: + - action: automation.turn_off + target: + entity_id: automation.bedroom_1_ikea_air_filter_auto_control + + - action: script.bedroom_1_ikea_air_filter_set_speed_if_needed + data: + target_speed: 6 + + - alias: "Medium mode" + conditions: + - condition: state + entity_id: input_select.bedroom_1_ikea_fan + state: "Medium" + sequence: + - action: automation.turn_off + target: + entity_id: automation.bedroom_1_ikea_air_filter_auto_control + + - action: script.bedroom_1_ikea_air_filter_set_speed_if_needed + data: + target_speed: 4 + + - alias: "Low mode" + conditions: + - condition: state + entity_id: input_select.bedroom_1_ikea_fan + state: "Low" + sequence: + - action: automation.turn_off + target: + entity_id: automation.bedroom_1_ikea_air_filter_auto_control + + - action: script.bedroom_1_ikea_air_filter_set_speed_if_needed + data: + target_speed: 2 + + - alias: "Quiet mode" + conditions: + - condition: state + entity_id: input_select.bedroom_1_ikea_fan + state: "Quiet" + sequence: + - action: automation.turn_off + target: + entity_id: automation.bedroom_1_ikea_air_filter_auto_control + + - action: script.bedroom_1_ikea_air_filter_set_speed_if_needed + data: + target_speed: 1 diff --git a/packages/lounge_lighting_scenes.yaml b/packages/lounge_lighting_scenes.yaml index b3499c3..2e06baa 100644 --- a/packages/lounge_lighting_scenes.yaml +++ b/packages/lounge_lighting_scenes.yaml @@ -1,6 +1,42 @@ # yaml-language-server: $schema=null ############################################################################### -# PACKAGE: Lounge scenes (delta-only settings) + manual override +# PACKAGE: Lounge scenes controller +# +# PURPOSE: +# - Provides selectable lounge lighting scenes from Home Assistant. +# - Scenes are selected via input_select.lounge_scene. +# - The selector resets back to "Select Scene:" after each scene is applied, so +# the same scene can be selected repeatedly. +# - Scenes are delta-only: only entities listed in a scene are changed. +# - Manual override can block automatic scene changes. +# - Last remembered scene is tracked so TV automations can avoid changing lights +# when the lounge was deliberately set to All Off. +# +# SUPPORTED SCENE VALUE TYPES: +# - light.*: +# 0 = turn off +# 1-100 = turn on at brightness percentage +# "on" = turn on +# "off" = turn off +# "toggle" = toggle on/off, using 100 percent when turning on +# "toggle_75" = if on, turn off; if off, turn on at 75 percent +# +# - switch.*: +# "on" = turn on +# "off" = turn off +# "toggle" = toggle on/off +# +# - number.*: +# numeric value is sent using number.set_value +# +# - input_number.*: +# numeric value is sent using input_number.set_value +# Example: input_number.openhasp_1_backlight_level: 180 +# +# NOTES: +# - Keep on/off/toggle values quoted for clarity. +# - Numeric values can be left unquoted. +# - Only scenes not listed in excluded_scenes update input_text.lounge_last_scene. ############################################################################### input_select: @@ -49,7 +85,7 @@ script: data: option: "{{ scene_name }}" - # NEW: Wait briefly for HA to commit the new state (avoids race) + # Wait briefly for HA to commit the new state before reading it. - wait_template: "{{ states('input_select.lounge_scene') == scene_name }}" timeout: "00:00:02" continue_on_timeout: true @@ -62,15 +98,19 @@ script: sequence: - variables: sel: "{{ states('input_select.lounge_scene') }}" + ##################################################################### - # EXCLUDED SCENES (these are temp scenes, not remembered as last) + # EXCLUDED SCENES + # These are temporary/action scenes and are not remembered as last. ##################################################################### excluded_scenes: - "Select Scene:" - "Reading On/Off" #- "Night Light" + ##################################################################### - # SCENE SETTINGS (delta only - only list entities you want to change) + # SCENE SETTINGS + # Delta only - only list entities you want to change. ##################################################################### scene_settings: Bright: @@ -91,6 +131,7 @@ script: #switch.esp_loungenorthswitch_relay_1_main_lights: "on" number.esp_loungepelmetleds2_lounge_pelmet_leds_a_output_set_0_100: 100 number.esp_loungepelmetleds2_lounge_pelmet_leds_b_output_set_0_100: 100 + input_number.openhasp_1_backlight_level: 255 TV General: light.esp_lounge6chdimmer_couch_spots_left: 60 @@ -109,6 +150,7 @@ script: #switch.esp_loungenorthswitch_relay_1_main_lights: "on" number.esp_loungepelmetleds2_lounge_pelmet_leds_a_output_set_0_100: 60 number.esp_loungepelmetleds2_lounge_pelmet_leds_b_output_set_0_100: 60 + input_number.openhasp_1_backlight_level: 120 Movie: light.esp_lounge6chdimmer_couch_spots_left: 20 @@ -127,6 +169,7 @@ script: #switch.esp_loungenorthswitch_relay_1_main_lights: "on" number.esp_loungepelmetleds2_lounge_pelmet_leds_a_output_set_0_100: 40 number.esp_loungepelmetleds2_lounge_pelmet_leds_b_output_set_0_100: 40 + input_number.openhasp_1_backlight_level: 15 Reading On/Off: light.esp_lounge6chdimmer_couch_spots_left: "toggle_75" @@ -154,6 +197,7 @@ script: #switch.esp_loungenorthswitch_relay_1_main_lights: "on" number.esp_loungepelmetleds2_lounge_pelmet_leds_a_output_set_0_100: 5 number.esp_loungepelmetleds2_lounge_pelmet_leds_b_output_set_0_100: 5 + input_number.openhasp_1_backlight_level: 8 Warm Cozy: light.esp_lounge6chdimmer_couch_spots_left: 20 @@ -170,6 +214,7 @@ script: #switch.esp_loungenorthswitch_relay_1_main_lights: "on" number.esp_loungepelmetleds2_lounge_pelmet_leds_a_output_set_0_100: 20 number.esp_loungepelmetleds2_lounge_pelmet_leds_b_output_set_0_100: 20 + input_number.openhasp_1_backlight_level: 12 All Off: light.esp_lounge6chdimmer_couch_spots_left: 0 @@ -188,12 +233,11 @@ script: #switch.esp_loungenorthswitch_relay_1_main_lights: "on" number.esp_loungepelmetleds2_lounge_pelmet_leds_a_output_set_0_100: 0 number.esp_loungepelmetleds2_lounge_pelmet_leds_b_output_set_0_100: 0 + #input_number.openhasp_1_backlight_level: 100 - ##################################################################### - ##################################################################### - ##################################################################### - - # Only remember scenes that are not excluded + ######################################################################### + # REMEMBER LAST SCENE + ######################################################################### - choose: - conditions: "{{ sel not in excluded_scenes }}" sequence: @@ -202,6 +246,9 @@ script: data: value: "{{ sel }}" + ######################################################################### + # APPLY SELECTED SCENE + ######################################################################### - repeat: for_each: "{{ (scene_settings.get(sel, {})).keys() | list }}" sequence: @@ -210,23 +257,32 @@ script: val: "{{ (scene_settings.get(sel, {}))[ent] }}" is_light: "{{ ent.startswith('light.') }}" is_switch: "{{ ent.startswith('switch.') }}" + is_number_ent: "{{ ent.startswith('number.') }}" + is_input_number_ent: "{{ ent.startswith('input_number.') }}" is_num: "{{ val is number }}" vstr: "{{ (val|string) if not is_num else '' }}" is_toggle: "{{ (not is_num) and (vstr|lower).startswith('toggle') }}" toggle_pct: "{{ (vstr.split('_')|last)|int if ('_' in vstr) else 100 }}" currently_on: "{{ is_state(ent, 'on') }}" - is_number_ent: "{{ ent.startswith('number.') }}" # 🔹 NEW - choose: - # 0) Numbers: set numeric value (e.g. 0..100) - - conditions: "{{ is_number_ent and is_num }}" # 🔹 NEW + # 0a) Numbers: set numeric value + - conditions: "{{ is_number_ent and is_num }}" sequence: - service: number.set_value data: entity_id: "{{ ent }}" value: "{{ val|float }}" - # 1) Lights: toggle[_NN] -> if on turn off, else turn on to NN (default 100) + # 0b) Input numbers: set numeric value + - conditions: "{{ is_input_number_ent and is_num }}" + sequence: + - service: input_number.set_value + data: + entity_id: "{{ ent }}" + value: "{{ val|float }}" + + # 1) Lights: toggle[_NN] -> if on turn off, else turn on to NN - conditions: "{{ is_light and is_toggle }}" sequence: - choose: @@ -248,13 +304,14 @@ script: data: entity_id: "{{ ent }}" - # 3) Lights: numeric brightness (0 = off) + # 3) Lights: numeric brightness, where 0 = off - conditions: "{{ is_light and is_num and (val|int) > 0 }}" sequence: - service: light.turn_on data: entity_id: "{{ ent }}" brightness_pct: "{{ val|int }}" + - conditions: "{{ is_light and is_num and (val|int) == 0 }}" sequence: - service: light.turn_off @@ -267,6 +324,7 @@ script: - service: light.turn_on data: entity_id: "{{ ent }}" + - conditions: "{{ is_light and not is_num and (val in [false, 'off', 'Off', 'OFF']) }}" sequence: - service: light.turn_off @@ -279,6 +337,7 @@ script: - service: switch.turn_on data: entity_id: "{{ ent }}" + - conditions: "{{ is_switch and (val in [false, 'off', 'Off', 'OFF']) }}" sequence: - service: switch.turn_off @@ -322,7 +381,7 @@ automation: - platform: state entity_id: remote.lounge_tv to: "on" - for: "00:00:02" #small debounce + for: "00:00:02" condition: - condition: state entity_id: input_boolean.lounge_manual_override @@ -340,7 +399,7 @@ automation: - platform: state entity_id: remote.lounge_tv to: "off" - for: "00:00:02" #small debounce + for: "00:00:02" condition: - condition: state entity_id: input_boolean.lounge_manual_override diff --git a/packages/openhasp_lounge1_wallplate.yaml b/packages/openhasp_lounge1_wallplate.yaml index 1de0a94..740841d 100644 --- a/packages/openhasp_lounge1_wallplate.yaml +++ b/packages/openhasp_lounge1_wallplate.yaml @@ -4,41 +4,17 @@ # # PAGE OBJECTS USED # - p1b1 = main temperature display -# - p1b2 = alternating Setpoint / Humidity display +# - p1b2 = setpoint display # - p1b3 = setpoint down # - p1b4 = setpoint up # - p1b5 = heater status text # - p1b6 = heater operation / flame button +# - p1b7 = humidity display ############################################################################### ############################################################################### # HELPERS ############################################################################### -input_boolean: - hasp_setpoint_hold_active: - name: HASP Setpoint Hold Active - initial: false - -input_number: - # How long Setpoint and Humidity are each shown before rotating - hasp_setpoint_humidity_cycle_seconds: - name: HASP Setpoint Humidity Cycle Seconds - min: 1 - max: 60 - step: 1 - mode: box - unit_of_measurement: s - initial: 10 - - # After the setpoint changes, hold the setpoint screen for this long - hasp_setpoint_hold_seconds: - name: HASP Setpoint Hold Seconds - min: 1 - max: 60 - step: 1 - mode: box - unit_of_measurement: s - initial: 15 ############################################################################### # MQTT ENTITIES @@ -48,6 +24,14 @@ mqtt: - name: "Lounge Environment Humidity MQTT" unique_id: "lounge_environment_humidity_mqtt" state_topic: "viewroad-status/lounge-environment/humidity" + value_template: > + {% set raw = value | trim %} + {% if raw.startswith('{') %} + {% set j = raw | from_json %} + {{ j.humidity | default(j.value | default(raw)) }} + {% else %} + {{ raw | replace('%', '') | trim }} + {% endif %} ############################################################################### # AUTOMATIONS @@ -99,35 +83,53 @@ automation: {{ s if s not in ['unknown', 'unavailable', ''] else 'HEATER --' }} # ------------------------------------------------------------------------- - # When setpoint changes: - # - immediately show Setpoint - # - hold it for hold_seconds - # - then allow normal Setpoint/Humidity rotation again + # Setpoint display on left side of centre row + # - Strips any existing units so only one °C is shown # ------------------------------------------------------------------------- - - id: hasp_setpoint_changed_hold_display - alias: "HASP: show setpoint immediately and hold" - mode: restart + - id: hasp_setpoint_to_btn_text + alias: "HASP: update setpoint (p1b2.text)" + mode: queued trigger: - platform: state entity_id: sensor.rinnai_neo_gas_heater_setpoint_text action: - - service: input_boolean.turn_on - target: - entity_id: input_boolean.hasp_setpoint_hold_active - - service: mqtt.publish data: topic: "hasp/openhasp_1/command/p1b2.text" payload: > - {% set s = states('sensor.rinnai_neo_gas_heater_setpoint_text') %} - {{ 'Setpoint: ' ~ (s if s not in ['unknown', 'unavailable', ''] else '--') }} + {% set raw = states('sensor.rinnai_neo_gas_heater_setpoint_text') %} + {% set cleaned = raw + | replace('°C', '') + | replace('° C', '') + | replace('C', '') + | trim %} + {% if cleaned not in ['unknown', 'unavailable', '', 'none', 'None'] %} + Set: {{ cleaned | float(0) | round(1) }}°C + {% else %} + Set: -- °C + {% endif %} - - delay: - seconds: "{{ states('input_number.hasp_setpoint_hold_seconds') | int(15) }}" - - - service: input_boolean.turn_off - target: - entity_id: input_boolean.hasp_setpoint_hold_active + # ------------------------------------------------------------------------- + # Humidity display on right side of centre row + # ------------------------------------------------------------------------- + - id: hasp_humidity_to_btn_text + alias: "HASP: update humidity (p1b7.text)" + mode: queued + trigger: + - platform: state + entity_id: sensor.lounge_environment_humidity_mqtt + action: + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command/p1b7.text" + payload: > + {% set raw = states('sensor.lounge_environment_humidity_mqtt') %} + {% set cleaned = raw | replace('%', '') | trim %} + {% if cleaned in ['unknown', 'unavailable', '', 'none', 'None'] %} + Hum: Unknown + {% else %} + Hum: {{ cleaned }}% + {% endif %} # ------------------------------------------------------------------------- # Up / Down buttons on the HASP plate @@ -298,9 +300,6 @@ automation: event: start action: - service: script.hasp_push_static_texts - - service: script.turn_on - target: - entity_id: script.hasp_rotate_setpoint_humidity # ------------------------------------------------------------------------- # Re-push screen state when panel comes online @@ -316,9 +315,6 @@ automation: action: - delay: "00:00:05" - service: script.hasp_push_static_texts - - service: script.turn_on - target: - entity_id: script.hasp_rotate_setpoint_humidity ############################################################################### # SCRIPTS @@ -332,6 +328,9 @@ script: alias: "HASP: push static texts" mode: queued sequence: + # Push Set/Hum display styling first, so the centre row is not blue + - service: script.hasp_style_center_info_buttons + - service: mqtt.publish data: topic: "hasp/openhasp_1/command/p1b1.text" @@ -359,8 +358,29 @@ script: data: topic: "hasp/openhasp_1/command/p1b2.text" payload: > - {% set s = states('sensor.rinnai_neo_gas_heater_setpoint_text') %} - {{ 'Setpoint: ' ~ (s if s not in ['unknown', 'unavailable', ''] else '--') }} + {% set raw = states('sensor.rinnai_neo_gas_heater_setpoint_text') %} + {% set cleaned = raw + | replace('°C', '') + | replace('° C', '') + | replace('C', '') + | trim %} + {% if cleaned not in ['unknown', 'unavailable', '', 'none', 'None'] %} + Set: {{ cleaned | float(0) | round(1) }}°C + {% else %} + Set: -- °C + {% endif %} + + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command/p1b7.text" + payload: > + {% set raw = states('sensor.lounge_environment_humidity_mqtt') %} + {% set cleaned = raw | replace('%', '') | trim %} + {% if cleaned in ['unknown', 'unavailable', '', 'none', 'None'] %} + Hum: Unknown + {% else %} + Hum: {{ cleaned }}% + {% endif %} # Push middle flame button state - service: mqtt.publish @@ -445,42 +465,45 @@ script: payload: "#0095D9" # ------------------------------------------------------------------------- - # Rotate the middle information line between Setpoint and Humidity - # Pauses humidity display while the setpoint hold flag is active + # Style centre row info buttons as text panels + # - p1b2 = setpoint + # - p1b7 = humidity + # This removes the default blue button styling from these display-only fields. # ------------------------------------------------------------------------- - hasp_rotate_setpoint_humidity: - alias: "HASP: rotate setpoint and humidity" - mode: restart + hasp_style_center_info_buttons: + alias: "HASP: style centre info buttons" + mode: queued sequence: - - repeat: - while: - - condition: template - value_template: "{{ true }}" - sequence: - - service: mqtt.publish - data: - topic: "hasp/openhasp_1/command/p1b2.text" - payload: > - {% set s = states('sensor.rinnai_neo_gas_heater_setpoint_text') %} - {{ 'Setpoint: ' ~ (s if s not in ['unknown', 'unavailable', ''] else '--') }} + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command/p1b2.text_color" + payload: "#FFFFFF" + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command/p1b2.bg_color" + payload: "#000000" + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command/p1b2.bg_grad_color" + payload: "#000000" + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command/p1b2.border_color" + payload: "#000000" - - delay: - seconds: "{{ states('input_number.hasp_setpoint_humidity_cycle_seconds') | int(10) }}" - - - condition: state - entity_id: input_boolean.hasp_setpoint_hold_active - state: "off" - - - service: mqtt.publish - data: - topic: "hasp/openhasp_1/command/p1b2.text" - payload: > - {% set h = states('sensor.lounge_environment_humidity_mqtt') %} - {% if h in ['unknown', 'unavailable', ''] %} - Humidity: Unknown - {% else %} - Humidity: {{ h }}% - {% endif %} - - - delay: - seconds: "{{ states('input_number.hasp_setpoint_humidity_cycle_seconds') | int(10) }}" + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command/p1b7.text_color" + payload: "#FFFFFF" + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command/p1b7.bg_color" + payload: "#000000" + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command/p1b7.bg_grad_color" + payload: "#000000" + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command/p1b7.border_color" + payload: "#000000" diff --git a/packages/openhasp_lounge1_wallplate_backlight.yaml b/packages/openhasp_lounge1_wallplate_backlight.yaml new file mode 100644 index 0000000..afca588 --- /dev/null +++ b/packages/openhasp_lounge1_wallplate_backlight.yaml @@ -0,0 +1,199 @@ +############################################################################### +# PACKAGE: OpenHASP Lounge 1 Wall Plate Backlight Control +# NODE/TOPIC ROOT: hasp/openhasp_1/ +# +# INTENT: +# - Keep backlight control separate from the thermostat/heater UI package. +# - Avoid using brightness 0 as the main OFF method. +# - Wake the panel with "idle off" before applying brightness. +# +# OPENHASP NOTES: +# - backlight brightness range is 1..255 +# - backlight off is a state, not a brightness level +# - idle off clears idle/sleep state and behaves like a wake-up +############################################################################### + +############################################################################### +# HELPERS +############################################################################### +input_boolean: + openhasp_1_backlight_enable: + name: OpenHASP 1 Backlight Enable + icon: mdi:television-ambient-light + initial: true + +input_number: + openhasp_1_backlight_level: + name: OpenHASP 1 Backlight Level + min: 1 + max: 255 + step: 1 + mode: slider + initial: 255 + +############################################################################### +# AUTOMATIONS +############################################################################### +automation: + # ------------------------------------------------------------------------- + # Backlight enable helper changed + # - ON -> wake panel and apply current brightness level + # - OFF -> tell openHASP to switch the backlight off + # ------------------------------------------------------------------------- + - id: openhasp_1_backlight_enable_changed + alias: "OpenHASP 1: backlight enable changed" + mode: restart + trigger: + - platform: state + entity_id: input_boolean.openhasp_1_backlight_enable + action: + - choose: + - conditions: + - condition: state + entity_id: input_boolean.openhasp_1_backlight_enable + state: "on" + sequence: + - service: script.openhasp_1_backlight_apply + + default: + - service: script.openhasp_1_backlight_off + + # ------------------------------------------------------------------------- + # Backlight level helper changed + # - If backlight is enabled, apply the new level immediately + # - If disabled, just store the new level for next time + # ------------------------------------------------------------------------- + - id: openhasp_1_backlight_level_changed + alias: "OpenHASP 1: backlight level changed" + mode: restart + trigger: + - platform: state + entity_id: input_number.openhasp_1_backlight_level + condition: + - condition: state + entity_id: input_boolean.openhasp_1_backlight_enable + state: "on" + action: + - service: script.openhasp_1_backlight_apply + + # ------------------------------------------------------------------------- + # Re-apply requested backlight state when Home Assistant starts + # ------------------------------------------------------------------------- + - id: openhasp_1_backlight_on_ha_start + alias: "OpenHASP 1: apply backlight on HA start" + mode: queued + trigger: + - platform: homeassistant + event: start + action: + - delay: "00:00:10" + - service: script.openhasp_1_backlight_apply_requested_state + + # ------------------------------------------------------------------------- + # Re-apply requested backlight state when the panel reconnects + # ------------------------------------------------------------------------- + - id: openhasp_1_backlight_on_panel_online + alias: "OpenHASP 1: apply backlight on panel online" + mode: queued + trigger: + - platform: mqtt + topic: "hasp/openhasp_1/LWT" + condition: + - condition: template + value_template: "{{ trigger.payload == 'online' }}" + action: + - delay: "00:00:05" + - service: script.openhasp_1_backlight_apply_requested_state + + # ------------------------------------------------------------------------- + # If openHASP enters idle while HA says the backlight should be enabled, + # wake it and re-apply the requested brightness. + # + # This is intended to stop the "black until touched" behaviour when the + # plate's own idle state has blanked the display. + # ------------------------------------------------------------------------- + - id: openhasp_1_backlight_recover_from_idle + alias: "OpenHASP 1: recover backlight from idle" + mode: restart + trigger: + - platform: mqtt + topic: "hasp/openhasp_1/state/idle" + condition: + - condition: state + entity_id: input_boolean.openhasp_1_backlight_enable + state: "on" + - condition: template + value_template: "{{ trigger.payload | lower != 'off' }}" + action: + - delay: "00:00:01" + - service: script.openhasp_1_backlight_apply + +############################################################################### +# SCRIPTS +############################################################################### +script: + # ------------------------------------------------------------------------- + # Apply current requested brightness. + # + # Important: + # - Send idle off first so the panel is awake. + # - Then send a JSON backlight command with state ON and brightness 1..255. + # ------------------------------------------------------------------------- + openhasp_1_backlight_apply: + alias: "OpenHASP 1: apply backlight level" + mode: restart + sequence: + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command" + payload: "idle off" + + - delay: "00:00:00.25" + + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command" + payload: > + backlight {"state":"on","brightness":{{ states('input_number.openhasp_1_backlight_level') | int(255) }}} + + # Send once more shortly after. This helps with panels that miss the + # brightness change while coming out of idle. + - delay: "00:00:00.75" + + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command" + payload: > + backlight {"state":"on","brightness":{{ states('input_number.openhasp_1_backlight_level') | int(255) }}} + + # ------------------------------------------------------------------------- + # Turn the backlight off using the openHASP backlight OFF state. + # Do not set brightness to 0. + # ------------------------------------------------------------------------- + openhasp_1_backlight_off: + alias: "OpenHASP 1: backlight off" + mode: restart + sequence: + - service: mqtt.publish + data: + topic: "hasp/openhasp_1/command" + payload: "backlight off" + + # ------------------------------------------------------------------------- + # Apply either ON/current-level or OFF according to helper state. + # Used on HA startup and panel reconnect. + # ------------------------------------------------------------------------- + openhasp_1_backlight_apply_requested_state: + alias: "OpenHASP 1: apply requested backlight state" + mode: restart + sequence: + - choose: + - conditions: + - condition: state + entity_id: input_boolean.openhasp_1_backlight_enable + state: "on" + sequence: + - service: script.openhasp_1_backlight_apply + + default: + - service: script.openhasp_1_backlight_off