Compare commits

...

17 Commits

Author SHA1 Message Date
zorruno 79597fb021 various package updates 2026-06-19 11:38:07 +12:00
ESPHome Device Builder d4f123600c Edit esp-masterbdrmwest.yaml 2026-06-12 22:18:54 +12:00
ESPHome Device Builder ca1f1637cb Edit esp-masterbdrmsouth.yaml 2026-06-12 22:17:50 +12:00
ESPHome Device Builder 02f3e44248 Edit esp-masterbdrmwest.yaml 2026-06-12 22:03:22 +12:00
ESPHome Device Builder 6276d6fb90 Edit esp-masterbdrmsouth.yaml 2026-06-12 22:03:22 +12:00
ESPHome Device Builder 3423c18e8e Edit esp-gasheater.yaml 2026-06-12 17:37:07 +12:00
ESPHome Device Builder a839326029 Delete esp-gasheater copy.yaml 2026-06-12 17:32:53 +12:00
ESPHome Device Builder a9ce83fcc4 Add esp-gasheater copy.yaml 2026-06-12 17:32:06 +12:00
ESPHome Device Builder 3955435c9e Edit esp-gasheater.yaml 2026-06-12 17:32:06 +12:00
ESPHome Device Builder 6e2d5bee4d Edit esp-bedrm1airfilter.yaml 2026-06-12 12:35:31 +12:00
ESPHome Device Builder 892c7c4577 Edit esp-masterbdrmwest.yaml 2026-06-12 12:32:02 +12:00
ESPHome Device Builder 21c40e6ec7 Edit esp-masterbdrmsouth.yaml 2026-06-12 12:32:02 +12:00
ESPHome Device Builder b9874c5053 Edit esp-masterbdrmwest.yaml 2026-06-11 22:27:00 +12:00
ESPHome Device Builder 4b0d8b573c Edit esp-masterbdrmsouth.yaml 2026-06-11 22:27:00 +12:00
ESPHome Device Builder 1989b45a87 Add esp-masterbdrmwest.yaml 2026-06-11 22:22:00 +12:00
ESPHome Device Builder 95144489af Add esp-masterbdrmsouth.yaml 2026-06-11 22:22:00 +12:00
ESPHome Device Builder 005e653f73 Edit esp-loungeenvironment.yaml 2026-06-10 20:57:09 +12:00
22 changed files with 3845 additions and 441 deletions
+1 -1
View File
@@ -1 +1 @@
{"pid": 71, "version": 1, "ha_version": "2026.6.0", "start_ts": 1780535631.6044178}
{"pid": 73, "version": 1, "ha_version": "2026.6.3", "start_ts": 1781782769.6367064}
+235 -3
View File
@@ -295,9 +295,6 @@
"esp-bedrm2ceilingfan.yaml": {
"board_id": "d1-mini"
},
"_remote_build": {
"dashboard_id": "vhamVDnWEnx-FvAa24uVv49Wsddurh84"
},
"esp-entbtproxy copy.yaml": {
"board_id": "generic-esp32"
},
@@ -306,6 +303,231 @@
"mac_address": "88:13:BF:68:39:2C"
},
"_firmware_jobs": [
{
"job_id": "dfabed27adf5",
"configuration": "esp-masterbdrmwest.yaml",
"job_type": "upload",
"status": "completed",
"created_at": "2026-06-12T10:19:14.926585+00:00",
"started_at": "2026-06-12T10:22:25.692593+00:00",
"completed_at": "2026-06-12T10:22:49.018883+00:00",
"exit_code": 0,
"error": null,
"port": "OTA",
"new_name": "",
"depends_on": "b03c87e5954e",
"dependency_released": true,
"progress": 100,
"remote_peer": "",
"remote_job_id": "",
"remote_peer_label": "",
"device_name": "",
"device_friendly_name": "",
"source": "local",
"source_pin_sha256": "",
"source_label": "",
"source_esphome_version": ""
},
{
"job_id": "b03c87e5954e",
"configuration": "esp-masterbdrmwest.yaml",
"job_type": "compile",
"status": "completed",
"created_at": "2026-06-12T10:19:14.926538+00:00",
"started_at": "2026-06-12T10:20:08.467305+00:00",
"completed_at": "2026-06-12T10:22:25.683155+00:00",
"exit_code": 0,
"error": null,
"port": "",
"new_name": "",
"depends_on": "",
"dependency_released": false,
"progress": null,
"remote_peer": "",
"remote_job_id": "",
"remote_peer_label": "",
"device_name": "",
"device_friendly_name": "",
"source": "local",
"source_pin_sha256": "",
"source_label": "",
"source_esphome_version": ""
},
{
"job_id": "0dc6aca98f76",
"configuration": "esp-masterbdrmsouth.yaml",
"job_type": "upload",
"status": "completed",
"created_at": "2026-06-12T10:17:56.852471+00:00",
"started_at": "2026-06-12T10:20:08.462596+00:00",
"completed_at": "2026-06-12T10:20:24.266523+00:00",
"exit_code": 0,
"error": null,
"port": "OTA",
"new_name": "",
"depends_on": "b299a38f73d6",
"dependency_released": true,
"progress": 100,
"remote_peer": "",
"remote_job_id": "",
"remote_peer_label": "",
"device_name": "",
"device_friendly_name": "",
"source": "local",
"source_pin_sha256": "",
"source_label": "",
"source_esphome_version": ""
},
{
"job_id": "b299a38f73d6",
"configuration": "esp-masterbdrmsouth.yaml",
"job_type": "compile",
"status": "completed",
"created_at": "2026-06-12T10:17:56.852426+00:00",
"started_at": "2026-06-12T10:17:56.853172+00:00",
"completed_at": "2026-06-12T10:20:08.461368+00:00",
"exit_code": 0,
"error": null,
"port": "",
"new_name": "",
"depends_on": "",
"dependency_released": false,
"progress": null,
"remote_peer": "",
"remote_job_id": "",
"remote_peer_label": "",
"device_name": "",
"device_friendly_name": "",
"source": "local",
"source_pin_sha256": "",
"source_label": "",
"source_esphome_version": ""
},
{
"job_id": "0a4602631e6e",
"configuration": "esp-nspanel1masterbdrm.yaml",
"job_type": "compile",
"status": "cancelled",
"created_at": "2026-06-12T08:31:00.645286+00:00",
"started_at": "2026-06-12T08:31:00.645530+00:00",
"completed_at": "2026-06-12T08:31:07.627462+00:00",
"exit_code": -15,
"error": null,
"port": "",
"new_name": "",
"depends_on": "",
"dependency_released": false,
"progress": null,
"remote_peer": "",
"remote_job_id": "",
"remote_peer_label": "",
"device_name": "",
"device_friendly_name": "",
"source": "local",
"source_pin_sha256": "",
"source_label": "",
"source_esphome_version": ""
},
{
"job_id": "0b7b51e5af2a",
"configuration": "esp-gasheater.yaml",
"job_type": "upload",
"status": "completed",
"created_at": "2026-06-12T05:37:12.378996+00:00",
"started_at": "2026-06-12T05:40:20.938981+00:00",
"completed_at": "2026-06-12T05:40:40.151165+00:00",
"exit_code": 0,
"error": null,
"port": "OTA",
"new_name": "",
"depends_on": "4bb434e78efe",
"dependency_released": true,
"progress": 100,
"remote_peer": "",
"remote_job_id": "",
"remote_peer_label": "",
"device_name": "",
"device_friendly_name": "",
"source": "local",
"source_pin_sha256": "",
"source_label": "",
"source_esphome_version": ""
},
{
"job_id": "4bb434e78efe",
"configuration": "esp-gasheater.yaml",
"job_type": "compile",
"status": "completed",
"created_at": "2026-06-12T05:37:12.378923+00:00",
"started_at": "2026-06-12T05:37:12.379876+00:00",
"completed_at": "2026-06-12T05:40:20.934723+00:00",
"exit_code": 0,
"error": null,
"port": "",
"new_name": "",
"depends_on": "",
"dependency_released": false,
"progress": null,
"remote_peer": "",
"remote_job_id": "",
"remote_peer_label": "",
"device_name": "",
"device_friendly_name": "",
"source": "local",
"source_pin_sha256": "",
"source_label": "",
"source_esphome_version": ""
},
{
"job_id": "6f388364d3b9",
"configuration": "esp-loungeenvironment.yaml",
"job_type": "upload",
"status": "completed",
"created_at": "2026-06-10T08:58:25.598581+00:00",
"started_at": "2026-06-10T09:04:32.328122+00:00",
"completed_at": "2026-06-10T09:04:56.870002+00:00",
"exit_code": 0,
"error": null,
"port": "OTA",
"new_name": "",
"depends_on": "42c6b98c9a18",
"dependency_released": true,
"progress": 100,
"remote_peer": "",
"remote_job_id": "",
"remote_peer_label": "",
"device_name": "",
"device_friendly_name": "",
"source": "local",
"source_pin_sha256": "",
"source_label": "",
"source_esphome_version": ""
},
{
"job_id": "42c6b98c9a18",
"configuration": "esp-loungeenvironment.yaml",
"job_type": "compile",
"status": "completed",
"created_at": "2026-06-10T08:58:25.598449+00:00",
"started_at": "2026-06-10T08:58:25.599111+00:00",
"completed_at": "2026-06-10T09:04:32.319499+00:00",
"exit_code": 0,
"error": null,
"port": "",
"new_name": "",
"depends_on": "",
"dependency_released": false,
"progress": null,
"remote_peer": "",
"remote_job_id": "",
"remote_peer_label": "",
"device_name": "",
"device_friendly_name": "",
"source": "local",
"source_pin_sha256": "",
"source_label": "",
"source_esphome_version": ""
},
{
"job_id": "06e53abdd6ff",
"configuration": "esp-3dprinterpow.yaml",
@@ -2750,5 +2972,15 @@
"esp-ledmatrix1 copy.yaml": {
"board_id": "generic-esp32c3",
"mac_address": "64:E8:33:84:FD:C8"
},
"esp-masterbdrmwest.yaml": {
"mac_address": "84:F3:EB:EC:6E:3F"
},
"esp-masterbdrmsouth.yaml": {
"mac_address": "84:F3:EB:EC:6E:60"
},
"_board_id_user_set_migrated": true,
"_dashboard_identity": {
"dashboard_id": "vhamVDnWEnx-FvAa24uVv49Wsddurh84"
}
}
+1 -1
View File
@@ -79,7 +79,7 @@ substitutions:
device_name: "esp-bedrm1airfilter"
friendly_name: "Bedroom 1 Air Filter"
description_comment: "Wall Mount Air Filter :: D1 Mini ESP8266 (Layout V1.1)"
device_area: "Bedroom 1" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant.
device_area: "Master Bedroom" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant.
# Project Naming
project_name: "Generic.D1 Mini" # Project Details
File diff suppressed because it is too large Load Diff
+106 -32
View File
@@ -6,6 +6,10 @@
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-gasheater.yaml
#:########################################################################################:#
# VERSIONS:
# V1.12 2026-06-12 Added managed OFF mode separate from BYPASS (Remote Only).
# OFF is now the normal startup/thermostat-off mode and will
# take over if the heater is locally started via the physical button.
# BYPASS remains a true hands-off/remote-only service mode.
# V1.11 2026-04-26 Restored Heater Operation after regression; restored fast optimistic
# master setpoint number with debounced MQTT publish; restored proportional
# flame tuning; kept current GPIO12/13/14 relay mapping
@@ -65,12 +69,19 @@
# - AUTO may still be selected manually later for testing
# - Heater Operation is a high-level thermostat enable helper:
# - ON = turn Test Mode OFF (if needed) and set Mode to AUTO
# - OFF = force heater Power OFF and set Mode to BYPASS (Remote Only)
# - In any mode except BYPASS (Remote Only), if the controller intends heater power OFF
# but either heater LED still shows the heater ON, ESPHome will pulse the heater ON/OFF
# button to force it fully OFF.
# - OFF = force heater Power OFF and set Mode to OFF
# - OFF mode is the normal thermostat-off / managed-standby mode:
# - The controller will not call for heat by itself.
# - If the heater is locally started using the physical heater button, the LEDs will
# show that the heater is ON and ESPHome will switch to AUTO and take over.
# - BYPASS (Remote Only) is a true hands-off / service mode:
# - ESPHome may still observe the LEDs/status.
# - ESPHome will not enforce OFF and will not take over after a local physical start.
# - In AUTO or MANUAL, if the controller intends heater power OFF but either heater LED
# still shows the heater ON, ESPHome will pulse the heater ON/OFF button to force it
# fully OFF.
# - On every ESPHome restart (including yaml upload or power loss), the controller first
# forces the heater OFF, then returns to BYPASS (Remote Only).
# forces the heater OFF, then returns to OFF mode.
# - On power ON in AUTO, the controller:
# - Applies a warmup lockout
# - Calibrates to level 1 using 6x DOWN
@@ -120,7 +131,7 @@ substitutions:
# Project Naming
project_name: "Sonoff Technologies.Sonoff DEV"
project_version: "v1.11"
project_version: "v1.12"
# Passwords & Secrets
api_key: !secret esp-api_key
@@ -421,14 +432,30 @@ binary_sensor:
if (!id(test_mode_enabled).state && id(mode_select).state == "AUTO") {
id(warmup_until_ms) = millis() + (${warmup_seconds} * 1000UL);
}
# ESPHome does not support Home Assistant's `choose:` action here,
# so this is written as nested if/else logic.
- if:
condition:
lambda: 'return !id(test_mode_enabled).state && id(mode_select).state != "BYPASS (Remote Only)" && !id(power_intended_on) && !id(self_power_on);'
# OFF mode watches for local physical heater starts.
# If someone presses the real heater ON button, the LEDs come on, this
# powered_state turns ON, and ESPHome changes to AUTO and takes over.
lambda: 'return !id(test_mode_enabled).state && id(mode_select).state == "OFF" && !id(self_power_on);'
then:
- script.execute: local_on_takeover
else:
- if:
condition:
# In AUTO/MANUAL, if the controller intended the heater OFF but LEDs say ON,
# force the heater OFF. OFF is excluded because OFF performs local-start takeover.
lambda: 'return !id(test_mode_enabled).state && id(mode_select).state != "BYPASS (Remote Only)" && id(mode_select).state != "OFF" && !id(power_intended_on) && !id(self_power_on);'
then:
- script.execute: enforce_heater_off
else:
- if:
condition:
lambda: 'return !id(test_mode_enabled).state && (id(mode_select).state == "AUTO") && id(power_intended_on) && (!id(self_power_on));'
# If the heater turns on externally while AUTO already intended it ON,
# re-baseline after warmup and continue normal automatic control.
lambda: 'return !id(test_mode_enabled).state && id(mode_select).state == "AUTO" && id(power_intended_on) && !id(self_power_on);'
then:
- lambda: |-
id(ramp_until_ms) = millis() + (${fast_ramp_seconds} * 1000UL);
@@ -493,7 +520,8 @@ switch:
# High-level thermostat enable helper.
# ON = thermostat logic active in AUTO
# OFF = force BYPASS and turn heater power OFF
# OFF = managed OFF mode. Heater is forced OFF, but local physical starts are monitored
# and will cause ESPHome to switch back to AUTO and take over.
- platform: template
id: heater_operation_switch
name: "Heater Operation"
@@ -514,10 +542,9 @@ switch:
turn_off_action:
- lambda: |-
id(power_intended_on) = false;
- switch.turn_off: power_switch
- select.set:
id: mode_select
option: "BYPASS (Remote Only)"
option: "OFF"
- platform: template
id: power_switch
@@ -650,31 +677,42 @@ text_sensor:
}
if (use_test_temp) {
if (mode == "OFF") {
return std::string("Off (Test)");
}
if (mode == "BYPASS (Remote Only)") {
return std::string("Bypass (Remote Only) (Test)");
}
if (mode == "MANUAL") {
if (id(powered_state).state) {
char buf[48];
snprintf(buf, sizeof(buf), "Manual (Level %d) (Test)", id(current_level));
return std::string(buf);
}
if (id(led_standby_red).state) return std::string("Manual (Standby) (Test)");
return std::string("Manual (Off) (Test)");
}
if (id(powered_state).state) {
char buf[40];
snprintf(buf, sizeof(buf), "Operating (Level %d) (Test)", id(current_level));
snprintf(buf, sizeof(buf), "Auto (Level %d) (Test)", id(current_level));
return std::string(buf);
}
if (mode == "AUTO" && !isnan(effective_t) && effective_t >= at_temp_threshold) {
return std::string("At Temperature (Test)");
return std::string("Auto (At Temperature) (Test)");
}
return std::string("Off (Test)");
return std::string("Auto (Standby) (Test)");
}
if (mode == "OFF") {
return std::string("Off");
}
if (mode == "BYPASS (Remote Only)") {
if (id(led_operating_blue).state) return std::string("Remote Only (Operating)");
if (id(led_standby_red).state) return std::string("Remote Only (Standby)");
return std::string("Remote Only (Off)");
if (id(led_operating_blue).state) return std::string("Bypass (Operating)");
if (id(led_standby_red).state) return std::string("Bypass (Standby)");
return std::string("Bypass (Remote Only)");
}
if (mode == "MANUAL") {
@@ -689,14 +727,14 @@ text_sensor:
if (id(led_operating_blue).state) {
char buf[32];
snprintf(buf, sizeof(buf), "Operating (Level %d)", id(current_level));
snprintf(buf, sizeof(buf), "Auto (Level %d)", id(current_level));
return std::string(buf);
}
if (!isnan(effective_t) && effective_t >= at_temp_threshold) {
return std::string("At Temperature");
return std::string("Auto (At Temperature)");
}
if (id(led_standby_red).state) return std::string("Standby");
return std::string("Standby");
if (id(led_standby_red).state) return std::string("Auto (Standby)");
return std::string("Auto (Standby)");
on_value:
- script.execute: publish_status
@@ -728,18 +766,24 @@ select:
- platform: template
id: mode_select
name: "Mode"
options: ["AUTO","MANUAL","BYPASS (Remote Only)"]
options: ["OFF","AUTO","MANUAL","BYPASS (Remote Only)"]
optimistic: true
initial_option: "BYPASS (Remote Only)"
initial_option: "OFF"
restore_value: false
set_action:
- lambda: |-
if (x == "BYPASS (Remote Only)") {
if (x == "OFF" || x == "BYPASS (Remote Only)") {
id(power_intended_on) = false;
}
if (x != "AUTO") {
id(room_temp_lost_fault) = false;
}
- if:
condition:
lambda: 'return x == "OFF" && !id(test_mode_enabled).state && id(powered_state).state;'
then:
- logger.log: "Mode OFF selected: heater appears ON, pulsing power to force OFF."
- script.execute: press_power
- platform: template
id: flame_level_select
@@ -932,13 +976,15 @@ interval:
then:
- script.execute: auto_control_tick
# Outside BYPASS, if the controller intends power OFF but LEDs still show ON,
# Outside OFF/BYPASS, if the controller intends power OFF but LEDs still show ON,
# keep checking and force the heater OFF.
# OFF is excluded because it deliberately watches for local physical starts and
# switches to AUTO instead of forcing the heater back off.
- interval: ${power_off_check_interval_s}s
then:
- if:
condition:
lambda: 'return !id(test_mode_enabled).state && id(mode_select).state != "BYPASS (Remote Only)" && !id(power_intended_on) && id(powered_state).state && !id(self_power_on);'
lambda: 'return !id(test_mode_enabled).state && id(mode_select).state != "BYPASS (Remote Only)" && id(mode_select).state != "OFF" && !id(power_intended_on) && id(powered_state).state && !id(self_power_on);'
then:
- script.execute: enforce_heater_off
@@ -984,7 +1030,29 @@ script:
id(powered_state).publish_state(new_state);
# Force heater OFF after an ESPHome reboot before returning to BYPASS.
# OFF-mode local physical-start takeover.
# When Mode is OFF and the heater is started from its own front panel, the LED
# inputs make powered_state turn ON. This script changes to AUTO, warms up,
# re-baselines the flame level, and resumes thermostat control.
- id: local_on_takeover
mode: restart
then:
- logger.log: "OFF mode: local physical heater start detected. Switching to AUTO and taking over."
- lambda: |-
id(power_intended_on) = true;
id(warmup_until_ms) = millis() + (${warmup_seconds} * 1000UL);
id(ramp_until_ms) = millis() + (${fast_ramp_seconds} * 1000UL);
- select.set:
id: mode_select
option: "AUTO"
- delay: ${warmup_seconds}s
- script.execute: calibrate_to_min
- script.execute: auto_control_tick
- script.execute: adjust_to_desired
- lambda: |-
id(self_power_on) = false;
# Force heater OFF after an ESPHome reboot before returning to OFF.
- id: boot_force_heater_off
mode: restart
then:
@@ -1003,12 +1071,13 @@ script:
- script.execute: recompute_powered_state
- select.set:
id: mode_select
option: "BYPASS (Remote Only)"
option: "OFF"
- lambda: |-
id(power_intended_on) = false;
# In non-BYPASS modes, if the controller intends power OFF but LEDs still show ON,
# In AUTO/MANUAL, if the controller intends power OFF but LEDs still show ON,
# pulse the heater ON/OFF button to force the heater fully OFF.
# OFF mode is excluded because a physical start in OFF should trigger AUTO takeover.
- id: enforce_heater_off
mode: restart
then:
@@ -1016,7 +1085,7 @@ script:
- script.execute: recompute_powered_state
- if:
condition:
lambda: 'return !id(test_mode_enabled).state && id(mode_select).state != "BYPASS (Remote Only)" && !id(power_intended_on) && id(powered_state).state && !id(self_power_on);'
lambda: 'return !id(test_mode_enabled).state && id(mode_select).state != "BYPASS (Remote Only)" && id(mode_select).state != "OFF" && !id(power_intended_on) && id(powered_state).state && !id(self_power_on);'
then:
- logger.log: "Heater should be OFF but LEDs still show ON. Pulsing power to force OFF."
- script.execute: press_power
@@ -1125,7 +1194,7 @@ script:
then:
- if:
condition:
lambda: 'return id(mode_select).state != "BYPASS (Remote Only)" && id(powered_state).state;'
lambda: 'return id(mode_select).state != "BYPASS (Remote Only)" && id(mode_select).state != "OFF" && id(powered_state).state;'
then:
- lambda: |-
id(calibrating) = true;
@@ -1245,6 +1314,11 @@ script:
mode: queued
then:
- lambda: |-
if (id(mode_select).state != "AUTO") {
ESP_LOGD("gas", "AUTO: skipping control tick because Mode is not AUTO");
return;
}
const bool use_test_temp = id(test_mode_enabled).state;
const float t = use_test_temp ? id(test_temp_c) : id(room_temp).state;
const float sp = id(setpoint_c);
+1 -1
View File
@@ -352,7 +352,7 @@ sensor:
state_class: measurement
accuracy_decimals: 2
filters:
- offset: -2.9
- offset: 0.27
on_value:
then:
# Publish current temperature to local MQTT status topic
+602
View File
@@ -0,0 +1,602 @@
#:########################################################################################:#
# TITLE: MAIN BEDROOM MAIN LIGHTS SOUTH
# zorruno.com layout v1.1 2026
#:########################################################################################:#
# REPO:
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-masterbdrmsouth.yaml
#:########################################################################################:#
# VERSIONS:
# v1.3 2026-06-12 Reassigned logical controls after live wiring checks.
# Relay 1 on both switches = North Lights virtual/Tasmota control.
# Relay 2 on both switches = South Lights control; WEST Relay 2 is physical load.
# Button 1 now controls North Lights; Button 2 now controls South Lights.
# v1.2 2026-06-12 Reworked after live wiring checks.
# Confirmed North Lights are controlled by Tasmota/iFan02 Power1.
# Confirmed South Lights physical load is on WEST switch Relay 2.
# v1.1 2026-06-12 North Lights controlled via Tasmota iFan02 MQTT cmnd/stat topics.
# v1.0 2026-06-11 Initial YAML for Master Bedroom South 3-gang switch.
#:########################################################################################:#
# HARDWARE:
# - Zemismart KS-811 Triple push button (ESP8266)
# - Pinout/Schematic: https://community.home-assistant.io/t/zemismart-ks-811-working-with-esphome/
#:########################################################################################:#
# OPERATION NOTES:
# - 3-gang KS-811 triple switch.
# - Relay/Button 1 = Master Bedroom North Lights.
# * Actual North Lights are controlled by Tasmota/iFan02 light.tasmo_ifan02_3793_bedrm1_1.
# * Button 1 sends MQTT TOGGLE to the Tasmota POWER1 command topic.
# * Relay 1 is virtual/mirror only and follows Tasmota POWER1 feedback.
# * Relay 1 can also be switched from HA; ON/OFF sends explicit Tasmota ON/OFF.
# - Relay/Button 2 = Master Bedroom South Lights.
# * On this SOUTH switch, Relay 2 is virtual/mirror/control only.
# * Actual South Lights physical load is WEST switch Relay 2.
# * Button 2 toggles Relay 2, which sends the South Lights command to the WEST switch.
# - Button 3 = Bedside Lamp Jump/Fade via Home Assistant action.
# * Requires ESPHome integration permission: "Allow the device to perform Home Assistant actions".
# - Relay 3 = unused output. Forced off/internal; Button 3 does NOT toggle Relay 3.
# - GPIO16 does not support interrupts; polling is used for Button 1 (suppresses warning).
#:########################################################################################:#
# MQTT COMMANDS:
# - North Lights Tasmota command topic:
# ${north_lights_tasmota_command_topic}
# Payload "TOGGLE" -> iFan02 Light toggle
# Payload "ON" -> iFan02 Light ON
# Payload "OFF" -> iFan02 Light OFF
# Empty payload -> Query current POWER1 state
#
# - North Lights Tasmota status topic:
# ${north_lights_tasmota_status_topic}
# Payload "ON" / "OFF" updates Relay 1 mirror state on both switches.
#
# - North Lights local command topic:
# ${mqtt_local_command_topic1}/set
# Payload "On" / "ON" -> North Lights ON via Tasmota POWER1
# Payload "Off" / "OFF" -> North Lights OFF via Tasmota POWER1
# NOTE: TOGGLE is intentionally not handled here to avoid double-toggle if both switches receive it.
#
# - North Lights retained status/sync topic:
# ${mqtt_local_status_topic1}/state
# Payload "On" / "Off" mirrors the actual North Lights state.
#
# - South Lights local command topic:
# ${mqtt_local_command_topic2}/set
# Payload "On" / "ON" -> South Lights ON via WEST Relay 2
# Payload "Off" / "OFF" -> South Lights OFF via WEST Relay 2
# NOTE: TOGGLE is intentionally not handled here to avoid ambiguous multi-device toggles.
#
# - South Lights retained status/sync topic:
# ${mqtt_local_status_topic2}/state
# Payload "On" / "Off" mirrors the actual South Lights state.
#:########################################################################################:#
# OFFLINE NOTES:
# a) Home Assistant OFFLINE, WiFi OK, MQTT broker OK
# - Button 1 controls the North Lights through direct Tasmota MQTT command.
# - Button 2 controls the South Lights through MQTT command to the WEST switch.
# - Tasmota feedback updates Relay 1 mirror via stat POWER1.
# - WEST Relay 2 status updates Relay 2 mirror via the South Lights retained status topic.
# - Button 3 cannot toggle the bedside lamp because it requires Home Assistant/API.
#
# b) MQTT OFFLINE (or broker unreachable), Home Assistant/API OK
# - Button 1 attempts HA action fallback to light.toggle the Tasmota/iFan02 HA entity.
# - Button 2 attempts HA action fallback to the WEST physical South Lights relay entity.
# - Button 3 can still toggle the bedside lamp via Home Assistant/API.
#
# c) Home Assistant and MQTT OFFLINE, WiFi OK or entire network OFFLINE
# - This SOUTH switch cannot control the remote loads.
# - Button 3 cannot control the bedside lamp.
# - Accurate time is NOT needed (SNTP not needed).
#:########################################################################################:#
#:########################################################################################:#
# SUBSTITUTIONS: Specific device variable substitutions
# If NOT using a secrets file, just replace these with the passwords etc (in quotes)
#:########################################################################################:#
substitutions:
# Device Naming
device_name: "esp-masterbdrmsouth"
friendly_name: "Master Bedroom Main Lights South"
description_comment: "Master Bedroom Main Lights South using a Zemismart KS-811 Triple Push Button. Relay/Button 1 North Lights Tasmota/iFan02 virtual mirror, Relay/Button 2 South Lights virtual control, Button 3 toggles bedside lamp jump/fade. (Layout V1.1)"
device_area: "Master Bedroom"
# Project Naming
project_name: "Zemismart Technologies.KS-811 Triple"
project_version: "v1.3"
# Passwords & Secrets
api_key: !secret esp-api_key
ota_pass: !secret esp-ota_pass
static_ip_address: !secret esp-masterbdrmsouth_ip
mqtt_command_main_topic: !secret mqtt_command_main_topic
mqtt_status_main_topic: !secret mqtt_status_main_topic
# If we are changing IP addresses, you must update the current IP address here, otherwise it remains
# Don't forget to switch it back when changed.
current_ip_address: ${static_ip_address}
# Device Settings
log_level: "INFO"
update_interval: "60s"
# MQTT LOCAL Controls
# NOTE: device_name1 is North Lights and maps to Relay 1 / Tasmota/iFan02 light.
mqtt_device_name1: "masterbrdm-northlights"
mqtt_local_command_topic1: "${mqtt_command_main_topic}/${mqtt_device_name1}"
mqtt_local_status_topic1: "${mqtt_status_main_topic}/${mqtt_device_name1}"
# NOTE: device_name2 is South Lights and maps to Relay 2 / WEST switch physical load.
mqtt_device_name2: "masterbrdm-southlights"
mqtt_local_command_topic2: "${mqtt_command_main_topic}/${mqtt_device_name2}"
mqtt_local_status_topic2: "${mqtt_status_main_topic}/${mqtt_device_name2}"
# Tasmota/iFan02 North Lights MQTT control and feedback
# NOTE: Tasmota status topic observed as uppercase POWER1. MQTT topics are case-sensitive.
north_lights_tasmota_command_topic: "cmnd/tasmo-ifan02-3793-bedrm1-1/POWER1"
north_lights_tasmota_status_topic: "stat/tasmo-ifan02-3793-bedrm1-1/POWER1"
# Home Assistant fallback entities
north_lights_ha_entity: "light.tasmo_ifan02_3793_bedrm1_1"
# NOTE: If HA renames WEST Relay 2 after this YAML name correction, update this entity_id.
south_lights_physical_entity: "switch.master_bedroom_master_bedroom_main_lights_west_relay_2_master_bedroom_south_lights"
# Switch/Relay/Button Naming
switch_1_name: "Master Bedroom North Lights"
switch_2_name: "Master Bedroom South Lights"
switch_3_name: "Bedside Lamp Jump/Fade" # NOTE: Relay 3 output is unused and forced off
# Home Assistant bedside lamp entity controlled by Button 3
bedside_lamp_entity: "switch.master_bedroom_bedside_lamp_arlec_bulb_master_bedroom_bedside_lamp_arlec_bulb_jump_fade_up_down"
#:########################################################################################:#
# PACKAGES: Included Common Packages
# https://esphome.io/components/packages.html
#:########################################################################################:#
packages:
#### WIFI, Network (Static/DHCP/IPV6 etc), Fallback AP, Safemode ####
common_wifi: !include
file: common/network_common.yaml
vars:
local_device_name: "${device_name}"
local_static_ip_address: "${static_ip_address}"
local_ota_pass: "${ota_pass}"
local_current_ip_address: "${current_ip_address}"
#### HOME ASSISTANT API (choose encryption or no encryption options) ####
common_api: !include
file: common/api_common.yaml
#file: common/api_common_noencryption.yaml
vars:
local_api_key: "${api_key}"
#### MQTT ####
common_mqtt: !include
file: common/mqtt_common.yaml
vars:
local_device_name: "${device_name}"
#### DIAGNOSTICS Sensors ####
diag_basic: !include common/include_basic_diag_sensors.yaml
diag_more: !include common/include_more_diag_sensors.yaml
#diag_debug: !include common/include_debug_diag_sensors.yaml
#diag_resetcount: !include common/include_resetcount_diag_sensors.yaml
#:########################################################################################:#
# ESPHOME
# https://esphome.io/components/esphome.html
#:########################################################################################:#
esphome:
name: "${device_name}"
friendly_name: "${friendly_name}"
comment: "${description_comment}"
area: "${device_area}"
project:
name: "${project_name}"
version: "${project_version}"
#:########################################################################################:#
# ESP PLATFORM AND FRAMEWORK
# https://esphome.io/components/esp8266.html
#:########################################################################################:#
esp8266:
board: esp01_1m
early_pin_init: false
board_flash_mode: dout
#:########################################################################################:#
# LOGGING
# https://esphome.io/components/logger.html
#:########################################################################################:#
logger:
level: "${log_level}"
#baud_rate: 0
#:########################################################################################:#
# GLOBALS
# Used to suppress MQTT/HA feedback loops and hold the last known remote states.
#:########################################################################################:#
globals:
- id: north_update_from_sync
type: bool
restore_value: false
initial_value: 'false'
- id: south_update_from_sync
type: bool
restore_value: false
initial_value: 'false'
- id: north_lights_state
type: bool
restore_value: false
initial_value: 'false'
- id: south_lights_state
type: bool
restore_value: false
initial_value: 'false'
#:########################################################################################:#
# MQTT: Device-specific MQTT command + retained status/sync triggers
# This adds device-specific MQTT command triggers to the common MQTT configuration.
#:########################################################################################:#
mqtt:
# Query the iFan02/Tasmota light state whenever this ESP reconnects to MQTT.
# Tasmota treats an empty command payload as a status query for that command.
on_connect:
then:
- delay: 2s
- mqtt.publish:
topic: "${north_lights_tasmota_command_topic}"
payload: ""
on_message:
# -------------------------------------------------------------------------
# North Lights local command topic -> Tasmota/iFan02 command topic
# NOTE: TOGGLE is intentionally ignored here to avoid double-toggle if both
# switches receive the same MQTT command. Buttons publish TOGGLE directly.
# -------------------------------------------------------------------------
- topic: "${mqtt_local_command_topic1}/set"
then:
- lambda: |-
if (x == "On" || x == "ON" || x == "on") {
id(request_north_lights_on).execute();
} else if (x == "Off" || x == "OFF" || x == "off") {
id(request_north_lights_off).execute();
}
# Tasmota/iFan02 POWER1 feedback is the source of truth for North Lights state.
- topic: "${north_lights_tasmota_status_topic}"
then:
- lambda: |-
if (x == "ON" || x == "On" || x == "on") {
id(set_north_on_from_tasmota).execute();
} else if (x == "OFF" || x == "Off" || x == "off") {
id(set_north_off_from_tasmota).execute();
}
# Retained North status is a secondary mirror/sync path.
- topic: "${mqtt_local_status_topic1}/state"
then:
- lambda: |-
if (x == "On" || x == "ON" || x == "on") {
id(set_north_on_from_sync).execute();
} else if (x == "Off" || x == "OFF" || x == "off") {
id(set_north_off_from_sync).execute();
}
# -------------------------------------------------------------------------
# South Lights status. Actual physical South load is WEST Relay 2.
# This SOUTH switch publishes commands but does NOT subscribe to its own
# command topic, to avoid command echo/chatter. The WEST switch publishes
# retained status after the physical Relay 2 changes, and this switch mirrors
# that status onto its local Relay 2.
# -------------------------------------------------------------------------
- topic: "${mqtt_local_status_topic2}/state"
then:
- lambda: |-
if (x == "On" || x == "ON" || x == "on") {
id(set_south_on_from_sync).execute();
} else if (x == "Off" || x == "OFF" || x == "off") {
id(set_south_off_from_sync).execute();
}
#:########################################################################################:#
# STATUS LED
# https://esphome.io/components/status_led.html
#:########################################################################################:#
status_led:
pin:
number: GPIO2
inverted: true
#:########################################################################################:#
# BINARY SENSORS
# https://esphome.io/components/binary_sensor/
#:########################################################################################:#
binary_sensor:
# Button 1 (GPIO16) - polling (GPIO16 has no interrupts)
# North Lights: direct TOGGLE to Tasmota/iFan02. Relay 1 follows Tasmota feedback only.
- platform: gpio
pin:
number: GPIO16
mode: INPUT
inverted: true
use_interrupt: false
name: "Button 1: ${switch_1_name}"
on_press:
- script.execute: request_north_lights_toggle
# Button 2 (GPIO05)
# South Lights: toggles virtual Relay 2, which sends command to WEST physical Relay 2.
- platform: gpio
pin:
number: GPIO05
mode: INPUT
inverted: true
name: "Button 2: ${switch_2_name}"
on_press:
- switch.toggle: relay_2
# Button 3 (GPIO04)
# NOTE: This does not toggle Relay 3. It checks the HA entity state and calls the opposite HA action.
- platform: gpio
pin:
number: GPIO4
mode: INPUT
inverted: true
name: "Button 3: ${switch_3_name}"
on_press:
- script.execute: toggle_bedside_lamp_jump_fade
# Home Assistant state import used by Button 3 so it can choose ON vs OFF explicitly.
- platform: homeassistant
id: bedside_lamp_jump_fade_state
entity_id: "${bedside_lamp_entity}"
internal: true
#:########################################################################################:#
# SCRIPT
# https://esphome.io/components/script.html
#:########################################################################################:#
script:
# ---------------------------------------------------------------------------
# North Lights helpers
# Actual North Lights are Tasmota/iFan02 POWER1. Relay 1 is the local mirror/control.
# ---------------------------------------------------------------------------
- id: request_north_lights_toggle
mode: restart
then:
- if:
condition:
mqtt.connected:
then:
- mqtt.publish:
topic: "${north_lights_tasmota_command_topic}"
payload: "TOGGLE"
else:
- if:
condition:
api.connected:
state_subscription_only: true
then:
- homeassistant.action:
action: light.toggle
data:
entity_id: "${north_lights_ha_entity}"
else:
- logger.log:
level: WARN
format: "Button 1 pressed, but MQTT and HA/API are not connected. North Lights not changed."
- id: request_north_lights_on
mode: restart
then:
- mqtt.publish:
topic: "${north_lights_tasmota_command_topic}"
payload: "ON"
- id: request_north_lights_off
mode: restart
then:
- mqtt.publish:
topic: "${north_lights_tasmota_command_topic}"
payload: "OFF"
- id: set_north_on_from_tasmota
mode: restart
then:
- lambda: |-
id(north_lights_state) = true;
id(north_update_from_sync) = true;
- switch.turn_on: relay_1
- mqtt.publish:
topic: "${mqtt_local_status_topic1}/state"
payload: "On"
retain: true
- lambda: |-
id(north_update_from_sync) = false;
- id: set_north_off_from_tasmota
mode: restart
then:
- lambda: |-
id(north_lights_state) = false;
id(north_update_from_sync) = true;
- switch.turn_off: relay_1
- mqtt.publish:
topic: "${mqtt_local_status_topic1}/state"
payload: "Off"
retain: true
- lambda: |-
id(north_update_from_sync) = false;
- id: set_north_on_from_sync
mode: restart
then:
- lambda: |-
id(north_lights_state) = true;
id(north_update_from_sync) = true;
- switch.turn_on: relay_1
- lambda: |-
id(north_update_from_sync) = false;
- id: set_north_off_from_sync
mode: restart
then:
- lambda: |-
id(north_lights_state) = false;
id(north_update_from_sync) = true;
- switch.turn_off: relay_1
- lambda: |-
id(north_update_from_sync) = false;
# ---------------------------------------------------------------------------
# South Lights helpers
# Actual physical South Lights relay is WEST switch Relay 2. Relay 2 is the local mirror/control.
# ---------------------------------------------------------------------------
- id: request_south_lights_on
mode: restart
then:
- if:
condition:
mqtt.connected:
then:
- mqtt.publish:
topic: "${mqtt_local_command_topic2}/set"
payload: "On"
else:
- if:
condition:
api.connected:
state_subscription_only: true
then:
- homeassistant.action:
action: switch.turn_on
data:
entity_id: "${south_lights_physical_entity}"
else:
- logger.log:
level: WARN
format: "Button 2 pressed, but MQTT and HA/API are not connected. South Lights not changed."
- id: request_south_lights_off
mode: restart
then:
- if:
condition:
mqtt.connected:
then:
- mqtt.publish:
topic: "${mqtt_local_command_topic2}/set"
payload: "Off"
else:
- if:
condition:
api.connected:
state_subscription_only: true
then:
- homeassistant.action:
action: switch.turn_off
data:
entity_id: "${south_lights_physical_entity}"
else:
- logger.log:
level: WARN
format: "Button 2 pressed, but MQTT and HA/API are not connected. South Lights not changed."
- id: set_south_on_from_sync
mode: restart
then:
- lambda: |-
id(south_lights_state) = true;
id(south_update_from_sync) = true;
- switch.turn_on: relay_2
- lambda: |-
id(south_update_from_sync) = false;
- id: set_south_off_from_sync
mode: restart
then:
- lambda: |-
id(south_lights_state) = false;
id(south_update_from_sync) = true;
- switch.turn_off: relay_2
- lambda: |-
id(south_update_from_sync) = false;
# ---------------------------------------------------------------------------
# Bedside lamp helper
# ---------------------------------------------------------------------------
- id: toggle_bedside_lamp_jump_fade
mode: restart
then:
- if:
condition:
api.connected:
state_subscription_only: true
then:
- if:
condition:
binary_sensor.is_on: bedside_lamp_jump_fade_state
then:
- homeassistant.action:
action: switch.turn_off
data:
entity_id: "${bedside_lamp_entity}"
else:
- homeassistant.action:
action: switch.turn_on
data:
entity_id: "${bedside_lamp_entity}"
else:
- logger.log:
level: WARN
format: "Button 3 pressed, but Home Assistant/API is not connected. Bedside lamp not toggled."
#:########################################################################################:#
# SWITCH
# https://esphome.io/components/switch/
#:########################################################################################:#
switch:
# Relay 1 (GPIO13) - Master Bedroom North Lights - VIRTUAL/MIRROR ONLY ON THIS SWITCH
- platform: gpio
name: "Relay 1: ${switch_1_name}"
pin: GPIO13
id: relay_1
restore_mode: RESTORE_DEFAULT_OFF
on_turn_on:
- lambda: |-
if (!id(north_update_from_sync)) {
id(request_north_lights_on).execute();
}
on_turn_off:
- lambda: |-
if (!id(north_update_from_sync)) {
id(request_north_lights_off).execute();
}
# Relay 2 (GPIO12) - Master Bedroom South Lights - VIRTUAL/MIRROR ONLY ON THIS SWITCH
- platform: gpio
name: "Relay 2: ${switch_2_name}"
pin: GPIO12
id: relay_2
restore_mode: RESTORE_DEFAULT_OFF
on_turn_on:
- lambda: |-
if (!id(south_update_from_sync)) {
id(request_south_lights_on).execute();
}
on_turn_off:
- lambda: |-
if (!id(south_update_from_sync)) {
id(request_south_lights_off).execute();
}
# Relay 3 (GPIO14) - unused output, intentionally not exposed to Home Assistant.
# Button 3 controls the bedside lamp via Home Assistant/API instead.
- platform: gpio
name: "Relay 3: Unused Output Forced Off"
pin: GPIO14
id: relay_3
restore_mode: ALWAYS_OFF
internal: true
+585
View File
@@ -0,0 +1,585 @@
#:########################################################################################:#
# TITLE: MASTER BEDROOM MAIN LIGHTS WEST
# zorruno.com layout v1.1 2026
#:########################################################################################:#
# REPO:
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-masterbdrmwest.yaml
#:########################################################################################:#
# VERSIONS:
# v1.3 2026-06-12 Reassigned logical controls after live wiring checks.
# Relay 1 on both switches = North Lights virtual/Tasmota control.
# Relay 2 on both switches = South Lights control; WEST Relay 2 is physical load.
# Button 1 now controls North Lights; Button 2 now controls South Lights.
# v1.2 2026-06-12 Reworked after live wiring checks.
# Confirmed North Lights are controlled by Tasmota/iFan02 Power1.
# Confirmed South Lights physical load is on WEST switch Relay 2.
# v1.1 2026-06-12 North Lights controlled via Tasmota iFan02 MQTT cmnd/stat topics.
# v1.0 2026-06-11 Initial YAML for Master Bedroom West 3-gang switch.
#:########################################################################################:#
# HARDWARE:
# - Zemismart KS-811 Triple push button (ESP8266)
# - Pinout/Schematic: https://community.home-assistant.io/t/zemismart-ks-811-working-with-esphome/
#:########################################################################################:#
# OPERATION NOTES:
# - 3-gang KS-811 triple switch.
# - Relay/Button 1 = Master Bedroom North Lights.
# * Actual North Lights are controlled by Tasmota/iFan02 light.tasmo_ifan02_3793_bedrm1_1.
# * Button 1 sends MQTT TOGGLE to the Tasmota POWER1 command topic.
# * Relay 1 is virtual/mirror only and follows Tasmota POWER1 feedback.
# * Relay 1 can also be switched from HA; ON/OFF sends explicit Tasmota ON/OFF.
# - Relay/Button 2 = Master Bedroom South Lights.
# * On this WEST switch, Relay 2 is the physical South Lights load.
# * Button 2 toggles Relay 2 directly.
# * Relay 2 publishes retained South Lights status for the SOUTH switch mirror.
# - Button 3 = Bedside Lamp Jump/Fade via Home Assistant action.
# * Requires ESPHome integration permission: "Allow the device to perform Home Assistant actions".
# - Relay 3 = unused output. Forced off/internal; Button 3 does NOT toggle Relay 3.
# - GPIO16 does not support interrupts; polling is used for Button 1 (suppresses warning).
#:########################################################################################:#
# MQTT COMMANDS:
# - North Lights Tasmota command topic:
# ${north_lights_tasmota_command_topic}
# Payload "TOGGLE" -> iFan02 Light toggle
# Payload "ON" -> iFan02 Light ON
# Payload "OFF" -> iFan02 Light OFF
# Empty payload -> Query current POWER1 state
#
# - North Lights Tasmota status topic:
# ${north_lights_tasmota_status_topic}
# Payload "ON" / "OFF" updates Relay 1 mirror state on both switches.
#
# - North Lights local command topic:
# ${mqtt_local_command_topic1}/set
# Payload "On" / "ON" -> North Lights ON via Tasmota POWER1
# Payload "Off" / "OFF" -> North Lights OFF via Tasmota POWER1
# NOTE: TOGGLE is intentionally not handled here to avoid double-toggle if both switches receive it.
#
# - North Lights retained status/sync topic:
# ${mqtt_local_status_topic1}/state
# Payload "On" / "Off" mirrors the actual North Lights state.
#
# - South Lights local command topic:
# ${mqtt_local_command_topic2}/set
# Payload "On" / "ON" -> Relay 2 ON
# Payload "Off" / "OFF" -> Relay 2 OFF
# NOTE: TOGGLE is intentionally not handled here to avoid ambiguous multi-device toggles.
#
# - South Lights retained status/sync topic:
# ${mqtt_local_status_topic2}/state
# Payload "On" / "Off" published when Relay 2 changes state.
#:########################################################################################:#
# OFFLINE NOTES:
# a) Home Assistant OFFLINE, WiFi OK, MQTT broker OK
# - Button 1 controls the North Lights through direct Tasmota MQTT command.
# - Button 2 controls the South Lights locally through Relay 2.
# - Tasmota feedback updates Relay 1 mirror via stat POWER1.
# - Relay 2 status updates the SOUTH switch mirror via the South Lights retained status topic.
# - Button 3 cannot toggle the bedside lamp because it requires Home Assistant/API.
#
# b) MQTT OFFLINE (or broker unreachable), Home Assistant/API OK
# - Button 1 attempts HA action fallback to light.toggle the Tasmota/iFan02 HA entity.
# - Button 2 still controls the local South Lights Relay 2.
# - Button 3 can still toggle the bedside lamp via Home Assistant/API.
#
# c) Home Assistant and MQTT OFFLINE, WiFi OK or entire network OFFLINE
# - Button 2 still controls local Relay 2 / South Lights.
# - Button 1 cannot control the North Lights unless MQTT or HA/API is available.
# - Button 3 cannot control the bedside lamp.
# - Accurate time is NOT needed (SNTP not needed).
#:########################################################################################:#
#:########################################################################################:#
# SUBSTITUTIONS: Specific device variable substitutions
# If NOT using a secrets file, just replace these with the passwords etc (in quotes)
#:########################################################################################:#
substitutions:
# Device Naming
device_name: "esp-masterbdrmwest"
friendly_name: "Master Bedroom Main Lights West"
description_comment: "Master Bedroom Main Lights West using a Zemismart KS-811 Triple Push Button. Relay/Button 1 North Lights Tasmota/iFan02 virtual mirror, Relay/Button 2 South Lights physical load, Button 3 toggles bedside lamp jump/fade. (Layout V1.1)"
device_area: "Master Bedroom"
# Project Naming
project_name: "Zemismart Technologies.KS-811 Triple"
project_version: "v1.3"
# Passwords & Secrets
api_key: !secret esp-api_key
ota_pass: !secret esp-ota_pass
static_ip_address: !secret esp-masterbdrmwest_ip
mqtt_command_main_topic: !secret mqtt_command_main_topic
mqtt_status_main_topic: !secret mqtt_status_main_topic
# If we are changing IP addresses, you must update the current IP address here, otherwise it remains
# Don't forget to switch it back when changed.
current_ip_address: ${static_ip_address}
# Device Settings
log_level: "INFO"
update_interval: "60s"
# MQTT LOCAL Controls
# NOTE: device_name1 is North Lights and maps to Relay 1 / Tasmota/iFan02 light.
mqtt_device_name1: "masterbrdm-northlights"
mqtt_local_command_topic1: "${mqtt_command_main_topic}/${mqtt_device_name1}"
mqtt_local_status_topic1: "${mqtt_status_main_topic}/${mqtt_device_name1}"
# NOTE: device_name2 is South Lights and maps to Relay 2 / this WEST switch physical load.
mqtt_device_name2: "masterbrdm-southlights"
mqtt_local_command_topic2: "${mqtt_command_main_topic}/${mqtt_device_name2}"
mqtt_local_status_topic2: "${mqtt_status_main_topic}/${mqtt_device_name2}"
# Tasmota/iFan02 North Lights MQTT control and feedback
# NOTE: Tasmota status topic observed as uppercase POWER1. MQTT topics are case-sensitive.
north_lights_tasmota_command_topic: "cmnd/tasmo-ifan02-3793-bedrm1-1/POWER1"
north_lights_tasmota_status_topic: "stat/tasmo-ifan02-3793-bedrm1-1/POWER1"
# Home Assistant fallback entity
north_lights_ha_entity: "light.tasmo_ifan02_3793_bedrm1_1"
# Switch/Relay/Button Naming
switch_1_name: "Master Bedroom North Lights"
switch_2_name: "Master Bedroom South Lights"
switch_3_name: "Bedside Lamp Jump/Fade" # NOTE: Relay 3 output is unused and forced off
# Home Assistant bedside lamp entity controlled by Button 3
bedside_lamp_entity: "switch.master_bedroom_bedside_lamp_arlec_bulb_master_bedroom_bedside_lamp_arlec_bulb_jump_fade_up_down"
#:########################################################################################:#
# PACKAGES: Included Common Packages
# https://esphome.io/components/packages.html
#:########################################################################################:#
packages:
#### WIFI, Network (Static/DHCP/IPV6 etc), Fallback AP, Safemode ####
common_wifi: !include
file: common/network_common.yaml
vars:
local_device_name: "${device_name}"
local_static_ip_address: "${static_ip_address}"
local_ota_pass: "${ota_pass}"
local_current_ip_address: "${current_ip_address}"
#### HOME ASSISTANT API (choose encryption or no encryption options) ####
common_api: !include
file: common/api_common.yaml
#file: common/api_common_noencryption.yaml
vars:
local_api_key: "${api_key}"
#### MQTT ####
common_mqtt: !include
file: common/mqtt_common.yaml
vars:
local_device_name: "${device_name}"
#### DIAGNOSTICS Sensors ####
diag_basic: !include common/include_basic_diag_sensors.yaml
diag_more: !include common/include_more_diag_sensors.yaml
#diag_debug: !include common/include_debug_diag_sensors.yaml
#diag_resetcount: !include common/include_resetcount_diag_sensors.yaml
#:########################################################################################:#
# ESPHOME
# https://esphome.io/components/esphome.html
#:########################################################################################:#
esphome:
name: "${device_name}"
friendly_name: "${friendly_name}"
comment: "${description_comment}"
area: "${device_area}"
project:
name: "${project_name}"
version: "${project_version}"
#:########################################################################################:#
# ESP PLATFORM AND FRAMEWORK
# https://esphome.io/components/esp8266.html
#:########################################################################################:#
esp8266:
board: esp01_1m
early_pin_init: false
board_flash_mode: dout
#:########################################################################################:#
# LOGGING
# https://esphome.io/components/logger.html
#:########################################################################################:#
logger:
level: "${log_level}"
#baud_rate: 0
#:########################################################################################:#
# GLOBALS
# Used to suppress MQTT/HA feedback loops and hold the last known remote states.
#:########################################################################################:#
globals:
- id: north_update_from_sync
type: bool
restore_value: false
initial_value: 'false'
- id: south_update_from_sync
type: bool
restore_value: false
initial_value: 'false'
- id: north_lights_state
type: bool
restore_value: false
initial_value: 'false'
- id: south_lights_state
type: bool
restore_value: false
initial_value: 'false'
#:########################################################################################:#
# MQTT: Device-specific MQTT command + retained status/sync triggers
# This adds device-specific MQTT command triggers to the common MQTT configuration.
#:########################################################################################:#
mqtt:
# Query the iFan02/Tasmota light state whenever this ESP reconnects to MQTT.
# Tasmota treats an empty command payload as a status query for that command.
on_connect:
then:
- delay: 2s
- mqtt.publish:
topic: "${north_lights_tasmota_command_topic}"
payload: ""
on_message:
# -------------------------------------------------------------------------
# North Lights local command topic -> Tasmota/iFan02 command topic
# NOTE: TOGGLE is intentionally ignored here to avoid double-toggle if both
# switches receive the same MQTT command. Buttons publish TOGGLE directly.
# -------------------------------------------------------------------------
- topic: "${mqtt_local_command_topic1}/set"
then:
- lambda: |-
if (x == "On" || x == "ON" || x == "on") {
id(request_north_lights_on).execute();
} else if (x == "Off" || x == "OFF" || x == "off") {
id(request_north_lights_off).execute();
}
# Tasmota/iFan02 POWER1 feedback is the source of truth for North Lights state.
- topic: "${north_lights_tasmota_status_topic}"
then:
- lambda: |-
if (x == "ON" || x == "On" || x == "on") {
id(set_north_on_from_tasmota).execute();
} else if (x == "OFF" || x == "Off" || x == "off") {
id(set_north_off_from_tasmota).execute();
}
# Retained North status is a secondary mirror/sync path.
- topic: "${mqtt_local_status_topic1}/state"
then:
- lambda: |-
if (x == "On" || x == "ON" || x == "on") {
id(set_north_on_from_sync).execute();
} else if (x == "Off" || x == "OFF" || x == "off") {
id(set_north_off_from_sync).execute();
}
# -------------------------------------------------------------------------
# South Lights command/status. Actual physical South load is this Relay 2.
# -------------------------------------------------------------------------
- topic: "${mqtt_local_command_topic2}/set"
then:
- lambda: |-
if (x == "On" || x == "ON" || x == "on") {
id(request_south_lights_on).execute();
} else if (x == "Off" || x == "OFF" || x == "off") {
id(request_south_lights_off).execute();
}
# Retained South status keeps this physical relay and the SOUTH mirror in step.
- topic: "${mqtt_local_status_topic2}/state"
then:
- lambda: |-
if (x == "On" || x == "ON" || x == "on") {
id(set_south_on_from_sync).execute();
} else if (x == "Off" || x == "OFF" || x == "off") {
id(set_south_off_from_sync).execute();
}
#:########################################################################################:#
# STATUS LED
# https://esphome.io/components/status_led.html
#:########################################################################################:#
status_led:
pin:
number: GPIO2
inverted: true
#:########################################################################################:#
# BINARY SENSORS
# https://esphome.io/components/binary_sensor/
#:########################################################################################:#
binary_sensor:
# Button 1 (GPIO16) - polling (GPIO16 has no interrupts)
# North Lights: direct TOGGLE to Tasmota/iFan02. Relay 1 follows Tasmota feedback only.
- platform: gpio
pin:
number: GPIO16
mode: INPUT
inverted: true
use_interrupt: false
name: "Button 1: ${switch_1_name}"
on_press:
- script.execute: request_north_lights_toggle
# Button 2 (GPIO05)
# South Lights: direct local physical control through Relay 2.
- platform: gpio
pin:
number: GPIO05
mode: INPUT
inverted: true
name: "Button 2: ${switch_2_name}"
on_press:
- switch.toggle: relay_2
# Button 3 (GPIO04)
# NOTE: This does not toggle Relay 3. It checks the HA entity state and calls the opposite HA action.
- platform: gpio
pin:
number: GPIO4
mode: INPUT
inverted: true
name: "Button 3: ${switch_3_name}"
on_press:
- script.execute: toggle_bedside_lamp_jump_fade
# Home Assistant state import used by Button 3 so it can choose ON vs OFF explicitly.
- platform: homeassistant
id: bedside_lamp_jump_fade_state
entity_id: "${bedside_lamp_entity}"
internal: true
#:########################################################################################:#
# SCRIPT
# https://esphome.io/components/script.html
#:########################################################################################:#
script:
# ---------------------------------------------------------------------------
# North Lights helpers
# Actual North Lights are Tasmota/iFan02 POWER1. Relay 1 is the local mirror/control.
# ---------------------------------------------------------------------------
- id: request_north_lights_toggle
mode: restart
then:
- if:
condition:
mqtt.connected:
then:
- mqtt.publish:
topic: "${north_lights_tasmota_command_topic}"
payload: "TOGGLE"
else:
- if:
condition:
api.connected:
state_subscription_only: true
then:
- homeassistant.action:
action: light.toggle
data:
entity_id: "${north_lights_ha_entity}"
else:
- logger.log:
level: WARN
format: "Button 1 pressed, but MQTT and HA/API are not connected. North Lights not changed."
- id: request_north_lights_on
mode: restart
then:
- mqtt.publish:
topic: "${north_lights_tasmota_command_topic}"
payload: "ON"
- id: request_north_lights_off
mode: restart
then:
- mqtt.publish:
topic: "${north_lights_tasmota_command_topic}"
payload: "OFF"
- id: set_north_on_from_tasmota
mode: restart
then:
- lambda: |-
id(north_lights_state) = true;
id(north_update_from_sync) = true;
- switch.turn_on: relay_1
- mqtt.publish:
topic: "${mqtt_local_status_topic1}/state"
payload: "On"
retain: true
- lambda: |-
id(north_update_from_sync) = false;
- id: set_north_off_from_tasmota
mode: restart
then:
- lambda: |-
id(north_lights_state) = false;
id(north_update_from_sync) = true;
- switch.turn_off: relay_1
- mqtt.publish:
topic: "${mqtt_local_status_topic1}/state"
payload: "Off"
retain: true
- lambda: |-
id(north_update_from_sync) = false;
- id: set_north_on_from_sync
mode: restart
then:
- lambda: |-
id(north_lights_state) = true;
id(north_update_from_sync) = true;
- switch.turn_on: relay_1
- lambda: |-
id(north_update_from_sync) = false;
- id: set_north_off_from_sync
mode: restart
then:
- lambda: |-
id(north_lights_state) = false;
id(north_update_from_sync) = true;
- switch.turn_off: relay_1
- lambda: |-
id(north_update_from_sync) = false;
# ---------------------------------------------------------------------------
# South Lights helpers
# Actual physical South Lights relay is this WEST switch Relay 2.
# ---------------------------------------------------------------------------
- id: request_south_lights_on
mode: restart
then:
- switch.turn_on: relay_2
- id: request_south_lights_off
mode: restart
then:
- switch.turn_off: relay_2
- id: set_south_on_from_sync
mode: restart
then:
- lambda: |-
id(south_lights_state) = true;
id(south_update_from_sync) = true;
- switch.turn_on: relay_2
- lambda: |-
id(south_update_from_sync) = false;
- id: set_south_off_from_sync
mode: restart
then:
- lambda: |-
id(south_lights_state) = false;
id(south_update_from_sync) = true;
- switch.turn_off: relay_2
- lambda: |-
id(south_update_from_sync) = false;
# ---------------------------------------------------------------------------
# Bedside lamp helper
# ---------------------------------------------------------------------------
- id: toggle_bedside_lamp_jump_fade
mode: restart
then:
- if:
condition:
api.connected:
state_subscription_only: true
then:
- if:
condition:
binary_sensor.is_on: bedside_lamp_jump_fade_state
then:
- homeassistant.action:
action: switch.turn_off
data:
entity_id: "${bedside_lamp_entity}"
else:
- homeassistant.action:
action: switch.turn_on
data:
entity_id: "${bedside_lamp_entity}"
else:
- logger.log:
level: WARN
format: "Button 3 pressed, but Home Assistant/API is not connected. Bedside lamp not toggled."
#:########################################################################################:#
# SWITCH
# https://esphome.io/components/switch/
#:########################################################################################:#
switch:
# Relay 1 (GPIO13) - Master Bedroom North Lights - VIRTUAL/MIRROR ONLY ON THIS SWITCH
- platform: gpio
name: "Relay 1: ${switch_1_name}"
pin: GPIO13
id: relay_1
restore_mode: RESTORE_DEFAULT_OFF
on_turn_on:
- lambda: |-
if (!id(north_update_from_sync)) {
id(request_north_lights_on).execute();
}
on_turn_off:
- lambda: |-
if (!id(north_update_from_sync)) {
id(request_north_lights_off).execute();
}
# Relay 2 (GPIO12) - Master Bedroom South Lights - PHYSICAL LOAD CONNECTED HERE
- platform: gpio
name: "Relay 2: ${switch_2_name}"
pin: GPIO12
id: relay_2
restore_mode: RESTORE_DEFAULT_OFF
on_turn_on:
- lambda: |-
id(south_lights_state) = true;
if (!id(south_update_from_sync)) {
// Publish retained state for the SOUTH switch mirror and any MQTT consumers.
}
- if:
condition:
lambda: 'return !id(south_update_from_sync);'
then:
- mqtt.publish:
topic: "${mqtt_local_status_topic2}/state"
payload: "On"
retain: true
on_turn_off:
- lambda: |-
id(south_lights_state) = false;
if (!id(south_update_from_sync)) {
// Publish retained state for the SOUTH switch mirror and any MQTT consumers.
}
- if:
condition:
lambda: 'return !id(south_update_from_sync);'
then:
- mqtt.publish:
topic: "${mqtt_local_status_topic2}/state"
payload: "Off"
retain: true
# Relay 3 (GPIO14) - unused output, intentionally not exposed to Home Assistant.
# Button 3 controls the bedside lamp via Home Assistant/API instead.
- platform: gpio
name: "Relay 3: Unused Output Forced Off"
pin: GPIO14
id: relay_3
restore_mode: ALWAYS_OFF
internal: true
+23 -3
View File
@@ -113,11 +113,12 @@ automation:
power_entity: "sensor.esp_laundrywashpow_load_power"
icon: "mdi:washing-machine"
short_announcement: "/s/sWashing complete"
# long_announcement: # Created by the processor
matrix_indicator: "wash,1,15,10,6,5,1,0"
# Optional washing-machine-specific overrides
resolution: 5
standby_power: 3
operating_power_min: 20
operating_power_min: 10
announcement_channels: "pushover_zorruno"
- appliance_id: dryer
@@ -126,11 +127,12 @@ automation:
power_entity: "sensor.esp_laundrydrypow_load_power"
icon: "mdi:tumble-dryer"
short_announcement: "/s/sDryer complete"
# long_announcement: # Created by the processor
matrix_indicator: "dry,1,15,10,6,5,1,0"
# Optional dryer-specific overrides
resolution: 4
standby_power: 3
operating_power_min: 20
operating_power_min: 10
announcement_channels: "pushover_zorruno"
- appliance_id: main_dishwasher
@@ -139,8 +141,23 @@ automation:
power_entity: "sensor.esp_maindishwasherpower_power"
icon: "mdi:dishwasher"
short_announcement: "/sDishwasher complete"
# long_announcement: # Created by the processor
matrix_indicator: "dish,1,15,10,6,5,1,0"
- appliance_id: evcharger_32a
appliance_name: "EV Charger (32A)"
appliance_action: "EV charging (32A)"
power_entity: "sensor.main_house_3_phase_power_monitor_32a_wallcharger_power"
monitor_sensor: "sensor.appliance_monitor_ev_charger_32a_monitor"
icon: "mdi:ev-plug-type2"
short_announcement: "/s/s32A EV Charging complete"
# long_announcement: # Created by the processor
matrix_indicator: "ev32a,1,15,10,6,5,1,0"
resolution: 6
standby_power: 3
operating_power_min: 50
announcement_channels: "pushover_zorruno"
action:
##########################################################################
# PUBLISH / REFRESH MQTT DISCOVERY CONFIG
@@ -203,7 +220,10 @@ automation:
{{ mqtt_local_status_topic }}/{{ mqtt_device_name }}/{{ repeat.item.appliance_id }}/state
am_monitor_sensor: >-
sensor.{{ mqtt_device_name }}_{{ repeat.item.appliance_id }}_monitor
{{
repeat.item.monitor_sensor
| default('sensor.' ~ mqtt_device_name ~ '_' ~ repeat.item.appliance_id ~ '_monitor', true)
}}
am_title: >-
{{ repeat.item.title | default(repeat.item.appliance_name ~ ' Notification', true) }}
+35 -23
View File
@@ -4,14 +4,21 @@
# DESCRIPTION:
# Replaces the Node-RED automation for the underhouse storage lights.
# Doors, PIR sensors, a remote switch, and local relay changes can start or
# extend a 1 hour lighting timer.
# extend the storage lighting timer.
#
# VERSION:
# V1.1 2026-06-14
# - Door-open trigger now watches each real door contact individually.
# - This means any door changing from closed to open turns the lights on
# immediately, even if another door is already open.
# - All doors closed now turns the lights off immediately.
# - PIR occupancy can still start/restart the motion timer after that.
#
# V1.0 2026-05-12
# - Initial Home Assistant package version.
#
# EDIT NOTES:
# - Add future door contacts in BOTH places marked FUTURE DOOR.
# - Add future door contacts in all places marked FUTURE DOOR.
# - Add future PIR sensors in the PIR group marked FUTURE PIR.
# - The generated group entities used below should be:
# binary_sensor.underhouse_storage_door_contacts
@@ -27,8 +34,7 @@ binary_sensor:
entities:
- binary_sensor.underhouse_stealth_door_x07rs_contact
- binary_sensor.underhouse_north_door_door
# FUTURE DOOR: add the next underhouse storage door contact here.
# - binary_sensor.underhouse_future_door_contact
- binary_sensor.under_house_underhouse_east_stealth_door_door
- platform: group
name: Underhouse Storage PIR Presence
@@ -38,8 +44,8 @@ binary_sensor:
entities:
- binary_sensor.underhouse_pir_sensor_south_occupancy
- binary_sensor.underhouse_pir_sensor_north_occupancy
# FUTURE PIR: uncomment this when the north PIR exists.
# - binary_sensor.underhouse_pir_sensor_north_occupancy
# FUTURE PIR: add any extra PIR sensors here.
# - binary_sensor.underhouse_future_pir_occupancy
timer:
underhouse_storage_lights_runtime:
@@ -67,7 +73,7 @@ script:
target:
entity_id: timer.underhouse_storage_lights_runtime
data:
duration: "0:20:00"
duration: "00:20:00"
underhouse_storage_lights_stop_runtime:
alias: Underhouse Storage Lights - Stop Runtime
@@ -97,19 +103,28 @@ automation:
- id: underhouse_storage_lights_start_extend_runtime
alias: Underhouse Storage Lights - Start Or Extend Runtime
description: >-
Turns the storage lights on and starts or restarts the 1 hour timer when
a door opens, PIR detects occupancy, or the remote switch is pressed.
Turns the storage lights on and starts or restarts the timer when
an individual door opens, PIR detects occupancy, or the remote switch
is pressed.
mode: restart
trigger:
- platform: state
entity_id: binary_sensor.underhouse_storage_door_contacts
entity_id:
- binary_sensor.underhouse_stealth_door_x07rs_contact
- binary_sensor.underhouse_north_door_door
# FUTURE DOOR: replace this with the actual third underhouse storage door contact.
#- binary_sensor.underhouse_future_door_contact
from: "off"
to: "on"
- platform: state
entity_id: binary_sensor.underhouse_storage_pir_presence
to: "on"
- platform: state
entity_id: binary_sensor.esp_underhouselights_underhouse_storage_lights_remote_switch
to: "on"
action:
- action: script.underhouse_storage_lights_start_runtime
@@ -174,35 +189,32 @@ automation:
- id: underhouse_storage_lights_all_doors_closed
alias: Underhouse Storage Lights - All Doors Closed
description: >-
When all door contacts are closed, turn the lights off immediately if no
PIR occupancy is active. If PIR is still active, the PIR clear automation
below will turn them off after 60 seconds without occupancy.
When all door contacts are closed, turn the lights off immediately.
PIR occupancy can still turn the lights back on again afterwards.
mode: restart
trigger:
- platform: state
entity_id: binary_sensor.underhouse_storage_door_contacts
from: "on"
to: "off"
condition:
- condition: state
entity_id: binary_sensor.underhouse_storage_pir_presence
state: "off"
action:
- action: script.underhouse_storage_lights_stop_runtime
- id: underhouse_storage_lights_individual_door_closed_check
alias: Underhouse Storage Lights - Individual Door Closed No Motion Check
alias: Underhouse Storage Lights - Individual Door Closed Safety Check
description: >-
When a real door contact closes, wait 60 seconds and then turn the relay
off only if all doors are closed and there is no PIR occupancy.
Safety cleanup. When a real door contact closes, wait 60 seconds and then
ensure the relay is off only if all doors are closed and there is no PIR
occupancy. The normal all-doors-closed automation above should usually
turn the lights off immediately first.
mode: restart
trigger:
- platform: state
entity_id:
- binary_sensor.underhouse_stealth_door_x07rs_contact
- binary_sensor.underhouse_north_door_door
# FUTURE DOOR: add the next underhouse storage door contact here too.
# - binary_sensor.underhouse_future_door_contact
# FUTURE DOOR: replace this with the actual third underhouse storage door contact.
#- binary_sensor.underhouse_future_door_contact
from: "on"
to: "off"
action:
@@ -222,7 +234,7 @@ automation:
alias: Underhouse Storage Lights - PIR Clear After Doors Closed
description: >-
If the doors are closed and PIR occupancy clears for 60 seconds, turn the
storage lights off.
storage lights off. This is mainly a cleanup path for PIR-triggered use.
mode: restart
trigger:
- platform: state
+170
View File
@@ -0,0 +1,170 @@
#:########################################################################################:#
# Package: BYD Charging Schedule At Home
# File: byd_charging_schedule_athome.yaml
# Version: v1.0.1
# Date: 2026-06-19
#
# Purpose:
# Enables the BYD Atto 3 charging schedule when the car arrives home.
# Disables the schedule only after the car has been confirmed away for a
# configurable delay, to avoid rapid toggling from uncertain presence detection.
#
# Behaviour:
# - Confirmed arrival home:
# Turns ON switch.atto_3_schedule_enabled,
# but only if the car had previously been confirmed away.
#
# - Confirmed away:
# After away_confirm_minutes, turns OFF switch.atto_3_schedule_enabled.
#
# - Manual control:
# If the car is already home and the user or another HA automation turns
# the schedule OFF manually, this package will not turn it back ON unless
# the car is later confirmed away and then arrives home again.
#
# Notes:
# - Home Assistant usually stores the device_tracker home state as "home",
# even if the UI displays "At Home".
# - Change away_confirm_minutes below if you want a longer/shorter debounce.
#:########################################################################################:#
input_boolean:
byd_atto3_charging_schedule_confirmed_away:
name: "BYD Atto 3 Charging Schedule - Confirmed Away"
icon: mdi:car-off
automation:
#:######################################################################################:#
# ARRIVAL - Turn schedule ON only after a confirmed away period
#:######################################################################################:#
- id: byd_atto3_charging_schedule_arrival_enable
alias: "BYD Atto 3 Charging Schedule - Arrival Enable"
description: >
Turns the BYD charging schedule ON when the car arrives home, but only if
it was previously confirmed away. This prevents brief away/home glitches
from undoing a manual OFF while the car is actually still at home.
mode: single
variables: &byd_charging_schedule_vars
tracker_entity: device_tracker.byd_atto3_ibeacon_bur4c2a_bermuda_tracker
schedule_switch_entity: switch.atto_3_schedule_enabled
# HA normally stores "At Home" as "home".
home_state: "home"
# Adjustable YAML-only delay before the car is treated as truly away.
away_confirm_minutes: 15
# These states will not be treated as confirmed away.
ignored_tracker_states:
- "unknown"
- "unavailable"
triggers:
- trigger: state
entity_id: device_tracker.byd_atto3_ibeacon_bur4c2a_bermuda_tracker
conditions:
- condition: template
value_template: >-
{{ trigger.to_state is not none
and trigger.from_state is not none
and trigger.to_state.state == home_state
and trigger.from_state.state != home_state }}
actions:
- choose:
- conditions:
- condition: state
entity_id: input_boolean.byd_atto3_charging_schedule_confirmed_away
state: "on"
sequence:
- action: switch.turn_on
target:
entity_id: "{{ schedule_switch_entity }}"
- action: input_boolean.turn_off
target:
entity_id: input_boolean.byd_atto3_charging_schedule_confirmed_away
#:######################################################################################:#
# DEPARTURE - Turn schedule OFF only after confirmed away timeout
#:######################################################################################:#
- id: byd_atto3_charging_schedule_departure_disable_after_timeout
alias: "BYD Atto 3 Charging Schedule - Departure Disable After Timeout"
description: >
When the car leaves home, waits for the configured away timeout. If the car
is still away after that delay, turns the BYD charging schedule OFF and
marks the car as confirmed away.
mode: restart
variables: *byd_charging_schedule_vars
triggers:
- trigger: state
entity_id: device_tracker.byd_atto3_ibeacon_bur4c2a_bermuda_tracker
conditions:
- condition: template
value_template: >-
{% set new_state = trigger.to_state.state if trigger.to_state is not none else '' %}
{{ new_state != home_state
and new_state not in ignored_tracker_states }}
actions:
- delay:
minutes: "{{ away_confirm_minutes | int(15) }}"
- condition: template
value_template: >-
{% set current_state = states(tracker_entity) %}
{{ current_state != home_state
and current_state not in ignored_tracker_states }}
- action: switch.turn_off
target:
entity_id: "{{ schedule_switch_entity }}"
- action: input_boolean.turn_on
target:
entity_id: input_boolean.byd_atto3_charging_schedule_confirmed_away
#:######################################################################################:#
# STARTUP SAFETY - If HA restarts while the car is away, re-arm arrival logic
#:######################################################################################:#
- id: byd_atto3_charging_schedule_startup_away_check
alias: "BYD Atto 3 Charging Schedule - Startup Away Check"
description: >
On Home Assistant startup, if the car is already away and remains away
beyond the configured timeout, turn the schedule OFF and mark the car as
confirmed away so that the next confirmed arrival can enable the schedule.
mode: single
variables: *byd_charging_schedule_vars
triggers:
- trigger: homeassistant
event: start
actions:
- delay: "00:01:00"
- condition: template
value_template: >-
{% set current_state = states(tracker_entity) %}
{{ current_state != home_state
and current_state not in ignored_tracker_states }}
- delay:
minutes: "{{ away_confirm_minutes | int(15) }}"
- condition: template
value_template: >-
{% set current_state = states(tracker_entity) %}
{{ current_state != home_state
and current_state not in ignored_tracker_states }}
- action: switch.turn_off
target:
entity_id: "{{ schedule_switch_entity }}"
- action: input_boolean.turn_on
target:
entity_id: input_boolean.byd_atto3_charging_schedule_confirmed_away
+270 -127
View File
@@ -1,32 +1,35 @@
# =====================================================================
# Home Assistant Summary - package (ESPHome, Tasmota, ZHA, Zigbee2MQTT)
# Robust Z2M detection using device identifiers containing "zigbee2mqtt".
# Home Assistant Summary - package
#
# Counts:
# - Total devices/entities/broken entities
# - Cameras, areas, scenes, scripts, automations
# - ESPHome devices/entities
# - Tasmota devices/entities
# - Zigbee ZHA devices/entities
# - Zigbee2MQTT devices/entities
# - Matter devices/entities
# - Matter Wi-Fi/Ethernet devices/entities - best effort
# - Matter Thread devices/entities - best effort
# - Matter unclassified devices/entities
# - Cloud-ish devices/entities
# - Supervisor add-ons - approximate
# - Updates available
#
# Important:
# - This uses only modern template: syntax.
# - There must be no root-level:
# sensor:
# - platform: template
# in this package.
# =====================================================================
template:
- trigger:
- platform: event
event_type: homeassistant_started
- platform: event
event_type: entity_registry_updated
- platform: event
event_type: device_registry_updated
- platform: event
event_type: area_registry_updated
- platform: event
event_type: core_config_updated
- platform: time_pattern
minutes: "/5"
sensor:
- sensor:
# ---------- Totals and basics ----------
- name: HA Summary - entities total
unique_id: ha_summary_entities_total
state: "{{ states | list | length }}"
unit_of_measurement: "entities"
- name: HA Summary - devices total
unique_id: ha_summary_devices_total
unit_of_measurement: "devices"
state: >-
{% set ids = namespace(v=[]) %}
{% for s in states %}
@@ -36,23 +39,27 @@ template:
{% endif %}
{% endfor %}
{{ ids.v | length }}
unit_of_measurement: "devices"
- name: HA Summary - entities total
unique_id: ha_summary_entities_total
unit_of_measurement: "entities"
state: "{{ states | list | length }}"
- name: HA Summary - entities broken
unique_id: ha_summary_entities_broken
unit_of_measurement: "entities"
state: >-
{{ states | selectattr('state','in',['unavailable','unknown']) | list | length }}
unit_of_measurement: "entities"
- name: HA Summary - cameras
unique_id: ha_summary_cameras
state: "{{ states.camera | list | length }}"
unit_of_measurement: "cameras"
state: "{{ states.camera | list | length }}"
- name: HA Summary - areas
unique_id: ha_summary_areas
state: "{{ areas() | list | length }}"
unit_of_measurement: "areas"
state: "{{ areas() | list | length }}"
- name: HA Summary - scenes
unique_id: ha_summary_scenes
@@ -66,13 +73,41 @@ template:
unique_id: ha_summary_automations
state: "{{ states.automation | list | length }}"
# ---------- ESPHome ----------
- name: HA Summary - esphome entities
unique_id: ha_summary_esphome_entities
state: "{{ integration_entities('esphome') | list | length }}"
# ---------- Add-ons and updates ----------
- name: HA Summary - addons
unique_id: ha_summary_addons
icon: mdi:puzzle
state: >-
{% set hassio_entities = integration_entities('hassio') | list %}
{% set skip = [
'update.home_assistant_core_update',
'update.home_assistant_supervisor_update',
'update.home_assistant_operating_system_update'
] %}
{% set count = namespace(v=0) %}
{% for u in states.update %}
{% if u.entity_id in hassio_entities and u.entity_id not in skip %}
{% set count.v = count.v + 1 %}
{% endif %}
{% endfor %}
{{ count.v }}
- name: HA Summary - hacs updates
unique_id: ha_summary_hacs_updates
icon: mdi:download
state: >-
{% set count = namespace(v=0) %}
{% for u in states.update %}
{% if u.state == 'on' and not (u.attributes.in_progress | default(false)) %}
{% set count.v = count.v + 1 %}
{% endif %}
{% endfor %}
{{ count.v }}
# ---------- ESPHome ----------
- name: HA Summary - esphome devices
unique_id: ha_summary_esphome_devices
unit_of_measurement: "devices"
state: >-
{% set ids = namespace(v=[]) %}
{% for e in integration_entities('esphome') %}
@@ -83,13 +118,15 @@ template:
{% endfor %}
{{ ids.v | length }}
# ---------- Tasmota ----------
- name: HA Summary - tasmota entities
unique_id: ha_summary_tasmota_entities
state: "{{ integration_entities('tasmota') | list | length }}"
- name: HA Summary - esphome entities
unique_id: ha_summary_esphome_entities
unit_of_measurement: "entities"
state: "{{ integration_entities('esphome') | list | length }}"
# ---------- Tasmota ----------
- name: HA Summary - tasmota devices
unique_id: ha_summary_tasmota_devices
unit_of_measurement: "devices"
state: >-
{% set ids = namespace(v=[]) %}
{% for e in integration_entities('tasmota') %}
@@ -100,13 +137,15 @@ template:
{% endfor %}
{{ ids.v | length }}
# ---------- Zigbee via ZHA ----------
- name: HA Summary - zha entities
unique_id: ha_summary_zha_entities
state: "{{ integration_entities('zha') | list | length }}"
- name: HA Summary - tasmota entities
unique_id: ha_summary_tasmota_entities
unit_of_measurement: "entities"
state: "{{ integration_entities('tasmota') | list | length }}"
- name: HA Summary - zha devices
unique_id: ha_summary_zha_devices
# ---------- Zigbee via ZHA ----------
- name: HA Summary - zigbee zha devices
unique_id: ha_summary_zigbee_zha_devices
unit_of_measurement: "devices"
state: >-
{% set ids = namespace(v=[]) %}
{% for e in integration_entities('zha') %}
@@ -117,77 +156,191 @@ template:
{% endfor %}
{{ ids.v | length }}
# ---------- Zigbee via Zigbee2MQTT (robust) ----------
# Match if device identifiers mention "zigbee2mqtt".
# Fallbacks: entity_id or unique_id contains "zigbee2mqtt" or ".z2m_".
- name: HA Summary - z2m entities
unique_id: ha_summary_z2m_entities
state: >-
{% set ent = namespace(v=[]) %}
{% for s in states %}
{% set eid = s.entity_id %}
{% set did = device_id(eid) %}
{% set match = false %}
{% if did %}
{% set ident = device_attr(did,'identifiers') | default([], true) %}
{% if (ident | string | lower) | regex_search('zigbee2mqtt') %}
{% set match = true %}
{% endif %}
{% endif %}
{% if not match %}
{% set uid = state_attr(eid,'unique_id') | default('', true) | string | lower %}
{% if 'zigbee2mqtt' in eid or '.z2m_' in eid or 'zigbee2mqtt' in uid %}
{% set match = true %}
{% endif %}
{% endif %}
{% if match %}
{% set ent.v = ent.v + [eid] %}
{% endif %}
{% endfor %}
{{ ent.v | unique | list | length }}
- name: HA Summary - zigbee zha entities
unique_id: ha_summary_zigbee_zha_entities
unit_of_measurement: "entities"
state: "{{ integration_entities('zha') | list | length }}"
- name: HA Summary - z2m devices
unique_id: ha_summary_z2m_devices
# ---------- Zigbee via Zigbee2MQTT ----------
# This counts MQTT entities whose Home Assistant device identifiers
# include "zigbee2mqtt". This should catch devices with clean entity IDs
# like sensor.lounge_blind_b_xbl12_battery.
- name: HA Summary - zigbee z2m devices
unique_id: ha_summary_zigbee_z2m_devices
unit_of_measurement: "devices"
state: >-
{% set dids = namespace(v=[]) %}
{% for s in states %}
{% set eid = s.entity_id %}
{% set did = device_id(eid) %}
{% for e in integration_entities('mqtt') | list %}
{% set did = device_id(e) %}
{% if did and did not in dids.v %}
{% set ident = device_attr(did,'identifiers') | default([], true) %}
{% set uid = state_attr(eid,'unique_id') | default('', true) | string | lower %}
{% if (ident | string | lower) | regex_search('zigbee2mqtt') or
'zigbee2mqtt' in eid or '.z2m_' in eid or 'zigbee2mqtt' in uid %}
{% set txt = (
((device_attr(did, 'identifiers') or []) | string) ~ ' ' ~
((device_attr(did, 'connections') or []) | string) ~ ' ' ~
((device_attr(did, 'name') or '') | string) ~ ' ' ~
((device_attr(did, 'model') or '') | string) ~ ' ' ~
((device_attr(did, 'manufacturer') or '') | string)
) | lower %}
{% if 'zigbee2mqtt' in txt %}
{% set dids.v = dids.v + [did] %}
{% endif %}
{% endif %}
{% endfor %}
{{ dids.v | length }}
# ---------- Cloud devices/entities (edit list as desired) ----------
- name: HA Summary - cloud entities
unique_id: ha_summary_cloud_entities
- name: HA Summary - zigbee z2m entities
unique_id: ha_summary_zigbee_z2m_entities
unit_of_measurement: "entities"
state: >-
{% set list_all =
integration_entities('tuya') +
integration_entities('ewelink') +
integration_entities('meross_cloud') +
integration_entities('tapo') +
integration_entities('tplink') +
integration_entities('nest') +
integration_entities('ring') +
integration_entities('arlo') +
integration_entities('wyze') +
integration_entities('myq') +
integration_entities('nuki') +
integration_entities('august') +
integration_entities('switchbot') +
integration_entities('icloud')
%}
{{ list_all | unique | list | length }}
{% set ent = namespace(v=[]) %}
{% for e in integration_entities('mqtt') | list %}
{% set did = device_id(e) %}
{% if did %}
{% set txt = (
((device_attr(did, 'identifiers') or []) | string) ~ ' ' ~
((device_attr(did, 'connections') or []) | string) ~ ' ' ~
((device_attr(did, 'name') or '') | string) ~ ' ' ~
((device_attr(did, 'model') or '') | string) ~ ' ' ~
((device_attr(did, 'manufacturer') or '') | string)
) | lower %}
{% if 'zigbee2mqtt' in txt %}
{% set ent.v = ent.v + [e] %}
{% endif %}
{% endif %}
{% endfor %}
{{ ent.v | unique | list | length }}
# ---------- Matter total ----------
- name: HA Summary - matter devices
unique_id: ha_summary_matter_devices
unit_of_measurement: "devices"
state: >-
{% set ids = namespace(v=[]) %}
{% for e in integration_entities('matter') %}
{% set did = device_id(e) %}
{% if did and did not in ids.v %}
{% set ids.v = ids.v + [did] %}
{% endif %}
{% endfor %}
{{ ids.v | length }}
- name: HA Summary - matter entities
unique_id: ha_summary_matter_entities
unit_of_measurement: "entities"
state: "{{ integration_entities('matter') | list | length }}"
# ---------- Matter over Wi-Fi / Ethernet - best effort ----------
- name: HA Summary - matter wifi devices
unique_id: ha_summary_matter_wifi_devices
unit_of_measurement: "devices"
state: >-
{% set dids = namespace(v=[]) %}
{% for e in integration_entities('matter') %}
{% set did = device_id(e) %}
{% if did and did not in dids.v %}
{% set txt = (
((device_attr(did, 'identifiers') or []) | string) ~ ' ' ~
((device_attr(did, 'connections') or []) | string) ~ ' ' ~
((device_attr(did, 'name') or '') | string) ~ ' ' ~
((device_attr(did, 'model') or '') | string) ~ ' ' ~
((device_attr(did, 'manufacturer') or '') | string)
) | lower %}
{% if 'wifi' in txt or 'wi-fi' in txt or 'wlan' in txt or 'ethernet' in txt %}
{% set dids.v = dids.v + [did] %}
{% endif %}
{% endif %}
{% endfor %}
{{ dids.v | length }}
- name: HA Summary - matter wifi entities
unique_id: ha_summary_matter_wifi_entities
unit_of_measurement: "entities"
state: >-
{% set ent = namespace(v=[]) %}
{% for e in integration_entities('matter') %}
{% set did = device_id(e) %}
{% if did %}
{% set txt = (
((device_attr(did, 'identifiers') or []) | string) ~ ' ' ~
((device_attr(did, 'connections') or []) | string) ~ ' ' ~
((device_attr(did, 'name') or '') | string) ~ ' ' ~
((device_attr(did, 'model') or '') | string) ~ ' ' ~
((device_attr(did, 'manufacturer') or '') | string)
) | lower %}
{% if 'wifi' in txt or 'wi-fi' in txt or 'wlan' in txt or 'ethernet' in txt %}
{% set ent.v = ent.v + [e] %}
{% endif %}
{% endif %}
{% endfor %}
{{ ent.v | unique | list | length }}
# ---------- Matter over Thread - best effort ----------
- name: HA Summary - matter thread devices
unique_id: ha_summary_matter_thread_devices
unit_of_measurement: "devices"
state: >-
{% set dids = namespace(v=[]) %}
{% for e in integration_entities('matter') %}
{% set did = device_id(e) %}
{% if did and did not in dids.v %}
{% set txt = (
((device_attr(did, 'identifiers') or []) | string) ~ ' ' ~
((device_attr(did, 'connections') or []) | string) ~ ' ' ~
((device_attr(did, 'name') or '') | string) ~ ' ' ~
((device_attr(did, 'model') or '') | string) ~ ' ' ~
((device_attr(did, 'manufacturer') or '') | string)
) | lower %}
{% if 'thread' in txt or 'openthread' in txt or 'otbr' in txt or 'border router' in txt or 'border-router' in txt or '802.15.4' in txt %}
{% set dids.v = dids.v + [did] %}
{% endif %}
{% endif %}
{% endfor %}
{{ dids.v | length }}
- name: HA Summary - matter thread entities
unique_id: ha_summary_matter_thread_entities
unit_of_measurement: "entities"
state: >-
{% set ent = namespace(v=[]) %}
{% for e in integration_entities('matter') %}
{% set did = device_id(e) %}
{% if did %}
{% set txt = (
((device_attr(did, 'identifiers') or []) | string) ~ ' ' ~
((device_attr(did, 'connections') or []) | string) ~ ' ' ~
((device_attr(did, 'name') or '') | string) ~ ' ' ~
((device_attr(did, 'model') or '') | string) ~ ' ' ~
((device_attr(did, 'manufacturer') or '') | string)
) | lower %}
{% if 'thread' in txt or 'openthread' in txt or 'otbr' in txt or 'border router' in txt or 'border-router' in txt or '802.15.4' in txt %}
{% set ent.v = ent.v + [e] %}
{% endif %}
{% endif %}
{% endfor %}
{{ ent.v | unique | list | length }}
# ---------- Matter unclassified ----------
- name: HA Summary - matter unclassified devices
unique_id: ha_summary_matter_unclassified_devices
unit_of_measurement: "devices"
state: >-
{% set total = states('sensor.ha_summary_matter_devices') | int(0) %}
{% set wifi = states('sensor.ha_summary_matter_wifi_devices') | int(0) %}
{% set thread = states('sensor.ha_summary_matter_thread_devices') | int(0) %}
{{ [total - wifi - thread, 0] | max }}
- name: HA Summary - matter unclassified entities
unique_id: ha_summary_matter_unclassified_entities
unit_of_measurement: "entities"
state: >-
{% set total = states('sensor.ha_summary_matter_entities') | int(0) %}
{% set wifi = states('sensor.ha_summary_matter_wifi_entities') | int(0) %}
{% set thread = states('sensor.ha_summary_matter_thread_entities') | int(0) %}
{{ [total - wifi - thread, 0] | max }}
# ---------- Cloud devices/entities ----------
- name: HA Summary - cloud devices
unique_id: ha_summary_cloud_devices
unit_of_measurement: "devices"
state: >-
{% set dids = namespace(v=[]) %}
{% for e in (
@@ -213,34 +366,24 @@ template:
{% endfor %}
{{ dids.v | length }}
# ---------- Supervisor add-ons (approximate) ----------
# Union of: update entities in hassio integration + updates attributed to Supervisor.
- name: HA Summary - addons
unique_id: ha_summary_addons
icon: mdi:puzzle
- name: HA Summary - cloud entities
unique_id: ha_summary_cloud_entities
unit_of_measurement: "entities"
state: >-
{% set hassio_updates = (integration_entities('hassio') | list) %}
{% set a = states.update
| selectattr('entity_id','in', hassio_updates)
| map(attribute='entity_id')
| list %}
{% set b = states.update
| selectattr('attributes.attribution','defined')
| selectattr('attributes.attribution','search','Supervisor')
| map(attribute='entity_id')
| list %}
{% set ids = (a + b)
| unique
| reject('search','^update\\.home_assistant_(core|supervisor|operating_system)_update$')
| list %}
{{ ids | length }}
# ---------- HACS updates available (proxy only) ----------
- name: HA Summary - hacs updates
unique_id: ha_summary_hacs_updates
icon: mdi:download
state: >-
{{ states.update
| selectattr('state','eq','on')
| rejectattr('attributes.in_progress','true')
| list | length }}
{% set list_all =
integration_entities('tuya') +
integration_entities('ewelink') +
integration_entities('meross_cloud') +
integration_entities('tapo') +
integration_entities('tplink') +
integration_entities('nest') +
integration_entities('ring') +
integration_entities('arlo') +
integration_entities('wyze') +
integration_entities('myq') +
integration_entities('nuki') +
integration_entities('august') +
integration_entities('switchbot') +
integration_entities('icloud')
%}
{{ list_all | unique | list | length }}
@@ -1,30 +1,4 @@
automation:
# Two lights and two switches for these corridor lights
- id: garage_entry_outside_2way_sync
alias: Garage Entry & Outside Light 2-Way Sync
initial_state: true
use_blueprint:
path: zorruno/two_three_way_sync.yaml
input:
members:
- switch.esp_garageentrylights_relay_1_corridor_lights
- switch.esp_loungesouthleftswitch_relay_2_outside_light_1
sync_on_start: true
startup_delay: 5
# Internal Garage Lights (Light controller is sonoff basic in cupboard)
- id: garage_lights_2way_sync
alias: Garage Lights 2-Way Sync
initial_state: true
use_blueprint:
path: zorruno/two_three_way_sync.yaml
input:
members:
- switch.esp_garageentrylights_relay_3_garage_lights
- switch.esp_garagelights_garage_lights
sync_on_start: true
startup_delay: 5
# Stair Lights Top
- id: stair_lights_top_2way_sync
alias: Stair lights Top Lights 2-Way Sync
@@ -0,0 +1,26 @@
automation:
# Two lights and two switches for these corridor lights
- id: garage_entry_outside_2way_sync
alias: Garage Entry & Outside Light 2-Way Sync
initial_state: true
use_blueprint:
path: zorruno/two_three_way_sync.yaml
input:
members:
- switch.esp_garageentrylights_relay_1_corridor_lights
- switch.esp_loungesouthleftswitch_relay_2_outside_light_1
sync_on_start: true
startup_delay: 5
# Internal Garage Lights (Light controller is sonoff basic in cupboard)
- id: garage_lights_2way_sync
alias: Garage Lights 2-Way Sync
initial_state: true
use_blueprint:
path: zorruno/two_three_way_sync.yaml
input:
members:
- switch.esp_garageentrylights_relay_3_garage_lights
- switch.esp_garagelights_garage_lights
sync_on_start: true
startup_delay: 5
@@ -0,0 +1,64 @@
################################################################################
# PACKAGE: Master Bedroom Main Lights Switch Sync
################################################################################
#
# Version: 1.3
# Date: 2026-06-12
#
# Purpose:
# Home Assistant fast-path sync for the two Master Bedroom Zemismart switches.
#
# Confirmed wiring/logic after live checks:
# - North Lights real controller:
# light.tasmo_ifan02_3793_bedrm1_1
# - South Lights real physical load:
# WEST switch Relay 2
# - Relay 1 on both switches:
# North Lights virtual/mirror/control
# - Relay 2 on both switches:
# South Lights control/mirror; WEST Relay 2 is the real physical load
#
# NOTE:
# Entity IDs may retain their old names in HA after the ESPHome friendly-name change.
# Confirm the final entity IDs in HA after flashing.
################################################################################
automation:
##############################################################################
# Master Bedroom North Lights Sync
##############################################################################
- id: master_bedroom_north_lights_3way_sync
alias: Master Bedroom North Lights 3-Way Sync
initial_state: true
use_blueprint:
path: zorruno/two_three_way_sync.yaml
input:
members:
# SOUTH switch virtual North Lights control/mirror.
- switch.master_bedroom_master_bedroom_main_lights_south_relay_1_master_bedroom_north_lights
# WEST switch virtual North Lights control/mirror.
- switch.master_bedroom_master_bedroom_main_lights_west_relay_1_master_bedroom_north_lights
# Actual North Lights controller.
- light.tasmo_ifan02_3793_bedrm1_1
sync_on_start: false
startup_delay: 5
##############################################################################
# Master Bedroom South Lights Sync
##############################################################################
- id: master_bedroom_south_lights_2way_sync
alias: Master Bedroom South Lights 2-Way Sync
initial_state: true
use_blueprint:
path: zorruno/two_three_way_sync.yaml
input:
members:
# SOUTH switch virtual South Lights control/mirror.
- switch.master_bedroom_master_bedroom_main_lights_south_relay_2_master_bedroom_south_lights
# WEST switch physical South Lights relay.
- switch.master_bedroom_master_bedroom_main_lights_west_relay_2_master_bedroom_south_lights
sync_on_start: false
startup_delay: 5
+7 -7
View File
@@ -135,7 +135,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
input_number.openhasp_1_backlight_level: 100
number.lounge_nspanel_touchscreen_display_brightness: 100
TV General:
@@ -156,8 +156,8 @@ 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
number.lounge_nspanel_touchscreen_display_brightness: 80
input_number.openhasp_1_backlight_level: 30
number.lounge_nspanel_touchscreen_display_brightness: 210
Balanced:
light.esp_lounge6chdimmer_couch_spots_left: 40
@@ -177,7 +177,7 @@ script:
#switch.esp_loungenorthswitch_relay_1_main_lights: "on"
number.esp_loungepelmetleds2_lounge_pelmet_leds_a_output_set_0_100: 50
number.esp_loungepelmetleds2_lounge_pelmet_leds_b_output_set_0_100: 50
input_number.openhasp_1_backlight_level: 100
input_number.openhasp_1_backlight_level: 30
number.lounge_nspanel_touchscreen_display_brightness: 80
Movie:
@@ -198,7 +198,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
input_number.openhasp_1_backlight_level: 10
switch.esp_mainkitchenlights_relay_1_main_lights: "off"
switch.esp_mainkitchenlights_relay_2_benchtop_lights: "off"
number.lounge_nspanel_touchscreen_display_brightness: 40
@@ -238,7 +238,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: 10
input_number.openhasp_1_backlight_level: 6
number.lounge_nspanel_touchscreen_display_brightness: 30
Warm Cozy:
@@ -278,7 +278,7 @@ 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
input_number.openhasp_1_backlight_level: 60
#switch.esp_mainkitchenlights_relay_1_main_lights: "off"
#switch.esp_mainkitchenlights_relay_2_benchtop_lights: "off"
number.lounge_nspanel_touchscreen_display_brightness: 80
+160 -142
View File
@@ -1,15 +1,24 @@
###############################################################################
# PACKAGE: OpenHASP Thermostat / Gas Heater Control
# VERSION: v1.1 (2026-06-18)
# NODE/TOPIC ROOT: hasp/openhasp_1/
#
# PAGE OBJECTS USED
# - p1b1 = main temperature display
# - p1b2 = setpoint display
# - p1b2 = setpoint icon/value display
# - p1b3 = setpoint down
# - p1b4 = setpoint up
# - p1b5 = heater status text
# - p1b6 = heater operation / flame button
# - p1b7 = humidity display
# - p1b7 = humidity icon/value display
#
# NOTES:
# - The HASP flame button toggles the ESPHome Heater Operation switch.
# - Heater Operation ON = ESPHome Mode AUTO.
# - Heater Operation OFF = ESPHome Mode OFF.
# - BYPASS (Remote Only) is only shown when that ESPHome mode is selected.
# - p1b2 and p1b7 include their icons in the MQTT text payload so HA updates
# do not overwrite the icons loaded from pages.jsonl.
###############################################################################
###############################################################################
@@ -41,6 +50,7 @@ automation:
# Main temperature display
# - Shows normal lounge temperature
# - Normalises values so we do not get duplicated °C or long float strings
# - No space is used before °C
# -------------------------------------------------------------------------
- id: hasp_temp_to_btn_text
alias: "HASP: update temp (p1b1.text)"
@@ -60,31 +70,32 @@ automation:
| replace('C', '')
| trim %}
{% if cleaned not in ['unknown', 'unavailable', '', 'none', 'None'] %}
{{ cleaned | float(0) | round(1) }} °C
{{ cleaned | float(0) | round(1) }}°C
{% else %}
-- °C
--°C
{% endif %}
# -------------------------------------------------------------------------
# Heater status text at bottom of screen
# - Shows OFF when ESPHome Mode is OFF
# - Shows BYPASS only when ESPHome Mode is BYPASS (Remote Only)
# - Otherwise mirrors the ESPHome status text, now normally Auto (...)
# -------------------------------------------------------------------------
- id: hasp_heater_status_to_btn_text
alias: "HASP: update heater status (p1b5.text)"
mode: queued
trigger:
- platform: state
entity_id: sensor.rinnai_neo_gas_heater_rinnai_neo_gas_heater_status_text
entity_id:
- sensor.rinnai_neo_gas_heater_rinnai_neo_gas_heater_status_text
- select.rinnai_neo_gas_heater_mode
action:
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b5.text"
payload: >
{% set s = states('sensor.rinnai_neo_gas_heater_rinnai_neo_gas_heater_status_text') %}
{{ s if s not in ['unknown', 'unavailable', ''] else 'HEATER --' }}
- service: script.hasp_publish_heater_status_text
# -------------------------------------------------------------------------
# Setpoint display on left side of centre row
# - Strips any existing units so only one °C is shown
# - Includes thermometer icon so HA updates do not remove it
# -------------------------------------------------------------------------
- id: hasp_setpoint_to_btn_text
alias: "HASP: update setpoint (p1b2.text)"
@@ -104,13 +115,14 @@ automation:
| replace('C', '')
| trim %}
{% if cleaned not in ['unknown', 'unavailable', '', 'none', 'None'] %}
Set: {{ cleaned | float(0) | round(1) }}°C
{{ '\uE50F' }} {{ cleaned | float(0) | round(1) }}°C
{% else %}
Set: -- °C
{{ '\uE50F' }} --°C
{% endif %}
# -------------------------------------------------------------------------
# Humidity display on right side of centre row
# - Includes water/drop icon so HA updates do not remove it
# -------------------------------------------------------------------------
- id: hasp_humidity_to_btn_text
alias: "HASP: update humidity (p1b7.text)"
@@ -126,9 +138,9 @@ automation:
{% set raw = states('sensor.lounge_environment_humidity_mqtt') %}
{% set cleaned = raw | replace('%', '') | trim %}
{% if cleaned in ['unknown', 'unavailable', '', 'none', 'None'] %}
Hum: Unknown
{{ '\uE58C' }} --%
{% else %}
Hum: {{ cleaned }}%
{{ '\uE58C' }} {{ cleaned }}%
{% endif %}
# -------------------------------------------------------------------------
@@ -165,7 +177,9 @@ automation:
# -------------------------------------------------------------------------
# Middle flame button
# - Tap toggles heater operation
# - Tap toggles Heater Operation, which maps AUTO <-> OFF in ESPHome
# - If Mode is BYPASS (Remote Only), tapping this will turn Heater Operation ON
# and therefore return the ESPHome controller to AUTO.
# -------------------------------------------------------------------------
- id: hasp_btn_heat_toggle
alias: "HASP: p1b6 up -> heater operation toggle"
@@ -183,9 +197,10 @@ automation:
# -------------------------------------------------------------------------
# Flame button appearance
# - Operation OFF: dark grey background, light grey flame
# - Operation ON, Activate Flame OFF: theme blue background, white flame
# - Operation ON, Activate Flame ON: theme blue background, solid red flame
# - OFF: dark grey background, light grey flame
# - BYPASS: dark grey background, amber flame
# - AUTO, flame OFF: theme blue background, white flame
# - AUTO, flame ON: theme blue background, solid red flame
# -------------------------------------------------------------------------
- id: hasp_heat_button_state
alias: "HASP: update heat button state (p1b6)"
@@ -193,102 +208,11 @@ automation:
trigger:
- platform: state
entity_id:
- select.rinnai_neo_gas_heater_mode
- switch.rinnai_neo_gas_heater_heater_operation
- switch.rinnai_neo_gas_heater_rinnai_neo_gas_heater_activate_flame
action:
- choose:
# Heater Operation OFF -> dark grey button, light grey flame
- conditions:
- condition: state
entity_id: switch.rinnai_neo_gas_heater_heater_operation
state: "off"
sequence:
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.text"
payload: "\uE238"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.text_color"
payload: "#C0C0C0"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.bg_color"
payload: "#2F3240"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.bg_grad_color"
payload: "#2F3240"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.border_color"
payload: "#2F3240"
# Heater Operation ON, flame OFF -> blue button, white flame
- conditions:
- condition: state
entity_id: switch.rinnai_neo_gas_heater_heater_operation
state: "on"
- condition: state
entity_id: switch.rinnai_neo_gas_heater_rinnai_neo_gas_heater_activate_flame
state: "off"
sequence:
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.text"
payload: "\uE238"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.text_color"
payload: "#FFFFFF"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.bg_color"
payload: "#0095D9"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.bg_grad_color"
payload: "#00B6FF"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.border_color"
payload: "#0095D9"
# Heater Operation ON, flame ON -> blue button, red flame
- conditions:
- condition: state
entity_id: switch.rinnai_neo_gas_heater_heater_operation
state: "on"
- condition: state
entity_id: switch.rinnai_neo_gas_heater_rinnai_neo_gas_heater_activate_flame
state: "on"
sequence:
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.text"
payload: "\uE238"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.text_color"
payload: "#FF3030"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.bg_color"
payload: "#0095D9"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.bg_grad_color"
payload: "#00B6FF"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.border_color"
payload: "#0095D9"
- service: script.hasp_apply_heat_button_state
# -------------------------------------------------------------------------
# Start / restart display when HA starts
@@ -342,17 +266,12 @@ script:
| replace('C', '')
| trim %}
{% if cleaned not in ['unknown', 'unavailable', '', 'none', 'None'] %}
{{ cleaned | float(0) | round(1) }} °C
{{ cleaned | float(0) | round(1) }}°C
{% else %}
-- °C
--°C
{% endif %}
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b5.text"
payload: >
{% set s = states('sensor.rinnai_neo_gas_heater_rinnai_neo_gas_heater_status_text') %}
{{ s if s not in ['unknown', 'unavailable', ''] else 'HEATER --' }}
- service: script.hasp_publish_heater_status_text
- service: mqtt.publish
data:
@@ -365,9 +284,9 @@ script:
| replace('C', '')
| trim %}
{% if cleaned not in ['unknown', 'unavailable', '', 'none', 'None'] %}
Set: {{ cleaned | float(0) | round(1) }}°C
{{ '\uE50F' }} {{ cleaned | float(0) | round(1) }}°C
{% else %}
Set: -- °C
{{ '\uE50F' }} --°C
{% endif %}
- service: mqtt.publish
@@ -377,23 +296,104 @@ script:
{% set raw = states('sensor.lounge_environment_humidity_mqtt') %}
{% set cleaned = raw | replace('%', '') | trim %}
{% if cleaned in ['unknown', 'unavailable', '', 'none', 'None'] %}
Hum: Unknown
{{ '\uE58C' }} --%
{% else %}
Hum: {{ cleaned }}%
{{ '\uE58C' }} {{ cleaned }}%
{% endif %}
# Push middle flame button state
- service: script.hasp_apply_heat_button_state
# -------------------------------------------------------------------------
# Publish heater status text to p1b5
# Keeps OFF/BYPASS display based on ESPHome Mode, not just the operation switch.
#
# Display behaviour:
# - OFF -> Off
# - BYPASS (Remote Only) -> Bypass (Remote Only)
# - Auto but flame OFF -> Auto (Up to Temp)
# - Auto (Level 1) -> Auto 🔥
# - Auto (Level 2) -> Auto 🔥🔥
# - Auto (Level 3) -> Auto 🔥🔥🔥
# - Auto (Level 4) -> Auto 🔥🔥🔥🔥
#
# The flame is the openHASP/Material icon \uE238, not an emoji.
# -------------------------------------------------------------------------
hasp_publish_heater_status_text:
alias: "HASP: publish heater status text"
mode: queued
sequence:
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b5.text"
payload: >
{% set mode = states('select.rinnai_neo_gas_heater_mode') %}
{% set op = states('switch.rinnai_neo_gas_heater_heater_operation') %}
{% set flame_on = is_state('switch.rinnai_neo_gas_heater_rinnai_neo_gas_heater_activate_flame', 'on') %}
{% set s = states('sensor.rinnai_neo_gas_heater_rinnai_neo_gas_heater_status_text') %}
{% set flame = '\uE238' %}
{% if mode == 'OFF' %}
Off
{% elif mode == 'BYPASS (Remote Only)' %}
Bypass (Remote Only)
{% elif op == 'on' and not flame_on %}
Auto
{% elif s not in ['unknown', 'unavailable', '', 'none', 'None'] %}
{% set matches = s | regex_findall('Level ([0-9]+)', ignorecase=True) %}
{% set level = matches[0] | int(0) if matches | count > 0 else 0 %}
{% if level > 0 %}
Auto {{ flame * level }}
{% else %}
{{ s }}
{% endif %}
{% elif mode not in ['unknown', 'unavailable', '', 'none', 'None'] %}
{{ mode }}
{% else %}
HEATER --
{% endif %}
# -------------------------------------------------------------------------
# Publish middle flame button icon and colours to p1b6
# -------------------------------------------------------------------------
hasp_apply_heat_button_state:
alias: "HASP: apply heat button state"
mode: queued
sequence:
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.text"
payload: "\uE238"
- choose:
# Heater Operation OFF -> dark grey button, light grey flame
# BYPASS (Remote Only) -> dark button, amber flame so it is visibly different
# from ordinary OFF, but still looks inactive.
- conditions:
- condition: state
entity_id: switch.rinnai_neo_gas_heater_heater_operation
state: "off"
entity_id: select.rinnai_neo_gas_heater_mode
state: "BYPASS (Remote Only)"
sequence:
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.text_color"
payload: "#FFB000"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.bg_color"
payload: "#2F3240"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.bg_grad_color"
payload: "#2F3240"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.border_color"
payload: "#2F3240"
# OFF -> dark grey button, light grey flame
- conditions:
- condition: state
entity_id: select.rinnai_neo_gas_heater_mode
state: "OFF"
sequence:
- service: mqtt.publish
data:
@@ -412,7 +412,33 @@ script:
topic: "hasp/openhasp_1/command/p1b6.border_color"
payload: "#2F3240"
# Heater Operation ON, flame OFF -> blue button, white flame
# AUTO, flame ON -> blue button, red flame
- conditions:
- condition: state
entity_id: switch.rinnai_neo_gas_heater_heater_operation
state: "on"
- condition: state
entity_id: switch.rinnai_neo_gas_heater_rinnai_neo_gas_heater_activate_flame
state: "on"
sequence:
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.text_color"
payload: "#FF3030"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.bg_color"
payload: "#0095D9"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.bg_grad_color"
payload: "#00B6FF"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.border_color"
payload: "#0095D9"
# AUTO, flame OFF -> blue button, white flame
- conditions:
- condition: state
entity_id: switch.rinnai_neo_gas_heater_heater_operation
@@ -438,31 +464,23 @@ script:
topic: "hasp/openhasp_1/command/p1b6.border_color"
payload: "#0095D9"
# Heater Operation ON, flame ON -> blue button, red flame
- conditions:
- condition: state
entity_id: switch.rinnai_neo_gas_heater_heater_operation
state: "on"
- condition: state
entity_id: switch.rinnai_neo_gas_heater_rinnai_neo_gas_heater_activate_flame
state: "on"
sequence:
default:
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.text_color"
payload: "#FF3030"
payload: "#C0C0C0"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.bg_color"
payload: "#0095D9"
payload: "#2F3240"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.bg_grad_color"
payload: "#00B6FF"
payload: "#2F3240"
- service: mqtt.publish
data:
topic: "hasp/openhasp_1/command/p1b6.border_color"
payload: "#0095D9"
payload: "#2F3240"
# -------------------------------------------------------------------------
# Style centre row info buttons as text panels
+5 -53
View File
@@ -50,7 +50,7 @@
#
# MQTT STATUS:
# - Topic:
# viewroad-status/homeassistant/quiet-time
# viewroad-status/quiet-time
# - Payload:
# on
# off
@@ -126,22 +126,11 @@ automation:
target:
entity_id: input_boolean.quiet_time
- id: quiet_time_state_change_actions_and_mqtt
alias: "Quiet Time - State Change Actions and MQTT"
description: "Publishes the Quiet Time state to MQTT and runs the configured start/stop entity actions."
- id: quiet_time_mqtt
alias: "Quiet Time - State to MQTT"
description: "Publishes the Quiet Time state to MQTT"
mode: single
variables:
quiet_time_start_turn_off_entities:
- light.led_matrix_display_led_matrix_display_display
quiet_time_start_turn_on_entities: []
quiet_time_stop_turn_off_entities: []
quiet_time_stop_turn_on_entities:
- light.led_matrix_display_led_matrix_display_display
triggers:
- trigger: state
entity_id: input_boolean.quiet_time
@@ -155,42 +144,5 @@ automation:
actions:
- action: mqtt.publish
data:
topic: viewroad-status/homeassistant/quiet-time
topic: viewroad-status/quiet-time
payload: "{{ trigger.to_state.state }}"
- choose:
- conditions:
- condition: template
value_template: "{{ trigger.to_state.state == 'on' }}"
sequence:
- repeat:
for_each: "{{ quiet_time_start_turn_off_entities }}"
sequence:
- action: homeassistant.turn_off
target:
entity_id: "{{ repeat.item }}"
- repeat:
for_each: "{{ quiet_time_start_turn_on_entities }}"
sequence:
- action: homeassistant.turn_on
target:
entity_id: "{{ repeat.item }}"
- conditions:
- condition: template
value_template: "{{ trigger.to_state.state == 'off' }}"
sequence:
- repeat:
for_each: "{{ quiet_time_stop_turn_off_entities }}"
sequence:
- action: homeassistant.turn_off
target:
entity_id: "{{ repeat.item }}"
- repeat:
for_each: "{{ quiet_time_stop_turn_on_entities }}"
sequence:
- action: homeassistant.turn_on
target:
entity_id: "{{ repeat.item }}"
+190
View File
@@ -0,0 +1,190 @@
################################################################################
# Quiet Time On / Off Actions Package
################################################################################
#
# TITLE:
# Quiet Time On / Off Actions
#
# FILE:
# quiet_time_on_off_actions.yaml
#
# VERSION:
# V1.1 2026-06-15
#
# VERSION HISTORY:
# V1.1 2026-06-15
# - Added separate Quiet Time ON and Quiet Time OFF action sections.
# - Added support for non-binary Quiet Time OFF actions.
# - Sets input_number.openhasp_1_backlight_level to 200 when Quiet Time ends.
#
# V1.0 2026-06-15
# - Initial standalone package split out from the main Quiet Time package.
# - Turns selected entities on/off when input_boolean.quiet_time changes.
#
# PURPOSE:
# Runs local Home Assistant actions when Quiet Time is turned ON or OFF.
#
# The main Quiet Time package should continue to own:
# - input_boolean.quiet_time
# - daily reset behaviour
# - MQTT quiet_time state publishing
#
# This package only handles extra local actions that should happen when
# Quiet Time starts or ends.
#
# EDITING NOTES:
# - Add normal on/off entities to the relevant entity lists.
# - Empty lists are fine and will simply do nothing.
# - Use the "Other Quiet Time OFF Actions" section for things that are not
# simple on/off actions, such as setting input_numbers, selects, scenes,
# scripts, or custom service calls.
#
################################################################################
################################################################################
# AUTOMATIONS
################################################################################
automation:
- id: quiet_time_state_change_actions
alias: "Quiet Time - State Change Actions"
description: "Runs configured Home Assistant actions when Quiet Time is turned on or off."
mode: single
variables:
##########################################################################
# QUIET TIME ON ACTION LISTS
##########################################################################
#
# These actions run when input_boolean.quiet_time changes to "on".
#
quiet_time_on_turn_off_entities:
- light.led_matrix_display_led_matrix_display_display
quiet_time_on_turn_on_entities: []
##########################################################################
# QUIET TIME OFF ACTION LISTS
##########################################################################
#
# These actions run when input_boolean.quiet_time changes to "off".
#
quiet_time_off_turn_off_entities: []
quiet_time_off_turn_on_entities:
- light.led_matrix_display_led_matrix_display_display
##########################################################################
# OTHER QUIET TIME OFF ACTION VALUES
##########################################################################
#
# Use this list for input_number helpers that should be set when Quiet Time
# ends.
#
# Format:
# - entity_id: input_number.some_helper
# value: 123
#
quiet_time_off_input_number_values:
- entity_id: input_number.openhasp_1_backlight_level
value: 200
triggers:
- trigger: state
entity_id: input_boolean.quiet_time
from:
- "on"
- "off"
to:
- "on"
- "off"
actions:
- choose:
######################################################################
# QUIET TIME ON ACTIONS
######################################################################
#
# Runs when Quiet Time starts.
#
- conditions:
- condition: template
value_template: "{{ trigger.to_state.state == 'on' }}"
sequence:
- repeat:
for_each: "{{ quiet_time_on_turn_off_entities }}"
sequence:
- action: homeassistant.turn_off
target:
entity_id: "{{ repeat.item }}"
- repeat:
for_each: "{{ quiet_time_on_turn_on_entities }}"
sequence:
- action: homeassistant.turn_on
target:
entity_id: "{{ repeat.item }}"
######################################################################
# QUIET TIME OFF ACTIONS
######################################################################
#
# Runs when Quiet Time ends.
#
- conditions:
- condition: template
value_template: "{{ trigger.to_state.state == 'off' }}"
sequence:
##################################################################
# Quiet Time OFF - binary/entity actions
##################################################################
- repeat:
for_each: "{{ quiet_time_off_turn_off_entities }}"
sequence:
- action: homeassistant.turn_off
target:
entity_id: "{{ repeat.item }}"
- repeat:
for_each: "{{ quiet_time_off_turn_on_entities }}"
sequence:
- action: homeassistant.turn_on
target:
entity_id: "{{ repeat.item }}"
##################################################################
# Quiet Time OFF - input_number set actions
##################################################################
- repeat:
for_each: "{{ quiet_time_off_input_number_values }}"
sequence:
- action: input_number.set_value
target:
entity_id: "{{ repeat.item.entity_id }}"
data:
value: "{{ repeat.item.value | float }}"
##################################################################
# Quiet Time OFF - other custom actions
##################################################################
#
# Add any extra non-binary service calls here later.
#
# Examples:
#
# - action: script.some_script
#
# - action: select.select_option
# target:
# entity_id: select.some_select
# data:
# option: "Some Option"
#
##################################################################