Add shelly-plug-us-gen4.yaml

This commit is contained in:
2026-01-26 02:02:02 -07:00
parent 69197dc0b3
commit 8f785c8b61

516
shelly-plug-us-gen4.yaml Normal file
View File

@@ -0,0 +1,516 @@
# shelly plug us gen4
# PCB markings: "US_plug_Gen4_V0.0.3"
# Model: S4PL-00116US
# Case markings: B2508
# SoC: ESP-Shelly-C68F
# CPU: ESP32-C6 variant
# Power sensor: BL0942
# References:
# - https://documentation.espressif.com/esp32-c6_datasheet_en.pdf
# - https://documentation.espressif.com/esp32-c6_technical_reference_manual_en.pdf
# - https://optoelectronics.liteon.com/upload/download/DS86-2013-0004/LTR-303ALS-01_DS_V1.1.PDF
# - https://www.belling.com.cn/media/file_object/bel_product/BL0942/datasheet/BL0942_V1.06_en.pdf
# GPIOs:
# GPIO3 - NTC? Maybe? Maybe not?
# GPIO4 - Relay
# GPIO5? - seems to have some other device transmitting on it (maybe the Zigbee interface? maybe just a floating pin? ADC?)
# GPIO6 - WS2812-compatible LED strip (12 LEDs)
# GPIO7 - Button (active low)
# GPIO9 - boot strapping pin (-> DTR)
# GPIO10 - I2C SDA
# GPIO11 - I2C SCL
# GPIO16 - U0TXD
# GPIO17 - U0RXD
# GPIO18 - BL0942 RX
# GPIO19 - BL0942 TX
# Test points:
# TP1: +3.3V
# TP2: GPIO9
# TP3: EN
# TP4: GND
# TP5: RxD
# TP6: TxD
# Floating GPIO values: Pulled-down GPIO values: Pulled-up GPIO values:
# - 'GPIO0': LOW - 'GPIO0': LOW - 'GPIO0': HIGH
# - 'GPIO1': LOW - 'GPIO1': LOW - 'GPIO1': HIGH
# - 'GPIO2': HIGH - 'GPIO2': HIGH ! 'GPIO2': HIGH
# - 'GPIO3': HIGH * 'GPIO3': LOW - 'GPIO3': HIGH
# - 'GPIO4': LOW - 'GPIO4': LOW ! 'GPIO4': LOW - Relay
# - 'GPIO5': LOW - 'GPIO5': LOW - 'GPIO5': HIGH
# - 'GPIO6': LOW - 'GPIO6': LOW ! 'GPIO6': LOW
# - 'GPIO7': HIGH - 'GPIO7': HIGH ! 'GPIO7': HIGH - Button
# - 'GPIO8': HIGH - 'GPIO8': HIGH ! 'GPIO8': HIGH
# - 'GPIO9': HIGH - 'GPIO9': HIGH ! 'GPIO9': HIGH - Boot select pin
# - 'GPIO10': HIGH - 'GPIO10': HIGH ! 'GPIO10': HIGH - I2C SDA
# - 'GPIO11': HIGH - 'GPIO11': HIGH ! 'GPIO11': HIGH - I2C SCL
# - 'GPIO12': LOW - 'GPIO12': LOW - 'GPIO12': HIGH
# - 'GPIO13': HIGH * 'GPIO13': LOW - 'GPIO13': HIGH
# - 'GPIO14': LOW - 'GPIO14': LOW - 'GPIO14': HIGH
# - 'GPIO15': LOW - 'GPIO15': LOW - 'GPIO15': HIGH
# - 'GPIO17': HIGH - 'GPIO17': HIGH ! 'GPIO17': HIGH - U0RXD
# - 'GPIO18': HIGH * 'GPIO18': LOW - 'GPIO18': HIGH
# - 'GPIO19': HIGH # flickers on and off * 'GPIO19': LOW - 'GPIO19': HIGH
# - 'GPIO20': LOW - 'GPIO20': LOW ! 'GPIO20': LOW
# - 'GPIO21': LOW - 'GPIO21': LOW - 'GPIO21': HIGH
# - 'GPIO22': LOW - 'GPIO22': LOW - 'GPIO22': HIGH
# - 'GPIO23': LOW - 'GPIO23': LOW - 'GPIO23': HIGH
# I2C devices:
# 0x29 - Liteon LTR-303ALS illuminance sensor (or compatible)
# There's an illuminance sensor
# "shelly_pm_bl0942.cp:102 BL0942 Voltage coeff 4012891.1, Current coeff 399000.0, Power coeff 102974600.0, Energy coeff 11909381970.3"
# "shelly_ltr303_sensor.cpp"
# TODO:
# - Calibration
# - Overheating protection
# - Overvoltage protection
# - Overcurrent protection
# - Overpower protection
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
#calibration_voltage_reference: 4012891.1 # from Shelly firmware log
#calibration_current_reference: 399000.0 # from Shelly firmware log
#calibration_power_reference: 102974600.0 # from Shelly firmware log
#calibration_energy_reference: 11909381970.3 # from Shelly firmware log
calibration_voltage_reference: !remove
calibration_current_reference: !remove
calibration_power_reference: !remove
calibration_energy_reference: !remove
line_frequency: 60Hz
update_interval: 5s
shelly_ntc_temperature_pin: GPIO5 # (?)
dashboard_import:
package_import_url: github://dlitz/esphome-configs-dlitz/shelly-plug-us-gen4.yaml@${git_branch}
packages:
- !include common/esp-shelly-c68f.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-ntc-temperature.yaml
- !include common/time.yaml
- !include common/uptime-info.yaml
- !include common/wifi-info.yaml
esphome:
name: shelly-plug-us-gen4
friendly_name: "Shelly Plug US Gen4"
comment: "Shelly Plus US Gen4 - Smart plug with multi-protocol connectivity, power metering and LED indication"
name_add_mac_suffix: true
project:
name: "dlitz.shelly-plug-us-gen4"
version: "v0.0.0"
# Enable logging
logger:
level: DEBUG
baud_rate: 115200
hardware_uart: uart0
logs:
ltr_als_ps: INFO
# debug:
# 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 default_ap_password
captive_portal:
network:
enable_ipv6: true
i2c:
sda: GPIO10
scl: GPIO11
frequency: 100kHz
scan: true
# esp32_ble_tracker:
# scan_parameters:
# active: false
#
# bluetooth_proxy:
# active: false
sensor:
- id: !extend ntc_temp_analog_reading
update_interval: 5s
- id: !extend cpu_temperature
update_interval: 5s
- platform: ltr_als_ps
address: 0x29
update_interval: 30s
type: ALS
ambient_light:
name: "LTR303 Ambient light"
infrared_counts:
name: "LTR303 Infrared counts"
full_spectrum_counts:
name: "LTR303 Full spectrum counts"
actual_gain:
name: "LTR303 Actual gain"
actual_integration_time:
name: "LTR303 Actual integration time"
- platform: bl0942
id: bl0942_id
uart_id: bl0942_uart_id
update_interval: ${update_interval}
voltage_reference: ${calibration_voltage_reference}
current_reference: ${calibration_current_reference}
power_reference: ${calibration_power_reference}
energy_reference: ${calibration_energy_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
- { platform: adc, name: ADC_GPIO0, pin: GPIO0, update_interval: 5s }
- { platform: adc, name: ADC_GPIO1, pin: GPIO1, update_interval: 5s }
- { platform: adc, name: ADC_GPIO2, pin: GPIO2, update_interval: 5s }
- { platform: adc, name: ADC_GPIO3, pin: GPIO3, update_interval: 5s }
#- { platform: adc, name: ADC_GPIO4, pin: GPIO4, update_interval: 5s }
#- { platform: adc, name: ADC_GPIO5, pin: GPIO5, update_interval: 5s }
#- { platform: adc, name: ADC_GPIO6, pin: GPIO6, update_interval: 5s }
- platform: template
name: App state (uint8)
lambda: |-
return App.get_app_state();
update_interval: 1s
uart:
- id: bl0942_uart_id
tx_pin: GPIO18
rx_pin:
number: GPIO19
mode:
input: true
pullup: true
baud_rate: 9600
debug:
direction: BOTH
after:
delimiter: "\n"
sequence:
- lambda: UARTDebug::log_hex(direction, bytes, 32);
output:
- platform: gpio
id: "relay_output"
pin: GPIO4
- platform: template
id: status_led_output
type: binary
write_action:
- lambda: |-
static int prev_state = -1;
//ESP_LOGD("my_status", "prev_state: %d, state: %d, state size: %d", prev_state, (int)state, sizeof(state));
if (prev_state <= 0 && state > 0) {
prev_state = state;
auto call = id(rgb_led).turn_on();
call.set_brightness(0.30);
call.set_rgb(1.0, 0.5, 0.0); // amber
call.set_transition_length(0);
call.set_publish(false); // Don't spam the logs
call.perform();
} else if (prev_state > 0 && state == 0) {
prev_state = state;
auto call = id(rgb_led).make_call();
call.from_light_color_values(id(rgb_led).remote_values);
call.set_transition_length(0);
call.set_publish(false); // Don't spam the logs
call.perform();
}
light:
- platform: esp32_rmt_led_strip
id: rgb_led
name: RGB LED
#entity_category: config
pin: GPIO6
rgb_order: GRB
chipset: WS2812
rmt_symbols: 48
num_leds: 12
restore_mode: ALWAYS_ON
initial_state: # XXX This doesn't seem to work for some rason
state: true
red: 0%
green: 100%
blue: 0%
brightness: 30%
effects:
- strobe:
- addressable_color_wipe:
name: Identify
add_led_interval: 100ms
colors:
- red: 0%
green: 0%
blue: 100%
num_leds: 3
- red: 0%
green: 0%
blue: 100%
num_leds: 3
- red: 0%
green: 50%
blue: 50%
num_leds: 3
- red: 0%
green: 50%
blue: 50%
num_leds: 3
- strobe:
name: Blink Green
colors:
- state: true
brightness: 100%
red: 0%
green: 100%
blue: 0%
duration: 250ms
- state: false
brightness: 0%
duration: 250ms
- strobe:
name: Fast Blink Red Blue
colors:
- state: true
brightness: 100%
red: 100%
green: 0%
blue: 0%
duration: 50ms
- state: true
brightness: 100%
red: 0%
green: 0%
blue: 100%
duration: 50ms
- platform: status_led
internal: true
id: status_led_id
output: status_led_output
switch:
- platform: output
id: "relay"
name: "Relay"
output: "relay_output"
restore_mode: RESTORE_DEFAULT_OFF
on_turn_on:
- logger.log: "Relay turned on"
on_turn_off:
- logger.log: "Relay turned off"
- platform: template
name: Identify
entity_category: config
optimistic: true
turn_on_action:
- light.turn_on:
id: rgb_led
effect: Identify
- logger.log:
format: "identify ON"
level: INFO
turn_off_action:
- light.turn_off:
id: rgb_led
- logger.log:
format: "identify OFF"
level: INFO
- platform: template
id: trig_warning
name: Trigger warning
entity_category: diagnostic
disabled_by_default: true
optimistic: true
turn_on_action:
- lambda: |-
id(trig_warning).status_set_warning(LOG_STR("foo"));
turn_off_action:
- lambda: |-
id(trig_warning).status_clear_warning();
- platform: template
id: trig_error
name: Trigger error
entity_category: diagnostic
disabled_by_default: true
optimistic: true
turn_on_action:
- lambda: |-
id(trig_error).status_set_error(LOG_STR("foo"));
turn_off_action:
- lambda: |-
id(trig_error).status_clear_error();
binary_sensor:
- platform: gpio
id: button_id
name: "Button"
pin:
number: GPIO7
inverted: true
mode:
input: true
pullup: true
filters:
- settle: 10ms
on_multi_click:
- timing:
- ON for at most 1s
- OFF for at least 10ms
then:
- switch.toggle: relay
- logger.log: "Button pressed. Toggled relay."
- timing:
- ON for at least 5s
then:
- light.turn_on:
id: status_led_id
effect: Blink Green
- timing:
- ON for 5s to 10s
- OFF for at least 10ms
then:
- logger.log: "Button held for 5-10s: Restart"
- button.press: button_restart
- timing:
- ON for at least 10s
then:
- light.turn_on:
id: status_led_id
effect: Fast Blink Red Blue
- 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