homebridge-bedjet 0.1.8 → 0.2.1
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.
- package/dist/accessory.d.ts +7 -0
- package/dist/accessory.js +33 -7
- package/dist/accessory.js.map +1 -1
- package/dist/bedjet/BedJet.d.ts +9 -11
- package/dist/bedjet/BedJet.js +101 -183
- package/dist/bedjet/BedJet.js.map +1 -1
- package/package.json +2 -2
- package/src/accessory.ts +46 -12
- package/src/bedjet/BedJet.ts +113 -230
package/dist/accessory.d.ts
CHANGED
|
@@ -10,6 +10,13 @@ export declare class BedJetAccessory {
|
|
|
10
10
|
private readonly bedjet;
|
|
11
11
|
private tempDebounce;
|
|
12
12
|
private fanDebounce;
|
|
13
|
+
private pendingTemp;
|
|
14
|
+
private pendingTempTimer;
|
|
15
|
+
private pendingFanSpeed;
|
|
16
|
+
private pendingFanSpeedTimer;
|
|
17
|
+
private pendingMode;
|
|
18
|
+
private pendingModeTimer;
|
|
19
|
+
private setPending;
|
|
13
20
|
constructor(platform: BedJetPlatform, accessory: PlatformAccessory, config: BedJetConfig);
|
|
14
21
|
private _syncHomeKit;
|
|
15
22
|
}
|
package/dist/accessory.js
CHANGED
|
@@ -21,6 +21,15 @@ const TARGET_TO_MODE = {
|
|
|
21
21
|
3: constants_1.OperatingMode.HEAT, // AUTO — fall back to heat
|
|
22
22
|
};
|
|
23
23
|
class BedJetAccessory {
|
|
24
|
+
setPending(value, store, timer, ms = 3000) {
|
|
25
|
+
if (this[timer]) {
|
|
26
|
+
clearTimeout(this[timer]);
|
|
27
|
+
}
|
|
28
|
+
this[store] = value;
|
|
29
|
+
this[timer] = setTimeout(() => {
|
|
30
|
+
this[store] = null;
|
|
31
|
+
}, ms);
|
|
32
|
+
}
|
|
24
33
|
constructor(platform, accessory, config) {
|
|
25
34
|
this.platform = platform;
|
|
26
35
|
this.accessory = accessory;
|
|
@@ -28,6 +37,13 @@ class BedJetAccessory {
|
|
|
28
37
|
// Debounce handles for setter commands
|
|
29
38
|
this.tempDebounce = null;
|
|
30
39
|
this.fanDebounce = null;
|
|
40
|
+
// Optimistic values — held after a set to suppress stale BLE notification bounces
|
|
41
|
+
this.pendingTemp = null;
|
|
42
|
+
this.pendingTempTimer = null;
|
|
43
|
+
this.pendingFanSpeed = null;
|
|
44
|
+
this.pendingFanSpeedTimer = null;
|
|
45
|
+
this.pendingMode = null;
|
|
46
|
+
this.pendingModeTimer = null;
|
|
31
47
|
const { Service, Characteristic } = platform.api.hap;
|
|
32
48
|
// AccessoryInformation
|
|
33
49
|
const infoService = this.accessory.getService(Service.AccessoryInformation)
|
|
@@ -45,8 +61,9 @@ class BedJetAccessory {
|
|
|
45
61
|
.onGet(() => this.bedjet.state.currentTemperature);
|
|
46
62
|
this.thermostatService.getCharacteristic(Characteristic.TargetTemperature)
|
|
47
63
|
.setProps({ minValue: 19, maxValue: 43, minStep: 0.5 })
|
|
48
|
-
.onGet(() => this.bedjet.state.targetTemperature)
|
|
64
|
+
.onGet(() => this.pendingTemp ?? this.bedjet.state.targetTemperature)
|
|
49
65
|
.onSet((value) => {
|
|
66
|
+
this.setPending(value, 'pendingTemp', 'pendingTempTimer');
|
|
50
67
|
if (this.tempDebounce) {
|
|
51
68
|
clearTimeout(this.tempDebounce);
|
|
52
69
|
}
|
|
@@ -66,6 +83,7 @@ class BedJetAccessory {
|
|
|
66
83
|
return 1; // HEAT / TURBO / EXTENDED_HEAT
|
|
67
84
|
})
|
|
68
85
|
.onSet((value) => {
|
|
86
|
+
this.setPending(value, 'pendingMode', 'pendingModeTimer');
|
|
69
87
|
const mode = TARGET_TO_MODE[value] ?? constants_1.OperatingMode.STANDBY;
|
|
70
88
|
this.bedjet.setOperatingMode(mode).catch(err => this.platform.log.error(`[${config.name}] setOperatingMode failed: ${err}`));
|
|
71
89
|
});
|
|
@@ -77,6 +95,9 @@ class BedJetAccessory {
|
|
|
77
95
|
? Characteristic.Active.ACTIVE
|
|
78
96
|
: Characteristic.Active.INACTIVE)
|
|
79
97
|
.onSet((value) => {
|
|
98
|
+
// pendingMode: 0=OFF, 1=HEAT (active), used to suppress stale BLE bounce
|
|
99
|
+
const pendingModeValue = value === Characteristic.Active.INACTIVE ? 0 : 1;
|
|
100
|
+
this.setPending(pendingModeValue, 'pendingMode', 'pendingModeTimer');
|
|
80
101
|
if (value === Characteristic.Active.INACTIVE) {
|
|
81
102
|
this.bedjet.setOperatingMode(constants_1.OperatingMode.STANDBY).catch(err => this.platform.log.error(`[${config.name}] setOperatingMode(STANDBY) failed: ${err}`));
|
|
82
103
|
}
|
|
@@ -89,8 +110,9 @@ class BedJetAccessory {
|
|
|
89
110
|
});
|
|
90
111
|
this.fanService.getCharacteristic(Characteristic.RotationSpeed)
|
|
91
112
|
.setProps({ minValue: 5, maxValue: 100, minStep: 5 })
|
|
92
|
-
.onGet(() => this.bedjet.state.fanSpeed)
|
|
113
|
+
.onGet(() => this.pendingFanSpeed ?? this.bedjet.state.fanSpeed)
|
|
93
114
|
.onSet((value) => {
|
|
115
|
+
this.setPending(value, 'pendingFanSpeed', 'pendingFanSpeedTimer');
|
|
94
116
|
if (this.fanDebounce) {
|
|
95
117
|
clearTimeout(this.fanDebounce);
|
|
96
118
|
}
|
|
@@ -122,18 +144,22 @@ class BedJetAccessory {
|
|
|
122
144
|
.getCharacteristic(Characteristic.TargetTemperature)
|
|
123
145
|
.setProps({ minValue: minTemp, maxValue: maxTemp });
|
|
124
146
|
this.thermostatService.updateCharacteristic(Characteristic.CurrentTemperature, clamp(state.currentTemperature, -270, 100));
|
|
125
|
-
|
|
126
|
-
this.
|
|
127
|
-
const
|
|
147
|
+
// Use pending (optimistic) values if set — suppresses stale BLE notification bounces
|
|
148
|
+
const targetTemp = this.pendingTemp ?? clamp(state.targetTemperature, minTemp, maxTemp);
|
|
149
|
+
const fanSpeed = this.pendingFanSpeed ?? clamp(state.fanSpeed, 5, 100);
|
|
150
|
+
const derivedTargetState = state.operatingMode === constants_1.OperatingMode.STANDBY || state.operatingMode === constants_1.OperatingMode.WAIT
|
|
128
151
|
? 0
|
|
129
152
|
: state.operatingMode === constants_1.OperatingMode.COOL || state.operatingMode === constants_1.OperatingMode.DRY
|
|
130
153
|
? 2
|
|
131
154
|
: 1;
|
|
155
|
+
const targetState = this.pendingMode ?? derivedTargetState;
|
|
156
|
+
this.thermostatService.updateCharacteristic(Characteristic.TargetTemperature, targetTemp);
|
|
157
|
+
this.thermostatService.updateCharacteristic(Characteristic.CurrentHeatingCoolingState, CURRENT_STATE_MAP[state.operatingMode] ?? 0);
|
|
132
158
|
this.thermostatService.updateCharacteristic(Characteristic.TargetHeatingCoolingState, targetState);
|
|
133
|
-
this.fanService.updateCharacteristic(Characteristic.Active,
|
|
159
|
+
this.fanService.updateCharacteristic(Characteristic.Active, targetState !== 0
|
|
134
160
|
? Characteristic.Active.ACTIVE
|
|
135
161
|
: Characteristic.Active.INACTIVE);
|
|
136
|
-
this.fanService.updateCharacteristic(Characteristic.RotationSpeed,
|
|
162
|
+
this.fanService.updateCharacteristic(Characteristic.RotationSpeed, fanSpeed);
|
|
137
163
|
}
|
|
138
164
|
}
|
|
139
165
|
exports.BedJetAccessory = BedJetAccessory;
|
package/dist/accessory.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accessory.js","sourceRoot":"","sources":["../src/accessory.ts"],"names":[],"mappings":";;;AACA,4CAAyC;AACzC,kDAAmD;AAInD,mDAAmD;AACnD,MAAM,iBAAiB,GAAkC;IACvD,CAAC,yBAAa,CAAC,OAAO,CAAC,EAAQ,CAAC,EAAE,MAAM;IACxC,CAAC,yBAAa,CAAC,IAAI,CAAC,EAAW,CAAC,EAAE,OAAO;IACzC,CAAC,yBAAa,CAAC,KAAK,CAAC,EAAU,CAAC,EAAE,OAAO;IACzC,CAAC,yBAAa,CAAC,aAAa,CAAC,EAAE,CAAC,EAAE,OAAO;IACzC,CAAC,yBAAa,CAAC,IAAI,CAAC,EAAW,CAAC,EAAE,OAAO;IACzC,CAAC,yBAAa,CAAC,GAAG,CAAC,EAAY,CAAC,EAAE,OAAO;IACzC,CAAC,yBAAa,CAAC,IAAI,CAAC,EAAW,CAAC,EAAE,MAAM;CACzC,CAAC;AAEF,kDAAkD;AAClD,MAAM,cAAc,GAAkC;IACpD,CAAC,EAAE,yBAAa,CAAC,OAAO;IACxB,CAAC,EAAE,yBAAa,CAAC,IAAI;IACrB,CAAC,EAAE,yBAAa,CAAC,IAAI;IACrB,CAAC,EAAE,yBAAa,CAAC,IAAI,EAAE,2BAA2B;CACnD,CAAC;AAEF,MAAa,eAAe;
|
|
1
|
+
{"version":3,"file":"accessory.js","sourceRoot":"","sources":["../src/accessory.ts"],"names":[],"mappings":"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"}
|
package/dist/bedjet/BedJet.d.ts
CHANGED
|
@@ -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
|
|
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
|
}
|
package/dist/bedjet/BedJet.js
CHANGED
|
@@ -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
|
-
|
|
6
|
-
|
|
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
|
-
//
|
|
12
|
-
|
|
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
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
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.
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
this.log.info(`[${this.config.name}]
|
|
118
|
-
|
|
119
|
-
await
|
|
120
|
-
this.log.info(`[${this.config.name}]
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
this.log.
|
|
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
|
-
|
|
141
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
//
|
|
237
|
-
await this.commandChar.
|
|
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
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
`
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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
|
-
|
|
331
|
-
this.reconnectAttempts = constants_1.MAX_RECONNECT_ATTEMPTS;
|
|
332
|
-
if (this.peripheral) {
|
|
252
|
+
if (this.bleDestroy) {
|
|
333
253
|
try {
|
|
334
|
-
|
|
335
|
-
}
|
|
336
|
-
catch {
|
|
337
|
-
// ignore errors during intentional disconnect
|
|
254
|
+
this.bleDestroy();
|
|
338
255
|
}
|
|
339
|
-
|
|
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
|
|
4
|
+
"version": "0.2.1",
|
|
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
|
-
"
|
|
19
|
+
"node-ble": "^1.10.1"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/node": "^20.0.0",
|
package/src/accessory.ts
CHANGED
|
@@ -32,6 +32,29 @@ export class BedJetAccessory {
|
|
|
32
32
|
private tempDebounce: NodeJS.Timeout | null = null;
|
|
33
33
|
private fanDebounce: NodeJS.Timeout | null = null;
|
|
34
34
|
|
|
35
|
+
// Optimistic values — held after a set to suppress stale BLE notification bounces
|
|
36
|
+
private pendingTemp: number | null = null;
|
|
37
|
+
private pendingTempTimer: NodeJS.Timeout | null = null;
|
|
38
|
+
private pendingFanSpeed: number | null = null;
|
|
39
|
+
private pendingFanSpeedTimer: NodeJS.Timeout | null = null;
|
|
40
|
+
private pendingMode: number | null = null;
|
|
41
|
+
private pendingModeTimer: NodeJS.Timeout | null = null;
|
|
42
|
+
|
|
43
|
+
private setPending<T>(
|
|
44
|
+
value: T,
|
|
45
|
+
store: 'pendingTemp' | 'pendingFanSpeed' | 'pendingMode',
|
|
46
|
+
timer: 'pendingTempTimer' | 'pendingFanSpeedTimer' | 'pendingModeTimer',
|
|
47
|
+
ms = 3000,
|
|
48
|
+
): void {
|
|
49
|
+
if (this[timer]) {
|
|
50
|
+
clearTimeout(this[timer] as NodeJS.Timeout);
|
|
51
|
+
}
|
|
52
|
+
(this as unknown as Record<string, unknown>)[store] = value;
|
|
53
|
+
(this as unknown as Record<string, unknown>)[timer] = setTimeout(() => {
|
|
54
|
+
(this as unknown as Record<string, unknown>)[store] = null;
|
|
55
|
+
}, ms);
|
|
56
|
+
}
|
|
57
|
+
|
|
35
58
|
constructor(
|
|
36
59
|
private readonly platform: BedJetPlatform,
|
|
37
60
|
private readonly accessory: PlatformAccessory,
|
|
@@ -59,8 +82,9 @@ export class BedJetAccessory {
|
|
|
59
82
|
|
|
60
83
|
this.thermostatService.getCharacteristic(Characteristic.TargetTemperature)
|
|
61
84
|
.setProps({ minValue: 19, maxValue: 43, minStep: 0.5 })
|
|
62
|
-
.onGet(() => this.bedjet.state.targetTemperature)
|
|
85
|
+
.onGet(() => this.pendingTemp ?? this.bedjet.state.targetTemperature)
|
|
63
86
|
.onSet((value: CharacteristicValue) => {
|
|
87
|
+
this.setPending(value as number, 'pendingTemp', 'pendingTempTimer');
|
|
64
88
|
if (this.tempDebounce) {
|
|
65
89
|
clearTimeout(this.tempDebounce);
|
|
66
90
|
}
|
|
@@ -82,6 +106,7 @@ export class BedJetAccessory {
|
|
|
82
106
|
return 1; // HEAT / TURBO / EXTENDED_HEAT
|
|
83
107
|
})
|
|
84
108
|
.onSet((value: CharacteristicValue) => {
|
|
109
|
+
this.setPending(value as number, 'pendingMode', 'pendingModeTimer');
|
|
85
110
|
const mode = TARGET_TO_MODE[value as number] ?? OperatingMode.STANDBY;
|
|
86
111
|
this.bedjet.setOperatingMode(mode).catch(err =>
|
|
87
112
|
this.platform.log.error(`[${config.name}] setOperatingMode failed: ${err}`),
|
|
@@ -99,6 +124,9 @@ export class BedJetAccessory {
|
|
|
99
124
|
: Characteristic.Active.INACTIVE,
|
|
100
125
|
)
|
|
101
126
|
.onSet((value: CharacteristicValue) => {
|
|
127
|
+
// pendingMode: 0=OFF, 1=HEAT (active), used to suppress stale BLE bounce
|
|
128
|
+
const pendingModeValue = value === Characteristic.Active.INACTIVE ? 0 : 1;
|
|
129
|
+
this.setPending(pendingModeValue, 'pendingMode', 'pendingModeTimer');
|
|
102
130
|
if (value === Characteristic.Active.INACTIVE) {
|
|
103
131
|
this.bedjet.setOperatingMode(OperatingMode.STANDBY).catch(err =>
|
|
104
132
|
this.platform.log.error(`[${config.name}] setOperatingMode(STANDBY) failed: ${err}`),
|
|
@@ -115,8 +143,9 @@ export class BedJetAccessory {
|
|
|
115
143
|
|
|
116
144
|
this.fanService.getCharacteristic(Characteristic.RotationSpeed)
|
|
117
145
|
.setProps({ minValue: 5, maxValue: 100, minStep: 5 })
|
|
118
|
-
.onGet(() => this.bedjet.state.fanSpeed)
|
|
146
|
+
.onGet(() => this.pendingFanSpeed ?? this.bedjet.state.fanSpeed)
|
|
119
147
|
.onSet((value: CharacteristicValue) => {
|
|
148
|
+
this.setPending(value as number, 'pendingFanSpeed', 'pendingFanSpeedTimer');
|
|
120
149
|
if (this.fanDebounce) {
|
|
121
150
|
clearTimeout(this.fanDebounce);
|
|
122
151
|
}
|
|
@@ -165,9 +194,21 @@ export class BedJetAccessory {
|
|
|
165
194
|
clamp(state.currentTemperature, -270, 100),
|
|
166
195
|
);
|
|
167
196
|
|
|
197
|
+
// Use pending (optimistic) values if set — suppresses stale BLE notification bounces
|
|
198
|
+
const targetTemp = this.pendingTemp ?? clamp(state.targetTemperature, minTemp, maxTemp);
|
|
199
|
+
const fanSpeed = this.pendingFanSpeed ?? clamp(state.fanSpeed, 5, 100);
|
|
200
|
+
|
|
201
|
+
const derivedTargetState =
|
|
202
|
+
state.operatingMode === OperatingMode.STANDBY || state.operatingMode === OperatingMode.WAIT
|
|
203
|
+
? 0
|
|
204
|
+
: state.operatingMode === OperatingMode.COOL || state.operatingMode === OperatingMode.DRY
|
|
205
|
+
? 2
|
|
206
|
+
: 1;
|
|
207
|
+
const targetState = this.pendingMode ?? derivedTargetState;
|
|
208
|
+
|
|
168
209
|
this.thermostatService.updateCharacteristic(
|
|
169
210
|
Characteristic.TargetTemperature,
|
|
170
|
-
|
|
211
|
+
targetTemp,
|
|
171
212
|
);
|
|
172
213
|
|
|
173
214
|
this.thermostatService.updateCharacteristic(
|
|
@@ -175,13 +216,6 @@ export class BedJetAccessory {
|
|
|
175
216
|
CURRENT_STATE_MAP[state.operatingMode] ?? 0,
|
|
176
217
|
);
|
|
177
218
|
|
|
178
|
-
const targetState =
|
|
179
|
-
state.operatingMode === OperatingMode.STANDBY || state.operatingMode === OperatingMode.WAIT
|
|
180
|
-
? 0
|
|
181
|
-
: state.operatingMode === OperatingMode.COOL || state.operatingMode === OperatingMode.DRY
|
|
182
|
-
? 2
|
|
183
|
-
: 1;
|
|
184
|
-
|
|
185
219
|
this.thermostatService.updateCharacteristic(
|
|
186
220
|
Characteristic.TargetHeatingCoolingState,
|
|
187
221
|
targetState,
|
|
@@ -189,14 +223,14 @@ export class BedJetAccessory {
|
|
|
189
223
|
|
|
190
224
|
this.fanService.updateCharacteristic(
|
|
191
225
|
Characteristic.Active,
|
|
192
|
-
|
|
226
|
+
targetState !== 0
|
|
193
227
|
? Characteristic.Active.ACTIVE
|
|
194
228
|
: Characteristic.Active.INACTIVE,
|
|
195
229
|
);
|
|
196
230
|
|
|
197
231
|
this.fanService.updateCharacteristic(
|
|
198
232
|
Characteristic.RotationSpeed,
|
|
199
|
-
|
|
233
|
+
fanSpeed,
|
|
200
234
|
);
|
|
201
235
|
}
|
|
202
236
|
}
|
package/src/bedjet/BedJet.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
//
|
|
27
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
147
|
-
this.
|
|
148
|
-
this.log.info(`[${this.config.name}] Connecting to ${peripheral.address}…`);
|
|
74
|
+
const { bluetooth, destroy } = createBluetooth();
|
|
75
|
+
this.bleDestroy = destroy;
|
|
149
76
|
|
|
150
|
-
|
|
77
|
+
this.log.info(`[${this.config.name}] Getting Bluetooth adapter…`);
|
|
78
|
+
const adapter = await bluetooth.defaultAdapter();
|
|
151
79
|
|
|
152
|
-
|
|
153
|
-
await
|
|
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
|
-
|
|
160
|
-
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
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}]
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
98
|
+
device.on('disconnect', () => {
|
|
99
|
+
this.log.warn(`[${this.config.name}] Device disconnected`);
|
|
100
|
+
this._onDisconnected();
|
|
101
|
+
});
|
|
189
102
|
|
|
190
|
-
|
|
191
|
-
this.log.info(`[${this.config.name}]
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
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
|
-
|
|
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
|
|
120
|
+
const statusData = await statusChar.readValue();
|
|
121
|
+
this._handleStatusRead(statusData);
|
|
203
122
|
} catch (err) {
|
|
204
|
-
this.log.warn(`[${this.config.name}]
|
|
123
|
+
this.log.warn(`[${this.config.name}] Could not read device status: ${err}`);
|
|
205
124
|
}
|
|
206
|
-
|
|
125
|
+
|
|
126
|
+
// Read device name
|
|
207
127
|
try {
|
|
208
|
-
await
|
|
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}]
|
|
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
|
|
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
|
-
|
|
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:
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
314
|
-
await this.commandChar.
|
|
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
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
`
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
-
|
|
427
|
-
|
|
428
|
-
|
|
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
|
}
|