diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d8b4157 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Gitignore settings for ESPHome +# This is an example and may include too much for your use-case. +# You can modify this file to suit your needs. +/.esphome/ +/secrets.yaml diff --git a/LICENSE b/LICENSE index d3a1a37..2a0181c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 dlitz +Copyright (c) 2025-2026 Darsey Litzenberger Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including diff --git a/common/bk72xx-mdns-debug.yaml b/common/bk72xx-mdns-debug.yaml new file mode 100644 index 0000000..b5b2bb4 --- /dev/null +++ b/common/bk72xx-mdns-debug.yaml @@ -0,0 +1,54 @@ +esphome: + includes: + - "" # for struct mdns_service, struct mdns_txtdata + - "" # for LWIP_VERSION_STRING + +bk72xx: + framework: + loglevel: debug + debug: + - mdns + - lwip + sdk_silent: auto + +text_sensor: + - platform: libretiny + version: + id: libretiny_version_id + name: LibreTiny Version + + - platform: template + name: "LWIP_VERSION_STRING" + entity_category: diagnostic + lambda: |- + static const std::string s = LWIP_VERSION_STRING; + return s; + +sensor: + - platform: template + name: "MDNS_TXT_RDATA_SIZE" + accuracy_decimals: 0 + entity_category: diagnostic + lambda: |- + #ifdef MDNS_TXT_RDATA_SIZE + return MDNS_TXT_RDATA_SIZE; + #else + return {}; + #endif + + - platform: template + name: "size of mdns_service.txtdata" + accuracy_decimals: 0 + entity_category: diagnostic + lambda: |- + struct mdns_service srv; + return sizeof(srv.txtdata); + + - platform: template + name: "size of mdns_txtdata.rdata" + accuracy_decimals: 0 + entity_category: diagnostic + lambda: |- + struct mdns_txtdata txt; + return sizeof(txt.rdata); + diff --git a/common/bk72xx-mdns-fix.yaml b/common/bk72xx-mdns-fix.yaml new file mode 100644 index 0000000..68c6bfc --- /dev/null +++ b/common/bk72xx-mdns-fix.yaml @@ -0,0 +1,7 @@ +packages: + - !include libretiny-mdns-fix.yaml + +bk72xx: + framework: + options: + MDNS_TXT_RDATA_SIZE: 384 # https://github.com/esphome/esphome/issues/11281 diff --git a/common/cpu-temperature.yaml b/common/cpu-temperature.yaml new file mode 100644 index 0000000..bbd90fa --- /dev/null +++ b/common/cpu-temperature.yaml @@ -0,0 +1,4 @@ +sensor: + - platform: internal_temperature + id: cpu_temperature + name: "CPU Temperature" diff --git a/common/esp-shelly-c38f.yaml b/common/esp-shelly-c38f.yaml new file mode 100644 index 0000000..185fc0d --- /dev/null +++ b/common/esp-shelly-c38f.yaml @@ -0,0 +1,19 @@ +# Common options for the ESP-Shelly-C38F SoC + +packages: + - !include esp32c3.yaml + +esphome: + platformio_options: + board_build.flash_mode: dio + +esp32: + variant: esp32c3 + # cpu_frequency: 240MHz + flash_size: 8MB + framework: + type: esp-idf + sdkconfig_options: + CONFIG_MBEDTLS_SSL_PROTO_TLS1_3: "y" # TLS1.3 support still isn't enabled by default in IDF 5.5.2 + # CONFIG_MBEDTLS_DEFAULT_CERTIFICATE_BUNDLE: CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN + # CONFIG_LWIP_NETIF_LOOPBACK: "n" # Apparently there's a bug causing the ICMPv6 Router Solicitation to go out over the loopback interface rather than wifi diff --git a/common/esp32-u4wdh.yaml b/common/esp32-u4wdh.yaml new file mode 100644 index 0000000..1fbdf03 --- /dev/null +++ b/common/esp32-u4wdh.yaml @@ -0,0 +1,25 @@ +# Common options for the ESP32-U4WDH SoC +# CPU: ESP32-U4WDH (revision v3.1) +# Features: Wi-Fi, BT, Dual Core + LP Core, 240MHz, Embedded Flash, Vref calibration in eFuse, Coding Scheme None +# Crystal frequency: 40MHz +# Memory: 320KB RAM, 4MB Flash + +packages: + - !include esp32.yaml + +esphome: + platformio_options: + board_build.flash_mode: dio + +esp32: + variant: esp32 + # cpu_frequency: 240MHz + flash_size: 4MB + framework: + type: esp-idf + # advanced: + # minimum_chip_revision: "3.1" # Requires ESPHome 2026.1.0 ? + sdkconfig_options: + CONFIG_MBEDTLS_SSL_PROTO_TLS1_3: "y" # TLS1.3 support still isn't enabled by default in IDF 5.5.2 + CONFIG_MBEDTLS_DEFAULT_CERTIFICATE_BUNDLE: CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN + # CONFIG_LWIP_NETIF_LOOPBACK: "n" # Apparently there's a bug causing the ICMPv6 Router Solicitation to go out over the loopback interface rather than wifi diff --git a/common/esp32.yaml b/common/esp32.yaml new file mode 100644 index 0000000..40eb951 --- /dev/null +++ b/common/esp32.yaml @@ -0,0 +1,17 @@ +esp32: + variant: esp32 + framework: + type: esp-idf + sdkconfig_options: + CONFIG_MBEDTLS_SSL_PROTO_TLS1_3: "y" # TLS1.3 support still isn't enabled by default in IDF 5.5.2 + # CONFIG_LWIP_NETIF_LOOPBACK: "n" # Apparently there's a bug causing the ICMPv6 Router Solicitation to go out over the loopback interface rather than wifi + + # -- begin copied from https://github.com/athom-tech/esp32-configs -- + # @grigi found in testing that these options resulted in better responsiveness. + # BLE 4.2 is supported by ALL ESP32 boards that have bluetooth, the original and derivatives. + CONFIG_BT_BLE_42_FEATURES_SUPPORTED: y + # Also enable this on any derivative boards (S2, C3 etc) but not the original ESP32. + # CONFIG_BT_BLE_50_FEATURES_SUPPORTED: y + # Extend the watchdog timeout, so the device reboots if the device appears locked up for over 10 seconds. + CONFIG_ESP_TASK_WDT_TIMEOUT_S: "10" + # -- end copied from https://github.com/athom-tech/esp32-configs -- diff --git a/common/esp32c3.yaml b/common/esp32c3.yaml new file mode 100644 index 0000000..24034c2 --- /dev/null +++ b/common/esp32c3.yaml @@ -0,0 +1,17 @@ +esp32: + variant: esp32c3 + framework: + type: esp-idf + sdkconfig_options: + CONFIG_MBEDTLS_SSL_PROTO_TLS1_3: "y" # TLS1.3 support still isn't enabled by default in IDF 5.5.2 + # CONFIG_LWIP_NETIF_LOOPBACK: "n" # Apparently there's a bug causing the ICMPv6 Router Solicitation to go out over the loopback interface rather than wifi + + # -- begin copied from https://github.com/athom-tech/esp32-configs -- + # @grigi found in testing that these options resulted in better responsiveness. + # BLE 4.2 is supported by ALL ESP32 boards that have bluetooth, the original and derivatives. + # CONFIG_BT_BLE_42_FEATURES_SUPPORTED: y + # Also enable this on any derivative boards (S2, C3 etc) but not the original ESP32. + CONFIG_BT_BLE_50_FEATURES_SUPPORTED: y + # Extend the watchdog timeout, so the device reboots if the device appears locked up for over 10 seconds. + # CONFIG_ESP_TASK_WDT_TIMEOUT_S: "10" + # -- end copied from https://github.com/athom-tech/esp32-configs -- diff --git a/common/factory-reset.yaml b/common/factory-reset.yaml new file mode 100644 index 0000000..1b9f0e4 --- /dev/null +++ b/common/factory-reset.yaml @@ -0,0 +1,13 @@ +factory_reset: + resets_required: 10 + max_delay: 10s + on_increment: + - logger.log: + format: "(factory_reset) Fast power cycle count now %u, target %u" + args: [x, target] + +button: + - platform: factory_reset + id: button_factory_reset + name: Factory reset + disabled_by_default: true diff --git a/common/flash-write-interval.yaml b/common/flash-write-interval.yaml new file mode 100644 index 0000000..6755388 --- /dev/null +++ b/common/flash-write-interval.yaml @@ -0,0 +1,2 @@ +preferences: + flash_write_interval: 5min diff --git a/common/libretiny-mdns-fix.yaml b/common/libretiny-mdns-fix.yaml new file mode 100644 index 0000000..d94b7e5 --- /dev/null +++ b/common/libretiny-mdns-fix.yaml @@ -0,0 +1,4 @@ +esphome: + platformio_options: + platform_packages: + - "library-lwip @ https://github.com/dlitz/lwip.git#mdns-big-txt_bdk" diff --git a/common/ota-update-password.yaml b/common/ota-update-password.yaml new file mode 100644 index 0000000..a4821fd --- /dev/null +++ b/common/ota-update-password.yaml @@ -0,0 +1,35 @@ +substitutions: + ota_password: !secret fallback_ota_password + ota_new_password: "" + ota_update_password: 0 + +esphome: + on_boot: + - lambda: |- + #define OTA_UPDATE_PASSWORD ${ota_update_password} + #if !(OTA_UPDATE_PASSWORD == 0 || OTA_UPDATE_PASSWORD == 1) + # error ota_update_password must be 0 or 1 + #endif + #ifdef USE_OTA_PASSWORD + const bool ota_update_password = bool(OTA_UPDATE_PASSWORD); + const std::string ota_new_password = "${ota_new_password}"; + if (ota_update_password) { + if (ota_new_password.empty()) { + ESP_LOGI("ota_update_password", "Setting blank OTA password"); + } else { + ESP_LOGI("ota_update_password", "Setting new OTA password"); + } + id(ota_id).set_auth_password(ota_new_password); + } else { + ESP_LOGI("ota_update_password", "No new OTA password"); + } + #elif OTA_UPDATE_PASSWORD == 1 + # error Cannot set new OTA password - not compiled with OTA support (hint: set a dummy ota_password) + #else + # warning No OTA password set. + #endif + +ota: + - id: !extend ota_id + platform: esphome + password: "${ota_password or ota_new_password}" diff --git a/common/ota.yaml b/common/ota.yaml new file mode 100644 index 0000000..a5083d5 --- /dev/null +++ b/common/ota.yaml @@ -0,0 +1,7 @@ +substitutions: + ota_password: !secret fallback_ota_password + +ota: + - platform: esphome + id: ota_id + password: "${ota_password}" diff --git a/common/restart.yaml b/common/restart.yaml new file mode 100644 index 0000000..d222351 --- /dev/null +++ b/common/restart.yaml @@ -0,0 +1,4 @@ +button: + - platform: restart + id: button_restart + name: Restart diff --git a/common/safe-mode.yaml b/common/safe-mode.yaml new file mode 100644 index 0000000..499f4a4 --- /dev/null +++ b/common/safe-mode.yaml @@ -0,0 +1,7 @@ +safe_mode: + reboot_timeout: 10min + +button: + - platform: safe_mode + id: button_safe_mode + name: Safe Mode Boot diff --git a/common/shelly-button.yaml b/common/shelly-button.yaml new file mode 100644 index 0000000..43f34c9 --- /dev/null +++ b/common/shelly-button.yaml @@ -0,0 +1,42 @@ +substitutions: + # shelly_button_gpio_number: GPIO1 + shelly_button_gpio_pin: + number: ${shelly_button_gpio_number} + mode: + input: true + pullup: true + inverted: true + shelly_button_default_action: [] + shelly_button_longpress_action: [] + # Separate _internal substitutions so that assigning to the above replaces + # (rather than appends to) the default action. + _shelly_button_default_action_internal: + - button.press: button_safe_mode + _shelly_button_longpress_action_internal: + - button.press: button_factory_reset + +binary_sensor: + - platform: gpio + id: shelly_button_id + name: "Button" + entity_category: diagnostic + pin: ${shelly_button_gpio_pin} + filters: + - settle: 10ms + on_multi_click: + - timing: + - ON for at least 5s + then: + - logger.log: "Button held for 5s" + - timing: + - ON for at least 10s + then: + - logger.log: "Button held for 10s" + - timing: + - ON for 5s to 10s + - OFF for at least 100ms + then: ${shelly_button_default_action or _shelly_button_default_action_internal} + - timing: + - ON for at least 10s + - OFF for at least 100ms + then: ${shelly_button_longpress_action or _shelly_button_longpress_action_internal} diff --git a/common/shelly-header-gpio.yaml b/common/shelly-header-gpio.yaml new file mode 100644 index 0000000..e74d6d9 --- /dev/null +++ b/common/shelly-header-gpio.yaml @@ -0,0 +1,16 @@ +substitutions: + # shelly_header_gpio_number: GPIO19 + shelly_header_gpio_pin: + number: ${shelly_header_gpio_number} + inverted: true + mode: + input: true + pullup: true + +binary_sensor: + - platform: gpio + id: binary_sensor_header_gpio_id + name: "Header GPIO" + entity_category: diagnostic + disabled_by_default: true + pin: ${shelly_header_gpio_pin} diff --git a/common/shelly-ntc-temperature.yaml b/common/shelly-ntc-temperature.yaml new file mode 100644 index 0000000..0322fba --- /dev/null +++ b/common/shelly-ntc-temperature.yaml @@ -0,0 +1,26 @@ +substitutions: + # shelly_ntc_temperature_pin: GPIO3 + +sensor: + - platform: ntc + id: ntc_temperature + name: "NTC Temperature" + entity_category: diagnostic + unit_of_measurement: "°C" + accuracy_decimals: 1 + icon: "mdi:thermometer" + sensor: temp_resistance_reading + calibration: + b_constant: 3350 + reference_resistance: 10kOhm + reference_temperature: 298.15K + - platform: resistance + id: temp_resistance_reading + sensor: temp_analog_reading + configuration: DOWNSTREAM + resistor: 10kOhm + - platform: adc + id: temp_analog_reading + pin: ${shelly_ntc_temperature_pin} + attenuation: 12db + diff --git a/common/time.yaml b/common/time.yaml new file mode 100644 index 0000000..c77a7db --- /dev/null +++ b/common/time.yaml @@ -0,0 +1,11 @@ +time: + - platform: homeassistant + id: homeassistant_time + # on_time_sync: + # - logger.log: "HA time synchronized!" + + - platform: sntp + id: sntp_time + # on_time_sync: + # - logger.log: "SNTP time synchronized!" + diff --git a/common/uptime-info.yaml b/common/uptime-info.yaml new file mode 100644 index 0000000..004f870 --- /dev/null +++ b/common/uptime-info.yaml @@ -0,0 +1,16 @@ +# Requires time.yaml + +sensor: + - platform: uptime + id: uptime_seconds + type: seconds + name: Uptime + disabled_by_default: true + + - platform: uptime + id: uptime_timestamp + type: timestamp + name: Last Boot + # FIXME: Fix the uptime component so that it can use any configured time + # source, rather than just the first one. + time_id: sntp_time diff --git a/common/wifi-info.yaml b/common/wifi-info.yaml new file mode 100644 index 0000000..614a2e1 --- /dev/null +++ b/common/wifi-info.yaml @@ -0,0 +1,42 @@ +sensor: + - platform: wifi_signal # Reports the WiFi signal strength/RSSI in dB + name: "WiFi Signal dB" + id: wifi_signal_db + update_interval: 60s + + - platform: copy # Reports the WiFi signal strength in % + id: wifi_signal_percent + source_id: wifi_signal_db + name: "WiFi Signal Percent" + filters: + - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0); + unit_of_measurement: "Signal %" + entity_category: diagnostic + device_class: "" + +text_sensor: + - platform: wifi_info + ip_address: + name: Device IP Address + address_0: + name: Device IP Address 0 + address_1: + name: Device IP Address 1 + address_2: + name: Device IP Address 2 + # address_3: + # name: Device IP Address 3 + # address_4: + # name: Device IP Address 4 + ssid: + name: Device Connected SSID + bssid: + name: Device Connected BSSID + mac_address: + name: Device MAC Address + # scan_results: + # name: Device Latest Scan Results + # dns_address: + # name: Device DNS Address + # power_save_mode: + # name: Device Wifi Power Save Mode diff --git a/components/.gitignore b/components/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/huacaoe-tywb-4ch-common.yaml b/huacaoe-tywb-4ch-common.yaml new file mode 100644 index 0000000..839bc18 --- /dev/null +++ b/huacaoe-tywb-4ch-common.yaml @@ -0,0 +1,268 @@ +substitutions: + # Use BK7231GUIFlashTool to dump tuya_config.json and then paste the result here. + # tuya_config: { ... } + button_default_action: [] + _button_default_action_internal: + - button.press: button_toggle_all + + # Setting this to true allows LT_AUTO_DOWNLOAD_REBOOT to work, but disables the K3 button. + debug_remove_button_k3: false # set true for LT_AUTO_DOWNLOAD_REBOOT support + +packages: + # - !include common/bk72xx-mdns-debug.yaml + - !include common/bk72xx-mdns-fix.yaml + - !include common/cpu-temperature.yaml + - !include common/factory-reset.yaml + - !include common/flash-write-interval.yaml + - !include common/git-branch.yaml + - !include common/ota.yaml + - !include common/ota-update-password.yaml + - !include common/restart.yaml + - !include common/safe-mode.yaml + - !include common/time.yaml + - !include common/uptime-info.yaml + - !include common/wifi-info.yaml + +esphome: + name: huacaoe-tywb-4ch-common + friendly_name: "Huacaoe 4-Channel Relay" + comment: "Huacaoe TYWB 4ch-RF 4-channel relay (common template)" + name_add_mac_suffix: true + project: + name: "dlitz.huacaoe-tywb-4ch-common" + version: "v0.0.0" + platformio_options: + upload_speed: 2_000_000 + +bk72xx: + board: cb3s # Flash size: 2MiB, CPU Frequency: 120MHz + framework: # LibreTiny + options: + # LT_LOG_HEAP: 1 + LT_AUTO_DOWNLOAD_REBOOT: 1 # this only works when the RXD1 GPIO is configured as a serial port + # MDNS_TXT_RDATA_SIZE: 384 # https://github.com/esphome/esphome/issues/11281 + LWIP_DHCP_DOES_ACD_CHECK: 0 # https://github.com/libretiny-eu/libretiny/issues/316 + + # version: "1.9.2" + # source: "https://github.com/dlitz/libretiny.git#mdns-big-txt" + +external_components: + - source: + type: git + url: https://github.com/dlitz/esphome-configs-dlitz.git + ref: ${git_branch} + refresh: always + +# Logs are output on the TXD2 pin (Pin 11 - third from the bottom on the right side) +logger: + level: DEBUG + baud_rate: 115200 + +debug: + +# Enable Home Assistant API with dynamic key +api: + encryption: + +wifi: + # Enable fallback hotspot (captive portal) in case wifi connection fails + ap: + password: !secret fallback_ap_password + +captive_portal: + +network: + enable_ipv6: true + +button: + - platform: template + id: button_toggle_all + name: Toggle All + on_press: + - if: + any: + - switch.is_on: relay_1 + - switch.is_on: relay_2 + - switch.is_on: relay_3 + - switch.is_on: relay_4 + then: + - switch.turn_off: relay_1 + - switch.turn_off: relay_2 + - switch.turn_off: relay_3 + - switch.turn_off: relay_4 + - logger.log: "Toggle All button pressed: Turned all off" + else: + - switch.turn_on: relay_1 + - switch.turn_on: relay_2 + - switch.turn_on: relay_3 + - switch.turn_on: relay_4 + - logger.log: "Toggle All button pressed: Turned all on" + +light: + - platform: status_led + name: "Status LED" + id: status_led_id + disabled_by_default: true + entity_category: diagnostic + pin: + number: "${tuya_config.netled1_pin}" + inverted: ${not(tuya_config.netled1_lv | int)} + effects: + - strobe: + name: Blink + colors: + - state: true + duration: 250ms + - state: false + duration: 250ms + - strobe: + name: Fast Blink + colors: + - state: true + duration: 50ms + - state: false + duration: 50ms + +binary_sensor: + - platform: status + name: "Status" + id: status_sensor + + - platform: gpio + id: toggle_all_button + pin: + number: "${tuya_config.total_bt_pin}" + inverted: ${not(tuya_config.total_bt_lv | int)} + mode: + input: true + pullup: true + filters: + - settle: 10ms + name: "Reset, Toggle All Button" + on_multi_click: + # FIXME: status_led effects get overridden when app is in a warning or error state + - timing: + - ON for at most 1s + - OFF for at least 10ms + then: ${button_default_action or _button_default_action_internal} + - timing: + - ON for at least 2s + then: + - light.turn_on: + id: status_led_id + effect: Blink + - timing: + - ON for 2s to 10s + - OFF for at least 10ms + then: + - logger.log: "Button held for 2-10s: Restart" + - button.press: button_restart + - timing: + - ON for at least 10s + then: + - light.turn_on: + id: status_led_id + effect: Fast Blink + - timing: + - ON for at least 10s + - OFF for at least 10ms + then: + - logger.log: "Button held for over 10s: Factory reset" + - button.press: button_factory_reset + + - platform: gpio + name: "Button K1" + id: button_k1 + pin: + number: "${tuya_config.bt1_pin}" + inverted: ${not(tuya_config.bt1_lv | int)} + mode: + input: true + pullup: true + filters: + - settle: 10ms + on_click: + then: + - switch.toggle: relay_1 + - logger.log: "Button K1 pressed: Toggle Relay 1" + + - platform: gpio + name: "Button K2" + id: button_k2 + pin: + number: "${tuya_config.bt2_pin}" + inverted: ${not(tuya_config.bt2_lv | int)} + mode: + input: true + pullup: true + filters: + - settle: 10ms + on_click: + then: + - switch.toggle: relay_2 + - logger.log: "Button K2 pressed: Toggle Relay 2" + + - platform: gpio + name: "Button K3" + id: button_k3 + pin: + number: "${tuya_config.bt3_pin}" + inverted: ${not(tuya_config.bt3_lv | int)} + mode: + input: true + pullup: true + filters: + - settle: 10ms + on_click: + then: + - switch.toggle: relay_3 + - logger.log: "Button K3 pressed: Toggle Relay 3" + + - platform: gpio + name: "Button K4" + id: button_k4 + pin: + number: "${tuya_config.bt4_pin}" + inverted: ${not(tuya_config.bt4_lv | int)} + mode: + input: true + pullup: true + filters: + - settle: 10ms + on_click: + then: + - switch.toggle: relay_4 + - logger.log: "Button K4 pressed: Toggle Relay 4" + + # This allows LT_AUTO_DOWNLOAD_REBOOT to work, but breaks the button. + - id: !remove ${ "button_k3" if debug_remove_button_k3 else ""} + +switch: + - platform: gpio + name: "Relay 1" + id: relay_1 + pin: + number: "${tuya_config.rl1_pin}" + inverted: ${not(tuya_config.rl1_lv | int)} + + - platform: gpio + name: "Relay 2" + id: relay_2 + pin: + number: "${tuya_config.rl2_pin}" + inverted: ${not(tuya_config.rl2_lv | int)} + + - platform: gpio + id: relay_3 + name: "Relay 3" + pin: + number: "${tuya_config.rl3_pin}" + inverted: ${not(tuya_config.rl3_lv | int)} + + - platform: gpio + id: relay_4 + name: "Relay 4" + pin: + number: "${tuya_config.rl4_pin}" + inverted: ${not(tuya_config.rl4_lv | int)} + diff --git a/huacaoe-tywb-4ch-grey.yaml b/huacaoe-tywb-4ch-grey.yaml new file mode 100644 index 0000000..87b6036 --- /dev/null +++ b/huacaoe-tywb-4ch-grey.yaml @@ -0,0 +1,21 @@ +# https://www.amazon.ca/dp/B0DF7LPGRK + +substitutions: + # tuya_config.json generated by BK7231GUIFlashTool + tuya_config: {"rl1_lv":"1","bt1_pin":"10","net_trig":"4","netled1_lv":"0","onoff_rst_type":"0","total_bt_pin":"9","nety_led":"1","total_stat":"0","bt1_lv":"0","remote_add_dp":"49","remote_list_dp":"50","rf_width":"345","module":"CB3S","rl2_pin":"6","series_ctrl":"0","cyc_dpid":"43","bt2_lv":"0","rl1_pin":"24","rl4_lv":"1","onoff_rst_m":"0","rl3_pin":"26","rl3_lv":"1","rand_dpid":"42","rl4_pin":"14","jv":"100","bt3_lv":"0","reset_t":"5","netled1_pin":"20","rl2_lv":"1","net_type":"0","bt4_pin":"7","inch_dp":"44","bt3_pin":"8","ch_cddpid1":"9","remote_io":"22","init_conf":"38","bt4_lv":"0","zero_select":"0","onoff_type":"2","bt2_pin":"11","ch_cddpid4":"12","ch_cddpid2":"10","ch_cddpid3":"11","total_bt_lv":"0","remote_select":"1","ch_num":"4","ch_dpid3":"3","ch_dpid4":"4","netn_led":"0","ch_dpid1":"1","ch_dpid2":"2","crc":"79"} + +dashboard_import: + package_import_url: github://dlitz/esphome-configs-dlitz/huacaoe-tywb-4ch-grey.yaml@${git_branch} + # import_full_config: true + +packages: + - !include huacaoe-tywb-4ch-common.yaml + +esphome: + name: huacaoe-tywb-4ch-grey + friendly_name: "Huacaoe 4-Channel Relay" + comment: "Huacaoe TYWB 4ch-RF 4-channel relay (grey, 7-32V power, 5-pin external switch port, ASIN:B0DSM7PLDT)" + name_add_mac_suffix: true + project: + name: "dlitz.huacaoe-tywb-4ch-grey" + version: "v0.0.0" diff --git a/huacaoe-tywb-4ch-white.yaml b/huacaoe-tywb-4ch-white.yaml new file mode 100644 index 0000000..e4db08f --- /dev/null +++ b/huacaoe-tywb-4ch-white.yaml @@ -0,0 +1,20 @@ +# https://www.amazon/ca/dp/B0DF7LPGRK + +substitutions: + # tuya_config.json generated by BK7231GUIFlashTool + tuya_config: {"rl1_lv":"1","bt1_pin":"8","net_trig":"4","netled1_lv":"0","onoff_rst_type":"0","total_bt_pin":"7","nety_led":"0","total_stat":"0","bt1_lv":"0","remote_add_dp":"49","remote_list_dp":"50","module":"CB3S","rl2_pin":"6","series_ctrl":"0","cyc_dpid":"43","bt2_lv":"0","rl1_pin":"24","rl4_lv":"1","onoff_rst_m":"0","rl3_pin":"26","rl3_lv":"1","rand_dpid":"42","rl4_pin":"14","jv":"100","bt3_lv":"0","reset_t":"7","netled1_pin":"22","rl2_lv":"1","net_type":"0","bt4_pin":"11","inch_dp":"44","bt3_pin":"10","ch_cddpid1":"9","init_conf":"38","bt4_lv":"0","zero_select":"0","onoff_type":"2","bt2_pin":"9","ch_cddpid4":"12","ch_cddpid2":"10","ch_cddpid3":"11","total_bt_lv":"0","remote_select":"0","ch_num":"4","ch_dpid3":"3","ch_dpid4":"4","netn_led":"0","ch_dpid1":"1","ch_dpid2":"2","crc":"73"} + +dashboard_import: + package_import_url: github://dlitz/esphome-configs-dlitz/huacaoe-tywb-4ch-white.yaml@${git_branch} + # import_full_config: true + +packages: + - !include huacaoe-tywb-4ch-common.yaml + +esphome: + name: "huacaoe-tywb-4ch-white" + friendly_name: "Huacaoe 4-Channel Relay" + comment: "Huacaoe TYWB 4ch-RF 4-channel relay (white, 7-32V power, no external switch port, ASIN:B0DF7LPGRK)" + project: + name: "dlitz.huacaoe-tywb-4ch-white" + version: "v0.0.0" diff --git a/secrets.example.yaml b/secrets.example.yaml new file mode 100644 index 0000000..b7d500e --- /dev/null +++ b/secrets.example.yaml @@ -0,0 +1,2 @@ +fallback_ap_password: "" +fallback_ota_password: "" diff --git a/shelly-em-gen3.yaml b/shelly-em-gen3.yaml new file mode 100644 index 0000000..81f0efc --- /dev/null +++ b/shelly-em-gen3.yaml @@ -0,0 +1,281 @@ +# Shelly EM Gen3 +# PCB markings: "EM Gen3_v0.1.3", "B2519" +# SoC: ESP-Shelly-C38F (similar to ESP32-C3) +# CPU: ESP32-C3 (QFN32) (revision v0.4) +# Features: Wi-Fi, BT 5 (LE), Single Core, 160MHz, Embedded Flash 8MB (GD) +# Crystal frequency: 40MHz +# References: +# - https://devices.esphome.io/devices/shelly-em-gen3/ +# - https://community.home-assistant.io/t/shelly-em-gen3-solved/944171 +# - https://kb.shelly.cloud/knowledge-base/shelly-em-gen3 +# +# GPIOs: +# GPIO0 - Dry-contact relay (normally open) +# GPIO1 - Pushbutton +# GPIO3 - ADC for NTC temperature probe +# GPIO6 - I2C SCL +# GPIO7 - I2C SDA +# GPIO9 - Status LED (active low) +# GPIO18 - Pin 1 of 7-pin header +# +# I2C devices: +# 0x38 - ADE7953 Power Sensor +# 0x51 - AiP8563 Battery-backed real-time clock (not configured here) + +# Outputting LOW on GPIO5 seems to produce HIGH on GPIO4, and vice versa + +substitutions: + # Set dual_sensors to false to remove the "B" and "Total" sensors + dual_sensors: true + + # How often to update the power meter. + update_interval: 20s + + # Calibrated on US-style 120V power with 50A current transformers. + # You may need to calibrate this yourself if the numbers are inaccurate. + calibration_voltage_gain: 0x32c78f # Calibrated on device E4:B0:63:D4:3E:00 + calibration_current_gain_a: 0x490bb5 # Calibrated on device E4:B0:63:D4:3E:00 + calibration_current_gain_b: 0x498c15 # Calibrated on device E4:B0:63:D4:3E:00 + + # GPIOs for this type of device + shelly_button_gpio_number: GPIO1 + shelly_header_gpio_number: GPIO18 + shelly_ntc_temperature_pin: GPIO3 + +dashboard_import: + package_import_url: github://dlitz/esphome-configs-dlitz/shelly-em-gen3.yaml@${git_branch} + +packages: + - !include common/esp-shelly-c38f.yaml + - !include common/cpu-temperature.yaml + - !include common/factory-reset.yaml + - !include common/flash-write-interval.yaml + - !include common/git-branch.yaml + - !include common/ota.yaml + - !include common/ota-update-password.yaml + - !include common/restart.yaml + - !include common/safe-mode.yaml + - !include common/shelly-button.yaml + - !include common/shelly-header-gpio.yaml + - !include common/shelly-ntc-temperature.yaml + - !include common/time.yaml + - !include common/uptime-info.yaml + - !include common/wifi-info.yaml + +esphome: + name: "shelly-em-gen3" + friendly_name: "Shelly EM Gen3" + comment: "Shelly EM Gen3 - Smart wifi and bluetooth energy meter with dual current transformers" + name_add_mac_suffix: true + project: + name: "dlitz.shelly-em-gen3" + version: "v0.0.0" + min_version: 2025.11.3 # for ade7953 bugfix #12180 + +# Enable logging +logger: + level: DEBUG + hardware_uart: uart0 + +# Enable Home Assistant API with dynamic key +api: + encryption: + +wifi: + # ssid: !secret wifi_ssid + # password: !secret wifi_password + # fast_connect: true + enable_btm: true + enable_rrm: true + min_auth_mode: WPA2 + + # Enable fallback hotspot (captive portal) in case wifi connection fails + ap: + password: !secret fallback_ap_password + +captive_portal: + +network: + enable_ipv6: true + +i2c: + scl: GPIO6 + sda: GPIO7 + scan: true + +# esp32_ble_tracker: +# scan_parameters: +# active: false +# +# bluetooth_proxy: +# active: false + +light: + - platform: status_led + name: "Status LED" + id: status_led_id + disabled_by_default: true + entity_category: diagnostic + pin: + number: GPIO9 + inverted: true + ignore_strapping_warning: true + +switch: + # Dry-contact relay - normally open + - platform: gpio + name: Relay + id: relay_id + pin: GPIO0 + +sensor: + # Note that on the Shelly EM Gen3, channels "A" and "B" are swapped between the chipset and the labels on the device. + # This device also apparently does not need/have an irq_pin. + - platform: ade7953_i2c + id: ade7953_id + update_interval: ${update_interval} + use_accumulated_energy_registers: true + + voltage_gain: ${calibration_voltage_gain} + current_gain_a: ${calibration_current_gain_a} + current_gain_b: ${calibration_current_gain_b} + + voltage: + id: line_voltage + name: Voltage + frequency: + id: line_frequency + name: Frequency + current_b: + id: current_a + name: ${ "Current A" if dual_sensors else "Current" } + accuracy_decimals: 3 + current_a: + id: current_b + name: Current B + accuracy_decimals: 3 + power_factor_b: + id: power_factor_a + name: ${ "Power Factor A" if dual_sensors else "Power Factor" } + filters: + - multiply: -1 + power_factor_a: + id: power_factor_b + name: "Power Factor B" + filters: + - multiply: -1 + apparent_power_b: + id: apparent_power_a + name: ${ "Apparent Power A" if dual_sensors else "Apparent Power" } + apparent_power_a: + id: apparent_power_b + name: "Apparent Power B" + active_power_b: + id: active_power_a + name: ${ "Power A" if dual_sensors else "Power" } + active_power_a: + id: active_power_b + name: "Power B" + reactive_power_b: + id: reactive_power_a + name: ${ "Reactive Power A" if dual_sensors else "Reactive Power" } + filters: + - multiply: -1 + reactive_power_a: + id: reactive_power_b + name: "Reactive Power B" + filters: + - multiply: -1 + + - platform: integration + id: energy_a + name: ${ "Energy A" if dual_sensors else "Energy" } + sensor: active_power_a + restore: false + time_unit: h + icon: mdi:meter-electric + unit_of_measurement: "kWh" + state_class: total_increasing + device_class: energy + accuracy_decimals: 3 + filters: + - multiply: 0.001 + + - platform: integration + id: energy_b + name: 'Energy B' + sensor: active_power_b + restore: false + time_unit: h + icon: mdi:meter-electric + unit_of_measurement: "kWh" + state_class: total_increasing + device_class: energy + accuracy_decimals: 3 + filters: + - multiply: 0.001 + + - platform: total_daily_energy + id: daily_energy_consumed_a + name: ${ "Daily Energy Consumed A" if dual_sensors else "Daily Energy Consumed" } + power_id: active_power_a + icon: "mdi:hours-24" + unit_of_measurement: "kWh" + state_class: total_increasing + device_class: energy + accuracy_decimals: 3 + filters: + - multiply: 0.001 + + - platform: total_daily_energy + id: daily_energy_consumed_b + name: "Daily Energy Consumed B" + power_id: active_power_b + icon: "mdi:hours-24" + unit_of_measurement: "kWh" + state_class: total_increasing + device_class: energy + accuracy_decimals: 3 + filters: + - multiply: 0.001 + + - platform: combination + id: power_ab + type: sum + name: "Power Total" + sources: + - source: active_power_a + - source: active_power_b + device_class: power + state_class: measurement + + - platform: combination + id: energy_ab + type: sum + name: "Energy Total" + sources: + - source: energy_a + - source: energy_b + state_class: total_increasing + accuracy_decimals: 3 + + - platform: combination + id: daily_energy_consumed_ab + type: sum + name: "Daily Energy Consumed Total" + sources: + - source: daily_energy_consumed_a + - source: daily_energy_consumed_b + state_class: total_increasing + accuracy_decimals: 3 + + - id: !remove ${ "active_power_b" if not dual_sensors else "" } + - id: !remove ${ "apparent_power_b" if not dual_sensors else "" } + - id: !remove ${ "current_b" if not dual_sensors else "" } + - id: !remove ${ "daily_energy_consumed_b" if not dual_sensors else "" } + - id: !remove ${ "daily_energy_consumed_ab" if not dual_sensors else "" } + - id: !remove ${ "energy_ab" if not dual_sensors else "" } + - id: !remove ${ "energy_b" if not dual_sensors else "" } + - id: !remove ${ "power_ab" if not dual_sensors else "" } + - id: !remove ${ "power_factor_b" if not dual_sensors else "" } + - id: !remove ${ "reactive_power_b" if not dual_sensors else "" } diff --git a/shelly-plus-1pm-ul.yaml b/shelly-plus-1pm-ul.yaml new file mode 100644 index 0000000..59bc08e --- /dev/null +++ b/shelly-plus-1pm-ul.yaml @@ -0,0 +1,249 @@ +# Shelly Plus 1PM UL +# PCB markings: "Shelly 1PM+_v0.2.2", "B2519" +# CPU: ESP32-U4WDH (revision v3.1) +# Features: Wi-Fi, BT, Dual Core + LP Core, 240MHz, Embedded Flash, Vref calibration in eFuse, Coding Scheme None +# Crystal frequency: 40MHz +# References: +# - https://devices.esphome.io/devices/shelly-plus-1pm/ +# - https://kb.shelly.cloud/knowledge-base/shelly-plus-1pm-ul +# +# GPIOs: +# GPIO0 - Status LED and programming strapping pin +# GPIO5 - BL0937 CF pin +# GPIO18 - BL0937 CF1 pin +# GPIO19 - Pin 1 of 7-pin header +# GPIO23 - BL0937 SEL pin +# GPIO25 - Pushbutton +# GPIO26 - Dry-contact relay (normally open) +# GPIO32 - ADC for NTC temperature probe + +substitutions: + device_name: "Shelly Plus 1PM UL" + # Higher value gives lower watt readout + current_res: "0.001" + # Lower value gives lower voltage readout + voltage_div: "1925" + shelly_button_gpio_number: GPIO25 + shelly_header_gpio_number: GPIO19 + shelly_ntc_temperature_pin: GPIO32 + calibration_relay_adc_multiplier: 0.96184045 # Calibrated on device 88:13:bf:a1:be:b4 + name: shelly-plus-1pm-ul + friendly_name: "${device_name}" + room: "" + device_description: "Shelly Plus 1PM UL - UL-certified small form factor smart switch with power measurement" + +dashboard_import: + package_import_url: github://dlitz/esphome-configs-dlitz/shelly-plus-1pm-ul.yaml@${git_branch} + +packages: + - !include common/esp32-u4wdh.yaml + - !include common/cpu-temperature.yaml + - !include common/factory-reset.yaml + - !include common/flash-write-interval.yaml + - !include common/git-branch.yaml + - !include common/ota.yaml + - !include common/ota-update-password.yaml + - !include common/restart.yaml + - !include common/safe-mode.yaml + - !include common/shelly-button.yaml + - !include common/shelly-header-gpio.yaml + - !include common/shelly-ntc-temperature.yaml + - !include common/time.yaml + - !include common/uptime-info.yaml + - !include common/wifi-info.yaml + +external_components: + - source: + type: git + url: https://github.com/dlitz/esphome-configs-dlitz.git + ref: ${git_branch} + refresh: always + +esphome: + name: "${name}" + friendly_name: "${friendly_name}" + area: "${room}" + comment: "${device_description}" + name_add_mac_suffix: true + project: + name: "dlitz.shelly-plus-1pm-ul" + version: "v0.0.0" + +logger: + level: DEBUG + hardware_uart: uart0 + +debug: + +api: + encryption: + +wifi: + # ssid: !secret wifi_ssid + # password: !secret wifi_password + # fast_connect: true + enable_btm: true + enable_rrm: true + min_auth_mode: WPA2 + + # Enable fallback hotspot (captive portal) in case wifi connection fails + ap: + password: !secret fallback_ap_password + +# web_server: + +captive_portal: + +output: + - platform: gpio + id: "relay_output" + pin: GPIO26 + +switch: + - platform: output + id: "relay" + name: "Relay" + output: "relay_output" + +binary_sensor: + - platform: gpio + name: "Switch" + pin: GPIO4 + on_press: + then: + - switch.turn_on: "relay" + on_release: + then: + - switch.turn_off: "relay" + filters: + - delayed_on_off: 50ms + +sensor: + - id: !extend temp_analog_reading + update_interval: 5s + - id: !extend ntc_temperature + on_value_range: + - above: "80.0" + then: + - switch.turn_off: "relay" + + - platform: adc + name: "Relay Supply Voltage" + pin: GPIO33 + attenuation: 12db + filters: + - multiply: ${ 8 * calibration_relay_adc_multiplier } + + - platform: hlw8012 + model: BL0937 + id: bl0937_id + sel_pin: + number: GPIO23 + inverted: true + cf_pin: + number: GPIO5 + ignore_strapping_warning: true + cf1_pin: GPIO18 + current_resistor: ${current_res} + voltage_divider: ${voltage_div} + current: + name: "Current" + id: current + unit_of_measurement: A + accuracy_decimals: 3 + icon: mdi:flash-outline + on_value: + then: + - component.update: apparent_power + # on_value_range: + # - above: "16" + # then: + # - switch.turn_off: "relay" + # - logger.log: "Over-current protection triggered!" + voltage: + name: "Voltage" + id: voltage + unit_of_measurement: V + accuracy_decimals: 1 + icon: mdi:flash-outline + on_value: + then: + - component.update: apparent_power + # on_value_range: + # - above: "250" + # then: + # - switch.turn_off: "relay" + # - logger.log: "Over-voltage protection triggered!" + power: + name: "Power" + unit_of_measurement: W + id: power + icon: mdi:flash-outline + on_value_range: + - above: "3600" + then: + - switch.turn_off: "relay" + - logger.log: "Over-power protection triggered!" + change_mode_every: 2 + update_interval: 10s + + - platform: total_daily_energy + id: daily_energy_consumed + name: "Daily Energy Consumed" + power_id: power + filters: + - multiply: 0.001 + unit_of_measurement: kWh + icon: mdi:clock-alert + + - platform: template + id: apparent_power + name: "Apparent Power" + state_class: measurement + device_class: apparent_power + unit_of_measurement: "VA" + update_interval: never + lambda: |- + return id(voltage).state * id(current).state; + on_value: + then: + - component.update: power_factor + - component.update: reactive_power + + - platform: template + id: reactive_power + name: "Reactive Power" + state_class: measurement + device_class: reactive_power + unit_of_measurement: "var" + update_interval: never + lambda: |- + return id(apparent_power).state * (1 - id(power_factor).state); + + - platform: template + id: power_factor + name: "Power Factor" + state_class: measurement + device_class: power_factor + update_interval: never + lambda: |- + float r = id(power).state; + float a = id(apparent_power).state; + if (r == 0.0 && a == 0.0) { + return 1.0; + } + return r / a; + on_value: + then: + - component.update: reactive_power + +light: + - platform: status_led + name: "Status LED" + id: status_led_id + disabled_by_default: true + entity_category: diagnostic + pin: + number: GPIO0 + inverted: true + ignore_strapping_warning: true diff --git a/shelly-plus-i4.yaml b/shelly-plus-i4.yaml new file mode 100644 index 0000000..e788c5e --- /dev/null +++ b/shelly-plus-i4.yaml @@ -0,0 +1,131 @@ +# Shelly Plus i4 +# PCB markings: "Shelly i4_v0.1.1", "B2724" +# CPU: ESP32-U4WDH (revision v3.1) +# Features: Wi-Fi, BT, Dual Core + LP Core, 240MHz, Embedded Flash, Vref calibration in eFuse, Coding Scheme None +# Crystal frequency: 40MHz +# References: +# - https://devices.esphome.io/devices/shelly-plus-i4/ +# - https://kb.shelly.cloud/knowledge-base/shelly-plus-i4 +# +# GPIOs: +# GPIO0 - Status LED and programming strapping pin +# GPIO12 - Switch 1 +# GPIO14 - Switch 2 +# GPIO19 - Pin 1 of 7-pin header +# GPIO25 - Pushbutton +# GPIO26 - Switch 4 +# GPIO27 - Switch 3 +# GPIO32 - ADC for NTC temperature probe + +substitutions: + device_name: "Shelly Plus i4" + shelly_button_gpio_number: GPIO25 + shelly_header_gpio_number: GPIO19 + shelly_ntc_temperature_pin: GPIO32 + name: shelly-plus-i4 + friendly_name: "${device_name}" + room: "" + device_description: "Shelly Plus i4 - 4x smart Wi-Fi switch inputs" + +dashboard_import: + package_import_url: github://dlitz/esphome-configs-dlitz/shelly-plus-i4.yaml@${git_branch} + +packages: + - !include common/esp32-u4wdh.yaml + - !include common/cpu-temperature.yaml + - !include common/factory-reset.yaml + - !include common/flash-write-interval.yaml + - !include common/git-branch.yaml + - !include common/ota.yaml + - !include common/ota-update-password.yaml + - !include common/restart.yaml + - !include common/safe-mode.yaml + - !include common/shelly-button.yaml + - !include common/shelly-header-gpio.yaml + - !include common/shelly-ntc-temperature.yaml + - !include common/time.yaml + - !include common/uptime-info.yaml + - !include common/wifi-info.yaml + +external_components: + - source: + type: git + url: https://github.com/dlitz/esphome-configs-dlitz.git + ref: ${git_branch} + refresh: always + +esphome: + name: "${name}" + friendly_name: "${friendly_name}" + area: "${room}" + comment: "${device_description}" + name_add_mac_suffix: true + project: + name: "dlitz.shelly-plus-i4" + version: "v0.0.0" + +logger: + level: DEBUG + +debug: + +api: + encryption: + +wifi: + # ssid: !secret wifi_ssid + # password: !secret wifi_password + # fast_connect: true + enable_btm: true + enable_rrm: true + min_auth_mode: WPA2 + + # Enable fallback hotspot (captive portal) in case wifi connection fails + ap: + password: !secret fallback_ap_password + +# web_server: + +captive_portal: + +binary_sensor: + - platform: gpio + id: sw1 + name: "Switch 1" + pin: + number: GPIO12 + ignore_strapping_warning: true + filters: + - settle: 50ms + + - platform: gpio + id: sw2 + name: "Switch 2" + pin: GPIO14 + filters: + - settle: 50ms + + - platform: gpio + id: sw3 + name: "Switch 3" + pin: GPIO27 + filters: + - settle: 50ms + + - platform: gpio + id: sw4 + name: "Switch 4" + pin: GPIO26 + filters: + - settle: 50ms + +light: + - platform: status_led + name: "Status LED" + id: status_led_id + disabled_by_default: true + entity_category: diagnostic + pin: + number: GPIO0 + inverted: true + ignore_strapping_warning: true diff --git a/shelly-pm-mini-gen3.yaml b/shelly-pm-mini-gen3.yaml new file mode 100644 index 0000000..acb4ea3 --- /dev/null +++ b/shelly-pm-mini-gen3.yaml @@ -0,0 +1,204 @@ +# MAC address: ec:da:3b:c4:9f:7c +# Device: Shelly PM Mini Gen3 +# References: +# - https://devices.esphome.io/devices/shelly-pm-mini-gen3/ +# - https://kb.shelly.cloud/knowledge-base/shelly-pm-mini-gen3 + +substitutions: + # calibration_voltage_reference: 15873.35944299 # esphome default + calibration_voltage_reference: 14393.902006359718 # calibrated on device ec:da:3b:c4:9f:7c + # calibration_current_reference: 251213.46469622 # esphome default + calibration_current_reference: 245340.94214487978 # calibrated on device ec:da:3b:c4:9f:7c + device_name: "Shelly PM Mini Gen3" + line_frequency: 60Hz + shelly_button_gpio_number: GPIO1 + shelly_ntc_temperature_pin: GPIO3 + update_interval: 5s + +dashboard_import: + package_import_url: github://dlitz/esphome-configs-dlitz/shelly-pm-mini-gen3.yaml@${git_branch} + +packages: + - !include common/esp-shelly-c38f.yaml # TODO: Does this device have this SoC + - !include common/cpu-temperature.yaml + - !include common/factory-reset.yaml + - !include common/flash-write-interval.yaml + - !include common/git-branch.yaml + - !include common/ota.yaml + - !include common/ota-update-password.yaml + - !include common/restart.yaml + - !include common/safe-mode.yaml + - !include common/shelly-button.yaml + - !include common/shelly-ntc-temperature.yaml + - !include common/time.yaml + - !include common/uptime-info.yaml + - !include common/wifi-info.yaml + +esphome: + name: "shelly-pm-mini-gen3" + friendly_name: "shelly-pm-mini-gen3" + comment: "Shelly PM Mini Gen3 - Smart wifi energy meter" + name_add_mac_suffix: true + project: + name: "dlitz.shelly-pm-mini-gen3" + version: "v0.0.0" + min_version: 2025.11.3 # for ade7953 bugfix #12180 + +# Enable logging +logger: + hardware_uart: uart0 + +# Enable Home Assistant API +api: + encryption: + +wifi: + # ssid: !secret wifi_ssid + # password: !secret wifi_password + # fast_connect: true + enable_btm: true + enable_rrm: true + min_auth_mode: WPA2 + + # Enable fallback hotspot (captive portal) in case wifi connection fails + ap: + password: !secret fallback_ap_password + +captive_portal: + +network: + enable_ipv6: true + # min_ipv6_addr_count: 2 + +# esp32_ble_tracker: +# scan_parameters: +# active: false + +# bluetooth_proxy: +# active: false + +light: + - platform: status_led + name: "Status LED" + id: status_led_id + disabled_by_default: true + entity_category: diagnostic + pin: + number: GPIO0 + inverted: true + +sensor: + - platform: bl0942 + id: bl0942_id + uart_id: uart_0 + update_interval: ${update_interval} + voltage_reference: ${calibration_voltage_reference} + current_reference: ${calibration_current_reference} + line_frequency: ${line_frequency} + + voltage: + name: "Voltage" + id: voltage + icon: mdi:alpha-v-circle-outline + device_class: voltage + on_value: + then: + - component.update: apparent_power + current: + name: "Current" + id: current + icon: mdi:alpha-a-circle-outline + device_class: current + on_value: + then: + - component.update: apparent_power + power: + name: "Power" + id: power + icon: mdi:transmission-tower + device_class: power + state_class: measurement + on_value: + then: + - component.update: power_factor + energy: + name: "Energy" + id: energy + icon: mdi:meter-electric + device_class: energy + frequency: + name: "Frequency" + id: line_frequency + accuracy_decimals: 2 + icon: mdi:cosine-wave + device_class: frequency + state_class: measurement + + - platform: total_daily_energy + id: daily_energy_consumed_id + name: "Daily Energy Consumed" + power_id: power + icon: mdi:hours-24 + unit_of_measurement: "kWh" + state_class: total_increasing + device_class: energy + accuracy_decimals: 3 + filters: + - multiply: 0.001 + + - platform: template + id: apparent_power + name: "Apparent Power" + state_class: measurement + device_class: apparent_power + unit_of_measurement: "VA" + update_interval: never + lambda: |- + return id(voltage).state * id(current).state; + on_value: + then: + - component.update: power_factor + - component.update: reactive_power + + - platform: template + id: reactive_power + name: "Reactive Power" + state_class: measurement + device_class: reactive_power + unit_of_measurement: "var" + update_interval: never + lambda: |- + return id(apparent_power).state * (1 - id(power_factor).state); + + - platform: template + id: power_factor + name: "Power Factor" + state_class: measurement + device_class: power_factor + update_interval: never + lambda: |- + float r = id(power).state; + float a = id(apparent_power).state; + if (r == 0.0 && a == 0.0) { + return 1.0; + } + return r / a; + on_value: + then: + - component.update: reactive_power + +uart: + - id: uart_0 + tx_pin: GPIO6 + rx_pin: + number: GPIO7 + mode: + pullup: true + input: true + baud_rate: 9600 + debug: + direction: BOTH + after: + delimiter: "\n" + sequence: + - lambda: UARTDebug::log_hex(direction, bytes, 32);