Controlling light bulb using power switch button - WAF life hack

I have invested in wifi light bulb (Biltema/Tuya concerted to ESPHome) but then everything is controlled remotely and the Wife/Spouse/Family Acceptance Factor has not been that great. I have taped the light switch buttons and the family members are stretching the tape. :rofl:

I have now created a Switch button sequence where the light bulb changes brightness (it’s so far just a monochrome light bulb) each time I power cycle it, e.g. 100%, 75%, 50%, 10%, off, 100%, 75%, etc, etc.

Please note that it’s important to separate the Power-On boot behavior from the other boot sequences.

I share the code below if someone else would like to do the same, or if you are more fluent in ESPHome YAML hacking than I am and are willing to improve what I have done.

In short what this does:

  1. Defines global variables for counting Power-On boots, storing last on/off state, and storing last brightness
  2. Saving on/off state and brightness whenever possible at a shutdown, i.e. some kind of controlled shutdown like a SW reboot. This eliminates the need for defining a light component restore_mode and will save the FLASH memory since there will be fewer write cycles
  3. Checks the boot counter to find out the sequence number of Power-On reboots using a template switch and case statement. If not a Power-On boot, we just restore the previous state saved in the global variables
  4. To not have to wait one minute when power-cycle the switch I needed to decrease the flash write latency using the preferences component: preferences: flash_write_interval: 2s
  5. And I needed to turn off the restore mode in the light component
  6. Whenever having a group light bulbs connected to the same switch we need to sync the counters. I did this by exposing a boot counter reset button to HA

And that’s pretty much all

esphome:
  name: "iot-esphome-light-e27-nb-m-bt06"
  friendly_name: "iot-esphome-light-e27-nb-m-bt06"

  on_boot:
    priority: 601
    then:
      - if:
          condition:
            lambda: |-
              return ESP.getResetReason() == "Power-On";
          then:
            - light.turn_on: 
                id: light_monochromatic
                brightness: !lambda |-
                  float brightness;
                  switch ((id(boot_count) += 1) % 5) {
                    case 0: brightness = 1.0; break; // 100% brightness
                    case 1: brightness = 0.75; break; // 75% brightness
                    case 2: brightness = 0.5; break; // 50% brightness
                    case 3: brightness = 0.1; break; // 10% brightness
                    case 4: brightness = 0.0; break; // Off
                    default: brightness = 1.0; // Just in case
                  }
                  return brightness;
          else:
            - light.control: 
                id: light_monochromatic
                state: !lambda |-
                  return id(last_light_state);
                brightness: !lambda |-
                  return id(last_brightness);

  on_shutdown: 
    then:
      - lambda: |-
          id(last_light_state) = id(light_monochromatic).current_values.is_on();
          id(last_brightness) = id(light_monochromatic).current_values.get_brightness();

globals:
  - id: boot_count
    type: int
    restore_value: yes
    initial_value: '0'
  - id: last_brightness
    type: float
    restore_value: yes
    initial_value: "0.0"
  - id: last_light_state
    type: bool
    restore_value: yes
    initial_value: 'false'

preferences:
  flash_write_interval: 2s #Shorten the default value from 1 min to 2 second

bk72xx:
  board: generic-bk7231t-qfn32-tuya

logger:
  level: DEBUG

web_server:

captive_portal:

mdns:

api:
  encryption:
    key: !secret api_encryption-key

ota:
  platform: esphome
  id: my_ota
  password: !secret ota_password

wifi:
  networks:
  - ssid: !secret wifi_ssid_iot
    password: !secret wifi_password_iot


  ap:
    password: !secret wifi_password1

button:
  - platform: restart
    name: Restart
  - platform: template
    name: "Reset Boot Count"
    on_press:
      then:
        - lambda: |-
            id(boot_count) = 0; // Reset the boot_count global variable

text_sensor:
  - platform: debug
    reset_reason:
      name: Reset Reason

sensor:
  - platform: uptime
    name: Uptime
  - platform: template
    name: "Boot Count"
    id: boot_count_sensor
    lambda: |-
      return id(boot_count);

output:
  - platform: libretiny_pwm
    id: output_cold
    pin: P7

light:
  - platform: monochromatic
    id: light_monochromatic
    name: Light
    output: output_cold
    #restore_mode: ALWAYS_ON
    #restore_mode: RESTORE_DEFAULT_ON
    default_transition_length: 
      seconds: 2

But there are some more things to do:

  • Making the code easier to modify for those have more complex setup with colors etc.
  • Saving some Flash writes by checking if the on/off state and brightness is the same as the stored values in the shutdown trigger. This since the ESP/Tuya chip with ESPHome FW sometimes reboots occasionally and we don’t write the the Flash that if not needed.
  • Check if the flash_write_interval that I set to 2 seconds really works. From what I can see, I can toggle the switch button faster than 2 seconds and it still works. Having longer time will show some mercy to the chip power circuits

Edit: I added the same feature for a light bulb that I could controol the tone and then it looks like this in the boot automation. As can be seen, quite messy

  on_boot:
#    - priority: 0
#      then:
#        - lambda: |-
#            id(my_ota).set_auth_password(ESPHOME_IDFN("ota_password"));
    priority: 601
    then:
      - if:
          condition:
            lambda: |-
              id(boot_count) += 1;
              return ESP.getResetReason() == "Power-On";
          then:
            - light.turn_on: 
                id: light_cwww
                brightness: !lambda |-
                  float brightness;
                  switch (id(boot_count) % 5) {
                    case 0: brightness = 1.0; break;
                    case 1: brightness = 1.0; break;
                    case 2: brightness = 0.2; break;
                    case 3: brightness = 0.2; break;
                    default: brightness = 0.0; // Off
                  }
                  return brightness;
                cold_white: !lambda |-
                  float cold_white;
                  switch (id(boot_count) % 5) {
                    case 0: cold_white = 1.0; break;
                    case 1: cold_white = 0.0; break;
                    case 2: cold_white = 1.0; break;
                    case 3: cold_white = 0.0; break;
                    default: cold_white = 0.0; // Off
                  }
                  return cold_white;
                warm_white: !lambda |-
                  float warm_white;
                  switch (id(boot_count) % 5) {
                    case 0: warm_white = 0.0; break;
                    case 1: warm_white = 1.0; break;
                    case 2: warm_white = 0.0; break;
                    case 3: warm_white = 1.0; break;
                    default: warm_white = 0.0; // Off
                  }
                  return warm_white;
          else:
            - light.control: 
                id: light_cwww
                state: !lambda |-
                  return id(last_light_state);
                cold_white: !lambda |-
                  return id(last_cold_white);
                warm_white: !lambda |-
                  return id(last_warm_white);
                brightness: !lambda |-
                  return id(last_brightness);

  on_shutdown: 
    then:
      - lambda: |-
          id(last_light_state) = id(light_cwww).current_values.is_on();
          id(last_cold_white) = id(light_cwww).current_values.get_cold_white();
          id(last_warm_white) = id(light_cwww).current_values.get_warm_white();
          id(last_brightness) = id(light_cwww).current_values.get_brightness();

Taping off or forbidding the use of regular buttons would not sit well with me either. I’d rather invest in smart switches than smart bulbs. If they have to be smart bulbs, a smart switch in detached mode would be a good choice. Another choice would be to wire for permanent power and use a wall remote. If you use the last two options, a direct binding between the remote and the smart bulb would make sense. Otherwise the light won’t turn on if HA is down. That does also not sit well with family members.

1 Like

Having an old house without the zero/neutral wire at the switches and only surface mounted wall switches makes it challenging to use smart switches.
And it’s so much easier to just replace the light bulb not being an certified electrician. Yes… I do a lot of this myself, even building 230 VAC power regulators for my radiators, but as I said, it’s a lot of work to get smart wall mounted light switches. Been there, Done that…

I have tried to figure out how to replace my switches to smart swtches in a “sometime selling the house” maner but haven find a way in all these years

And the smart light bulbs only costs me 4 EUR/USD

1 Like

Yes I have seen these. But they are to big to fit within the thin surface mounted wall switches I currently have and buying thicker just to fit the SonOff switches would feel like a down-grade