############################################# ############################################# # GARAGE DB CONTROLS # Controlled by a Sonoff 4Ch R2 # https://github.com/jvyoralek/homeassistant-config/blob/master/esphome/sonoff-4ch.yaml # # V1.0 2025-10-23 Initial Version # ########################################################################################## ########################################################################################## ########################################################################################## # SPECIFIC DEVICE VARIABLE SUBSTITUTIONS # If NOT using a secrets file, just replace these with the passwords etc (in quotes) ########################################################################################## substitutions: # Device Custom Naming device_name: "esp-garagedbcontrols" friendly_name: "Garage DB Control" description_comment: "Garage DB Power, EV Power & Mesure, Lighting :: Sonoff 4ch R2" device_area: "Garage" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. static_ip_address: !secret esp-garagedbcontrols_ip #mqtt_device_name_1: "garage-dbcontrols" # I/O Naming switch_1_name: "Small EV Charger" button_1_name: "Button 1" relay_1_icon: "mdi:power-socket-au" switch_2_name: "Garage West Power" button_2_name: "Button 2" relay_2_icon: "mdi:power-socket-au" switch_3_name: "Spare 3" button_3_name: "Button 3" relay_3_icon: "mdi:toggle-switch" switch_4_name: "Spare 4" button_4_name: "Button 4" relay_4_icon: "mdi:toggle-switch" # Project Naming project_name: "Sonoff Technologies.Sonoff 4Ch R2" # Project Details project_version: "v1.0" # Project V denotes release of yaml file, allowing checking of deployed vs latest version # MQTT Controls #mqtt_command_main_topic: !secret mqtt_command_main_topic #mqtt_status_main_topic: !secret mqtt_status_main_topic #mqtt_command_topic_1: "${mqtt_command_main_topic}/${mqtt_device_name_1}" #mqtt_status_topic_1: "${mqtt_status_main_topic}/${mqtt_device_name_1}" #mqtt_command_ON: "ON" #mqtt_command_OFF: "OFF" # Passwords & Secrets (unfortunately you can't use substitutions inside secrets names) api_key: !secret esp-api_key ota_pass: !secret esp-ota_pass # General Settings log_level: "ERROR" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE update_interval: "60s" # update time for for general sensors etc ########################################################################################## # PACKAGES: Included Common Packages # https://esphome.io/components/packages.html ########################################################################################## packages: #### WIFI, Network (Static/DHCP/IPV6 etc), Fallback AP, Safemode #### common_wifi: !include file: common/network_common.yaml vars: local_device_name: "${device_name}" local_static_ip_address: "${static_ip_address}" local_ota_pass: "${ota_pass}" #### 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}" # Appears on the esphome page in HA area: "${device_area}" # On boot, initialise the window start so the first publish isn't huge on_boot: priority: -10 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; } ########################################################################################## # ESP Platform and Framework # https://esphome.io/components/esp32.html ########################################################################################## esp8266: board: esp8285 #board: esp01_1m restore_from_flash: True # restore some values on reboot preferences: flash_write_interval: 5min mdns: disabled: False ########################################################################################## # ESPHome Logging Enable # https://esphome.io/components/logger.html ########################################################################################## logger: 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 ########################################################################################## # SWITCH COMPONENT # https://esphome.io/components/switch/ ########################################################################################## # Sonoff 4Ch R2 Relays are GPIO12,05,04,15 ############################################# switch: - platform: gpio id: relay1 name: "${switch_1_name}" restore_mode: RESTORE_DEFAULT_OFF pin: GPIO12 icon: "${relay_1_icon}" - platform: gpio id: relay2 name: "${switch_2_name}" restore_mode: RESTORE_DEFAULT_ON pin: GPIO5 icon: "${relay_2_icon}" - platform: gpio id: relay3 name: "${switch_3_name}" restore_mode: RESTORE_DEFAULT_ON pin: GPIO4 icon: "${relay_3_icon}" - platform: gpio id: relay4 name: "${switch_4_name}" restore_mode: RESTORE_DEFAULT_OFF pin: GPIO15 icon: "${relay_4_icon}" ########################################################################################## # BINARY SENSORS # https://esphome.io/components/binary_sensor/ ########################################################################################## # Sonoff 4Ch R2 Buttons are GPIO00,09,10,14 ############################################# binary_sensor: - platform: gpio id: button1 name: "${button_1_name}" pin: number: GPIO0 mode: INPUT_PULLUP inverted: True on_press: - switch.toggle: relay1 - platform: gpio id: button2 name: "${button_2_name}" pin: number: GPIO9 mode: INPUT_PULLUP inverted: True on_press: - switch.toggle: relay2 - platform: gpio id: button3 name: "${button_3_name}" pin: number: GPIO10 mode: INPUT_PULLUP inverted: True on_press: - switch.toggle: relay3 - platform: gpio id: button4 name: "${button_4_name}" pin: number: GPIO14 mode: INPUT_PULLUP inverted: True on_press: - switch.toggle: relay4 ########################################################################################## # STATUS LED # https://esphome.io/components/status_led.html ########################################################################################## status_led: pin: number: GPIO13 inverted: yes ########################################################################################## # EV CHARGER ENERGY (PULSE INPUT ON GPIO2, 2.0 Wh PER PULSE) # - Uses pulse_meter for stable pulse rate and total count # - Derives instantaneous Power (W), 1h average Power, Today kWh, and lifetime kWh ########################################################################################## sensor: # Pulse rate -> Power Now (W) - platform: pulse_meter id: ev_pulse_pm pin: number: GPIO2 mode: INPUT_PULLUP # GPIO2 must be HIGH at boot inverted: false name: "${switch_1_name} Power Now" # pulse_meter reports pulses/min; each pulse = 2 Wh => W = pulses/min * 2 * 60 = x120 unit_of_measurement: "W" device_class: power state_class: measurement accuracy_decimals: 0 internal_filter: 10ms # debounce pulses timeout: 2min # set to 0 W if no pulses for 2 minutes filters: - multiply: 120.0 # convert pulses/min to Watts # Lifetime energy total (monotonic) 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: - multiply: 0.002 # kWh per pulse (2.0 Wh = 0.002 kWh) # 1-hour sliding average of power (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 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 # Today's energy (kWh), integrates "Power Now" and resets at local midnight - platform: total_daily_energy 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: - multiply: 0.001 # total_daily_energy outputs Wh; convert to kWh # Expose "Energy Yesterday" as a normal sensor - platform: template id: ev_energy_yesterday_sensor name: "${switch_1_name} Energy Yesterday" unit_of_measurement: "kWh" device_class: energy state_class: total accuracy_decimals: 1 update_interval: 30s lambda: |- return id(ev_energy_yesterday_kwh); ########################################################################################## # DHT SENSOR # https://esphome.io/components/sensor/dht/ ########################################################################################## #sensor: - platform: dht pin: GPIO3 temperature: name: "${friendly_name} Temperature" humidity: name: "${friendly_name} Humidity" update_interval: 60s ########################################################################################## # Tasmota-like teleperiod publish of windowed Wh count every 60 s ########################################################################################## # Store the last published total (kWh) 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" # Teleperiod = 60s: publish JSON and advance the window interval: - interval: 60s then: - mqtt.publish: topic: "stat/tasmo-s4chan-7594-garage-1/EnergyMeterCount" qos: 0 retain: false payload: !lambda |- // Current lifetime kWh from pulse_meter "total" float current = id(ev_energy_total_kwh).state; if (!isfinite(current)) current = id(ev_last_total_kwh); // Delta since last publish (kWh -> Wh) 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; // Advance the window (equivalent of "counter1 0") id(ev_last_total_kwh) = current; // Build JSON payload char buf[64]; snprintf(buf, sizeof(buf), "{\"EVChargerWhCount\":%.3f}", delta_wh); return std::string(buf); ########################################################################################## # YESTERDAY ENERGY (device-side snapshot of today's total at 23:59:59) ########################################################################################## time: - platform: sntp id: ev_time_for_ev_snap on_time: - seconds: 59 minutes: 59 hours: 23 then: - lambda: |- // Snapshot today's kWh just before midnight reset if (isfinite(id(ev_energy_today_kwh).state)) { id(ev_energy_yesterday_kwh) = id(ev_energy_today_kwh).state; }