Compare commits

..
5 Commits
37 changed files with 3491 additions and 2386 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

+1 -1
View File
@@ -1 +1 @@
{"pid": 71, "version": 1, "ha_version": "2026.8.3", "start_ts": 1788051080.3314128}
{"pid": 71, "version": 1, "ha_version": "2026.9.2", "start_ts": 1789211351.1286738}
+67
View File
@@ -0,0 +1,67 @@
TP-Link Deco Guest Wi-Fi Recovery Archive
==========================================
Version: 1.0.0
Created: 2026-09-02
Project: P026 Flounder downstairs guest-area controls
Purpose
-------
This archive preserves the locally maintained TP-Link Deco Guest Wi-Fi
customisation. The upstream tplink_deco integration does not provide this
service or sensor. Reinstalling or overwriting custom_components/tplink_deco
can remove them.
Archive
-------
File: docs/tplink_deco_guest_wifi_recovery_2026-09-02.tgz
SHA-256: 452ed7b7be5e8c469dc7740d5297959179204d24593a22c965f1915ca727c48d
Contents
--------
- custom_components/tplink_deco/api.py
- custom_components/tplink_deco/__init__.py
- custom_components/tplink_deco/const.py
- custom_components/tplink_deco/services.yaml
- custom_components/tplink_deco/coordinator.py
- custom_components/tplink_deco/binary_sensor.py
- packages/tplink_deco_guest_wifi.yaml
Behaviour Preserved
-------------------
- Service: tplink_deco.set_guest_network
- Service field: enable (boolean)
- Router state: binary_sensor.data_cupboard_new_sundamage_12_deco_guest_network
- Requested state: input_boolean.wifi_guest_network
- Flounder control: the requested-state helper; the package reconciles it to
the router-confirmed sensor.
- Write protocol: read each returned wireless band, retain its host, guest and
backhaul settings, set only guest.enable, add radio.channel from host.channel,
write each band separately, then read back and verify every written band.
- Deco firmware can return a malformed successful response containing
{"error_code": 0}; the integration accepts that response only for writes and
relies on the read-back verification as the final confirmation.
Recovery Procedure
------------------
1. Stop before updating, reinstalling, or deleting custom_components/tplink_deco.
2. Verify the archive checksum above.
3. Extract the archive from /homeassistant, preserving the listed paths.
4. Run Home Assistant configuration validation and Python compilation for the
restored integration files.
5. Restart Home Assistant. A restart is required for custom integration code.
6. Confirm tplink_deco.set_guest_network is registered and the Guest Network
binary sensor is available before operating Guest Wi-Fi.
Update Policy
-------------
Do not replace custom_components/tplink_deco with an upstream or HACS update
without first comparing it to this archive and deliberately reapplying the
Guest Wi-Fi customisation. The archive contains no credentials, tokens, session
cookies, or router address.
Binary file not shown.
+2170 -2108
View File
File diff suppressed because it is too large Load Diff
+68 -40
View File
@@ -6,6 +6,7 @@
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-bedside-panel.yaml
#:########################################################################################:#
# VERSIONS:
# V1.56 2026-09-04 Added latched blackout after 10 seconds below 1 lx with immediate exit
# V1.55 2026-08-16 Show only Clock time and date in Blackout ambient-light mode
# V1.54 2026-08-16 Dimmed Clock time and date palette in Blackout ambient-light mode
# V1.53 2026-07-31 Expanded Jobs Summary to nine active jobs in a 3x4 grid with three spare slots
@@ -248,7 +249,6 @@ substitutions:
# Bright light: approximately 642-643 lx
ambient_lux_multiplier_default: "200"
display_blackout_lux_threshold_default: "2"
display_dark_lux_threshold_default: "20"
display_bright_lux_threshold_default: "250"
@@ -921,20 +921,6 @@ number:
step: 5
mode: box
- platform: template
id: display_blackout_lux_threshold
name: "${friendly_name} Blackout Lux Threshold"
icon: mdi:brightness-1
entity_category: config
optimistic: true
restore_value: true
initial_value: "${display_blackout_lux_threshold_default}"
unit_of_measurement: "lx"
min_value: 0
max_value: 20
step: 0.5
mode: box
- platform: template
id: display_dark_lux_threshold
name: "${friendly_name} Dark Lux Threshold"
@@ -1241,6 +1227,36 @@ sensor:
}
return voltage * multiplier;
on_value:
then:
- if:
condition:
lambda: |-
return !isnan(x) && x >= 1.0f;
then:
- script.stop: latch_display_blackout
- lambda: |-
id(display_blackout_active) = false;
- if:
condition:
lambda: |-
return id(lvgl_ready) && id(display_is_dimmed);
then:
- script.execute: apply_dim_backlight
else:
- if:
condition:
lambda: |-
return id(lvgl_ready);
then:
- script.execute: apply_active_backlight
else:
- if:
condition:
lambda: |-
return !isnan(x) && x < 1.0f && !id(display_blackout_active);
then:
- script.execute: latch_display_blackout
- platform: homeassistant
id: moon_phase
@@ -1918,6 +1934,12 @@ globals:
restore_value: false
initial_value: "false"
# True after ambient light remains below 1 lx for 10 continuous seconds.
- id: display_blackout_active
type: bool
restore_value: false
initial_value: "false"
# True only while the Security page is visible.
- id: security_page_active
type: bool
@@ -3142,6 +3164,32 @@ script:
# The active and clock/idle brightness levels are independently adjustable
# and restored after reboot.
- id: latch_display_blackout
mode: single
then:
- delay: 10s
- if:
condition:
lambda: |-
const float lux = id(ambient_light_illuminance).state;
return !isnan(lux) && lux < 1.0f;
then:
- lambda: |-
id(display_blackout_active) = true;
- if:
condition:
lambda: |-
return id(lvgl_ready) && id(display_is_dimmed);
then:
- script.execute: apply_dim_backlight
else:
- if:
condition:
lambda: |-
return id(lvgl_ready);
then:
- script.execute: apply_active_backlight
# Keep the idle Clock unobtrusive at the minimum usable backlight level.
- id: apply_clock_blackout_palette
mode: restart
@@ -3149,15 +3197,7 @@ script:
- if:
condition:
lambda: |-
const float lux = id(ambient_light_illuminance).state;
float blackout_threshold =
id(display_blackout_lux_threshold).state;
if (isnan(blackout_threshold)) {
blackout_threshold = 2.0f;
}
return !isnan(lux) && lux < blackout_threshold;
return id(display_blackout_active);
then:
- lvgl.label.update:
@@ -3212,20 +3252,14 @@ script:
brightness: !lambda |-
const float lux = id(ambient_light_illuminance).state;
float blackout_threshold =
id(display_blackout_lux_threshold).state;
float dark_threshold =
id(display_dark_lux_threshold).state;
float bright_threshold =
id(display_bright_lux_threshold).state;
if (isnan(blackout_threshold)) {
blackout_threshold = 2.0f;
}
if (
isnan(dark_threshold) ||
dark_threshold <= blackout_threshold
dark_threshold <= 0.0f
) {
dark_threshold = 20.0f;
}
@@ -3239,7 +3273,7 @@ script:
float brightness_percent;
if (!isnan(lux) && lux < blackout_threshold) {
if (id(display_blackout_active)) {
brightness_percent =
id(display_blackout_brightness).state;
id(ambient_light_band_text).publish_state("Blackout");
@@ -3280,20 +3314,14 @@ script:
brightness: !lambda |-
const float lux = id(ambient_light_illuminance).state;
float blackout_threshold =
id(display_blackout_lux_threshold).state;
float dark_threshold =
id(display_dark_lux_threshold).state;
float bright_threshold =
id(display_bright_lux_threshold).state;
if (isnan(blackout_threshold)) {
blackout_threshold = 2.0f;
}
if (
isnan(dark_threshold) ||
dark_threshold <= blackout_threshold
dark_threshold <= 0.0f
) {
dark_threshold = 20.0f;
}
@@ -3307,7 +3335,7 @@ script:
float brightness_percent;
if (!isnan(lux) && lux < blackout_threshold) {
if (id(display_blackout_active)) {
brightness_percent =
id(display_blackout_brightness).state;
id(ambient_light_band_text).publish_state("Blackout");
+45 -14
View File
@@ -6,6 +6,8 @@
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-officeduallights.yaml
#:########################################################################################:#
# VERSIONS:
# V3.0 2026-09-12 Makes HA the primary Office synchronizer. Direct MQTT peer synchronization
# runs only while HA's native API state subscription is disconnected.
# V2.9 2026-08-16 Makes this physical relay the sole overhead status authority. Removes the
# bidirectional shared retained-status topic that could chatter during reconnects.
# V2.8 2026-07-25 Added 2-way MQTT sync with esp-officelights (office main lightswitch).
@@ -93,7 +95,7 @@ substitutions:
# Project Naming
project_name: "Sonoff Technologies.Sonoff Dual R1"
project_version: "v2.9"
project_version: "v3.0"
# Passwords and Secrets
api_key: !secret esp-api_key
@@ -239,13 +241,21 @@ mqtt:
payload: "On"
then:
- script.stop: relay_1_timer_script
- light.turn_on: light_1
- if:
condition:
lambda: "return !id(relay_1).state;"
then:
- light.turn_on: light_1
- topic: "${mqtt_local_command1_topic}"
payload: "Off"
then:
- script.stop: relay_1_timer_script
- light.turn_off: light_1
- if:
condition:
lambda: "return id(relay_1).state;"
then:
- light.turn_off: light_1
- topic: "${mqtt_local_command1_topic}"
then:
@@ -277,13 +287,21 @@ mqtt:
payload: "On"
then:
- script.stop: relay_2_timer_script
- light.turn_on: light_2
- if:
condition:
lambda: "return !id(relay_2).state;"
then:
- light.turn_on: light_2
- topic: "${mqtt_local_command2_topic}"
payload: "Off"
then:
- script.stop: relay_2_timer_script
- light.turn_off: light_2
- if:
condition:
lambda: "return id(relay_2).state;"
then:
- light.turn_off: light_2
- topic: "${mqtt_local_command2_topic}"
then:
@@ -311,16 +329,19 @@ mqtt:
id(relay_2_timer_script)->execute(timer_seconds * 1000);
# Mirror remote bunker command from esp-officelights onto light_1, but only if a real
# state change is needed. Do not republish the command from here. Relay status is
# published by the relay callback below.
# MQTT peer synchronization is the HA-down fallback. When HA is connected it is the
# sole synchronizer, so remote MQTT commands must not change either physical relay.
- topic: "${mqtt_mainlight_command_bunker}"
payload: "${mqtt_command_on}"
then:
- script.stop: relay_1_timer_script
- if:
condition:
lambda: "return !id(relay_1).state;"
and:
- not:
api.connected:
state_subscription_only: true
- lambda: "return !id(relay_1).state;"
then:
- light.turn_on: light_1
@@ -330,19 +351,25 @@ mqtt:
- script.stop: relay_1_timer_script
- if:
condition:
lambda: "return id(relay_1).state;"
and:
- not:
api.connected:
state_subscription_only: true
- lambda: "return id(relay_1).state;"
then:
- light.turn_off: light_1
# Mirror remote overhead command from esp-officelights onto light_2, but only if a real
# state change is needed.
- topic: "${mqtt_mainlight_command_overhead}"
payload: "${mqtt_command_on}"
then:
- script.stop: relay_2_timer_script
- if:
condition:
lambda: "return !id(relay_2).state;"
and:
- not:
api.connected:
state_subscription_only: true
- lambda: "return !id(relay_2).state;"
then:
- light.turn_on: light_2
@@ -352,7 +379,11 @@ mqtt:
- script.stop: relay_2_timer_script
- if:
condition:
lambda: "return id(relay_2).state;"
and:
- not:
api.connected:
state_subscription_only: true
- lambda: "return id(relay_2).state;"
then:
- light.turn_off: light_2
+75 -26
View File
@@ -6,6 +6,8 @@
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-officelights.yaml
#:########################################################################################:#
# VERSIONS:
# v3.2 2026-09-12 Makes HA the primary Office synchronizer. Direct MQTT peer synchronization
# runs only while HA's native API state subscription is disconnected.
# v3.1 2026-08-16 Makes the Sonoff Dual the sole overhead status authority. Removes the
# bidirectional shared retained-status topic that could chatter during reconnects.
# v3.0 2026-07-25 Refactored MQTT sync to use template HA switch actions; physical button and
@@ -81,7 +83,7 @@ substitutions:
# Project Naming
project_name: "Zemismart Technologies.KS-811 Triple"
project_version: "v3.1"
project_version: "v3.2"
# Passwords & Secrets
api_key: !secret esp-api_key
@@ -215,15 +217,18 @@ mqtt:
retain: true
on_message:
# Mirror remote overhead command onto virtual Relay_2, but only if a real state change
# is needed. Do not republish the command from here. Relay status is published by the
# relay callback below.
# MQTT peer synchronization is the HA-down fallback. When HA is connected it is the
# sole synchronizer, so remote MQTT state must not change this mirror.
- topic: "${mqtt_this_device_command_topic_overhead}"
payload: "${mqtt_command_on}"
then:
- if:
condition:
lambda: "return !id(Relay_2).state;"
and:
- not:
api.connected:
state_subscription_only: true
- lambda: "return !id(Relay_2).state;"
then:
- switch.turn_on: Relay_2
@@ -232,7 +237,11 @@ mqtt:
then:
- if:
condition:
lambda: "return id(Relay_2).state;"
and:
- not:
api.connected:
state_subscription_only: true
- lambda: "return id(Relay_2).state;"
then:
- switch.turn_off: Relay_2
@@ -244,7 +253,11 @@ mqtt:
then:
- if:
condition:
lambda: "return !id(Relay_2).state;"
and:
- not:
api.connected:
state_subscription_only: true
- lambda: "return !id(Relay_2).state;"
then:
- switch.turn_on: Relay_2
@@ -253,7 +266,11 @@ mqtt:
then:
- if:
condition:
lambda: "return id(Relay_2).state;"
and:
- not:
api.connected:
state_subscription_only: true
- lambda: "return id(Relay_2).state;"
then:
- switch.turn_off: Relay_2
@@ -265,7 +282,11 @@ mqtt:
then:
- if:
condition:
lambda: "return !id(Relay_1).state;"
and:
- not:
api.connected:
state_subscription_only: true
- lambda: "return !id(Relay_1).state;"
then:
- switch.turn_on: Relay_1
@@ -274,7 +295,11 @@ mqtt:
then:
- if:
condition:
lambda: "return id(Relay_1).state;"
and:
- not:
api.connected:
state_subscription_only: true
- lambda: "return id(Relay_1).state;"
then:
- switch.turn_off: Relay_1
@@ -341,10 +366,16 @@ script:
lambda: "return !id(Relay_1).state;"
then:
- switch.turn_on: Relay_1
- mqtt.publish:
topic: "${mqtt_this_device_command_topic_bunker}"
payload: "${mqtt_command_on}"
retain: false
- if:
condition:
not:
api.connected:
state_subscription_only: true
then:
- mqtt.publish:
topic: "${mqtt_this_device_command_topic_bunker}"
payload: "${mqtt_command_on}"
retain: false
# Local/HA requested OFF for Bunker Lights.
- id: office_bunker_off
@@ -355,10 +386,16 @@ script:
lambda: "return id(Relay_1).state;"
then:
- switch.turn_off: Relay_1
- mqtt.publish:
topic: "${mqtt_this_device_command_topic_bunker}"
payload: "${mqtt_command_off}"
retain: false
- if:
condition:
not:
api.connected:
state_subscription_only: true
then:
- mqtt.publish:
topic: "${mqtt_this_device_command_topic_bunker}"
payload: "${mqtt_command_off}"
retain: false
# Physical Button 1 toggle logic for Bunker Lights. This is the only place that decides
# ON vs OFF for the bunker.
@@ -383,10 +420,16 @@ script:
lambda: "return !id(Relay_2).state;"
then:
- switch.turn_on: Relay_2
- mqtt.publish:
topic: "${mqtt_this_device_command_topic_overhead}"
payload: "${mqtt_command_on}"
retain: false
- if:
condition:
not:
api.connected:
state_subscription_only: true
then:
- mqtt.publish:
topic: "${mqtt_this_device_command_topic_overhead}"
payload: "${mqtt_command_on}"
retain: false
# Local/HA requested OFF for Overhead Lights.
- id: office_overhead_off
@@ -397,10 +440,16 @@ script:
lambda: "return id(Relay_2).state;"
then:
- switch.turn_off: Relay_2
- mqtt.publish:
topic: "${mqtt_this_device_command_topic_overhead}"
payload: "${mqtt_command_off}"
retain: false
- if:
condition:
not:
api.connected:
state_subscription_only: true
then:
- mqtt.publish:
topic: "${mqtt_this_device_command_topic_overhead}"
payload: "${mqtt_command_off}"
retain: false
# Physical Button 2 toggle logic for Overhead Lights.
- id: office_overhead_toggle
+223
View File
@@ -0,0 +1,223 @@
--- a/esp-bedside-panel.yaml
+++ b/esp-bedside-panel.yaml
@@ -5,6 +5,7 @@
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-bedside-panel.yaml
#:########################################################################################:#
# VERSIONS:
+# V1.56 2026-09-04 Added latched blackout after 10 seconds below 1 lx with immediate exit
# V1.55 2026-08-16 Show only Clock time and date in Blackout ambient-light mode
# V1.54 2026-08-16 Dimmed Clock time and date palette in Blackout ambient-light mode
@@ -248,7 +249,6 @@
# Bright light: approximately 642-643 lx
ambient_lux_multiplier_default: "200"
-
- display_blackout_lux_threshold_default: "2"
display_dark_lux_threshold_default: "20"
display_bright_lux_threshold_default: "250"
@@ -921,21 +921,6 @@
step: 5
mode: box
- - platform: template
- id: display_blackout_lux_threshold
- name: "${friendly_name} Blackout Lux Threshold"
- icon: mdi:brightness-1
- entity_category: config
- optimistic: true
- restore_value: true
- initial_value: "${display_blackout_lux_threshold_default}"
- unit_of_measurement: "lx"
- min_value: 0
- max_value: 20
- step: 0.5
- mode: box
-
- platform: template
id: display_dark_lux_threshold
@@ -1228,6 +1213,45 @@
update_interval: 3s
lambda: |-
const float voltage = id(ambient_light_voltage).state;
+
+ if (isnan(voltage)) {
+ return NAN;
+ }
+
+ float multiplier = id(ambient_lux_multiplier).state;
+
+ if (isnan(multiplier) || multiplier <= 0.0f) {
+ multiplier = 200.0f;
+ }
+
+ return voltage * multiplier;
+ on_value:
+ then:
+ - if:
+ condition:
+ lambda: |-
+ return !isnan(x) && x >= 1.0f;
+ then:
+ - script.stop: latch_display_blackout
+ - lambda: |-
+ id(display_blackout_active) = false;
+ - if:
+ condition:
+ lambda: |-
+ return id(lvgl_ready) && id(display_is_dimmed);
+ then:
+ - script.execute: apply_dim_backlight
+ else:
+ - if:
+ condition:
+ lambda: |-
+ return id(lvgl_ready);
+ then:
+ - script.execute: apply_active_backlight
+ else:
+ - if:
+ condition:
+ lambda: |-
+ return !isnan(x) && x < 1.0f && !id(display_blackout_active);
+ then:
+ - script.execute: latch_display_blackout
- float multiplier = id(ambient_lux_multiplier).state;
-
- if (isnan(voltage)) {
- return NAN;
- }
-
- if (isnan(multiplier) || multiplier <= 0.0f) {
- multiplier = 200.0f;
- }
-
- return voltage * multiplier;
- platform: homeassistant
@@ -1918,6 +1942,12 @@
restore_value: false
initial_value: "false"
+ # True after ambient light remains below 1 lx for 10 continuous seconds.
+ - id: display_blackout_active
+ type: bool
+ restore_value: false
+ initial_value: "false"
+
# True only while the Security page is visible.
@@ -3133,31 +3163,52 @@
- # Four ambient ranges are used:
- # Blackout: lux < Blackout Lux Threshold
- # Dark: Blackout threshold to Dark Lux Threshold
- # Normal: between Dark and Bright thresholds
- # Bright: lux >= Bright Lux Threshold
+ # Blackout latches after ambient light remains below 1 lx for 10 seconds and
+ # exits immediately when ambient light reaches 1 lx. The remaining ranges are:
+ # Dark: lux <= Dark Lux Threshold
+ # Normal: between Dark and Bright thresholds
+ # Bright: lux >= Bright Lux Threshold
#
# The active and clock/idle brightness levels are independently adjustable
# and restored after reboot.
+ - id: latch_display_blackout
+ mode: single
+ then:
+ - delay: 10s
+ - if:
+ condition:
+ lambda: |-
+ const float lux = id(ambient_light_illuminance).state;
+ return !isnan(lux) && lux < 1.0f;
+ then:
+ - lambda: |-
+ id(display_blackout_active) = true;
+ - if:
+ condition:
+ lambda: |-
+ return id(lvgl_ready) && id(display_is_dimmed);
+ then:
+ - script.execute: apply_dim_backlight
+ else:
+ - if:
+ condition:
+ lambda: |-
+ return id(lvgl_ready);
+ then:
+ - script.execute: apply_active_backlight
+
# Keep the idle Clock unobtrusive at the minimum usable backlight level.
- id: apply_clock_blackout_palette
@@ -3146,15 +3197,7 @@
- if:
condition:
lambda: |-
- const float lux = id(ambient_light_illuminance).state;
- float blackout_threshold =
- id(display_blackout_lux_threshold).state;
-
- if (isnan(blackout_threshold)) {
- blackout_threshold = 2.0f;
- }
-
- return !isnan(lux) && lux < blackout_threshold;
+ return id(display_blackout_active);
@@ -3212,22 +3255,12 @@
brightness: !lambda |-
const float lux = id(ambient_light_illuminance).state;
- float blackout_threshold =
- id(display_blackout_lux_threshold).state;
float dark_threshold =
id(display_dark_lux_threshold).state;
float bright_threshold =
id(display_bright_lux_threshold).state;
- if (isnan(blackout_threshold)) {
- blackout_threshold = 2.0f;
- }
-
if (
isnan(dark_threshold) ||
- dark_threshold <= blackout_threshold
+ dark_threshold <= 0.0f
) {
dark_threshold = 20.0f;
}
@@ -3239,7 +3272,7 @@
float brightness_percent;
- if (!isnan(lux) && lux < blackout_threshold) {
+ if (id(display_blackout_active)) {
brightness_percent =
id(display_blackout_brightness).state;
@@ -3280,22 +3313,12 @@
brightness: !lambda |-
const float lux = id(ambient_light_illuminance).state;
- float blackout_threshold =
- id(display_blackout_lux_threshold).state;
float dark_threshold =
id(display_dark_lux_threshold).state;
float bright_threshold =
id(display_bright_lux_threshold).state;
- if (isnan(blackout_threshold)) {
- blackout_threshold = 2.0f;
- }
-
if (
isnan(dark_threshold) ||
- dark_threshold <= blackout_threshold
+ dark_threshold <= 0.0f
) {
dark_threshold = 20.0f;
}
@@ -3307,7 +3330,7 @@
float brightness_percent;
- if (!isnan(lux) && lux < blackout_threshold) {
+ if (id(display_blackout_active)) {
brightness_percent =
id(display_blackout_brightness).state;
@@ -1,26 +1,28 @@
#:########################################################################################:#
# PACKAGE: Office Lights Home Assistant Fallback Synchronization
# FILE: Lighting_2Way_Sync/office_lights_ha_fallback_sync.yaml
# VERSION: v1.0.0
# DATE: 2026-08-16
# VERSION: v1.2.0
# DATE: 2026-09-12
#:########################################################################################:#
#
# PURPOSE:
# Provides the Home Assistant API transport for Office two-way light control
# when MQTT is unavailable. Direct MQTT remains the HA-down transport.
# Provides Home Assistant-primary synchronization for the Office two-way
# light controls. Direct MQTT peer control is the HA-down transport.
#
# OPERATION:
# - Synchronizes the Office bunker and overhead control pairs in both directions.
# - Uses idempotent Home Assistant actions, so MQTT and HA may both be online
# without creating a control loop.
# - ESPHome permits direct MQTT peer synchronization only while HA's native
# API state subscription is disconnected, preventing competing controllers.
# - Does not reconcile state during Home Assistant startup, preventing a stale
# restored entity state from changing a physical light.
#
# DEPENDENCIES:
# - Blueprint: zorruno/two_three_way_sync.yaml
# - esp-officelights and esp-officeduallights native ESPHome API connections.
# - esp-officelights v3.2 and esp-officeduallights v3.0 or later.
#
# VERSION HISTORY:
# - v1.2.0 (2026-09-12): Enable after guarded HA-primary firmware deployment.
# - v1.1.0 (2026-09-12): Disabled until guarded HA-primary firmware deployment.
# - v1.0.0 (2026-08-16): Initial MQTT-down HA fallback for Office lights.
#:########################################################################################:#
@@ -0,0 +1,57 @@
#:########################################################################################:#
# PACKAGE: Office Lights Home Assistant Fallback Synchronization
# FILE: Lighting_2Way_Sync/office_lights_ha_fallback_sync.yaml
# VERSION: v1.1.0
# DATE: 2026-09-12
#:########################################################################################:#
#
# PURPOSE:
# Provides the Home Assistant API transport for Office two-way light control
# when MQTT is unavailable. Direct MQTT remains the HA-down transport.
#
# OPERATION:
# - Synchronizes the Office bunker and overhead control pairs in both directions.
# - The automations remain disabled until both ESPHome devices enforce the
# HA-primary/MQTT-fallback authority boundary.
# - Does not reconcile state during Home Assistant startup, preventing a stale
# restored entity state from changing a physical light.
#
# DEPENDENCIES:
# - Blueprint: zorruno/two_three_way_sync.yaml
# - esp-officelights and esp-officeduallights native ESPHome API connections.
#
# VERSION HISTORY:
# - v1.1.0 (2026-09-12): Disable until guarded HA-primary firmware is deployed.
# - v1.0.0 (2026-08-16): Initial MQTT-down HA fallback for Office lights.
#:########################################################################################:#
automation:
#:##########################################################################:#
# BUNKER LIGHTS
#:##########################################################################:#
- id: office_bunker_lights_ha_fallback_sync
alias: "Office Bunker Lights HA Fallback Sync"
initial_state: false
use_blueprint:
path: zorruno/two_three_way_sync.yaml
input:
members:
- switch.office_office_main_lightswitch_3_relay_1_bunker_lights
- light.esp_officeduallights_light_1_right_hand_bunker_light
sync_on_start: false
startup_delay: 5
#:##########################################################################:#
# OVERHEAD LIGHTS
#:##########################################################################:#
- id: office_overhead_lights_ha_fallback_sync
alias: "Office Overhead Lights HA Fallback Sync"
initial_state: false
use_blueprint:
path: zorruno/two_three_way_sync.yaml
input:
members:
- switch.office_office_main_lightswitch_3_relay_2_overhead_lights
- light.esp_officeduallights_light_2_cool_white_overhead_lights
sync_on_start: false
startup_delay: 5
@@ -9,9 +9,13 @@
# packages/Pool_Controls/pool_light_operation.yaml
#
# VERSION:
# V1.2 2026-07-20
# V1.3 2026-09-06
#
# VERSION HISTORY:
# V1.3 2026-09-06
# - Treat the colour selector's "0= OFF" as a true Operation Mode OFF and
# arm the midnight TIMER reset.
#
# V1.2 2026-07-20
# - Corrected the documented package filename.
# - Documented the relationship with the native ESPHome controls.
@@ -151,6 +155,40 @@ automation:
target:
entity_id: input_boolean.timer_light_midnight_pending
- id: pool_light_schedule_timer_when_operation_mode_off
alias: "Pool Light - Schedule TIMER When Operation Mode Is OFF"
description: "Arms the midnight TIMER reset for direct Operation Mode OFF commands."
mode: single
trigger:
- platform: state
entity_id: select.esp_poollightpower2_operation_mode
to: "OFF"
action:
- action: input_boolean.turn_on
target:
entity_id: input_boolean.timer_light_midnight_pending
- id: pool_light_send_off_when_colour_off_selected
alias: "Pool Light - Send OFF When Colour Off Is Selected"
description: "Converts colour selector 0= OFF into Operation Mode OFF with midnight reset."
mode: restart
trigger:
- platform: state
entity_id: select.esp_poollightpower2_pool_light_mode
to: "0= OFF"
action:
- action: mqtt.publish
data:
topic: "viewroad-commands/poollight-timer/operation"
payload: "OFF"
- action: input_boolean.turn_on
target:
entity_id: input_boolean.timer_light_midnight_pending
- id: pool_light_sync_ui_switch_with_real_output
alias: "Pool Light - Sync UI Switch With Real Output"
description: "Keeps the UI switch aligned with the real ESPHome pool light output without sending MQTT commands."
+24
View File
@@ -0,0 +1,24 @@
# Package: BYD Lock Button
# Purpose:
# Provides a voice-assistant-safe BYD lock control that can only issue a lock command.
# Notes:
# The icon reflects the live state of the underlying BYD lock. This button has no
# unlock action.
# Version history:
# - 1.0.0: Initial lock-only BYD button.
#
#:########################################################################################:#
# Template Button
#:########################################################################################:#
template:
- button:
- name: "BYD Lock"
unique_id: byd_lock_button
default_entity_id: button.byd_lock
availability: "{{ has_value('lock.atto_3_lock') }}"
icon: >-
{{ 'mdi:lock' if is_state('lock.atto_3_lock', 'locked') else 'mdi:lock-open' }}
press:
- action: lock.lock
target:
entity_id: lock.atto_3_lock
+6 -4
View File
@@ -2,8 +2,8 @@
# PACKAGE: Energy Cost Monitoring
################################################################################
#
# Version: 1.3.0
# Date: 2026-08-23
# Version: 1.3.1
# Date: 2026-09-01
#
# Purpose:
# Track GST-inclusive electricity costs for the three-phase grid supply and
@@ -18,6 +18,8 @@
# boundary without averaging the old and new rates.
#
# Version history:
# 1.3.1 (2026-09-01): Move Downstairs Fridge and Freezer shared power-cost
# telemetry from X41PP to X68PM.
# 1.3.0 (2026-08-24): Add Drinks Fridge and Vertical Freezer energy and
# cost monitoring.
# 1.2.0 (2026-08-23): Add Downstairs Lounge North GPO (X48PM) energy and
@@ -326,11 +328,11 @@ template:
unique_id: energy_cost_downstairs_fridge_rate
unit_of_measurement: "NZD/h"
state_class: measurement
availability: "{{ has_value('sensor.downstairs_fridge_power_x41pp_power') }}"
availability: "{{ has_value('sensor.downstairs_fridge_gpo_power_x68pm_power') }}"
state: >-
{{
(
states('sensor.downstairs_fridge_power_x41pp_power') | float(0)
states('sensor.downstairs_fridge_gpo_power_x68pm_power') | float(0)
/ 1000
* states('sensor.electricity_current_price') | float(0)
) | round(6)
+55 -49
View File
@@ -1,7 +1,13 @@
#:########################################################################################:#
# PACKAGE: Fridge and Freezer Monitoring
# FILE: packages/fridge_freezer_monitoring.yaml
# VERSION: v1.11.0 2026-08-30
# VERSION: v1.13.0 2026-09-03
# - Moves Downstairs Freezer temperature, humidity, and battery monitoring
# from X62TS to X70TS.
# - Moves shared Downstairs Fridge and Freezer power monitoring from X41PP to
# the metering-only X68PM GPO.
# - Reports an unavailable Main Kitchen Fridge battery monitor explicitly,
# rather than showing an unqualified issue.
# - Moves Main Kitchen Fridge temperature, humidity, and battery monitoring to
# X65TS and adds X64LS illuminance-based door monitoring.
# - v1.10.0 2026-08-29: Adds Downstairs Freezer temperature, battery, door, humidity, and shared
@@ -61,8 +67,7 @@
# - Seven configured fridge/freezer temperature and battery monitor pairs.
# - switch.upstairs_fridge_power_x31pp
# - sensor.upstairs_fridge_power_x31pp_power
# - switch.downstairs_fridge_power_x41pp
# - sensor.downstairs_fridge_power_x41pp_power
# - sensor.downstairs_fridge_gpo_power_x68pm_power
# - switch.drinks_fridge_power_x59pm
# - sensor.drinks_fridge_power_x59pm_power
# - switch.vertical_freezer_power_x60pm
@@ -173,7 +178,7 @@ sensor:
- platform: statistics
name: "Downstairs Freezer Temperature Average 7 Days"
unique_id: downstairs_freezer_temperature_average_7_days
entity_id: sensor.downstairs_freezer_temperature_x62ts_temperature
entity_id: sensor.downstairs_freezer_temperature_x70ts_temperature
state_characteristic: average_step
max_age:
days: 7
@@ -182,7 +187,7 @@ sensor:
- platform: statistics
name: "Downstairs Freezer Temperature 95th Percentile 7 Days"
unique_id: downstairs_freezer_temperature_95th_percentile_7_days
entity_id: sensor.downstairs_freezer_temperature_x62ts_temperature
entity_id: sensor.downstairs_freezer_temperature_x70ts_temperature
state_characteristic: percentile
percentile: 95
max_age:
@@ -192,7 +197,7 @@ sensor:
- platform: derivative
name: "Downstairs Freezer Temperature Change Rate"
unique_id: downstairs_freezer_temperature_change_rate
source: sensor.downstairs_freezer_temperature_x62ts_temperature
source: sensor.downstairs_freezer_temperature_x70ts_temperature
unit_time: h
time_window: "00:30:00"
max_sub_interval: "00:05:00"
@@ -414,11 +419,10 @@ template:
unique_id: downstairs_fridge_power_draw_lost
device_class: problem
state: >-
{% set power = states('sensor.downstairs_fridge_power_x41pp_power') | float(none) %}
{% set changed = states.sensor.downstairs_fridge_power_x41pp_power.last_changed %}
{% set power = states('sensor.downstairs_fridge_gpo_power_x68pm_power') | float(none) %}
{% set changed = states.sensor.downstairs_fridge_gpo_power_x68pm_power.last_changed %}
{{
is_state('switch.downstairs_fridge_power_x41pp', 'on')
and power is not none
power is not none
and power < 1
and (now() - changed).total_seconds() >= 4 * 60 * 60
}}
@@ -450,7 +454,11 @@ template:
{{
temperature_missing
or illuminance_missing
or (states('sensor.main_kitchen_fridge_temperature_x65ts_battery') | float(-1)) <= 20
or battery_missing
or (
not battery_missing
and (states('sensor.main_kitchen_fridge_temperature_x65ts_battery') | float(0)) <= 20
)
or is_state('binary_sensor.main_kitchen_fridge_door_open_too_long', 'on')
or (
not temperature_missing
@@ -477,7 +485,9 @@ template:
{% if illuminance_missing %}
{% set ns.items = ns.items + ['Door monitor unavailable'] %}
{% endif %}
{% if not battery_missing and (states('sensor.main_kitchen_fridge_temperature_x65ts_battery') | float(-1)) <= 20 %}
{% if battery_missing %}
{% set ns.items = ns.items + ['Battery monitor unavailable'] %}
{% elif (states('sensor.main_kitchen_fridge_temperature_x65ts_battery') | float(0)) <= 20 %}
{% set ns.items = ns.items + ['Low monitor battery'] %}
{% endif %}
{% if states('switch.upstairs_fridge_power_x31pp') in ['unknown', 'unavailable'] or power_missing %}
@@ -596,7 +606,6 @@ template:
{% set threshold = [9, percentile + 0.5] | min if percentile is not none else 9 %}
{{
temperature_missing
or battery_missing
or (states('sensor.lcd_temp_humidity_btt04_battery') | float(-1)) <= 20
or (not temperature_missing and (states('sensor.lcd_temp_humidity_btt04_temperature') | float(0)) > threshold)
}}
@@ -608,8 +617,8 @@ template:
{% set battery_missing = not has_value('sensor.lcd_temp_humidity_btt04_battery') %}
{% set percentile = states('sensor.wine_fridge_temperature_95th_percentile_7_days') | float(none) %}
{% set threshold = [9, percentile + 0.5] | min if percentile is not none else 9 %}
{% if temperature_missing or battery_missing %}
{% set ns.items = ns.items + ['Monitor offline'] %}
{% if temperature_missing %}
{% set ns.items = ns.items + ['Temperature monitor unavailable'] %}
{% endif %}
{% if not battery_missing and (states('sensor.lcd_temp_humidity_btt04_battery') | float(-1)) <= 20 %}
{% set ns.items = ns.items + ['Low monitor battery'] %}
@@ -638,7 +647,7 @@ template:
{% set temperature_missing = not has_value('sensor.downstairs_fridge_temperature_x43ts_temperature') %}
{% set battery_missing = not has_value('sensor.downstairs_fridge_temperature_x43ts_battery') %}
{% set illuminance_missing = not has_value('sensor.downstairs_fridge_light_sensor_x58ls_illuminance') %}
{% set power_missing = not has_value('sensor.downstairs_fridge_power_x41pp_power') %}
{% set power_missing = not has_value('sensor.downstairs_fridge_gpo_power_x68pm_power') %}
{% set percentile = states('sensor.downstairs_fridge_temperature_95th_percentile_7_days') | float(none) %}
{% set threshold = [10, percentile + 0.5] | min if percentile is not none else 10 %}
{{
@@ -651,7 +660,6 @@ template:
and not is_state('binary_sensor.downstairs_fridge_temperature_recovery_grace', 'on')
and (states('sensor.downstairs_fridge_temperature_x43ts_temperature') | float(0)) > threshold
)
or states('switch.downstairs_fridge_power_x41pp') != 'on'
or power_missing
or is_state('binary_sensor.downstairs_fridge_power_draw_lost', 'on')
}}
@@ -662,7 +670,7 @@ template:
{% set temperature_missing = not has_value('sensor.downstairs_fridge_temperature_x43ts_temperature') %}
{% set battery_missing = not has_value('sensor.downstairs_fridge_temperature_x43ts_battery') %}
{% set illuminance_missing = not has_value('sensor.downstairs_fridge_light_sensor_x58ls_illuminance') %}
{% set power_missing = not has_value('sensor.downstairs_fridge_power_x41pp_power') %}
{% set power_missing = not has_value('sensor.downstairs_fridge_gpo_power_x68pm_power') %}
{% set percentile = states('sensor.downstairs_fridge_temperature_95th_percentile_7_days') | float(none) %}
{% set threshold = [10, percentile + 0.5] | min if percentile is not none else 10 %}
{% if temperature_missing %}
@@ -674,10 +682,8 @@ template:
{% if not battery_missing and (states('sensor.downstairs_fridge_temperature_x43ts_battery') | float(-1)) <= 20 %}
{% set ns.items = ns.items + ['Low monitor battery'] %}
{% endif %}
{% if states('switch.downstairs_fridge_power_x41pp') in ['unknown', 'unavailable'] or power_missing %}
{% if power_missing %}
{% set ns.items = ns.items + ['Power monitoring unavailable'] %}
{% elif not is_state('switch.downstairs_fridge_power_x41pp', 'on') %}
{% set ns.items = ns.items + ['Power switched off'] %}
{% elif is_state('binary_sensor.downstairs_fridge_power_draw_lost', 'on') %}
{% set ns.items = ns.items + ['Power lost - no power draw for 4 hours'] %}
{% endif %}
@@ -701,7 +707,7 @@ template:
{{ [10, percentile + 0.5] | min if percentile is not none else 10 }}
change_rate: "{{ states('sensor.downstairs_fridge_temperature_change_rate') }}"
battery: "{{ states('sensor.downstairs_fridge_temperature_x43ts_battery') }}"
power: "{{ states('sensor.downstairs_fridge_power_x41pp_power') }}"
power: "{{ states('sensor.downstairs_fridge_gpo_power_x68pm_power') }}"
door_open: "{{ is_state('binary_sensor.downstairs_fridge_door_open', 'on') }}"
illuminance: "{{ states('sensor.downstairs_fridge_light_sensor_x58ls_illuminance') }}"
@@ -710,22 +716,22 @@ template:
device_class: problem
delay_on: "00:01:00"
state: >-
{% set temperature_missing = not has_value('sensor.downstairs_freezer_temperature_x62ts_temperature') %}
{% set battery_missing = not has_value('sensor.downstairs_freezer_temperature_x62ts_battery') %}
{% set power_missing = not has_value('sensor.downstairs_fridge_power_x41pp_power') %}
{% set temperature_missing = not has_value('sensor.downstairs_freezer_temperature_x70ts_temperature') %}
{% set battery_missing = not has_value('sensor.downstairs_freezer_temperature_x70ts_battery') %}
{% set illuminance_missing = not has_value('sensor.downstairs_freezer_light_sensor_x61ls_illuminance') %}
{% set power_missing = not has_value('sensor.downstairs_fridge_gpo_power_x68pm_power') %}
{% set percentile = states('sensor.downstairs_freezer_temperature_95th_percentile_7_days') | float(none) %}
{% set threshold = [-5, percentile + 0.5] | min if percentile is not none else -5 %}
{{
temperature_missing
or battery_missing
or (states('sensor.downstairs_freezer_temperature_x62ts_battery') | float(-1)) <= 20
or illuminance_missing
or (states('sensor.downstairs_freezer_temperature_x70ts_battery') | float(-1)) <= 20
or is_state('binary_sensor.downstairs_freezer_door_open_too_long', 'on')
or (
not temperature_missing
and not is_state('binary_sensor.downstairs_freezer_temperature_recovery_grace', 'on')
and (states('sensor.downstairs_freezer_temperature_x62ts_temperature') | float(0)) > threshold
and (states('sensor.downstairs_freezer_temperature_x70ts_temperature') | float(0)) > threshold
)
or states('switch.downstairs_fridge_power_x41pp') != 'on'
or power_missing
or is_state('binary_sensor.downstairs_fridge_power_draw_lost', 'on')
}}
@@ -733,21 +739,23 @@ template:
appliance: "Downstairs Freezer"
issues: >-
{% set ns = namespace(items=[]) %}
{% set temperature_missing = not has_value('sensor.downstairs_freezer_temperature_x62ts_temperature') %}
{% set battery_missing = not has_value('sensor.downstairs_freezer_temperature_x62ts_battery') %}
{% set power_missing = not has_value('sensor.downstairs_fridge_power_x41pp_power') %}
{% set temperature_missing = not has_value('sensor.downstairs_freezer_temperature_x70ts_temperature') %}
{% set battery_missing = not has_value('sensor.downstairs_freezer_temperature_x70ts_battery') %}
{% set illuminance_missing = not has_value('sensor.downstairs_freezer_light_sensor_x61ls_illuminance') %}
{% set power_missing = not has_value('sensor.downstairs_fridge_gpo_power_x68pm_power') %}
{% set percentile = states('sensor.downstairs_freezer_temperature_95th_percentile_7_days') | float(none) %}
{% set threshold = [-5, percentile + 0.5] | min if percentile is not none else -5 %}
{% if temperature_missing or battery_missing %}
{% set ns.items = ns.items + ['Monitor offline'] %}
{% if temperature_missing %}
{% set ns.items = ns.items + ['Temperature monitor unavailable'] %}
{% endif %}
{% if not battery_missing and (states('sensor.downstairs_freezer_temperature_x62ts_battery') | float(-1)) <= 20 %}
{% if illuminance_missing %}
{% set ns.items = ns.items + ['Door monitor unavailable'] %}
{% endif %}
{% if not battery_missing and (states('sensor.downstairs_freezer_temperature_x70ts_battery') | float(-1)) <= 20 %}
{% set ns.items = ns.items + ['Low monitor battery'] %}
{% endif %}
{% if states('switch.downstairs_fridge_power_x41pp') in ['unknown', 'unavailable'] or power_missing %}
{% if power_missing %}
{% set ns.items = ns.items + ['Shared power monitoring unavailable'] %}
{% elif not is_state('switch.downstairs_fridge_power_x41pp', 'on') %}
{% set ns.items = ns.items + ['Shared power switched off'] %}
{% elif is_state('binary_sensor.downstairs_fridge_power_draw_lost', 'on') %}
{% set ns.items = ns.items + ['Shared power lost - no power draw for 4 hours'] %}
{% endif %}
@@ -755,7 +763,7 @@ template:
{% set ns.items = ns.items + ['Door open too long'] %}
{% elif not temperature_missing and is_state('binary_sensor.downstairs_freezer_temperature_recovery_grace', 'on') %}
{% set ns.items = ns.items + ['Temperature alert delayed - door recently open'] %}
{% elif not temperature_missing and (states('sensor.downstairs_freezer_temperature_x62ts_temperature') | float(0)) > threshold %}
{% elif not temperature_missing and (states('sensor.downstairs_freezer_temperature_x70ts_temperature') | float(0)) > threshold %}
{% if (states('sensor.downstairs_freezer_temperature_change_rate') | float(0)) >= 2 %}
{% set ns.items = ns.items + ['Warming rapidly - possible open door'] %}
{% else %}
@@ -763,15 +771,15 @@ template:
{% endif %}
{% endif %}
{{ ns.items }}
temperature: "{{ states('sensor.downstairs_freezer_temperature_x62ts_temperature') }}"
humidity: "{{ states('sensor.downstairs_freezer_temperature_x62ts_humidity') }}"
temperature: "{{ states('sensor.downstairs_freezer_temperature_x70ts_temperature') }}"
humidity: "{{ states('sensor.downstairs_freezer_temperature_x70ts_humidity') }}"
average_7_days: "{{ states('sensor.downstairs_freezer_temperature_average_7_days') }}"
alert_threshold: >-
{% set percentile = states('sensor.downstairs_freezer_temperature_95th_percentile_7_days') | float(none) %}
{{ [-5, percentile + 0.5] | min if percentile is not none else -5 }}
change_rate: "{{ states('sensor.downstairs_freezer_temperature_change_rate') }}"
battery: "{{ states('sensor.downstairs_freezer_temperature_x62ts_battery') }}"
power: "{{ states('sensor.downstairs_fridge_power_x41pp_power') }}"
battery: "{{ states('sensor.downstairs_freezer_temperature_x70ts_battery') }}"
power: "{{ states('sensor.downstairs_fridge_gpo_power_x68pm_power') }}"
door_open: "{{ is_state('binary_sensor.downstairs_freezer_door_open', 'on') }}"
illuminance: "{{ states('sensor.downstairs_freezer_light_sensor_x61ls_illuminance') }}"
@@ -787,7 +795,6 @@ template:
{% set threshold = [-5, percentile + 0.5] | min if percentile is not none else -5 %}
{{
temperature_missing
or battery_missing
or (states('sensor.drinks_freezer_environment_lcd_btt02_battery') | float(-1)) <= 20
or states('switch.drinks_fridge_power_x59pm') != 'on'
or power_missing
@@ -803,8 +810,8 @@ template:
{% set power_missing = not has_value('sensor.drinks_fridge_power_x59pm_power') %}
{% set percentile = states('sensor.drinks_freezer_temperature_95th_percentile_7_days') | float(none) %}
{% set threshold = [-5, percentile + 0.5] | min if percentile is not none else -5 %}
{% if temperature_missing or battery_missing %}
{% set ns.items = ns.items + ['Monitor offline'] %}
{% if temperature_missing %}
{% set ns.items = ns.items + ['Temperature monitor unavailable'] %}
{% endif %}
{% if not battery_missing and (states('sensor.drinks_freezer_environment_lcd_btt02_battery') | float(-1)) <= 20 %}
{% set ns.items = ns.items + ['Low monitor battery'] %}
@@ -845,7 +852,6 @@ template:
{% set threshold = [-10, percentile + 6] | min if percentile is not none else -10 %}
{{
temperature_missing
or battery_missing
or (states('sensor.lcd_temp_humidity_btt03_battery') | float(-1)) <= 20
or states('switch.vertical_freezer_power_x60pm') != 'on'
or power_missing
@@ -861,8 +867,8 @@ template:
{% set power_missing = not has_value('sensor.vertical_freezer_power_x60pm_power') %}
{% set percentile = states('sensor.vertical_freezer_temperature_95th_percentile_7_days') | float(none) %}
{% set threshold = [-10, percentile + 6] | min if percentile is not none else -10 %}
{% if temperature_missing or battery_missing %}
{% set ns.items = ns.items + ['Monitor offline'] %}
{% if temperature_missing %}
{% set ns.items = ns.items + ['Temperature monitor unavailable'] %}
{% endif %}
{% if not battery_missing and (states('sensor.lcd_temp_humidity_btt03_battery') | float(-1)) <= 20 %}
{% set ns.items = ns.items + ['Low monitor battery'] %}
+13 -2
View File
@@ -9,9 +9,12 @@
# packages/garage_corner_light_cctv_motion.yaml
#
# VERSION:
# V1.0 2026-06-29
# V1.1 2026-08-31
#
# VERSION HISTORY:
# V1.1 2026-08-31
# - Restrict CCTV motion activation to one hour before sunset through one hour after sunrise.
#
# V1.0 2026-06-29
# - Initial package.
# - Turns the garage external corner light on when CCTV motion MQTT payload "1" is received.
@@ -20,7 +23,8 @@
# - Adds a Home Assistant enable switch for CCTV movement triggers.
#
# PURPOSE:
# Turns on the garage external corner light from selected CCTV motion MQTT triggers.
# Turns on the garage external corner light from selected CCTV motion MQTT triggers
# during the configured night window.
#
# NOTES:
# - The light is:
@@ -31,6 +35,7 @@
#
# - Turning OFF "Garage corner light movement triggers" prevents new CCTV triggers.
# - If a timer is already running, it will still finish and turn the light off.
# - CCTV triggers run from one hour before sunset until one hour after sunrise.
#
#:########################################################################################:#
# User Configuration #
@@ -91,6 +96,12 @@ automation:
entity_id: input_boolean.garage_corner_light_movement_triggers
state: "on"
- condition: sun
after: sunset
after_offset: "-01:00:00"
before: sunrise
before_offset: "01:00:00"
action:
- service: switch.turn_on
target:
@@ -9,7 +9,7 @@
# jobs_tracker_appliance_completion_triggers.yaml
#
# VERSION:
# V1.0 2026-08-15
# V1.1 2026-09-03
#
# PURPOSE:
# Provides time-qualified appliance door and lid triggers for Jobs Tracker completion.
@@ -19,9 +19,13 @@
#
# OPERATION:
# Each contact must remain open for five seconds before its corresponding Jobs Tracker
# completion route is called. The job registry owns the completion entity mappings.
# completion route is called. Starting the dryer or washer clears only a previous Due
# unload job, so the next finished cycle creates its own new unload task.
#
# VERSION HISTORY:
# V1.1 2026-09-03
# - Clear a pending dryer or washer unload job when its next cycle starts.
#
# V1.0 2026-08-15
# - Added five-second door and lid open completion triggers for dryer and washer jobs.
#
@@ -63,3 +67,43 @@ automation:
data:
completion_entity: binary_sensor.washing_machine_lid_status_x39rs_contact
completion_to_state: "on"
- id: jobs_tracker_clear_dryer_unload_on_start
alias: "Jobs Tracker - Clear Dryer Unload on Start"
description: "Clears an outstanding Empty the Dryer job when a new dryer cycle starts."
mode: single
triggers:
- trigger: state
entity_id: sensor.appliance_monitor_dryer_monitor
to: Operating
conditions:
- condition: state
entity_id: sensor.jobs_tracker_empty_dryer
state: Due
actions:
- action: script.jobs_tracker_dispatch
data:
job_id: empty_dryer
- id: jobs_tracker_clear_washer_unload_on_start
alias: "Jobs Tracker - Clear Washer Unload on Start"
description: "Clears an outstanding Empty the Washer job when a new washer cycle starts."
mode: single
triggers:
- trigger: state
entity_id: sensor.appliance_monitor_washing_machine_monitor
to: Operating
conditions:
- condition: state
entity_id: sensor.jobs_tracker_empty_washing_machine
state: Due
actions:
- action: script.jobs_tracker_dispatch
data:
job_id: empty_washing_machine
@@ -0,0 +1,65 @@
#:########################################################################################:#
# Jobs Tracker Appliance Completion Triggers Package #
#:########################################################################################:#
#
# TITLE:
# Jobs Tracker Appliance Completion Triggers
#
# FILE:
# jobs_tracker_appliance_completion_triggers.yaml
#
# VERSION:
# V1.0 2026-08-15
#
# PURPOSE:
# Provides time-qualified appliance door and lid triggers for Jobs Tracker completion.
#
# DEPENDS ON:
# packages/Job_Chore_Tracking/jobs_tracker_jobs.yaml V3.5 or newer.
#
# OPERATION:
# Each contact must remain open for five seconds before its corresponding Jobs Tracker
# completion route is called. The job registry owns the completion entity mappings.
#
# VERSION HISTORY:
# V1.0 2026-08-15
# - Added five-second door and lid open completion triggers for dryer and washer jobs.
#
#:########################################################################################:#
automation:
- id: jobs_tracker_empty_dryer_door_open
alias: "Jobs Tracker - Empty Dryer on Door Open"
description: "Marks Empty the Dryer complete after the dryer door is open for five seconds."
mode: single
triggers:
- trigger: state
entity_id: binary_sensor.dryer_door_status_x34rs_contact
to: "on"
for:
seconds: 5
actions:
- action: script.jobs_tracker_route_completion
data:
completion_entity: binary_sensor.dryer_door_status_x34rs_contact
completion_to_state: "on"
- id: jobs_tracker_empty_washing_machine_lid_open
alias: "Jobs Tracker - Empty Washer on Lid Open"
description: "Marks Empty the Washer complete after the washer lid is open for five seconds."
mode: single
triggers:
- trigger: state
entity_id: binary_sensor.washing_machine_lid_status_x39rs_contact
to: "on"
for:
seconds: 5
actions:
- action: script.jobs_tracker_route_completion
data:
completion_entity: binary_sensor.washing_machine_lid_status_x39rs_contact
completion_to_state: "on"
+12 -6
View File
@@ -635,10 +635,13 @@ script:
entity_id: input_datetime.outdoor_lighting_number_16_last_schedule_event
data:
datetime: "{{ (selected_event_ts | int(0)) | timestamp_custom('%Y-%m-%d %H:%M:%S', true) }}"
- action: script.outdoor_lighting_ramp_number_16
- action: script.turn_on
target:
entity_id: script.outdoor_lighting_ramp_number_16
data:
target_brightness: "{{ selected_target | int(0) }}"
ramp_seconds: "{{ ramp_to_use | int(0) }}"
variables:
target_brightness: "{{ selected_target | int(0) }}"
ramp_seconds: "{{ ramp_to_use | int(0) }}"
#############################################################################
# 4) Main Entrance Outside LED Lights
@@ -787,10 +790,13 @@ script:
entity_id: input_datetime.outdoor_lighting_main_entrance_last_schedule_event
data:
datetime: "{{ (selected_event_ts | int(0)) | timestamp_custom('%Y-%m-%d %H:%M:%S', true) }}"
- action: script.outdoor_lighting_ramp_main_entrance
- action: script.turn_on
target:
entity_id: script.outdoor_lighting_ramp_main_entrance
data:
target_brightness: "{{ selected_target | int(0) }}"
ramp_seconds: "{{ ramp_to_use | int(0) }}"
variables:
target_brightness: "{{ selected_target | int(0) }}"
ramp_seconds: "{{ ramp_to_use | int(0) }}"
#############################################################################
# Reconcile all
+80 -4
View File
@@ -1,8 +1,8 @@
#:########################################################################################:#
# Package: Pet Feeding and Jobs Timing
# File: pet_feeding_and_jobs.yaml
# Version: v1.4.0
# Date: 2026-07-26
# Version: v1.5.2
# Date: 2026-09-09
#
# Purpose:
# Provides legacy MQTT pet activity sensors and Jobs-derived timing sensors for
@@ -17,6 +17,11 @@
# instead of exposing a numeric sentinel value.
#
# Version History:
# - v1.5.2 (2026-09-12): Ignore stale restored dog-button event states so
# reconnects cannot create false timestamps or visual feedback.
# - v1.5.1 (2026-09-09): Extended dog-button feedback intervals to 500 ms.
# - v1.5.0 (2026-09-09): Added visual feedback flashes for physical dog
# completion buttons, using the Lounge outside light relay.
# - v1.4.0 (2026-07-26): Announce four physical job completions on the Sony
# lounge TV and both Pushover devices.
# - v1.3.0 (2026-07-26): Restore retained dog-fed and jobs-done activity
@@ -122,7 +127,12 @@ automation:
conditions:
- condition: template
value_template: >-
{{ trigger.to_state.state not in ['unknown', 'unavailable'] }}
{% set valid_state = trigger.to_state is not none
and trigger.to_state.state not in ['unknown', 'unavailable'] %}
{% set event_ts = as_timestamp(trigger.to_state.state, 0)
if valid_state else 0 %}
{% set event_age = as_timestamp(now()) - event_ts %}
{{ valid_state and event_ts > 0 and event_age >= 0 and event_age <= 30 }}
actions:
- action: input_datetime.set_datetime
target:
@@ -145,7 +155,12 @@ automation:
conditions:
- condition: template
value_template: >-
{{ trigger.to_state.state not in ['unknown', 'unavailable'] }}
{% set valid_state = trigger.to_state is not none
and trigger.to_state.state not in ['unknown', 'unavailable'] %}
{% set event_ts = as_timestamp(trigger.to_state.state, 0)
if valid_state else 0 %}
{% set event_age = as_timestamp(now()) - event_ts %}
{{ valid_state and event_ts > 0 and event_age >= 0 and event_age <= 30 }}
actions:
- action: input_datetime.set_datetime
target:
@@ -227,3 +242,64 @@ automation:
title: "Jobs Announcement"
short_announcement: "{{ completion_message }}"
long_announcement: "{{ completion_message }}"
#:######################################################################################:#
# AUTOMATION: Physical Dog-Button Visual Feedback
#:######################################################################################:#
- id: dog_button_visual_feedback
alias: "Jobs - Dog Button Visual Feedback"
description: >-
Flashes the Lounge outside light relay for one physical single-press,
preserving its original state after dog feeding or depooping is recorded.
mode: queued
max: 10
triggers:
- trigger: state
entity_id:
- event.jobs_dog_fed_button_x14bt_action
- event.jobs_dog_depooped_button_x15bt_action
conditions:
- condition: template
value_template: >-
{% set valid_state = trigger.to_state is not none
and trigger.to_state.state not in ['unknown', 'unavailable'] %}
{% set event_ts = as_timestamp(trigger.to_state.state, 0)
if valid_state else 0 %}
{% set event_age = as_timestamp(now()) - event_ts %}
{{
valid_state
and event_ts > 0
and event_age >= 0
and event_age <= 30
and trigger.to_state.attributes.event_type == 'single'
}}
actions:
- variables:
initial_state: "{{ states('switch.esp_loungesouthleftswitch_relay_2_outside_light_1') }}"
flash_count: >-
{{ 2 if trigger.entity_id == 'event.jobs_dog_fed_button_x14bt_action' else 3 }}
- repeat:
count: "{{ flash_count | int }}"
sequence:
- choose:
- conditions:
- condition: template
value_template: "{{ initial_state == 'off' }}"
sequence:
- action: switch.turn_on
target:
entity_id: switch.esp_loungesouthleftswitch_relay_2_outside_light_1
- delay: "00:00:00.500"
- action: switch.turn_off
target:
entity_id: switch.esp_loungesouthleftswitch_relay_2_outside_light_1
- delay: "00:00:00.500"
default:
- action: switch.turn_off
target:
entity_id: switch.esp_loungesouthleftswitch_relay_2_outside_light_1
- delay: "00:00:00.500"
- action: switch.turn_on
target:
entity_id: switch.esp_loungesouthleftswitch_relay_2_outside_light_1
- delay: "00:00:00.500"
+27 -122
View File
@@ -9,42 +9,24 @@
# quiet_time_on_off_actions.yaml
#
# VERSION:
# V1.2 2026-06-20
#
# VERSION HISTORY:
# V1.2 2026-06-20
# - Added Quiet Time OFF action to change OpenHASP panel openhasp_1
# back to page 1.
#
# 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.
# V1.3 2026-09-04
#
# 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
# The main Quiet Time package owns the helper, daily reset behaviour, and MQTT
# publishing. This package owns extra local Quiet Time actions.
#
# This package only handles extra local actions that should happen when
# Quiet Time starts or ends.
# VERSION HISTORY:
# V1.3 2026-09-04
# - Disables Flounder Motion Wake during Quiet Time and requests a one-off
# 10-second presence fade to Screen Off.
# - Restores Flounder Motion Wake when Quiet Time 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, MQTT commands, or custom service calls.
# V1.2 2026-06-20
# - Added Quiet Time OFF action to change OpenHASP panel openhasp_1 back to
# page 1.
#
################################################################################
################################################################################
# AUTOMATIONS
################################################################################
@@ -56,56 +38,15 @@ automation:
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
##########################################################################
# OPENHASP QUIET TIME OFF PAGE ACTION
##########################################################################
#
# Sends the OpenHASP panel back to the main page when Quiet Time ends.
#
# Current OpenHASP MQTT command topic:
# hasp/openhasp_1/command/page
#
quiet_time_off_openhasp_page_topic: "hasp/openhasp_1/command/page"
quiet_time_off_openhasp_page: 1
@@ -121,13 +62,6 @@ automation:
actions:
- choose:
######################################################################
# QUIET TIME ON ACTIONS
######################################################################
#
# Runs when Quiet Time starts.
#
- conditions:
- condition: template
value_template: "{{ trigger.to_state.state == 'on' }}"
@@ -138,47 +72,44 @@ automation:
- 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.
#
# Stop camera motion from extending presence, then fade the
# current session to the existing Screen Off base state.
- action: switch.turn_off
target:
entity_id: switch.flounder_kiosk_motion_wake
- wait_template: "{{ is_state('switch.flounder_kiosk_motion_wake', 'off') }}"
timeout: "00:00:10"
continue_on_timeout: true
- action: mqtt.publish
data:
topic: "viewroad-commands/flounder/presence/end"
payload: "10"
retain: false
- 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
##################################################################
- action: switch.turn_on
target:
entity_id: switch.flounder_kiosk_motion_wake
- repeat:
for_each: "{{ quiet_time_off_input_number_values }}"
sequence:
@@ -187,34 +118,8 @@ automation:
entity_id: "{{ repeat.item.entity_id }}"
data:
value: "{{ repeat.item.value | float }}"
##################################################################
# Quiet Time OFF - OpenHASP page action
##################################################################
#
# Change OpenHASP panel back to page 1 when Quiet Time ends.
#
- action: mqtt.publish
data:
topic: "{{ quiet_time_off_openhasp_page_topic }}"
payload: "{{ quiet_time_off_openhasp_page | int }}"
retain: false
##################################################################
# 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"
#
##################################################################
+245
View File
@@ -0,0 +1,245 @@
################################################################################
# Quiet Time On / Off Actions Package
################################################################################
#
# TITLE:
# Quiet Time On / Off Actions
#
# FILE:
# quiet_time_on_off_actions.yaml
#
# VERSION:
# V1.3 2026-09-04
# - Disables Flounder Motion Wake during Quiet Time and requests a one-off
# 10-second presence fade to Screen Off.
# - Restores Flounder Motion Wake when Quiet Time ends.
#
# V1.2 2026-06-20
#
# VERSION HISTORY:
# V1.2 2026-06-20
# - Added Quiet Time OFF action to change OpenHASP panel openhasp_1
# back to page 1.
#
# 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, MQTT commands, 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
##########################################################################
# OPENHASP QUIET TIME OFF PAGE ACTION
##########################################################################
#
# Sends the OpenHASP panel back to the main page when Quiet Time ends.
#
# Current OpenHASP MQTT command topic:
# hasp/openhasp_1/command/page
#
quiet_time_off_openhasp_page_topic: "hasp/openhasp_1/command/page"
quiet_time_off_openhasp_page: 1
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 }}"
# Stop camera motion from extending presence, then fade the
# current session to the existing Screen Off base state.
- action: switch.turn_off
target:
entity_id: switch.flounder_kiosk_motion_wake
- wait_template: "{{ is_state('switch.flounder_kiosk_motion_wake', 'off') }}"
timeout: "00:00:10"
continue_on_timeout: true
- action: mqtt.publish
data:
topic: "viewroad-commands/flounder/presence/end"
payload: "10"
retain: false
######################################################################
# 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 }}"
- action: switch.turn_on
target:
entity_id: switch.flounder_kiosk_motion_wake
##################################################################
# 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 - OpenHASP page action
##################################################################
#
# Change OpenHASP panel back to page 1 when Quiet Time ends.
#
- action: mqtt.publish
data:
topic: "{{ quiet_time_off_openhasp_page_topic }}"
payload: "{{ quiet_time_off_openhasp_page | int }}"
retain: false
##################################################################
# 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"
#
##################################################################
+164
View File
@@ -0,0 +1,164 @@
#:########################################################################################:#
# View Road Smoke Alarm RF Package - V1.2.0 #
#:########################################################################################:#
# Receives normalized AJ-765 RF state from the decoder running on beaver. #
# #
# V1.2 performs early RF fingerprint classification, normally shortly after RF activity. #
# #
# Current RF fingerprints: #
# - detector_test : TEST button transmission from a detector #
# - remote_test : TEST transmission matching the remote capture #
# - remote_hush : HUSH/LOCATE button on the Goldair remote #
# - smoke_alarm : Experimental only - not yet considered authoritative #
# #
# Individual detector identity has NOT been found in the RF protocol. #
#:########################################################################################:#
mqtt:
- binary_sensor:
name: "RF Interconnect"
unique_id: view_road_smoke_alarm_rf_interconnect
default_entity_id: binary_sensor.smoke_alarm_rf_interconnect
state_topic: "viewroad-status/smokealarms/rf/interconnect"
payload_on: "ON"
payload_off: "OFF"
availability_topic: "viewroad-status/smokealarms/rf/availability"
payload_available: "online"
payload_not_available: "offline"
icon: mdi:radio-tower
device:
identifiers:
- view_road_smoke_alarm_rf
name: "Smoke Alarm RF Monitor"
manufacturer: "Zorruno"
model: "AJ-765 RF Monitor"
sw_version: "1.2.0"
- event:
name: "RF Event"
unique_id: view_road_smoke_alarm_rf_event
default_entity_id: event.smoke_alarm_rf_event
state_topic: "viewroad-status/smokealarms/rf/event"
event_types:
- interconnect_start
- detector_test
- remote_test
- remote_hush
- smoke_alarm
- unknown_aj765
- interconnect_end
availability_topic: "viewroad-status/smokealarms/rf/availability"
payload_available: "online"
payload_not_available: "offline"
icon: mdi:radio-tower
device:
identifiers:
- view_road_smoke_alarm_rf
- sensor:
name: "RF Current Type"
unique_id: view_road_smoke_alarm_rf_current_type
default_entity_id: sensor.smoke_alarm_rf_current_type
state_topic: "viewroad-status/smokealarms/rf/current_type"
availability_topic: "viewroad-status/smokealarms/rf/availability"
payload_available: "online"
payload_not_available: "offline"
icon: mdi:waveform
device:
identifiers:
- view_road_smoke_alarm_rf
- sensor:
name: "RF Last Type"
unique_id: view_road_smoke_alarm_rf_last_type
default_entity_id: sensor.smoke_alarm_rf_last_type
state_topic: "viewroad-status/smokealarms/rf/last_type"
entity_category: diagnostic
availability_topic: "viewroad-status/smokealarms/rf/availability"
payload_available: "online"
payload_not_available: "offline"
icon: mdi:waveform
device:
identifiers:
- view_road_smoke_alarm_rf
- sensor:
name: "RF Last Confidence"
unique_id: view_road_smoke_alarm_rf_last_confidence
default_entity_id: sensor.smoke_alarm_rf_last_confidence
state_topic: "viewroad-status/smokealarms/rf/last_confidence"
unit_of_measurement: "%"
state_class: measurement
entity_category: diagnostic
availability_topic: "viewroad-status/smokealarms/rf/availability"
payload_available: "online"
payload_not_available: "offline"
icon: mdi:percent
device:
identifiers:
- view_road_smoke_alarm_rf
- sensor:
name: "RF Last Seen"
unique_id: view_road_smoke_alarm_rf_last_seen
default_entity_id: sensor.smoke_alarm_rf_last_seen
state_topic: "viewroad-status/smokealarms/rf/last_seen"
device_class: timestamp
entity_category: diagnostic
availability_topic: "viewroad-status/smokealarms/rf/availability"
payload_available: "online"
payload_not_available: "offline"
device:
identifiers:
- view_road_smoke_alarm_rf
- sensor:
name: "RF Last Duration"
unique_id: view_road_smoke_alarm_rf_last_duration
default_entity_id: sensor.smoke_alarm_rf_last_duration
state_topic: "viewroad-status/smokealarms/rf/last_duration"
unit_of_measurement: "s"
device_class: duration
state_class: measurement
entity_category: diagnostic
availability_topic: "viewroad-status/smokealarms/rf/availability"
payload_available: "online"
payload_not_available: "offline"
device:
identifiers:
- view_road_smoke_alarm_rf