homebridge-cync-app 0.3.4 → 0.4.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.
@@ -0,0 +1,96 @@
1
+ ---
2
+ name: Unsupported or Incorrectly Detected Cync Device
3
+ about: Accessory is detected, but has the wrong type or missing features
4
+ title: 'Cync Device Issue: [MODEL]'
5
+ labels: enhancement
6
+ assignees: dash16
7
+
8
+ ---
9
+
10
+ <!--Thank you for helping expand device support in **homebridge-cync-app**.
11
+
12
+ To improve compatibility for an incorrectly identified/unidentified Cync device, I need diagnostic information from your plugin installation and the Cync app.
13
+
14
+ Please replace the placeholders below with your information.
15
+ You can delete these instruction comments before submitting.
16
+ -->
17
+ ---
18
+
19
+ ## 1. Device Information
20
+
21
+ **1.1 Product name as shown in the Cync app**
22
+
23
+ <!--e.g., “6" Recessed Can Retrofit Fixture (Matter)”, “Indoor Smart Plug (3-in-1)”, “Indoor Smart Plug”-->
24
+ ```
25
+ <insert product name>
26
+ ```
27
+ **1.2 Product page URL (GE/Cync or retailer)**
28
+
29
+ <!-- Paste a GE/Cync or retailer product link, if available. -->
30
+ ```
31
+ <insert URL>
32
+ ```
33
+ **1.3 What kind of device is this?**
34
+
35
+ <!-- Example: plug, dimmer, tunable white light, full color light, fan, switch, sensor, other. -->
36
+ ```
37
+ <insert device type>
38
+ ```
39
+ ---
40
+
41
+ ## 2. Discovery Logs (Required)
42
+
43
+ <!--Please enable **Homebridge Debug Mode**, restart Homebridge, and paste the startup discovery logs for the affected device.
44
+
45
+ Look for lines like:
46
+
47
+ ```text
48
+ [Cync] CyncClient: loading Cync cloud configuration…
49
+ [Cync] Fetching Cync devices from https://api.gelighting.com/...
50
+ [Cync] Cync devices payload: top-level array length=...
51
+ [Cync] CyncClient: probing properties for mesh ... (id=..., product_id=...)
52
+ [Cync] CyncClient: mesh ... properties keys=...
53
+ [Cync] CyncClient: mesh ... bulbsArray length=...
54
+ [Cync] CyncClient: bulb #... for mesh ... → {
55
+ displayName: ...
56
+ deviceID: ...
57
+ deviceType: ...
58
+ raw: { ... }
59
+ }
60
+ ```
61
+
62
+ Please include the full bulb #... block for the affected device.
63
+ You may redact Wi-Fi names, MAC addresses, and tokens.
64
+ -->
65
+ ```
66
+ <insert logs>
67
+ ```
68
+
69
+ ---
70
+
71
+ ## 3. Screenshots from the **Cync App**
72
+
73
+ <!--Please attach screenshots **from the Cync mobile app (not Apple Home)** showing:
74
+
75
+ * The **device settings page**
76
+ * The **main control UI** (brightness / color / mode controls)
77
+ * Any **advanced settings** available
78
+
79
+ These help determine which capabilities the device supports.
80
+ -->
81
+ ---
82
+
83
+ ## 4. Anything Else?
84
+
85
+ <!--If the device behaves in an unusual way (multiple endpoints, segmented lights, unusual controls, etc.), please describe it here.
86
+ -->
87
+ ```
88
+ <notes>
89
+ ```
90
+
91
+ ---
92
+
93
+ <!--
94
+ Thank You!
95
+ Once you submit this, I’ll analyze the device capabilities, determine the correct HomeKit service type, and add support to the plugin.
96
+ -->
package/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ # v0.4.0 — Capability Classifier & Fan Support
4
+
5
+ **Release Date:** 2026-05-27
6
+
7
+ ## Added
8
+ - Added support for Cync fan switch devices (deviceType 81) using HomeKit `Fanv2`
9
+ - Added dedicated `cync-fan-accessory.ts` accessory handler
10
+ - Added centralized `device-classifier.ts` subsystem for accessory classification
11
+ - Added classification debug logging showing:
12
+ - device name
13
+ - device ID
14
+ - resolved device type
15
+ - detected capabilities
16
+ - selected accessory type
17
+ - classification reason
18
+ - Added unsupported device fallback logging to improve future device onboarding
19
+
20
+ ## Changed
21
+ - Refactored accessory selection to use capability-aware classification instead of relying solely on static device type lists
22
+ - Improved separation of concerns by moving classification logic out of `platform.ts`
23
+ - Expanded known Cync device catalogs using data cross-referenced from the Home Assistant Cync integration
24
+
25
+ ## Fixed
26
+ - Fixed fan switches incorrectly appearing as Lightbulb accessories in HomeKit
27
+ - Restored proper handling for several light and outlet device types after classifier integration
28
+ - Improved accessory fallback behavior for unknown or partially supported devices
29
+
30
+ ## Developer Notes
31
+ - Classification decisions are now centralized in `src/cync/device-classifier.ts`
32
+ - Future device onboarding should require significantly less modification to `platform.ts`
33
+ - New debug logs should make community issue reports substantially easier to triage
34
+
3
35
  ## 0.3.4 — Composite Switch ID Crash Fix
4
36
 
5
37
  **Release Date:** 2026-05-22
@@ -0,0 +1,4 @@
1
+ import type { PlatformAccessory } from 'homebridge';
2
+ import type { CyncDevice, CyncDeviceMesh } from './config-client.js';
3
+ import type { CyncAccessoryEnv } from './cync-accessory-helpers.js';
4
+ export declare function configureCyncFanAccessory(env: CyncAccessoryEnv, mesh: CyncDeviceMesh, device: CyncDevice, accessory: PlatformAccessory, deviceName: string, deviceId: string): void;
@@ -0,0 +1,96 @@
1
+ import { applyAccessoryInformationFromCyncDevice, resolveDeviceType, } from './cync-accessory-helpers.js';
2
+ function clampFanSpeed(value) {
3
+ const speed = Math.round(Number(value));
4
+ if (!Number.isFinite(speed)) {
5
+ return 0;
6
+ }
7
+ return Math.max(0, Math.min(100, speed));
8
+ }
9
+ // Fan Accessory Configurator: Exposes Cync fan switches as HomeKit Fanv2 accessories
10
+ export function configureCyncFanAccessory(env, mesh, device, accessory, deviceName, deviceId) {
11
+ const Service = env.api.hap.Service;
12
+ const Characteristic = env.api.hap.Characteristic;
13
+ const existingLight = accessory.getService(Service.Lightbulb);
14
+ if (existingLight) {
15
+ env.log.info('Cync: removing stale Lightbulb service from %s (deviceId=%s) before configuring as Fan', deviceName, deviceId);
16
+ accessory.removeService(existingLight);
17
+ }
18
+ const existingSwitch = accessory.getService(Service.Switch);
19
+ if (existingSwitch) {
20
+ env.log.info('Cync: removing stale Switch service from %s (deviceId=%s) before configuring as Fan', deviceName, deviceId);
21
+ accessory.removeService(existingSwitch);
22
+ }
23
+ const existingOutlet = accessory.getService(Service.Outlet);
24
+ if (existingOutlet) {
25
+ env.log.info('Cync: removing stale Outlet service from %s (deviceId=%s) before configuring as Fan', deviceName, deviceId);
26
+ accessory.removeService(existingOutlet);
27
+ }
28
+ const service = accessory.getService(Service.Fanv2) ||
29
+ accessory.addService(Service.Fanv2, deviceName);
30
+ if (accessory.category !== 3 /* env.api.hap.Categories.FAN */) {
31
+ accessory.category = 3 /* env.api.hap.Categories.FAN */;
32
+ }
33
+ applyAccessoryInformationFromCyncDevice(env.api, accessory, device, deviceName, deviceId);
34
+ const ctx = accessory.context;
35
+ ctx.cync = ctx.cync ?? {
36
+ meshId: mesh.id,
37
+ deviceId,
38
+ productId: device.product_id,
39
+ on: false,
40
+ };
41
+ const resolvedDeviceType = resolveDeviceType(device);
42
+ if (typeof resolvedDeviceType === 'number' && Number.isFinite(resolvedDeviceType)) {
43
+ ctx.cync.deviceType = resolvedDeviceType;
44
+ }
45
+ env.registerAccessoryForDevice(deviceId, accessory);
46
+ env.markDeviceSeen(deviceId);
47
+ env.startPollingDevice(deviceId);
48
+ service
49
+ .getCharacteristic(Characteristic.Active)
50
+ .onGet(() => {
51
+ return ctx.cync?.on ? Characteristic.Active.ACTIVE : Characteristic.Active.INACTIVE;
52
+ })
53
+ .onSet(async (value) => {
54
+ const cyncMeta = ctx.cync;
55
+ if (!cyncMeta?.deviceId) {
56
+ env.log.warn('Cync: Fan Active.set called for %s but no cync.deviceId in context', deviceName);
57
+ return;
58
+ }
59
+ const on = value === Characteristic.Active.ACTIVE;
60
+ cyncMeta.on = on;
61
+ try {
62
+ await env.tcpClient.setSwitchState(cyncMeta.deviceId, { on });
63
+ env.markDeviceSeen(cyncMeta.deviceId);
64
+ }
65
+ catch (err) {
66
+ env.log.warn('Cync: Fan Active.set failed for %s (deviceId=%s): %s', deviceName, cyncMeta.deviceId, err.message ?? String(err));
67
+ throw new env.api.hap.HapStatusError(-70402 /* env.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
68
+ }
69
+ });
70
+ service
71
+ .getCharacteristic(Characteristic.RotationSpeed)
72
+ .onGet(() => {
73
+ return typeof ctx.cync?.brightness === 'number'
74
+ ? ctx.cync.brightness
75
+ : ctx.cync?.on ? 100 : 0;
76
+ })
77
+ .onSet(async (value) => {
78
+ const cyncMeta = ctx.cync;
79
+ if (!cyncMeta?.deviceId) {
80
+ env.log.warn('Cync: Fan RotationSpeed.set called for %s but no cync.deviceId in context', deviceName);
81
+ return;
82
+ }
83
+ const speed = clampFanSpeed(value);
84
+ cyncMeta.brightness = speed;
85
+ cyncMeta.on = speed > 0;
86
+ try {
87
+ await env.tcpClient.setBrightness(cyncMeta.deviceId, speed, cyncMeta.deviceType);
88
+ env.markDeviceSeen(cyncMeta.deviceId);
89
+ }
90
+ catch (err) {
91
+ env.log.warn('Cync: Fan RotationSpeed.set failed for %s (deviceId=%s): %s', deviceName, cyncMeta.deviceId, err.message ?? String(err));
92
+ throw new env.api.hap.HapStatusError(-70402 /* env.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
93
+ }
94
+ });
95
+ }
96
+ //# sourceMappingURL=cync-fan-accessory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cync-fan-accessory.js","sourceRoot":"","sources":["../../src/cync/cync-fan-accessory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ type CyncAccessoryType = 'light' | 'fan' | 'outlet' | 'switch' | 'unsupported';
2
+ export interface CyncDeviceLike {
3
+ id?: string;
4
+ name?: string;
5
+ deviceType?: number;
6
+ type?: number;
7
+ capabilities?: string[] | Record<string, unknown>;
8
+ }
9
+ export interface CyncDeviceClassification {
10
+ accessoryType: CyncAccessoryType;
11
+ deviceType?: number;
12
+ capabilities: string[];
13
+ reason: string;
14
+ }
15
+ export declare function classifyCyncDevice(device: CyncDeviceLike, resolvedDeviceType?: number): CyncDeviceClassification;
16
+ export {};
@@ -0,0 +1,94 @@
1
+ // src/cync/device-classifier.ts
2
+ // Cync Device Classifier: Centralizes accessory type selection and classification logging context
3
+ const CYNC_LIGHT_DEVICE_TYPES = new Set([
4
+ 1, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 17, 18, 19, 20, 21,
5
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
6
+ 37, 46, 47, 48, 49, 55, 56, 80, 82, 83, 85, 123, 128, 129, 130,
7
+ 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
8
+ 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
9
+ 155, 156, 158, 159, 160, 161, 162, 163, 164, 165, 166, 169,
10
+ 170, 171,
11
+ ]);
12
+ const CYNC_OUTLET_DEVICE_TYPES = new Set([
13
+ 64, 65, 66, 67, 68, 172,
14
+ ]);
15
+ const CYNC_FAN_DEVICE_TYPES = new Set([
16
+ 81,
17
+ ]);
18
+ function normalizeCapabilities(capabilities) {
19
+ if (!capabilities) {
20
+ return [];
21
+ }
22
+ if (Array.isArray(capabilities)) {
23
+ return capabilities
24
+ .map(capability => capability.toLowerCase())
25
+ .sort();
26
+ }
27
+ return Object.keys(capabilities)
28
+ .map(capability => capability.toLowerCase())
29
+ .sort();
30
+ }
31
+ function getDeviceType(device) {
32
+ return device.deviceType ?? device.type;
33
+ }
34
+ export function classifyCyncDevice(device, resolvedDeviceType) {
35
+ const deviceType = resolvedDeviceType ?? getDeviceType(device);
36
+ const capabilities = normalizeCapabilities(device.capabilities);
37
+ if (capabilities.includes('fan')) {
38
+ return {
39
+ accessoryType: 'fan',
40
+ deviceType,
41
+ capabilities,
42
+ reason: 'capability: fan',
43
+ };
44
+ }
45
+ if (capabilities.includes('color') ||
46
+ capabilities.includes('colortemp') ||
47
+ capabilities.includes('brightness')) {
48
+ return {
49
+ accessoryType: 'light',
50
+ deviceType,
51
+ capabilities,
52
+ reason: 'capability: light control',
53
+ };
54
+ }
55
+ if (deviceType !== undefined && CYNC_FAN_DEVICE_TYPES.has(deviceType)) {
56
+ return {
57
+ accessoryType: 'fan',
58
+ deviceType,
59
+ capabilities,
60
+ reason: `deviceType: ${deviceType}`,
61
+ };
62
+ }
63
+ if (deviceType !== undefined && CYNC_LIGHT_DEVICE_TYPES.has(deviceType)) {
64
+ return {
65
+ accessoryType: 'light',
66
+ deviceType,
67
+ capabilities,
68
+ reason: `deviceType: ${deviceType}`,
69
+ };
70
+ }
71
+ if (deviceType !== undefined && CYNC_OUTLET_DEVICE_TYPES.has(deviceType)) {
72
+ return {
73
+ accessoryType: 'outlet',
74
+ deviceType,
75
+ capabilities,
76
+ reason: `deviceType: ${deviceType}`,
77
+ };
78
+ }
79
+ if (capabilities.includes('onoff')) {
80
+ return {
81
+ accessoryType: 'switch',
82
+ deviceType,
83
+ capabilities,
84
+ reason: 'capability: onoff fallback',
85
+ };
86
+ }
87
+ return {
88
+ accessoryType: 'unsupported',
89
+ deviceType,
90
+ capabilities,
91
+ reason: 'no supported capabilities or known deviceType',
92
+ };
93
+ }
94
+ //# sourceMappingURL=device-classifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-classifier.js","sourceRoot":"","sources":["../../src/cync/device-classifier.ts"],"names":[],"mappings":"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"}
package/dist/platform.js CHANGED
@@ -6,38 +6,16 @@ import { resolveDeviceType, rgbToHsv, } from './cync/cync-accessory-helpers.js';
6
6
  import { configureCyncLightAccessory } from './cync/cync-light-accessory.js';
7
7
  import { configureCyncSwitchAccessory } from './cync/cync-switch-accessory.js';
8
8
  import { configureCyncOutletAccessory } from './cync/cync-outlet-accessory.js';
9
+ import { configureCyncFanAccessory } from './cync/cync-fan-accessory.js';
10
+ import { classifyCyncDevice } from './cync/device-classifier.js';
9
11
  const toCyncLogger = (log) => ({
10
12
  debug: log.debug.bind(log),
11
13
  info: log.info.bind(log),
12
14
  warn: log.warn.bind(log),
13
15
  error: log.error.bind(log),
14
16
  });
15
- // Known Cync device types that should be treated as Lightbulb accessories
16
- const CYNC_LIGHT_DEVICE_TYPES = new Set([
17
- 1, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 17, 18, 19, 20, 21,
18
- 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
19
- 37, 46, 47, 48, 49, 55, 56, 80, 81, 82, 83, 85, 123, 128, 129, 130,
20
- 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
21
- 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
22
- 155, 156, 158, 159, 160, 161, 162, 163, 164, 165, 166, 169,
23
- 170, 171,
24
- ]);
25
- function isCyncLightDeviceType(deviceType) {
26
- return deviceType !== undefined
27
- && CYNC_LIGHT_DEVICE_TYPES.has(deviceType);
28
- }
29
- // Known Cync device types that should be treated as Outlet accessories
30
- const CYNC_OUTLET_DEVICE_TYPES = new Set([
31
- 64, 65, 66, 67, 68, 172,
32
- ]);
33
- function isCyncOutletDeviceType(deviceType) {
34
- return deviceType !== undefined
35
- && CYNC_OUTLET_DEVICE_TYPES.has(deviceType);
36
- }
37
- function getDefaultCapabilitiesForDeviceType(deviceType) {
38
- const isLight = isCyncLightDeviceType(deviceType);
39
- // Conservative defaults: only claim what you can prove.
40
- // We'll promote color/brightness once LAN proves it.
17
+ function getDefaultCapabilitiesForDeviceType() {
18
+ const isLight = false;
41
19
  return {
42
20
  isLight,
43
21
  supportsBrightness: false,
@@ -116,18 +94,19 @@ export class CyncAppPlatform {
116
94
  const Service = this.api.hap.Service;
117
95
  const Characteristic = this.api.hap.Characteristic;
118
96
  const lightService = accessory.getService(Service.Lightbulb);
97
+ const fanService = accessory.getService(Service.Fanv2);
119
98
  const outletService = accessory.getService(Service.Outlet);
120
99
  const switchService = accessory.getService(Service.Switch);
121
- const primaryService = lightService || outletService || switchService;
100
+ const primaryService = lightService || fanService || outletService || switchService;
122
101
  if (!primaryService) {
123
- this.log.debug('Cync: accessory %s has no Lightbulb, Outlet, or Switch service for deviceId=%s', accessory.displayName, update.deviceId);
102
+ this.log.debug('Cync: accessory %s has no Lightbulb, Fan, Outlet, or Switch service for deviceId=%s', accessory.displayName, update.deviceId);
124
103
  return;
125
104
  }
126
105
  // Update cached context state
127
106
  const ctx = accessory.context;
128
107
  ctx.cync = ctx.cync ?? { meshId: '', deviceId: update.deviceId };
129
108
  if (!ctx.cync.capabilities) {
130
- ctx.cync.capabilities = getDefaultCapabilitiesForDeviceType(ctx.cync.deviceType);
109
+ ctx.cync.capabilities = getDefaultCapabilitiesForDeviceType();
131
110
  }
132
111
  const promoted = promoteCapabilitiesFromLan(ctx.cync.capabilities, update);
133
112
  if (promoted) {
@@ -137,10 +116,28 @@ export class CyncAppPlatform {
137
116
  if (typeof update.on === 'boolean') {
138
117
  ctx.cync.on = update.on;
139
118
  this.log.info('Cync: LAN update -> %s is now %s (deviceId=%s)', accessory.displayName, update.on ? 'ON' : 'OFF', update.deviceId);
140
- primaryService.updateCharacteristic(Characteristic.On, update.on);
119
+ if (lightService || outletService || switchService) {
120
+ primaryService.updateCharacteristic(Characteristic.On, update.on);
121
+ }
141
122
  if (outletService && outletService.testCharacteristic(Characteristic.OutletInUse)) {
142
123
  outletService.updateCharacteristic(Characteristic.OutletInUse, update.on);
143
124
  }
125
+ if (fanService && fanService.testCharacteristic(Characteristic.Active)) {
126
+ fanService.updateCharacteristic(Characteristic.Active, update.on ? Characteristic.Active.ACTIVE : Characteristic.Active.INACTIVE);
127
+ }
128
+ }
129
+ // ---- Fan Speed ----
130
+ if (fanService) {
131
+ let speedPct;
132
+ if (typeof update.brightnessPct === 'number' && Number.isFinite(update.brightnessPct)) {
133
+ speedPct = Math.max(0, Math.min(100, Math.round(update.brightnessPct)));
134
+ }
135
+ if (speedPct !== undefined) {
136
+ ctx.cync.brightness = speedPct;
137
+ if (fanService.testCharacteristic(Characteristic.RotationSpeed)) {
138
+ fanService.updateCharacteristic(Characteristic.RotationSpeed, speedPct);
139
+ }
140
+ }
144
141
  }
145
142
  // ----- Brightness -----
146
143
  if (lightService) {
@@ -312,15 +309,31 @@ export class CyncAppPlatform {
312
309
  // Optional safety net (accessory modules also register this)
313
310
  this.deviceIdToAccessory.set(deviceId, accessory);
314
311
  const deviceType = resolveDeviceType(device);
312
+ const classification = classifyCyncDevice(device, deviceType);
313
+ this.log.debug('Cync device classification: ' +
314
+ `name="${deviceName}" ` +
315
+ `deviceId=${deviceId} ` +
316
+ `deviceType=${classification.deviceType ?? 'unknown'} ` +
317
+ `capabilities=${classification.capabilities.join(',') || 'none'} ` +
318
+ `accessoryType=${classification.accessoryType} ` +
319
+ `reason="${classification.reason}"`);
315
320
  const deviceTypeStr = typeof deviceType === 'number' ? String(deviceType) : 'unknown';
316
- if (isCyncLightDeviceType(deviceType)) {
321
+ if (classification.accessoryType === 'fan') {
322
+ this.log.info('Cync: configuring %s as Fan (deviceType=%s, deviceId=%s)', deviceName, deviceTypeStr, deviceId);
323
+ configureCyncFanAccessory(this.accessoryEnv, mesh, device, accessory, deviceName, deviceId);
324
+ }
325
+ else if (classification.accessoryType === 'light') {
317
326
  this.log.info('Cync: configuring %s as Lightbulb (deviceType=%s, deviceId=%s)', deviceName, deviceTypeStr, deviceId);
318
327
  configureCyncLightAccessory(this.accessoryEnv, mesh, device, accessory, deviceName, deviceId);
319
328
  }
320
- else if (isCyncOutletDeviceType(deviceType)) {
329
+ else if (classification.accessoryType === 'outlet') {
321
330
  this.log.info('Cync: configuring %s as Outlet (deviceType=%s, deviceId=%s)', deviceName, deviceTypeStr, deviceId);
322
331
  configureCyncOutletAccessory(this.accessoryEnv, mesh, device, accessory, deviceName, deviceId);
323
332
  }
333
+ else if (classification.accessoryType === 'unsupported') {
334
+ this.log.warn('Cync: unsupported device %s (deviceType=%s, deviceId=%s); configuring as Switch fallback', deviceName, deviceTypeStr, deviceId);
335
+ configureCyncSwitchAccessory(this.accessoryEnv, mesh, device, accessory, deviceName, deviceId);
336
+ }
324
337
  else {
325
338
  this.log.info('Cync: configuring %s as Switch (deviceType=%s, deviceId=%s)', deviceName, deviceTypeStr, deviceId);
326
339
  configureCyncSwitchAccessory(this.accessoryEnv, mesh, device, accessory, deviceName, deviceId);
@@ -1 +1 @@
1
- {"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "homebridge-cync-app",
3
3
  "displayName": "Homebridge Cync App",
4
4
  "type": "module",
5
- "version": "0.3.4",
5
+ "version": "0.4.0",
6
6
  "private": false,
7
7
  "description": "Homebridge plugin that integrates your GE Cync account (via the Cync app/API) and exposes all supported devices: plugs, lights, switches, etc",
8
8
  "author": "Dustin Newell",
@@ -0,0 +1,165 @@
1
+ // src/cync/cync-fan-accessory.ts
2
+ import type { PlatformAccessory } from 'homebridge';
3
+ import type { CyncDevice, CyncDeviceMesh } from './config-client.js';
4
+ import type { CyncAccessoryContext, CyncAccessoryEnv } from './cync-accessory-helpers.js';
5
+ import {
6
+ applyAccessoryInformationFromCyncDevice,
7
+ resolveDeviceType,
8
+ } from './cync-accessory-helpers.js';
9
+
10
+ function clampFanSpeed(value: unknown): number {
11
+ const speed = Math.round(Number(value));
12
+
13
+ if (!Number.isFinite(speed)) {
14
+ return 0;
15
+ }
16
+
17
+ return Math.max(0, Math.min(100, speed));
18
+ }
19
+
20
+ // Fan Accessory Configurator: Exposes Cync fan switches as HomeKit Fanv2 accessories
21
+ export function configureCyncFanAccessory(
22
+ env: CyncAccessoryEnv,
23
+ mesh: CyncDeviceMesh,
24
+ device: CyncDevice,
25
+ accessory: PlatformAccessory,
26
+ deviceName: string,
27
+ deviceId: string,
28
+ ): void {
29
+ const Service = env.api.hap.Service;
30
+ const Characteristic = env.api.hap.Characteristic;
31
+
32
+ const existingLight = accessory.getService(Service.Lightbulb);
33
+ if (existingLight) {
34
+ env.log.info(
35
+ 'Cync: removing stale Lightbulb service from %s (deviceId=%s) before configuring as Fan',
36
+ deviceName,
37
+ deviceId,
38
+ );
39
+ accessory.removeService(existingLight);
40
+ }
41
+
42
+ const existingSwitch = accessory.getService(Service.Switch);
43
+ if (existingSwitch) {
44
+ env.log.info(
45
+ 'Cync: removing stale Switch service from %s (deviceId=%s) before configuring as Fan',
46
+ deviceName,
47
+ deviceId,
48
+ );
49
+ accessory.removeService(existingSwitch);
50
+ }
51
+
52
+ const existingOutlet = accessory.getService(Service.Outlet);
53
+ if (existingOutlet) {
54
+ env.log.info(
55
+ 'Cync: removing stale Outlet service from %s (deviceId=%s) before configuring as Fan',
56
+ deviceName,
57
+ deviceId,
58
+ );
59
+ accessory.removeService(existingOutlet);
60
+ }
61
+
62
+ const service =
63
+ accessory.getService(Service.Fanv2) ||
64
+ accessory.addService(Service.Fanv2, deviceName);
65
+
66
+ if (accessory.category !== env.api.hap.Categories.FAN) {
67
+ accessory.category = env.api.hap.Categories.FAN;
68
+ }
69
+
70
+ applyAccessoryInformationFromCyncDevice(env.api, accessory, device, deviceName, deviceId);
71
+
72
+ const ctx = accessory.context as CyncAccessoryContext;
73
+ ctx.cync = ctx.cync ?? {
74
+ meshId: mesh.id,
75
+ deviceId,
76
+ productId: device.product_id,
77
+ on: false,
78
+ };
79
+
80
+ const resolvedDeviceType = resolveDeviceType(device);
81
+ if (typeof resolvedDeviceType === 'number' && Number.isFinite(resolvedDeviceType)) {
82
+ ctx.cync.deviceType = resolvedDeviceType;
83
+ }
84
+
85
+ env.registerAccessoryForDevice(deviceId, accessory);
86
+ env.markDeviceSeen(deviceId);
87
+ env.startPollingDevice(deviceId);
88
+
89
+ service
90
+ .getCharacteristic(Characteristic.Active)
91
+ .onGet(() => {
92
+ return ctx.cync?.on ? Characteristic.Active.ACTIVE : Characteristic.Active.INACTIVE;
93
+ })
94
+ .onSet(async (value) => {
95
+ const cyncMeta = ctx.cync;
96
+
97
+ if (!cyncMeta?.deviceId) {
98
+ env.log.warn(
99
+ 'Cync: Fan Active.set called for %s but no cync.deviceId in context',
100
+ deviceName,
101
+ );
102
+ return;
103
+ }
104
+
105
+ const on = value === Characteristic.Active.ACTIVE;
106
+
107
+ cyncMeta.on = on;
108
+
109
+ try {
110
+ await env.tcpClient.setSwitchState(cyncMeta.deviceId, { on });
111
+ env.markDeviceSeen(cyncMeta.deviceId);
112
+ } catch (err) {
113
+ env.log.warn(
114
+ 'Cync: Fan Active.set failed for %s (deviceId=%s): %s',
115
+ deviceName,
116
+ cyncMeta.deviceId,
117
+ (err as Error).message ?? String(err),
118
+ );
119
+
120
+ throw new env.api.hap.HapStatusError(
121
+ env.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE,
122
+ );
123
+ }
124
+ });
125
+
126
+ service
127
+ .getCharacteristic(Characteristic.RotationSpeed)
128
+ .onGet(() => {
129
+ return typeof ctx.cync?.brightness === 'number'
130
+ ? ctx.cync.brightness
131
+ : ctx.cync?.on ? 100 : 0;
132
+ })
133
+ .onSet(async (value) => {
134
+ const cyncMeta = ctx.cync;
135
+
136
+ if (!cyncMeta?.deviceId) {
137
+ env.log.warn(
138
+ 'Cync: Fan RotationSpeed.set called for %s but no cync.deviceId in context',
139
+ deviceName,
140
+ );
141
+ return;
142
+ }
143
+
144
+ const speed = clampFanSpeed(value);
145
+
146
+ cyncMeta.brightness = speed;
147
+ cyncMeta.on = speed > 0;
148
+
149
+ try {
150
+ await env.tcpClient.setBrightness(cyncMeta.deviceId, speed, cyncMeta.deviceType);
151
+ env.markDeviceSeen(cyncMeta.deviceId);
152
+ } catch (err) {
153
+ env.log.warn(
154
+ 'Cync: Fan RotationSpeed.set failed for %s (deviceId=%s): %s',
155
+ deviceName,
156
+ cyncMeta.deviceId,
157
+ (err as Error).message ?? String(err),
158
+ );
159
+
160
+ throw new env.api.hap.HapStatusError(
161
+ env.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE,
162
+ );
163
+ }
164
+ });
165
+ }
@@ -0,0 +1,130 @@
1
+ // src/cync/device-classifier.ts
2
+ // Cync Device Classifier: Centralizes accessory type selection and classification logging context
3
+
4
+ type CyncAccessoryType = 'light' | 'fan' | 'outlet' | 'switch' | 'unsupported';
5
+
6
+ export interface CyncDeviceLike {
7
+ id?: string;
8
+ name?: string;
9
+ deviceType?: number;
10
+ type?: number;
11
+ capabilities?: string[] | Record<string, unknown>;
12
+ }
13
+
14
+ export interface CyncDeviceClassification {
15
+ accessoryType: CyncAccessoryType;
16
+ deviceType?: number;
17
+ capabilities: string[];
18
+ reason: string;
19
+ }
20
+
21
+ const CYNC_LIGHT_DEVICE_TYPES = new Set([
22
+ 1, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 17, 18, 19, 20, 21,
23
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
24
+ 37, 46, 47, 48, 49, 55, 56, 80, 82, 83, 85, 123, 128, 129, 130,
25
+ 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
26
+ 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
27
+ 155, 156, 158, 159, 160, 161, 162, 163, 164, 165, 166, 169,
28
+ 170, 171,
29
+ ]);
30
+
31
+ const CYNC_OUTLET_DEVICE_TYPES = new Set([
32
+ 64, 65, 66, 67, 68, 172,
33
+ ]);
34
+
35
+ const CYNC_FAN_DEVICE_TYPES = new Set([
36
+ 81,
37
+ ]);
38
+
39
+ function normalizeCapabilities(capabilities: CyncDeviceLike['capabilities']): string[] {
40
+ if (!capabilities) {
41
+ return [];
42
+ }
43
+
44
+ if (Array.isArray(capabilities)) {
45
+ return capabilities
46
+ .map(capability => capability.toLowerCase())
47
+ .sort();
48
+ }
49
+
50
+ return Object.keys(capabilities)
51
+ .map(capability => capability.toLowerCase())
52
+ .sort();
53
+ }
54
+
55
+ function getDeviceType(device: CyncDeviceLike): number | undefined {
56
+ return device.deviceType ?? device.type;
57
+ }
58
+
59
+ export function classifyCyncDevice(
60
+ device: CyncDeviceLike,
61
+ resolvedDeviceType?: number,
62
+ ): CyncDeviceClassification {
63
+ const deviceType = resolvedDeviceType ?? getDeviceType(device);
64
+ const capabilities = normalizeCapabilities(device.capabilities);
65
+
66
+ if (capabilities.includes('fan')) {
67
+ return {
68
+ accessoryType: 'fan',
69
+ deviceType,
70
+ capabilities,
71
+ reason: 'capability: fan',
72
+ };
73
+ }
74
+
75
+ if (
76
+ capabilities.includes('color') ||
77
+ capabilities.includes('colortemp') ||
78
+ capabilities.includes('brightness')
79
+ ) {
80
+ return {
81
+ accessoryType: 'light',
82
+ deviceType,
83
+ capabilities,
84
+ reason: 'capability: light control',
85
+ };
86
+ }
87
+
88
+ if (deviceType !== undefined && CYNC_FAN_DEVICE_TYPES.has(deviceType)) {
89
+ return {
90
+ accessoryType: 'fan',
91
+ deviceType,
92
+ capabilities,
93
+ reason: `deviceType: ${deviceType}`,
94
+ };
95
+ }
96
+
97
+ if (deviceType !== undefined && CYNC_LIGHT_DEVICE_TYPES.has(deviceType)) {
98
+ return {
99
+ accessoryType: 'light',
100
+ deviceType,
101
+ capabilities,
102
+ reason: `deviceType: ${deviceType}`,
103
+ };
104
+ }
105
+
106
+ if (deviceType !== undefined && CYNC_OUTLET_DEVICE_TYPES.has(deviceType)) {
107
+ return {
108
+ accessoryType: 'outlet',
109
+ deviceType,
110
+ capabilities,
111
+ reason: `deviceType: ${deviceType}`,
112
+ };
113
+ }
114
+
115
+ if (capabilities.includes('onoff')) {
116
+ return {
117
+ accessoryType: 'switch',
118
+ deviceType,
119
+ capabilities,
120
+ reason: 'capability: onoff fallback',
121
+ };
122
+ }
123
+
124
+ return {
125
+ accessoryType: 'unsupported',
126
+ deviceType,
127
+ capabilities,
128
+ reason: 'no supported capabilities or known deviceType',
129
+ };
130
+ }
package/src/platform.ts CHANGED
@@ -23,6 +23,8 @@ import {
23
23
  import { configureCyncLightAccessory } from './cync/cync-light-accessory.js';
24
24
  import { configureCyncSwitchAccessory } from './cync/cync-switch-accessory.js';
25
25
  import { configureCyncOutletAccessory } from './cync/cync-outlet-accessory.js';
26
+ import { configureCyncFanAccessory } from './cync/cync-fan-accessory.js';
27
+ import { classifyCyncDevice } from './cync/device-classifier.js';
26
28
 
27
29
  const toCyncLogger = (log: Logger): CyncLogger => ({
28
30
  debug: log.debug.bind(log),
@@ -31,37 +33,9 @@ const toCyncLogger = (log: Logger): CyncLogger => ({
31
33
  error: log.error.bind(log),
32
34
  });
33
35
 
34
- // Known Cync device types that should be treated as Lightbulb accessories
35
- const CYNC_LIGHT_DEVICE_TYPES = new Set([
36
- 1, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 17, 18, 19, 20, 21,
37
- 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
38
- 37, 46, 47, 48, 49, 55, 56, 80, 81, 82, 83, 85, 123, 128, 129, 130,
39
- 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
40
- 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
41
- 155, 156, 158, 159, 160, 161, 162, 163, 164, 165, 166, 169,
42
- 170, 171,
43
- ]);
44
-
45
- function isCyncLightDeviceType(deviceType: number | undefined): boolean {
46
- return deviceType !== undefined
47
- && CYNC_LIGHT_DEVICE_TYPES.has(deviceType);
48
- }
49
-
50
- // Known Cync device types that should be treated as Outlet accessories
51
- const CYNC_OUTLET_DEVICE_TYPES = new Set([
52
- 64, 65, 66, 67, 68, 172,
53
- ]);
54
-
55
- function isCyncOutletDeviceType(deviceType: number | undefined): boolean {
56
- return deviceType !== undefined
57
- && CYNC_OUTLET_DEVICE_TYPES.has(deviceType);
58
- }
59
-
60
- function getDefaultCapabilitiesForDeviceType(deviceType: number | undefined): CyncCapabilityProfile {
61
- const isLight = isCyncLightDeviceType(deviceType);
36
+ function getDefaultCapabilitiesForDeviceType(): CyncCapabilityProfile {
37
+ const isLight = false;
62
38
 
63
- // Conservative defaults: only claim what you can prove.
64
- // We'll promote color/brightness once LAN proves it.
65
39
  return {
66
40
  isLight,
67
41
  supportsBrightness: false,
@@ -163,13 +137,14 @@ export class CyncAppPlatform implements DynamicPlatformPlugin {
163
137
  const Characteristic = this.api.hap.Characteristic;
164
138
 
165
139
  const lightService = accessory.getService(Service.Lightbulb);
140
+ const fanService = accessory.getService(Service.Fanv2);
166
141
  const outletService = accessory.getService(Service.Outlet);
167
142
  const switchService = accessory.getService(Service.Switch);
168
- const primaryService = lightService || outletService || switchService;
143
+ const primaryService = lightService || fanService || outletService || switchService;
169
144
 
170
145
  if (!primaryService) {
171
146
  this.log.debug(
172
- 'Cync: accessory %s has no Lightbulb, Outlet, or Switch service for deviceId=%s',
147
+ 'Cync: accessory %s has no Lightbulb, Fan, Outlet, or Switch service for deviceId=%s',
173
148
  accessory.displayName,
174
149
  update.deviceId,
175
150
  );
@@ -180,7 +155,7 @@ export class CyncAppPlatform implements DynamicPlatformPlugin {
180
155
  const ctx = accessory.context as CyncAccessoryContext;
181
156
  ctx.cync = ctx.cync ?? { meshId: '', deviceId: update.deviceId };
182
157
  if (!ctx.cync.capabilities) {
183
- ctx.cync.capabilities = getDefaultCapabilitiesForDeviceType(ctx.cync.deviceType);
158
+ ctx.cync.capabilities = getDefaultCapabilitiesForDeviceType();
184
159
  }
185
160
  const promoted = promoteCapabilitiesFromLan(ctx.cync.capabilities, update);
186
161
  if (promoted) {
@@ -203,10 +178,34 @@ export class CyncAppPlatform implements DynamicPlatformPlugin {
203
178
  update.deviceId,
204
179
  );
205
180
 
206
- primaryService.updateCharacteristic(Characteristic.On, update.on);
181
+ if (lightService || outletService || switchService) {
182
+ primaryService.updateCharacteristic(Characteristic.On, update.on);
183
+ }
207
184
  if (outletService && outletService.testCharacteristic(Characteristic.OutletInUse)) {
208
185
  outletService.updateCharacteristic(Characteristic.OutletInUse, update.on);
209
186
  }
187
+ if (fanService && fanService.testCharacteristic(Characteristic.Active)) {
188
+ fanService.updateCharacteristic(
189
+ Characteristic.Active,
190
+ update.on ? Characteristic.Active.ACTIVE : Characteristic.Active.INACTIVE,
191
+ );
192
+ }
193
+ }
194
+ // ---- Fan Speed ----
195
+ if (fanService) {
196
+ let speedPct: number | undefined;
197
+
198
+ if (typeof update.brightnessPct === 'number' && Number.isFinite(update.brightnessPct)) {
199
+ speedPct = Math.max(0, Math.min(100, Math.round(update.brightnessPct)));
200
+ }
201
+
202
+ if (speedPct !== undefined) {
203
+ ctx.cync.brightness = speedPct;
204
+
205
+ if (fanService.testCharacteristic(Characteristic.RotationSpeed)) {
206
+ fanService.updateCharacteristic(Characteristic.RotationSpeed, speedPct);
207
+ }
208
+ }
210
209
  }
211
210
 
212
211
  // ----- Brightness -----
@@ -465,10 +464,36 @@ export class CyncAppPlatform implements DynamicPlatformPlugin {
465
464
  this.deviceIdToAccessory.set(deviceId, accessory);
466
465
 
467
466
  const deviceType = resolveDeviceType(device);
467
+ const classification = classifyCyncDevice(device, deviceType);
468
+
469
+ this.log.debug(
470
+ 'Cync device classification: ' +
471
+ `name="${deviceName}" ` +
472
+ `deviceId=${deviceId} ` +
473
+ `deviceType=${classification.deviceType ?? 'unknown'} ` +
474
+ `capabilities=${classification.capabilities.join(',') || 'none'} ` +
475
+ `accessoryType=${classification.accessoryType} ` +
476
+ `reason="${classification.reason}"`,
477
+ );
468
478
  const deviceTypeStr =
469
479
  typeof deviceType === 'number' ? String(deviceType) : 'unknown';
470
480
 
471
- if (isCyncLightDeviceType(deviceType)) {
481
+ if (classification.accessoryType === 'fan') {
482
+ this.log.info(
483
+ 'Cync: configuring %s as Fan (deviceType=%s, deviceId=%s)',
484
+ deviceName,
485
+ deviceTypeStr,
486
+ deviceId,
487
+ );
488
+ configureCyncFanAccessory(
489
+ this.accessoryEnv,
490
+ mesh,
491
+ device,
492
+ accessory,
493
+ deviceName,
494
+ deviceId,
495
+ );
496
+ } else if (classification.accessoryType === 'light') {
472
497
  this.log.info(
473
498
  'Cync: configuring %s as Lightbulb (deviceType=%s, deviceId=%s)',
474
499
  deviceName,
@@ -483,7 +508,7 @@ export class CyncAppPlatform implements DynamicPlatformPlugin {
483
508
  deviceName,
484
509
  deviceId,
485
510
  );
486
- } else if (isCyncOutletDeviceType(deviceType)) {
511
+ } else if (classification.accessoryType === 'outlet') {
487
512
  this.log.info(
488
513
  'Cync: configuring %s as Outlet (deviceType=%s, deviceId=%s)',
489
514
  deviceName,
@@ -498,6 +523,21 @@ export class CyncAppPlatform implements DynamicPlatformPlugin {
498
523
  deviceName,
499
524
  deviceId,
500
525
  );
526
+ } else if (classification.accessoryType === 'unsupported') {
527
+ this.log.warn(
528
+ 'Cync: unsupported device %s (deviceType=%s, deviceId=%s); configuring as Switch fallback',
529
+ deviceName,
530
+ deviceTypeStr,
531
+ deviceId,
532
+ );
533
+ configureCyncSwitchAccessory(
534
+ this.accessoryEnv,
535
+ mesh,
536
+ device,
537
+ accessory,
538
+ deviceName,
539
+ deviceId,
540
+ );
501
541
  } else {
502
542
  this.log.info(
503
543
  'Cync: configuring %s as Switch (deviceType=%s, deviceId=%s)',
@@ -1,142 +0,0 @@
1
- ---
2
- name: Add Support for a New Cync Device
3
- about: Request adding support for a Cync device
4
- title: Add Cync Device [MODEL]
5
- labels: enhancement
6
- assignees: dash16
7
-
8
- ---
9
-
10
- Thank you for helping expand device support in **homebridge-cync-app**.
11
-
12
- To add support for a new device model, I need diagnostic information from your plugin installation and the Cync app.
13
-
14
- Please fill out all sections below.
15
-
16
- ---
17
-
18
- ## 1. Device Information
19
-
20
- **1.1 Product name as shown in the Cync app**
21
-
22
- (e.g., “6" Recessed Can Retrofit Fixture (Matter)”, “Indoor Smart Plug (3-in-1)”, “Indoor Smart Plug”)
23
-
24
- **1.2 Product page URL (GE/Cync or retailer)**
25
-
26
- Link to the product page if available.
27
-
28
- Example:
29
- https://www.gelighting.com/smart-home/led-bulbs/soft-white
30
-
31
- **1.3 What kind of device is this?**
32
-
33
- * Plug (on/off only)
34
- * Dimmer
35
- * Tunable white
36
- * Full color light
37
- * Multi-zone light
38
- * Switch
39
- * Downlight
40
- * Other (describe)
41
-
42
- ---
43
-
44
- ## 2. Discovery Logs (Required)
45
-
46
- Please enable **Homebridge Debug Mode** (`-D` or toggle in the UI), restart Homebridge, and paste the **full debug block for this device**.
47
-
48
- Look for log lines like:
49
-
50
- ```
51
-
52
- [Cync App] Fetched device: {
53
- deviceId: …,
54
- productId: …,
55
- model: …,
56
- capabilities: …,
57
- raw: …
58
- }
59
-
60
- ```
61
-
62
- Paste them here (you may redact serial numbers if desired):
63
-
64
- ```
65
-
66
- <insert logs>
67
- ```
68
-
69
- ---
70
-
71
- ## 3. Cloud `/property` Responses
72
-
73
- To understand how the device reports state, please:
74
-
75
- 1. Turn the device **on**
76
- 2. Turn it **off**
77
- 3. Adjust **brightness** or **color** in the **Cync app**
78
-
79
- Then copy any log lines showing cloud property responses such as:
80
-
81
- ```
82
- [Cync App] [cloud] GET /product/.../device/.../property → …
83
- ```
84
-
85
- Paste them here:
86
-
87
- ```
88
- <insert logs>
89
- ```
90
-
91
- ---
92
-
93
- ## 4. LAN / TCP Logs (If Available)
94
-
95
- Some Cync devices support **LAN/TCP communication** for faster updates.
96
-
97
- If you see logs like `[Cync TCP] Connecting…`, please include examples such as:
98
-
99
- ```
100
- [Cync TCP][recv] …
101
- [Cync TCP][send] …
102
- ```
103
-
104
- Paste them here:
105
-
106
- ```
107
- <insert logs>
108
- ```
109
-
110
- If your device does **not** show TCP logs, say so:
111
-
112
- ```
113
- No TCP messages observed.
114
- ```
115
-
116
- ---
117
-
118
- ## 5. Screenshots from the **Cync App**
119
-
120
- Please attach screenshots **from the Cync mobile app (not Apple Home)** showing:
121
-
122
- * The **device settings page**
123
- * The **main control UI** (brightness / color / mode controls)
124
- * Any **advanced settings** available
125
-
126
- These help determine which capabilities the device supports.
127
-
128
- ---
129
-
130
- ## 6. Anything Else?
131
-
132
- If the device behaves in an unusual way (multiple endpoints, segmented lights, unusual controls, etc.), please describe it here.
133
-
134
- ```
135
- <notes>
136
- ```
137
-
138
- ---
139
-
140
- ## Thank You
141
-
142
- Once you submit this, I’ll analyze the device capabilities, determine the correct HomeKit service type, and add support to the plugin.