Big update to the Powerpal ESPHome component — battery sensor fixed, more reliable, new features
Been doing a proper pass over this project recently and there’s a lot to share. If you’re running this component, it’s worth updating — several real bugs are fixed and a few new things are worth turning on.
What’s fixed:
- The battery sensor was silently broken — it never actually read the battery level from the device. It works properly now.
- Reconnects after a BLE dropout are faster and more reliable (removed a race that could stack up duplicate reconnect attempts).
- Power readings used to go blank for a while after any reconnect — they now come back immediately, with a sanity check that skips the calculation only for a genuinely long outage rather than showing a garbage number.
- Fixed compiler warnings/errors some people were hitting on newer ESPHome versions.
- Fixed a
web_serverauth deprecation warning. - Cleaned out a chunk of old dead code and a couple of unnecessary BLE round-trips on every reconnect.
What’s new:
- A LED Sensitivity sensor (shows the device’s pulse-detection sensitivity setting).
- A button to retrieve your Powerpal’s API key and device ID straight into Home Assistant — no more digging through logs.
- An optional automatic RTC sync for the Powerpal’s onboard clock (syncs on boot and every 6 hours).
- A documented way to disable WiFi power-saving (
power_save_mode: none), which can help BLE reliability since the ESP32 shares one radio for both.
Also cleaned up: the README no longer references cloud-uploading as a working feature (it never actually was — if you want your data in the Powerpal app too, there’s now a documented Home Assistant rest_command approach that actually works, using the API key/device ID this component already exposes).
Credit where it’s due — a few things came from the community, not just from me:
- CV8R — found the LED Sensitivity characteristic UUID (in PR #68), which is what made that new sensor possible.
- SleepinDevil — the original idea for retrieving the API key via a getter, and the RTC time-sync button pattern.
- skinnybuddah — packaged that into a proper automatic sync script, and flagged the
power_save_modetip. - elysiandc — correctly diagnosed a real compile error in PR #58 (already independently fixed a different way by the time I got to it, but the diagnosis was right).
- BenBarat and craigmate — reported the compile-warning and battery issues that are now fixed.
How to update: point your external_components at ref: "main" instead of a pinned version tag, so you get fixes as they land rather than waiting on a tagged release:
esphome:
name: powerpal-gateway
friendly_name: Powerpal Gateway
on_boot:
priority: -100
then:
- script.execute: sync_powerpal_time
esp32:
board: esp32dev
framework:
type: esp-idf
external_components:
- source:
type: git
url: https://github.com/gurrier/esphome-powerpal_ble.git
ref: "main" # or a specific version tag, e.g. "1.6.1", to pin to a stable release
components: [ powerpal_ble ]
time:
- platform: homeassistant
id: homeassistant_time
ble_client:
- mac_address: XX:XX:XX:XX:XX:XX # << EDIT HERE
id: powerpal
button:
- platform: restart
name: "Restart"
icon: "mdi:restart"
- platform: template
name: "Retrieve Powerpal API Key and Device ID"
on_press:
then:
- lambda: |-
id(powerpal_api_key).publish_state(id(powerpal_ble_sensor).get_apikey());
id(powerpal_device_id).publish_state(id(powerpal_ble_sensor).get_device_id());
text_sensor:
- platform: template
name: "Powerpal API Key"
id: powerpal_api_key
- platform: template
name: "Powerpal Device ID"
id: powerpal_device_id
script:
- id: sync_powerpal_time
mode: restart
then:
# Wait for Home Assistant time (max 30s)
- wait_until:
condition:
lambda: 'return id(homeassistant_time).now().is_valid();'
timeout: 30s
# Wait for BLE connection (max 30s)
- wait_until:
condition:
lambda: 'return id(powerpal).connected();'
timeout: 30s
# Only sync if both are ready
- if:
condition:
lambda: |-
return id(homeassistant_time).now().is_valid() &&
id(powerpal).connected();
then:
- logger.log: "Syncing Powerpal RTC time"
- ble_client.ble_write:
id: powerpal
service_uuid: '59DAABCD-12F4-25A6-7D4F-55961DCE4205'
characteristic_uuid: '59DA0004-12F4-25A6-7D4F-55961DCE4205'
value: !lambda |-
uint32_t t = id(homeassistant_time).now().timestamp;
return std::vector<uint8_t>{
(uint8_t)(t), (uint8_t)(t >> 8),
(uint8_t)(t >> 16), (uint8_t)(t >> 24)
};
else:
- logger.log: "Skipping RTC sync (timeout or not connected)"
- delay: 2min
- script.execute: sync_powerpal_time
interval:
- interval: 6h
then:
- script.execute: sync_powerpal_time
sensor:
- platform: powerpal_ble
id: powerpal_ble_sensor
ble_client_id: powerpal
power:
name: "Powerpal Power"
battery_level:
name: "Powerpal Battery"
led_sensitivity:
name: "Powerpal LED Sensitivity"
daily_energy:
name: "Powerpal Daily Energy"
daily_pulses:
name: "Powerpal Daily Pulses"
energy:
name: "Powerpal Total Energy"
watt_hours:
name: "Powerpal Watt Hours"
pulses:
name: "Powerpal Pulses"
timestamp:
name: "Powerpal Timestamp"
cost:
name: "Powerpal Cost"
pairing_code: 123456 # << EDIT HERE
notification_interval: 1 # In seconds
pulses_per_kwh: 1000
time_id: homeassistant_time
logger:
# level: DEBUG
# logs:
# powerpal_ble: DEBUG
api:
encryption:
key: !secret esphome_api_encryption_key
ota:
platform: esphome
password: "XXXXXXXXXXXXXXXXXXXXX" # << EDIT HERE
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: none # avoid WiFi power-saving interfering with BLE timing/reliability
ap:
ssid: "Powerpal Fallback Hotspot"
password: "XXXXXXXX" # << EDIT HERE
web_server:
port: 80
auth:
type: digest
username: !secret esphome_web_username
password: !secret esphome_web_password
Repo’s here if you want to look at the code or file an issue: GitHub - gurrier/esphome-powerpal_ble · GitHub