From 2649a330513e6d74c88d1dedc60d35f45ad7174e Mon Sep 17 00:00:00 2001 From: root Date: Wed, 4 Mar 2026 23:41:30 +1300 Subject: [PATCH] yaml layout updates to v1.1 and minor fixes --- .ha_run.lock | 2 +- esphome/esp-arcademachine1.yaml | 42 ++- esphome/esp-bedrm2ceilingfan.yaml | 28 +- esphome/esp-bedrm2fanswitch.yaml | 28 +- esphome/esp-bedrm2lights.yaml | 26 +- esphome/esp-garagedbcontrols.yaml | 594 +++++++++++++++++++++++------- esphome/esp-lounge6chdimmer.yaml | 43 +-- esphome/esp-mainbathfancombo.yaml | 161 ++++---- esphome/esp-mainpowermonitor.yaml | 25 +- packages/ev_charging.yaml | 117 +++--- 10 files changed, 724 insertions(+), 342 deletions(-) diff --git a/.ha_run.lock b/.ha_run.lock index 470d9d4..dc686f2 100644 --- a/.ha_run.lock +++ b/.ha_run.lock @@ -1 +1 @@ -{"pid": 68, "version": 1, "ha_version": "2026.2.3", "start_ts": 1771822737.408799} \ No newline at end of file +{"pid": 67, "version": 1, "ha_version": "2026.2.3", "start_ts": 1772599646.9007783} \ No newline at end of file diff --git a/esphome/esp-arcademachine1.yaml b/esphome/esp-arcademachine1.yaml index 8dbc450..bf62ede 100644 --- a/esphome/esp-arcademachine1.yaml +++ b/esphome/esp-arcademachine1.yaml @@ -1,9 +1,13 @@ #:########################################################################################:# # TITLE: ARCADE CABINET 1 POWER (ATHOM SMART PLUG V1) -# zorruno.com layout v1.0 2026 +# zorruno.com layout v1.1 2026 +#:########################################################################################:# +# REPO: +# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-arcademachine1.yaml #:########################################################################################:# # VERSIONS: -# V1.0 Unknown date - Initial Version (date not present in source header) +# v1.1 2026-03-04 Updated yaml to zorruno layout V1.1 +# v1.0 Unknown date - Initial Version (date not present in source header) #:########################################################################################:# # HARDWARE: # - Athom Smart Plug Power V1 (ESP8285 / ESP8266 family) @@ -15,10 +19,20 @@ # - Local power button toggles relay state #:########################################################################################:# # MQTT COMMANDS: -# +# - None device-specific in this YAML (uses common MQTT package for base connectivity). #:########################################################################################:# # OFFLINE NOTES: +# a) Home Assistant OFFLINE, WiFi OK +# - Local button still toggles the relay. +# - Power monitoring continues locally; HA updates resume when HA returns. # +# b) MQTT OFFLINE (or broker unreachable) +# - Local button and HA API (if HA reachable) still operate the relay. +# - MQTT state/telemetry (from common packages) will not publish until MQTT returns. +# +# c) Entire WiFi/Network OFFLINE +# - Local button still toggles the relay. +# - Accurate time is NOT needed for operation (no schedules). SNTP not needed. #:########################################################################################:# @@ -30,12 +44,12 @@ substitutions: # Device Naming device_name: "esp-arcademachine1" friendly_name: "Arcade Cabinet 1" - description_comment: "MAME Arcade 1 (Defender) machine power :: Athom Smart Plug Power V1 (Layout V1.0)" + description_comment: "MAME Arcade 1 (Defender) machine power :: Athom Smart Plug Power V1 (Layout V1.1)" device_area: "Downstairs Flat" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. # Project Naming - project_name: "Athom Technology.Smart Plug V1" # Project Details - project_version: "v1.0" # Project V denotes release of yaml file, allowing checking of deployed vs latest version + project_name: "Athom Technology.Smart Plug V1" + project_version: "v1.1" # Passwords api_key: !secret esp-api_key # unfortunately you can't use substitutions inside secrets names @@ -47,10 +61,9 @@ substitutions: current_ip_address: ${static_ip_address} # Device Settings - log_level: "NONE" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE + log_level: "NONE" # NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE update_interval: "10s" # update time for for general sensors etc - # Device Settings relay_icon: "mdi:power-socket-au" current_limit: "10" # Current Limit in Amps. AU Plug = 10. IL, BR, EU, UK, US Plug = 16. @@ -106,11 +119,6 @@ packages: common_general_sensors: !include file: common/sensors_common.yaml - #### DEBUGGING Sensors #### - # A count of various resets - #common_resetcount_debugging: !include - # file: common/resetcount_common.yaml - # Device Specific included packages common_athompowermonV1: !include file: common/athompowermonv1_common.yaml @@ -142,8 +150,6 @@ esphome: esp8266: board: esp8285 restore_from_flash: true # mainly for calculating cumulative energy, but not that important here - #framework: - # version: 2.7.4 #:########################################################################################:# @@ -167,10 +173,8 @@ mdns: # https://esphome.io/components/logger.html #:########################################################################################:# logger: - level: "${log_level}" # INFO Level suggested, or DEBUG for testing + level: "${log_level}" baud_rate: 0 # set to 0 for no logging via UART, needed if you are using it for other serial things (eg PZEM) - #esp8266_store_log_strings_in_flash: false - #tx_buffer_size: 64 #:########################################################################################:# @@ -193,10 +197,8 @@ binary_sensor: then: - lambda: |- if (id(relay).state) { - // Relay is ON: turn it OFF id(relay).turn_off(); } else { - // Relay is OFF: turn it ON id(relay).turn_on(); } diff --git a/esphome/esp-bedrm2ceilingfan.yaml b/esphome/esp-bedrm2ceilingfan.yaml index 43433d1..b7f01a5 100644 --- a/esphome/esp-bedrm2ceilingfan.yaml +++ b/esphome/esp-bedrm2ceilingfan.yaml @@ -1,12 +1,16 @@ #:########################################################################################:# # TITLE: CEILING FAN - BEDROOM 2 -# zorruno.com layout v1.0 2026 +# zorruno.com layout v1.1 2026 +#:########################################################################################:# +# REPO: +# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-bedrm2ceilingfan.yaml #:########################################################################################:# # VERSIONS: +# V1.1 2026-03-04 Updated yaml to zorruno layout V1.1 # V1.0 2025-07-21 First Setup (and replacement of Tasmota) #:########################################################################################:# # HARDWARE: -# Controlled by a Sonoff iFan02 (3-speed fan + 1x light relay) +# - Controlled by a Sonoff iFan02 (3-speed fan + 1x light relay) #:########################################################################################:# # OPERATION NOTES: # - Fan and light can be controlled via MQTT (local topics below) and via Home Assistant API @@ -36,8 +40,10 @@ # - No HA API and no MQTT control available # - If power remains on, the relays keep their last driven state (fan speed relays and light relay) # - If the device reboots while offline, fan/light restore_mode defaults apply (RESTORE_DEFAULT_OFF) +# - Accurate time is NOT needed (SNTP not required) for core operation (no schedules/timeclocks used) #:########################################################################################:# + #:########################################################################################:# # SUBSTITUTIONS: Specific device variable substitutions # If NOT using a secrets file, just replace these with the passwords etc (in quotes) @@ -46,12 +52,12 @@ substitutions: # Device Naming device_name: "esp-bedrm2ceilingfan" friendly_name: "Bedroom 2 Ceiling Fan" - description_comment: "3 Speed Overhead Ceiling Fan Bedroom 2 :: Sonoff iFan02 (Layout V1.0)" + description_comment: "3 Speed Overhead Ceiling Fan Bedroom 2 :: Sonoff iFan02 (Layout V1.1)" device_area: "Bedroom 2" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. # Project Naming project_name: "Sonoff Technologies.Sonoff iFan02" # Project Details - project_version: "v1.0" # Project V denotes release of yaml file, allowing checking of deployed vs latest version + project_version: "v1.1" # Project V denotes release of yaml file, allowing checking of deployed vs latest version # Passwords and Secrets api_key: !secret esp-api_key @@ -79,6 +85,7 @@ substitutions: switch_3_name: "Fan 3 Relay" switch_4_name: "Fan 4 Relay" + #:########################################################################################:# # PACKAGES: Included Common Packages # https://esphome.io/components/packages.html @@ -118,6 +125,7 @@ packages: 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 @@ -141,6 +149,7 @@ esphome: # - "-fno-exceptions" # strip C++ exceptions # - "-fno-rtti" # strip C++ RTTI + #:########################################################################################:# # ESP PLATFORM AND FRAMEWORK: # https://esphome.io/components/esp8266/ @@ -156,6 +165,7 @@ preferences: mdns: disabled: true # Disabling will make the build file smaller (and it is still available via static IP) + #:########################################################################################:# # GLOBAL VARIABLES: # https://esphome.io/guides/automations.html#global-variables @@ -166,6 +176,7 @@ globals: restore_value: true initial_value: "0" + #:########################################################################################:# # LOGGING: ESPHome Logging Enable # https://esphome.io/components/logger.html @@ -174,6 +185,7 @@ logger: level: "${log_level}" # INFO suggested, or DEBUG for testing baud_rate: 0 # set to 0 for no logging via UART (UART0 used for fan MCU sniffing) + #:########################################################################################:# # UART: # Fan MCU / RF Remote Sniff (iFan02 -> ESP over UART0) @@ -191,6 +203,7 @@ uart: # NOTE: If no frames appear at either 9600 or 19200, try moving rx_pin to GPIO13 and reflash: # rx_pin: GPIO13 + #:########################################################################################:# # INTERVAL COMPONENT: # https://esphome.io/components/interval.html @@ -222,6 +235,7 @@ interval: frame.clear(); } + #:########################################################################################:# # TEXT SENSORS COMPONENT: # https://esphome.io/components/text_sensor/index.html @@ -232,6 +246,7 @@ text_sensor: name: "iFan02 Remote Last Frame" update_interval: never + #:########################################################################################:# # MQTT: # This adds device-specific MQTT command triggers to the common MQTT configuration. @@ -282,6 +297,7 @@ mqtt: id: ifan02_fan speed: !lambda "return id(speed_value);" + #:########################################################################################:# # SWITCH COMPONENT: # https://esphome.io/components/switch/ @@ -311,6 +327,7 @@ switch: restore_mode: RESTORE_DEFAULT_OFF internal: true + #:########################################################################################:# # SELECT COMPONENT: # https://esphome.io/components/select/index.html @@ -375,6 +392,7 @@ select: id: ifan02_fan speed: 3 + #:########################################################################################:# # OUTPUT COMPONENT: # https://esphome.io/components/output/ @@ -412,6 +430,7 @@ output: id(fan4sw).turn_on(); } + #:########################################################################################:# # LIGHT COMPONENT: # https://esphome.io/components/light/ @@ -432,6 +451,7 @@ light: topic: "${mqtt_local_status_topic}/light/state" payload: "OFF" + #:########################################################################################:# # FAN COMPONENT: # https://esphome.io/components/fan/index.html diff --git a/esphome/esp-bedrm2fanswitch.yaml b/esphome/esp-bedrm2fanswitch.yaml index 69610e1..6c720db 100644 --- a/esphome/esp-bedrm2fanswitch.yaml +++ b/esphome/esp-bedrm2fanswitch.yaml @@ -1,13 +1,17 @@ #:########################################################################################:# # TITLE: BEDROOM 2 FAN SWITCH (WALL SWITCH) -# zorruno.com layout v1.0 2026 +# zorruno.com layout v1.1 2026 +#:########################################################################################:# +# REPO: +# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-bedrm2fanswitch.yaml #:########################################################################################:# # VERSIONS: +# V1.1 2026-03-04 Updated yaml to zorruno layout V1.1 # V1.0 2025-07-23 First Setup (and replacement of Tasmota) #:########################################################################################:# # HARDWARE: -# Controlled by a Zemismart KS-811 Triple push button (KS-811-3) -# pinout/schematic: https://community.home-assistant.io/t/zemismart-ks-811-working-with-esphome/ +# - Zemismart KS-811 Triple push button (KS-811-3) +# pinout/schematic: https://community.home-assistant.io/t/zemismart-ks-811-working-with-esphome/ #:########################################################################################:# # OPERATION NOTES: # - Wall switch for Bedroom 2 ceiling fan @@ -40,8 +44,10 @@ # - No MQTT publish/subscribe and no HA API connectivity # - Buttons will do nothing to the fan (no transport) # - Relay indicators will remain in their last state until connectivity returns (or reboot) +# - Accurate time is NOT needed (SNTP not required) for core operation (no time-of-day schedules) #:########################################################################################:# + #:########################################################################################:# # SUBSTITUTIONS: Specific device variable substitutions # If NOT using a secrets file, just replace these with the passwords etc (in quotes) @@ -50,12 +56,12 @@ substitutions: # Device Naming device_name: "esp-bedrm2fanswitch" friendly_name: "Bedroom 2 Fan Wall Switch (3)" - description_comment: "Switch for Bedroom 2 Ceiling Fan using Zemismart KS-811 Triple Push Button. Speed Up (1), Speed Down (2), Fan Off (3) (Layout V1.0)" + description_comment: "Switch for Bedroom 2 Ceiling Fan using Zemismart KS-811 Triple Push Button. Speed Up (1), Speed Down (2), Fan Off (3) (Layout V1.1)" device_area: "Bedroom 2" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. # Project Naming project_name: "Zemismart Technologies.KS-811-3 (Triple)" # Project Details - project_version: "v1" # Project V denotes release of yaml file, allowing checking of deployed vs latest version + project_version: "v1.1" # Project V denotes release of yaml file, allowing checking of deployed vs latest version # Passwords api_key: !secret esp-api_key # unfortunately you can't use substitutions inside secrets names @@ -87,6 +93,7 @@ substitutions: switch_2_name: "Fan Speed Down" # Virtual intent only, no load connected to relay 2 switch_3_name: "Fan Off" # Virtual intent only, no load connected to relay 3 + #:########################################################################################:# # PACKAGES: Included Common Packages # https://esphome.io/components/packages.html @@ -126,6 +133,7 @@ packages: 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 @@ -145,6 +153,7 @@ esphome: # - "-fno-exceptions" # strip C++ exceptions # - "-fno-rtti" # strip C++ RTTI + #:########################################################################################:# # ESP PLATFORM AND FRAMEWORK: # https://esphome.io/components/esp8266/ @@ -155,6 +164,7 @@ esp8266: early_pin_init: false # Recommended false where switches are involved. Defaults to true. board_flash_mode: dout # Default is dout + #:########################################################################################:# # LOGGING: ESPHome Logging Enable # https://esphome.io/components/logger.html @@ -165,6 +175,7 @@ logger: #esp8266_store_log_strings_in_flash: false #tx_buffer_size: 64 + #:########################################################################################:# # STATUS LED: # https://esphome.io/components/status_led.html @@ -172,9 +183,10 @@ logger: #:########################################################################################:# status_led: pin: - number: GPIO02 + number: GPIO2 inverted: true + #:########################################################################################:# # BINARY SENSORS: # https://esphome.io/components/binary_sensor/ @@ -195,7 +207,7 @@ binary_sensor: - platform: gpio pin: - number: GPIO05 + number: GPIO5 mode: INPUT inverted: true name: "Button 2: ${switch_2_name}" @@ -215,6 +227,7 @@ binary_sensor: topic: "${mqtt_remote_device_command_topic}" payload: "${mqtt_remote_device_command3}" + #:########################################################################################:# # SWITCH COMPONENT: # https://esphome.io/components/switch/ @@ -236,6 +249,7 @@ switch: pin: GPIO14 id: Relay_3 + #:########################################################################################:# # MQTT: # Subscribe to fan speed state and mirror it to relay indicator pattern diff --git a/esphome/esp-bedrm2lights.yaml b/esphome/esp-bedrm2lights.yaml index 975fe72..aa2c82e 100644 --- a/esphome/esp-bedrm2lights.yaml +++ b/esphome/esp-bedrm2lights.yaml @@ -1,13 +1,17 @@ #:########################################################################################:# # TITLE: BEDROOM 2 LIGHTSWITCH -# zorruno.com layout v1.0 2026 +# zorruno.com layout v1.1 2026 +#:########################################################################################:# +# REPO: +# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-bedrm2lights.yaml #:########################################################################################:# # VERSIONS: +# V3.6 2026-03-04 Updated yaml to zorruno layout V1.1 # V3.5 2025-07-24 YAML tidyups #:########################################################################################:# # HARDWARE: -# Zemismart KS-811 Double push button (KS-811-2) -# pinout/schematic: https://community.home-assistant.io/t/zemismart-ks-811-working-with-esphome/ +# - Zemismart KS-811 Double push button (KS-811-2) +# pinout/schematic: https://community.home-assistant.io/t/zemismart-ks-811-working-with-esphome/ #:########################################################################################:# # OPERATION NOTES: # - 2 button wall switch controlling 2 relays @@ -30,8 +34,10 @@ # c) Entire WiFi/Network OFFLINE # - Physical buttons still toggle the relays locally (standalone operation) # - No HA API access and no MQTT connectivity until network returns +# - Accurate time is NOT needed (SNTP not required) for core operation (no time-of-day schedules) #:########################################################################################:# + #:########################################################################################:# # SUBSTITUTIONS: Specific device variable substitutions # If NOT using a secrets file, just replace these with the passwords etc (in quotes) @@ -40,12 +46,12 @@ substitutions: # Device Naming device_name: "esp-bedrm2lights" friendly_name: "Bedroom 2 Lightswitch (2)" - description_comment: "Bedroom 2 Main Lightswitch using a Zemismart KS-811 Double Push Button. Main Lights (1), Desk Lights (2) (Layout V1.0)" + description_comment: "Bedroom 2 Main Lightswitch using a Zemismart KS-811 Double Push Button. Main Lights (1), Desk Lights (2) (Layout V1.1)" device_area: "Bedroom 2" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. # Project Naming project_name: "Zemismart Technologies.KS-811 Double" # Project Details - project_version: "v3.5" # Project V denotes release of yaml file, allowing checking of deployed vs latest version + project_version: "v3.6" # Project V denotes release of yaml file, allowing checking of deployed vs latest version # Passwords and Secrets api_key: !secret esp-api_key # unfortunately you can't use substitutions inside secrets names @@ -64,6 +70,7 @@ substitutions: switch_1_name: "Main Lights" switch_2_name: "Desk Lights" + #:########################################################################################:# # PACKAGES: Included Common Packages # https://esphome.io/components/packages.html @@ -103,6 +110,7 @@ packages: 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 @@ -120,16 +128,17 @@ esphome: # then: # - switch.turn_on: Relay_2 + #:########################################################################################:# # ESP PLATFORM AND FRAMEWORK: # https://esphome.io/components/esp8266/ -# https://esphome.io/components/esp32/ #:########################################################################################:# esp8266: board: esp01_1m early_pin_init: false # Recommended false where switches are involved. Defaults to true. board_flash_mode: dout # Default is dout + #:########################################################################################:# # LOGGING: ESPHome Logging Enable # https://esphome.io/components/logger.html @@ -140,6 +149,7 @@ logger: #esp8266_store_log_strings_in_flash: false #tx_buffer_size: 64 + #:########################################################################################:# # STATUS LED: # https://esphome.io/components/status_led.html @@ -149,6 +159,7 @@ status_led: number: GPIO2 inverted: true + #:########################################################################################:# # BINARY SENSORS: # https://esphome.io/components/binary_sensor/ @@ -167,7 +178,7 @@ binary_sensor: - platform: gpio pin: - number: GPIO05 + number: GPIO5 mode: INPUT inverted: true name: "Button 2: ${switch_2_name}" @@ -184,6 +195,7 @@ binary_sensor: # on_press: # - switch.toggle: Relay_3 + #:########################################################################################:# # SWITCH COMPONENT: # https://esphome.io/components/switch/ diff --git a/esphome/esp-garagedbcontrols.yaml b/esphome/esp-garagedbcontrols.yaml index 11946dd..401cbbd 100644 --- a/esphome/esp-garagedbcontrols.yaml +++ b/esphome/esp-garagedbcontrols.yaml @@ -6,6 +6,10 @@ # https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-garagedbcontrols.yaml #:########################################################################################:# # VERSIONS: +# v1.2 2026-03-03 EV monitoring updated to match Device A: +# - Rolling average EV power (default 5 min @ 10s) +# - EV charging binary sensor (threshold configurable) +# - EV totals and energy summaries (today/yesterday/month/year) # v1.1 2026-02-25 Updated yaml to zorruno layout V1.1 # v1.0 2025-10-23 Initial Version #:########################################################################################:# @@ -22,36 +26,27 @@ # OPERATION NOTES: # - Provides 4 relay outputs with matching local buttons. # - Relay restore modes are set per-output (some default ON, some default OFF). -# - Includes EV charger energy monitoring via pulse input (2.0 Wh per pulse): -# - "Power Now" derived from pulse rate (pulse_meter). -# - Lifetime kWh total from pulse total (pulse_meter total). -# - 1-hour sliding average for power. -# - Daily kWh using total_daily_energy, with "Energy Yesterday" snap at 23:59 when time is valid. -# - Publishes a Tasmota-like MQTT telemetry message once per 60s with windowed Wh delta. -#:########################################################################################:# -# MQTT COMMANDS: -# - Publishes windowed EV Wh count every 60s: -# Topic: stat/tasmo-s4chan-7594-garage-1/EnergyMeterCount -# Payload example: {"EVChargerWhCount":123.456} +# - EV charger monitoring (2 Wh per pulse on GPIO2): +# - Lifetime total energy (kWh) from persisted absolute pulse total. +# - Rolling average power (default 5 min) to reduce spiky readings at low load. +# - Charging binary sensor based on averaged power threshold. +# - Energy summaries: today, yesterday, this month, last month, this year, last year. #:########################################################################################:# # OFFLINE NOTES: # a) Home Assistant OFFLINE, WiFi OK -# - Device continues relay/button operation and continues pulse metering locally. -# - MQTT telemetry publishing depends on MQTT broker availability. -# +# - Device continues relay/button operation and continues pulse counting locally. +# - API/MQTT publishing will fail until HA (and/or broker) is back. # b) MQTT OFFLINE (or broker unreachable) -# - Relays/buttons still operate locally and via HA API (if HA is available). -# - MQTT telemetry messages will not be delivered until broker returns. -# +# - Relays/buttons still operate locally and via HA API (if HA is available). +# - MQTT state publishing depends on broker. # c) Entire WiFi/Network OFFLINE -# - Accurate time IS needed for the "Energy Yesterday" snapshot at 23:59 (SNTP required). -# - Power/energy totals still accumulate, but "Energy Yesterday" will not update without valid time. +# - Accurate time IS needed for daily/monthly/year rollovers (SNTP required for correct period boundaries). +# - Loss of network will drift the timeclocks; time of day can be reset by powering off then on again at 12pm (midday) #:########################################################################################:# #:########################################################################################:# # SUBSTITUTIONS: Specific device variable substitutions -# If NOT using a secrets file, just replace these with the passwords etc (in quotes) #:########################################################################################:# substitutions: # Device Naming @@ -62,7 +57,7 @@ substitutions: # Project Naming project_name: "Sonoff Technologies.Sonoff 4Ch R2" - project_version: "v1.1" + project_version: "v1.2" # Passwords & Secrets api_key: !secret esp-api_key @@ -77,8 +72,12 @@ substitutions: log_level: "ERROR" update_interval: "60s" + # Globals persistence tuning (gentler on flash) + globals_save_interval_slow: "10min" + globals_save_interval_pulses: "2min" + # I/O Naming - switch_1_name: "Small EV Charger" + switch_1_name: "16A Wallcharger Operation" button_1_name: "Button 1" relay_1_icon: "mdi:power-socket-au" @@ -94,15 +93,43 @@ substitutions: button_4_name: "Button 4" relay_4_icon: "mdi:toggle-switch" - # EV pulse settings - ev_pulse_wh: "2.0" # Wh per pulse - ev_pulse_filter: "10ms" # debounce pulses - ev_pulse_timeout: "2min" # set power to 0W if no pulses - ev_tasmota_tele_topic: "stat/tasmo-s4chan-7594-garage-1/EnergyMeterCount" + ######################################################################################## + # EV Pulse Settings (Device A logic) + ######################################################################################## + ev_pulse_wh: "2.0" # Each pulse is 2Wh + ev_pulse_filter_ms: "10ms" # Debounce / filter + ev_pulse_pin_mode: "INPUT_PULLUP" + + # EV Charging Detection (Averaged power + binary sensor) + # Minimum EV charging power threshold (W) for binary sensor + ev_charging_min_power_w: "200" + + # Rolling average window - configuration notes: + # - Substitutions are string replacements, so window samples must be updated manually if you change times. + # - window_samples = (avg_minutes * 60) / sample_interval_seconds + ev_power_avg_window_minutes: "5" # informational + ev_power_avg_sample_interval: "10s" + ev_power_avg_sample_interval_seconds: "10" # must match the value above + ev_power_avg_window_samples: "15" # 5 min @ 10s = 30 samples + ev_power_avg_buffer_size: "60" # ring buffer >= window_samples (180 @ 10s = 30 minutes) + + # Rollover check interval (time-valid day/month/year updates) + rollover_check_interval: "30s" + + # Names - EV Wallcharger (Device B) + ev_charger_name: "16A Wallcharger" + ev_power_name: "${ev_charger_name} Power" + ev_charging_name: "${ev_charger_name} Charging" + ev_total_energy_name: "${ev_charger_name} Total Energy" + ev_today_name: "${ev_charger_name} Energy Today" + ev_yesterday_name: "${ev_charger_name} Energy Yesterday" + ev_month_name: "${ev_charger_name} Energy This Month" + ev_last_month_name: "${ev_charger_name} Energy Last Month" + ev_year_name: "${ev_charger_name} Energy This Year" + ev_last_year_name: "${ev_charger_name} Energy Last Year" #:########################################################################################:# # PACKAGES: Included Common Packages -# https://esphome.io/components/packages.html #:########################################################################################:# packages: #### WIFI, Network (Static/DHCP/IPV6 etc), Fallback AP, Safemode #### @@ -114,10 +141,9 @@ packages: local_ota_pass: "${ota_pass}" local_current_ip_address: "${current_ip_address}" - #### HOME ASSISTANT API (choose encryption or no encryption options) #### + #### HOME ASSISTANT API #### common_api: !include file: common/api_common.yaml - #file: common/api_common_noencryption.yaml vars: local_api_key: "${api_key}" @@ -127,18 +153,15 @@ packages: vars: local_device_name: "${device_name}" - #### SNTP (Only use if you want/need accurate timeclocks) #### + #### SNTP #### common_sntp: !include common/sntp_common.yaml #### 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}" @@ -148,20 +171,15 @@ esphome: project: name: "${project_name}" version: "${project_version}" - # On boot, initialise the window start so the first publish isn't huge on_boot: - priority: -10 + priority: -100 then: - - lambda: |- - if (isfinite(id(ev_energy_total_kwh).state)) { - id(ev_last_total_kwh) = id(ev_energy_total_kwh).state; - } else { - id(ev_last_total_kwh) = 0.0f; - } + - script.execute: init_ev_history_arrays + - delay: 3s + - script.execute: ev_rollover_check #:########################################################################################:# # ESP PLATFORM AND FRAMEWORK -# https://esphome.io/components/esp8266.html #:########################################################################################:# esp8266: board: esp8285 @@ -175,7 +193,6 @@ mdns: #:########################################################################################:# # LOGGING -# https://esphome.io/components/logger.html #:########################################################################################:# logger: level: "${log_level}" @@ -183,7 +200,6 @@ logger: #:########################################################################################:# # STATUS LED -# https://esphome.io/components/status_led.html #:########################################################################################:# status_led: pin: @@ -191,30 +207,93 @@ status_led: inverted: yes #:########################################################################################:# -# GLOBALS -# https://esphome.io/components/globals.html +# GLOBALS (EV totals + rollovers + rolling average history) #:########################################################################################:# globals: - # Store the last published total (kWh) for 60s delta windowing - - id: ev_last_total_kwh + # Date tracking for rollovers (restored) + - id: g_last_day + type: int + restore_value: yes + update_interval: ${globals_save_interval_slow} + initial_value: "0" + - id: g_last_month + type: int + restore_value: yes + update_interval: ${globals_save_interval_slow} + initial_value: "0" + - id: g_last_year + type: int + restore_value: yes + update_interval: ${globals_save_interval_slow} + initial_value: "0" + + # EV baseline totals for period calculations (restored) + - id: g_ev_day_start_kwh type: float - restore_value: true + restore_value: yes + update_interval: ${globals_save_interval_slow} + initial_value: "-1.0" + - id: g_ev_month_start_kwh + type: float + restore_value: yes + update_interval: ${globals_save_interval_slow} + initial_value: "-1.0" + - id: g_ev_year_start_kwh + type: float + restore_value: yes + update_interval: ${globals_save_interval_slow} + initial_value: "-1.0" + + # EV stored prior periods (restored) + - id: g_ev_yesterday_kwh + type: float + restore_value: yes + update_interval: ${globals_save_interval_slow} + initial_value: "0.0" + - id: g_ev_last_month_kwh + type: float + restore_value: yes + update_interval: ${globals_save_interval_slow} + initial_value: "0.0" + - id: g_ev_last_year_kwh + type: float + restore_value: yes + update_interval: ${globals_save_interval_slow} initial_value: "0.0" - - id: ev_energy_yesterday_kwh + # EV pulse accumulation (persisted across reboots) (restored) + - id: g_ev_pulses_total_abs + type: uint32_t + restore_value: yes + update_interval: ${globals_save_interval_pulses} + initial_value: "0" + - id: g_ev_pulses_raw_last + type: uint32_t + restore_value: yes + update_interval: ${globals_save_interval_pulses} + initial_value: "0" + + # EV rolling average power (RAM only) + - id: g_ev_power_avg_w type: float - restore_value: true + restore_value: no initial_value: "0.0" - # Track last day-of-year we snapped so it only happens once per day - - id: ev_last_snapshot_doy + # EV pulse history ring buffer for rolling average (RAM only) + - id: g_ev_pulses_hist + type: uint32_t[${ev_power_avg_buffer_size}] + restore_value: no + - id: g_ev_pulses_hist_index type: int restore_value: no - initial_value: "-1" + initial_value: "0" + - id: g_ev_pulses_hist_count + type: int + restore_value: no + initial_value: "0" #:########################################################################################:# # SWITCHES -# https://esphome.io/components/switch/ #:########################################################################################:# switch: # Sonoff 4Ch R2 Relays are GPIO12, GPIO5, GPIO4, GPIO15 @@ -248,7 +327,6 @@ switch: #:########################################################################################:# # BINARY SENSORS -# https://esphome.io/components/binary_sensor/ #:########################################################################################:# binary_sensor: # Sonoff 4Ch R2 Buttons are GPIO0, GPIO9, GPIO10, GPIO14 @@ -292,133 +370,377 @@ binary_sensor: on_press: - switch.toggle: relay4 + # EV charging detection (Device A logic: averaged power threshold) + - platform: template + name: "${ev_charging_name}" + id: ev_wallcharger_charging + device_class: power + icon: "mdi:ev-station" + lambda: |- + const float p = id(ev_power_avg_w).state; + if (isnan(p)) return false; + return (p >= ${ev_charging_min_power_w}); + #:########################################################################################:# # SENSORS -# https://esphome.io/components/sensor/ #:########################################################################################:# sensor: ######################################################################################## - # EV CHARGER ENERGY (PULSE INPUT ON GPIO2, 2.0 Wh PER PULSE) - # - Uses pulse_meter for stable pulse rate and total count - # - Derives Power (W), 1h average Power, Today kWh, and lifetime kWh + # EV WALLCHARGER PULSE ENERGY (GPIO2) ######################################################################################## - - platform: pulse_meter - id: ev_pulse_pm + - platform: pulse_counter + id: ev_pulse_rate_ppm + internal: true pin: number: GPIO2 - mode: INPUT_PULLUP # GPIO2 must be HIGH at boot + mode: ${ev_pulse_pin_mode} # GPIO2 must be HIGH at boot inverted: false - name: "${switch_1_name} Power Now" + update_interval: ${ev_power_avg_sample_interval} + internal_filter: ${ev_pulse_filter_ms} + total: + id: ev_pulses_raw_total + internal: true + on_value: + then: + - lambda: |- + uint32_t raw = (uint32_t) x; + uint32_t last = id(g_ev_pulses_raw_last); + if (raw < last) { + last = 0; + } + id(g_ev_pulses_total_abs) += (raw - last); + id(g_ev_pulses_raw_last) = raw; + + - platform: template + name: "${ev_power_name}" + id: ev_power_avg_w unit_of_measurement: "W" device_class: power state_class: measurement accuracy_decimals: 0 - internal_filter: ${ev_pulse_filter} - timeout: ${ev_pulse_timeout} - # pulse_meter reports pulses/min; each pulse = 2 Wh => W = pulses/min * 2 * 60 = x120 - filters: - - multiply: 120.0 - - total: - id: ev_energy_total_kwh - name: "${switch_1_name} Energy Cumulative Total" - unit_of_measurement: "kWh" - device_class: energy - state_class: total_increasing - accuracy_decimals: 1 - filters: - # kWh per pulse (2.0 Wh = 0.002 kWh) - - multiply: 0.002 + update_interval: "${ev_power_avg_sample_interval}" + lambda: |- + return id(g_ev_power_avg_w); - platform: template - id: ev_power_avg_1h - name: "${switch_1_name} Power (1h avg)" - unit_of_measurement: "W" - device_class: power - state_class: measurement - accuracy_decimals: 1 - update_interval: 10s + name: "${ev_total_energy_name}" + id: ev_total_energy_kwh + unit_of_measurement: "kWh" + device_class: energy + state_class: total_increasing + accuracy_decimals: 3 + update_interval: ${ev_power_avg_sample_interval} lambda: |- - return id(ev_pulse_pm).state; - filters: - - sliding_window_moving_average: - window_size: 360 # 10 s * 360 ≈ 1 hour - send_every: 6 # update HA once per minute + return ((float) id(g_ev_pulses_total_abs)) * (${ev_pulse_wh} / 1000.0f); - - platform: total_daily_energy + - platform: template + name: "${ev_today_name}" id: ev_energy_today_kwh - name: "${switch_1_name} Energy Today" - power_id: ev_pulse_pm unit_of_measurement: "kWh" device_class: energy state_class: total - accuracy_decimals: 1 - filters: - # total_daily_energy outputs Wh; convert to kWh - - multiply: 0.001 + accuracy_decimals: 3 + update_interval: 60s + lambda: |- + const float total = id(ev_total_energy_kwh).state; + if (isnan(total) || id(g_ev_day_start_kwh) < 0.0f) return NAN; + float v = total - id(g_ev_day_start_kwh); + if (v < 0.0f) v = 0.0f; + return v; - platform: template - id: ev_energy_yesterday_sensor - name: "${switch_1_name} Energy Yesterday" + name: "${ev_yesterday_name}" + id: ev_energy_yesterday_kwh unit_of_measurement: "kWh" device_class: energy state_class: total - accuracy_decimals: 1 - update_interval: 30s + accuracy_decimals: 3 + update_interval: 10min lambda: |- - return id(ev_energy_yesterday_kwh); + return id(g_ev_yesterday_kwh); + + - platform: template + name: "${ev_month_name}" + id: ev_energy_this_month_kwh + unit_of_measurement: "kWh" + device_class: energy + state_class: total + accuracy_decimals: 3 + update_interval: 10min + lambda: |- + const float total = id(ev_total_energy_kwh).state; + if (isnan(total) || id(g_ev_month_start_kwh) < 0.0f) return NAN; + float v = total - id(g_ev_month_start_kwh); + if (v < 0.0f) v = 0.0f; + return v; + + - platform: template + name: "${ev_last_month_name}" + id: ev_energy_last_month_kwh + unit_of_measurement: "kWh" + device_class: energy + state_class: total + accuracy_decimals: 3 + update_interval: 30min + lambda: |- + return id(g_ev_last_month_kwh); + + - platform: template + name: "${ev_year_name}" + id: ev_energy_this_year_kwh + unit_of_measurement: "kWh" + device_class: energy + state_class: total + accuracy_decimals: 3 + update_interval: 30min + lambda: |- + const float total = id(ev_total_energy_kwh).state; + if (isnan(total) || id(g_ev_year_start_kwh) < 0.0f) return NAN; + float v = total - id(g_ev_year_start_kwh); + if (v < 0.0f) v = 0.0f; + return v; + + - platform: template + name: "${ev_last_year_name}" + id: ev_energy_last_year_kwh + unit_of_measurement: "kWh" + device_class: energy + state_class: total + accuracy_decimals: 3 + update_interval: 60min + lambda: |- + return id(g_ev_last_year_kwh); ######################################################################################## # DHT SENSOR (GPIO3) - # NOTE: GPIO3 is UART RX on ESP8266; logger baud_rate=0 avoids conflicts. ######################################################################################## - platform: dht pin: GPIO3 - update_interval: 60s + update_interval: ${update_interval} temperature: name: "${friendly_name} Temperature" + unit_of_measurement: "°C" + accuracy_decimals: 1 humidity: name: "${friendly_name} Humidity" + unit_of_measurement: "%" + accuracy_decimals: 0 #:########################################################################################:# -# INTERVALS -# https://esphome.io/components/interval.html +# INTERVALS (EV rolling average + rollover checks) #:########################################################################################:# interval: - # Teleperiod = 60s: publish JSON and advance the window - - interval: 60s + - interval: "${rollover_check_interval}" then: - - mqtt.publish: - topic: "${ev_tasmota_tele_topic}" - qos: 0 - retain: false - payload: !lambda |- - float current = id(ev_energy_total_kwh).state; - if (!isfinite(current)) current = id(ev_last_total_kwh); + - script.execute: ev_rollover_check - float delta_kwh = current - id(ev_last_total_kwh); - if (delta_kwh < 0.0f || !isfinite(delta_kwh)) delta_kwh = 0.0f; - float delta_wh = delta_kwh * 1000.0f; + - interval: "${ev_power_avg_sample_interval}" + then: + - script.execute: ev_power_avg_update - id(ev_last_total_kwh) = current; +#:########################################################################################:# +# SCRIPTS (EV) +#:########################################################################################:# +script: + # Initialize EV pulse history buffer so rolling average starts stable after boot + - id: init_ev_history_arrays + mode: queued + then: + - lambda: |- + const int ev_buf = ${ev_power_avg_buffer_size}; + const uint32_t p0 = id(g_ev_pulses_total_abs); - char buf[64]; - snprintf(buf, sizeof(buf), "{\"EVChargerWhCount\":%.3f}", delta_wh); - return std::string(buf); + for (int i = 0; i < ev_buf; i++) { + id(g_ev_pulses_hist)[i] = p0; + } + id(g_ev_pulses_hist_index) = 0; + id(g_ev_pulses_hist_count) = ev_buf; + id(g_ev_power_avg_w) = 0.0f; - # YESTERDAY ENERGY snapshot (once per day at 23:59, when time is valid) - - interval: 60s + # EV rolling average update (based on absolute pulse total) + - id: ev_power_avg_update + mode: queued + then: + - lambda: |- + const int buf = ${ev_power_avg_buffer_size}; + int idx = id(g_ev_pulses_hist_index); + if (idx < 0 || idx >= buf) idx = 0; + + const uint32_t p_now = id(g_ev_pulses_total_abs); + id(g_ev_pulses_hist)[idx] = p_now; + + idx = (idx + 1) % buf; + id(g_ev_pulses_hist_index) = idx; + + if (id(g_ev_pulses_hist_count) < buf) id(g_ev_pulses_hist_count)++; + + int win = ${ev_power_avg_window_samples}; + if (win < 1) win = 1; + if (win > buf) win = buf; + + if (id(g_ev_pulses_hist_count) < win) { + return; + } + + int old_idx = idx - win; + if (old_idx < 0) old_idx += buf; + + const uint32_t p_old = id(g_ev_pulses_hist)[old_idx]; + const uint32_t dp = (p_now >= p_old) ? (p_now - p_old) : 0; + + const float wh = ((float) dp) * (${ev_pulse_wh}); + const float seconds = ((float) win) * (${ev_power_avg_sample_interval_seconds}); + + if (seconds <= 0.0f) { + id(g_ev_power_avg_w) = 0.0f; + return; + } + + // W = Wh * 3600 / seconds + id(g_ev_power_avg_w) = wh * 3600.0f / seconds; + + # Period rollover check (day/month/year) for EV totals + - id: ev_rollover_check + mode: restart then: - lambda: |- auto now = id(sntp_time).now(); - if (!now.is_valid()) return; - - if (now.hour != 23 || now.minute != 59) return; - - if (id(ev_last_snapshot_doy) == now.day_of_year) return; - - if (isfinite(id(ev_energy_today_kwh).state)) { - id(ev_energy_yesterday_kwh) = id(ev_energy_today_kwh).state; + if (!now.is_valid()) { + return; } - id(ev_last_snapshot_doy) = now.day_of_year; \ No newline at end of file + const int d = now.day_of_month; + const int m = now.month; + const int y = now.year; + + const float ev_total = id(ev_total_energy_kwh).state; + + // Initialize date markers on first valid time + if (id(g_last_year) == 0) { + id(g_last_day) = d; + id(g_last_month) = m; + id(g_last_year) = y; + + if (!isnan(ev_total)) { + id(g_ev_day_start_kwh) = ev_total; + id(g_ev_month_start_kwh) = ev_total; + id(g_ev_year_start_kwh) = ev_total; + } + return; + } + + // Protect against meter reset / wrap (total < baseline) + if (!isnan(ev_total)) { + if (id(g_ev_day_start_kwh) < 0.0f) id(g_ev_day_start_kwh) = ev_total; + if (id(g_ev_month_start_kwh) < 0.0f) id(g_ev_month_start_kwh) = ev_total; + if (id(g_ev_year_start_kwh) < 0.0f) id(g_ev_year_start_kwh) = ev_total; + + if (ev_total < id(g_ev_day_start_kwh)) id(g_ev_day_start_kwh) = ev_total; + if (ev_total < id(g_ev_month_start_kwh)) id(g_ev_month_start_kwh) = ev_total; + if (ev_total < id(g_ev_year_start_kwh)) id(g_ev_year_start_kwh) = ev_total; + } + + // YEAR rollover + if (y != id(g_last_year)) { + if (!isnan(ev_total) && id(g_ev_year_start_kwh) >= 0.0f) { + float ykwh = ev_total - id(g_ev_year_start_kwh); + if (ykwh < 0.0f) ykwh = 0.0f; + id(g_ev_last_year_kwh) = ykwh; + id(g_ev_year_start_kwh) = ev_total; + } + id(g_last_year) = y; + } + + // MONTH rollover + if (m != id(g_last_month)) { + if (!isnan(ev_total) && id(g_ev_month_start_kwh) >= 0.0f) { + float mkwh = ev_total - id(g_ev_month_start_kwh); + if (mkwh < 0.0f) mkwh = 0.0f; + id(g_ev_last_month_kwh) = mkwh; + id(g_ev_month_start_kwh) = ev_total; + } + id(g_last_month) = m; + } + + // DAY rollover + if (d != id(g_last_day)) { + if (!isnan(ev_total) && id(g_ev_day_start_kwh) >= 0.0f) { + float dkwh = ev_total - id(g_ev_day_start_kwh); + if (dkwh < 0.0f) dkwh = 0.0f; + id(g_ev_yesterday_kwh) = dkwh; + id(g_ev_day_start_kwh) = ev_total; + } + id(g_last_day) = d; + } + +#:########################################################################################:# +# ROLLBACK COPY (v1.1) - PREVIOUS EV SECTION (pulse_meter + daily snapshot + mqtt window) +# NOTE: Commented out. Kept for quick revert if needed. +#:########################################################################################:# +#globals: +# - id: ev_last_total_kwh +# type: float +# restore_value: true +# initial_value: "0.0" +# +# - id: ev_energy_yesterday_kwh +# type: float +# restore_value: true +# initial_value: "0.0" +# +# - id: ev_last_snapshot_doy +# type: int +# restore_value: no +# initial_value: "-1" +# +#sensor: +# - platform: pulse_meter +# id: ev_pulse_pm +# pin: +# number: GPIO2 +# mode: INPUT_PULLUP +# inverted: false +# name: "${switch_1_name} Power Now" +# unit_of_measurement: "W" +# accuracy_decimals: 0 +# internal_filter: ${ev_pulse_filter} +# timeout: ${ev_pulse_timeout} +# filters: +# - multiply: 120.0 +# total: +# id: ev_energy_total_kwh +# name: "${switch_1_name} Energy Cumulative Total" +# unit_of_measurement: "kWh" +# accuracy_decimals: 1 +# filters: +# - multiply: 0.002 +# +#interval: +# - interval: 60s +# then: +# - mqtt.publish: +# topic: "${ev_tasmota_tele_topic}" +# qos: 0 +# retain: false +# payload: !lambda |- +# float current = id(ev_energy_total_kwh).state; +# if (!isfinite(current)) current = id(ev_last_total_kwh); +# float delta_kwh = current - id(ev_last_total_kwh); +# if (delta_kwh < 0.0f || !isfinite(delta_kwh)) delta_kwh = 0.0f; +# float delta_wh = delta_kwh * 1000.0f; +# id(ev_last_total_kwh) = current; +# char buf[64]; +# snprintf(buf, sizeof(buf), "{\"EVChargerWhCount\":%.3f}", delta_wh); +# return std::string(buf); +# +# - interval: 60s +# then: +# - lambda: |- +# auto now = id(sntp_time).now(); +# if (!now.is_valid()) return; +# if (now.hour != 23 || now.minute != 59) return; +# if (id(ev_last_snapshot_doy) == now.day_of_year) return; +# if (isfinite(id(ev_energy_today_kwh).state)) { +# id(ev_energy_yesterday_kwh) = id(ev_energy_today_kwh).state; +# } +# id(ev_last_snapshot_doy) = now.day_of_year; \ No newline at end of file diff --git a/esphome/esp-lounge6chdimmer.yaml b/esphome/esp-lounge6chdimmer.yaml index b692a9f..6a3e0dd 100644 --- a/esphome/esp-lounge6chdimmer.yaml +++ b/esphome/esp-lounge6chdimmer.yaml @@ -1,15 +1,19 @@ #:########################################################################################:# # TITLE: 6 CHANNEL LOUNGE DMX MAINS DIMMER -# zorruno.com layout v1.0 2026 +# zorruno.com layout v1.1 2026 +#:########################################################################################:# +# REPO: +# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-lounge6chdimmer.yaml #:########################################################################################:# # VERSIONS: +# V1.2 2026-03-04 Updated yaml to zorruno layout V1.1 # V1.1 2025-09-13 Extended to 3 modules and installed # V1.0 2025-09-06 First Rev (only tested one module, 2 channels at this stage, and incandescent) #:########################################################################################:# # HARDWARE: # - ESP8266 D1 Mini Pro # - 3 x 2-channel DMX mains dimmer modules (1.2 A per channel) -# Contoller: https://www.aliexpress.com/item/32838426377.html +# Controller: https://www.aliexpress.com/item/32838426377.html # - RS-485 TTL converter (MAX485 style) # RS-485 TTL converter: https://www.aliexpress.com/item/1005005737922222.html # @@ -49,14 +53,20 @@ # https://github.com/andyboeh/esphome-dmx512 #:########################################################################################:# # MQTT COMMANDS: -# +# - None (DMX is driven by HA light entities). #:########################################################################################:# # OFFLINE NOTES: -# - If Home Assistant is offline, lights can still be controlled locally (device continues DMX output) -# - If Wi-Fi is offline, wall switch power-cycling restores dimmer module output state (modules remember) +# a) Home Assistant OFFLINE (WiFi + network still online): +# - Device continues DMX output at last set levels (dimmers remember output). +# +# b) MQTT OFFLINE (or HA/MQTT offline): +# - No MQTT dependency for operation; last levels remain active. +# +# c) Entire WiFi/Network OFFLINE: +# - Accurate time is not needed (SNTP not needed; no schedules/timeclocks). +# - Wall switch power-cycling restores dimmer module output state (modules remember). #:########################################################################################:# - #:########################################################################################:# # SUBSTITUTIONS: Specific device variable substitutions # If NOT using a secrets file, just replace these with the passwords etc (in quotes) @@ -65,20 +75,19 @@ substitutions: # Device Naming device_name: "esp-lounge6chdimmer" friendly_name: "6 Channel Lounge Dimmer" - description_comment: "Lounge 6 Channel Mains Lighting Dimmer (3 x 2 channel modules) controlled using DMX512 protocol with a D1 Mini Pro (Layout V1.0)" + description_comment: "Lounge 6 Channel Mains Lighting Dimmer (3 x 2 channel modules) controlled using DMX512 protocol with a D1 Mini Pro (Layout V1.1)" device_area: "Lounge" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. # Project Naming project_name: "Generic ESP8266.D1 Mini" # Project Details - project_version: "v1.1" # Project V denotes release of yaml file, allowing checking of deployed vs latest version + project_version: "v1.2" # Project V denotes release of yaml file, allowing checking of deployed vs latest version - # Passwords & Secrets + # Passwords and Secrets api_key: !secret esp-api_key # unfortunately you can't use substitutions inside secrets names ota_pass: !secret esp-ota_pass # unfortunately you can't use substitutions inside secrets names static_ip_address: !secret esp-lounge6chdimmer_ip - # 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. + # Layout v1.1 requirement current_ip_address: ${static_ip_address} # Device Settings @@ -110,7 +119,6 @@ substitutions: gamma_output_5: "2.2" gamma_output_6: "2.2" - #:########################################################################################:# # PACKAGES: Included Common Packages # https://esphome.io/components/packages.html @@ -151,7 +159,6 @@ packages: 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 @@ -175,7 +182,6 @@ esphome: id(dmx_ch5_sensor).publish_state(0); id(dmx_ch6_sensor).publish_state(0); - #:########################################################################################:# # ESP PLATFORM AND FRAMEWORK: # https://esphome.io/components/esp8266/ @@ -186,7 +192,6 @@ esp8266: board_flash_mode: dout # Default is dout restore_from_flash: true # restore some values on reboot - #:########################################################################################:# # MDNS: # https://esphome.io/components/mdns.html @@ -194,7 +199,6 @@ esp8266: mdns: disabled: false # Disabling will make the build file smaller (and it is still available via static IP) - #:########################################################################################:# # LOGGING: ESPHome Logging Enable # https://esphome.io/components/logger.html @@ -205,7 +209,6 @@ logger: #esp8266_store_log_strings_in_flash: false #tx_buffer_size: 64 - #:########################################################################################:# # STATUS LED: # https://esphome.io/components/status_led.html @@ -215,7 +218,6 @@ logger: # number: GPIO2 # inverted: true - #:########################################################################################:# # UART COMPONENT: # https://esphome.io/components/uart/ @@ -231,7 +233,6 @@ uart: allow_other_uses: true # For ESPHome >= 2023.12.0 stop_bits: 2 - #:########################################################################################:# # LIGHT COMPONENT: # https://esphome.io/components/light/ @@ -285,7 +286,6 @@ light: gamma_correct: "${gamma_output_6}" restore_mode: RESTORE_DEFAULT_OFF - #:########################################################################################:# # SENSOR COMPONENT: # https://esphome.io/components/sensor/ @@ -382,7 +382,6 @@ sensor: min_value: 0 max_value: 255 - #:########################################################################################:# # EXTERNAL COMPONENTS: # https://github.com/andyboeh/esphome-dmx512 @@ -390,7 +389,6 @@ sensor: external_components: - source: github://andyboeh/esphome-dmx512 - #:########################################################################################:# # DMX512 COMPONENT: # https://github.com/andyboeh/esphome-dmx512 @@ -409,7 +407,6 @@ dmx512: #custom_mab_len: 12 # optional. Mark after Break Length. Default is 12mS #update_interval: 500 # optional. Custom update interval. Default is 500mS - #:########################################################################################:# # OUTPUT COMPONENT: # https://esphome.io/components/output/ diff --git a/esphome/esp-mainbathfancombo.yaml b/esphome/esp-mainbathfancombo.yaml index 83567fe..1631242 100644 --- a/esphome/esp-mainbathfancombo.yaml +++ b/esphome/esp-mainbathfancombo.yaml @@ -1,18 +1,23 @@ -########################################################################################## +#:########################################################################################:# # TITLE: MAIN BATHROOM FAN/HEAT COMBO SWITCH -########################################################################################## +# zorruno.com layout v1.1 2026 +#:########################################################################################:# +# REPO: +# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-mainbathfancombo.yaml +#:########################################################################################:# # VERSIONS: +# V1.6 2026-03-04 Updated yaml to zorruno layout V1.1; adjusted Button 2 click timing for towel rail MQTT reliability # V1.5 2026-02-23 MQTT numeric timer only applies if fan already ON (does not turn fan ON) # V1.4 2026-02-23 Add mm:ss countdown text sensor (eg 22:12) + per-second countdown engine # V1.3 2026-02-23 Add 60-min cap timer + Remaining Minutes sensor (counts down) # V1.2 2026-02-18 Stop delayed-off countdown when Relay_1 turns OFF # V1.1 2025-08-26 Minor Changes (MQTT) # V1.0 2025-06-01 Initial Version -########################################################################################## +#:########################################################################################:# # HARDWARE: -# Zemismart KS-811 Double Push Button -# pinout/schematic https://community.home-assistant.io/t/zemismart-ks-811-working-with-esphome/ -########################################################################################## +# - Zemismart KS-811 Double Push Button +# pinout/schematic https://community.home-assistant.io/t/zemismart-ks-811-working-with-esphome/ +#:########################################################################################:# # OPERATION NOTES: # a) MQTT Local Usage # Send ON to ${mqtt_command_main_topic}/${mqtt_local_device_name} and it will turn the fan ON (60 min cap) @@ -26,32 +31,34 @@ # By default, the fan will be on for 60mins max. Other values can be sent via MQTT/HA # d) Other device control # This device can turn on the heated towel rail via MQTT (doubleclick button 2) -########################################################################################## +#:########################################################################################:# # OFFLINE NOTES: # a) Home Assistant OFFLINE, but Network and MQTT ONLINE -# - The fan can still be controlled with ON, OFF and given a timer value, via MQTT. +# - The fan can still be controlled with ON, OFF and given a timer value, via MQTT. # - The Heated towel rail can also be switched on. # b) HA/MQTT OFFLINE # With no automation controllers, fan and heater can be controlled on/off with the switches. # Auto timers cannot be changed, but the fam will be on for 60mins max. # c) Internet/Wifi OFFLINE # No further changes, SNTP not required as timing functions do not rely on accurate clock. -########################################################################################## +# - Accurate time is NOT needed (SNTP not required) for core operation (no time-of-day schedules) +#:########################################################################################:# -########################################################################################## -# SPECIFIC DEVICE VARIABLE SUBSTITUTIONS: + +#:########################################################################################:# +# 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-mainbathfancombo" friendly_name: "Main Bathroom Fan-Heat Combo Switch (2)" - description_comment: "Main Bathroom Fan/Heat Switch using a Zemismart KS-811 Double Push Button. Extract Fan (1), IR heater (2)" + description_comment: "Main Bathroom Fan/Heat Switch using a Zemismart KS-811 Double Push Button. Extract Fan (1), IR heater (2) (Layout V1.1)" device_area: "Main Bathroom" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. # Project Naming project_name: "Zemismart Technologies.KS-811-2 (Double)" # Project Details - project_version: "v1.5" # Project V denotes release of yaml file, allowing checking of deployed vs latest version + project_version: "v1.6" # Project V denotes release of yaml file, allowing checking of deployed vs latest version entity_prefix: "Main Bathroom" # Simple device name where we want to prefix a sensor or switch, eg "Load" Current. @@ -81,10 +88,11 @@ substitutions: mqtt_remote_device1_command_topic: "${mqtt_command_main_topic}/${mqtt_remote_device1_name}" mqtt_remote_device1_command1: "BOOST" -########################################################################################## + +#:########################################################################################:# # PACKAGES: Included Common Packages # https://esphome.io/components/packages.html -########################################################################################## +#:########################################################################################:# packages: #### WIFI, Network (Static/DHCP/IPV6 etc), Fallback AP, Safemode #### common_wifi: !include @@ -114,20 +122,21 @@ packages: #common_sntp: !include common/sntp_common.yaml #### DIAGNOSTICS Sensors #### - diag_basic: !include common/include_basic_diag_sensors.yaml - diag_more: !include common/include_more_diag_sensors.yaml + 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 + +#:########################################################################################:# +# ESPHOME: # https://esphome.io/components/esphome.html -########################################################################################## +#:########################################################################################:# esphome: - name: ${device_name} - friendly_name: ${friendly_name} - comment: ${description_comment} #Appears on the esphome page in HA - area: ${device_area} + name: "${device_name}" + friendly_name: "${friendly_name}" + comment: "${description_comment}" #Appears on the esphome page in HA + area: "${device_area}" project: name: "${project_name}" version: "${project_version}" @@ -156,29 +165,32 @@ esphome: - lambda: |- id(relay2_forced_relay1) = false; -########################################################################################## -# ESP PLATFORM AND FRAMEWORK + +#:########################################################################################:# +# ESP PLATFORM AND FRAMEWORK: # https://esphome.io/components/esp32.html -########################################################################################## +#:########################################################################################:# esp8266: board: esp01_1m early_pin_init: false # Recommended false where switches are involved. board_flash_mode: dout # Default is dout -########################################################################################## -# LOGGING + +#:########################################################################################:# +# LOGGING: # https://esphome.io/components/logger.html -########################################################################################## +#:########################################################################################:# logger: - level: "${log_level}" # INFO Level suggested, or DEBUG for testing + level: "${log_level}" # INFO Level suggested, or DEBUG for testing #baud_rate: 0 # set to 0 for no logging via UART, needed if you are using it for other serial things (eg PZEM) #esp8266_store_log_strings_in_flash: false #tx_buffer_size: 64 -########################################################################################## -# GLOBAL VARIABLES + +#:########################################################################################:# +# GLOBAL VARIABLES: # https://esphome.io/guides/automations.html?highlight=globals#global-variables -########################################################################################## +#:########################################################################################:# globals: # Tracks if Relay_2 forced Relay_1 ON (so we can decide if Relay_1 should turn OFF when Relay_2 turns OFF) - id: relay2_forced_relay1 @@ -213,20 +225,21 @@ globals: restore_value: no initial_value: "0" -########################################################################################## -# STATUS LED + +#:########################################################################################:# +# STATUS LED: # https://esphome.io/components/status_led.html -########################################################################################## +#:########################################################################################:# # Status LED for KS-811 is gpio2 -########################################################################################## status_led: pin: number: GPIO2 inverted: yes -########################################################################################## -# SENSORS -########################################################################################## + +#:########################################################################################:# +# SENSORS: +#:########################################################################################:# sensor: - platform: template name: "${entity_prefix} Fan Remaining Minutes" @@ -253,10 +266,11 @@ text_sensor: snprintf(buf, sizeof(buf), "%d:%02d", m, s); return std::string(buf); -########################################################################################## -# MQTT COMMANDS + +#:########################################################################################:# +# MQTT: Device-specific MQTT command triggers # This adds device-specific MQTT command triggers to the common MQTT configuration. -########################################################################################## +#:########################################################################################:# mqtt: on_message: # Fan control: ON / OFF / 1..60 minutes (timer only if fan already ON) @@ -307,9 +321,10 @@ mqtt: ESP_LOGW("mqtt", "Ignoring payload '%s' on %s", p.c_str(), "${mqtt_local_command1_topic}"); } -########################################################################################## -# SCRIPTS -########################################################################################## + +#:########################################################################################:# +# SCRIPTS: +#:########################################################################################:# script: # Starts/restarts mm:ss countdown, and optionally turns fan ON (used for MQTT ON / auto-start) - id: relay_1_delayed_off @@ -344,12 +359,12 @@ script: then: - switch.turn_on: Relay_1 -########################################################################################## -# BINARY SENSORS + +#:########################################################################################:# +# BINARY SENSORS: # https://esphome.io/components/binary_sensor/ -########################################################################################## +#:########################################################################################:# # Buttons for KS-811-2 are GPIO16, GPIO05 -########################################################################################## binary_sensor: - platform: gpio pin: @@ -359,7 +374,7 @@ binary_sensor: use_interrupt: false # GPIO16 pin doesn't support interrupts so use polling. Only suppresses a warning. name: "Button 1: Extract Fan" on_press: - - switch.toggle: "Relay_1" + - switch.toggle: Relay_1 - platform: gpio pin: @@ -372,30 +387,30 @@ binary_sensor: - delayed_off: 20ms on_multi_click: - # -- Single click: one quick press & release -- + # Double click: easier to hit; also prevents single-click heater toggle from stealing the first click - timing: - - ON for at most 400ms - - OFF for at least 200ms - then: - - switch.toggle: "Relay_2" - - # -- Double click: two quick presses/releases -- - - timing: - - ON for at most 400ms - - OFF for at most 400ms - - ON for at most 400ms - - OFF for at least 200ms + - ON for at most 500ms + - OFF for at most 900ms + - ON for at most 500ms + - OFF for at least 250ms then: - mqtt.publish: topic: "${mqtt_remote_device1_command_topic}" payload: "${mqtt_remote_device1_command1}" -########################################################################################## -# SWITCH COMPONENT + # Single click: delayed recognition so a (slower) double-click still works reliably + - timing: + - ON for at most 600ms + - OFF for at least 1200ms + then: + - switch.toggle: Relay_2 + + +#:########################################################################################:# +# SWITCH COMPONENT: # https://esphome.io/components/switch/ -########################################################################################## +#:########################################################################################:# # Relays for KS-811-2 are GPIO13, GPIO12 -########################################################################################## switch: - platform: gpio name: "Relay 1: Extract Fan" @@ -478,12 +493,12 @@ switch: then: - switch.turn_off: Relay_1 -########################################################################################## -# INTERVAL COMPONENT + +#:########################################################################################:# +# INTERVAL COMPONENT: # https://esphome.io/components/interval/ -########################################################################################## +#:########################################################################################:# # Assurance: if Relay_2 (Heater) is ever on, check that Relay_1 (Fan) is on also -########################################################################################## interval: - interval: 1s then: diff --git a/esphome/esp-mainpowermonitor.yaml b/esphome/esp-mainpowermonitor.yaml index 0f895d5..1d1e24a 100644 --- a/esphome/esp-mainpowermonitor.yaml +++ b/esphome/esp-mainpowermonitor.yaml @@ -145,15 +145,16 @@ substitutions: mains_daily_min_365_name: "Main Power Daily Energy Min (365d)" # Names - EV Wallcharger - ev_power_name: "EV Wallcharger Power" - ev_charging_name: "EV Wallcharger Charging" - ev_total_energy_name: "EV Wallcharger Total Energy" - ev_today_name: "EV Wallcharger Energy Today" - ev_yesterday_name: "EV Wallcharger Energy Yesterday" - ev_month_name: "EV Wallcharger Energy This Month" - ev_last_month_name: "EV Wallcharger Energy Last Month" - ev_year_name: "EV Wallcharger Energy This Year" - ev_last_year_name: "EV Wallcharger Energy Last Year" + ev_charger_name: "32A Wallcharger" + ev_power_name: "${ev_charger_name} Power" + ev_charging_name: "${ev_charger_name} Charging" + ev_total_energy_name: "${ev_charger_name} Total Energy" + ev_today_name: "${ev_charger_name} Energy Today" + ev_yesterday_name: "${ev_charger_name} Energy Yesterday" + ev_month_name: "${ev_charger_name} Energy This Month" + ev_last_month_name: "${ev_charger_name} Energy Last Month" + ev_year_name: "${ev_charger_name} Energy This Year" + ev_last_year_name: "${ev_charger_name} Energy Last Year" # Names - Environment temp_name: "Main Power Monitor Temperature" @@ -1252,9 +1253,9 @@ script: #:########################################################################################:# # SWITCHES: #:########################################################################################:# -switch: - - platform: restart - name: "Main Power Monitor Restart" +#switch: +# - platform: restart +# name: "Main Power Monitor Restart" #:########################################################################################:# # ROLLBACK COPY (v1.1) - EV POWER SECTION (for quick revert) diff --git a/packages/ev_charging.yaml b/packages/ev_charging.yaml index e3dadc4..208ad77 100644 --- a/packages/ev_charging.yaml +++ b/packages/ev_charging.yaml @@ -1,25 +1,25 @@ -mqtt: - sensor: - - unique_id: 32a_ev_wallcharger_power - name: "32A EV Wallcharger Power" - device_class: power - unit_of_measurement: kW - state_topic: "stat/tasmo-wemosd1-7280-powermon-1/EnergyMeterCount" - value_template: "{{ value_json.EVChargerWhCount * 2 * 60 / 1000 }}" - - unique_id: 16a_ev_wallcharger_power - name: "16A EV Wallcharger Power" - device_class: power - unit_of_measurement: kW - state_topic: "stat/tasmo-s4chan-7594-garage-1/EnergyMeterCount" - value_template: "{{ value_json.EVChargerWhCount * 2 * 60 / 1000 }}" +#mqtt: +# sensor: +# - unique_id: 32a_ev_wallcharger_power +# name: "32A EV Wallcharger Power" +# device_class: power +# unit_of_measurement: kW +# state_topic: "stat/tasmo-wemosd1-7280-powermon-1/EnergyMeterCount" +# value_template: "{{ value_json.EVChargerWhCount * 2 * 60 / 1000 }}" +# - unique_id: 16a_ev_wallcharger_power +# name: "16A EV Wallcharger Power" +# device_class: power +# unit_of_measurement: kW +# state_topic: "stat/tasmo-s4chan-7594-garage-1/EnergyMeterCount" +# value_template: "{{ value_json.EVChargerWhCount * 2 * 60 / 1000 }}" # Use the Riemann sum integral to calculate energy in kWh # from the contimuous power measuring sensors sensor: - platform: integration unique_id: 32a_ev_wallcharger_power_total - name: "32A EV Wallcharger Power Total" - source: sensor.32a_ev_wallcharger_power + name: "EV 32A Wallcharger Power Total (Integral)" + source: sensor.main_house_3_phase_power_monitor_32a_wallcharger_power #device_class: energy #unit_of_measurement: Wh #unit_time: h @@ -27,22 +27,22 @@ sensor: round: 0 # Only need 0 decimals accuracy - platform: integration unique_id: 16a_ev_wallcharger_power_total - name: "16A EV Wallcharger Power Total" - source: sensor.16a_ev_wallcharger_power + name: "EV 16A EV Wallcharger Power Total (Integral)" + source: sensor.garage_db_control_16a_wallcharger_power #device_class: energy #unit_of_measurement: Wh #unit_time: h #unit_prefix: k round: 0 - - platform: filter - name: "32A EV Charger Power 1m Avg" - unique_id: ev_charger_32a_power_1m_avg - entity_id: sensor.main_house_3_phase_power_monitor_ev_wallcharger_power - filters: - - filter: time_simple_moving_average - window_size: "00:01" - precision: 3 +# - platform: filter +# name: "32A EV Charger Power 1m Avg" +# unique_id: ev_charger_32a_power_1m_avg +# entity_id: sensor.main_house_3_phase_power_monitor_ev_wallcharger_power +# filters: +# - filter: time_simple_moving_average +# window_size: "00:01" +# precision: 3 input_select: leaf_ev_charging_mode: @@ -71,36 +71,35 @@ automation: "1hr top-up now":"boost", "Charge now until full":"now-full", "Stop charging now":"stop" } %} {% set dta = trigger.to_state.state %} {{ mapping[dta] }} ' - -template: - - trigger: - - platform: time_pattern - seconds: "/10" - - platform: homeassistant - event: start - binary_sensor: - - name: "32A EV Charger Charging" - unique_id: ev_charger_32a_charging - icon: mdi:car-electric - state: >- - {{ states('sensor.32a_ev_charger_power_1m_avg') | float(0) > 0.1 }} - delay_on: - seconds: 60 - delay_off: - seconds: 60 - - - name: "16A EV Charger Charging" - unique_id: ev_charger_16a_charging - icon: mdi:car-electric - state: >- - {{ (states('sensor.16a_ev_wallcharger_power') | float(0)) > 0.1 }} - delay_on: - seconds: 60 - delay_off: - seconds: 60 - - - binary_sensor: - - name: 32A EV Charger Charging UI - unique_id: ev_charger_charging_ui - state: "{{ is_state('sensor.32a_ev_charger_charging', 'on') }}" - icon: mdi:car-electric +#template: +# - trigger: +# - platform: time_pattern +# seconds: "/10" +# - platform: homeassistant +# event: start +# binary_sensor: +# - name: "32A EV Charger Charging" +# unique_id: ev_charger_32a_charging +# icon: mdi:car-electric +# state: >- +# {{ states('sensor.32a_ev_charger_power_1m_avg') | float(0) > 0.1 }} +# delay_on: +# seconds: 60 +# delay_off: +# seconds: 60 +# +# - name: "16A EV Charger Charging" +# unique_id: ev_charger_16a_charging +# icon: mdi:car-electric +# state: >- +# {{ (states('sensor.16a_ev_wallcharger_power') | float(0)) > 0.1 }} +# delay_on: +# seconds: 60 +# delay_off: +# seconds: 60 +# +# - binary_sensor: +# - name: 32A EV Charger Charging UI +# unique_id: ev_charger_charging_ui +# state: "{{ is_state('sensor.32a_ev_charger_charging', 'on') }}" +# icon: mdi:car-electric