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 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
@@ -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": "Optional Settings",
80
- "description": "Optional settings for the plugin, including global logging settings.",
73
+ "title": "Advanced Settings",
74
+ "description": "Advanced settings for the plugin, including refresh options.",
81
75
  "expandable": true,
82
- "items": ["refreshInterval", "threshold", "disableDeviceLogging", "debug", "disablePlugin"]
76
+ "items": ["disableDeviceLogging", "refreshInterval"]
83
77
  }
84
78
  ]
85
79
  }
@@ -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: 'currentusage',
246
- bucket: 'MIN',
247
- since_datetime: fromWhen,
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 deviceValve {
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
- const usage =
62
- params.waterInfo.currentusage &&
63
- params.waterInfo.currentusage[0] &&
64
- params.waterInfo.currentusage[0].value
65
- ? params.waterInfo.currentusage[0].value
66
- : 0
67
- if (usage > this.threshold) {
68
- this.log(
69
- '[%s] usage detected - [%s] gallons within the last [%s] minutes.',
70
- this.name,
71
- usage,
72
- this.refreshInterval
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
- const since = this.lastSync
237
- .toISOString()
238
- .substring(0, 19)
239
- .replace('T', ' ')
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 = { since }
231
+ const toReturn = {}
243
232
  const devInfo = await this.httpClient.getDeviceInfo(accessory.context.deviceId)
244
233
  toReturn.devInfo = devInfo
245
- const waterInfo = await this.httpClient.getWaterInfo(accessory.context.deviceId, since)
246
- toReturn.waterInfo = waterInfo
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/valve'))(this, accessory)
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)
@@ -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.0.0",
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.1 || ^16.13.0"
27
+ "node": "^14.18.2 || ^16.13.1"
28
28
  },
29
29
  "repository": {
30
30
  "type": "git",