@akospasztor/homebridge-create-ceiling-fan 1.0.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,108 @@
1
+ import { CreateCeilingFanAccessory } from './platformAccessory.js';
2
+ import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js';
3
+ /**
4
+ * CreateCeilingFanPlatform: the platform implementation for CREATE ceiling fans.
5
+ *
6
+ * This class contains the platform implementation and follows the Homebridge
7
+ * plugin development recommendations.
8
+ *
9
+ * @see https://developers.homebridge.io/#/
10
+ * @see https://github.com/homebridge/homebridge-plugin-template/blob/latest/README.md
11
+ */
12
+ export class CreateCeilingFanPlatform {
13
+ log;
14
+ config;
15
+ api;
16
+ /** The HomeKit Accessory Protocol (HAP) service. */
17
+ Service;
18
+ /** The HomeKit Accessory Protocol (HAP) characteristic. */
19
+ Characteristic;
20
+ /** Used for tracking the restored cached accessories. */
21
+ accessories = new Map();
22
+ /** Used for tracking the cached UUIDs. */
23
+ discoveredCacheUUIDs = [];
24
+ /**
25
+ * The CreateCeilingFanPlatform constructor.
26
+ *
27
+ * Note: This constructor is called by Homebridge.
28
+ *
29
+ * @param log The logging object.
30
+ * @param config The platform configuration object.
31
+ * @param api The API object.
32
+ */
33
+ constructor(log, config, api) {
34
+ this.log = log;
35
+ this.config = config;
36
+ this.api = api;
37
+ this.Service = api.hap.Service;
38
+ this.Characteristic = api.hap.Characteristic;
39
+ // When this event is fired, it means Homebridge has restored all cached
40
+ // accessories from the disk. Dynamic Platform plugins should only register
41
+ // new accessories after this event was fired, in order to ensure they
42
+ // weren't added to Homebridge already. This event can also be used to
43
+ // start discovery of new accessories.
44
+ this.api.on('didFinishLaunching', () => {
45
+ this.discoverDevices();
46
+ });
47
+ }
48
+ /**
49
+ * This function is invoked when Homebridge restores cached accessories from
50
+ * the disk at startup. It should be used to set up event handlers for
51
+ * characteristics and update respective values.
52
+ *
53
+ * @param accessory The accessory restored from the disk at startup.
54
+ */
55
+ configureAccessory(accessory) {
56
+ this.log.info('Loading accessory from cache:', accessory.displayName);
57
+ // Add the restored accessory to the accessories cache to track if it has
58
+ // already been registered
59
+ this.accessories.set(accessory.UUID, accessory);
60
+ }
61
+ /**
62
+ * Register discovered accessories.
63
+ *
64
+ * Accessories must only be registered once, previously created accessories
65
+ * must not be registered again to prevent "duplicate UUID" errors.
66
+ */
67
+ discoverDevices() {
68
+ // Loop over the devices and register each one if it has not already been registered
69
+ for (const device of this.config.devices) {
70
+ // Generate a unique id for the accessory
71
+ // Note: this should be generated from something globally unique, but
72
+ // constant, for example, the device serial number or MAC address
73
+ const uuid = this.api.hap.uuid.generate(device.id);
74
+ // See if an accessory with the same uuid has already been registered and restored
75
+ // from the cached devices we stored in the `configureAccessory` method above
76
+ const existingAccessory = this.accessories.get(uuid);
77
+ if (existingAccessory) {
78
+ this.log.info('Restoring existing accessory from cache: %s (device id: %s)', existingAccessory.displayName, existingAccessory.context.device.id);
79
+ // Update the `accessory.context` in case the plugin configuration (in the config.json) has changed
80
+ existingAccessory.context.device = device;
81
+ this.api.updatePlatformAccessories([existingAccessory]);
82
+ // Create the accessory handler for the restored accessory
83
+ new CreateCeilingFanAccessory(this, existingAccessory);
84
+ }
85
+ else {
86
+ this.log.info('Adding new accessory: %s (device id: %s)', device.name, device.id);
87
+ // Create a new accessory
88
+ const accessory = new this.api.platformAccessory(device.name, uuid);
89
+ // Store a copy of the device object in the `accessory.context`
90
+ accessory.context.device = device;
91
+ // Create the accessory handler for the newly created accessory
92
+ new CreateCeilingFanAccessory(this, accessory);
93
+ // Link the accessory to the platform
94
+ this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
95
+ }
96
+ // Push into discoveredCacheUUIDs
97
+ this.discoveredCacheUUIDs.push(uuid);
98
+ }
99
+ // Remove existing cached accessories from Homebridge if they are no longer present
100
+ for (const [uuid, accessory] of this.accessories) {
101
+ if (!this.discoveredCacheUUIDs.includes(uuid)) {
102
+ this.log.info('Removing existing accessory from cache: %s (device id: %s)', accessory.displayName, accessory.context.device.id);
103
+ this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
104
+ }
105
+ }
106
+ }
107
+ }
108
+ //# sourceMappingURL=platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,309 @@
1
+ import type { CharacteristicValue, PlatformAccessory } from 'homebridge';
2
+ import type { CreateCeilingFanPlatform } from './platform.js';
3
+ /**
4
+ * CreateCeilingFanAccessory: the accessory implementation for CREATE ceiling fans.
5
+ *
6
+ * This class contains the accessory implementation and follows the Homebridge
7
+ * plugin development recommendations.
8
+ *
9
+ * #### Exposed services and characteristics
10
+ *
11
+ * | Service | Characteristics |
12
+ * | ------------| ------------------------------------------------ |
13
+ * | `Fanv2` | `Active`, `Rotation Direction`, `Rotation Speed` |
14
+ * | `Lightbulb` | `On` |
15
+ *
16
+ * Note: the `Lightbulb` service is only exposed if the accessory has been
17
+ * configured with the light option enabled.
18
+ *
19
+ * #### Fan rotation speed representation
20
+ *
21
+ * CREATE fans have speed settings ranging from `1` (lowest) to `6` (highest).
22
+ * The HomeKit UI slider for the fan rotation speed provides a value from `1`
23
+ * to `100` and `0` means the fan is turned off. The slider steps can be
24
+ * configured to a desired value via the `Min Step` Characteristic. This works
25
+ * well e.g. for Dyson devices where the speed settings ranges from `1` to `10`.
26
+ * In this case the `Min Step` Characteristic can be configured to `10`,
27
+ * resulting in a nice user experience where the `0` - `100` UI slider is
28
+ * divided into 10 steps. However, this does not work well for CREATE fans with
29
+ * 6 speed steps. In order to provide smooth user experience, the following
30
+ * representation is implemented:
31
+ *
32
+ * - The UI slider is configured with the default `Min Step` Characteristic of
33
+ * `1`. This provides smooth and fluid user input and slider operation.
34
+ * - The following UI slider inputs represent the different speed settings of
35
+ * the fan:
36
+ * | Device fan speed | Corresponding UI slider value | User input range on the slider |
37
+ * | :--------------: | :---------------------------: | :----------------------------: |
38
+ * | 1 | 10 | 1 - 19 |
39
+ * | 2 | 30 | 20 - 39 |
40
+ * | 3 | 50 | 40 - 59 |
41
+ * | 4 | 70 | 60 - 79 |
42
+ * | 5 | 90 | 80 - 94 |
43
+ * | 6 | 100 | 95 - 100 |
44
+ *
45
+ * - When the user operates the slider, a debouncing timer with the value of
46
+ * {@link fanSetSpeedDebouncePeriod} member is set. When the timer fires, the
47
+ * current state of the slider value is converted to the nearest value that
48
+ * corresponds to the device fan speed. The purpose of the debounce timer is
49
+ * to provide great user experience. Without the debounce timer, every moment
50
+ * the user slides the finger would result in generating lots of events -
51
+ * making the slider jump around immediately, without waiting for the user to
52
+ * finish adjusting the speed.
53
+ *
54
+ * ```text
55
+ * UI slider │ Fan speed
56
+ * input value │ value
57
+ * ─────────────┼──────────────
58
+ * ┌──────┐ │
59
+ * │ 100 ─┼─────┼─► Fan speed 6
60
+ * │ │ ▲ │
61
+ * │ 95 ├─┘ │
62
+ * │ 94 ├─┐ │
63
+ * │ │ ▼ │
64
+ * │ 90 ─┼─────┼─► Fan speed 5
65
+ * │ │ ▲ │
66
+ * │ 80 ├─┘ │
67
+ * │ 79 ├─┐ │
68
+ * │ │ ▼ │
69
+ * │ 70 ─┼─────┼─► Fan speed 4
70
+ * │ │ ▲ │
71
+ * │ 60 ├─┘ │
72
+ * │ 59 ├─┐ │
73
+ * │ │ ▼ │
74
+ * │ 50 ─┼─────┼─► Fan speed 3
75
+ * │ │ ▲ │
76
+ * │ 40 ├─┘ │
77
+ * │ 39 ├─┐ │
78
+ * │ │ ▼ │
79
+ * │ 30 ─┼─────┼─► Fan speed 2
80
+ * │ │ ▲ │
81
+ * │ 20 ├─┘ │
82
+ * │ 19 ├─┐ │
83
+ * │ │ ▼ │
84
+ * │ 10 ─┼─────┼─► Fan speed 1
85
+ * │ │ ▲ │
86
+ * │ 1 ├─┘ │
87
+ * │ 0 ─┼─────┼─► Fan off
88
+ * └──────┘ │
89
+ * ```
90
+ *
91
+ * #### Fan rotation direction representation
92
+ *
93
+ * The rotation of the device as seen from standing below the fan follows the
94
+ * HomeKit rotation representation.
95
+ *
96
+ * | Device direction raw value | HomeKit representation | Fan operation |
97
+ * | -------------------------- | ---------------------- | --------------------------------------- |
98
+ * | `forward` (default) | Counter-clockwise | Fan blows downwards (i.e. summer mode) |
99
+ * | `reverse` | Clockwise | Fan blows upwards (i.e. winter mode) |
100
+ *
101
+ * #### Device communication
102
+ *
103
+ * The fan is a Tuya-compatible device and its firmware is mildly put: not the best.
104
+ * Among other issues, devices with these firmware are known to stop responding
105
+ * to commands, randomly drop connection, etc (see e.g.
106
+ * https://github.com/jasonacox/tinytuya/discussions/443 and
107
+ * https://github.com/moifort/homebridge-create-fan/issues/18).
108
+ *
109
+ * To provide reliable operation via HomeKit, this plugin implements the device
110
+ * communication the following way:
111
+ *
112
+ * - The device accepts one connection at a time. Therefore, a mutex with a
113
+ * waiting queue is used for ensuring that only one command is sent to the
114
+ * device at a time. For detailed description refer to: {@link Mutex}.
115
+ * - The device state is cached. Whenever the device state is requested by
116
+ * HomeKit (e.g. the user opens up the Home app), the implementation
117
+ * immediately returns the requested value from the cache.
118
+ * - The status of the device is periodically (defined by
119
+ * {@link getDeviceStatusPeriod}) queried from the device. After obtaining the
120
+ * device status, the device state cache as well as the HomeKit
121
+ * Characteristics are automatically updated.
122
+ * - If the device fails to respond to the get status request, the polling
123
+ * period is reduced significantly (defined by
124
+ * {@link getDeviceStatusFastRetryPeriod}), so that the attempt is retried
125
+ * quickly.
126
+ * - The communication with the device happens in a synchronous way, supported
127
+ * by the mutex queue and timeout mechanism. This seems to provide the most
128
+ * reliable communication with the device.
129
+ *
130
+ * Communication failure tends to happen when attempting to control the device
131
+ * via HomeKit right after using the device's physical remote. The device used
132
+ * for development & testing (CREATE Wind Calm model purchased in 2025) seemed
133
+ * to always recover from a failed communication latest after a few retry
134
+ * attempts.
135
+ *
136
+ * @see https://developers.homebridge.io/#/
137
+ * @see [Fanv2 service type](https://developers.homebridge.io/#/service/Fanv2)
138
+ * @see [Lightbulb service type](https://developers.homebridge.io/#/service/Lightbulb)
139
+ */
140
+ export declare class CreateCeilingFanAccessory {
141
+ private readonly platform;
142
+ private readonly accessory;
143
+ private fanService;
144
+ private lightService;
145
+ private getDeviceStatusTimer;
146
+ private fanSetSpeedDebounceTimer;
147
+ private deviceCommunicator;
148
+ private mutex;
149
+ private isGetStatusInProgress;
150
+ private readonly fanSetSpeedDebouncePeriod;
151
+ private readonly getDeviceStatusFastRetryPeriod;
152
+ private readonly getDeviceStatusPeriod;
153
+ private readonly getDeviceStatusConnectTimeout;
154
+ private readonly getDeviceStatusReadTimeout;
155
+ private readonly fanRotationSpeedNormalized;
156
+ private state;
157
+ /**
158
+ * Constructor for the CreateCeilingFanAccessory object.
159
+ *
160
+ * @param platform The plugin platform object.
161
+ * @param accessory The homebridge platform accessory object.
162
+ */
163
+ constructor(platform: CreateCeilingFanPlatform, accessory: PlatformAccessory);
164
+ /**
165
+ * Handle the get requests from HomeKit to get the current value of the Fanv2 Active characteristic
166
+ *
167
+ * @return The Fanv2 Active characteristic value from the device state cache.
168
+ */
169
+ handleFanActiveStateGet(): Promise<CharacteristicValue>;
170
+ /**
171
+ * Handle the set requests from HomeKit to set the device with the Fanv2 Active characteristic value
172
+ *
173
+ * @param value The Fanv2 Active characteristic value to be set.
174
+ */
175
+ handleFanActiveStateSet(value: CharacteristicValue): Promise<void>;
176
+ /**
177
+ * Handle the get requests from HomeKit to get the current value of the Fanv2 Rotation Speed characteristic
178
+ *
179
+ * @return The Fanv2 Rotation Speed characteristic value from the device state cache.
180
+ */
181
+ handleFanRotationSpeedGet(): Promise<CharacteristicValue>;
182
+ /**
183
+ * Handle the set requests from HomeKit to set the device with the Fanv2 Rotation Speed characteristic value
184
+ *
185
+ * @param value The Fanv2 Rotation Speed characteristic value to be set.
186
+ */
187
+ handleFanRotationSpeedSet(value: CharacteristicValue): Promise<void>;
188
+ /**
189
+ * Handle the get requests from HomeKit to get the current value of the Fanv2 Rotation Direction characteristic
190
+ *
191
+ * @return The Fanv2 Rotation Direction characteristic value from the device state cache.
192
+ */
193
+ handleFanRotationDirectionGet(): Promise<CharacteristicValue>;
194
+ /**
195
+ * Handle the set requests from HomeKit to set the device with the Fanv2 Rotation Direction characteristic value
196
+ *
197
+ * @param value The Fanv2 Rotation Direction characteristic value to be set.
198
+ */
199
+ handleFanRotationDirectionSet(value: CharacteristicValue): Promise<void>;
200
+ /**
201
+ * Handle the get requests from HomeKit to get the current value of the Lightbulb On characteristic
202
+ *
203
+ * @return The Lightbulb On characteristic value from the device state cache.
204
+ */
205
+ handleLightOnGet(): Promise<CharacteristicValue>;
206
+ /**
207
+ * Handle the set requests from HomeKit to set the device with the Lightbulb On characteristic value
208
+ *
209
+ * @param value The Lightbulb On characteristic value to be set.
210
+ */
211
+ handleLightOnSet(value: CharacteristicValue): Promise<void>;
212
+ /**
213
+ * Get device status.
214
+ *
215
+ * This method reads the status of the device periodically and updates the
216
+ * accessory state after completing the operation. The actual communication
217
+ * is carried out with a timeout mechanism, so that an unresponsive device
218
+ * will not make the plugin and HomeKit unresponsive. If the communication
219
+ * fails, the periodic timer will be set with a shorter timeout so that the
220
+ * next retry attempt happens fast. After the communication with the device
221
+ * is recovered, the period will be reset to the original period value.
222
+ *
223
+ * The function can be called in two ways:
224
+ *
225
+ * 1. Calling it with `await`: the method starts the reading operation and
226
+ * waits (i.e. blocks) until the reading has been completed (or timed out).
227
+ * 2. Simply calling it (without `await`): the method starts the reading
228
+ * operation and returns right afterwards, not waiting for the reading
229
+ * to be completed. This is used when handling accessory GET requests
230
+ * from HomeKit. Get requests should return as fast as possible, because
231
+ * long delays will result in HomeKit being unresponsive and a bad user
232
+ * experience in general.
233
+ *
234
+ * If the function is called again while there is already an ongoing read
235
+ * operation, the reading will simply be skipped - the accessory status will
236
+ * be updated anyway after executing the already ongoing reading operation.
237
+ * This can happen e.g. when a periodic read operation is already in place
238
+ * and a GET request arrives from HomeKit at the same time.
239
+ */
240
+ private getDeviceStatus;
241
+ /**
242
+ * Set device value.
243
+ *
244
+ * This method sends a command to the device to set the device into the
245
+ * required state. Only one parameter can be set at a time.
246
+ *
247
+ * @param dps The data point index of the device to be set.
248
+ * @param value The value to be set.
249
+ */
250
+ private setDeviceValue;
251
+ /**
252
+ * Wait for a promise to be resolved within a given timeout.
253
+ *
254
+ * @param promise The promise to be resolved within a given timeout.
255
+ * @param ms The timeout in [ms] within the promise should be resolved.
256
+ * @return The resolved promise if it gets resolved within the timeout, otherwise reject the promise.
257
+ */
258
+ private waitForPromiseWithTimeout;
259
+ /**
260
+ * Throw a HomeKit No Response status if the accessory state is invalid.
261
+ */
262
+ private throwErrorIfDeviceUnresponsive;
263
+ /**
264
+ * Update the accessory state with the values received from the device.
265
+ *
266
+ * @param status The raw data points object received from the device.
267
+ */
268
+ private updateDeviceState;
269
+ /**
270
+ * Update all accessory service characteristics based on the accessory state.
271
+ */
272
+ private updateAccessoryState;
273
+ /**
274
+ * Convert the fan active state of the accessory to Fanv2 Active characteristic value.
275
+ *
276
+ * @return The Fanv2 Active characteristic value.
277
+ */
278
+ private fanActiveValueToCharacteristicValue;
279
+ /**
280
+ * Convert the fan rotation speed of the accessory to Fanv2 Rotation Speed characteristic value.
281
+ *
282
+ * @return The Fanv2 Rotation Speed characteristic value.
283
+ */
284
+ private fanRotationSpeedValueToCharacteristicValue;
285
+ /**
286
+ * Convert the fan rotation direction of the accessory to Fanv2 Rotation Direction characteristic value.
287
+ *
288
+ * @return The Fanv2 Rotation Direction characteristic value.
289
+ */
290
+ private fanRotationDirectionValueToCharacteristicValue;
291
+ /**
292
+ * Convert the light on state of the accessory to Lightbulb On characteristic value.
293
+ *
294
+ * @return The Lightbulb On characteristic value.
295
+ */
296
+ private lightOnValueToCharacteristicValue;
297
+ /**
298
+ * Adjust the rotation speed input value.
299
+ *
300
+ * This method is used for converting the input state of the slider value to
301
+ * the nearest value that corresponds to the device fan speed.
302
+ *
303
+ * See the {@link CreateCeilingFanAccessory} class description for more details.
304
+ *
305
+ * @param value The input value of the rotation speed.
306
+ * @return The adjusted value of the rotation speed.
307
+ */
308
+ private adjustInputRotationSpeed;
309
+ }