homebridge-bedjet 0.1.7 → 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":";;;AAAA,mCAAsC;AAEtC,oCAAqC;AACrC,MAAM,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC;AACpC,2CAaqB;AAErB,mCAAwC;AAExC,+EAA+E;AAC/E,qEAAqE;AAErE,MAAa,MAAO,SAAQ,qBAAY;IActC,YACmB,MAAoB,EACpB,GAAW;QAE5B,KAAK,EAAE,CAAC;QAHS,WAAM,GAAN,MAAM,CAAc;QACpB,QAAG,GAAH,GAAG,CAAQ;QAftB,WAAM,GAAgB,EAAE,GAAG,qBAAa,EAAE,CAAC;QAC3C,eAAU,GAAkB,IAAI,CAAC;QACjC,oBAAe,GAAkB,IAAI,CAAC;QAEtC,sBAAiB,GAAG,CAAC,CAAC;QACtB,eAAU,GAAG,KAAK,CAAC;QACnB,cAAS,GAAG,KAAK,CAAC;QAElB,eAAU,GAAwB,IAAI,CAAC;QACvC,gBAAW,GAAsC,IAAI,CAAC;QACtD,oBAAe,GAA0B,IAAI,CAAC;QAC9C,eAAU,GAA0B,IAAI,CAAC;IAOjD,CAAC;IAED,IAAI,KAAK,KAAkB,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAChD,IAAI,IAAI,KAAa,OAAO,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAClE,IAAI,QAAQ,KAAoB,OAAO,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC;IAE9D,8EAA8E;IAE9E,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACtC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QAC1B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,qBAAqB,GAAG,EAAE,CAAC,CAAC;YAC/D,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC5B,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QAC1B,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,UAAU;QACtB,2CAA2C;QAC3C,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,IAAI,CAAC;gBAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;YACjD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;QAED,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,eAAe,EAAE,CAAC;QACjD,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC;QAE1B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,8BAA8B,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,cAAc,EAAE,CAAC;QAEjD,IAAI,CAAC,MAAM,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;YACnC,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC;QACjC,CAAC;QAED,MAAM,SAAS,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;QACzD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,wBAAwB,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;QAElF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,UAAU,CACrC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,EACjC,SAAS,CACV,CAAC;QAEF,MAAM,OAAO,CAAC,aAAa,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAmC,CAAC,CAAC,CAAC;QAE/E,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,eAAe,CAAC,CAAC;QACnD,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QACvB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,oCAAoC,CAAC,CAAC;QAExE,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,GAAG,EAAE;YAC3B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,uBAAuB,CAAC,CAAC;YAC3D,IAAI,CAAC,eAAe,EAAE,CAAC;QACzB,CAAC,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACjC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,6CAA6C,CAAC,CAAC;QAEjF,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,gCAAoB,CAAC,CAAC;QACnE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,0CAA0C,CAAC,CAAC;QAE9E,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,iBAAiB,CAAC,+BAAmB,CAAC,CAAC;QACxE,IAAI,CAAC,WAAW,GAAI,MAAM,OAAO,CAAC,iBAAiB,CAAC,gCAAoB,CAAC,CAAC;QAE1E,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,uDAAuD,CAAC,CAAC;QAC3F,MAAM,UAAU,CAAC,kBAAkB,EAAE,CAAC;QACtC,UAAU,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC;QAE9E,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,uCAAuC,CAAC,CAAC;QAE3E,kDAAkD;QAClD,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,UAAU,CAAC,SAAS,EAAE,CAAC;YAChD,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,mCAAmC,GAAG,EAAE,CAAC,CAAC;QAC9E,CAAC;QAED,mBAAmB;QACnB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,iBAAiB,CAAC,6BAAiB,CAAC,CAAC;YACpE,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,CAAC;YAC5C,IAAI,CAAC,UAAU,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YACtE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,kBAAkB,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;QACzE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,iCAAiC,GAAG,EAAE,CAAC,CAAC;QAC5E,CAAC;QAED,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;QACpD,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC7B,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAExB,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACvB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAEtC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,CAAC;IAC/C,CAAC;IAED,+EAA+E;IAEvE,mBAAmB,CAAC,IAAY;QACtC,IAAI,IAAI,CAAC,MAAM,KAAK,uCAA2B,EAAE,CAAC;YAChD,OAAO;QACT,CAAC;QACD,IAAI,CAAC,MAAM,GAAG;YACZ,GAAG,IAAI,CAAC,MAAM;YACd,cAAc,EAAM,IAAI,CAAC,CAAC,CAAC;YAC3B,gBAAgB,EAAI,IAAI,CAAC,CAAC,CAAC;YAC3B,gBAAgB,EAAI,IAAI,CAAC,CAAC,CAAC;YAC3B,kBAAkB,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;YAC/B,iBAAiB,EAAG,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;YAC/B,aAAa,EAAO,IAAI,CAAC,CAAC,CAAkB;YAC5C,QAAQ,EAAY,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC;YACtC,kBAAkB,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC;YAChC,kBAAkB,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC;YAChC,gBAAgB,EAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;YAC9C,kBAAkB,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC;YAChC,WAAW,EAAS,IAAI;SACzB,CAAC;QACF,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IAEO,iBAAiB,CAAC,IAAY;QACpC,IAAI,IAAI,CAAC,MAAM,KAAK,iCAAqB,EAAE,CAAC;YAC1C,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG;YACZ,GAAG,IAAI,CAAC,MAAM;YACd,UAAU,EAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;YACxC,cAAc,EAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YACtC,UAAU,EAAQ,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YACtC,UAAU,EAAQ,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YACtC,UAAU,EAAQ,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YACtC,gBAAgB,EAAE,IAAI,CAAC,CAAC,CAAC;SAC1B,CAAC;IACJ,CAAC;IAED,+EAA+E;IAEvE,KAAK,CAAC,YAAY,CAAC,OAAsB,EAAE,GAAG,IAAc;QAClE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,iBAAiB,CAAC,CAAC;QACzD,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;QAC5C,IAAI,CAAC;YACH,uEAAuE;YACvE,MAAM,IAAI,CAAC,WAAW,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC;YACnD,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC/B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,eAAe,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,YAAY,GAAG,EAAE,CAAC,CAAC;YACzF,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,OAAe;QAClC,MAAM,IAAI,CAAC,YAAY,CAAC,yBAAa,CAAC,eAAe,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;IAClF,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAAe;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACpE,MAAM,IAAI,CAAC,YAAY,CAAC,yBAAa,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,IAAmB;QACxC,MAAM,IAAI,CAAC,YAAY,CAAC,yBAAa,CAAC,MAAM,EAAE,qCAAyB,CAAC,IAAI,CAAC,CAAC,CAAC;IACjF,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,MAAoB;QACpC,MAAM,IAAI,CAAC,YAAY,CAAC,yBAAa,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAY,EAAE,MAAc;QACzC,MAAM,IAAI,CAAC,YAAY,CAAC,yBAAa,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,KAAa,EAAE,OAAe;QACtD,MAAM,IAAI,CAAC,YAAY,CAAC,yBAAa,CAAC,WAAW,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAW;QACtB,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,wBAAY,CAAC,MAAM,CAAC,CAAC,CAAC,wBAAY,CAAC,OAAO,CAAC,CAAC;IAC1E,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,KAAc;QAC3B,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,wBAAY,CAAC,IAAI,CAAC,CAAC,CAAC,wBAAY,CAAC,MAAM,CAAC,CAAC;IAC1E,CAAC;IAED,+EAA+E;IAEvE,qBAAqB;QAC3B,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,YAAY,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACrC,CAAC;QACD,IAAI,CAAC,eAAe,GAAG,UAAU,CAAC,KAAK,IAAI,EAAE;YAC3C,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,sCAAsC,CAAC,CAAC;YAC3E,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QAC1B,CAAC,EAAE,+BAAmB,CAAC,CAAC;IAC1B,CAAC;IAEO,gBAAgB;QACtB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAChC,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE;YAChC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,mDAAmD,CAAC,CAAC;YACvF,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;YACrD,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACxC,CAAC,EAAE,KAAM,CAAC,CAAC;IACb,CAAC;IAED,+EAA+E;IAEvE,eAAe;QACrB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAExB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,YAAY,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YACnC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC9B,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC9B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;QACrD,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC1B,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAEtC,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAC5B,CAAC;IAEO,kBAAkB;QACxB,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,iBAAiB,IAAI,kCAAsB,EAAE,CAAC;YACvE,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,iBAAiB,IAAI,kCAAsB,EAAE,CAAC;gBACxE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,kCAAkC,CAAC,CAAC;gBACvE,IAAI,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;YACnC,CAAC;YACD,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC;QAC7D,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,IAAI,CAAC,GAAG,CAAC,IAAI,CACX,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,qBAAqB,KAAK,GAAG,IAAI,IAAI;YACzD,YAAY,IAAI,CAAC,iBAAiB,IAAI,kCAAsB,GAAG,CAChE,CAAC;QACF,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CACzB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,uBAAuB,GAAG,EAAE,CAAC,CACjE,CAAC;QACJ,CAAC,EAAE,KAAK,CAAC,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,UAAU;QACd,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAExB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,YAAY,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YACnC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC9B,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC9B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,IAAI,CAAC;gBAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;YACjD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;IACH,CAAC;CACF;AAvSD,wBAuSC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-bedjet",
3
3
  "displayName": "BedJet",
4
- "version": "0.1.7",
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,161 +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
- this.log.info(`[${this.config.name}] Char check: status=${!!this.statusChar} command=${!!this.commandChar}`);
92
+ await adapter.stopDiscovery().catch(() => { /* ignore if already stopped */ });
182
93
 
183
- if (!this.commandChar || !this.statusChar) {
184
- throw new Error(`[${this.config.name}] Required characteristics not found`);
185
- }
94
+ this.log.info(`[${this.config.name}] Connecting…`);
95
+ await device.connect();
96
+ this.log.info(`[${this.config.name}] Connected — getting GATT server…`);
186
97
 
187
- // Subscribe to notifications on the status characteristic
188
- this.log.info(`[${this.config.name}] Subscribing to notifications…`);
189
- try {
190
- await this.statusChar.subscribeAsync();
191
- } catch (err) {
192
- throw new Error(`[${this.config.name}] subscribeAsync failed: ${err}`);
193
- }
194
- this.log.info(`[${this.config.name}] Subscribed — reading device status…`);
195
- this.statusChar.on('data', (data: Buffer) => this._handleNotification(data));
98
+ device.on('disconnect', () => {
99
+ this.log.warn(`[${this.config.name}] Device disconnected`);
100
+ this._onDisconnected();
101
+ });
102
+
103
+ const gatt = await device.gatt();
104
+ this.log.info(`[${this.config.name}] Got GATT server — getting BedJet service…`);
196
105
 
197
- // Read extended status and device name
106
+ const service = await gatt.getPrimaryService(BEDJET3_SERVICE_UUID);
107
+ this.log.info(`[${this.config.name}] Got service — getting characteristics…`);
108
+
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
198
119
  try {
199
- await this._readDeviceStatus();
120
+ const statusData = await statusChar.readValue();
121
+ this._handleStatusRead(statusData);
200
122
  } catch (err) {
201
- this.log.warn(`[${this.config.name}] _readDeviceStatus failed: ${err}`);
123
+ this.log.warn(`[${this.config.name}] Could not read device status: ${err}`);
202
124
  }
203
- this.log.info(`[${this.config.name}] Reading device name…`);
125
+
126
+ // Read device name
204
127
  try {
205
- 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}`);
206
132
  } catch (err) {
207
- this.log.warn(`[${this.config.name}] _readDeviceName failed: ${err}`);
133
+ this.log.warn(`[${this.config.name}] Could not read device name: ${err}`);
208
134
  }
209
135
 
210
136
  this.reconnectAttempts = 0;
@@ -215,16 +141,15 @@ export class BedJet extends EventEmitter {
215
141
  this.emit('connected');
216
142
  this.emit('stateChange', this._state);
217
143
 
218
- this.log.info(`[${this.config.name}] Ready (firmware: ${this.firmwareVersion ?? 'unknown'})`);
144
+ this.log.info(`[${this.config.name}] Ready`);
219
145
  }
220
146
 
221
- // ── Packet parsing ────────────────────────────────────────────────────────
147
+ // ── Packet parsing ──────────────────────────────────────────────────────────
222
148
 
223
149
  private _handleNotification(data: Buffer): void {
224
150
  if (data.length !== BEDJET3_NOTIFICATION_LENGTH) {
225
151
  return;
226
152
  }
227
-
228
153
  this._state = {
229
154
  ...this._state,
230
155
  hoursRemaining: data[4],
@@ -234,16 +159,12 @@ export class BedJet extends EventEmitter {
234
159
  targetTemperature: data[8] / 2,
235
160
  operatingMode: data[9] as OperatingMode,
236
161
  fanSpeed: (data[10] + 1) * 5,
237
- // data[11] = maximum_hours, data[12] = maximum_minutes (not stored in state)
238
162
  minimumTemperature: data[13] / 2,
239
163
  maximumTemperature: data[14] / 2,
240
- // bytes [15–16] = turbo_time uint16 big-endian
241
164
  turboTimeSeconds: (data[15] << 8) | data[16],
242
165
  ambientTemperature: data[17] / 2,
243
- // data[18] = shutdown_reason (not stored)
244
- isConnected: true,
166
+ isConnected: true,
245
167
  };
246
-
247
168
  this._resetStaleTimer();
248
169
  this.emit('stateChange', this._state);
249
170
  }
@@ -252,54 +173,19 @@ export class BedJet extends EventEmitter {
252
173
  if (data.length !== BEDJET3_STATUS_LENGTH) {
253
174
  return;
254
175
  }
255
-
256
176
  const flags = data[7];
257
177
  this._state = {
258
178
  ...this._state,
259
- isDualZone: (data[2] & 0x02) !== 0,
260
- // flags bits MSB→LSB: [_, _, connTestPassed, ledEnabled, _, unitsSetup, _, beepsMuted]
261
- connTestPassed: (flags & 0x20) !== 0,
262
- ledEnabled: (flags & 0x10) !== 0,
263
- unitsSetup: (flags & 0x04) !== 0,
264
- 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,
265
184
  notificationCode: data[9],
266
185
  };
267
186
  }
268
187
 
269
- private async _readDeviceName(): Promise<void> {
270
- if (!this.peripheral) {
271
- return;
272
- }
273
- try {
274
- // Rediscover to get the name characteristic
275
- const { characteristics } = await this.peripheral.discoverSomeServicesAndCharacteristicsAsync(
276
- [NORM_SERVICE_UUID],
277
- [NORM_NAME_UUID],
278
- );
279
- const nameChar = characteristics.find(c => normalize(c.uuid) === NORM_NAME_UUID);
280
- if (nameChar) {
281
- const data = await nameChar.readAsync();
282
- this.deviceName = data.toString('utf8').replace(/\0/g, '').trim();
283
- this.log.debug(`[${this.config.name}] Device name: ${this.deviceName}`);
284
- }
285
- } catch (err) {
286
- this.log.warn(`[${this.config.name}] Could not read device name: ${err}`);
287
- }
288
- }
289
-
290
- private async _readDeviceStatus(): Promise<void> {
291
- if (!this.statusChar) {
292
- return;
293
- }
294
- try {
295
- const data = await this.statusChar.readAsync();
296
- this._handleStatusRead(data);
297
- } catch (err) {
298
- this.log.warn(`[${this.config.name}] Could not read device status: ${err}`);
299
- }
300
- }
301
-
302
- // ── Commands ──────────────────────────────────────────────────────────────
188
+ // ── Commands ────────────────────────────────────────────────────────────────
303
189
 
304
190
  private async _sendCommand(command: BedJetCommand, ...args: number[]): Promise<void> {
305
191
  if (!this.commandChar) {
@@ -307,8 +193,8 @@ export class BedJet extends EventEmitter {
307
193
  }
308
194
  const buf = Buffer.from([command, ...args]);
309
195
  try {
310
- // withoutResponse = false (write with response) required for V3
311
- await this.commandChar.writeAsync(buf, false);
196
+ // writeValueWithResponse = write with response, required for BedJet V3
197
+ await this.commandChar.writeValueWithResponse(buf);
312
198
  this._resetDisconnectTimer();
313
199
  } catch (err) {
314
200
  this.log.error(`[${this.config.name}] Command 0x${command.toString(16)} failed: ${err}`);
@@ -349,7 +235,7 @@ export class BedJet extends EventEmitter {
349
235
  await this.pressButton(muted ? BedJetButton.MUTE : BedJetButton.UNMUTE);
350
236
  }
351
237
 
352
- // ── Timers ────────────────────────────────────────────────────────────────
238
+ // ── Timers ──────────────────────────────────────────────────────────────────
353
239
 
354
240
  private _resetDisconnectTimer(): void {
355
241
  if (this.disconnectTimer) {
@@ -361,7 +247,6 @@ export class BedJet extends EventEmitter {
361
247
  }, DISCONNECT_DELAY_MS);
362
248
  }
363
249
 
364
- // Mark stale and update HomeKit if no notification arrives within 65s
365
250
  private _resetStaleTimer(): void {
366
251
  if (this.staleTimer) {
367
252
  clearTimeout(this.staleTimer);
@@ -373,11 +258,10 @@ export class BedJet extends EventEmitter {
373
258
  }, 65_000);
374
259
  }
375
260
 
376
- // ── Disconnect / reconnect ────────────────────────────────────────────────
261
+ // ── Disconnect / reconnect ──────────────────────────────────────────────────
377
262
 
378
263
  private _onDisconnected(): void {
379
264
  this.commandChar = null;
380
- this.statusChar = null;
381
265
 
382
266
  if (this.disconnectTimer) {
383
267
  clearTimeout(this.disconnectTimer);
@@ -392,26 +276,34 @@ export class BedJet extends EventEmitter {
392
276
  this.emit('disconnected');
393
277
  this.emit('stateChange', this._state);
394
278
 
395
- if (this.reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
396
- // Exponential backoff: 2s, 4s, 8s, 16s, 32s
397
- const delay = Math.pow(2, this.reconnectAttempts + 1) * 1000;
398
- this.reconnectAttempts++;
399
- this.log.info(
400
- `[${this.config.name}] Disconnected reconnecting in ${delay / 1000}s ` +
401
- `(attempt ${this.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`,
402
- );
403
- setTimeout(() => {
404
- this.connect().catch(err =>
405
- this.log.error(`[${this.config.name}] Reconnect failed: ${err}`),
406
- );
407
- }, delay);
408
- } else {
409
- this.log.error(`[${this.config.name}] Max reconnect attempts reached — giving up`);
410
- 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;
411
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);
412
301
  }
413
302
 
414
303
  async disconnect(): Promise<void> {
304
+ this.destroyed = true;
305
+ this.commandChar = null;
306
+
415
307
  if (this.disconnectTimer) {
416
308
  clearTimeout(this.disconnectTimer);
417
309
  this.disconnectTimer = null;
@@ -420,15 +312,9 @@ export class BedJet extends EventEmitter {
420
312
  clearTimeout(this.staleTimer);
421
313
  this.staleTimer = null;
422
314
  }
423
- // Setting max attempts prevents the disconnect handler from triggering a reconnect
424
- this.reconnectAttempts = MAX_RECONNECT_ATTEMPTS;
425
- if (this.peripheral) {
426
- try {
427
- await this.peripheral.disconnectAsync();
428
- } catch {
429
- // ignore errors during intentional disconnect
430
- }
431
- this.peripheral = null;
315
+ if (this.bleDestroy) {
316
+ try { this.bleDestroy(); } catch { /* ignore */ }
317
+ this.bleDestroy = null;
432
318
  }
433
319
  }
434
320
  }