@homebridge-plugins/homebridge-firstalert 0.0.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +2 -0
- package/.github/FUNDING.yml +4 -0
- package/.github/ISSUE_TEMPLATE/bug-report.yml +97 -0
- package/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/.github/ISSUE_TEMPLATE/feature-request.yml +38 -0
- package/.github/ISSUE_TEMPLATE/support-request.yml +85 -0
- package/.github/ISSUE_TEMPLATE.md +52 -0
- package/.github/PULL_REQUEST_TEMPLATE/pull_request.md +27 -0
- package/.github/dependabot.yml +17 -0
- package/.github/labeler.yml +38 -0
- package/.github/release-drafter.yml +33 -0
- package/.github/workflows/beta-release.yml +55 -0
- package/.github/workflows/build.yml +18 -0
- package/.github/workflows/changerelease.yml +11 -0
- package/.github/workflows/labeler.yml +9 -0
- package/.github/workflows/release-drafter.yml +14 -0
- package/.github/workflows/release.yml +35 -0
- package/.github/workflows/stale.yml +12 -0
- package/CHANGELOG.md +10 -0
- package/LICENSE +14 -0
- package/README.md +67 -0
- package/SECURITY.md +19 -0
- package/branding/Homebridge_x_FirstAlert.svg +48 -0
- package/branding/icon.png +0 -0
- package/config.schema.json +58 -0
- package/dist/homebridge-ui/public/index.html +548 -0
- package/dist/src/api/resideoClient.d.ts +32 -0
- package/dist/src/api/resideoClient.d.ts.map +1 -0
- package/dist/src/api/resideoClient.js +76 -0
- package/dist/src/api/resideoClient.js.map +1 -0
- package/dist/src/devices/device.d.ts +40 -0
- package/dist/src/devices/device.d.ts.map +1 -0
- package/dist/src/devices/device.js +207 -0
- package/dist/src/devices/device.js.map +1 -0
- package/dist/src/devices/leaksensors.d.ts +34 -0
- package/dist/src/devices/leaksensors.d.ts.map +1 -0
- package/dist/src/devices/leaksensors.js +163 -0
- package/dist/src/devices/leaksensors.js.map +1 -0
- package/dist/src/devices/smoke.d.ts +35 -0
- package/dist/src/devices/smoke.d.ts.map +1 -0
- package/dist/src/devices/smoke.js +150 -0
- package/dist/src/devices/smoke.js.map +1 -0
- package/dist/src/devices/thermostats.d.ts +42 -0
- package/dist/src/devices/thermostats.d.ts.map +1 -0
- package/dist/src/devices/thermostats.js +192 -0
- package/dist/src/devices/thermostats.js.map +1 -0
- package/dist/src/devices/valve.d.ts +21 -0
- package/dist/src/devices/valve.d.ts.map +1 -0
- package/dist/src/devices/valve.js +131 -0
- package/dist/src/devices/valve.js.map +1 -0
- package/dist/src/homebridge-ui/server.d.ts +5 -0
- package/dist/src/homebridge-ui/server.d.ts.map +1 -0
- package/dist/src/homebridge-ui/server.js +95 -0
- package/dist/src/homebridge-ui/server.js.map +1 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +7 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/platform.d.ts +18 -0
- package/dist/src/platform.d.ts.map +1 -0
- package/dist/src/platform.js +108 -0
- package/dist/src/platform.js.map +1 -0
- package/dist/src/settings.d.ts +341 -0
- package/dist/src/settings.d.ts.map +1 -0
- package/dist/src/settings.js +25 -0
- package/dist/src/settings.js.map +1 -0
- package/dist/src/utils.d.ts +21 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +58 -0
- package/dist/src/utils.js.map +1 -0
- package/dist/test/index.test.d.ts +2 -0
- package/dist/test/index.test.d.ts.map +1 -0
- package/dist/test/index.test.js +14 -0
- package/dist/test/index.test.js.map +1 -0
- package/dist/test/platform.test.d.ts +2 -0
- package/dist/test/platform.test.d.ts.map +1 -0
- package/dist/test/platform.test.js +56 -0
- package/dist/test/platform.test.js.map +1 -0
- package/dist/test/settings.test.d.ts +2 -0
- package/dist/test/settings.test.d.ts.map +1 -0
- package/dist/test/settings.test.js +48 -0
- package/dist/test/settings.test.js.map +1 -0
- package/dist/test/utils.test.d.ts +2 -0
- package/dist/test/utils.test.d.ts.map +1 -0
- package/dist/test/utils.test.js +17 -0
- package/dist/test/utils.test.js.map +1 -0
- package/docs/.nojekyll +1 -0
- package/docs/assets/hierarchy.js +1 -0
- package/docs/assets/highlight.css +22 -0
- package/docs/assets/icons.js +18 -0
- package/docs/assets/icons.svg +1 -0
- package/docs/assets/main.js +60 -0
- package/docs/assets/navigation.js +1 -0
- package/docs/assets/search.js +1 -0
- package/docs/assets/style.css +1633 -0
- package/docs/hierarchy.html +1 -0
- package/docs/index.html +77 -0
- package/docs/modules.html +1 -0
- package/docs/variables/default.html +1 -0
- package/eslint.config.js +44 -0
- package/nodemon.json +10 -0
- package/package.json +106 -0
- package/scripts/free-dev-ports.mjs +105 -0
- package/src/api/resideoClient.ts +106 -0
- package/src/devices/device.ts +226 -0
- package/src/devices/leaksensors.ts +206 -0
- package/src/devices/smoke.ts +173 -0
- package/src/devices/thermostats.ts +243 -0
- package/src/devices/valve.ts +162 -0
- package/src/homebridge-ui/public/index.html +548 -0
- package/src/homebridge-ui/server.ts +102 -0
- package/src/index.ts +13 -0
- package/src/platform.ts +112 -0
- package/src/settings.ts +402 -0
- package/src/utils.ts +61 -0
- package/test/index.test.ts +18 -0
- package/test/platform.test.ts +65 -0
- package/test/settings.test.ts +56 -0
- package/test/utils.test.ts +20 -0
- package/tsconfig.json +27 -0
- package/typedoc.json +22 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!DOCTYPE html><html class="default" lang="en" data-base="./"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>@homebridge-plugins/homebridge-firstalert</title><meta name="description" content="Documentation for @homebridge-plugins/homebridge-firstalert"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="assets/style.css"/><link rel="stylesheet" href="assets/highlight.css"/><script defer src="assets/main.js"></script><script async src="assets/icons.js" id="tsd-icons-script"></script><script async src="assets/search.js" id="tsd-search-script"></script><script async src="assets/navigation.js" id="tsd-nav-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><a href="index.html" class="title">@homebridge-plugins/homebridge-firstalert</a><div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/><ul role="listbox" id="tsd-search-results"></ul><div id="tsd-search-status" aria-live="polite" aria-atomic="true"><div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-menu"></use></svg></a></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><h1>@homebridge-plugins/homebridge-firstalert</h1></div><h2>Hierarchy Summary</h2></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-chevronDown"></use></svg><h3>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div></div><div class="site-menu"><nav class="tsd-navigation"><a href="modules.html">@homebridge-plugins/homebridge-firstalert</a><ul class="tsd-small-nested-navigation" id="tsd-nav-container"><li>Loading...</li></ul></nav></div></div></div><footer></footer><div class="overlay"></div></body></html>
|
package/docs/index.html
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<!DOCTYPE html><html class="default" lang="en" data-base="./"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>@homebridge-plugins/homebridge-firstalert</title><meta name="description" content="Documentation for @homebridge-plugins/homebridge-firstalert"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="assets/style.css"/><link rel="stylesheet" href="assets/highlight.css"/><script defer src="assets/main.js"></script><script async src="assets/icons.js" id="tsd-icons-script"></script><script async src="assets/search.js" id="tsd-search-script"></script><script async src="assets/navigation.js" id="tsd-nav-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><a href="index.html" class="title">@homebridge-plugins/homebridge-firstalert</a><div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/><ul role="listbox" id="tsd-search-results"></ul><div id="tsd-search-status" aria-live="polite" aria-atomic="true"><div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-menu"></use></svg></a></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><h1>@homebridge-plugins/homebridge-firstalert</h1></div><div class="tsd-panel tsd-typography"><span align="center">
|
|
2
|
+
<p><a href="https://github.com/homebridge/verified/blob/latest/verified-plugins.json"><img alt="homebridge-verified" src="https://raw.githubusercontent.com/homebridge-plugins/homebridge-firstalert/latest/branding/Homebridge_x_Resideo.svg?sanitize=true" width="350px"></a></p>
|
|
3
|
+
<h1 id="homebridge-firstalert" class="tsd-anchor-link">Homebridge FirstAlert<a href="#homebridge-firstalert" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h1>
|
|
4
|
+
<p><a href="https://www.npmjs.com/package/homebridge-firstalert"><img title="npm version" src="https://badgen.net/npm/v/homebridge-firstalert?icon=npm&label" ></a>
|
|
5
|
+
<a href="https://www.npmjs.com/package/homebridge-firstalert"><img title="npm downloads" src="https://badgen.net/npm/dt/homebridge-firstalert?label=downloads" ></a>
|
|
6
|
+
<a href="https://discord.gg/8fpZA4S"><img title="discord-firstalert" src="https://badgen.net/discord/online-members/8fpZA4S?icon=discord&label=discord" ></a>
|
|
7
|
+
<a href="https://paypal.me/donavanbecker"><img title="donate" src="https://badgen.net/badge/donate/paypal/yellow" ></a></p>
|
|
8
|
+
<p>The Homebridge <a href="https://firstalert.com">FirstAlert</a>
|
|
9
|
+
plugin allows you to access your FirstAlert Device(s) from HomeKit with
|
|
10
|
+
<a href="https://homebridge.io">Homebridge</a>.
|
|
11
|
+
</p>
|
|
12
|
+
</span>
|
|
13
|
+
<h2 id="installation" class="tsd-anchor-link">Installation<a href="#installation" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2>
|
|
14
|
+
<ol>
|
|
15
|
+
<li>Search for "FirstAlert" on the plugin screen of <a href="https://github.com/oznu/homebridge-config-ui-x">Homebridge Config UI X</a>.</li>
|
|
16
|
+
<li>Click <strong>Install</strong>.</li>
|
|
17
|
+
</ol>
|
|
18
|
+
<h2 id="configuration" class="tsd-anchor-link">Configuration<a href="#configuration" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2>
|
|
19
|
+
<ol>
|
|
20
|
+
<li>Login / create an account at <a href="https://developer.honeywellhome.com/user">https://developer.honeywellhome.com/user</a>
|
|
21
|
+
<ul>
|
|
22
|
+
<li>Your FirstAlert Developer Account, this account is different then your FirstAlert Account that you log into the FirstAlert App with</li>
|
|
23
|
+
</ul>
|
|
24
|
+
</li>
|
|
25
|
+
<li>Click <strong>Create New App</strong></li>
|
|
26
|
+
<li>Give your application a name</li>
|
|
27
|
+
<li>Copy the hostname found on #3 of the Intro Page into the Callback URL field</li>
|
|
28
|
+
</ol>
|
|
29
|
+
<p align="center">
|
|
30
|
+
<img src="https://user-images.githubusercontent.com/9875439/192078620-6998511c-1c11-4e06-b7e1-475b22ef5180.png" width="300px">
|
|
31
|
+
</p>
|
|
32
|
+
<ol start="5">
|
|
33
|
+
<li>Enter the generated consumer key and secret into the plugin settings screen of <a href="https://github.com/oznu/homebridge-config-ui-x">Homebridge Config UI X</a></li>
|
|
34
|
+
<li>Click <strong>Link Account</strong></li>
|
|
35
|
+
</ol>
|
|
36
|
+
<p align="center">
|
|
37
|
+
<img src="https://user-images.githubusercontent.com/9875439/192078614-e568d121-e0e4-4123-bdda-4238e8a6e601.png" width="300px">
|
|
38
|
+
</p>
|
|
39
|
+
<ol start="7">
|
|
40
|
+
<li>Login to your <a href="https://account.honeywellhome.com">https://www.firstalert.com</a>.</li>
|
|
41
|
+
<li>Click Allow</li>
|
|
42
|
+
<li>Select Devices
|
|
43
|
+
<ul>
|
|
44
|
+
<li>I would recommend selecting all devices since you can restrict the devices you don't want in the Home app later, by DeviceID.</li>
|
|
45
|
+
</ul>
|
|
46
|
+
</li>
|
|
47
|
+
<li>Click Connect</li>
|
|
48
|
+
<li>Click Save
|
|
49
|
+
<ul>
|
|
50
|
+
<li>If you plan on adding this plugin into a child bridge, I would configure that at this time before restarting Homebridge.
|
|
51
|
+
<ul>
|
|
52
|
+
<li>Reminder that you will have to add this child bridge into the home app to get FirstAlert accessories to show up.</li>
|
|
53
|
+
</ul>
|
|
54
|
+
</li>
|
|
55
|
+
</ul>
|
|
56
|
+
</li>
|
|
57
|
+
<li>Restart Homebridge</li>
|
|
58
|
+
</ol>
|
|
59
|
+
<h2 id="supported-firstalert-devices" class="tsd-anchor-link">Supported FirstAlert Devices<a href="#supported-firstalert-devices" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2>
|
|
60
|
+
<ul>
|
|
61
|
+
<li><a href="https://www.firstalert.com/us/en/products/air/thermostats/wifi-thermostats/t10-pro-smart-thermostat-with-redlinkr-room-sensor-thx321wfs2001w-u/">T10 Thermostat</a> - Already HomeKit Certified</li>
|
|
62
|
+
<li><a href="https://www.firstalert.com/us/en/products/air/thermostats/wifi-thermostats/t9-smart-thermostat-with-sensor-rcht9610wfsw2003-u/">T9 Thermostat</a> - Already HomeKit Certified
|
|
63
|
+
<ul>
|
|
64
|
+
<li><a href="https://www.firstalert.com/us/en/products/air/thermostat-accessories/t9-smart-sensor-rchtsensor-1pk-u/">T9 Smart Roomsensors</a></li>
|
|
65
|
+
</ul>
|
|
66
|
+
</li>
|
|
67
|
+
<li><a href="https://www.firstalert.com/us/en/products/air/thermostats/wifi-thermostats/t6-pro-smart-thermostat-multi-stage-3-heat-2-cool-th6320wf2003-u/">T6 Thermostat</a> - Already HomeKit Certified</li>
|
|
68
|
+
<li><a href="https://www.firstalert.com/us/en/products/air/thermostats/wifi-thermostats/t5-smart-thermostat-with-c-wire-adapter-rcht8612wf2005-u/">T5 Thermostat</a> - Already HomeKit Certified</li>
|
|
69
|
+
<li><a href="https://www.firstalert.com/us/en/products/air/thermostats/wifi-thermostats/the-round-smart-thermostat-rch9310wf5003-u/">Round Thermostat</a> - Already HomeKit Certified</li>
|
|
70
|
+
<li>Some Total Comfort Control Thermostats
|
|
71
|
+
<ul>
|
|
72
|
+
<li>Pushing Commands may not be supported on some.</li>
|
|
73
|
+
</ul>
|
|
74
|
+
</li>
|
|
75
|
+
<li><a href="https://www.firstalert.com/us/en/products/water/spot-leak-detection/wifi-water-leak-freeze-detector-rchw3610wf1001-u/">WiFi Water Leak & Freeze Detector</a></li>
|
|
76
|
+
</ul>
|
|
77
|
+
</div></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-chevronDown"></use></svg><h3>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div><details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-chevronDown"></use></svg><h3>On This Page</h3></summary><div class="tsd-accordion-details"><a href="#homebridge-firstalert"><span>Homebridge <wbr/>First<wbr/>Alert</span></a><ul><li><a href="#installation"><span>Installation</span></a></li><li><a href="#configuration"><span>Configuration</span></a></li><li><a href="#supported-firstalert-devices"><span>Supported <wbr/>First<wbr/>Alert <wbr/>Devices</span></a></li></ul></div></details></div><div class="site-menu"><nav class="tsd-navigation"><a href="modules.html">@homebridge-plugins/homebridge-firstalert</a><ul class="tsd-small-nested-navigation" id="tsd-nav-container"><li>Loading...</li></ul></nav></div></div></div><footer></footer><div class="overlay"></div></body></html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!DOCTYPE html><html class="default" lang="en" data-base="./"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>@homebridge-plugins/homebridge-firstalert</title><meta name="description" content="Documentation for @homebridge-plugins/homebridge-firstalert"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="assets/style.css"/><link rel="stylesheet" href="assets/highlight.css"/><script defer src="assets/main.js"></script><script async src="assets/icons.js" id="tsd-icons-script"></script><script async src="assets/search.js" id="tsd-search-script"></script><script async src="assets/navigation.js" id="tsd-nav-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><a href="index.html" class="title">@homebridge-plugins/homebridge-firstalert</a><div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/><ul role="listbox" id="tsd-search-results"></ul><div id="tsd-search-status" aria-live="polite" aria-atomic="true"><div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-menu"></use></svg></a></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><ul class="tsd-breadcrumb" aria-label="Breadcrumb"></ul><h1>@homebridge-plugins/homebridge-firstalert</h1></div><details class="tsd-panel-group tsd-member-group tsd-accordion" open><summary class="tsd-accordion-summary" data-key="section-Variables"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-chevronDown"></use></svg><h2>Variables</h2></summary><dl class="tsd-member-summaries"><dt class="tsd-member-summary" id="default"><span class="tsd-member-summary-name"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Variable"><use href="assets/icons.svg#icon-32"></use></svg><a href="variables/default.html">default</a><a href="#default" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></span></dt><dd class="tsd-member-summary"></dd></dl></details></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-chevronDown"></use></svg><h3>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div><details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-chevronDown"></use></svg><h3>On This Page</h3></summary><div class="tsd-accordion-details"><details open class="tsd-accordion tsd-page-navigation-section"><summary class="tsd-accordion-summary" data-key="section-Variables"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-chevronDown"></use></svg>Variables</summary><div><a href="#default"><svg class="tsd-kind-icon" viewBox="0 0 24 24" aria-label="Variable"><use href="assets/icons.svg#icon-32"></use></svg><span>default</span></a></div></details></div></details></div><div class="site-menu"><nav class="tsd-navigation"><a href="modules.html" class="current">@homebridge-plugins/homebridge-firstalert</a><ul class="tsd-small-nested-navigation" id="tsd-nav-container"><li>Loading...</li></ul></nav></div></div></div><footer></footer><div class="overlay"></div></body></html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>default | @homebridge-plugins/homebridge-firstalert</title><meta name="description" content="Documentation for @homebridge-plugins/homebridge-firstalert"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><a href="../index.html" class="title">@homebridge-plugins/homebridge-firstalert</a><div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/><ul role="listbox" id="tsd-search-results"></ul><div id="tsd-search-status" aria-live="polite" aria-atomic="true"><div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><ul class="tsd-breadcrumb" aria-label="Breadcrumb"><li><a href="" aria-current="page">default</a></li></ul><h1>Variable default</h1></div><div class="tsd-signature"><span class="tsd-kind-variable">default</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">api</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">API</span><span class="tsd-signature-symbol">)</span> <span class="tsd-signature-symbol">=></span> <span class="tsd-signature-type">void</span></div><div class="tsd-type-declaration"><h4>Type Declaration</h4><ul class="tsd-parameters"><li class="tsd-parameter-signature"><ul class="tsd-signatures"><li class="tsd-signature" id="__type"><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">api</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">API</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">void</span></li><li class="tsd-description"><div class="tsd-parameters"><h4 class="tsd-parameters-title">Parameters</h4><ul class="tsd-parameter-list"><li><span><span class="tsd-kind-parameter">api</span>: <span class="tsd-signature-type">API</span></span></li></ul></div><h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">void</span></h4></li></ul></li></ul></div><aside class="tsd-sources"><ul><li>Defined in <a href="https://github.com/homebridge-plugins/homebridge-firstalert/blob/4e9b7a0dafe971e2a3d4653fe9b6c5b65ad9c47d/src/index.ts#L11">index.ts:11</a></li></ul></aside></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg><h3>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div></div><div class="site-menu"><nav class="tsd-navigation"><a href="../modules.html">@homebridge-plugins/homebridge-firstalert</a><ul class="tsd-small-nested-navigation" id="tsd-nav-container"><li>Loading...</li></ul></nav></div></div></div><footer></footer><div class="overlay"></div></body></html>
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import antfu from '@antfu/eslint-config'
|
|
2
|
+
|
|
3
|
+
export default antfu(
|
|
4
|
+
{
|
|
5
|
+
ignores: ['dist', 'docs'],
|
|
6
|
+
jsx: false,
|
|
7
|
+
typescript: true,
|
|
8
|
+
formatters: {
|
|
9
|
+
markdown: true,
|
|
10
|
+
},
|
|
11
|
+
rules: {
|
|
12
|
+
'curly': ['error', 'multi-line'],
|
|
13
|
+
'import/order': 0,
|
|
14
|
+
'jsdoc/check-alignment': 'error',
|
|
15
|
+
'jsdoc/check-line-alignment': 'error',
|
|
16
|
+
'no-undef': 'error',
|
|
17
|
+
'perfectionist/sort-exports': 'error',
|
|
18
|
+
'perfectionist/sort-imports': [
|
|
19
|
+
'error',
|
|
20
|
+
{
|
|
21
|
+
groups: [
|
|
22
|
+
'builtin',
|
|
23
|
+
'external',
|
|
24
|
+
'internal',
|
|
25
|
+
['parent', 'sibling', 'index'],
|
|
26
|
+
'unknown',
|
|
27
|
+
],
|
|
28
|
+
order: 'asc',
|
|
29
|
+
type: 'natural',
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
'perfectionist/sort-named-exports': 'error',
|
|
33
|
+
'perfectionist/sort-named-imports': 'error',
|
|
34
|
+
'sort-imports': 0,
|
|
35
|
+
'style/brace-style': ['error', '1tbs', { allowSingleLine: true }],
|
|
36
|
+
'style/quote-props': ['error', 'consistent-as-needed'],
|
|
37
|
+
'test/no-only-tests': 'error',
|
|
38
|
+
'unicorn/no-useless-spread': 'error',
|
|
39
|
+
'unused-imports/no-unused-vars': ['error', { caughtErrors: 'none' }],
|
|
40
|
+
'no-new': 0, // Disable the no-new rule
|
|
41
|
+
'new-cap': 0, // Disable the new-cap rule
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
)
|
package/nodemon.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@homebridge-plugins/homebridge-firstalert",
|
|
3
|
+
"displayName": "FirstAlert",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"version": "0.0.1-beta.0",
|
|
6
|
+
"description": "The FirstAlert plugin allows you to access your FirstAlert device(s) from HomeKit.",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "donavanbecker",
|
|
9
|
+
"url": "https://github.com/donavanbecker"
|
|
10
|
+
},
|
|
11
|
+
"maintainers": [
|
|
12
|
+
"donavanbecker"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"funding": [
|
|
19
|
+
{
|
|
20
|
+
"type": "Paypal",
|
|
21
|
+
"url": "https://paypal.me/donavanbecker"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"type": "GitHub",
|
|
25
|
+
"url": "https://github.com/sponsors/donavanbecker"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"homepage": "https://github.com/homebridge-plugins/homebridge-firstalert",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/homebridge-plugins/homebridge-firstalert.git"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/homebridge-plugins/homebridge-firstalert/issues"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"homebridge-plugin",
|
|
38
|
+
"honeywell",
|
|
39
|
+
"honeywell-home",
|
|
40
|
+
"thermostat",
|
|
41
|
+
"leaksensor",
|
|
42
|
+
"leak sensor",
|
|
43
|
+
"lcc",
|
|
44
|
+
"tcc",
|
|
45
|
+
"FirstAlert",
|
|
46
|
+
"First Alert",
|
|
47
|
+
"Resideo",
|
|
48
|
+
"honeywellhome",
|
|
49
|
+
"honeywell home",
|
|
50
|
+
"temperature",
|
|
51
|
+
"humidity",
|
|
52
|
+
"leak"
|
|
53
|
+
],
|
|
54
|
+
"main": "dist/index.js",
|
|
55
|
+
"engines": {
|
|
56
|
+
"homebridge": "^1.11.3 || ^2.0.0-beta.77",
|
|
57
|
+
"node": "^22 || ^24"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"check": "npm install && npm outdated",
|
|
61
|
+
"ports:free": "node scripts/free-dev-ports.mjs",
|
|
62
|
+
"typecheck": "tsc --noEmit",
|
|
63
|
+
"lint": "eslint src/**/*.ts test/**/*.ts src/homebridge-ui/**/*.html && npm run typecheck",
|
|
64
|
+
"lint:src": "eslint src/**/*.ts",
|
|
65
|
+
"lint:test": "eslint test/**/*.ts",
|
|
66
|
+
"lint:ui": "eslint src/homebridge-ui/**/*.html",
|
|
67
|
+
"lint:fix": "eslint src/**/*.ts test/**/*.ts src/homebridge-ui/**/*.html --fix && npm run typecheck",
|
|
68
|
+
"watch": "npm run ports:free && npm run build && npm run plugin-ui && npm link && nodemon",
|
|
69
|
+
"watch:ui": "npm run ports:free && bash -c 'npx homebridge -I -U -D ~/.homebridge-dev & HB_PID=$!; sleep 6; UIX_STORAGE_PATH=~/.homebridge-dev node ./node_modules/homebridge-config-ui-x/dist/bin/standalone.js & npm run watch:files'",
|
|
70
|
+
"watch:files": "nodemon",
|
|
71
|
+
"plugin-ui": "mkdir -p ./dist/homebridge-ui/public && if [ -d ./src/homebridge-ui/public ]; then rsync -a ./src/homebridge-ui/public/ ./dist/homebridge-ui/public/; fi",
|
|
72
|
+
"build": "npm run clean && tsc && npm run plugin-ui",
|
|
73
|
+
"prepublishOnly": "npm run lint && npm run build && npm run plugin-ui && npm run docs && npm run lint-docs",
|
|
74
|
+
"postpublish": "npm run clean && npm ci",
|
|
75
|
+
"clean": "shx rm -rf ./dist",
|
|
76
|
+
"test": "npm run lint",
|
|
77
|
+
"docs": "typedoc",
|
|
78
|
+
"lint-docs": "typedoc --emit none --treatWarningsAsErrors"
|
|
79
|
+
},
|
|
80
|
+
"dependencies": {
|
|
81
|
+
"@homebridge/plugin-ui-utils": "^2.2.2",
|
|
82
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
83
|
+
"@types/ms": "^2.1.0",
|
|
84
|
+
"class-validator": "^0.15.1",
|
|
85
|
+
"rxjs": "^7.8.1",
|
|
86
|
+
"undici": "^7.24.5"
|
|
87
|
+
},
|
|
88
|
+
"devDependencies": {
|
|
89
|
+
"@antfu/eslint-config": "^7.7.3",
|
|
90
|
+
"@types/debug": "^4.1.13",
|
|
91
|
+
"@types/node": "^25.5.0",
|
|
92
|
+
"@types/semver": "^7.7.1",
|
|
93
|
+
"@vitest/coverage-v8": "^4.1.0",
|
|
94
|
+
"esbuild": "^0.27.4",
|
|
95
|
+
"eslint": "^10.1.0",
|
|
96
|
+
"eslint-plugin-format": "^2.0.1",
|
|
97
|
+
"homebridge": "^1.11.3",
|
|
98
|
+
"homebridge-config-ui-x": "^5.20.0",
|
|
99
|
+
"nodemon": "^3.1.14",
|
|
100
|
+
"shx": "^0.4.0",
|
|
101
|
+
"ts-node": "^10.9.2",
|
|
102
|
+
"typedoc": "^0.28.18",
|
|
103
|
+
"typescript": "^5.9.3",
|
|
104
|
+
"vitest": "^4.1.0"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process'
|
|
2
|
+
import { readFileSync } from 'node:fs'
|
|
3
|
+
import { homedir } from 'node:os'
|
|
4
|
+
import { resolve } from 'node:path'
|
|
5
|
+
|
|
6
|
+
function resolvePorts() {
|
|
7
|
+
let bridgePort = 51829
|
|
8
|
+
let uiPort = 8581
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
const configPath = resolve(homedir(), '.homebridge-dev', 'config.json')
|
|
12
|
+
const config = JSON.parse(readFileSync(configPath, 'utf8'))
|
|
13
|
+
|
|
14
|
+
if (typeof config?.bridge?.port === 'number') {
|
|
15
|
+
bridgePort = config.bridge.port
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const configUiPlatform = Array.isArray(config?.platforms)
|
|
19
|
+
? config.platforms.find(platform => platform?.platform === 'config')
|
|
20
|
+
: undefined
|
|
21
|
+
|
|
22
|
+
if (typeof configUiPlatform?.port === 'number') {
|
|
23
|
+
uiPort = configUiPlatform.port
|
|
24
|
+
}
|
|
25
|
+
} catch {}
|
|
26
|
+
|
|
27
|
+
return [...new Set([bridgePort, uiPort])]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function pidsForPort(port) {
|
|
31
|
+
try {
|
|
32
|
+
const output = execSync(`lsof -ti tcp:${port}`, { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim()
|
|
33
|
+
return output ? output.split('\n').filter(Boolean) : []
|
|
34
|
+
} catch {
|
|
35
|
+
return []
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function freePort(port) {
|
|
40
|
+
const initialPids = pidsForPort(port)
|
|
41
|
+
if (initialPids.length === 0) {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
execSync(`lsof -ti tcp:${port} | xargs -r kill -TERM`, { stdio: ['ignore', 'ignore', 'ignore'] })
|
|
47
|
+
} catch {}
|
|
48
|
+
|
|
49
|
+
const remainingPids = pidsForPort(port)
|
|
50
|
+
if (remainingPids.length > 0) {
|
|
51
|
+
try {
|
|
52
|
+
execSync(`lsof -ti tcp:${port} | xargs -r kill -KILL`, { stdio: ['ignore', 'ignore', 'ignore'] })
|
|
53
|
+
} catch {}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function cleanupDevProcesses() {
|
|
58
|
+
let output = ''
|
|
59
|
+
try {
|
|
60
|
+
output = execSync('ps -axo pid=,command=', { stdio: ['ignore', 'pipe', 'ignore'] }).toString()
|
|
61
|
+
} catch {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const targetPids = output
|
|
66
|
+
.split('\n')
|
|
67
|
+
.map(line => line.trim())
|
|
68
|
+
.filter(Boolean)
|
|
69
|
+
.map((line) => {
|
|
70
|
+
const firstSpace = line.indexOf(' ')
|
|
71
|
+
if (firstSpace === -1) {
|
|
72
|
+
return null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const pid = Number(line.slice(0, firstSpace).trim())
|
|
76
|
+
const command = line.slice(firstSpace + 1)
|
|
77
|
+
return Number.isFinite(pid) ? { pid, command } : null
|
|
78
|
+
})
|
|
79
|
+
.filter((entry) => {
|
|
80
|
+
if (!entry) {
|
|
81
|
+
return false
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (entry.pid === process.pid || entry.pid === process.ppid) {
|
|
85
|
+
return false
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const isNodemonProcess = entry.command.includes('homebridge-switchbot/node_modules/.bin/nodemon')
|
|
89
|
+
const isConfigUiProcess = entry.command.trim().startsWith('homebridge-config-ui-x')
|
|
90
|
+
return isNodemonProcess || isConfigUiProcess
|
|
91
|
+
})
|
|
92
|
+
.map(entry => entry.pid)
|
|
93
|
+
|
|
94
|
+
for (const pid of targetPids) {
|
|
95
|
+
try {
|
|
96
|
+
process.kill(pid, 'SIGTERM')
|
|
97
|
+
} catch {}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
cleanupDevProcesses()
|
|
102
|
+
|
|
103
|
+
for (const port of resolvePorts()) {
|
|
104
|
+
freePort(port)
|
|
105
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// FirstAlert API client for OAuth and device polling
|
|
2
|
+
// Implements token refresh, account/device fetch, and strong typing
|
|
3
|
+
|
|
4
|
+
import { request } from 'undici'
|
|
5
|
+
|
|
6
|
+
export interface ResideoDevice {
|
|
7
|
+
id: string
|
|
8
|
+
name: string
|
|
9
|
+
deviceId: string
|
|
10
|
+
globalDeviceType: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ResideoAccount {
|
|
14
|
+
id: string
|
|
15
|
+
firstName: string
|
|
16
|
+
lastName: string
|
|
17
|
+
contactEmail: string
|
|
18
|
+
countryCode: string
|
|
19
|
+
locale: string
|
|
20
|
+
devices: ResideoDevice[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ResideoDeviceState {
|
|
24
|
+
name: string
|
|
25
|
+
deviceType: string
|
|
26
|
+
isOnline: boolean
|
|
27
|
+
deviceState: any
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class ResideoClient {
|
|
31
|
+
private clientId = 'SRmiA7CaYi1JgivDZdzzoZu4X5VBogGt'
|
|
32
|
+
private refreshToken: string
|
|
33
|
+
private accessToken: string | null = null
|
|
34
|
+
|
|
35
|
+
constructor(refreshToken: string) {
|
|
36
|
+
this.refreshToken = refreshToken
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async refreshAccessToken(): Promise<string> {
|
|
40
|
+
const body = JSON.stringify({
|
|
41
|
+
grant_type: 'refresh_token',
|
|
42
|
+
refresh_token: this.refreshToken,
|
|
43
|
+
client_id: this.clientId,
|
|
44
|
+
})
|
|
45
|
+
const { body: resBody } = await request('https://login.firstalert.com/oauth/token', {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
headers: { 'Content-Type': 'application/json' },
|
|
48
|
+
body,
|
|
49
|
+
})
|
|
50
|
+
const data = await resBody.json() as { access_token?: string }
|
|
51
|
+
this.accessToken = data.access_token ?? ''
|
|
52
|
+
if (!this.accessToken) {
|
|
53
|
+
throw new Error('Failed to obtain access token from FirstAlert API')
|
|
54
|
+
}
|
|
55
|
+
return this.accessToken
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async getAccessToken(): Promise<string> {
|
|
59
|
+
if (!this.accessToken) {
|
|
60
|
+
await this.refreshAccessToken()
|
|
61
|
+
}
|
|
62
|
+
return this.accessToken!
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async getAccount(): Promise<ResideoAccount> {
|
|
66
|
+
const token = await this.getAccessToken()
|
|
67
|
+
const { body: resBody } = await request('https://api.firstalert.com/ris-public-api/api/v1/accounts', {
|
|
68
|
+
method: 'GET',
|
|
69
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
70
|
+
})
|
|
71
|
+
const resp = await resBody.json() as { data: any }
|
|
72
|
+
// Parse and flatten devices
|
|
73
|
+
const data = resp.data
|
|
74
|
+
const devices: ResideoDevice[] = []
|
|
75
|
+
for (const user of data.consumerUsers) {
|
|
76
|
+
for (const loc of user.consumerAccount.locations) {
|
|
77
|
+
for (const dev of loc.consumerDevices) {
|
|
78
|
+
devices.push({
|
|
79
|
+
id: dev.id,
|
|
80
|
+
name: dev.name,
|
|
81
|
+
deviceId: dev.device.deviceId,
|
|
82
|
+
globalDeviceType: dev.device.globalDeviceType,
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
id: data.id,
|
|
89
|
+
firstName: data.firstName,
|
|
90
|
+
lastName: data.lastName,
|
|
91
|
+
contactEmail: data.contactEmail,
|
|
92
|
+
countryCode: data.countryCode,
|
|
93
|
+
locale: data.locale,
|
|
94
|
+
devices,
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async getDeviceState(deviceId: string): Promise<ResideoDeviceState> {
|
|
99
|
+
const token = await this.getAccessToken()
|
|
100
|
+
const { body: resBody } = await request(`https://api.firstalert.com/ris-public-api/api/v2/devices/smokeDetectors/${deviceId}/state`, {
|
|
101
|
+
method: 'GET',
|
|
102
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
103
|
+
})
|
|
104
|
+
return await resBody.json() as ResideoDeviceState
|
|
105
|
+
}
|
|
106
|
+
}
|