Import YAML files into repository

This commit is contained in:
2026-01-20 23:08:05 -07:00
parent af94e08fe4
commit 3239e56c8d
31 changed files with 1550 additions and 1 deletions

5
.gitignore vendored Normal file
View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,54 @@
esphome:
includes:
- "<lwip/apps/mdns_priv.h>" # for struct mdns_service, struct mdns_txtdata
- "<lwip/init.h>" # 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);

View File

@@ -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

View File

@@ -0,0 +1,4 @@
sensor:
- platform: internal_temperature
id: cpu_temperature
name: "CPU Temperature"

View File

@@ -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

25
common/esp32-u4wdh.yaml Normal file
View File

@@ -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

17
common/esp32.yaml Normal file
View File

@@ -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 --

17
common/esp32c3.yaml Normal file
View File

@@ -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 --

13
common/factory-reset.yaml Normal file
View File

@@ -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

View File

@@ -0,0 +1,2 @@
preferences:
flash_write_interval: 5min

View File

@@ -0,0 +1,4 @@
esphome:
platformio_options:
platform_packages:
- "library-lwip @ https://github.com/dlitz/lwip.git#mdns-big-txt_bdk"

View File

@@ -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}"

7
common/ota.yaml Normal file
View File

@@ -0,0 +1,7 @@
substitutions:
ota_password: !secret fallback_ota_password
ota:
- platform: esphome
id: ota_id
password: "${ota_password}"

4
common/restart.yaml Normal file
View File

@@ -0,0 +1,4 @@
button:
- platform: restart
id: button_restart
name: Restart

7
common/safe-mode.yaml Normal file
View File

@@ -0,0 +1,7 @@
safe_mode:
reboot_timeout: 10min
button:
- platform: safe_mode
id: button_safe_mode
name: Safe Mode Boot

42
common/shelly-button.yaml Normal file
View File

@@ -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}

View File

@@ -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}

View File

@@ -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

11
common/time.yaml Normal file
View File

@@ -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!"

16
common/uptime-info.yaml Normal file
View File

@@ -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

42
common/wifi-info.yaml Normal file
View File

@@ -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

0
components/.gitignore vendored Normal file
View File

View File

@@ -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)}

View File

@@ -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"

View File

@@ -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"

2
secrets.example.yaml Normal file
View File

@@ -0,0 +1,2 @@
fallback_ap_password: ""
fallback_ota_password: ""

281
shelly-em-gen3.yaml Normal file
View File

@@ -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 "" }

249
shelly-plus-1pm-ul.yaml Normal file
View File

@@ -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

131
shelly-plus-i4.yaml Normal file
View File

@@ -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

204
shelly-pm-mini-gen3.yaml Normal file
View File

@@ -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);