homebridge-bedjet 0.2.7 → 0.2.8

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.
@@ -31,6 +31,35 @@
31
31
  "type": "integer",
32
32
  "default": 30,
33
33
  "description": "How long to scan for the device before giving up (default: 30)"
34
+ },
35
+ "defaultMode": {
36
+ "title": "Default Mode (when turned on)",
37
+ "type": "string",
38
+ "default": "heat",
39
+ "oneOf": [
40
+ { "title": "Heat", "enum": ["heat"] },
41
+ { "title": "Turbo", "enum": ["turbo"] },
42
+ { "title": "Extended Heat", "enum": ["extendedHeat"] },
43
+ { "title": "Cool (Fan Only)", "enum": ["cool"] },
44
+ { "title": "Dry", "enum": ["dry"] }
45
+ ],
46
+ "description": "Mode to activate when the BedJet is turned on from HomeKit"
47
+ },
48
+ "defaultTemperature": {
49
+ "title": "Default Temperature °C (when turned on)",
50
+ "type": "number",
51
+ "minimum": 19,
52
+ "maximum": 43,
53
+ "multipleOf": 0.5,
54
+ "description": "Target temperature to set when turned on. Range: 19–43°C (66–109°F). Leave blank to keep the BedJet's last-used temperature."
55
+ },
56
+ "defaultFanSpeed": {
57
+ "title": "Default Fan Speed % (when turned on)",
58
+ "type": "integer",
59
+ "minimum": 5,
60
+ "maximum": 100,
61
+ "multipleOf": 5,
62
+ "description": "Fan speed to set when turned on (5–100% in 5% steps). Leave blank to keep the BedJet's last-used speed."
34
63
  }
35
64
  }
36
65
  }
@@ -18,5 +18,13 @@ export declare class BedJetAccessory {
18
18
  private pendingModeTimer;
19
19
  private setPending;
20
20
  constructor(platform: BedJetPlatform, accessory: PlatformAccessory, config: BedJetConfig);
21
+ /**
22
+ * Set the operating mode, then optionally apply default temperature and fan
23
+ * speed from config. applyDefaults=true when turning on from off.
24
+ * applyMode=true when the mode itself should come from defaultMode config
25
+ * (fan turn-on path); false when HomeKit supplied the mode explicitly
26
+ * (thermostat path).
27
+ */
28
+ private _applyModeAndDefaults;
21
29
  private _syncHomeKit;
22
30
  }
package/dist/accessory.js CHANGED
@@ -3,6 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BedJetAccessory = void 0;
4
4
  const BedJet_1 = require("./bedjet/BedJet");
5
5
  const constants_1 = require("./bedjet/constants");
6
+ const DEFAULT_MODE_MAP = {
7
+ heat: constants_1.OperatingMode.HEAT,
8
+ turbo: constants_1.OperatingMode.TURBO,
9
+ extendedHeat: constants_1.OperatingMode.EXTENDED_HEAT,
10
+ cool: constants_1.OperatingMode.COOL,
11
+ dry: constants_1.OperatingMode.DRY,
12
+ };
6
13
  // OperatingMode → CurrentHeatingCoolingState value
7
14
  const CURRENT_STATE_MAP = {
8
15
  [constants_1.OperatingMode.STANDBY]: 0, // OFF
@@ -84,8 +91,12 @@ class BedJetAccessory {
84
91
  })
85
92
  .onSet((value) => {
86
93
  this.setPending(value, 'pendingMode', 'pendingModeTimer');
94
+ const wasOff = this.bedjet.state.operatingMode === constants_1.OperatingMode.STANDBY
95
+ || this.bedjet.state.operatingMode === constants_1.OperatingMode.WAIT;
96
+ const turningOn = value !== 0;
87
97
  const mode = TARGET_TO_MODE[value] ?? constants_1.OperatingMode.STANDBY;
88
- this.bedjet.setOperatingMode(mode).catch(err => this.platform.log.error(`[${config.name}] setOperatingMode failed: ${err}`));
98
+ // Apply the requested mode, then defaults (temp/fan) if turning on from off
99
+ this._applyModeAndDefaults(mode, wasOff && turningOn, false).catch(err => this.platform.log.error(`[${config.name}] setOperatingMode failed: ${err}`));
89
100
  });
90
101
  // FanV2 service
91
102
  this.fanService = this.accessory.getService(Service.Fanv2)
@@ -95,17 +106,16 @@ class BedJetAccessory {
95
106
  ? Characteristic.Active.ACTIVE
96
107
  : Characteristic.Active.INACTIVE)
97
108
  .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');
101
109
  if (value === Characteristic.Active.INACTIVE) {
110
+ this.setPending(0, 'pendingMode', 'pendingModeTimer');
102
111
  this.bedjet.setOperatingMode(constants_1.OperatingMode.STANDBY).catch(err => this.platform.log.error(`[${config.name}] setOperatingMode(STANDBY) failed: ${err}`));
103
112
  }
104
- else {
105
- // Only turn on if currently off
106
- if (this.bedjet.state.operatingMode === constants_1.OperatingMode.STANDBY) {
107
- this.bedjet.setOperatingMode(constants_1.OperatingMode.HEAT).catch(err => this.platform.log.error(`[${config.name}] setOperatingMode(HEAT) failed: ${err}`));
108
- }
113
+ else if (this.bedjet.state.operatingMode === constants_1.OperatingMode.STANDBY) {
114
+ // Turning on use configured default mode and apply all defaults
115
+ const mode = this.config.defaultMode
116
+ ? DEFAULT_MODE_MAP[this.config.defaultMode]
117
+ : constants_1.OperatingMode.HEAT;
118
+ this._applyModeAndDefaults(mode, true, true).catch(err => this.platform.log.error(`[${config.name}] turn on failed: ${err}`));
109
119
  }
110
120
  });
111
121
  this.fanService.getCharacteristic(Characteristic.RotationSpeed)
@@ -133,6 +143,32 @@ class BedJetAccessory {
133
143
  // Start connecting — errors are logged but don't crash Homebridge
134
144
  this.bedjet.connect().catch(err => this.platform.log.error(`[${config.name}] Initial connect failed: ${err}`));
135
145
  }
146
+ /**
147
+ * Set the operating mode, then optionally apply default temperature and fan
148
+ * speed from config. applyDefaults=true when turning on from off.
149
+ * applyMode=true when the mode itself should come from defaultMode config
150
+ * (fan turn-on path); false when HomeKit supplied the mode explicitly
151
+ * (thermostat path).
152
+ */
153
+ async _applyModeAndDefaults(mode, applyDefaults, applyMode) {
154
+ const { config } = this;
155
+ // Determine HomeKit target state for pending optimistic value
156
+ const pendingState = mode === constants_1.OperatingMode.STANDBY ? 0
157
+ : mode === constants_1.OperatingMode.COOL || mode === constants_1.OperatingMode.DRY ? 2
158
+ : 1;
159
+ this.setPending(pendingState, 'pendingMode', 'pendingModeTimer', 5000);
160
+ await this.bedjet.setOperatingMode(mode);
161
+ if (applyDefaults) {
162
+ if (config.defaultTemperature !== undefined) {
163
+ this.setPending(config.defaultTemperature, 'pendingTemp', 'pendingTempTimer', 5000);
164
+ await this.bedjet.setTemperature(config.defaultTemperature);
165
+ }
166
+ if (config.defaultFanSpeed !== undefined) {
167
+ this.setPending(config.defaultFanSpeed, 'pendingFanSpeed', 'pendingFanSpeedTimer', 5000);
168
+ await this.bedjet.setFanSpeed(config.defaultFanSpeed);
169
+ }
170
+ }
171
+ }
136
172
  _syncHomeKit(state) {
137
173
  const { Characteristic } = this.platform.api.hap;
138
174
  // Clamp helper — keeps values within the bounds HomeKit expects
@@ -17,9 +17,13 @@ export interface BedJetState {
17
17
  unitsSetup?: boolean;
18
18
  notificationCode?: number;
19
19
  }
20
+ export type DefaultMode = 'heat' | 'turbo' | 'extendedHeat' | 'cool' | 'dry';
20
21
  export interface BedJetConfig {
21
22
  name: string;
22
23
  address: string;
23
24
  scanTimeout?: number;
25
+ defaultMode?: DefaultMode;
26
+ defaultTemperature?: number;
27
+ defaultFanSpeed?: number;
24
28
  }
25
29
  export declare const DEFAULT_STATE: BedJetState;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-bedjet",
3
3
  "displayName": "BedJet",
4
- "version": "0.2.7",
4
+ "version": "0.2.8",
5
5
  "description": "Homebridge plugin for BedJet V3 via Bluetooth LE",
6
6
  "license": "MIT",
7
7
  "main": "dist/index.js",
package/src/accessory.ts CHANGED
@@ -1,9 +1,17 @@
1
1
  import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge';
2
2
  import { BedJet } from './bedjet/BedJet';
3
3
  import { OperatingMode } from './bedjet/constants';
4
- import type { BedJetConfig, BedJetState } from './bedjet/types';
4
+ import type { BedJetConfig, BedJetState, DefaultMode } from './bedjet/types';
5
5
  import type { BedJetPlatform } from './platform';
6
6
 
7
+ const DEFAULT_MODE_MAP: Record<DefaultMode, OperatingMode> = {
8
+ heat: OperatingMode.HEAT,
9
+ turbo: OperatingMode.TURBO,
10
+ extendedHeat: OperatingMode.EXTENDED_HEAT,
11
+ cool: OperatingMode.COOL,
12
+ dry: OperatingMode.DRY,
13
+ };
14
+
7
15
  // OperatingMode → CurrentHeatingCoolingState value
8
16
  const CURRENT_STATE_MAP: Record<OperatingMode, number> = {
9
17
  [OperatingMode.STANDBY]: 0, // OFF
@@ -107,8 +115,12 @@ export class BedJetAccessory {
107
115
  })
108
116
  .onSet((value: CharacteristicValue) => {
109
117
  this.setPending(value as number, 'pendingMode', 'pendingModeTimer');
118
+ const wasOff = this.bedjet.state.operatingMode === OperatingMode.STANDBY
119
+ || this.bedjet.state.operatingMode === OperatingMode.WAIT;
120
+ const turningOn = (value as number) !== 0;
110
121
  const mode = TARGET_TO_MODE[value as number] ?? OperatingMode.STANDBY;
111
- this.bedjet.setOperatingMode(mode).catch(err =>
122
+ // Apply the requested mode, then defaults (temp/fan) if turning on from off
123
+ this._applyModeAndDefaults(mode, wasOff && turningOn, false).catch(err =>
112
124
  this.platform.log.error(`[${config.name}] setOperatingMode failed: ${err}`),
113
125
  );
114
126
  });
@@ -124,20 +136,19 @@ export class BedJetAccessory {
124
136
  : Characteristic.Active.INACTIVE,
125
137
  )
126
138
  .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');
130
139
  if (value === Characteristic.Active.INACTIVE) {
140
+ this.setPending(0, 'pendingMode', 'pendingModeTimer');
131
141
  this.bedjet.setOperatingMode(OperatingMode.STANDBY).catch(err =>
132
142
  this.platform.log.error(`[${config.name}] setOperatingMode(STANDBY) failed: ${err}`),
133
143
  );
134
- } else {
135
- // Only turn on if currently off
136
- if (this.bedjet.state.operatingMode === OperatingMode.STANDBY) {
137
- this.bedjet.setOperatingMode(OperatingMode.HEAT).catch(err =>
138
- this.platform.log.error(`[${config.name}] setOperatingMode(HEAT) failed: ${err}`),
139
- );
140
- }
144
+ } else if (this.bedjet.state.operatingMode === OperatingMode.STANDBY) {
145
+ // Turning on use configured default mode and apply all defaults
146
+ const mode = this.config.defaultMode
147
+ ? DEFAULT_MODE_MAP[this.config.defaultMode]
148
+ : OperatingMode.HEAT;
149
+ this._applyModeAndDefaults(mode, true, true).catch(err =>
150
+ this.platform.log.error(`[${config.name}] turn on failed: ${err}`),
151
+ );
141
152
  }
142
153
  });
143
154
 
@@ -174,6 +185,41 @@ export class BedJetAccessory {
174
185
  );
175
186
  }
176
187
 
188
+ /**
189
+ * Set the operating mode, then optionally apply default temperature and fan
190
+ * speed from config. applyDefaults=true when turning on from off.
191
+ * applyMode=true when the mode itself should come from defaultMode config
192
+ * (fan turn-on path); false when HomeKit supplied the mode explicitly
193
+ * (thermostat path).
194
+ */
195
+ private async _applyModeAndDefaults(
196
+ mode: OperatingMode,
197
+ applyDefaults: boolean,
198
+ applyMode: boolean,
199
+ ): Promise<void> {
200
+ const { config } = this;
201
+
202
+ // Determine HomeKit target state for pending optimistic value
203
+ const pendingState =
204
+ mode === OperatingMode.STANDBY ? 0
205
+ : mode === OperatingMode.COOL || mode === OperatingMode.DRY ? 2
206
+ : 1;
207
+ this.setPending(pendingState, 'pendingMode', 'pendingModeTimer', 5000);
208
+
209
+ await this.bedjet.setOperatingMode(mode);
210
+
211
+ if (applyDefaults) {
212
+ if (config.defaultTemperature !== undefined) {
213
+ this.setPending(config.defaultTemperature, 'pendingTemp', 'pendingTempTimer', 5000);
214
+ await this.bedjet.setTemperature(config.defaultTemperature);
215
+ }
216
+ if (config.defaultFanSpeed !== undefined) {
217
+ this.setPending(config.defaultFanSpeed, 'pendingFanSpeed', 'pendingFanSpeedTimer', 5000);
218
+ await this.bedjet.setFanSpeed(config.defaultFanSpeed);
219
+ }
220
+ }
221
+ }
222
+
177
223
  private _syncHomeKit(state: BedJetState): void {
178
224
  const { Characteristic } = this.platform.api.hap;
179
225
 
@@ -20,10 +20,15 @@ export interface BedJetState {
20
20
  notificationCode?: number;
21
21
  }
22
22
 
23
+ export type DefaultMode = 'heat' | 'turbo' | 'extendedHeat' | 'cool' | 'dry';
24
+
23
25
  export interface BedJetConfig {
24
26
  name: string;
25
- address: string; // BLE MAC e.g. "AA:BB:CC:DD:EE:FF"
26
- scanTimeout?: number; // seconds (default 30)
27
+ address: string; // BLE MAC e.g. "AA:BB:CC:DD:EE:FF"
28
+ scanTimeout?: number; // seconds (default 30)
29
+ defaultMode?: DefaultMode; // mode to activate when turned on from HomeKit
30
+ defaultTemperature?: number; // °C, applied on turn-on
31
+ defaultFanSpeed?: number; // percent 5–100, applied on turn-on
27
32
  }
28
33
 
29
34
  export const DEFAULT_STATE: BedJetState = {