Shelly Plus Plug S - ESPHOME

Flashed the bluetooth component yesterday. The device is online but I am not able to check if the bluetooth works.

I used the exact same configuration as you posted, but disabled the web proxy in addition.

Flashed via OTA. Two things I found to be aware off:

  1. it is not the bluetooth component that require the flashing via wires, it is the change of the framework instead. Because the framework is the same as at my first flashing, I flashed this time via OTA.
  2. Disable webserver because there is a risk to run out of memory.

After flashing there was no new entity or device like “bt proxy” or so. My Shelly BLU Door is not delivered yet. Therefore I am not sure if it works fine but the software is installed. :slight_smile:

Ressources: Bluetooth Proxy — ESPHome

The ESP32 Platform component should be configured to use the esp-idfframework, as the arduino framework uses significantly more memory and performs poorly with the Bluetooth proxy enabled. When switching fromarduino to esp-idf, make sure to update the device with a serial cable as the partition table is different between the two frameworks as OTA Update Component updates will not change the partition table.

Good luck!

Thanks, no idea how I missed the part that says that only switching type needs the TTL flash…

However I just tested my second working shelly plus plug via ota flash (was esp-idf) and now it is “dead”:

No clue what is going on. I just added the parts I mentioned before and flashed . Without the BT part it worked.
Config is now like this. Maybe you can spot a differenc to your

substitutions:
  device_name: "shelly-plus-plug-s-black1"
  friendly_name: shelly-plus-plug-s-black1
  channel_1: Relay
  max_power: "1500" # 2000
  max_temp: "65.0" # 70.0
  # Higher value gives lower watt readout.
  current_res: "0.001"
  # Lower value gives lower voltage readout.
  voltage_div: "1830"


esphome:
  name: ${device_name}
  friendly_name: ${friendly_name}
  on_boot:
    - delay: 10s
    - lambda: !lambda |-
        id(rgb_ready) = true;
    - script.execute: set_rgb

esp32:
  board: esp32doit-devkit-v1
  framework:
    type: esp-idf

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "XXXXX"

ota:
  password: "XXXXX"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  use_address: 10.0.100.27
  domain: .xxxxx.lan

  # Enable fallback hotspot in case wifi connection fails
  ap:
    ssid: ${device_name}

esp32_ble_tracker:
  scan_parameters:
    active: false

bluetooth_proxy:
  active: true

time:
  - platform: homeassistant
    id: my_time

globals:
  - id: rgb_ready
    type: bool
    restore_value: false
    initial_value: 'false'
  - id: total_energy
    type: float
    restore_value: true
    initial_value: '0.0'


    #### only needed for RGB LED to set up a while after boot. Not available with esp-idf framework #####

script:
  - id: set_rgb
    mode: queued
    then:
      - if:
          condition:
            lambda: 'return id(rgb_ready);'
          then:
            - if:
                condition:
                  lambda: 'return id(relay).state;'
                then:
                  - if:
                      condition:
                        lambda: 'return id(ring_on).remote_values.is_on();'
                      then:
                        - light.turn_on:
                            id: rgb_light1
                            brightness: !lambda |-
                              return id(ring_on).remote_values.get_brightness();
                            red: !lambda |-
                              return id(ring_on).remote_values.get_red();
                            green: !lambda |-
                              return id(ring_on).remote_values.get_green();
                            blue: !lambda |-
                              return id(ring_on).remote_values.get_blue();
                        - light.turn_on:
                            id: rgb_light2
                            brightness: !lambda |-
                              return id(ring_on).remote_values.get_brightness();
                            red: !lambda |-
                              return id(ring_on).remote_values.get_red();
                            green: !lambda |-
                              return id(ring_on).remote_values.get_green();
                            blue: !lambda |-
                              return id(ring_on).remote_values.get_blue();
                      else:
                        - light.turn_off: rgb_light1
                        - light.turn_off: rgb_light2
                else:
                  - if:
                      condition:
                        lambda: 'return id(ring_off).remote_values.is_on();'
                      then:
                        - light.turn_on:
                            id: rgb_light1
                            brightness: !lambda |-
                              return id(ring_off).remote_values.get_brightness();
                            red: !lambda |-
                              return id(ring_off).remote_values.get_red();
                            green: !lambda |-
                              return id(ring_off).remote_values.get_green();
                            blue: !lambda |-
                              return id(ring_off).remote_values.get_blue();
                        - light.turn_on:
                            id: rgb_light2
                            brightness: !lambda |-
                              return id(ring_off).remote_values.get_brightness();
                            red: !lambda |-
                              return id(ring_off).remote_values.get_red();
                            green: !lambda |-
                              return id(ring_off).remote_values.get_green();
                            blue: !lambda |-
                              return id(ring_off).remote_values.get_blue();
                      else:
                        - light.turn_off: rgb_light1
                        - light.turn_off: rgb_light2

output:
  - platform: template
    id: r_out_on
    type: float
    write_action:
      - lambda: |-
  - platform: template
    id: g_out_on
    type: float
    write_action:
      - lambda: |-
  - platform: template
    id: b_out_on
    type: float
    write_action:
      - lambda: |-
  - platform: template
    id: r_out_off
    type: float
    write_action:
      - lambda: |-
  - platform: template
    id: g_out_off
    type: float
    write_action:
      - lambda: |-
  - platform: template
    id: b_out_off
    type: float
    write_action:
      - lambda: |-

light:
  - platform: rgb
    id: ring_on
    name: "${channel_1} Ring when On"
    icon: "mdi:circle-outline"
    default_transition_length: 0s
    red: r_out_on
    green: g_out_on
    blue: b_out_on
    restore_mode: RESTORE_DEFAULT_OFF
    entity_category: config
    on_state:
      - delay: 50ms
      - script.execute: set_rgb
  - platform: rgb
    id: ring_off
    name: "${channel_1} Ring when Off"
    icon: "mdi:circle-outline"
    default_transition_length: 0s
    red: r_out_off
    green: g_out_off
    blue: b_out_off
    restore_mode: RESTORE_DEFAULT_OFF
    entity_category: config
    on_state:
      - delay: 50ms
      - script.execute: set_rgb

  - platform: esp32_rmt_led_strip
    rgb_order: GRB
    rmt_channel: 0
    chipset: ws2812
    pin: GPIO25
    num_leds: 2
    id: rgb_light1
    internal: true
    default_transition_length: 700ms
    restore_mode: ALWAYS_OFF
  - platform: esp32_rmt_led_strip
    rgb_order: GRB
    rmt_channel: 1
    chipset: ws2812
    pin: GPIO26
    num_leds: 2
    id: rgb_light2
    internal: true
    default_transition_length: 700ms
    restore_mode: ALWAYS_OFF

binary_sensor:
  - platform: gpio
    id: "push_button"
    internal: true
    pin:
      number: GPIO9
      inverted: yes
      mode:
        input: true
        pullup: true
    on_click:
      then:
        - if:
            condition:
              switch.is_off: button_lock
            then:
              - switch.toggle: relay
    filters:
      - delayed_on_off: 5ms

switch:
  - platform: gpio
    pin: GPIO4
    id: relay
    name: "${channel_1}"
    restore_mode: RESTORE_DEFAULT_OFF
    on_turn_on:
      - script.execute: set_rgb
    on_turn_off:
      - script.execute: set_rgb
  - platform: template
    entity_category: 'config'
    name: "Button lock"
    id: button_lock
    optimistic: true
    restore_mode: ALWAYS_OFF

sensor:
  - platform: ntc
    sensor: temp_resistance_reading
    name: "${device_name} Temperature"
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    icon: "mdi:thermometer"
    entity_category: 'diagnostic'
    calibration:
      b_constant: 3350
      reference_temperature: 298.15K
      reference_resistance: 10kOhm
    on_value_range:
      - above: ${max_temp}
        then:
          - switch.turn_off: "relay"
          - homeassistant.service:
              service: persistent_notification.create
              data:
                title: Message from ${device_name}
              data_template:
                message: Switch turned off because temperature exceeded ${max_temp} °C
  - platform: resistance
    id: temp_resistance_reading
    sensor: temp_analog_reading
    configuration: DOWNSTREAM
    resistor: 10kOhm
  - platform: adc
    id: temp_analog_reading
    pin: GPIO33
    attenuation: 11db
    update_interval: 10s

  - platform: hlw8012
    model: BL0937
    sel_pin:
      number: GPIO19
      inverted: true
    cf_pin: GPIO10
    cf1_pin: GPIO22
    current_resistor: ${current_res}
    voltage_divider: ${voltage_div}
    change_mode_every: 1
    update_interval: 5s
    current:
      id: current
      unit_of_measurement: A
      accuracy_decimals: 3
      internal: true
      name: "${channel_1} current"
    voltage:
      id: voltage
      unit_of_measurement: V
      accuracy_decimals: 1
      internal: false
      name: "${channel_1} voltage"
    power:
      name: "${channel_1} power"
      unit_of_measurement: W
      id: power
      icon: mdi:flash-outline
      force_update: true
      on_value_range:
        - above: ${max_power}
          then:
            - switch.turn_off: relay
            - homeassistant.service:
                service: persistent_notification.create
                data:
                  title: Message from ${device_name}
                data_template:
                  message: Switch turned off because power exceeded ${max_power}W

  - platform: total_daily_energy
    name: "${channel_1} energy"
    power_id: power
    state_class: total_increasing
    unit_of_measurement: kWh
    filters:
      # Multiplication factor from W to kW is 0.001
      - multiply: 0.001
      - lambda: !lambda |-
          static auto last_state = x;
          if (x < last_state) { // x was reset
            id(total_energy) += last_state;
            ESP_LOGI("main", "Energy channel 1 was reset: %f", id(total_energy));
          }
          last_state = x;
          return id(total_energy) + x;

THX

Sorry, I can’t spot any difference in the important areas. :frowning:

There is a new version V2 without screw, I have no idea to open it for flashing it via serial cable: (


thanks for looking.

Will probably flash again without the bt parts

Yeah, the V2 don’t have a screw.

Might try it with a small drill, as I “bricked” mine.

You can still do the initial flash via converting to tasmota first, as mentioned above. So as long as you don’t brick it, there is no need to serial flash it.

How can I find out these values for bl0937? ( I have one plug by local bytes that I forgot to make note of the values before converting to esphome and now I can’t get super precise measurements)

  # Higher value gives lower watt readout
  current_res: "0.001"
  # Lower value gives lower voltage readout
  voltage_div: "1830"

I flashed Tasmota with GitHub - tasmota/mgos32-to-tasmota32: ESP32 Mongoose OS to Tasmota32 OTA Firmware Updates for Shelly and repartitioned to make one big app partition. Is it possible to flash ESPHome by using Tasmota web gui? Can I just build firmware and flash it in Tasmota gui?

1 Like

No, not possible, since the partition scheme used is the Tasmota safeboot scheme. This partition scheme has only one OTA slot, the other slot used for the OTA updating is a factory partition slot fashed with a special (very small) Tasmota version just made doing the update job. So the partition scheme is totally different to the one used for esphome.
For OTA flashing esphome a convert Shelly to esphome update firmware needs to be written in Mongoose SDK. This tool has to do the repartitioning from Shelly scheme to esphome scheme and writes a new bootloader. The Shelly bootloader allows only signed firmwares.
Tasmota does this all right after boot when Autoconfig feature menu in Tasmota is used. The Autoconfig downloads a new bootloader and the Berry runtime Code to flash The new bootloader and writes the Tasmota Partition scheme. Without the Repl Berry included in Tasmota this tool would have not been possible with Tasmota.

Shelly raised the secure. The new convert tool is only compatible with Tasmota

But if I flashed Tasmota bootloader, it will accept all firmwares, not signed only? If yes, then I could flash ESPHome if partition scheme was ESPHome compatible?

Has anyone flashed to Tasmota the UK version of this plug lately? It’s labelled “Shelly Plus Plug UK (type G)”. I flashed one back in May using http://ota.tasmota.com/tasmota32/shelly/mgos32-to-tasmota32-PlusPlugUK.zip, but that appears to be deleted and the tasmota release site doesn’t seem to list it (or anything else for Shelly) and https://github.com/tasmota/mgos32-to-tasmota32 seems to have dropped support for it.

Edit: I’ve had a fix added to mgos32-to-tasmota32 so this now works, and then I uploaded a “legacy” esphome image and it worked, and was found by HA. I then tried to add this to the working yaml:

esp32_ble_tracker:
  scan_parameters:
    active: false

bluetooth_proxy:
  active: true

and the device is now seemingly bricked. It was my understanding that this should be fine unless I was changing frameworks, but in this case I didn’t - it was IDF for both.

Did you convert to tasmota safeboot partition scheme? Since this will be incompatible with esphome.

not knowingly - just the mgos32-to-tasmota32 dance, then had esphome build a “legacy” image and upload it via the tasmota web ui. it did come up OK running esphome, it was just once I’d enabled bluetooth proxy, rebuilt and done an OTA update that it stopped responding.

There does not seem to be a good way to switch the new Shellys with the locked bootloader to esphome through the mgos32-to-tasmota32:

  • if you upgrade the partition layout change then you get the bootloader rewriten and unlocked but this breaks esphome OTAs (as it does not support the safeboot process)
  • if you flash esphome with the layout change then OTAs seem to work but to the locked bootloader it keeps booting the first esphome you program.

I described in the esphome dev discord channel what ideas I have to try to enable updates after flashing but I have not started trying yet.

UPDATE:

4 Likes

Same happened to me. Have not found a fix so far :-/

I tested 3 migrations Shelly Mgos → Tasmota → ESPHome, all over the air without using the serial programming. See instructions in the PR.

It should allow to migrate from any Tasmota v12/v13 with the Safeboot partition.

3 Likes

I have Shelly Plus Plug S converted to Tasmota Safeboot. Can I use instructions in this PR?

Yes, you can. Just ensure to adjust the config.yaml to your device. In fact I developed the extended OTA as I expect to buy multiple Shelly Plus Plugs and I do not want to have to open them.

If anything goes wrong do not disconnect the power. So far you do not reboot you can keep trying. The only problem I encountered was once that I have tried flashing a full configuration that was too large to fit into the safebot partition. When I got the error I just retried with the right configuration and then it wnt well.

1 Like

Hi,
I do not really understand, that`s why I want to ask the following question.
Could somebody explain me what is the benefit or the motive about to change shelly firmware?