Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

image

philips_shaver_native

A native ESPHome external component for Philips BLE shavers (Series 5000–9000, OneBlade, i9000 — anything exposing the proprietary 0x0100/0x0300 GATT services). Runs entirely on an ESP32: pairing, decoding and control happen locally, with no cloud, no phone app, and no Home Assistant dependency at runtime.

Developed and hardware-tested against an S7882; the protocol layer follows the reverse-engineered reference in docs/PROTOCOL.md (decompiled official APK + upstream community integration + live captures), so other models should degrade gracefully: characteristics a model does not expose simply leave their entities empty.

Current version: 1.16.0 — 70 GATT data points decoded. See CHANGELOG.md.

Highlights

  • Full sensor coverage: battery, motor RPM/current (raw ÷ 3.036), pressure (signed, as the manufacturer app reads it), head wear, session stats, charge/cleaning cycles, per-session motion summary, sanitizing case, and more — plus diagnostic text sensors (mode, motion, load type, notifications, firmware/serial…).
  • Control: shaving mode select, light-ring brightness, GuidanceModes coaching select (the two-bit field at 0x0319 bits 4–5, written correctly as a unit), light-ring RGB colours with a working anti-echo, charging-stand animation, greeting, blade-replacement reset, notification management.
  • History playback: the on-device session log (≈20 sessions) is replayed as esphome.philips_shaver_history Home Assistant events, in the exact read-record-then-advance sequence the device expects.
  • Cleaning cartridge model: usage-based estimate persisted in NVS, with an SNTP clock or banked-uptime fallback.
  • Robust BLE: LE Secure Connections pairing with auto-confirmed Numeric Comparison, a single-ATT-slot state machine with watchdogs on reads, writes and subscriptions, reconnect backoff, stale-bond purge, and decoded SMP failure reasons (PAIR_NOT_SUPPORT means the shaver's single pairing slot is taken — see Troubleshooting).
  • Research tools: read_characteristic / write_characteristic services, a GATT table dump, a BLE scan helper, and an explore_unknown mode that watches the characteristics nobody has named yet.

Requirements

  • ESP32 with the esp-idf framework (Bluedroid).
  • esp32_ble_tracker with active: true — the shaver only advertises its service UUID in the scan response.
  • esp32_ble: io_capability: display_yes_no and max_notifications: 30 (the table declares 27 notifying characteristics — 24 features plus 3 explorer targets — so a maxed-out model subscribes up to 27; an S7882 lands at 19 with the explorer on. Bluedroid's default of 5 is far too low).
  • CONFIG_BT_GATTC_MAX_CACHE_CHAR: "100" — the shaver exposes ~66 characteristics, the default cache of 40 truncates discovery.
  • api: custom_services: true and homeassistant_services: true (the component registers services and fires events unconditionally).
  • Optional but recommended: an SNTP time: platform for the cartridge model and the sync_time_of_day button.

Install

Copy components/philips_shaver_native/ next to your YAML and reference it:

external_components:
  - source:
      type: local
      path: components
    components: [philips_shaver_native]

ble_client:
  - mac_address: "AA:BB:CC:DD:EE:FF"   # static random — find it with scan_ble
    id: shaver_ble
    auto_connect: true

philips_shaver_native:
  id: shaver
  ble_client_id: shaver_ble
  battery_level:
    name: "Battery"
  pressure:
    name: "Pressure"
  connected:
    name: "Connected"

The full example shows every entity, the recommended radio/boot ordering (enable_on_boot: false + ble.enable after Wi-Fi), and the two options the colour lights require (gamma_correct: 1.0, default_transition_length: 0s — without them the anti-echo cannot match bytes and colour changes bounce back over BLE).

Pairing — read this first

The shaver holds one pairing slot. It must be unpaired from the phone, the app and on the handle before the ESP can bond. Pairing is LE Secure Connections; the component auto-confirms the Numeric Comparison. If logs show PAIR_NOT_SUPPORT, the slot is taken — clear it on the shaver itself, then press the Pair button entity. Full flow and failure codes: docs/TROUBLESHOOTING.md.

Documentation

Document Contents
docs/CONFIGURATION.md Every option, entity, service and event, with the GATT source of each value
docs/PROTOCOL.md The BLE protocol reference: services, characteristics, enums, composite blocks, write payloads, open questions
docs/TROUBLESHOOTING.md Pairing, failure codes, backoff, discovery timing, empty entities
docs/ARCHITECTURE.md How the component works inside: phase machine, watchdogs, anti-echo, history flow, explorer
CHANGELOG.md Version history, including which decodes are speculative

Known unknowns

Honesty section. These are documented in code comments and the changelog too:

  • An S7882 emits 10, 13, 19, 28 on the motion characteristic 0x0305 — values the official app cannot decode either. The motion_raw sensor and the three per-session motion sensors exist to chart this.
  • The sanitizing-case field sizes, the 0x0315 record packing and the 0x0329 time encoding are flagged assumptions — decoded to entities or logs with the raw bytes available for verification.
  • The 0x0600 BLE Serial Service is named by the APK but its payload format is undocumented: out of scope until someone captures it.

Logs from other models — especially explore lines and Resolved N/70 counts — are the most useful contribution.

Credits

Protocol knowledge assembled from the decompiled official Philips APK, the upstream community integration this component mirrors where it is right (and diverges from where it is not — see docs/PROTOCOL.md §10), live BLE captures on an S7882, and Philips patent US11154997B2 for the motion-histogram model.

About

No description or website provided.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages