@switchbot/homebridge-switchbot 5.0.0-beta.3 → 5.0.0-beta.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +23 -3
- package/config.schema.json +70 -4
- package/dist/devices-hap/device.d.ts +1 -0
- package/dist/devices-hap/device.d.ts.map +1 -1
- package/dist/devices-hap/device.js +70 -30
- package/dist/devices-hap/device.js.map +1 -1
- package/dist/devices-matter/BaseMatterAccessory.d.ts +23 -0
- package/dist/devices-matter/BaseMatterAccessory.d.ts.map +1 -1
- package/dist/devices-matter/BaseMatterAccessory.js +167 -5
- package/dist/devices-matter/BaseMatterAccessory.js.map +1 -1
- package/dist/devices-matter/ColorLightAccessory.d.ts.map +1 -1
- package/dist/devices-matter/ColorLightAccessory.js +12 -12
- package/dist/devices-matter/ColorLightAccessory.js.map +1 -1
- package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts.map +1 -1
- package/dist/devices-matter/ColorTemperatureLightAccessory.js +5 -7
- package/dist/devices-matter/ColorTemperatureLightAccessory.js.map +1 -1
- package/dist/devices-matter/DimmableLightAccessory.js +9 -9
- package/dist/devices-matter/DimmableLightAccessory.js.map +1 -1
- package/dist/devices-matter/ExtendedColorLightAccessory.d.ts.map +1 -1
- package/dist/devices-matter/ExtendedColorLightAccessory.js +14 -15
- package/dist/devices-matter/ExtendedColorLightAccessory.js.map +1 -1
- package/dist/devices-matter/OnOffLightAccessory.d.ts.map +1 -1
- package/dist/devices-matter/OnOffLightAccessory.js +8 -16
- package/dist/devices-matter/OnOffLightAccessory.js.map +1 -1
- package/dist/devices-matter/OnOffOutletAccessory.d.ts +2 -0
- package/dist/devices-matter/OnOffOutletAccessory.d.ts.map +1 -1
- package/dist/devices-matter/OnOffOutletAccessory.js +10 -7
- package/dist/devices-matter/OnOffOutletAccessory.js.map +1 -1
- package/dist/devices-matter/OnOffSwitchAccessory.js +2 -2
- package/dist/devices-matter/OnOffSwitchAccessory.js.map +1 -1
- package/dist/homebridge-ui/public/index.html +48 -1
- package/dist/homebridge-ui/server.js +35 -0
- package/dist/homebridge-ui/server.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -5
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +7 -2
- package/dist/index.test.js.map +1 -1
- package/dist/irdevice/irdevice.d.ts +11 -10
- package/dist/irdevice/irdevice.d.ts.map +1 -1
- package/dist/irdevice/irdevice.js +76 -35
- package/dist/irdevice/irdevice.js.map +1 -1
- package/dist/platform-hap.d.ts +11 -14
- package/dist/platform-hap.d.ts.map +1 -1
- package/dist/platform-hap.js +64 -64
- package/dist/platform-hap.js.map +1 -1
- package/dist/platform-matter.d.ts +87 -6
- package/dist/platform-matter.d.ts.map +1 -1
- package/dist/platform-matter.js +1845 -84
- package/dist/platform-matter.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/test/hap/platform-hap.logging.test.d.ts +2 -0
- package/dist/test/hap/platform-hap.logging.test.d.ts.map +1 -0
- package/dist/test/hap/platform-hap.logging.test.js +33 -0
- package/dist/test/hap/platform-hap.logging.test.js.map +1 -0
- package/dist/test/hap/platform-hap.test.d.ts +2 -0
- package/dist/test/hap/platform-hap.test.d.ts.map +1 -0
- package/dist/test/hap/platform-hap.test.js +62 -0
- package/dist/test/hap/platform-hap.test.js.map +1 -0
- package/dist/test/helpers/platform-fixtures.d.ts +9 -0
- package/dist/test/helpers/platform-fixtures.d.ts.map +1 -0
- package/dist/test/helpers/platform-fixtures.js +30 -0
- package/dist/test/helpers/platform-fixtures.js.map +1 -0
- package/dist/test/matter/devices-matter/baseMatterAccessory.test.d.ts +2 -0
- package/dist/test/matter/devices-matter/baseMatterAccessory.test.d.ts.map +1 -0
- package/dist/test/matter/devices-matter/baseMatterAccessory.test.js +71 -0
- package/dist/test/matter/devices-matter/baseMatterAccessory.test.js.map +1 -0
- package/dist/test/matter/platform-matter.additional.test.d.ts +2 -0
- package/dist/test/matter/platform-matter.additional.test.d.ts.map +1 -0
- package/dist/test/matter/platform-matter.additional.test.js +35 -0
- package/dist/test/matter/platform-matter.additional.test.js.map +1 -0
- package/dist/test/matter/platform-matter.bleparse.test.d.ts +2 -0
- package/dist/test/matter/platform-matter.bleparse.test.d.ts.map +1 -0
- package/dist/test/matter/platform-matter.bleparse.test.js +43 -0
- package/dist/test/matter/platform-matter.bleparse.test.js.map +1 -0
- package/dist/test/matter/platform-matter.cleanup.test.d.ts +2 -0
- package/dist/test/matter/platform-matter.cleanup.test.d.ts.map +1 -0
- package/dist/test/matter/platform-matter.cleanup.test.js +70 -0
- package/dist/test/matter/platform-matter.cleanup.test.js.map +1 -0
- package/dist/test/matter/platform-matter.keepstale.test.d.ts +2 -0
- package/dist/test/matter/platform-matter.keepstale.test.d.ts.map +1 -0
- package/dist/test/matter/platform-matter.keepstale.test.js +27 -0
- package/dist/test/matter/platform-matter.keepstale.test.js.map +1 -0
- package/dist/test/matter/platform-matter.logging.test.d.ts +2 -0
- package/dist/test/matter/platform-matter.logging.test.d.ts.map +1 -0
- package/dist/test/matter/platform-matter.logging.test.js +29 -0
- package/dist/test/matter/platform-matter.logging.test.js.map +1 -0
- package/dist/test/matter/platform-matter.mapping.test.d.ts +2 -0
- package/dist/test/matter/platform-matter.mapping.test.d.ts.map +1 -0
- package/dist/test/matter/platform-matter.mapping.test.js +43 -0
- package/dist/test/matter/platform-matter.mapping.test.js.map +1 -0
- package/dist/test/matter/platform-matter.openapi-mapping.test.d.ts +2 -0
- package/dist/test/matter/platform-matter.openapi-mapping.test.d.ts.map +1 -0
- package/dist/test/matter/platform-matter.openapi-mapping.test.js +84 -0
- package/dist/test/matter/platform-matter.openapi-mapping.test.js.map +1 -0
- package/dist/test/matter/platform-matter.test.d.ts +2 -0
- package/dist/test/matter/platform-matter.test.d.ts.map +1 -0
- package/dist/test/matter/platform-matter.test.js +117 -0
- package/dist/test/matter/platform-matter.test.js.map +1 -0
- package/dist/test/matter/platform-matter.unregister.test.d.ts +2 -0
- package/dist/test/matter/platform-matter.unregister.test.d.ts.map +1 -0
- package/dist/test/matter/platform-matter.unregister.test.js +30 -0
- package/dist/test/matter/platform-matter.unregister.test.js.map +1 -0
- package/dist/utils.d.ts +127 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +405 -0
- package/dist/utils.js.map +1 -1
- package/dist/utils.test.d.ts +2 -0
- package/dist/utils.test.d.ts.map +1 -0
- package/dist/utils.test.js +95 -0
- package/dist/utils.test.js.map +1 -0
- package/dist/verifyconfig.test.js +2 -2
- package/dist/verifyconfig.test.js.map +1 -1
- package/docs/assets/main.js +2 -2
- package/docs/index.html +20 -2
- package/docs/variables/default.html +1 -1
- package/package.json +14 -14
- package/src/devices-hap/device.ts +68 -30
- package/src/devices-matter/BaseMatterAccessory.ts +168 -5
- package/src/devices-matter/ColorLightAccessory.ts +12 -12
- package/src/devices-matter/ColorTemperatureLightAccessory.ts +5 -7
- package/src/devices-matter/DimmableLightAccessory.ts +9 -9
- package/src/devices-matter/ExtendedColorLightAccessory.ts +14 -15
- package/src/devices-matter/OnOffLightAccessory.ts +8 -16
- package/src/devices-matter/OnOffOutletAccessory.ts +12 -7
- package/src/devices-matter/OnOffSwitchAccessory.ts +2 -2
- package/src/homebridge-ui/public/index.html +48 -1
- package/src/homebridge-ui/server.ts +37 -0
- package/src/index.test.ts +7 -2
- package/src/index.ts +4 -5
- package/src/irdevice/irdevice.ts +74 -35
- package/src/platform-hap.ts +68 -73
- package/src/platform-matter.ts +1879 -87
- package/src/settings.ts +15 -0
- package/src/test/hap/platform-hap.logging.test.ts +36 -0
- package/src/test/hap/platform-hap.test.ts +70 -0
- package/src/test/helpers/platform-fixtures.ts +33 -0
- package/src/test/matter/devices-matter/baseMatterAccessory.test.ts +88 -0
- package/src/test/matter/platform-matter.additional.test.ts +44 -0
- package/src/test/matter/platform-matter.bleparse.test.ts +47 -0
- package/src/test/matter/platform-matter.cleanup.test.ts +86 -0
- package/src/test/matter/platform-matter.keepstale.test.ts +37 -0
- package/src/test/matter/platform-matter.logging.test.ts +33 -0
- package/src/test/matter/platform-matter.mapping.test.ts +57 -0
- package/src/test/matter/platform-matter.openapi-mapping.test.ts +109 -0
- package/src/test/matter/platform-matter.test.ts +144 -0
- package/src/test/matter/platform-matter.unregister.test.ts +39 -0
- package/src/utils.test.ts +96 -0
- package/src/utils.ts +419 -3
- package/src/verifyconfig.test.ts +11 -10
package/docs/index.html
CHANGED
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
</ul>
|
|
61
61
|
<h2 id="troubleshooting" class="tsd-anchor-link">Troubleshooting<a href="#troubleshooting" 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><ul>
|
|
62
62
|
<li>
|
|
63
|
-
<h3 id="if-using-linux
|
|
63
|
+
<h3 id="if-using-linux-raspberry-pi-os" class="tsd-anchor-link">If using Linux / Raspberry Pi OS<a href="#if-using-linux-raspberry-pi-os" 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><ol>
|
|
64
64
|
<li>
|
|
65
65
|
<p><code>bluetoothctl</code> must be installed on the device, otherwise it cannot communicate via Bluetooth. Enable it with <code>sudo bluetoothctl power on</code>.</p>
|
|
66
66
|
</li>
|
|
@@ -373,6 +373,24 @@ Without this step, then you will receive the following error when the swichbot p
|
|
|
373
373
|
</ul>
|
|
374
374
|
</li>
|
|
375
375
|
</ul>
|
|
376
|
+
<h2 id="matter-platform" class="tsd-anchor-link">Matter Platform<a href="#matter-platform" 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><h3 id="batched-refresh-and-api-load-control" class="tsd-anchor-link">Batched refresh and API load control<a href="#batched-refresh-and-api-load-control" 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><p>By default, the Matter platform uses a single batched refresh to update device status. You can tune or override this behavior with the following options under <code>options</code>:</p>
|
|
377
|
+
<ul>
|
|
378
|
+
<li><code>matterBatchEnabled</code> (boolean, default true): enable/disable platform-level batched refresh. Devices with a per-device <code>refreshRate</code> still run their own timers.</li>
|
|
379
|
+
<li><code>matterBatchRefreshRate</code> (number, seconds): batch interval (falls back to <code>options.refreshRate</code>, then 300 if not set).</li>
|
|
380
|
+
<li><code>matterBatchConcurrency</code> (number): limit of parallel OpenAPI status calls during a batch (default 5).</li>
|
|
381
|
+
<li><code>matterBatchJitter</code> (number, seconds): random startup delay before the first batch to reduce synchronized spikes.</li>
|
|
382
|
+
</ul>
|
|
383
|
+
<p>Device-level override:</p>
|
|
384
|
+
<ul>
|
|
385
|
+
<li>If a device sets <code>refreshRate</code> in its config, it uses a per-device timer and is excluded from the platform batch.</li>
|
|
386
|
+
</ul>
|
|
387
|
+
<p>Reliability and rate-limiting:</p>
|
|
388
|
+
<ul>
|
|
389
|
+
<li>Each device status call retries with exponential backoff on non-success responses.</li>
|
|
390
|
+
<li>After exhausting retries, the device enters a short cooldown before being retried; cooldowns are persisted across restarts.</li>
|
|
391
|
+
<li>The batch worklist is randomized every cycle to further distribute API load.</li>
|
|
392
|
+
</ul>
|
|
393
|
+
<p>These controls keep API usage smooth and predictable while preserving per-device control when needed.</p>
|
|
376
394
|
<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><ul>
|
|
377
395
|
<li><a href="https://github.com/OpenWonderLabs/node-switchbot">OpenWonderLabs/node-switchbot</a>
|
|
378
396
|
<ul>
|
|
@@ -386,4 +404,4 @@ Without this step, then you will receive the following error when the swichbot p
|
|
|
386
404
|
<li><a href="https://www.facebook.com/SwitchBotRobot/">Facebook @SwitchBotRobot</a></li>
|
|
387
405
|
<li><a href="https://twitter.com/switchbot">Twitter @SwitchBot</a></li>
|
|
388
406
|
</ul>
|
|
389
|
-
</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="#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
|
|
407
|
+
</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="#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="#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-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 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="#switchbot-apis"><span>Switch<wbr/>Bot <wbr/>AP<wbr/>Is</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 +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-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/OpenWonderLabs/homebridge-switchbot/blob/
|
|
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-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/OpenWonderLabs/homebridge-switchbot/blob/43cdf5e76dd30e281e39cf30d76122f4297960db/src/index.ts#L13">index.ts:13</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-beta.
|
|
5
|
+
"version": "5.0.0-beta.31",
|
|
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": [
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"icon": "https://raw.githubusercontent.com/OpenWonderLabs/homebridge-switchbot/latest/branding/icon.png",
|
|
56
56
|
"engineStrict": true,
|
|
57
57
|
"engines": {
|
|
58
|
-
"homebridge": "^2.0.0-beta.
|
|
58
|
+
"homebridge": "^2.0.0-beta.39",
|
|
59
59
|
"node": "^20 || ^22 || ^24"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
@@ -81,31 +81,31 @@
|
|
|
81
81
|
"@homebridge/plugin-ui-utils": "^2.1.0",
|
|
82
82
|
"async-mqtt": "^2.6.3",
|
|
83
83
|
"fakegato-history": "^0.6.7",
|
|
84
|
-
"homebridge-lib": "^7.1.
|
|
84
|
+
"homebridge-lib": "^7.1.12",
|
|
85
85
|
"node-switchbot": "^3.6.0",
|
|
86
86
|
"rxjs": "^7.8.2"
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
|
89
|
-
"@antfu/eslint-config": "^
|
|
89
|
+
"@antfu/eslint-config": "^6.2.0",
|
|
90
90
|
"@types/aes-js": "^3.1.4",
|
|
91
91
|
"@types/debug": "^4.1.12",
|
|
92
92
|
"@types/fs-extra": "^11.0.4",
|
|
93
93
|
"@types/mdast": "^4.0.4",
|
|
94
|
-
"@types/node": "^24.
|
|
95
|
-
"@types/semver": "^7.7.
|
|
94
|
+
"@types/node": "^24.9.2",
|
|
95
|
+
"@types/semver": "^7.7.1",
|
|
96
96
|
"@types/source-map-support": "^0.5.10",
|
|
97
|
-
"@vitest/coverage-v8": "^
|
|
98
|
-
"eslint": "^9.
|
|
99
|
-
"eslint-plugin-format": "^1.0.
|
|
97
|
+
"@vitest/coverage-v8": "^4.0.6",
|
|
98
|
+
"eslint": "^9.38.0",
|
|
99
|
+
"eslint-plugin-format": "^1.0.2",
|
|
100
100
|
"eslint-plugin-import": "^2.32.0",
|
|
101
|
-
"homebridge": "^2.0.0-beta.
|
|
102
|
-
"homebridge-config-ui-x": "5.
|
|
101
|
+
"homebridge": "^2.0.0-beta.39",
|
|
102
|
+
"homebridge-config-ui-x": "^5.8.0",
|
|
103
103
|
"nodemon": "^3.1.10",
|
|
104
104
|
"shx": "^0.4.0",
|
|
105
105
|
"ts-node": "^10.9.2",
|
|
106
|
-
"typedoc": "^0.28.
|
|
107
|
-
"typescript": "^5.9.
|
|
108
|
-
"vitest": "^
|
|
106
|
+
"typedoc": "^0.28.14",
|
|
107
|
+
"typescript": "^5.9.3",
|
|
108
|
+
"vitest": "^4.0.6"
|
|
109
109
|
},
|
|
110
110
|
"directories": {
|
|
111
111
|
"doc": "docs"
|
|
@@ -288,11 +288,7 @@ export abstract class deviceBase {
|
|
|
288
288
|
this.device.bleMac = formattedDeviceId
|
|
289
289
|
this.debugLog(`bleMac: ${this.device.bleMac}`)
|
|
290
290
|
this.historyService = device.history
|
|
291
|
-
? new this.platform.fakegatoAPI('room', accessory, {
|
|
292
|
-
log: this.platform.log,
|
|
293
|
-
storage: 'fs',
|
|
294
|
-
filename: `${hostname().split('.')[0]}_${this.device.bleMac}_persist.json`,
|
|
295
|
-
})
|
|
291
|
+
? new this.platform.fakegatoAPI('room', accessory, { log: this.platform.log, storage: 'fs', filename: `${hostname().split('.')[0]}_${this.device.bleMac}_persist.json` })
|
|
296
292
|
: null
|
|
297
293
|
} catch (error) {
|
|
298
294
|
this.errorLog(`failed to format device ID as MAC, Error: ${error}`)
|
|
@@ -744,59 +740,101 @@ export abstract class deviceBase {
|
|
|
744
740
|
* Logging for Device
|
|
745
741
|
*/
|
|
746
742
|
infoLog(...log: any[]): void {
|
|
747
|
-
if (this.enablingDeviceLogging()) {
|
|
748
|
-
|
|
743
|
+
if (!this.enablingDeviceLogging()) {
|
|
744
|
+
return
|
|
749
745
|
}
|
|
746
|
+
this.logWith('info', `${this.device.deviceType}: ${this.accessory.displayName}`, undefined, String(...log))
|
|
750
747
|
}
|
|
751
748
|
|
|
752
749
|
successLog(...log: any[]): void {
|
|
753
|
-
if (this.enablingDeviceLogging()) {
|
|
754
|
-
|
|
750
|
+
if (!this.enablingDeviceLogging()) {
|
|
751
|
+
return
|
|
755
752
|
}
|
|
753
|
+
this.logWith('success', `${this.device.deviceType}: ${this.accessory.displayName}`, undefined, String(...log))
|
|
756
754
|
}
|
|
757
755
|
|
|
758
756
|
debugSuccessLog(...log: any[]): void {
|
|
759
|
-
if (this.enablingDeviceLogging()) {
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
757
|
+
if (!this.enablingDeviceLogging()) {
|
|
758
|
+
return
|
|
759
|
+
}
|
|
760
|
+
if (!this.loggingIsDebug()) {
|
|
761
|
+
return
|
|
763
762
|
}
|
|
763
|
+
this.logWith('success', `[DEBUG] ${this.device.deviceType}: ${this.accessory.displayName}`, 'debugSuccessLog', String(...log))
|
|
764
764
|
}
|
|
765
765
|
|
|
766
766
|
warnLog(...log: any[]): void {
|
|
767
|
-
if (this.enablingDeviceLogging()) {
|
|
768
|
-
|
|
767
|
+
if (!this.enablingDeviceLogging()) {
|
|
768
|
+
return
|
|
769
769
|
}
|
|
770
|
+
this.logWith('warn', `${this.device.deviceType}: ${this.accessory.displayName}`, undefined, String(...log))
|
|
770
771
|
}
|
|
771
772
|
|
|
772
773
|
debugWarnLog(...log: any[]): void {
|
|
773
|
-
if (this.enablingDeviceLogging()) {
|
|
774
|
-
|
|
775
|
-
this.log.warn(`[DEBUG] ${this.device.deviceType}: ${this.accessory.displayName}`, String(...log))
|
|
776
|
-
}
|
|
774
|
+
if (!this.enablingDeviceLogging() || !this.loggingIsDebug()) {
|
|
775
|
+
return
|
|
777
776
|
}
|
|
777
|
+
this.logWith('warn', `[DEBUG] ${this.device.deviceType}: ${this.accessory.displayName}`, 'debugWarnLog', String(...log))
|
|
778
778
|
}
|
|
779
779
|
|
|
780
780
|
errorLog(...log: any[]): void {
|
|
781
|
-
if (this.enablingDeviceLogging()) {
|
|
782
|
-
|
|
781
|
+
if (!this.enablingDeviceLogging()) {
|
|
782
|
+
return
|
|
783
783
|
}
|
|
784
|
+
this.logWith('error', `${this.device.deviceType}: ${this.accessory.displayName}`, undefined, String(...log))
|
|
784
785
|
}
|
|
785
786
|
|
|
786
787
|
debugErrorLog(...log: any[]): void {
|
|
787
|
-
if (this.enablingDeviceLogging()) {
|
|
788
|
-
|
|
789
|
-
this.log.error(`[DEBUG] ${this.device.deviceType}: ${this.accessory.displayName}`, String(...log))
|
|
790
|
-
}
|
|
788
|
+
if (!this.enablingDeviceLogging() || !this.loggingIsDebug()) {
|
|
789
|
+
return
|
|
791
790
|
}
|
|
791
|
+
this.logWith('error', `[DEBUG] ${this.device.deviceType}: ${this.accessory.displayName}`, 'debugErrorLog', String(...log))
|
|
792
792
|
}
|
|
793
793
|
|
|
794
794
|
debugLog(...log: any[]): void {
|
|
795
|
-
if (this.enablingDeviceLogging()) {
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
795
|
+
if (!this.enablingDeviceLogging()) {
|
|
796
|
+
return
|
|
797
|
+
}
|
|
798
|
+
// debug behaves differently depending on debug vs debugMode
|
|
799
|
+
if (this.deviceLogging === 'debug') {
|
|
800
|
+
this.logWith('debug', `[DEBUG] ${this.device.deviceType}: ${this.accessory.displayName}`, 'debugLog', String(...log))
|
|
801
|
+
} else if (this.deviceLogging === 'debugMode') {
|
|
802
|
+
this.logWith('debug', `${this.device.deviceType}: ${this.accessory.displayName}`, 'debugLog', String(...log))
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// Generic logger used by device log helpers. Attempts to call a platform
|
|
807
|
+
// helper method first (if present), then falls back to the local logger.
|
|
808
|
+
protected logWith(level: string, message: string, platformMethodName?: string, payload?: string): void {
|
|
809
|
+
const method = platformMethodName ?? `${level}Log`
|
|
810
|
+
const pFn = (this.platform as any)?.[method]
|
|
811
|
+
if (typeof pFn === 'function') {
|
|
812
|
+
try {
|
|
813
|
+
// Forward both a short message (title) and optional payload to the
|
|
814
|
+
// platform logger so it can format/attach metadata consistently.
|
|
815
|
+
if (payload !== undefined) {
|
|
816
|
+
pFn(message, payload)
|
|
817
|
+
} else {
|
|
818
|
+
pFn(message)
|
|
819
|
+
}
|
|
820
|
+
return
|
|
821
|
+
} catch (_err) {
|
|
822
|
+
// fallthrough to local logger
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
const map: Record<string, string> = {
|
|
826
|
+
info: 'info',
|
|
827
|
+
success: 'success',
|
|
828
|
+
debug: 'debug',
|
|
829
|
+
warn: 'warn',
|
|
830
|
+
error: 'error',
|
|
831
|
+
}
|
|
832
|
+
const local = (this.log as any)[map[level] ?? level]
|
|
833
|
+
if (typeof local === 'function') {
|
|
834
|
+
if (payload !== undefined) {
|
|
835
|
+
local.call(this.log, message, payload)
|
|
836
|
+
} else {
|
|
837
|
+
local.call(this.log, message)
|
|
800
838
|
}
|
|
801
839
|
}
|
|
802
840
|
}
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
import type { API, EndpointType, Logger, MatterAccessory } from 'homebridge'
|
|
12
12
|
|
|
13
|
+
import { rgb2hs } from '../utils.js'
|
|
14
|
+
|
|
13
15
|
export interface BaseMatterAccessoryConfig {
|
|
14
16
|
uuid: string
|
|
15
17
|
displayName: string
|
|
@@ -86,26 +88,187 @@ export abstract class BaseMatterAccessory implements MatterAccessory {
|
|
|
86
88
|
*/
|
|
87
89
|
protected async updateState(cluster: string, attributes: Record<string, unknown>): Promise<void> {
|
|
88
90
|
await this.api.matter.updateAccessoryState(this.uuid, cluster, attributes)
|
|
89
|
-
this.
|
|
91
|
+
this.logDebug(`Updated ${cluster} state:`, attributes)
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
/**
|
|
93
95
|
* Log helper methods
|
|
94
96
|
*/
|
|
97
|
+
// Generic logging delegation: prefer platform-provided log helpers in
|
|
98
|
+
// `this.context` (infoLog/debugLog/warnLog/errorLog) and fall back to the
|
|
99
|
+
// local `this.log` methods when not available.
|
|
100
|
+
protected logWith(level: 'info' | 'error' | 'debug' | 'warn', message: string, ...args: unknown[]): void {
|
|
101
|
+
const ctx: any = this.context as any
|
|
102
|
+
const map: Record<string, string> = {
|
|
103
|
+
info: 'infoLog',
|
|
104
|
+
error: 'errorLog',
|
|
105
|
+
debug: 'debugLog',
|
|
106
|
+
warn: 'warnLog',
|
|
107
|
+
}
|
|
108
|
+
const fn = ctx?.[map[level]]
|
|
109
|
+
if (typeof fn === 'function') {
|
|
110
|
+
fn(`[${this.displayName}] ${message}`, ...args)
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
const local = (this.log as any)[level]
|
|
114
|
+
if (typeof local === 'function') {
|
|
115
|
+
local.call(this.log, `[${this.displayName}] ${message}`, ...args)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
95
119
|
protected logInfo(message: string, ...args: unknown[]): void {
|
|
96
|
-
this.
|
|
120
|
+
this.logWith('info', message, ...args)
|
|
97
121
|
}
|
|
98
122
|
|
|
99
123
|
protected logError(message: string, ...args: unknown[]): void {
|
|
100
|
-
this.
|
|
124
|
+
this.logWith('error', message, ...args)
|
|
101
125
|
}
|
|
102
126
|
|
|
103
127
|
protected logDebug(message: string, ...args: unknown[]): void {
|
|
104
|
-
this.
|
|
128
|
+
this.logWith('debug', message, ...args)
|
|
105
129
|
}
|
|
106
130
|
|
|
107
131
|
protected logWarn(message: string, ...args: unknown[]): void {
|
|
108
|
-
this.
|
|
132
|
+
this.logWith('warn', message, ...args)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Logging helpers parity: allow Matter accessories to ask whether device-level
|
|
137
|
+
* logging is enabled or in debug mode. These mirror the helpers used by the
|
|
138
|
+
* HAP/IR device bases so behavior is consistent across platforms.
|
|
139
|
+
*/
|
|
140
|
+
public async loggingIsDebug(): Promise<boolean> {
|
|
141
|
+
const ctx: any = this.context as any
|
|
142
|
+
const deviceLogging = ctx?.deviceLogging
|
|
143
|
+
// deviceLogging may be a string ('debug', 'debugMode', 'standard') or undefined
|
|
144
|
+
return deviceLogging === 'debugMode' || deviceLogging === 'debug'
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public async enablingDeviceLogging(): Promise<boolean> {
|
|
148
|
+
const ctx: any = this.context as any
|
|
149
|
+
const deviceLogging = ctx?.deviceLogging
|
|
150
|
+
// If deviceLogging isn't provided, fall back to the platform-wide flag
|
|
151
|
+
// that indicates whether platform logging is enabled.
|
|
152
|
+
if (deviceLogging === undefined) {
|
|
153
|
+
return Boolean((ctx as any)?.platformLogging)
|
|
154
|
+
}
|
|
155
|
+
return deviceLogging === 'debugMode' || deviceLogging === 'debug' || deviceLogging === 'standard'
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Convenience helpers that delegate OpenAPI/BLE commands to platform-provided
|
|
160
|
+
* functions that are injected into the accessory `context` by
|
|
161
|
+
* `platform-matter` when the accessory is created from a discovered device.
|
|
162
|
+
*
|
|
163
|
+
* These methods are intentionally thin wrappers — the platform controls
|
|
164
|
+
* retries, discovery and client lifecycle via the helper functions.
|
|
165
|
+
*/
|
|
166
|
+
protected async sendOpenAPICommand(command: string, parameter = 'default'): Promise<any> {
|
|
167
|
+
const ctx: any = this.context as any
|
|
168
|
+
const fn = ctx?.sendOpenAPI
|
|
169
|
+
if (typeof fn === 'function') {
|
|
170
|
+
return fn(command, parameter)
|
|
171
|
+
}
|
|
172
|
+
throw new Error('OpenAPI helper not available')
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
protected async sendBLECommand(methodName: string, ...args: any[]): Promise<any> {
|
|
176
|
+
const ctx: any = this.context as any
|
|
177
|
+
const fn = ctx?.sendBLE
|
|
178
|
+
if (typeof fn === 'function') {
|
|
179
|
+
return fn(methodName, ...args)
|
|
180
|
+
}
|
|
181
|
+
throw new Error('BLE helper not available')
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
public async sendOnCommand(deviceId?: string): Promise<void> {
|
|
185
|
+
const ctx: any = this.context as any
|
|
186
|
+
const id = deviceId ?? ctx?.deviceId
|
|
187
|
+
try {
|
|
188
|
+
if (ctx?.connectionType === 'BLE') {
|
|
189
|
+
await this.sendBLECommand('turnOn')
|
|
190
|
+
} else {
|
|
191
|
+
await this.sendOpenAPICommand('turnOn')
|
|
192
|
+
}
|
|
193
|
+
// update our matter state
|
|
194
|
+
await this.updateState(this.api.matter.clusterNames.OnOff, { onOff: true })
|
|
195
|
+
this.logInfo(`sendOnCommand successful for ${id}`)
|
|
196
|
+
} catch (e: any) {
|
|
197
|
+
this.logError(`sendOnCommand failed for ${id}: ${String(e?.message ?? e)}`)
|
|
198
|
+
throw e
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
public async sendOffCommand(deviceId?: string): Promise<void> {
|
|
203
|
+
const ctx: any = this.context as any
|
|
204
|
+
const id = deviceId ?? ctx?.deviceId
|
|
205
|
+
try {
|
|
206
|
+
if (ctx?.connectionType === 'BLE') {
|
|
207
|
+
await this.sendBLECommand('turnOff')
|
|
208
|
+
} else {
|
|
209
|
+
await this.sendOpenAPICommand('turnOff')
|
|
210
|
+
}
|
|
211
|
+
await this.updateState(this.api.matter.clusterNames.OnOff, { onOff: false })
|
|
212
|
+
this.logInfo(`sendOffCommand successful for ${id}`)
|
|
213
|
+
} catch (e: any) {
|
|
214
|
+
this.logError(`sendOffCommand failed for ${id}: ${String(e?.message ?? e)}`)
|
|
215
|
+
throw e
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
public async sendSetBrightness(percent: number, deviceId?: string): Promise<void> {
|
|
220
|
+
const ctx: any = this.context as any
|
|
221
|
+
const id = deviceId ?? ctx?.deviceId
|
|
222
|
+
try {
|
|
223
|
+
if (ctx?.connectionType === 'BLE') {
|
|
224
|
+
await this.sendBLECommand('setBrightness', percent)
|
|
225
|
+
} else {
|
|
226
|
+
await this.sendOpenAPICommand('setBrightness', String(percent))
|
|
227
|
+
}
|
|
228
|
+
const level = Math.round((percent / 100) * 254)
|
|
229
|
+
await this.updateState(this.api.matter.clusterNames.LevelControl, { currentLevel: level })
|
|
230
|
+
this.logInfo(`sendSetBrightness successful for ${id}: ${percent}%`)
|
|
231
|
+
} catch (e: any) {
|
|
232
|
+
this.logError(`sendSetBrightness failed for ${id}: ${String(e?.message ?? e)}`)
|
|
233
|
+
throw e
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
public async sendSetColor(r: number, g: number, b: number, deviceId?: string): Promise<void> {
|
|
238
|
+
const ctx: any = this.context as any
|
|
239
|
+
const id = deviceId ?? ctx?.deviceId
|
|
240
|
+
try {
|
|
241
|
+
if (ctx?.connectionType === 'BLE') {
|
|
242
|
+
await this.sendBLECommand('setRGB', r, g, b)
|
|
243
|
+
} else {
|
|
244
|
+
await this.sendOpenAPICommand('setColor', `${r}:${g}:${b}`)
|
|
245
|
+
}
|
|
246
|
+
// Convert to hue/sat for matter state update
|
|
247
|
+
const [h, s] = rgb2hs(r, g, b)
|
|
248
|
+
await this.updateState(this.api.matter.clusterNames.ColorControl, { currentHue: Math.round((h / 360) * 254), currentSaturation: Math.round((s / 100) * 254) })
|
|
249
|
+
this.logInfo(`sendSetColor successful for ${id}: ${r},${g},${b}`)
|
|
250
|
+
} catch (e: any) {
|
|
251
|
+
this.logError(`sendSetColor failed for ${id}: ${String(e?.message ?? e)}`)
|
|
252
|
+
throw e
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
public async sendSetColorTemperature(kelvin: number, deviceId?: string): Promise<void> {
|
|
257
|
+
const ctx: any = this.context as any
|
|
258
|
+
const id = deviceId ?? ctx?.deviceId
|
|
259
|
+
try {
|
|
260
|
+
if (ctx?.connectionType === 'BLE') {
|
|
261
|
+
await this.sendBLECommand('setColorTemperature', kelvin)
|
|
262
|
+
} else {
|
|
263
|
+
await this.sendOpenAPICommand('setColorTemperature', `${kelvin}`)
|
|
264
|
+
}
|
|
265
|
+
const mireds = Math.round(1000000 / kelvin)
|
|
266
|
+
await this.updateState(this.api.matter.clusterNames.ColorControl, { colorTemperatureMireds: mireds })
|
|
267
|
+
this.logInfo(`sendSetColorTemperature successful for ${id}: ${kelvin}K`)
|
|
268
|
+
} catch (e: any) {
|
|
269
|
+
this.logError(`sendSetColorTemperature failed for ${id}: ${String(e?.message ?? e)}`)
|
|
270
|
+
throw e
|
|
271
|
+
}
|
|
109
272
|
}
|
|
110
273
|
|
|
111
274
|
/**
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import type { API, Logger, MatterRequests } from 'homebridge'
|
|
7
7
|
|
|
8
|
+
import { hs2rgb } from '../utils.js'
|
|
8
9
|
import { BaseMatterAccessory } from './BaseMatterAccessory.js'
|
|
9
10
|
|
|
10
11
|
export class ColorLightAccessory extends BaseMatterAccessory {
|
|
@@ -56,38 +57,37 @@ export class ColorLightAccessory extends BaseMatterAccessory {
|
|
|
56
57
|
|
|
57
58
|
private async handleOn(): Promise<void> {
|
|
58
59
|
this.logInfo('turning on.')
|
|
59
|
-
|
|
60
|
+
await this.sendOnCommand()
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
private async handleOff(): Promise<void> {
|
|
63
64
|
this.logInfo('turning off.')
|
|
64
|
-
|
|
65
|
+
await this.sendOffCommand()
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
private async handleSetLevel(request: MatterRequests.MoveToLevel): Promise<void> {
|
|
68
69
|
this.logInfo(`MoveToLevel request: ${JSON.stringify(request)}`)
|
|
69
70
|
const { level } = request
|
|
70
71
|
const brightnessPercent = Math.round((level / 254) * 100)
|
|
71
|
-
this.
|
|
72
|
-
// TODO: await myLightAPI.setBrightness(brightnessPercent)
|
|
72
|
+
await this.sendSetBrightness(brightnessPercent)
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
private async handleSetColor(request: MatterRequests.MoveToColor): Promise<void> {
|
|
76
76
|
this.logInfo(`MoveToColor request: ${JSON.stringify(request)}`)
|
|
77
|
-
const { colorX, colorY
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
const { colorX, colorY } = request
|
|
78
|
+
const hueApprox = Math.round((colorX / 65535) * 360)
|
|
79
|
+
const satApprox = Math.round((colorY / 65535) * 100)
|
|
80
|
+
const [r, g, b] = hs2rgb(hueApprox, satApprox)
|
|
81
|
+
await this.sendSetColor(r, g, b)
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
private async handleSetHueSaturation(request: MatterRequests.MoveToHueAndSaturation): Promise<void> {
|
|
85
85
|
this.logInfo(`MoveToHueAndSaturation request: ${JSON.stringify(request)}`)
|
|
86
|
-
const { hue, saturation
|
|
86
|
+
const { hue, saturation } = request
|
|
87
87
|
const hueDegrees = Math.round((hue / 254) * 360)
|
|
88
88
|
const saturationPercent = Math.round((saturation / 254) * 100)
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
const [r, g, b] = hs2rgb(hueDegrees, saturationPercent)
|
|
90
|
+
await this.sendSetColor(r, g, b)
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
public updateOnOffState(isOn: boolean): void {
|
|
@@ -52,28 +52,26 @@ export class ColorTemperatureLightAccessory extends BaseMatterAccessory {
|
|
|
52
52
|
|
|
53
53
|
private async handleOn(): Promise<void> {
|
|
54
54
|
this.logInfo('turning on.')
|
|
55
|
-
|
|
55
|
+
await this.sendOnCommand()
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
private async handleOff(): Promise<void> {
|
|
59
59
|
this.logInfo('turning off.')
|
|
60
|
-
|
|
60
|
+
await this.sendOffCommand()
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
private async handleSetLevel(request: MatterRequests.MoveToLevel): Promise<void> {
|
|
64
64
|
this.logInfo(`MoveToLevel request: ${JSON.stringify(request)}`)
|
|
65
65
|
const { level } = request
|
|
66
66
|
const brightnessPercent = Math.round((level / 254) * 100)
|
|
67
|
-
this.
|
|
68
|
-
// TODO: await myLightAPI.setBrightness(brightnessPercent)
|
|
67
|
+
await this.sendSetBrightness(brightnessPercent)
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
private async handleSetColorTemperature(request: MatterRequests.MoveToColorTemperature): Promise<void> {
|
|
72
71
|
this.logInfo(`MoveToColorTemperature request: ${JSON.stringify(request)}`)
|
|
73
|
-
const { colorTemperatureMireds
|
|
72
|
+
const { colorTemperatureMireds } = request
|
|
74
73
|
const kelvin = Math.round(1000000 / colorTemperatureMireds)
|
|
75
|
-
this.
|
|
76
|
-
// TODO: await myLightAPI.setColorTemperature(kelvin, transitionTime)
|
|
74
|
+
await this.sendSetColorTemperature(kelvin)
|
|
77
75
|
}
|
|
78
76
|
|
|
79
77
|
public updateOnOffState(isOn: boolean): void {
|
|
@@ -69,10 +69,10 @@ export class DimmableLightAccessory extends BaseMatterAccessory {
|
|
|
69
69
|
this.logInfo('turning on.')
|
|
70
70
|
|
|
71
71
|
try {
|
|
72
|
-
//
|
|
73
|
-
|
|
72
|
+
// Use platform helper (OpenAPI/BLE) when available
|
|
73
|
+
await this.sendOnCommand()
|
|
74
74
|
|
|
75
|
-
this.logInfo('physical device turned on.')
|
|
75
|
+
this.logInfo('physical device turned on (via platform helper).')
|
|
76
76
|
} catch (error) {
|
|
77
77
|
this.logError('failed to turn on:', error)
|
|
78
78
|
throw error
|
|
@@ -86,10 +86,10 @@ export class DimmableLightAccessory extends BaseMatterAccessory {
|
|
|
86
86
|
this.logInfo('turning off.')
|
|
87
87
|
|
|
88
88
|
try {
|
|
89
|
-
//
|
|
90
|
-
|
|
89
|
+
// Use platform helper (OpenAPI/BLE) when available
|
|
90
|
+
await this.sendOffCommand()
|
|
91
91
|
|
|
92
|
-
this.logInfo('physical device turned off.')
|
|
92
|
+
this.logInfo('physical device turned off (via platform helper).')
|
|
93
93
|
} catch (error) {
|
|
94
94
|
this.logError('failed to turn off:', error)
|
|
95
95
|
throw error
|
|
@@ -110,10 +110,10 @@ export class DimmableLightAccessory extends BaseMatterAccessory {
|
|
|
110
110
|
this.logInfo(`setting brightness to ${brightnessPercent}% (level: ${level}), transitionTime: ${transitionTime}.`)
|
|
111
111
|
|
|
112
112
|
try {
|
|
113
|
-
//
|
|
114
|
-
|
|
113
|
+
// Use platform helper (OpenAPI/BLE) when available
|
|
114
|
+
await this.sendSetBrightness(brightnessPercent)
|
|
115
115
|
|
|
116
|
-
this.logInfo(`physical device brightness set to ${brightnessPercent}
|
|
116
|
+
this.logInfo(`physical device brightness set to ${brightnessPercent}% (via platform helper).`)
|
|
117
117
|
} catch (error) {
|
|
118
118
|
this.logError('Failed to set brightness:', error)
|
|
119
119
|
throw error
|