homebridge-unifi-protect 4.4.4 → 5.0.3

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/README.md CHANGED
@@ -20,16 +20,18 @@ This plugin attempts to bridge a gap in the UniFi Protect ecosystem by providing
20
20
 
21
21
  What does *just works* mean in practice? It means that this plugin will discover all your supported UniFi Protect devices and make them available in HomeKit. It supports all known UniFi Protect controller configurations (UniFi CloudKey Gen2+, UniFi Dream Machine Pro, and UniFi Protect NVR).
22
22
 
23
- For the more technically inclined - this plugin has continued to pioneer the HomeKit user experience for UniFi Protect by being the ***first*** Homebridge plugin (and first third-party app, to my knowledge) to successfully reverse engineer the UniFi Protect realtime events API that was introduced with UniFi OS. This means that rather than poll the Protect controller every few seconds to catch events, we're able to capture motion and doorbell ring events in realtime, providing an immediate and extremely responsive HomeKit experience, further reducing the potential performance impact to Protect controllers.
23
+ For the more technically inclined - this plugin has continued to pioneer the HomeKit user experience for UniFi Protect by being the ***first*** Homebridge plugin (and first third-party app, to my knowledge) to successfully reverse engineer the UniFi Protect realtime events API that was introduced with UniFi OS. This means that rather than poll the Protect controller every few seconds to catch events, we're able to capture motion and doorbell ring events in realtime, providing an immediate and extremely responsive HomeKit experience, further reducing the potential performance impact to Protect controllers. Since reverse engineering the realtime events API, most of the major open source smart automation projects have benefitted and also incorporated that work.
24
24
 
25
25
  ### Features
26
26
  - ***Easy* configuration - all you need is your UniFi Protect controller IP address, username, and password to get started.** The defaults work for the vast majority of users. When you want more, there are [advanced options](https://github.com/hjdhjd/homebridge-unifi-protect/blob/master/docs/AdvancedOptions.md) you can play with, if you choose.
27
27
 
28
- - **Blazing fast video streaming.** Video streaming from HomeKit will start within in 1-2 seconds for G3-series cameras and 3-4 seconds for G4-series cameras, in most cases. I've spent the time to optimize the video streaming experience to ensure it feels very responsive, and *just works*.
28
+ - **Full HomeKit upport for the UniFi Protect ecosystem.** All generally available UniFi Protect devices are supported, including cameras, doorbells, lights, sensors, and ViewPorts.
29
29
 
30
- - **[Full UniFi Protect G4 Doorbell support.](https://github.com/hjdhjd/homebridge-unifi-protect/blob/master/docs/Doorbell.md).** This plugin provides complete support for [UniFi Protect G4 Doorbells](https://store.ui.com/collections/unifi-protect/products/uvc-g4-doorbell). We support all the features of the doorbell including - doorbell rings, two-way audio, and the use of the onboard LCD screen for messages. Two-way audio has caveats you should be aware of.
30
+ - **Blazing fast video streaming.** Video streaming from HomeKit will start within in 1-2 seconds for cameras, in most cases. I've spent the time to optimize the video streaming experience to ensure it feels very responsive, and *just works*.
31
31
 
32
- - **[Two-way audio support](https://github.com/hjdhjd/homebridge-unifi-protect/blob/master/docs/Doorbell.md#doorbell-twoway) for all UniFi Protect cameras that support it**. Some Protect devices that support two-way audio capabilities include [UniFi Protect G4 Doorbells](https://store.ui.com/collections/unifi-protect/products/uvc-g4-doorbell), the [UniFi Protect G3 Micro](https://store.ui.com/collections/unifi-protect/products/unifi-video-g3-micro), and more. If the Protect device supports two-way audio, that functionality is available to you in HomeKit.
32
+ - **[Full UniFi Protect Doorbell support.](https://github.com/hjdhjd/homebridge-unifi-protect/blob/master/docs/Doorbell.md).** This plugin provides complete support for [UniFi Protect Doorbells](https://store.ui.com/collections/unifi-protect/products/uvc-g4-doorbell). We support all the features of the doorbell including - doorbell rings, two-way audio, and the use of the onboard LCD screen for messages. Two-way audio has caveats you should be aware of.
33
+
34
+ - **[Two-way audio support](https://github.com/hjdhjd/homebridge-unifi-protect/blob/master/docs/Doorbell.md#doorbell-twoway) for all UniFi Protect cameras that support it**. Some Protect devices that support two-way audio capabilities include [UniFi Protect Doorbells](https://store.ui.com/collections/unifi-protect/products/uvc-g4-doorbell), the [UniFi Protect Instant Cameras](https://store.ui.com/collections/unifi-protect/products/unifi-video-g3-micro), and more. If the Protect device supports two-way audio, that functionality is available to you in HomeKit.
33
35
 
34
36
  - **Support for multiple controllers.** This plugin can support multiple UniFi Protect controllers. If you have more than one controller, it's easy to add them to this plugin, and integrate them seamlessly into HomeKit.
35
37
 
@@ -72,13 +74,7 @@ If you are new to Homebridge, please first read the [Homebridge](https://homebri
72
74
 
73
75
  If you have installed the [Homebridge Config UI](https://github.com/oznu/homebridge-config-ui-x), you can intall this plugin by going to the `Plugins` tab and searching for `homebridge-unifi-protect` and installing it.
74
76
 
75
- If you prefer to install `homebridge-unifi-protect` from the command line, you can do so by executing:
76
-
77
- ```sh
78
- sudo npm install --unsafe-perm -g homebridge-unifi-protect
79
- ```
80
-
81
- You will need a working **ffmpeg** installation for `homebridge-unifi-protect` to work correctly. To make installation more convenient, this plugin uses [ffmpeg-for-homebridge](https://www.npmjs.com/package/ffmpeg-for-homebridge) which provides prebuilt versions of ffmpeg for some of the more popular platforms. [Click here](https://github.com/homebridge/ffmpeg-for-homebridge#supported-platforms) for a list of platforms supported by `ffmpeg-for-homebridge`. If you don't find your platform listed, you'll need to install a working version of ffmpeg for yourself, if you want video streaming to work. **Setting up and configuring ffmpeg is beyond the scope of this documentation.**
77
+ **Important:** you will need a working **ffmpeg** installation for `homebridge-unifi-protect` to work correctly. To make installation more convenient, this plugin uses [ffmpeg-for-homebridge](https://www.npmjs.com/package/ffmpeg-for-homebridge) which provides prebuilt versions of ffmpeg for some of the more popular platforms. [Click here](https://github.com/homebridge/ffmpeg-for-homebridge#supported-platforms) for a list of platforms supported by `ffmpeg-for-homebridge`. If you don't find your platform listed, you'll need to install a working version of ffmpeg for yourself, if you want video streaming to work. **Setting up and configuring ffmpeg is beyond the scope of this documentation.**
82
78
 
83
79
  ### Audio
84
80
  Audio on cameras is tricky in the HomeKit world to begin with, and when you throw in some of the specifics of how UniFi Protect works, it gets even more interesting. Some things to keep in mind if you want to use audio with UniFi Protect:
@@ -112,6 +112,14 @@
112
112
  }
113
113
  },
114
114
 
115
+ "name": {
116
+ "type": "string",
117
+ "title": "Plugin Name",
118
+ "required": true,
119
+ "placeholder": "UniFi Protect",
120
+ "description": "Name to use for Homebridge logging purposes. Default: UniFi Protect."
121
+ },
122
+
115
123
  "options": {
116
124
  "type": "array",
117
125
  "title": "Feature Options",
@@ -125,14 +133,6 @@
125
133
  }
126
134
  },
127
135
 
128
- "videoProcessor": {
129
- "type": "string",
130
- "title": "Video Processor",
131
- "required": false,
132
- "placeholder": "e.g. ffmpeg",
133
- "description": "Location of ffmpeg or your preferred video processor. Default: builtin or in your local path."
134
- },
135
-
136
136
  "motionDuration": {
137
137
  "type": "integer",
138
138
  "title": "Motion Event Duration (seconds)",
@@ -143,12 +143,39 @@
143
143
  "description": "Duration of a single motion event. Setting this too low will potentially cause a lot of excess notifications. Default: 10."
144
144
  },
145
145
 
146
+ "ringDuration": {
147
+ "type": "integer",
148
+ "title": "Ring Event Duration (seconds)",
149
+ "required": false,
150
+ "minimum": 4,
151
+ "maximum": 10,
152
+ "placeholder": "e.g. 4",
153
+ "description": "Duration of a single ring event. Default: 4."
154
+ },
155
+
146
156
  "verboseFfmpeg": {
147
157
  "type": "boolean",
148
158
  "title": "Verbose FFmpeg Logging",
149
159
  "required": false,
150
160
  "description": "Enable additional logging for FFmpeg. This will create a lot of logging when video streams are running. Default: false."
161
+ },
162
+
163
+ "videoEncoder": {
164
+ "type": "string",
165
+ "title": "Video Encoder",
166
+ "required": false,
167
+ "placeholder": "e.g. h264_omx",
168
+ "description": "FFmpeg video encoder to use for transcoding. Useful for hardware-accelerated video encoding such as on a Raspberry Pi. Default: libx264."
169
+ },
170
+
171
+ "videoProcessor": {
172
+ "type": "string",
173
+ "title": "Video Processor",
174
+ "required": false,
175
+ "placeholder": "e.g. ffmpeg",
176
+ "description": "Location of ffmpeg or your preferred video processor. Default: builtin or in your local path."
151
177
  }
178
+
152
179
  }
153
180
  },
154
181
 
@@ -259,9 +286,12 @@
259
286
  {
260
287
  "description": "These settings should be rarely used or needed by most people. Use these with caution.",
261
288
  "items": [
289
+ "name",
262
290
  "videoProcessor",
291
+ "videoEncoder",
292
+ "verboseFfmpeg",
263
293
  "motionDuration",
264
- "verboseFfmpeg"
294
+ "ringDuration"
265
295
  ]
266
296
  }
267
297
  ]
@@ -1,6 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ProtectAccessory = exports.ProtectBase = void 0;
3
+ exports.ProtectAccessory = exports.ProtectBase = exports.PROTECT_SWITCH_MOTION_TRIGGER = exports.PROTECT_SWITCH_MOTION_SENSOR = exports.PROTECT_SWITCH_DOORBELL_TRIGGER = exports.PROTECT_CONTACT_SENSOR_ALARM_SOUND = exports.PROTECT_CONTACT_SENSOR = exports.PROTECT_CONTACT_MOTION_SMARTDETECT = void 0;
4
+ // Manage our contact sensor types.
5
+ exports.PROTECT_CONTACT_MOTION_SMARTDETECT = "ContactMotionSmartDetect";
6
+ exports.PROTECT_CONTACT_SENSOR = "ContactSensor";
7
+ exports.PROTECT_CONTACT_SENSOR_ALARM_SOUND = "ContactAlarmSound";
8
+ // Manage our switch types.
9
+ exports.PROTECT_SWITCH_DOORBELL_TRIGGER = "DoorbellTrigger";
10
+ exports.PROTECT_SWITCH_MOTION_SENSOR = "MotionSensorSwitch";
11
+ exports.PROTECT_SWITCH_MOTION_TRIGGER = "MotionSensorTrigger";
4
12
  class ProtectBase {
5
13
  // The constructor initializes key variables and calls configureDevice().
6
14
  constructor(nvr) {
@@ -12,6 +20,38 @@ class ProtectBase {
12
20
  this.nvrApi = nvr.nvrApi;
13
21
  this.platform = nvr.platform;
14
22
  }
23
+ // Configure the device device information for HomeKit.
24
+ setInfo(accessory, device) {
25
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
26
+ // If we don't have a device, we're done.
27
+ if (!device) {
28
+ return false;
29
+ }
30
+ // Update the manufacturer information for this device.
31
+ (_a = accessory
32
+ .getService(this.hap.Service.AccessoryInformation)) === null || _a === void 0 ? void 0 : _a.updateCharacteristic(this.hap.Characteristic.Manufacturer, "Ubiquiti Networks");
33
+ // Update the model information for this device.
34
+ if ((_b = device.type) === null || _b === void 0 ? void 0 : _b.length) {
35
+ (_c = accessory
36
+ .getService(this.hap.Service.AccessoryInformation)) === null || _c === void 0 ? void 0 : _c.updateCharacteristic(this.hap.Characteristic.Model, device.type);
37
+ }
38
+ // Update the serial number for this device.
39
+ if ((_d = device.mac) === null || _d === void 0 ? void 0 : _d.length) {
40
+ (_e = accessory
41
+ .getService(this.hap.Service.AccessoryInformation)) === null || _e === void 0 ? void 0 : _e.updateCharacteristic(this.hap.Characteristic.SerialNumber, device.mac);
42
+ }
43
+ // Update the hardware revision for this device, if available.
44
+ if ((_f = device.hardwareRevision) === null || _f === void 0 ? void 0 : _f.length) {
45
+ (_g = accessory
46
+ .getService(this.hap.Service.AccessoryInformation)) === null || _g === void 0 ? void 0 : _g.updateCharacteristic(this.hap.Characteristic.HardwareRevision, device.hardwareRevision);
47
+ }
48
+ // Update the firmware revision for this device.
49
+ if ((_h = device.firmwareVersion) === null || _h === void 0 ? void 0 : _h.length) {
50
+ (_j = accessory
51
+ .getService(this.hap.Service.AccessoryInformation)) === null || _j === void 0 ? void 0 : _j.updateCharacteristic(this.hap.Characteristic.FirmwareRevision, device.firmwareVersion);
52
+ }
53
+ return true;
54
+ }
15
55
  // Utility function to return the fully enumerated name of this camera.
16
56
  name() {
17
57
  return this.nvr.nvrApi.getNvrName();
@@ -28,10 +68,113 @@ class ProtectAccessory extends ProtectBase {
28
68
  // Configure the device.
29
69
  void this.configureDevice();
30
70
  }
71
+ // Configure the device device information for HomeKit.
72
+ configureInfo() {
73
+ return this.setInfo(this.accessory, this.accessory.context.device);
74
+ }
75
+ // Configure the Protect motion sensor for HomeKit.
76
+ configureMotionSensor(isEnabled = true) {
77
+ var _a, _b;
78
+ const accessory = this.accessory;
79
+ const hap = this.hap;
80
+ // Find the motion sensor service, if it exists.
81
+ let motionService = accessory.getService(hap.Service.MotionSensor);
82
+ // Have we disabled motion sensors?
83
+ if (!isEnabled || !((_a = this.nvr) === null || _a === void 0 ? void 0 : _a.optionEnabled(accessory.context.device, "Motion.Sensor"))) {
84
+ if (motionService) {
85
+ accessory.removeService(motionService);
86
+ (_b = this.nvr.mqtt) === null || _b === void 0 ? void 0 : _b.unsubscribe(accessory, "motion/trigger");
87
+ this.log.info("%s: Disabling motion sensor.", this.name());
88
+ }
89
+ return false;
90
+ }
91
+ // The motion sensor has already been configured.
92
+ if (motionService) {
93
+ this.configureMqttMotionTrigger();
94
+ return true;
95
+ }
96
+ // We don't have it, add the motion sensor to the camera.
97
+ motionService = new hap.Service.MotionSensor(accessory.displayName);
98
+ if (!motionService) {
99
+ this.log.error("%s: Unable to add motion sensor.", this.name());
100
+ return false;
101
+ }
102
+ accessory.addService(motionService);
103
+ this.configureMqttMotionTrigger();
104
+ this.log.info("%s: Enabling motion sensor.", this.name());
105
+ return true;
106
+ }
107
+ // Configure a switch to easily activate or deactivate motion sensor detection for HomeKit.
108
+ configureMotionSwitch() {
109
+ var _a, _b, _c;
110
+ // Find the switch service, if it exists.
111
+ let switchService = this.accessory.getServiceById(this.hap.Service.Switch, exports.PROTECT_SWITCH_MOTION_SENSOR);
112
+ // Have we disabled motion sensors or the motion switch? Motion switches are disabled by default.
113
+ if (!((_a = this.nvr) === null || _a === void 0 ? void 0 : _a.optionEnabled(this.accessory.context.device, "Motion.Sensor")) ||
114
+ !((_b = this.nvr) === null || _b === void 0 ? void 0 : _b.optionEnabled(this.accessory.context.device, "Motion.Switch", false))) {
115
+ if (switchService) {
116
+ this.accessory.removeService(switchService);
117
+ }
118
+ // If we disable the switch, make sure we fully reset it's state.
119
+ this.accessory.context.detectMotion = true;
120
+ return false;
121
+ }
122
+ this.log.info("%s: Enabling motion sensor switch.", this.name());
123
+ // Add the switch to the camera, if needed.
124
+ if (!switchService) {
125
+ switchService = new this.hap.Service.Switch(this.accessory.displayName + " Motion Events", exports.PROTECT_SWITCH_MOTION_SENSOR);
126
+ if (!switchService) {
127
+ this.log.error("%s: Unable to add motion sensor switch.", this.name());
128
+ return false;
129
+ }
130
+ this.accessory.addService(switchService);
131
+ }
132
+ // Activate or deactivate motion detection.
133
+ (_c = switchService
134
+ .getCharacteristic(this.hap.Characteristic.On)) === null || _c === void 0 ? void 0 : _c.onGet(() => {
135
+ return this.accessory.context.detectMotion === true;
136
+ }).onSet((value) => {
137
+ if (this.accessory.context.detectMotion !== value) {
138
+ this.log.info("%s: Motion detection %s.", this.name(), (value === true) ? "enabled" : "disabled");
139
+ }
140
+ this.accessory.context.detectMotion = value === true;
141
+ });
142
+ // Initialize the switch.
143
+ switchService.updateCharacteristic(this.hap.Characteristic.On, this.accessory.context.detectMotion);
144
+ return true;
145
+ }
146
+ // Configure MQTT motion triggers.
147
+ configureMqttMotionTrigger() {
148
+ var _a;
149
+ // Trigger a motion event in MQTT, if requested to do so.
150
+ (_a = this.nvr.mqtt) === null || _a === void 0 ? void 0 : _a.subscribe(this.accessory, "motion/trigger", (message) => {
151
+ const value = message.toString();
152
+ // When we get the right message, we trigger the motion event.
153
+ if ((value === null || value === void 0 ? void 0 : value.toLowerCase()) !== "true") {
154
+ return;
155
+ }
156
+ // Trigger the motion event.
157
+ this.nvr.events.motionEventHandler(this.accessory, Date.now());
158
+ this.log.info("%s: Motion event triggered via MQTT.", this.name());
159
+ });
160
+ return true;
161
+ }
162
+ // Utility function for reserved identifiers for switches.
163
+ isReservedName(name) {
164
+ return name === undefined ? false :
165
+ [
166
+ exports.PROTECT_CONTACT_MOTION_SMARTDETECT.toUpperCase(),
167
+ exports.PROTECT_SWITCH_DOORBELL_TRIGGER.toUpperCase(),
168
+ exports.PROTECT_SWITCH_MOTION_SENSOR.toUpperCase(),
169
+ exports.PROTECT_SWITCH_MOTION_TRIGGER.toUpperCase(),
170
+ exports.PROTECT_CONTACT_SENSOR.toUpperCase(),
171
+ exports.PROTECT_CONTACT_SENSOR_ALARM_SOUND.toUpperCase()
172
+ ].includes(name.toUpperCase());
173
+ }
31
174
  // Utility function to return the fully enumerated name of this camera.
32
175
  name() {
33
176
  var _a;
34
- return this.nvr.nvrApi.getFullName((_a = this.accessory.context.camera) !== null && _a !== void 0 ? _a : null);
177
+ return this.nvr.nvrApi.getFullName((_a = this.accessory.context.device) !== null && _a !== void 0 ? _a : null);
35
178
  }
36
179
  }
37
180
  exports.ProtectAccessory = ProtectAccessory;
@@ -1 +1 @@
1
- {"version":3,"file":"protect-accessory.js","sourceRoot":"","sources":["../src/protect-accessory.ts"],"names":[],"mappings":";;;AAoBA,MAAsB,WAAW;IAS/B,yEAAyE;IACzE,YAAY,GAAe;QAEzB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC;QAC5B,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;QACxB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC;QAC5B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAED,uEAAuE;IAChE,IAAI;QACT,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;IACtC,CAAC;CACF;AAzBD,kCAyBC;AAED,MAAsB,gBAAiB,SAAQ,WAAW;IAGxD,yEAAyE;IACzE,YAAY,GAAe,EAAE,SAA4B;QAEvD,0CAA0C;QAC1C,KAAK,CAAC,GAAG,CAAC,CAAC;QAEX,qBAAqB;QACrB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAE3B,wBAAwB;QACxB,KAAK,IAAI,CAAC,eAAe,EAAE,CAAC;IAC9B,CAAC;IAMD,uEAAuE;IAChE,IAAI;;QACT,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,MAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAA8B,mCAAI,IAAI,CAAC,CAAC;IACrG,CAAC;CACF;AAxBD,4CAwBC"}
1
+ {"version":3,"file":"protect-accessory.js","sourceRoot":"","sources":["../src/protect-accessory.ts"],"names":[],"mappings":";;;AAgBA,mCAAmC;AACtB,QAAA,kCAAkC,GAAG,0BAA0B,CAAC;AAChE,QAAA,sBAAsB,GAAG,eAAe,CAAC;AACzC,QAAA,kCAAkC,GAAG,mBAAmB,CAAC;AAEtE,2BAA2B;AACd,QAAA,+BAA+B,GAAG,iBAAiB,CAAC;AACpD,QAAA,4BAA4B,GAAG,oBAAoB,CAAC;AACpD,QAAA,6BAA6B,GAAG,qBAAqB,CAAC;AAOnE,MAAsB,WAAW;IAU/B,yEAAyE;IACzE,YAAY,GAAe;QAEzB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC;QAC5B,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;QACxB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC;QAC5B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAED,uDAAuD;IAC7C,OAAO,CAAC,SAA4B,EAAE,MAA0D;;QAExG,yCAAyC;QACzC,IAAG,CAAC,MAAM,EAAE;YACV,OAAO,KAAK,CAAC;SACd;QAED,uDAAuD;QACvD,MAAA,SAAS;aACN,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,oBAAoB,CAAC,0CAChD,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,YAAY,EAAE,mBAAmB,CAAC,CAAC;QAEpF,gDAAgD;QAChD,IAAG,MAAA,MAAM,CAAC,IAAI,0CAAE,MAAM,EAAE;YACtB,MAAA,SAAS;iBACN,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,oBAAoB,CAAC,0CAChD,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;SACtE;QAED,4CAA4C;QAC5C,IAAG,MAAA,MAAM,CAAC,GAAG,0CAAE,MAAM,EAAE;YACrB,MAAA,SAAS;iBACN,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,oBAAoB,CAAC,0CAChD,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,YAAY,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;SAC5E;QAED,8DAA8D;QAC9D,IAAG,MAAA,MAAM,CAAC,gBAAgB,0CAAE,MAAM,EAAE;YAClC,MAAA,SAAS;iBACN,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,oBAAoB,CAAC,0CAChD,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC;SAC7F;QAED,gDAAgD;QAChD,IAAG,MAAA,MAAM,CAAC,eAAe,0CAAE,MAAM,EAAE;YACjC,MAAA,SAAS;iBACN,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,oBAAoB,CAAC,0CAChD,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,gBAAgB,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;SAC5F;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED,uEAAuE;IAChE,IAAI;QACT,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;IACtC,CAAC;CACF;AAtED,kCAsEC;AAED,MAAsB,gBAAiB,SAAQ,WAAW;IAIxD,yEAAyE;IACzE,YAAY,GAAe,EAAE,SAA4B;QAEvD,0CAA0C;QAC1C,KAAK,CAAC,GAAG,CAAC,CAAC;QAEX,qBAAqB;QACrB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAE3B,wBAAwB;QACxB,KAAK,IAAI,CAAC,eAAe,EAAE,CAAC;IAC9B,CAAC;IAMD,uDAAuD;IAC7C,aAAa;QAErB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAgC,CAAC,CAAC;IAC/F,CAAC;IAED,mDAAmD;IACzC,qBAAqB,CAAC,SAAS,GAAG,IAAI;;QAE9C,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QACjC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QAErB,gDAAgD;QAChD,IAAI,aAAa,GAAG,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAEnE,mCAAmC;QACnC,IAAG,CAAC,SAAS,IAAI,CAAC,CAAA,MAAA,IAAI,CAAC,GAAG,0CAAE,aAAa,CAAC,SAAS,CAAC,OAAO,CAAC,MAA6B,EAAE,eAAe,CAAC,CAAA,EAAE;YAE3G,IAAG,aAAa,EAAE;gBAChB,SAAS,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;gBACvC,MAAA,IAAI,CAAC,GAAG,CAAC,IAAI,0CAAE,WAAW,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC;gBACxD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,8BAA8B,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;aAC5D;YAED,OAAO,KAAK,CAAC;SACd;QAED,iDAAiD;QACjD,IAAG,aAAa,EAAE;YAChB,IAAI,CAAC,0BAA0B,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC;SACb;QAED,yDAAyD;QACzD,aAAa,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QAEpE,IAAG,CAAC,aAAa,EAAE;YACjB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,kCAAkC,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YAChE,OAAO,KAAK,CAAC;SACd;QAED,SAAS,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;QACpC,IAAI,CAAC,0BAA0B,EAAE,CAAC;QAElC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,6BAA6B,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAE1D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,2FAA2F;IACjF,qBAAqB;;QAE7B,yCAAyC;QACzC,IAAI,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,oCAA4B,CAAC,CAAC;QAEzG,iGAAiG;QACjG,IAAG,CAAC,CAAA,MAAA,IAAI,CAAC,GAAG,0CAAE,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAA6B,EAAE,eAAe,CAAC,CAAA;YAChG,CAAC,CAAA,MAAA,IAAI,CAAC,GAAG,0CAAE,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAA6B,EAAE,eAAe,EAAE,KAAK,CAAC,CAAA,EAAE;YAExG,IAAG,aAAa,EAAE;gBAChB,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;aAC7C;YAED,iEAAiE;YACjE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;YAC3C,OAAO,KAAK,CAAC;SACd;QAED,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,oCAAoC,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAEjE,2CAA2C;QAC3C,IAAG,CAAC,aAAa,EAAE;YACjB,aAAa,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,GAAG,gBAAgB,EAAE,oCAA4B,CAAC,CAAC;YAEzH,IAAG,CAAC,aAAa,EAAE;gBACjB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,yCAAyC,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;gBACvE,OAAO,KAAK,CAAC;aACd;YAED,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;SAC1C;QAED,2CAA2C;QAC3C,MAAA,aAAa;aACV,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC,0CAC5C,KAAK,CAAC,GAAG,EAAE;YAEX,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,YAAY,KAAK,IAAI,CAAC;QACtD,CAAC,EACA,KAAK,CAAC,CAAC,KAA0B,EAAE,EAAE;YAEpC,IAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,YAAY,KAAK,KAAK,EAAE;gBAChD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,0BAA0B,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;aACnG;YAED,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,YAAY,GAAG,KAAK,KAAK,IAAI,CAAC;QACvD,CAAC,CAAC,CAAC;QAGL,yBAAyB;QACzB,aAAa,CAAC,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,YAAuB,CAAC,CAAC;QAE/G,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kCAAkC;IAC1B,0BAA0B;;QAEhC,yDAAyD;QACzD,MAAA,IAAI,CAAC,GAAG,CAAC,IAAI,0CAAE,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,EAAE,CAAC,OAAe,EAAE,EAAE;YAE7E,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;YAEjC,8DAA8D;YAC9D,IAAG,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,WAAW,EAAE,MAAK,MAAM,EAAE;gBAClC,OAAO;aACR;YAED,4BAA4B;YAC5B,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;YAC/D,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,sCAAsC,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACrE,CAAC,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC;IACd,CAAC;IAED,0DAA0D;IACnD,cAAc,CAAC,IAAwB;QAE5C,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACjC;gBACE,0CAAkC,CAAC,WAAW,EAAE;gBAChD,uCAA+B,CAAC,WAAW,EAAE;gBAC7C,oCAA4B,CAAC,WAAW,EAAE;gBAC1C,qCAA6B,CAAC,WAAW,EAAE;gBAC3C,8BAAsB,CAAC,WAAW,EAAE;gBACpC,0CAAkC,CAAC,WAAW,EAAE;aACjD,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IACnC,CAAC;IAED,uEAAuE;IAChE,IAAI;;QACT,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,MAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAA8B,mCAAI,IAAI,CAAC,CAAC;IACrG,CAAC;CACF;AArKD,4CAqKC"}