@homebridge-plugins/homebridge-meross 10.8.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +1346 -0
  2. package/LICENSE +21 -0
  3. package/README.md +68 -0
  4. package/config.schema.json +2066 -0
  5. package/eslint.config.js +49 -0
  6. package/lib/connection/http.js +345 -0
  7. package/lib/connection/mqtt.js +174 -0
  8. package/lib/device/baby.js +532 -0
  9. package/lib/device/cooler-single.js +447 -0
  10. package/lib/device/diffuser.js +730 -0
  11. package/lib/device/fan.js +530 -0
  12. package/lib/device/garage-main.js +225 -0
  13. package/lib/device/garage-single.js +495 -0
  14. package/lib/device/garage-sub.js +376 -0
  15. package/lib/device/heater-single.js +445 -0
  16. package/lib/device/hub-contact.js +56 -0
  17. package/lib/device/hub-leak.js +86 -0
  18. package/lib/device/hub-main.js +403 -0
  19. package/lib/device/hub-sensor.js +115 -0
  20. package/lib/device/hub-smoke.js +40 -0
  21. package/lib/device/hub-valve.js +377 -0
  22. package/lib/device/humidifier.js +521 -0
  23. package/lib/device/index.js +63 -0
  24. package/lib/device/light-cct.js +474 -0
  25. package/lib/device/light-dimmer.js +312 -0
  26. package/lib/device/light-rgb.js +528 -0
  27. package/lib/device/outlet-multi.js +383 -0
  28. package/lib/device/outlet-single.js +405 -0
  29. package/lib/device/power-strip.js +282 -0
  30. package/lib/device/purifier-single.js +372 -0
  31. package/lib/device/purifier.js +403 -0
  32. package/lib/device/roller-location.js +317 -0
  33. package/lib/device/roller.js +234 -0
  34. package/lib/device/sensor-presence.js +201 -0
  35. package/lib/device/switch-multi.js +403 -0
  36. package/lib/device/switch-single.js +371 -0
  37. package/lib/device/template.js +177 -0
  38. package/lib/device/thermostat.js +493 -0
  39. package/lib/fakegato/LICENSE +21 -0
  40. package/lib/fakegato/fakegato-history.js +814 -0
  41. package/lib/fakegato/fakegato-storage.js +108 -0
  42. package/lib/fakegato/fakegato-timer.js +125 -0
  43. package/lib/fakegato/uuid.js +27 -0
  44. package/lib/homebridge-ui/public/index.html +316 -0
  45. package/lib/homebridge-ui/server.js +10 -0
  46. package/lib/index.js +8 -0
  47. package/lib/platform.js +1256 -0
  48. package/lib/utils/colour.js +581 -0
  49. package/lib/utils/constants.js +377 -0
  50. package/lib/utils/custom-chars.js +165 -0
  51. package/lib/utils/eve-chars.js +130 -0
  52. package/lib/utils/functions.js +39 -0
  53. package/lib/utils/lang-en.js +114 -0
  54. package/package.json +70 -0
@@ -0,0 +1,108 @@
1
+ import fs from 'node:fs'
2
+ import os from 'node:os'
3
+ import path from 'node:path'
4
+
5
+ const hostname = os.hostname().split('.')[0]
6
+
7
+ export default class {
8
+ constructor(params) {
9
+ if (!params) {
10
+ params = {}
11
+ }
12
+ this.writers = []
13
+ this.log = params.log || {}
14
+ if (!this.log) {
15
+ this.log = () => {}
16
+ }
17
+ this.addingWriter = false
18
+ }
19
+
20
+ addWriter(service, params) {
21
+ if (!this.addingWriter) {
22
+ this.addingWriter = true
23
+ if (!params) {
24
+ params = {}
25
+ }
26
+ this.log('[%s] FGS addWriter().', service.accessoryName)
27
+ const newWriter = {
28
+ service,
29
+ callback: params.callback,
30
+ fileName: `${hostname}_${service.accessoryName}_persist.json`,
31
+ }
32
+ const onReady = typeof params.onReady === 'function' ? params.onReady : () => {}
33
+ newWriter.storageHandler = fs
34
+ newWriter.path = params.path || path.join(os.homedir(), '.homebridge')
35
+ this.writers.push(newWriter)
36
+ this.addingWriter = false
37
+ onReady()
38
+ } else {
39
+ setTimeout(() => this.addWriter(service, params), 100)
40
+ }
41
+ }
42
+
43
+ getWriter(service) {
44
+ return this.writers.find(ele => ele.service === service)
45
+ }
46
+
47
+ _getWriterIndex(service) {
48
+ return this.writers.findIndex(ele => ele.service === service)
49
+ }
50
+
51
+ getWriters() {
52
+ return this.writers
53
+ }
54
+
55
+ delWriter(service) {
56
+ const index = this._getWriterIndex(service)
57
+ this.writers.splice(index, 1)
58
+ }
59
+
60
+ write(params) {
61
+ if (!this.writing) {
62
+ this.writing = true
63
+ const writer = this.getWriter(params.service)
64
+ const callBack = typeof params.callback === 'function'
65
+ ? params.callback
66
+ : typeof writer.callback === 'function'
67
+ ? writer.callback
68
+ : () => {}
69
+ const fileLoc = path.join(writer.path, writer.fileName)
70
+ this.log(
71
+ '[%s] FGS write file [%s] [%s].',
72
+ params.service.accessoryName,
73
+ fileLoc,
74
+ params.data.substr(1, 80),
75
+ )
76
+ writer.storageHandler.writeFile(fileLoc, params.data, 'utf8', (...args) => {
77
+ this.writing = false
78
+ callBack(args)
79
+ })
80
+ } else {
81
+ setTimeout(() => this.write(params), 100)
82
+ }
83
+ }
84
+
85
+ read(params) {
86
+ const writer = this.getWriter(params.service)
87
+ const callBack = typeof params.callback === 'function'
88
+ ? params.callback
89
+ : typeof writer.callback === 'function'
90
+ ? writer.callback
91
+ : () => {}
92
+ const fileLoc = path.join(writer.path, writer.fileName)
93
+ this.log('[%s] FGS read file [%s].', params.service.accessoryName, fileLoc)
94
+ writer.storageHandler.readFile(fileLoc, 'utf8', callBack)
95
+ }
96
+
97
+ remove(params) {
98
+ const writer = this.getWriter(params.service)
99
+ const callBack = typeof params.callback === 'function'
100
+ ? params.callback
101
+ : typeof writer.callback === 'function'
102
+ ? writer.callback
103
+ : () => {}
104
+ const fileLoc = path.join(writer.path, writer.fileName)
105
+ this.log('[%s] FGS delete file [%s].', params.service.accessoryName, fileLoc)
106
+ writer.storageHandler.unlink(fileLoc, callBack)
107
+ }
108
+ }
@@ -0,0 +1,125 @@
1
+ export default class {
2
+ constructor(params) {
3
+ if (!params) {
4
+ params = {}
5
+ }
6
+ this.subscribedServices = []
7
+ this.minutes = params.minutes || 10
8
+ this.intervalID = null
9
+ this.running = false
10
+ this.log = params.log || {}
11
+ if (!this.log) {
12
+ this.log = () => {}
13
+ }
14
+ }
15
+
16
+ subscribe(service, callback) {
17
+ this.log('[%s] FGT new subscription.', service.accessoryName)
18
+ const newService = {
19
+ service,
20
+ callback,
21
+ backLog: [],
22
+ previousBackLog: [],
23
+ previousAvrg: {},
24
+ }
25
+ this.subscribedServices.push(newService)
26
+ }
27
+
28
+ getSubscriber(service) {
29
+ return this.subscribedServices.find(el => el.service === service)
30
+ }
31
+
32
+ _getSubscriberIndex(service) {
33
+ return this.subscribedServices.findIndex(el => el.service === service)
34
+ }
35
+
36
+ getSubscribers() {
37
+ return this.subscribedServices
38
+ }
39
+
40
+ unsubscribe(service) {
41
+ const index = this._getSubscriberIndex(service)
42
+ this.subscribedServices.splice(index, 1)
43
+ if (this.subscribedServices.length === 0 && this.running) {
44
+ this.stop()
45
+ }
46
+ }
47
+
48
+ start() {
49
+ this.log('Starting global FGT [%s minutes].', this.minutes)
50
+ if (this.running) {
51
+ this.stop()
52
+ }
53
+ this.running = true
54
+ this.intervalID = setInterval(this.executeCallbacks.bind(this), this.minutes * 60 * 1000)
55
+ }
56
+
57
+ stop() {
58
+ this.log('Stopping global FGT.')
59
+ clearInterval(this.intervalID)
60
+ this.running = false
61
+ this.intervalID = null
62
+ }
63
+
64
+ executeCallbacks() {
65
+ this.log('FGT executeCallbacks().')
66
+ if (this.subscribedServices.length !== 0) {
67
+ for (const s in this.subscribedServices) {
68
+ if (Object.prototype.hasOwnProperty.call(this.subscribedServices, s)) {
69
+ const service = this.subscribedServices[s]
70
+ if (typeof service.callback === 'function') {
71
+ service.previousAvrg = service.callback({
72
+ backLog: service.backLog,
73
+ previousAvrg: service.previousAvrg,
74
+ timer: this,
75
+ immediate: false,
76
+ })
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+
83
+ executeImmediateCallback(service) {
84
+ this.log('[%s] FGT executeImmediateCallback().', service.accessoryName)
85
+ if (typeof service.callback === 'function' && service.backLog.length) {
86
+ service.callback({
87
+ backLog: service.backLog,
88
+ timer: this,
89
+ immediate: true,
90
+ })
91
+ }
92
+ }
93
+
94
+ addData(params) {
95
+ const data = params.entry
96
+ const { service } = params
97
+ const immediateCallback = params.immediateCallback || false
98
+ this.log(
99
+ '[%s] FGT addData() [%s] immediate [%s].',
100
+ service.accessoryName,
101
+ data,
102
+ immediateCallback,
103
+ )
104
+ if (immediateCallback) {
105
+ this.getSubscriber(service).backLog[0] = data
106
+ } else {
107
+ this.getSubscriber(service).backLog.push(data)
108
+ }
109
+ if (immediateCallback) {
110
+ this.executeImmediateCallback(this.getSubscriber(service))
111
+ }
112
+ if (!this.running) {
113
+ this.start()
114
+ }
115
+ }
116
+
117
+ emptyData(service) {
118
+ this.log('[%s] FGT emptyData().', service.accessoryName)
119
+ const source = this.getSubscriber(service)
120
+ if (source.backLog.length) {
121
+ source.previousBackLog = source.backLog
122
+ }
123
+ source.backLog = []
124
+ }
125
+ }
@@ -0,0 +1,27 @@
1
+ // https://github.com/homebridge/HAP-NodeJS/blob/master/src/lib/util/uuid.ts
2
+
3
+ function isValid(UUID) {
4
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
5
+ return uuidRegex.test(UUID)
6
+ }
7
+
8
+ function toLongFormUUID(uuid, base = '-0000-1000-8000-0026BB765291') {
9
+ const shortRegex = /^[0-9a-f]{1,8}$/i
10
+ if (isValid(uuid)) {
11
+ return uuid.toUpperCase()
12
+ }
13
+ if (!shortRegex.test(uuid)) {
14
+ throw new TypeError('uuid was not a valid UUID or short form UUID')
15
+ }
16
+ if (!isValid(`00000000${base}`)) {
17
+ throw new TypeError('base was not a valid base UUID')
18
+ }
19
+ return ((`00000000${uuid}`).substr(-8) + base).toUpperCase()
20
+ }
21
+
22
+ function toShortFormUUID(uuid, base = '-0000-1000-8000-0026BB765291') {
23
+ uuid = toLongFormUUID(uuid, base)
24
+ return uuid.substr(0, 8)
25
+ }
26
+
27
+ export { toLongFormUUID, toShortFormUUID }
@@ -0,0 +1,316 @@
1
+ <p class="text-center">
2
+ <img
3
+ src="https://user-images.githubusercontent.com/43026681/127397024-8b15fc07-f31b-44bd-89e3-51d738d2609a.png"
4
+ alt="homebridge-meross logo"
5
+ style="width: 60%;"
6
+ />
7
+ </p>
8
+ <div id="pageIntro" class="text-center" style="display: none;">
9
+ <p class="lead">Thank you for installing <strong>homebridge-meross</strong></p>
10
+ <p>
11
+ You can enter your Meross username and password on the next page
12
+ </p>
13
+ <button type="button" class="btn btn-primary" id="introContinue">
14
+ Continue &rarr;
15
+ </button>
16
+ </div>
17
+ <div
18
+ id="menuWrapper"
19
+ class="btn-group w-100 mb-0"
20
+ role="group"
21
+ aria-label="UI Menu"
22
+ style="display: none;"
23
+ >
24
+ <button type="button" class="btn btn-primary ml-0" id="menuSettings">
25
+ Settings
26
+ </button>
27
+ <button type="button" class="btn btn-primary" id="menuDevices">
28
+ My Devices
29
+ </button>
30
+ <button type="button" class="btn btn-primary mr-0" id="menuHome">
31
+ Support
32
+ </button>
33
+ </div>
34
+ <div id="pageDevices" class="mt-4" style="display: none;">
35
+ <div id="deviceInfo">
36
+ <form>
37
+ <div class="form-group">
38
+ <select class="form-control" id="deviceSelect"></select>
39
+ </div>
40
+ </form>
41
+ <table class="table w-100" id="deviceTable" style="display: none;">
42
+ <thead>
43
+ <tr class="table-active">
44
+ <th scope="col" style="width: 40%;">Device Name</th>
45
+ <th scope="col" style="width: 60%;" id="displayName"></th>
46
+ </tr>
47
+ </thead>
48
+ <tbody>
49
+ <tr>
50
+ <th scope="row">Status</th>
51
+ <td id="status"></td>
52
+ </tr>
53
+ <tr>
54
+ <th scope="row">Connection Type</th>
55
+ <td id="connection_type"></td>
56
+ </tr>
57
+ <tr>
58
+ <th scope="row">Device UUID</th>
59
+ <td id="serial_number"></td>
60
+ </tr>
61
+ <tr>
62
+ <th scope="row">Model</th>
63
+ <td id="model"></td>
64
+ </tr>
65
+ <tr>
66
+ <th scope="row">IP Address</th>
67
+ <td id="ip_address"></td>
68
+ </tr>
69
+ <tr>
70
+ <th scope="row">MAC Address</th>
71
+ <td id="mac_address"></td>
72
+ </tr>
73
+ <tr>
74
+ <th scope="row">Hardware Version</th>
75
+ <td id="hardware"></td>
76
+ </tr>
77
+ <tr>
78
+ <th scope="row">Firmware Version</th>
79
+ <td id="firmware"></td>
80
+ </tr>
81
+ </tbody>
82
+ </table>
83
+ </div>
84
+ </div>
85
+ <div id="pageSupport" class="mt-4" style="display: none;">
86
+ <p class="text-center lead">Thank you for using <strong>homebridge-meross</strong></p>
87
+ <p class="text-center">The links below will take you to our GitHub wiki</p>
88
+ <h4>Setup</h4>
89
+ <ul>
90
+ <li>
91
+ <a href="https://github.com/homebridge-plugins/homebridge-meross/wiki/Installation" target="_blank"
92
+ >Installation</a
93
+ >
94
+ </li>
95
+ <li>
96
+ <a href="https://github.com/homebridge-plugins/homebridge-meross/wiki/Configuration" target="_blank"
97
+ >Configuration</a
98
+ >
99
+ </li>
100
+ <li>
101
+ <a href="https://github.com/homebridge/homebridge/wiki/How-to-Install-Alternate-Plugin-Versions" target="_blank"
102
+ >Beta Version</a
103
+ >
104
+ </li>
105
+ <li>
106
+ <a href="https://github.com/homebridge-plugins/homebridge-meross/wiki/Node-Version" target="_blank"
107
+ >Node Version</a
108
+ >
109
+ </li>
110
+ </ul>
111
+ <h4>Features</h4>
112
+ <ul>
113
+ <li>
114
+ <a href="https://github.com/homebridge-plugins/homebridge-meross/wiki/Supported-Devices" target="_blank"
115
+ >Supported Devices</a
116
+ >
117
+ </li>
118
+ <li>
119
+ <a href="https://github.com/homebridge-plugins/homebridge-meross/wiki/Cloud-Control" target="_blank"
120
+ >Cloud Control</a
121
+ >
122
+ </li>
123
+ <li>
124
+ <a href="https://github.com/homebridge-plugins/homebridge-meross/wiki/Local-Control" target="_blank"
125
+ >Local Control</a
126
+ >
127
+ </li>
128
+ </ul>
129
+ <h4>Help/About</h4>
130
+ <ul>
131
+ <li>
132
+ <a href="https://github.com/homebridge-plugins/homebridge-meross/wiki/Common-Errors" target="_blank"
133
+ >Common Errors</a
134
+ >
135
+ </li>
136
+ <li>
137
+ <a href="https://github.com/homebridge-plugins/homebridge-meross/issues/new/choose" target="_blank"
138
+ >Support Request</a
139
+ >
140
+ </li>
141
+ <li>
142
+ <a href="https://github.com/homebridge-plugins/homebridge-meross/blob/latest/CHANGELOG.md" target="_blank"
143
+ >Changelog</a
144
+ >
145
+ </li>
146
+ <li>
147
+ <a href="https://github.com/sponsors/bwp91" target="_blank">About Me</a>
148
+ </li>
149
+ </ul>
150
+ <h4>Credits</h4>
151
+ <ul>
152
+ <li>
153
+ This is a fork of the work originally done by
154
+ <a href="https://github.com/Robdel12" target="_blank">@Robdel12</a> and
155
+ <a href="https://github.com/dylanfrankcom" target="_blank">@dylanfrankcom</a>.
156
+ </li>
157
+ <li>
158
+ To <a href="https://github.com/donavanbecker" target="_blank">@donavanbecker</a> the previous
159
+ maintainer of this plugin.
160
+ </li>
161
+ <li>
162
+ To <a href="https://github.com/Apollon77" target="_blank">@Apollon77</a> and
163
+ <a href="https://github.com/colthreepv" target="_blank">@colthreepv</a> for the
164
+ <a href="https://github.com/Apollon77/meross-cloud" target="_blank">meross-cloud</a> library
165
+ (contained in this plugin).
166
+ </li>
167
+ <li>
168
+ To the creator of the awesome plugin header logo:
169
+ <a href="https://www.instagram.com/keryan.me" target="_blank">Keryan Belahcene</a>.
170
+ </li>
171
+ <li>
172
+ To the creators/contributors of
173
+ <a href="https://github.com/simont77/fakegato-history" target="_blank">Fakegato</a>:
174
+ <a href="https://github.com/simont77" target="_blank">@simont77</a> and
175
+ <a href="https://github.com/NorthernMan54" target="_blank">@NorthernMan54</a>.
176
+ </li>
177
+ <li>
178
+ To the creators/contributors of
179
+ <a href="https://homebridge.io" target="_blank">Homebridge</a> who make this plugin possible.
180
+ </li>
181
+ </ul>
182
+
183
+ <h4>Disclaimer</h4>
184
+ <ul>
185
+ <li>
186
+ I am in no way affiliated with Meross and this plugin is a personal project that I maintain in
187
+ my free time.
188
+ </li>
189
+ <li>
190
+ Use this plugin entirely at your own risk - please see licence for more information.
191
+ </li>
192
+ </ul>
193
+ </div>
194
+ <script>
195
+ ;(async () => {
196
+ try {
197
+ const currentConfig = await homebridge.getPluginConfig()
198
+ showIntro = () => {
199
+ const introContinue = document.getElementById('introContinue')
200
+ introContinue.addEventListener('click', () => {
201
+ homebridge.showSpinner()
202
+ document.getElementById('pageIntro').style.display = 'none'
203
+ document.getElementById('menuWrapper').style.display = 'inline-flex'
204
+ showSettings()
205
+ homebridge.hideSpinner()
206
+ })
207
+ document.getElementById('pageIntro').style.display = 'block'
208
+ }
209
+ showDevices = async () => {
210
+ homebridge.showSpinner()
211
+ homebridge.hideSchemaForm()
212
+ document.getElementById('menuHome').classList.remove('btn-elegant')
213
+ document.getElementById('menuHome').classList.add('btn-primary')
214
+ document.getElementById('menuDevices').classList.add('btn-elegant')
215
+ document.getElementById('menuDevices').classList.remove('btn-primary')
216
+ document.getElementById('menuSettings').classList.remove('btn-elegant')
217
+ document.getElementById('menuSettings').classList.add('btn-primary')
218
+ document.getElementById('pageSupport').style.display = 'none'
219
+ document.getElementById('pageDevices').style.display = 'block'
220
+ const cachedAccessories =
221
+ typeof homebridge.getCachedAccessories === 'function'
222
+ ? await homebridge.getCachedAccessories()
223
+ : await homebridge.request('/getCachedAccessories')
224
+ if (cachedAccessories.length > 0) {
225
+ cachedAccessories.sort((a, b) => {
226
+ return a.displayName.toLowerCase() > b.displayName.toLowerCase()
227
+ ? 1
228
+ : b.displayName.toLowerCase() > a.displayName.toLowerCase()
229
+ ? -1
230
+ : 0
231
+ })
232
+ }
233
+ const deviceSelect = document.getElementById('deviceSelect')
234
+ deviceSelect.innerHTML = ''
235
+ cachedAccessories.forEach(a => {
236
+ const option = document.createElement('option')
237
+ option.text = a.displayName
238
+ option.value = a.UUID
239
+ deviceSelect.add(option)
240
+ })
241
+ showDeviceInfo = async UUID => {
242
+ homebridge.showSpinner()
243
+ const thisAcc = cachedAccessories.find(x => x.UUID === UUID)
244
+ const context = thisAcc.context
245
+ document.getElementById('displayName').innerHTML = thisAcc.displayName
246
+ document.getElementById('serial_number').innerHTML =
247
+ context.subSerialNumber || context.serialNumber
248
+ document.getElementById('connection_type').innerHTML =
249
+ context.connection[0].toUpperCase() + context.connection.slice(1)
250
+ document.getElementById('status').innerHTML = context.isOnline
251
+ ? '<i class="fas fa-circle mr-1 green-text"></i> Online'
252
+ : '<i class="fas fa-circle mr-1 red-text"></i> Offline'
253
+
254
+ document.getElementById('model').innerHTML = context.model
255
+ document.getElementById('ip_address').innerHTML = context.ipAddress || 'N/A'
256
+ document.getElementById('mac_address').innerHTML = context.macAddress || 'N/A'
257
+ document.getElementById('hardware').innerHTML = context.hardware || 'N/A'
258
+ document.getElementById('firmware').innerHTML = context.firmware || 'N/A'
259
+ document.getElementById('deviceTable').style.display = 'inline-table'
260
+ homebridge.hideSpinner()
261
+ }
262
+ deviceSelect.addEventListener('change', event => showDeviceInfo(event.target.value))
263
+ if (cachedAccessories.length > 0) {
264
+ showDeviceInfo(cachedAccessories[0].UUID)
265
+ } else {
266
+ const option = document.createElement('option')
267
+ option.text = 'No Devices'
268
+ deviceSelect.add(option)
269
+ deviceSelect.disabled = true
270
+ }
271
+ homebridge.hideSpinner()
272
+ }
273
+ showSupport = () => {
274
+ homebridge.showSpinner()
275
+ homebridge.hideSchemaForm()
276
+ document.getElementById('menuHome').classList.add('btn-elegant')
277
+ document.getElementById('menuHome').classList.remove('btn-primary')
278
+ document.getElementById('menuDevices').classList.remove('btn-elegant')
279
+ document.getElementById('menuDevices').classList.add('btn-primary')
280
+ document.getElementById('menuSettings').classList.remove('btn-elegant')
281
+ document.getElementById('menuSettings').classList.add('btn-primary')
282
+ document.getElementById('pageSupport').style.display = 'block'
283
+ document.getElementById('pageDevices').style.display = 'none'
284
+ homebridge.hideSpinner()
285
+ }
286
+ showSettings = () => {
287
+ homebridge.showSpinner()
288
+ document.getElementById('menuHome').classList.remove('btn-elegant')
289
+ document.getElementById('menuHome').classList.add('btn-primary')
290
+ document.getElementById('menuDevices').classList.remove('btn-elegant')
291
+ document.getElementById('menuDevices').classList.add('btn-primary')
292
+ document.getElementById('menuSettings').classList.add('btn-elegant')
293
+ document.getElementById('menuSettings').classList.remove('btn-primary')
294
+ document.getElementById('pageSupport').style.display = 'none'
295
+ document.getElementById('pageDevices').style.display = 'none'
296
+ homebridge.showSchemaForm()
297
+ homebridge.hideSpinner()
298
+ }
299
+ menuHome.addEventListener('click', () => showSupport())
300
+ menuDevices.addEventListener('click', () => showDevices())
301
+ menuSettings.addEventListener('click', () => showSettings())
302
+ if (currentConfig.length) {
303
+ document.getElementById('menuWrapper').style.display = 'inline-flex'
304
+ showSettings()
305
+ } else {
306
+ currentConfig.push({ name: 'Meross' })
307
+ await homebridge.updatePluginConfig(currentConfig)
308
+ showIntro()
309
+ }
310
+ } catch (err) {
311
+ homebridge.toast.error(err.message, 'Error')
312
+ } finally {
313
+ homebridge.hideSpinner()
314
+ }
315
+ })()
316
+ </script>
@@ -0,0 +1,10 @@
1
+ import { HomebridgePluginUiServer } from '@homebridge/plugin-ui-utils'
2
+
3
+ class PluginUiServer extends HomebridgePluginUiServer {
4
+ constructor() {
5
+ super()
6
+ this.ready()
7
+ }
8
+ }
9
+
10
+ (() => new PluginUiServer())()
package/lib/index.js ADDED
@@ -0,0 +1,8 @@
1
+ import { createRequire } from 'node:module'
2
+
3
+ import merossPlatform from './platform.js'
4
+
5
+ const require = createRequire(import.meta.url)
6
+ const plugin = require('../package.json')
7
+
8
+ export default hb => hb.registerPlatform(plugin.alias, merossPlatform)