(Bosch) Ebike charging automation

I have a different approach as my Shimano STEPS system charges full speed all the way through 100% so I needed an another trigger than a power drop of the smart plug.

Background: The battery management of Shimano is a desaster, my first battery has only an 79% healty status (capacity) after just 71 charging cycles… so before I invest in a new one, I made precautions :wink:

I built a light sensor from a ESP8266 and a light resistor (<10€), programmed by ESPhome (see code below). The sensor is placed above the last one of the five charging LEDs of the battery, which starts to blink if charge level exceeds 80%. As the sensor detects that this LED starts blinking, a simple automation shuts down the smart power. My battery gets charged to exactly 82% reproducibly.

Two small but strong ring magnets hold the sensor in place over the last LED of the battery.

The sensor

  • checks light every 1,5 sec → “light detected”
  • checks if light was detected at least once in the last 10 seconds → “LED is blinking or on”
  • reports “LED is blinking or on” to Home Assistant every 5 seconds

This logic was needed as the LED blink as the charge level surpasses 80%. The LED is on exactly every other second (1 sec on, one sec off), so light gets checked every 1,5 sec just to be asynchron with my detection and the blinking. A threshold of 0.7 for light detection is used (0 is darkness, 1 is full light, the LED produces 0.5 in my setup).

esphome:
  name: esp8266-steps
  friendly_name: ESP8266-STEPS

esp8266:
  board: esp01_1m

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx="

ota:
  - platform: esphome
    password: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esp8266-Steps Fallback Hotspot"
    password: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

captive_portal:


sensor:
  - platform: adc
    pin: A0
    name: "LDR Sensor"
    id: ldr_sensor

binary_sensor:
  - platform: template
    name: "LED Blinking or On"
    id: led_blinking_or_on
    device_class: light
    lambda: |-
      // Gibt 'true' zurück, wenn in den letzten 10 Sekunden Licht erkannt wurde
      for (int i = 0; i < 10; i++) {
        if (id(light_history)[i]) {
          return true;
        }
      }
      return false;

globals:
  - id: light_detected
    type: bool
    restore_value: no
    initial_value: "false"

  - id: light_history
    type: std::vector<bool>
    restore_value: no
    initial_value: "std::vector<bool>(10, false)"

interval:
  # Aktualisiere den LDR-Sensor jede Sekunde und speichere Erkennungsergebnisse
  - interval: 1.5s
    then:
      - component.update: ldr_sensor
      - if:
          condition:
            # Wenn der LDR-Wert unter dem Schwellenwert liegt, wird Licht erkannt
            lambda: 'return id(ldr_sensor).state < 0.7;'
          then:
            - globals.set:
                id: light_detected
                value: "true"
          else:
            - globals.set:
                id: light_detected
                value: "false"

      # Speichere das aktuelle Ergebnis in der Liste und verschiebe die Einträge
      - lambda: |-
          id(light_history).erase(id(light_history).begin());
          id(light_history).push_back(id(light_detected));

  # Alle 5 Sekunden prüfen, ob innerhalb der letzten 10 Sekunden Licht erkannt wurde
  - interval: 5s
    then:
      - if:
          condition:
            lambda: |-
              // Prüfen, ob mindestens ein 'true' in den letzten 10 Sekunden gespeichert ist
              for (int i = 0; i < 10; i++) {
                if (id(light_history)[i]) {
                  return true;
                }
              }
              return false;
          then:
            - logger.log: "Light detected in the last 10 seconds."
          else:
            - logger.log: "No light detected in the last 10 seconds."
3 Likes