@homebridge-plugins/homebridge-wemo 7.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 +809 -0
- package/LICENSE +21 -0
- package/README.md +66 -0
- package/config.schema.json +814 -0
- package/eslint.config.js +50 -0
- package/lib/connection/http.js +174 -0
- package/lib/connection/upnp.js +155 -0
- package/lib/device/coffee.js +167 -0
- package/lib/device/crockpot.js +380 -0
- package/lib/device/dimmer.js +280 -0
- package/lib/device/heater.js +416 -0
- package/lib/device/humidifier.js +379 -0
- package/lib/device/index.js +39 -0
- package/lib/device/insight.js +353 -0
- package/lib/device/lightswitch.js +154 -0
- package/lib/device/link-bulb.js +467 -0
- package/lib/device/link-hub.js +63 -0
- package/lib/device/maker-garage.js +426 -0
- package/lib/device/maker-switch.js +202 -0
- package/lib/device/motion.js +148 -0
- package/lib/device/outlet.js +148 -0
- package/lib/device/purifier.js +468 -0
- package/lib/device/simulation/purifier-insight.js +357 -0
- package/lib/device/simulation/purifier.js +159 -0
- package/lib/device/simulation/switch-insight.js +315 -0
- package/lib/device/simulation/switch.js +154 -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 +315 -0
- package/lib/homebridge-ui/server.js +10 -0
- package/lib/index.js +8 -0
- package/lib/platform.js +1423 -0
- package/lib/utils/colour.js +135 -0
- package/lib/utils/constants.js +129 -0
- package/lib/utils/eve-chars.js +130 -0
- package/lib/utils/functions.js +52 -0
- package/lib/utils/lang-en.js +125 -0
- package/package.json +70 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import platformConsts from '../utils/constants.js'
|
|
2
|
+
import { hasProperty, parseError } from '../utils/functions.js'
|
|
3
|
+
import platformLang from '../utils/lang-en.js'
|
|
4
|
+
|
|
5
|
+
export default class {
|
|
6
|
+
constructor(platform, accessory) {
|
|
7
|
+
// Set up variables from the platform
|
|
8
|
+
this.eveChar = platform.eveChar
|
|
9
|
+
this.hapChar = platform.api.hap.Characteristic
|
|
10
|
+
this.hapServ = platform.api.hap.Service
|
|
11
|
+
this.platform = platform
|
|
12
|
+
|
|
13
|
+
// Set up variables from the accessory
|
|
14
|
+
this.accessory = accessory
|
|
15
|
+
|
|
16
|
+
// Set up custom variables for this device type
|
|
17
|
+
const deviceConf = platform.deviceConf[accessory.context.serialNumber] || {}
|
|
18
|
+
this.noMotionTimer = deviceConf.noMotionTimer || platformConsts.defaultValues.noMotionTimer
|
|
19
|
+
|
|
20
|
+
// Add the motion sensor service if it doesn't already exist
|
|
21
|
+
this.service = this.accessory.getService(this.hapServ.MotionSensor)
|
|
22
|
+
|| this.accessory.addService(this.hapServ.MotionSensor)
|
|
23
|
+
|
|
24
|
+
// Pass the accessory to fakegato to set up the Eve info service
|
|
25
|
+
this.accessory.historyService = new platform.eveService('motion', this.accessory, {
|
|
26
|
+
log: () => {},
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
// Output the customised options to the log
|
|
30
|
+
const opts = JSON.stringify({
|
|
31
|
+
noMotionTimer: this.noMotionTimer,
|
|
32
|
+
})
|
|
33
|
+
platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
|
|
34
|
+
|
|
35
|
+
// Request a device update immediately
|
|
36
|
+
this.requestDeviceUpdate()
|
|
37
|
+
|
|
38
|
+
// Start a polling interval if the user has disabled upnp
|
|
39
|
+
if (this.accessory.context.connection === 'http') {
|
|
40
|
+
this.pollingInterval = setInterval(
|
|
41
|
+
() => this.requestDeviceUpdate(),
|
|
42
|
+
platform.config.pollingInterval * 1000,
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
receiveDeviceUpdate(attribute) {
|
|
48
|
+
// Log the receiving update if debug is enabled
|
|
49
|
+
this.accessory.logDebug(`${platformLang.recUpd} [${attribute.name}: ${JSON.stringify(attribute.value)}]`)
|
|
50
|
+
|
|
51
|
+
// Send a HomeKit needed true/false argument
|
|
52
|
+
// attribute.value is 1 if and only if motion is detected
|
|
53
|
+
this.externalUpdate(attribute.value === 1)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async requestDeviceUpdate() {
|
|
57
|
+
try {
|
|
58
|
+
// Request the update
|
|
59
|
+
const data = await this.platform.httpClient.sendDeviceUpdate(
|
|
60
|
+
this.accessory,
|
|
61
|
+
'urn:Belkin:service:basicevent:1',
|
|
62
|
+
'GetBinaryState',
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
// Check for existence since BinaryState can be int 0
|
|
66
|
+
if (hasProperty(data, 'BinaryState')) {
|
|
67
|
+
// Send the data to the receiver function
|
|
68
|
+
this.receiveDeviceUpdate({
|
|
69
|
+
name: 'BinaryState',
|
|
70
|
+
value: Number.parseInt(data.BinaryState, 10),
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
} catch (err) {
|
|
74
|
+
const eText = parseError(err, [
|
|
75
|
+
platformLang.timeout,
|
|
76
|
+
platformLang.timeoutUnreach,
|
|
77
|
+
platformLang.noService,
|
|
78
|
+
])
|
|
79
|
+
this.accessory.logDebugWarn(`${platformLang.rduErr} ${eText}`)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
externalUpdate(value) {
|
|
84
|
+
try {
|
|
85
|
+
// Obtain the previous state of the motion sensor
|
|
86
|
+
const prevState = this.service.getCharacteristic(this.hapChar.MotionDetected).value
|
|
87
|
+
|
|
88
|
+
// Don't continue in the following cases:
|
|
89
|
+
// (1) the previous state is the same as before and the motion timer isn't running
|
|
90
|
+
// (2) the new value is 'no motion detected' but the motion timer is still running
|
|
91
|
+
if ((value === prevState && !this.motionTimer) || (!value && this.motionTimer)) {
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Next logic depends on two cases
|
|
96
|
+
if (value || this.noMotionTimer === 0) {
|
|
97
|
+
// CASE: new motion detected or the user motion timer is set to 0 seconds
|
|
98
|
+
// If a motion timer is already present then stop it
|
|
99
|
+
if (this.motionTimer) {
|
|
100
|
+
this.accessory.log(platformLang.timerStopped)
|
|
101
|
+
clearTimeout(this.motionTimer)
|
|
102
|
+
this.motionTimer = false
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Update the HomeKit characteristics
|
|
106
|
+
this.service.updateCharacteristic(this.hapChar.MotionDetected, value)
|
|
107
|
+
|
|
108
|
+
// Add the entry to Eve
|
|
109
|
+
this.accessory.historyService.addEntry({ status: value ? 1 : 0 })
|
|
110
|
+
|
|
111
|
+
// If motion detected then update the LastActivation Eve characteristic
|
|
112
|
+
if (value) {
|
|
113
|
+
this.service.updateCharacteristic(
|
|
114
|
+
this.eveChar.LastActivation,
|
|
115
|
+
Math.round(new Date().valueOf() / 1000) - this.accessory.historyService.getInitialTime(),
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Log the change if appropriate
|
|
120
|
+
this.accessory.log(`${platformLang.motionSensor} [${value ? platformLang.motionYes : platformLang.motionNo}]`)
|
|
121
|
+
} else {
|
|
122
|
+
// CASE: motion not detected and the user motion timer is more than 0 seconds
|
|
123
|
+
this.accessory.log(`${platformLang.timerStarted} [${this.noMotionTimer}s]`)
|
|
124
|
+
|
|
125
|
+
// Clear any existing timers
|
|
126
|
+
clearTimeout(this.motionTimer)
|
|
127
|
+
|
|
128
|
+
// Create a new 'no motion timer'
|
|
129
|
+
this.motionTimer = setTimeout(() => {
|
|
130
|
+
// Update the HomeKit characteristic to false
|
|
131
|
+
this.service.updateCharacteristic(this.hapChar.MotionDetected, false)
|
|
132
|
+
|
|
133
|
+
// Add a no motion detected value to Eve
|
|
134
|
+
this.accessory.historyService.addEntry({ status: 0 })
|
|
135
|
+
|
|
136
|
+
// Log the change if appropriate
|
|
137
|
+
this.accessory.log(`${platformLang.motionSensor} [${platformLang.motionNo}] [${platformLang.timerComplete}]`)
|
|
138
|
+
|
|
139
|
+
// Set the motion timer in use to false
|
|
140
|
+
this.motionTimer = false
|
|
141
|
+
}, this.noMotionTimer * 1000)
|
|
142
|
+
}
|
|
143
|
+
} catch (err) {
|
|
144
|
+
// Catch any errors
|
|
145
|
+
this.accessory.logWarn(`${platformLang.cantUpd} ${parseError(err)}`)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { hasProperty, parseError } from '../utils/functions.js'
|
|
2
|
+
import platformLang from '../utils/lang-en.js'
|
|
3
|
+
|
|
4
|
+
export default class {
|
|
5
|
+
constructor(platform, accessory) {
|
|
6
|
+
// Set up variables from the platform
|
|
7
|
+
this.hapChar = platform.api.hap.Characteristic
|
|
8
|
+
this.hapErr = platform.api.hap.HapStatusError
|
|
9
|
+
this.hapServ = platform.api.hap.Service
|
|
10
|
+
this.platform = platform
|
|
11
|
+
|
|
12
|
+
// Set up variables from the accessory
|
|
13
|
+
this.accessory = accessory
|
|
14
|
+
|
|
15
|
+
// If the accessory has a switch service then remove it
|
|
16
|
+
if (this.accessory.getService(this.hapServ.Switch)) {
|
|
17
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.Switch))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// If the accessory has an air purifier service then remove it
|
|
21
|
+
if (this.accessory.getService(this.hapServ.AirPurifier)) {
|
|
22
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.AirPurifier))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Add the outlet service if it doesn't already exist
|
|
26
|
+
this.service = this.accessory.getService(this.hapServ.Outlet)
|
|
27
|
+
|| this.accessory.addService(this.hapServ.Outlet)
|
|
28
|
+
|
|
29
|
+
// Add the set handler to the outlet on/off characteristic
|
|
30
|
+
this.service
|
|
31
|
+
.getCharacteristic(this.hapChar.On)
|
|
32
|
+
.removeOnSet()
|
|
33
|
+
.onSet(async value => this.internalStateUpdate(value))
|
|
34
|
+
|
|
35
|
+
// Remove the outlet-in-use characteristic as it only matches the state in this case
|
|
36
|
+
if (this.service.testCharacteristic(this.hapChar.OutletInUse)) {
|
|
37
|
+
this.service.removeCharacteristic(this.service.getCharacteristic(this.hapChar.OutletInUse))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Output the customised options to the log
|
|
41
|
+
const opts = JSON.stringify({
|
|
42
|
+
showAs: 'outlet',
|
|
43
|
+
})
|
|
44
|
+
platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
|
|
45
|
+
|
|
46
|
+
// Request a device update immediately
|
|
47
|
+
this.requestDeviceUpdate()
|
|
48
|
+
|
|
49
|
+
// Start a polling interval if the user has disabled upnp
|
|
50
|
+
if (this.accessory.context.connection === 'http') {
|
|
51
|
+
this.pollingInterval = setInterval(
|
|
52
|
+
() => this.requestDeviceUpdate(),
|
|
53
|
+
platform.config.pollingInterval * 1000,
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
receiveDeviceUpdate(attribute) {
|
|
59
|
+
// Log the receiving update if debug is enabled
|
|
60
|
+
this.accessory.logDebug(`${platformLang.recUpd} [${attribute.name}: ${JSON.stringify(attribute.value)}]`)
|
|
61
|
+
|
|
62
|
+
// Send a HomeKit needed true/false argument
|
|
63
|
+
// attribute.value is 0 if and only if the outlet is off
|
|
64
|
+
this.externalStateUpdate(attribute.value !== 0)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async sendDeviceUpdate(value) {
|
|
68
|
+
// Log the sending update if debug is enabled
|
|
69
|
+
this.accessory.logDebug(`${platformLang.senUpd} ${JSON.stringify(value)}`)
|
|
70
|
+
|
|
71
|
+
// Send the update
|
|
72
|
+
await this.platform.httpClient.sendDeviceUpdate(
|
|
73
|
+
this.accessory,
|
|
74
|
+
'urn:Belkin:service:basicevent:1',
|
|
75
|
+
'SetBinaryState',
|
|
76
|
+
value,
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async requestDeviceUpdate() {
|
|
81
|
+
try {
|
|
82
|
+
// Request the update
|
|
83
|
+
const data = await this.platform.httpClient.sendDeviceUpdate(
|
|
84
|
+
this.accessory,
|
|
85
|
+
'urn:Belkin:service:basicevent:1',
|
|
86
|
+
'GetBinaryState',
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
// Check for existence since BinaryState can be int 0
|
|
90
|
+
if (hasProperty(data, 'BinaryState')) {
|
|
91
|
+
// Send the data to the receiver function
|
|
92
|
+
this.receiveDeviceUpdate({
|
|
93
|
+
name: 'BinaryState',
|
|
94
|
+
value: Number.parseInt(data.BinaryState, 10),
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
} catch (err) {
|
|
98
|
+
const eText = parseError(err, [
|
|
99
|
+
platformLang.timeout,
|
|
100
|
+
platformLang.timeoutUnreach,
|
|
101
|
+
platformLang.noService,
|
|
102
|
+
])
|
|
103
|
+
this.accessory.logDebugWarn(`${platformLang.rduErr} ${eText}`)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async internalStateUpdate(value) {
|
|
108
|
+
try {
|
|
109
|
+
// Send the update
|
|
110
|
+
await this.sendDeviceUpdate({
|
|
111
|
+
BinaryState: value ? 1 : 0,
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
// Update the cache value and log the change if appropriate
|
|
115
|
+
this.cacheState = value
|
|
116
|
+
this.accessory.log(`${platformLang.curState} [${value ? 'on' : 'off'}]`)
|
|
117
|
+
} catch (err) {
|
|
118
|
+
// Catch any errors
|
|
119
|
+
const eText = parseError(err, [platformLang.timeout, platformLang.timeoutUnreach])
|
|
120
|
+
this.accessory.logWarn(`${platformLang.cantCtl} ${eText}`)
|
|
121
|
+
|
|
122
|
+
// Throw a 'no response' error and set a timeout to revert this after 2 seconds
|
|
123
|
+
setTimeout(() => {
|
|
124
|
+
this.service.updateCharacteristic(this.hapChar.On, this.cacheState)
|
|
125
|
+
}, 2000)
|
|
126
|
+
throw new this.hapErr(-70402)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
externalStateUpdate(value) {
|
|
131
|
+
try {
|
|
132
|
+
// Check to see if the cache value is different
|
|
133
|
+
if (value === this.cacheState) {
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Update the HomeKit characteristics
|
|
138
|
+
this.service.updateCharacteristic(this.hapChar.On, value)
|
|
139
|
+
|
|
140
|
+
// Update the cache value and log the change if appropriate
|
|
141
|
+
this.cacheState = value
|
|
142
|
+
this.accessory.log(`${platformLang.curState} [${value ? 'on' : 'off'}]`)
|
|
143
|
+
} catch (err) {
|
|
144
|
+
// Catch any errors
|
|
145
|
+
this.accessory.logWarn(`${platformLang.cantUpd} ${parseError(err)}`)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|