Skip to content

Conversation

@Xmister
Copy link

@Xmister Xmister commented Jun 15, 2023

This is an initial try on Deep Sleep for the Beken platform.
Most of the code are tailored from OpenBeken.
Platform Changes
BDK Changes

NOTE: I'm still having linking issues, and can't figure out why it can't find a definition for bk_enter_deep_sleep_mode

Maybe someone can help me figure out the last steps. @kuba2k2 maybe?

@kuba2k2
Copy link
Member

kuba2k2 commented Jun 15, 2023

Let's keep the discussion in the main PR (the platform one). I'll reply there tomorrow with a little review.

@samhunt
Copy link

samhunt commented Jul 18, 2023

Just wanted to leave my 2c here of testing that I have done with the deep-sleep functionality on my Door Sensor. FWIW, the device is going to sleep, but it is constantly waking up every 30 seconds (without changing the wakeup pin) as is shown in the logs below. I have also attached my device yaml if there is something that I have done wrong in the config.

I'll keep playing around to see if there's anything I can see as to why it keeps waking

INFO Successfully connected to 192.168.0.97
[01:07:36][I][deep_sleep:136]: Beginning Deep Sleep
[01:07:36][D][lt.preferences:104]: Saving 1 preferences to flash...
[01:07:36][D][lt.preferences:132]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
INFO Processing expected disconnect from ESPHome API for 192.168.0.97
WARNING Disconnected from API
INFO Successfully connected to 192.168.0.97
[01:08:10][I][deep_sleep:136]: Beginning Deep Sleep
[01:08:10][D][lt.preferences:104]: Saving 1 preferences to flash...
[01:08:11][D][lt.preferences:132]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
INFO Processing expected disconnect from ESPHome API for 192.168.0.97
WARNING Disconnected from API
INFO Successfully connected to 192.168.0.97
[01:08:45][I][deep_sleep:136]: Beginning Deep Sleep
[01:08:45][D][lt.preferences:104]: Saving 1 preferences to flash...
[01:08:45][D][lt.preferences:132]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
INFO Processing expected disconnect from ESPHome API for 192.168.0.97
WARNING Disconnected from API
INFO Successfully connected to 192.168.0.97
[01:09:20][I][deep_sleep:136]: Beginning Deep Sleep
[01:09:20][D][lt.preferences:104]: Saving 1 preferences to flash...
[01:09:20][D][lt.preferences:132]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
INFO Processing expected disconnect from ESPHome API for 192.168.0.97
WARNING Disconnected from API
INFO Successfully connected to 192.168.0.97
[01:09:55][I][deep_sleep:136]: Beginning Deep Sleep
[01:09:55][D][lt.preferences:104]: Saving 1 preferences to flash...
[01:09:55][D][lt.preferences:132]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
INFO Processing expected disconnect from ESPHome API for 192.168.0.97
WARNING Disconnected from API
INFO Successfully connected to 192.168.0.97
[01:10:29][I][deep_sleep:136]: Beginning Deep Sleep
[01:10:29][D][lt.preferences:104]: Saving 1 preferences to flash...
[01:10:29][D][lt.preferences:132]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
INFO Processing expected disconnect from ESPHome API for 192.168.0.97
WARNING Disconnected from API
INFO Successfully connected to 192.168.0.97
[01:11:04][I][deep_sleep:136]: Beginning Deep Sleep
[01:11:04][D][lt.preferences:104]: Saving 1 preferences to flash...
[01:11:04][D][lt.preferences:132]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
INFO Processing expected disconnect from ESPHome API for 192.168.0.97
WARNING Disconnected from API
INFO Successfully connected to 192.168.0.97
[01:11:39][I][deep_sleep:136]: Beginning Deep Sleep
[01:11:39][D][lt.preferences:104]: Saving 1 preferences to flash...
[01:11:39][D][lt.preferences:132]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
INFO Processing expected disconnect from ESPHome API for 192.168.0.97
WARNING Disconnected from API
INFO Successfully connected to 192.168.0.97
substitutions:
  name: "door-sensor-laundry-02"
  wifi_ssid: !secret wifi_ssid
  wifi_password: !secret wifi_password
  static_ip: 192.168.0.97
  gateway: 192.168.0.1
  subnet: 255.255.255.0
  sensor_pin: P16

esphome:
  name: $name

bk72xx:
  board: generic-bk7231n-qfn32-tuya
  framework:
    version: dev

api:
 encryption:
   key: !secret api_password

ota:
  password: !secret ota_password

wifi:
  ssid: $wifi_ssid
  password: $wifi_password
  fast_connect: true
  manual_ip:
    static_ip: $static_ip
    gateway: $gateway
    subnet: $subnet

captive_portal:

mdns:

logger:

web_server:
  local: true
      
text_sensor:
  - platform: wifi_info
    ip_address:
      id: ip_address
      name: IP Address

binary_sensor:
  - platform: gpio
    pin: 
      number: $sensor_pin
      mode:
        input: true
        pullup: true
    name: $name Sensor
    device_class: door
    id: door

deep_sleep:
  wakeup_pin: $sensor_pin
  id: deep_sleep_1
  run_duration: 30s

@Xmister
Copy link
Author

Xmister commented Jul 18, 2023

@samhunt In my case multiple pins were reacting to a magnet, and I had to find the proper one for wake source by trial and error.
You can also try if using wakeup_pins (plural) makes any difference.

@samhunt
Copy link

samhunt commented Jul 28, 2023

@samhunt In my case multiple pins were reacting to a magnet, and I had to find the proper one for wake source by trial and error. You can also try if using wakeup_pins (plural) makes any difference.

Thanks, I'll give that a try

@alessiocappa
Copy link

@samhunt, did you manage to make it works?
I'm trying to configure deep sleep on a CBU board, linked to a PIR sensor, but it doesn't seem to work.

I've tried several combination of the deep_sleep parameters, but I always encounter the same problem.
Basically the device goes into deep sleep, but then after something like 5 seconds it wakes up without any changes on the PIN status.

Here is my current setup for deep_sleep and binary_sensor

deep_sleep:
  id: deep_sleep_pir
  run_duration: 15s
  wakeup_pin: P16
  wakeup_pin_mode: INVERT_WAKEUP

binary_sensor:
  - platform: gpio
    pin:
      number: P16
      mode:
        input: true
        pulldown: true
    name: "PIR Sensor"
    device_class: motion

What I noticed is that if I use the INVERT_WAKEUP option, the device goes to sleep and then it wakes up after 5 seconds even if the sensor value is ON or OFF.
If I remove it and keep the default IGNORE option, it does the same while the sensor state is OFF, and it stays in deep sleep if the state is ON until it goes back to OFF.

I tried also with both pullup and pulldown, but the result is the same.

I don't know if there is something wrong with my configuration or with the INVERT_WAKEUP option.
My goal is to put the device in sleep mode as soon as possible and wake it up only if there is a status change, which is what that option should do as far as I understood.

@samhunt
Copy link

samhunt commented Aug 22, 2023

@samhunt, did you manage to make it works? I'm trying to configure deep sleep on a CBU board, linked to a PIR sensor, but it doesn't seem to work.

I've tried several combination of the deep_sleep parameters, but I always encounter the same problem. Basically the device goes into deep sleep, but then after something like 5 seconds it wakes up without any changes on the PIN status.

Here is my current setup for deep_sleep and binary_sensor

deep_sleep:
  id: deep_sleep_pir
  run_duration: 15s
  wakeup_pin: P16
  wakeup_pin_mode: INVERT_WAKEUP

binary_sensor:
  - platform: gpio
    pin:
      number: P16
      mode:
        input: true
        pulldown: true
    name: "PIR Sensor"
    device_class: motion

What I noticed is that if I use the INVERT_WAKEUP option, the device goes to sleep and then it wakes up after 5 seconds even if the sensor value is ON or OFF. If I remove it and keep the default IGNORE option, it does the same while the sensor state is OFF, and it stays in deep sleep if the state is ON until it goes back to OFF.

I tried also with both pullup and pulldown, but the result is the same.

I don't know if there is something wrong with my configuration or with the INVERT_WAKEUP option. My goal is to put the device in sleep mode as soon as possible and wake it up only if there is a status change, which is what that option should do as far as I understood.

No I wasn't able to get libretiny to work. OpenBreken worked fine with the same device, and the batteries were able to last over a month instead of a day or so with this code.
I've ended up replacing my device with one that uses zigbee since the ~8 second deep_sleep start up time didn't work with my use case

@alessiocappa
Copy link

@samhunt, did you manage to make it works? I'm trying to configure deep sleep on a CBU board, linked to a PIR sensor, but it doesn't seem to work.
I've tried several combination of the deep_sleep parameters, but I always encounter the same problem. Basically the device goes into deep sleep, but then after something like 5 seconds it wakes up without any changes on the PIN status.
Here is my current setup for deep_sleep and binary_sensor

deep_sleep:
  id: deep_sleep_pir
  run_duration: 15s
  wakeup_pin: P16
  wakeup_pin_mode: INVERT_WAKEUP

binary_sensor:
  - platform: gpio
    pin:
      number: P16
      mode:
        input: true
        pulldown: true
    name: "PIR Sensor"
    device_class: motion

What I noticed is that if I use the INVERT_WAKEUP option, the device goes to sleep and then it wakes up after 5 seconds even if the sensor value is ON or OFF. If I remove it and keep the default IGNORE option, it does the same while the sensor state is OFF, and it stays in deep sleep if the state is ON until it goes back to OFF.
I tried also with both pullup and pulldown, but the result is the same.
I don't know if there is something wrong with my configuration or with the INVERT_WAKEUP option. My goal is to put the device in sleep mode as soon as possible and wake it up only if there is a status change, which is what that option should do as far as I understood.

No I wasn't able to get libretiny to work. OpenBreken worked fine with the same device, and the batteries were able to last over a month instead of a day or so with this code. I've ended up replacing my device with one that uses zigbee since the ~8 second deep_sleep start up time didn't work with my use case

I'm in the exact same situation... I tried with OpenBeken, same device and same pin settings, and deep sleep seems to be working fine.
However, as you said, the device has a really slow response with that firmware (~8 second also in my case, while it was ~4 with the original firmware), so it's not ideal for me neither. I don't need a super fast response, that's why I'm waiting a bit before moving to a ZigBee device.

With libretiny the response seems to be faster, at least when it reboot immediately after going into deep sleep based on what I was able to test.
If I use a timer instead of a wake up pin, it also works and the connection time seems acceptable for me, but I would need to use a pin to wake up only when needed.

I'm not an expert of C/C++, so it's a bit tricky for me to change the code and do some tests.
However, I can try to compare what is done in the OpenBeken firmware and see if there is any difference with the deep_sleep implementation in libretiny, in order to understand what could be the root cause.

Any help would me more than appreciated. 😃

@alessiocappa
Copy link

I think I found where the problem is.

The condition in lt_deep_sleep_config_gpio is wrong compared to OpenBeken (but also to ESP framework).
Here is the condition in the ESP framework when the wake up GPIO is defined in esp_deep_sleep_enable_gpio_wakeup:

if (mode == ESP_GPIO_WAKEUP_GPIO_HIGH) {
    s_config.gpio_trigger_mode |= (mode << gpio_idx);
} else {
    s_config.gpio_trigger_mode &= ~(mode << gpio_idx);
}

while in libretiny when the deep sleep GPIO is configured in lt_deep_sleep_config_gpio the if statement is reversed:

if (on_high) {
    deep_sleep_param.gpio_edge_map &= (~gpio_index_map);
} else {
    deep_sleep_param.gpio_edge_map |= gpio_index_map;
}

That's why as soon as the device went to sleep using the INVERT_WAKEUP option, it was immediately turned on again.

I didn't want to recompile all the platform code (and honestly I don't even know how to do it 😃), but I tried to apply a quick change in the esp-home deep_sleep component, and it seems to work!
Basically I changed in deep_sleep_component.cpp the initialization of the level variable, by removing the NOT operator:

bool level = this->wakeup_pin_->is_inverted();

In this way I can invert the condition in the platform code, even if it's not correct logically speaking.
I see the level variable is initialized in the same way when multiple wake up pins are defined, so maybe that's the reason why it was working for some people.

I will do some more test, but so far it seems to work as expected. Hope that helps! 🙂

@Xmister
Copy link
Author

Xmister commented Aug 22, 2023

@alessiocappa This what openbeken has to say about it:

gpio_edge_map:The gpio edge bitmap for wakeup gpios,
 *              gpio_edge_map is hex and every bits is map to gpio0-gpio31.
 *              0:rising,1:falling.

Also in their code:

if (falling) {
		if (index >= 32)
			g_gpio_edge_map[1] |= (1 << (index - 32));
		else
			g_gpio_edge_map[0] |= (1 << index);
	}

So it looks like the same implementation as mine. (On rising (high), zero out the pin, on falling, set it to 1).
My INVERT_WAKEUP config also works with it, so I'm not sure what's causing the difference for you

@alessiocappa
Copy link

@Xmister are you using single or multiple wake up pins? I see in your code that the level variable is initialized differently in case multiple pins are defined, which is the same way that works for me. That might be the reason why it works in your case, idk.

I flashed OpenBeken and deep sleep was working on the same device, so I don't think it's an hardware problem.
This is how they set the falling variable:

value = HAL_PIN_ReadDigitalInput(i);
if (value) {
	// on falling edge wake up
	falling = 1;
}
else {
	// on rising edge wake up
	falling = 0;
}

As far as I understand, if the pin is HIGH falling is 1, while if the pin is LOW falling is 0.
I think the naming is a bit confusing; in other words (and based on my understanding), falling is true when the wake up pin is HIGH and we are expecting to exit from deep sleep as soon as it goes LOW and viceversa.

@Xmister
Copy link
Author

Xmister commented Aug 23, 2023

@alessiocappa This is the code for handling the invertion, i.e. if it's currently high, wake up on low.
The platform code you copied from libretiny was just setting a bitmask based on what esphome decided should be the edge. So it's one step later in the process.

@samhunt
Copy link

samhunt commented Aug 23, 2023

I did test out changing the code in my cached libretiny which didn't seem to make any difference. I then reverted, and made the change to libretiny-esphome which also didn't seem to make a difference.
In both cases, my Door Sensor with deep_sleep enabled would randomly wake up after a minute or so.

FWIW my device only has one pin available to attach to deep_sleep

@alessiocappa
Copy link

@alessiocappa This is the code for handling the invertion, i.e. if it's currently high, wake up on low. The platform code you copied from libretiny was just setting a bitmask based on what esphome decided should be the edge. So it's one step later in the process.

I see, it was just to provide the full context. 🙂
Let me try to simulate the same scenario, considering the deep_sleep setting that I'm using:

deep_sleep:
  id: deep_sleep_pir
  run_duration: 15s
  wakeup_pin: P16
  wakeup_pin_mode: INVERT_WAKEUP

And the following code in libretiny-esphome:

bool level = !this->wakeup_pin_->is_inverted();
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
  level = !level;
}

Let's assume P16 is LOW.
P16 is not defined as inverted, so level will be initialized as "1". Then, wakeup_pin_mode is set to INVERT_WAKEUP and digital_read should return 0 for P16, so level will not be inverted and it will stay "1", which is correct considering that we are expecting to wake up the device when the pin goes HIGH.

The function lt_deep_sleep_config_gpio is then called, passing level as second parameter (which is "1"), so when the condition on on_high is evaluated, the pin is set to 0:

if (on_high) {
    deep_sleep_param.gpio_edge_map &= (~gpio_index_map);
} else {
    deep_sleep_param.gpio_edge_map |= gpio_index_map;
}

So the device is woken up immediately, considering that the pin is already in a LOW state.

I double checked the OpenBeken code and indeed the logic seems to be the same, but I think the main difference is that OpenBeken was providing an inverted value by default to me, so I guess that why it was working with OB and it works with libretiny if I modify the definition of level. Not sure why the value is read inverted in OB though, in ESP home I get the right value (ON when HIGH, OFF when LOW), unless I explicitly declare it as inverted.

Also, it's not clear to me why level is set differently in libretiny-esphome in case a single or multiple wake up pins are defined:

if (this->wakeup_pin_ != nullptr) {
    bool level = !this->wakeup_pin_->is_inverted();
    if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
      level = !level;
    }
if (wakeup_pins_.size() > 0) {
    bool level;
    for (WakeUpPinItem item : this->wakeup_pins_) {
      level = item.wakeup_pin->is_inverted();
      if (item.wakeup_pin_mode == WAKEUP_PIN_MODE_INVERT_WAKEUP && item.wakeup_pin->digital_read()) {
        level = !level;
      }
      lt_deep_sleep_config_gpio(1 << item.wakeup_pin->get_pin(), level);
    }
  }

Thanks for your help! 👍

@Xmister
Copy link
Author

Xmister commented Aug 23, 2023

@alessiocappa I see, if the openbeken logic is based on an inverted value all the time, then yes, our logic is wrong. I indeed use the wakeup_pins definition which has the "bug" of wrongly initializing the level variable, and that's why it works for me.
And indeed, the translated manufacturer docs agree that "1" should be the rising edge: https://docs-bekencorp-com.translate.goog/sdk_3.0.x/bk7238/html/developer-guide/power_save/sleep_test.html?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=hu&_x_tr_pto=wapp

@Xmister
Copy link
Author

Xmister commented Aug 23, 2023

Created libretiny-eu/libretiny#159
And also github://Xmister/libretuya-esphome@deep-sleep-compat should work with the current libretiny version.

@samhunt
Copy link

samhunt commented Aug 24, 2023

I don't know what I am doing wrong, but I cannot get that change in libretiny (which i have in my esphome cache) and this branch to work.
Even with many different combinations of the binary_sensor and deep_sleep mode (INPUT/INPUT_PINUP) and wakeup_pin_mode (IGNORE/INVERT_WAKEUP) this door sensor wakes up within 30-60 seconds.

external_components:
  - source: github://Xmister/libretuya-esphome@deep-sleep
    components: [ esp32, deep_sleep ]

binary_sensor:
  - platform: gpio
    pin: 
      number: $sensor_pin
      mode: INPUT
    name: $name Sensor
    device_class: door
    id: door

deep_sleep:
  wakeup_pin: 
    number: $sensor_pin    
   # mode: INPUT_PULLUP
  wakeup_pin_mode: INVERT_WAKEUP
  id: deep_sleep_1
  run_duration: 60s
  sleep_duration: 1440min

@Xmister
Copy link
Author

Xmister commented Sep 6, 2023

@samhunt Is it also happening when you leave out sleep_duration?

@samhunt
Copy link

samhunt commented Sep 26, 2023

@samhunt Is it also happening when you leave out sleep_duration?

Sorry for the late reply. I haven't been able to give that a long test, but that seems to have done the trick

@Xmister Xmister requested a review from kuba2k2 as a code owner October 27, 2023 13:28
@alessiocappa
Copy link

Hi @Xmister, first of all, thanks a lot for having updated the deep sleep component in order to work with the official ESPHome release! I hope it will be implemented in the official firmware as well, but for now I can use it as an external component.

However, I would like to check with you how does it work in your case from a performance point of view.
I was initially testing it on a motion sensor, but it was too slow for my use case and I decided to use another device; however, I had a few Wi-Fi door sensors laying around and I wanted to do some tests on them as well.
On one of them I have installed ESPHome, enabled deep_sleep + wakeup_pin and connected the device to my MQTT broker, while the other sensor is still using the Tuya firmware.

What I noticed is that the response time on the one with ESPHome is way more slower; indeed, the one with the stock firmware seems to be pretty fast, with an average response time of about 3s, while the device with ESPHome installed can take up to 8/9, sometimes 10 seconds to report the state.
I've already tried several options, but I can't achieve a good response time. At the moment I have in my configuration a static IP set, as well as the fast_connect option enabled, and based on what I found on the internet, the step to connect to the network seems to be the main bottleneck.

What is the response time in your case? Do you know if there is a way to improve it?
I don't know how the stock firmware works, but I assume that if the device can report its status in about 3s with the Tuya firmware, which is also communicating to the Tuya cloud platform, I would expect at least the same response time using ESPHome (if not better considering that everything is local).
Maybe the stock firmware is using a different power saving approach, instead of the deep sleep?

@Xmister
Copy link
Author

Xmister commented Nov 1, 2023

@alessiocappa Hi,
8-10s is what I get as well. What I can think of is neither esphome or libretiny was written for fast connection times. They probably miss some tricks that can speed up WiFi negotiation.

In my use case (door sensor as gas meter) I can just update the internal state on wake within 1s, and only report back to HA every 10-100 wakes.

@Liionboy
Copy link

How do you put deep_sleep on same pin because at me tell me always duplicate pin use

@Liionboy
Copy link

deep_sleep:
id: deep_sleep_pir
run_duration: 15s
wakeup_pin: P16
wakeup_pin_mode: INVERT_WAKEUP

how you use 2 PIN in yaml? bacuse always i have :
Pin 6 is used in multiple places.

And can't compile

@samhunt
Copy link

samhunt commented Jan 30, 2024

deep_sleep:
id: deep_sleep_pir
run_duration: 15s
wakeup_pin: P16
wakeup_pin_mode: INVERT_WAKEUP

how you use 2 PIN in yaml? bacuse always i have : Pin 6 is used in multiple places.

And can't compile

Is this your yaml that is not compiling?

CodeInPolish and others added 25 commits May 13, 2024 11:25
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Hills <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Hills <[email protected]>
@SimoPk
Copy link

SimoPk commented Aug 28, 2025

I made a couple of changes starting from @Xmister code, in order to support keeping pins floating upon deep sleep (it was useful to me for a water leak sensor based on cb3s, but you can find an exact same issue discussed by other guys here: https://www.elektroda.com/rtvforum/topic3954826.html). You can find my code here: https://github.com/SimoPk/deep_sleep_libretiny, feel free to incorporate it into this pr which has probably more visibility to the other users.

@Minims
Copy link

Minims commented Aug 28, 2025

I made a couple of changes starting from @Xmister code, in order to support keeping pins floating upon deep sleep (it was useful to me for a water leak sensor based on cb3s, but you can find an exact same issue discussed by other guys here: https://www.elektroda.com/rtvforum/topic3954826.html). You can find my code here: https://github.com/SimoPk/deep_sleep_libretiny, feel free to incorporate it into this pr which has probably more visibility to the other users.

@SimoPk Hi, i'm trying to make my water leak sensor (cb3s) working too, can you share your esphome yaml for deepsleep ?

@SimoPk
Copy link

SimoPk commented Sep 1, 2025

Hi, this is the configuration I've developed. This config won't work if you just copy/paste it because it relies on the structure of my repo with all my other esphome devices and on some components I developed but are not relevant for the discussion. The "meat" related to the water leak sensor is all there though.
This is the device I'm using: https://www.amazon.it/dp/B0C4B2BRQC?ref=ppx_yo2ov_dt_b_fed_asin_title&th=1

Let me say that even though this is working at the moment, I'm not satisfied overall with the solution. In this week that I developed this I learned that using wifi for this battery operated sensor is not the right choice... based on some back of the envelope math the batteries will last a couple of months tops, even after having optimized all that can be optimized. Zigbee is the way to go for this application, let's hope in the esp32 c6 for the future.

a few notes:

  • the long deep sleep is completely fine IMO, because the cpu is listening during sleep to the water contact, so it will exit and stay out of deep sleep, sounding the alarm, in case of water
  • power save mode for the wifi component is set to none because the wifi signal where I'll put the sensor is non optimal, plus from my measurements the saving is negligible during the brief period the sensor stays awake.

pins:

pin_led: P26
pin_button: P24
pin_bat_relay: P14
pin_bat_level: P23
pin_contact: P8
pin_buzzer: P6

config

esphome:
  name: ${devicename}
  friendly_name: ${friendlyname}
  on_boot:
    - priority: -100
      then:
        - wait_until:
            switch.is_on: mqtt_ready
        - lambda: |-
            id(sleeping).publish_state(false);
        - component.update: wifi_signal_db
        - component.update: battery_voltage
        - component.update: rev_describe
        - component.update: rev_commit
        - component.update: rev_build
        - lambda: |-
            id(deep_sleep_centralized_suspend).publish_state(id(deep_sleep_centralized_suspend).state);
        - lambda: |-
            id(input_contact).publish_state(id(input_contact).state);
        - lambda: |-
            id(input_button).publish_state(id(input_button).state);
        - wait_until:
            condition:
              switch.is_on: deep_sleep_centralized_suspend_received
            # make sure the centralized suspend is created and actuated at least once
            # (even in the case the mqtt broker is purged, and thus the retained topic is deleted)
            # otherwise this timeout will always be hit, affecting battery life significantly
            # (that is adding 10s per cycle to the 23s it usually uses)
            timeout: 10s
        - if:
            condition:
              switch.is_on: deep_sleep_state
            then:
              - lambda: |-
                  id(sleeping).publish_state(true);
              - deep_sleep.enter: deep_sleep_control
  on_shutdown:
    - priority: -100
      then:
        - lambda: |-
            id(sleeping).publish_state(true);

bk72xx:
  board: cb3s

external_components:
  - source:
      type: git
      url: <put url to deep_sleep_libretiny here>
      ref: main

wifi:
  ssid: ${wifi_ssid}
  password: ${wifi_password}
  domain: ${domain}
  power_save_mode: none
  fast_connect: true

  manual_ip:
    static_ip: <ip>
    gateway: <gateway>
    subnet: <subnet>
    dns1: <dns>

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: ${fallback_ssid} ${devicename}
    password: ${fallback_password}

captive_portal:

# Enable logging
logger:
  level: NONE

mqtt:
  id: mqtt_client
  broker: ${mqtt_broker}
  port: ${mqtt_broker_port}
  username: ${mqtt_username}
  password: ${mqtt_password}
  birth_message:
  will_message:
  on_connect:
    - lambda: |-
        id(mqtt_client).subscribe("leak_sensors/deep_sleep_suspend", [=](const std::string &topic, const std::string &payload) {
            if (payload.compare("ON") == 0) {
              id(deep_sleep_centralized_suspend).publish_state(true);
            } else {
              id(deep_sleep_centralized_suspend).publish_state(false);
            }

            id(deep_sleep_centralized_suspend_received).publish_state(true);
        });
    - switch.turn_on: mqtt_ready

ota:
  platform: esphome
  password: ${ota_password}

button:
  - platform: restart
    name: Restart
    device_class: restart

binary_sensor:
  - platform: gpio
    pin:
      number: ${pin_contact}
      allow_other_uses: true
    name: Water contact
    id: input_contact
    trigger_on_initial_state: true
    on_state:
      - lambda: |-
          if (x) {
            id(deep_sleep_state).turn_off();
            id(sound_alarm).execute();
          } else {
            id(rtttl_player).stop();
            id(sound_alarm).stop();

            if (id(deep_sleep_centralized_suspend).state) {
              id(deep_sleep_state).turn_off();
            } else {
              id(deep_sleep_state).turn_on();
            }
          }

  - platform: gpio
    pin:
      number: ${pin_button}
      allow_other_uses: true
      inverted: true
    name: Button
    id: input_button
    on_state:
      - lambda: |-
          if (x) {
            id(button_long_press).execute();
            id(rtttl_player).stop();
            id(sound_alarm).stop();
          } else {
            id(button_long_press).stop();
          }

  - platform: template
    name: Sleeping
    id: sleeping

  - platform: template
    name: Deep sleep centralized suspend
    id: deep_sleep_centralized_suspend
    on_state_change:
      - lambda: |-
          if (x.has_value()) {
            if (x.value()) {
              id(deep_sleep_state).turn_off();
            } else {
              if (x_previous.has_value()) { 
                // if it's the initial state, setting deep_sleep_state here could override
                // a manual command given using the physical button BEFORE api is connected,
                // which is likely to happen because the user will long press the button
                // to avoid deep sleep exactly during the first seconds the device is on
                id(deep_sleep_state).turn_on();
              }
            }
          }

output:
  - platform: libretiny_pwm
    pin: ${pin_buzzer}
    frequency: 10000 Hz
    id: output_buzzer

  - platform: gpio
    pin: ${pin_led}
    id: status_led

sensor:
  - platform: uptime
    name: Uptime Sensor
    id: uptime_s
    entity_category: "diagnostic"

  - platform: wifi_signal
    name: WiFi Signal Sensor
    id: wifi_signal_db
    update_interval: 15s
    entity_category: "diagnostic"

  - platform: adc
    pin: ${pin_bat_level}
    id: battery_voltage
    name: Battery Voltage
    filters:
      - multiply: 2.4
    on_value:
      - lambda: |-
          float percent = min(max(100 * (x - 2.2) / 0.8, 0.0), 100.0);
          id(battery_percent).publish_state(percent);
    icon: mdi:battery

  - platform: template
    name: Battery %
    id: battery_percent
    unit_of_measurement: "%"

switch:
  - platform: shutdown
    id: shutdown_device
    internal: true

  - platform: gpio
    # enables measuring voltage - see https://www.elektroda.com/rtvforum/topic3914412-30.html#gallery-2
    id: batt_dv
    internal: true
    restore_mode: ALWAYS_ON
    pin: ${pin_bat_relay}

  - platform: template
    id: deep_sleep_state
    name: "Deep sleep enabled"
    restore_mode: ALWAYS_ON ########### THIS IS FOR PRODUCTION
    # restore_mode: ALWAYS_OFF ######## THIS IS FOR TESTING
    internal: true
    optimistic: true
    on_turn_on:
      - deep_sleep.allow: deep_sleep_control
      - output.turn_off: status_led
    on_turn_off:
      - deep_sleep.prevent: deep_sleep_control
      - output.turn_on: status_led

  - platform: template
    name: MQTT ready
    id: mqtt_ready
    internal: true
    entity_category: config
    restore_mode: ALWAYS_OFF
    assumed_state: off
    optimistic: true

  - platform: template
    name: Deep sleep centralized suspend received
    id: deep_sleep_centralized_suspend_received
    internal: true
    entity_category: config
    restore_mode: ALWAYS_OFF
    assumed_state: off
    optimistic: true

script:
  - id: sound_alarm
    then:
      - while:
          condition:
            lambda: 'return 1;'
          then:
            # - rtttl.play: "siren:d=8,o=5,b=100:d,e,d,e,d,e,d,e"
            - rtttl.play: "two_short:d=4,o=5,b=100:16e6,16e6"
            - delay: 2s

  - id: button_long_press
    then:
      - delay: 2s
      - switch.toggle: deep_sleep_state

rtttl:
  output: output_buzzer
  id: rtttl_player
  gain: 60%

deep_sleep:
  id: deep_sleep_control
  sleep_duration: 3600s
  run_duration: 60s
  wakeup_pins:
    - pin:
        number: ${pin_contact}
        allow_other_uses: true
      wakeup_pin_keep_floating: true
    - pin:
        number: ${pin_button}
        allow_other_uses: true
        inverted: true

switch for suspending deep sleep on homeassistant

mqtt:
  switch:
    - command_topic: "leak_sensors/deep_sleep_suspend"
      state_topic: "leak_sensors/deep_sleep_suspend"
      unique_id: leak_sensors_deep_sleep_suspend
      name: "Leak Sensors Suspend Deep Sleep"
      icon: "mdi:battery-lock"
      retain: true

@Minims
Copy link

Minims commented Sep 2, 2025

thanks @SimoPk , I will try esphome with mine, Currently i can use only openbeken, but i would prefer esphome.
https://www.elektroda.com/rtvforum/topic3954826.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.