Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 79597fb021 | |||
| d4f123600c | |||
| ca1f1637cb | |||
| 02f3e44248 | |||
| 6276d6fb90 | |||
| 3423c18e8e | |||
| a839326029 | |||
| a9ce83fcc4 | |||
| 3955435c9e | |||
| 6e2d5bee4d | |||
| 892c7c4577 | |||
| 21c40e6ec7 | |||
| b9874c5053 | |||
| 4b0d8b573c | |||
| 1989b45a87 | |||
| 95144489af | |||
| 005e653f73 |
+1
-1
@@ -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}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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
+116
-42
@@ -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,21 +432,37 @@ 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: enforce_heater_off
|
||||
- if:
|
||||
condition:
|
||||
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);
|
||||
- delay: ${warmup_seconds}s
|
||||
- script.execute: calibrate_to_min
|
||||
- script.execute: auto_control_tick
|
||||
- script.execute: adjust_to_desired
|
||||
- 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:
|
||||
# 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);
|
||||
- delay: ${warmup_seconds}s
|
||||
- script.execute: calibrate_to_min
|
||||
- script.execute: auto_control_tick
|
||||
- script.execute: adjust_to_desired
|
||||
|
||||
on_release:
|
||||
- lambda: |-
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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) }}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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 }}
|
||||
|
||||
-26
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
- 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"
|
||||
default:
|
||||
- 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"
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Style centre row info buttons as text panels
|
||||
|
||||
@@ -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 }}"
|
||||
|
||||
@@ -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"
|
||||
#
|
||||
##################################################################
|
||||
Reference in New Issue
Block a user