homebridge-flume 0.1.0 → 0.3.1
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 +21 -0
- package/README.md +49 -36
- package/config.schema.json +55 -22
- package/lib/connection/http.js +218 -0
- package/lib/device/valve.js +38 -0
- package/lib/homebridge-ui/public/index.html +162 -0
- package/lib/homebridge-ui/server.js +14 -0
- package/lib/index.js +307 -191
- package/lib/utils/constants.js +31 -0
- package/lib/utils/functions.js +33 -0
- package/lib/utils/lang-en.js +64 -0
- package/package.json +6 -11
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to homebridge-flume will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## 0.3.1 (2021-11-23)
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Some logging references to Thermobit rather than Flume
|
|
10
|
+
|
|
11
|
+
## 0.3.0 (2021-11-22)
|
|
12
|
+
|
|
13
|
+
Changed some configuration options
|
|
14
|
+
|
|
15
|
+
## 0.2.0 (2021-11-22)
|
|
16
|
+
|
|
17
|
+
Converted from accessory plugin to platform plugin
|
|
18
|
+
|
|
19
|
+
## 0.1.0 (2021-11-22)
|
|
20
|
+
|
|
21
|
+
Initial release
|
package/README.md
CHANGED
|
@@ -1,36 +1,49 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
#
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
1
|
+
<span align="center">
|
|
2
|
+
|
|
3
|
+
# homebridge-flume
|
|
4
|
+
|
|
5
|
+
Homebridge plugin to integrate Flume devices into HomeKit
|
|
6
|
+
|
|
7
|
+
[](https://github.com/homebridge/homebridge/wiki/Verified-Plugins)
|
|
8
|
+
[](https://www.npmjs.com/package/homebridge-flume)
|
|
9
|
+
[](https://github.com/bwp91/homebridge-flume/wiki/Beta-Version)
|
|
10
|
+
[](https://www.npmjs.com/package/homebridge-flume)
|
|
11
|
+
[](https://standardjs.com)
|
|
12
|
+
[](https://discord.com/channels/784827113378676736/784827113378676739)
|
|
13
|
+
[](https://discord.com/channels/432663330281226270/742733745743855627)
|
|
14
|
+
|
|
15
|
+
</span>
|
|
16
|
+
|
|
17
|
+
### Plugin Information
|
|
18
|
+
|
|
19
|
+
- This plugin allows you to view and control your Flume devices within HomeKit.
|
|
20
|
+
|
|
21
|
+
### Prerequisites
|
|
22
|
+
|
|
23
|
+
- To use this plugin, you will need to already have [Homebridge](https://homebridge.io) (at least v1.3) or [HOOBS](https://hoobs.org) (at least v4.1) installed. Refer to the links for more information and installation instructions.
|
|
24
|
+
- Whilst it is recommended to use [Node](https://nodejs.org/en/) v16, the plugin supports v12 and v14 as per the [Homebridge guidelines](https://github.com/homebridge/homebridge/wiki/How-To-Update-Node.js).
|
|
25
|
+
|
|
26
|
+
### Setup
|
|
27
|
+
|
|
28
|
+
- [Installation](https://github.com/bwp91/homebridge-flume/wiki/Installation)
|
|
29
|
+
- [Configuration](https://github.com/bwp91/homebridge-flume/wiki/Configuration)
|
|
30
|
+
- [Beta Version](https://github.com/bwp91/homebridge-flume/wiki/Beta-Version)
|
|
31
|
+
- [Node Version](https://github.com/bwp91/homebridge-flume/wiki/Node-Version)
|
|
32
|
+
- [Uninstallation](https://github.com/bwp91/homebridge-flume/wiki/Uninstallation)
|
|
33
|
+
|
|
34
|
+
### Help/About
|
|
35
|
+
|
|
36
|
+
- [Common Errors](https://github.com/bwp91/homebridge-flume/wiki/Common-Errors)
|
|
37
|
+
- [Support Request](https://github.com/bwp91/homebridge-flume/issues/new/choose)
|
|
38
|
+
- [Changelog](https://github.com/bwp91/homebridge-flume/blob/latest/CHANGELOG.md)
|
|
39
|
+
- [About Me](https://github.com/sponsors/bwp91)
|
|
40
|
+
|
|
41
|
+
### Credits
|
|
42
|
+
|
|
43
|
+
- This is a forked rewrite of the [homebridge-flume-water-sensor](https://www.npmjs.com/package/homebridge-flume-water-sensor) plugin by @weallknowwhoisatfaulthere
|
|
44
|
+
- To the creators/contributors of [Homebridge](https://homebridge.io) who make this plugin possible.
|
|
45
|
+
|
|
46
|
+
### Disclaimer
|
|
47
|
+
|
|
48
|
+
- I am in no way affiliated with Flume and this plugin is a personal project that I maintain in my free time.
|
|
49
|
+
- Use this plugin entirely at your own risk - please see licence for more information.
|
package/config.schema.json
CHANGED
|
@@ -1,58 +1,91 @@
|
|
|
1
1
|
{
|
|
2
2
|
"pluginAlias": "Flume",
|
|
3
|
-
"pluginType": "
|
|
3
|
+
"pluginType": "platform",
|
|
4
4
|
"singular": true,
|
|
5
|
-
"
|
|
5
|
+
"customUi": true,
|
|
6
|
+
"customUiPath": "./lib/homebridge-ui",
|
|
7
|
+
"headerDisplay": "<p align=\"center\">For help and support please visit our <a href=\"https://github.com/bwp91/homebridge-flume/wiki\">GitHub Wiki</a>. We hope you find this plugin useful!</p>",
|
|
6
8
|
"schema": {
|
|
7
9
|
"type": "object",
|
|
8
10
|
"properties": {
|
|
9
11
|
"name": {
|
|
10
|
-
"title": "
|
|
12
|
+
"title": "Plugin Name",
|
|
11
13
|
"type": "string",
|
|
12
|
-
"
|
|
13
|
-
"default": "Flume Water Monitor",
|
|
14
|
-
"description": "The name used for the Flume water monitor in HomeKit"
|
|
14
|
+
"default": "Flume"
|
|
15
15
|
},
|
|
16
16
|
"username": {
|
|
17
17
|
"title": "Username",
|
|
18
18
|
"type": "string",
|
|
19
19
|
"required": true,
|
|
20
|
-
"description": "Your username for
|
|
20
|
+
"description": "Your username for accessing the Flume site at https://portal.flumetech.com"
|
|
21
21
|
},
|
|
22
22
|
"password": {
|
|
23
23
|
"title": "Password",
|
|
24
24
|
"type": "string",
|
|
25
25
|
"required": true,
|
|
26
|
-
"description": "Your password for
|
|
26
|
+
"description": "Your password for accessing the Flume site"
|
|
27
27
|
},
|
|
28
28
|
"client_id": {
|
|
29
29
|
"title": "Client ID",
|
|
30
30
|
"type": "string",
|
|
31
31
|
"placeholder": "12345678901234567890",
|
|
32
32
|
"required": true,
|
|
33
|
-
"description": "Your Client ID for API access, found in Settings on the Flume site
|
|
33
|
+
"description": "Your Client ID for API access, found in Settings on the Flume site"
|
|
34
34
|
},
|
|
35
35
|
"client_secret": {
|
|
36
36
|
"title": "Client Secret",
|
|
37
37
|
"type": "string",
|
|
38
38
|
"placeholder": "1234567890",
|
|
39
39
|
"required": true,
|
|
40
|
-
"description": "Your Client Secret for API access, found in Settings on the Flume site
|
|
40
|
+
"description": "Your Client Secret for API access, found in Settings on the Flume site"
|
|
41
41
|
},
|
|
42
|
-
"
|
|
43
|
-
"title": "
|
|
42
|
+
"refreshInterval": {
|
|
43
|
+
"title": "Refresh interval",
|
|
44
44
|
"type": "integer",
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"description": "Number of minutes between updates. Defaults to 1 minute. API has a cap of 120 calls per hour. (optional)"
|
|
45
|
+
"placeholder": 1,
|
|
46
|
+
"description": "Number of minutes between updates. API has a cap of 120 calls per hour."
|
|
48
47
|
},
|
|
49
|
-
"
|
|
48
|
+
"threshold": {
|
|
50
49
|
"title": "Adjustment for False Positives",
|
|
51
|
-
"type": "
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
"type": "number",
|
|
51
|
+
"placeholder": 0,
|
|
52
|
+
"description": "Set to ignore a steady water draw below this value (gallons)."
|
|
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
|
+
"debug": {
|
|
60
|
+
"title": "Debug Logging",
|
|
61
|
+
"type": "boolean",
|
|
62
|
+
"description": "Global logging setting for the plugin. If true then debug information will be added to the log."
|
|
63
|
+
},
|
|
64
|
+
"disablePlugin": {
|
|
65
|
+
"title": "Disable Plugin",
|
|
66
|
+
"type": "boolean",
|
|
67
|
+
"description": "If true, the plugin will remove all accessories and not load the plugin on restart."
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"layout": [
|
|
72
|
+
{
|
|
73
|
+
"type": "fieldset",
|
|
74
|
+
"title": "Required Settings",
|
|
75
|
+
"items": ["username", "password", "client_id", "client_secret"]
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"type": "fieldset",
|
|
79
|
+
"title": "Optional Settings",
|
|
80
|
+
"description": "Optional settings for the plugin, including global logging settings.",
|
|
81
|
+
"expandable": true,
|
|
82
|
+
"items": [
|
|
83
|
+
"refreshInterval",
|
|
84
|
+
"threshold",
|
|
85
|
+
"disableDeviceLogging",
|
|
86
|
+
"debug",
|
|
87
|
+
"disablePlugin"
|
|
88
|
+
]
|
|
56
89
|
}
|
|
57
|
-
|
|
90
|
+
]
|
|
58
91
|
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/* jshint node: true, esversion: 10, -W014, -W033 */
|
|
2
|
+
/* eslint-disable new-cap */
|
|
3
|
+
'use strict'
|
|
4
|
+
|
|
5
|
+
const axios = require('axios')
|
|
6
|
+
const jwtDecode = require('jwt-decode')
|
|
7
|
+
|
|
8
|
+
module.exports = class connectionHTTP {
|
|
9
|
+
constructor (platform) {
|
|
10
|
+
// Create variables usable by the class
|
|
11
|
+
this.consts = platform.consts
|
|
12
|
+
this.debug = platform.config.debug
|
|
13
|
+
this.funcs = platform.funcs
|
|
14
|
+
this.lang = platform.lang
|
|
15
|
+
this.log = platform.log
|
|
16
|
+
this.username = platform.config.username
|
|
17
|
+
this.password = platform.config.password
|
|
18
|
+
this.client_id = platform.config.client_id
|
|
19
|
+
this.client_secret = platform.config.client_secret
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async obtainToken () {
|
|
23
|
+
try {
|
|
24
|
+
// Generate the JSON data to send
|
|
25
|
+
const body = {
|
|
26
|
+
grant_type: 'password',
|
|
27
|
+
client_id: this.client_id,
|
|
28
|
+
client_secret: this.client_secret,
|
|
29
|
+
username: this.username,
|
|
30
|
+
password: this.password
|
|
31
|
+
}
|
|
32
|
+
const now = Date.now()
|
|
33
|
+
|
|
34
|
+
// Perform the HTTP request
|
|
35
|
+
const res = await axios.post('https://api.flumetech.com/oauth/token', body, {
|
|
36
|
+
timeout: 10000
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// Check to see we got a response
|
|
40
|
+
if (!res.data) {
|
|
41
|
+
throw new Error(this.lang.noToken)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/*
|
|
45
|
+
{
|
|
46
|
+
success: true,
|
|
47
|
+
code: 602,
|
|
48
|
+
message: 'Request OK',
|
|
49
|
+
http_code: 200,
|
|
50
|
+
http_message: 'OK',
|
|
51
|
+
detailed: null,
|
|
52
|
+
data: [
|
|
53
|
+
{
|
|
54
|
+
token_type: 'bearer',
|
|
55
|
+
access_token: '',
|
|
56
|
+
expires_in: 604800,
|
|
57
|
+
refresh_token: ''
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
count: 1,
|
|
61
|
+
pagination: null
|
|
62
|
+
}
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
// Make the token available in other functions
|
|
66
|
+
this.accessToken = res.data.data[0].access_token
|
|
67
|
+
this.refreshToken = res.data.data[0].refresh_token
|
|
68
|
+
this.expiresIn = now + res.data.data[0].expires_in
|
|
69
|
+
|
|
70
|
+
// Obtain the user ID
|
|
71
|
+
this.userId = jwtDecode(this.accessToken).user_id
|
|
72
|
+
|
|
73
|
+
/*
|
|
74
|
+
{
|
|
75
|
+
user_id: 0000,
|
|
76
|
+
type: 'USER',
|
|
77
|
+
scope: [ 'read:personal', 'update:personal', 'query:personal' ],
|
|
78
|
+
iat: 0000000000,
|
|
79
|
+
exp: 0000000000,
|
|
80
|
+
iss: 'flume_oauth',
|
|
81
|
+
sub: ''
|
|
82
|
+
}
|
|
83
|
+
*/
|
|
84
|
+
} catch (err) {
|
|
85
|
+
if (err.code && this.consts.httpRetryCodes.includes(err.code)) {
|
|
86
|
+
// Retry if another attempt could be successful
|
|
87
|
+
this.log.warn('[HTTP login()] %s [%s].', this.lang.httpRetry, err.code)
|
|
88
|
+
await this.funcs.sleep(30000)
|
|
89
|
+
return await this.login()
|
|
90
|
+
} else {
|
|
91
|
+
throw new Error('[HTTP obtainToken()] ' + err.message)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async renewToken () {
|
|
97
|
+
try {
|
|
98
|
+
// Generate the JSON data to send
|
|
99
|
+
const body = {
|
|
100
|
+
grant_type: 'refresh_token',
|
|
101
|
+
client_id: this.client_id,
|
|
102
|
+
client_secret: this.client_secret,
|
|
103
|
+
refresh_token: this.refreshToken
|
|
104
|
+
}
|
|
105
|
+
const now = Date.now()
|
|
106
|
+
|
|
107
|
+
// Perform the HTTP request
|
|
108
|
+
const res = await axios.post('https://api.flumetech.com/oauth/token', body, {
|
|
109
|
+
timeout: 10000
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
// Check to see we got a response
|
|
113
|
+
if (!res.data) {
|
|
114
|
+
throw new Error(this.lang.noToken)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/*
|
|
118
|
+
{
|
|
119
|
+
success: true,
|
|
120
|
+
code: 602,
|
|
121
|
+
message: 'Request OK',
|
|
122
|
+
http_code: 200,
|
|
123
|
+
http_message: 'OK',
|
|
124
|
+
detailed: null,
|
|
125
|
+
data: [
|
|
126
|
+
{
|
|
127
|
+
token_type: 'bearer',
|
|
128
|
+
access_token: '',
|
|
129
|
+
expires_in: 604800,
|
|
130
|
+
refresh_token: ''
|
|
131
|
+
}
|
|
132
|
+
],
|
|
133
|
+
count: 1,
|
|
134
|
+
pagination: null
|
|
135
|
+
}
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
// Make the token available in other functions
|
|
139
|
+
this.accessToken = res.data.data[0].access_token
|
|
140
|
+
this.refreshToken = res.data.data[0].refresh_token
|
|
141
|
+
this.expiresIn = now + res.data.data[0].expires_in
|
|
142
|
+
} catch (err) {
|
|
143
|
+
if (err.code && this.consts.httpRetryCodes.includes(err.code)) {
|
|
144
|
+
// Retry if another attempt could be successful
|
|
145
|
+
this.log.warn('[HTTP login()] %s [%s].', this.lang.httpRetry, err.code)
|
|
146
|
+
await this.funcs.sleep(30000)
|
|
147
|
+
return await this.login()
|
|
148
|
+
} else {
|
|
149
|
+
throw new Error('[HTTP refreshToken()] ' + err.message)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async getDevices () {
|
|
155
|
+
try {
|
|
156
|
+
// Check we have a user id
|
|
157
|
+
if (!this.userId) {
|
|
158
|
+
throw new Error('No user id has been retrieved')
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Perform the HTTP request
|
|
162
|
+
const res = await axios.get('https://api.flumetech.com/users/' + this.userId + '/devices', {
|
|
163
|
+
headers: { Authorization: 'Bearer ' + this.accessToken },
|
|
164
|
+
timeout: 10000
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
// Check to see we got a response
|
|
168
|
+
if (!res.data) {
|
|
169
|
+
throw new Error('No data received')
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return res.data.data
|
|
173
|
+
} catch (err) {
|
|
174
|
+
if (err.code && this.consts.httpRetryCodes.includes(err.code)) {
|
|
175
|
+
// Retry if another attempt could be successful
|
|
176
|
+
this.log.warn('[HTTP login()] %s [%s].', this.lang.httpRetry, err.code)
|
|
177
|
+
await this.funcs.sleep(30000)
|
|
178
|
+
return await this.login()
|
|
179
|
+
} else {
|
|
180
|
+
throw new Error('[HTTP getDevices()] ' + err.message)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async getDeviceInfo (deviceId, fromWhen) {
|
|
186
|
+
// Refresh the access token if it has expired already
|
|
187
|
+
if (Date.now() > this.expiresIn) {
|
|
188
|
+
await this.renewToken()
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Generate the JSON data to send
|
|
192
|
+
const body = {
|
|
193
|
+
queries: [
|
|
194
|
+
{
|
|
195
|
+
request_id: 'currentusage',
|
|
196
|
+
bucket: 'MIN',
|
|
197
|
+
since_datetime: fromWhen,
|
|
198
|
+
operation: 'SUM',
|
|
199
|
+
units: 'GALLONS'
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Send the request
|
|
205
|
+
const res = await axios.post(
|
|
206
|
+
'https://api.flumetech.com/users/' + this.userId + '/devices/' + deviceId + '/query',
|
|
207
|
+
body,
|
|
208
|
+
{
|
|
209
|
+
headers: {
|
|
210
|
+
Authorization: 'Bearer ' + this.accessToken
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
// Parse the response
|
|
216
|
+
return res.data.data[0]
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* jshint node: true, esversion: 10, -W014, -W033 */
|
|
2
|
+
/* eslint-disable new-cap */
|
|
3
|
+
'use strict'
|
|
4
|
+
|
|
5
|
+
module.exports = class deviceValve {
|
|
6
|
+
constructor (platform, accessory) {
|
|
7
|
+
// Set up variables from the platform
|
|
8
|
+
this.accessory = accessory
|
|
9
|
+
this.disableDeviceLogging = platform.config.disableDeviceLogging
|
|
10
|
+
this.funcs = platform.funcs
|
|
11
|
+
this.threshold = platform.config.threshold
|
|
12
|
+
this.hapChar = platform.api.hap.Characteristic
|
|
13
|
+
this.hapErr = platform.api.hap.HapStatusError
|
|
14
|
+
this.hapServ = platform.api.hap.Service
|
|
15
|
+
this.lang = platform.lang
|
|
16
|
+
this.log = platform.log
|
|
17
|
+
this.name = accessory.displayName
|
|
18
|
+
this.platform = platform
|
|
19
|
+
this.refreshInterval = platform.config.refreshInterval
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
externalUpdate (params) {
|
|
23
|
+
// Here we deal with the incoming data
|
|
24
|
+
const usage =
|
|
25
|
+
params.currentusage && params.currentusage[0] && params.currentusage[0].value
|
|
26
|
+
? params.currentusage[0].value
|
|
27
|
+
: 0
|
|
28
|
+
if (usage > this.threshold) {
|
|
29
|
+
this.log(
|
|
30
|
+
'[%s] %s - [%s] gallons within the last [%s] minutes.',
|
|
31
|
+
this.name,
|
|
32
|
+
'usage detected',
|
|
33
|
+
usage,
|
|
34
|
+
this.refreshInterval
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
<div id="pageIntro" class="text-center" style="display: none;">
|
|
2
|
+
<p class="lead">Thank you for installing <strong>homebridge-flume</strong></p>
|
|
3
|
+
<p>
|
|
4
|
+
You will need your Flume username, password, client ID and secret to continue
|
|
5
|
+
</p>
|
|
6
|
+
<button type="button" class="btn btn-primary" id="introContinue">Continue →</button>
|
|
7
|
+
</div>
|
|
8
|
+
<div
|
|
9
|
+
id="menuWrapper"
|
|
10
|
+
class="btn-group w-100 mb-0"
|
|
11
|
+
role="group"
|
|
12
|
+
aria-label="UI Menu"
|
|
13
|
+
style="display: none;"
|
|
14
|
+
>
|
|
15
|
+
<button type="button" class="btn btn-primary ml-0" id="menuSettings">Settings</button>
|
|
16
|
+
<button type="button" class="btn btn-primary mr-0" id="menuHome">Support</button>
|
|
17
|
+
</div>
|
|
18
|
+
<div
|
|
19
|
+
id="disabledBanner"
|
|
20
|
+
class="alert alert-secondary mb-0 mt-3"
|
|
21
|
+
role="alert"
|
|
22
|
+
style="display: none;"
|
|
23
|
+
>
|
|
24
|
+
Plugin is currently disabled
|
|
25
|
+
<button id="disabledEnable" type="button" class="btn btn-link p-0 m-0 float-right">Enable</button>
|
|
26
|
+
</div>
|
|
27
|
+
<div id="pageSupport" class="mt-4" style="display: none;">
|
|
28
|
+
<p class="text-center lead">Thank you for using <strong>homebridge-flume</strong></p>
|
|
29
|
+
<p class="text-center">The links below will take you to our GitHub wiki</p>
|
|
30
|
+
<h4>Setup</h4>
|
|
31
|
+
<ul>
|
|
32
|
+
<li>
|
|
33
|
+
<a href="https://github.com/bwp91/homebridge-flume/wiki/Installation" target="_blank"
|
|
34
|
+
>Installation</a
|
|
35
|
+
>
|
|
36
|
+
</li>
|
|
37
|
+
<li>
|
|
38
|
+
<a href="https://github.com/bwp91/homebridge-flume/wiki/Configuration" target="_blank"
|
|
39
|
+
>Configuration</a
|
|
40
|
+
>
|
|
41
|
+
</li>
|
|
42
|
+
<li>
|
|
43
|
+
<a href="https://github.com/bwp91/homebridge-flume/wiki/Beta-Version" target="_blank"
|
|
44
|
+
>Beta Version</a
|
|
45
|
+
>
|
|
46
|
+
</li>
|
|
47
|
+
<li>
|
|
48
|
+
<a href="https://github.com/bwp91/homebridge-flume/wiki/Node-Version" target="_blank"
|
|
49
|
+
>Node Version</a
|
|
50
|
+
>
|
|
51
|
+
</li>
|
|
52
|
+
<li>
|
|
53
|
+
<a href="https://github.com/bwp91/homebridge-flume/wiki/Uninstallation" target="_blank"
|
|
54
|
+
>Uninstallation</a
|
|
55
|
+
>
|
|
56
|
+
</li>
|
|
57
|
+
</ul>
|
|
58
|
+
<h4>Help/About</h4>
|
|
59
|
+
<ul>
|
|
60
|
+
<li>
|
|
61
|
+
<a href="https://github.com/bwp91/homebridge-flume/wiki/Common-Errors" target="_blank"
|
|
62
|
+
>Common Errors</a
|
|
63
|
+
>
|
|
64
|
+
</li>
|
|
65
|
+
<li>
|
|
66
|
+
<a href="https://github.com/bwp91/homebridge-flume/issues/new/choose" target="_blank"
|
|
67
|
+
>Support Request</a
|
|
68
|
+
>
|
|
69
|
+
</li>
|
|
70
|
+
<li>
|
|
71
|
+
<a href="https://github.com/bwp91/homebridge-flume/blob/latest/CHANGELOG.md" target="_blank"
|
|
72
|
+
>Changelog</a
|
|
73
|
+
>
|
|
74
|
+
</li>
|
|
75
|
+
<li><a href="https://github.com/sponsors/bwp91" target="_blank">About Me</a></li>
|
|
76
|
+
</ul>
|
|
77
|
+
<h4>Credits</h4>
|
|
78
|
+
<ul>
|
|
79
|
+
<li>
|
|
80
|
+
This is a forked rewrite of the <a href="https://www.npmjs.com/package/homebridge-flume-water-sensor" target="_blank">homebridge-flume-water-sensor</a> plugin by @weallknowwhoisatfaulthere
|
|
81
|
+
</li>
|
|
82
|
+
<li>
|
|
83
|
+
To the creators/contributors of
|
|
84
|
+
<a href="https://homebridge.io" target="_blank">Homebridge</a> who make this plugin possible.
|
|
85
|
+
</li>
|
|
86
|
+
</ul>
|
|
87
|
+
<h4>Disclaimer</h4>
|
|
88
|
+
<ul>
|
|
89
|
+
<li>
|
|
90
|
+
I am in no way affiliated with Flume and this plugin is a personal project that I maintain in
|
|
91
|
+
my free time.
|
|
92
|
+
</li>
|
|
93
|
+
<li>Use this plugin entirely at your own risk - please see licence for more information.</li>
|
|
94
|
+
</ul>
|
|
95
|
+
</div>
|
|
96
|
+
<script>
|
|
97
|
+
;(async () => {
|
|
98
|
+
try {
|
|
99
|
+
const currentConfig = await homebridge.getPluginConfig()
|
|
100
|
+
showIntro = () => {
|
|
101
|
+
const introContinue = document.getElementById('introContinue')
|
|
102
|
+
introContinue.addEventListener('click', () => {
|
|
103
|
+
homebridge.showSpinner()
|
|
104
|
+
document.getElementById('pageIntro').style.display = 'none'
|
|
105
|
+
document.getElementById('menuWrapper').style.display = 'inline-flex'
|
|
106
|
+
showSettings()
|
|
107
|
+
homebridge.hideSpinner()
|
|
108
|
+
})
|
|
109
|
+
document.getElementById('pageIntro').style.display = 'block'
|
|
110
|
+
}
|
|
111
|
+
showSupport = () => {
|
|
112
|
+
homebridge.showSpinner()
|
|
113
|
+
homebridge.hideSchemaForm()
|
|
114
|
+
document.getElementById('menuHome').classList.add('btn-elegant')
|
|
115
|
+
document.getElementById('menuHome').classList.remove('btn-primary')
|
|
116
|
+
document.getElementById('menuSettings').classList.remove('btn-elegant')
|
|
117
|
+
document.getElementById('menuSettings').classList.add('btn-primary')
|
|
118
|
+
document.getElementById('pageSupport').style.display = 'block'
|
|
119
|
+
homebridge.hideSpinner()
|
|
120
|
+
}
|
|
121
|
+
showSettings = () => {
|
|
122
|
+
homebridge.showSpinner()
|
|
123
|
+
document.getElementById('menuHome').classList.remove('btn-elegant')
|
|
124
|
+
document.getElementById('menuHome').classList.add('btn-primary')
|
|
125
|
+
document.getElementById('menuSettings').classList.add('btn-elegant')
|
|
126
|
+
document.getElementById('menuSettings').classList.remove('btn-primary')
|
|
127
|
+
document.getElementById('pageSupport').style.display = 'none'
|
|
128
|
+
homebridge.showSchemaForm()
|
|
129
|
+
homebridge.hideSpinner()
|
|
130
|
+
}
|
|
131
|
+
showDisabledBanner = () => {
|
|
132
|
+
document.getElementById('disabledBanner').style.display = 'block'
|
|
133
|
+
}
|
|
134
|
+
enablePlugin = async () => {
|
|
135
|
+
homebridge.showSpinner()
|
|
136
|
+
document.getElementById('disabledBanner').style.display = 'none'
|
|
137
|
+
currentConfig[0].disablePlugin = false
|
|
138
|
+
await homebridge.updatePluginConfig(currentConfig)
|
|
139
|
+
await homebridge.savePluginConfig()
|
|
140
|
+
homebridge.hideSpinner()
|
|
141
|
+
}
|
|
142
|
+
menuHome.addEventListener('click', () => showSupport())
|
|
143
|
+
menuSettings.addEventListener('click', () => showSettings())
|
|
144
|
+
disabledEnable.addEventListener('click', () => enablePlugin())
|
|
145
|
+
if (currentConfig.length) {
|
|
146
|
+
document.getElementById('menuWrapper').style.display = 'inline-flex'
|
|
147
|
+
showSettings()
|
|
148
|
+
if (currentConfig[0].disablePlugin) {
|
|
149
|
+
showDisabledBanner()
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
currentConfig.push({ name: 'Flume' })
|
|
153
|
+
await homebridge.updatePluginConfig(currentConfig)
|
|
154
|
+
showIntro()
|
|
155
|
+
}
|
|
156
|
+
} catch (err) {
|
|
157
|
+
homebridge.toast.error(err.message, 'Error')
|
|
158
|
+
} finally {
|
|
159
|
+
homebridge.hideSpinner()
|
|
160
|
+
}
|
|
161
|
+
})()
|
|
162
|
+
</script>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* jshint node: true, esversion: 10, -W014, -W033 */
|
|
2
|
+
/* eslint-disable new-cap */
|
|
3
|
+
'use strict'
|
|
4
|
+
|
|
5
|
+
const { HomebridgePluginUiServer } = require('@homebridge/plugin-ui-utils')
|
|
6
|
+
|
|
7
|
+
class PluginUiServer extends HomebridgePluginUiServer {
|
|
8
|
+
constructor () {
|
|
9
|
+
super()
|
|
10
|
+
this.ready()
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
;(() => new PluginUiServer())()
|