diff --git a/esphome/common/athompowermonv1_common.yaml b/esphome/common/athompowermonv1_common.yaml new file mode 100644 index 0000000..5885f0f --- /dev/null +++ b/esphome/common/athompowermonv1_common.yaml @@ -0,0 +1,142 @@ +########################################################################################## +# GLOBAL VARIABLES +# https://esphome.io/guides/automations.html?highlight=globals#global-variables +########################################################################################## +globals: + #################################################### + # total_energy: cumulative power + # Not restored, so each boot starts fresh at 0. + #################################################### + - id: total_energy + type: float + restore_value: yes + initial_value: '0.0' + +########################################################################################## +# GENERAL COMMON SENSORS +# https://esphome.io/components/sensor/ +########################################################################################## +sensor: + ############################################# + # CSE7766 POWER SENSOR + # https://esphome.io/components/sensor/cse7766.html + ############################################# + - platform: hlw8012 + id: athom_hlw8012 + sel_pin: + number: GPIO12 + inverted: True + cf_pin: GPIO4 + cf1_pin: GPIO5 + voltage_divider: 780 + + current: + name: "Current" + id: current + unit_of_measurement: A + accuracy_decimals: 2 + icon: mdi:current-ac + filters: + - calibrate_linear: + - 0.0000 -> 0.0110 # Relay off no load + - 0.0097 -> 0.0260 # Relay on no load + - 0.9270 -> 0.7570 + - 2.0133 -> 1.6330 + - 2.9307 -> 2.3750 + - 5.4848 -> 4.4210 + - 8.4308 -> 6.8330 + - 9.9171 -> 7.9830 + # Normalize for plug load + - lambda: if (x < 0.0260) return 0; else return (x - 0.0260); + on_value_range: + - above: ${local_current_limit} + then: + - switch.turn_off: relay + + voltage: + name: "Voltage" + id: voltage + unit_of_measurement: V + accuracy_decimals: 1 + icon: mdi:sine-wave + filters: + - skip_initial: 2 + + power: + name: "Power" + id: power_sensor + unit_of_measurement: W + accuracy_decimals: 1 + icon: mdi:power + filters: + - calibrate_linear: + - 0.0000 -> 0.5900 # Relay off no load + - 0.0000 -> 1.5600 # Relay on no load + - 198.5129 -> 87.8300 + - 434.2469 -> 189.5000 + - 628.6241 -> 273.9000 + - 1067.0067 -> 460.1000 + - 1619.8098 -> 699.2000 + - 2043.0282 -> 885.0000 + # Normalize for plug load + - lambda: if (x < 1.5600) return 0; else return (x - 1.5600); + change_mode_every: 1 + update_interval: 5s + + # Shows the Energy kWh since the device was last started + energy: + name: "Energy (Since Restart)" + id: energy + icon: mdi:lightning-bolt + unit_of_measurement: kWh + accuracy_decimals: 3 + filters: + # Multiplication factor from W to kW is 0.001 + - multiply: 0.001 + on_value: + then: + - lambda: |- + static float previous_energy_value = 0.0; + float current_energy_value = id(energy).state; + id(total_energy) += current_energy_value - previous_energy_value; + previous_energy_value = current_energy_value; + id(total_energy_sensor).update(); + # internal: ${hide_energy_sensor} + + ############################################# + # Total Energy (All Time) + ############################################# + - platform: template + name: "Total Energy" + id: total_energy_sensor + unit_of_measurement: kWh + device_class: "energy" + state_class: "total_increasing" + icon: mdi:lightning-bolt + accuracy_decimals: 3 + lambda: |- + return id(total_energy); + update_interval: "60s" + + ############################################# + # Total Daily Energy + # https://esphome.io/components/sensor/total_daily_energy.html + ############################################# + - platform: total_daily_energy + name: "Total Daily Energy" + restore: true + power_id: power_sensor + unit_of_measurement: kWh + icon: mdi:hours-24 + accuracy_decimals: 3 + filters: + - multiply: 0.001 + +########################################################################################## +# STATUS LED +# https://esphome.io/components/status_led.html +########################################################################################## +status_led: + pin: + number: GPIO13 + inverted: True \ No newline at end of file diff --git a/esphome/common/athompowermonv3_common.yaml b/esphome/common/athompowermonv3_common.yaml new file mode 100644 index 0000000..a7b015b --- /dev/null +++ b/esphome/common/athompowermonv3_common.yaml @@ -0,0 +1,127 @@ +########################################################################################## +# GLOBAL VARIABLES +# https://esphome.io/guides/automations.html?highlight=globals#global-variables +########################################################################################## +globals: + #################################################### + # total_energy: cumulative power + # Not restored, so each boot starts fresh at 0. + #################################################### + - id: total_energy + type: float + restore_value: yes + initial_value: '0.0' + +########################################################################################## +# GENERAL COMMON SENSORS +# https://esphome.io/components/sensor/ +########################################################################################## +sensor: + ############################################# + # CSE7766 POWER SENSOR + # https://esphome.io/components/sensor/cse7766.html + ############################################# + - platform: cse7766 + id: athom_cse7766 + current: + name: "Current" + icon: mdi:current-ac + filters: + - throttle_average: "10s" + - lambda: if (x < 0.060) return 0.0; else return x; #For the chip will report less than 3w power when no load is connected + on_value_range: + - above: "${local_current_limit}" + then: + - switch.turn_off: relay + voltage: + name: "Voltage" + icon: mdi:sine-wave + filters: + - throttle_average: "10s" + power: + name: "Power" + id: power_sensor + icon: mdi:power + filters: + - throttle_average: "10s" + - lambda: if (x < 3.0) return 0.0; else return x; #For the chip will report less than 3w power when no load is connected + energy: + name: "Energy" + id: energy + icon: mdi:lightning-bolt + unit_of_measurement: kWh + filters: + - throttle: "10s" + # Multiplication factor from W to kW is 0.001 + - multiply: 0.001 + on_value: + then: + - lambda: |- + static float previous_energy_value = 0.0; + float current_energy_value = id(energy).state; + id(total_energy) += current_energy_value - previous_energy_value; + previous_energy_value = current_energy_value; + id(total_energy_sensor).update(); + apparent_power: + name: "Apparent Power" + icon: mdi:power + filters: + - throttle_average: "10s" + reactive_power: + name: "Reactive Power" + icon: mdi:flash + filters: + - throttle_average: "10s" + power_factor: + name: "Power Factor" + icon: mdi:percent-outline + filters: + - throttle_average: "10s" + + ############################################# + # Total Energy (All Time) + ############################################# + - platform: template + name: "Total Energy" + id: total_energy_sensor + unit_of_measurement: kWh + device_class: "energy" + state_class: "total_increasing" + icon: mdi:lightning-bolt + accuracy_decimals: 3 + lambda: |- + return id(total_energy); + update_interval: "60s" + + ############################################# + # Total Daily Energy + # https://esphome.io/components/sensor/total_daily_energy.html + ############################################# + - platform: total_daily_energy + name: "Total Daily Energy" + restore: true + power_id: power_sensor + unit_of_measurement: kWh + icon: mdi:hours-24 + accuracy_decimals: 3 + filters: + - multiply: 0.001 +########################################################################################## +# STATUS LED +# https://esphome.io/components/status_led.html +########################################################################################## +status_led: + pin: + number: GPIO06 + inverted: False + +########################################################################################## +# UART Bus +# https://esphome.io/components/uart.html +########################################################################################## +uart: + rx_pin: GPIO20 + baud_rate: 4800 + data_bits: 8 + stop_bits: 1 + parity: EVEN \ No newline at end of file diff --git a/esphome/common/network_common.yaml b/esphome/common/network_common.yaml index f718f2e..0796710 100644 --- a/esphome/common/network_common.yaml +++ b/esphome/common/network_common.yaml @@ -37,6 +37,8 @@ substitutions: wifi: ssid: ${wifi_ssid} password: ${wifi_password} + #enable_rrm: true # (ESP32 only) enable 802.11k Radio Resource Management + #enable_btm: true # (ESP32 only) enable 802.11v BSS Transition Management #power_save_mode: LIGHT # https://esphome.io/components/wifi.html#wifi-power-save-mode manual_ip: # optional static IP address static_ip: ${local_static_ip_address} diff --git a/esphome/common/sensors_common copy.yaml b/esphome/common/sensors_common copy.yaml deleted file mode 100644 index ed0de57..0000000 --- a/esphome/common/sensors_common copy.yaml +++ /dev/null @@ -1,111 +0,0 @@ -############################################# -# GENERAL COMMON SENSORS -# https://esphome.io/components/sensor/ -############################################# -sensor: - - platform: uptime # Uptime for this device in seconds - name: "Uptime (s):" - update_interval: ${local_update_interval} - id: uptime_sensor - entity_category: "diagnostic" - - platform: wifi_signal # Wifi Strength - name: "Wifi (dB):" - id: wifi_signal_db - update_interval: ${local_update_interval} - entity_category: "diagnostic" - - platform: copy # Reports the WiFi signal strength in % - source_id: wifi_signal_db - name: "WiFi (%):" - filters: - - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0); - unit_of_measurement: "% Max" - entity_category: "diagnostic" - device_class: "" - -binary_sensor: - - platform: status - name: "Network Status" - icon: mdi:check-network-outline - entity_category: diagnostic - -############################################# -# Text Sensors -# https://esphome.io/components/text_sensor/index.html -############################################# -text_sensor: - ###################################################### - # General ESPHome Info - ###################################################### - - platform: version - name: "Version:" - entity_category: "diagnostic" - - platform: wifi_info - ip_address: - icon: mdi:ip-network - entity_category: diagnostic - name: "IP Address:" - ssid: - name: "Connected SSID" - icon: mdi:wifi-strength-2 - entity_category: diagnostic - mac_address: - name: "MAC Address:" - icon: mdi:network-pos - entity_category: diagnostic - - platform: uptime # Uptime for this device human readable - name: "Uptime:" - icon: mdi:clock-start - update_interval: ${local_update_interval} - entity_category: "diagnostic" - - - ###################################################### - # Creates a sensor showing when the device was last restarted - # Uptime template sensor, and SNTP are needed - ###################################################### - #- platform: template - # name: ${local_friendly_name} Last Restart - # id: device_last_restart - # icon: mdi:clock - # entity_category: diagnostic - # #device_class: timestamp - - ###################################################### - # Creates a sensor of the uptime of the device, in formatted days, hours, minutes and seconds - ###################################################### - - platform: template - name: "Uptime (Days)" - entity_category: diagnostic - lambda: |- - int seconds = (id(uptime_sensor).state); - int days = seconds / (24 * 3600); - seconds = seconds % (24 * 3600); - int hours = seconds / 3600; - seconds = seconds % 3600; - int minutes = seconds / 60; - seconds = seconds % 60; - if ( days > 3650 ) { - return { "Starting up" }; - } else if ( days ) { - return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() }; - } else if ( hours ) { - return { (String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() }; - } else if ( minutes ) { - return { (String(minutes) +"m "+ String(seconds) +"s").c_str() }; - } else { - return { (String(seconds) +"s").c_str() }; - } - icon: mdi:clock-start - -button: - - platform: safe_mode - name: "Safe Mode Restart: ${local_friendly_name}" - entity_category: "diagnostic" - - platform: restart - name: "Restart: ${local_friendly_name}" - entity_category: "diagnostic" - disabled_by_default: true - - platform: factory_reset - name: "FACTORY RESET: ${local_friendly_name}" - entity_category: "diagnostic" - disabled_by_default: true diff --git a/esphome/esp-downstbathtowelrail.yaml b/esphome/esp-downstbathtowelrail.yaml index 63774b1..54357ac 100644 --- a/esphome/esp-downstbathtowelrail.yaml +++ b/esphome/esp-downstbathtowelrail.yaml @@ -3,6 +3,7 @@ # DOWNSTAIRS BATHROOM HEATED TOWEL RAIL # Controlled by a Sonoff Basic # +# V2.2 2025-06-14 Fixes to offline time when sntp/network unavailable # V2.1 2025-06-12 Added select and button to chose modes, added countdown & startup to boost # V2.0 2025-06-05 YAML Tidyups # V1.1 2025-04-12 Fixes to timers and offline modes @@ -35,6 +36,12 @@ # mqtt_timer_topic/operation/TIMER : Device will obey timer settings # mqtt_timer_topic/operation/BOOST : Turn on for (boost_duration) minutes then BOOST (also on startup) # +# operation_mode: +# 0 = OFF +# 1 = ON +# 2 = TIMER +# 3 = BOOST +# ########################################################################################## ########################################################################################## @@ -46,12 +53,12 @@ substitutions: # Device Naming device_name: "esp-downstbathtowelrail" friendly_name: "Downstairs Bathroom Towelrail" - description_comment: "Sonoff Basic controlling ON/OFF/Timer for the Heated Towel Rail in the Downstairs Bathroom" + description_comment: "Heated Towel Rail, Downstairs Bathroom :: Sonoff Basic" device_area: "Downstairs Bathroom" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. # Project Naming project_name: "Sonoff Technologies.Sonoff Basic V1" # Project Details - project_version: "v2.1" # Project V denotes release of yaml file, allowing checking of deployed vs latest version + project_version: "v2.2" # 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 @@ -596,23 +603,14 @@ interval: - interval: "1min" # Must be 1min as this is used to calculate times then: - lambda: |- - // Do we have correct time from SNTP? If not... - if (!id(time_sync).has_state()) { - // Set minutes since midnight - id(current_mins) = id(current_mins) +1 ; - - // wrap around at 1440 => next day - if (id(current_mins) >= 1440) { - id(current_mins) = 0; - } - - // If we do have proper SNMP time... + if (!id(sntp_time).now().is_valid()) { + id(current_mins) += 1; + if (id(current_mins) >= 1440) id(current_mins) = 0; } else { - // Use real time from SNTP - auto now = id(sntp_time).now(); - id(current_mins) = now.hour * 60 + now.minute; - } + auto now = id(sntp_time).now(); + id(current_mins) = now.hour * 60 + now.minute; + } // operation_mode: // 0 = OFF diff --git a/esphome/esp-downstdishwasher.yaml b/esphome/esp-downstdishwasher.yaml index 4d38599..ebc29f7 100644 --- a/esphome/esp-downstdishwasher.yaml +++ b/esphome/esp-downstdishwasher.yaml @@ -1,14 +1,15 @@ -############################################# -############################################# -# Athom Smart Plug Power Monitor ESP32-C3 +########################################################################################## +########################################################################################## +# DOWNSTAIRS DISHWASHER POWER MONITOR +# Controlled by a Athom Smart Plug V3 +# +# dashboard_import: +# package_import_url: github://athom-tech/esp32-configs/athom-smart-plug.yaml +# +# V1.1 2025-06-12 Tidyups and packages added # -# based on https://github.com/athom-tech/esp32-configs/blob/main/athom-smart-plug.yaml -# -# SUMMARY -# Smart plug with power monitoring. -# -############################################# -############################################# +########################################################################################## +########################################################################################## ############################################# @@ -16,21 +17,27 @@ # If NOT using a secrets file, just replace these with the passwords etc (in quotes) ############################################# substitutions: - devicename: "esp-downstdishwasher" + # Device Naming + device_name: "esp-downstdishwasher" friendly_name: "Downstairs Dishwasher Power" - description_comment: "Downstairs Dishwasher Power, Athom Smart Plug Power Monitor" - room: "Downstairs Kitchen" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. + description_comment: "Downstairs Dishwasher Power :: Athom Smart Plug Power V3" + device_area: "Downstairs Kitchen" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. + + # Project Naming + project_name: "Athom Technology.Smart Plug V3" # Project Details + project_version: "v1.1" # Project V denotes release of yaml file, allowing checking of deployed vs latest version + + # Passwords api_key: !secret esp-downstdishwasher_api_key # unfortunately you can't use substitutions inside secrets names ota_pass: !secret esp-downstdishwasher_ota_pass # unfortunately you can't use substitutions inside secrets names static_ip_address: !secret esp-downstdishwasher_ip + + # Device Settings log_level: "INFO" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE update_interval: "60s" # update time for for general sensors etc - ############################################# - # SPECIFIC PROJECT VARIABLE SUBSTITUTIONS - ############################################# - project_name: "Athom Technology.Smart Plug V3" # Project Details - project_version: "v1.0.7" # Project V denotes release of yaml file, allowing checking of deployed vs latest version + # 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. ############################################# @@ -41,33 +48,41 @@ packages: common_wifi: !include file: common/network_common.yaml vars: - local_static_ip_address: ${static_ip_address} - local_ota_pass: ${ota_pass} + local_static_ip_address: "${static_ip_address}" + local_ota_pass: "${ota_pass}" common_api: !include file: common/api_common.yaml vars: - local_api_key: ${api_key} -# common_webportal: !include -# file: common/webportal_common.yaml - common_mqtt: !include - file: common/mqtt_common.yaml + local_api_key: "${api_key}" common_sntp: !include file: common/sntp_common.yaml common_general_sensors: !include file: common/sensors_common.yaml vars: - local_friendly_name: ${friendly_name} - local_update_interval: ${update_interval} + local_friendly_name: "${friendly_name}" + local_update_interval: "${update_interval}" + + # Web and MQTT Packages + #common_webportal: !include + # file: common/webportal_common.yaml + common_mqtt: !include + file: common/mqtt_common.yaml + + # Device Specific included packages + common_athompowermonV3: !include + file: common/athompowermonv3_common.yaml + vars: + local_current_limit: "${current_limit}" ############################################# # ESPHome # https://esphome.io/components/esphome.html ############################################# esphome: - name: ${devicename} - friendly_name: ${friendly_name} - comment: ${description_comment} #Appears on the esphome page in HA - area: "${room}" + name: "${device_name}" + friendly_name: "${friendly_name}" + comment: "${description_comment}" #Appears on the esphome page in HA + area: "${device_area}" name_add_mac_suffix: False min_version: 2024.6.0 project: @@ -87,41 +102,29 @@ esp32: flash_size: 4MB variant: ESP32C3 framework: - type: arduino - version: recommended + type: esp-idf # "esp-idf" OR "arduino". Suggested ESP-IDF Framework, or Plug Out the UART Cable Might Cause ESP32 Hang. + version: recommended # recommended, latest or dev preferences: flash_write_interval: 5min +esp32_improv: + authorizer: none + ############################################# # ESPHome Logging Enable # 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 -esp32_improv: - authorizer: none - -dashboard_import: - package_import_url: github://athom-tech/esp32-configs/athom-smart-plug.yaml - -uart: - rx_pin: GPIO20 - baud_rate: 4800 - data_bits: 8 - stop_bits: 1 - parity: EVEN - -globals: - - id: total_energy - type: float - restore_value: yes - initial_value: '0.0' - +########################################################################################## +# BINARY SENSORS +# https://esphome.io/components/binary_sensor/ +########################################################################################## binary_sensor: - platform: gpio pin: @@ -139,9 +142,10 @@ binary_sensor: lambda: |- return id(relay).state; -status_led: - pin: GPIO06 - +################################################################################################# +# SWITCH COMPONENT +# https://esphome.io/components/switch/ +################################################################################################# switch: - platform: gpio name: "Power Output" @@ -149,94 +153,9 @@ switch: id: relay restore_mode: RESTORE_DEFAULT_OFF # Ensures the relay is restored (or off) at boot #internal: true # Hides the switch from Home Assistant - icon: "mdi:power-socket-au" + icon: "${relay_icon}" -sensor: - - platform: cse7766 - id: athom_cse7766 - - current: - name: "Current" - icon: mdi:current-ac - filters: - - throttle_average: ${update_interval} - - lambda: if (x < 0.060) return 0.0; else return x; #For the chip will report less than 3w power when no load is connected - on_value_range: - - above: ${current_limit} - then: - - switch.turn_off: relay - - voltage: - name: "Voltage" - icon: mdi:sine-wave - filters: - - throttle_average: ${update_interval} - - power: - name: "Power" - id: power_sensor - icon: mdi:power - filters: - - throttle_average: ${update_interval} - - lambda: if (x < 3.0) return 0.0; else return x; #For the chip will report less than 3w power when no load is connected - - energy: - name: "Energy" - id: energy - icon: mdi:lightning-bolt - unit_of_measurement: kWh - filters: - - throttle: ${update_interval} - # Multiplication factor from W to kW is 0.001 - - multiply: 0.001 - on_value: - then: - - lambda: |- - static float previous_energy_value = 0.0; - float current_energy_value = id(energy).state; - id(total_energy) += current_energy_value - previous_energy_value; - previous_energy_value = current_energy_value; - id(total_energy_sensor).update(); - - apparent_power: - name: "Apparent Power" - icon: mdi:power - filters: - - throttle_average: ${update_interval} - - reactive_power: - name: "Reactive Power" - icon: mdi:flash - filters: - - throttle_average: ${update_interval} - - power_factor: - name: "Power Factor" - icon: mdi:percent-outline - filters: - - throttle_average: ${update_interval} - - - platform: template - name: "Total Energy" - id: total_energy_sensor - unit_of_measurement: kWh - device_class: "energy" - state_class: "total_increasing" - icon: mdi:lightning-bolt - accuracy_decimals: 3 - lambda: |- - return id(total_energy); - update_interval: ${update_interval} - - - platform: total_daily_energy - name: "Total Daily Energy" - restore: true - power_id: power_sensor - unit_of_measurement: kWh - icon: mdi:hours-24 - accuracy_decimals: 3 - filters: - - multiply: 0.001 + diff --git a/esphome/esp-maindishwasherpower.yaml b/esphome/esp-maindishwasherpower.yaml index 1ad8e17..f91162a 100644 --- a/esphome/esp-maindishwasherpower.yaml +++ b/esphome/esp-maindishwasherpower.yaml @@ -4,6 +4,7 @@ # Controlled by a Athom Smart Plug V1 # package_import_url: github://athom-tech/athom-configs/athom-smart-plug.yaml # +# V1.1 2025-06-12 package added for energy entities # V1.0 2025-06-10 YAML Tidyups # ########################################################################################## @@ -17,12 +18,12 @@ substitutions: # Device Naming device_name: "esp-maindishwasherpower" friendly_name: "Main Dishwasher Power" - description_comment: "Main Dishwasher Power, Athom Smart Plug V1 Power Monitor" + description_comment: "Main Dishwasher Power Monitor :: Athom Smart Plug Power Monitor V1" device_area: "Kitchen" # 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_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 @@ -51,10 +52,6 @@ packages: file: common/api_common.yaml vars: local_api_key: "${api_key}" -# common_webportal: !include -# file: common/webportal_common.yaml -# common_mqtt: !include -# file: common/mqtt_common.yaml common_sntp: !include file: common/sntp_common.yaml common_general_sensors: !include @@ -63,6 +60,20 @@ packages: local_friendly_name: "${friendly_name}" local_update_interval: "${update_interval}" + # Web and MQTT Packages + #common_webportal: !include + # file: common/webportal_common.yaml + #common_mqtt: !include + # file: common/mqtt_common.yaml + + + # Device Specific included packages + common_athompowermonV1: !include + file: common/athompowermonv1_common.yaml + vars: + local_friendly_name: "${friendly_name}" + local_current_limit: "${current_limit}" + ########################################################################################## # ESPHome # https://esphome.io/components/esphome.html @@ -109,32 +120,6 @@ logger: #esp8266_store_log_strings_in_flash: false #tx_buffer_size: 64 -########################################################################################## -# GLOBAL VARIABLES -# https://esphome.io/guides/automations.html?highlight=globals#global-variables -########################################################################################## -globals: - # Tracks the time (in seconds from midnight) at the previous boot - - id: last_boot_time_s - type: int - restore_value: true - initial_value: "0" - - # Counts how many consecutive boots have occurred (within X seconds) - - id: boot_count - type: int - restore_value: true - initial_value: "0" - - #################################################### - # total_energy: cumulative power - # Restored, so keeps counting up. - #################################################### - - id: total_energy - type: float - restore_value: yes - initial_value: "0.0" - ########################################################################################## # STATUS LED # https://esphome.io/components/status_led.html @@ -170,123 +155,6 @@ binary_sensor: lambda: |- return id(relay).state; -########################################################################################## -# Sensors -# https://esphome.io/components/text_sensor/index.html -########################################################################################## -sensor: - - platform: template - name: "Total Energy" - id: total_energy_sensor - unit_of_measurement: kWh - device_class: "energy" - state_class: "total_increasing" - icon: mdi:lightning-bolt - accuracy_decimals: 3 - lambda: |- - return id(total_energy); - update_interval: "${update_interval}" - - ############################################# - # CSE7766 POWER SENSOR - # https://esphome.io/components/sensor/cse7766.html - ############################################# - - platform: hlw8012 - id: athom_hlw8012 - sel_pin: - number: GPIO12 - inverted: True - cf_pin: GPIO4 - cf1_pin: GPIO5 - voltage_divider: 780 - - current: - name: "Current" - id: current - unit_of_measurement: A - accuracy_decimals: 2 - icon: mdi:current-ac - filters: - - calibrate_linear: - - 0.0000 -> 0.0110 # Relay off no load - - 0.0097 -> 0.0260 # Relay on no load - - 0.9270 -> 0.7570 - - 2.0133 -> 1.6330 - - 2.9307 -> 2.3750 - - 5.4848 -> 4.4210 - - 8.4308 -> 6.8330 - - 9.9171 -> 7.9830 - # Normalize for plug load - - lambda: if (x < 0.0260) return 0; else return (x - 0.0260); - on_value_range: - - above: ${current_limit} - then: - - switch.turn_off: relay - - voltage: - name: "Voltage" - id: voltage - unit_of_measurement: V - accuracy_decimals: 1 - icon: mdi:sine-wave - filters: - - skip_initial: 2 - - power: - name: "Power" - id: power_sensor - unit_of_measurement: W - accuracy_decimals: 1 - icon: mdi:power - filters: - - calibrate_linear: - - 0.0000 -> 0.5900 # Relay off no load - - 0.0000 -> 1.5600 # Relay on no load - - 198.5129 -> 87.8300 - - 434.2469 -> 189.5000 - - 628.6241 -> 273.9000 - - 1067.0067 -> 460.1000 - - 1619.8098 -> 699.2000 - - 2043.0282 -> 885.0000 - # Normalize for plug load - - lambda: if (x < 1.5600) return 0; else return (x - 1.5600); - change_mode_every: 1 - update_interval: 5s - - # Shows the Energy kWh since the device was last started - energy: - name: "Energy (Since Restart)" - id: energy - icon: mdi:lightning-bolt - unit_of_measurement: kWh - accuracy_decimals: 3 - filters: - # Multiplication factor from W to kW is 0.001 - - multiply: 0.001 - on_value: - then: - - lambda: |- - static float previous_energy_value = 0.0; - float current_energy_value = id(energy).state; - id(total_energy) += current_energy_value - previous_energy_value; - previous_energy_value = current_energy_value; - id(total_energy_sensor).update(); - # internal: ${hide_energy_sensor} - - ############################################# - # Total Daily Energy - # https://esphome.io/components/sensor/total_daily_energy.html - ############################################# - - platform: total_daily_energy - name: "Total Daily Energy" - restore: true - power_id: power_sensor - unit_of_measurement: kWh - icon: mdi:hours-24 - accuracy_decimals: 3 - filters: - - multiply: 0.001 - ################################################################################################# # SWITCH COMPONENT # https://esphome.io/components/switch/ diff --git a/esphome/esp-masterbathtowelrail.yaml b/esphome/esp-masterbathtowelrail.yaml index f7c828c..07dacb2 100644 --- a/esphome/esp-masterbathtowelrail.yaml +++ b/esphome/esp-masterbathtowelrail.yaml @@ -3,6 +3,7 @@ # MASTER BATHROOM HEATED TOWEL RAIL # Controlled by a Sonoff Basic # +# V2.2 2025-06-14 Fixes to offline time when sntp/network unavailable # V2.1 2025-06-12 Added select and button to chose modes, added countdown & startup to boost # V2.0 2025-06-05 YAML Tidyups # V1.1 2025-04-12 Fixes to timers and offline modes @@ -35,6 +36,12 @@ # mqtt_timer_topic/operation/TIMER : Device will obey timer settings # mqtt_timer_topic/operation/BOOST : Turn on for (boost_duration) minutes then BOOST (also on startup) # +# operation_mode: +# 0 = OFF +# 1 = ON +# 2 = TIMER +# 3 = BOOST +# ########################################################################################## ########################################################################################## @@ -68,7 +75,7 @@ substitutions: morning_on_default: "300" # Default in minutes from midnight. Default 05:00 => 300 morning_off_default: "420" # Default in minutes from midnight. Default 07:00 => 420 evening_on_default: "1260" # Default in minutes from midnight. Default 21:00 => 1260 - evening_off_default: "1440" # Default in minutes from midnight. Default 24:00 => 1440 => 1440 is midnight + evening_off_default: "1439" # Default in minutes from midnight. Default 23:59 => 1439 => 1440 is midnight ############################################# # Included Common Packages @@ -596,23 +603,14 @@ interval: - interval: "1min" # Must be 1min as this is used to calculate times then: - lambda: |- - // Do we have correct time from SNTP? If not... - if (!id(time_sync).has_state()) { - // Set minutes since midnight - id(current_mins) = id(current_mins) +1 ; - - // wrap around at 1440 => next day - if (id(current_mins) >= 1440) { - id(current_mins) = 0; - } - - // If we do have proper SNMP time... + if (!id(sntp_time).now().is_valid()) { + id(current_mins) += 1; + if (id(current_mins) >= 1440) id(current_mins) = 0; } else { - // Use real time from SNTP - auto now = id(sntp_time).now(); - id(current_mins) = now.hour * 60 + now.minute; - } + auto now = id(sntp_time).now(); + id(current_mins) = now.hour * 60 + now.minute; + } // operation_mode: // 0 = OFF diff --git a/esphome/esp-poollightpower.yaml b/esphome/esp-poollightpower.yaml index ac10728..2e80269 100644 --- a/esphome/esp-poollightpower.yaml +++ b/esphome/esp-poollightpower.yaml @@ -4,44 +4,46 @@ # Controlled by a Athom Smart Plug V1 # package_import_url: github://athom-tech/athom-configs/athom-smart-plug.yaml # +# V2.2 2025-06-14 Fixes to offline time when sntp/network unavailable +# V2.1 2025-06-12 Added select and button to chose modes, added countdown & startup to boost # V2.0 2025-06-05 YAML Tidyups # # INSTRUCTIONS -# - It allows a device to work in a standalone operation -# - On startup, it will turn on for (startup_duration) hours then go into timer mode -# - The timer has a morning and evening time (but no weekday/weekend setting) +# - It allows the device to work in a standalone timer style operation +# - The timer has a morning and evening time (but no weekday/weekend settings) +# - Default values are set, but changed values are remembered in flash # - It uses SNTP for time setting (but obviously only if wifi & networking are working) # - It will default to an internal timer if no wifi. To reset internal timer, reboot the device at 12pm (noon) -# - If on a network and there is a MQTT server, you can set the 4 on/off times via MQTT (See below commands) -# - You can set 4 modes ON/OFF/TIMER/STARTUP via MQTT. That way, you can set to STARTUP for a short boost +# - If on a network and there is a MQTT server, you can set the on/off times via MQTT (See below commands) +# - You can set 4 modes ON/OFF/TIMER/BOOST via MQTT. Setting BOOST gives you a oneshot operation # - Any new timer times set via MQTT will be remembered though a reboot -# - On a reboot, the device will always turn on for the Startup Duration (STARTUP mode, default 2 hours) -# - TIMER mode will always be switched on after startup mode is complete +# - On startup, or a reboot, the device will always turn on for the BOOST Duration (BOOST mode, default 2 hours) +# - TIMER mode will always be switched on after BOOST mode is complete +# - Home Assistant entities are set so that BOOST mode can be pressed with a button and other modes selectable with a dropdown # - If you need it ON continuously with no MQTT, toggle power ON/OFF 4 times within 30 seconds (with ~2 secs in between to allow it to boot) # # MQTT Commands # Values will be set in place on the update_interval time, not immediately -# Use 00:00 in 24hr format for time setting. Note there is no weekday/weekend setting +# Use 00:00 in 24hr format for time setting. (Note there is no weekday/weekend setting) # mqtt_timer_topic/morning-on/06:00 : Time device will go on # mqtt_timer_topic/morning-off/08:00 : Time device will go off # mqtt_timer_topic/evening-on/09:00 : Time device will go on # mqtt_timer_topic/evening-off/00:00 : Time device will go off +# mqtt_timer_topic/boost-time/0000 : Time in minutes device will temporarily go on for (1-1439) # mqtt_timer_topic/operation/ON : Device permanently on # mqtt_timer_topic/operation/OFF : Device permanently off # mqtt_timer_topic/operation/TIMER : Device will obey timer settings -# mqtt_timer_topic/operation/STARTUP : Turn on device for (startup_duration) hours then TIMER (also on startup) +# mqtt_timer_topic/operation/BOOST : Turn on for (boost_duration) minutes then BOOST (also on startup) # # operation_mode: # 0 = OFF # 1 = ON # 2 = TIMER -# 3 = STARTUP +# 3 = BOOST # ########################################################################################## ########################################################################################## - - ########################################################################################## # SPECIFIC DEVICE VARIABLE SUBSTITUTIONS # If NOT using a secrets file, just replace these with the passwords etc (in quotes) @@ -50,12 +52,12 @@ substitutions: # Device Naming device_name: "esp-poollightpower" friendly_name: "Pool Light Power" - description_comment: "Pool Light Power, Athom Smart Plug Power Monitor" + description_comment: "Pool Light Power :: Athom Smart Plug Power V1" device_area: "Downstairs Kitchen" # 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: "v2.0" # Project V denotes release of yaml file, allowing checking of deployed vs latest version + project_version: "v2.2" # 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 @@ -66,15 +68,15 @@ substitutions: log_level: "INFO" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE update_interval: "60s" # update time for for general sensors etc - # Timer Settings + # Device and Timer Settings relay_icon: "mdi:light-flood-up" current_limit : "10" # Current Limit in Amps. AU Plug = 10. IL, BR, EU, UK, US Plug = 16. mqtt_timer_topic: "viewroad-commands/poollight-timer" # Topics you will use to change stuff - startup_duration: "180" # Minutes to stay ON in STARTUP mode before reverting to TIMER - morning_on_default: "450" # Default in minutes from midnight. Default 07:30 => 450 - morning_off_default: "450" # Default in minutes from midnight. Default 07:30 => 450 (same as ON as no need for morning schedule) - evening_on_default: "1140" # Default in minutes from midnight. Default 19:00 => 1140 - evening_off_default: "1350" # Default in minutes from midnight. Default 22:30 => 1350 => 1440 is midnight + boost_duration_default: "180" # Minutes to stay ON in BOOST mode before reverting to TIMER + morning_on_default: "450" # Default in minutes from midnight. Default 07:30 => 450 + morning_off_default: "450" # Default in minutes from midnight. Default 07:30 => 450 (same as ON as no need for morning schedule) + evening_on_default: "1140" # Default in minutes from midnight. Default 19:00 => 1140 + evening_off_default: "1350" # Default in minutes from midnight. Default 22:30 => 1350 => 1440 is midnight ########################################################################################## # PACKAGES @@ -90,10 +92,6 @@ packages: file: common/api_common.yaml vars: local_api_key: "${api_key}" -# common_webportal: !include -# file: common/webportal_common.yaml - common_mqtt: !include - file: common/mqtt_common.yaml common_sntp: !include file: common/sntp_common.yaml common_general_sensors: !include @@ -102,6 +100,19 @@ packages: local_friendly_name: "${friendly_name}" local_update_interval: "${update_interval}" + # Web and MQTT Packages + #common_webportal: !include + # file: common/webportal_common.yaml + common_mqtt: !include + file: common/mqtt_common.yaml + + # Device Specific included packages + common_athompowermonV1: !include + file: common/athompowermonv1_common.yaml + vars: + local_friendly_name: "${friendly_name}" + local_current_limit: "${current_limit}" + ########################################################################################## # ESPHome # https://esphome.io/components/esphome.html @@ -126,7 +137,7 @@ esp8266: restore_from_flash: true # mainly for calculating cumulative energy, but not that important here preferences: - flash_write_interval: 5min + flash_write_interval: 10min mdns: disabled: false @@ -145,7 +156,7 @@ logger: #tx_buffer_size: 64 ########################################################################################## -# GLOBAL VARIABLES +# Global Variables for use in automations etc # https://esphome.io/guides/automations.html?highlight=globals#global-variables ########################################################################################## globals: @@ -178,20 +189,26 @@ globals: - id: evening_on type: int restore_value: true - initial_value: "${evening_on_default}" + initial_value: "${morning_off_default}" # Evening Off time (minutes from midnight), - id: evening_off type: int restore_value: true - initial_value: "${evening_off_default}" + initial_value: "${morning_off_default}" + + # Boost Duration (minutes), + - id: boost_duration + type: int + restore_value: true + initial_value: "${boost_duration_default}" #################################################### # operation_mode: # 0 = OFF # 1 = ON # 2 = TIMER - # 3 = STARTUP + # 3 = BOOST #################################################### - id: operation_mode type: int @@ -210,42 +227,15 @@ globals: initial_value: "720" # 720 is 12:00 Noon #################################################### - # startup_timer: counts minutes in STARTUP mode - # After 'startup_duration' minutes, revert to TIMER. + # boost_timer: counts minutes in BOOST mode + # After 'boost_duration' minutes, revert to TIMER. # Not restored, so each boot starts fresh at 0. #################################################### - - id: startup_timer + - id: boost_timer type: int restore_value: false initial_value: "0" - #################################################### - # total_energy: cumulative power - # Restored, so keeps counting up. - #################################################### - - id: total_energy - type: float - restore_value: yes - initial_value: "0.0" - -########################################################################################## -# UART Bus -# https://esphome.io/components/uart.html -########################################################################################## -#uart: -# rx_pin: RX -# baud_rate: 4800 -# parity: EVEN - -########################################################################################## -# STATUS LED -# https://esphome.io/components/status_led.html -########################################################################################## -status_led: - pin: - number: GPIO13 - inverted: True - ########################################################################################## # Text Sensors # https://esphome.io/components/text_sensor/index.html @@ -336,15 +326,37 @@ text_sensor: ESP_LOGW("timer","Invalid Evening Off format: %s", x.c_str()); } #################################################### + # Boost duration => 1 - 1439 + #################################################### + - platform: mqtt_subscribe + name: "Boost Duration" + id: boost_time_topic + topic: "${mqtt_timer_topic}/boost-time" # Stored as an integer from 1-1439 + internal: True # No need to show this in Home Assistant as there is a sensor that shows the set value + on_value: + then: + - lambda: |- + // parse as integer + char *endptr; + long v = strtol(x.c_str(), &endptr, 10); + + // invalid if nothing parsed, trailing chars, or out of 0–1439 + if (endptr == x.c_str() || *endptr != '\0' || v < 0 || v > 1439) { + ESP_LOGE("boost_time", "Invalid boost_time '%s'", x.c_str()); + } else { + id(boost_duration) = static_cast(v); + } + + #################################################### # Subscribe to operation mode: - # OFF, ON, TIMER, STARTUP + # OFF, ON, TIMER, BOOST # We do case-insensitive compare using strcasecmp # (Requires typically included in ESPHome) #################################################### - platform: mqtt_subscribe name: "Operation Mode Setting" id: timer_operation_mode_topic - topic: "${mqtt_timer_topic}/operation" # STARTUP,ON,OFF,TIMER + topic: "${mqtt_timer_topic}/operation" # BOOST,ON,OFF,TIMER internal: True # No need to show this in Home Assistant as there is a sensor that shows the set value on_value: then: @@ -363,30 +375,29 @@ text_sensor: } else if (strcasecmp(x.c_str(), "OFF") == 0) { id(operation_mode) = 0; ESP_LOGI("timer","Operation mode set to OFF"); - } else if (strcasecmp(x.c_str(), "STARTUP") == 0) { + } else if (strcasecmp(x.c_str(), "BOOST") == 0) { id(operation_mode) = 3; - id(startup_timer) = 0; // Reset the startup timer to zero - ESP_LOGI("timer","Operation mode set to STARTUP"); + id(boost_timer) = 0; // Reset the BOOST timer to zero + ESP_LOGI("timer","Operation mode set to BOOST"); } else { ESP_LOGW("timer","Invalid operation mode: %s", x.c_str()); } ###################################################### - # Expose the current operation mode (OFF, ON, TIMER, STARTUP) + # Expose the current operation mode (OFF, ON, TIMER, BOOST) ###################################################### - platform: template name: "Operation Mode State" lambda: |- - // 0=OFF, 1=ON, 2=TIMER, 3=STARTUP + // 0=OFF, 1=ON, 2=TIMER, 3=BOOST switch (id(operation_mode)) { case 0: return {"OFF"}; case 1: return {"ON"}; case 2: return {"TIMER"}; - case 3: return {"STARTUP"}; + case 3: return {"BOOST"}; default: return {"UNKNOWN"}; } - #update_interval: "${update_interval}" - update_interval: "5s" + update_interval: 5s ###################################################### # Expose the "Morning On" time as a text (HH:MM) @@ -471,7 +482,7 @@ binary_sensor: id(relay).turn_off(); id(operation_mode) = 2; } else { - // Relay is OFF: turn it ON and set mode to 3 (Startup) + // Relay is OFF: turn it ON and set mode to 3 (BOOST) id(relay).turn_on(); id(operation_mode) = 3; } @@ -495,25 +506,12 @@ sensor: lambda: |- return id(current_mins); - # A calculation for total energy used, ever (so long as wuth esp8266 restore_from_flash: true) - - platform: template - name: "Total Energy" - id: total_energy_sensor - unit_of_measurement: "kWh" - device_class: "energy" - state_class: "total_increasing" - icon: mdi:lightning-bolt - accuracy_decimals: "3" - lambda: |- - return id(total_energy); - update_interval: "${update_interval}" - # A value in mins if a timer is running showing how many mins left - platform: template name: "Timer Minutes Remaining" id: timer_minutes_remaining unit_of_measurement: "Mins" - update_interval: 10s + update_interval: 5s accuracy_decimals: "0" lambda: |- // always zero if relay is off @@ -521,7 +519,7 @@ sensor: return 0; } int rem = 0; - // only calculate for mode 2 (scheduled) or mode 3 (startup) + // only calculate for mode 2 (scheduled) or mode 3 (BOOST) if (id(operation_mode) == 2) { int a = id(morning_off) - id(current_mins); int b = id(evening_off) - id(current_mins); @@ -529,110 +527,11 @@ sensor: rem = (a < 0) ? b : (a < b ? a : b); } else if (id(operation_mode) == 3) { - rem = ${startup_duration} - id(startup_timer); + rem = id(boost_duration) - id(boost_timer); } // never return negative return rem > 0 ? rem : 0; - ############################################# - # CSE7766 POWER SENSOR - # https://esphome.io/components/sensor/cse7766.html - ############################################# - - platform: hlw8012 - id: athom_hlw8012 - sel_pin: - number: GPIO12 - inverted: True - cf_pin: GPIO4 - cf1_pin: GPIO5 - voltage_divider: 780 - - current: - name: "Current" - id: current - unit_of_measurement: A - accuracy_decimals: 2 - icon: mdi:current-ac - filters: - - calibrate_linear: - - 0.0000 -> 0.0110 # Relay off no load - - 0.0097 -> 0.0260 # Relay on no load - - 0.9270 -> 0.7570 - - 2.0133 -> 1.6330 - - 2.9307 -> 2.3750 - - 5.4848 -> 4.4210 - - 8.4308 -> 6.8330 - - 9.9171 -> 7.9830 - # Normalize for plug load - - lambda: if (x < 0.0260) return 0; else return (x - 0.0260); - on_value_range: - - above: ${current_limit} - then: - - switch.turn_off: relay - - voltage: - name: "Voltage" - id: voltage - unit_of_measurement: V - accuracy_decimals: 1 - icon: mdi:sine-wave - filters: - - skip_initial: 2 - - power: - name: "Power" - id: power_sensor - unit_of_measurement: W - accuracy_decimals: 1 - icon: mdi:power - filters: - - calibrate_linear: - - 0.0000 -> 0.5900 # Relay off no load - - 0.0000 -> 1.5600 # Relay on no load - - 198.5129 -> 87.8300 - - 434.2469 -> 189.5000 - - 628.6241 -> 273.9000 - - 1067.0067 -> 460.1000 - - 1619.8098 -> 699.2000 - - 2043.0282 -> 885.0000 - # Normalize for plug load - - lambda: if (x < 1.5600) return 0; else return (x - 1.5600); - change_mode_every: 1 - update_interval: 5s - - # Shows the Energy kWh since the device was last started - energy: - name: "Energy (Since Restart)" - id: energy - icon: mdi:lightning-bolt - unit_of_measurement: kWh - accuracy_decimals: 3 - filters: - # Multiplication factor from W to kW is 0.001 - - multiply: 0.001 - on_value: - then: - - lambda: |- - static float previous_energy_value = 0.0; - float current_energy_value = id(energy).state; - id(total_energy) += current_energy_value - previous_energy_value; - previous_energy_value = current_energy_value; - id(total_energy_sensor).update(); - # internal: ${hide_energy_sensor} - - ############################################# - # Total Daily Energy - # https://esphome.io/components/sensor/total_daily_energy.html - ############################################# - - platform: total_daily_energy - name: "Total Daily Energy" - restore: true - power_id: power_sensor - unit_of_measurement: kWh - icon: mdi:hours-24 - accuracy_decimals: 3 - filters: - - multiply: 0.001 ################################################################################################# # SWITCH COMPONENT @@ -653,16 +552,16 @@ switch: ################################################################################################# button: - platform: template - name: "Startup: ${startup_duration} Minutes" - id: startup_button + name: "Boost now" + id: boost_button icon: "mdi:play-circle-outline" # optional, pick any MaterialDesign icon you like on_press: then: - # 1) set the mode to STARTUP (3) + # 1) set the mode to BOOST (3) - lambda: |- - id(startup_timer) = 0; // Reset the startup timer to zero - id(operation_mode) = 3; // Set to STARTUP - ESP_LOGD("main", "operation_mode set to %d via STARTUP button", id(operation_mode)); + id(boost_timer) = 0; // Reset the BOOST timer to zero + id(operation_mode) = 3; // Set to BOOST + ESP_LOGD("main", "operation_mode set to %d via BOOST button", id(operation_mode)); # 2) turn on the relay switch - switch.turn_on: id: relay @@ -680,14 +579,14 @@ select: - "OFF" - "ON" - "TIMER" - - "STARTUP" + - "BOOST" # Getter: maps your integer into one of the four strings lambda: |- switch (id(operation_mode)) { case 1: return std::string("ON"); case 2: return std::string("TIMER"); - case 3: return std::string("STARTUP"); + case 3: return std::string("BOOST"); default: return std::string("OFF"); } @@ -698,61 +597,52 @@ select: else if (x == "ON") id(operation_mode) = 1; else if (x == "TIMER") id(operation_mode) = 2; else { - id(startup_timer) = 0; // Reset the startup timer to zero - id(operation_mode) = 3; /* STARTUP */ + id(boost_timer) = 0; // Reset the BOOST timer to zero + id(operation_mode) = 3; /* BOOST */ } ESP_LOGD("main", "operation_mode set to %d", id(operation_mode)); -################################################################################################# -# INTERVAL COMPONENT -# https://esphome.io/components/interval.html -################################################################################################# + +#################################################### +# Check every minute to decide relay state +#################################################### interval: - interval: "1min" # Must be 1min as this is used to calculate times then: - lambda: |- - // Do we have correct time from SNTP? If not... - if (!id(time_sync).has_state()) { - // Set minutes since midnight - id(current_mins) = id(current_mins) +1 ; - - // wrap around at 1440 => next day - if (id(current_mins) >= 1440) { - id(current_mins) = 0; - } - - // If we do have proper SNMP time... + if (!id(sntp_time).now().is_valid()) { + id(current_mins) += 1; + if (id(current_mins) >= 1440) id(current_mins) = 0; } else { - // Use real time from SNTP - auto now = id(sntp_time).now(); - id(current_mins) = now.hour * 60 + now.minute; - } + auto now = id(sntp_time).now(); + id(current_mins) = now.hour * 60 + now.minute; + } // operation_mode: // 0 = OFF // 1 = ON // 2 = TIMER - // 3 = STARTUP + // 3 = BOOST int mode = id(operation_mode); ////////////////////////////////////////////////// - // STARTUP MODE: Relay ON for 'startup_duration' + // BOOST MODE: Relay ON for 'boost_duration' // minutes, then automatically revert to TIMER. ////////////////////////////////////////////////// if (mode == 3) { - id(startup_timer) = id(startup_timer) + 1 ; // works as long as update_interval in seconds - // Compare with the substitution startup_duration - if (id(startup_timer) < (int) ${startup_duration}) { - // Still within the STARTUP period => turn relay on + id(boost_timer) = id(boost_timer) + 1 ; // works as long as update_interval in seconds + // Compare with the substitution boost_duration + if (id(boost_timer) < id(boost_duration)) { + // Still within the BOOST period => turn relay on id(relay).turn_on(); } else { - // After 'startup_duration' minutes => switch to TIMER + // After 'boost_duration' minutes => switch to TIMER id(operation_mode) = 2; id(mqtt_client).publish("${mqtt_timer_topic}/operation", "TIMER"); } // Skip the rest of the logic - ESP_LOGI("startup_timer", "startup_timer=%d", id(startup_timer)); + ESP_LOGI("boost_timer", "boost_timer=%d", id(boost_timer)); return; } @@ -778,6 +668,7 @@ interval: ////////////////////////////////////////////////// if (mode == 2) { + bool should_on = false; // Check morning window @@ -803,3 +694,6 @@ interval: } } + + + diff --git a/esphome/esp-poolpumppower.yaml b/esphome/esp-poolpumppower.yaml index 66b2142..785b272 100644 --- a/esphome/esp-poolpumppower.yaml +++ b/esphome/esp-poolpumppower.yaml @@ -1,43 +1,47 @@ ########################################################################################## ########################################################################################## -# POOL PUMP POWER AND tiMEr +# POOL PUMP POWER AND TIMER # Controlled by a Athom Smart Plug V3 # # dashboard_import: # package_import_url: github://athom-tech/esp32-configs/athom-smart-plug.yaml # +# V2.2 2025-06-14 Fixes to offline time when sntp/network unavailable +# V2.1 2025-06-12 Added select and button to chose modes, added countdown & startup to boost # V2.0 2025-06-05 YAML Tidyups # # INSTRUCTIONS -# - It allows a device to work in a standalone operation -# - On startup, it will turn on for (startup_duration) hours then go into timer mode -# - The timer has a morning and evening time (but no weekday/weekend setting) +# - It allows the device to work in a standalone timer style operation +# - The timer has a morning and evening time (but no weekday/weekend settings) +# - Default values are set, but changed values are remembered in flash # - It uses SNTP for time setting (but obviously only if wifi & networking are working) # - It will default to an internal timer if no wifi. To reset internal timer, reboot the device at 12pm (noon) -# - If on a network and there is a MQTT server, you can set the 4 on/off times via MQTT (See below commands) -# - You can set 4 modes ON/OFF/TIMER/STARTUP via MQTT. That way, you can set to STARTUP for a short boost +# - If on a network and there is a MQTT server, you can set the on/off times via MQTT (See below commands) +# - You can set 4 modes ON/OFF/TIMER/BOOST via MQTT. Setting BOOST gives you a oneshot operation # - Any new timer times set via MQTT will be remembered though a reboot -# - On a reboot, the device will always turn on for the Startup Duration (STARTUP mode, default 2 hours) -# - TIMER mode will always be switched on after startup mode is complete +# - On startup, or a reboot, the device will always turn on for the BOOST Duration (BOOST mode, default 2 hours) +# - TIMER mode will always be switched on after BOOST mode is complete +# - Home Assistant entities are set so that BOOST mode can be pressed with a button and other modes selectable with a dropdown # - If you need it ON continuously with no MQTT, toggle power ON/OFF 4 times within 30 seconds (with ~2 secs in between to allow it to boot) # # MQTT Commands # Values will be set in place on the update_interval time, not immediately -# Use 00:00 in 24hr format for time setting. Note there is no weekday/weekend setting +# Use 00:00 in 24hr format for time setting. (Note there is no weekday/weekend setting) # mqtt_timer_topic/morning-on/06:00 : Time device will go on # mqtt_timer_topic/morning-off/08:00 : Time device will go off # mqtt_timer_topic/evening-on/09:00 : Time device will go on # mqtt_timer_topic/evening-off/00:00 : Time device will go off +# mqtt_timer_topic/boost-time/0000 : Time in minutes device will temporarily go on for (1-1439) # mqtt_timer_topic/operation/ON : Device permanently on # mqtt_timer_topic/operation/OFF : Device permanently off # mqtt_timer_topic/operation/TIMER : Device will obey timer settings -# mqtt_timer_topic/operation/STARTUP : Turn on device for (startup_duration) hours then TIMER (also on startup) +# mqtt_timer_topic/operation/BOOST : Turn on for (boost_duration) minutes then BOOST (also on startup) # # operation_mode: # 0 = OFF # 1 = ON # 2 = TIMER -# 3 = STARTUP +# 3 = BOOST # ########################################################################################## ########################################################################################## @@ -50,12 +54,12 @@ substitutions: # Device Naming device_name: "esp-poolpumppower" friendly_name: "Pool Pump Power" - description_comment: "Pool Pump Power, Athom Smart Plug Power Monitor" + description_comment: "Pool Pump Power :: Athom Smart Plug Power V3" device_area: "Outside" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. # Project Naming - project_name: "Athom Technology.Smart Plug V3" # Project Details - project_version: "v1.0.7" # Project V denotes release of yaml file, allowing checking of deployed vs latest version + project_name: "Athom Technology.Smart Plug V3" # Project Details + project_version: "v2.2" # Project V denotes release of yaml file, allowing checking of deployed vs latest version # Passwords api_key: !secret esp-poolpumppower_api_key # unfortunately you can't use substitutions inside secrets names @@ -64,17 +68,17 @@ substitutions: # Device Settings log_level: "INFO" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE - update_interval: "60s" # update time for for general sensors etc + update_interval: "20s" # update time for for general sensors etc - # Timer Settings + # Device Settings relay_icon: "mdi:pool" current_limit : "10" # Current Limit in Amps. AU Plug = 10. IL, BR, EU, UK, US Plug = 16. mqtt_timer_topic: "viewroad-commands/poolpump-timer" # Topics you will use to change stuff - startup_duration: "60" # Minutes to stay ON in STARTUP mode before reverting to TIMER - morning_on_default: "450" # Default in minutes from midnight. Default 07:30 => 450 - morning_off_default: "450" # Default in minutes from midnight. Default 07:30 => 450 (same as ON as no need for morning schedule) - evening_on_default: "1260" # Default in minutes from midnight. Default 21:00 => 1260 - evening_off_default: "1320" # Default in minutes from midnight. Default 22:00 => 1320 => 1440 is midnight + boost_duration_default: "60" # Minutes to stay ON in STARTUP mode before reverting to TIMER + morning_on_default: "450" # Default in minutes from midnight. Default 07:30 => 450 + morning_off_default: "450" # Default in minutes from midnight. Default 07:30 => 450 (same as ON as no need for morning schedule) + evening_on_default: "1260" # Default in minutes from midnight. Default 21:00 => 1260 + evening_off_default: "1320" # Default in minutes from midnight. Default 22:00 => 1320 => 1440 is midnight ########################################################################################## # PACKAGES @@ -90,10 +94,6 @@ packages: file: common/api_common.yaml vars: local_api_key: "${api_key}" -# common_webportal: !include -# file: common/webportal_common.yaml - common_mqtt: !include - file: common/mqtt_common.yaml common_sntp: !include file: common/sntp_common.yaml common_general_sensors: !include @@ -102,6 +102,20 @@ packages: local_friendly_name: "${friendly_name}" local_update_interval: "${update_interval}" + # Web and MQTT Packages + #common_webportal: !include + # file: common/webportal_common.yaml + common_mqtt: !include + file: common/mqtt_common.yaml + + # Device Specific included packages + common_athompowermonV3: !include + file: common/athompowermonv3_common.yaml + vars: + local_friendly_name: "${friendly_name}" + local_current_limit: "${current_limit}" + + ########################################################################################## # ESPHome # https://esphome.io/components/esphome.html @@ -130,11 +144,11 @@ esp32: flash_size: 4MB variant: ESP32C3 framework: - type: arduino - version: recommended + type: esp-idf # "esp-idf" OR "arduino". Suggested ESP-IDF Framework, or Plug Out the UART Cable Might Cause ESP32 Hang. + version: recommended # recommended, latest or dev preferences: - flash_write_interval: 5min + flash_write_interval: 10min esp32_improv: authorizer: none @@ -191,12 +205,18 @@ globals: restore_value: true initial_value: "${evening_off_default}" + # Boost Duration (minutes), + - id: boost_duration + type: int + restore_value: true + initial_value: "${boost_duration_default}" + #################################################### # operation_mode: # 0 = OFF # 1 = ON # 2 = TIMER - # 3 = STARTUP + # 3 = BOOST #################################################### - id: operation_mode type: int @@ -214,44 +234,16 @@ globals: restore_value: false initial_value: "720" # 720 is 12:00 Noon - #################################################### - # startup_timer: counts minutes in STARTUP mode - # After 'startup_duration' minutes, revert to TIMER. + #################################################### + # boost_timer: counts minutes in BOOST mode + # After 'boost_duration' minutes, revert to TIMER. # Not restored, so each boot starts fresh at 0. #################################################### - - id: startup_timer + - id: boost_timer type: int restore_value: false initial_value: "0" - #################################################### - # total_energy: cumulative power - # Not restored, so each boot starts fresh at 0. - #################################################### - - id: total_energy - type: float - restore_value: yes - initial_value: '0.0' - -########################################################################################## -# UART Bus -# https://esphome.io/components/uart.html -########################################################################################## -uart: - rx_pin: GPIO20 - baud_rate: 4800 - data_bits: 8 - stop_bits: 1 - parity: EVEN - -########################################################################################## -# STATUS LED -# https://esphome.io/components/status_led.html -########################################################################################## -status_led: - pin: - number: GPIO06 - inverted: False ########################################################################################## # Text Sensors @@ -343,15 +335,36 @@ text_sensor: ESP_LOGW("timer","Invalid Evening Off format: %s", x.c_str()); } #################################################### + # Boost duration => 1 - 1439 + #################################################### + - platform: mqtt_subscribe + name: "Boost Duration" + id: boost_time_topic + topic: "${mqtt_timer_topic}/boost-time" # Stored as an integer from 1-1439 + internal: True # No need to show this in Home Assistant as there is a sensor that shows the set value + on_value: + then: + - lambda: |- + // parse as integer + char *endptr; + long v = strtol(x.c_str(), &endptr, 10); + + // invalid if nothing parsed, trailing chars, or out of 0–1439 + if (endptr == x.c_str() || *endptr != '\0' || v < 0 || v > 1439) { + ESP_LOGE("boost_time", "Invalid boost_time '%s'", x.c_str()); + } else { + id(boost_duration) = static_cast(v); + } + #################################################### # Subscribe to operation mode: - # OFF, ON, TIMER, STARTUP + # OFF, ON, TIMER, BOOST # We do case-insensitive compare using strcasecmp # (Requires typically included in ESPHome) #################################################### - platform: mqtt_subscribe name: "Operation Mode Setting" id: timer_operation_mode_topic - topic: "${mqtt_timer_topic}/operation" # STARTUP,ON,OFF,TIMER + topic: "${mqtt_timer_topic}/operation" # BOOST,ON,OFF,TIMER internal: True # No need to show this in Home Assistant as there is a sensor that shows the set value on_value: then: @@ -370,29 +383,29 @@ text_sensor: } else if (strcasecmp(x.c_str(), "OFF") == 0) { id(operation_mode) = 0; ESP_LOGI("timer","Operation mode set to OFF"); - } else if (strcasecmp(x.c_str(), "STARTUP") == 0) { + } else if (strcasecmp(x.c_str(), "BOOST") == 0) { id(operation_mode) = 3; - id(startup_timer) = 0; // Reset the startup timer to zero - ESP_LOGI("timer","Operation mode set to STARTUP"); + id(boost_timer) = 0; // Reset the BOOST timer to zero + ESP_LOGI("timer","Operation mode set to BOOST"); } else { ESP_LOGW("timer","Invalid operation mode: %s", x.c_str()); } ###################################################### - # Expose the current operation mode (OFF, ON, TIMER, STARTUP) + # Expose the current operation mode (OFF, ON, TIMER, BOOST) ###################################################### - platform: template name: "Operation Mode State" lambda: |- - // 0=OFF, 1=ON, 2=TIMER, 3=STARTUP + // 0=OFF, 1=ON, 2=TIMER, 3=BOOST switch (id(operation_mode)) { case 0: return {"OFF"}; case 1: return {"ON"}; case 2: return {"TIMER"}; - case 3: return {"STARTUP"}; + case 3: return {"BOOST"}; default: return {"UNKNOWN"}; } - update_interval: "5s" + update_interval: 5s ###################################################### # Expose the "Morning On" time as a text (HH:MM) @@ -473,11 +486,11 @@ binary_sensor: then: - lambda: |- if (id(relay).state) { - // Relay is ON: turn it OFF and set mode to 0 (Timer) + // Relay is ON: turn it OFF and set mode to 0 (TIMER) id(relay).turn_off(); id(operation_mode) = 2; } else { - // Relay is OFF: turn it ON and set mode to 3 (Startup) + // Relay is OFF: turn it ON and set mode to 3 (BOOST) id(relay).turn_on(); id(operation_mode) = 3; } @@ -501,24 +514,12 @@ sensor: lambda: |- return id(current_mins); - - platform: template - name: "Total Energy" - id: total_energy_sensor - unit_of_measurement: kWh - device_class: "energy" - state_class: "total_increasing" - icon: mdi:lightning-bolt - accuracy_decimals: 3 - lambda: |- - return id(total_energy); - update_interval: "${update_interval}" - - # A value in mins if a timer is running showing how many mins left + # A value in mins if a timer is running showing how many mins left - platform: template name: "Timer Minutes Remaining" id: timer_minutes_remaining unit_of_measurement: "Mins" - update_interval: 10s + update_interval: 5s accuracy_decimals: "0" lambda: |- // always zero if relay is off @@ -526,7 +527,7 @@ sensor: return 0; } int rem = 0; - // only calculate for mode 2 (scheduled) or mode 3 (startup) + // only calculate for mode 2 (scheduled) or mode 3 (BOOST) if (id(operation_mode) == 2) { int a = id(morning_off) - id(current_mins); int b = id(evening_off) - id(current_mins); @@ -534,86 +535,11 @@ sensor: rem = (a < 0) ? b : (a < b ? a : b); } else if (id(operation_mode) == 3) { - rem = ${startup_duration} - id(startup_timer); + rem = id(boost_duration) - id(boost_timer); } // never return negative return rem > 0 ? rem : 0; - ############################################# - # CSE7766 POWER SENSOR - # https://esphome.io/components/sensor/cse7766.html - ############################################# - - platform: cse7766 - id: athom_cse7766 - current: - name: "Current" - icon: mdi:current-ac - filters: - - throttle_average: "${update_interval}" - - lambda: if (x < 0.060) return 0.0; else return x; #For the chip will report less than 3w power when no load is connected - on_value_range: - - above: "${current_limit}" - then: - - switch.turn_off: relay - voltage: - name: "Voltage" - icon: mdi:sine-wave - filters: - - throttle_average: "${update_interval}" - power: - name: "Power" - id: power_sensor - icon: mdi:power - filters: - - throttle_average: "${update_interval}" - - lambda: if (x < 3.0) return 0.0; else return x; #For the chip will report less than 3w power when no load is connected - energy: - name: "Energy" - id: energy - icon: mdi:lightning-bolt - unit_of_measurement: kWh - filters: - - throttle: "${update_interval}" - # Multiplication factor from W to kW is 0.001 - - multiply: 0.001 - on_value: - then: - - lambda: |- - static float previous_energy_value = 0.0; - float current_energy_value = id(energy).state; - id(total_energy) += current_energy_value - previous_energy_value; - previous_energy_value = current_energy_value; - id(total_energy_sensor).update(); - apparent_power: - name: "Apparent Power" - icon: mdi:power - filters: - - throttle_average: "${update_interval}" - reactive_power: - name: "Reactive Power" - icon: mdi:flash - filters: - - throttle_average: "${update_interval}" - power_factor: - name: "Power Factor" - icon: mdi:percent-outline - filters: - - throttle_average: "${update_interval}" - - ############################################# - # Total Daily Energy - # https://esphome.io/components/sensor/total_daily_energy.html - ############################################# - - platform: total_daily_energy - name: "Total Daily Energy" - restore: true - power_id: power_sensor - unit_of_measurement: kWh - icon: mdi:hours-24 - accuracy_decimals: 3 - filters: - - multiply: 0.001 - ################################################################################################# # SWITCH COMPONENT # https://esphome.io/components/switch/ @@ -633,16 +559,16 @@ switch: ################################################################################################# button: - platform: template - name: "Startup: ${startup_duration} Minutes" - id: startup_button + name: "Boost now" + id: boost_button icon: "mdi:play-circle-outline" # optional, pick any MaterialDesign icon you like on_press: then: - # 1) set the mode to STARTUP (3) + # 1) set the mode to BOOST (3) - lambda: |- - id(startup_timer) = 0; // Reset the startup timer to zero - id(operation_mode) = 3; // Set to STARTUP - ESP_LOGD("main", "operation_mode set to %d via STARTUP button", id(operation_mode)); + id(boost_timer) = 0; // Reset the BOOST timer to zero + id(operation_mode) = 3; // Set to BOOST + ESP_LOGD("main", "operation_mode set to %d via BOOST button", id(operation_mode)); # 2) turn on the relay switch - switch.turn_on: id: relay @@ -660,14 +586,14 @@ select: - "OFF" - "ON" - "TIMER" - - "STARTUP" + - "BOOST" # Getter: maps your integer into one of the four strings lambda: |- switch (id(operation_mode)) { case 1: return std::string("ON"); case 2: return std::string("TIMER"); - case 3: return std::string("STARTUP"); + case 3: return std::string("BOOST"); default: return std::string("OFF"); } @@ -678,61 +604,51 @@ select: else if (x == "ON") id(operation_mode) = 1; else if (x == "TIMER") id(operation_mode) = 2; else { - id(startup_timer) = 0; // Reset the startup timer to zero - id(operation_mode) = 3; /* STARTUP */ + id(boost_timer) = 0; // Reset the BOOST timer to zero + id(operation_mode) = 3; /* BOOST */ } ESP_LOGD("main", "operation_mode set to %d", id(operation_mode)); -################################################################################################# -# INTERVAL COMPONENT -# https://esphome.io/components/interval.html -################################################################################################# +#################################################### +# Check every minute to decide relay state +#################################################### interval: - interval: "1min" # Must be 1min as this is used to calculate times then: - lambda: |- - // Do we have correct time from SNTP? If not... - if (!id(time_sync).has_state()) { - // Set minutes since midnight - id(current_mins) = id(current_mins) +1 ; - - // wrap around at 1440 => next day - if (id(current_mins) >= 1440) { - id(current_mins) = 0; - } - - // If we do have proper SNMP time... + if (!id(sntp_time).now().is_valid()) { + id(current_mins) += 1; + if (id(current_mins) >= 1440) id(current_mins) = 0; } else { - // Use real time from SNTP - auto now = id(sntp_time).now(); - id(current_mins) = now.hour * 60 + now.minute; - } + auto now = id(sntp_time).now(); + id(current_mins) = now.hour * 60 + now.minute; + } // operation_mode: // 0 = OFF // 1 = ON // 2 = TIMER - // 3 = STARTUP + // 3 = BOOST int mode = id(operation_mode); ////////////////////////////////////////////////// - // STARTUP MODE: Relay ON for 'startup_duration' + // BOOST MODE: Relay ON for 'boost_duration' // minutes, then automatically revert to TIMER. ////////////////////////////////////////////////// if (mode == 3) { - id(startup_timer) = id(startup_timer) + 1 ; // works as long as update_interval in seconds - // Compare with the substitution startup_duration - if (id(startup_timer) < (int) ${startup_duration}) { - // Still within the STARTUP period => turn relay on + id(boost_timer) = id(boost_timer) + 1 ; // works as long as update_interval in seconds + // Compare with the substitution boost_duration + if (id(boost_timer) < id(boost_duration)) { + // Still within the BOOST period => turn relay on id(relay).turn_on(); } else { - // After 'startup_duration' minutes => switch to TIMER + // After 'boost_duration' minutes => switch to TIMER id(operation_mode) = 2; id(mqtt_client).publish("${mqtt_timer_topic}/operation", "TIMER"); } // Skip the rest of the logic - ESP_LOGI("startup_timer", "startup_timer=%d", id(startup_timer)); + ESP_LOGI("boost_timer", "boost_timer=%d", id(boost_timer)); return; }