homebridge-flume 1.0.0 → 1.2.2
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 +29 -0
- package/config.schema.json +15 -21
- package/lib/connection/http.js +30 -3
- package/lib/device/{valve.js → leak-sensor.js} +45 -15
- package/lib/index.js +15 -25
- package/lib/utils/constants.js +4 -7
- package/lib/utils/custom-chars.js +50 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to homebridge-flume will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 1.2.2 (2021-12-21)
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Some config options rearranged for easier access
|
|
10
|
+
|
|
11
|
+
## 1.2.1 (2021-12-08)
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Bump `homebridge` recommended version to v1.3.8
|
|
16
|
+
- Bump `node` recommended versions to v14.18.2 or v16.13.1
|
|
17
|
+
|
|
18
|
+
## 1.2.0 (2021-12-01)
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Previous month usage custom characteristic (viewable in HomeKit apps like Eve)
|
|
23
|
+
|
|
24
|
+
## 1.1.0 (2021-11-30)
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- Daily and monthly usage custom characteristics (viewable in HomeKit apps like Eve)
|
|
29
|
+
|
|
30
|
+
### Removed
|
|
31
|
+
|
|
32
|
+
- `threshold` configuration option as unused
|
|
33
|
+
|
|
5
34
|
## 1.0.0 (2021-11-29)
|
|
6
35
|
|
|
7
36
|
### Added
|
package/config.schema.json
CHANGED
|
@@ -39,23 +39,6 @@
|
|
|
39
39
|
"required": true,
|
|
40
40
|
"description": "Your Flume Client Secret, found at https://portal.flumetech.com."
|
|
41
41
|
},
|
|
42
|
-
"refreshInterval": {
|
|
43
|
-
"title": "Refresh Interval",
|
|
44
|
-
"type": "integer",
|
|
45
|
-
"placeholder": 2,
|
|
46
|
-
"description": "Number of minutes between updates. Must be 2 or more."
|
|
47
|
-
},
|
|
48
|
-
"threshold": {
|
|
49
|
-
"title": "Threshold",
|
|
50
|
-
"type": "number",
|
|
51
|
-
"placeholder": 0,
|
|
52
|
-
"description": "Ignore a steady water draw below this value in gallons per refresh interval. Must be 0 or more."
|
|
53
|
-
},
|
|
54
|
-
"disableDeviceLogging": {
|
|
55
|
-
"type": "boolean",
|
|
56
|
-
"title": "Disable Device Logging",
|
|
57
|
-
"description": "Global logging setting for accessory status changes. If true then accessory status changes will not be logged."
|
|
58
|
-
},
|
|
59
42
|
"debug": {
|
|
60
43
|
"title": "Debug Logging",
|
|
61
44
|
"type": "boolean",
|
|
@@ -65,6 +48,17 @@
|
|
|
65
48
|
"title": "Disable Plugin",
|
|
66
49
|
"type": "boolean",
|
|
67
50
|
"description": "If true, the plugin will remove all accessories and not load the plugin on restart."
|
|
51
|
+
},
|
|
52
|
+
"disableDeviceLogging": {
|
|
53
|
+
"type": "boolean",
|
|
54
|
+
"title": "Disable Device Logging",
|
|
55
|
+
"description": "Global logging setting for accessory status changes. If true then accessory status changes will not be logged."
|
|
56
|
+
},
|
|
57
|
+
"refreshInterval": {
|
|
58
|
+
"title": "Refresh Interval",
|
|
59
|
+
"type": "integer",
|
|
60
|
+
"placeholder": 2,
|
|
61
|
+
"description": "Number of minutes between updates. Must be 2 or more."
|
|
68
62
|
}
|
|
69
63
|
}
|
|
70
64
|
},
|
|
@@ -72,14 +66,14 @@
|
|
|
72
66
|
{
|
|
73
67
|
"type": "fieldset",
|
|
74
68
|
"title": "Required Settings",
|
|
75
|
-
"items": ["username", "password", "clientId", "clientSecret"]
|
|
69
|
+
"items": ["username", "password", "clientId", "clientSecret", "debug", "disablePlugin"]
|
|
76
70
|
},
|
|
77
71
|
{
|
|
78
72
|
"type": "fieldset",
|
|
79
|
-
"title": "
|
|
80
|
-
"description": "
|
|
73
|
+
"title": "Advanced Settings",
|
|
74
|
+
"description": "Advanced settings for the plugin, including refresh options.",
|
|
81
75
|
"expandable": true,
|
|
82
|
-
"items": ["
|
|
76
|
+
"items": ["disableDeviceLogging", "refreshInterval"]
|
|
83
77
|
}
|
|
84
78
|
]
|
|
85
79
|
}
|
package/lib/connection/http.js
CHANGED
|
@@ -238,13 +238,40 @@ module.exports = class connectionHTTP {
|
|
|
238
238
|
await this.renewToken()
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
+
// Generate dates for the query data
|
|
242
|
+
const date = new Date()
|
|
243
|
+
const startOfToday = date.toISOString().substring(0, 10) + ' 00:00:00'
|
|
244
|
+
|
|
245
|
+
// Set the date to the first of the current month
|
|
246
|
+
date.setDate(1)
|
|
247
|
+
const startOfCurrMonth = date.toISOString().substring(0, 10) + ' 00:00:00'
|
|
248
|
+
|
|
249
|
+
// Set the month to the previous month
|
|
250
|
+
date.setMonth(date.getMonth() - 1)
|
|
251
|
+
const startOfPrevMonth = date.toISOString().substring(0, 10) + ' 00:00:00'
|
|
252
|
+
|
|
241
253
|
// Generate the JSON data to send
|
|
242
254
|
const body = {
|
|
243
255
|
queries: [
|
|
244
256
|
{
|
|
245
|
-
request_id: '
|
|
246
|
-
bucket: '
|
|
247
|
-
since_datetime:
|
|
257
|
+
request_id: 'today',
|
|
258
|
+
bucket: 'DAY',
|
|
259
|
+
since_datetime: startOfToday,
|
|
260
|
+
operation: 'SUM',
|
|
261
|
+
units: 'GALLONS'
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
request_id: 'month',
|
|
265
|
+
bucket: 'MON',
|
|
266
|
+
since_datetime: startOfCurrMonth,
|
|
267
|
+
operation: 'SUM',
|
|
268
|
+
units: 'GALLONS'
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
request_id: 'prevMonth',
|
|
272
|
+
bucket: 'MON',
|
|
273
|
+
since_datetime: startOfPrevMonth,
|
|
274
|
+
until_datetime: startOfCurrMonth,
|
|
248
275
|
operation: 'SUM',
|
|
249
276
|
units: 'GALLONS'
|
|
250
277
|
}
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
/* eslint-disable new-cap */
|
|
3
3
|
'use strict'
|
|
4
4
|
|
|
5
|
-
module.exports = class
|
|
5
|
+
module.exports = class deviceLeakSensor {
|
|
6
6
|
constructor (platform, accessory) {
|
|
7
7
|
// Set up variables from the platform
|
|
8
8
|
this.accessory = accessory
|
|
9
|
+
this.cusChar = platform.cusChar
|
|
9
10
|
this.funcs = platform.funcs
|
|
10
|
-
this.threshold = platform.config.threshold
|
|
11
11
|
this.hapChar = platform.api.hap.Characteristic
|
|
12
12
|
this.hapErr = platform.api.hap.HapStatusError
|
|
13
13
|
this.hapServ = platform.api.hap.Service
|
|
@@ -25,6 +25,17 @@ module.exports = class deviceValve {
|
|
|
25
25
|
this.cacheLeak = !!this.leakService.getCharacteristic(this.hapChar.LeakDetected).value
|
|
26
26
|
this.cacheBatt = !this.leakService.getCharacteristic(this.hapChar.StatusLowBattery).value
|
|
27
27
|
this.cacheStatus = !this.leakService.getCharacteristic(this.hapChar.StatusFault).value
|
|
28
|
+
|
|
29
|
+
// Add the custom characteristics if they haven't been already
|
|
30
|
+
if (!this.leakService.testCharacteristic(this.cusChar.TodayUsage)) {
|
|
31
|
+
this.leakService.addCharacteristic(this.cusChar.TodayUsage)
|
|
32
|
+
}
|
|
33
|
+
if (!this.leakService.testCharacteristic(this.cusChar.MonthUsage)) {
|
|
34
|
+
this.leakService.addCharacteristic(this.cusChar.MonthUsage)
|
|
35
|
+
}
|
|
36
|
+
if (!this.leakService.testCharacteristic(this.cusChar.PrevMonthUsage)) {
|
|
37
|
+
this.leakService.addCharacteristic(this.cusChar.PrevMonthUsage)
|
|
38
|
+
}
|
|
28
39
|
}
|
|
29
40
|
|
|
30
41
|
externalUpdate (params) {
|
|
@@ -58,19 +69,38 @@ module.exports = class deviceValve {
|
|
|
58
69
|
this.log('[%s] current status [%sconnected].', this.name, this.cacheStatus ? '' : 'not ')
|
|
59
70
|
}
|
|
60
71
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
// Water info
|
|
73
|
+
if (params.waterInfo) {
|
|
74
|
+
if (
|
|
75
|
+
params.waterInfo.today &&
|
|
76
|
+
params.waterInfo.today[0] &&
|
|
77
|
+
this.funcs.hasProperty(params.waterInfo.today[0], 'value')
|
|
78
|
+
) {
|
|
79
|
+
this.leakService.updateCharacteristic(
|
|
80
|
+
this.cusChar.TodayUsage,
|
|
81
|
+
params.waterInfo.today[0].value
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
if (
|
|
85
|
+
params.waterInfo.month &&
|
|
86
|
+
params.waterInfo.month[0] &&
|
|
87
|
+
this.funcs.hasProperty(params.waterInfo.month[0], 'value')
|
|
88
|
+
) {
|
|
89
|
+
this.leakService.updateCharacteristic(
|
|
90
|
+
this.cusChar.MonthUsage,
|
|
91
|
+
params.waterInfo.month[0].value
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
if (
|
|
95
|
+
params.waterInfo.prevMonth &&
|
|
96
|
+
params.waterInfo.prevMonth[0] &&
|
|
97
|
+
this.funcs.hasProperty(params.waterInfo.prevMonth[0], 'value')
|
|
98
|
+
) {
|
|
99
|
+
this.leakService.updateCharacteristic(
|
|
100
|
+
this.cusChar.PrevMonthUsage,
|
|
101
|
+
params.waterInfo.prevMonth[0].value
|
|
102
|
+
)
|
|
103
|
+
}
|
|
74
104
|
}
|
|
75
105
|
}
|
|
76
106
|
}
|
package/lib/index.js
CHANGED
|
@@ -106,18 +106,6 @@ class FlumePlatform {
|
|
|
106
106
|
}
|
|
107
107
|
this.config[key] = val === 'false' ? false : !!val
|
|
108
108
|
break
|
|
109
|
-
case 'threshold': {
|
|
110
|
-
if (typeof v === 'string') {
|
|
111
|
-
logQuotes(key)
|
|
112
|
-
}
|
|
113
|
-
const numVal = Number(val)
|
|
114
|
-
if (isNaN(numVal)) {
|
|
115
|
-
logIgnore(key)
|
|
116
|
-
} else {
|
|
117
|
-
this.config[key] = numVal
|
|
118
|
-
}
|
|
119
|
-
break
|
|
120
|
-
}
|
|
121
109
|
case 'name':
|
|
122
110
|
case 'platform':
|
|
123
111
|
case 'plugin_map':
|
|
@@ -167,6 +155,9 @@ class FlumePlatform {
|
|
|
167
155
|
throw new Error(this.lang.noCreds)
|
|
168
156
|
}
|
|
169
157
|
|
|
158
|
+
// Require any libraries that the accessory instances use
|
|
159
|
+
this.cusChar = new (require('./utils/custom-chars'))(this.api)
|
|
160
|
+
|
|
170
161
|
// Setup the HTTP client if Thermobit username and password have been provided
|
|
171
162
|
this.httpClient = new (require('./connection/http'))(this)
|
|
172
163
|
await this.httpClient.obtainToken()
|
|
@@ -193,10 +184,8 @@ class FlumePlatform {
|
|
|
193
184
|
}
|
|
194
185
|
})
|
|
195
186
|
|
|
196
|
-
// Set up an initial last sync time
|
|
197
|
-
this.lastSync = new Date(Date.now() - this.config.refreshInterval * 60000)
|
|
198
|
-
|
|
199
187
|
// Perform a first sync and setup the refresh interval
|
|
188
|
+
this.counter = 0
|
|
200
189
|
this.flumeSync()
|
|
201
190
|
this.refreshInterval = setInterval(
|
|
202
191
|
() => this.flumeSync(),
|
|
@@ -233,17 +222,19 @@ class FlumePlatform {
|
|
|
233
222
|
|
|
234
223
|
async flumeSync () {
|
|
235
224
|
try {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
.
|
|
239
|
-
|
|
225
|
+
// Reset the counter for water info once we reach 10
|
|
226
|
+
if (this.counter === 10) {
|
|
227
|
+
this.counter = 0
|
|
228
|
+
}
|
|
240
229
|
this.devicesInHB.forEach(async accessory => {
|
|
241
230
|
try {
|
|
242
|
-
const toReturn = {
|
|
231
|
+
const toReturn = {}
|
|
243
232
|
const devInfo = await this.httpClient.getDeviceInfo(accessory.context.deviceId)
|
|
244
233
|
toReturn.devInfo = devInfo
|
|
245
|
-
|
|
246
|
-
|
|
234
|
+
if (this.counter === 0) {
|
|
235
|
+
const waterInfo = await this.httpClient.getWaterInfo(accessory.context.deviceId)
|
|
236
|
+
toReturn.waterInfo = waterInfo
|
|
237
|
+
}
|
|
247
238
|
const leakInfo = await this.httpClient.getLeakInfo(accessory.context.deviceId)
|
|
248
239
|
toReturn.leakInfo = leakInfo
|
|
249
240
|
accessory.control.externalUpdate(toReturn)
|
|
@@ -251,9 +242,8 @@ class FlumePlatform {
|
|
|
251
242
|
const eText = this.funcs.parseError(err)
|
|
252
243
|
this.log.warn('[%s] %s %s.', accessory.displayName, this.lang.devNotRef, eText)
|
|
253
244
|
}
|
|
245
|
+
this.counter++
|
|
254
246
|
})
|
|
255
|
-
|
|
256
|
-
this.lastSync = new Date()
|
|
257
247
|
} catch (err) {
|
|
258
248
|
// Catch any errors performing the sync
|
|
259
249
|
const eText = this.funcs.parseError(err, [])
|
|
@@ -289,7 +279,7 @@ class FlumePlatform {
|
|
|
289
279
|
}
|
|
290
280
|
|
|
291
281
|
// Create the instance for this device type
|
|
292
|
-
accessory.control = new (require('./device/
|
|
282
|
+
accessory.control = new (require('./device/leak-sensor'))(this, accessory)
|
|
293
283
|
|
|
294
284
|
// Log the device initialisation
|
|
295
285
|
this.log('[%s] %s [%s].', accessory.displayName, this.lang.devInit, device.id)
|
package/lib/utils/constants.js
CHANGED
|
@@ -9,22 +9,19 @@ module.exports = {
|
|
|
9
9
|
password: '',
|
|
10
10
|
clientId: '',
|
|
11
11
|
clientSecret: '',
|
|
12
|
-
refreshInterval: 1,
|
|
13
|
-
threshold: 0,
|
|
14
|
-
disableDeviceLogging: false,
|
|
15
12
|
debug: false,
|
|
16
13
|
disablePlugin: false,
|
|
14
|
+
disableDeviceLogging: false,
|
|
15
|
+
refreshInterval: 1,
|
|
17
16
|
platform: 'Flume'
|
|
18
17
|
},
|
|
19
18
|
|
|
20
19
|
defaultValues: {
|
|
21
|
-
refreshInterval: 2
|
|
22
|
-
threshold: 0
|
|
20
|
+
refreshInterval: 2
|
|
23
21
|
},
|
|
24
22
|
|
|
25
23
|
minValues: {
|
|
26
|
-
refreshInterval: 2
|
|
27
|
-
threshold: 0
|
|
24
|
+
refreshInterval: 2
|
|
28
25
|
},
|
|
29
26
|
|
|
30
27
|
httpRetryCodes: ['ENOTFOUND', 'ETIMEDOUT', 'EAI_AGAIN', 'ECONNABORTED']
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/* jshint node: true, esversion: 10, -W014, -W033 */
|
|
2
|
+
/* eslint-disable new-cap */
|
|
3
|
+
'use strict'
|
|
4
|
+
|
|
5
|
+
module.exports = class customCharacteristics {
|
|
6
|
+
constructor (api) {
|
|
7
|
+
this.hapServ = api.hap.Service
|
|
8
|
+
this.hapChar = api.hap.Characteristic
|
|
9
|
+
this.uuids = {
|
|
10
|
+
todayUsage: 'E966F001-079E-48FF-8F27-9C2605A29F52',
|
|
11
|
+
monthUsage: 'E966F002-079E-48FF-8F27-9C2605A29F52',
|
|
12
|
+
prevMonthUsage: 'E966F003-079E-48FF-8F27-9C2605A29F52'
|
|
13
|
+
}
|
|
14
|
+
const self = this
|
|
15
|
+
this.TodayUsage = function () {
|
|
16
|
+
self.hapChar.call(this, 'Today Usage', self.uuids.todayUsage)
|
|
17
|
+
this.setProps({
|
|
18
|
+
format: self.hapChar.Formats.UINT32,
|
|
19
|
+
perms: [self.hapChar.Perms.READ, self.hapChar.Perms.NOTIFY],
|
|
20
|
+
unit: 'Gallons'
|
|
21
|
+
})
|
|
22
|
+
this.value = this.getDefaultValue()
|
|
23
|
+
}
|
|
24
|
+
this.MonthUsage = function () {
|
|
25
|
+
self.hapChar.call(this, 'Month Usage', self.uuids.monthUsage)
|
|
26
|
+
this.setProps({
|
|
27
|
+
format: self.hapChar.Formats.UINT32,
|
|
28
|
+
perms: [self.hapChar.Perms.READ, self.hapChar.Perms.NOTIFY],
|
|
29
|
+
unit: 'Gallons'
|
|
30
|
+
})
|
|
31
|
+
this.value = this.getDefaultValue()
|
|
32
|
+
}
|
|
33
|
+
this.PrevMonthUsage = function () {
|
|
34
|
+
self.hapChar.call(this, 'Previous Month', self.uuids.prevMonthUsage)
|
|
35
|
+
this.setProps({
|
|
36
|
+
format: self.hapChar.Formats.UINT32,
|
|
37
|
+
perms: [self.hapChar.Perms.READ, self.hapChar.Perms.NOTIFY],
|
|
38
|
+
unit: 'Gallons'
|
|
39
|
+
})
|
|
40
|
+
this.value = this.getDefaultValue()
|
|
41
|
+
}
|
|
42
|
+
const inherits = require('util').inherits
|
|
43
|
+
inherits(this.TodayUsage, this.hapChar)
|
|
44
|
+
inherits(this.MonthUsage, this.hapChar)
|
|
45
|
+
inherits(this.PrevMonthUsage, this.hapChar)
|
|
46
|
+
this.TodayUsage.UUID = this.uuids.todayUsage
|
|
47
|
+
this.MonthUsage.UUID = this.uuids.monthUsage
|
|
48
|
+
this.PrevMonthUsage.UUID = this.uuids.prevMonthUsage
|
|
49
|
+
}
|
|
50
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-flume",
|
|
3
3
|
"alias": "Flume",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.2.2",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Ben Potter",
|
|
7
7
|
"email": "bwp91@icloud.com"
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
],
|
|
25
25
|
"engines": {
|
|
26
26
|
"homebridge": "^1.3.8",
|
|
27
|
-
"node": "^14.18.
|
|
27
|
+
"node": "^14.18.2 || ^16.13.1"
|
|
28
28
|
},
|
|
29
29
|
"repository": {
|
|
30
30
|
"type": "git",
|