homebridge-ac-freedom 2.3.5 → 2.3.7

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.3.7
4
+
5
+ - **Fix inverted swing mode on local connection** (#1) — local Broadlink protocol uses fixation `0` = swinging and `7` = fixed; the plugin had it reversed, so the HomeKit swing toggle showed the opposite of the actual louvre state. Cloud connection was unaffected.
6
+
7
+ ## 2.3.6
8
+
9
+ - **Config UI: feature toggles redesigned** — checkboxes removed; each feature is now a colour-coded pill: bright green when enabled, muted grey when disabled
10
+
3
11
  ## 2.3.5
4
12
 
5
13
  - **Fix double beep on power-on** — `TargetHeaterCoolerState` command is suppressed when fired within 200 ms of a power command; mode is already included in `sendPower`
package/README.md CHANGED
@@ -31,13 +31,13 @@ A [Homebridge](https://homebridge.io) plugin for controlling **AUX-based air con
31
31
  - Self Clean
32
32
  - Comfortable Wind
33
33
  - **Display** switch — LED display on/off
34
- - **Auto-reset** — fan and sleep reset to default when AC turns on/off or mode changes
35
- - **Hybrid** — single cloud login at platform level; optional IP + MAC per device for local Broadlink UDP (local preferred, cloud fallback)
36
- - **Local** — Direct Broadlink UDP only, no internet required
34
+ - **Auto-reset** — fan resets to Auto and Sleep turns off when the AC is powered off
35
+ - **Consistent tile order** — Fan is always first, followed by Sleep, Display, Health, Clean, Eco, Comf. Wind
36
+ - **Hybrid** — single cloud login at platform level; optional IP + MAC per device for direct local Broadlink UDP (local preferred, cloud fallback)
37
+ - **Local** — direct Broadlink UDP only, no internet required
37
38
  - **Multi-device** — configure as many ACs as needed
38
39
  - **Custom config UI** — built-in Homebridge UI with Fetch Devices button
39
40
  - Homebridge v1 & v2 compatible
40
- - English UI
41
41
 
42
42
  ---
43
43
 
@@ -196,8 +196,6 @@ Required for Hybrid mode. Configured once, shared by all hybrid devices.
196
196
  | `name` | string | Yes | — | Device name in HomeKit |
197
197
  | `connection` | string | No | `"hybrid"` | `"hybrid"` or `"local"` |
198
198
  | `endpointId` | string | No | — | Cloud device ID (auto-detected if empty) |
199
- | `pollInterval` | integer | No | `30` | Polling interval in seconds (5–300) |
200
- | `tempStep` | number | No | `0.5` | Temperature step: `0.5` or `1` |
201
199
 
202
200
  ### Local Settings (`local` object, per device)
203
201
 
@@ -218,7 +216,7 @@ Required for Hybrid mode. Configured once, shared by all hybrid devices.
218
216
  | `showComfWind` | boolean | `false` | Comfortable Wind switch |
219
217
  | `showDisplay` | boolean | `false` | Display (LED) switch |
220
218
 
221
- > **Tip:** For a clean HomeKit appearance, enable **Fan** and only one preset mode**Sleep** is the recommended choice.
219
+ > **Tip:** For a clean HomeKit appearance, enable only the features you use. **Fan** and **Sleep** are enabled by default most users need nothing else.
222
220
 
223
221
  ---
224
222
 
@@ -226,13 +224,11 @@ Required for Hybrid mode. Configured once, shared by all hybrid devices.
226
224
 
227
225
  Once configured, the AC appears as a **HeaterCooler** tile. Tapping it reveals:
228
226
 
229
- - Temperature control with heating/cooling thresholds
227
+ - Temperature control (1 °C steps, 16–32 °C)
230
228
  - Mode selector — Auto / Heat / Cool
231
229
  - Fan speed slider — 0% Auto · 25% Low · 50% Medium · 75% High · 100% Turbo
232
230
  - Swing toggle
233
- - Preset switches Sleep, Health, Eco, Clean, Comfortable Wind, Display
234
-
235
- All linked services appear as tiles inside the climate card.
231
+ - Linked service tiles in a fixed order: Fan → Sleep → Display → Health Clean Eco → Comf. Wind
236
232
 
237
233
  ---
238
234
 
@@ -246,7 +242,7 @@ All linked services appear as tiles inside the climate card.
246
242
  | Works remotely | Yes | No |
247
243
  | Local fallback | Yes (when IP + MAC configured) | — |
248
244
 
249
- > ¹ **Hybrid + IP/MAC:** if internet is unavailable at startup, the device starts in local-only mode automatically. Full hybrid resumes on the next restart when internet is available.
245
+ > ¹ **Hybrid + IP/MAC:** if internet is unavailable at Homebridge startup, the device starts in local-only mode automatically. Full hybrid resumes on the next restart when internet is available.
250
246
 
251
247
  ---
252
248
 
@@ -258,19 +254,22 @@ All linked services appear as tiles inside the climate card.
258
254
  - Check the `region` matches your account region
259
255
  - Logging into the AC Freedom app may invalidate the plugin session — restart Homebridge after using the app
260
256
 
261
- **Device not found**
257
+ **Device not found after Fetch**
262
258
  - Leave `endpointId` empty for auto-detection (first discovered device is used)
263
259
  - Use the **Fetch** button in the Homebridge UI to discover and auto-fill your device IDs
260
+ - If the device is missing from the cloud list, power cycle the AC and try again
264
261
 
265
- **"Server busy" errors**
266
- - These are transient and automatically suppressed
267
- - If persistent, increase `pollInterval` to 60 or higher
268
-
269
- **AC not responding**
262
+ **AC not responding / "Service Communication Failure" in HomeKit**
270
263
  - Check the AC is powered on and connected to Wi-Fi
271
- - Local mode: Homebridge and AC must be on the same network
264
+ - Local mode: Homebridge and the AC must be on the same network
272
265
  - Try restarting Homebridge
273
266
 
267
+ **"Server busy" errors in logs**
268
+ - These are transient cloud API errors and are automatically suppressed — no action needed
269
+
270
+ **Tile order is wrong in HomeKit**
271
+ - Remove the accessory from the Home app (the individual tile, not the whole bridge), then restart Homebridge — it will re-appear with the correct order
272
+
274
273
  ---
275
274
 
276
275
  ## Support
@@ -154,17 +154,17 @@
154
154
  .toggle-row { display: flex; gap: 6px; }
155
155
  .toggle-item {
156
156
  flex: 1; min-width: 0;
157
- display: flex; align-items: center; justify-content: center; gap: 5px;
157
+ display: flex; align-items: center; justify-content: center;
158
158
  padding: 7px 4px; border-radius: 7px;
159
159
  border: 1px solid var(--border); background: var(--card-bg);
160
- cursor: pointer; font-size: 11px; font-weight: 500; color: var(--text);
161
- white-space: nowrap; transition: border-color .15s, background .15s;
160
+ cursor: pointer; font-size: 11px; font-weight: 500; color: var(--text-muted);
161
+ white-space: nowrap; transition: border-color .15s, background .15s, color .15s;
162
+ user-select: none;
162
163
  }
163
- .toggle-item:hover { border-color: var(--accent); }
164
- .toggle-item input[type="checkbox"] {
165
- width: 13px; height: 13px; accent-color: var(--accent); flex-shrink: 0; cursor: pointer;
164
+ .toggle-item:not(.on):hover { border-color: #9ca3af; }
165
+ .toggle-item.on {
166
+ background: #16a34a; border-color: #16a34a; color: #fff; font-weight: 600;
166
167
  }
167
- .toggle-item.checked { border-color: var(--accent-border); background: var(--accent-light); }
168
168
 
169
169
  /* ── Fieldset ── */
170
170
  .fieldset {
@@ -253,23 +253,22 @@
253
253
  function featToggles(d, i) {
254
254
  const presets = d.presets || {};
255
255
  const feats = [
256
- { key: 'showFan', label: t.fan||'Fan', val: bool(d.showFan) },
257
- { key: 'sleep', label: t.sleep||'Sleep', val: bool(presets.sleep), preset: true },
258
- { key: 'showDisplay', label: t.display||'Display', val: bool(d.showDisplay) },
259
- { key: 'health', label: t.health||'Health', val: bool(presets.health), preset: true },
260
- { key: 'clean', label: t.clean||'Clean', val: bool(presets.clean), preset: true },
261
- { key: 'eco', label: t.eco||'Eco', val: bool(presets.eco), preset: true },
262
- { key: 'showComfWind', label: t.comfWind||'Comf. Wind', val: bool(d.showComfWind) },
256
+ { key: 'showFan', label: t.fan||'Fan', val: bool(d.showFan) },
257
+ { key: 'sleep', label: t.sleep||'Sleep', val: bool(presets.sleep), preset: true },
258
+ { key: 'showDisplay', label: t.display||'Display', val: bool(d.showDisplay) },
259
+ { key: 'health', label: t.health||'Health', val: bool(presets.health), preset: true },
260
+ { key: 'clean', label: t.clean||'Clean', val: bool(presets.clean), preset: true },
261
+ { key: 'eco', label: t.eco||'Eco', val: bool(presets.eco), preset: true },
262
+ { key: 'showComfWind', label: t.comfWind||'Comf. Wind', val: bool(d.showComfWind) },
263
263
  ];
264
+
264
265
  return `
265
266
  <div class="toggle-row">
266
267
  ${feats.map(f => `
267
- <label class="toggle-item ${f.val ? 'checked' : ''}">
268
- <input type="checkbox" class="feat-cb"
269
- data-i="${i}" data-k="${f.key}" data-preset="${f.preset||false}"
270
- ${f.val ? 'checked' : ''}>
268
+ <div class="toggle-item ${f.val ? 'on' : ''}"
269
+ data-i="${i}" data-k="${f.key}" data-preset="${f.preset||false}">
271
270
  ${f.label}
272
- </label>
271
+ </div>
273
272
  `).join('')}
274
273
  </div>
275
274
  `;
@@ -624,19 +623,18 @@
624
623
  });
625
624
  });
626
625
 
627
- /* Feature checkboxes */
628
- document.querySelectorAll('.feat-cb').forEach(cb => {
629
- cb.closest('.toggle-item').classList.toggle('checked', cb.checked);
630
- cb.addEventListener('change', () => {
631
- const i = +cb.dataset.i, k = cb.dataset.k;
632
- const isPreset = cb.dataset.preset === 'true';
626
+ /* Feature toggles */
627
+ document.querySelectorAll('.toggle-item').forEach(el => {
628
+ el.addEventListener('click', () => {
629
+ const isOn = el.classList.toggle('on');
630
+ const i = +el.dataset.i, k = el.dataset.k;
631
+ const isPreset = el.dataset.preset === 'true';
633
632
  if (isPreset) {
634
633
  if (!config.devices[i].presets) config.devices[i].presets = {};
635
- config.devices[i].presets[k] = cb.checked;
634
+ config.devices[i].presets[k] = isOn;
636
635
  } else {
637
- config.devices[i][k] = cb.checked;
636
+ config.devices[i][k] = isOn;
638
637
  }
639
- cb.closest('.toggle-item').classList.toggle('checked', cb.checked);
640
638
  save();
641
639
  });
642
640
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homebridge-ac-freedom",
3
- "version": "2.3.5",
3
+ "version": "2.3.7",
4
4
  "displayName": "Homebridge AC Freedom",
5
5
  "description": "Homebridge plugin for AUX air conditioners via Broadlink (local UDP) and AUX Cloud API",
6
6
  "main": "index.js",
@@ -462,8 +462,9 @@ class AcFreedomAccessory {
462
462
  this.state.currentTemp = s.ambientTemp;
463
463
  // Normalise local fan speed → canonical cloud numbering
464
464
  this.state.fanSpeed = FAN_REMAP[s.fanSpeed] ?? FAN_SPEED.AUTO;
465
- this.state.swingV = s.verticalFixation === 7;
466
- this.state.swingH = s.horizontalFixation === 7;
465
+ // Local fixation: 0 = swinging, 7 = fixed
466
+ this.state.swingV = s.verticalFixation === 0;
467
+ this.state.swingH = s.horizontalFixation === 0;
467
468
  this.state.sleep = !!s.sleep;
468
469
  this.state.health = !!s.health;
469
470
  this.state.eco = !!s.mildew;
@@ -613,8 +614,9 @@ class AcFreedomAccessory {
613
614
  return this._send(
614
615
  () => {
615
616
  const a = this._localApi;
616
- a.state.verticalFixation = v ? 7 : 0;
617
- a.state.horizontalFixation = h ? 7 : 0;
617
+ // Local fixation: 0 = swinging, 7 = fixed
618
+ a.state.verticalFixation = v ? 0 : 7;
619
+ a.state.horizontalFixation = h ? 0 : 7;
618
620
  return a.setState();
619
621
  },
620
622
  () => this.cloudSet({ [CLOUD.SWING_V]: v ? 1 : 0, [CLOUD.SWING_H]: h ? 1 : 0 }),