homebridge-ac-freedom 1.0.0 → 1.0.2

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.
Files changed (2) hide show
  1. package/package.json +2 -1
  2. package/src/ac-accessory.js +57 -14
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-ac-freedom",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
+ "displayName": "AC Freedom",
4
5
  "description": "Homebridge plugin for AUX air conditioners via Broadlink (local UDP) and AUX Cloud API",
5
6
  "main": "index.js",
6
7
  "scripts": {
@@ -66,6 +66,8 @@ class AcFreedomAccessory {
66
66
 
67
67
  this.setupAccessoryInfo();
68
68
  this.setupHeaterCooler();
69
+ this.reorderLinkedServices();
70
+ this.setupFanService();
69
71
  this.setupPresetSwitches();
70
72
  this.setupDisplaySwitch();
71
73
 
@@ -164,15 +166,6 @@ class AcFreedomAccessory {
164
166
  await this.sendTemperature(value);
165
167
  });
166
168
 
167
- // Rotation speed (fan: 0=auto, 20=mute, 40=low, 60=med, 80=high, 100=turbo)
168
- this.heaterCooler.getCharacteristic(C.RotationSpeed)
169
- .setProps({ minValue: 0, maxValue: 100, minStep: 20 })
170
- .onGet(() => this.fanSpeedToPercent(this.state.fanSpeed))
171
- .onSet(async (value) => {
172
- this.state.fanSpeed = this.percentToFanSpeed(value);
173
- await this.sendFanSpeed(this.state.fanSpeed);
174
- });
175
-
176
169
  // Swing mode
177
170
  this.heaterCooler.getCharacteristic(C.SwingMode)
178
171
  .onGet(() => (this.state.swingV || this.state.swingH)
@@ -186,6 +179,51 @@ class AcFreedomAccessory {
186
179
  });
187
180
  }
188
181
 
182
+ // ── Reorder linked services ───────────────────────────────────
183
+ // Remove cached linked services from the accessory so they are
184
+ // re-added in the correct display order: Fan → Presets → Display.
185
+ reorderLinkedServices() {
186
+ const ids = [
187
+ { type: this.Service.Fanv2, subtype: 'fan' },
188
+ { type: this.Service.Switch, subtype: 'sleep' },
189
+ { type: this.Service.Switch, subtype: 'health' },
190
+ { type: this.Service.Switch, subtype: 'eco' },
191
+ { type: this.Service.Switch, subtype: 'clean' },
192
+ { type: this.Service.Switch, subtype: 'display' },
193
+ ];
194
+ for (const { type, subtype } of ids) {
195
+ const svc = this.accessory.getServiceById(type, subtype);
196
+ if (svc) this.accessory.removeService(svc);
197
+ }
198
+ }
199
+
200
+ // ── Fan Service (linked to HeaterCooler) ────────────────────────
201
+ setupFanService() {
202
+ const C = this.Characteristic;
203
+
204
+ this.fanService = this.accessory.addService(this.Service.Fanv2, 'Fan', 'fan');
205
+
206
+ // Fan Active follows AC power
207
+ this.fanService.getCharacteristic(C.Active)
208
+ .onGet(() => this.state.power ? C.Active.ACTIVE : C.Active.INACTIVE)
209
+ .onSet(async (value) => {
210
+ this.state.power = value === C.Active.ACTIVE;
211
+ await this.sendPower(this.state.power);
212
+ });
213
+
214
+ // Rotation speed: 0=auto, 17=mute, 33=low, 50=med, 67=high, 100=turbo
215
+ this.fanService.getCharacteristic(C.RotationSpeed)
216
+ .setProps({ minValue: 0, maxValue: 100, minStep: 1 })
217
+ .onGet(() => this.fanSpeedToPercent(this.state.fanSpeed))
218
+ .onSet(async (value) => {
219
+ this.state.fanSpeed = this.percentToFanSpeed(value);
220
+ await this.sendFanSpeed(this.state.fanSpeed);
221
+ });
222
+
223
+ // Link fan to HeaterCooler so it appears inside the climate card
224
+ this.heaterCooler.addLinkedService(this.fanService);
225
+ }
226
+
189
227
  // ── Preset Switches (linked to HeaterCooler) ──────────────────
190
228
  setupPresetSwitches() {
191
229
  const presets = this.config.presets || { sleep: true, health: true, eco: true, clean: true };
@@ -200,8 +238,7 @@ class AcFreedomAccessory {
200
238
  continue;
201
239
  }
202
240
 
203
- const switchService = this.accessory.getServiceById(this.Service.Switch, key)
204
- || this.accessory.addService(this.Service.Switch, cfg.label, key);
241
+ const switchService = this.accessory.addService(this.Service.Switch, cfg.label, key);
205
242
 
206
243
  switchService.setCharacteristic(this.Characteristic.Name, cfg.label);
207
244
 
@@ -242,8 +279,7 @@ class AcFreedomAccessory {
242
279
  return;
243
280
  }
244
281
 
245
- this.displaySwitch = this.accessory.getServiceById(this.Service.Switch, 'display')
246
- || this.accessory.addService(this.Service.Switch, 'Display', 'display');
282
+ this.displaySwitch = this.accessory.addService(this.Service.Switch, 'Display', 'display');
247
283
 
248
284
  this.displaySwitch.setCharacteristic(this.Characteristic.Name, 'Display');
249
285
 
@@ -365,10 +401,17 @@ class AcFreedomAccessory {
365
401
  this.heaterCooler.updateCharacteristic(C.CurrentTemperature, this.state.currentTemp);
366
402
  this.heaterCooler.updateCharacteristic(C.CoolingThresholdTemperature, this.state.targetTemp);
367
403
  this.heaterCooler.updateCharacteristic(C.HeatingThresholdTemperature, this.state.targetTemp);
368
- this.heaterCooler.updateCharacteristic(C.RotationSpeed, this.fanSpeedToPercent(this.state.fanSpeed));
369
404
  this.heaterCooler.updateCharacteristic(C.SwingMode,
370
405
  (this.state.swingV || this.state.swingH) ? C.SwingMode.SWING_ENABLED : C.SwingMode.SWING_DISABLED);
371
406
 
407
+ // Update fan service
408
+ if (this.fanService) {
409
+ this.fanService.updateCharacteristic(C.Active,
410
+ this.state.power ? C.Active.ACTIVE : C.Active.INACTIVE);
411
+ this.fanService.updateCharacteristic(C.RotationSpeed,
412
+ this.fanSpeedToPercent(this.state.fanSpeed));
413
+ }
414
+
372
415
  // Update preset switches
373
416
  for (const [key, svc] of Object.entries(this.presetSwitches || {})) {
374
417
  svc.updateCharacteristic(C.On, this.state[key]);