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 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
- # homebridge-flume
2
-
3
- # Configuration
4
-
5
- Configuration sample:
6
-
7
- ```
8
- "accessories": [
9
- {
10
- "accessory": "Flume",
11
- "name": "Flume Water Monitor",
12
- "username": "user@domain.com",
13
- "password": "password",
14
- "client_id": "12345678901234567890",
15
- "client_secret": "1234567890",
16
- "polling_minutes": 1,
17
- "handicap": 0.1,
18
- "valveType": 3
19
- }
20
- ]
21
- ```
22
-
23
- Fields:
24
-
25
- - "accessory": Must always be "Flume" (required)
26
- - "username": Your username for accessing the Flume site at https://portal.flumetech.com/ (required)
27
- - "password": Your password for accessing the Flume site (required)
28
- - "client_id": Your Client ID for API access, found in Settings on the Flume site (required)
29
- - "client_secret": Your Client Secret for API access, found in Settings on the Flume site (required)
30
- - "polling_minutes": Number of minutes between updates. Defaults to 1 minute. API has a cap of 120 calls per hour. (optional)
31
- - "handicap": Set to ignore a steady water draw below this value. Defaults to zero (0.0) gallons. (optional)
32
-
33
- # Retrieving API Login Credentials
34
-
35
- You'll need to get your API Access Client ID and Client Secret from Flume's site at https://portal.flumetech.com/
36
- This guide offers the steps to take: https://flumetech.readme.io/reference#accessing-the-api
1
+ <span align="center">
2
+
3
+ # homebridge-flume
4
+
5
+ Homebridge plugin to integrate Flume devices into HomeKit
6
+
7
+ [![verified-by-homebridge](https://badgen.net/badge/homebridge/verified/purple)](https://github.com/homebridge/homebridge/wiki/Verified-Plugins)
8
+ [![npm](https://img.shields.io/npm/v/homebridge-flume/latest?label=latest)](https://www.npmjs.com/package/homebridge-flume)
9
+ [![npm](https://img.shields.io/npm/v/homebridge-flume/beta?label=beta)](https://github.com/bwp91/homebridge-flume/wiki/Beta-Version)
10
+ [![npm](https://img.shields.io/npm/dt/homebridge-flume)](https://www.npmjs.com/package/homebridge-flume)
11
+ [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
12
+ [![Discord](https://img.shields.io/discord/784827113378676736?color=728ED5&logo=discord&label=bwp91-discord)](https://discord.com/channels/784827113378676736/784827113378676739)
13
+ [![Discord](https://img.shields.io/discord/432663330281226270?color=728ED5&logo=discord&label=hb-discord)](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.
@@ -1,58 +1,91 @@
1
1
  {
2
2
  "pluginAlias": "Flume",
3
- "pluginType": "accessory",
3
+ "pluginType": "platform",
4
4
  "singular": true,
5
- "headerDisplay": "[Flume Water Monitor](https://flumetech.com) plugin for Homebridge.",
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": "Accessory Name",
12
+ "title": "Plugin Name",
11
13
  "type": "string",
12
- "required": true,
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 acessing the Flume site at https://portal.flumetech.com/ (required)"
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 acessing the Flume site (required)"
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 (required)"
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 (required)"
40
+ "description": "Your Client Secret for API access, found in Settings on the Flume site"
41
41
  },
42
- "polling_minutes": {
43
- "title": "Update Interval",
42
+ "refreshInterval": {
43
+ "title": "Refresh interval",
44
44
  "type": "integer",
45
- "default": "1",
46
- "required": false,
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
- "handicap": {
48
+ "threshold": {
50
49
  "title": "Adjustment for False Positives",
51
- "type": "float",
52
- "default": "0.0",
53
- "required": false,
54
- "description": "Set to ignore a steady water draw below this value. Defaults to zero (0.0) gallons. (optional)"
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 &rarr;</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())()