@homebridge-plugins/homebridge-meross 10.8.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 +1346 -0
- package/LICENSE +21 -0
- package/README.md +68 -0
- package/config.schema.json +2066 -0
- package/eslint.config.js +49 -0
- package/lib/connection/http.js +345 -0
- package/lib/connection/mqtt.js +174 -0
- package/lib/device/baby.js +532 -0
- package/lib/device/cooler-single.js +447 -0
- package/lib/device/diffuser.js +730 -0
- package/lib/device/fan.js +530 -0
- package/lib/device/garage-main.js +225 -0
- package/lib/device/garage-single.js +495 -0
- package/lib/device/garage-sub.js +376 -0
- package/lib/device/heater-single.js +445 -0
- package/lib/device/hub-contact.js +56 -0
- package/lib/device/hub-leak.js +86 -0
- package/lib/device/hub-main.js +403 -0
- package/lib/device/hub-sensor.js +115 -0
- package/lib/device/hub-smoke.js +40 -0
- package/lib/device/hub-valve.js +377 -0
- package/lib/device/humidifier.js +521 -0
- package/lib/device/index.js +63 -0
- package/lib/device/light-cct.js +474 -0
- package/lib/device/light-dimmer.js +312 -0
- package/lib/device/light-rgb.js +528 -0
- package/lib/device/outlet-multi.js +383 -0
- package/lib/device/outlet-single.js +405 -0
- package/lib/device/power-strip.js +282 -0
- package/lib/device/purifier-single.js +372 -0
- package/lib/device/purifier.js +403 -0
- package/lib/device/roller-location.js +317 -0
- package/lib/device/roller.js +234 -0
- package/lib/device/sensor-presence.js +201 -0
- package/lib/device/switch-multi.js +403 -0
- package/lib/device/switch-single.js +371 -0
- package/lib/device/template.js +177 -0
- package/lib/device/thermostat.js +493 -0
- package/lib/fakegato/LICENSE +21 -0
- package/lib/fakegato/fakegato-history.js +814 -0
- package/lib/fakegato/fakegato-storage.js +108 -0
- package/lib/fakegato/fakegato-timer.js +125 -0
- package/lib/fakegato/uuid.js +27 -0
- package/lib/homebridge-ui/public/index.html +316 -0
- package/lib/homebridge-ui/server.js +10 -0
- package/lib/index.js +8 -0
- package/lib/platform.js +1256 -0
- package/lib/utils/colour.js +581 -0
- package/lib/utils/constants.js +377 -0
- package/lib/utils/custom-chars.js +165 -0
- package/lib/utils/eve-chars.js +130 -0
- package/lib/utils/functions.js +39 -0
- package/lib/utils/lang-en.js +114 -0
- package/package.json +70 -0
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import PQueue from 'p-queue'
|
|
2
|
+
import { TimeoutError } from 'p-timeout'
|
|
3
|
+
|
|
4
|
+
import mqttClient from '../connection/mqtt.js'
|
|
5
|
+
import platformConsts from '../utils/constants.js'
|
|
6
|
+
import { hasProperty, parseError } from '../utils/functions.js'
|
|
7
|
+
import platformLang from '../utils/lang-en.js'
|
|
8
|
+
|
|
9
|
+
export default class {
|
|
10
|
+
constructor(platform, accessory) {
|
|
11
|
+
// Set up variables from the platform
|
|
12
|
+
this.cusChar = platform.cusChar
|
|
13
|
+
this.hapChar = platform.api.hap.Characteristic
|
|
14
|
+
this.hapErr = platform.api.hap.HapStatusError
|
|
15
|
+
this.hapServ = platform.api.hap.Service
|
|
16
|
+
this.platform = platform
|
|
17
|
+
|
|
18
|
+
// Set up variables from the accessory
|
|
19
|
+
this.accessory = accessory
|
|
20
|
+
this.name = accessory.displayName
|
|
21
|
+
const cloudRefreshRate = hasProperty(platform.config, 'cloudRefreshRate')
|
|
22
|
+
? platform.config.cloudRefreshRate
|
|
23
|
+
: platformConsts.defaultValues.cloudRefreshRate
|
|
24
|
+
const localRefreshRate = hasProperty(platform.config, 'refreshRate')
|
|
25
|
+
? platform.config.refreshRate
|
|
26
|
+
: platformConsts.defaultValues.refreshRate
|
|
27
|
+
this.pollInterval = accessory.context.connection === 'local'
|
|
28
|
+
? localRefreshRate
|
|
29
|
+
: cloudRefreshRate
|
|
30
|
+
|
|
31
|
+
this.mode2Label = {
|
|
32
|
+
0: 'manual',
|
|
33
|
+
1: 'heat',
|
|
34
|
+
2: 'cool',
|
|
35
|
+
3: 'auto',
|
|
36
|
+
4: 'economy',
|
|
37
|
+
}
|
|
38
|
+
this.mode2Char = {
|
|
39
|
+
0: false,
|
|
40
|
+
1: this.cusChar.ValveHeatMode,
|
|
41
|
+
2: this.cusChar.ValveCoolMode,
|
|
42
|
+
3: this.cusChar.ValveAutoMode,
|
|
43
|
+
4: this.cusChar.ValveEconomyMode,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Add the thermostat service if it doesn't already exist
|
|
47
|
+
this.service = this.accessory.getService(this.hapServ.Thermostat)
|
|
48
|
+
|| this.accessory.addService(this.hapServ.Thermostat)
|
|
49
|
+
|
|
50
|
+
this.service
|
|
51
|
+
.getCharacteristic(this.hapChar.TargetHeatingCoolingState)
|
|
52
|
+
.setProps({
|
|
53
|
+
minValue: 0,
|
|
54
|
+
maxValue: 1,
|
|
55
|
+
validValues: [0, 1],
|
|
56
|
+
})
|
|
57
|
+
.onSet(async value => this.internalStateUpdate(value))
|
|
58
|
+
this.cacheState = this.service.getCharacteristic(this.hapChar.TargetHeatingCoolingState).value
|
|
59
|
+
|
|
60
|
+
this.service
|
|
61
|
+
.getCharacteristic(this.hapChar.TargetTemperature)
|
|
62
|
+
.setProps({
|
|
63
|
+
minValue: 5,
|
|
64
|
+
maxValue: 35,
|
|
65
|
+
minStep: 0.5,
|
|
66
|
+
})
|
|
67
|
+
.onSet(async value => this.internalTargetUpdate(value))
|
|
68
|
+
this.cacheTarg = this.service.getCharacteristic(this.hapChar.TargetTemperature).value
|
|
69
|
+
|
|
70
|
+
this.cacheTemp = this.service.getCharacteristic(this.hapChar.CurrentTemperature).value
|
|
71
|
+
this.updateCache()
|
|
72
|
+
|
|
73
|
+
if (!this.service.testCharacteristic(this.cusChar.ValveHeatMode)) {
|
|
74
|
+
this.service.addCharacteristic(this.cusChar.ValveHeatMode)
|
|
75
|
+
}
|
|
76
|
+
this.service
|
|
77
|
+
.getCharacteristic(this.cusChar.ValveHeatMode)
|
|
78
|
+
.onSet(async value => this.internalModeUpdate(value, 1))
|
|
79
|
+
if (!this.service.testCharacteristic(this.cusChar.ValveCoolMode)) {
|
|
80
|
+
this.service.addCharacteristic(this.cusChar.ValveCoolMode)
|
|
81
|
+
}
|
|
82
|
+
this.service
|
|
83
|
+
.getCharacteristic(this.cusChar.ValveCoolMode)
|
|
84
|
+
.onSet(async value => this.internalModeUpdate(value, 2))
|
|
85
|
+
if (!this.service.testCharacteristic(this.cusChar.ValveAutoMode)) {
|
|
86
|
+
this.service.addCharacteristic(this.cusChar.ValveAutoMode)
|
|
87
|
+
}
|
|
88
|
+
this.service
|
|
89
|
+
.getCharacteristic(this.cusChar.ValveAutoMode)
|
|
90
|
+
.onSet(async value => this.internalModeUpdate(value, 3))
|
|
91
|
+
if (!this.service.testCharacteristic(this.cusChar.ValveEconomyMode)) {
|
|
92
|
+
this.service.addCharacteristic(this.cusChar.ValveEconomyMode)
|
|
93
|
+
}
|
|
94
|
+
this.cacheMode = 0
|
|
95
|
+
this.service
|
|
96
|
+
.getCharacteristic(this.cusChar.ValveEconomyMode)
|
|
97
|
+
.onSet(async value => this.internalModeUpdate(value, 4))
|
|
98
|
+
if (!this.service.testCharacteristic(this.cusChar.ValveWindowOpen)) {
|
|
99
|
+
this.service.addCharacteristic(this.cusChar.ValveWindowOpen)
|
|
100
|
+
}
|
|
101
|
+
this.cacheWindow = this.service.getCharacteristic(this.cusChar.ValveWindowOpen).value
|
|
102
|
+
|
|
103
|
+
// Pass the accessory to Fakegato to set up with Eve
|
|
104
|
+
this.accessory.eveService = new platform.eveService('custom', this.accessory, { log: () => {} })
|
|
105
|
+
|
|
106
|
+
// Create the queue used for sending device requests
|
|
107
|
+
this.updateInProgress = false
|
|
108
|
+
this.queue = new PQueue({
|
|
109
|
+
concurrency: 1,
|
|
110
|
+
interval: 250,
|
|
111
|
+
intervalCap: 1,
|
|
112
|
+
timeout: 10000,
|
|
113
|
+
throwOnTimeout: true,
|
|
114
|
+
})
|
|
115
|
+
this.queue.on('idle', () => {
|
|
116
|
+
this.updateInProgress = false
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// Set up the mqtt client for cloud devices to send and receive device updates
|
|
120
|
+
if (accessory.context.connection !== 'local') {
|
|
121
|
+
this.accessory.mqtt = new mqttClient(platform, this.accessory)
|
|
122
|
+
this.accessory.mqtt.connect()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Always request a device update on startup, then start the interval for polling
|
|
126
|
+
setTimeout(() => this.requestUpdate(true), 2000)
|
|
127
|
+
this.accessory.refreshInterval = setInterval(
|
|
128
|
+
() => this.requestUpdate(),
|
|
129
|
+
this.pollInterval * 1000,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
// Output the customised options to the log
|
|
133
|
+
const opts = JSON.stringify({
|
|
134
|
+
connection: this.accessory.context.connection,
|
|
135
|
+
})
|
|
136
|
+
platform.log('[%s] %s %s.', this.name, platformLang.devInitOpts, opts)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async internalStateUpdate(value) {
|
|
140
|
+
try {
|
|
141
|
+
// Add the request to the queue so updates are sent apart
|
|
142
|
+
await this.queue.add(async () => {
|
|
143
|
+
// Don't continue if the state is the same as before
|
|
144
|
+
if (value === this.cacheState) {
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// This flag stops the plugin from requesting updates while pending on others
|
|
149
|
+
this.updateInProgress = true
|
|
150
|
+
|
|
151
|
+
// Generate the payload and namespace
|
|
152
|
+
const namespace = 'Appliance.Control.Thermostat.Mode'
|
|
153
|
+
const payload = {
|
|
154
|
+
mode: [
|
|
155
|
+
{
|
|
156
|
+
channel: 0,
|
|
157
|
+
onoff: value ? 1 : 0,
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Use the platform function to send the update to the device
|
|
163
|
+
await this.platform.sendUpdate(this.accessory, {
|
|
164
|
+
namespace,
|
|
165
|
+
payload,
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
// Update the cache and log the update has been successful
|
|
169
|
+
this.cacheState = value
|
|
170
|
+
this.accessory.log(`${platformLang.curState} [${value ? 'on' : 'off'}]`)
|
|
171
|
+
})
|
|
172
|
+
} catch (err) {
|
|
173
|
+
// Catch any errors whilst updating the device
|
|
174
|
+
const eText = err instanceof TimeoutError ? platformLang.timeout : parseError(err)
|
|
175
|
+
this.accessory.logWarn(`${platformLang.sendFailed} ${eText}`)
|
|
176
|
+
setTimeout(() => {
|
|
177
|
+
this.service.updateCharacteristic(this.hapChar.TargetHeatingCoolingState, this.cacheState)
|
|
178
|
+
}, 2000)
|
|
179
|
+
throw new this.hapErr(-70402)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async internalModeUpdate(value, newMode) {
|
|
184
|
+
try {
|
|
185
|
+
// If turning off then set to manual mode
|
|
186
|
+
if (!value) {
|
|
187
|
+
newMode = 0
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Add the request to the queue so updates are sent apart
|
|
191
|
+
await this.queue.add(async () => {
|
|
192
|
+
// Don't continue if the state is the same as before
|
|
193
|
+
if (newMode === this.cacheMode) {
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// This flag stops the plugin from requesting updates while pending on others
|
|
198
|
+
this.updateInProgress = true
|
|
199
|
+
|
|
200
|
+
// Generate the payload and namespace
|
|
201
|
+
const namespace = 'Appliance.Control.Thermostat.Mode'
|
|
202
|
+
const payload = {
|
|
203
|
+
mode: [
|
|
204
|
+
{
|
|
205
|
+
state: newMode,
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Use the platform function to send the update to the device
|
|
211
|
+
await this.platform.sendUpdate(this.accessory, {
|
|
212
|
+
namespace,
|
|
213
|
+
payload,
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
// Update the cache and log the update has been successful
|
|
217
|
+
this.cacheState = value
|
|
218
|
+
this.accessory.log(`${platformLang.curMode} [${this.mode2Label[newMode]}]`)
|
|
219
|
+
|
|
220
|
+
// Turn the other modes off
|
|
221
|
+
Object.entries(this.mode2Char).forEach((entry) => {
|
|
222
|
+
const [mode, char] = entry
|
|
223
|
+
if (char && mode !== newMode.toString()) {
|
|
224
|
+
this.service.updateCharacteristic(char, false)
|
|
225
|
+
}
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
} catch (err) {
|
|
229
|
+
// Catch any errors whilst updating the device
|
|
230
|
+
const eText = err instanceof TimeoutError ? platformLang.timeout : parseError(err)
|
|
231
|
+
this.accessory.logWarn(`${platformLang.sendFailed} ${eText}`)
|
|
232
|
+
setTimeout(() => {
|
|
233
|
+
this.service.updateCharacteristic(this.mode2Char[newMode], false)
|
|
234
|
+
}, 2000)
|
|
235
|
+
throw new this.hapErr(-70402)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async internalTargetUpdate(value) {
|
|
240
|
+
try {
|
|
241
|
+
// Add the request to the queue so updates are sent apart
|
|
242
|
+
await this.queue.add(async () => {
|
|
243
|
+
// Don't continue if the state is the same as before
|
|
244
|
+
if (value === this.cacheTarg) {
|
|
245
|
+
return
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// This flag stops the plugin from requesting updates while pending on others
|
|
249
|
+
this.updateInProgress = true
|
|
250
|
+
|
|
251
|
+
// Generate the payload and namespace
|
|
252
|
+
const namespace = 'Appliance.Control.Thermostat.Mode'
|
|
253
|
+
const payload = {
|
|
254
|
+
mode: [
|
|
255
|
+
{
|
|
256
|
+
channel: 0,
|
|
257
|
+
mode: 4,
|
|
258
|
+
manualTemp: value * 10,
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Use the platform function to send the update to the device
|
|
264
|
+
await this.platform.sendUpdate(this.accessory, {
|
|
265
|
+
namespace,
|
|
266
|
+
payload,
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
// Update the cache and log the update has been successful
|
|
270
|
+
this.cacheTarg = value
|
|
271
|
+
this.accessory.log(`${platformLang.curTarg} [${value}°C]`)
|
|
272
|
+
|
|
273
|
+
// Update the current heating state
|
|
274
|
+
this.service.updateCharacteristic(
|
|
275
|
+
this.hapChar.CurrentHeatingCoolingState,
|
|
276
|
+
value > this.cacheTemp ? 1 : 0,
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
// Turn the modes off as back to manual mode
|
|
280
|
+
Object.values(this.mode2Char).forEach((char) => {
|
|
281
|
+
if (char) {
|
|
282
|
+
this.service.updateCharacteristic(char, false)
|
|
283
|
+
}
|
|
284
|
+
})
|
|
285
|
+
})
|
|
286
|
+
} catch (err) {
|
|
287
|
+
// Catch any errors whilst updating the device
|
|
288
|
+
const eText = err instanceof TimeoutError ? platformLang.timeout : parseError(err)
|
|
289
|
+
this.accessory.logWarn(`${platformLang.sendFailed} ${eText}`)
|
|
290
|
+
setTimeout(() => {
|
|
291
|
+
this.service.updateCharacteristic(this.hapChar.TargetTemperature, this.cacheTarg)
|
|
292
|
+
}, 2000)
|
|
293
|
+
throw new this.hapErr(-70402)
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async updateCache() {
|
|
298
|
+
// Don't continue if the storage client hasn't initialised properly
|
|
299
|
+
if (!this.platform.storageClientData) {
|
|
300
|
+
return
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Attempt to save the new temperature to the cache
|
|
304
|
+
try {
|
|
305
|
+
await this.platform.storageData.setItem(
|
|
306
|
+
`${this.accessory.context.serialNumber}_temp`,
|
|
307
|
+
this.cacheTemp,
|
|
308
|
+
)
|
|
309
|
+
} catch (err) {
|
|
310
|
+
this.accessory.logWarn(`${platformLang.storageWriteErr} ${parseError(err)}`)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async requestUpdate(firstRun = false) {
|
|
315
|
+
try {
|
|
316
|
+
// Don't continue if an update is currently being sent to the device
|
|
317
|
+
if (this.updateInProgress) {
|
|
318
|
+
return
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Add the request to the queue so updates are sent apart
|
|
322
|
+
await this.queue.add(async () => {
|
|
323
|
+
// This flag stops the plugin from requesting updates while pending on others
|
|
324
|
+
this.updateInProgress = true
|
|
325
|
+
|
|
326
|
+
// Send the request
|
|
327
|
+
const res = await this.platform.sendUpdate(this.accessory, {
|
|
328
|
+
namespace: 'Appliance.System.All',
|
|
329
|
+
payload: {},
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
// Log the received data
|
|
333
|
+
this.accessory.logDebug(`${platformLang.incPoll}: ${JSON.stringify(res.data)}`)
|
|
334
|
+
|
|
335
|
+
// Check the response is in a useful format
|
|
336
|
+
const data = res.data.payload
|
|
337
|
+
if (data.all) {
|
|
338
|
+
if (data.all.digest?.thermostat) {
|
|
339
|
+
this.applyUpdate(data.all.digest.thermostat)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// A flag to check if we need to update the accessory context
|
|
343
|
+
let needsUpdate = false
|
|
344
|
+
|
|
345
|
+
// Get the mac address and hardware version of the device
|
|
346
|
+
if (data.all.system) {
|
|
347
|
+
// Mac address and hardware don't change regularly so only get on first poll
|
|
348
|
+
if (firstRun && data.all.system.hardware) {
|
|
349
|
+
this.accessory.context.macAddress = data.all.system.hardware.macAddress.toUpperCase()
|
|
350
|
+
this.accessory.context.hardware = data.all.system.hardware.version
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Get the ip address and firmware of the device
|
|
354
|
+
if (data.all.system.firmware) {
|
|
355
|
+
// Check for an IP change each and every time the device is polled
|
|
356
|
+
if (this.accessory.context.ipAddress !== data.all.system.firmware.innerIp) {
|
|
357
|
+
this.accessory.context.ipAddress = data.all.system.firmware.innerIp
|
|
358
|
+
needsUpdate = true
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Firmware doesn't change regularly so only get on first poll
|
|
362
|
+
if (firstRun) {
|
|
363
|
+
this.accessory.context.firmware = data.all.system.firmware.version
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Get the cloud online status of the device
|
|
369
|
+
if (data.all.system.online) {
|
|
370
|
+
const isOnline = data.all.system.online.status === 1
|
|
371
|
+
if (this.accessory.context.isOnline !== isOnline) {
|
|
372
|
+
this.accessory.context.isOnline = isOnline
|
|
373
|
+
needsUpdate = true
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Update the accessory cache if anything has changed
|
|
378
|
+
if (needsUpdate || firstRun) {
|
|
379
|
+
this.platform.updateAccessory(this.accessory)
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
})
|
|
383
|
+
} catch (err) {
|
|
384
|
+
const eText = err instanceof TimeoutError ? platformLang.timeout : parseError(err)
|
|
385
|
+
this.accessory.logDebugWarn(`${platformLang.reqFailed}: ${eText}`)
|
|
386
|
+
|
|
387
|
+
// Set the homebridge-ui status of the device to offline if local and error is timeout
|
|
388
|
+
if (
|
|
389
|
+
(this.accessory.context.isOnline || firstRun)
|
|
390
|
+
&& ['EHOSTUNREACH', 'timed out'].some(el => eText.includes(el))
|
|
391
|
+
) {
|
|
392
|
+
this.accessory.context.isOnline = false
|
|
393
|
+
this.platform.updateAccessory(this.accessory)
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
receiveUpdate(params) {
|
|
399
|
+
try {
|
|
400
|
+
// Log the received data
|
|
401
|
+
this.accessory.logDebug(`${platformLang.incMQTT}: ${JSON.stringify(params)}`)
|
|
402
|
+
if (params.payload) {
|
|
403
|
+
this.applyUpdate(params.payload)
|
|
404
|
+
}
|
|
405
|
+
} catch (err) {
|
|
406
|
+
this.accessory.logWarn(`${platformLang.refFailed} ${parseError(err)}`)
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
applyUpdate(data) {
|
|
411
|
+
try {
|
|
412
|
+
const modeData = data.mode?.[0]
|
|
413
|
+
if (modeData) {
|
|
414
|
+
let needsUpdate = false
|
|
415
|
+
if (hasProperty(modeData, 'state')) {
|
|
416
|
+
const newState = modeData.state
|
|
417
|
+
|
|
418
|
+
// Check against the cache and update HomeKit and the cache if needed
|
|
419
|
+
if (this.cacheState !== newState) {
|
|
420
|
+
this.service.updateCharacteristic(this.hapChar.TargetHeatingCoolingState, newState)
|
|
421
|
+
this.cacheState = newState
|
|
422
|
+
this.accessory.log(`${platformLang.curState} [${newState === 1 ? 'on' : 'off'}]`)
|
|
423
|
+
needsUpdate = true
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (hasProperty(modeData, 'targetTemp')) {
|
|
427
|
+
const newTarg = modeData.targetTemp / 10
|
|
428
|
+
|
|
429
|
+
// Check against the cache and update HomeKit and the cache if needed
|
|
430
|
+
if (this.cacheTarg !== newTarg) {
|
|
431
|
+
this.service.updateCharacteristic(this.hapChar.TargetTemperature, newTarg)
|
|
432
|
+
this.cacheTarg = newTarg
|
|
433
|
+
this.accessory.log(`${platformLang.curTarg} [${newTarg}°C]`)
|
|
434
|
+
needsUpdate = true
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (hasProperty(modeData, 'currentTemp')) {
|
|
438
|
+
const newTemp = modeData.currentTemp / 10
|
|
439
|
+
|
|
440
|
+
// Check against the cache and update HomeKit and the cache if needed
|
|
441
|
+
if (this.cacheTemp !== newTemp) {
|
|
442
|
+
this.service.updateCharacteristic(this.hapChar.CurrentTemperature, newTemp)
|
|
443
|
+
this.cacheTemp = newTemp
|
|
444
|
+
this.accessory.eveService.addEntry({ temp: newTemp })
|
|
445
|
+
this.accessory.log(`${platformLang.curTemp} [${newTemp}°C]`)
|
|
446
|
+
needsUpdate = true
|
|
447
|
+
|
|
448
|
+
// Update the cache file with the new temperature
|
|
449
|
+
this.updateCache()
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Update the current heating state
|
|
454
|
+
if (needsUpdate) {
|
|
455
|
+
this.service.updateCharacteristic(
|
|
456
|
+
this.hapChar.CurrentHeatingCoolingState,
|
|
457
|
+
this.cacheState === 1 && this.cacheTarg > this.cacheTemp ? 1 : 0,
|
|
458
|
+
)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Todo - data.openWindow and data.mode
|
|
462
|
+
// if (hasProperty(data, 'openWindow')) {
|
|
463
|
+
// const newWindow = data.openWindow === 1;
|
|
464
|
+
//
|
|
465
|
+
// // Check against the cache and update HomeKit and the cache if needed
|
|
466
|
+
// if (this.cacheWindow !== newWindow) {
|
|
467
|
+
// this.service.updateCharacteristic(this.cusChar.ValveWindowOpen, newWindow);
|
|
468
|
+
// this.cacheWindow = newWindow;
|
|
469
|
+
// this.accessory.log(`${platformLang.curWindow} [${newWindow ? 'open' : 'closed'}]`);
|
|
470
|
+
// }
|
|
471
|
+
// }
|
|
472
|
+
//
|
|
473
|
+
// if (hasProperty(data, 'mode')) {
|
|
474
|
+
// const newMode = data.mode;
|
|
475
|
+
//
|
|
476
|
+
// // Check against the cache and update HomeKit and the cache if needed
|
|
477
|
+
// if (this.cacheMode !== newMode) {
|
|
478
|
+
// Object.entries(this.mode2Char).forEach((entry) => {
|
|
479
|
+
// const [mode, char] = entry;
|
|
480
|
+
// if (char) {
|
|
481
|
+
// this.service.updateCharacteristic(char, mode === newMode.toString());
|
|
482
|
+
// }
|
|
483
|
+
// });
|
|
484
|
+
// this.cacheMode = newMode;
|
|
485
|
+
// this.accessory.log(`${platformLang.curMode} [${this.mode2Label[newMode]}]`);
|
|
486
|
+
// }
|
|
487
|
+
// }
|
|
488
|
+
}
|
|
489
|
+
} catch (err) {
|
|
490
|
+
this.accessory.logWarn(`${platformLang.refFailed} ${parseError(err)}`)
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017 simont77
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|