@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,135 @@
|
|
|
1
|
+
function hs2rgb(h, s) {
|
|
2
|
+
/*
|
|
3
|
+
Credit:
|
|
4
|
+
https://github.com/WickyNilliams/pure-color
|
|
5
|
+
*/
|
|
6
|
+
h = Number.parseInt(h, 10) / 60
|
|
7
|
+
s = Number.parseInt(s, 10) / 100
|
|
8
|
+
const f = h - Math.floor(h)
|
|
9
|
+
const p = 255 * (1 - s)
|
|
10
|
+
const q = 255 * (1 - s * f)
|
|
11
|
+
const t = 255 * (1 - s * (1 - f))
|
|
12
|
+
let rgb
|
|
13
|
+
switch (Math.floor(h) % 6) {
|
|
14
|
+
case 0:
|
|
15
|
+
rgb = [255, t, p]
|
|
16
|
+
break
|
|
17
|
+
case 1:
|
|
18
|
+
rgb = [q, 255, p]
|
|
19
|
+
break
|
|
20
|
+
case 2:
|
|
21
|
+
rgb = [p, 255, t]
|
|
22
|
+
break
|
|
23
|
+
case 3:
|
|
24
|
+
rgb = [p, q, 255]
|
|
25
|
+
break
|
|
26
|
+
case 4:
|
|
27
|
+
rgb = [t, p, 255]
|
|
28
|
+
break
|
|
29
|
+
case 5:
|
|
30
|
+
rgb = [255, p, q]
|
|
31
|
+
break
|
|
32
|
+
default:
|
|
33
|
+
return []
|
|
34
|
+
}
|
|
35
|
+
if (rgb[0] === 255 && rgb[1] <= 25 && rgb[2] <= 25) {
|
|
36
|
+
rgb[1] = 0
|
|
37
|
+
rgb[2] = 0
|
|
38
|
+
}
|
|
39
|
+
return [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2])]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function rgb2hs(r, g, b) {
|
|
43
|
+
/*
|
|
44
|
+
Credit:
|
|
45
|
+
https://github.com/WickyNilliams/pure-color
|
|
46
|
+
*/
|
|
47
|
+
r = Number.parseInt(r, 10)
|
|
48
|
+
g = Number.parseInt(g, 10)
|
|
49
|
+
b = Number.parseInt(b, 10)
|
|
50
|
+
const min = Math.min(r, g, b)
|
|
51
|
+
const max = Math.max(r, g, b)
|
|
52
|
+
const delta = max - min
|
|
53
|
+
let h
|
|
54
|
+
let s
|
|
55
|
+
if (max === 0) {
|
|
56
|
+
s = 0
|
|
57
|
+
} else {
|
|
58
|
+
s = (delta / max) * 100
|
|
59
|
+
}
|
|
60
|
+
if (max === min) {
|
|
61
|
+
h = 0
|
|
62
|
+
} else if (r === max) {
|
|
63
|
+
h = (g - b) / delta
|
|
64
|
+
} else if (g === max) {
|
|
65
|
+
h = 2 + (b - r) / delta
|
|
66
|
+
} else if (b === max) {
|
|
67
|
+
h = 4 + (r - g) / delta
|
|
68
|
+
}
|
|
69
|
+
h = Math.min(h * 60, 360)
|
|
70
|
+
|
|
71
|
+
if (h < 0) {
|
|
72
|
+
h += 360
|
|
73
|
+
}
|
|
74
|
+
return [Math.round(h), Math.round(s)]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function rgb2xy(r, g, b) {
|
|
78
|
+
const redC = r / 255
|
|
79
|
+
const greenC = g / 255
|
|
80
|
+
const blueC = b / 255
|
|
81
|
+
const redN = redC > 0.04045 ? ((redC + 0.055) / (1.0 + 0.055)) ** 2.4 : redC / 12.92
|
|
82
|
+
const greenN = greenC > 0.04045 ? ((greenC + 0.055) / (1.0 + 0.055)) ** 2.4 : greenC / 12.92
|
|
83
|
+
const blueN = blueC > 0.04045 ? ((blueC + 0.055) / (1.0 + 0.055)) ** 2.4 : blueC / 12.92
|
|
84
|
+
const X = redN * 0.664511 + greenN * 0.154324 + blueN * 0.162028
|
|
85
|
+
const Y = redN * 0.283881 + greenN * 0.668433 + blueN * 0.047685
|
|
86
|
+
const Z = redN * 0.000088 + greenN * 0.07231 + blueN * 0.986039
|
|
87
|
+
const x = X / (X + Y + Z)
|
|
88
|
+
const y = Y / (X + Y + Z)
|
|
89
|
+
return [x, y]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function xy2rgb(x, y) {
|
|
93
|
+
const z = 1 - x - y
|
|
94
|
+
const X = x / y
|
|
95
|
+
const Z = z / y
|
|
96
|
+
let red = X * 1.656492 - 1 * 0.354851 - Z * 0.255038
|
|
97
|
+
let green = -X * 0.707196 + 1 * 1.655397 + Z * 0.036152
|
|
98
|
+
let blue = X * 0.051713 - 1 * 0.121364 + Z * 1.01153
|
|
99
|
+
if (red > blue && red > green && red > 1) {
|
|
100
|
+
green /= red
|
|
101
|
+
blue /= red
|
|
102
|
+
red = 1
|
|
103
|
+
} else if (green > blue && green > red && green > 1) {
|
|
104
|
+
red /= green
|
|
105
|
+
blue /= green
|
|
106
|
+
green = 1
|
|
107
|
+
} else if (blue > red && blue > green && blue > 1.0) {
|
|
108
|
+
red /= blue
|
|
109
|
+
green /= blue
|
|
110
|
+
blue = 1.0
|
|
111
|
+
}
|
|
112
|
+
red = red <= 0.0031308 ? 12.92 * red : (1.0 + 0.055) * red ** (1.0 / 2.4) - 0.055
|
|
113
|
+
green = green <= 0.0031308 ? 12.92 * green : (1.0 + 0.055) * green ** (1.0 / 2.4) - 0.055
|
|
114
|
+
blue = blue <= 0.0031308 ? 12.92 * blue : (1.0 + 0.055) * blue ** (1.0 / 2.4) - 0.055
|
|
115
|
+
red = Math.abs(Math.round(red * 255))
|
|
116
|
+
green = Math.abs(Math.round(green * 255))
|
|
117
|
+
blue = Math.abs(Math.round(blue * 255))
|
|
118
|
+
if (Number.isNaN(red)) {
|
|
119
|
+
red = 0
|
|
120
|
+
}
|
|
121
|
+
if (Number.isNaN(green)) {
|
|
122
|
+
green = 0
|
|
123
|
+
}
|
|
124
|
+
if (Number.isNaN(blue)) {
|
|
125
|
+
blue = 0
|
|
126
|
+
}
|
|
127
|
+
return [red, green, blue]
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export {
|
|
131
|
+
hs2rgb,
|
|
132
|
+
rgb2hs,
|
|
133
|
+
rgb2xy,
|
|
134
|
+
xy2rgb,
|
|
135
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
defaultConfig: {
|
|
3
|
+
name: 'Wemo',
|
|
4
|
+
mode: 'auto',
|
|
5
|
+
hideConnectionErrors: false,
|
|
6
|
+
disablePlugin: false,
|
|
7
|
+
discoveryInterval: 30,
|
|
8
|
+
pollingInterval: 30,
|
|
9
|
+
upnpInterval: 300,
|
|
10
|
+
disableUPNP: false,
|
|
11
|
+
disableDeviceLogging: false,
|
|
12
|
+
removeByName: '',
|
|
13
|
+
wemoClient: {
|
|
14
|
+
callback_url: '',
|
|
15
|
+
listen_interface: '',
|
|
16
|
+
port: 0,
|
|
17
|
+
discover_opts: {
|
|
18
|
+
interfaces: '',
|
|
19
|
+
explicitSocketBind: true,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
makerTypes: [],
|
|
23
|
+
wemoInsights: [],
|
|
24
|
+
wemoLights: [],
|
|
25
|
+
wemoLinks: [],
|
|
26
|
+
wemoMotions: [],
|
|
27
|
+
wemoOthers: [],
|
|
28
|
+
wemoOutlets: [],
|
|
29
|
+
platform: 'Wemo',
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
defaultValues: {
|
|
33
|
+
adaptiveLightingShift: 0,
|
|
34
|
+
brightnessStep: 1,
|
|
35
|
+
discoveryInterval: 30,
|
|
36
|
+
makerTimer: 20,
|
|
37
|
+
noMotionTimer: 60,
|
|
38
|
+
pollingInterval: 30,
|
|
39
|
+
port: 0,
|
|
40
|
+
showAs: 'default',
|
|
41
|
+
timeDiff: 1,
|
|
42
|
+
transitionTime: 0,
|
|
43
|
+
upnpInterval: 300,
|
|
44
|
+
wattDiff: 1,
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
minValues: {
|
|
48
|
+
adaptiveLightingShift: -1,
|
|
49
|
+
discoveryInterval: 15,
|
|
50
|
+
brightnessStep: 1,
|
|
51
|
+
makerTimer: 1,
|
|
52
|
+
noMotionTimer: 0,
|
|
53
|
+
pollingInterval: 15,
|
|
54
|
+
port: 0,
|
|
55
|
+
timeDiff: 1,
|
|
56
|
+
transitionTime: 0,
|
|
57
|
+
upnpInterval: 60,
|
|
58
|
+
wattDiff: 1,
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
allowed: {
|
|
62
|
+
mode: ['auto', 'semi', 'manual'],
|
|
63
|
+
makerTypes: [
|
|
64
|
+
'label',
|
|
65
|
+
'serialNumber',
|
|
66
|
+
'ignoreDevice',
|
|
67
|
+
'makerType',
|
|
68
|
+
'makerTimer',
|
|
69
|
+
'reversePolarity',
|
|
70
|
+
'manualIP',
|
|
71
|
+
'listenerType',
|
|
72
|
+
],
|
|
73
|
+
wemoInsights: [
|
|
74
|
+
'label',
|
|
75
|
+
'serialNumber',
|
|
76
|
+
'ignoreDevice',
|
|
77
|
+
'showTodayTC',
|
|
78
|
+
'wattDiff',
|
|
79
|
+
'timeDiff',
|
|
80
|
+
'showAs',
|
|
81
|
+
'outletInUseTrue',
|
|
82
|
+
'manualIP',
|
|
83
|
+
'listenerType',
|
|
84
|
+
],
|
|
85
|
+
wemoLights: [
|
|
86
|
+
'label',
|
|
87
|
+
'serialNumber',
|
|
88
|
+
'ignoreDevice',
|
|
89
|
+
'enableColourControl',
|
|
90
|
+
'adaptiveLightingShift',
|
|
91
|
+
'brightnessStep',
|
|
92
|
+
'transitionTime',
|
|
93
|
+
'manualIP',
|
|
94
|
+
'listenerType',
|
|
95
|
+
],
|
|
96
|
+
wemoLinks: ['label', 'serialNumber', 'ignoreDevice', 'manualIP', 'listenerType'],
|
|
97
|
+
wemoMotions: [
|
|
98
|
+
'label',
|
|
99
|
+
'serialNumber',
|
|
100
|
+
'ignoreDevice',
|
|
101
|
+
'noMotionTimer',
|
|
102
|
+
'manualIP',
|
|
103
|
+
],
|
|
104
|
+
wemoOthers: [
|
|
105
|
+
'label',
|
|
106
|
+
'serialNumber',
|
|
107
|
+
'ignoreDevice',
|
|
108
|
+
'manualIP',
|
|
109
|
+
'listenerType',
|
|
110
|
+
],
|
|
111
|
+
wemoOutlets: [
|
|
112
|
+
'label',
|
|
113
|
+
'serialNumber',
|
|
114
|
+
'ignoreDevice',
|
|
115
|
+
'showAs',
|
|
116
|
+
'manualIP',
|
|
117
|
+
'listenerType',
|
|
118
|
+
],
|
|
119
|
+
listenerType: ['default', 'http'],
|
|
120
|
+
showAs: ['default', 'switch', 'purifier'],
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
portsToScan: [49153, 49152, 49154, 49155, 49151, 49156, 49157, 49158, 49159],
|
|
124
|
+
servicesToSubscribe: [
|
|
125
|
+
'urn:Belkin:service:basicevent:1',
|
|
126
|
+
'urn:Belkin:service:insight:1',
|
|
127
|
+
'urn:Belkin:service:bridge:1',
|
|
128
|
+
],
|
|
129
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { inherits } from 'node:util'
|
|
2
|
+
|
|
3
|
+
export default class {
|
|
4
|
+
constructor(api) {
|
|
5
|
+
this.hapServ = api.hap.Service
|
|
6
|
+
this.hapChar = api.hap.Characteristic
|
|
7
|
+
this.uuids = {
|
|
8
|
+
currentConsumption: 'E863F10D-079E-48FF-8F27-9C2605A29F52',
|
|
9
|
+
totalConsumption: 'E863F10C-079E-48FF-8F27-9C2605A29F52',
|
|
10
|
+
voltage: 'E863F10A-079E-48FF-8F27-9C2605A29F52',
|
|
11
|
+
electricCurrent: 'E863F126-079E-48FF-8F27-9C2605A29F52',
|
|
12
|
+
resetTotal: 'E863F112-079E-48FF-8F27-9C2605A29F52',
|
|
13
|
+
lastActivation: 'E863F11A-079E-48FF-8F27-9C2605A29F52',
|
|
14
|
+
openDuration: 'E863F118-079E-48FF-8F27-9C2605A29F52',
|
|
15
|
+
closedDuration: 'E863F119-079E-48FF-8F27-9C2605A29F52',
|
|
16
|
+
timesOpened: 'E863F129-079E-48FF-8F27-9C2605A29F52',
|
|
17
|
+
}
|
|
18
|
+
const self = this
|
|
19
|
+
this.CurrentConsumption = function CurrentConsumption() {
|
|
20
|
+
self.hapChar.call(this, 'Current Consumption', self.uuids.currentConsumption)
|
|
21
|
+
this.setProps({
|
|
22
|
+
format: api.hap.Formats.UINT16,
|
|
23
|
+
unit: 'W',
|
|
24
|
+
maxValue: 100000,
|
|
25
|
+
minValue: 0,
|
|
26
|
+
minStep: 1,
|
|
27
|
+
perms: [api.hap.Perms.PAIRED_READ, api.hap.Perms.NOTIFY],
|
|
28
|
+
})
|
|
29
|
+
this.value = this.getDefaultValue()
|
|
30
|
+
}
|
|
31
|
+
this.TotalConsumption = function TotalConsumption() {
|
|
32
|
+
self.hapChar.call(this, 'Total Consumption', self.uuids.totalConsumption)
|
|
33
|
+
this.setProps({
|
|
34
|
+
format: api.hap.Formats.FLOAT,
|
|
35
|
+
unit: 'kWh',
|
|
36
|
+
maxValue: 100000000000,
|
|
37
|
+
minValue: 0,
|
|
38
|
+
minStep: 0.01,
|
|
39
|
+
perms: [api.hap.Perms.PAIRED_READ, api.hap.Perms.NOTIFY],
|
|
40
|
+
})
|
|
41
|
+
this.value = this.getDefaultValue()
|
|
42
|
+
}
|
|
43
|
+
this.Voltage = function Voltage() {
|
|
44
|
+
self.hapChar.call(this, 'Voltage', self.uuids.voltage)
|
|
45
|
+
this.setProps({
|
|
46
|
+
format: api.hap.Formats.FLOAT,
|
|
47
|
+
unit: 'V',
|
|
48
|
+
maxValue: 100000000000,
|
|
49
|
+
minValue: 0,
|
|
50
|
+
minStep: 1,
|
|
51
|
+
perms: [api.hap.Perms.PAIRED_READ, api.hap.Perms.NOTIFY],
|
|
52
|
+
})
|
|
53
|
+
this.value = this.getDefaultValue()
|
|
54
|
+
}
|
|
55
|
+
this.ElectricCurrent = function ElectricCurrent() {
|
|
56
|
+
self.hapChar.call(this, 'Electric Current', self.uuids.electricCurrent)
|
|
57
|
+
this.setProps({
|
|
58
|
+
format: api.hap.Formats.FLOAT,
|
|
59
|
+
unit: 'A',
|
|
60
|
+
maxValue: 100000000000,
|
|
61
|
+
minValue: 0,
|
|
62
|
+
minStep: 0.1,
|
|
63
|
+
perms: [api.hap.Perms.PAIRED_READ, api.hap.Perms.NOTIFY],
|
|
64
|
+
})
|
|
65
|
+
this.value = this.getDefaultValue()
|
|
66
|
+
}
|
|
67
|
+
this.ResetTotal = function ResetTotal() {
|
|
68
|
+
self.hapChar.call(this, 'Reset Total', self.uuids.resetTotal)
|
|
69
|
+
this.setProps({
|
|
70
|
+
format: api.hap.Formats.UINT32,
|
|
71
|
+
unit: api.hap.Units.seconds,
|
|
72
|
+
perms: [api.hap.Perms.PAIRED_READ, api.hap.Perms.NOTIFY, api.hap.Perms.PAIRED_WRITE],
|
|
73
|
+
})
|
|
74
|
+
this.value = this.getDefaultValue()
|
|
75
|
+
}
|
|
76
|
+
this.LastActivation = function LastActivation() {
|
|
77
|
+
self.hapChar.call(this, 'Last Activation', self.uuids.lastActivation)
|
|
78
|
+
this.setProps({
|
|
79
|
+
format: api.hap.Formats.UINT32,
|
|
80
|
+
unit: api.hap.Units.SECONDS,
|
|
81
|
+
perms: [api.hap.Perms.PAIRED_READ, api.hap.Perms.NOTIFY],
|
|
82
|
+
})
|
|
83
|
+
this.value = this.getDefaultValue()
|
|
84
|
+
}
|
|
85
|
+
this.OpenDuration = function OpenDuration() {
|
|
86
|
+
self.hapChar.call(this, 'Open Duration', self.uuids.openDuration)
|
|
87
|
+
this.setProps({
|
|
88
|
+
format: api.hap.Formats.UINT32,
|
|
89
|
+
unit: api.hap.Units.SECONDS,
|
|
90
|
+
perms: [api.hap.Perms.PAIRED_READ, api.hap.Perms.NOTIFY, api.hap.Perms.PAIRED_WRITE],
|
|
91
|
+
})
|
|
92
|
+
this.value = this.getDefaultValue()
|
|
93
|
+
}
|
|
94
|
+
this.ClosedDuration = function ClosedDuration() {
|
|
95
|
+
self.hapChar.call(this, 'Closed Duration', self.uuids.closedDuration)
|
|
96
|
+
this.setProps({
|
|
97
|
+
format: api.hap.Formats.UINT32,
|
|
98
|
+
unit: api.hap.Units.SECONDS,
|
|
99
|
+
perms: [api.hap.Perms.PAIRED_READ, api.hap.Perms.NOTIFY, api.hap.Perms.PAIRED_WRITE],
|
|
100
|
+
})
|
|
101
|
+
this.value = this.getDefaultValue()
|
|
102
|
+
}
|
|
103
|
+
this.TimesOpened = function TimesOpened() {
|
|
104
|
+
self.hapChar.call(this, 'Times Opened', self.uuids.timesOpened)
|
|
105
|
+
this.setProps({
|
|
106
|
+
format: api.hap.Formats.UINT32,
|
|
107
|
+
perms: [api.hap.Perms.PAIRED_READ, api.hap.Perms.NOTIFY],
|
|
108
|
+
})
|
|
109
|
+
this.value = this.getDefaultValue()
|
|
110
|
+
}
|
|
111
|
+
inherits(this.CurrentConsumption, this.hapChar)
|
|
112
|
+
inherits(this.TotalConsumption, this.hapChar)
|
|
113
|
+
inherits(this.Voltage, this.hapChar)
|
|
114
|
+
inherits(this.ElectricCurrent, this.hapChar)
|
|
115
|
+
inherits(this.LastActivation, this.hapChar)
|
|
116
|
+
inherits(this.ResetTotal, this.hapChar)
|
|
117
|
+
inherits(this.OpenDuration, this.hapChar)
|
|
118
|
+
inherits(this.ClosedDuration, this.hapChar)
|
|
119
|
+
inherits(this.TimesOpened, this.hapChar)
|
|
120
|
+
this.CurrentConsumption.UUID = this.uuids.currentConsumption
|
|
121
|
+
this.TotalConsumption.UUID = this.uuids.totalConsumption
|
|
122
|
+
this.Voltage.UUID = this.uuids.voltage
|
|
123
|
+
this.ElectricCurrent.UUID = this.uuids.electricCurrent
|
|
124
|
+
this.LastActivation.UUID = this.uuids.lastActivation
|
|
125
|
+
this.ResetTotal.UUID = this.uuids.resetTotal
|
|
126
|
+
this.OpenDuration.UUID = this.uuids.openDuration
|
|
127
|
+
this.ClosedDuration.UUID = this.uuids.closedDuration
|
|
128
|
+
this.TimesOpened.UUID = this.uuids.timesOpened
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
function decodeXML(input) {
|
|
2
|
+
return input
|
|
3
|
+
.replace(/</g, '<')
|
|
4
|
+
.replace(/>/g, '>')
|
|
5
|
+
.replace(/"/g, '"')
|
|
6
|
+
.replace(/'/g, '\'')
|
|
7
|
+
.replace(/&/g, '&')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function generateRandomString(length) {
|
|
11
|
+
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789'
|
|
12
|
+
let nonce = ''
|
|
13
|
+
while (nonce.length < length) {
|
|
14
|
+
nonce += chars.charAt(Math.floor(Math.random() * chars.length))
|
|
15
|
+
}
|
|
16
|
+
return nonce
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const hasProperty = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
|
|
20
|
+
|
|
21
|
+
function parseError(err, hideStack = []) {
|
|
22
|
+
let toReturn = err.message
|
|
23
|
+
if (err?.stack?.length > 0 && !hideStack.includes(err.message)) {
|
|
24
|
+
const stack = err.stack.split('\n')
|
|
25
|
+
if (stack[1]) {
|
|
26
|
+
toReturn += stack[1].replace(' ', '')
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return toReturn
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseSerialNumber(input) {
|
|
33
|
+
return input
|
|
34
|
+
.toString()
|
|
35
|
+
.replace(/[\s'"]+/g, '')
|
|
36
|
+
.toUpperCase()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function sleep(ms) {
|
|
40
|
+
return new Promise((resolve) => {
|
|
41
|
+
setTimeout(resolve, ms)
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
decodeXML,
|
|
47
|
+
generateRandomString,
|
|
48
|
+
hasProperty,
|
|
49
|
+
parseError,
|
|
50
|
+
parseSerialNumber,
|
|
51
|
+
sleep,
|
|
52
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
accNotFound: 'accessory not found',
|
|
3
|
+
accNotReady: 'has not been discovered yet so commands will fail',
|
|
4
|
+
alDisabled: 'adaptive lighting disabled due to significant colour change',
|
|
5
|
+
awaiting: 'The following devices have still not been initially found',
|
|
6
|
+
beta: 'You are using a beta version of the plugin - you will experience more logging than normal',
|
|
7
|
+
brand: 'Belkin Wemo',
|
|
8
|
+
brightnessFail: 'could not obtain updated brightness as',
|
|
9
|
+
cantCtl: 'sending update failed as',
|
|
10
|
+
cantUpd: 'receiving update failed as',
|
|
11
|
+
cfgDef: 'is not a valid number so using default of',
|
|
12
|
+
cfgDup: 'will be ignored since another entry with this ID already exists',
|
|
13
|
+
cfgIgn: 'is not configured correctly so ignoring',
|
|
14
|
+
cfgIgnItem: 'has an invalid entry which will be ignored',
|
|
15
|
+
cfgItem: 'Config entry',
|
|
16
|
+
cfgLow: 'is set too low so increasing to',
|
|
17
|
+
cfgRmv: 'is unused and can be removed',
|
|
18
|
+
cfgQts: 'should not have quotes around its entry',
|
|
19
|
+
complete: '✓ Setup complete',
|
|
20
|
+
connError: 'connection error',
|
|
21
|
+
curAir: 'current air quality',
|
|
22
|
+
curBright: 'current brightness',
|
|
23
|
+
curCCT: 'current cct',
|
|
24
|
+
curColour: 'current colour',
|
|
25
|
+
curCons: 'current consumption',
|
|
26
|
+
curCont: 'current contact',
|
|
27
|
+
curFilter: 'current filter level',
|
|
28
|
+
curHumi: 'current humidity',
|
|
29
|
+
curIon: 'current ionizer',
|
|
30
|
+
curOIU: 'current outlet-in-use',
|
|
31
|
+
curMode: 'current mode',
|
|
32
|
+
curState: 'current state',
|
|
33
|
+
curTemp: 'current temperature',
|
|
34
|
+
curTimer: 'current timer',
|
|
35
|
+
detectedNo: 'not detected',
|
|
36
|
+
detectedYes: 'detected',
|
|
37
|
+
devAdd: 'has been added to Homebridge',
|
|
38
|
+
devInitOpts: 'initialising with options',
|
|
39
|
+
devNotAdd: 'could not be added to Homebridge as',
|
|
40
|
+
devNotConf: 'could not be configured as',
|
|
41
|
+
devNotInit: 'could not be initialised as',
|
|
42
|
+
devNotRemove: 'could not be removed from Homebridge as',
|
|
43
|
+
devOffline: 'appears to be offline',
|
|
44
|
+
devRemove: 'has been removed from Homebridge',
|
|
45
|
+
disabling: 'Disabling plugin',
|
|
46
|
+
hbVersionFail: 'Your version of Homebridge is too low - please update to v1.6',
|
|
47
|
+
identify: 'identify button pressed',
|
|
48
|
+
incFail: 'failed to process incoming message as',
|
|
49
|
+
incKnown: 'incoming notification',
|
|
50
|
+
incUnknown: 'incoming notification from unknown accessory',
|
|
51
|
+
initSer: 'initialised with s/n',
|
|
52
|
+
initMac: 'and ip/port',
|
|
53
|
+
initialised: 'Plugin initialised. Setting up accessories...',
|
|
54
|
+
initialising: 'Initialising plugin',
|
|
55
|
+
insCons: 'consumption',
|
|
56
|
+
insOnTime: 'today ontime',
|
|
57
|
+
insTC: 'total consumption',
|
|
58
|
+
httpFail: 'http error (will be re-attempted)',
|
|
59
|
+
httpGood: 'http has been established',
|
|
60
|
+
labelAuto: 'auto',
|
|
61
|
+
labelClosed: 'closed',
|
|
62
|
+
labelClosing: 'closing',
|
|
63
|
+
labelEco: 'eco',
|
|
64
|
+
labelExc: 'excellent',
|
|
65
|
+
labelFair: 'fair',
|
|
66
|
+
labelFP: 'frost-protect',
|
|
67
|
+
labelHigh: 'high',
|
|
68
|
+
labelLow: 'low',
|
|
69
|
+
labelMax: 'max',
|
|
70
|
+
labelMed: 'med',
|
|
71
|
+
labelMin: 'min',
|
|
72
|
+
labelPoor: 'poor',
|
|
73
|
+
labelOff: 'off',
|
|
74
|
+
labelOpen: 'open',
|
|
75
|
+
labelOpening: 'opening',
|
|
76
|
+
labelStopped: 'stopped',
|
|
77
|
+
labelWarm: 'warm',
|
|
78
|
+
listenerClosed: 'Listener server gracefully closed',
|
|
79
|
+
listenerError: 'Listener server error',
|
|
80
|
+
listenerPort: 'Listener server port',
|
|
81
|
+
makerClosed: 'is already closed so ignoring command',
|
|
82
|
+
makerClosing: 'is already closing so ignoring command',
|
|
83
|
+
makerNeedMMode: 'must be set to momentary mode to work as a garage door',
|
|
84
|
+
makerOpen: 'is already open so ignoring command',
|
|
85
|
+
makerOpening: 'is already opening so ignoring command',
|
|
86
|
+
makerTrigExt: 'triggered externally',
|
|
87
|
+
modelLED: 'LED Bulb (Via Link)',
|
|
88
|
+
motionNo: 'clear',
|
|
89
|
+
motionSensor: 'motion sensor',
|
|
90
|
+
motionYes: 'motion detected',
|
|
91
|
+
noInterface: 'Unable to find interface',
|
|
92
|
+
noPort: 'could not find correct port for device',
|
|
93
|
+
noService: 'device does not have this service',
|
|
94
|
+
noServices: 'device does not have any upnp services',
|
|
95
|
+
noSockets: 'NodeSSDP error [no sockets], if this continues to happen try restarting Homebridge',
|
|
96
|
+
notConfigured: 'Plugin has not been configured',
|
|
97
|
+
proEr: 'could not be processed as',
|
|
98
|
+
purifyNo: 'not purifying',
|
|
99
|
+
purifyYes: 'purifying',
|
|
100
|
+
rduErr: 'http polling failed as',
|
|
101
|
+
recUpd: 'receiving update',
|
|
102
|
+
repError: 'reported error',
|
|
103
|
+
senUpd: 'sending update',
|
|
104
|
+
ssdpFail: 'SSDP search failed as',
|
|
105
|
+
ssdpStopped: 'SSDP client gracefully stopped',
|
|
106
|
+
stoppedSubs: 'existing upnp subscriptions have been stopped',
|
|
107
|
+
subError: 'subscription error, retrying in 10 seconds',
|
|
108
|
+
subInit: 'initial subscription for service',
|
|
109
|
+
subPending: 'subscription still pending',
|
|
110
|
+
subscribeError: 'could not subscribe as',
|
|
111
|
+
tarHumi: 'target humidity',
|
|
112
|
+
tarState: 'target state',
|
|
113
|
+
tarTemp: 'target temperature',
|
|
114
|
+
timeout: 'a connection timeout occurred',
|
|
115
|
+
timeoutUnreach: 'a connection timeout occurred (EHOSTUNREACH)',
|
|
116
|
+
timerComplete: 'timer complete',
|
|
117
|
+
timerStarted: 'timer started',
|
|
118
|
+
timerStopped: 'timer stopped',
|
|
119
|
+
unsubFail: 'unsubscribe failed',
|
|
120
|
+
unsupported: 'is unsupported but feel free to create a GitHub issue',
|
|
121
|
+
upnpFail: 'upnp error (will be re-attempted)',
|
|
122
|
+
upnpGood: 'upnp has been established',
|
|
123
|
+
viaAL: 'via adaptive lighting',
|
|
124
|
+
welcome: 'This plugin has been made with ♥ by bwp91, please consider a ☆ on GitHub if you are finding it useful!',
|
|
125
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@homebridge-plugins/homebridge-wemo",
|
|
3
|
+
"alias": "Wemo",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"version": "7.0.0",
|
|
6
|
+
"description": "Homebridge plugin to integrate Wemo devices into HomeKit.",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "bwp91",
|
|
9
|
+
"email": "bwp91@icloud.com"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"funding": [
|
|
13
|
+
{
|
|
14
|
+
"type": "github",
|
|
15
|
+
"url": "https://github.com/sponsors/bwp91"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"type": "kofi",
|
|
19
|
+
"url": "https://ko-fi.com/bwp91"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"type": "patreon",
|
|
23
|
+
"url": "https://www.patreon.com/bwp91"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"type": "paypal",
|
|
27
|
+
"url": "https://www.paypal.me/BenPotter"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"homepage": "https://github.com/homebridge-plugins/homebridge-wemo",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/homebridge-plugins/homebridge-wemo.git"
|
|
34
|
+
},
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/homebridge-plugins/homebridge-wemo/issues"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"homebridge",
|
|
40
|
+
"homebridge-plugin",
|
|
41
|
+
"hoobs",
|
|
42
|
+
"hoobs-plugin",
|
|
43
|
+
"homekit",
|
|
44
|
+
"siri",
|
|
45
|
+
"wemo",
|
|
46
|
+
"belkin"
|
|
47
|
+
],
|
|
48
|
+
"main": "lib/index.js",
|
|
49
|
+
"engines": {
|
|
50
|
+
"homebridge": "^1.6.0 || ^2.0.0-beta.0",
|
|
51
|
+
"node": "^18.20.7 || ^20.19.0 || ^22.14.0"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"lint": "eslint . --fix",
|
|
55
|
+
"rebuild": "rm -rf package-lock.json && rm -rf node_modules && npm install"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@homebridge/plugin-ui-utils": "^2.0.1",
|
|
59
|
+
"axios": "^1.8.4",
|
|
60
|
+
"ip": "^2.0.1",
|
|
61
|
+
"node-ssdp": "^4.0.1",
|
|
62
|
+
"p-queue": "^8.1.0",
|
|
63
|
+
"xml2js": "^0.6.2",
|
|
64
|
+
"xmlbuilder": "^15.1.1"
|
|
65
|
+
},
|
|
66
|
+
"devDependencies": {
|
|
67
|
+
"@antfu/eslint-config": "^4.10.2",
|
|
68
|
+
"eslint": "^9.23.0"
|
|
69
|
+
}
|
|
70
|
+
}
|