#:########################################################################################:# # TITLE: LAUNDRY DRYER MACHINE POWER # zorruno.com layout v1.1 2026 #:########################################################################################:# # REPO: # https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-laundrydrypow.yaml #:########################################################################################:# # VERSIONS: # V1.2 2026-07-12 Added cumulative Load Energy integration sensor # Added seven-day maximum power sensor # Maximum power resets after 168 powered hours # V1.1 2026-03-11 Updated yaml to zorruno layout V1.1 # V1.0 2025-05-28 Initial Version #:########################################################################################:# # HARDWARE: # - Sonoff POW R1 # - ESP8266 based # - HLW8012 power monitoring # - Single relay output # - Blue status LED on GPIO15 # - Local push button on GPIO0 # - Relay output on GPIO12 # - Reference: https://devices.esphome.io/devices/Sonoff-POW-R1 #:########################################################################################:# # OPERATION NOTES: # - Monitors dryer load current, voltage, power, and cumulative energy # - Load Energy integrates measured power over time and reports kWh # - Load Energy resets to zero if the ESPHome device reboots # - Home Assistant handles the reset because the sensor uses total_increasing # - Load Maximum Power 7 Days records the highest measured power # - Maximum power resets after 168 powered hours # - The maximum power value and elapsed-hour counter are retained across reboots # - The elapsed-hour counter is saved every six hours to reduce flash writes # - An unexpected power loss may extend the reset period by up to approximately six hours # - No accurate clock or SNTP connection is required for the seven-day reset # - Provides a virtual relay switch that controls the physical relay and LED together # - Local button toggles the virtual relay switch # - On boot, the virtual relay is turned ON # - "Load Running" turns ON when measured power is above 4 W # - "Load Running" uses delayed_off: 15s to avoid rapid state changes #:########################################################################################:# # OFFLINE NOTES: # a) Home Assistant offline (network and MQTT online): # - Device continues running normally # - Local button still works # - Power, energy, running state, and maximum power continue operating locally # - MQTT remains available if the common MQTT package is enabled and broker is reachable # # b) MQTT offline (or HA/MQTT offline): # - Device continues running locally # - Local button, relay control, LED control, and power monitoring still work # - Energy integration and maximum power monitoring continue locally # # c) Entire WiFi/Network offline: # - Device continues running locally # - Local button, relay control, LED control, and power monitoring still work # - Energy integration and maximum power monitoring continue locally # - Accurate time is not needed; SNTP is not required for operation #:########################################################################################:# #:########################################################################################:# # 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-laundrydrypow" friendly_name: "Laundry Dryer Power" description_comment: "Laundry Dryer Power using a Sonoff Pow R1. (Layout V1.1)" device_area: "Laundry" # Project Naming project_name: "Sonoff Technologies.POW R1" project_version: "v1.2" # Entity Naming entity_prefix: "Load" # Passwords and Secrets api_key: !secret esp-laundrydrypow_api_key ota_pass: !secret esp-laundrydrypow_ota_pass static_ip_address: !secret esp-laundrydrypow_ip current_ip_address: ${static_ip_address} # Device Settings log_level: "INFO" update_interval: "60s" #:########################################################################################:# # PACKAGES: # Included common packages # https://esphome.io/components/packages.html #:########################################################################################:# packages: #### WIFI, Network (Static/DHCP/IPV6 etc), Fallback AP, Safemode #### common_wifi: !include file: common/network_common.yaml vars: local_device_name: "${device_name}" local_static_ip_address: "${static_ip_address}" local_current_ip_address: "${current_ip_address}" local_ota_pass: "${ota_pass}" #### HOME ASSISTANT API (choose encryption or no encryption options) #### common_api: !include file: common/api_common.yaml # file: common/api_common_noencryption.yaml vars: local_api_key: "${api_key}" #### MQTT #### common_mqtt: !include file: common/mqtt_common.yaml vars: local_device_name: "${device_name}" #### WEB PORTAL #### # common_webportal: !include common/webportal_common.yaml #### SNTP (Only use if you want/need accurate timeclocks) #### # 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}" friendly_name: "${friendly_name}" comment: "${description_comment}" area: "${device_area}" project: name: "${project_name}" version: "${project_version}" on_boot: priority: 200 then: - switch.turn_on: virtual_button #:########################################################################################:# # ESP PLATFORM AND FRAMEWORK: # https://esphome.io/components/esp8266.html #:########################################################################################:# esp8266: board: esp01_1m early_pin_init: false board_flash_mode: dout #:########################################################################################:# # LOGGER: # https://esphome.io/components/logger.html #:########################################################################################:# logger: level: "${log_level}" # baud_rate: 0 # esp8266_store_log_strings_in_flash: false # tx_buffer_size: 64 #:########################################################################################:# # STATUS LED: # https://esphome.io/components/status_led.html #:########################################################################################:# # status_led: # pin: # number: GPIO2 # inverted: true #:########################################################################################:# # GLOBALS: # Retained maximum power and elapsed-hour counter # https://esphome.io/components/globals.html #:########################################################################################:# globals: # Highest measured power during the current seven-day period. # The value is checked for changes every five minutes before being saved. - id: max_power_7d type: float restore_value: true initial_value: '0.0' update_interval: 5min # Number of powered hours elapsed in the current seven-day period. # Saved every six hours to reduce ESP8266 flash writes. - id: max_power_hours_elapsed type: uint16_t restore_value: true initial_value: '0' update_interval: 6h #:########################################################################################:# # BINARY SENSORS: # https://esphome.io/components/binary_sensor/ #:########################################################################################:# binary_sensor: - platform: gpio pin: number: GPIO0 mode: INPUT_PULLUP inverted: true name: "Button" on_press: - switch.toggle: virtual_button - platform: template name: "${entity_prefix} Running" filters: - delayed_off: 15s lambda: |- if (isnan(id(power).state)) { return {}; } else if (id(power).state > 4.0f) { return true; } else { return false; } #:########################################################################################:# # SENSORS: # https://esphome.io/components/sensor/ #:########################################################################################:# sensor: - platform: hlw8012 sel_pin: GPIO5 cf_pin: GPIO14 cf1_pin: GPIO13 update_interval: 2s current: name: "${entity_prefix} Current" device_class: current state_class: measurement unit_of_measurement: "A" accuracy_decimals: 2 voltage: name: "${entity_prefix} Voltage" device_class: voltage state_class: measurement unit_of_measurement: "V" accuracy_decimals: 1 power: name: "${entity_prefix} Power" id: power device_class: power state_class: measurement unit_of_measurement: "W" accuracy_decimals: 1 # Update the seven-day maximum whenever a new peak is measured. on_value: then: - lambda: |- if (!isnan(x) && x > id(max_power_7d)) { id(max_power_7d) = x; id(max_power_7d_sensor).publish_state(x); ESP_LOGI( "max_power", "New seven-day maximum power: %.1f W", x ); } # on_value_range: # - above: 4.0 # then: # - light.turn_on: led # - below: 3.0 # then: # - light.turn_off: led # Integrates instantaneous watts over time. # Watts integrated over hours produces Wh, then multiply converts Wh to kWh. - platform: integration name: "${entity_prefix} Energy" id: energy sensor: power time_unit: h integration_method: trapezoid restore: false unit_of_measurement: "kWh" device_class: energy state_class: total_increasing accuracy_decimals: 3 filters: - multiply: 0.001 # Highest power reading recorded during the current 168 powered-hour period. - platform: template name: "${entity_prefix} Maximum Power 7 Days" id: max_power_7d_sensor unit_of_measurement: "W" device_class: power state_class: measurement accuracy_decimals: 1 update_interval: 60s lambda: |- return id(max_power_7d); #:########################################################################################:# # INTERVAL: # Seven-day maximum power reset # https://esphome.io/components/interval.html #:########################################################################################:# interval: # Count one powered hour at a time. # 168 hours equals seven days. - interval: 1h then: - lambda: |- id(max_power_hours_elapsed) += 1; ESP_LOGD( "max_power", "Seven-day maximum power period: %u of 168 powered hours elapsed", id(max_power_hours_elapsed) ); if (id(max_power_hours_elapsed) >= 168) { id(max_power_hours_elapsed) = 0; id(max_power_7d) = 0.0f; id(max_power_7d_sensor).publish_state(0.0f); ESP_LOGI( "max_power", "Seven-day maximum power value has been reset" ); } #:########################################################################################:# # SWITCH: # https://esphome.io/components/switch/ #:########################################################################################:# switch: - platform: template name: "Relay" id: virtual_button optimistic: true turn_on_action: - switch.turn_on: relay - light.turn_on: led turn_off_action: - switch.turn_off: relay - light.turn_off: led - platform: gpio id: relay pin: GPIO12 # restore_mode: ALWAYS_ON #:########################################################################################:# # OUTPUT: # https://esphome.io/components/output/ #:########################################################################################:# output: - platform: esp8266_pwm id: pow_blue_led pin: number: GPIO15 inverted: true #:########################################################################################:# # LIGHT: # https://esphome.io/components/light/ #:########################################################################################:# light: - platform: monochromatic name: "Status LED" output: pow_blue_led id: led