homebridge-ring-hksv 14.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1765 -0
- package/LICENSE +21 -0
- package/README.md +119 -0
- package/branding/Homebridge_x_Ring.svg +1 -0
- package/branding/icon.png +0 -0
- package/branding/lighting.png +0 -0
- package/branding/product.png +0 -0
- package/branding/security-system.png +0 -0
- package/config.schema.json +297 -0
- package/lib/base-accessory.js +81 -0
- package/lib/base-data-accessory.js +60 -0
- package/lib/base-device-accessory.js +109 -0
- package/lib/beam.js +66 -0
- package/lib/brightness-only.js +52 -0
- package/lib/camera-source.js +669 -0
- package/lib/camera.js +201 -0
- package/lib/chime.js +116 -0
- package/lib/co-alarm.js +24 -0
- package/lib/config.js +46 -0
- package/lib/contact-sensor.js +24 -0
- package/lib/fan.js +41 -0
- package/lib/flood-freeze-sensor.js +46 -0
- package/lib/freeze-sensor.js +31 -0
- package/lib/hap.js +4 -0
- package/lib/homebridge-ui/public/index.html +180 -0
- package/lib/homebridge-ui/public/tsconfig.json +10 -0
- package/lib/homebridge-ui/server.js +71 -0
- package/lib/index.js +6 -0
- package/lib/intercom.js +122 -0
- package/lib/location-mode-switch.js +107 -0
- package/lib/lock.js +60 -0
- package/lib/motion-sensor.js +20 -0
- package/lib/multi-level-switch.js +42 -0
- package/lib/outlet.js +30 -0
- package/lib/panic-buttons.js +105 -0
- package/lib/ring-platform.js +277 -0
- package/lib/security-panel.js +137 -0
- package/lib/smoke-alarm.js +24 -0
- package/lib/smoke-co-listener.js +37 -0
- package/lib/switch.js +25 -0
- package/lib/target-value-timer.js +24 -0
- package/lib/temperature-sensor.js +23 -0
- package/lib/thermostat.js +179 -0
- package/lib/unknown-zwave-switch.js +25 -0
- package/lib/util.js +8 -0
- package/lib/valve.js +34 -0
- package/lib/water-sensor.js +31 -0
- package/media/camera-offline.jpg +0 -0
- package/media/snapshots-blocked.jpg +0 -0
- package/package.json +82 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Dusty Greif
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://github.com/homebridge/verified/blob/master/verified-plugins.json"><img alt="Homebridge Verified" src="https://raw.githubusercontent.com/dgreif/ring/main/packages/homebridge-ring/branding/Homebridge_x_Ring.svg?sanitize=true" width="500px"></a>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# homebridge-ring-hksv
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/homebridge-ring-hksv)
|
|
8
|
+
[](https://www.npmjs.com/package/homebridge-ring-hksv)
|
|
9
|
+
[](https://github.com/homebridge/homebridge/wiki/Verified-Plugins)
|
|
10
|
+
[](https://plugins.hoobs.org/plugin/homebridge-ring-hksv)
|
|
11
|
+
[](https://buymeacoffee.com/trinityhades)
|
|
12
|
+
|
|
13
|
+
# homebridge-ring-hksv
|
|
14
|
+
|
|
15
|
+
`homebridge-ring-hksv` is a Homebridge platform plugin for Ring devices, with HomeKit Secure Video (HKSV) support.
|
|
16
|
+
|
|
17
|
+
## Origin and Attribution
|
|
18
|
+
|
|
19
|
+
This project is based on Dustin Greif's original Ring Homebridge ecosystem:
|
|
20
|
+
|
|
21
|
+
- Upstream plugin: `dgreif/homebridge-ring`
|
|
22
|
+
- Upstream API library: `dgreif/ring-client-api`
|
|
23
|
+
|
|
24
|
+
Big thanks to Dustin and all upstream contributors. This fork reuses and extends that foundation.
|
|
25
|
+
|
|
26
|
+
## What This Plugin Does
|
|
27
|
+
|
|
28
|
+
This plugin integrates Ring locations and devices into HomeKit via Homebridge, including:
|
|
29
|
+
|
|
30
|
+
- Ring cameras and doorbells
|
|
31
|
+
- Ring Alarm devices and sensors
|
|
32
|
+
- Ring chimes and intercoms
|
|
33
|
+
- Ring Smart Lighting and supported alarm-connected devices
|
|
34
|
+
- Thermostats, locks, outlets/switches, valves, and other supported accessory types
|
|
35
|
+
|
|
36
|
+
It also introduces HKSV-focused controls such as:
|
|
37
|
+
|
|
38
|
+
- `enableHksv`
|
|
39
|
+
- `disableHksvOnBattery`
|
|
40
|
+
- `hksvPrebufferLengthMs`
|
|
41
|
+
- `hksvFragmentLengthMs`
|
|
42
|
+
- `hksvMaxRecordingSeconds`
|
|
43
|
+
|
|
44
|
+
## How This Fork Differs From Upstream
|
|
45
|
+
|
|
46
|
+
This fork is intended as a standalone package/repository with your own plugin identity and HKSV-focused iteration.
|
|
47
|
+
|
|
48
|
+
In short:
|
|
49
|
+
|
|
50
|
+
- Standalone plugin packaging
|
|
51
|
+
- Custom branding/repository ownership
|
|
52
|
+
- Additional HKSV recording controls exposed in config
|
|
53
|
+
|
|
54
|
+
Core Ring platform behavior still relies on `ring-client-api`, so many upstream troubleshooting guides remain relevant.
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
If Homebridge is installed globally:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm i -g --unsafe-perm homebridge-ring-hksv
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
If running from source:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm install
|
|
68
|
+
npm run build
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Basic Configuration
|
|
72
|
+
|
|
73
|
+
Use Homebridge UI (`homebridge-config-ui-x`) when possible.
|
|
74
|
+
|
|
75
|
+
Add a platform block with your Ring refresh token:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"platform": "RingHKSV",
|
|
80
|
+
"refreshToken": "your-refresh-token"
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Important Options
|
|
85
|
+
|
|
86
|
+
| Option | Purpose |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| `enableHksv` | Enables experimental HKSV support for eligible cameras |
|
|
89
|
+
| `disableHksvOnBattery` | Disables HKSV on battery cameras to reduce battery/network usage |
|
|
90
|
+
| `hksvPrebufferLengthMs` | HKSV prebuffer duration (minimum 4000ms) |
|
|
91
|
+
| `hksvFragmentLengthMs` | HKSV fragment duration target |
|
|
92
|
+
| `hksvMaxRecordingSeconds` | Optional safety cap for a recording session |
|
|
93
|
+
| `cameraVideoCodec` | Preferred H.264 encoder (`h264_videotoolbox` or `libx264`) |
|
|
94
|
+
| `hideDoorbellSwitch` / `hideCameraMotionSensor` / `hideCameraSirenSwitch` | Hides specific HomeKit-exposed services |
|
|
95
|
+
| `showPanicButtons` | Adds panic switches (use with caution) |
|
|
96
|
+
| `ffmpegPath` | Override FFmpeg binary path |
|
|
97
|
+
| `debug` | Enables additional logging |
|
|
98
|
+
| `disableLogs` | Disables plugin logging |
|
|
99
|
+
|
|
100
|
+
## HKSV Status
|
|
101
|
+
|
|
102
|
+
HKSV support is experimental and actively evolving. Behavior may vary by camera model, wired vs battery power, Ring API changes, and FFmpeg environment.
|
|
103
|
+
|
|
104
|
+
## Troubleshooting
|
|
105
|
+
|
|
106
|
+
For support and debugging:
|
|
107
|
+
|
|
108
|
+
- This repository issues: <https://github.com/trinityhades/homebridge-ring-hksv/issues>
|
|
109
|
+
- Upstream Ring wiki (token/auth/camera references): <https://github.com/dgreif/ring/wiki>
|
|
110
|
+
|
|
111
|
+
## Disclaimer
|
|
112
|
+
|
|
113
|
+
This plugin is not affiliated with Ring or Amazon.
|
|
114
|
+
|
|
115
|
+
Use emergency/panic-related automations at your own risk.
|
|
116
|
+
|
|
117
|
+
## License
|
|
118
|
+
|
|
119
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1800" height="630" viewBox="0 0 1800 630"><defs><style>.cls-1{isolation:isolate;}.cls-2{fill:#535556;}.cls-3{fill:#1c9ad6;}.cls-4{fill:#9596ae;}.cls-5{fill:url(#linear-gradient);}.cls-6{fill:url(#radial-gradient);}.cls-7{opacity:0.25;mix-blend-mode:multiply;}.cls-8{fill:#fff;}.cls-9{fill:none;stroke:#9596ae;stroke-linecap:round;stroke-miterlimit:10;stroke-width:5px;}</style><linearGradient id="linear-gradient" x1="450" y1="86.48" x2="450" y2="386.48" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#57277c"/><stop offset="1" stop-color="#491f59"/></linearGradient><radialGradient id="radial-gradient" cx="450" cy="236.48" r="150" gradientUnits="userSpaceOnUse"><stop offset="0" stop-opacity="0"/><stop offset="1" stop-opacity="0.1"/></radialGradient></defs><g class="cls-1"><g id="Plugin_Logo" data-name="Plugin Logo"><path class="cls-2" d="M1210.33,243.93a9.27,9.27,0,0,1-2.9,6.77,9.61,9.61,0,0,1-7.08,2.82,10,10,0,0,1-6.77-2.9c-7.1-7.1-18.76-9.94-26.38-9.91q-17.39,0-27.2,11.9t-9.83,33.79V391.09a8.7,8.7,0,0,1-3.05,7.25,10.06,10.06,0,0,1-13.18,0,8.62,8.62,0,0,1-3-7.25V286.4a86.7,86.7,0,0,1,3.54-25.25,60.49,60.49,0,0,1,10.62-20.58,50.73,50.73,0,0,1,17.53-14.09,55.09,55.09,0,0,1,24.59-5.14,61.34,61.34,0,0,1,21.25,4.18,51.37,51.37,0,0,1,18.66,11.58,9.58,9.58,0,0,1,3.25,6.84Z"/><path class="cls-2" d="M1258.57,391.08a8.68,8.68,0,0,1-3,7.26,10.22,10.22,0,0,1-13.21,0,8.68,8.68,0,0,1-3-7.26V234.27a8.67,8.67,0,0,1,3-7.25,10.24,10.24,0,0,1,13.21,0,8.69,8.69,0,0,1,3,7.25Z"/><path class="cls-2" d="M1426,391.08a8.72,8.72,0,0,1-3.07,7.26,10.24,10.24,0,0,1-13.19,0,8.64,8.64,0,0,1-3-7.26V284.16a42.28,42.28,0,0,0-3.4-16.89,43.47,43.47,0,0,0-80.11,0,42.25,42.25,0,0,0-3.37,16.89V391.08a8.68,8.68,0,0,1-3,7.26,10.22,10.22,0,0,1-13.21,0,8.68,8.68,0,0,1-3-7.26V284.16a61.22,61.22,0,0,1,4.92-24.47,63.29,63.29,0,0,1,13.35-20,64,64,0,0,1,19.68-13.35,62.85,62.85,0,0,1,49.18,0,61.7,61.7,0,0,1,33.29,33.29,61,61,0,0,1,4.92,24.47Z"/><path class="cls-2" d="M1590,402.34a60.9,60.9,0,0,1-4.91,24.59,62.18,62.18,0,0,1-33.37,33.15,61.22,61.22,0,0,1-24.47,4.92,73.94,73.94,0,0,1-14.16-1.45,81.45,81.45,0,0,1-14.76-4.35,64.35,64.35,0,0,1-13.5-7.38,40.48,40.48,0,0,1-10.13-10.3,9.26,9.26,0,0,1-1.6-5.49,8.83,8.83,0,0,1,3-6.91,10,10,0,0,1,6.91-2.75,11,11,0,0,1,4,.81,7.79,7.79,0,0,1,3.71,3.37c3.83,5.76,12.08,10.32,17.54,12.07a61,61,0,0,0,18.81,3.08,42.45,42.45,0,0,0,16.89-3.4,43.67,43.67,0,0,0,23.16-23.16,42.24,42.24,0,0,0,3.52-16.8V380.21a62.5,62.5,0,0,1-19.67,12.71,61.72,61.72,0,0,1-23.8,4.68,61,61,0,0,1-24.6-4.92,62.63,62.63,0,0,1-33.14-33.15,60.57,60.57,0,0,1-4.92-24.59V284.16a61,61,0,0,1,4.92-24.47,62.9,62.9,0,0,1,13.35-20,63.91,63.91,0,0,1,19.67-13.35,62.85,62.85,0,0,1,49.18,0,62.51,62.51,0,0,1,38.22,57.76v118.2Zm-19.3-118.18a42.27,42.27,0,0,0-3.39-16.89,43.58,43.58,0,0,0-80.22,0,42.68,42.68,0,0,0-3.37,16.9v50.53a42.7,42.7,0,0,0,3.37,17,43.72,43.72,0,0,0,9.35,13.84,43.19,43.19,0,0,0,13.84,9.32,43.47,43.47,0,0,0,57-23.16,42.38,42.38,0,0,0,3.47-17Z"/><circle class="cls-3" cx="1248.94" cy="181.5" r="16.5"/></g><g id="Homebridge"><g id="Text"><path class="cls-4" d="M136.23,471.8h29V440.67H178v75.9H165.27V482.8h-29v33.77H123.47v-75.9h12.76Z"/><path class="cls-4" d="M222.8,517.56c-16.94,0-28.38-10.23-28.38-34.1v-9.68c0-23.87,11.44-34.1,28.38-34.1s28.49,10.23,28.49,34.1v9.68C251.29,507.33,239.85,517.56,222.8,517.56Zm-15.62-34.1c0,16.39,5.5,23,15.62,23s15.73-6.6,15.73-23v-9.68c0-16.39-5.61-23-15.73-23s-15.62,6.6-15.62,23Z"/><path class="cls-4" d="M301.67,484.67l21.89-44h12.21v75.9H323.45V465.75l-17.38,36.19h-9L280,466.08v50.49H267.68v-75.9h12.1Z"/><path class="cls-4" d="M401.77,440.67v11.11H368v20h30.47v11H368v22.77h33.77v11H355.24v-75.9Z"/><path class="cls-4" d="M441,440.67c17.82,0,26.95,6.6,26.95,19.69a17.12,17.12,0,0,1-11.11,16.5c8.58,2.42,13.53,8.36,13.53,17.93,0,11.44-7.26,21.78-27,21.78H416.62v-75.9Zm-11.66,31h12.43c9.46,0,13.42-4.29,13.42-10.56,0-5.94-3-9.46-15.07-9.46H429.38Zm0,33.88h13.31c10.23,0,15-4.18,15-11.44,0-8-5.28-11.44-16.5-11.44H429.38Z"/><path class="cls-4" d="M527.72,516.57l-14.63-30.8H498.35v30.8H485.59v-75.9h25.85c17.49,0,27.94,7.59,27.94,22.66,0,10.45-5,17.16-13.75,20.35l16.17,32.89Zm-17.16-41.8c10.78,0,16.06-2.86,16.06-11.44s-5.28-11.55-16.06-11.55H498.35v23Z"/><path class="cls-4" d="M569.4,516.57H556.64v-75.9H569.4Z"/><path class="cls-4" d="M589.86,516.57v-75.9h25.63c17,0,28.49,9.13,28.49,33.44v9c0,24.31-11.44,33.44-28.49,33.44Zm41.36-33.44v-9c0-15.62-4.84-22.33-15.73-22.33H602.62v53.79h12.87C626.38,505.57,631.22,498.75,631.22,483.13Z"/><path class="cls-4" d="M713.61,477.63v5.83c0,23.87-10.89,34.1-27.94,34.1s-28.38-10.23-28.38-34.1v-9.13c0-24.42,11.44-34.65,28.38-34.65,15.4,0,25.19,8.58,27.61,24.53H701c-1.87-8.8-6.71-13.42-15.29-13.42-10.12,0-15.62,6.6-15.62,23v9.68c0,16.39,5.5,23,15.62,23,9.68,0,14.52-5.72,15.4-17.71H686V477.63Z"/><path class="cls-4" d="M776.53,440.67v11.11H742.76v20h30.47v11H742.76v22.77h33.77v11H730v-75.9Z"/></g><g id="Logo"><circle class="cls-5" cx="450" cy="236.48" r="150"/><circle class="cls-6" cx="450" cy="236.48" r="150"/><image class="cls-7" width="255" height="258" transform="translate(324 110.23)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAP8AAAECCAYAAAAxcwG5AAAACXBIWXMAAAsSAAALEgHS3X78AAAgAElEQVR4Xu2d63ajOrNFl51L78v3/k969qU7F58fztpMFiWBHScxDjWGhjDGIKSaq0qCdO8Oh4M222yz72f7uQM222yz27QN/s02+6Z2P3fAZuu33W63mzvmsM3/vp3ttjG/PVsA+05Sd+A3Mbh92+C/ESuA7wnAHPyj7zYhuE3b0v4V2wzwre3WvibgvMwmBLdjW+RfoQX01Xar5nE58IdGXe7bRGD9tkX+lRnAr+rcruDndgvwgwrgeazbsYnAem2L/CuxItpXoLdK/oaWoPdKdfwmACu1LfKvwBrRvir7qHtCUKX5B0mvjW1/TtuygJXaBv+VW4DPbQK+R2l9nov8hP0VhZ93GgvC6Dy73W63CcB6bIP/Sq2R5rei/J0G2O+KfTxeqBP8LC/FPgpAZgJbFrAi2+C/QptJ8/dFueuUPJbnrcB/QZ3F+y0APIftIB3vYROA67YN/iuzDviZ0hPwe9T3+FyJADOKhN+QP3dqg08RaK4FbAJwvbbBf0U2A34V6Qn7Q9QpBISfkb8CP8vTW73XIAS7t9rGacDh7ftNAK7YNvivxBaC34Le5TE+VwJQwV+B/4Ryj20LAKcinhJImwCsxjb4r8BOAL+C/rFRUgQy+mfKX0H/CyXXDygiiu1cDPxUAYjF0q59Vpuu0Tb4v9jOAL+C/kdRUgCcNRDaTPkr6H9quo5QiYCnADuNo7/tYgJwCtxzNneuS7T3Wm2D/wutAT4X9rhgl+m9Af/trfyI7RSACn6m/Y76hP4nfv+vpvDvoqRd7FFgA9KLiUBh/z21+G/HiW2+dtvg/yI7E/yM9Ib996gtAITfkTvh53zfkf8nyr/x+x78mQVIhQBIWvQosAB+7nNr35wtgfpwa0Kwwf8FdgL4TPVb0GfJ6F/N+22O/Dnfr8Bvpf1utzp1rgFIjWnADPB53t72qTaaomjc9smx52Yw12Qb/J9sFwLfoP9R1L+hOFPIFX9bwv+sY8r/QwP4Ffxzqb9rZwDdJwGqrScoc2JT2U79CH/o1P+1N3+zZhHY4P9Eeyf4Ge3/KAqjv9N+wk9g7dSc8xv+X5o+LcgnBnOpf8vKF4Leav4uYa8EptpH67VjAjJqltY+2sUWMz/TNvg/yc4An6v5BJ+w/6k2/Hzkx6htWOjMXO1vQW/wud0CPwFNS7CqvnHNMnfNlgh4Xwku6l55LfaNzrO2LGCD/xPswuD/2agNf7XYl8Da6NgvGq5P6DPdn4v+Kmpvexrga3p/JQB5XvbXPva1RIDnoxHOOdj9OHQf+7oisAYB2OD/YLsQ+Ab7T5QK/N81XeirIjXNjpyAJ+xz0C8pNs79E/wEPqG/a+zvXc/nnoBaFMPO7Rdsv76di8fwfDutRAA2+D/QLgA+V/H/LApT/tYiX8JKO7ztt9NW4OXvE7a56FtBWP1lYPZRlkqIKiGYu740iEAv0vOvGFvbbH+5mHnNArDB/0F2QfA5v6/gn4v4BIGOWDml23avGuZKCKpjWiJQfWY7+HsC3pp+VNOQ1rVtFfiEnpCz5F847lCnCKxCADb4P8A+GPz/YR/BZ8Q3ELaMTKylMYRuIy2ByrIk4vKzweHcmeeqoM/1iBSDOQGgLQW/+uvGvYa/cKQA6O33e4UA6Eptg//C9oHg/0/juX4V8Z3m+9qZztrZhTqBJSzMAvh93lPCNlcMz4vGcFTg32v89OHcdw9oLfgr6PmHTndR+/z5RiP7f9HbjF9hG/wXtCsCP6HnHJUCII1hTnj8/V0ce0qpfufoeafxgln2ER87PkbdE4CqDbQK/oz4vb9uJPjV+aXxfV1l+r/BfyH7YvDvNFy3imR2aO+vUm3C4206+HsEgIXRnYtmbAf76EHjac2j+gLAPvd5XWfWQ4HMqN/668YUmLS8Ru6/Gtvgv4BdAfgZ8dOJvW0HJ/yG0Y59H4WOTpER9mfh9zb2hyP/s6ZtYar/qOFe+ZeKXNjMl5ha2YuNcLZS/h747G9annevQVwk6erS/w3+d9qVgC8NTszXdJ9QWwQY/TPaPkR51TTaGdAKeNZZGPHvNW6P28J2uI/8ajNrvsTk45mtEP6EVJqm/c5AqqjvP2sm/C1RsZjcaZxZ8ZirsQ3+d9iVgm/H/Rm1BYBzf8LMubWh83lbcElT+Fuf9yj3OrbJ0dbHJvz8mwaWfIORAsX+93kry8hfwf9Tx+twauHzVuehoOw19LGPu6rov8F/pl0Z+ExX+ee4rH9pcO6Mtneats9g+tiHt+tZAHYaMgBbC3z2yb2GaFq1xUD7ft1XrjPyG36fP6/bM4Jbpf3MLnyN1u+rQjEYicA12Ab/GXaF4PPv8P99K/9g+18N0b+Xavs6FAk6sY0ZgC1hSxHItP9Rx/b4/Pkdo34P/kzH5yK+v/P9pAAQ/hQXnpPQvxTFqT+LJF1N9N/gP9GuFHynqIY+i+F39H99O4fb7HbyGEf9hN91TgESfJYE/0GDEGXaz5T/h8bgM+Wv0vEl0d7GSEwBsPBV4Fs0qmwh4X/Fb7JdXw6+tMF/kl0x+Iz2/0j6G8XfMfWvIr/h97oA4c8U1kbobOnwhJ/z+WxLL/L3ov4ehcZ2ersaPx7zqmE6wlSf8Lo/novy9PYb999eYxG4CuhtG/wLbQXg/60j+H9pDL8j/0/Np/2cGmTk7xnhy8jL/mHfVFlIwp8CUKX7VTqeZU6c/L0FwGNZgc9ob+BZvP9OHQG4htR/g3+BXSn4XthjtP8r6oz8Ffx7DSku09dW1G85rAWgAi3T/swwfCzhTwGYA7+CvhIutmkX+6RxJtED/1dRPJ1piYew/aXgSxv8s7Yy8F0y8vv4uUd9jlo8xk7actYEiOm39/k+ON+v2sIsxPCzrsCvgHdfpXBVYkRAK2HQ228T/N80fhyYi4+ViLi9V2Eb/B1bCfiM9iwWBc73e/BXUX9JxLcZXvZRgpYi86wxmFVfPmgKl88tjTMUpuO8F7Y923NXfKfY/6rhKcgPjaF329w+ihN9xdObq7EN/oatHPxWym/4Cdxe88Ak+HNCkI7P/nrQAKmhFY51fxKoXNyzuZ2vms7BeT95v2zLPb6/1zQDyGzkSeOxrsDP6J+ZxUHSl8/7N/gLWyH4/xef+YiPUZ8pvc3304v45zgoU2pCVGUXUt23mea7Xwy803FD77k3F+B4HV7DGciDxu2QxhnMQdPxfihKFfUJ/tXZBn/YisFnxCf4GfUzEu50vG4F/qlGJ/d5fT/uO0brbEdmCYTJ507w3UdOxV3mpjlcV+C9si3sH445hakX9TPip2+d08cXsw1+2I2Bzxd7MuondJw3J/xZLzWC4/ui5fkSftYJPgWE4PtdB4qe75vXcLTmkw8bIZ0TgASe4DPyK+pT+/JDbIP/zW4QfEZ8z4cz1ZYGZ7y0Q7IfDU8rAlZ9zX1SDT7n+eynfKWZkf9OQ9TnFIjiQ+iF7cxMloCfGcDV2Aa/Vgl+65FeBT7TfUZ2W8JfReQllqBwf4Kzj/38PoGxEWDDnyl/vt7M1J/wP6p+3yEh9n5peh+VCPTE4+rs28O/cvBdV4t7ueI9N59/1byzzn2fVoHv/QlLBQ5BJ/gHDffl5+7sM77fUMHvvnHU36tO5d0W91fCP9f+vBcKwpfbt4Z/xeC7ZLTPVL+CvgLfYBiGF9UOe47jZp/6nrOPve39NkK/wz6n/b5fv+rMDOBfDX1x0HANg28x8MIdy4uG9vD+CXZLsPxZqGm+ny+1bwv/ysF3ZJt7nFfN79OqfY6s74F/VxSDcqdxuu19CZI0vQeD735z32X6z3k/4b/XNOI/aNx/fARIIZLqe+J9sK+y/yb2lc/6vyX8NwB+K9XPaE/baxw91dm2uX0VyF2nDqsEYK9xOxOm6rfMXty2fLnHTzjcn4R/p+N4cgrAl3f4slNrfcQ12/re/vkS+3bwfzPwfU+KfS3LvpHGoJ3i0Dy2giKzgBQrw57GyM/oTxH4hfqnxpHf6f5exzEh9Jk1ca3BlmC3Pqct7bdPs28F/w2Bb+i5mu1FLTprOm213TMfVwlAOn3PWvDsi+L2J/gUiDkBYCaQc36m/wk9F0cZ+SsRSuB7+67Svg38NwA+oz3T2Srat2CrvpPGULHmcYagWgvgcdX583vCv6RtLK+xnQJQCQHhl4ZFvxb0FfjsE/pQBXnla1dn3wL+GwE/U/0E3/fma+U9VpDtNHbuqvg7wviq4/VPdfL8vgLGxj5LGCkCr1EIMsH273ZxTJY8v1C37Nz7/1K7efhvCPze/J5A8944r65EwFaB1YLOguH7STHpWQ/2bI/byvZU4Fft7UG9n/neRbG91Ob64GrspuG/MfCriC+NYSbsd41tH+fftqB/iZqA2F51bI9tieP3YGI72H+9lLz1udpP8KtjKgFo2ZzgzX3/5Xaz8N8g+Iz4dkzek695r/F9+TNFoIr8CX3OhzNV9u8OmmYAS+xQ1AT1XkMf+ho9EbhUOddOyYCuwm4S/hsBP9/ae9bg8NLgbITepfpzU4uAs4MKfkLOVfNqVTzhe9EyS8gqAN0WZyo+/xIBUNS5v7XPdikxuHq7OfhvCPz8Rzdzjm+IE/rHqF2YARD+BM7gPzVKioABcTbQinoVSBX0bsejpv2Z4tMSAWl6Pfdb7qu2T7VVRPq0m4L/xsDPxT07J+f1Cf2jju15jMIMIOGXxtAx4v8qCoVgpyl8rQygFX17AvCgoa1sXyv7qASgJwhpc9+nrRJ6283Af8PgM9VP8BP6qjADSPidUhvajPr5mqy3OX3IrMQZAK0FZAV9CtG9xqk/M49e+j8H8lJBONVWIwg3Af+Ng890mml+Qp//uQX/rXtG/zn4Gdl/vp3DU5Bq7WCvqVClABCwHvyu3ZZHDdfzd72Xc3oCcNBwv5vpBuC/YfDt1NLxXnwfjPYG3G37DbUF4FT4mfL7Ph51bB/hz/WDncYC4HPSEsoW/Iz87mem/lx/aGUAtvy82ZutGv4bB98O6+iaab6jPO+BImABcHvPgd9R3xG4ivy7KGz/IbYJvesKfrblUeO+ftF47aFaBMzrpW1ioBXD/43Bd0T/XdN74D5GfsOf0dpG6Djn/6mxcDDqV/DTDGMF4VL4n3S8D19Xb99bnNxnmQEcotg26GGrhP+bgJ+pPuf22X4Wpv1c8Ksitq2KuE9qPyrswZ8iYBhbGQDBb7XD0Z+pP58+tB4Bpghk2761rQ7+bwZ+K+IT9j81Bp/w96J+BT/Bu4+S4Ge638oApHFUf8b+hLOC/4eO/XSv43UtJO73VgbQiv6bvdmq4P/m4FfR/s/4zPk+I34110/4Dd6dju25i7JvlB70tFY0zuhPEWLkN/w+17OGRckqA6imHZsQwFYD/wZ+2Xa2+Q9NF/ncFwmqNIXV3/Ugb0HfO68tQX+Offyugv9Bw3UP+K4X/RP+zWCrgP8bg88V/Qr6OfDdL4TzEHWa+9TbLC3wVdRpvl4FZYJP+A2578fn9zj0oj+vJU2v963t6uHfwJ+ATwHgPL8CnyC+vtUtxyfI/rzXYBXsFfxLLNcAqgyA8P/SMB6+jsfDC38/1Y/+KX7f3q4a/g38LvhudzXHdx9JA2SMrCkAhriVzt/Fcbntz6eY+8B1ZgZc9OOiZY6JxaFa/U/49xrf97cWgquFfwN/EfhudwV+K5VOAUjI96j38V0L9lPBl6aiZOM+wp+LlhYOpv+t1N92iPpb21XCv4F/Evg/NF7Yk8bQGyDWbIc0tCX7lSKgtzp95lzwpWN7DLvbVMHPOT/hz2NcMvWXhqhfgf8txeDq4N/APxn8bHcLimdNoWA67H51lH1QHXEpAEvBT7gyAif4eQ9uA+/Tv+f0gPeaIrfT+LrfPgu4Kvg38N8NvqM9F8oyJX5SG34D/6gBnkeNMwuXey2HP60SgwTfY3Cn473kVCRFwgJQpfw8ntf+aPCrqdG5fXZxuxr4N/AvAj4jIB+B/cS24X/RYIz6jzqe//lt+/WtPmjwF04FLmXZV4b3RWPRITyVABB8pvwtATjVdrpyqJfaVcC/gX8x8B3Zf6K4Xf7MttnYrh9xjCFKaD5CAKRxVmLbRbFlpuB+IPi2vS4b9XsC0BKIq7Ivh38D/6LgM8q7XWyf4X/SNO13yl+tmLci5nsFIGHMkgCn5bEs7PtTov7c97S5DKCXJXj7lOtd1L4U/g38DwHfbXL7/sE+p/6tyP+oNvwtEN8rAJURUl77UNSEn7VtH/taArAEwsxAWFrfK7avxr4M/g38i4PvtvyjY/tcKvhbkf9RU/Az5a8geY8A8HwV3BYptqMq+b009q2EviUC/i6tF8Hpu7l9tSLwJfBv4H8Y+H9H+UuDGJya9lcpfy86vkcA0gi/wZam0b2Cnm30PL8qNn5u3V8P9n1nO4Ugz3GQjjwcDode336IfTr8G/gXBT/b7TazZptzwc/ws62thb4EhrXtkgIgTQHPfa1sQDre16sGyLKtrXtqWUsA5kqKBc2f5679Ifap8G/gfwr4bO9f+L5qs+1U+Ht2CQFImN0OR/I8JqcmtEocEvo8pzQ+xtYD/y4K9+exFKXjib8g+n8a/Bv4Hwr+knZzvv+sadp/ryn4LajmnPQSAmCrhMAA+nsel1aB3hOE6jy7KD3o6ceVGPj37nvb4bMF4FPg38D/MvDddv+GK/0J/7PqqH+uvVcAKiilfqrP46QhulZAV+forR9IY/ATdI876xSAO/WzqU8VgA+H/4bAryKnNI4GHni3odX2VrsfNTgMHffUdme63xItw2/haoFPAE9xzPcKAC1FwLAyIleA5+dWWQK+NI76hP4htrnPWZX72JHf15WGsfg0AfhQ+G8cfA/Okoi/FHy32/110BCR59r9f/icbecqf67iG5wqzU8QznHISwlA1Sbu831UNgd9VarrUCgt0oTevkv4HzW/jmIR+1QB+DD4Vw4+oybBZ7osDffBwW9F/Kxb4Fft5gs8p4DviJ9Rnw44B/8l7D0C0GtL9d3cdGUO+koEbPZdjnv6rv2QfZ7vTVT9vMf+TxGAD4H/BsD/W/OLZBnx3Ya5iP+HLgs+P1fZSjphy6npjLZLOt57BEAatyUjZ3Wsxyn3GfxKAF6i9ATAUZ+ib+hb8Cf0evvObflUAbg4/N8E/Crit1J91j3wCeAp4FfRngt8Ffhz8C9xthznJXaOACxpizQ+7lV1u1IAXlC7PMdnQmvRb2V8v2kYt1bEpwCkEHyqAFwU/hsAf8kcnwPPVC/bXqX5f+h4n1zcc/9IQ7ufNX1ltwd+b32i53xphIbw2OkrOwV+6XQBaAlSD4adjvfibWkKvsvzTHH/2b968D+h9NJ9qd3+TxOAi8F/I+D/o/Ycvxp0pvqO6lW093cJ/lyqn6/stsBnxK/Ar2B3tLd529C84Li0nhgssVMFoGfVfb1ofH9sK+FntH8qSpWyZ/R/wDkq0UjhtfVA/hQBuAj8Nww+AciIvxR8t7kC3w7ai/icz/dSfc/vW3PMncbO621aRklHRn/XEoJz7JIC0DKK3k7j+yL4Cf8vTQUgs7SE/1HTvs8xYNSfg5gCYLuoALwb/hsGn53OgXbE9xx/CfhucwX+QdOI7zZlxO8t7DHaM70ktO6rQ9Q0igNBaQnFe+ySAsD2+B7YD7a8L4L/qyh8Vr9H2ek4jq9vx7xGqcDv9Xua2y+Nj7+YALwL/hsHn1GS7Sf4bnsLfB/ne25F/CrVr9ru/RnxuTaR4Hs8Rg5UbKczMQugADzF9++1SwqAzW1LEZem4Ffw24e8IGsBYFspAPeawl+BvwR++5yzTqm4B7P3HhE4G/5vAn4r4rPtCb73E3w7z52OZge04xH8jPis5+b3dhK3Xeo7Wjpk5ZwUAU6DcvzfY5cWALev6hffZwoAwX/UsY8Jvn2Oqb/7+T7O2wNfsW3boVTmc4/2vScLOAv+bwg+I36r7Ql+ttngS8N8k+1ugc+S4NtxE3o6UGtbqh3UTkYhSGi8DiBdBn7pcgJQAeT2c5vwU4AfiuLxI/zOBKSpAKSgJvzSFPaqzS4UsMk1zs0CToa/Ab7LdwD/D43bzvYSfEZ8OnU6XC7uVfAzzf+pcbTngPM6u04RfkfY59JWHu9pwKXgly4nADa3jwJp8z3mtIvQ24cJP++Zi4B3GveR66VAtsaJ30tD/6ednAWcDP+bJfgetATf8Nwa+Iz2PfDpMAcN4DvFnAM/28yIn+ms1xJ8DxwXOi2dys5J4F8adSUAzAAuZZcWAOl4zzn/5z086eivHpMK+lYfpgD43EusBXy131Ou16ilwXdPEoCT4EfUl5aBbxDWDr5TerYzF/fcZoLvPkrws9098KuFPUd8OgwdkOPRE4AKfLczt6tsgBnAJe0SAtACiNC4/RaAnxp8+C4KBZV9KE0XA2kJYtWuLDlWeQ/e9phIZwjAYvgj3WcjK/gz4rci59rAz4jv77i4x8UhqQbf0X4J+Lmw5zZLY4eko95HTeel4xJ+wm4YnqNQBGytDIBwnGPnCsAuau43PJnB2Kdaopng09ifc23msRXY1fW4/aThHmhnCcAi+E8A33OlfCTGxbC1g8+I30v1pXFkaa3ot8Bnml9FfDtFiu59sZ0CYKPzJ/hVoQj4tzyH9H7oaXMw9YztSMgoAByjBJ7wtUpaq8299rSusS+2LQBpEwEojhnZLPwN8NkgOx4jfj4LT4CuHfzfVbc729yL+EvA/0vLwGe6yr6/17Tvc6U6BYCOQ3AT/F9R7jRERwvATkO7mA2kVc661E4VgArKyoeZQTl7cb/OQc9zVtZrc4ujvHZLgIQ6jQIw++8CzsL/ZrwYG5lRp7VIlgDdAvitOb40QJXgt1bzXapHeWyzNBVcX9/t59MVCgDh32k6b0/wf6KuFr/cn4pzSe+DvbJTBSCtgsbZTvaDI+up4Oe+bHPrfBXoc9OOXv9ShLvpfxf+xgIfoUknNMBM93Oef6vgV202PDm/d1v9Oef4hjAjPsFP4PluAUWgBb/ezp3wO9JbsPL3dEZHS7bTome7lBC8VwBoOx3vVRp8wmszPfBZllhG7Qr8SgBa8M9d176d+0pbEvmzsWygHZHRhxG/gsjlu4OfqX7+cU4FPoU2p1cuKQCO/j34mfK73a3MISMR7aAxSLQ5x11ilxCAbIczAGkYP9pcu3dR53eEvMXSUvil6XUOUaRxpthM/5vwR9SXxo2u0v3WPP+Pol47+D80gEiQeuBnu3sR31BKY/DZZk6t2OcUgISfjkRIM/K7z5fCb99gXzP9nAPoFLuEANA4fjl1oVDy2J4R0p0GwU0BYB+2gOdvaJyqMHNxyd+cnvZr3Fg21I2tHNJO2Er3Df8S8BOiawG/muMvaXPO9VsR34Nl55lbTHV/e1/Cb6FKZ2ql/b01g3ROaewjLQG4pF1KANgXfIyaU5e0lgikUFT8VBy1Pvt8CTLBZ5v3+H6nTsovNeA/4WUeO0fllH8UJSPTHPh8Hn5t4GebubjXarO3q4ifqX4lsEzv2afMprJ/E950JLbf91D9roK/cnAbI9IcTOfYpQSAxinAAdtzlve/FPwe9OxfaQr9C7Zf337vbV73IKlM/XuRv7oZN86RpJWGVvDTOZfMl6tXX68V/CXTkxb4rYjvfvaUym3OhdQUVsPv6M2onw7lPrLTP+F4lwr8yrnTX5ZkABSLc+w9AtC79intrjipSoJdlR1qg5tRntM01i57jdvttk2ygLm0X5o2lpGfi08JPgWAEcnHEyJGB4NE8BN6zpfXAH62eQ58tzfBZ5/mNGou6ie0vh6dqoJ+r/r3c0JgmwPpvfYeAehZr90JO7ezf3qQV/sJK9tAgXZ5Rl2NEcd5YhP4Zx7vJfy9yE+HdGHEv3u7xhz4c4/H1gL+32o/x0/wGfEzzU/wM+pnxM+ILdSHt/2vOIalSkXnoOc1PCa+x1NS6VPsvQJAn6e1BCD70ds9+KvsqYJemqb3rZevqjFuCkCm/q3I3xpUn5xpfwoARaCVht5paFQL/Iz6FIJbBz/XUBL6KupnxKezJZg2f56DueVYc7/jNTw2KQDZpnPtvQLQshQAtpf3yP6YA9+BL/vQVoHP9y+4kFuJSjXWE1ua9vOGqsjvYof9ESUbS4fwDfrGMt2voM8/ePku4Cf8bG+KK/tZGsDrmdvBz1VpRZiEnp+5mk7Rn3XSE+wzBMALl9W9Ev67KLnP7dxhm+JoPyNfnMrNRX2aP4/GfwR/J+XnzRH+KvVvgV+BxKhfwc/Uvzdf/i7gZ7qf0yk6gzQGPmvbLmppClAL8rmSximAI+kangJIfQHYR0nwyQzhr9h6wbHMru+jUFB6Yty0JZFfmt5gJQCZBRB6grTTWOGqqM+0n5/zmTijyXcBP9vby6oOKPxMo9N4m9/dxz7h+FPL09vvUwAubZ8hAPaHvMce/AQ1YbWRtbvYzt+3gM/zlRlfBT9/XN2YC1XoIUqmKGy41J7TGKx/o3h/vgyT4Ls93wX8HxrumcLqfnEfvWrcX+4zqXY0ioDbaev5R8sBK2NbrnERsGWvqFMAKvDvo04WpKG/U7BtCfOpfV1aL/L3BOAONW/OQpDpiY+3c1I9qwWNqnh+T/Cl7w1+ZlSE3n3L2kJg+CuHZUrJyGIBWAr+nJPmGsAaBGCncbtTACikLRHgeOntPN5HkeZYUQxa2yfbKWl/CgBvNGFvgd+DPx9juPCZJqOXNHZepvp85PgdwJfqbIp11X8ew8zicvxSAIR9hLoHO837LVJrEgBp3I/Z56eC//r2Pfskx8zXy+seiiLUuT2yOfh7Cr5vlLtiHx2B8PuG3Hmtkk4rnLcC6Xe9H6Q1gu/+smimgFbwU6w9XeO0jZHJv7GzzkFffU/LNYBrFoDqHjKQPWnoU4JfCal///L2PccyA2EGwMzkqgxh1ubgT6sEIMUg91eDTvhZXqLwxmwGSBoc0QuM3xV8Rp5q+sQpU9wtFXkAACAASURBVAU/12p+vB33Q0P/5/TCTmzrAb/U1iAAaYSOfpORPwOhf+txM/w7DSLeGkOKQE8AMhOYiMIp8FeD63puu2WZBVQNT7Gx2QkfNIDBVJ8wnQrS2sC3U9BZuGDKhVL3tfvTfeh2PenYLjrXQcN0gAJv/6nGPcd+zhek9QkAxbTKANy/CX4e+4Dvvd/wV4vePRFYHP1Pgf8Q9SUtncXORQd1W339XrpfwX8KSGsDn1HCj0TzvQg7TMLvtPSHxhkChYKC7BSWv8+xqz5X8Ff71iQAO00FwNE8wU/4fZynWAm//dBjmX6WAtDLACRp8j/6nAJ/Wl6gVVrGaE7Iq/nS69tvPGiM+k73W2/DfRfw7SDVuxGO/tk+w/ug8fwyHaoaTwpARn3bkmhf2TULQOXT7it/b7B/adpHh7djPU171CCo+Xv6YuVvVfRfwp6kZfDPwU0HaSlPNoTgJ/R2RoNN8Ll9p/Fc35ATepfftAykNYLvdthBWBj57SxuIwWX8PdSyRxHt2unOgN4j12zANB2GiAkE8+aZj3+juLg6RTh9++Z+mcm14v+c/xJmoc/f5TAs9BhXuLzq8YdnZHeEd4wc3FKb8f4s3/bSvlZftf0FdhbBD+hd1vTWegkFfxM+StnatmlQEq7FgFogXSImtHXAPNYgu+o7zE2/D6Pj8m5fyvyJ49zYzYLvzS98Qp4l2eNncflToMAMOoz0ht6Lza54XcaK6t/y0UqPtdnmm+Qbg38bC+BZ1tPgX8O/F4k4Zhe2r5aAFoAVSLgbXNCq+B31PdCqs3cOLJTAFgvTvtP+gc8Nb2hVJYE/qlRP2ucHko1/D80djpCnulqBReBN2zfCXy29S98XzmK28sxaEWRFvCKfU77c5wvYV8lABXUc2LoNnrMyA6ZedKQ7nOshWN9HAWAdU+wpfa4SarhpyPnDfbAr4q/5wpxRv4H1Q33984I/F0FGAUg5/engLQ28Kv2ZsrvxT6PB/uYbX5BaUV9xTbN5/L45hrAe+0zBaACOu8/uSAf3M/+bIHvfrNVQkERSMZaYyZJZdSXTk/78yYSfi8a/dQRgF8apzVMDRMWNtjfMR19xXfOCJj6GzZ/5kLKuSCtFXzD799wsa+Cv0od6Ui0hCIts7xL2qUFINtoASAXCX2C3itkZ6chC37SOOJnf5GzKsg+R+G4ZWnaCP7D4XDA3/TnSXoNMvAuXrhLABkRDPG9xg3l/kdch/C74wx/Fl+Xan4qSGsC///w+R+UTA8r+J1hVY6rqGcdCtZKpd9r7xWAuSlJJQBSLQJkotreYdu/f9YYeNdsj8/vc3nsnmObgp3XOZ7oHf9XH284b5bgM+L/qz78TA/TOeiMnOv7Bn2M4fd6AUtmGtJpIK0R/Iz4TPfdbrY94bfTMHqo2E6bE4JrFQBaJQIpABS+FEmPVQXnLn7D/tpFofFaLxpfgyXbcYjStRb8dA43wk7CG2W6/1PjVNwQttKbSgAMvuHvRSsPDguvZbNyPmsepFsC/6em88I5+Hugn2vXLACELmHMQFTBT+Cz3GsqAPQfWyU+PaGhIFSi/d849qK+VMDfSf3zhj134byekdhgGMhK4SgAhPpOU3Wjw3JgCLzPJw1tzqnJLYH/t+bBz+wpHSKhr75/r12DALRAz88ud6htFQcuT0Wp/NHF+ypLkBN0fq7KLPjSfNov9QXgWVP4qyhcda6tEoA71Tcp/J6ZBK8h/I7gG4xbAJ+fCT7n+Bnx01mk471UgrAr9r3XvloAEnRu05d6AkAOyACB91Qrp7v52xwLnz9r/q7azmMXgS/14c9Gvep4Ey863pAd9U7HmzXsvOks7HienwKgt7q6OWk6cBQSdk4FPiPlmsF3yWjfW+DrOZs0RCOpDcp77SsEINtPv8mMs/LXKnpbUBP4n5qudaUA9CK3UHu7VVRsLwZfasAfqb80vihVb6/jTbuzMgWvoOc5WSuOTyOA1XccfGckmeozWq4ZfEf7KuIT+syapMEJvU1jH6a4XgJ+6fMFoIKe8KevVp+53+P/ouN4Ev4fGq99EXyaOVoizC3QJ/ycAr40n/ZnYzL6WwCe1FbOdCKeL2unVy3BSEvo58BP+NcKfivVr8CXxgAcNO7X3HZ/um2VCCwZm559hgCwb6U29CzVtNU+aR/wuavHzNVTpwyC9NlX1MJ+W08M/ts+FXpbE/5O9KcAtJQ091fnqTrAq6rZYZX5N1UaZpA8x890PxfKmDLfAvgJfW6nVWBzfJ7ju0vYEgGorpXtyGMYTSlgGcET9uqR8cPbOeiL/B2fblW/7/lx+j/BbwnA5PO54EvzkV8aX8wK1YK/B71Ug0+AX9VWzDwPf8eFlwS/ivqZNvN5uNvh9l8j+Ibe4Fv8OFZzoLa+S6gSokuKwBIBmDO2d6fjGFYC4CyV8BNWR+9f2LYv8Dr+PQUgtzOjyICYoGehTeB+D/C0LvyI/qlE2SFz0Pt3LehZqpSJ56wGNR/ncWX/76IYNIOXkVO6PvAZ7TNLyWhPoBLmOWh5HlolACrqU+0UAaj8rPJBCoDeavsXV+d/ReFTEmaA2bc5ZbgvSgV99lUlANz/n10KeNps5G+k/wbk5a2ec4AW+E4pOSjVamkFfzWYVbpf1RaHKmWWxup+LeBnqp/gVzD2Cm3OCdn3FAB16lOsEoDqfL1z5/2lKErtbJHb9iv6aPZDNYVglM/S63vbqM8/Ava0Wfhh2RiKwJy14M9B8Oopo3+m/jzHXNSvCqMnwff9XTP4rfk9ndIOxn6rnJDGsam2eQ1p6PtKBM61uQwgrzVXqkxOqn2RwHP7EMXnyT7ulVafZ3u8/SngSwvhb6T/54CfHZ7w+q/y+KiE6ZPPx0FiCkf4KQDeZspcgb/TdYLPOX7l1OmQd1H3nLEnzIx8FAEf+6zB3gu/NLTrrthffe6BmOPLY6p775XKst+romK7a58FvrQQfumiAkBwDb/B52LLKZGfj/ZSAKqXYPw7n4dO54WbR10P+K0sxQ5N6DkPrVLSFvwZBTMS+roEwt/zfOfaQcc2twRAGre9Vewvdxr6LMWdopj9ca5VgLe2pbaofJothl9Szv/pAC3jMQl+wv9LR3g458+FE5+zlTkYbsNevfnmeZ3bJE3Bv4aIX01Pss0Jfa46sw9d/BtbJcrsW5dKBITfpZ0Kk8/H8a4EwNaCnvDf69h37jeOc7U418qQ5sShyhbYRzzmauwk+GHuyKUCkNGF8BJcvizBgWHn+1wJv8/DDMDb/j7nytIw2Jnq/66vBd/wt+b46eT5nJnPmyshTTFNQWW/PuEzswKOqzQG5FT40yoBqIBM8CmEfBTqDG+vIbNr9VVmBrTKl6vPPRGotj/dToY/0v85AUjwXVdRuwJ/Lu2ngKQA/Ir96bR2BMJTwf9V4LfWJejoCb378Ac+p1NXYsqoX/Wn63tN+5Nj7AxgLlIutRSAFJdKAAj/g8ZZkzQW+t80zjbnfM9Gn2ZG9Noo9H+h/lI7GX7pZAGQpg6S4Br+VqTqwZ8OW0UsDow0dpg7jef4jPiE/6vAp9O0or0d+AcKRcD9ugT+Skh/vp3jJ86RfevzvMcqKJZkAFXkd3t/abg3n+9O0wVd9hv7a8737H+ZNaUgHIoi1ff8KXYW/NIiAahuNjvPHfWgIbJklHLncwAq1c05ag6A20eHYcS3E2TEpwh8BfgULLfZ7baz2nnTkZkBVH1a9WUPfF/7p8bjwj62wJ8a+QkBwXAErjKAuaj/qGGB1+3zedx/HvPMAnrwV/72VOxrZQWVAHy6CJwNvzQrABz8Fvz3GjrOnZ1O2oPf53nVtLNd2LE+V0ZPg9KL+p8Nvu9LGhyWWUpC/zvqH6qd2X3bivx2WoKfKXGOy17TLMUCsNQq8LmdAtCCn5mcA0oF/15DP1YC0II/++mpUVIAKvC/3N4FvzQRgLQqC3An3GnoyAr6jPoJfyUArP29bY+6Bb4LBcD7GFF74Bua94JvR2XET/DttFkyja0iv439SIf2ffyreupQiTPbzr7vGWFobUtjf3B/VJHf/WT4qz7177lG4v7M/krw7bPMjrJUAsBAlEWSPvUZv3QB+KWh0Q0RcBbgG91rDK0H9Vlj6DnQzCKEc72i7qmrnZ1Rn8rfA9+1HYMRQZqCz5eL3gO+VEd8g51tds3oX0V+wipN4adjV1Gf4Kcw23oC0AO8t+02ZAZQRX4LACOx/UIa4LcA/ECd4m7fzXFmdpSPlHM8rzL6XwR+W5EFWGlbkXunY8dkpM+I0oI/RYBFOFfClFG/WuhzqdLnHviG339E9F7wGfHZ1qq9mfo7ilVpbI5HRjWCn9OG1vjwMx2e1gKcn3MseZzFx2ObAsA+Y/RN+DNLeETNqM/+oTAa9nynhMJwcvT/TLso/JJ66wDSOAt41ZAF7DWIAAezAt9WdR6vRfC9nVGU6V5G0SrV74FvB2hB/17wq3ZWi5EZ9auUvxJT38+zhqhJ4Pn7HvxplQC0rII/9z+8baeQJfyPGoPH87l/faz7OftJGoOf0Oc7GYz+zDpakf9LoLddHH5pIgD/7dbYWSgQr8V36VhplWPYKB78bPB7kXQJ+IbFkYBzfEf9Cnq+uUfwDQcdeUl2wqcR2e4W+C347eT3OrYpoU/wE/6eCFAAEuoK+LlCY1sIP9PtCn4Gg7xHtz/Bp8gzy6vGtpX6l9B/9nxf+iD4Jc2tA1AIhO1qHy3PVXWYB5YDTaAcFTLyVyXB9/UTfEZ9Ap+lBT7b2ov4LfCZ8nOK4rlrRsnsV+nYhr2GDIyRdAn0PGd1fkY9W24vLflbRmq38V7TaGujT1T3JY0zod60juAzA+CiH8FvTVE/3T4MfltHBCwA0hR61rlty07jOXKgqfIZ+SsByDn+ncbgZxqYUd8iQAdJ8OkQ0jhqsX25sPdn1JnuJ/hVtGff2/zZ90o4dhrDkcC3zpXm+32J/XOwu/RSZrfbbaZI5G+q+6Dv+FoUeMNdiXorq/M4V5H/v/Z/RdSXPgF+28wjwRQCW8uJKqPDch/B5/yO0d8CUC2SOeqnU+T8r3KIFviM+G43wW+l+i3wLVqtxb0KFlvV7+7DBHwp/D1LAWDblgBfFa4DVH4gTe+/arPP57Ucgk9hr8aakT8X/Djn743Fp9qnwS+NFW5GCKTBaees5Xh00IyqFfyPGoPfSvfpGPlor3IIpoEZ8au2vTfVZwpcgSLUCXZGd+l4Ph6T2/68xLItXOTk99WxS0TAVt1Hy7I91eJejm1mddVY98Cv2vzp9qnw02I6IF2mI3Kg7dAZ/SkAKQaZMrN9jvp0jlz4+TdKD/ydhjZRjJjKZ7RvLe4x2ktjYF5jm33NPqrmwHrbpq+cAz6tSoETiNZ3eS95Ho+dNM5gWOc1LOoZ8TmlM/R8d6M1res95x/d51el/NIXwm/Lmz9RDByRK+gZzXJFlwLAKO+Si2R0NjtI61lvpn/5uMftzYjPVN+A98BnxpLrEgSDK94v+M5G2N0e3r/7IAVAOh38HFP263NxbEJfiUBVHt5qjiGzgDxPC3ym+/nCVivlz0d8VzfXt305/GlLOiQEouWAuygZ/VslHd/tsZM48jP1txD8ikIH8HlOBd/wewrA+X0PfLaV7XBb3HfOPtgvFMOcR9tnTgVfGvqAY1xFxAr6jPYpboT4RbWIJ/z8TfX0JtdyMuoTfAt9jnvr3r7crg7+JYbFQ2kMFS3Bz/Q/MwF+9rE+Twuqp0apBl8anHAp+FzYS/CrJxEHtdtXPXZi3zAbekRtEcgM4D3w5z7273Psq8Qgwc97bi18tuD376qXeFqLfNW6Ti7yUZhG0C8Jch9tq4RfmggAjYNcRf8sd1HbUfz7KuIwwlTRNQfc1yY8BL83x2+Bb6Ey+G5XZiQ5B33WFP47jcF/fqtf3+qDBgGgMF7CDEEFSQ/8jNouXjch/B5bWi/qe67PyD/3bL8l/NU9XYWtFv4F1hKAU0qaHbByRH7nATZc3t5pAD8X96pVfYJvocjFyIMGZ8tpCEvrMSPFyG2iSPCe7C+XFgAb+zEzgJYIZ5bzQ8d75cLtHPw512fk5yPbalHX4FeP9lK8DpKuIupLtwE/Qa2A7VkP9DQO2GgwYT4XI+S9xqARst7jvASfqayvY/Azba2eOHAdogW/waFIpBPbPkIAEvQe/Bn1ff/5ohMzFo8zz0PhqPrvn6jZpz3wy767FvCl24DfnbnDNvf3rAVxZRSYLDmN8JxeOjqA8F2m+6eAzwhG8A12Om2VmtpBDT/XH3Khsge/dDkByPP7mm5nigCnXQSXT0By0bKCvxKPXPBj/+X8frXgS7cBf1p2cDrPKaUC3bDnYuE9yoOOdqejEzDCMuoT9FPAp+PSYXtpagt+tssOzTlr9knaJQSgN2Zua6b7OV/ndOcBpVrL4fkoIE79mQFU0yf3E9dRWuAfL3hl4Eu3Cb/Uhvm1KC9Re+DSEnoC/6hxqqy376sIm4t8jPw98NNxCX7OT1ky9e+l/Uz55/qDdq4AHIqa0Lj/KERV5P+hQQAIPqN+D/5M/Skk3Pb3hH4O/Lm++zK7Zfhd96IGowc/+9hM9Q0/F8gq8PcaQ8aU/1HDXN+wE3q/wFO9tZfOSvCr59EJP9tZwc9+SPATzHTqcwVAqs/pa71ofO0cRwPYAn8p/Jn+UwiY3me0nwX/GqO+tHL4i+f92fEt8J87xcc42vbAf9IRWDuoNMBEMdjhd9XjPb6158d5dl7en9ueEZ/Q558TG35GrITfwsQ+OBRlzk4VgOoaHLespfH4UAjdv5znE36Po+IalU9wGpDA00dWC760cvg7tgT8J00HOQfXcEhD6s4IbpAqyO0Y0ji6OvLzj3fyrb1M9RmhDH7vnfO/8F2m/C34M5IRPNqcMy8VgB74vDaBYrssAM8aRPmXavAZ9W30j7mg8KJx/7T6aTXgS7cHf+U8PfB/FSXnihn973FOwsEI6mfl/I7wO8KzNvi8tu9lCfi9qM+U3/Db3O50aDrzqTYnAIRkDnwCyn162+drPWu4F0KfYyhNr0UfYWkBz5J9tQrwpduB3x29U3tgqeQV+D81fS2U0Gfq7zffpLEwWFjsrJlaG/IfUed1pRp8pvq9fyeQq/zVQl62revMsa+yjKw9AWhB3wKSYst2SuMxSuATflpei9ecu3b206hv1gC+dAPwd+b9ObCGiODzEQ4jb0b+ew0OdKexEfxq3pyiQQHwtq/J9NRtdnsNfs7vq6if75xTkBhBeU908Mqpz3HolgDwnK+oCV0Wt9G/829smaGxdp/ST1xXAlCVbGueYzXQ21YPf8MqZzKUTu0N/L8aQMwFIzqPI3I6c4Ldiq4WCC5MVVMMtz3Bz0d5c+B7ka+K+oRmj33nwE6wMrpKUwFIYUkAW/CnAGSbsx0s0rRtlQCxHRXsWf47z9rAl24T/sqpGPW9MMRU/18NYLbm+1ItAATbC32t6OpyH599jH/TAj8X9Vj4Ug9fSMm5PqOW3j5bAJrOXVjCxM8JnwWyArASaY6Xy72GeT0jeIqZ8H0KEn9nq6BeAvx/51gj+NKNwP+W+o92qe1YdqafGgOf4HO+SKMA0LFfNMBPZyEEPjevkY7sdvbA/z9N5/j5WM/gM92v4KfoKL6rPks1VAlcVSySOT45Rgm+91GUhXMknLYKfFvebwvwqm8O0nqht90E/IXNwf9LQwQm9Cx0sp4A7HW81p2mzmij87O2sZ0WpqXgt1J9PtpL8Fvw90SgZRXgmTVZ+CgA0niMEvoUMI8To38lAtK47Tl2NB6X0Oe+0fFrB1+6Pfg9IDsdncG1nctRl/C3oM+SxnTdTt0CJ0UkgXP7OB1J8Al9gm/458BPQHao54z9UPUPha1VeJw0bhfh/6XjI1Dfi9djfsW5bClep4LZEoHJ97cAve1m4C9W/aVBAF7eagvAnINWwFcRQfG7llWO6m1H+yriL1nVnwPf8FdwEPwqK2hZigD7L6dNLWG90xFoTnUM/g+NH732FmJplxKAyedbgt52M/DDUrEpAHuNBSAdNeFPaF27+E08Fb+tLKHP+X0+zluyuLcU/FcdLcHn5z2OoyXsFfQJerWW4r7aYdvw8xEoH4USfopICkCCfyqsk+NvEXjaTcEf0V8anCAzgMqJW9Am9K9RewGLEWk/Pc3o97mwVUV8g58C0FrcmwOf92FL535VWwCkqQgQ+oT9oSivGk+X3G95/Bz4PZEl+CfDe+vA024KfhgH0I7cgz+dqRIQQ+RiuLzCn08KWiJkMLmw1fojnapesrjXBZ8OXvyPyhSAhL2CnuATXr44xbUHX9vnSeHgOxAJfmZqcyJw3PhGQJ9iNwc/on9GOIqA6wp8HiON4bcDv6Dw1dyck+Y5MtV31Df4hJ/An7Kqvxh8fy4EwMe4zyiaFfhejDPwXKWv2iWc605jEWCdpQd+JQAb+B27OfgltdJ/OrI04zQaQ59R+xnlSdN386v5KNN9Rn3DXMFfLexV7+ufBb6tIQBSnTVlHzx1SraNwuLzUlBSDPZF6UX7kW3g9+0m4Yfl4FME5ozQZ7QnxNW/GdeCP4HhIp/Br2qD33pz72zwbY2MiZmAS/YD+yO3OU3K8/AaFeQ96FO4Z4Vgs6ndLPwNZz4V/Ap+w8tn0RX8+zhfC/5/NY3+uZr/oeB3jJlAr7xG4f4W9NIY2oS7KurUm51oNwu/9C4BqJw64bcA+M9yK/gJzlzkN/ysE3rOpRl1PwL8zW7cbhp+aSQA0gBGTwBaUc2gEXz+IxxL4M/MIZ/v/xvb/p7Qfyb4jK690krRW+m6je2rMooqa2jVm51oNw8/7KABRmm5APTg5+Msr3pzoYrnS/grAWDhan61gFal2b7WyeBDIKs0m6Vahc8V+nzs2RMB9nGvLBGCzU6wbwH/gtXs0eFR5lL+6mWU1oKfz1EJQFUS+s8A33VG9Ap2Po+vCh9/3kkl+An/S9Q9EaCddM+bfRP4pZMFQGo7ZoJrAOjkPfgrAeD5+Dmh/0zwCX8L+HwdN6dBrX6Rxn3CvmWdZS4T2OwE+zbwS4sEIB2K8FfgMtXPVLeCvzoPRYCfe9B/JPh7tcFP6P3PkPtfH+Y/PV7924Qt+LMvqj7p9YPtpPv/7vat4JdmBWBuLvqiY589a3DojGxz8FMA0vFfNHV2OvwB9UeD34I+gf+9sZ3wcx2EwprZVC8Dqvojo/+WCSy0bwe/NBGAtMqp7Kh3Gpw158CtxS3FOSoBmEtxK2f3eT8afMPPaE/Y+V+O+XMFv+f80rHdCf05ax7ZH5udYN8SfmkAphCBg6bQHnSEguAakr3a4FfnqbIJ1tw+oM5yPPHHg9+C/o+i8H8fcvrPJyDS0P4XTcH3Y8586nFq9D+5X76jfVv4bZ0s4FXDtCDBNegvGs+PK/Cl4dwtAXgt9vPY/P1Zzv0O8Al/Rvs/NRUA/u9DGfUtbo76rfccqsedGfmzbzY7wb49/FJXAA4aC8BOgyjso/a28JnncZ1iUtVV+e88XwB+Qv8nSv734k75+dYjo/6rjhBXLzhlyRecMvI3xXGzedvgf7PONMD2+lYb7FdsVyWtJQC9oti+BPjePhd8RnwKQMJP8Bn1HfGZ5rf+noECsHjef04ffUfb4A9rZAEHDeBwW9jOyOrtPE/WLdBH9bkOXYDvwnWK1qO8VpqfhfP9CvyDhnSf0Cf41V8x9tL+CfibLbcN/sIWLAa6VqeurAS7qEfb54AP6KU++J6TV4/zWuD/T+2IXy3yGfyM+NW/W8B/oizT/uppyCHKZgttg79jM1MBCoE0hZ6fq9/ObZ8FvTSJ9q4T/NbjvNaqfgX+n5pf3Sf4nuPnv1lQ/cMlueKfKX8J/Ll99h1tg3+BLVgPEPYbuOq42X3vcd5GtPf2XssjPsEn6C3wl0R8gu+S/0Bp/lNlP3XGY77NltkG/wlGMGeEoGXl8e8B3jYT7S8R8XN1n+B7nr8U/PyHSav/fWjpfP8/u0Q/fifb4D/T0tEA3yIHvJSjLoj2PfAdrTnH/02nLe6dCv7cf0JSzfdb7/ZvUf8dtsF/IbsUzKfYzKJepvoEv1rVd8mXdy4NPqP9UvCr5/sj8L+i/9duG/wrtYWLevkor0r1qzf3Enw+37dInDrHr8Cv5vpzL/bwpR5JG/jn2gb/uq0H/p2m4PcW91wIPmtPCd4Lfv6Ho/liTyvqVxF/g/4dtsG/Quuk+5niZ5o/Bz5hz4W9c1f158Bf8ly/Oc/fov75tsG/Mlvwxl61ms/5PVf1e3+hx/n90hd4loJfvdBTrexv4H+gbfCv05aAz2hvcA1xFfETes7v/Xu/G1CB7zf3loCfC3z+7ZJn+pI28C9hG/wrskj3pXnwmeJX0Cf4v6PmX+f1/jTX4PM9/SXgG/6M+HPgb9BfyDb412e9R3pLVvMJeJXiZ5qf4BvEBN/wG/BTwM/Xd1uv8G7p/gVtg38l1oj6VeRnqu8Xd1rpfUZ7L+pxft8Cn3+Tzz/SMeyXAJ+P9TbwL2wb/OuyXtRnyp8v7+RKfj67d7TP+f3927lbEb96ZTfLBv6V2gb/Oo3g71S/tsvIb+Dz/fxqRd+Zgxf2DL7n3/znt6pUP6HfwL9S2+BfgRXP9V235vuO/oz8TPv/p2nUz4jv88+B7wW+VtTfwL9S2+Bfj6UAVPP9TP35TL815++9tUfwl6zsb6n+imyDf13Wm/NXq/0UgN81zgJynu/FPYJvIFsr+wl+pvr5p7kb+FdkG/zrtRSA6s0+CgCzAD77d8R/0HRxr3qJpzXPr8BP+Dfwr8j22myNxnn/Pkr1F3wUAhZ/54hv+KUB/N4CX1WY6i/509wN/C+yDf4rt5l/rKO1+FeJQBZnCgl9K+LzsV5LAKpUv/WnuRv4X2xb2r9O663+WwQqQchCAWGq72ifqX4L/Ezz5/4mfwP/CmyD/zasJwaVHTSG/UXDvizVygAAAwNJREFUXN8R3xBzgY/g/xOlAp/Qb+BfmW3wr9MSFH4mVAk5i4E09P5NvrbLV3d7ET/n+Jzn+5ob+FdkG/zrt4ScoL9EMYxPGt7Xf9Ww9uOUn8/y/9Y44mfUr+b4XNzbwL9S2+C/cjsc/vvvw6QBbm63gH9G/YTyU+M/1HnWEX6fg8/zDXvrxZ0q4ler+hQAud7A/1rb4F+npQBU4HPRzmm8H+k54j9reJX3gN/mX+rNrepnml9Bv4F/ZbbBv14j+K4r8H9q/CyfoPt1XmEf4edbfEtW9quIv4F/pbbBvy6rUn1Dy6hP8P1cn6/tekXfz/tbK/35Jl8Ffi/Vz/Zu4F+RbfCvxw6aPpPPlN8Q/tL4ZR6C73m9/4IvswEKR/Vsnyl/6zl+Bf8G/pXZBv8KrFj0M6wV/F7J/6n6zb1nDX/Ik3+zb/gd/fMZ/9wC3xbxV2Qb/Os0gr/TEby9hpX76u09g+/IXmUFjt5cJKQItBb5WnP844k38K/SNvjXZYQoU/8XTV/tbcGff8xTHcP0nwt7c2/wTdL9za7TNvhXYpH6S3X03+kIpTQFn/N5LgLynLloyEeF+Yc6GfUz1d/S/Su3Df71GWF6fat77/BzPYD/Gi/n+57z+9gUgAR+yWO9Dfwrtw3+FVmx8OfaIjD5iaaLgZzrVwuCKQCt0gN/g34FtsG/TjtoCm11TMLPf+uv9ye9FAD/9qUo2+r+im2Df2WG6J9wpQAk/Iz2vb/nrwTgtag38FduG/wrtAUCkCDvdYQ2oW/BTwGoygH1f9BLG/hrsg3+ldqMAORLQAa+Bb4tBYAiUAFP8DfoV2a7TajXbVgAJMwJeIKfx9sSZgpAC/gt6q/UNvhvwEIAss4on9u0Cuoe8Adpg36ttsF/I9b5L716NbfpCJnKt+oN/BXbBv+NWUMEetuVVUIw2t6gX79t8N+ohQhINfCttL+5b4P+dmyD/xtYIQT/fRWfS2fYgL9N2+D/ZtYRgpFtwN++bfBvttk3tb0222yzb2kb/Jtt9k3t/wHapaQspPP8TQAAAABJRU5ErkJggg=="/><path class="cls-8" d="M551.72,221.24h-.16l-18.34-18.33a3.58,3.58,0,0,1-1-2.5v-42.3A16.24,16.24,0,0,0,516,141.89H504.51a16.25,16.25,0,0,0-16.24,16.22v.24l-27.06-26.21a16.32,16.32,0,0,0-22.78.19L346.76,224a16.21,16.21,0,0,0,0,22.93l0,0,8.73,8.68a16.25,16.25,0,0,0,22.86.09l70.46-69.42a3.54,3.54,0,0,1,5,0l69,69a3.56,3.56,0,0,1,0,5l-8.48,8.48a3.56,3.56,0,0,1-5,0l-48.47-47a16.28,16.28,0,0,0-22.82.2l-46.51,46.75a16.26,16.26,0,0,0,0,22.92l8.61,8.61a16.27,16.27,0,0,0,22.89.06l25.07-24.78a3.57,3.57,0,0,1,5,0l24.94,24.51a3.51,3.51,0,0,1,0,5L453.38,329.9c-.11,0-.2-.07-.32-.07a9.26,9.26,0,1,0,9.26,9.26v-.16l24.81-24.8a16.24,16.24,0,0,0,0-23l-.1-.09-24.93-24.5a16.29,16.29,0,0,0-22.79,0l-25.15,24.81a3.54,3.54,0,0,1-5,0l-8.6-8.62a3.56,3.56,0,0,1,0-5L447.11,231a3.55,3.55,0,0,1,5-.05l48.46,47.09a16.3,16.3,0,0,0,22.78-.17l8.49-8.49a16.24,16.24,0,0,0,0-22.95l-69.06-69.06a16.26,16.26,0,0,0-22.87-.1l-70.46,69.4a3.57,3.57,0,0,1-5,0L355.76,238a3.54,3.54,0,0,1,0-5l91.67-91.67a3.56,3.56,0,0,1,5,0l48.59,47V158.11a3.55,3.55,0,0,1,3.54-3.54H516a3.57,3.57,0,0,1,3.55,3.54v42.36A16.37,16.37,0,0,0,524.31,212l18.24,18.24c0,.12-.07.21-.07.32a9.26,9.26,0,1,0,9.25-9.27Z"/></g></g><g id="Divider"><line class="cls-9" x1="998" y1="14.23" x2="902" y2="614.23"/></g></g></svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pluginAlias": "Homebridge Ring HKSV",
|
|
3
|
+
"pluginType": "platform",
|
|
4
|
+
"singular": true,
|
|
5
|
+
"customUi": true,
|
|
6
|
+
"customUiPath": "./lib/homebridge-ui",
|
|
7
|
+
"customUiDevServer": "http://localhost:3000/api/plugins/settings-ui/homebridge-ring-hksv",
|
|
8
|
+
"headerDisplay": "See [the wiki](https://github.com/dgreif/ring/wiki/Refresh-Tokens) for details on generating a refresh token",
|
|
9
|
+
"accountLinkingHeader": "Please supply your Ring credentials. These will be used to contact the Ring servers and generate a unique access token for your account. Your email and password will be sent directly to Ring and will not be stored anywhere.",
|
|
10
|
+
"schema": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"properties": {
|
|
13
|
+
"refreshToken": {
|
|
14
|
+
"title": "Refresh Token",
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "Click \"Generate New Refresh Token\" above to change accounts or if your refresh token has expired",
|
|
17
|
+
"placeholder": "Refresh Token"
|
|
18
|
+
},
|
|
19
|
+
"alarmOnEntryDelay": {
|
|
20
|
+
"title": "Alarm on Entry Delay",
|
|
21
|
+
"type": "boolean",
|
|
22
|
+
"description": "If enabled, HomeKit will register a delayed entry event as a triggered alarm. Useful if you want to know as soon as someone opens a door with the alarm armed"
|
|
23
|
+
},
|
|
24
|
+
"enableHksv": {
|
|
25
|
+
"title": "Enable HKSV (Experimental)",
|
|
26
|
+
"type": "boolean",
|
|
27
|
+
"description": "Enables HomeKit Secure Video services for supported Ring cameras. This implementation is currently in-progress."
|
|
28
|
+
},
|
|
29
|
+
"disableHksvOnBattery": {
|
|
30
|
+
"title": "Disable HKSV On Battery Cameras",
|
|
31
|
+
"type": "boolean",
|
|
32
|
+
"description": "If enabled, HKSV is disabled on battery-powered cameras to reduce battery and network impact.",
|
|
33
|
+
"default": true
|
|
34
|
+
},
|
|
35
|
+
"hksvPrebufferLengthMs": {
|
|
36
|
+
"title": "HKSV Prebuffer Length (ms)",
|
|
37
|
+
"type": "integer",
|
|
38
|
+
"minimum": 4000,
|
|
39
|
+
"maximum": 16000,
|
|
40
|
+
"description": "How much pre-event video is retained for HKSV. Minimum is 4000ms."
|
|
41
|
+
},
|
|
42
|
+
"hksvFragmentLengthMs": {
|
|
43
|
+
"title": "HKSV Fragment Length (ms)",
|
|
44
|
+
"type": "integer",
|
|
45
|
+
"minimum": 1000,
|
|
46
|
+
"maximum": 8000,
|
|
47
|
+
"description": "Target recording fragment duration for HKSV fragmented MP4 output."
|
|
48
|
+
},
|
|
49
|
+
"hksvMaxRecordingSeconds": {
|
|
50
|
+
"title": "HKSV Max Recording Seconds",
|
|
51
|
+
"type": "integer",
|
|
52
|
+
"minimum": 5,
|
|
53
|
+
"maximum": 300,
|
|
54
|
+
"description": "Optional safety cap for a single HKSV recording stream duration."
|
|
55
|
+
},
|
|
56
|
+
"cameraVideoCodec": {
|
|
57
|
+
"title": "Camera Video Codec",
|
|
58
|
+
"type": "string",
|
|
59
|
+
"description": "Preferred ffmpeg H.264 encoder for camera paths.",
|
|
60
|
+
"oneOf": [
|
|
61
|
+
{
|
|
62
|
+
"title": "h264_videotoolbox",
|
|
63
|
+
"enum": ["h264_videotoolbox"]
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"title": "libx264",
|
|
67
|
+
"enum": ["libx264"]
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
"hideLightGroups": {
|
|
72
|
+
"title": "Hide Light Groups",
|
|
73
|
+
"type": "boolean",
|
|
74
|
+
"description": "Hides Ring lighting groups, allowing you to group lights differently in HomeKit than how you group them in the Ring app"
|
|
75
|
+
},
|
|
76
|
+
"hideDoorbellSwitch": {
|
|
77
|
+
"title": "Hide Doorbell Programmable Switch",
|
|
78
|
+
"type": "boolean",
|
|
79
|
+
"description": "Hides the Programmable Switch for Ring doorbells. This switch is only useful for triggering an automation when the doorbell is pressed, so most users can safely hide it"
|
|
80
|
+
},
|
|
81
|
+
"hideCameraLight": {
|
|
82
|
+
"title": "Hide Camera Light",
|
|
83
|
+
"type": "boolean"
|
|
84
|
+
},
|
|
85
|
+
"hideCameraMotionSensor": {
|
|
86
|
+
"title": "Hide Camera Motion Sensors",
|
|
87
|
+
"type": "boolean"
|
|
88
|
+
},
|
|
89
|
+
"hideCameraSirenSwitch": {
|
|
90
|
+
"title": "Hide Camera Siren Switch",
|
|
91
|
+
"type": "boolean"
|
|
92
|
+
},
|
|
93
|
+
"hideInHomeDoorbellSwitch": {
|
|
94
|
+
"title": "Hide In-Home Doorbell Switch",
|
|
95
|
+
"type": "boolean"
|
|
96
|
+
},
|
|
97
|
+
"hideAlarmSirenSwitch": {
|
|
98
|
+
"title": "Hide Alarm Siren Switch",
|
|
99
|
+
"type": "boolean",
|
|
100
|
+
"description": "Hides switch that allows you to turn on the siren of the Ring Alarm. Enable this is your Siri commands keep setting off the siren and you don't care about having the switch"
|
|
101
|
+
},
|
|
102
|
+
"externalCameraIdSalt": {
|
|
103
|
+
"title": "External Camera ID Salt",
|
|
104
|
+
"type": "string",
|
|
105
|
+
"description": "Advanced recovery option: changing this forces new HomeKit identities for Ring camera external accessories."
|
|
106
|
+
},
|
|
107
|
+
"showPanicButtons": {
|
|
108
|
+
"title": "Show Panic Buttons",
|
|
109
|
+
"type": "boolean",
|
|
110
|
+
"description": "Creates a new `Panic Buttons` device in HomeKit with `Burglar Alarm` and `Fire Alarm` switches. **Use these at your own risk. I do not guarantee functionality in case of emergency, nor do I take responsibility for any false alarms**. These function just like the SOS sliders in the Ring app."
|
|
111
|
+
},
|
|
112
|
+
"ffmpegPath": {
|
|
113
|
+
"title": "Custom FFmpeg Path",
|
|
114
|
+
"type": "string",
|
|
115
|
+
"description": "A custom path to the ffmpeg executable. By default, the static binaries built in ffmpeg-for-homebridge will be used. If you prefer to use your own version of ffmpeg, you can pass a complete path, or simply [ ffmpeg ] to use ffmpeg from your PATH."
|
|
116
|
+
},
|
|
117
|
+
"nightModeBypassFor": {
|
|
118
|
+
"title": "Night Mode as Bypass For Mode",
|
|
119
|
+
"type": "string",
|
|
120
|
+
"description": "Allows you to use Night mode to \"Bypass and Arm\" a Ring Alarm. If set and Night mode is activated from HomeKit, any open contact sensors will automatically be bypassed and the alarm will be set to the mode selected here.",
|
|
121
|
+
"oneOf": [
|
|
122
|
+
{
|
|
123
|
+
"title": "Home",
|
|
124
|
+
"enum": ["some"]
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"title": "Away",
|
|
128
|
+
"enum": ["all"]
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
},
|
|
132
|
+
"avoidSnapshotBatteryDrain": {
|
|
133
|
+
"title": "Avoid Snapshot Battery Drain",
|
|
134
|
+
"type": "boolean",
|
|
135
|
+
"description": "Causes snapshots for battery cameras to be fetched at a minimum 10 minute interval to avoid draining the battery. This is useful if have the Home app open constantly, causing frequent snapshot requests."
|
|
136
|
+
},
|
|
137
|
+
"beamDurationSeconds": {
|
|
138
|
+
"title": "Ring Smart Lighting Timer",
|
|
139
|
+
"type": "integer",
|
|
140
|
+
"minimum": 1,
|
|
141
|
+
"maximum": 32767,
|
|
142
|
+
"description": "Ring Smart Lighting will only stay on for a set amount of time. You can specify that duration here (in seconds). If not specified, the light will stay on for the last duration used within the Ring app for individual lights, and 60 seconds for lighting groups. Max value is 32767 which is ~9.1 hours"
|
|
143
|
+
},
|
|
144
|
+
"debug": {
|
|
145
|
+
"title": "Debug Logging",
|
|
146
|
+
"type": "boolean",
|
|
147
|
+
"description": "Turns on additional logging. In particular, ffmpeg logging."
|
|
148
|
+
},
|
|
149
|
+
"disableLogs": {
|
|
150
|
+
"title": "Disable All Logging",
|
|
151
|
+
"type": "boolean",
|
|
152
|
+
"description": "Disables all logs generated by the Ring plugin"
|
|
153
|
+
},
|
|
154
|
+
"cameraStatusPollingSeconds": {
|
|
155
|
+
"title": "Camera/Chime Status Polling",
|
|
156
|
+
"type": "integer",
|
|
157
|
+
"minimum": 1,
|
|
158
|
+
"placeholder": "Get camera status every __ seconds",
|
|
159
|
+
"description": "Camera/Chime status (such as light/siren/volume/snooze state) does not update live and must be fetched periodically. By default this is done every 20 seconds"
|
|
160
|
+
},
|
|
161
|
+
"locationModePollingSeconds": {
|
|
162
|
+
"title": "Location Mode Polling",
|
|
163
|
+
"type": "integer",
|
|
164
|
+
"minimum": 0,
|
|
165
|
+
"placeholder": "Get current Location Mode every __ seconds",
|
|
166
|
+
"description": "Location Modes do not update live and must be fetched periodically. By default this is done every 20 seconds for locations that have Location Modes enabled, and that do not have a Ring Alarm. The Location Mode switch can be hidden by setting this option to 0"
|
|
167
|
+
},
|
|
168
|
+
"hideDeviceIds": {
|
|
169
|
+
"title": "Hide Device ID",
|
|
170
|
+
"type": "array",
|
|
171
|
+
"items": {
|
|
172
|
+
"type": "string"
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
"locationIds": {
|
|
176
|
+
"title": "Location ID",
|
|
177
|
+
"type": "array",
|
|
178
|
+
"items": {
|
|
179
|
+
"type": "string"
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
"onlyDeviceTypes": {
|
|
183
|
+
"title": "Device Types",
|
|
184
|
+
"type": "array",
|
|
185
|
+
"items": {
|
|
186
|
+
"type": "string"
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
"name": {
|
|
190
|
+
"title": "Name",
|
|
191
|
+
"type": "string"
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
"required": ["refreshToken"]
|
|
195
|
+
},
|
|
196
|
+
"layout": [
|
|
197
|
+
{
|
|
198
|
+
"type": "flex",
|
|
199
|
+
"flex-flow": "row wrap",
|
|
200
|
+
"items": ["refreshToken"]
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"type": "fieldset",
|
|
204
|
+
"title": "Optional Configuration",
|
|
205
|
+
"expandable": true,
|
|
206
|
+
"items": [
|
|
207
|
+
"alarmOnEntryDelay",
|
|
208
|
+
"enableHksv",
|
|
209
|
+
"disableHksvOnBattery",
|
|
210
|
+
"hksvPrebufferLengthMs",
|
|
211
|
+
"hksvFragmentLengthMs",
|
|
212
|
+
"hksvMaxRecordingSeconds",
|
|
213
|
+
"cameraVideoCodec",
|
|
214
|
+
"hideLightGroups",
|
|
215
|
+
"hideDoorbellSwitch",
|
|
216
|
+
"hideCameraLight",
|
|
217
|
+
"hideCameraMotionSensor",
|
|
218
|
+
"hideCameraSirenSwitch",
|
|
219
|
+
"hideInHomeDoorbellSwitch",
|
|
220
|
+
"hideAlarmSirenSwitch",
|
|
221
|
+
"externalCameraIdSalt",
|
|
222
|
+
"showPanicButtons",
|
|
223
|
+
"nightModeBypassFor",
|
|
224
|
+
"avoidSnapshotBatteryDrain",
|
|
225
|
+
"beamDurationSeconds",
|
|
226
|
+
"debug",
|
|
227
|
+
"disableLogs",
|
|
228
|
+
"cameraStatusPollingSeconds",
|
|
229
|
+
"locationModePollingSeconds",
|
|
230
|
+
{
|
|
231
|
+
"key": "hideDeviceIds",
|
|
232
|
+
"add": "Add Another Device ID",
|
|
233
|
+
"type": "array",
|
|
234
|
+
"items": [
|
|
235
|
+
{
|
|
236
|
+
"type": "div",
|
|
237
|
+
"displayFlex": true,
|
|
238
|
+
"flex-direction": "row",
|
|
239
|
+
"items": [
|
|
240
|
+
{
|
|
241
|
+
"key": "hideDeviceIds[]",
|
|
242
|
+
"placeholder": "Device ID"
|
|
243
|
+
}
|
|
244
|
+
]
|
|
245
|
+
}
|
|
246
|
+
]
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"type": "help",
|
|
250
|
+
"helpvalue": "<p>If you would like to hide individual devices in HomeKit, enter the desired device IDs above. The ID for each device is logged every time homebridge starts.</p>"
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
"key": "locationIds",
|
|
254
|
+
"type": "array",
|
|
255
|
+
"items": [
|
|
256
|
+
{
|
|
257
|
+
"type": "div",
|
|
258
|
+
"displayFlex": true,
|
|
259
|
+
"flex-direction": "row",
|
|
260
|
+
"items": [
|
|
261
|
+
{
|
|
262
|
+
"key": "locationIds[]",
|
|
263
|
+
"placeholder": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx or xxxxx-xxxxx-x"
|
|
264
|
+
}
|
|
265
|
+
]
|
|
266
|
+
}
|
|
267
|
+
]
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"type": "help",
|
|
271
|
+
"helpvalue": "<p>If you have multiple locations configured in Ring but only want some of them in HomeKit, enter the desired location IDs above. The ID for each location is logged every time homebridge starts.</p>"
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"key": "onlyDeviceTypes",
|
|
275
|
+
"type": "array",
|
|
276
|
+
"items": [
|
|
277
|
+
{
|
|
278
|
+
"type": "div",
|
|
279
|
+
"displayFlex": true,
|
|
280
|
+
"flex-direction": "row",
|
|
281
|
+
"items": [
|
|
282
|
+
{
|
|
283
|
+
"key": "onlyDeviceTypes[]",
|
|
284
|
+
"placeholder": "Device Type"
|
|
285
|
+
}
|
|
286
|
+
]
|
|
287
|
+
}
|
|
288
|
+
]
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
"type": "help",
|
|
292
|
+
"helpvalue": "<p>If you only want to display certain device types, you can specify a those device types here. All other device types will be removed from HomeKit. Example: security-panel to show your alarm, hp_cam_v1 to show floodlight cams.</p>"
|
|
293
|
+
}
|
|
294
|
+
]
|
|
295
|
+
}
|
|
296
|
+
]
|
|
297
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { hap } from "./hap.js";
|
|
2
|
+
import { shareReplay, take } from 'rxjs/operators';
|
|
3
|
+
import { firstValueFrom } from 'rxjs';
|
|
4
|
+
import { debug } from "./config.js";
|
|
5
|
+
import { logError, logInfo } from 'ring-client-api/util';
|
|
6
|
+
function isServiceInstance(serviceType) {
|
|
7
|
+
return typeof serviceType === 'object';
|
|
8
|
+
}
|
|
9
|
+
export class BaseAccessory {
|
|
10
|
+
servicesInUse = [];
|
|
11
|
+
initBase() {
|
|
12
|
+
this.pruneUnusedServices();
|
|
13
|
+
}
|
|
14
|
+
getService(serviceType, name = this.device.name, subType) {
|
|
15
|
+
if (isServiceInstance(serviceType)) {
|
|
16
|
+
return serviceType;
|
|
17
|
+
}
|
|
18
|
+
if (debug) {
|
|
19
|
+
name = 'TEST ' + name;
|
|
20
|
+
}
|
|
21
|
+
const existingService = subType
|
|
22
|
+
? this.accessory.getServiceById(serviceType, subType)
|
|
23
|
+
: this.accessory.getService(serviceType), service = existingService ||
|
|
24
|
+
this.accessory.addService(serviceType, name, subType);
|
|
25
|
+
if (debug &&
|
|
26
|
+
existingService &&
|
|
27
|
+
existingService.displayName &&
|
|
28
|
+
name !== existingService.displayName) {
|
|
29
|
+
throw new Error(`Overlapping services for device ${this.device.name} - ${name} != ${existingService.displayName} - ${serviceType}`);
|
|
30
|
+
}
|
|
31
|
+
if (!this.servicesInUse.includes(service)) {
|
|
32
|
+
this.servicesInUse.push(service);
|
|
33
|
+
}
|
|
34
|
+
return service;
|
|
35
|
+
}
|
|
36
|
+
registerObservableCharacteristic({ characteristicType, serviceType, serviceSubType, onValue, setValue, name, requestUpdate, }) {
|
|
37
|
+
const service = this.getService(serviceType, name, serviceSubType), characteristic = service.getCharacteristic(characteristicType), onCachedValue = onValue.pipe(shareReplay(1));
|
|
38
|
+
onCachedValue.subscribe((value) => {
|
|
39
|
+
characteristic.updateValue(value);
|
|
40
|
+
});
|
|
41
|
+
if (requestUpdate) {
|
|
42
|
+
// Only register for GET if an async request should be made to get an updated value
|
|
43
|
+
onCachedValue.pipe(take(1)).subscribe(() => {
|
|
44
|
+
// allow GET once a value is cached
|
|
45
|
+
characteristic.on("get" /* CharacteristicEventTypes.GET */, async (callback) => {
|
|
46
|
+
try {
|
|
47
|
+
const value = await firstValueFrom(onCachedValue);
|
|
48
|
+
callback(null, value);
|
|
49
|
+
requestUpdate();
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
callback(e);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (setValue) {
|
|
58
|
+
characteristic.on("set" /* CharacteristicEventTypes.SET */, (newValue, callback) => {
|
|
59
|
+
Promise.resolve(setValue(newValue)).catch((e) => {
|
|
60
|
+
logError(e);
|
|
61
|
+
});
|
|
62
|
+
callback();
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
pruneUnusedServices() {
|
|
67
|
+
const safeServiceUUIDs = [
|
|
68
|
+
hap.Service.CameraRTPStreamManagement.UUID,
|
|
69
|
+
hap.Service.CameraRecordingManagement?.UUID,
|
|
70
|
+
hap.Service.CameraOperatingMode?.UUID,
|
|
71
|
+
hap.Service.DataStreamTransportManagement?.UUID,
|
|
72
|
+
].filter(Boolean);
|
|
73
|
+
this.accessory.services.forEach((service) => {
|
|
74
|
+
if (!this.servicesInUse.includes(service) &&
|
|
75
|
+
!safeServiceUUIDs.includes(service.UUID)) {
|
|
76
|
+
logInfo('Pruning unused service', service.UUID, service.displayName || service.name, 'from', this.device.name);
|
|
77
|
+
this.accessory.removeService(service);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|