@switchbot/homebridge-switchbot 5.0.0 → 5.1.0-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/.github/npm-version-script-esm.js +97 -0
- package/CHANGELOG.md +21 -8
- package/MIGRATION.md +5 -0
- package/README.md +25 -0
- package/config.schema.json +11 -0
- package/dist/SwitchBotHAPPlatform.d.ts.map +1 -1
- package/dist/SwitchBotHAPPlatform.js +33 -4
- package/dist/SwitchBotHAPPlatform.js.map +1 -1
- package/dist/SwitchBotMatterPlatform.d.ts +1 -0
- package/dist/SwitchBotMatterPlatform.d.ts.map +1 -1
- package/dist/SwitchBotMatterPlatform.js +38 -5
- package/dist/SwitchBotMatterPlatform.js.map +1 -1
- package/dist/homebridge-ui/public/js/api.d.ts +6 -0
- package/dist/homebridge-ui/public/js/api.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/api.js +18 -0
- package/dist/homebridge-ui/public/js/api.js.map +1 -1
- package/dist/homebridge-ui/public/js/api.ts +31 -1
- package/dist/homebridge-ui/public/js/app.js +78 -2
- package/dist/homebridge-ui/public/js/app.js.map +2 -2
- package/dist/homebridge-ui/public/js/devices.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/devices.js +6 -0
- package/dist/homebridge-ui/public/js/devices.js.map +1 -1
- package/dist/homebridge-ui/public/js/devices.ts +6 -0
- package/dist/homebridge-ui/public/js/modals.d.ts +1 -0
- package/dist/homebridge-ui/public/js/modals.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/modals.js +53 -0
- package/dist/homebridge-ui/public/js/modals.js.map +1 -1
- package/dist/homebridge-ui/public/js/modals.ts +58 -0
- package/dist/homebridge-ui/public/js/render.d.ts.map +1 -1
- package/dist/homebridge-ui/public/js/render.js +2 -1
- package/dist/homebridge-ui/public/js/render.js.map +1 -1
- package/dist/homebridge-ui/public/js/render.ts +2 -1
- package/dist/homebridge-ui/settings.js.map +1 -1
- package/dist/settings.d.ts +11 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js.map +1 -1
- package/dist/utils.d.ts +5 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +27 -0
- package/dist/utils.js.map +1 -1
- package/docs/assets/highlight.css +15 -15
- package/docs/index.html +22 -5
- package/docs/variables/default.html +1 -1
- package/package.json +1 -1
- package/src/SwitchBotHAPPlatform.ts +31 -6
- package/src/SwitchBotMatterPlatform.ts +35 -7
- package/src/homebridge-ui/public/js/api.ts +31 -1
- package/src/homebridge-ui/public/js/devices.ts +6 -0
- package/src/homebridge-ui/public/js/modals.ts +58 -0
- package/src/homebridge-ui/public/js/render.ts +2 -1
- package/src/settings.ts +12 -0
- package/src/utils.ts +31 -0
- package/test/utils/utils.spec.ts +15 -0
package/docs/index.html
CHANGED
|
@@ -40,6 +40,23 @@
|
|
|
40
40
|
</ul>
|
|
41
41
|
<p>Click <strong>Save Advanced Settings</strong> to apply changes. These settings match the options available in <code>config.schema.json</code> and can be overridden per device.</p>
|
|
42
42
|
<!-- Optionally add a screenshot here -->
|
|
43
|
+
<h3 id="per-device-external-publishing-hap-or-matter" class="tsd-anchor-link">Per-Device External Publishing (HAP or Matter)<a href="#per-device-external-publishing-hap-or-matter" 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></h3>
|
|
44
|
+
<p>Each device now supports an <code>externalPublishProtocol</code> option to control whether it is published as an external accessory.</p>
|
|
45
|
+
<ul>
|
|
46
|
+
<li><code>none</code> (default): normal platform registration behavior.</li>
|
|
47
|
+
<li><code>hap</code>: publish this device as a HAP external accessory.</li>
|
|
48
|
+
<li><code>matter</code>: publish this device as a Matter external accessory.</li>
|
|
49
|
+
</ul>
|
|
50
|
+
<p>Behavior notes:</p>
|
|
51
|
+
<ul>
|
|
52
|
+
<li>This is configured per device in the Homebridge UI under <strong>Publish as External Device</strong> when adding or editing a device.</li>
|
|
53
|
+
<li>If the selected external publish API is not available on your Homebridge runtime, the plugin falls back to normal platform registration for that device.</li>
|
|
54
|
+
<li>Use <code>hap</code> for HAP-focused setups and <code>matter</code> for Matter-focused setups; leave as <code>none</code> unless you specifically want external accessory publishing.</li>
|
|
55
|
+
</ul>
|
|
56
|
+
<p>Example device config excerpt:</p>
|
|
57
|
+
<pre><code class="json"><span class="hl-0">{</span><br/><span class="hl-0"> </span><span class="hl-1">"deviceId"</span><span class="hl-0">: </span><span class="hl-2">"E7F8A1B2C3D4"</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-1">"configDeviceName"</span><span class="hl-0">: </span><span class="hl-2">"Bedroom Bot"</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-1">"configDeviceType"</span><span class="hl-0">: </span><span class="hl-2">"Bot"</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-1">"externalPublishProtocol"</span><span class="hl-0">: </span><span class="hl-2">"hap"</span><br/><span class="hl-0">}</span>
|
|
58
|
+
</code><button type="button">Copy</button></pre>
|
|
59
|
+
|
|
43
60
|
<ul>
|
|
44
61
|
<li>
|
|
45
62
|
<h3 id="if-using-openapi-connection" class="tsd-anchor-link">If using OpenAPI Connection<a href="#if-using-openapi-connection" 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></h3>
|
|
@@ -117,7 +134,7 @@
|
|
|
117
134
|
(This is what is intended in documentation for the noble bluetooth package <a href="https://github.com/abandonware/noble#prerequisites">prerequisites</a> by "Add terminal app", however for HomeBridge it is <code>node</code> that needs the permission granted, not <code>terminal</code>.
|
|
118
135
|
Without this step, then you will receive the following error when the swichbot plugin launches, which will cause Homebridge or the child bridge process to restart:</li>
|
|
119
136
|
</ol>
|
|
120
|
-
<pre><code><span class="hl-
|
|
137
|
+
<pre><code><span class="hl-3">Error</span><span class="hl-0">: </span><span class="hl-4">Failed</span><span class="hl-0"> </span><span class="hl-4">to</span><span class="hl-0"> </span><span class="hl-4">initialize</span><span class="hl-0"> </span><span class="hl-4">the</span><span class="hl-0"> </span><span class="hl-4">Noble</span><span class="hl-0"> </span><span class="hl-3">object</span><span class="hl-0">: </span><span class="hl-4">unauthorized</span><br/><span class="hl-0"> </span><span class="hl-4">at</span><span class="hl-0"> </span><span class="hl-4">Noble</span><span class="hl-0">.<</span><span class="hl-5">anonymous</span><span class="hl-0">> (</span><span class="hl-4">file</span><span class="hl-0">:</span><span class="hl-6">///usr/local/lib/node_modules/@switchbot/homebridge-switchbot/node_modules/node-switchbot/src/switchbot.ts:244:19)</span><br/><span class="hl-0"> </span><span class="hl-5">at</span><span class="hl-0"> </span><span class="hl-5">Object</span><span class="hl-0">.</span><span class="hl-5">onceWrapper</span><span class="hl-0"> (</span><span class="hl-4">node</span><span class="hl-0">:</span><span class="hl-5">events</span><span class="hl-0">:</span><span class="hl-7">629</span><span class="hl-0">:</span><span class="hl-7">26</span><span class="hl-0">)</span><br/><span class="hl-0"> </span><span class="hl-5">at</span><span class="hl-0"> </span><span class="hl-5">Noble</span><span class="hl-0">.</span><span class="hl-5">emit</span><span class="hl-0"> (</span><span class="hl-4">node</span><span class="hl-0">:</span><span class="hl-5">events</span><span class="hl-0">:</span><span class="hl-7">514</span><span class="hl-0">:</span><span class="hl-7">28</span><span class="hl-0">)</span><br/><span class="hl-0"> </span><span class="hl-5">at</span><span class="hl-0"> </span><span class="hl-5">Noble</span><span class="hl-0">.</span><span class="hl-5">onStateChange</span><span class="hl-0"> (/</span><span class="hl-5">usr</span><span class="hl-0">/</span><span class="hl-5">local</span><span class="hl-0">/</span><span class="hl-5">lib</span><span class="hl-0">/</span><span class="hl-5">node_modules</span><span class="hl-0">/@</span><span class="hl-5">switchbot</span><span class="hl-0">/</span><span class="hl-5">homebridge</span><span class="hl-0">-</span><span class="hl-5">switchbot</span><span class="hl-0">/</span><span class="hl-5">node_modules</span><span class="hl-0">/@</span><span class="hl-5">stoprocent</span><span class="hl-0">/</span><span class="hl-5">noble</span><span class="hl-0">/</span><span class="hl-5">lib</span><span class="hl-0">/</span><span class="hl-5">noble</span><span class="hl-0">.</span><span class="hl-5">js</span><span class="hl-0">:</span><span class="hl-7">92</span><span class="hl-0">:</span><span class="hl-7">8</span><span class="hl-0">)</span><br/><span class="hl-0"> </span><span class="hl-5">at</span><span class="hl-0"> </span><span class="hl-5">NobleMac</span><span class="hl-0">.</span><span class="hl-5">emit</span><span class="hl-0"> (</span><span class="hl-4">node</span><span class="hl-0">:</span><span class="hl-5">events</span><span class="hl-0">:</span><span class="hl-7">514</span><span class="hl-0">:</span><span class="hl-7">28</span><span class="hl-0">)</span>
|
|
121
138
|
</code><button>Copy</button></pre>
|
|
122
139
|
|
|
123
140
|
</li>
|
|
@@ -345,7 +362,7 @@ Without this step, then you will receive the following error when the swichbot p
|
|
|
345
362
|
<h4 id="how-to-configure-in-homebridge" class="tsd-anchor-link">How to Configure in Homebridge<a href="#how-to-configure-in-homebridge" 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></h4>
|
|
346
363
|
<p>In the Homebridge UI, when adding or editing a SwitchBot device, enter the <strong>Encryption Key</strong> and <strong>Key ID</strong> in the provided fields. These values will be securely used for BLE communication with your device.</p>
|
|
347
364
|
<p><strong>Example device config excerpt:</strong></p>
|
|
348
|
-
<pre><code class="json"><span class="hl-
|
|
365
|
+
<pre><code class="json"><span class="hl-0">{</span><br/><span class="hl-0"> </span><span class="hl-1">"deviceId"</span><span class="hl-0">: </span><span class="hl-2">"E7F8A1B2C3D4"</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-1">"deviceName"</span><span class="hl-0">: </span><span class="hl-2">"SwitchBot Lock"</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-1">"enableBLE"</span><span class="hl-0">: </span><span class="hl-8">true</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-1">"encryptionKey"</span><span class="hl-0">: </span><span class="hl-2">"0123456789abcdef0123456789abcdef"</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-1">"keyId"</span><span class="hl-0">: </span><span class="hl-2">"01"</span><br/><span class="hl-0">}</span>
|
|
349
366
|
</code><button type="button">Copy</button></pre>
|
|
350
367
|
|
|
351
368
|
<h4 id="which-devices-require-ble-encryption" class="tsd-anchor-link">Which Devices Require BLE Encryption?<a href="#which-devices-require-ble-encryption" 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></h4>
|
|
@@ -494,7 +511,7 @@ Without this step, then you will receive the following error when the swichbot p
|
|
|
494
511
|
<li><code>dailyApiResetLocalMidnight</code> (boolean, default false): if true, resets the daily counter at local midnight; when false, resets at UTC midnight.</li>
|
|
495
512
|
</ul>
|
|
496
513
|
<p>Example (excerpt):</p>
|
|
497
|
-
<pre><code class="json"><span class="hl-
|
|
514
|
+
<pre><code class="json"><span class="hl-0">{</span><br/><span class="hl-0"> </span><span class="hl-1">"options"</span><span class="hl-0">: {</span><br/><span class="hl-0"> </span><span class="hl-1">"dailyApiLimit"</span><span class="hl-0">: </span><span class="hl-7">10000</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-1">"dailyApiReserveForCommands"</span><span class="hl-0">: </span><span class="hl-7">1000</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-1">"webhookOnlyOnReserve"</span><span class="hl-0">: </span><span class="hl-8">false</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-1">"dailyApiResetLocalMidnight"</span><span class="hl-0">: </span><span class="hl-8">true</span><br/><span class="hl-0"> }</span><br/><span class="hl-0">}</span>
|
|
498
515
|
</code><button type="button">Copy</button></pre>
|
|
499
516
|
|
|
500
517
|
<h2 id="switchbot-apis" class="tsd-anchor-link">SwitchBot APIs<a href="#switchbot-apis" 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>
|
|
@@ -510,7 +527,7 @@ Without this step, then you will receive the following error when the swichbot p
|
|
|
510
527
|
<ul>
|
|
511
528
|
<li>
|
|
512
529
|
<p>Run unit tests:</p>
|
|
513
|
-
<pre><code class="bash"><span class="hl-9">npm</span><span class="hl-
|
|
530
|
+
<pre><code class="bash"><span class="hl-9">npm</span><span class="hl-0"> </span><span class="hl-2">run</span><span class="hl-0"> </span><span class="hl-2">test</span>
|
|
514
531
|
</code><button type="button">Copy</button></pre>
|
|
515
532
|
|
|
516
533
|
</li>
|
|
@@ -527,4 +544,4 @@ Without this step, then you will receive the following error when the swichbot p
|
|
|
527
544
|
<li><a href="https://www.facebook.com/SwitchBotRobot/">Facebook @SwitchBotRobot</a></li>
|
|
528
545
|
<li><a href="https://twitter.com/switchbot">Twitter @SwitchBot</a></li>
|
|
529
546
|
</ul>
|
|
530
|
-
</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="#switchbothomebridge-switchbot"><span>@switchbot/homebridge-<wbr/>switchbot</span></a><ul><li><a href="#installation"><span>Installation</span></a></li><li><a href="#configuration"><span>Configuration</span></a></li><li><ul><li><a href="#openapi-pollingrate-advanced-settings-ui"><span>Open<wbr/>API <wbr/>Polling/<wbr/>Rate <wbr/>Advanced <wbr/>Settings (<wbr/>UI)</span></a></li><li><a href="#if-using-openapi-connection"><span>If using <wbr/>Open<wbr/>API <wbr/>Connection</span></a></li><li><a href="#if-using-ble-connection"><span>If using <wbr/>BLE <wbr/>Connection</span></a></li></ul></li><li><a href="#troubleshooting"><span>Troubleshooting</span></a></li><li><ul><li><a href="#if-using-linux-raspberry-pi-os"><span>If using <wbr/>Linux / <wbr/>Raspberry <wbr/>Pi <wbr/>OS</span></a></li><li><a href="#if-using-macos"><span>If using <wbr/>Mac<wbr/>OS</span></a></li></ul></li><li><a href="#supported-switchbot-devices"><span>Supported <wbr/>Switch<wbr/>Bot <wbr/>Devices</span></a></li><li><a href="#ble-encryption-support"><span>BLE <wbr/>Encryption <wbr/>Support</span></a></li><li><ul><li><a href="#ble-encryption-key-and-key-id"><span>BLE <wbr/>Encryption <wbr/>Key and <wbr/>Key <wbr/>ID</span></a></li><li><ul><li><a href="#how-to-obtain-ble-encryption-key-and-key-id"><span>How to <wbr/>Obtain <wbr/>BLE <wbr/>Encryption <wbr/>Key and <wbr/>Key <wbr/>ID</span></a></li><li><a href="#how-to-configure-in-homebridge"><span>How to <wbr/>Configure in <wbr/>Homebridge</span></a></li><li><a href="#which-devices-require-ble-encryption"><span>Which <wbr/>Devices <wbr/>Require <wbr/>BLE <wbr/>Encryption?</span></a></li></ul></li></ul></li><li><a href="#supported-ir-devices"><span>Supported <wbr/>IR <wbr/>Devices</span></a></li><li><ul><li><a href="#all-ir-devices-require-switchbot-hub-2-switchbot-hub-mini-2-switchbot-hub-3-or-hub-mini"><span>(<wbr/>All <wbr/>IR <wbr/>Devices require <wbr/>Switch<wbr/>Bot <wbr/>Hub 2, <wbr/>Switch<wbr/>Bot <wbr/>Hub <wbr/>Mini 2, <wbr/>Switch<wbr/>Bot <wbr/>Hub 3, or <wbr/>Hub <wbr/>Mini)</span></a></li></ul></li><li><a href="#matter-platform"><span>Matter <wbr/>Platform</span></a></li><li><ul><li><a href="#batched-refresh-and-api-load-control"><span>Batched refresh and <wbr/>API load control</span></a></li></ul></li><li><a href="#whats-new-with-node-switchbot-v400"><span>What's new with node-<wbr/>switchbot v4.0.0</span></a></li><li><a href="#openapi-rate-limiting-and-daily-budget"><span>Open<wbr/>API rate limiting and daily budget</span></a></li><li><a href="#switchbot-apis"><span>Switch<wbr/>Bot <wbr/>AP<wbr/>Is</span></a></li><li><a href="#development-tests"><span>Development / <wbr/>Tests</span></a></li><li><a href="#community"><span>Community</span></a></li></ul></div></details></div><div class="site-menu"><nav class="tsd-navigation"><a href="modules.html">@switchbot/homebridge-switchbot</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>
|
|
547
|
+
</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="#switchbothomebridge-switchbot"><span>@switchbot/homebridge-<wbr/>switchbot</span></a><ul><li><a href="#installation"><span>Installation</span></a></li><li><a href="#configuration"><span>Configuration</span></a></li><li><ul><li><a href="#openapi-pollingrate-advanced-settings-ui"><span>Open<wbr/>API <wbr/>Polling/<wbr/>Rate <wbr/>Advanced <wbr/>Settings (<wbr/>UI)</span></a></li><li><a href="#per-device-external-publishing-hap-or-matter"><span>Per-<wbr/>Device <wbr/>External <wbr/>Publishing (<wbr/>HAP or <wbr/>Matter)</span></a></li><li><a href="#if-using-openapi-connection"><span>If using <wbr/>Open<wbr/>API <wbr/>Connection</span></a></li><li><a href="#if-using-ble-connection"><span>If using <wbr/>BLE <wbr/>Connection</span></a></li></ul></li><li><a href="#troubleshooting"><span>Troubleshooting</span></a></li><li><ul><li><a href="#if-using-linux-raspberry-pi-os"><span>If using <wbr/>Linux / <wbr/>Raspberry <wbr/>Pi <wbr/>OS</span></a></li><li><a href="#if-using-macos"><span>If using <wbr/>Mac<wbr/>OS</span></a></li></ul></li><li><a href="#supported-switchbot-devices"><span>Supported <wbr/>Switch<wbr/>Bot <wbr/>Devices</span></a></li><li><a href="#ble-encryption-support"><span>BLE <wbr/>Encryption <wbr/>Support</span></a></li><li><ul><li><a href="#ble-encryption-key-and-key-id"><span>BLE <wbr/>Encryption <wbr/>Key and <wbr/>Key <wbr/>ID</span></a></li><li><ul><li><a href="#how-to-obtain-ble-encryption-key-and-key-id"><span>How to <wbr/>Obtain <wbr/>BLE <wbr/>Encryption <wbr/>Key and <wbr/>Key <wbr/>ID</span></a></li><li><a href="#how-to-configure-in-homebridge"><span>How to <wbr/>Configure in <wbr/>Homebridge</span></a></li><li><a href="#which-devices-require-ble-encryption"><span>Which <wbr/>Devices <wbr/>Require <wbr/>BLE <wbr/>Encryption?</span></a></li></ul></li></ul></li><li><a href="#supported-ir-devices"><span>Supported <wbr/>IR <wbr/>Devices</span></a></li><li><ul><li><a href="#all-ir-devices-require-switchbot-hub-2-switchbot-hub-mini-2-switchbot-hub-3-or-hub-mini"><span>(<wbr/>All <wbr/>IR <wbr/>Devices require <wbr/>Switch<wbr/>Bot <wbr/>Hub 2, <wbr/>Switch<wbr/>Bot <wbr/>Hub <wbr/>Mini 2, <wbr/>Switch<wbr/>Bot <wbr/>Hub 3, or <wbr/>Hub <wbr/>Mini)</span></a></li></ul></li><li><a href="#matter-platform"><span>Matter <wbr/>Platform</span></a></li><li><ul><li><a href="#batched-refresh-and-api-load-control"><span>Batched refresh and <wbr/>API load control</span></a></li></ul></li><li><a href="#whats-new-with-node-switchbot-v400"><span>What's new with node-<wbr/>switchbot v4.0.0</span></a></li><li><a href="#openapi-rate-limiting-and-daily-budget"><span>Open<wbr/>API rate limiting and daily budget</span></a></li><li><a href="#switchbot-apis"><span>Switch<wbr/>Bot <wbr/>AP<wbr/>Is</span></a></li><li><a href="#development-tests"><span>Development / <wbr/>Tests</span></a></li><li><a href="#community"><span>Community</span></a></li></ul></div></details></div><div class="site-menu"><nav class="tsd-navigation"><a href="modules.html">@switchbot/homebridge-switchbot</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>
|
|
@@ -1,3 +1,3 @@
|
|
|
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 | @switchbot/homebridge-switchbot</title><meta name="description" content="Documentation for @switchbot/homebridge-switchbot"/><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">@switchbot/homebridge-switchbot</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-comment tsd-typography"><p>Registers the SwitchBot platform with Homebridge.</p>
|
|
2
2
|
</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><div class="tsd-comment tsd-typography"><p>The Homebridge API instance.</p>
|
|
3
|
-
</div></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/OpenWonderLabs/homebridge-switchbot/blob/
|
|
3
|
+
</div></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/OpenWonderLabs/homebridge-switchbot/blob/b3af65f017e09fd27d158673c1c37935372c3fd8/src/index.ts#L22">index.ts:22</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">@switchbot/homebridge-switchbot</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/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@switchbot/homebridge-switchbot",
|
|
3
3
|
"displayName": "SwitchBot",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "5.0.0",
|
|
5
|
+
"version": "5.1.0-beta.0",
|
|
6
6
|
"description": "The SwitchBot plugin allows you to access your SwitchBot device(s) from HomeKit.",
|
|
7
7
|
"author": "SwitchBot <support@wondertechlabs.com> (https://github.com/SwitchBot)",
|
|
8
8
|
"contributors": [
|
|
@@ -4,7 +4,7 @@ import type { API, Logger, PlatformConfig } from 'homebridge'
|
|
|
4
4
|
import { createDevice } from './deviceFactory.js'
|
|
5
5
|
import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js'
|
|
6
6
|
import { SwitchBotClient } from './switchbotClient.js'
|
|
7
|
-
import { normalizeTypeForMatter } from './utils.js'
|
|
7
|
+
import { normalizeTypeForMatter, resolveExternalPublishProtocol } from './utils.js'
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Homebridge platform class for SwitchBot HAP (HomeKit Accessory Protocol) integration.
|
|
@@ -140,7 +140,7 @@ export class SwitchBotHAPPlatform {
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
const devices = (this.config as any)?.devices ?? []
|
|
143
|
-
const createdDevices: { created: any, d: any, type: string }[] = []
|
|
143
|
+
const createdDevices: { created: any, d: any, type: string, externalPublishProtocol: 'none' | 'hap' | 'matter' }[] = []
|
|
144
144
|
for (const raw of devices) {
|
|
145
145
|
// Normalize config keys from UI schema to internal shape (for cross-platform consistency)
|
|
146
146
|
const d: any = {
|
|
@@ -152,12 +152,13 @@ export class SwitchBotHAPPlatform {
|
|
|
152
152
|
_raw: raw,
|
|
153
153
|
}
|
|
154
154
|
const type: string = normalizeTypeForMatter(d.type)
|
|
155
|
+
const externalPublishProtocol = resolveExternalPublishProtocol(raw)
|
|
155
156
|
const deviceOpts: any = { id: d.id, type, name: d.name, encryptionKey: d.encryptionKey, keyId: d.keyId, log: this.log }
|
|
156
157
|
this.log.debug(`[HAP/Debug] Device options for ${d.name ?? d.id}:`, JSON.stringify(deviceOpts, null, 2))
|
|
157
158
|
try {
|
|
158
159
|
const created = await createDevice(deviceOpts, this.config, false)
|
|
159
160
|
this.devices.push(created)
|
|
160
|
-
createdDevices.push({ created, d, type })
|
|
161
|
+
createdDevices.push({ created, d, type, externalPublishProtocol })
|
|
161
162
|
} catch (e: any) {
|
|
162
163
|
this.log.error(`Failed to create HAP device ${d.id}:`, e instanceof Error ? e.stack || e.message : e)
|
|
163
164
|
}
|
|
@@ -188,14 +189,15 @@ export class SwitchBotHAPPlatform {
|
|
|
188
189
|
*
|
|
189
190
|
* If the Homebridge HAP API is not available, registration is skipped and a log message is emitted.
|
|
190
191
|
*/
|
|
191
|
-
private async registerHAPAccessories(createdDevices: { created: any, d: any, type: string }[]): Promise<void> {
|
|
192
|
+
private async registerHAPAccessories(createdDevices: { created: any, d: any, type: string, externalPublishProtocol: 'none' | 'hap' | 'matter' }[]): Promise<void> {
|
|
192
193
|
if (!this.api || !(this.api as any).hap || typeof (this.api as any).hap.registerPlatformAccessories !== 'function') {
|
|
193
194
|
this.log.info('HAP API not available to register accessories')
|
|
194
195
|
return
|
|
195
196
|
}
|
|
196
197
|
const hap = (this.api as any).hap
|
|
197
198
|
const accessoriesToRegister: any[] = []
|
|
198
|
-
|
|
199
|
+
const externalAccessoriesToPublish: any[] = []
|
|
200
|
+
for (const { created, d, type, externalPublishProtocol } of createdDevices) {
|
|
199
201
|
try {
|
|
200
202
|
const uuid = hap.uuid.generate(`${d.id}`)
|
|
201
203
|
let accessory: any = this.accessories.get(uuid)
|
|
@@ -220,7 +222,11 @@ export class SwitchBotHAPPlatform {
|
|
|
220
222
|
} catch (e) {
|
|
221
223
|
// ignore context failures
|
|
222
224
|
}
|
|
223
|
-
|
|
225
|
+
if (externalPublishProtocol === 'hap') {
|
|
226
|
+
externalAccessoriesToPublish.push(accessory)
|
|
227
|
+
} else {
|
|
228
|
+
accessoriesToRegister.push(accessory)
|
|
229
|
+
}
|
|
224
230
|
this.accessories.set(uuid, accessory)
|
|
225
231
|
} else {
|
|
226
232
|
try {
|
|
@@ -298,6 +304,24 @@ export class SwitchBotHAPPlatform {
|
|
|
298
304
|
} else {
|
|
299
305
|
this.log.info('No HAP accessories to register')
|
|
300
306
|
}
|
|
307
|
+
if (externalAccessoriesToPublish.length > 0) {
|
|
308
|
+
if (typeof (this.api as any).publishExternalAccessories === 'function') {
|
|
309
|
+
try {
|
|
310
|
+
(this.api as any).publishExternalAccessories(PLUGIN_NAME, externalAccessoriesToPublish)
|
|
311
|
+
this.log.info(`Published ${externalAccessoriesToPublish.length} HAP external accessory(ies) with Homebridge`)
|
|
312
|
+
} catch (e) {
|
|
313
|
+
this.log.warn('Failed to publish HAP external accessories', e)
|
|
314
|
+
}
|
|
315
|
+
} else {
|
|
316
|
+
this.log.warn('HAP external accessory publishing API not available; falling back to regular platform registration')
|
|
317
|
+
try {
|
|
318
|
+
(this.api as any).registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, externalAccessoriesToPublish)
|
|
319
|
+
this.log.info(`Registered ${externalAccessoriesToPublish.length} HAP accessory(ies) as fallback (external publishing unavailable)`)
|
|
320
|
+
} catch (e) {
|
|
321
|
+
this.log.warn('Failed to register fallback HAP accessories', e)
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
301
325
|
}
|
|
302
326
|
|
|
303
327
|
/**
|
|
@@ -332,6 +356,7 @@ export class SwitchBotHAPPlatform {
|
|
|
332
356
|
id: d.deviceId ?? d.id,
|
|
333
357
|
type: d.configDeviceType ?? d.type,
|
|
334
358
|
name: d.configDeviceName ?? d.name,
|
|
359
|
+
externalPublishProtocol: resolveExternalPublishProtocol(d),
|
|
335
360
|
})))
|
|
336
361
|
}
|
|
337
362
|
|
|
@@ -4,7 +4,7 @@ import type { API, Logger, PlatformConfig } from 'homebridge'
|
|
|
4
4
|
import { createDevice } from './deviceFactory.js'
|
|
5
5
|
import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js'
|
|
6
6
|
import { SwitchBotClient } from './switchbotClient.js'
|
|
7
|
-
import { createMatterHandlers, DEVICE_MATTER_CLUSTERS, DEVICE_MATTER_SUPPORTED, normalizeTypeForMatter, resolveMatterDeviceType } from './utils.js'
|
|
7
|
+
import { createMatterHandlers, DEVICE_MATTER_CLUSTERS, DEVICE_MATTER_SUPPORTED, normalizeTypeForMatter, resolveExternalPublishProtocol, resolveMatterDeviceType } from './utils.js'
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Homebridge platform class for SwitchBot Matter integration.
|
|
@@ -113,7 +113,7 @@ export class SwitchBotMatterPlatform {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
const devices = (this.config as any)?.devices ?? []
|
|
116
|
-
const createdDevices: { created: any, d: any, type: string, useMatter: boolean, matterAvailable: boolean }[] = []
|
|
116
|
+
const createdDevices: { created: any, d: any, type: string, useMatter: boolean, matterAvailable: boolean, externalPublishProtocol: 'none' | 'hap' | 'matter' }[] = []
|
|
117
117
|
for (const raw of devices) {
|
|
118
118
|
// Normalize config keys from UI schema to internal shape (for cross-platform consistency)
|
|
119
119
|
const d: any = {
|
|
@@ -125,6 +125,7 @@ export class SwitchBotMatterPlatform {
|
|
|
125
125
|
_raw: raw,
|
|
126
126
|
}
|
|
127
127
|
const type: string = normalizeTypeForMatter(d.type)
|
|
128
|
+
const externalPublishProtocol = resolveExternalPublishProtocol(raw)
|
|
128
129
|
const deviceOpts: any = { id: d.id, type, name: d.name, encryptionKey: d.encryptionKey, keyId: d.keyId, log: this.log }
|
|
129
130
|
this.log.debug(`[Matter/Debug] Device options for ${d.name ?? d.id}:`, JSON.stringify(deviceOpts, null, 2))
|
|
130
131
|
const matterSupported = !!DEVICE_MATTER_SUPPORTED[(type || '').toLowerCase()]
|
|
@@ -134,7 +135,7 @@ export class SwitchBotMatterPlatform {
|
|
|
134
135
|
try {
|
|
135
136
|
const created = await createDevice(deviceOpts, this.config, useMatter)
|
|
136
137
|
this.devices.push(created)
|
|
137
|
-
createdDevices.push({ created, d, type, useMatter, matterAvailable })
|
|
138
|
+
createdDevices.push({ created, d, type, useMatter, matterAvailable, externalPublishProtocol })
|
|
138
139
|
if (useMatter) {
|
|
139
140
|
this.log.info(`Prepared Matter accessory for ${d.id} (${type})${matterAvailable ? ' (auto-detected)' : ' (manually enabled)'}`)
|
|
140
141
|
} else {
|
|
@@ -179,14 +180,15 @@ export class SwitchBotMatterPlatform {
|
|
|
179
180
|
* If the Homebridge Matter API is not available, registration is skipped and a log message is emitted.
|
|
180
181
|
* Accessories that are not enabled for Matter are ignored.
|
|
181
182
|
*/
|
|
182
|
-
async registerMatterAccessories(createdDevices: { created: any, d: any, type: string, useMatter: boolean, matterAvailable: boolean }[]): Promise<void> {
|
|
183
|
+
async registerMatterAccessories(createdDevices: { created: any, d: any, type: string, useMatter: boolean, matterAvailable: boolean, externalPublishProtocol: 'none' | 'hap' | 'matter' }[]): Promise<void> {
|
|
183
184
|
if (!this.api || !(this.api as any).matter || typeof (this.api as any).matter.registerPlatformAccessories !== 'function') {
|
|
184
185
|
this.log.info('Homebridge Matter API not available; skipping Matter accessory registration')
|
|
185
186
|
return
|
|
186
187
|
}
|
|
187
188
|
const matterApi = (this.api as any).matter
|
|
188
189
|
const accessoriesToRegister: any[] = []
|
|
189
|
-
|
|
190
|
+
const externalAccessoriesToPublish: any[] = []
|
|
191
|
+
for (const { created, d, type, useMatter, matterAvailable, externalPublishProtocol } of createdDevices) {
|
|
190
192
|
// Only register accessories where Matter is enabled and supported
|
|
191
193
|
if (!useMatter) {
|
|
192
194
|
// Log reason for skipping registration
|
|
@@ -237,7 +239,11 @@ export class SwitchBotMatterPlatform {
|
|
|
237
239
|
handlers: createdDesc.handlers || createMatterHandlers(this.log, d.id, type, (this.config as any)?._client) || undefined,
|
|
238
240
|
context: { deviceId: d.id, type, created: true },
|
|
239
241
|
}
|
|
240
|
-
|
|
242
|
+
if (externalPublishProtocol === 'matter') {
|
|
243
|
+
externalAccessoriesToPublish.push(accessory)
|
|
244
|
+
} else {
|
|
245
|
+
accessoriesToRegister.push(accessory)
|
|
246
|
+
}
|
|
241
247
|
this.accessories.set(uuid, accessory)
|
|
242
248
|
} else {
|
|
243
249
|
// Ensure context and update properties for restored accessory
|
|
@@ -260,7 +266,11 @@ export class SwitchBotMatterPlatform {
|
|
|
260
266
|
accessory.handlers = createdDesc.handlers || createMatterHandlers(this.log, d.id, type, (this.config as any)?._client) || undefined
|
|
261
267
|
accessory.displayName = createdDesc.name || d.name || type
|
|
262
268
|
accessory.UUID = accessory.UUID || accessory.uuid || uuid
|
|
263
|
-
|
|
269
|
+
if (externalPublishProtocol === 'matter') {
|
|
270
|
+
externalAccessoriesToPublish.push(accessory)
|
|
271
|
+
} else {
|
|
272
|
+
accessoriesToRegister.push(accessory)
|
|
273
|
+
}
|
|
264
274
|
this.accessories.set(accessory.UUID || uuid, accessory)
|
|
265
275
|
}
|
|
266
276
|
this.log.info(`Created/updated Matter accessory ${d.id} (${type})`)
|
|
@@ -278,6 +288,23 @@ export class SwitchBotMatterPlatform {
|
|
|
278
288
|
} else {
|
|
279
289
|
this.log.info('No Matter accessories to register')
|
|
280
290
|
}
|
|
291
|
+
if (externalAccessoriesToPublish.length > 0) {
|
|
292
|
+
try {
|
|
293
|
+
if (typeof matterApi.publishExternalAccessories === 'function') {
|
|
294
|
+
await matterApi.publishExternalAccessories(PLUGIN_NAME, externalAccessoriesToPublish)
|
|
295
|
+
this.log.info(`Published ${externalAccessoriesToPublish.length} Matter external accessory(ies) with Homebridge`)
|
|
296
|
+
} else if (typeof matterApi.registerExternalAccessories === 'function') {
|
|
297
|
+
await matterApi.registerExternalAccessories(PLUGIN_NAME, PLATFORM_NAME, externalAccessoriesToPublish)
|
|
298
|
+
this.log.info(`Registered ${externalAccessoriesToPublish.length} Matter external accessory(ies) with Homebridge`)
|
|
299
|
+
} else {
|
|
300
|
+
this.log.warn('Matter external accessory publishing API not available; falling back to regular platform registration')
|
|
301
|
+
await matterApi.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, externalAccessoriesToPublish)
|
|
302
|
+
this.log.info(`Registered ${externalAccessoriesToPublish.length} Matter accessory(ies) as fallback (external publishing unavailable)`)
|
|
303
|
+
}
|
|
304
|
+
} catch (e) {
|
|
305
|
+
this.log.warn('Failed to publish/register Matter external accessories', e)
|
|
306
|
+
}
|
|
307
|
+
}
|
|
281
308
|
}
|
|
282
309
|
|
|
283
310
|
/**
|
|
@@ -312,6 +339,7 @@ export class SwitchBotMatterPlatform {
|
|
|
312
339
|
id: d.deviceId ?? d.id,
|
|
313
340
|
type: d.configDeviceType ?? d.type,
|
|
314
341
|
name: d.configDeviceName ?? d.name,
|
|
342
|
+
externalPublishProtocol: resolveExternalPublishProtocol(d),
|
|
315
343
|
})))
|
|
316
344
|
}
|
|
317
345
|
|
|
@@ -189,7 +189,19 @@ export async function addDevice(
|
|
|
189
189
|
deviceId: string,
|
|
190
190
|
name: string,
|
|
191
191
|
type: string,
|
|
192
|
-
options?: {
|
|
192
|
+
options?: {
|
|
193
|
+
address?: string
|
|
194
|
+
model?: string
|
|
195
|
+
rssi?: number
|
|
196
|
+
connectionPreference?: string
|
|
197
|
+
externalPublishProtocol?: string
|
|
198
|
+
room?: string
|
|
199
|
+
encryptionKey?: string
|
|
200
|
+
keyId?: string
|
|
201
|
+
refreshRate?: number
|
|
202
|
+
blePollingEnabled?: boolean
|
|
203
|
+
blePollIntervalMs?: number
|
|
204
|
+
},
|
|
193
205
|
): Promise<any> {
|
|
194
206
|
if (typeof homebridge.getPluginConfig !== 'function' || typeof homebridge.updatePluginConfig !== 'function') {
|
|
195
207
|
throw new TypeError('Homebridge UI API not available')
|
|
@@ -224,6 +236,24 @@ export async function addDevice(
|
|
|
224
236
|
if (options?.keyId) {
|
|
225
237
|
newDevice.keyId = options.keyId
|
|
226
238
|
}
|
|
239
|
+
if (options?.connectionPreference) {
|
|
240
|
+
newDevice.connectionPreference = options.connectionPreference
|
|
241
|
+
}
|
|
242
|
+
if (options?.externalPublishProtocol) {
|
|
243
|
+
newDevice.externalPublishProtocol = options.externalPublishProtocol
|
|
244
|
+
}
|
|
245
|
+
if (options?.room) {
|
|
246
|
+
newDevice.room = options.room
|
|
247
|
+
}
|
|
248
|
+
if (typeof options?.refreshRate === 'number' && Number.isFinite(options.refreshRate)) {
|
|
249
|
+
newDevice.refreshRate = options.refreshRate
|
|
250
|
+
}
|
|
251
|
+
if (typeof options?.blePollingEnabled === 'boolean') {
|
|
252
|
+
newDevice.blePollingEnabled = options.blePollingEnabled
|
|
253
|
+
}
|
|
254
|
+
if (typeof options?.blePollIntervalMs === 'number' && Number.isFinite(options.blePollIntervalMs)) {
|
|
255
|
+
newDevice.blePollIntervalMs = options.blePollIntervalMs
|
|
256
|
+
}
|
|
227
257
|
config.devices.push(newDevice)
|
|
228
258
|
await homebridge.updatePluginConfig(configArr)
|
|
229
259
|
if (typeof homebridge.savePluginConfig === 'function') {
|
|
@@ -26,8 +26,14 @@ export async function addDeviceToConfig(device: any, options: { refresh?: boolea
|
|
|
26
26
|
address: importValues.address,
|
|
27
27
|
model: device.model,
|
|
28
28
|
rssi: device.rssi,
|
|
29
|
+
connectionPreference: importValues.connectionPreference,
|
|
30
|
+
externalPublishProtocol: importValues.externalPublishProtocol,
|
|
31
|
+
room: importValues.room,
|
|
29
32
|
encryptionKey: importValues.encryptionKey,
|
|
30
33
|
keyId: importValues.keyId,
|
|
34
|
+
refreshRate: importValues.refreshRate,
|
|
35
|
+
blePollingEnabled: importValues.blePollingEnabled,
|
|
36
|
+
blePollIntervalMs: importValues.blePollIntervalMs,
|
|
31
37
|
})
|
|
32
38
|
uiLog.info('Add device response:', resp)
|
|
33
39
|
|
|
@@ -6,6 +6,7 @@ export type ImportDiscoveredDeviceResult = {
|
|
|
6
6
|
configDeviceType: string
|
|
7
7
|
address?: string
|
|
8
8
|
connectionPreference?: string
|
|
9
|
+
externalPublishProtocol?: string
|
|
9
10
|
room?: string
|
|
10
11
|
encryptionKey?: string
|
|
11
12
|
keyId?: string
|
|
@@ -154,6 +155,30 @@ export async function importDiscoveredDevice(device: any): Promise<ImportDiscove
|
|
|
154
155
|
connectionPrefSelect.appendChild(opt)
|
|
155
156
|
})
|
|
156
157
|
|
|
158
|
+
// --- External Publish Protocol ---
|
|
159
|
+
const externalPublishLabel = document.createElement('label')
|
|
160
|
+
externalPublishLabel.textContent = 'Publish as External Device'
|
|
161
|
+
externalPublishLabel.style.display = 'block'
|
|
162
|
+
externalPublishLabel.style.marginBottom = '6px'
|
|
163
|
+
externalPublishLabel.style.fontWeight = '500'
|
|
164
|
+
externalPublishLabel.style.fontSize = '12px'
|
|
165
|
+
externalPublishLabel.style.color = '#6b7280'
|
|
166
|
+
|
|
167
|
+
const externalPublishSelect = document.createElement('select')
|
|
168
|
+
externalPublishSelect.style.width = '100%'
|
|
169
|
+
externalPublishSelect.style.marginBottom = '12px'
|
|
170
|
+
externalPublishSelect.style.padding = '8px 10px'
|
|
171
|
+
externalPublishSelect.style.borderRadius = '6px'
|
|
172
|
+
externalPublishSelect.style.fontSize = '14px'
|
|
173
|
+
externalPublishSelect.style.boxSizing = 'border-box'
|
|
174
|
+
;['none', 'hap', 'matter'].forEach((val) => {
|
|
175
|
+
const opt = document.createElement('option')
|
|
176
|
+
opt.value = val
|
|
177
|
+
opt.text = val === 'none' ? 'None' : val.toUpperCase()
|
|
178
|
+
opt.selected = (device.externalPublishProtocol || 'none') === val
|
|
179
|
+
externalPublishSelect.appendChild(opt)
|
|
180
|
+
})
|
|
181
|
+
|
|
157
182
|
// --- Room ---
|
|
158
183
|
const roomLabel = document.createElement('label')
|
|
159
184
|
roomLabel.textContent = 'Room'
|
|
@@ -308,6 +333,7 @@ export async function importDiscoveredDevice(device: any): Promise<ImportDiscove
|
|
|
308
333
|
configDeviceType: typeSelect.value || device.type,
|
|
309
334
|
address: macInput.value || undefined,
|
|
310
335
|
connectionPreference: connectionPrefSelect.value || undefined,
|
|
336
|
+
externalPublishProtocol: externalPublishSelect.value || 'none',
|
|
311
337
|
room: roomInput.value || undefined,
|
|
312
338
|
encryptionKey: encryptionKeyInput.value || undefined,
|
|
313
339
|
keyId: keyIdInput.value || undefined,
|
|
@@ -331,6 +357,8 @@ export async function importDiscoveredDevice(device: any): Promise<ImportDiscove
|
|
|
331
357
|
contentDiv.appendChild(typeSelect)
|
|
332
358
|
contentDiv.appendChild(connectionPrefLabel)
|
|
333
359
|
contentDiv.appendChild(connectionPrefSelect)
|
|
360
|
+
contentDiv.appendChild(externalPublishLabel)
|
|
361
|
+
contentDiv.appendChild(externalPublishSelect)
|
|
334
362
|
contentDiv.appendChild(roomLabel)
|
|
335
363
|
contentDiv.appendChild(roomInput)
|
|
336
364
|
contentDiv.appendChild(macLabel)
|
|
@@ -573,6 +601,30 @@ export async function editDevice(device: any): Promise<void> {
|
|
|
573
601
|
connectionPrefSelect.appendChild(opt)
|
|
574
602
|
})
|
|
575
603
|
|
|
604
|
+
// --- External Publish Protocol ---
|
|
605
|
+
const externalPublishLabel = document.createElement('label')
|
|
606
|
+
externalPublishLabel.textContent = 'Publish as External Device'
|
|
607
|
+
externalPublishLabel.style.display = 'block'
|
|
608
|
+
externalPublishLabel.style.marginBottom = '6px'
|
|
609
|
+
externalPublishLabel.style.fontWeight = '500'
|
|
610
|
+
externalPublishLabel.style.fontSize = '12px'
|
|
611
|
+
externalPublishLabel.style.color = '#6b7280'
|
|
612
|
+
|
|
613
|
+
const externalPublishSelect = document.createElement('select')
|
|
614
|
+
externalPublishSelect.style.width = '100%'
|
|
615
|
+
externalPublishSelect.style.marginBottom = '12px'
|
|
616
|
+
externalPublishSelect.style.padding = '8px 10px'
|
|
617
|
+
externalPublishSelect.style.borderRadius = '6px'
|
|
618
|
+
externalPublishSelect.style.fontSize = '14px'
|
|
619
|
+
externalPublishSelect.style.boxSizing = 'border-box'
|
|
620
|
+
;['none', 'hap', 'matter'].forEach((val) => {
|
|
621
|
+
const opt = document.createElement('option')
|
|
622
|
+
opt.value = val
|
|
623
|
+
opt.text = val === 'none' ? 'None' : val.toUpperCase()
|
|
624
|
+
opt.selected = (device.externalPublishProtocol || 'none') === val
|
|
625
|
+
externalPublishSelect.appendChild(opt)
|
|
626
|
+
})
|
|
627
|
+
|
|
576
628
|
// --- Room ---
|
|
577
629
|
const roomLabel = document.createElement('label')
|
|
578
630
|
roomLabel.textContent = 'Room'
|
|
@@ -674,6 +726,7 @@ export async function editDevice(device: any): Promise<void> {
|
|
|
674
726
|
configDeviceName: nameInput.value || undefined,
|
|
675
727
|
configDeviceType: typeSelect.value,
|
|
676
728
|
connectionPreference: connectionPrefSelect.value,
|
|
729
|
+
externalPublishProtocol: externalPublishSelect.value,
|
|
677
730
|
room: roomInput.value || undefined,
|
|
678
731
|
encryptionKey: encryptionKeyInput.value || undefined,
|
|
679
732
|
keyId: keyIdInput.value || undefined,
|
|
@@ -690,6 +743,9 @@ export async function editDevice(device: any): Promise<void> {
|
|
|
690
743
|
if (params.room !== undefined) {
|
|
691
744
|
options.room = params.room
|
|
692
745
|
}
|
|
746
|
+
if (params.externalPublishProtocol !== undefined) {
|
|
747
|
+
options.externalPublishProtocol = params.externalPublishProtocol
|
|
748
|
+
}
|
|
693
749
|
if (params.encryptionKey !== undefined) {
|
|
694
750
|
options.encryptionKey = params.encryptionKey
|
|
695
751
|
}
|
|
@@ -739,6 +795,8 @@ export async function editDevice(device: any): Promise<void> {
|
|
|
739
795
|
contentDiv.appendChild(typeSelect)
|
|
740
796
|
contentDiv.appendChild(connectionPrefLabel)
|
|
741
797
|
contentDiv.appendChild(connectionPrefSelect)
|
|
798
|
+
contentDiv.appendChild(externalPublishLabel)
|
|
799
|
+
contentDiv.appendChild(externalPublishSelect)
|
|
742
800
|
contentDiv.appendChild(roomLabel)
|
|
743
801
|
contentDiv.appendChild(roomInput)
|
|
744
802
|
contentDiv.appendChild(encryptionKeyLabel)
|
|
@@ -1011,8 +1011,9 @@ export function renderDeviceList(list: any[]): void {
|
|
|
1011
1011
|
|
|
1012
1012
|
const typeText = d.type ? `type: ${d.type}` : ''
|
|
1013
1013
|
const connText = d.connectionPreference ? `conn: ${d.connectionPreference}` : ''
|
|
1014
|
+
const extText = d.externalPublishProtocol && d.externalPublishProtocol !== 'none' ? `external: ${d.externalPublishProtocol}` : ''
|
|
1014
1015
|
const roomText = d.room ? `room: ${d.room}` : ''
|
|
1015
|
-
meta.textContent = [typeText, connText, roomText].filter(Boolean).join(' | ')
|
|
1016
|
+
meta.textContent = [typeText, connText, extText, roomText].filter(Boolean).join(' | ')
|
|
1016
1017
|
|
|
1017
1018
|
info.appendChild(nameContainer)
|
|
1018
1019
|
info.appendChild(meta)
|
package/src/settings.ts
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
export const PLUGIN_NAME = '@switchbot/homebridge-switchbot'
|
|
2
2
|
export const PLATFORM_NAME = 'SwitchBot'
|
|
3
3
|
|
|
4
|
+
export interface SwitchBotDeviceConfig {
|
|
5
|
+
deviceId?: string
|
|
6
|
+
id?: string
|
|
7
|
+
configDeviceName?: string
|
|
8
|
+
name?: string
|
|
9
|
+
configDeviceType?: string
|
|
10
|
+
type?: string
|
|
11
|
+
externalPublishProtocol?: 'none' | 'hap' | 'matter'
|
|
12
|
+
[key: string]: any
|
|
13
|
+
}
|
|
14
|
+
|
|
4
15
|
export interface SwitchBotPluginConfig {
|
|
5
16
|
openApiToken?: string
|
|
6
17
|
openApiSecret?: string
|
|
7
18
|
preferMatter?: boolean
|
|
8
19
|
enableMatter?: boolean
|
|
9
20
|
enableBLE?: boolean // Enable or disable BLE support
|
|
21
|
+
devices?: SwitchBotDeviceConfig[]
|
|
10
22
|
// other plugin-specific configuration
|
|
11
23
|
[key: string]: any
|
|
12
24
|
}
|