Skip to content

v6.0.0 - #355

Merged
donavanbecker merged 4 commits into
latestfrom
beta-6.0.0
Oct 30, 2026
Merged

v6.0.0#355
donavanbecker merged 4 commits into
latestfrom
beta-6.0.0

Conversation

@donavanbecker

Copy link
Copy Markdown
Contributor

node-switchbot 6.0.0

What's new

Breaking changes

  • SwitchBotBLEModel values now use pySwitchbot's primary advertisement codes:
    • Meter Pro 4
    • Lock Pro $
    • Keypad y
    • Strip Light r
    • Hub 2 v
    • Air Purifier *
    • Air Purifier Table 7
    • Remote b
    • Relay Switch 1 ;
    • Relay Switch 1PM <
    • Water Leak Detector &
    • Humidifier 2 #
    • Battery Circulator Fan ~
    • K10+ Pro Combo / K10 Pro 3
    • K10+ }
  • Removed enum members: KeypadTouch, RobotVacuumCleanerS1, RobotVacuumCleanerS1Plus and PresenceSensor.

Migration (homebridge-switchbot)

  • Code that compares SwitchBotBLEModel values must use the new codes. homebridge-switchbot doesn't switch on these values, so it needs no change there.
  • Pass keys with setKey. homebridge-switchbot does this as of #1432, and #1433 moves it to 6.0.0-beta.

Tested as 6.0.0-beta.0 / beta.1 on npm. 435 tests pass. Build and lint pass.


Generated by Claude Code

donavanbecker and others added 4 commits October 24, 2026 10:04
… model codes (#347)

BLE transport changes to match
[pySwitchbot](https://github.com/sblibs/pySwitchbot), shipped first as
**6.0.0 betas** (`npm i node-switchbot@beta`) so they can be tested on
real devices before a stable release. Each change has tests; I couldn't
test against hardware.

## 1. Replies come from notifications — `fix(ble)`
SwitchBot devices answer a command with a **notification**, which
pySwitchbot waits for. node-switchbot read the notify characteristic
after writing, which can return a stale value. Now:
- The wait is registered before writing, so a fast reply isn't missed.
The existing `expectNotification` path wrote first, which was a race.
- One listener per notify characteristic: a notification answers the
pending command, otherwise it goes to `subscribeNotifications()`
handlers.
- A pending reply fails on disconnect; the timeout is pySwitchbot's 5 s.
- Backends without notification events still fall back to a read.

## 2. pySwitchbot's encryption protocol — `feat(ble)!`
node-switchbot's encryption didn't match the devices: it encrypted the
whole command, prepended `0x11`, and used an IV from config.
`BLEConnection.sendEncryptedCommand` now implements
`SwitchbotEncryptedDevice`:
- **Session setup per connection:** `57 000000 0F2103 <keyId>` returns
the AES mode (CTR or GCM) and the device's IV.
- **Commands:** sent as `57 <keyId> <2-byte header> <AES(rest)>`.
- **Replies:** decrypted to status plus payload. GCM increments the IV
per command, and the session resets on disconnect.

Also:
- Added **`setKey({ encryptionKey, keyId })`**. homebridge-switchbot
already calls it, but it didn't exist, so **encrypted devices never
received their key**.
- Bulbs, relay switches and air purifiers no longer pre-encrypt when a
key id is set.
- The air purifier **filter reset now works on encrypted devices**,
verified from the decrypted reply.

Tests use a simulated device implementing the protocol in both CTR and
GCM, plus pySwitchbot's init and mode cases.

## 3. `SwitchBotBLEModel` values — `feat!`
Thirteen values were codes belonging to other products. Some examples:
- `MeterPro` `'o'` was the Lock's code.
- `WaterLeakDetector` `'y'` was the Keypad's.
- `Remote` `0x05` was HubMini Matter's.
- `RelaySwitch1PM` `0x0D` was Lock Lite's.

Every value is now the product's primary pySwitchbot code. New members
include `RelaySwitch2PM`, `GarageDoorOpener`, `LockLite`, `RollerShade`
and `PlugMiniEU`.

## Breaking changes / migration
- **Encrypted devices need `keyId` alongside `encryptionKey`** (both
from the SwitchBot account, as for pySwitchbot). Without a key id the
old scheme is used, which didn't work with real devices anyway.
- **`SwitchBotBLEModel` values changed.** `KeypadTouch`,
`RobotVacuumCleanerS1`, `RobotVacuumCleanerS1Plus` and `PresenceSensor`
are removed (no single-byte code; their values named other products).
homebridge-switchbot doesn't use this enum.

## Checks
Build, 429 tests (+27) and lint pass. Labelled `major`. Merging into
`beta-6.0.0` publishes `6.0.0-beta.x`; promoting `beta-6.0.0` to
`latest` releases 6.0.0.

https://claude.ai/code/session_01TYWa2RXoWsaUTWh76oNv8s

---
_Generated by [Claude Code](https://claude.ai/code)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
BLEConnection logged "Unexpected disconnect" for every disconnect it started
itself (the idle timer after each command, disconnectAll, a failed
connection), because nothing marked them as expected. Meanwhile a disconnect
started by the device (out of range, powered off) was never noticed: the dead
peripheral stayed in the connection map and the next command reused it.

- disconnect() is always intentional: it logs at debug level.
- A 'disconnect' listener on the peripheral drops the connection state
  (characteristics, encryption, pending reply) and warns only when the device
  dropped the link; markExpectedDisconnect() silences that warning.
- The characteristic-discovery failure path uses disconnect().

Supersedes #348.

Co-authored-by: dnicolson <dnicolson@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TYWa2RXoWsaUTWh76oNv8s
Brings in the BLE logging changes from #351, #352 and #353. The one conflict was the disconnect fix that landed on both branches (#349, #350); the beta-6.0.0 version also clears the encrypted session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TYWa2RXoWsaUTWh76oNv8s
Merges `latest` (5.2.1 to 5.2.4) into `beta-6.0.0` before the 6.0.0
release, so 6.0.0 keeps dnicolson's BLE logging changes (#351, #352,
#353).

There was one conflict: the disconnect fix landed on both branches (#349
to `latest`, #350 to `beta-6.0.0`). I kept the `beta-6.0.0` version,
which also clears the encrypted session on disconnect.

I checked that no log line from `latest` was dropped. 435 tests pass.
Build and lint pass.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01TYWa2RXoWsaUTWh76oNv8s)_
@donavanbecker donavanbecker added the major major version change label Oct 30, 2026 — with Claude
@donavanbecker
donavanbecker merged commit e46ba26 into latest Oct 30, 2026
16 checks passed
@donavanbecker
donavanbecker deleted the beta-6.0.0 branch October 30, 2026 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

major major version change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants