homebridge-generac 0.1.0-beta.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.
Files changed (71) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE +200 -0
  3. package/NOTICE +20 -0
  4. package/README.md +179 -0
  5. package/SECURITY.md +17 -0
  6. package/config.schema.json +133 -0
  7. package/dist/accessory.d.ts +54 -0
  8. package/dist/accessory.js +191 -0
  9. package/dist/accessory.js.map +1 -0
  10. package/dist/api.d.ts +43 -0
  11. package/dist/api.js +124 -0
  12. package/dist/api.js.map +1 -0
  13. package/dist/attention.d.ts +18 -0
  14. package/dist/attention.js +40 -0
  15. package/dist/attention.js.map +1 -0
  16. package/dist/auth.d.ts +56 -0
  17. package/dist/auth.js +412 -0
  18. package/dist/auth.js.map +1 -0
  19. package/dist/captures.d.ts +9 -0
  20. package/dist/captures.js +42 -0
  21. package/dist/captures.js.map +1 -0
  22. package/dist/cli.d.ts +2 -0
  23. package/dist/cli.js +116 -0
  24. package/dist/cli.js.map +1 -0
  25. package/dist/exercise.d.ts +42 -0
  26. package/dist/exercise.js +115 -0
  27. package/dist/exercise.js.map +1 -0
  28. package/dist/files.d.ts +5 -0
  29. package/dist/files.js +28 -0
  30. package/dist/files.js.map +1 -0
  31. package/dist/index.d.ts +3 -0
  32. package/dist/index.js +6 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/model.d.ts +74 -0
  35. package/dist/model.js +167 -0
  36. package/dist/model.js.map +1 -0
  37. package/dist/platform.d.ts +117 -0
  38. package/dist/platform.js +596 -0
  39. package/dist/platform.js.map +1 -0
  40. package/dist/settings.d.ts +75 -0
  41. package/dist/settings.js +115 -0
  42. package/dist/settings.js.map +1 -0
  43. package/dist/state.d.ts +28 -0
  44. package/dist/state.js +43 -0
  45. package/dist/state.js.map +1 -0
  46. package/dist/types.d.ts +98 -0
  47. package/dist/types.js +48 -0
  48. package/dist/types.js.map +1 -0
  49. package/dist/ui/server.d.ts +96 -0
  50. package/dist/ui/server.js +328 -0
  51. package/dist/ui/server.js.map +1 -0
  52. package/homebridge-ui/public/generac-banner.png +0 -0
  53. package/homebridge-ui/public/generac.css +255 -0
  54. package/homebridge-ui/public/index.css +510 -0
  55. package/homebridge-ui/public/index.html +14 -0
  56. package/homebridge-ui/public/js/api.js +31 -0
  57. package/homebridge-ui/public/js/app.js +1 -0
  58. package/homebridge-ui/public/js/card.js +27 -0
  59. package/homebridge-ui/public/js/copy.js +167 -0
  60. package/homebridge-ui/public/js/dom.js +247 -0
  61. package/homebridge-ui/public/js/footer.js +26 -0
  62. package/homebridge-ui/public/js/format.js +62 -0
  63. package/homebridge-ui/public/js/main.js +288 -0
  64. package/homebridge-ui/public/js/mark.js +34 -0
  65. package/homebridge-ui/public/js/model.js +120 -0
  66. package/homebridge-ui/public/js/sections/account.js +272 -0
  67. package/homebridge-ui/public/js/sections/generators.js +165 -0
  68. package/homebridge-ui/public/js/sections/settings.js +114 -0
  69. package/homebridge-ui/public/js/validate.js +45 -0
  70. package/homebridge-ui/server.js +8 -0
  71. package/package.json +70 -0
@@ -0,0 +1,596 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { GeneratorAccessory } from './accessory.js';
4
+ import { ApiError, backoffMs, InvalidGrantError, MobileLinkClient, readCredentials } from './api.js';
5
+ import { ATTENTION_NAME, AttentionAccessory } from './attention.js';
6
+ import { capturesDir, writeCapture } from './captures.js';
7
+ import { ExerciseTracker, inWatchWindow, msUntilWatchWindow, parseHHMM } from './exercise.js';
8
+ import { isActive, toGeneratorState } from './model.js';
9
+ import { credentialCandidates, dataDir, displayNameFor, firmwareVersion, PLATFORM_NAME, PLUGIN_NAME, pluginVersion, RESET_MARKER, resolveConfig, statePath, } from './settings.js';
10
+ import { buildStateFile, readStateFile, toGeneratorSnapshot, writeStateFile, } from './state.js';
11
+ import { DEVICE_TYPE, DEVICE_TYPE_LABEL } from './types.js';
12
+ /** How often the credentials file is re-read (SPEC section 4.4): for a sign-in while there is no working client, and for a Disconnect while there is one. */
13
+ export const CREDENTIAL_CHECK_MS = 60 * 1000;
14
+ /** Retry interval after `invalid_grant` (SPEC section 4.3). */
15
+ export const RECONNECT_RETRY_MS = 60 * 60 * 1000;
16
+ /** Backoff cap for other poll failures (SPEC section 8). */
17
+ export const BACKOFF_MAX_MS = 30 * 60 * 1000;
18
+ export class GeneracPlatform {
19
+ log;
20
+ api;
21
+ config;
22
+ version = pluginVersion();
23
+ /** What the accessories report as FirmwareRevision (SPEC section 7). */
24
+ firmware = firmwareVersion(this.version);
25
+ storagePath;
26
+ credentialsPath;
27
+ attentionUuid;
28
+ cached = new Map();
29
+ generators = new Map();
30
+ /** Exercise detection per generator (SPEC section 7), seeded from state.json on start. */
31
+ trackers = new Map();
32
+ holdTimers = new Map();
33
+ /** `lastExerciseAt` per generator as state.json held it on start; a missing entry means first run for that unit. */
34
+ persistedExercise = new Map();
35
+ others = new Map();
36
+ attention = null;
37
+ client = null;
38
+ credentials = null;
39
+ accountState = 'not_connected';
40
+ lastChecked;
41
+ reconnectLogged = false;
42
+ pollTimer = null;
43
+ credentialTimer = null;
44
+ inFlight = null;
45
+ failures = 0;
46
+ stopped = false;
47
+ /** Delay of the most recently scheduled poll in milliseconds, for the debug log and tests. */
48
+ nextPollMs = null;
49
+ /** The clock, replaceable by tests (the hold timer and the watch window read it). */
50
+ now = Date.now;
51
+ constructor(log, rawConfig, api) {
52
+ this.log = log;
53
+ this.api = api;
54
+ const raw = rawConfig;
55
+ this.config = resolveConfig(raw);
56
+ this.credentialsPath = raw.credentialsPath;
57
+ this.storagePath = api.user.storagePath();
58
+ this.attentionUuid = api.hap.uuid.generate(`${PLUGIN_NAME}:attention`);
59
+ // No global uncaughtException handler here: that was the original plugin's
60
+ // way of swallowing its own auth errors for the whole Homebridge process.
61
+ api.on('didFinishLaunching', () => void this.start());
62
+ api.on('shutdown', () => this.shutdown());
63
+ }
64
+ configureAccessory(accessory) {
65
+ this.cached.set(accessory.UUID, accessory);
66
+ }
67
+ /** Account state as the settings page sees it. */
68
+ get account() {
69
+ return this.accountState;
70
+ }
71
+ /**
72
+ * Runs once Homebridge has restored cached accessories. Public so tests can
73
+ * await the first poll.
74
+ */
75
+ async start() {
76
+ this.applyResetMarker();
77
+ this.setupAttention();
78
+ this.loadPersistedExercise();
79
+ if (this.loadCredentials()) {
80
+ await this.poll();
81
+ }
82
+ else {
83
+ this.writeState();
84
+ }
85
+ this.scheduleCredentialCheck();
86
+ }
87
+ shutdown() {
88
+ this.stopped = true;
89
+ if (this.pollTimer) {
90
+ clearTimeout(this.pollTimer);
91
+ this.pollTimer = null;
92
+ }
93
+ if (this.credentialTimer) {
94
+ clearTimeout(this.credentialTimer);
95
+ this.credentialTimer = null;
96
+ }
97
+ for (const timer of this.holdTimers.values()) {
98
+ clearTimeout(timer);
99
+ }
100
+ this.holdTimers.clear();
101
+ }
102
+ /**
103
+ * The settings page's Reset dialog leaves a marker (SPEC section 11.3 E, "Removes every generator and its sensors
104
+ * from the Home app"): every cached accessory goes on this start, once, and the marker with it.
105
+ */
106
+ applyResetMarker() {
107
+ const marker = path.join(dataDir(this.storagePath), RESET_MARKER);
108
+ if (!fs.existsSync(marker)) {
109
+ return;
110
+ }
111
+ const accessories = [...this.cached.values()];
112
+ if (accessories.length > 0) {
113
+ this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, accessories);
114
+ this.cached.clear();
115
+ }
116
+ fs.rmSync(marker, { force: true });
117
+ this.log.info(`Reset from the settings page: removed ${accessories.length} cached accessor${accessories.length === 1 ? 'y' : 'ies'}.`);
118
+ }
119
+ // ---------------------------------------------------------------------------
120
+ // Attention needed sensor (SPEC section 7)
121
+ // ---------------------------------------------------------------------------
122
+ setupAttention() {
123
+ const existing = this.cached.get(this.attentionUuid);
124
+ if (this.config.attentionSensor) {
125
+ let acc = existing;
126
+ if (!acc) {
127
+ acc = new this.api.platformAccessory(ATTENTION_NAME, this.attentionUuid, 10 /* this.api.hap.Categories.SENSOR */);
128
+ this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [acc]);
129
+ this.cached.set(this.attentionUuid, acc);
130
+ this.log.info(`Added the "${ATTENTION_NAME}" sensor`);
131
+ }
132
+ this.attention = new AttentionAccessory(this, acc);
133
+ this.attention.set(this.accountState === 'reconnect_needed');
134
+ }
135
+ else if (existing) {
136
+ this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existing]);
137
+ this.cached.delete(this.attentionUuid);
138
+ this.log.info(`Removed the "${ATTENTION_NAME}" sensor (attentionSensor is off)`);
139
+ }
140
+ }
141
+ // ---------------------------------------------------------------------------
142
+ // Credentials (SPEC section 4.4)
143
+ // ---------------------------------------------------------------------------
144
+ findCredentials() {
145
+ for (const file of credentialCandidates(this.storagePath, this.credentialsPath)) {
146
+ try {
147
+ const creds = readCredentials(file);
148
+ if (creds) {
149
+ return { file, creds };
150
+ }
151
+ }
152
+ catch (err) {
153
+ if (err.code === 'EACCES') {
154
+ this.once(`eacces-${file}`, 'warn', `Credentials at ${file} exist but are not readable by this user. Fix the file's permissions or sign in again as the user Homebridge runs as.`);
155
+ }
156
+ else if (fs.existsSync(file)) {
157
+ this.once(`parse-${file}-${err.message}`, 'warn', `Could not parse credentials at ${file}: ${err.message}`);
158
+ }
159
+ }
160
+ }
161
+ return null;
162
+ }
163
+ static signatureOf(file, creds) {
164
+ if (creds.created_at) {
165
+ return `${file}|${creds.created_at}`;
166
+ }
167
+ try {
168
+ return `${file}|mtime:${fs.statSync(file).mtimeMs}`;
169
+ }
170
+ catch {
171
+ return `${file}|unknown`;
172
+ }
173
+ }
174
+ /**
175
+ * Load credentials when a usable file exists and differs from what is loaded
176
+ * (compared by `created_at`). Returns true when a new client was created.
177
+ */
178
+ loadCredentials() {
179
+ const found = this.findCredentials();
180
+ if (!found) {
181
+ if (!this.client) {
182
+ const looked = credentialCandidates(this.storagePath, this.credentialsPath).join(', ');
183
+ this.once('no-credentials', 'error', 'No Mobile Link credentials found. Connect your account from the plugin\'s settings page, ' +
184
+ `or run \`homebridge-generac login\` in a terminal as the user Homebridge runs as. Looked in: ${looked}`);
185
+ }
186
+ return false;
187
+ }
188
+ const signature = GeneracPlatform.signatureOf(found.file, found.creds);
189
+ if (this.credentials && this.credentials.signature === signature) {
190
+ return false;
191
+ }
192
+ this.client = MobileLinkClient.fromCredentials(found.creds, { log: (step, msg) => this.debug(`[auth] ${step}: ${msg}`) });
193
+ this.credentials = { file: found.file, signature, email: found.creds.email };
194
+ this.failures = 0;
195
+ this.reconnectLogged = false;
196
+ this.log.info(`Using Mobile Link credentials for ${found.creds.email} from ${found.file}`);
197
+ return true;
198
+ }
199
+ scheduleCredentialCheck() {
200
+ if (this.stopped || this.credentialTimer) {
201
+ return;
202
+ }
203
+ this.credentialTimer = setTimeout(() => {
204
+ this.credentialTimer = null;
205
+ void this.checkCredentials();
206
+ }, CREDENTIAL_CHECK_MS);
207
+ this.credentialTimer.unref();
208
+ }
209
+ /**
210
+ * Re-read the credentials file. When a new or changed file is usable, poll
211
+ * immediately so a sign-in takes effect without a restart; when the file the
212
+ * client came from is gone (Disconnect), stop. Runs every 60 s. Public for tests.
213
+ */
214
+ async checkCredentials() {
215
+ if (this.stopped) {
216
+ return;
217
+ }
218
+ try {
219
+ if (this.client && !this.findCredentials()) {
220
+ this.credentialsRemoved();
221
+ return;
222
+ }
223
+ if (this.loadCredentials()) {
224
+ if (this.pollTimer) {
225
+ clearTimeout(this.pollTimer);
226
+ this.pollTimer = null;
227
+ }
228
+ await this.poll();
229
+ }
230
+ }
231
+ finally {
232
+ this.scheduleCredentialCheck();
233
+ }
234
+ }
235
+ /**
236
+ * The credentials file disappeared (the settings page's Disconnect, or a hand delete): stop polling, drop the
237
+ * client, report not_connected, mark the sensors not responding, and keep checking the file every 60 s.
238
+ */
239
+ credentialsRemoved() {
240
+ this.log.warn('Mobile Link credentials were removed. Polling stopped until you connect again from the settings page or with `homebridge-generac login`.');
241
+ if (this.pollTimer) {
242
+ clearTimeout(this.pollTimer);
243
+ this.pollTimer = null;
244
+ }
245
+ this.client = null;
246
+ this.credentials = null;
247
+ this.accountState = 'not_connected';
248
+ this.lastChecked = undefined;
249
+ this.failures = 0;
250
+ this.reconnectLogged = false;
251
+ // The "no credentials found" error is for a start without a sign-in; after a deliberate Disconnect it is noise.
252
+ this.onceKeys.add('no-credentials');
253
+ for (const g of this.generators.values()) {
254
+ g.markUnreachable();
255
+ }
256
+ this.attention?.set(false);
257
+ this.writeState();
258
+ }
259
+ // ---------------------------------------------------------------------------
260
+ // Polling (SPEC section 8)
261
+ // ---------------------------------------------------------------------------
262
+ schedule(ms) {
263
+ if (this.stopped) {
264
+ return;
265
+ }
266
+ if (this.pollTimer) {
267
+ clearTimeout(this.pollTimer);
268
+ }
269
+ this.nextPollMs = ms;
270
+ this.pollTimer = setTimeout(() => void this.poll(), ms);
271
+ this.pollTimer.unref();
272
+ }
273
+ /** One poll: /Apparatus/list, then details for each generator. Public for tests. */
274
+ poll() {
275
+ if (!this.inFlight) {
276
+ this.inFlight = this.doPoll().finally(() => {
277
+ this.inFlight = null;
278
+ });
279
+ }
280
+ return this.inFlight;
281
+ }
282
+ async doPoll() {
283
+ if (!this.client || this.stopped) {
284
+ return;
285
+ }
286
+ if (!this.findCredentials()) {
287
+ // Disconnected between two polls: never poll with a sign-in the user has removed.
288
+ this.credentialsRemoved();
289
+ return;
290
+ }
291
+ const started = Date.now();
292
+ let anyActive = false;
293
+ let count = 0;
294
+ try {
295
+ const list = await this.client.listApparatus();
296
+ const seen = new Set();
297
+ const seenIds = new Set();
298
+ const others = new Map();
299
+ for (const raw of list) {
300
+ const label = DEVICE_TYPE_LABEL[raw.type] ?? `type ${raw.type}`;
301
+ if (raw.type === DEVICE_TYPE.GENERATOR) {
302
+ const uuid = this.api.hap.uuid.generate(`${PLUGIN_NAME}:generator:${raw.apparatusId}`);
303
+ seen.add(uuid);
304
+ seenIds.add(raw.apparatusId);
305
+ count++;
306
+ const active = await this.syncGenerator(raw, uuid);
307
+ anyActive ||= active;
308
+ continue;
309
+ }
310
+ others.set(raw.apparatusId, { type: raw.type, name: raw.name, apparatusId: raw.apparatusId });
311
+ if (raw.type === DEVICE_TYPE.PROPANE_MONITOR) {
312
+ this.once(`propane-${raw.apparatusId}`, 'info', `Found ${label} "${raw.name}" (id ${raw.apparatusId}). Tank level support is planned; not exposed yet.`);
313
+ }
314
+ else if (raw.type === DEVICE_TYPE.LINKED_ECOBEE) {
315
+ this.once(`ecobee-${raw.apparatusId}`, 'info', `Skipping ${label} "${raw.name}". It is already a native HomeKit device; Generac's details endpoint 500s on it.`);
316
+ }
317
+ else {
318
+ this.once(`unknown-${raw.apparatusId}`, 'info', `Skipping ${label} "${raw.name}" (id ${raw.apparatusId}).`);
319
+ }
320
+ }
321
+ this.others = others;
322
+ // Drop cached generator accessories that no longer appear on the account.
323
+ for (const [uuid, acc] of this.cached) {
324
+ if (uuid !== this.attentionUuid && !seen.has(uuid)) {
325
+ this.log.info(`Removing stale accessory ${acc.displayName}`);
326
+ this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [acc]);
327
+ this.cached.delete(uuid);
328
+ }
329
+ }
330
+ for (const id of this.generators.keys()) {
331
+ if (!seenIds.has(id)) {
332
+ this.generators.delete(id);
333
+ this.trackers.delete(id);
334
+ const timer = this.holdTimers.get(id);
335
+ if (timer) {
336
+ clearTimeout(timer);
337
+ this.holdTimers.delete(id);
338
+ }
339
+ }
340
+ }
341
+ this.failures = 0;
342
+ this.reconnectLogged = false;
343
+ this.accountState = 'connected';
344
+ this.lastChecked = new Date().toISOString();
345
+ this.attention?.set(false);
346
+ }
347
+ catch (err) {
348
+ if (err instanceof InvalidGrantError) {
349
+ this.accountState = 'reconnect_needed';
350
+ const msg = `Mobile Link signed this plugin out (${err.message}). ` +
351
+ 'Sign in again from the plugin\'s settings page or with `homebridge-generac login`. Retrying hourly.';
352
+ if (this.reconnectLogged) {
353
+ this.debug(msg);
354
+ }
355
+ else {
356
+ this.log.error(msg);
357
+ this.reconnectLogged = true;
358
+ }
359
+ for (const g of this.generators.values()) {
360
+ g.markUnreachable();
361
+ }
362
+ this.attention?.set(true);
363
+ this.writeState();
364
+ this.schedule(RECONNECT_RETRY_MS);
365
+ this.scheduleCredentialCheck();
366
+ return;
367
+ }
368
+ this.failures++;
369
+ const wait = backoffMs(this.failures, this.config.pollActiveSeconds * 1000, BACKOFF_MAX_MS);
370
+ const status = err instanceof ApiError ? ` (HTTP ${err.status})` : '';
371
+ this.log.warn(`Poll failed${status}: ${err.message}. Retry in ${Math.round(wait / 1000)}s (failure ${this.failures}).`);
372
+ if (this.failures >= 3) {
373
+ for (const g of this.generators.values()) {
374
+ g.markUnreachable();
375
+ }
376
+ }
377
+ this.writeState();
378
+ this.schedule(wait);
379
+ return;
380
+ }
381
+ const intervalMs = this.nextInterval(anyActive);
382
+ this.debug(`Poll finished in ${Date.now() - started} ms: ${count} generator(s), ${anyActive ? 'active' : 'idle'}, ` +
383
+ `next poll in ${Math.round(intervalMs / 1000)}s`);
384
+ this.writeState();
385
+ this.schedule(intervalMs);
386
+ }
387
+ /**
388
+ * The delay until the next poll (SPEC section 8): the active interval while any generator is active or the
389
+ * exercise watch window is open, else the idle interval, cut short so the next poll lands when the window opens.
390
+ */
391
+ nextInterval(anyActive) {
392
+ const active = this.config.pollActiveSeconds * 1000;
393
+ const idle = this.config.pollIdleMinutes * 60 * 1000;
394
+ const minutes = this.exerciseMinutes();
395
+ if (minutes === null) {
396
+ return anyActive ? active : idle;
397
+ }
398
+ const now = new Date(this.now());
399
+ if (inWatchWindow(minutes, now)) {
400
+ return active;
401
+ }
402
+ return Math.min(anyActive ? active : idle, msUntilWatchWindow(minutes, now));
403
+ }
404
+ /**
405
+ * The exercise time the watch window follows, as minutes past local midnight: the `exerciseTime` setting,
406
+ * else the first generator's Exercise Minutes from the API, else null (no window).
407
+ */
408
+ exerciseMinutes() {
409
+ const configured = parseHHMM(this.config.exerciseTime);
410
+ if (configured !== null) {
411
+ return configured;
412
+ }
413
+ for (const gen of this.generators.values()) {
414
+ const fromApi = parseHHMM(gen.current?.exerciseTimeFromApi);
415
+ if (fromApi !== null) {
416
+ return fromApi;
417
+ }
418
+ }
419
+ return null;
420
+ }
421
+ /** Returns true when the generator is in an "active" state that warrants faster polling. */
422
+ async syncGenerator(raw, uuid) {
423
+ let detail;
424
+ try {
425
+ detail = await this.client.apparatusDetails(raw.apparatusId);
426
+ }
427
+ catch (err) {
428
+ // Per-apparatus tolerance: one bad details call must not stall the rest.
429
+ this.log.warn(`Details for "${raw.name}" (id ${raw.apparatusId}) failed: ${err.message}`);
430
+ return false;
431
+ }
432
+ if (!detail) {
433
+ return false;
434
+ }
435
+ const state = toGeneratorState(detail, raw, this.config);
436
+ const displayName = displayNameFor(this.config, raw.apparatusId, state.name);
437
+ let gen = this.generators.get(raw.apparatusId);
438
+ const previousStatus = gen?.current?.status;
439
+ if (!gen) {
440
+ let acc = this.cached.get(uuid);
441
+ let restored = false;
442
+ if (!acc) {
443
+ acc = new this.api.platformAccessory(displayName, uuid);
444
+ this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [acc]);
445
+ this.cached.set(uuid, acc);
446
+ this.log.info(`Added generator "${displayName}" (${state.model}, S/N ${state.serial})`);
447
+ }
448
+ else {
449
+ restored = true;
450
+ this.log.info(`Restored generator "${acc.displayName}" from cache`);
451
+ }
452
+ const previousName = acc.displayName;
453
+ gen = new GeneratorAccessory(this, acc, state, displayName);
454
+ this.generators.set(raw.apparatusId, gen);
455
+ if (restored && acc.displayName !== previousName) {
456
+ this.api.updatePlatformAccessories([acc]);
457
+ this.log.info(`Renamed "${previousName}" to "${displayName}"`);
458
+ }
459
+ }
460
+ else {
461
+ const previousName = gen.accessory.displayName;
462
+ if (gen.setDisplayName(displayName)) {
463
+ this.api.updatePlatformAccessories([gen.accessory]);
464
+ this.log.info(`Renamed "${previousName}" to "${displayName}"`);
465
+ }
466
+ gen.update(state);
467
+ }
468
+ const retroactive = this.observeExercise(raw.apparatusId, gen, state);
469
+ const statusChanged = previousStatus !== undefined && previousStatus !== state.status;
470
+ if (this.config.debug && (statusChanged || retroactive)) {
471
+ this.capture(gen, detail, state.status);
472
+ }
473
+ return isActive(state);
474
+ }
475
+ /** A debug capture of the raw payload (SPEC section 12). Never blocks a poll: a write failure is warned about once. */
476
+ capture(gen, detail, status) {
477
+ const dir = capturesDir(this.storagePath);
478
+ try {
479
+ const file = writeCapture(dir, detail, status, new Date(this.now()));
480
+ this.debug(`${gen.accessory.displayName}: captured the details payload to ${file}`);
481
+ }
482
+ catch (err) {
483
+ this.once(`capture-${err.message}`, 'warn', `Could not write a capture under ${dir}: ${err.message}`);
484
+ }
485
+ }
486
+ // ---------------------------------------------------------------------------
487
+ // Exercising sensor (SPEC section 7, service 6)
488
+ // ---------------------------------------------------------------------------
489
+ loadPersistedExercise() {
490
+ const state = readStateFile(statePath(this.storagePath));
491
+ for (const g of state?.generators ?? []) {
492
+ if (typeof g.id === 'number') {
493
+ this.persistedExercise.set(g.id, typeof g.lastExerciseAt === 'string' ? g.lastExerciseAt : null);
494
+ }
495
+ }
496
+ }
497
+ /** Feeds one poll into the generator's tracker and drives the sensor. Returns true on a retroactive detection. */
498
+ observeExercise(id, gen, state) {
499
+ let tracker = this.trackers.get(id);
500
+ if (!tracker) {
501
+ tracker = new ExerciseTracker(this.persistedExercise.get(id), this.config.exerciseHoldMinutes * 60 * 1000);
502
+ this.trackers.set(id, tracker);
503
+ }
504
+ const now = this.now();
505
+ const result = tracker.observe(state.status, state.lastExerciseAt, now);
506
+ if (result.triggered) {
507
+ const how = result.retroactive
508
+ ? `Mobile Link reports an exercise finished at ${state.lastExerciseAt?.toISOString() ?? 'unknown'}`
509
+ : 'status is Exercising';
510
+ this.log.info(`${gen.accessory.displayName}: exercise detected (${how})`);
511
+ this.scheduleHoldExpiry(id, gen, tracker);
512
+ }
513
+ gen.setExercising(result.open);
514
+ return result.retroactive;
515
+ }
516
+ /** Closes the sensor when the hold runs out between polls. */
517
+ scheduleHoldExpiry(id, gen, tracker) {
518
+ const existing = this.holdTimers.get(id);
519
+ if (existing) {
520
+ clearTimeout(existing);
521
+ this.holdTimers.delete(id);
522
+ }
523
+ const remaining = tracker.holdRemaining(this.now());
524
+ if (remaining === null || this.stopped) {
525
+ return;
526
+ }
527
+ const timer = setTimeout(() => {
528
+ this.holdTimers.delete(id);
529
+ gen.setExercising(tracker.isOpen(this.now()));
530
+ }, remaining);
531
+ timer.unref();
532
+ this.holdTimers.set(id, timer);
533
+ }
534
+ /** Re-evaluates every Exercising sensor against the clock. Public for tests, which drive the clock by hand. */
535
+ refreshExerciseSensors() {
536
+ for (const [id, gen] of this.generators) {
537
+ const tracker = this.trackers.get(id);
538
+ if (tracker) {
539
+ gen.setExercising(tracker.isOpen(this.now()));
540
+ }
541
+ }
542
+ }
543
+ // ---------------------------------------------------------------------------
544
+ // State file (SPEC section 10)
545
+ // ---------------------------------------------------------------------------
546
+ /** The last state of every generator, for the state file and tests. */
547
+ get generatorStates() {
548
+ return [...this.generators.values()].map((g) => g.current).filter((s) => s !== null);
549
+ }
550
+ /** The generator states as the state file carries them, with the persisted `lastExerciseAt` per unit. */
551
+ snapshots() {
552
+ const out = [];
553
+ for (const [id, gen] of this.generators) {
554
+ const state = gen.current;
555
+ if (state) {
556
+ const tracker = this.trackers.get(id);
557
+ out.push(toGeneratorSnapshot(state, tracker ? tracker.lastExerciseAt : (this.persistedExercise.get(id) ?? null)));
558
+ }
559
+ }
560
+ return out;
561
+ }
562
+ writeState() {
563
+ const file = statePath(this.storagePath);
564
+ const account = { state: this.accountState, email: this.credentials?.email, lastChecked: this.lastChecked };
565
+ try {
566
+ writeStateFile(file, buildStateFile(account, this.snapshots(), this.others.values()));
567
+ }
568
+ catch (err) {
569
+ this.once(`state-${err.message}`, 'warn', `Could not write ${file}: ${err.message}`);
570
+ }
571
+ }
572
+ // ---------------------------------------------------------------------------
573
+ // Logging (SPEC section 12)
574
+ // ---------------------------------------------------------------------------
575
+ /**
576
+ * Debug lines. Sent through log.debug so Homebridge's own -D shows them;
577
+ * with the plugin's `debug` setting on they are raised to info so they show
578
+ * without -D, since Homebridge drops log.debug output otherwise.
579
+ */
580
+ debug(msg) {
581
+ if (this.config.debug) {
582
+ this.log.info(msg);
583
+ }
584
+ else {
585
+ this.log.debug(msg);
586
+ }
587
+ }
588
+ onceKeys = new Set();
589
+ once(key, level, msg) {
590
+ if (!this.onceKeys.has(key)) {
591
+ this.onceKeys.add(key);
592
+ this.log[level](msg);
593
+ }
594
+ }
595
+ }
596
+ //# sourceMappingURL=platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AACrG,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC9F,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAuB,MAAM,YAAY,CAAC;AAC7E,OAAO,EACL,oBAAoB,EACpB,OAAO,EACP,cAAc,EACd,eAAe,EACf,aAAa,EACb,WAAW,EACX,aAAa,EACb,YAAY,EACZ,aAAa,EACb,SAAS,GAGV,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,cAAc,GAKf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAsE,MAAM,YAAY,CAAC;AAEhI,6JAA6J;AAC7J,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,GAAG,IAAI,CAAC;AAC7C,+DAA+D;AAC/D,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AACjD,4DAA4D;AAC5D,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAQ7C,MAAM,OAAO,eAAe;IAuCf;IAEA;IAxCF,MAAM,CAAiB;IACvB,OAAO,GAAG,aAAa,EAAE,CAAC;IACnC,wEAAwE;IAC/D,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAEjC,WAAW,CAAS;IACpB,eAAe,CAAqB;IACpC,aAAa,CAAS;IAEtB,MAAM,GAAG,IAAI,GAAG,EAA6B,CAAC;IAC9C,UAAU,GAAG,IAAI,GAAG,EAA8B,CAAC;IACpE,0FAA0F;IACzE,QAAQ,GAAG,IAAI,GAAG,EAA2B,CAAC;IAC9C,UAAU,GAAG,IAAI,GAAG,EAA0B,CAAC;IAChE,oHAAoH;IACnG,iBAAiB,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC9D,MAAM,GAAG,IAAI,GAAG,EAAuB,CAAC;IACxC,SAAS,GAA8B,IAAI,CAAC;IAE5C,MAAM,GAA4B,IAAI,CAAC;IACvC,WAAW,GAA6B,IAAI,CAAC;IAC7C,YAAY,GAAiB,eAAe,CAAC;IAC7C,WAAW,CAAqB;IAChC,eAAe,GAAG,KAAK,CAAC;IAExB,SAAS,GAA0B,IAAI,CAAC;IACxC,eAAe,GAA0B,IAAI,CAAC;IAC9C,QAAQ,GAAyB,IAAI,CAAC;IACtC,QAAQ,GAAG,CAAC,CAAC;IACb,OAAO,GAAG,KAAK,CAAC;IAExB,8FAA8F;IAC9F,UAAU,GAAkB,IAAI,CAAC;IAEjC,qFAAqF;IACrF,GAAG,GAAiB,IAAI,CAAC,GAAG,CAAC;IAE7B,YACW,GAAW,EACpB,SAAyB,EAChB,GAAQ;QAFR,QAAG,GAAH,GAAG,CAAQ;QAEX,QAAG,GAAH,GAAG,CAAK;QAEjB,MAAM,GAAG,GAAG,SAA0B,CAAC;QACvC,IAAI,CAAC,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,CAAC,eAAe,GAAG,GAAG,CAAC,eAAe,CAAC;QAC3C,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAC1C,IAAI,CAAC,aAAa,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,WAAW,YAAY,CAAC,CAAC;QAEvE,2EAA2E;QAC3E,0EAA0E;QAE1E,GAAG,CAAC,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACtD,GAAG,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,kBAAkB,CAAC,SAA4B;QAC7C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC7C,CAAC;IAED,kDAAkD;IAClD,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC7B,IAAI,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;YAC3B,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACpB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,CAAC;QACD,IAAI,CAAC,uBAAuB,EAAE,CAAC;IACjC,CAAC;IAED,QAAQ;QACN,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,CAAC;QACD,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,YAAY,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YACnC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC9B,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;YAC7C,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC;IAED;;;OAGG;IACK,gBAAgB;QACtB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,YAAY,CAAC,CAAC;QAClE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,OAAO;QACT,CAAC;QACD,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAC9C,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC,GAAG,CAAC,6BAA6B,CAAC,WAAW,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC;YAChF,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACtB,CAAC;QACD,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,yCAAyC,WAAW,CAAC,MAAM,mBAAmB,WAAW,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;IACzI,CAAC;IAED,8EAA8E;IAC9E,2CAA2C;IAC3C,8EAA8E;IAEtE,cAAc;QACpB,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACrD,IAAI,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;YAChC,IAAI,GAAG,GAAG,QAAQ,CAAC;YACnB,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,GAAG,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,cAAc,EAAE,IAAI,CAAC,aAAa,0CAAiC,CAAC;gBACzG,IAAI,CAAC,GAAG,CAAC,2BAA2B,CAAC,WAAW,EAAE,aAAa,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;gBACxE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;gBACzC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,cAAc,UAAU,CAAC,CAAC;YACxD,CAAC;YACD,IAAI,CAAC,SAAS,GAAG,IAAI,kBAAkB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YACnD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,KAAK,kBAAkB,CAAC,CAAC;QAC/D,CAAC;aAAM,IAAI,QAAQ,EAAE,CAAC;YACpB,IAAI,CAAC,GAAG,CAAC,6BAA6B,CAAC,WAAW,EAAE,aAAa,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC/E,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACvC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,gBAAgB,cAAc,mCAAmC,CAAC,CAAC;QACnF,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,iCAAiC;IACjC,8EAA8E;IAEtE,eAAe;QACrB,KAAK,MAAM,IAAI,IAAI,oBAAoB,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC;YAChF,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;gBACpC,IAAI,KAAK,EAAE,CAAC;oBACV,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;gBACzB,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACrD,IAAI,CAAC,IAAI,CACP,UAAU,IAAI,EAAE,EAChB,MAAM,EACN,kBAAkB,IAAI,uHAAuH,CAC9I,CAAC;gBACJ,CAAC;qBAAM,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC/B,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,IAAK,GAAa,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,kCAAkC,IAAI,KAAM,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;gBACpI,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,MAAM,CAAC,WAAW,CAAC,IAAY,EAAE,KAAwB;QAC/D,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YACrB,OAAO,GAAG,IAAI,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACvC,CAAC;QACD,IAAI,CAAC;YACH,OAAO,GAAG,IAAI,UAAU,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,GAAG,IAAI,UAAU,CAAC;QAC3B,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,eAAe;QACrB,MAAM,KAAK,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QACrC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACjB,MAAM,MAAM,GAAG,oBAAoB,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACvF,IAAI,CAAC,IAAI,CACP,gBAAgB,EAChB,OAAO,EACP,2FAA2F;oBACzF,gGAAgG,MAAM,EAAE,CAC3G,CAAC;YACJ,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,SAAS,GAAG,eAAe,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QACvE,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACjE,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,gBAAgB,CAAC,eAAe,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,IAAI,KAAK,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAC1H,IAAI,CAAC,WAAW,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAC7E,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;QAClB,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,qCAAqC,KAAK,CAAC,KAAK,CAAC,KAAK,SAAS,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3F,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,uBAAuB;QAC7B,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,eAAe,GAAG,UAAU,CAAC,GAAG,EAAE;YACrC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;YAC5B,KAAK,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC/B,CAAC,EAAE,mBAAmB,CAAC,CAAC;QACxB,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,gBAAgB;QACpB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;gBAC3C,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBAC1B,OAAO;YACT,CAAC;YACD,IAAI,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;gBAC3B,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;oBACnB,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;oBAC7B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;gBACxB,CAAC;gBACD,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;YACpB,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,uBAAuB,EAAE,CAAC;QACjC,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,kBAAkB;QACxB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,0IAA0I,CAAC,CAAC;QAC1J,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,YAAY,GAAG,eAAe,CAAC;QACpC,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAC7B,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;QAClB,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAC7B,gHAAgH;QAChH,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACpC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;YACzC,CAAC,CAAC,eAAe,EAAE,CAAC;QACtB,CAAC;QACD,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3B,IAAI,CAAC,UAAU,EAAE,CAAC;IACpB,CAAC;IAED,8EAA8E;IAC9E,2BAA2B;IAC3B,8EAA8E;IAEtE,QAAQ,CAAC,EAAU;QACzB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC/B,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QACxD,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;IAED,oFAAoF;IACpF,IAAI;QACF,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;gBACzC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACvB,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAEO,KAAK,CAAC,MAAM;QAClB,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;YAC5B,kFAAkF;YAClF,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC1B,OAAO;QACT,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;YAC/C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;YAC/B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;YAClC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuB,CAAC;YAE9C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,MAAM,KAAK,GAAG,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;gBAChE,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,CAAC,SAAS,EAAE,CAAC;oBACvC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,WAAW,cAAc,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;oBACvF,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;oBACf,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;oBAC7B,KAAK,EAAE,CAAC;oBACR,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;oBACnD,SAAS,KAAK,MAAM,CAAC;oBACrB,SAAS;gBACX,CAAC;gBACD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;gBAC9F,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,CAAC,eAAe,EAAE,CAAC;oBAC7C,IAAI,CAAC,IAAI,CACP,WAAW,GAAG,CAAC,WAAW,EAAE,EAC5B,MAAM,EACN,SAAS,KAAK,KAAK,GAAG,CAAC,IAAI,SAAS,GAAG,CAAC,WAAW,oDAAoD,CACxG,CAAC;gBACJ,CAAC;qBAAM,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,CAAC,aAAa,EAAE,CAAC;oBAClD,IAAI,CAAC,IAAI,CACP,UAAU,GAAG,CAAC,WAAW,EAAE,EAC3B,MAAM,EACN,YAAY,KAAK,KAAK,GAAG,CAAC,IAAI,kFAAkF,CACjH,CAAC;gBACJ,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,YAAY,KAAK,KAAK,GAAG,CAAC,IAAI,SAAS,GAAG,CAAC,WAAW,IAAI,CAAC,CAAC;gBAC9G,CAAC;YACH,CAAC;YACD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;YAErB,0EAA0E;YAC1E,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBACtC,IAAI,IAAI,KAAK,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;oBACnD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,4BAA4B,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;oBAC7D,IAAI,CAAC,GAAG,CAAC,6BAA6B,CAAC,WAAW,EAAE,aAAa,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;oBAC1E,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBAC3B,CAAC;YACH,CAAC;YACD,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC;gBACxC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;oBACrB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;oBAC3B,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;oBACzB,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBACtC,IAAI,KAAK,EAAE,CAAC;wBACV,YAAY,CAAC,KAAK,CAAC,CAAC;wBACpB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;oBAC7B,CAAC;gBACH,CAAC;YACH,CAAC;YAED,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;YAClB,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;YAC7B,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC;YAChC,IAAI,CAAC,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC5C,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,iBAAiB,EAAE,CAAC;gBACrC,IAAI,CAAC,YAAY,GAAG,kBAAkB,CAAC;gBACvC,MAAM,GAAG,GACP,uCAAuC,GAAG,CAAC,OAAO,KAAK;oBACvD,qGAAqG,CAAC;gBACxG,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;oBACzB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAClB,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oBACpB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;gBAC9B,CAAC;gBACD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;oBACzC,CAAC,CAAC,eAAe,EAAE,CAAC;gBACtB,CAAC;gBACD,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;gBAC1B,IAAI,CAAC,UAAU,EAAE,CAAC;gBAClB,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;gBAClC,IAAI,CAAC,uBAAuB,EAAE,CAAC;gBAC/B,OAAO;YACT,CAAC;YACD,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChB,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,iBAAiB,GAAG,IAAI,EAAE,cAAc,CAAC,CAAC;YAC5F,MAAM,MAAM,GAAG,GAAG,YAAY,QAAQ,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACtE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,MAAM,KAAM,GAAa,CAAC,OAAO,cAAc,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,cAAc,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;YACnI,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC;gBACvB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;oBACzC,CAAC,CAAC,eAAe,EAAE,CAAC;gBACtB,CAAC;YACH,CAAC;YACD,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACpB,OAAO;QACT,CAAC;QAED,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;QAChD,IAAI,CAAC,KAAK,CACR,oBAAoB,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,QAAQ,KAAK,kBAAkB,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,IAAI;YACtG,gBAAgB,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,CACnD,CAAC;QACF,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACK,YAAY,CAAC,SAAkB;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,iBAAiB,GAAG,IAAI,CAAC;QACpD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,eAAe,GAAG,EAAE,GAAG,IAAI,CAAC;QACrD,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QACvC,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QACnC,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QACjC,IAAI,aAAa,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC;YAChC,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,kBAAkB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IAC/E,CAAC;IAED;;;OAGG;IACH,eAAe;QACb,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QACvD,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;YACxB,OAAO,UAAU,CAAC;QACpB,CAAC;QACD,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;YAC3C,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;YAC5D,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;gBACrB,OAAO,OAAO,CAAC;YACjB,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,4FAA4F;IACpF,KAAK,CAAC,aAAa,CAAC,GAAiB,EAAE,IAAY;QACzD,IAAI,MAAM,CAAC;QACX,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,IAAI,CAAC,MAAO,CAAC,gBAAgB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAChE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,yEAAyE;YACzE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,gBAAgB,GAAG,CAAC,IAAI,SAAS,GAAG,CAAC,WAAW,aAAc,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;YACrG,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,KAAK,GAAG,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACzD,MAAM,WAAW,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAE7E,IAAI,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC/C,MAAM,cAAc,GAAG,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC;QAC5C,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,IAAI,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,QAAQ,GAAG,KAAK,CAAC;YACrB,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,GAAG,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;gBACxD,IAAI,CAAC,GAAG,CAAC,2BAA2B,CAAC,WAAW,EAAE,aAAa,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;gBACxE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;gBAC3B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,oBAAoB,WAAW,MAAM,KAAK,CAAC,KAAK,SAAS,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;YAC1F,CAAC;iBAAM,CAAC;gBACN,QAAQ,GAAG,IAAI,CAAC;gBAChB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,GAAG,CAAC,WAAW,cAAc,CAAC,CAAC;YACtE,CAAC;YACD,MAAM,YAAY,GAAG,GAAG,CAAC,WAAW,CAAC;YACrC,GAAG,GAAG,IAAI,kBAAkB,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;YAC5D,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;YAC1C,IAAI,QAAQ,IAAI,GAAG,CAAC,WAAW,KAAK,YAAY,EAAE,CAAC;gBACjD,IAAI,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC1C,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,YAAY,SAAS,WAAW,GAAG,CAAC,CAAC;YACjE,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,YAAY,GAAG,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC;YAC/C,IAAI,GAAG,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,CAAC;gBACpC,IAAI,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;gBACpD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,YAAY,SAAS,WAAW,GAAG,CAAC,CAAC;YACjE,CAAC;YACD,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;QACD,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACtE,MAAM,aAAa,GAAG,cAAc,KAAK,SAAS,IAAI,cAAc,KAAK,KAAK,CAAC,MAAM,CAAC;QACtF,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,aAAa,IAAI,WAAW,CAAC,EAAE,CAAC;YACxD,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED,uHAAuH;IAC/G,OAAO,CAAC,GAAuB,EAAE,MAA0B,EAAE,MAAc;QACjF,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACrE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,SAAS,CAAC,WAAW,qCAAqC,IAAI,EAAE,CAAC,CAAC;QACtF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,IAAI,CAAC,WAAY,GAAa,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,mCAAmC,GAAG,KAAM,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9H,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,gDAAgD;IAChD,8EAA8E;IAEtE,qBAAqB;QAC3B,MAAM,KAAK,GAAG,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;QACzD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,UAAU,IAAI,EAAE,EAAE,CAAC;YACxC,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;gBAC7B,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,cAAc,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACnG,CAAC;QACH,CAAC;IACH,CAAC;IAED,kHAAkH;IAC1G,eAAe,CAAC,EAAU,EAAE,GAAuB,EAAE,KAAqB;QAChF,IAAI,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACpC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,mBAAmB,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;YAC3G,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACjC,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;QACxE,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW;gBAC5B,CAAC,CAAC,+CAA+C,KAAK,CAAC,cAAc,EAAE,WAAW,EAAE,IAAI,SAAS,EAAE;gBACnG,CAAC,CAAC,sBAAsB,CAAC;YAC3B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,SAAS,CAAC,WAAW,wBAAwB,GAAG,GAAG,CAAC,CAAC;YAC1E,IAAI,CAAC,kBAAkB,CAAC,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QAC5C,CAAC;QACD,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC/B,OAAO,MAAM,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED,8DAA8D;IACtD,kBAAkB,CAAC,EAAU,EAAE,GAAuB,EAAE,OAAwB;QACtF,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzC,IAAI,QAAQ,EAAE,CAAC;YACb,YAAY,CAAC,QAAQ,CAAC,CAAC;YACvB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC7B,CAAC;QACD,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QACpD,IAAI,SAAS,KAAK,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACvC,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC3B,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAChD,CAAC,EAAE,SAAS,CAAC,CAAC;QACd,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACjC,CAAC;IAED,+GAA+G;IAC/G,sBAAsB;QACpB,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACxC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACtC,IAAI,OAAO,EAAE,CAAC;gBACZ,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YAChD,CAAC;QACH,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,+BAA+B;IAC/B,8EAA8E;IAE9E,uEAAuE;IACvE,IAAI,eAAe;QACjB,OAAO,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAuB,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IAC5G,CAAC;IAED,yGAAyG;IACjG,SAAS;QACf,MAAM,GAAG,GAAwB,EAAE,CAAC;QACpC,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACxC,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC;YAC1B,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACtC,GAAG,CAAC,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;YACpH,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAEO,UAAU;QAChB,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACzC,MAAM,OAAO,GAAoB,EAAE,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC;QAC7H,IAAI,CAAC;YACH,cAAc,CAAC,IAAI,EAAE,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACxF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,IAAI,CAAC,SAAU,GAAa,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,mBAAmB,IAAI,KAAM,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7G,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,4BAA4B;IAC5B,8EAA8E;IAE9E;;;;OAIG;IACK,KAAK,CAAC,GAAW;QACvB,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACtB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAEgB,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACtC,IAAI,CAAC,GAAW,EAAE,KAAgC,EAAE,GAAW;QACrE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;CACF"}