homebridge-bedjet 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,27 +5,24 @@ import type { BedJetConfig, BedJetState } from './types';
5
5
  export declare class BedJet extends EventEmitter {
6
6
  private readonly config;
7
7
  private readonly log;
8
- private peripheral;
9
- private commandChar;
10
- private statusChar;
11
- private disconnectTimer;
12
- private staleTimer;
13
- private reconnectAttempts;
14
- private connecting;
15
8
  private _state;
16
9
  private deviceName;
17
10
  private firmwareVersion;
11
+ private reconnectAttempts;
12
+ private connecting;
13
+ private destroyed;
14
+ private bleDestroy;
15
+ private commandChar;
16
+ private disconnectTimer;
17
+ private staleTimer;
18
18
  constructor(config: BedJetConfig, log: Logger);
19
19
  get state(): BedJetState;
20
20
  get name(): string;
21
21
  get firmware(): string | null;
22
22
  connect(): Promise<void>;
23
- private _scan;
24
- private _connectPeripheral;
23
+ private _doConnect;
25
24
  private _handleNotification;
26
25
  private _handleStatusRead;
27
- private _readDeviceName;
28
- private _readDeviceStatus;
29
26
  private _sendCommand;
30
27
  setTemperature(celsius: number): Promise<void>;
31
28
  setFanSpeed(percent: number): Promise<void>;
@@ -38,5 +35,6 @@ export declare class BedJet extends EventEmitter {
38
35
  private _resetDisconnectTimer;
39
36
  private _resetStaleTimer;
40
37
  private _onDisconnected;
38
+ private _scheduleReconnect;
41
39
  disconnect(): Promise<void>;
42
40
  }
@@ -2,159 +2,112 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BedJet = void 0;
4
4
  const events_1 = require("events");
5
- // noble is a singleton process-level instance; require() gives us the live object.
6
- // The default ESM export doesn't expose `.state` in its type — we patch it here.
7
- // eslint-disable-next-line @typescript-eslint/no-require-imports
8
- const noble = require('@abandonware/noble');
5
+ const NodeBle = require("node-ble");
6
+ const { createBluetooth } = NodeBle;
9
7
  const constants_1 = require("./constants");
10
8
  const types_1 = require("./types");
11
- // noble strips hyphens from UUIDs internally
12
- const normalize = (uuid) => uuid.replace(/-/g, '').toLowerCase();
13
- const NORM_STATUS_UUID = normalize(constants_1.BEDJET3_STATUS_UUID);
14
- const NORM_NAME_UUID = normalize(constants_1.BEDJET3_NAME_UUID);
15
- const NORM_COMMAND_UUID = normalize(constants_1.BEDJET3_COMMAND_UUID);
16
- const NORM_SERVICE_UUID = normalize(constants_1.BEDJET3_SERVICE_UUID);
9
+ // node-ble uses BlueZ D-Bus works alongside BlueZ without raw HCI conflicts.
10
+ // This mirrors how Python bleak / Home Assistant ha-bedjet connects.
17
11
  class BedJet extends events_1.EventEmitter {
18
12
  constructor(config, log) {
19
13
  super();
20
14
  this.config = config;
21
15
  this.log = log;
22
- this.peripheral = null;
23
- this.commandChar = null;
24
- this.statusChar = null;
25
- this.disconnectTimer = null;
26
- this.staleTimer = null;
27
- this.reconnectAttempts = 0;
28
- this.connecting = false;
29
16
  this._state = { ...types_1.DEFAULT_STATE };
30
17
  this.deviceName = null;
31
18
  this.firmwareVersion = null;
19
+ this.reconnectAttempts = 0;
20
+ this.connecting = false;
21
+ this.destroyed = false;
22
+ this.bleDestroy = null;
23
+ this.commandChar = null;
24
+ this.disconnectTimer = null;
25
+ this.staleTimer = null;
32
26
  }
33
- get state() {
34
- return this._state;
35
- }
36
- get name() {
37
- return this.deviceName ?? this.config.name;
38
- }
39
- get firmware() {
40
- return this.firmwareVersion;
41
- }
42
- // ── Connection ────────────────────────────────────────────────────────────
27
+ get state() { return this._state; }
28
+ get name() { return this.deviceName ?? this.config.name; }
29
+ get firmware() { return this.firmwareVersion; }
30
+ // ── Connection ─────────────────────────────────────────────────────────────
43
31
  async connect() {
44
- if (this.connecting) {
32
+ if (this.connecting || this.destroyed) {
45
33
  return;
46
34
  }
47
35
  this.connecting = true;
48
36
  try {
49
- await this._scan();
37
+ await this._doConnect();
50
38
  }
51
39
  catch (err) {
40
+ this.log.error(`[${this.config.name}] Connect failed: ${err}`);
41
+ this._scheduleReconnect();
42
+ }
43
+ finally {
52
44
  this.connecting = false;
53
- throw err;
54
45
  }
55
- this.connecting = false;
56
46
  }
57
- _scan() {
58
- return new Promise((resolve, reject) => {
59
- // On macOS, noble exposes a CoreBluetooth UUID (e.g. BF5DEE8A-4B98-...)
60
- // rather than a real MAC. We match against both peripheral.uuid and
61
- // peripheral.address so the config works on Linux (MAC) and macOS (UUID).
62
- const targetNorm = this.config.address.toLowerCase().replace(/:/g, '').replace(/-/g, '');
63
- const timeoutMs = (this.config.scanTimeout ?? 30) * 1000;
64
- const scanTimeout = setTimeout(() => {
65
- noble.stopScanning();
66
- noble.removeListener('discover', onDiscover);
67
- reject(new Error(`[${this.config.name}] Scan timed out after ${this.config.scanTimeout ?? 30}s`));
68
- }, timeoutMs);
69
- const onDiscover = async (peripheral) => {
70
- const addrNorm = peripheral.address.toLowerCase().replace(/:/g, '').replace(/-/g, '');
71
- const uuidNorm = (peripheral.uuid ?? '').toLowerCase().replace(/-/g, '');
72
- if (addrNorm !== targetNorm && uuidNorm !== targetNorm) {
73
- return;
74
- }
75
- clearTimeout(scanTimeout);
76
- noble.stopScanning();
77
- noble.removeListener('discover', onDiscover);
78
- try {
79
- await this._connectPeripheral(peripheral);
80
- resolve();
81
- }
82
- catch (err) {
83
- reject(err);
84
- }
85
- };
86
- noble.on('discover', onDiscover);
87
- const startScan = () => {
88
- // Scan with no service UUID filter — BlueZ on Linux silently drops devices
89
- // when filtering by 128-bit custom UUIDs. We match by address in onDiscover instead.
90
- noble.startScanning([], false, (err) => {
91
- if (err) {
92
- clearTimeout(scanTimeout);
93
- noble.removeListener('discover', onDiscover);
94
- reject(new Error(`[${this.config.name}] startScanning failed: ${err}`));
95
- }
96
- });
97
- };
98
- if (noble.state === 'poweredOn') {
99
- startScan();
100
- }
101
- else {
102
- noble.once('stateChange', (state) => {
103
- if (state === 'poweredOn') {
104
- startScan();
105
- }
106
- else {
107
- clearTimeout(scanTimeout);
108
- noble.removeListener('discover', onDiscover);
109
- reject(new Error(`[${this.config.name}] Bluetooth not available (state: ${state})`));
110
- }
111
- });
47
+ async _doConnect() {
48
+ // Clean up any previous bluetooth instance
49
+ if (this.bleDestroy) {
50
+ try {
51
+ this.bleDestroy();
112
52
  }
53
+ catch { /* ignore */ }
54
+ this.bleDestroy = null;
55
+ }
56
+ const { bluetooth, destroy } = createBluetooth();
57
+ this.bleDestroy = destroy;
58
+ this.log.info(`[${this.config.name}] Getting Bluetooth adapter…`);
59
+ const adapter = await bluetooth.defaultAdapter();
60
+ if (!await adapter.isDiscovering()) {
61
+ await adapter.startDiscovery();
62
+ }
63
+ const timeoutMs = (this.config.scanTimeout ?? 30) * 1000;
64
+ this.log.info(`[${this.config.name}] Waiting for device ${this.config.address}…`);
65
+ const device = await adapter.waitDevice(this.config.address.toUpperCase(), timeoutMs);
66
+ await adapter.stopDiscovery().catch(() => { });
67
+ this.log.info(`[${this.config.name}] Connecting…`);
68
+ await device.connect();
69
+ this.log.info(`[${this.config.name}] Connected — getting GATT server…`);
70
+ device.on('disconnect', () => {
71
+ this.log.warn(`[${this.config.name}] Device disconnected`);
72
+ this._onDisconnected();
113
73
  });
114
- }
115
- async _connectPeripheral(peripheral) {
116
- this.peripheral = peripheral;
117
- this.log.info(`[${this.config.name}] Connecting to ${peripheral.address}…`);
118
- peripheral.once('disconnect', () => this._onDisconnected());
119
- await peripheral.connectAsync();
120
- this.log.info(`[${this.config.name}] Connecteddiscovering services…`);
121
- // Wrap service discovery in a timeout — it hangs indefinitely on Linux/BlueZ
122
- // if the adapter is busy or the device is in a bad state.
123
- const { characteristics } = await Promise.race([
124
- peripheral.discoverAllServicesAndCharacteristicsAsync(),
125
- new Promise((_, reject) => setTimeout(() => reject(new Error(`[${this.config.name}] Service discovery timed out`)), 10000)),
126
- ]);
127
- this.log.info(`[${this.config.name}] Found ${characteristics.length} characteristics`);
128
- for (const char of characteristics) {
129
- const uuid = normalize(char.uuid);
130
- this.log.debug(`[${this.config.name}] char: ${uuid}`);
131
- if (uuid === NORM_STATUS_UUID) {
132
- this.statusChar = char;
133
- this.log.info(`[${this.config.name}] Found status characteristic`);
134
- }
135
- else if (uuid === NORM_COMMAND_UUID) {
136
- this.commandChar = char;
137
- this.log.info(`[${this.config.name}] Found command characteristic`);
138
- }
74
+ const gatt = await device.gatt();
75
+ this.log.info(`[${this.config.name}] Got GATT server — getting BedJet service…`);
76
+ const service = await gatt.getPrimaryService(constants_1.BEDJET3_SERVICE_UUID);
77
+ this.log.info(`[${this.config.name}] Got service — getting characteristics…`);
78
+ const statusChar = await service.getCharacteristic(constants_1.BEDJET3_STATUS_UUID);
79
+ this.commandChar = await service.getCharacteristic(constants_1.BEDJET3_COMMAND_UUID);
80
+ this.log.info(`[${this.config.name}] Got characteristics subscribing to notifications…`);
81
+ await statusChar.startNotifications();
82
+ statusChar.on('valuechanged', (buf) => this._handleNotification(buf));
83
+ this.log.info(`[${this.config.name}] Subscribed reading initial state…`);
84
+ // Read the 11-byte status characteristic directly
85
+ try {
86
+ const statusData = await statusChar.readValue();
87
+ this._handleStatusRead(statusData);
88
+ }
89
+ catch (err) {
90
+ this.log.warn(`[${this.config.name}] Could not read device status: ${err}`);
139
91
  }
140
- if (!this.commandChar || !this.statusChar) {
141
- throw new Error(`[${this.config.name}] Required characteristics not found (status=${!!this.statusChar} command=${!!this.commandChar})`);
92
+ // Read device name
93
+ try {
94
+ const nameChar = await service.getCharacteristic(constants_1.BEDJET3_NAME_UUID);
95
+ const nameData = await nameChar.readValue();
96
+ this.deviceName = nameData.toString('utf8').replace(/\0/g, '').trim();
97
+ this.log.info(`[${this.config.name}] Device name: ${this.deviceName}`);
98
+ }
99
+ catch (err) {
100
+ this.log.warn(`[${this.config.name}] Could not read device name: ${err}`);
142
101
  }
143
- // Subscribe to notifications on the status characteristic
144
- await this.statusChar.subscribeAsync();
145
- this.statusChar.on('data', (data) => this._handleNotification(data));
146
- // Read extended status and device name
147
- await this._readDeviceStatus();
148
- await this._readDeviceName();
149
102
  this.reconnectAttempts = 0;
150
103
  this._state = { ...this._state, isConnected: true };
151
104
  this._resetDisconnectTimer();
152
105
  this._resetStaleTimer();
153
106
  this.emit('connected');
154
107
  this.emit('stateChange', this._state);
155
- this.log.info(`[${this.config.name}] Ready (firmware: ${this.firmwareVersion ?? 'unknown'})`);
108
+ this.log.info(`[${this.config.name}] Ready`);
156
109
  }
157
- // ── Packet parsing ────────────────────────────────────────────────────────
110
+ // ── Packet parsing ──────────────────────────────────────────────────────────
158
111
  _handleNotification(data) {
159
112
  if (data.length !== constants_1.BEDJET3_NOTIFICATION_LENGTH) {
160
113
  return;
@@ -168,13 +121,10 @@ class BedJet extends events_1.EventEmitter {
168
121
  targetTemperature: data[8] / 2,
169
122
  operatingMode: data[9],
170
123
  fanSpeed: (data[10] + 1) * 5,
171
- // data[11] = maximum_hours, data[12] = maximum_minutes (not stored in state)
172
124
  minimumTemperature: data[13] / 2,
173
125
  maximumTemperature: data[14] / 2,
174
- // bytes [15–16] = turbo_time uint16 big-endian
175
126
  turboTimeSeconds: (data[15] << 8) | data[16],
176
127
  ambientTemperature: data[17] / 2,
177
- // data[18] = shutdown_reason (not stored)
178
128
  isConnected: true,
179
129
  };
180
130
  this._resetStaleTimer();
@@ -188,7 +138,6 @@ class BedJet extends events_1.EventEmitter {
188
138
  this._state = {
189
139
  ...this._state,
190
140
  isDualZone: (data[2] & 0x02) !== 0,
191
- // flags bits MSB→LSB: [_, _, connTestPassed, ledEnabled, _, unitsSetup, _, beepsMuted]
192
141
  connTestPassed: (flags & 0x20) !== 0,
193
142
  ledEnabled: (flags & 0x10) !== 0,
194
143
  unitsSetup: (flags & 0x04) !== 0,
@@ -196,45 +145,15 @@ class BedJet extends events_1.EventEmitter {
196
145
  notificationCode: data[9],
197
146
  };
198
147
  }
199
- async _readDeviceName() {
200
- if (!this.peripheral) {
201
- return;
202
- }
203
- try {
204
- // Rediscover to get the name characteristic
205
- const { characteristics } = await this.peripheral.discoverSomeServicesAndCharacteristicsAsync([NORM_SERVICE_UUID], [NORM_NAME_UUID]);
206
- const nameChar = characteristics.find(c => normalize(c.uuid) === NORM_NAME_UUID);
207
- if (nameChar) {
208
- const data = await nameChar.readAsync();
209
- this.deviceName = data.toString('utf8').replace(/\0/g, '').trim();
210
- this.log.debug(`[${this.config.name}] Device name: ${this.deviceName}`);
211
- }
212
- }
213
- catch (err) {
214
- this.log.warn(`[${this.config.name}] Could not read device name: ${err}`);
215
- }
216
- }
217
- async _readDeviceStatus() {
218
- if (!this.statusChar) {
219
- return;
220
- }
221
- try {
222
- const data = await this.statusChar.readAsync();
223
- this._handleStatusRead(data);
224
- }
225
- catch (err) {
226
- this.log.warn(`[${this.config.name}] Could not read device status: ${err}`);
227
- }
228
- }
229
- // ── Commands ──────────────────────────────────────────────────────────────
148
+ // ── Commands ────────────────────────────────────────────────────────────────
230
149
  async _sendCommand(command, ...args) {
231
150
  if (!this.commandChar) {
232
151
  throw new Error(`[${this.config.name}] Not connected`);
233
152
  }
234
153
  const buf = Buffer.from([command, ...args]);
235
154
  try {
236
- // withoutResponse = false (write with response) required for V3
237
- await this.commandChar.writeAsync(buf, false);
155
+ // writeValueWithResponse = write with response, required for BedJet V3
156
+ await this.commandChar.writeValueWithResponse(buf);
238
157
  this._resetDisconnectTimer();
239
158
  }
240
159
  catch (err) {
@@ -267,7 +186,7 @@ class BedJet extends events_1.EventEmitter {
267
186
  async setMuted(muted) {
268
187
  await this.pressButton(muted ? constants_1.BedJetButton.MUTE : constants_1.BedJetButton.UNMUTE);
269
188
  }
270
- // ── Timers ────────────────────────────────────────────────────────────────
189
+ // ── Timers ──────────────────────────────────────────────────────────────────
271
190
  _resetDisconnectTimer() {
272
191
  if (this.disconnectTimer) {
273
192
  clearTimeout(this.disconnectTimer);
@@ -277,7 +196,6 @@ class BedJet extends events_1.EventEmitter {
277
196
  await this.disconnect();
278
197
  }, constants_1.DISCONNECT_DELAY_MS);
279
198
  }
280
- // Mark stale and update HomeKit if no notification arrives within 65s
281
199
  _resetStaleTimer() {
282
200
  if (this.staleTimer) {
283
201
  clearTimeout(this.staleTimer);
@@ -288,10 +206,9 @@ class BedJet extends events_1.EventEmitter {
288
206
  this.emit('stateChange', this._state);
289
207
  }, 65000);
290
208
  }
291
- // ── Disconnect / reconnect ────────────────────────────────────────────────
209
+ // ── Disconnect / reconnect ──────────────────────────────────────────────────
292
210
  _onDisconnected() {
293
211
  this.commandChar = null;
294
- this.statusChar = null;
295
212
  if (this.disconnectTimer) {
296
213
  clearTimeout(this.disconnectTimer);
297
214
  this.disconnectTimer = null;
@@ -303,22 +220,27 @@ class BedJet extends events_1.EventEmitter {
303
220
  this._state = { ...this._state, isConnected: false };
304
221
  this.emit('disconnected');
305
222
  this.emit('stateChange', this._state);
306
- if (this.reconnectAttempts < constants_1.MAX_RECONNECT_ATTEMPTS) {
307
- // Exponential backoff: 2s, 4s, 8s, 16s, 32s
308
- const delay = Math.pow(2, this.reconnectAttempts + 1) * 1000;
309
- this.reconnectAttempts++;
310
- this.log.info(`[${this.config.name}] Disconnected reconnecting in ${delay / 1000}s ` +
311
- `(attempt ${this.reconnectAttempts}/${constants_1.MAX_RECONNECT_ATTEMPTS})`);
312
- setTimeout(() => {
313
- this.connect().catch(err => this.log.error(`[${this.config.name}] Reconnect failed: ${err}`));
314
- }, delay);
315
- }
316
- else {
317
- this.log.error(`[${this.config.name}] Max reconnect attempts reached — giving up`);
318
- this.emit('maxReconnectReached');
223
+ this._scheduleReconnect();
224
+ }
225
+ _scheduleReconnect() {
226
+ if (this.destroyed || this.reconnectAttempts >= constants_1.MAX_RECONNECT_ATTEMPTS) {
227
+ if (!this.destroyed && this.reconnectAttempts >= constants_1.MAX_RECONNECT_ATTEMPTS) {
228
+ this.log.error(`[${this.config.name}] Max reconnect attempts reached`);
229
+ this.emit('maxReconnectReached');
230
+ }
231
+ return;
319
232
  }
233
+ const delay = Math.pow(2, this.reconnectAttempts + 1) * 1000;
234
+ this.reconnectAttempts++;
235
+ this.log.info(`[${this.config.name}] Reconnecting in ${delay / 1000}s ` +
236
+ `(attempt ${this.reconnectAttempts}/${constants_1.MAX_RECONNECT_ATTEMPTS})`);
237
+ setTimeout(() => {
238
+ this.connect().catch(err => this.log.error(`[${this.config.name}] Reconnect failed: ${err}`));
239
+ }, delay);
320
240
  }
321
241
  async disconnect() {
242
+ this.destroyed = true;
243
+ this.commandChar = null;
322
244
  if (this.disconnectTimer) {
323
245
  clearTimeout(this.disconnectTimer);
324
246
  this.disconnectTimer = null;
@@ -327,16 +249,12 @@ class BedJet extends events_1.EventEmitter {
327
249
  clearTimeout(this.staleTimer);
328
250
  this.staleTimer = null;
329
251
  }
330
- // Setting max attempts prevents the disconnect handler from triggering a reconnect
331
- this.reconnectAttempts = constants_1.MAX_RECONNECT_ATTEMPTS;
332
- if (this.peripheral) {
252
+ if (this.bleDestroy) {
333
253
  try {
334
- await this.peripheral.disconnectAsync();
335
- }
336
- catch {
337
- // ignore errors during intentional disconnect
254
+ this.bleDestroy();
338
255
  }
339
- this.peripheral = null;
256
+ catch { /* ignore */ }
257
+ this.bleDestroy = null;
340
258
  }
341
259
  }
342
260
  }
@@ -1 +1 @@
1
- {"version":3,"file":"BedJet.js","sourceRoot":"","sources":["../../src/bedjet/BedJet.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BedJet.js","sourceRoot":"","sources":["../../src/bedjet/BedJet.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-bedjet",
3
3
  "displayName": "BedJet",
4
- "version": "0.1.8",
4
+ "version": "0.2.0",
5
5
  "description": "Homebridge plugin for BedJet V3 via Bluetooth LE",
6
6
  "license": "MIT",
7
7
  "main": "dist/index.js",
@@ -16,7 +16,7 @@
16
16
  "node": ">=18.0.0"
17
17
  },
18
18
  "dependencies": {
19
- "@abandonware/noble": "^1.9.2-15"
19
+ "node-ble": "^1.10.1"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/node": "^20.0.0",
@@ -1,11 +1,7 @@
1
1
  import { EventEmitter } from 'events';
2
- import type { Peripheral, Characteristic } from '@abandonware/noble';
3
2
  import type { Logger } from 'homebridge';
4
-
5
- // noble is a singleton process-level instance; require() gives us the live object.
6
- // The default ESM export doesn't expose `.state` in its type — we patch it here.
7
- // eslint-disable-next-line @typescript-eslint/no-require-imports
8
- const noble = require('@abandonware/noble') as typeof import('@abandonware/noble') & { state: string };
3
+ import NodeBle = require('node-ble');
4
+ const { createBluetooth } = NodeBle;
9
5
  import {
10
6
  BEDJET3_SERVICE_UUID,
11
7
  BEDJET3_STATUS_UUID,
@@ -23,26 +19,23 @@ import {
23
19
  import type { BedJetConfig, BedJetState } from './types';
24
20
  import { DEFAULT_STATE } from './types';
25
21
 
26
- // noble strips hyphens from UUIDs internally
27
- const normalize = (uuid: string) => uuid.replace(/-/g, '').toLowerCase();
28
-
29
- const NORM_STATUS_UUID = normalize(BEDJET3_STATUS_UUID);
30
- const NORM_NAME_UUID = normalize(BEDJET3_NAME_UUID);
31
- const NORM_COMMAND_UUID = normalize(BEDJET3_COMMAND_UUID);
32
- const NORM_SERVICE_UUID = normalize(BEDJET3_SERVICE_UUID);
22
+ // node-ble uses BlueZ D-Bus works alongside BlueZ without raw HCI conflicts.
23
+ // This mirrors how Python bleak / Home Assistant ha-bedjet connects.
33
24
 
34
25
  export class BedJet extends EventEmitter {
35
- private peripheral: Peripheral | null = null;
36
- private commandChar: Characteristic | null = null;
37
- private statusChar: Characteristic | null = null;
38
- private disconnectTimer: NodeJS.Timeout | null = null;
39
- private staleTimer: NodeJS.Timeout | null = null;
40
- private reconnectAttempts = 0;
41
- private connecting = false;
42
26
  private _state: BedJetState = { ...DEFAULT_STATE };
43
27
  private deviceName: string | null = null;
44
28
  private firmwareVersion: string | null = null;
45
29
 
30
+ private reconnectAttempts = 0;
31
+ private connecting = false;
32
+ private destroyed = false;
33
+
34
+ private bleDestroy: (() => void) | null = null;
35
+ private commandChar: NodeBle.GattCharacteristic | null = null;
36
+ private disconnectTimer: NodeJS.Timeout | null = null;
37
+ private staleTimer: NodeJS.Timeout | null = null;
38
+
46
39
  constructor(
47
40
  private readonly config: BedJetConfig,
48
41
  private readonly log: Logger,
@@ -50,164 +43,94 @@ export class BedJet extends EventEmitter {
50
43
  super();
51
44
  }
52
45
 
53
- get state(): BedJetState {
54
- return this._state;
55
- }
46
+ get state(): BedJetState { return this._state; }
47
+ get name(): string { return this.deviceName ?? this.config.name; }
48
+ get firmware(): string | null { return this.firmwareVersion; }
56
49
 
57
- get name(): string {
58
- return this.deviceName ?? this.config.name;
59
- }
60
-
61
- get firmware(): string | null {
62
- return this.firmwareVersion;
63
- }
64
-
65
- // ── Connection ────────────────────────────────────────────────────────────
50
+ // ── Connection ─────────────────────────────────────────────────────────────
66
51
 
67
52
  async connect(): Promise<void> {
68
- if (this.connecting) {
53
+ if (this.connecting || this.destroyed) {
69
54
  return;
70
55
  }
71
56
  this.connecting = true;
72
-
73
57
  try {
74
- await this._scan();
58
+ await this._doConnect();
75
59
  } catch (err) {
60
+ this.log.error(`[${this.config.name}] Connect failed: ${err}`);
61
+ this._scheduleReconnect();
62
+ } finally {
76
63
  this.connecting = false;
77
- throw err;
78
64
  }
79
-
80
- this.connecting = false;
81
65
  }
82
66
 
83
- private _scan(): Promise<void> {
84
- return new Promise((resolve, reject) => {
85
- // On macOS, noble exposes a CoreBluetooth UUID (e.g. BF5DEE8A-4B98-...)
86
- // rather than a real MAC. We match against both peripheral.uuid and
87
- // peripheral.address so the config works on Linux (MAC) and macOS (UUID).
88
- const targetNorm = this.config.address.toLowerCase().replace(/:/g, '').replace(/-/g, '');
89
- const timeoutMs = (this.config.scanTimeout ?? 30) * 1000;
90
-
91
- const scanTimeout = setTimeout(() => {
92
- noble.stopScanning();
93
- noble.removeListener('discover', onDiscover);
94
- reject(new Error(`[${this.config.name}] Scan timed out after ${this.config.scanTimeout ?? 30}s`));
95
- }, timeoutMs);
96
-
97
- const onDiscover = async (peripheral: Peripheral) => {
98
- const addrNorm = peripheral.address.toLowerCase().replace(/:/g, '').replace(/-/g, '');
99
- const uuidNorm = (peripheral.uuid ?? '').toLowerCase().replace(/-/g, '');
100
- if (addrNorm !== targetNorm && uuidNorm !== targetNorm) {
101
- return;
102
- }
103
-
104
- clearTimeout(scanTimeout);
105
- noble.stopScanning();
106
- noble.removeListener('discover', onDiscover);
107
-
108
- try {
109
- await this._connectPeripheral(peripheral);
110
- resolve();
111
- } catch (err) {
112
- reject(err);
113
- }
114
- };
115
-
116
- noble.on('discover', onDiscover);
117
-
118
- const startScan = () => {
119
- // Scan with no service UUID filter — BlueZ on Linux silently drops devices
120
- // when filtering by 128-bit custom UUIDs. We match by address in onDiscover instead.
121
- noble.startScanning([], false, (err) => {
122
- if (err) {
123
- clearTimeout(scanTimeout);
124
- noble.removeListener('discover', onDiscover);
125
- reject(new Error(`[${this.config.name}] startScanning failed: ${err}`));
126
- }
127
- });
128
- };
129
-
130
- if (noble.state === 'poweredOn') {
131
- startScan();
132
- } else {
133
- noble.once('stateChange', (state) => {
134
- if (state === 'poweredOn') {
135
- startScan();
136
- } else {
137
- clearTimeout(scanTimeout);
138
- noble.removeListener('discover', onDiscover);
139
- reject(new Error(`[${this.config.name}] Bluetooth not available (state: ${state})`));
140
- }
141
- });
142
- }
143
- });
144
- }
67
+ private async _doConnect(): Promise<void> {
68
+ // Clean up any previous bluetooth instance
69
+ if (this.bleDestroy) {
70
+ try { this.bleDestroy(); } catch { /* ignore */ }
71
+ this.bleDestroy = null;
72
+ }
145
73
 
146
- private async _connectPeripheral(peripheral: Peripheral): Promise<void> {
147
- this.peripheral = peripheral;
148
- this.log.info(`[${this.config.name}] Connecting to ${peripheral.address}…`);
74
+ const { bluetooth, destroy } = createBluetooth();
75
+ this.bleDestroy = destroy;
149
76
 
150
- peripheral.once('disconnect', () => this._onDisconnected());
77
+ this.log.info(`[${this.config.name}] Getting Bluetooth adapter…`);
78
+ const adapter = await bluetooth.defaultAdapter();
151
79
 
152
- try {
153
- await peripheral.connectAsync();
154
- } catch (err) {
155
- throw new Error(`[${this.config.name}] connectAsync failed: ${err}`);
80
+ if (!await adapter.isDiscovering()) {
81
+ await adapter.startDiscovery();
156
82
  }
157
- this.log.info(`[${this.config.name}] Connected — discovering services…`);
158
83
 
159
- // Use async discovery works on Linux when the device is already bonded
160
- let characteristics: Characteristic[];
161
- try {
162
- const result = await peripheral.discoverAllServicesAndCharacteristicsAsync();
163
- characteristics = result.characteristics;
164
- } catch (err) {
165
- throw new Error(`[${this.config.name}] discoverAllServices failed: ${err}`);
166
- }
84
+ const timeoutMs = (this.config.scanTimeout ?? 30) * 1000;
85
+ this.log.info(`[${this.config.name}] Waiting for device ${this.config.address}…`);
167
86
 
168
- this.log.info(`[${this.config.name}] Found ${characteristics.length} characteristics`);
169
- for (const char of characteristics) {
170
- const uuid = normalize(char.uuid);
171
- this.log.info(`[${this.config.name}] char: ${uuid}`);
172
- if (uuid === NORM_STATUS_UUID) {
173
- this.statusChar = char;
174
- this.log.info(`[${this.config.name}] Found status characteristic`);
175
- } else if (uuid === NORM_COMMAND_UUID) {
176
- this.commandChar = char;
177
- this.log.info(`[${this.config.name}] Found command characteristic`);
178
- }
179
- }
87
+ const device = await adapter.waitDevice(
88
+ this.config.address.toUpperCase(),
89
+ timeoutMs,
90
+ );
180
91
 
181
- // Small delay to let noble/BlueZ internal events settle after discovery
182
- await new Promise<void>(resolve => setTimeout(resolve, 500));
92
+ await adapter.stopDiscovery().catch(() => { /* ignore if already stopped */ });
183
93
 
184
- this.log.info(`[${this.config.name}] Char check: status=${!!this.statusChar} command=${!!this.commandChar}`);
94
+ this.log.info(`[${this.config.name}] Connecting…`);
95
+ await device.connect();
96
+ this.log.info(`[${this.config.name}] Connected — getting GATT server…`);
185
97
 
186
- if (!this.commandChar || !this.statusChar) {
187
- throw new Error(`[${this.config.name}] Required characteristics not found`);
188
- }
98
+ device.on('disconnect', () => {
99
+ this.log.warn(`[${this.config.name}] Device disconnected`);
100
+ this._onDisconnected();
101
+ });
189
102
 
190
- // Subscribe to notifications on the status characteristic
191
- this.log.info(`[${this.config.name}] Subscribing to notifications…`);
192
- try {
193
- await this.statusChar.subscribeAsync();
194
- } catch (err) {
195
- throw new Error(`[${this.config.name}] subscribeAsync failed: ${err}`);
196
- }
197
- this.log.info(`[${this.config.name}] Subscribed — reading device status…`);
198
- this.statusChar.on('data', (data: Buffer) => this._handleNotification(data));
103
+ const gatt = await device.gatt();
104
+ this.log.info(`[${this.config.name}] Got GATT server — getting BedJet service…`);
105
+
106
+ const service = await gatt.getPrimaryService(BEDJET3_SERVICE_UUID);
107
+ this.log.info(`[${this.config.name}] Got service — getting characteristics…`);
199
108
 
200
- // Read extended status and device name
109
+ const statusChar = await service.getCharacteristic(BEDJET3_STATUS_UUID);
110
+ this.commandChar = await service.getCharacteristic(BEDJET3_COMMAND_UUID);
111
+
112
+ this.log.info(`[${this.config.name}] Got characteristics — subscribing to notifications…`);
113
+ await statusChar.startNotifications();
114
+ statusChar.on('valuechanged', (buf: Buffer) => this._handleNotification(buf));
115
+
116
+ this.log.info(`[${this.config.name}] Subscribed — reading initial state…`);
117
+
118
+ // Read the 11-byte status characteristic directly
201
119
  try {
202
- await this._readDeviceStatus();
120
+ const statusData = await statusChar.readValue();
121
+ this._handleStatusRead(statusData);
203
122
  } catch (err) {
204
- this.log.warn(`[${this.config.name}] _readDeviceStatus failed: ${err}`);
123
+ this.log.warn(`[${this.config.name}] Could not read device status: ${err}`);
205
124
  }
206
- this.log.info(`[${this.config.name}] Reading device name…`);
125
+
126
+ // Read device name
207
127
  try {
208
- await this._readDeviceName();
128
+ const nameChar = await service.getCharacteristic(BEDJET3_NAME_UUID);
129
+ const nameData = await nameChar.readValue();
130
+ this.deviceName = nameData.toString('utf8').replace(/\0/g, '').trim();
131
+ this.log.info(`[${this.config.name}] Device name: ${this.deviceName}`);
209
132
  } catch (err) {
210
- this.log.warn(`[${this.config.name}] _readDeviceName failed: ${err}`);
133
+ this.log.warn(`[${this.config.name}] Could not read device name: ${err}`);
211
134
  }
212
135
 
213
136
  this.reconnectAttempts = 0;
@@ -218,16 +141,15 @@ export class BedJet extends EventEmitter {
218
141
  this.emit('connected');
219
142
  this.emit('stateChange', this._state);
220
143
 
221
- this.log.info(`[${this.config.name}] Ready (firmware: ${this.firmwareVersion ?? 'unknown'})`);
144
+ this.log.info(`[${this.config.name}] Ready`);
222
145
  }
223
146
 
224
- // ── Packet parsing ────────────────────────────────────────────────────────
147
+ // ── Packet parsing ──────────────────────────────────────────────────────────
225
148
 
226
149
  private _handleNotification(data: Buffer): void {
227
150
  if (data.length !== BEDJET3_NOTIFICATION_LENGTH) {
228
151
  return;
229
152
  }
230
-
231
153
  this._state = {
232
154
  ...this._state,
233
155
  hoursRemaining: data[4],
@@ -237,16 +159,12 @@ export class BedJet extends EventEmitter {
237
159
  targetTemperature: data[8] / 2,
238
160
  operatingMode: data[9] as OperatingMode,
239
161
  fanSpeed: (data[10] + 1) * 5,
240
- // data[11] = maximum_hours, data[12] = maximum_minutes (not stored in state)
241
162
  minimumTemperature: data[13] / 2,
242
163
  maximumTemperature: data[14] / 2,
243
- // bytes [15–16] = turbo_time uint16 big-endian
244
164
  turboTimeSeconds: (data[15] << 8) | data[16],
245
165
  ambientTemperature: data[17] / 2,
246
- // data[18] = shutdown_reason (not stored)
247
- isConnected: true,
166
+ isConnected: true,
248
167
  };
249
-
250
168
  this._resetStaleTimer();
251
169
  this.emit('stateChange', this._state);
252
170
  }
@@ -255,54 +173,19 @@ export class BedJet extends EventEmitter {
255
173
  if (data.length !== BEDJET3_STATUS_LENGTH) {
256
174
  return;
257
175
  }
258
-
259
176
  const flags = data[7];
260
177
  this._state = {
261
178
  ...this._state,
262
- isDualZone: (data[2] & 0x02) !== 0,
263
- // flags bits MSB→LSB: [_, _, connTestPassed, ledEnabled, _, unitsSetup, _, beepsMuted]
264
- connTestPassed: (flags & 0x20) !== 0,
265
- ledEnabled: (flags & 0x10) !== 0,
266
- unitsSetup: (flags & 0x04) !== 0,
267
- beepsMuted: (flags & 0x01) !== 0,
179
+ isDualZone: (data[2] & 0x02) !== 0,
180
+ connTestPassed: (flags & 0x20) !== 0,
181
+ ledEnabled: (flags & 0x10) !== 0,
182
+ unitsSetup: (flags & 0x04) !== 0,
183
+ beepsMuted: (flags & 0x01) !== 0,
268
184
  notificationCode: data[9],
269
185
  };
270
186
  }
271
187
 
272
- private async _readDeviceName(): Promise<void> {
273
- if (!this.peripheral) {
274
- return;
275
- }
276
- try {
277
- // Rediscover to get the name characteristic
278
- const { characteristics } = await this.peripheral.discoverSomeServicesAndCharacteristicsAsync(
279
- [NORM_SERVICE_UUID],
280
- [NORM_NAME_UUID],
281
- );
282
- const nameChar = characteristics.find(c => normalize(c.uuid) === NORM_NAME_UUID);
283
- if (nameChar) {
284
- const data = await nameChar.readAsync();
285
- this.deviceName = data.toString('utf8').replace(/\0/g, '').trim();
286
- this.log.debug(`[${this.config.name}] Device name: ${this.deviceName}`);
287
- }
288
- } catch (err) {
289
- this.log.warn(`[${this.config.name}] Could not read device name: ${err}`);
290
- }
291
- }
292
-
293
- private async _readDeviceStatus(): Promise<void> {
294
- if (!this.statusChar) {
295
- return;
296
- }
297
- try {
298
- const data = await this.statusChar.readAsync();
299
- this._handleStatusRead(data);
300
- } catch (err) {
301
- this.log.warn(`[${this.config.name}] Could not read device status: ${err}`);
302
- }
303
- }
304
-
305
- // ── Commands ──────────────────────────────────────────────────────────────
188
+ // ── Commands ────────────────────────────────────────────────────────────────
306
189
 
307
190
  private async _sendCommand(command: BedJetCommand, ...args: number[]): Promise<void> {
308
191
  if (!this.commandChar) {
@@ -310,8 +193,8 @@ export class BedJet extends EventEmitter {
310
193
  }
311
194
  const buf = Buffer.from([command, ...args]);
312
195
  try {
313
- // withoutResponse = false (write with response) required for V3
314
- await this.commandChar.writeAsync(buf, false);
196
+ // writeValueWithResponse = write with response, required for BedJet V3
197
+ await this.commandChar.writeValueWithResponse(buf);
315
198
  this._resetDisconnectTimer();
316
199
  } catch (err) {
317
200
  this.log.error(`[${this.config.name}] Command 0x${command.toString(16)} failed: ${err}`);
@@ -352,7 +235,7 @@ export class BedJet extends EventEmitter {
352
235
  await this.pressButton(muted ? BedJetButton.MUTE : BedJetButton.UNMUTE);
353
236
  }
354
237
 
355
- // ── Timers ────────────────────────────────────────────────────────────────
238
+ // ── Timers ──────────────────────────────────────────────────────────────────
356
239
 
357
240
  private _resetDisconnectTimer(): void {
358
241
  if (this.disconnectTimer) {
@@ -364,7 +247,6 @@ export class BedJet extends EventEmitter {
364
247
  }, DISCONNECT_DELAY_MS);
365
248
  }
366
249
 
367
- // Mark stale and update HomeKit if no notification arrives within 65s
368
250
  private _resetStaleTimer(): void {
369
251
  if (this.staleTimer) {
370
252
  clearTimeout(this.staleTimer);
@@ -376,11 +258,10 @@ export class BedJet extends EventEmitter {
376
258
  }, 65_000);
377
259
  }
378
260
 
379
- // ── Disconnect / reconnect ────────────────────────────────────────────────
261
+ // ── Disconnect / reconnect ──────────────────────────────────────────────────
380
262
 
381
263
  private _onDisconnected(): void {
382
264
  this.commandChar = null;
383
- this.statusChar = null;
384
265
 
385
266
  if (this.disconnectTimer) {
386
267
  clearTimeout(this.disconnectTimer);
@@ -395,26 +276,34 @@ export class BedJet extends EventEmitter {
395
276
  this.emit('disconnected');
396
277
  this.emit('stateChange', this._state);
397
278
 
398
- if (this.reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
399
- // Exponential backoff: 2s, 4s, 8s, 16s, 32s
400
- const delay = Math.pow(2, this.reconnectAttempts + 1) * 1000;
401
- this.reconnectAttempts++;
402
- this.log.info(
403
- `[${this.config.name}] Disconnected reconnecting in ${delay / 1000}s ` +
404
- `(attempt ${this.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`,
405
- );
406
- setTimeout(() => {
407
- this.connect().catch(err =>
408
- this.log.error(`[${this.config.name}] Reconnect failed: ${err}`),
409
- );
410
- }, delay);
411
- } else {
412
- this.log.error(`[${this.config.name}] Max reconnect attempts reached — giving up`);
413
- this.emit('maxReconnectReached');
279
+ this._scheduleReconnect();
280
+ }
281
+
282
+ private _scheduleReconnect(): void {
283
+ if (this.destroyed || this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
284
+ if (!this.destroyed && this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
285
+ this.log.error(`[${this.config.name}] Max reconnect attempts reached`);
286
+ this.emit('maxReconnectReached');
287
+ }
288
+ return;
414
289
  }
290
+ const delay = Math.pow(2, this.reconnectAttempts + 1) * 1000;
291
+ this.reconnectAttempts++;
292
+ this.log.info(
293
+ `[${this.config.name}] Reconnecting in ${delay / 1000}s ` +
294
+ `(attempt ${this.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`,
295
+ );
296
+ setTimeout(() => {
297
+ this.connect().catch(err =>
298
+ this.log.error(`[${this.config.name}] Reconnect failed: ${err}`),
299
+ );
300
+ }, delay);
415
301
  }
416
302
 
417
303
  async disconnect(): Promise<void> {
304
+ this.destroyed = true;
305
+ this.commandChar = null;
306
+
418
307
  if (this.disconnectTimer) {
419
308
  clearTimeout(this.disconnectTimer);
420
309
  this.disconnectTimer = null;
@@ -423,15 +312,9 @@ export class BedJet extends EventEmitter {
423
312
  clearTimeout(this.staleTimer);
424
313
  this.staleTimer = null;
425
314
  }
426
- // Setting max attempts prevents the disconnect handler from triggering a reconnect
427
- this.reconnectAttempts = MAX_RECONNECT_ATTEMPTS;
428
- if (this.peripheral) {
429
- try {
430
- await this.peripheral.disconnectAsync();
431
- } catch {
432
- // ignore errors during intentional disconnect
433
- }
434
- this.peripheral = null;
315
+ if (this.bleDestroy) {
316
+ try { this.bleDestroy(); } catch { /* ignore */ }
317
+ this.bleDestroy = null;
435
318
  }
436
319
  }
437
320
  }