@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.
- package/CHANGELOG.md +5 -0
- package/LICENSE +176 -0
- package/README.md +151 -0
- package/config.schema.json +55 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/mutex.d.ts +61 -0
- package/dist/mutex.js +96 -0
- package/dist/mutex.js.map +1 -0
- package/dist/platform.d.ts +48 -0
- package/dist/platform.js +108 -0
- package/dist/platform.js.map +1 -0
- package/dist/platformAccessory.d.ts +309 -0
- package/dist/platformAccessory.js +542 -0
- package/dist/platformAccessory.js.map +1 -0
- package/dist/settings.d.ts +8 -0
- package/dist/settings.js +9 -0
- package/dist/settings.js.map +1 -0
- package/docs/fan-speed-slider.gif +0 -0
- package/docs/homebridge-create-logo.png +0 -0
- package/package.json +60 -0
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
import { Mutex } from './mutex.js';
|
|
2
|
+
import TuyAPI from 'tuyapi';
|
|
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 class CreateCeilingFanAccessory {
|
|
141
|
+
platform;
|
|
142
|
+
accessory;
|
|
143
|
+
fanService;
|
|
144
|
+
lightService;
|
|
145
|
+
getDeviceStatusTimer;
|
|
146
|
+
fanSetSpeedDebounceTimer = null;
|
|
147
|
+
deviceCommunicator;
|
|
148
|
+
mutex;
|
|
149
|
+
isGetStatusInProgress = false;
|
|
150
|
+
fanSetSpeedDebouncePeriod = 500;
|
|
151
|
+
getDeviceStatusFastRetryPeriod = 1000;
|
|
152
|
+
getDeviceStatusPeriod = 10000;
|
|
153
|
+
getDeviceStatusConnectTimeout = 3000;
|
|
154
|
+
getDeviceStatusReadTimeout = 1000;
|
|
155
|
+
fanRotationSpeedNormalized = [10, 30, 50, 70, 90, 100];
|
|
156
|
+
state = {
|
|
157
|
+
fanOn: false,
|
|
158
|
+
fanSpeed: 1,
|
|
159
|
+
fanRotationClockwise: false,
|
|
160
|
+
lightOn: false,
|
|
161
|
+
isValid: false,
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Constructor for the CreateCeilingFanAccessory object.
|
|
165
|
+
*
|
|
166
|
+
* @param platform The plugin platform object.
|
|
167
|
+
* @param accessory The homebridge platform accessory object.
|
|
168
|
+
*/
|
|
169
|
+
constructor(platform, accessory) {
|
|
170
|
+
this.platform = platform;
|
|
171
|
+
this.accessory = accessory;
|
|
172
|
+
// Set accessory information
|
|
173
|
+
this.accessory.getService(this.platform.Service.AccessoryInformation)
|
|
174
|
+
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'CREATE')
|
|
175
|
+
.setCharacteristic(this.platform.Characteristic.Model, accessory.context.device.name)
|
|
176
|
+
.setCharacteristic(this.platform.Characteristic.SerialNumber, accessory.context.device.id);
|
|
177
|
+
// Get the fan service if it exists, otherwise create a new service
|
|
178
|
+
this.fanService =
|
|
179
|
+
this.accessory.getService(this.platform.Service.Fanv2) ||
|
|
180
|
+
this.accessory.addService(this.platform.Service.Fanv2);
|
|
181
|
+
// Set the fan service name: this is what is displayed as the default name on the Home app
|
|
182
|
+
this.fanService.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.name);
|
|
183
|
+
// Register handlers for the fan Active (On/Off) Characteristic
|
|
184
|
+
this.fanService.getCharacteristic(this.platform.Characteristic.Active)
|
|
185
|
+
.onGet(this.handleFanActiveStateGet.bind(this))
|
|
186
|
+
.onSet(this.handleFanActiveStateSet.bind(this));
|
|
187
|
+
// Register handlers for the fan Rotation Speed Characteristic
|
|
188
|
+
this.fanService.getCharacteristic(this.platform.Characteristic.RotationSpeed)
|
|
189
|
+
.onGet(this.handleFanRotationSpeedGet.bind(this))
|
|
190
|
+
.onSet(this.handleFanRotationSpeedSet.bind(this));
|
|
191
|
+
// Set the properties of the fan RotationSpeed Characteristic
|
|
192
|
+
// Note: the fan has 6 different speed settings, which cannot be divided nicely into the 0-100% range of the UI
|
|
193
|
+
// slider that is displayed in the Home app. Therefore we allow the user to smoothly adjust the speed with the UI
|
|
194
|
+
// slider by setting the slider step size to 1, and we re-adjust the slider (after a debouncing timeout) to the
|
|
195
|
+
// value that corresponds to the fan speed (see `fanRotationSpeed` array).
|
|
196
|
+
this.fanService.getCharacteristic(this.platform.Characteristic.RotationSpeed)
|
|
197
|
+
.setProps({
|
|
198
|
+
minValue: 0,
|
|
199
|
+
maxValue: 100,
|
|
200
|
+
minStep: 1,
|
|
201
|
+
});
|
|
202
|
+
// Register handlers for the fan Rotation Direction Characteristic
|
|
203
|
+
this.fanService.getCharacteristic(this.platform.Characteristic.RotationDirection)
|
|
204
|
+
.onGet(this.handleFanRotationDirectionGet.bind(this))
|
|
205
|
+
.onSet(this.handleFanRotationDirectionSet.bind(this));
|
|
206
|
+
// Note: the light service is only created if the device is configured with the light option
|
|
207
|
+
if (this.accessory.context.device.hasLight) {
|
|
208
|
+
// Get the light service if it exists, otherwise create a new service
|
|
209
|
+
this.lightService =
|
|
210
|
+
this.accessory.getService(this.platform.Service.Lightbulb) ||
|
|
211
|
+
this.accessory.addService(this.platform.Service.Lightbulb);
|
|
212
|
+
// Set the light service name
|
|
213
|
+
this.lightService.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.name + ' Light');
|
|
214
|
+
// Register handlers for the light On/Off Characteristic
|
|
215
|
+
this.lightService.getCharacteristic(this.platform.Characteristic.On)
|
|
216
|
+
.onGet(this.handleLightOnGet.bind(this))
|
|
217
|
+
.onSet(this.handleLightOnSet.bind(this));
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
this.lightService =
|
|
221
|
+
this.accessory.getService(this.platform.Service.Lightbulb);
|
|
222
|
+
if (this.lightService) {
|
|
223
|
+
this.accessory.removeService(this.lightService);
|
|
224
|
+
this.platform.log.info('Stale light service has been removed');
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Create the device communicator object
|
|
228
|
+
this.deviceCommunicator = new TuyAPI({
|
|
229
|
+
id: this.accessory.context.device.id,
|
|
230
|
+
key: this.accessory.context.device.key,
|
|
231
|
+
ip: this.accessory.context.device.ip,
|
|
232
|
+
version: this.accessory.context.device.protocolVersion,
|
|
233
|
+
issueGetOnConnect: false,
|
|
234
|
+
});
|
|
235
|
+
this.deviceCommunicator.on('error', (error) => {
|
|
236
|
+
this.platform.log.info('Error during device communication:', error);
|
|
237
|
+
});
|
|
238
|
+
// Create the mutex to prevent concurrent communication with the device
|
|
239
|
+
this.mutex = new Mutex();
|
|
240
|
+
// Start the periodic get status timer
|
|
241
|
+
this.getDeviceStatusTimer = setTimeout(() => this.getDeviceStatus(), this.getDeviceStatusPeriod);
|
|
242
|
+
this.platform.log.info('Accessory initialized: %s (device id: %s)', this.accessory.context.device.name, this.accessory.context.device.id);
|
|
243
|
+
this.platform.log.debug('Accessory details:', this.accessory.context.device);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Handle the get requests from HomeKit to get the current value of the Fanv2 Active characteristic
|
|
247
|
+
*
|
|
248
|
+
* @return The Fanv2 Active characteristic value from the device state cache.
|
|
249
|
+
*/
|
|
250
|
+
async handleFanActiveStateGet() {
|
|
251
|
+
this.getDeviceStatus();
|
|
252
|
+
this.throwErrorIfDeviceUnresponsive();
|
|
253
|
+
return this.fanActiveValueToCharacteristicValue();
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Handle the set requests from HomeKit to set the device with the Fanv2 Active characteristic value
|
|
257
|
+
*
|
|
258
|
+
* @param value The Fanv2 Active characteristic value to be set.
|
|
259
|
+
*/
|
|
260
|
+
async handleFanActiveStateSet(value) {
|
|
261
|
+
this.platform.log.debug('Set Fan Active:', value);
|
|
262
|
+
await this.setDeviceValue(60, (value === this.platform.Characteristic.Active.ACTIVE) ? true : false);
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Handle the get requests from HomeKit to get the current value of the Fanv2 Rotation Speed characteristic
|
|
266
|
+
*
|
|
267
|
+
* @return The Fanv2 Rotation Speed characteristic value from the device state cache.
|
|
268
|
+
*/
|
|
269
|
+
async handleFanRotationSpeedGet() {
|
|
270
|
+
this.getDeviceStatus();
|
|
271
|
+
this.throwErrorIfDeviceUnresponsive();
|
|
272
|
+
return this.fanRotationSpeedValueToCharacteristicValue();
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Handle the set requests from HomeKit to set the device with the Fanv2 Rotation Speed characteristic value
|
|
276
|
+
*
|
|
277
|
+
* @param value The Fanv2 Rotation Speed characteristic value to be set.
|
|
278
|
+
*/
|
|
279
|
+
async handleFanRotationSpeedSet(value) {
|
|
280
|
+
this.platform.log.debug('Set RotationSpeed input:', value);
|
|
281
|
+
const adjustedValue = this.adjustInputRotationSpeed(Number(value));
|
|
282
|
+
// Stop any existing debounce timer
|
|
283
|
+
if (this.fanSetSpeedDebounceTimer) {
|
|
284
|
+
clearTimeout(this.fanSetSpeedDebounceTimer);
|
|
285
|
+
}
|
|
286
|
+
if (adjustedValue !== 0) {
|
|
287
|
+
this.fanService.updateCharacteristic(this.platform.Characteristic.RotationSpeed, adjustedValue);
|
|
288
|
+
this.platform.log.debug('Set RotationSpeed adjusted:', adjustedValue);
|
|
289
|
+
// Start a new debounce timer
|
|
290
|
+
this.fanSetSpeedDebounceTimer = setTimeout(async () => {
|
|
291
|
+
const dpsRotationSpeed = this.fanRotationSpeedNormalized.indexOf(adjustedValue) + 1;
|
|
292
|
+
this.platform.log.debug('Set RotationSpeed debounced, setting DPS value:', dpsRotationSpeed);
|
|
293
|
+
await this.setDeviceValue(62, dpsRotationSpeed);
|
|
294
|
+
}, this.fanSetSpeedDebouncePeriod);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Handle the get requests from HomeKit to get the current value of the Fanv2 Rotation Direction characteristic
|
|
299
|
+
*
|
|
300
|
+
* @return The Fanv2 Rotation Direction characteristic value from the device state cache.
|
|
301
|
+
*/
|
|
302
|
+
async handleFanRotationDirectionGet() {
|
|
303
|
+
this.getDeviceStatus();
|
|
304
|
+
this.throwErrorIfDeviceUnresponsive();
|
|
305
|
+
return this.fanRotationDirectionValueToCharacteristicValue();
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Handle the set requests from HomeKit to set the device with the Fanv2 Rotation Direction characteristic value
|
|
309
|
+
*
|
|
310
|
+
* @param value The Fanv2 Rotation Direction characteristic value to be set.
|
|
311
|
+
*/
|
|
312
|
+
async handleFanRotationDirectionSet(value) {
|
|
313
|
+
this.platform.log.debug('Set Fan RotationDirection:', (value === this.platform.Characteristic.RotationDirection.COUNTER_CLOCKWISE) ? 'COUNTER_CLOCKWISE' : 'CLOCKWISE');
|
|
314
|
+
await this.setDeviceValue(63, (value === this.platform.Characteristic.RotationDirection.COUNTER_CLOCKWISE) ? 'forward' : 'reverse');
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Handle the get requests from HomeKit to get the current value of the Lightbulb On characteristic
|
|
318
|
+
*
|
|
319
|
+
* @return The Lightbulb On characteristic value from the device state cache.
|
|
320
|
+
*/
|
|
321
|
+
async handleLightOnGet() {
|
|
322
|
+
this.getDeviceStatus();
|
|
323
|
+
this.throwErrorIfDeviceUnresponsive();
|
|
324
|
+
return this.lightOnValueToCharacteristicValue();
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Handle the set requests from HomeKit to set the device with the Lightbulb On characteristic value
|
|
328
|
+
*
|
|
329
|
+
* @param value The Lightbulb On characteristic value to be set.
|
|
330
|
+
*/
|
|
331
|
+
async handleLightOnSet(value) {
|
|
332
|
+
this.platform.log.debug('Set Light On:', value);
|
|
333
|
+
await this.setDeviceValue(20, value);
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Get device status.
|
|
337
|
+
*
|
|
338
|
+
* This method reads the status of the device periodically and updates the
|
|
339
|
+
* accessory state after completing the operation. The actual communication
|
|
340
|
+
* is carried out with a timeout mechanism, so that an unresponsive device
|
|
341
|
+
* will not make the plugin and HomeKit unresponsive. If the communication
|
|
342
|
+
* fails, the periodic timer will be set with a shorter timeout so that the
|
|
343
|
+
* next retry attempt happens fast. After the communication with the device
|
|
344
|
+
* is recovered, the period will be reset to the original period value.
|
|
345
|
+
*
|
|
346
|
+
* The function can be called in two ways:
|
|
347
|
+
*
|
|
348
|
+
* 1. Calling it with `await`: the method starts the reading operation and
|
|
349
|
+
* waits (i.e. blocks) until the reading has been completed (or timed out).
|
|
350
|
+
* 2. Simply calling it (without `await`): the method starts the reading
|
|
351
|
+
* operation and returns right afterwards, not waiting for the reading
|
|
352
|
+
* to be completed. This is used when handling accessory GET requests
|
|
353
|
+
* from HomeKit. Get requests should return as fast as possible, because
|
|
354
|
+
* long delays will result in HomeKit being unresponsive and a bad user
|
|
355
|
+
* experience in general.
|
|
356
|
+
*
|
|
357
|
+
* If the function is called again while there is already an ongoing read
|
|
358
|
+
* operation, the reading will simply be skipped - the accessory status will
|
|
359
|
+
* be updated anyway after executing the already ongoing reading operation.
|
|
360
|
+
* This can happen e.g. when a periodic read operation is already in place
|
|
361
|
+
* and a GET request arrives from HomeKit at the same time.
|
|
362
|
+
*/
|
|
363
|
+
async getDeviceStatus() {
|
|
364
|
+
this.platform.log.debug('[getDeviceStatus] --- Start');
|
|
365
|
+
let isCommunicationError = false;
|
|
366
|
+
clearTimeout(this.getDeviceStatusTimer);
|
|
367
|
+
if (!this.isGetStatusInProgress) {
|
|
368
|
+
this.isGetStatusInProgress = true;
|
|
369
|
+
const releaseMutex = await this.mutex.lock();
|
|
370
|
+
this.platform.log.debug(' * Mutex granted for reading, connecting to device...');
|
|
371
|
+
try {
|
|
372
|
+
await this.waitForPromiseWithTimeout(this.deviceCommunicator.connect(), this.getDeviceStatusConnectTimeout);
|
|
373
|
+
this.platform.log.debug(' * Device connected, reading...');
|
|
374
|
+
const status = await this.waitForPromiseWithTimeout(this.deviceCommunicator.get({ schema: true }), this.getDeviceStatusReadTimeout);
|
|
375
|
+
this.platform.log.debug(' * Status:', status);
|
|
376
|
+
this.updateDeviceState(status);
|
|
377
|
+
this.state.isValid = true;
|
|
378
|
+
this.updateAccessoryState();
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
this.platform.log.info(' *', error);
|
|
382
|
+
this.deviceCommunicator.disconnect();
|
|
383
|
+
this.state.isValid = false;
|
|
384
|
+
isCommunicationError = true;
|
|
385
|
+
}
|
|
386
|
+
finally {
|
|
387
|
+
this.isGetStatusInProgress = false;
|
|
388
|
+
releaseMutex();
|
|
389
|
+
this.platform.log.debug(' * Mutex unlocked after reading');
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
this.platform.log.debug(' * Get device status has already been in progress');
|
|
394
|
+
}
|
|
395
|
+
const nextGetStatusPeriod = (isCommunicationError)
|
|
396
|
+
? this.getDeviceStatusFastRetryPeriod
|
|
397
|
+
: this.getDeviceStatusPeriod;
|
|
398
|
+
this.getDeviceStatusTimer = setTimeout(() => this.getDeviceStatus(), nextGetStatusPeriod);
|
|
399
|
+
this.platform.log.debug(`[getDeviceStatus] --- Done. Next periodic getDeviceStatus in: ${nextGetStatusPeriod} ms`);
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Set device value.
|
|
403
|
+
*
|
|
404
|
+
* This method sends a command to the device to set the device into the
|
|
405
|
+
* required state. Only one parameter can be set at a time.
|
|
406
|
+
*
|
|
407
|
+
* @param dps The data point index of the device to be set.
|
|
408
|
+
* @param value The value to be set.
|
|
409
|
+
*/
|
|
410
|
+
async setDeviceValue(dps, value) {
|
|
411
|
+
this.platform.log.debug('[setDeviceStatus] --- Start');
|
|
412
|
+
const releaseMutex = await this.mutex.lock();
|
|
413
|
+
this.platform.log.debug(' * Mutex granted for sending, connecting to device...');
|
|
414
|
+
try {
|
|
415
|
+
await this.deviceCommunicator.connect();
|
|
416
|
+
this.platform.log.debug(' * Device connected, sending...');
|
|
417
|
+
const status = await this.deviceCommunicator.set({ dps: dps, set: value });
|
|
418
|
+
this.platform.log.debug(' * Status:', status);
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
this.platform.log.debug(' *', error);
|
|
422
|
+
this.deviceCommunicator.disconnect();
|
|
423
|
+
}
|
|
424
|
+
finally {
|
|
425
|
+
releaseMutex();
|
|
426
|
+
this.platform.log.debug(' * Mutex unlocked after sending');
|
|
427
|
+
}
|
|
428
|
+
this.platform.log.debug('[setDeviceStatus] --- Done');
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Wait for a promise to be resolved within a given timeout.
|
|
432
|
+
*
|
|
433
|
+
* @param promise The promise to be resolved within a given timeout.
|
|
434
|
+
* @param ms The timeout in [ms] within the promise should be resolved.
|
|
435
|
+
* @return The resolved promise if it gets resolved within the timeout, otherwise reject the promise.
|
|
436
|
+
*/
|
|
437
|
+
async waitForPromiseWithTimeout(promise, ms) {
|
|
438
|
+
const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error(`Waiting for promise timed out after ${ms} ms`)), ms));
|
|
439
|
+
return Promise.race([promise, timeout]);
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Throw a HomeKit No Response status if the accessory state is invalid.
|
|
443
|
+
*/
|
|
444
|
+
throwErrorIfDeviceUnresponsive() {
|
|
445
|
+
if (!this.state.isValid) {
|
|
446
|
+
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Update the accessory state with the values received from the device.
|
|
451
|
+
*
|
|
452
|
+
* @param status The raw data points object received from the device.
|
|
453
|
+
*/
|
|
454
|
+
updateDeviceState(status) {
|
|
455
|
+
this.state.fanOn = status.dps['60'];
|
|
456
|
+
this.state.fanSpeed = status.dps['62'];
|
|
457
|
+
this.state.fanRotationClockwise = (status.dps['63'] === 'forward') ? false : true;
|
|
458
|
+
this.state.lightOn = status.dps['20'];
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Update all accessory service characteristics based on the accessory state.
|
|
462
|
+
*/
|
|
463
|
+
updateAccessoryState() {
|
|
464
|
+
this.fanService.updateCharacteristic(this.platform.Characteristic.Active, this.fanActiveValueToCharacteristicValue());
|
|
465
|
+
this.fanService.updateCharacteristic(this.platform.Characteristic.RotationSpeed, this.fanRotationSpeedValueToCharacteristicValue());
|
|
466
|
+
this.fanService.updateCharacteristic(this.platform.Characteristic.RotationDirection, this.fanRotationDirectionValueToCharacteristicValue());
|
|
467
|
+
if (this.lightService) {
|
|
468
|
+
this.lightService.updateCharacteristic(this.platform.Characteristic.On, this.lightOnValueToCharacteristicValue());
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Convert the fan active state of the accessory to Fanv2 Active characteristic value.
|
|
473
|
+
*
|
|
474
|
+
* @return The Fanv2 Active characteristic value.
|
|
475
|
+
*/
|
|
476
|
+
fanActiveValueToCharacteristicValue() {
|
|
477
|
+
return (this.state.fanOn)
|
|
478
|
+
? this.platform.Characteristic.Active.ACTIVE
|
|
479
|
+
: this.platform.Characteristic.Active.INACTIVE;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Convert the fan rotation speed of the accessory to Fanv2 Rotation Speed characteristic value.
|
|
483
|
+
*
|
|
484
|
+
* @return The Fanv2 Rotation Speed characteristic value.
|
|
485
|
+
*/
|
|
486
|
+
fanRotationSpeedValueToCharacteristicValue() {
|
|
487
|
+
return this.fanRotationSpeedNormalized[this.state.fanSpeed - 1];
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Convert the fan rotation direction of the accessory to Fanv2 Rotation Direction characteristic value.
|
|
491
|
+
*
|
|
492
|
+
* @return The Fanv2 Rotation Direction characteristic value.
|
|
493
|
+
*/
|
|
494
|
+
fanRotationDirectionValueToCharacteristicValue() {
|
|
495
|
+
return (this.state.fanRotationClockwise)
|
|
496
|
+
? this.platform.Characteristic.RotationDirection.CLOCKWISE
|
|
497
|
+
: this.platform.Characteristic.RotationDirection.COUNTER_CLOCKWISE;
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Convert the light on state of the accessory to Lightbulb On characteristic value.
|
|
501
|
+
*
|
|
502
|
+
* @return The Lightbulb On characteristic value.
|
|
503
|
+
*/
|
|
504
|
+
lightOnValueToCharacteristicValue() {
|
|
505
|
+
return this.state.lightOn;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Adjust the rotation speed input value.
|
|
509
|
+
*
|
|
510
|
+
* This method is used for converting the input state of the slider value to
|
|
511
|
+
* the nearest value that corresponds to the device fan speed.
|
|
512
|
+
*
|
|
513
|
+
* See the {@link CreateCeilingFanAccessory} class description for more details.
|
|
514
|
+
*
|
|
515
|
+
* @param value The input value of the rotation speed.
|
|
516
|
+
* @return The adjusted value of the rotation speed.
|
|
517
|
+
*/
|
|
518
|
+
adjustInputRotationSpeed(value) {
|
|
519
|
+
if (value === 0) {
|
|
520
|
+
return 0;
|
|
521
|
+
}
|
|
522
|
+
if (value < 20) {
|
|
523
|
+
return this.fanRotationSpeedNormalized[0];
|
|
524
|
+
}
|
|
525
|
+
else if (value < 40) {
|
|
526
|
+
return this.fanRotationSpeedNormalized[1];
|
|
527
|
+
}
|
|
528
|
+
else if (value < 60) {
|
|
529
|
+
return this.fanRotationSpeedNormalized[2];
|
|
530
|
+
}
|
|
531
|
+
else if (value < 80) {
|
|
532
|
+
return this.fanRotationSpeedNormalized[3];
|
|
533
|
+
}
|
|
534
|
+
else if (value < 95) {
|
|
535
|
+
return this.fanRotationSpeedNormalized[4];
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
return this.fanRotationSpeedNormalized[5];
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
//# sourceMappingURL=platformAccessory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platformAccessory.js","sourceRoot":"","sources":["../src/platformAccessory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This is the name of the platform that users will use to register the plugin in the Homebridge config.json
|
|
3
|
+
*/
|
|
4
|
+
export declare const PLATFORM_NAME = "CreateCeilingFanPlatform";
|
|
5
|
+
/**
|
|
6
|
+
* This must match the name of your plugin as defined the package.json `name` property
|
|
7
|
+
*/
|
|
8
|
+
export declare const PLUGIN_NAME = "homebridge-create-ceiling-fan";
|
package/dist/settings.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This is the name of the platform that users will use to register the plugin in the Homebridge config.json
|
|
3
|
+
*/
|
|
4
|
+
export const PLATFORM_NAME = 'CreateCeilingFanPlatform';
|
|
5
|
+
/**
|
|
6
|
+
* This must match the name of your plugin as defined the package.json `name` property
|
|
7
|
+
*/
|
|
8
|
+
export const PLUGIN_NAME = 'homebridge-create-ceiling-fan';
|
|
9
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,0BAA0B,CAAC;AAExD;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,+BAA+B,CAAC"}
|
|
Binary file
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@akospasztor/homebridge-create-ceiling-fan",
|
|
3
|
+
"displayName": "Homebridge Create Ceiling Fan",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"private": false,
|
|
7
|
+
"description": "Homebridge plugin for CREATE ceiling fans",
|
|
8
|
+
"author": "Akos Pasztor",
|
|
9
|
+
"license": "Apache-2.0",
|
|
10
|
+
"homepage": "https://github.com/akospasztor/homebridge-create-ceiling-fan#readme",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/akospasztor/homebridge-create-ceiling-fan.git"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/akospasztor/homebridge-create-ceiling-fan/issues"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"homebridge-plugin",
|
|
20
|
+
"ceiling fan",
|
|
21
|
+
"fan",
|
|
22
|
+
"create",
|
|
23
|
+
"wind calm",
|
|
24
|
+
"windcalm",
|
|
25
|
+
"tuya"
|
|
26
|
+
],
|
|
27
|
+
"main": "dist/index.js",
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"docs",
|
|
31
|
+
"config.schema.json",
|
|
32
|
+
"CHANGELOG.md"
|
|
33
|
+
],
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": "^18.20.4 || ^20.18.0 || ^22.10.0",
|
|
36
|
+
"homebridge": "^1.8.0 || ^2.0.0-beta.0"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "rimraf ./dist && tsc",
|
|
40
|
+
"docs": "typedoc",
|
|
41
|
+
"lint": "eslint . --max-warnings=0",
|
|
42
|
+
"prepublishOnly": "npm run lint && npm run build",
|
|
43
|
+
"watch": "npm run build && npm link && nodemon"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"tuyapi": "^7.7.1"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@eslint/js": "^9.21.0",
|
|
50
|
+
"@types/node": "^22.13.5",
|
|
51
|
+
"eslint": "^9.21.0",
|
|
52
|
+
"homebridge": "^2.0.0-beta.0",
|
|
53
|
+
"nodemon": "^3.1.9",
|
|
54
|
+
"rimraf": "^6.0.1",
|
|
55
|
+
"ts-node": "^10.9.2",
|
|
56
|
+
"typedoc": "^0.28.7",
|
|
57
|
+
"typescript": "^5.7.3",
|
|
58
|
+
"typescript-eslint": "^8.24.1"
|
|
59
|
+
}
|
|
60
|
+
}
|