iobroker.govee-smart 2.32.1 → 2.34.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/README.md +24 -19
- package/admin/custom/assets/index-C-fBGUPX.js +1 -0
- package/admin/custom/assets/{mf-entry-bootstrap-0-cd5e50f5.js → mf-entry-bootstrap-0-0502dc7c.js} +1 -1
- package/admin/custom/i18n/de.json +1 -1
- package/admin/custom/i18n/en.json +1 -1
- package/admin/custom/i18n/es.json +1 -1
- package/admin/custom/i18n/fr.json +1 -1
- package/admin/custom/i18n/it.json +1 -1
- package/admin/custom/i18n/nl.json +1 -1
- package/admin/custom/i18n/pl.json +1 -1
- package/admin/custom/i18n/pt.json +1 -1
- package/admin/custom/i18n/ru.json +1 -1
- package/admin/custom/i18n/uk.json +1 -1
- package/admin/custom/i18n/zh-cn.json +1 -1
- package/build/lib/capability-mapper.js +203 -52
- package/build/lib/capability-mapper.js.map +3 -3
- package/build/lib/command-router.js +6 -5
- package/build/lib/command-router.js.map +2 -2
- package/build/lib/device-manager/cloud-merge.js +8 -2
- package/build/lib/device-manager/cloud-merge.js.map +2 -2
- package/build/lib/device-manager/library-loader.js +3 -1
- package/build/lib/device-manager/library-loader.js.map +2 -2
- package/build/lib/device-manager/lookups.js +20 -0
- package/build/lib/device-manager/lookups.js.map +2 -2
- package/build/lib/device-manager.js +32 -4
- package/build/lib/device-manager.js.map +2 -2
- package/build/lib/device-registry.js +2 -1
- package/build/lib/device-registry.js.map +2 -2
- package/build/lib/diagnostics.js +10 -7
- package/build/lib/diagnostics.js.map +2 -2
- package/build/lib/govee-cloud-client.js +10 -7
- package/build/lib/govee-cloud-client.js.map +2 -2
- package/build/lib/govee-constants.js +3 -1
- package/build/lib/govee-constants.js.map +2 -2
- package/build/lib/govee-mqtt-client.js +2 -1
- package/build/lib/govee-mqtt-client.js.map +2 -2
- package/build/lib/handlers/cloud-state-loader.js +2 -6
- package/build/lib/handlers/cloud-state-loader.js.map +2 -2
- package/build/lib/handlers/dropdown-reset-helpers.js +3 -0
- package/build/lib/handlers/dropdown-reset-helpers.js.map +2 -2
- package/build/lib/handlers/state-change-router.js +69 -4
- package/build/lib/handlers/state-change-router.js.map +2 -2
- package/build/lib/state-manager.js +2 -2
- package/build/lib/state-manager.js.map +2 -2
- package/build/lib/types.js.map +2 -2
- package/build/main.js +9 -0
- package/build/main.js.map +2 -2
- package/devices.json +3396 -474
- package/io-package.json +27 -27
- package/package.json +5 -5
- package/admin/custom/assets/index-CStRjtPo.js +0 -1
package/README.md
CHANGED
|
@@ -103,6 +103,30 @@ This adapter's MQTT authentication and BLE-over-LAN (ptReal) protocol implementa
|
|
|
103
103
|
### **WORK IN PROGRESS**
|
|
104
104
|
-->
|
|
105
105
|
|
|
106
|
+
### 2.34.0 (2026-09-10)
|
|
107
|
+
|
|
108
|
+
- Fixed: Air purifiers, heaters, humidifiers and fans — choosing a mode or a speed now reaches the device, where the adapter used to send a value Govee rejected as "Invalid parameter type" (#47)
|
|
109
|
+
- Fixed: The speed selector of an air purifier now offers the levels the device actually has, instead of the single unusable entry it showed before (#47)
|
|
110
|
+
- Fixed: On an appliance updating from an older version the level datapoint accepts values again — it kept the selection list of the previous version and refused every write against it
|
|
111
|
+
- Changed: On appliances whose modes share the same level numbers — kettles, some fans and humidifiers — the level is a plain number now; a selection list could only ever show one mode's levels
|
|
112
|
+
- Fixed: An installation with no light at all now reads its device states at start — filter life, air quality and every other reported value stayed empty forever (#47)
|
|
113
|
+
- Fixed: A heater's target temperature is sent in the shape the Govee API asks for, and the datapoint is labelled in the unit the heater itself reports — a 5–30 °C heater used to read °F
|
|
114
|
+
- Fixed: The current speed level now arrives from the cloud together with the mode — until now only the mode updated while the level datapoint kept showing its default
|
|
115
|
+
- Fixed: A command the Govee cloud rejects no longer counts as successful, so the datapoint stops showing a change the device never made, and the reason is named
|
|
116
|
+
- New: A device's night-light scene is selectable — the adapter received the scene list and the current scene from Govee and threw both away without creating a datapoint
|
|
117
|
+
- Fixed: The scene dropdown's "---" entry now carries the same value the adapter writes when it resets the dropdown, so the entry stopped being rewritten on every start
|
|
118
|
+
- New: The H7127 air purifier is confirmed by a user report — it is no longer listed as untested and no longer asks for the experimental switch at start
|
|
119
|
+
- Changed: The diagnostics report no longer repeats the privacy note the export button already shows, and says instead what only the file itself can say
|
|
120
|
+
|
|
121
|
+
### 2.33.0 (2026-09-08)
|
|
122
|
+
|
|
123
|
+
- Fixed: A light without a local API stays reachable while it reports its own state — Govee's device list lagged behind the bulb and overrode it every two minutes (reported for the H600D)
|
|
124
|
+
- Fixed: A status message the Govee cloud replays after a reconnect no longer counts as a fresh sign of life for the next half hour
|
|
125
|
+
- New: The H600D GU10 smart bulb is recognised from a user report
|
|
126
|
+
- New: 486 more Govee models start as experimental — every model the homebridge-govee project lists as of September 2026, from bulbs and strips to fans, heaters and ice makers
|
|
127
|
+
- New: An experimental model is tried by enabling "experimental device support"; a diagnostics report from the Expert tab confirms it for everyone
|
|
128
|
+
- Changed: The wiki's device list folds each device type into one block with its counts, so 602 entries stay readable
|
|
129
|
+
|
|
106
130
|
### 2.32.1 (2026-09-07)
|
|
107
131
|
|
|
108
132
|
- Fixed: Your devices and their recorded history no longer disappear from the object tree when the Govee cloud cannot be reached at startup
|
|
@@ -124,25 +148,6 @@ This adapter's MQTT authentication and BLE-over-LAN (ptReal) protocol implementa
|
|
|
124
148
|
|
|
125
149
|
- Fixed: When the adapter met a device model it does not know yet, its log asked the user to press a button that 2.31.0 had removed — it now points at the Expert tab, where the report is actually made
|
|
126
150
|
|
|
127
|
-
### 2.31.0 (2026-09-03)
|
|
128
|
-
|
|
129
|
-
- Fixed: On instances upgraded from 2.27.0 or newer, every admin card was dead — diagnostics, segment wizard and connection test alike; affected installations repair themselves on the next start
|
|
130
|
-
- Fixed: A card that could not reach the adapter reported "no devices yet" instead of the real error
|
|
131
|
-
- Changed: Segment detection and diagnostics now share one **Expert** tab with a button each
|
|
132
|
-
- Changed: The per-device `diag.export` button is gone; the Expert tab builds the report and hands you the file in one press
|
|
133
|
-
- Changed: `diag.lastExport` now records WHEN the last report was taken, instead of naming the file
|
|
134
|
-
- Improved: Both cards say "Loading devices …" while they search, and explain the wait if it takes long
|
|
135
|
-
- Fixed: The diagnostics report still described the reachability rule as it was before 2.30.0
|
|
136
|
-
|
|
137
|
-
### 2.30.0 (2026-09-03)
|
|
138
|
-
|
|
139
|
-
- Fixed: Devices without a local API were shown as unreachable although they switched and reported normally; they now show as reachable for as long as they are
|
|
140
|
-
- Fixed: Sensors and buttons behind a Govee gateway were shown as unreachable although their readings kept arriving; their gateway now decides whether they are reachable
|
|
141
|
-
- Fixed: Appliances stayed marked as reachable for up to two minutes after they had actually gone offline
|
|
142
|
-
- Improved: A device that is unplugged and put away is reported as unreachable within half an hour, instead of staying green until the adapter is restarted
|
|
143
|
-
- New: 37 additional device models are recognised, including smart plugs, a button remote, an air quality monitor and an aroma diffuser
|
|
144
|
-
- New: Battery buttons and remotes are supported as their own device kind, with battery level and reachability
|
|
145
|
-
|
|
146
151
|
[Older changelogs can be found there](CHANGELOG_OLD.md)
|
|
147
152
|
|
|
148
153
|
## Support
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./virtual_mf-exposes___mfe_internal__ConfigCustomGoveeSegmentSet__customComponents_js-DtuKK_L7.js";import"./virtual_mf-REMOTE_ENTRY_ID___mfe_internal__ConfigCustomGoveeSegmentSet__customComponents_js-TmDuENES.js";import{$t as e,Ba as t,La as n,Qr as r,Ya as i,an as a,cn as o,ho as s,ya as c,za as l}from"./_virtual_mf___mfe_internal__ConfigCustomGoveeSegmentSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-BqaV2gc_.js";import{t as u}from"./_virtual_mf___mfe_internal__ConfigCustomGoveeSegmentSet__mf_owner__1__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-ld5_knuQ.js";import{n as d,t as f}from"./ExpertPanel-BnVeJBS6.js";(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})(),s();var p={gsw_deviceLabel:`Device`,gsw_noDevices:`No segment-capable devices found. Enter a Cloud API key in the adapter settings so device capabilities are known.`,gsw_start:`Start measuring`,gsw_measureHint:`Watch the strip — which segment lights up bright white?`,gsw_question:`Segment %s — is it lit?`,gsw_lit:`Lit`,gsw_dark:`Dark (gap)`,gsw_finish:`Finished — end of strip`,gsw_cancel:`Cancel`,gsw_reviewHint:`Click a cell to flip lit / gap, then apply.`,gsw_resultNoGaps:`%s segments, no gaps`,gsw_resultGaps:`%s segments, %s gap(s)`,gsw_apply:`Apply`,gsw_remeasure:`Measure again`,gsw_applied:`Applied: %s segments.`,gsw_error:`Wizard error: %s`,gsw_close:`Close`,gsw_conn_lan_title:`LAN — always on`,gsw_conn_lan_desc:`Local control for lights. No credentials needed.`,gsw_conn_cloud_title:`Cloud API key`,gsw_conn_cloud_desc:`Optional. Adds device names, scenes, snapshots and segments.`,gsw_conn_apikey_label:`Govee API key`,gsw_conn_rt_title:`Real-time status (Govee account)`,gsw_conn_rt_desc:`Optional. Enables live status push over the Govee cloud (AWS IoT).`,gsw_conn_email_label:`Account email`,gsw_conn_password_label:`Account password`,gsw_conn_connect_btn:`Connect`,gsw_conn_2fa_hint:`Govee sent a verification code by email. Enter it here to finish sign-in.`,gsw_conn_code_label:`Verification code`,gsw_conn_request_btn:`Request new code`,gsw_conn_verify_btn:`Confirm code`,gsw_conn_st_ok:`Connected. Save the settings to keep real-time status enabled.`,gsw_conn_st_verifyRequired:`Govee needs a verification code for this device. Check your email and enter the code.`,gsw_conn_st_codeInvalid:`The verification code is invalid. Request a new one and try again.`,gsw_conn_st_passwordRejected:`Email or password was rejected. Please check your credentials.`,gsw_conn_st_emailNotRegistered:`This email is not registered with Govee.`,gsw_conn_st_rateLimited:`Too many attempts. Wait a while before trying again.`,gsw_conn_st_accountLocked:`The account is temporarily locked by Govee. Try again later.`,gsw_conn_st_loginFailed:`Login failed. Please try again.`,gsw_conn_st_mqttNotUp:`Real-time service is not ready yet. Try again in a moment.`,gsw_conn_st_codeSent:`A new verification code was sent to your email.`,gsw_conn_st_codeRejected:`Govee rejected the verification code. Request a new one.`,gsw_conn_st_needCredentials:`Enter email and password first.`,gsw_conn_st_throttled:`Please wait a moment before requesting another code.`,gsw_conn_st_unknownAction:`Unknown action.`,gsw_diagIntro:`Pick a device and press the button: the adapter builds a diagnostics report and your browser saves it as a file. Attach that file to a GitHub issue — the issue forms ask for exactly it.`,gsw_diagDevice:`Device`,gsw_diagExport:`Create report and download`,gsw_diagDone:`Saved as %s.`,gsw_diagOpenIssue:`Open an issue`,gsw_diagNoDevices:`No devices yet. The report is per device — once the adapter has found one, it appears here.`,gsw_diagExportFailed:`The report could not be created. The adapter log says why.`,gsw_diagPrivacy:`The report is pseudonymised: addresses, mail addresses and device names are replaced by stable markers, device ids are shortened, and credentials never appear.`,gsw_loadingDevices:`Loading devices …`,gsw_loadingDevicesSlow:`Still loading — this needs the adapter instance to be running.`,gsw_listFailed:`Could not load the device list. Is the instance running?`,gsw_deviceOption:`%s — currently %s segments`,gsw_expertIntro:`Two tools for when something needs a closer look. Pick one — the device list loads when you do.`,gsw_expertWizard:`Segment detection`,gsw_expertDiagnostics:`Diagnostics`},m={gsw_deviceLabel:`Gerät`,gsw_noDevices:`Keine Geräte mit Segmenten gefunden. Trage einen Cloud-API-Key in den Adaptereinstellungen ein, damit die Gerätefähigkeiten bekannt sind.`,gsw_start:`Messung starten`,gsw_measureHint:`Beobachte den Strip — welches Segment leuchtet hell weiß auf?`,gsw_question:`Segment %s — leuchtet es?`,gsw_lit:`Leuchtet`,gsw_dark:`Dunkel (Lücke)`,gsw_finish:`Fertig — Strip zu Ende`,gsw_cancel:`Abbrechen`,gsw_reviewHint:`Zelle anklicken, um zwischen Leuchtet/Lücke zu wechseln, dann übernehmen.`,gsw_resultNoGaps:`%s Segmente, keine Lücke`,gsw_resultGaps:`%s Segmente, %s Lücke(n)`,gsw_apply:`Übernehmen`,gsw_remeasure:`Nochmal messen`,gsw_applied:`Übernommen: %s Segmente.`,gsw_error:`Wizard-Fehler: %s`,gsw_close:`Schließen`,gsw_conn_lan_title:`LAN — immer aktiv`,gsw_conn_lan_desc:`Lokale Steuerung für Lampen. Keine Zugangsdaten nötig.`,gsw_conn_cloud_title:`Cloud-API-Key`,gsw_conn_cloud_desc:`Optional. Ergänzt Gerätenamen, Szenen, Snapshots und Segmente.`,gsw_conn_apikey_label:`Govee-API-Key`,gsw_conn_rt_title:`Echtzeit-Status (Govee-Konto)`,gsw_conn_rt_desc:`Optional. Aktiviert Live-Status-Push über die Govee-Cloud (AWS IoT).`,gsw_conn_email_label:`Konto-E-Mail`,gsw_conn_password_label:`Konto-Passwort`,gsw_conn_connect_btn:`Verbinden`,gsw_conn_2fa_hint:`Govee hat einen Bestätigungscode per E-Mail geschickt. Zum Abschließen hier eingeben.`,gsw_conn_code_label:`Bestätigungscode`,gsw_conn_request_btn:`Neuen Code anfordern`,gsw_conn_verify_btn:`Code bestätigen`,gsw_conn_st_ok:`Verbunden. Zum dauerhaften Aktivieren die Einstellungen speichern.`,gsw_conn_st_verifyRequired:`Govee benötigt für dieses Gerät einen Bestätigungscode. E-Mail prüfen und Code eingeben.`,gsw_conn_st_codeInvalid:`Der Bestätigungscode ist ungültig. Neuen anfordern und erneut versuchen.`,gsw_conn_st_passwordRejected:`E-Mail oder Passwort wurde abgelehnt. Bitte Zugangsdaten prüfen.`,gsw_conn_st_emailNotRegistered:`Diese E-Mail ist bei Govee nicht registriert.`,gsw_conn_st_rateLimited:`Zu viele Versuche. Bitte etwas warten, bevor du es erneut versuchst.`,gsw_conn_st_accountLocked:`Das Konto ist vorübergehend von Govee gesperrt. Später erneut versuchen.`,gsw_conn_st_loginFailed:`Anmeldung fehlgeschlagen. Bitte erneut versuchen.`,gsw_conn_st_mqttNotUp:`Der Echtzeit-Dienst ist noch nicht bereit. Gleich erneut versuchen.`,gsw_conn_st_codeSent:`Ein neuer Bestätigungscode wurde an deine E-Mail geschickt.`,gsw_conn_st_codeRejected:`Govee hat den Bestätigungscode abgelehnt. Neuen anfordern.`,gsw_conn_st_needCredentials:`Zuerst E-Mail und Passwort eingeben.`,gsw_conn_st_throttled:`Bitte kurz warten, bevor du einen weiteren Code anforderst.`,gsw_conn_st_unknownAction:`Unbekannte Aktion.`,gsw_diagIntro:`Gerät wählen und auf den Knopf drücken: Der Adapter erstellt einen Diagnosebericht, und der Browser legt ihn als Datei ab. Diese Datei an ein GitHub-Issue anhängen — die Formulare fragen genau danach.`,gsw_diagDevice:`Gerät`,gsw_diagExport:`Bericht erstellen und herunterladen`,gsw_diagDone:`Gespeichert als %s.`,gsw_diagOpenIssue:`Issue eröffnen`,gsw_diagNoDevices:`Noch keine Geräte. Der Bericht gilt je Gerät — sobald der Adapter eines gefunden hat, erscheint es hier.`,gsw_diagExportFailed:`Der Bericht konnte nicht erstellt werden. Warum, steht im Adapter-Log.`,gsw_diagPrivacy:`Der Bericht ist anonymisiert: Adressen, E-Mail-Adressen und Gerätenamen werden durch gleichbleibende Marken ersetzt, Gerätekennungen gekürzt, Zugangsdaten tauchen gar nicht auf.`,gsw_loadingDevices:`Geräte werden geladen …`,gsw_loadingDevicesSlow:`Lädt noch — dafür muss die Adapter-Instanz laufen.`,gsw_listFailed:`Die Geräteliste konnte nicht geladen werden. Läuft die Instanz?`,gsw_deviceOption:`%s — bisher %s Segmente`,gsw_expertIntro:`Zwei Werkzeuge für den genaueren Blick. Eines auswählen — die Geräteliste wird dann geladen.`,gsw_expertWizard:`Segment-Erkennung`,gsw_expertDiagnostics:`Diagnose`},h={app:e=>({backgroundColor:e.palette.background.default,color:e.palette.text.primary,height:`100%`}),item:{padding:50,width:600}},g=class extends e{constructor(e){let t={...e};super(e,t),this.state={...this.state,data:{},originalData:{},theme:this.createTheme()},a.extendTranslations(p,`en`),a.extendTranslations(m,`de`);let n=(navigator.language||navigator.userLanguage||`en`).toLowerCase();a.setLanguage(n.substring(0,2))}render(){return this.state.loaded?l(n,{injectFirst:!0,children:l(c,{theme:this.state.theme,children:t(r,{sx:h.app,children:[l(`div`,{style:h.item,children:l(d,{socket:this.socket,namespace:`${this.adapterName}.${this.instance}`,values:{apiKey:String(this.state.data.apiKey??``),email:String(this.state.data.goveeEmail??``),password:String(this.state.data.goveePassword??``),code:String(this.state.data.mqttVerificationCode??``)},onChange:(e,t)=>this.setState({data:{...this.state.data,[e]:t}})})}),l(`div`,{style:h.item,children:l(f,{socket:this.socket,namespace:`${this.adapterName}.${this.instance}`})})]})})}):l(n,{injectFirst:!0,children:l(c,{theme:this.state.theme,children:l(o,{themeType:this.state.themeType})})})}};s(),window.adapterName=`govee-smart`;var _=document.getElementById(`root`);_&&u(_).render(l(i.StrictMode,{children:l(g,{socket:{port:8081}})}));
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"gsw_diagOpenIssue": "Issue eröffnen",
|
|
52
52
|
"gsw_diagNoDevices": "Noch keine Geräte. Der Bericht gilt je Gerät — sobald der Adapter eines gefunden hat, erscheint es hier.",
|
|
53
53
|
"gsw_diagExportFailed": "Der Bericht konnte nicht erstellt werden. Warum, steht im Adapter-Log.",
|
|
54
|
-
"gsw_diagPrivacy": "Der Bericht ist anonymisiert: Adressen, E-Mail-Adressen und Gerätenamen werden durch gleichbleibende Marken ersetzt, Gerätekennungen gekürzt, Zugangsdaten tauchen gar nicht auf.
|
|
54
|
+
"gsw_diagPrivacy": "Der Bericht ist anonymisiert: Adressen, E-Mail-Adressen und Gerätenamen werden durch gleichbleibende Marken ersetzt, Gerätekennungen gekürzt, Zugangsdaten tauchen gar nicht auf.",
|
|
55
55
|
"gsw_loadingDevices": "Geräte werden geladen …",
|
|
56
56
|
"gsw_loadingDevicesSlow": "Lädt noch — dafür muss die Adapter-Instanz laufen.",
|
|
57
57
|
"gsw_listFailed": "Die Geräteliste konnte nicht geladen werden. Läuft die Instanz?",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"gsw_diagOpenIssue": "Open an issue",
|
|
52
52
|
"gsw_diagNoDevices": "No devices yet. The report is per device — once the adapter has found one, it appears here.",
|
|
53
53
|
"gsw_diagExportFailed": "The report could not be created. The adapter log says why.",
|
|
54
|
-
"gsw_diagPrivacy": "The report is pseudonymised: addresses, mail addresses and device names are replaced by stable markers, device ids are shortened, and credentials never appear.
|
|
54
|
+
"gsw_diagPrivacy": "The report is pseudonymised: addresses, mail addresses and device names are replaced by stable markers, device ids are shortened, and credentials never appear.",
|
|
55
55
|
"gsw_loadingDevices": "Loading devices …",
|
|
56
56
|
"gsw_loadingDevicesSlow": "Still loading — this needs the adapter instance to be running.",
|
|
57
57
|
"gsw_listFailed": "Could not load the device list. Is the instance running?",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"gsw_diagOpenIssue": "Abrir una issue",
|
|
52
52
|
"gsw_diagNoDevices": "Aún no hay dispositivos. El informe es por dispositivo: en cuanto el adaptador encuentre uno, aparecerá aquí.",
|
|
53
53
|
"gsw_diagExportFailed": "No se pudo crear el informe. El registro del adaptador explica por qué.",
|
|
54
|
-
"gsw_diagPrivacy": "El informe está seudonimizado: direcciones, direcciones de correo y nombres de dispositivos se sustituyen por marcadores estables, los identificadores se acortan y las credenciales nunca aparecen.
|
|
54
|
+
"gsw_diagPrivacy": "El informe está seudonimizado: direcciones, direcciones de correo y nombres de dispositivos se sustituyen por marcadores estables, los identificadores se acortan y las credenciales nunca aparecen.",
|
|
55
55
|
"gsw_loadingDevices": "Cargando dispositivos …",
|
|
56
56
|
"gsw_loadingDevicesSlow": "Sigue cargando: la instancia del adaptador debe estar en ejecución.",
|
|
57
57
|
"gsw_listFailed": "No se pudo cargar la lista de dispositivos. ¿Está la instancia en ejecución?",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"gsw_diagOpenIssue": "Ouvrir une issue",
|
|
52
52
|
"gsw_diagNoDevices": "Aucun appareil pour l'instant. Le rapport est par appareil — dès que l'adaptateur en trouve un, il apparaît ici.",
|
|
53
53
|
"gsw_diagExportFailed": "Le rapport n'a pas pu être créé. Le journal de l'adaptateur en donne la raison.",
|
|
54
|
-
"gsw_diagPrivacy": "Le rapport est pseudonymisé : adresses, adresses mail et noms d'appareils sont remplacés par des marqueurs stables, les identifiants sont raccourcis et les identifiants de connexion n'y figurent jamais.
|
|
54
|
+
"gsw_diagPrivacy": "Le rapport est pseudonymisé : adresses, adresses mail et noms d'appareils sont remplacés par des marqueurs stables, les identifiants sont raccourcis et les identifiants de connexion n'y figurent jamais.",
|
|
55
55
|
"gsw_loadingDevices": "Chargement des appareils …",
|
|
56
56
|
"gsw_loadingDevicesSlow": "Chargement en cours — l'instance de l'adaptateur doit être démarrée.",
|
|
57
57
|
"gsw_listFailed": "Impossible de charger la liste des appareils. L'instance est-elle démarrée ?",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"gsw_diagOpenIssue": "Apri una issue",
|
|
52
52
|
"gsw_diagNoDevices": "Ancora nessun dispositivo. Il rapporto è per dispositivo — appena l'adattatore ne trova uno, compare qui.",
|
|
53
53
|
"gsw_diagExportFailed": "Non è stato possibile creare il rapporto. Il log dell'adattatore spiega perché.",
|
|
54
|
-
"gsw_diagPrivacy": "Il rapporto è pseudonimizzato: indirizzi, indirizzi mail e nomi dei dispositivi sono sostituiti da marcatori stabili, gli id sono accorciati e le credenziali non compaiono mai.
|
|
54
|
+
"gsw_diagPrivacy": "Il rapporto è pseudonimizzato: indirizzi, indirizzi mail e nomi dei dispositivi sono sostituiti da marcatori stabili, gli id sono accorciati e le credenziali non compaiono mai.",
|
|
55
55
|
"gsw_loadingDevices": "Caricamento dispositivi …",
|
|
56
56
|
"gsw_loadingDevicesSlow": "Ancora in caricamento: l'istanza dell'adattatore deve essere in esecuzione.",
|
|
57
57
|
"gsw_listFailed": "Impossibile caricare l'elenco dei dispositivi. L'istanza è in esecuzione?",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"gsw_diagOpenIssue": "Issue openen",
|
|
52
52
|
"gsw_diagNoDevices": "Nog geen apparaten. Het rapport is per apparaat — zodra de adapter er een vindt, verschijnt het hier.",
|
|
53
53
|
"gsw_diagExportFailed": "Het rapport kon niet worden gemaakt. Het adapterlogboek zegt waarom.",
|
|
54
|
-
"gsw_diagPrivacy": "Het rapport is gepseudonimiseerd: adressen, mailadressen en apparaatnamen worden vervangen door vaste markeringen, apparaat-ids worden ingekort en inloggegevens komen er niet in voor.
|
|
54
|
+
"gsw_diagPrivacy": "Het rapport is gepseudonimiseerd: adressen, mailadressen en apparaatnamen worden vervangen door vaste markeringen, apparaat-ids worden ingekort en inloggegevens komen er niet in voor.",
|
|
55
55
|
"gsw_loadingDevices": "Apparaten laden …",
|
|
56
56
|
"gsw_loadingDevicesSlow": "Nog bezig — hiervoor moet de adapterinstantie draaien.",
|
|
57
57
|
"gsw_listFailed": "Kon de apparatenlijst niet laden. Draait de instantie?",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"gsw_diagOpenIssue": "Otwórz zgłoszenie",
|
|
52
52
|
"gsw_diagNoDevices": "Brak urządzeń. Raport dotyczy pojedynczego urządzenia — gdy adapter jakieś znajdzie, pojawi się tutaj.",
|
|
53
53
|
"gsw_diagExportFailed": "Nie udało się utworzyć raportu. Powód znajdziesz w logu adaptera.",
|
|
54
|
-
"gsw_diagPrivacy": "Raport jest pseudonimizowany: adresy, adresy e-mail i nazwy urządzeń zastępują stałe znaczniki, identyfikatory są skracane, a dane logowania nigdy się w nim nie pojawiają.
|
|
54
|
+
"gsw_diagPrivacy": "Raport jest pseudonimizowany: adresy, adresy e-mail i nazwy urządzeń zastępują stałe znaczniki, identyfikatory są skracane, a dane logowania nigdy się w nim nie pojawiają.",
|
|
55
55
|
"gsw_loadingDevices": "Wczytywanie urządzeń …",
|
|
56
56
|
"gsw_loadingDevicesSlow": "Nadal trwa wczytywanie — instancja adaptera musi być uruchomiona.",
|
|
57
57
|
"gsw_listFailed": "Nie udało się wczytać listy urządzeń. Czy instancja działa?",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"gsw_diagOpenIssue": "Abrir um issue",
|
|
52
52
|
"gsw_diagNoDevices": "Ainda sem dispositivos. O relatório é por dispositivo — assim que o adaptador encontrar um, aparece aqui.",
|
|
53
53
|
"gsw_diagExportFailed": "Não foi possível criar o relatório. O log do adaptador diz porquê.",
|
|
54
|
-
"gsw_diagPrivacy": "O relatório é pseudonimizado: endereços, endereços de e-mail e nomes de dispositivos são substituídos por marcadores estáveis, os identificadores são encurtados e as credenciais nunca aparecem.
|
|
54
|
+
"gsw_diagPrivacy": "O relatório é pseudonimizado: endereços, endereços de e-mail e nomes de dispositivos são substituídos por marcadores estáveis, os identificadores são encurtados e as credenciais nunca aparecem.",
|
|
55
55
|
"gsw_loadingDevices": "A carregar dispositivos …",
|
|
56
56
|
"gsw_loadingDevicesSlow": "Ainda a carregar — a instância do adaptador tem de estar em execução.",
|
|
57
57
|
"gsw_listFailed": "Não foi possível carregar a lista de dispositivos. A instância está a correr?",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"gsw_diagOpenIssue": "Открыть issue",
|
|
52
52
|
"gsw_diagNoDevices": "Пока нет устройств. Отчёт составляется по устройству — как только адаптер найдёт хотя бы одно, оно появится здесь.",
|
|
53
53
|
"gsw_diagExportFailed": "Не удалось создать отчёт. Причина указана в журнале адаптера.",
|
|
54
|
-
"gsw_diagPrivacy": "Отчёт псевдонимизирован: адреса, почтовые адреса и имена устройств заменены постоянными метками, идентификаторы сокращены, учётные данные не попадают в него.
|
|
54
|
+
"gsw_diagPrivacy": "Отчёт псевдонимизирован: адреса, почтовые адреса и имена устройств заменены постоянными метками, идентификаторы сокращены, учётные данные не попадают в него.",
|
|
55
55
|
"gsw_loadingDevices": "Загрузка устройств …",
|
|
56
56
|
"gsw_loadingDevicesSlow": "Всё ещё загружается — для этого экземпляр адаптера должен быть запущен.",
|
|
57
57
|
"gsw_listFailed": "Не удалось загрузить список устройств. Экземпляр запущен?",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"gsw_diagOpenIssue": "Відкрити issue",
|
|
52
52
|
"gsw_diagNoDevices": "Пристроїв ще немає. Звіт складається для пристрою — щойно адаптер знайде хоч один, він з'явиться тут.",
|
|
53
53
|
"gsw_diagExportFailed": "Не вдалося створити звіт. Причина — у журналі адаптера.",
|
|
54
|
-
"gsw_diagPrivacy": "Звіт псевдонімізовано: адреси, поштові адреси та назви пристроїв замінено сталими мітками, ідентифікатори скорочено, а облікові дані не потрапляють до нього.
|
|
54
|
+
"gsw_diagPrivacy": "Звіт псевдонімізовано: адреси, поштові адреси та назви пристроїв замінено сталими мітками, ідентифікатори скорочено, а облікові дані не потрапляють до нього.",
|
|
55
55
|
"gsw_loadingDevices": "Завантаження пристроїв …",
|
|
56
56
|
"gsw_loadingDevicesSlow": "Ще завантажується — для цього екземпляр адаптера має працювати.",
|
|
57
57
|
"gsw_listFailed": "Не вдалося завантажити список пристроїв. Чи запущено екземпляр?",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"gsw_diagOpenIssue": "创建 issue",
|
|
52
52
|
"gsw_diagNoDevices": "暂无设备。报告是按设备生成的——适配器找到设备后会显示在这里。",
|
|
53
53
|
"gsw_diagExportFailed": "无法生成报告。原因见适配器日志。",
|
|
54
|
-
"gsw_diagPrivacy": "
|
|
54
|
+
"gsw_diagPrivacy": "报告已做匿名化处理:地址、邮件地址和设备名称被替换为固定标记,设备标识被缩短,凭据完全不会出现。",
|
|
55
55
|
"gsw_loadingDevices": "正在加载设备 …",
|
|
56
56
|
"gsw_loadingDevicesSlow": "仍在加载 — 这需要适配器实例正在运行。",
|
|
57
57
|
"gsw_listFailed": "无法加载设备列表。适配器实例正在运行吗?",
|
|
@@ -25,13 +25,16 @@ __export(capability_mapper_exports, {
|
|
|
25
25
|
buildCloudStateDefs: () => buildCloudStateDefs,
|
|
26
26
|
buildLanStateDefs: () => buildLanStateDefs,
|
|
27
27
|
canonicalSyntheticId: () => canonicalSyntheticId,
|
|
28
|
+
classifyModeLevels: () => classifyModeLevels,
|
|
28
29
|
getDefaultLanStates: () => getDefaultLanStates,
|
|
29
30
|
getMusicModeOptions: () => getMusicModeOptions,
|
|
30
31
|
hasDynamicSceneCapability: () => hasDynamicSceneCapability,
|
|
31
32
|
mapCapabilities: () => mapCapabilities,
|
|
32
33
|
mapCloudStateValue: () => mapCloudStateValue,
|
|
34
|
+
mapCloudStateValues: () => mapCloudStateValues,
|
|
33
35
|
musicModeNameUsesRgb: () => musicModeNameUsesRgb,
|
|
34
|
-
planCloudCapabilityWrites: () => planCloudCapabilityWrites
|
|
36
|
+
planCloudCapabilityWrites: () => planCloudCapabilityWrites,
|
|
37
|
+
resolveWorkModeStruct: () => resolveWorkModeStruct
|
|
35
38
|
});
|
|
36
39
|
module.exports = __toCommonJS(capability_mapper_exports);
|
|
37
40
|
var import_types = require("./types");
|
|
@@ -293,10 +296,10 @@ function mapColorSetting(cap) {
|
|
|
293
296
|
}
|
|
294
297
|
function mapMode(cap) {
|
|
295
298
|
var _a;
|
|
296
|
-
if (cap.instance !== "
|
|
299
|
+
if (typeof cap.instance !== "string" || !Array.isArray((_a = cap.parameters) == null ? void 0 : _a.options)) {
|
|
297
300
|
return [];
|
|
298
301
|
}
|
|
299
|
-
const states = {
|
|
302
|
+
const states = { 0: "---" };
|
|
300
303
|
for (const opt of cap.parameters.options) {
|
|
301
304
|
if (!opt || typeof opt.name !== "string") {
|
|
302
305
|
continue;
|
|
@@ -306,13 +309,23 @@ function mapMode(cap) {
|
|
|
306
309
|
}
|
|
307
310
|
return [
|
|
308
311
|
{
|
|
309
|
-
id
|
|
310
|
-
|
|
312
|
+
// `presetScene` keeps the id it has had since the beginning — renaming it
|
|
313
|
+
// would orphan the datapoint on every existing installation. Every other
|
|
314
|
+
// instance takes its own sanitised name (nightlightScene →
|
|
315
|
+
// nightlight_scene), the same rule the rest of the mapper follows.
|
|
316
|
+
id: cap.instance === "presetScene" ? "scene" : sanitizeId(cap.instance),
|
|
317
|
+
// `capabilityName` is the shared resolver every other generic branch
|
|
318
|
+
// uses: it hands back the translation object for a known instance
|
|
319
|
+
// (`capNightlightScene` has existed in all 11 languages the whole time —
|
|
320
|
+
// only `mapMode` never asked for it) and the vendor text for an unknown
|
|
321
|
+
// one. `humanize()` here would put a plain string into `common.name`,
|
|
322
|
+
// which the object-inventory gate rejects fleet-wide.
|
|
323
|
+
name: cap.instance === "presetScene" ? (0, import_i18n.tName)("scene") : capabilityName(cap.instance),
|
|
311
324
|
type: "mixed",
|
|
312
325
|
role: "state",
|
|
313
326
|
write: true,
|
|
314
327
|
states,
|
|
315
|
-
def: "",
|
|
328
|
+
def: "0",
|
|
316
329
|
capabilityType: cap.type,
|
|
317
330
|
capabilityInstance: cap.instance
|
|
318
331
|
}
|
|
@@ -370,6 +383,107 @@ function mapProperty(cap) {
|
|
|
370
383
|
}
|
|
371
384
|
];
|
|
372
385
|
}
|
|
386
|
+
function classifyModeLevels(options) {
|
|
387
|
+
const levels = [];
|
|
388
|
+
const ranges = [];
|
|
389
|
+
for (const opt of options) {
|
|
390
|
+
if (!opt) {
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
const group = typeof opt.name === "string" ? opt.name : "";
|
|
394
|
+
if (Array.isArray(opt.options) && opt.options.length > 0) {
|
|
395
|
+
for (const sub of opt.options) {
|
|
396
|
+
const n2 = coerceNum(sub == null ? void 0 : sub.value);
|
|
397
|
+
if (n2 !== null) {
|
|
398
|
+
levels.push({ group, value: n2, name: typeof sub.name === "string" ? sub.name : void 0 });
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
const range = opt.range;
|
|
404
|
+
const rMin = coerceNum(range == null ? void 0 : range.min);
|
|
405
|
+
const rMax = coerceNum(range == null ? void 0 : range.max);
|
|
406
|
+
if (rMin !== null && rMax !== null) {
|
|
407
|
+
ranges.push({ min: rMin, max: rMax });
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
const n = coerceNum(opt.value);
|
|
411
|
+
if (n !== null) {
|
|
412
|
+
levels.push({ group: "", value: n, name: group || void 0 });
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if (levels.length === 0 && ranges.length === 0) {
|
|
416
|
+
return { kind: "none" };
|
|
417
|
+
}
|
|
418
|
+
const named = levels.every((l) => l.name !== void 0);
|
|
419
|
+
const owners = /* @__PURE__ */ new Map();
|
|
420
|
+
let collision = false;
|
|
421
|
+
for (const l of levels) {
|
|
422
|
+
const seen = owners.get(l.value);
|
|
423
|
+
if (seen !== void 0 && seen !== l.group) {
|
|
424
|
+
collision = true;
|
|
425
|
+
break;
|
|
426
|
+
}
|
|
427
|
+
owners.set(l.value, l.group);
|
|
428
|
+
}
|
|
429
|
+
if (named && !collision && ranges.length === 0) {
|
|
430
|
+
const states = {};
|
|
431
|
+
for (const l of levels) {
|
|
432
|
+
states[safeStringify(l.value)] = l.name;
|
|
433
|
+
}
|
|
434
|
+
return { kind: "dropdown", states };
|
|
435
|
+
}
|
|
436
|
+
const all = [...levels.map((l) => l.value), ...ranges.map((r) => r.min), ...ranges.map((r) => r.max)];
|
|
437
|
+
return { kind: "number", min: Math.min(...all), max: Math.max(...all) };
|
|
438
|
+
}
|
|
439
|
+
function resolveWorkModeStruct(cap, workModeKey, modeValueKey, changed) {
|
|
440
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
441
|
+
const fields = (_b = (_a = cap.parameters) == null ? void 0 : _a.fields) != null ? _b : [];
|
|
442
|
+
const modeOptions = (_d = (_c = fields.find((f) => f && f.fieldName === "workMode")) == null ? void 0 : _c.options) != null ? _d : [];
|
|
443
|
+
const levelOptions = (_f = (_e = fields.find((f) => f && f.fieldName === "modeValue")) == null ? void 0 : _e.options) != null ? _f : [];
|
|
444
|
+
const pick = (options, raw) => {
|
|
445
|
+
var _a2;
|
|
446
|
+
if (raw === null || raw === void 0) {
|
|
447
|
+
return void 0;
|
|
448
|
+
}
|
|
449
|
+
const s = String(raw).trim();
|
|
450
|
+
if (!s) {
|
|
451
|
+
return void 0;
|
|
452
|
+
}
|
|
453
|
+
return (_a2 = options.find((o) => o && o.value !== void 0 && o.value !== null && safeStringify(o.value) === s)) != null ? _a2 : options.find((o) => o && typeof o.name === "string" && o.name.toLowerCase() === s.toLowerCase());
|
|
454
|
+
};
|
|
455
|
+
const groups = levelOptions.filter((o) => o && Array.isArray(o.options) && o.options.length > 0);
|
|
456
|
+
const flatLevels = levelOptions.filter(
|
|
457
|
+
(o) => o && !Array.isArray(o.options) && o.value !== void 0 && o.value !== null
|
|
458
|
+
);
|
|
459
|
+
let modeOpt;
|
|
460
|
+
if (changed === "modeValue" && groups.length > 0 && classifyModeLevels(levelOptions).kind === "dropdown") {
|
|
461
|
+
const owner = groups.find((g) => pick(g.options, modeValueKey) !== void 0);
|
|
462
|
+
modeOpt = owner ? modeOptions.find((o) => o.name === owner.name) : void 0;
|
|
463
|
+
}
|
|
464
|
+
modeOpt != null ? modeOpt : modeOpt = pick(modeOptions, workModeKey);
|
|
465
|
+
const workMode = coerceNum(modeOpt == null ? void 0 : modeOpt.value);
|
|
466
|
+
if (modeOpt === void 0 || workMode === null) {
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
const groupEntry = levelOptions.find((o) => o && o.name === modeOpt.name);
|
|
470
|
+
const ownLevels = Array.isArray(groupEntry == null ? void 0 : groupEntry.options) && groupEntry.options.length > 0 ? groupEntry.options : flatLevels;
|
|
471
|
+
if (ownLevels.length > 0) {
|
|
472
|
+
const chosen = (_g = pick(ownLevels, modeValueKey)) != null ? _g : ownLevels[0];
|
|
473
|
+
const modeValue = coerceNum(chosen.value);
|
|
474
|
+
return modeValue === null ? null : { workMode, modeValue };
|
|
475
|
+
}
|
|
476
|
+
const range = groupEntry == null ? void 0 : groupEntry.range;
|
|
477
|
+
const rMin = coerceNum(range == null ? void 0 : range.min);
|
|
478
|
+
const rMax = coerceNum(range == null ? void 0 : range.max);
|
|
479
|
+
if (rMin !== null && rMax !== null) {
|
|
480
|
+
const written = coerceNum(modeValueKey);
|
|
481
|
+
const modeValue = written === null ? rMin : Math.min(Math.max(written, rMin), rMax);
|
|
482
|
+
return { workMode, modeValue };
|
|
483
|
+
}
|
|
484
|
+
const fallback = coerceNum(groupEntry == null ? void 0 : groupEntry.defaultValue);
|
|
485
|
+
return { workMode, modeValue: fallback != null ? fallback : 0 };
|
|
486
|
+
}
|
|
373
487
|
function mapWorkMode(cap) {
|
|
374
488
|
var _a;
|
|
375
489
|
const fields = (_a = cap.parameters) == null ? void 0 : _a.fields;
|
|
@@ -413,24 +527,36 @@ function mapWorkMode(cap) {
|
|
|
413
527
|
const valueField = fields.find((f) => f && f.fieldName === "modeValue");
|
|
414
528
|
if (valueField) {
|
|
415
529
|
if (valueField.options && valueField.options.length > 0) {
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
530
|
+
const level = classifyModeLevels(valueField.options);
|
|
531
|
+
if (level.kind === "dropdown") {
|
|
532
|
+
const firstKey = Object.keys(level.states)[0];
|
|
533
|
+
states.push({
|
|
534
|
+
id: "mode_value",
|
|
535
|
+
name: (0, import_i18n.tName)("modeValue"),
|
|
536
|
+
desc: (0, import_i18n.tDesc)("descModeValue"),
|
|
537
|
+
type: "mixed",
|
|
538
|
+
role: "state",
|
|
539
|
+
write: true,
|
|
540
|
+
states: level.states,
|
|
541
|
+
def: firstKey,
|
|
542
|
+
capabilityType: cap.type,
|
|
543
|
+
capabilityInstance: cap.instance
|
|
544
|
+
});
|
|
545
|
+
} else if (level.kind === "number") {
|
|
546
|
+
states.push({
|
|
547
|
+
id: "mode_value",
|
|
548
|
+
name: (0, import_i18n.tName)("modeValue"),
|
|
549
|
+
desc: (0, import_i18n.tDesc)("descModeValue"),
|
|
550
|
+
type: "number",
|
|
551
|
+
role: "level",
|
|
552
|
+
write: true,
|
|
553
|
+
min: level.min,
|
|
554
|
+
max: level.max,
|
|
555
|
+
def: level.min,
|
|
556
|
+
capabilityType: cap.type,
|
|
557
|
+
capabilityInstance: cap.instance
|
|
558
|
+
});
|
|
421
559
|
}
|
|
422
|
-
states.push({
|
|
423
|
-
id: "mode_value",
|
|
424
|
-
name: (0, import_i18n.tName)("modeValue"),
|
|
425
|
-
desc: (0, import_i18n.tDesc)("descModeValue"),
|
|
426
|
-
type: "mixed",
|
|
427
|
-
role: "state",
|
|
428
|
-
write: true,
|
|
429
|
-
states: valStates,
|
|
430
|
-
def: valueField.options[0] ? safeStringify(valueField.options[0].value) : "",
|
|
431
|
-
capabilityType: cap.type,
|
|
432
|
-
capabilityInstance: cap.instance
|
|
433
|
-
});
|
|
434
560
|
} else if (valueField.range) {
|
|
435
561
|
states.push({
|
|
436
562
|
id: "mode_value",
|
|
@@ -450,7 +576,7 @@ function mapWorkMode(cap) {
|
|
|
450
576
|
return states;
|
|
451
577
|
}
|
|
452
578
|
function mapTemperatureSetting(cap) {
|
|
453
|
-
var _a, _b
|
|
579
|
+
var _a, _b;
|
|
454
580
|
const fields = (_a = cap.parameters) == null ? void 0 : _a.fields;
|
|
455
581
|
if (Array.isArray(fields) && fields.length > 0) {
|
|
456
582
|
const tempField = fields.find((f) => {
|
|
@@ -463,7 +589,7 @@ function mapTemperatureSetting(cap) {
|
|
|
463
589
|
return f.fieldName.toLowerCase().includes("temperature");
|
|
464
590
|
});
|
|
465
591
|
if (tempField == null ? void 0 : tempField.range) {
|
|
466
|
-
const unit = (
|
|
592
|
+
const unit = temperatureUnit(cap);
|
|
467
593
|
return [
|
|
468
594
|
{
|
|
469
595
|
id: "target_temperature",
|
|
@@ -481,9 +607,9 @@ function mapTemperatureSetting(cap) {
|
|
|
481
607
|
];
|
|
482
608
|
}
|
|
483
609
|
}
|
|
484
|
-
const range = (
|
|
610
|
+
const range = (_b = cap.parameters) == null ? void 0 : _b.range;
|
|
485
611
|
if (range) {
|
|
486
|
-
const unit = (
|
|
612
|
+
const unit = temperatureUnit(cap);
|
|
487
613
|
return [
|
|
488
614
|
{
|
|
489
615
|
id: "target_temperature",
|
|
@@ -628,8 +754,19 @@ const UNIT_MAP = {
|
|
|
628
754
|
"unit.percent": "%",
|
|
629
755
|
"unit.kelvin": "K",
|
|
630
756
|
"unit.celsius": "\xB0C",
|
|
631
|
-
"unit.fahrenheit": "\xB0F"
|
|
757
|
+
"unit.fahrenheit": "\xB0F",
|
|
758
|
+
// The temperature_setting STRUCT names its unit like this (H7131 capture).
|
|
759
|
+
Celsius: "\xB0C",
|
|
760
|
+
Fahrenheit: "\xB0F"
|
|
632
761
|
};
|
|
762
|
+
function temperatureUnit(cap) {
|
|
763
|
+
var _a, _b, _c, _d, _e, _f;
|
|
764
|
+
const fields = (_a = cap.parameters) == null ? void 0 : _a.fields;
|
|
765
|
+
const unitField = fields == null ? void 0 : fields.find((f) => f && f.fieldName === "unit");
|
|
766
|
+
const declared = typeof (unitField == null ? void 0 : unitField.defaultValue) === "string" ? unitField.defaultValue : void 0;
|
|
767
|
+
const onTemperature = (_b = fields == null ? void 0 : fields.find((f) => f && f.fieldName === "temperature")) == null ? void 0 : _b.unit;
|
|
768
|
+
return (_f = (_e = (_c = normalizeUnit(declared)) != null ? _c : normalizeUnit(typeof onTemperature === "string" ? onTemperature : void 0)) != null ? _e : normalizeUnit((_d = cap.parameters) == null ? void 0 : _d.unit)) != null ? _f : "\xB0F";
|
|
769
|
+
}
|
|
633
770
|
function normalizeUnit(unit) {
|
|
634
771
|
var _a;
|
|
635
772
|
if (!unit) {
|
|
@@ -730,13 +867,13 @@ function mapCloudStateValue(cap) {
|
|
|
730
867
|
case "toggle":
|
|
731
868
|
return { stateId: sanitizeId(cap.instance), value: coerceBool(raw) };
|
|
732
869
|
case "mode":
|
|
733
|
-
if (cap.instance
|
|
734
|
-
return
|
|
735
|
-
stateId: "scene",
|
|
736
|
-
value: safeStringify(raw)
|
|
737
|
-
};
|
|
870
|
+
if (typeof cap.instance !== "string") {
|
|
871
|
+
return null;
|
|
738
872
|
}
|
|
739
|
-
return
|
|
873
|
+
return {
|
|
874
|
+
stateId: cap.instance === "presetScene" ? "scene" : sanitizeId(cap.instance),
|
|
875
|
+
value: safeStringify(raw)
|
|
876
|
+
};
|
|
740
877
|
case "dynamic_scene":
|
|
741
878
|
if (cap.instance === "snapshot") {
|
|
742
879
|
return null;
|
|
@@ -746,18 +883,12 @@ function mapCloudStateValue(cap) {
|
|
|
746
883
|
value: safeStringify(raw)
|
|
747
884
|
};
|
|
748
885
|
case "work_mode": {
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
return { stateId: "work_mode", value: n };
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
const direct = coerceNum(raw);
|
|
757
|
-
if (direct !== null) {
|
|
758
|
-
return { stateId: "work_mode", value: direct };
|
|
886
|
+
const struct = typeof raw === "object" && raw !== null ? raw : void 0;
|
|
887
|
+
const n = coerceNum(struct ? struct.workMode : raw);
|
|
888
|
+
if (n === null) {
|
|
889
|
+
return null;
|
|
759
890
|
}
|
|
760
|
-
return
|
|
891
|
+
return { stateId: "work_mode", value: safeStringify(n) };
|
|
761
892
|
}
|
|
762
893
|
case "temperature_setting": {
|
|
763
894
|
const direct = coerceNum(raw);
|
|
@@ -800,16 +931,12 @@ function mapCloudStateValue(cap) {
|
|
|
800
931
|
return null;
|
|
801
932
|
}
|
|
802
933
|
}
|
|
803
|
-
function planCloudCapabilityWrites(caps, hasLanIp, lanStateIds) {
|
|
934
|
+
function planCloudCapabilityWrites(caps, hasLanIp, lanStateIds, declared) {
|
|
804
935
|
const writes = [];
|
|
805
936
|
if (!Array.isArray(caps)) {
|
|
806
937
|
return writes;
|
|
807
938
|
}
|
|
808
|
-
for (const
|
|
809
|
-
const mapped = mapCloudStateValue(cap);
|
|
810
|
-
if (!mapped) {
|
|
811
|
-
continue;
|
|
812
|
-
}
|
|
939
|
+
for (const mapped of caps.flatMap((cap) => mapCloudStateValues(cap, declared))) {
|
|
813
940
|
if (hasLanIp && lanStateIds.has(mapped.stateId)) {
|
|
814
941
|
continue;
|
|
815
942
|
}
|
|
@@ -817,6 +944,27 @@ function planCloudCapabilityWrites(caps, hasLanIp, lanStateIds) {
|
|
|
817
944
|
}
|
|
818
945
|
return writes;
|
|
819
946
|
}
|
|
947
|
+
function mapCloudStateValues(cap, declared) {
|
|
948
|
+
var _a, _b, _c, _d, _e;
|
|
949
|
+
const primary = mapCloudStateValue(cap);
|
|
950
|
+
if (!primary) {
|
|
951
|
+
return [];
|
|
952
|
+
}
|
|
953
|
+
if (primary.stateId !== "work_mode") {
|
|
954
|
+
return [primary];
|
|
955
|
+
}
|
|
956
|
+
const raw = (_a = cap.state) == null ? void 0 : _a.value;
|
|
957
|
+
if (typeof raw !== "object" || raw === null) {
|
|
958
|
+
return [primary];
|
|
959
|
+
}
|
|
960
|
+
const level = coerceNum(raw.modeValue);
|
|
961
|
+
if (level === null) {
|
|
962
|
+
return [primary];
|
|
963
|
+
}
|
|
964
|
+
const levelOptions = (_e = (_d = (_c = (_b = declared == null ? void 0 : declared.find((c) => c.type === cap.type && c.instance === cap.instance)) == null ? void 0 : _b.parameters) == null ? void 0 : _c.fields) == null ? void 0 : _d.find((f) => f && f.fieldName === "modeValue")) == null ? void 0 : _e.options;
|
|
965
|
+
const asNumber = levelOptions !== void 0 && classifyModeLevels(levelOptions).kind === "number";
|
|
966
|
+
return [primary, { stateId: "mode_value", value: asNumber ? level : safeStringify(level) }];
|
|
967
|
+
}
|
|
820
968
|
const SCENE_DROPDOWN_RULES = [
|
|
821
969
|
{ id: "light_scene", cap: "lightScene", nameKey: "lightScene", channel: "scenes", source: (d) => d.scenes },
|
|
822
970
|
{ id: "diy_scene", cap: "diyScene", nameKey: "diyScene", channel: "scenes", source: (d) => d.diyScenes },
|
|
@@ -1082,12 +1230,15 @@ function buildGroupStateDefs(members) {
|
|
|
1082
1230
|
buildCloudStateDefs,
|
|
1083
1231
|
buildLanStateDefs,
|
|
1084
1232
|
canonicalSyntheticId,
|
|
1233
|
+
classifyModeLevels,
|
|
1085
1234
|
getDefaultLanStates,
|
|
1086
1235
|
getMusicModeOptions,
|
|
1087
1236
|
hasDynamicSceneCapability,
|
|
1088
1237
|
mapCapabilities,
|
|
1089
1238
|
mapCloudStateValue,
|
|
1239
|
+
mapCloudStateValues,
|
|
1090
1240
|
musicModeNameUsesRgb,
|
|
1091
|
-
planCloudCapabilityWrites
|
|
1241
|
+
planCloudCapabilityWrites,
|
|
1242
|
+
resolveWorkModeStruct
|
|
1092
1243
|
});
|
|
1093
1244
|
//# sourceMappingURL=capability-mapper.js.map
|