mirror of
https://github.com/joBr99/nspanel-lovelace-ui.git
synced 2026-02-18 10:57:01 +01:00
The subscribePowerSubscriptions() function was creating subscriptions via on() without storing the subscription ID in the subscriptions object. This caused UnsubscribeWatcher() to be unable to remove the subscription when the screensaver activated, leaving it active and triggering GeneratePage() on any entity change. Changes: - Store subscription in subscriptions object with key 'power_<id>.ACTUAL' - Add hasOwnProperty check to prevent duplicate subscriptions
15192 lines
775 KiB
TypeScript
15192 lines
775 KiB
TypeScript
/*-----------------------------------------------------------------------
|
|
TypeScript v5.0.2.1 zur Steuerung des SONOFF NSPanel mit dem ioBroker by @Armilar / @TT-Tom / @ticaki / @Britzelpuf / @Sternmiere / @ravenS0ne
|
|
- abgestimmt auf TFT 59 / v5.0.2 / BerryDriver 10 / Tasmota 15.0.1
|
|
|
|
Projekt:
|
|
https://github.com/joBr99/nspanel-lovelace-ui/tree/main/ioBroker
|
|
https://github.com/ticaki/ioBroker.nspanel-lovelace-ui/tree/main
|
|
|
|
NsPanelTs.ts (dieses TypeScript in ioBroker) Stable: https://github.com/joBr99/nspanel-lovelace-ui/blob/main/ioBroker/NsPanelTs.ts
|
|
icon_mapping.ts: https://github.com/joBr99/nspanel-lovelace-ui/blob/main/ioBroker/icon_mapping.ts (TypeScript muss in global liegen)
|
|
ioBroker-Unterstützung: https://forum.iobroker.net/topic/58170/sonoff-nspanel-mit-lovelace-ui
|
|
@Kuckuckmann: WIKI zu diesem Projekt unter: https://github.com/joBr99/nspanel-lovelace-ui/wiki (siehe Sidebar)
|
|
|
|
***************************************************************************************************************
|
|
Achtung: Keine Beispiele mehr im Script. Die Beispiele sind jetzt unter nachfolgendem Link zu finden:
|
|
- https://github.com/joBr99/nspanel-lovelace-ui/wiki/NSPanel-Page-%E2%80%90-Typen_How-2_Beispiele
|
|
***************************************************************************************************************
|
|
|
|
Icons unter: https://htmlpreview.github.io/?https://github.com/jobr99/Generate-HASP-Fonts/blob/master/cheatsheet.html
|
|
|
|
************************************************************************************************
|
|
Achtung Tasmota 15.1.0 lässt kein FlashNextion zu --> stattdessen v15.0.1 verwenden
|
|
************************************************************************************************
|
|
Achtung Änderung des Sonoff ESP-Temperatursensors
|
|
!!! Bitte "SetOption146 1" in der Tasmota-Console ausführen !!!
|
|
************************************************************************************************
|
|
Ab Tasmota > 13.0.0 ist für ein Upgrade ggfs. eine Umpartitionierung erforderlich
|
|
https://github.com/joBr99/nspanel-lovelace-ui/wiki/NSPanel-Tasmota-FAQ#3-tasmota-update-probleme
|
|
*****************************************************************************************************************************
|
|
Ab Script Version 4.3.2.1 muss in der JavaScript Instanz die npm Module 'moment' und 'moment-parseformat' eingetragen sein
|
|
https://github.com/joBr99/nspanel-lovelace-ui/wiki/iobroker---Basisinstallation#8--einstellungen-in-js-adapter-instanz
|
|
*****************************************************************************************************************************
|
|
|
|
ReleaseNotes:
|
|
Bugfixes und Erweiterungen:
|
|
See ChangeLog all Release Notes: https://github.com/joBr99/nspanel-lovelace-ui/wiki/Release-Notes
|
|
|
|
- 21.01.2025 - v4.5.0 TFT 54 / 4.5.0
|
|
- 23.01.2025 - v4.5.0.1 Change TFT URLs
|
|
- 23.01.2025 - v4.5.0.2 fix handleScreensaverUpdate => leftscreensaverEntity; fix Type leftscreensaverentitiy
|
|
- 23.01.2025 - v4.5.0.2 icon3 functionality also for thermometers and a function based on this in the screensaver
|
|
- 29.01.2025 - v4.5.0.3 Add: bottemEntityText from ID
|
|
- 30.01.2025 - v4.5.0.4 fix DetermineDimBrightness (function returns undefined, because wrong DP check)
|
|
- 03.02.2025 - v4.5.0.5 Bugfix InitDimmode by Gargano
|
|
- 14.03.2025 - v4.5.2 Fix Bugs in HUE-Light, Fix Icon-Colors with interpolateColors (Color, ColorTemp, Brightness), Fix ON instead of ON_ACTUAL for writing DP
|
|
- 15.03.2025 - v4.5.2.1 Add Functions to Calculate Colors of HUE Icons (Darken and CT (Kelvin/Mired))
|
|
- 15.03.2025 - v4.5.2.1 Remove New Sliders (popupLightNew), Fix TFT-Pictures in TFT --> with v4.6.0 / TFT 55
|
|
- 16.03.2025 - v4.6.0 Fix Bugs in Channels Light and RGBsingle-Light, Fix Icon-Colors with interpolateColors (Color, ColorTemp, Brightness), Fix ON instead of ON_ACTUAL for writing DP
|
|
- 16.03.2025 - v4.6.0.1 Add Functions to Calculate Colors of RGBsingle Icons (Darken and CT (Kelvin/Mired))
|
|
- 16.03.2025 - v4.6.0.1 Fix Light-Icons if Color-Temperature uses Mired instead of Kelvin (500 Mired - 153 Mired = 2000 K - 6536 K)
|
|
- 16.03.2025 - v4.6.0.1 Add icon2 to Lights
|
|
- 17.03.2025 - v4.6.0.1 Add CIE Channel to Lights
|
|
- 17.03.2025 - v4.6.0.1 Add Functions to Calculate Colors of RGB and CT Icons (Darken and CT (Kelvin/Mired))
|
|
- 18.03.2025 - v4.6.0.1 Add hidden Entity2 (Password/Switch) to cardQR (PageItem-Parameter "hideEntity2" true/false)
|
|
- 01.04.2025 - v4.6.1 TFT 55 / 4.6.1 - Add Some Adapter Functions
|
|
- 01.04.2025 - v4.6.2 TFT 55 / 4.6.2 - Add cardSchedule
|
|
- 01.04.2025 - v4.6.2.1 Add startup TFT-Release directly from NSPanel-TFT, Comparison between version number and release removed
|
|
- 02.04.2025 - v4.7.0 TFT 56 / 4.7.0 - Fix cardSchedule
|
|
- 10.04.2025 - v4.7.0.2 Add cardMedia "Music Player Daemon (MPD)" (One-Instance-Player with Playlists, Tracklists, Shuffle, Repeat, Seek/Crossfade); mpd.X - Instance required
|
|
- 10.04.2025 - v4.7.0.3 Fix cardMedia "Music Player Daemon (MPD)" shuffle with repeat and repeat with repeat/single
|
|
- 10.04.2025 - v4.7.1 TFT 56 / 4.7.1 - Add Player Icon-Logos logo-alexa, logo-spotify, logo-dlna, logo-sonos, logo-mpd, logo-volumios, logo-bose
|
|
- 10.04.2025 - v4.7.1.1 Add parameter playerMediaIcon to cardMedia
|
|
- 12.04.2025 - v4.7.1.2 Fix Play/Pause in MediaPlayers
|
|
- 13.04.2025 - v4.7.1.2 TFT 56 / 4.7.1 (US-P and US-L)
|
|
- 14.04.2025 - v4.7.1.3 MrIcons also allow other mqtt states
|
|
- 24.04.2025 - v4.7.2.1 Add popupSlider to cardMedia (alexa)
|
|
- 12.06.2025 - v4.7.2.2 States only respond to any if ack = false
|
|
- 20.06.2025 - v4.7.2.3 IconSelect left- and indicatorScreensaverEntity added
|
|
- 21.06.2025 - v4.7.2.4 Fix Demomodus Powerpage
|
|
- 22.06.2025 - v4.7.3 TFT 56 / 4.7.3 - Change Direction Pictures ColorTemperature (warmwhite left/coldwhite right)
|
|
- 23.06.2025 - v4.7.4 TFT 56 / 4.7.4 - Refactoring popupShutter (shutter/shutter2)
|
|
- 24.06.2025 - v4.7.4.1 Refactoring popupShutter (split into shutter/shutter2)
|
|
- 25.06.2025 - v4.7.5 TFT 56 / 4.7.5 - Refactoring popupLight2 (light/light2) --> EU + US-P
|
|
- 25.06.2025 - v4.7.5.1 Add popupLight2 (split into light/light2)
|
|
- 26.06.2025 - v4.7.5 TFT 56 / 4.7.5 - Refactoring popupLight2 (light/light2) --> US-L
|
|
- 30.06.2025 - v4.8.0 TFT 57 / 4.8.0 - Stable - Fix popupShutter2 (eu/us-l/us-p)
|
|
- 30.06.2025 - v4.9.0 TFT 58 / 4.9.0 - Beta - Adapter & Script (eu/us-l/us-p)
|
|
- 30.06.2025 - v4.9.0.1 Small Fixes
|
|
- 24.07.2025 - v4.9.1 Adapter Changes
|
|
- 24.07.2025 - v4.9.2.1 Add icon2 Parameter to Info Alias Channels
|
|
- 25.07.2025 - v4.9.2.2 Add OpenWeatherMap (AccuWeather deprecated)
|
|
- 28.07.2025 - v4.9.2.3 Quick-Fix Errors with TypeScript in JS > 9.X (by ticaki)
|
|
- 30.07.2025 - v4.9.3 TFT 58 / 4.9.3
|
|
- 30.07.2025 - v4.9.3.1 popupShutter2 Changes (new Parameter shutterZeroIsClosed changing Direction of %-Value in HMI (0 <--> 100))
|
|
- 05.08.2025 - v4.9.4 TFT 58 / 4.9.4 - Communication with 921600 bps with Berry Driver 10 / Slider Fix in card Entities
|
|
- 05.08.2025 - v4.9.4.1 Fix Sliders (volume/slider) in createEntity
|
|
- 05.08.2025 - v4.9.4.1 Add USERICONS and colorScale to Alias-Channel Slider
|
|
- 05.08.2025 - v4.9.4.2 Prevent version search to the old directory path (Berry-Driver) + New Berry Update Path (RAW)
|
|
- 08.08.2025 - v4.9.4.3 Add Beta Logic for cardThermo2 (future)
|
|
- 10.08.2025 - v4.9.4.3 Add Pirate-Weather Adapter
|
|
- 11.08.2025 - v4.9.5 TFT 58 / 4.9.5 - Add cardThermo2 (eu)
|
|
- 21.08.2025 - v4.9.5.2 Add Bright Sky Weather Adapter
|
|
- 05.09.2025 - v5.0.0 TFT 59 / 5.0.0 - EU Changes in cardMedia, popupInSel, card Grid 1, 2, 3
|
|
- 08.09.2025 - v5.0.0 TFT 59 / 5.0.0 - US-L/US-P Changes in cardMedia, popupInSel, card Grid 1, 2, 3
|
|
- 19.09.2025 - v5.0.0.2 Remove Startup Scheedule at 3:30am / Small fix
|
|
- 19.10.2025 - v5.0.2.1 TFT 59 / 5.0.2 - EU/US-L/US-P - Fix cardAlarm Icon; Fix Notification in Advanced Screensaver; Fix Dimensions in cardChart/cardLChart
|
|
|
|
|
|
***************************************************************************************************************
|
|
* DE: Für die Erstellung der Aliase durch das Skript, muss in der JavaScript Instanz "setObject" gesetzt sein! *
|
|
* EN: In order for the script to create the aliases, “setObject” must be set in the JavaScript instance! *
|
|
***************************************************************************************************************
|
|
|
|
Wenn Rule definiert, dann können die Hardware-Tasten ebenfalls für Seitensteuerung (dann nicht mehr als Relais) genutzt werden
|
|
|
|
Tasmota Konsole:
|
|
Rule2 on Button1#state do Publish %topic%/tele/RESULT {"CustomRecv":"event,button1"} endon on Button2#state do Publish %topic%/tele/RESULT {"CustomRecv":"event,button2"} endon
|
|
Rule2 1 (Rule aktivieren)
|
|
Rule2 0 (Rule deaktivieren)
|
|
|
|
Mögliche Seiten-Ansichten:
|
|
screensaver Page - wird nach definiertem Zeitraum (config) mit Dimm-Modus aktiv (Uhrzeit, Datum, Aktuelle Temperatur mit Symbol)
|
|
(die 4 kleineren Icons können als Wetter-Vorschau + 4Tage (Symbol + Höchsttemperatur) oder zur Anzeige definierter Infos konfiguriert werden)
|
|
- weitere Screensaver wie Advanced, Easyview und Alternativ
|
|
cardEntities Page - 4 vertikale angeordnete Steuerelemente - auch als Subpage
|
|
5 vertikale angeordnete Steuerelemente - auch als Subpage beim US-Modell im Portrait-Modus
|
|
cardSchedule Page - 6 vertikale angeordnete Text-Steuerelemente - auch als Subpage
|
|
cardGrid Page - 6 horizontal angeordnete Steuerelemente in 2 Reihen a 3 Steuerelemente - auch als Subpage
|
|
cardGrid2 Page - 8 horizontal angeordnete Steuerelemente in 2 Reihen a 4 Steuerelemente - auch als Subpage
|
|
9 horizontal angeordnete Steuerelemente in 3 Reihen a 3 Steuerelemente - auch als Subpage - beim US-Modell im Portrait-Modus
|
|
cardGrid3 Page - 4 horizontal angeordnete Steuerelemente in 2 Reihen a 2 Steuerelemente - auch als Subpage
|
|
cardThermo Page - Thermostat mit Solltemperatur, Isttemperatur, Mode - Weitere Eigenschaften können im Alias definiert werden
|
|
cardThermo2 Page - weiterer Thermostat (Circular Slider) mit Solltemperatur, Isttemperatur, Mode - Weitere Eigenschaften können im Alias definiert werden
|
|
cardMedia Page - Mediaplayer - Ausnahme: Alias sollte mit Alias-Manager automatisch über Alexa-Verzeichnis Player angelegt werden
|
|
cardAlarm Page - Alarmseite mit Zustand und Tastenfeld
|
|
cardPower Page - Energiefluss
|
|
cardChart Page - Balken-Diagramme aus History, SQL oder InfluxDB
|
|
cardLChart Page - Linien-Diagramme aus History, SQL oder InfluxDB
|
|
cardQR Page - QR Code für Bereitstellung Gäste-WLAN
|
|
|
|
Vollständige Liste zur Einrichtung unter:
|
|
https://github.com/joBr99/nspanel-lovelace-ui/wiki/ioBroker-Card-Definitionen-(Seiten)
|
|
|
|
Popup-Pages:
|
|
popupLight - in Abhängigkeit zum gewählten Alias werden "Helligkeit", "Farbtemperatur" und "Farbauswahl" bereitgestellt
|
|
popupLight2 - (größere Elemente) in Abhängigkeit zum gewählten Alias werden "Helligkeit", "Farbtemperatur" und "Farbauswahl" bereitgestellt
|
|
popupShutter - die Shutter-Position (Rollo, Jalousie, Markise, Leinwand, etc.) kann über einen Slider verändert werden.
|
|
popupShutter2 - die Shutter-Position (Rollo, Jalousie, Markise, Leinwand, etc.) kann über einen Slider verändert werden.
|
|
popupNotify - Info - Seite mit Headline Text und Buttons - Intern für manuelle Updates / Extern zur Befüllung von Datenpunkten unter 0_userdata
|
|
screensaver Notify - Über zwei externe Datenpunkte in 0_userdata können "Headline" und "Text" an den Screensaver zur Info gesendet werden
|
|
popupInSel - Auswahlliste (InputSelect)
|
|
popupSlider - 3 vertikal ausgerichtete Slider. Abweichender 0 Punkt möglich
|
|
popupFan - Ventilatorsteuerung
|
|
popupTimer - Stopuhr, Countdown, Wecker oder Zeitschaltuhr
|
|
|
|
Mögliche Aliase: (Vorzugsweise mit ioBroker-Adapter "Geräte verwalten" konfigurieren, da SET, GET, ACTUAL, etc. verwendet werden)
|
|
Info - Werte aus Datenpunkt
|
|
Schieberegler - Slider numerische Werte (SET/ACTUAL)
|
|
Lautstärke - Volume (SET/ACTUAL) und MUTE
|
|
Lautstärke-Gruppe - analog Lautstärke
|
|
Licht - An/Aus (Schalter)
|
|
Steckdose - An/Aus (Schalter)
|
|
Dimmer - An/Aus, Brightness
|
|
Farbtemperatur - An/Aus, Farbtemperatur und Brightness
|
|
CIE-Licht - Zum Schalten von Color-Leuchtmitteln über CIE-Wert [x,y] - Array, Brightness, Farbtemperatur, An/Aus
|
|
HUE-Licht - Zum Schalten von Color-Leuchtmitteln über HUE-Wert, Brightness, Farbtemperatur, An/Aus (HUE kann auch fehlen)
|
|
RGB-Licht - RGB-Leuchtmitteln/Stripes welche Rot/Grün/ und Blau separat benötigen (Tasmota, WifiLight, etc.) + Brightness, Farbtemperatur
|
|
RGB-Licht-einzeln - RGB-Leuchtmitteln/Stripes welche HEX-Farbwerte benötigen (Tasmota, WifiLight, etc.) + Brightness, Farbtemperatur
|
|
Jalousien - Up, Stop, Down, Position
|
|
Fenster - Sensor open
|
|
Tür - Sensor open
|
|
Tor - Sensor open
|
|
Bewegung - Sensor Presence
|
|
Verschluss - Türschloss SET/ACTUAL/OPEN
|
|
Taste - Für Szenen oder Radiosender, etc. --> Nur Funktionsaufruf - Kein Taster wie MonoButton - True/False
|
|
Tastensensor - Für Auswahlmenü (popupInSel)
|
|
Thermostat - Aktuelle Raumtemperatur, Setpoint, etc.
|
|
Temperatur - Temperatur aus Datenpunkt, analog Info
|
|
Klimaanlage - Buttons zur Steuerung der Klimaanlage im unteren Bereich
|
|
Temperatur - Anzeige von Temperatur - Datenpunkten, analog Info
|
|
Feuchtigkeit - Anzeige von Humidity - Datenpunkten, analog Info
|
|
Medien - Steuerung von Alexa, etc. - Der erforderliche Media Alias-Channel legt sich selbst an
|
|
Wettervorhersage - Aktuelle Außen-Temperatur (Temp) und aktuelles AccuWeather-Icon (Icon) für Screensaver
|
|
Warnung - Abfall, etc. -- Info mit IconColor
|
|
Ventilator - An/Aus mit Steuerung über popupFan
|
|
Timer (siehe Wiki)
|
|
|
|
Vollständige Liste zur Einrichtung unter:
|
|
https://github.com/joBr99/nspanel-lovelace-ui/wiki/ioBroker-ALIAS-Definitionen
|
|
|
|
Interne Sonoff-Sensoren (über Tasmota):
|
|
ESP-Temperatur - wird in 0_userdata.0. abgelegt, kann als Alias importiert werden --> SetOption146 1
|
|
Temperatur - Raumtemperatur - wird in 0_userdata.0. abgelegt, kann als Alias importiert werden
|
|
(!!! Achtung: der interne Sonoff-Sensor liefert keine exakten Daten, da das NSPanel-Board und der ESP selbst Hitze produzieren !!!
|
|
ggf. Offset einplanen oder besser einen externen Sensor über Zigbee etc. verwenden)
|
|
Timestamp - wird in 0_userdata.0. Zeitpunkt der letzten Sensorübertragung
|
|
|
|
Tasmota-Status0 - (zyklische Ausführung)
|
|
liefert relevanten Tasmota-Informationen und kann bei Bedarf in "function get_tasmota_status0()" erweitert werden. Daten werden in 0_userdata.0. abgelegt
|
|
|
|
Erforderliche Adapter:
|
|
|
|
Pirate-Weather oder BrightSky oder OpenWeatherMap --> Bei Nutzung der Wetterfunktionen (und zur Icon-Konvertierung) im Screensaver
|
|
!!!DasWetter deprecated - Dienst nur noch für ältere Accounts funktional
|
|
!!!AccuWeather deprecated - Dienst schaltet Free-Account ab!!!
|
|
Alexa2: - Bei Nutzung der dynamischen SpeakerList in der cardMedia
|
|
Geräte verwalten - Für Erstellung der Aliase
|
|
MQTT-Adapter - Für Kommunikation zwischen Skript und Tasmota
|
|
JavaScript-Adapter
|
|
|
|
Install/Upgrades in Konsole:
|
|
|
|
Tasmota BerryDriver Install: Backlog UrlFetch https://raw.githubusercontent.com/ticaki/ioBroker.nspanel-lovelace-ui/refs/heads/main/tasmota/berry/10/autoexec.be; Restart 1
|
|
Tasmota BerryDriver Update: Backlog UpdateDriverVersion https://raw.githubusercontent.com/ticaki/ioBroker.nspanel-lovelace-ui/refs/heads/main/tasmota/berry/10/autoexec.be; Restart 1
|
|
|
|
TFT EU STABLE Version: FlashNextionAdv0 http://nspanel.de/nspanel-v5.0.2.tft
|
|
|
|
TFT US-L STABLE Version: FlashNextionAdv0 http://nspanel.de/nspanel-us-l-v5.0.2.tft
|
|
TFT US-P STABLE Version: FlashNextionAdv0 http://nspanel.de/nspanel-us-p-v5.0.2.tft
|
|
---------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/******************************* Begin CONFIG Parameter *******************************/
|
|
|
|
// DE: liefert bei true detailliertere Meldundgen im Log.
|
|
// EN: if true, provides more detailed messages in the log.
|
|
var Debug: boolean = false;
|
|
|
|
|
|
/***** 1. Tasmota-Config *****/
|
|
|
|
// DE: Anpassen an die Verzeichnisse der MQTT-Adapter-Instanz
|
|
// EN: Adapt to the MQTT adapter instance directories
|
|
const NSPanelReceiveTopic: string = 'mqtt.0.SmartHome.NSPanel_1.tele.RESULT';
|
|
const NSPanelSendTopic: string = 'mqtt.0.SmartHome.NSPanel_1.cmnd.CustomSend';
|
|
|
|
// DE: nur ändern, falls der User im Tasmota vor dem Kompilieren umbenannt wurde (Standard Tasmota: admin)
|
|
// EN: only change if the user was renamed in Tasmota before compiling (default Tasmota: admin)
|
|
const tasmota_web_admin_user: string = 'admin';
|
|
|
|
// DE: setzten, falls "Web Admin Password" in Tasmota vergeben
|
|
// EN set if "Web Admin Password" is assigned in Tasmota
|
|
const tasmota_web_admin_password: string = '';
|
|
|
|
// DE: Setzen der bevorzugten Tasmota32-Version (für Updates)
|
|
// EN: Set preferred Tasmota32 version (for updates)
|
|
const tasmotaOtaVersion: string = 'tasmota32-nspanel.bin';
|
|
// DE: Es können ebenfalls andere Versionen verwendet werden wie zum Beispiel:
|
|
// EN: 'tasmota32-DE.bin' oder 'tasmota32.bin' oder 'tasmota32-DE.bin' oder etc.
|
|
// DE: !!!Anmerkung!!! Seit Tasmota v15.0.X wird der 4Mb PSRAM im ESP32 nur noch in der tasmota32-nspanel.bin verwendet
|
|
// EN: !!!Note!!! Since Tasmota v15.0.X, the 4Mb PSRAM in the ESP32 is only used in the tasmota32-nspanel.bin
|
|
|
|
|
|
/***** 2. Directories in 0_userdata.0... *****/
|
|
|
|
// DE: Anpassen an das jeweilige NSPanel
|
|
// EN: Adapt to the respective NSPanel
|
|
const NSPanel_Path = '0_userdata.0.NSPanel.1.';
|
|
|
|
// DE: Pfad für gemeinsame Nutzung durch mehrere Panels (bei Nutzung der cardAlarm/cardUnlock)
|
|
// EN: Path for sharing between multiple panels (when using cardAlarm/cardUnlock)
|
|
const NSPanel_Alarm_Path = '0_userdata.0.NSPanel.';
|
|
|
|
|
|
/***** 3. Weather adapter Config *****/
|
|
|
|
// DE: Mögliche Wetteradapter 'pirate-weather.0.' oder 'brightsky.0.' oder 'openweathermap.0.' oder 'daswetter.0.' (deprecated) oder 'accuweather.0.' (deprecated)
|
|
// EN: Possible weather adapters 'pirate-weather.0.' or 'brightsky.0.' or 'openweathermap.0.' or 'daswetter.0.' (deprecated) or 'accuweather.0.' (deprecated)
|
|
const weatherAdapterInstance: string = 'pirate-weather.0.';
|
|
|
|
// DE: Mögliche Werte: 'Min', 'Max' oder 'MinMax' im Screensaver
|
|
// EN: Possible values: 'Min', 'Max' or 'MinMax' in the screensaver
|
|
const weatherScreensaverTempMinMax: string = 'MinMax';
|
|
|
|
// DE: Dieser Alias wird automatisch für den gewählten Wetter erstellt und kann entsprechend angepasst werden
|
|
// EN: This alias is automatically created for the selected weather and can be adjusted accordingly
|
|
const weatherEntityPath: string = 'alias.0.Pirate_Weather'; //Please rename if change weatherAdapterInstance!
|
|
|
|
|
|
/***** 4. Color constants for use in the PageItems *****/
|
|
|
|
// DE: Bei Bedarf können weitere Farben definiert werden
|
|
// EN: If necessary, additional colors can be defined
|
|
const HMIOff: RGB = {red: 68, green: 115, blue: 158}; // Blue-Off - Original Entity Off
|
|
const HMIOn: RGB = {red: 3, green: 169, blue: 244}; // Blue-On
|
|
const HMIDark: RGB = {red: 29, green: 29, blue: 29}; // Original Background Color
|
|
const Off: RGB = {red: 253, green: 128, blue: 0}; // Orange-Off - nicer color transitions
|
|
const On: RGB = {red: 253, green: 216, blue: 53};
|
|
const MSRed: RGB = {red: 251, green: 105, blue: 98};
|
|
const MSYellow: RGB = {red: 255, green: 235, blue: 156};
|
|
const MSGreen: RGB = {red: 121, green: 222, blue: 121};
|
|
const Red: RGB = {red: 255, green: 0, blue: 0};
|
|
const White: RGB = {red: 255, green: 255, blue: 255};
|
|
const Yellow: RGB = {red: 255, green: 255, blue: 0};
|
|
const Green: RGB = {red: 0, green: 255, blue: 0};
|
|
const Blue: RGB = {red: 0, green: 0, blue: 255};
|
|
const DarkBlue: RGB = {red: 0, green: 0, blue: 136};
|
|
const Gray: RGB = {red: 136, green: 136, blue: 136};
|
|
const Black: RGB = {red: 0, green: 0, blue: 0};
|
|
const Cyan: RGB = {red: 0, green: 255, blue: 255};
|
|
const Magenta: RGB = {red: 255, green: 0, blue: 255};
|
|
const Orange: RGB = {red: 255, green: 130, blue: 0};
|
|
const colorSpotify: RGB = {red: 30, green: 215, blue: 96};
|
|
const colorAlexa: RGB = {red: 49, green: 196, blue: 243};
|
|
const colorSonos: RGB = {red: 216, green: 161, blue: 88};
|
|
const colorRadio: RGB = {red: 255, green: 127, blue: 0};
|
|
const BatteryFull: RGB = {red: 96, green: 176, blue: 62};
|
|
const BatteryEmpty: RGB = {red: 179, green: 45, blue: 25};
|
|
|
|
//Menu Icon Colors
|
|
const Menu: RGB = {red: 150, green: 150, blue: 100};
|
|
const MenuLowInd: RGB = {red: 255, green: 235, blue: 156};
|
|
const MenuHighInd: RGB = {red: 251, green: 105, blue: 98};
|
|
|
|
//Dynamische Indikatoren (Abstufung grün nach gelb nach rot)
|
|
const colorScale0: RGB = {red: 99, green: 190, blue: 123};
|
|
const colorScale1: RGB = {red: 129, green: 199, blue: 126};
|
|
const colorScale2: RGB = {red: 161, green: 208, blue: 127};
|
|
const colorScale3: RGB = {red: 129, green: 217, blue: 126};
|
|
const colorScale4: RGB = {red: 222, green: 226, blue: 131};
|
|
const colorScale5: RGB = {red: 254, green: 235, blue: 132};
|
|
const colorScale6: RGB = {red: 255, green: 210, blue: 129};
|
|
const colorScale7: RGB = {red: 251, green: 185, blue: 124};
|
|
const colorScale8: RGB = {red: 251, green: 158, blue: 117};
|
|
const colorScale9: RGB = {red: 248, green: 131, blue: 111};
|
|
const colorScale10: RGB = {red: 248, green: 105, blue: 107};
|
|
|
|
//Screensaver Default Theme Colors
|
|
const scbackground: RGB = {red: 0, green: 0, blue: 0};
|
|
const scbackgroundInd1: RGB = {red: 255, green: 0, blue: 0};
|
|
const scbackgroundInd2: RGB = {red: 121, green: 222, blue: 121};
|
|
const scbackgroundInd3: RGB = {red: 255, green: 255, blue: 0};
|
|
const sctime: RGB = {red: 255, green: 255, blue: 255};
|
|
const sctimeAMPM: RGB = {red: 255, green: 255, blue: 255};
|
|
const scdate: RGB = {red: 255, green: 255, blue: 255};
|
|
const sctMainIcon: RGB = {red: 255, green: 255, blue: 255};
|
|
const sctMainText: RGB = {red: 255, green: 255, blue: 255};
|
|
const sctForecast1: RGB = {red: 255, green: 255, blue: 255};
|
|
const sctForecast2: RGB = {red: 255, green: 255, blue: 255};
|
|
const sctForecast3: RGB = {red: 255, green: 255, blue: 255};
|
|
const sctForecast4: RGB = {red: 255, green: 255, blue: 255};
|
|
const sctF1Icon: RGB = {red: 255, green: 235, blue: 156};
|
|
const sctF2Icon: RGB = {red: 255, green: 235, blue: 156};
|
|
const sctF3Icon: RGB = {red: 255, green: 235, blue: 156};
|
|
const sctF4Icon: RGB = {red: 255, green: 235, blue: 156};
|
|
const sctForecast1Val: RGB = {red: 255, green: 255, blue: 255};
|
|
const sctForecast2Val: RGB = {red: 255, green: 255, blue: 255};
|
|
const sctForecast3Val: RGB = {red: 255, green: 255, blue: 255};
|
|
const sctForecast4Val: RGB = {red: 255, green: 255, blue: 255};
|
|
const scbar: RGB = {red: 255, green: 255, blue: 255};
|
|
const sctMainIconAlt: RGB = {red: 255, green: 255, blue: 255};
|
|
const sctMainTextAlt: RGB = {red: 255, green: 255, blue: 255};
|
|
const sctTimeAdd: RGB = {red: 255, green: 255, blue: 255};
|
|
|
|
//Auto-Weather-Colors
|
|
const swClearNight: RGB = {red: 150, green: 150, blue: 100};
|
|
const swCloudy: RGB = {red: 75, green: 75, blue: 75};
|
|
const swExceptional: RGB = {red: 255, green: 50, blue: 50};
|
|
const swFog: RGB = {red: 150, green: 150, blue: 150};
|
|
const swHail: RGB = {red: 200, green: 200, blue: 200};
|
|
const swLightning: RGB = {red: 200, green: 200, blue: 0};
|
|
const swLightningRainy: RGB = {red: 200, green: 200, blue: 150};
|
|
const swPartlycloudy: RGB = {red: 150, green: 150, blue: 150};
|
|
const swPouring: RGB = {red: 50, green: 50, blue: 255};
|
|
const swRainy: RGB = {red: 100, green: 100, blue: 255};
|
|
const swSnowy: RGB = {red: 150, green: 150, blue: 150};
|
|
const swSnowyRainy: RGB = {red: 150, green: 150, blue: 255};
|
|
const swSunny: RGB = {red: 255, green: 255, blue: 0};
|
|
const swWindy: RGB = {red: 150, green: 150, blue: 150};
|
|
|
|
|
|
/***** 5. Script - Parameters *****/
|
|
|
|
// DE: Für diese Option muss der Haken in setObjects in deiner javascript.X. Instanz gesetzt sein.
|
|
// EN: This option requires the check mark in setObjects in your javascript.X. instance must be set.
|
|
const autoCreateAlias = true;
|
|
|
|
// DE: Verzeichnis für Auto-Aliase (wird per Default aus dem NSPanel-Verzeichnis gebildet und muss nicht verändert werden)
|
|
// EN: Directory for auto aliases (is created by default from the NSPanel directory and does not need to be changed)
|
|
const AliasPath: string = 'alias.0.' + NSPanel_Path.substring(13, NSPanel_Path.length);
|
|
|
|
// DE: Default-Farbe für Off-Zustände
|
|
// EN: Default color for off states
|
|
const defaultOffColorParam: any = Off;
|
|
|
|
// DE: Default-Farbe für On-Zustände
|
|
// EN: Default color for on states
|
|
const defaultOnColorParam: any = On;
|
|
|
|
const defaultColorParam: any = Off;
|
|
|
|
// DE: Default-Hintergrundfarbe HMIDark oder Black
|
|
// EN: Default background color HMIDark or Black
|
|
const defaultBackgroundColorParam: any = HMIDark;
|
|
|
|
/******************************** End CONFIG Parameter ********************************/
|
|
|
|
//-- Anfang für eigene Seiten -- z.T. selbstdefinierte Aliase erforderlich ----------------
|
|
//-- Start for your own pages -- some self-defined aliases required ----------------
|
|
|
|
//-- https://github.com/joBr99/nspanel-lovelace-ui/wiki/NSPanel-Page-%E2%80%90-Typen_How-2_Beispiele
|
|
|
|
//-- ENDE für eigene Seiten -- z.T. selbstdefinierte Aliase erforderlich -------------------------
|
|
//-- END for your own pages -- some self-defined aliases required ------------------------
|
|
|
|
|
|
/***********************************************************************************************
|
|
** Service Pages mit Auto-Alias (Nachfolgende Seiten werden mit Alias automatisch angelegt) **
|
|
** https://github.com/joBr99/nspanel-lovelace-ui/wiki/NSPanel-Service-Men%C3%BC **
|
|
***********************************************************************************************/
|
|
|
|
/* DE: German
|
|
Wenn das Service Menü abgesichert werden soll, kann eine cardUnlock vorgeschaltet werden.
|
|
Für diesen Fall ist folgende Vorgehensweise erforderlich:
|
|
- cardUnlock Seite "Unlock_Service" in der Config unter pages auskommentieren ("//" entfernen)
|
|
- Servicemenü aus pages "NSPanel_Service" unter pages kommentieren ("//" hinzufügen)
|
|
*/
|
|
|
|
/***********************************************************************************************
|
|
** Service pages with auto alias (subsequent pages are automatically created with alias) **
|
|
** https://github.com/joBr99/nspanel-lovelace-ui/wiki/NSPanel-Service-Men%C3%BC **
|
|
***********************************************************************************************/
|
|
|
|
/* EN: English
|
|
If the service menu needs to be secured, a cardUnlock can be installed upstream.
|
|
In this case, the following procedure is required:
|
|
- comment out cardUnlock page "Unlock_Service" in the config under pages (remove "//")
|
|
- Comment service menu from pages "NSPanel_Service" under pages (add "//")
|
|
*/
|
|
|
|
//Level 0 (if service pages are used with cardUnlock)
|
|
let Unlock_Service: PageType =
|
|
{
|
|
'type': 'cardUnlock',
|
|
'heading': findLocaleServMenu('service_pages'),
|
|
'useColor': true,
|
|
'items': [/*PageItem*/{
|
|
id: 'alias.0.NSPanel.Unlock',
|
|
targetPage: 'NSPanel_Service_SubPage',
|
|
autoCreateALias: true
|
|
}
|
|
]
|
|
};
|
|
|
|
//Level_0 (if service pages are used without cardUnlock)
|
|
let NSPanel_Service: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('service_menu'),
|
|
'useColor': true,
|
|
'items': [
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_Infos', icon: 'information-outline', offColor: Menu, onColor: Menu, name: findLocaleServMenu('infos'), buttonText: findLocaleServMenu('more')},
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_Einstellungen', icon: 'monitor-edit', offColor: Menu, onColor: Menu, name: findLocaleServMenu('settings'), buttonText: findLocaleServMenu('more')},
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_Firmware', icon: 'update', offColor: Menu, onColor: Menu, name: findLocaleServMenu('firmware'), buttonText: findLocaleServMenu('more')},
|
|
/*PageItem*/{id: AliasPath + 'Config.rebootNSPanel', name: findLocaleServMenu('reboot'), icon: 'refresh', offColor: MSRed, onColor: MSGreen, buttonText: findLocaleServMenu('start')},
|
|
]
|
|
};
|
|
|
|
//Level_0 (if service pages are used with cardUnlock)
|
|
let NSPanel_Service_SubPage: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('service_menu'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': Unlock_Service,
|
|
'home': 'Unlock_Service',
|
|
'items': [
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_Infos', icon: 'information-outline', offColor: Menu, onColor: Menu, name: findLocaleServMenu('infos'), buttonText: findLocaleServMenu('more')},
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_Einstellungen', icon: 'monitor-edit', offColor: Menu, onColor: Menu, name: findLocaleServMenu('settings'), buttonText: findLocaleServMenu('more')},
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_Firmware', icon: 'update', offColor: Menu, onColor: Menu, name: findLocaleServMenu('firmware'), buttonText: findLocaleServMenu('more')},
|
|
/*PageItem*/{id: AliasPath + 'Config.rebootNSPanel', name: findLocaleServMenu('reboot'), icon: 'refresh', offColor: MSRed, onColor: MSGreen, buttonText: findLocaleServMenu('start')},
|
|
]
|
|
};
|
|
|
|
//Level_1
|
|
let NSPanel_Infos: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('nspanel_infos'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': NSPanel_Service,
|
|
'home': 'NSPanel_Service',
|
|
'items': [
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_Wifi_Info_1', icon: 'wifi', offColor: Menu, onColor: Menu, name: findLocaleServMenu('wifi'), buttonText: findLocaleServMenu('more')},
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_Sensoren', icon: 'memory', offColor: Menu, onColor: Menu, name: findLocaleServMenu('sensors_hardware'), buttonText: findLocaleServMenu('more')},
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_IoBroker', icon: 'information-outline', offColor: Menu, onColor: Menu, name: findLocaleServMenu('info_iobroker'), buttonText: findLocaleServMenu('more')},
|
|
/*PageItem*/{id: AliasPath + 'Config.Update.UpdateMessage', name: findLocaleServMenu('update_message'), icon: 'message-alert-outline', offColor: HMIOff, onColor: MSGreen},
|
|
]
|
|
};
|
|
//Level_2
|
|
let NSPanel_Wifi_Info_1: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('nspanel_wifi1'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': NSPanel_Infos,
|
|
'next': 'NSPanel_Wifi_Info_2',
|
|
'items': [
|
|
/*PageItem*/{id: AliasPath + 'ipAddress', name: findLocaleServMenu('ip_address'), icon: 'ip-network-outline', offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: AliasPath + 'Tasmota.Wifi.BSSId', name: findLocaleServMenu('mac_address'), icon: 'check-network', offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: AliasPath + 'Tasmota.Wifi.RSSI', name: findLocaleServMenu('rssi'), icon: 'signal', unit: '%', colorScale: {'val_min': 100, 'val_max': 0}},
|
|
/*PageItem*/{id: AliasPath + 'Tasmota.Wifi.Signal', name: findLocaleServMenu('wifi_signal'), icon: 'signal-distance-variant', unit: 'dBm', colorScale: {'val_min': 0, 'val_max': -100}},
|
|
]
|
|
};
|
|
|
|
let NSPanel_Wifi_Info_2: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('nspanel_wifi2'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'prev': 'NSPanel_Wifi_Info_1',
|
|
'home': 'NSPanel_Service',
|
|
'items': [
|
|
/*PageItem*/{id: AliasPath + 'Tasmota.Wifi.SSId', name: findLocaleServMenu('ssid'), icon: 'signal-distance-variant', offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: AliasPath + 'Tasmota.Wifi.Mode', name: findLocaleServMenu('mode'), icon: 'signal-distance-variant', offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: AliasPath + 'Tasmota.Wifi.Channel', name: findLocaleServMenu('channel'), icon: 'timeline-clock-outline', offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: AliasPath + 'Tasmota.Wifi.AP', name: findLocaleServMenu('accesspoint'), icon: 'router-wireless-settings', offColor: Menu, onColor: Menu},
|
|
]
|
|
};
|
|
|
|
let NSPanel_Sensoren: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('sensors1'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': NSPanel_Infos,
|
|
'next': 'NSPanel_Hardware',
|
|
'items': [
|
|
/*PageItem*/{id: AliasPath + 'Sensor.ANALOG.Temperature', name: findLocaleServMenu('room_temperature'), icon: 'home-thermometer-outline', unit: '°C', colorScale: {'val_min': 0, 'val_max': 40, 'val_best': 22}},
|
|
/*PageItem*/{id: AliasPath + 'Sensor.ESP32.Temperature', name: findLocaleServMenu('esp_temperature'), icon: 'thermometer', unit: '°C', colorScale: {'val_min': 0, 'val_max': 100, 'val_best': 50}},
|
|
/*PageItem*/{id: AliasPath + 'Sensor.TempUnit', name: findLocaleServMenu('temperature_unit'), icon: 'temperature-celsius', offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: AliasPath + 'Sensor.Time', name: findLocaleServMenu('refresh'), icon: 'clock-check-outline', offColor: Menu, onColor: Menu},
|
|
]
|
|
};
|
|
|
|
let NSPanel_Hardware: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('hardware2'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'prev': 'NSPanel_Sensoren',
|
|
'home': 'NSPanel_Service',
|
|
'items': [
|
|
/*PageItem*/{id: AliasPath + 'Tasmota.Product', name: findLocaleServMenu('product'), icon: 'devices', offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: AliasPath + 'Tasmota.Hardware', name: findLocaleServMenu('esp32_hardware'), icon: 'memory', offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: AliasPath + 'Display.Model', name: findLocaleServMenu('nspanel_version'), offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: AliasPath + 'Tasmota.Uptime', name: findLocaleServMenu('operating_time'), icon: 'timeline-clock-outline', offColor: Menu, onColor: Menu},
|
|
]
|
|
};
|
|
|
|
let NSPanel_IoBroker: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('info_iobroker'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': NSPanel_Infos,
|
|
'home': 'NSPanel_Service',
|
|
'items': [
|
|
/*PageItem*/{id: AliasPath + 'IoBroker.ScriptVersion', name: findLocaleServMenu('script_version_nspanelts'), offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: AliasPath + 'IoBroker.NodeJSVersion', name: findLocaleServMenu('nodejs_version'), offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: AliasPath + 'IoBroker.JavaScriptVersion', name: findLocaleServMenu('instance_javascript'), offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: AliasPath + 'IoBroker.ScriptName', name: findLocaleServMenu('scriptname'), offColor: Menu, onColor: Menu},
|
|
]
|
|
};
|
|
|
|
//Level_1
|
|
let NSPanel_Einstellungen: PageType =
|
|
{
|
|
'type': 'cardGrid',
|
|
'heading': findLocaleServMenu('settings'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': NSPanel_Service,
|
|
'home': 'NSPanel_Service',
|
|
'items': [
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_Screensaver', icon: 'monitor-dashboard', offColor: Menu, onColor: Menu, name: findLocaleServMenu('screensaver'), buttonText: findLocaleServMenu('more')},
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_Relays', icon: 'electric-switch', offColor: Menu, onColor: Menu, name: findLocaleServMenu('relays'), buttonText: findLocaleServMenu('more')},
|
|
/*PageItem*/{
|
|
id: AliasPath + 'Config.temperatureUnitNumber', icon: 'gesture-double-tap', name: findLocaleServMenu('temp_unit'), offColor: Menu, onColor: Menu,
|
|
modeList: ['°C', '°F', 'K']
|
|
},
|
|
/*PageItem*/{
|
|
id: AliasPath + 'Config.localeNumber', icon: 'select-place', name: findLocaleServMenu('language'), offColor: Menu, onColor: Menu,
|
|
modeList: ['en-US', 'de-DE', 'nl-NL', 'da-DK', 'es-ES', 'fr-FR', 'it-IT', 'ru-RU', 'nb-NO', 'nn-NO', 'pl-PL', 'pt-PT', 'af-ZA', 'ar-SY',
|
|
'bg-BG', 'ca-ES', 'cs-CZ', 'el-GR', 'et-EE', 'fa-IR', 'fi-FI', 'he-IL', 'hr-xx', 'hu-HU', 'hy-AM', 'id-ID', 'is-IS', 'lb-xx',
|
|
'lt-LT', 'ro-RO', 'sk-SK', 'sl-SI', 'sv-SE', 'th-TH', 'tr-TR', 'uk-UA', 'vi-VN', 'zh-CN', 'zh-TW']
|
|
},
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_Script', icon: 'code-json', offColor: Menu, onColor: Menu, name: findLocaleServMenu('script'), buttonText: findLocaleServMenu('more')},
|
|
]
|
|
};
|
|
|
|
//Level_2
|
|
let NSPanel_Screensaver: PageType =
|
|
{
|
|
'type': 'cardGrid',
|
|
'heading': findLocaleServMenu('screensaver'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': NSPanel_Einstellungen,
|
|
'home': 'NSPanel_Service',
|
|
'items': [
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_ScreensaverDimmode', icon: 'sun-clock', offColor: Menu, onColor: Menu, name: findLocaleServMenu('dimmode')},
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_ScreensaverBrightness', icon: 'brightness-5', offColor: Menu, onColor: Menu, name: findLocaleServMenu('brightness')},
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_ScreensaverLayout', icon: 'page-next-outline', offColor: Menu, onColor: Menu, name: findLocaleServMenu('layout')},
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_ScreensaverWeather', icon: 'weather-partly-rainy', offColor: Menu, onColor: Menu, name: findLocaleServMenu('weather')},
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_ScreensaverDateformat', icon: 'calendar-expand-horizontal', offColor: Menu, onColor: Menu, name: findLocaleServMenu('date_format')},
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_ScreensaverIndicators', icon: 'monitor-edit', offColor: Menu, onColor: Menu, name: findLocaleServMenu('indicators')}
|
|
]
|
|
};
|
|
|
|
//Level_3
|
|
let NSPanel_ScreensaverDimmode: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('dimmode'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': NSPanel_Screensaver,
|
|
'home': 'NSPanel_Service',
|
|
'items': [
|
|
/*PageItem*/{id: AliasPath + 'Dimmode.brightnessDay', name: findLocaleServMenu('brightness_day'), icon: 'brightness-5', offColor: Menu, onColor: Menu, minValue: 5, maxValue: 10},
|
|
/*PageItem*/{id: AliasPath + 'Dimmode.brightnessNight', name: findLocaleServMenu('brightness_night'), icon: 'brightness-4', offColor: Menu, onColor: Menu, minValue: 0, maxValue: 4},
|
|
/*PageItem*/{id: AliasPath + 'Dimmode.hourDay', name: findLocaleServMenu('hour_day'), icon: 'sun-clock', offColor: Menu, onColor: Menu, minValue: 0, maxValue: 23},
|
|
/*PageItem*/{id: AliasPath + 'Dimmode.hourNight', name: findLocaleServMenu('hour_night'), icon: 'sun-clock-outline', offColor: Menu, onColor: Menu, minValue: 0, maxValue: 23}
|
|
]
|
|
};
|
|
|
|
//Level_3
|
|
let NSPanel_ScreensaverBrightness: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('brightness'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': NSPanel_Screensaver,
|
|
'home': 'NSPanel_Service',
|
|
'items': [
|
|
/*PageItem*/{id: AliasPath + 'ScreensaverInfo.activeBrightness', name: findLocaleServMenu('brightness_activ'), icon: 'brightness-5', offColor: Menu, onColor: Menu, minValue: 20, maxValue: 100},
|
|
/*PageItem*/{id: AliasPath + 'Config.Screensaver.timeoutScreensaver', name: findLocaleServMenu('screensaver_timeout'), icon: 'clock-end', offColor: Menu, onColor: Menu, minValue: 0, maxValue: 60},
|
|
/*PageItem*/{id: AliasPath + 'Config.Screensaver.screenSaverDoubleClick', name: findLocaleServMenu('wakeup_doublecklick'), icon: 'gesture-two-double-tap', offColor: HMIOff, onColor: HMIOn}
|
|
]
|
|
};
|
|
|
|
//Level_3
|
|
let NSPanel_ScreensaverLayout: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('layout'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': NSPanel_Screensaver,
|
|
'home': 'NSPanel_Service',
|
|
'items': [
|
|
/*PageItem*/{id: AliasPath + 'Config.Screensaver.alternativeScreensaverLayout', name: findLocaleServMenu('alternative_layout'), icon: 'page-previous-outline', offColor: HMIOff, onColor: HMIOn},
|
|
/*PageItem*/{id: AliasPath + 'Config.Screensaver.ScreensaverAdvanced', name: findLocaleServMenu('advanced_layout'), icon: 'page-next-outline', offColor: HMIOff, onColor: HMIOn},
|
|
/*PageItem*/{id: AliasPath + 'Config.Screensaver.ScreensaverEasyView', name: findLocaleServMenu('easyview_layout'), icon: 'page-next-outline', offColor: HMIOff, onColor: HMIOn},
|
|
]
|
|
};
|
|
|
|
//Level_3
|
|
let NSPanel_ScreensaverWeather: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('weather_parameters'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': NSPanel_Screensaver,
|
|
'home': 'NSPanel_Service',
|
|
'items': [
|
|
/*PageItem*/{id: AliasPath + 'ScreensaverInfo.weatherForecast', name: findLocaleServMenu('weather_forecast_offon'), icon: 'weather-sunny-off', offColor: HMIOff, onColor: HMIOn},
|
|
/*PageItem*/{id: AliasPath + 'ScreensaverInfo.weatherForecastTimer', name: findLocaleServMenu('weather_forecast_change_switch'), icon: 'devices', offColor: HMIOff, onColor: HMIOn},
|
|
/*PageItem*/{id: AliasPath + 'ScreensaverInfo.entityChangeTime', name: findLocaleServMenu('weather_forecast_change_time'), icon: 'cog-sync', offColor: Menu, onColor: Menu, minValue: 15, maxValue: 60},
|
|
/*PageItem*/{id: AliasPath + 'Config.Screensaver.autoWeatherColorScreensaverLayout', name: findLocaleServMenu('weather_forecast_icon_colors'), icon: 'format-color-fill', offColor: HMIOff, onColor: HMIOn},
|
|
]
|
|
};
|
|
|
|
//Level_3
|
|
let NSPanel_ScreensaverDateformat: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('date_format'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': NSPanel_Screensaver,
|
|
'home': 'NSPanel_Service',
|
|
'items': [
|
|
/*PageItem*/{id: AliasPath + 'Config.Dateformat.Switch.weekday', name: findLocaleServMenu('weekday_large'), icon: 'calendar-expand-horizontal', offColor: HMIOff, onColor: HMIOn},
|
|
/*PageItem*/{id: AliasPath + 'Config.Dateformat.Switch.month', name: findLocaleServMenu('month_large'), icon: 'calendar-expand-horizontal', offColor: HMIOff, onColor: HMIOn},
|
|
]
|
|
};
|
|
|
|
//Level_3
|
|
let NSPanel_ScreensaverIndicators: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('indicators'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': NSPanel_Screensaver,
|
|
'home': 'NSPanel_Service',
|
|
'items': [
|
|
/*PageItem*/{id: AliasPath + 'Config.MRIcons.alternateMRIconSize.1', name: findLocaleServMenu('mr_icon1_size'), icon: 'format-size', offColor: HMIOff, onColor: HMIOn},
|
|
/*PageItem*/{id: AliasPath + 'Config.MRIcons.alternateMRIconSize.2', name: findLocaleServMenu('mr_icon2_size'), icon: 'format-size', offColor: HMIOff, onColor: HMIOn},
|
|
]
|
|
};
|
|
|
|
//Level_2
|
|
let NSPanel_Relays: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('relays'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': NSPanel_Einstellungen,
|
|
'home': 'NSPanel_Service',
|
|
'items': [
|
|
/*PageItem*/{id: AliasPath + 'Relay.1', name: findLocaleServMenu('relay1_onoff'), icon: 'power', offColor: HMIOff, onColor: HMIOn},
|
|
/*PageItem*/{id: AliasPath + 'Relay.2', name: findLocaleServMenu('relay2_onoff'), icon: 'power', offColor: HMIOff, onColor: HMIOn},
|
|
]
|
|
};
|
|
|
|
//Level_2
|
|
let NSPanel_Script: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('script'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': NSPanel_Einstellungen,
|
|
'home': 'NSPanel_Service',
|
|
'items': [
|
|
/*PageItem*/{id: AliasPath + 'Config.ScripgtDebugStatus', name: findLocaleServMenu('debugmode_offon'), icon: 'code-tags-check', offColor: HMIOff, onColor: HMIOn},
|
|
/*PageItem*/{id: AliasPath + 'Config.MQTT.portCheck', name: findLocaleServMenu('port_check_offon'), icon: 'check-network', offColor: HMIOff, onColor: HMIOn},
|
|
/*PageItem*/{id: AliasPath + 'Config.hiddenCards', name: findLocaleServMenu('hiddencards_offon'), icon: 'check-network', offColor: HMIOff, onColor: HMIOn},
|
|
]
|
|
};
|
|
|
|
//Level_1
|
|
let NSPanel_Firmware: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('firmware'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': NSPanel_Service,
|
|
'home': 'NSPanel_Service',
|
|
'items': [
|
|
/*PageItem*/{id: AliasPath + 'autoUpdate', name: findLocaleServMenu('automatically_updates'), icon: 'power', offColor: HMIOff, onColor: HMIOn},
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_FirmwareTasmota', icon: 'usb-flash-drive', offColor: Menu, onColor: Menu, name: findLocaleServMenu('tasmota_firmware'), buttonText: findLocaleServMenu('more')},
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_FirmwareBerry', icon: 'usb-flash-drive', offColor: Menu, onColor: Menu, name: findLocaleServMenu('berry_driver'), buttonText: findLocaleServMenu('more')},
|
|
/*PageItem*/{navigate: true, id: 'NSPanel_FirmwareNextion', icon: 'cellphone-cog', offColor: Menu, onColor: Menu, name: findLocaleServMenu('nextion_tft_firmware'), buttonText: findLocaleServMenu('more')}
|
|
]
|
|
};
|
|
|
|
let NSPanel_FirmwareTasmota: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('tasmota'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': NSPanel_Firmware,
|
|
'home': 'NSPanel_Service',
|
|
'items': [
|
|
/*PageItem*/{id: AliasPath + 'Tasmota.Version', name: findLocaleServMenu('installed_release'), offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: AliasPath + 'Tasmota_Firmware.onlineVersion', name: findLocaleServMenu('available_release'), offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: 'Divider'},
|
|
/*PageItem*/{id: AliasPath + 'Config.Update.UpdateTasmota', name: findLocaleServMenu('update_tasmota'), icon: 'refresh', offColor: HMIOff, onColor: MSGreen, buttonText: findLocaleServMenu('start')},
|
|
]
|
|
};
|
|
|
|
let NSPanel_FirmwareBerry: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('berry_driver'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': NSPanel_Firmware,
|
|
'home': 'NSPanel_Service',
|
|
'items': [
|
|
/*PageItem*/{id: AliasPath + 'Display.BerryDriver', name: findLocaleServMenu('installed_release'), offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: AliasPath + 'Berry_Driver.onlineVersion', name: findLocaleServMenu('available_release'), offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: 'Divider'},
|
|
/*PageItem*/{id: AliasPath + 'Config.Update.UpdateBerry', name: findLocaleServMenu('update_berry_driver'), icon: 'refresh', offColor: HMIOff, onColor: MSGreen, buttonText: findLocaleServMenu('start')},
|
|
]
|
|
};
|
|
|
|
let NSPanel_FirmwareNextion: PageType =
|
|
{
|
|
'type': 'cardEntities',
|
|
'heading': findLocaleServMenu('nextion_tft'),
|
|
'useColor': true,
|
|
'subPage': true,
|
|
'parent': NSPanel_Firmware,
|
|
'home': 'NSPanel_Service',
|
|
'items': [
|
|
/*PageItem*/{id: AliasPath + 'Display_Firmware.TFT.currentVersion', name: findLocaleServMenu('installed_release'), offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: AliasPath + 'Display_Firmware.TFT.desiredVersion', name: findLocaleServMenu('desired_release'), offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: AliasPath + 'Display.Model', name: findLocaleServMenu('nspanel_model'), offColor: Menu, onColor: Menu},
|
|
/*PageItem*/{id: AliasPath + 'Config.Update.UpdateNextion', name: 'Nextion TFT Update', icon: 'refresh', offColor: HMIOff, onColor: MSGreen, buttonText: findLocaleServMenu('start')},
|
|
]
|
|
};
|
|
|
|
// End of Service Pages
|
|
|
|
/***********************************************************************
|
|
** **
|
|
** Configuration **
|
|
** **
|
|
***********************************************************************/
|
|
|
|
// EN: Configuration
|
|
|
|
export const config: Config = {
|
|
// Seiteneinteilung / Page division
|
|
// Hauptseiten / Mainpages
|
|
pages: [
|
|
NSPanel_Service, //Auto-Alias Service Page
|
|
//Unlock_Service //Auto-Alias Service Page (Service Pages used with cardUnlock)
|
|
],
|
|
// Unterseiten / Subpages
|
|
subPages: [
|
|
NSPanel_Service_SubPage, //Auto-Alias Service Page (only used with cardUnlock)
|
|
NSPanel_Infos, //Auto-Alias Service Page
|
|
NSPanel_Wifi_Info_1, //Auto-Alias Service Page
|
|
NSPanel_Wifi_Info_2, //Auto-Alias Service Page
|
|
NSPanel_Sensoren, //Auto-Alias Service Page
|
|
NSPanel_Hardware, //Auto-Alias Service Page
|
|
NSPanel_IoBroker, //Auto-Alias Service Page
|
|
NSPanel_Einstellungen, //Auto-Alias Service Page
|
|
NSPanel_Screensaver, //Auto-Alias Service Page
|
|
NSPanel_ScreensaverDimmode, //Auto-Alias Service Page
|
|
NSPanel_ScreensaverBrightness, //Auto-Alias Service Page
|
|
NSPanel_ScreensaverLayout, //Auto-Alias Service Page
|
|
NSPanel_ScreensaverWeather, //Auto-Alias Service Page
|
|
NSPanel_ScreensaverDateformat, //Auto-Alias Service Page
|
|
NSPanel_ScreensaverIndicators, //Auto-Alias Service Page
|
|
NSPanel_Relays, //Auto-Alias Service Page
|
|
NSPanel_Script, //Auto-Alias Service Page
|
|
NSPanel_Firmware, //Auto-Alias Service Page
|
|
NSPanel_FirmwareTasmota, //Auto-Alias Service Page
|
|
NSPanel_FirmwareBerry, //Auto-Alias Service Page
|
|
NSPanel_FirmwareNextion, //Auto-Alias Service Page
|
|
],
|
|
|
|
/***********************************************************************
|
|
** **
|
|
** Screensaver Configuration **
|
|
** **
|
|
***********************************************************************/
|
|
leftScreensaverEntity: [
|
|
// Examples for Advanced-Screensaver: https://github.com/joBr99/nspanel-lovelace-ui/wiki/ioBroker-Config-Screensaver#entity-status-icons-ab-v400
|
|
],
|
|
|
|
bottomScreensaverEntity: [
|
|
|
|
// bottomScreensaverEntity 1
|
|
{
|
|
ScreensaverEntity: 'openweathermap.0.forecast.current.pressure',
|
|
ScreensaverEntityFactor: 1,
|
|
ScreensaverEntityDecimalPlaces: 0,
|
|
ScreensaverEntityIconOn: 'gauge',
|
|
ScreensaverEntityIconOff: null,
|
|
ScreensaverEntityText: 'Druck',
|
|
ScreensaverEntityUnitText: 'hPa',
|
|
ScreensaverEntityIconColor: {'val_min': 950, 'val_max': 1050, 'val_best': 1013}
|
|
},
|
|
// bottomScreensaverEntity 2
|
|
{
|
|
ScreensaverEntity: 'openweathermap.0.forecast.current.windSpeed',
|
|
ScreensaverEntityFactor: 1,
|
|
ScreensaverEntityDecimalPlaces: 1,
|
|
ScreensaverEntityIconOn: 'weather-windy',
|
|
ScreensaverEntityIconOff: null,
|
|
ScreensaverEntityText: "Wind",
|
|
ScreensaverEntityUnitText: 'm/s',
|
|
ScreensaverEntityIconColor: {'val_min': 0, 'val_max': 120}
|
|
},
|
|
// bottomScreensaverEntity 3
|
|
{
|
|
ScreensaverEntity: 'openweathermap.0.forecast.current.windGust',
|
|
ScreensaverEntityFactor: 1,
|
|
ScreensaverEntityDecimalPlaces: 1,
|
|
ScreensaverEntityIconOn: 'weather-tornado',
|
|
ScreensaverEntityIconOff: null,
|
|
ScreensaverEntityText: 'Böen',
|
|
ScreensaverEntityUnitText: 'm/s',
|
|
ScreensaverEntityIconColor: {'val_min': 0, 'val_max': 120}
|
|
},
|
|
|
|
// bottomScreensaverEntity 4
|
|
{
|
|
ScreensaverEntity: 'openweathermap.0.forecast.current.clouds',
|
|
ScreensaverEntityFactor: 1,
|
|
ScreensaverEntityDecimalPlaces: 0,
|
|
ScreensaverEntityIconOn: 'weather-cloudy',
|
|
ScreensaverEntityIconOff: null,
|
|
ScreensaverEntityText: 'Wolken',
|
|
ScreensaverEntityUnitText: '%',
|
|
ScreensaverEntityIconColor: {'val_min': 0, 'val_max': 100}
|
|
},
|
|
// bottomScreensaverEntity 5 (for Alternative and Advanced Screensaver)
|
|
{
|
|
ScreensaverEntity: 'openweathermap.0.forecast.current.humidity',
|
|
ScreensaverEntityFactor: 1,
|
|
ScreensaverEntityDecimalPlaces: 1,
|
|
ScreensaverEntityIconOn: 'water-percent',
|
|
ScreensaverEntityIconOff: null,
|
|
ScreensaverEntityText: 'Feuchte',
|
|
ScreensaverEntityUnitText: '%',
|
|
ScreensaverEntityIconColor: {'val_min': 0, 'val_max': 100, 'val_best': 65}
|
|
},
|
|
// bottomScreensaverEntity 6 (for Advanced Screensaver)
|
|
{
|
|
ScreensaverEntity: NSPanel_Path + 'Relay.1',
|
|
ScreensaverEntityIconOn: 'coach-lamp-variant',
|
|
ScreensaverEntityText: 'Street',
|
|
ScreensaverEntityOnColor: Yellow,
|
|
ScreensaverEntityOffColor: White,
|
|
ScreensaverEntityOnText: 'Is ON',
|
|
ScreensaverEntityOffText: 'Not ON'
|
|
},
|
|
// Examples for Advanced-Screensaver: https://github.com/joBr99/nspanel-lovelace-ui/wiki/ioBroker-Config-Screensaver#entity-status-icons-ab-v400
|
|
],
|
|
|
|
indicatorScreensaverEntity: [
|
|
// Examples for Advanced-Screensaver: https://github.com/joBr99/nspanel-lovelace-ui/wiki/ioBroker-Config-Screensaver#entity-status-icons-ab-v400
|
|
],
|
|
|
|
// Status Icon
|
|
mrIcon1ScreensaverEntity: {
|
|
ScreensaverEntity: NSPanel_Path + 'Relay.1',
|
|
ScreensaverEntityIconOn: 'lightbulb',
|
|
ScreensaverEntityIconOff: null,
|
|
ScreensaverEntityValue: null,
|
|
ScreensaverEntityValueDecimalPlace: 0,
|
|
ScreensaverEntityValueUnit: null,
|
|
ScreensaverEntityOnColor: On,
|
|
ScreensaverEntityOffColor: HMIOff
|
|
},
|
|
mrIcon2ScreensaverEntity: {
|
|
ScreensaverEntity: NSPanel_Path + 'Relay.2',
|
|
ScreensaverEntityIconOn: 'lightbulb',
|
|
ScreensaverEntityIconOff: null,
|
|
ScreensaverEntityValue: null,
|
|
ScreensaverEntityValueDecimalPlace: 0,
|
|
ScreensaverEntityValueUnit: null,
|
|
ScreensaverEntityOnColor: On,
|
|
ScreensaverEntityOffColor: HMIOff
|
|
},
|
|
// ------ DE: Ende der Screensaver Einstellungen --------------------
|
|
// ------ EN: End of screensaver settings ---------------------------
|
|
|
|
//-------DE: Anfang Einstellungen für Hardware Button, wenn Sie softwareseitig genutzt werden (Rule2) -------------
|
|
//-------EN: Start Settings for Hardware Button, if used in software (Rule2) --------------------------------------
|
|
// DE: Konfiguration des linken Schalters des NSPanels
|
|
// EN: Configuration of the left switch of the NSPanel
|
|
button1: {
|
|
// DE: Mögliche Werte wenn Rule2 definiert: 'page', 'toggle', 'set' - Wenn nicht definiert --> mode: null
|
|
// EN: Possible values if Rule2 defined: 'page', 'toggle', 'set' - If not defined --> mode: null
|
|
mode: null,
|
|
// DE: Zielpage - Verwendet wenn mode = page
|
|
// EN: Target page - Used if mode = page
|
|
page: null,
|
|
// DE: Zielentity - Verwendet wenn mode = set oder toggle
|
|
// EN: Target entity - Used if mode = set or toggle
|
|
entity: null,
|
|
// DE: Zielwert - Verwendet wenn mode = set
|
|
// EN: Target value - Used if mode = set
|
|
setValue: null
|
|
},
|
|
|
|
// DE: Konfiguration des rechten Schalters des NSPanels
|
|
// EN: Configuration of the right switch of the NSPanel
|
|
button2: {
|
|
mode: null,
|
|
page: null,
|
|
entity: null,
|
|
setValue: null
|
|
},
|
|
|
|
//--------- DE: Ende - Einstellungen für Hardware Button, wenn Sie softwareseitig genutzt werden (Rule2) -------------
|
|
//--------- EN: End - settings for hardware button if they are used in software (Rule2) ------------------------------
|
|
|
|
// DE: WICHTIG !! Parameter nicht ändern WICHTIG!!
|
|
// EN: IMPORTANT !! Do not change parameters IMPORTANT!!
|
|
panelRecvTopic: NSPanelReceiveTopic,
|
|
panelSendTopic: NSPanelSendTopic,
|
|
weatherEntity: weatherEntityPath,
|
|
defaultOffColor: defaultOffColorParam,
|
|
defaultOnColor: defaultOnColorParam,
|
|
defaultColor: defaultColorParam,
|
|
defaultBackgroundColor: defaultBackgroundColorParam,
|
|
};
|
|
|
|
// _________________________________ DE: Ab hier keine Konfiguration mehr _____________________________________
|
|
// _________________________________ EN: No more configuration from here _____________________________________
|
|
|
|
const scriptVersion: string = 'v5.0.2.1';
|
|
const tft_version: string = 'v5.0.2';
|
|
const desired_display_firmware_version = 59;
|
|
const berry_driver_version = 10;
|
|
|
|
const tasmotaOtaUrl: string = 'http://ota.tasmota.com/tasmota32/release/';
|
|
// @ts-ignore
|
|
const Icons = new IconsSelector();
|
|
let timeoutSlider: any;
|
|
let vwIconColor: number[] = [];
|
|
let weatherForecast: boolean;
|
|
let pageCounter: number = 0;
|
|
let alwaysOn: boolean = false;
|
|
let valueHiddenCards: boolean = false;
|
|
if (existsState(NSPanel_Path + 'Config.hiddenCards')) {
|
|
valueHiddenCards = getState(NSPanel_Path + 'Config.hiddenCards').val;
|
|
}
|
|
|
|
let buttonToggleState: {[key: string]: boolean} = {};
|
|
|
|
const axios = require('axios');
|
|
const moment = require('moment');
|
|
const parseFormat = require('moment-parseformat');
|
|
let firstRun: boolean = false;
|
|
if (existsState(NSPanel_Path + 'Config.locale')) {
|
|
moment.locale(getState(NSPanel_Path + 'Config.locale').val);
|
|
} else {
|
|
moment.locale('en-US');
|
|
firstRun = true;
|
|
}
|
|
|
|
const scheduleList: {[key: string]: any} = {};
|
|
|
|
const globalTextColor: any = White;
|
|
let checkBlindActive: boolean = false;
|
|
|
|
log('--- start of NsPanelTs: ' + NSPanel_Path + ' ---', 'info');
|
|
|
|
async function Init_momentjs () {
|
|
try {
|
|
|
|
moment.locale(`'${getMomentjsLocale()}'`);
|
|
|
|
} catch (err: any) {
|
|
log('error at function init_momentjs: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
Init_momentjs();
|
|
|
|
let useMediaEvents: boolean = false;
|
|
let timeoutMedia: any;
|
|
let timeoutPower: any;
|
|
let bgColorScrSaver: number = 0;
|
|
let globalTracklist: any;
|
|
let weatherAdapterInstanceNumber: number = 0;
|
|
let isSetOptionActive: boolean = false;
|
|
|
|
let nodeVersion: string = '';
|
|
let javaScriptVersion: string = '';
|
|
|
|
let scheduleInitDimModeDay: any;
|
|
let scheduleInitDimModeNight: any;
|
|
|
|
const ScreensaverAdvancedEndPath = 'Config.Screensaver.ScreensaverAdvanced';
|
|
const ScreensaverEasyViewEndPath = 'Config.Screensaver.ScreensaverEasyView';
|
|
|
|
onStop(function scriptStop () {
|
|
if (scheduleSendTime != null) _clearSchedule(scheduleSendTime);
|
|
if (scheduleSendDate != null) _clearSchedule(scheduleSendDate);
|
|
if (scheduleSwichScreensaver != null) _clearSchedule(scheduleSwichScreensaver);
|
|
if (scheduleCheckUpdates != null) _clearSchedule(scheduleCheckUpdates);
|
|
if (scheduleInitDimModeDay != null) _clearSchedule(scheduleInitDimModeDay);
|
|
if (scheduleInitDimModeNight != null) _clearSchedule(scheduleInitDimModeNight);
|
|
UnsubscribeWatcher();
|
|
}, 1000);
|
|
|
|
/**
|
|
* Checks all necessary parameters, objects and adapters for the script.
|
|
* If one is not reachable, an error message is written to the log.
|
|
* @function
|
|
*/
|
|
async function CheckConfigParameters () {
|
|
try {
|
|
if (existsObject(config.panelRecvTopic) == false) {
|
|
log('Config-Parameter: << config.panelRecvTopic - ' + config.panelRecvTopic + ' >> is not reachable. Please Check Parameters!', 'error');
|
|
}
|
|
if (config.panelRecvTopic.indexOf('.tele.') < 0) {
|
|
log('Config-Parameter: << config.panelRecvTopic - ' + config.panelRecvTopic + ' >> does not refer to the prefix .tele. Please Check Parameters!', 'error');
|
|
}
|
|
|
|
if (existsObject(config.panelSendTopic) == false) {
|
|
const n = config.panelSendTopic.split('.');
|
|
const a = n.shift();
|
|
const i = n.shift();
|
|
|
|
if (a && a.substring(0, 4) === 'mqtt' && !isNaN(Number(i))) {
|
|
sendTo(`${a}.${i}`, 'sendMessage2Client', {topic: n.join('/'), message: buildNSPanelString('time', '12:00')});
|
|
await sleep(500);
|
|
}
|
|
if ((await existsObjectAsync(config.panelSendTopic)) == false) {
|
|
log('Config-Parameter: << config.panelSendTopic - ' + config.panelSendTopic + ' >> is not reachable. Please Check Parameters!', 'error');
|
|
stopScript(scriptName);
|
|
}
|
|
}
|
|
if (weatherAdapterInstance.substring(0, weatherAdapterInstance.length - 3) == 'daswetter') {
|
|
if (existsObject(weatherAdapterInstance + 'NextHours.Location_1.Day_1.current.symbol_value') == false) {
|
|
log('Weather adapter: << weatherAdapterInstance - ' + weatherAdapterInstance + ' >> is not installed. Please Check Adapter!', 'error');
|
|
}
|
|
}
|
|
if (weatherAdapterInstance.substring(0, weatherAdapterInstance.length - 3) == 'accuweather') {
|
|
if (existsObject(weatherAdapterInstance + 'Current.WeatherIcon') == false) {
|
|
log('Weather adapter: << weatherAdapterInstance - ' + weatherAdapterInstance + ' >> is not installed. Please Check Adapter!', 'error');
|
|
}
|
|
}
|
|
if (weatherAdapterInstance.substring(0, weatherAdapterInstance.length - 3) == 'openweathermap') {
|
|
if (existsObject(weatherAdapterInstance + 'forecast.current.icon') == false) {
|
|
log('Weather adapter: << weatherAdapterInstance - ' + weatherAdapterInstance + ' >> is not installed. Please Check Adapter!', 'error');
|
|
}
|
|
}
|
|
|
|
let weatherAdapterInstanceArray: any = weatherAdapterInstance.split('.');
|
|
weatherAdapterInstanceNumber = weatherAdapterInstanceArray[1];
|
|
if (Debug) log('Number of weatherAdapterInstance: ' + weatherAdapterInstanceNumber, 'info');
|
|
|
|
const adapterList = $('system.adapter.*.alive');
|
|
adapterList.each(function (id, i) {
|
|
id = id.substring(0, id.lastIndexOf('.'));
|
|
if (existsObject(id)) {
|
|
let common = getObject(id).common;
|
|
if (common.name == 'javascript') {
|
|
javaScriptVersion = common.version;
|
|
setIfExists(NSPanel_Path + 'IoBroker.JavaScriptVersion', 'v' + javaScriptVersion, null, true);
|
|
setIfExists(NSPanel_Path + 'IoBroker.ScriptName', scriptName.split('.').slice(2).join('.'), null, true);
|
|
let jsVersion = common.version.split('.');
|
|
let jsV = 10 * parseInt(jsVersion[0]) + parseInt(jsVersion[1]);
|
|
if (jsV < 61) log('JS-Adapter: ' + common.name + ' must be at least v6.1.3. Currently: v' + common.version, 'error');
|
|
}
|
|
}
|
|
});
|
|
|
|
const hostList = $('system.host.*.nodeCurrent');
|
|
hostList.each(function (id, i) {
|
|
nodeVersion = getState(id).val;
|
|
setIfExists(NSPanel_Path + 'IoBroker.NodeJSVersion', 'v' + nodeVersion, null, true);
|
|
let nodeJSVersion = getState(id).val.split('.');
|
|
if (parseInt(nodeJSVersion[0]) < 18) {
|
|
log('nodeJS must be at least v18.X.X. Currently: v' + getState(id).val + '! Please Update your System! --> iob nodejs-update 18');
|
|
}
|
|
if (parseInt(nodeJSVersion[0]) % 2 != 0) {
|
|
log('nodeJS does not have an even version number. An odd version number is a developer version. Please correct nodeJS version', 'info');
|
|
}
|
|
});
|
|
if (config.mrIcon1ScreensaverEntity.ScreensaverEntity != null && existsObject(config.mrIcon1ScreensaverEntity.ScreensaverEntity) == false) {
|
|
if (existsState(NSPanel_Path + 'Config')) log('mrIcon1ScreensaverEntity data point in the config not available - please adjust', 'warn');
|
|
}
|
|
if (config.mrIcon2ScreensaverEntity.ScreensaverEntity != null && existsObject(config.mrIcon2ScreensaverEntity.ScreensaverEntity) == false) {
|
|
if (existsState(NSPanel_Path + 'Config')) log('mrIcon2ScreensaverEntity data point in the config not available - please adjust', 'warn');
|
|
}
|
|
if (CheckEnableSetObject()) {
|
|
log('setObjects enabled - create Alias Channels possible', 'info');
|
|
isSetOptionActive = true;
|
|
} else {
|
|
log('setObjects disabled - Please enable setObjects in JS-Adapter Instance - create Alias Channels not possible', 'warn');
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function CheckConfigParameters: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
CheckConfigParameters();
|
|
|
|
/**
|
|
* Function to create the script information data points.
|
|
* The function creates the data points for the script version, NodeJS version, JavaScript version and the script name.
|
|
* The data points are created in the NSPanel_Path + 'IoBroker.' with the corresponding name.
|
|
* The function also creates an alias for each data point with the name 'ACTUAL'.
|
|
* The data points are set to read only.
|
|
* @function
|
|
*/
|
|
async function InitIoBrokerInfo () {
|
|
try {
|
|
if (isSetOptionActive) {
|
|
// Script Version
|
|
await createStateAsync(NSPanel_Path + 'IoBroker.ScriptVersion', scriptVersion, {type: 'string', write: false});
|
|
setObject(AliasPath + 'IoBroker.ScriptVersion', {type: 'channel', common: {role: 'info', name: 'Version NSPanelTS'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'IoBroker.ScriptVersion.ACTUAL', NSPanel_Path + 'IoBroker.ScriptVersion', true, {type: 'string', role: 'state', name: 'ACTUAL'});
|
|
// NodeJS Verion
|
|
await createStateAsync(NSPanel_Path + 'IoBroker.NodeJSVersion', 'v' + nodeVersion, {type: 'string', write: false});
|
|
setObject(AliasPath + 'IoBroker.NodeJSVersion', {type: 'channel', common: {role: 'info', name: 'Version NodeJS'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'IoBroker.NodeJSVersion.ACTUAL', NSPanel_Path + 'IoBroker.NodeJSVersion', true, {type: 'string', role: 'state', name: 'ACTUAL'});
|
|
// JavaScript Version
|
|
await createStateAsync(NSPanel_Path + 'IoBroker.JavaScriptVersion', 'v' + javaScriptVersion, {type: 'string', write: false});
|
|
setObject(AliasPath + 'IoBroker.JavaScriptVersion', {type: 'channel', common: {role: 'info', name: 'Version JavaScript Instanz'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'IoBroker.JavaScriptVersion.ACTUAL', NSPanel_Path + 'IoBroker.JavaScriptVersion', true, {
|
|
type: 'string',
|
|
role: 'state',
|
|
name: 'ACTUAL',
|
|
});
|
|
// ScriptName
|
|
await createStateAsync(NSPanel_Path + 'IoBroker.ScriptName', 'v' + NSPanel_Path + 'IoBroker.ScriptName', {type: 'string', write: false});
|
|
setObject(AliasPath + 'IoBroker.ScriptName', {type: 'channel', common: {role: 'info', name: 'Scriptname'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'IoBroker.ScriptName.ACTUAL', NSPanel_Path + 'IoBroker.ScriptName', true, {type: 'string', role: 'state', name: 'ACTUAL'});
|
|
}
|
|
setIfExists(NSPanel_Path + 'IoBroker.ScriptVersion', scriptVersion, null, true);
|
|
} catch (err: any) {
|
|
log('error at funktion InitIoBrokerInfo ' + err.message, 'warn');
|
|
}
|
|
}
|
|
InitIoBrokerInfo();
|
|
|
|
/**
|
|
* Function to check the debug mode.
|
|
* If the debug mode is activated, a state NSPanel_Path + 'Config.ScripgtDebugStatus' is created.
|
|
* The state is a boolean and is set to true if the debug mode is activated.
|
|
* The state is set to false if the debug mode is disabled.
|
|
* The state is written to the object tree.
|
|
* The state is also used to set the Debug variable to true or false.
|
|
* If an error occurs, a warning is logged.
|
|
*/
|
|
async function CheckDebugMode () {
|
|
try {
|
|
if (isSetOptionActive) {
|
|
await createStateAsync(NSPanel_Path + 'Config.ScripgtDebugStatus', false, {type: 'boolean', write: true});
|
|
setObject(AliasPath + 'Config.ScripgtDebugStatus', {type: 'channel', common: {role: 'socket', name: 'ScripgtDebugStatus'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Config.ScripgtDebugStatus.ACTUAL', NSPanel_Path + 'Config.ScripgtDebugStatus', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Config.ScripgtDebugStatus.SET', NSPanel_Path + 'Config.ScripgtDebugStatus', true, {type: 'boolean', role: 'switch', name: 'SET'});
|
|
}
|
|
|
|
if (getState(NSPanel_Path + 'Config.ScripgtDebugStatus').val) {
|
|
Debug = true;
|
|
log('Debug mode activated', 'info');
|
|
} else {
|
|
Debug = false;
|
|
log('Debug mode disabled', 'info');
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function CheckDebugModus: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
CheckDebugMode();
|
|
|
|
/**
|
|
* This function checks if the MQTT-Port is used by another instance. If an instance is found which is using the same port, a warning is logged.
|
|
* If the debug mode is activated, the result of the iobroker command is logged.
|
|
* If an error occurs, a warning is logged.
|
|
*/
|
|
async function CheckMQTTPorts () {
|
|
try {
|
|
let instanceName: string = (config.panelRecvTopic).split('.')[0] + "." + (config.panelRecvTopic).split('.')[1];
|
|
|
|
if (isSetOptionActive) {
|
|
await createStateAsync(NSPanel_Path + 'Config.MQTT.portCheck', true, {type: 'boolean', write: true});
|
|
setObject(AliasPath + 'Config.MQTT.portCheck', {type: 'channel', common: {role: 'socket', name: 'mqttPortCheck'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Config.MQTT.portCheck.ACTUAL', NSPanel_Path + 'Config.MQTT.portCheck', true, {type: 'boolean', role: 'switch', name: 'ACTUAL'});
|
|
await createAliasAsync(AliasPath + 'Config.MQTT.portCheck.SET', NSPanel_Path + 'Config.MQTT.portCheck', true, {type: 'boolean', role: 'switch', name: 'SET'});
|
|
}
|
|
|
|
if (getState(NSPanel_Path + 'Config.MQTT.portCheck').val) {
|
|
let adapterArray: any = [];
|
|
let portArray: any = [];
|
|
exec('iob l i --port --enabled', async (error, result, stderr) => {
|
|
if (error == null) {
|
|
if (result != undefined) {
|
|
log('Start MQTT-Port-Check -------------------------------------', 'info');
|
|
let resultString1 = result.split('+');
|
|
for (let i: number = 1; i < resultString1.length - 1; i++) {
|
|
let resultString2: any = resultString1[i].split(':');
|
|
if (Debug) log(`MQTT-PORT-Check Result ` + JSON.stringify(resultString2));
|
|
let adapterInstanceName: string = resultString2[0].substring(16);
|
|
let adapterInstancePort: string = '';
|
|
if (resultString2[2].match('port') != null) {
|
|
adapterInstancePort = resultString2[3].substring(1, 5);
|
|
} else {
|
|
adapterInstancePort = 'Kein Port gefunden';
|
|
}
|
|
log('-- ' + adapterInstanceName + ' - ' + adapterInstancePort, 'info');
|
|
adapterArray[i] = adapterInstanceName.trim();
|
|
portArray[i] = adapterInstancePort.trim();
|
|
}
|
|
let mqttInstance = adapterArray.indexOf(instanceName);
|
|
|
|
const mqttConfig = getObject(`system.adapter.${adapterArray[mqttInstance]}`);
|
|
if (mqttConfig && mqttConfig.native && mqttConfig.native.type == 'client') {
|
|
log('- MQTT-Port-Check OK: Instance of Adapter: ' + adapterArray[mqttInstance] + ' is running as client!', 'info');
|
|
} else {
|
|
for (let j: number = 1; j < portArray.length; j++) {
|
|
if (portArray[j] == portArray[mqttInstance] && adapterArray[j] == adapterArray[mqttInstance]) {
|
|
log('- MQTT-Port-Check OK: Instance of Adapter: ' + adapterArray[j] + ' is running on Port:' + portArray[j], 'info');
|
|
} else if (portArray[j] == portArray[mqttInstance] && adapterArray[j] != adapterArray[mqttInstance]) {
|
|
log('Instance of Adapter: ' + adapterArray[j] + ' is running on same Port:' + portArray[j] + ' as ' + adapterArray[mqttInstance], 'warn');
|
|
log('Please Change Port of Instance: ' + adapterArray[j], 'warn');
|
|
}
|
|
}
|
|
}
|
|
log('End MQTT-Port-Check ---------------------------------------', 'info');
|
|
}
|
|
} else if (error.toString().substring(0, 21) == 'exec is not available') {
|
|
log('MQTT-Portcheck not possible - exec is not available. Please enable exec option in JS-Adapter instance settings', 'warn');
|
|
log('MQTT-Portcheck nicht möglich - exec ist nicht verfügbar. Bitte Haken bei -- Kommando Exec erlauben -- in JS-Adapter-Instanz setzen', 'warn');
|
|
}
|
|
});
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function CheckMQTTPorts: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
CheckMQTTPorts();
|
|
|
|
/**
|
|
* Creates states for the current and desired TFT firmware version.
|
|
*
|
|
* @since 4.4.0
|
|
*/
|
|
async function Init_Release () {
|
|
try {
|
|
if (existsObject(NSPanel_Path + 'Display_Firmware.desiredVersion') == false) {
|
|
await createStateAsync(NSPanel_Path + 'Display_Firmware.desiredVersion', desired_display_firmware_version, {type: 'number', write: false});
|
|
} else {
|
|
await setStateAsync(NSPanel_Path + 'Display_Firmware.desiredVersion', desired_display_firmware_version, true);
|
|
}
|
|
|
|
if (existsObject(NSPanel_Path + 'Config.Update.activ') == false) {
|
|
await createStateAsync(NSPanel_Path + 'Config.Update.activ', 1, {type: 'number', write: false});
|
|
} else {
|
|
await setStateAsync(NSPanel_Path + 'Config.Update.activ', 0, true);
|
|
}
|
|
|
|
let currentFWV = 0;
|
|
let currentFWR = '0.0.0'
|
|
let findFWIndex = 0;
|
|
log('Desired TFT Firmware: ' + desired_display_firmware_version + ' / ' + tft_version, 'info');
|
|
|
|
if (existsObject(NSPanel_Path + 'Display_Firmware.currentRelease')) {
|
|
currentFWV = parseInt(getState(NSPanel_Path + 'Display_Firmware.currentVersion').val);
|
|
currentFWR = getState(NSPanel_Path + 'Display_Firmware.currentRelease').val;
|
|
log('Installed TFT Firmware: ' + currentFWV + ' / v' + currentFWR, 'info');
|
|
}
|
|
//Create Long Term
|
|
if (existsObject(NSPanel_Path + 'Display_Firmware.TFT.desiredVersion') == false) {
|
|
//Create TFT DP's
|
|
if (isSetOptionActive) {
|
|
await createStateAsync(NSPanel_Path + 'Display_Firmware.TFT.currentVersion', currentFWV + ' / v' + currentFWR, {type: 'string', write: false});
|
|
await createStateAsync(NSPanel_Path + 'Display_Firmware.TFT.desiredVersion', String(desired_display_firmware_version), {type: 'string', write: false});
|
|
setObject(AliasPath + 'Display_Firmware.TFT.currentVersion', {type: 'channel', common: {role: 'info', name: 'current TFT-Version'}, native: {}});
|
|
setObject(AliasPath + 'Display_Firmware.TFT.desiredVersion', {type: 'channel', common: {role: 'info', name: 'desired TFT-Version'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Display_Firmware.TFT.currentVersion.ACTUAL', NSPanel_Path + 'Display_Firmware.TFT.currentVersion', true, {
|
|
type: 'string',
|
|
role: 'state',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Display_Firmware.TFT.desiredVersion.ACTUAL', NSPanel_Path + 'Display_Firmware.TFT.desiredVersion', true, {
|
|
type: 'string',
|
|
role: 'state',
|
|
name: 'ACTUAL',
|
|
});
|
|
}
|
|
} else {
|
|
//Create TFT DP's
|
|
await setStateAsync(NSPanel_Path + 'Display_Firmware.TFT.currentVersion', currentFWV + ' / v' + currentFWR, true);
|
|
await setStateAsync(NSPanel_Path + 'Display_Firmware.TFT.desiredVersion', String(desired_display_firmware_version) + ' / ' + tft_version, true);
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function Init_Release: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
Init_Release();
|
|
|
|
/**
|
|
* Function to initialize all Config Parameters in the NSPanel adapter.
|
|
* This includes the following config parameters:
|
|
* - alternativeScreensaverLayout (socket)
|
|
* - ScreensaverAdvanced (socket)
|
|
* - autoWeatherColorScreensaverLayout (socket)
|
|
* - timeoutScreensaver (Slider)
|
|
* - screenSaverDoubleClick (socket)
|
|
* - locale (string)
|
|
* - temperatureUnit (string)
|
|
* - localeNumber (buttonSensor)
|
|
* - temperatureUnitNumber (buttonSensor)
|
|
* - hiddenCards (socket)
|
|
* @function InitConfigParameters
|
|
*/
|
|
async function InitConfigParameters () {
|
|
try {
|
|
if (isSetOptionActive) {
|
|
// alternativeScreensaverLayout (socket)
|
|
await createStateAsync(NSPanel_Path + 'Config.Screensaver.alternativeScreensaverLayout', false, {type: 'boolean', write: true});
|
|
setObject(AliasPath + 'Config.Screensaver.alternativeScreensaverLayout', {type: 'channel', common: {role: 'socket', name: 'alternativeScreensaverLayout'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Config.Screensaver.alternativeScreensaverLayout.ACTUAL', NSPanel_Path + 'Config.Screensaver.alternativeScreensaverLayout', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Config.Screensaver.alternativeScreensaverLayout.SET', NSPanel_Path + 'Config.Screensaver.alternativeScreensaverLayout', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'SET',
|
|
});
|
|
|
|
await createStateAsync(`${NSPanel_Path}${ScreensaverAdvancedEndPath}`, false, {type: 'boolean', write: true});
|
|
setObject(AliasPath + ScreensaverAdvancedEndPath, {type: 'channel', common: {role: 'socket', name: 'ScreensaverAdvanced'}, native: {}});
|
|
await createAliasAsync(`${AliasPath}${ScreensaverAdvancedEndPath}.ACTUAL`, `${NSPanel_Path}${ScreensaverAdvancedEndPath}`, true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(`${AliasPath}${ScreensaverAdvancedEndPath}.SET`, `${NSPanel_Path}${ScreensaverAdvancedEndPath}`, true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'SET',
|
|
});
|
|
|
|
await createStateAsync(NSPanel_Path + ScreensaverEasyViewEndPath, false, {type: 'boolean', write: true});
|
|
setObject(AliasPath + ScreensaverEasyViewEndPath, {type: 'channel', common: {role: 'socket', name: 'Easy-View Screensaver'}, native: {}});
|
|
await createAliasAsync(`${AliasPath}${ScreensaverEasyViewEndPath}.ACTUAL`, NSPanel_Path + ScreensaverEasyViewEndPath, true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(`${AliasPath}${ScreensaverEasyViewEndPath}.SET`, NSPanel_Path + ScreensaverEasyViewEndPath, true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'SET',
|
|
});
|
|
|
|
// autoWeatherColorScreensaverLayout (socket)
|
|
await createStateAsync(NSPanel_Path + 'Config.Screensaver.autoWeatherColorScreensaverLayout', true, {type: 'boolean', write: true});
|
|
setObject(AliasPath + 'Config.Screensaver.autoWeatherColorScreensaverLayout', {type: 'channel', common: {role: 'socket', name: 'alternativeScreensaverLayout'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Config.Screensaver.autoWeatherColorScreensaverLayout.ACTUAL', NSPanel_Path + 'Config.Screensaver.autoWeatherColorScreensaverLayout', true, <
|
|
iobJS.StateCommon
|
|
> {type: 'boolean', role: 'switch', name: 'ACTUAL'});
|
|
await createAliasAsync(AliasPath + 'Config.Screensaver.autoWeatherColorScreensaverLayout.SET', NSPanel_Path + 'Config.Screensaver.autoWeatherColorScreensaverLayout', true, <
|
|
iobJS.StateCommon
|
|
> {type: 'boolean', role: 'switch', name: 'SET'});
|
|
|
|
// timeoutScreensaver 0-60 (Slider)
|
|
await createStateAsync(NSPanel_Path + 'Config.Screensaver.timeoutScreensaver', 10, {type: 'number', write: true});
|
|
setObject(AliasPath + 'Config.Screensaver.timeoutScreensaver', {type: 'channel', common: {role: 'slider', name: 'timeoutScreensaver'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Config.Screensaver.timeoutScreensaver.ACTUAL', NSPanel_Path + 'Config.Screensaver.timeoutScreensaver', true, {
|
|
type: 'number',
|
|
role: 'value',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Config.Screensaver.timeoutScreensaver.SET', NSPanel_Path + 'Config.Screensaver.timeoutScreensaver', true, {
|
|
type: 'number',
|
|
role: 'level',
|
|
name: 'SET',
|
|
});
|
|
|
|
// screenSaverDoubleClick (socket)
|
|
await createStateAsync(NSPanel_Path + 'Config.Screensaver.screenSaverDoubleClick', true, {type: 'boolean', write: true});
|
|
setObject(AliasPath + 'Config.Screensaver.screenSaverDoubleClick', {type: 'channel', common: {role: 'socket', name: 'screenSaverDoubleClick'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Config.Screensaver.screenSaverDoubleClick.ACTUAL', NSPanel_Path + 'Config.Screensaver.screenSaverDoubleClick', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Config.Screensaver.screenSaverDoubleClick.SET', NSPanel_Path + 'Config.Screensaver.screenSaverDoubleClick', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'SET',
|
|
});
|
|
|
|
if (existsObject(NSPanel_Path + 'Config.locale') == false) {
|
|
// en-US, de-DE, nl-NL, da-DK, es-ES, fr-FR, it-IT, ru-RU, etc.
|
|
await createStateAsync(NSPanel_Path + 'Config.locale', 'de-DE', {type: 'string', write: true});
|
|
setStateAsync(NSPanel_Path + 'Config.locale', 'de-DE');
|
|
}
|
|
|
|
if (existsObject(NSPanel_Path + 'Config.temperatureUnit') == false) {
|
|
// '°C', '°F', 'K'
|
|
await createStateAsync(NSPanel_Path + 'Config.temperatureUnit', '°C', {type: 'string', write: true});
|
|
}
|
|
|
|
// locale Tastensensor popupInSel buttonSensor
|
|
if (existsObject(NSPanel_Path + 'Config.localeNumber') == false) {
|
|
await createStateAsync(NSPanel_Path + 'Config.localeNumber', 1, {type: 'number', write: true});
|
|
setObject(AliasPath + 'Config.localeNumber', {type: 'channel', common: {role: 'buttonSensor', name: 'localeNumber'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Config.localeNumber.VALUE', NSPanel_Path + 'Config.localeNumber', true, {type: 'number', role: 'state', name: 'VALUE'});
|
|
}
|
|
// temperatureUnit popupInSel buttonSensor
|
|
if (existsObject(NSPanel_Path + 'Config.temperatureUnitNumber') == false) {
|
|
await createStateAsync(NSPanel_Path + 'Config.temperatureUnitNumber', 0, {type: 'number', write: true});
|
|
setObject(AliasPath + 'Config.temperatureUnitNumber', {type: 'channel', common: {role: 'buttonSensor', name: 'temperatureUnitNumber'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Config.temperatureUnitNumber.VALUE', NSPanel_Path + 'Config.temperatureUnitNumber', true, {
|
|
type: 'number',
|
|
role: 'state',
|
|
name: 'VALUE',
|
|
});
|
|
}
|
|
// Trigger DP for hiddenCards (with hiddenByTrigger)
|
|
if (existsObject(NSPanel_Path + 'Config.hiddenCards') == false) {
|
|
await createStateAsync(NSPanel_Path + 'Config.hiddenCards', false, {type: 'boolean', write: true});
|
|
setObject(AliasPath + 'Config.hiddenCards', {type: 'channel', common: {role: 'socket', name: 'hiddenCards'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Config.hiddenCards.ACTUAL', NSPanel_Path + 'Config.hiddenCards', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Config.hiddenCards.SET', NSPanel_Path + 'Config.hiddenCards', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'SET',
|
|
});
|
|
} else {
|
|
getState(NSPanel_Path + 'Config.hiddenCards').val ? log('hidden Cards activated', 'info') : log('hidden Cards disabled', 'info');
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function InitConfigParameters: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
InitConfigParameters();
|
|
|
|
// Trigger for hidden Cards - if hiddenByTrigger is true/false
|
|
/**
|
|
* This function is triggered when the state of `id: [NSPanel_Path + 'Config.hiddenCards']` changes.
|
|
* It logs a message indicating whether hidden cards are activated or disabled.
|
|
* If the state is true, it sets `valueHiddenCards` to the state value, sets `activePage` to the first page in `config.pages`,
|
|
* sets `pageId` to 0, and calls `GeneratePage` with `activePage`.
|
|
* If an error occurs, it logs a warning with the error message.
|
|
*
|
|
* @param {Object} obj - The object containing the state of the triggering state.
|
|
* @param {boolean} obj.state.val - The new value of the triggering state.
|
|
* @returns {Promise<void>} - A Promise that resolves when the function completes.
|
|
*/
|
|
on({id: [NSPanel_Path + 'Config.hiddenCards'], change: 'ne'}, async function (obj) {
|
|
try {
|
|
obj.state.val ? log('hidden Cards activated', 'info') : log('hidden Cards disabled', 'info');
|
|
valueHiddenCards = obj.state.val;
|
|
if (obj.state.val) {
|
|
activePage = config.pages[0];
|
|
pageId = 0;
|
|
GeneratePage(activePage);
|
|
}
|
|
} catch (err: any) {
|
|
log('error at Trigger hidden Cards Status: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
/**
|
|
* This function is triggered when the state of `id: [NSPanel_Path + 'Config.ScripgtDebugStatus']` changes.
|
|
* It logs a message indicating whether debug mode is activated or disabled.
|
|
* It sets the `Debug` variable to the new state value.
|
|
*
|
|
* @param {Object} obj - The object containing the new state.
|
|
* @param {boolean} obj.state.val - The new state value.
|
|
* @returns {Promise<void>} - A Promise that resolves when the function completes.
|
|
*/
|
|
on({id: [NSPanel_Path + 'Config.ScripgtDebugStatus'], change: 'ne'}, async function (obj) {
|
|
try {
|
|
obj.state.val ? log('Debug mode activated', 'info') : log('Debug mode disabled', 'info');
|
|
Debug = obj.state.val;
|
|
} catch (err: any) {
|
|
log('error at Trigger ScripgtDebugStatus: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
/**
|
|
* This function is triggered when the state of either `id: [NSPanel_Path + 'Config.localeNumber']` or `id: [NSPanel_Path + 'Config.temperatureUnitNumber']` changes.
|
|
* It updates the locale or temperature unit settings based on the new state value.
|
|
*
|
|
* @param {Object} obj - The object containing the new state.
|
|
* @param {string} obj.id - The ID of the state that changed.
|
|
* @param {number} obj.state.val - The new state value.
|
|
* @returns {Promise<void>} - A Promise that resolves when the function completes.
|
|
*/
|
|
on({id: [NSPanel_Path + 'Config.localeNumber', NSPanel_Path + 'Config.temperatureUnitNumber'], change: 'ne'}, async function (obj) {
|
|
try {
|
|
if (obj.id == NSPanel_Path + 'Config.localeNumber') {
|
|
/**
|
|
* List of supported locales.
|
|
* @type {string[]}
|
|
*/
|
|
let localesList = [
|
|
'en-US',
|
|
'de-DE',
|
|
'nl-NL',
|
|
'da-DK',
|
|
'es-ES',
|
|
'fr-FR',
|
|
'it-IT',
|
|
'ru-RU',
|
|
'nb-NO',
|
|
'nn-NO',
|
|
'pl-PL',
|
|
'pt-PT',
|
|
'af-ZA',
|
|
'ar-SY',
|
|
'bg-BG',
|
|
'ca-ES',
|
|
'cs-CZ',
|
|
'el-GR',
|
|
'et-EE',
|
|
'fa-IR',
|
|
'fi-FI',
|
|
'he-IL',
|
|
'hr-xx',
|
|
'hu-HU',
|
|
'hy-AM',
|
|
'id-ID',
|
|
'is-IS',
|
|
'lb-xx',
|
|
'lt-LT',
|
|
'ro-RO',
|
|
'sk-SK',
|
|
'sl-SI',
|
|
'sv-SE',
|
|
'th-TH',
|
|
'tr-TR',
|
|
'uk-UA',
|
|
'vi-VN',
|
|
'zh-CN',
|
|
'zh-TW',
|
|
];
|
|
/**
|
|
* Update the locale setting.
|
|
*/
|
|
setStateAsync(NSPanel_Path + 'Config.locale', localesList[obj.state.val]);
|
|
/**
|
|
* Send the updated date.
|
|
*/
|
|
SendDate();
|
|
}
|
|
if (obj.id == NSPanel_Path + 'Config.temperatureUnitNumber') {
|
|
/**
|
|
* List of supported temperature units.
|
|
* @type {string[]}
|
|
*/
|
|
let tempunitList = ['°C', '°F', 'K'];
|
|
/**
|
|
* Update the temperature unit setting.
|
|
*/
|
|
setStateAsync(NSPanel_Path + 'Config.temperatureUnit', tempunitList[obj.state.val]);
|
|
}
|
|
} catch (err: any) {
|
|
log('error at Trigger temperatureUnitNumber + localeNumber: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Creates the state for the screensaver advanced switch if it does not exist.
|
|
* This switch is used to switch between two different screensaver layouts.
|
|
* Screensaver 1 and Screensaver 2
|
|
* @function Init_ScreensaverAdvanced
|
|
*/
|
|
async function Init_ScreensaverAdvanced () {
|
|
try {
|
|
if (existsState(`${NSPanel_Path}${ScreensaverAdvancedEndPath}`) == false) {
|
|
await createStateAsync(`${NSPanel_Path}${ScreensaverAdvancedEndPath}`, false, true, {type: 'boolean', write: true});
|
|
}
|
|
if (existsState(`${NSPanel_Path}${ScreensaverEasyViewEndPath}`) == false) {
|
|
await createStateAsync(`${NSPanel_Path}${ScreensaverEasyViewEndPath}`, false, true, {type: 'boolean', write: true});
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function Init_ScreensaverAdvanced: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
Init_ScreensaverAdvanced();
|
|
|
|
|
|
/**
|
|
* Checks whether setObjects() is available for the instance (true/false)
|
|
* @returns {boolean} result of the check
|
|
*/
|
|
function CheckEnableSetObject () {
|
|
var enableSetObject = getObject('system.adapter.javascript.' + instance).native.enableSetObject;
|
|
return enableSetObject;
|
|
}
|
|
|
|
/**
|
|
* Creates the states for the current active page if they do not exist.
|
|
* These states are used to store the heading, type and id0 of the active page.
|
|
* @function Init_ActivePageData
|
|
*/
|
|
async function Init_ActivePageData () {
|
|
try {
|
|
if (existsState(NSPanel_Path + 'ActivePage.heading') == false) {
|
|
await createStateAsync(NSPanel_Path + 'ActivePage.heading', '', true, {type: 'string', write: false});
|
|
}
|
|
if (existsState(NSPanel_Path + 'ActivePage.type') == false) {
|
|
await createStateAsync(NSPanel_Path + 'ActivePage.type', '', true, {type: 'string', write: false});
|
|
}
|
|
if (existsState(NSPanel_Path + 'ActivePage.id0') == false) {
|
|
await createStateAsync(NSPanel_Path + 'ActivePage.id0', '', true, {type: 'string', write: false});
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function Init_ActivePageData: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
Init_ActivePageData();
|
|
|
|
/**
|
|
* Creates the state for the screensaver background color switch if it does not exist.
|
|
* This state is used to switch between different background colors for the screensaver.
|
|
* @function Init_Screensaver_Backckground_Color_Switch
|
|
*/
|
|
async function Init_Screensaver_Backckground_Color_Switch () {
|
|
try {
|
|
const objDef: iobJS.StateObject = {
|
|
_id: '',
|
|
type: 'state',
|
|
common: {
|
|
type: 'number',
|
|
name: 'Color Indicator',
|
|
role: 'level',
|
|
states: {0: 'black', 1: 'red', 2: 'green', 3: 'attention', 4: 'pink', 5: 'dark red'},
|
|
read: true,
|
|
write: true,
|
|
},
|
|
native: {},
|
|
};
|
|
await extendObjectAsync(NSPanel_Path + 'ScreensaverInfo.bgColorIndicator', objDef);
|
|
if (await existsStateAsync(NSPanel_Path + 'ScreensaverInfo.bgColorIndicator')) {
|
|
const obj = await getStateAsync(NSPanel_Path + 'ScreensaverInfo.bgColorIndicator');
|
|
if (obj && obj.val !== null && obj.val !== undefined) {
|
|
bgColorScrSaver = obj.val;
|
|
} else {
|
|
setStateAsync(NSPanel_Path + 'ScreensaverInfo.bgColorIndicator', bgColorScrSaver);
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function Init_Screensaver_Backckground_Color_Switch: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
Init_Screensaver_Backckground_Color_Switch();
|
|
|
|
/**
|
|
* Event listener for changes to the Screensaver background color indicator.
|
|
*
|
|
* When the value of the `bgColorIndicator` object changes, this function is triggered.
|
|
* It updates the `bgColorScrSaver` variable with the new value and calls the `HandleScreensaverUpdate` function if the value is less than 6.
|
|
*
|
|
* @param {object} obj - The object containing the new state value.
|
|
* @param {string} obj.state.val - The new value of the `bgColorIndicator` object.
|
|
*
|
|
* @async
|
|
* @throws {Error} If an error occurs while updating the `bgColorScrSaver` variable or calling `HandleScreensaverUpdate`.
|
|
*/
|
|
on({id: NSPanel_Path + 'ScreensaverInfo.bgColorIndicator', change: 'ne'}, async function (obj) {
|
|
try {
|
|
bgColorScrSaver = obj.state.val;
|
|
if (bgColorScrSaver < 6) {
|
|
HandleScreensaverUpdate();
|
|
}
|
|
} catch (err: any) {
|
|
log('error at trigger bgColorIndicator: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Event listener for changes to the Screensaver Advanced configuration.
|
|
*
|
|
* When the value of the `ScreensaverAdvanced` object changes, this function is triggered.
|
|
* If the value is true, it sets the state of the `alternativeScreensaverLayout` object to false.
|
|
*
|
|
* @param {object} obj - The object containing the new state value.
|
|
* @param {boolean} obj.state.val - The new value of the `ScreensaverAdvanced` object.
|
|
*
|
|
* @async
|
|
* @throws {Error} If an error occurs while updating the state of the `alternativeScreensaverLayout` object.
|
|
*/
|
|
on({id: `${NSPanel_Path}${ScreensaverAdvancedEndPath}`, change: 'ne'}, async function (obj) {
|
|
try {
|
|
if (obj.state.val) setState(`${NSPanel_Path}Config.Screensaver.alternativeScreensaverLayout`, false, true);
|
|
if (obj.state.val) setState(`${NSPanel_Path}${ScreensaverEasyViewEndPath}`, false, true);
|
|
if (obj.id) await setStateAsync(obj.id, obj.state.val, true);
|
|
//setState(config.panelSendTopic, 'pageType~pageStartup');
|
|
} catch (err: any) {
|
|
log('error at trigger Screensaver Advanced: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
on({id: `${NSPanel_Path}${ScreensaverEasyViewEndPath}`, change: 'ne'}, async function (obj) {
|
|
try {
|
|
if (obj.state.val) setState(`${NSPanel_Path}Config.Screensaver.alternativeScreensaverLayout`, false, true);
|
|
if (obj.state.val) setState(`${NSPanel_Path}${ScreensaverAdvancedEndPath}`, false, true);
|
|
if (obj.id) await setStateAsync(obj.id, obj.state.val, true);
|
|
//setState(config.panelSendTopic, 'pageType~pageStartup');
|
|
} catch (err: any) {
|
|
log('error at trigger Screensaver Advanced: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Event listener for changes to the alternative Screensaver layout configuration.
|
|
*
|
|
* When the value of the `alternativeScreensaverLayout` object changes, this function is triggered.
|
|
* If the value is true, it sets the state of the `ScreensaverAdvanced` object to false.
|
|
*
|
|
* @param {object} obj - The object containing the new state value.
|
|
* @param {boolean} obj.state.val - The new value of the `alternativeScreensaverLayout` object.
|
|
*
|
|
* @async
|
|
* @throws {Error} If an error occurs while updating the state of the `ScreensaverAdvanced` object.
|
|
*
|
|
* @note This function appears to be a toggle with the `ScreensaverAdvanced` configuration.
|
|
* When `alternativeScreensaverLayout` is true, `ScreensaverAdvanced` is set to false.
|
|
*/
|
|
on({id: NSPanel_Path + 'Config.Screensaver.alternativeScreensaverLayout', change: 'ne'}, async function (obj) {
|
|
try {
|
|
if (obj.state.val) setState(`${NSPanel_Path}${ScreensaverAdvancedEndPath}`, false, true);
|
|
if (obj.state.val) setState(`${NSPanel_Path}${ScreensaverEasyViewEndPath}`, false, true);
|
|
if (obj.id) await setStateAsync(obj.id, obj.state.val, true);
|
|
|
|
//setState(config.panelSendTopic, 'pageType~pageStartup');
|
|
} catch (err: any) {
|
|
log('error at trigger Screensaver Alternativ: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Initializes the `bExitPage` state object and sets the `alwaysOn` and `pageCounter` variables.
|
|
*
|
|
* This function is called once at startup and is used to initialize the `bExitPage` state object.
|
|
*
|
|
* @async
|
|
* @throws {Error} If an error occurs while creating the `bExitPage` state object.
|
|
*/
|
|
async function Init_bExit_Page_Change () {
|
|
try {
|
|
alwaysOn = false;
|
|
pageCounter = 0;
|
|
if (existsState(NSPanel_Path + 'ScreensaverInfo.bExitPage') == false) {
|
|
await createStateAsync(NSPanel_Path + 'ScreensaverInfo.bExitPage', -1, true, {type: 'number', write: true});
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function Init_bExit_Page_Change: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
Init_bExit_Page_Change();
|
|
|
|
/**
|
|
* Initializes the `Trigger_Dimmode` state object if it does not exist.
|
|
*
|
|
* This function checks if the `Trigger_Dimmode` state is present. If not, it creates the state
|
|
* with a default value of `false` and sets it to writable. This state is used to control
|
|
* whether the dim mode is triggered.
|
|
*
|
|
* @async
|
|
* @throws {Error} If an error occurs while creating the `Trigger_Dimmode` state.
|
|
*/
|
|
async function Init_Dimmode_Trigger () {
|
|
try {
|
|
if (existsState(NSPanel_Path + 'ScreensaverInfo.Trigger_Dimmode') == false) {
|
|
await createStateAsync(NSPanel_Path + 'ScreensaverInfo.Trigger_Dimmode', false, true, {type: 'boolean', write: true});
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function Init_Dimmode_Trigger: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
Init_Dimmode_Trigger();
|
|
|
|
/**
|
|
* Initializes the `activeBrightness` and `activeDimmodeBrightness` state objects if they do not exist.
|
|
* Also creates aliases for the `activeBrightness` state object with roles for actual and set values.
|
|
*
|
|
* This function checks if the necessary states exist and creates them if not. It then sets up aliases
|
|
* to manage `activeBrightness` with specific roles and names for easier access.
|
|
*
|
|
* @async
|
|
* @throws {Error} If an error occurs while creating the state objects or aliases.
|
|
*/
|
|
async function InitActiveBrightness () {
|
|
try {
|
|
if (isSetOptionActive) {
|
|
if (existsState(NSPanel_Path + 'ScreensaverInfo.activeBrightness') == false || existsState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness') == false) {
|
|
await createStateAsync(NSPanel_Path + 'ScreensaverInfo.activeBrightness', 100, {type: 'number', write: true});
|
|
await createStateAsync(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness', -1, {type: 'number', write: true});
|
|
}
|
|
//Create Alias activeBrightness
|
|
setObject(AliasPath + 'ScreensaverInfo.activeBrightness', {type: 'channel', common: {role: 'slider', name: 'activeBrightness'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'ScreensaverInfo.activeBrightness.ACTUAL', NSPanel_Path + 'ScreensaverInfo.activeBrightness', true, {
|
|
type: 'number',
|
|
role: 'value',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'ScreensaverInfo.activeBrightness.SET', NSPanel_Path + 'ScreensaverInfo.activeBrightness', true, {
|
|
type: 'number',
|
|
role: 'level',
|
|
name: 'SET',
|
|
});
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function InitActiveBrightness: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
InitActiveBrightness();
|
|
|
|
/**
|
|
* Event listener for changes to the active brightness value of the Screensaver.
|
|
*
|
|
* When the value of the `activeBrightness` object changes, this function is triggered.
|
|
* It retrieves the current dimmode brightness value, calculates the active brightness value,
|
|
* and sends a message to the panel with the updated brightness values.
|
|
*
|
|
* @param {object} obj - The object containing the new state value.
|
|
* @param {number} obj.state.val - The new value of the `activeBrightness` object.
|
|
*
|
|
* @async
|
|
* @throws {Error} If an error occurs while retrieving the dimmode brightness value or sending the message to the panel.
|
|
*/
|
|
on({id: [NSPanel_Path + 'ScreensaverInfo.activeBrightness'], change: 'ne'}, async function (obj) {
|
|
try {
|
|
let dimBrightness: number = -1;
|
|
if (existsState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness')) {
|
|
dimBrightness = getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val;
|
|
}
|
|
let active = dimBrightness ?? -1;
|
|
if (obj.state.val >= 0 || obj.state.val <= 100) {
|
|
log('action at trigger activeBrightness: ' + obj.state.val + ' - activeDimmodeBrightness: ' + active, 'info');
|
|
SendToPanel({payload: 'dimmode~' + active + '~' + obj.state.val + '~' + rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor)});
|
|
InitDimmode();
|
|
}
|
|
} catch (err: any) {
|
|
log('error at trigger activeBrightness: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Event listener for changes to the active dimmode brightness value of the Screensaver.
|
|
*
|
|
* When the value of the `activeDimmodeBrightness` object changes, this function is triggered.
|
|
* It retrieves the current brightness value, calculates the active dimmode brightness value,
|
|
* and sends a message to the panel with the updated brightness values.
|
|
*
|
|
* @param {object} obj - The object containing the new state value.
|
|
* @param {number} obj.state.val - The new value of the `activeDimmodeBrightness` object.
|
|
*
|
|
* @async
|
|
* @throws {Error} If an error occurs while retrieving the brightness value or sending the message to the panel.
|
|
*/
|
|
on({id: [NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness'], change: 'ne'}, async function (obj) {
|
|
try {
|
|
let brightness: number = 100;
|
|
if (existsState(NSPanel_Path + 'ScreensaverInfo.activeBrightness')) {
|
|
brightness = getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val;
|
|
}
|
|
let active = brightness ?? 80;
|
|
if (obj.state.val != null && obj.state.val != -1) {
|
|
if (obj.state.val < -1 || obj.state.val > 100) {
|
|
log('activeDimmodeBrightness value only between -1 and 100', 'info');
|
|
setStateAsync(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness', -1, true);
|
|
alwaysOn = false;
|
|
pageCounter = 0;
|
|
useMediaEvents = false;
|
|
screensaverEnabled = true;
|
|
InitDimmode();
|
|
//HandleMessage('event', 'startup', undefined, undefined);
|
|
} else {
|
|
if (Debug) log('action at trigger activeDimmodeBrightness: ' + obj.state.val + ' - activeBrightness: ' + active, 'info');
|
|
SendToPanel({payload: 'dimmode~' + obj.state.val + '~' + active + '~' + rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor)});
|
|
}
|
|
} else {
|
|
alwaysOn = false;
|
|
pageCounter = 0;
|
|
useMediaEvents = false;
|
|
screensaverEnabled = true;
|
|
InitDimmode();
|
|
//HandleMessage('event', 'startup', undefined, undefined);
|
|
}
|
|
} catch (err: any) {
|
|
log('error at trigger activeDimmodeBrightness: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Event listener for changes to the Trigger Dimmode state of the Screensaver.
|
|
*
|
|
* When the value of the `Trigger_Dimmode` object changes, this function is triggered.
|
|
* It retrieves the current brightness value, calculates the active dimmode brightness value,
|
|
* and sends a message to the panel with the updated brightness values if the trigger is enabled.
|
|
*
|
|
* @param {object} obj - The object containing the new state value.
|
|
* @param {boolean} obj.state.val - The new value of the `Trigger_Dimmode` object.
|
|
*
|
|
* @async
|
|
* @throws {Error} If an error occurs while retrieving the brightness value or sending the message to the panel.
|
|
*/
|
|
on({id: String(NSPanel_Path) + 'ScreensaverInfo.Trigger_Dimmode', change: 'ne'}, async function (obj) {
|
|
try {
|
|
let brightness: number = 100;
|
|
if (existsState(NSPanel_Path + 'ScreensaverInfo.activeBrightness')) {
|
|
brightness = getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val;
|
|
}
|
|
let active = brightness ?? 80;
|
|
if (obj.state.val) {
|
|
SendToPanel({payload: 'dimmode~' + 100 + '~' + active + '~' + rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor)});
|
|
} else {
|
|
InitDimmode();
|
|
}
|
|
} catch (err: any) {
|
|
log('error at trigger Trigger_Dimmode: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Initialize the Reboot NSPanel state and channel.
|
|
*
|
|
* If the `rebootNSPanel` state does not exist, this function creates it, sets its initial value to false, and creates an alias for it.
|
|
*
|
|
* @async
|
|
* @throws {Error} If an error occurs while creating the state or alias.
|
|
*/
|
|
async function InitRebootPanel () {
|
|
try {
|
|
if (existsState(NSPanel_Path + 'Config.rebootNSPanel') == false) {
|
|
await createStateAsync(NSPanel_Path + 'Config.rebootNSPanel', false, {type: 'boolean', write: true});
|
|
setObject(AliasPath + 'Config.rebootNSPanel', {type: 'channel', common: {role: 'button', name: 'Reboot NSPanel'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Config.rebootNSPanel.SET', NSPanel_Path + 'Config.rebootNSPanel', true, {type: 'boolean', role: 'state', name: 'SET'});
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function InitRebootPanel: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
InitRebootPanel();
|
|
|
|
/**
|
|
* Event listener for changes to the Reboot NSPanel state.
|
|
*
|
|
* When the value of the `rebootNSPanel.SET` object changes, this function is triggered.
|
|
* It sends a request to the Tasmota device to restart the NSPanel.
|
|
*
|
|
* @param {object} obj - The object containing the new state value.
|
|
* @param {boolean} obj.state.val - The new value of the `rebootNSPanel.SET` object.
|
|
*
|
|
* @async
|
|
* @throws {Error} If an error occurs while sending the request to the Tasmota device.
|
|
*/
|
|
on({id: AliasPath + 'Config.rebootNSPanel.SET', change: 'any'}, async function (obj) {
|
|
if (obj.state.val) {
|
|
try {
|
|
let urlString = `http://${get_current_tasmota_ip_address()}/cm?cmnd=Backlog Restart 1`;
|
|
if (tasmota_web_admin_password != '') {
|
|
urlString = `http://${get_current_tasmota_ip_address()}/cm?user=${tasmota_web_admin_user}&password=${tasmota_web_admin_password}&cmnd=Backlog Restart 1;`;
|
|
}
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
SendToPanel({payload: 'pageType~pageStartup'});
|
|
log('Tasmota Reboot', 'info');
|
|
setStateAsync(AliasPath + 'Config.rebootNSPanel.SET', false);
|
|
log('Name: ' + scriptName, 'info');
|
|
log('Instanz: ' + instance, 'info');
|
|
} else {
|
|
log('Axios Status - Requesting locales: ' + response.state, 'warn');
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
if (error.code === 'EHOSTUNREACH') {
|
|
log(`Can't connect to display!`, 'warn');
|
|
} else log(error, 'warn');
|
|
})
|
|
.finally(function () {
|
|
if (Debug) {
|
|
log('Reboot NSPanel... done', 'info');
|
|
}
|
|
});
|
|
} catch (err: any) {
|
|
log('error at Trigger Restart NSPanel: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
/**
|
|
* Initializes the datapoints for the update functionality of the NSPanel.
|
|
* These datapoints are created in the namespace of the NSPanel and have the
|
|
* following names:
|
|
* - `Config.Update.UpdateTasmota`
|
|
* - `Config.Update.UpdateBerry`
|
|
* - `Config.Update.UpdateNextion`
|
|
*
|
|
* The datapoints are only created if the `setOption` option is set to `true`.
|
|
* The datapoints are created with the following properties:
|
|
* - `type`: `boolean`
|
|
* - `write`: `true`
|
|
* - `role`: `button`
|
|
* - `name`: The name of the datapoint is set to the name of the update type.
|
|
*
|
|
* Additionally, the function creates aliases for the datapoints in the namespace
|
|
* of the NSPanel with the following names:
|
|
* - `Config.Update.UpdateTasmota.SET`
|
|
* - `Config.Update.UpdateBerry.SET`
|
|
* - `Config.Update.UpdateNextion.SET`
|
|
*
|
|
* The aliases are created with the following properties:
|
|
* - `type`: `boolean`
|
|
* - `role`: `state`
|
|
* - `name`: `SET`
|
|
*
|
|
* @throws {Error} If an error occurs while creating the datapoints or aliases.
|
|
*/
|
|
async function InitUpdateDatapoints () {
|
|
try {
|
|
if (existsState(NSPanel_Path + 'Config.Update.UpdateTasmota') == false) {
|
|
if (isSetOptionActive) {
|
|
await createStateAsync(NSPanel_Path + 'Config.Update.UpdateTasmota', false, {type: 'boolean', write: true});
|
|
await createStateAsync(NSPanel_Path + 'Config.Update.UpdateBerry', false, {type: 'boolean', write: true});
|
|
await createStateAsync(NSPanel_Path + 'Config.Update.UpdateNextion', false, {type: 'boolean', write: true});
|
|
setObject(AliasPath + 'Config.Update.UpdateTasmota', {type: 'channel', common: {role: 'button', name: 'Tassmota update'}, native: {}});
|
|
setObject(AliasPath + 'Config.Update.UpdateBerry', {type: 'channel', common: {role: 'button', name: 'Berry-Driver update'}, native: {}});
|
|
setObject(AliasPath + 'Config.Update.UpdateNextion', {type: 'channel', common: {role: 'button', name: 'Nextion TFT update'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Config.Update.UpdateTasmota.SET', NSPanel_Path + 'Config.Update.UpdateTasmota', true, {
|
|
type: 'boolean',
|
|
role: 'state',
|
|
name: 'SET',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Config.Update.UpdateBerry.SET', NSPanel_Path + 'Config.Update.UpdateBerry', true, {
|
|
type: 'boolean',
|
|
role: 'state',
|
|
name: 'SET',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Config.Update.UpdateNextion.SET', NSPanel_Path + 'Config.Update.UpdateNextion', true, {
|
|
type: 'boolean',
|
|
role: 'state',
|
|
name: 'SET',
|
|
});
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('function InitUpdateDatapoints: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
InitUpdateDatapoints();
|
|
|
|
/**
|
|
* Event listener for changes to the Update Firmware states.
|
|
*
|
|
* When the value of one of the `UpdateTasmota`, `UpdateBerry`, or `UpdateNextion` objects changes,
|
|
* this function is triggered. It performs the corresponding firmware update action.
|
|
*
|
|
* @param {object} obj - The object containing the new state value.
|
|
* @param {string} obj.id - The ID of the object that changed.
|
|
*
|
|
* @async
|
|
* @throws {Error} If an error occurs while performing the firmware update action.
|
|
*/
|
|
on({id: [NSPanel_Path + 'Config.Update.UpdateTasmota', NSPanel_Path + 'Config.Update.UpdateBerry', NSPanel_Path + 'Config.Update.UpdateNextion'], change: 'any'}, async function (obj) {
|
|
try {
|
|
switch (obj.id) {
|
|
case NSPanel_Path + 'Config.Update.UpdateTasmota':
|
|
if (Debug) log('Tasmota Upgrade durchführen', 'info');
|
|
update_tasmota_firmware();
|
|
break;
|
|
case NSPanel_Path + 'Config.Update.UpdateBerry':
|
|
if (Debug) log('Berry Driver Update durchführen', 'info');
|
|
update_berry_driver_version();
|
|
break;
|
|
case NSPanel_Path + 'Config.Update.UpdateNextion':
|
|
if (Debug) log('FlashNextion durchführen', 'info');
|
|
update_tft_firmware();
|
|
break;
|
|
}
|
|
} catch (err: any) {
|
|
log('error at Trigger Update Firmware: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Initializes the relay datapoints and their corresponding aliases for the NSPanel.
|
|
*
|
|
* This function checks if the relay states `Relay.1` and `Relay.2` exist in the
|
|
* namespace. If not, it creates them with the type `boolean` and allows writing.
|
|
*
|
|
* The function then sets up aliases for these relays under the alias path.
|
|
* Each relay has an `ACTUAL` and `SET` alias created with the type `boolean` and
|
|
* role `switch`.
|
|
*
|
|
* The relays are represented as channels with the role `socket`.
|
|
*
|
|
* @async
|
|
* @throws {Error} Logs an error message if any error occurs during the initialization.
|
|
*/
|
|
async function Init_Relays () {
|
|
try {
|
|
if (isSetOptionActive) {
|
|
if (existsState(NSPanel_Path + 'Relay.1') == false || existsState(NSPanel_Path + 'Relay.2') == false) {
|
|
await createStateAsync(NSPanel_Path + 'Relay.1', true, {type: 'boolean', write: true});
|
|
await createStateAsync(NSPanel_Path + 'Relay.2', true, {type: 'boolean', write: true});
|
|
}
|
|
setObject(AliasPath + 'Relay.1', {type: 'channel', common: {role: 'socket', name: 'Relay.1'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Relay.1.ACTUAL', NSPanel_Path + 'Relay.1', true, {type: 'boolean', role: 'switch', name: 'ACTUAL'});
|
|
await createAliasAsync(AliasPath + 'Relay.1.SET', NSPanel_Path + 'Relay.1', true, {type: 'boolean', role: 'switch', name: 'SET'});
|
|
//Create Alias alternateMRIconSize 2
|
|
setObject(AliasPath + 'Relay.2', {type: 'channel', common: {role: 'socket', name: 'Relay.2'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Relay.2.ACTUAL', NSPanel_Path + 'Relay.2', true, {type: 'boolean', role: 'switch', name: 'ACTUAL'});
|
|
await createAliasAsync(AliasPath + 'Relay.2.SET', NSPanel_Path + 'Relay.2', true, {type: 'boolean', role: 'switch', name: 'SET'});
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function Init_Relays: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
Init_Relays();
|
|
|
|
/**
|
|
* Initializes the alternateMRIconSize datapoints and their corresponding aliases for the NSPanel.
|
|
*
|
|
* This function checks if the alternateMRIconSize states `Config.MRIcons.alternateMRIconSize.1` and `Config.MRIcons.alternateMRIconSize.2` exist in the
|
|
* namespace. If not, it creates them with the type `boolean` and allows writing.
|
|
*
|
|
* The function then sets up aliases for these alternateMRIconSize states under the alias path.
|
|
* Each alternateMRIconSize state has an `ACTUAL` and `SET` alias created with the type `boolean` and
|
|
* role `switch`.
|
|
*
|
|
* The alternateMRIconSize states are represented as channels with the role `socket`.
|
|
*
|
|
* @async
|
|
* @throws {Error} Logs an error message if any error occurs during the initialization.
|
|
*/
|
|
async function InitAlternateMRIconsSize () {
|
|
try {
|
|
if (isSetOptionActive) {
|
|
if (existsState(NSPanel_Path + 'Config.MRIcons.alternateMRIconSize.1') == false || existsState(NSPanel_Path + 'Config.MRIcons.alternateMRIconSize.2') == false) {
|
|
await createStateAsync(NSPanel_Path + 'Config.MRIcons.alternateMRIconSize.1', false, {type: 'boolean', write: true});
|
|
await createStateAsync(NSPanel_Path + 'Config.MRIcons.alternateMRIconSize.2', false, {type: 'boolean', write: true});
|
|
}
|
|
//Create Alias alternateMRIconSize 1
|
|
setObject(AliasPath + 'Config.MRIcons.alternateMRIconSize.1', {type: 'channel', common: {role: 'socket', name: 'alternateMRIconSize.1'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Config.MRIcons.alternateMRIconSize.1.ACTUAL', NSPanel_Path + 'Config.MRIcons.alternateMRIconSize.1', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Config.MRIcons.alternateMRIconSize.1.SET', NSPanel_Path + 'Config.MRIcons.alternateMRIconSize.1', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'SET',
|
|
});
|
|
//Create Alias alternateMRIconSize 2
|
|
setObject(AliasPath + 'Config.MRIcons.alternateMRIconSize.2', {type: 'channel', common: {role: 'socket', name: 'alternateMRIconSize.2'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Config.MRIcons.alternateMRIconSize.2.ACTUAL', NSPanel_Path + 'Config.MRIcons.alternateMRIconSize.2', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Config.MRIcons.alternateMRIconSize.2.SET', NSPanel_Path + 'Config.MRIcons.alternateMRIconSize.2', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'SET',
|
|
});
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function InitAlternateMRIconsSize: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
InitAlternateMRIconsSize();
|
|
|
|
|
|
/**
|
|
* Creates all necessary states for the dateformat settings.
|
|
* If the user option is set, it creates the states for the weekday and month format and the corresponding aliases.
|
|
*/
|
|
async function InitDateformat () {
|
|
try {
|
|
if (isSetOptionActive) {
|
|
if (
|
|
existsState(NSPanel_Path + 'Config.Dateformat.weekday') == false ||
|
|
existsState(NSPanel_Path + 'Config.Dateformat.month') == false ||
|
|
existsState(NSPanel_Path + 'Config.Dateformat.customFormat') == false
|
|
) {
|
|
await createStateAsync(NSPanel_Path + 'Config.Dateformat.weekday', 'long', {type: 'string', write: true});
|
|
await createStateAsync(NSPanel_Path + 'Config.Dateformat.month', 'long', {type: 'string', write: true});
|
|
await createStateAsync(NSPanel_Path + 'Config.Dateformat.customFormat', '', {type: 'string', write: true});
|
|
}
|
|
if (existsState(NSPanel_Path + 'Config.Dateformat.Switch.weekday') == false || existsState(NSPanel_Path + 'Config.Dateformat.Switch.month') == false) {
|
|
await createStateAsync(NSPanel_Path + 'Config.Dateformat.Switch.weekday', true, {type: 'boolean', write: true});
|
|
await createStateAsync(NSPanel_Path + 'Config.Dateformat.Switch.month', true, {type: 'boolean', write: true});
|
|
setObject(AliasPath + 'Config.Dateformat.Switch.weekday', {type: 'channel', common: {role: 'socket', name: 'Dateformat Switch weekday'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Config.Dateformat.Switch.weekday.ACTUAL', NSPanel_Path + 'Config.Dateformat.Switch.weekday', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Config.Dateformat.Switch.weekday.SET', NSPanel_Path + 'Config.Dateformat.Switch.weekday', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'SET',
|
|
});
|
|
setObject(AliasPath + 'Config.Dateformat.Switch.month', {type: 'channel', common: {role: 'socket', name: 'Dateformat Switch month'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Config.Dateformat.Switch.month.ACTUAL', NSPanel_Path + 'Config.Dateformat.Switch.month', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Config.Dateformat.Switch.month.SET', NSPanel_Path + 'Config.Dateformat.Switch.month', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'SET',
|
|
});
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function InitDateformat: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
InitDateformat();
|
|
|
|
/**
|
|
* Event listener for changes to the Dateformat states.
|
|
*
|
|
* When the value of one of the `Switch.weekday` or `Switch.month` objects changes,
|
|
* this function is triggered. It updates the corresponding date format state and sends a message to the panel.
|
|
*
|
|
* @param {object} obj - The object containing the new state value.
|
|
* @param {string} obj.id - The ID of the object that changed.
|
|
*
|
|
* @async
|
|
* @throws {Error} If an error occurs while updating the date format state.
|
|
*/
|
|
on({id: [String(NSPanel_Path) + 'Config.Dateformat.Switch.weekday', String(NSPanel_Path) + 'Config.Dateformat.Switch.month'], change: 'ne'}, async function (obj) {
|
|
try {
|
|
if (obj.id == NSPanel_Path + 'Config.Dateformat.Switch.weekday') {
|
|
if (getState(NSPanel_Path + 'Config.Dateformat.Switch.weekday').val) {
|
|
setStateAsync(NSPanel_Path + 'Config.Dateformat.weekday', 'long');
|
|
} else {
|
|
setStateAsync(NSPanel_Path + 'Config.Dateformat.weekday', 'short');
|
|
}
|
|
} else if (obj.id == NSPanel_Path + 'Config.Dateformat.Switch.month') {
|
|
if (getState(NSPanel_Path + 'Config.Dateformat.Switch.month').val) {
|
|
setStateAsync(NSPanel_Path + 'Config.Dateformat.month', 'long');
|
|
} else {
|
|
setStateAsync(NSPanel_Path + 'Config.Dateformat.month', 'short');
|
|
}
|
|
}
|
|
SendDate();
|
|
} catch (err: any) {
|
|
log('error at Trigger Config.Dateformat: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
//Set Relays from Tasmota
|
|
const NSPanelStatTopic = NSPanelSendTopic.replace('.cmnd.', '.stat.').replace(/\.CustomSend$/g, '.');
|
|
|
|
/**
|
|
* Event listener for changes to the POWER1 and POWER2 states in the Tasmota device.
|
|
*
|
|
* When the value of one of the POWER1 or POWER2 objects changes, this function is triggered.
|
|
* It updates the corresponding Relay state in the NSPanel device if the values are different.
|
|
*
|
|
* @param {object} obj - The object containing the new state value.
|
|
* @param {string} obj.id - The ID of the object that changed.
|
|
* @param {string} obj.state.val - The new value of the object.
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
on({id: [String(NSPanelStatTopic) + 'POWER1', String(NSPanelStatTopic) + 'POWER2'], change: 'ne'}, (obj) => {
|
|
if (!obj || !obj.id) return;
|
|
const n = obj.id.substring(obj.id.length - 1);
|
|
if (n === '1' || n === '2') {
|
|
if (getState(NSPanel_Path + 'Relay.' + n).val != obj.state.val) {
|
|
setState(NSPanel_Path + 'Relay.' + n, obj.state.val == 'ON' ? true : false, true);
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
/**
|
|
* Event listener for changes to the Relay.1 and Relay.2 states.
|
|
*
|
|
* When the value of one of the Relay.1 or Relay.2 objects changes, this function is triggered.
|
|
* It sends a request to the Tasmota device to update the corresponding relay state.
|
|
*
|
|
* @param {object} obj - The object containing the new state value.
|
|
* @param {string} obj.id - The ID of the object that changed.
|
|
* @param {boolean} obj.state - The new value of the object.
|
|
*
|
|
* @async
|
|
* @throws {Error} If an error occurs while sending the request to the Tasmota device.
|
|
*/
|
|
on({id: [String(NSPanel_Path) + 'Relay.1', String(NSPanel_Path) + 'Relay.2'], change: 'ne', ack: false}, async function (obj) {
|
|
try {
|
|
let Button = obj.id!.split('.');
|
|
let urlString: string = ['http://', get_current_tasmota_ip_address(), '/cm?cmnd=Power', Button[Button.length - 1], ' ', obj.state ? obj.state.val : ''].join('');
|
|
|
|
axios
|
|
.get(urlString)
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(response.data, 'info');
|
|
}
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
if (error.code === 'EHOSTUNREACH') {
|
|
log(`Can't connect to display!`, 'warn');
|
|
} else log(error, 'warn');
|
|
});
|
|
} catch (err: any) {
|
|
log('error at Trigger Relay1/2: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Subscribe to the specified MQTT entities and their values.
|
|
*
|
|
* This function subscribes to the MQTT entities specified in the configuration
|
|
* and listens for changes to their values. When a value changes, the corresponding
|
|
* relay state is updated.
|
|
*
|
|
* @throws {Error} If an error occurs while subscribing to the MQTT entities.
|
|
*/
|
|
async function SubscribeMRIcons () {
|
|
try {
|
|
const mrEntities = [config.mrIcon1ScreensaverEntity, config.mrIcon2ScreensaverEntity];
|
|
let arr: string[] = []
|
|
for (const mrEntity of mrEntities) {
|
|
arr = typeof mrEntity.ScreensaverEntity == 'string' && mrEntity.ScreensaverEntity !== '' ? [mrEntity.ScreensaverEntity] : [];
|
|
arr = typeof mrEntity.ScreensaverEntityValue == 'string' && mrEntity.ScreensaverEntityValue !== '' ? [...arr, mrEntity.ScreensaverEntityValue] : arr;
|
|
}
|
|
if (arr.length > 0) {
|
|
on({id: arr, change: 'ne'}, async function (obj) {
|
|
if (obj.id && obj.id.substring(0, 4) == 'mqtt' &&(obj.id.endsWith('.stat.POWER1') || obj.id.endsWith('.stat.Power2'))) {
|
|
let Button = obj.id.split('.');
|
|
if (getState(NSPanel_Path + 'Relay.' + Button[Button.length - 1].substring(5, 6)).val != obj.state.val) {
|
|
await setStateAsync(NSPanel_Path + 'Relay.' + Button[Button.length - 1].substring(5, 6), obj.state.val == 'ON' ? true : false);
|
|
}
|
|
} else {
|
|
HandleScreensaverStatusIcons();
|
|
}
|
|
});
|
|
}
|
|
|
|
} catch (err: any) {
|
|
log('error at function SubscribeMRIcons: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
SubscribeMRIcons();
|
|
|
|
/**
|
|
* Create an alias for the weather adapter if it does not exist yet.
|
|
* This alias is needed for the weather display in the screensaver.
|
|
* @async
|
|
* @function CreateWeatherAlias
|
|
*/
|
|
async function CreateWeatherAlias () {
|
|
try {
|
|
if (autoCreateAlias) {
|
|
if (weatherAdapterInstance == 'daswetter.' + weatherAdapterInstanceNumber + '.') {
|
|
try {
|
|
if (isSetOptionActive) {
|
|
if (!existsState(config.weatherEntity + '.ICON') && existsState('daswetter.' + weatherAdapterInstanceNumber + '.NextHours.Location_1.Day_1.current.symbol_value')) {
|
|
log('Weather alias for daswetter.' + weatherAdapterInstanceNumber + '. does not exist yet, will be created now', 'info');
|
|
setObject(config.weatherEntity, {_id: config.weatherEntity, type: 'channel', common: {role: 'weatherCurrent', name: 'weatherCurrent'}, native: {}});
|
|
await createAliasAsync(config.weatherEntity + '.ICON', 'daswetter.' + weatherAdapterInstanceNumber + '.NextHours.Location_1.Day_1.current.symbol_value', true, <
|
|
iobJS.StateCommon
|
|
> {type: 'number', role: 'value', name: 'ICON'});
|
|
await createAliasAsync(config.weatherEntity + '.TEMP', 'daswetter.' + weatherAdapterInstanceNumber + '.NextHours.Location_1.Day_1.current.temp_value', true, <
|
|
iobJS.StateCommon
|
|
> {type: 'number', role: 'value.temperature', name: 'TEMP'});
|
|
await createAliasAsync(config.weatherEntity + '.TEMP_MIN', 'daswetter.' + weatherAdapterInstanceNumber + '.NextDays.Location_1.Day_1.Minimale_Temperatur_value', true, <
|
|
iobJS.StateCommon
|
|
> {type: 'number', role: 'value.temperature.forecast.0', name: 'TEMP_MIN'});
|
|
await createAliasAsync(config.weatherEntity + '.TEMP_MAX', 'daswetter.' + weatherAdapterInstanceNumber + '.NextDays.Location_1.Day_1.Maximale_Temperatur_value', true, <
|
|
iobJS.StateCommon
|
|
> {type: 'number', role: 'value.temperature.max.forecast.0', name: 'TEMP_MAX'});
|
|
} else {
|
|
log('weather alias for daswetter.' + weatherAdapterInstanceNumber + '. already exists', 'info');
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function CreateWeatherAlias daswetter.' + weatherAdapterInstanceNumber + '. : ' + err.message, 'warn');
|
|
}
|
|
} else if (weatherAdapterInstance == 'accuweather.' + weatherAdapterInstanceNumber + '.') {
|
|
try {
|
|
if (isSetOptionActive) {
|
|
if (!existsState(config.weatherEntity + '.ICON') && existsState('accuweather.' + weatherAdapterInstanceNumber + '.Current.WeatherIcon')) {
|
|
log('Weather alias for accuweather.' + weatherAdapterInstanceNumber + '. does not exist yet, will be created now', 'info');
|
|
setObject(config.weatherEntity, {_id: config.weatherEntity, type: 'channel', common: {role: 'weatherCurrent', name: 'weatherCurrent'}, native: {}});
|
|
await createAliasAsync(config.weatherEntity + '.ICON', 'accuweather.' + weatherAdapterInstanceNumber + '.Current.WeatherIcon', true, {
|
|
type: 'number',
|
|
role: 'value',
|
|
name: 'ICON',
|
|
});
|
|
await createAliasAsync(config.weatherEntity + '.TEMP', 'accuweather.' + weatherAdapterInstanceNumber + '.Current.Temperature', true, {
|
|
type: 'number',
|
|
role: 'value.temperature',
|
|
name: 'TEMP',
|
|
});
|
|
await createAliasAsync(config.weatherEntity + '.TEMP_MIN', 'accuweather.' + weatherAdapterInstanceNumber + '.Daily.Day1.Temperature.Minimum', true, {
|
|
type: 'number',
|
|
role: 'value.temperature.forecast.0',
|
|
name: 'TEMP_MIN',
|
|
});
|
|
await createAliasAsync(config.weatherEntity + '.TEMP_MAX', 'accuweather.' + weatherAdapterInstanceNumber + '.Daily.Day1.Temperature.Maximum', true, {
|
|
type: 'number',
|
|
role: 'value.temperature.max.forecast.0',
|
|
name: 'TEMP_MAX',
|
|
});
|
|
} else {
|
|
log('weather alias for accuweather.' + weatherAdapterInstanceNumber + '. already exists', 'info');
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function CreateWeatherAlias accuweather.' + weatherAdapterInstanceNumber + '.: ' + err.message, 'warn');
|
|
}
|
|
} else if (weatherAdapterInstance == 'openweathermap.' + weatherAdapterInstanceNumber + '.') {
|
|
try {
|
|
if (isSetOptionActive) {
|
|
if (!existsState(config.weatherEntity + '.ICON') && existsState('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.current.icon')) {
|
|
log('Weather alias for openweathermap.' + weatherAdapterInstanceNumber + '. does not exist yet, will be created now', 'info');
|
|
setObject(config.weatherEntity, {_id: config.weatherEntity, type: 'channel', common: {role: 'weatherCurrent', name: 'weatherCurrent'}, native: {}});
|
|
await createAliasAsync(config.weatherEntity + '.ICON', ('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.current.icon'), true, {
|
|
type: 'string',
|
|
role: 'value',
|
|
name: 'ICON',
|
|
alias: {id: 'openweathermap.' + weatherAdapterInstanceNumber + '.forecast.current.icon', read: '(val.split("/").pop()).split(".").shift()'},
|
|
});
|
|
await createAliasAsync(config.weatherEntity + '.TEMP', 'openweathermap.' + weatherAdapterInstanceNumber + '.forecast.current.temperature', true, {
|
|
type: 'number',
|
|
role: 'value.temperature',
|
|
name: 'TEMP',
|
|
alias: {id: 'openweathermap.' + weatherAdapterInstanceNumber + '.forecast.current.temperature', read: 'Math.round(val*10)/10'},
|
|
});
|
|
await createAliasAsync(config.weatherEntity + '.TEMP_MIN', 'openweathermap.' + weatherAdapterInstanceNumber + '.forecast.current.temperatureMin', true, {
|
|
type: 'number',
|
|
role: 'value.temperature.forecast.0',
|
|
name: 'TEMP_MIN',
|
|
alias: {id: 'openweathermap.' + weatherAdapterInstanceNumber + '.forecast.current.temperatureMin', read: 'Math.round(val)'},
|
|
});
|
|
await createAliasAsync(config.weatherEntity + '.TEMP_MAX', 'openweathermap.' + weatherAdapterInstanceNumber + '.forecast.current.temperatureMax', true, {
|
|
type: 'number',
|
|
role: 'value.temperature.max.forecast.0',
|
|
name: 'TEMP_MAX',
|
|
alias: {id: 'openweathermap.' + weatherAdapterInstanceNumber + '.forecast.current.temperatureMax', read: 'Math.round(val)'},
|
|
});
|
|
} else {
|
|
log('weather alias for openweathermap.' + weatherAdapterInstanceNumber + '. already exists', 'info');
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function CreateWeatherAlias openweathermap.' + weatherAdapterInstanceNumber + '.: ' + err.message, 'warn');
|
|
}
|
|
} else if (weatherAdapterInstance == 'pirate-weather.' + weatherAdapterInstanceNumber + '.') {
|
|
try {
|
|
if (isSetOptionActive) {
|
|
if (!existsState(config.weatherEntity + '.ICON') && existsState('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.currently.icon')) {
|
|
log('Weather alias for pirate-weather.' + weatherAdapterInstanceNumber + '. does not exist yet, will be created now', 'info');
|
|
setObject(config.weatherEntity, {_id: config.weatherEntity, type: 'channel', common: {role: 'weatherCurrent', name: 'weatherCurrent'}, native: {}});
|
|
await createAliasAsync(config.weatherEntity + '.ICON', ('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.currently.icon'), true, {
|
|
type: 'string',
|
|
role: 'value',
|
|
name: 'ICON',
|
|
alias: {id: 'pirate-weather.' + weatherAdapterInstanceNumber + '.weather.currently.icon'},
|
|
});
|
|
await createAliasAsync(config.weatherEntity + '.TEMP', 'pirate-weather.' + weatherAdapterInstanceNumber + '.weather.currently.temperature', true, {
|
|
type: 'number',
|
|
role: 'value.temperature',
|
|
name: 'TEMP',
|
|
alias: {id: 'pirate-weather.' + weatherAdapterInstanceNumber + '.weather.currently.temperature', read: 'Math.round(val*10)/10'},
|
|
});
|
|
await createAliasAsync(config.weatherEntity + '.TEMP_MIN', 'pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.00.temperatureMin', true, {
|
|
type: 'number',
|
|
role: 'value.temperature.forecast.0',
|
|
name: 'TEMP_MIN',
|
|
alias: {id: 'pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.00.temperatureMin', read: 'Math.round(val)'},
|
|
});
|
|
await createAliasAsync(config.weatherEntity + '.TEMP_MAX', 'pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.00.temperatureMax', true, {
|
|
type: 'number',
|
|
role: 'value.temperature.max.forecast.0',
|
|
name: 'TEMP_MAX',
|
|
alias: {id: 'pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.00.temperatureMax', read: 'Math.round(val)'},
|
|
});
|
|
} else {
|
|
log('weather alias for pirate-weather.' + weatherAdapterInstanceNumber + '. already exists', 'info');
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function CreateWeatherAlias pirate-weather.' + weatherAdapterInstanceNumber + '.: ' + err.message, 'warn');
|
|
}
|
|
} else if (weatherAdapterInstance == 'brightsky.' + weatherAdapterInstanceNumber + '.') {
|
|
try {
|
|
if (isSetOptionActive) {
|
|
if (!existsState(config.weatherEntity + '.ICON') && existsState('brightsky.' + weatherAdapterInstanceNumber + '.current.icon')) {
|
|
log('Weather alias for brightsky.' + weatherAdapterInstanceNumber + '. does not exist yet, will be created now', 'info');
|
|
setObject(config.weatherEntity, {_id: config.weatherEntity, type: 'channel', common: {role: 'weatherCurrent', name: 'weatherCurrent'}, native: {}});
|
|
await createAliasAsync(config.weatherEntity + '.ICON', ('brightsky.' + weatherAdapterInstanceNumber + '.current.icon'), true, {
|
|
type: 'string',
|
|
role: 'value',
|
|
name: 'ICON',
|
|
alias: {id: 'brightsky.' + weatherAdapterInstanceNumber + '.current.icon'},
|
|
});
|
|
await createAliasAsync(config.weatherEntity + '.TEMP', 'brightsky.' + weatherAdapterInstanceNumber + '.current.temperature', true, {
|
|
type: 'number',
|
|
role: 'value.temperature',
|
|
name: 'TEMP',
|
|
alias: {id: 'brightsky.' + weatherAdapterInstanceNumber + '.current.temperature', read: 'Math.round(val*10)/10'},
|
|
});
|
|
await createAliasAsync(config.weatherEntity + '.TEMP_MIN', 'brightsky.' + weatherAdapterInstanceNumber + '.daily.00.temperature_min', true, {
|
|
type: 'number',
|
|
role: 'value.temperature.forecast.0',
|
|
name: 'TEMP_MIN',
|
|
alias: {id: 'brightsky.' + weatherAdapterInstanceNumber + '.daily.00.temperature_min', read: 'Math.round(val)'},
|
|
});
|
|
await createAliasAsync(config.weatherEntity + '.TEMP_MAX', 'brightsky.' + weatherAdapterInstanceNumber + '.daily.00.temperature_max', true, {
|
|
type: 'number',
|
|
role: 'value.temperature.max.forecast.0',
|
|
name: 'TEMP_MAX',
|
|
alias: {id: 'brightsky.' + weatherAdapterInstanceNumber + '.daily.00.temperature_max', read: 'Math.round(val)'},
|
|
});
|
|
} else {
|
|
log('weather alias for brightsky.' + weatherAdapterInstanceNumber + '. already exists', 'info');
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function CreateWeatherAlias brightsky.' + weatherAdapterInstanceNumber + '.: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function CreateWeatherAlias: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
CreateWeatherAlias();
|
|
|
|
/**
|
|
* Initializes the PageNavi state if it does not exist.
|
|
* This function creates a new state for PageNavi with a default value if it is not already present.
|
|
* It sets the state to a JSON string representing the default page navigation.
|
|
* Logs a warning message if an error occurs during the state creation or setting process.
|
|
*
|
|
* @returns {Promise<void>} - A Promise that resolves when the state is created and set successfully.
|
|
*/
|
|
async function InitPageNavi () {
|
|
try {
|
|
if (!existsState(NSPanel_Path + 'PageNavi')) {
|
|
await createStateAsync(NSPanel_Path + 'PageNavi', {type: 'string', write: true});
|
|
await setStateAsync(NSPanel_Path + 'PageNavi', {val: "{ pagetype: 'page', pageId: 0 }", ack: true});
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function InitPageNavi: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
InitPageNavi();
|
|
|
|
/**
|
|
* Event listener for changes to the PageNavi state.
|
|
*
|
|
* When the value of the PageNavi object changes, this function is triggered.
|
|
* It generates a page or subpage based on the new value.
|
|
*
|
|
* @param {object} obj - The object containing the new state value.
|
|
* @param {string} obj.state.val - The new value of the object as a JSON string.
|
|
*
|
|
* @async
|
|
* @throws {Error} If an error occurs while parsing the JSON value or generating the page.
|
|
*/
|
|
on({id: [NSPanel_Path + 'PageNavi'], change: 'any'}, async function (obj) {
|
|
try {
|
|
if (existsState(NSPanel_Path + 'PageNavi')) {
|
|
try {
|
|
let vObj = JSON.parse(obj.state.val);
|
|
if (vObj.pagetype == 'page') {
|
|
GeneratePage(config.pages[vObj.pageId]);
|
|
} else if (vObj.pagetype == 'subpage') {
|
|
GeneratePage(config.subPages[vObj.pageId]);
|
|
}
|
|
} catch (e) {
|
|
log('non valid JSON at trigger PageNavi', 'info');
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at Trigger PageNavi: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @function ScreensaverDimmode
|
|
* @description This function sets the dimmode based on the current time and the time settings in the config.
|
|
* @param {NSPanel.DimMode} timeDimMode - The object containing the settings for the screensaver dimmode.
|
|
* @returns {void}
|
|
* @throws {Error} If an error occurs while sending the payload to the panel.
|
|
*/
|
|
function ScreensaverDimmode (timeDimMode: NSPanel.DimMode) {
|
|
try {
|
|
let brightness: number = 100;
|
|
let dimBrightness: number = -1;
|
|
if (existsState(NSPanel_Path + 'ScreensaverInfo.activeBrightness')) {
|
|
brightness = getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val;
|
|
}
|
|
if (existsState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness')) {
|
|
dimBrightness = getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val;
|
|
}
|
|
let active = brightness ?? 80;
|
|
let dimmode = dimBrightness ?? -1;
|
|
if (Debug) {
|
|
log('function ScreensaverDimmode Background rgb_dec565: ' + rgb_dec565(config.defaultBackgroundColor), 'info');
|
|
}
|
|
if (Debug) {
|
|
log('function ScreensaverDimmode Dimmode=' + timeDimMode.dimmodeOn, 'info');
|
|
}
|
|
if (timeDimMode.dimmodeOn != undefined ? timeDimMode.dimmodeOn : false) {
|
|
if (compareTime(timeDimMode.timeNight != undefined ? timeDimMode.timeNight : '22:00', timeDimMode.timeDay != undefined ? timeDimMode.timeDay : '07:00', 'not between', undefined)) {
|
|
SendToPanel({payload: 'dimmode~' + timeDimMode.brightnessDay + '~' + active + '~' + rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor)});
|
|
if (Debug) {
|
|
log('function ScreensaverDimmode -> Day NSPanel.Payload: ' + 'dimmode~' + timeDimMode.brightnessDay + '~' + active, 'info');
|
|
}
|
|
} else {
|
|
SendToPanel({
|
|
payload: 'dimmode~' + timeDimMode.brightnessNight + '~' + active + '~' + rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor)});
|
|
if (Debug) {
|
|
log('function ScreensaverDimmode -> Night NSPanel.Payload: ' + 'dimmode~' + timeDimMode.brightnessNight + '~' + active, 'info');
|
|
}
|
|
}
|
|
} else {
|
|
SendToPanel({payload: 'dimmode~' + dimmode + '~' + active + '~' + rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor)});
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function ScreensaverDimmode: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initializes the weather forecast states if they do not exist.
|
|
* This function creates a new state for weatherForecast, weatherForecastTimer and entityChangeTime with a default value if they are not already present.
|
|
* It sets the state to a JSON string representing the default page navigation.
|
|
* Logs a warning message if an error occurs during the state creation or setting process.
|
|
*
|
|
* @returns {Promise<void>} - A Promise that resolves when the states are created and set successfully.
|
|
*/
|
|
async function InitWeatherForecast () {
|
|
try {
|
|
if (isSetOptionActive) {
|
|
//----Ability to choose between Accu-Weather Forecast or self-defined values in the screensaver---------------------------------
|
|
if (
|
|
existsState(NSPanel_Path + 'ScreensaverInfo.weatherForecast') == false ||
|
|
existsState(NSPanel_Path + 'ScreensaverInfo.weatherForecastTimer') == false ||
|
|
existsState(NSPanel_Path + 'ScreensaverInfo.entityChangeTime') == false
|
|
) {
|
|
await createStateAsync(NSPanel_Path + 'ScreensaverInfo.weatherForecast', true, {type: 'boolean', write: true});
|
|
await createStateAsync(NSPanel_Path + 'ScreensaverInfo.weatherForecastTimer', true, {type: 'boolean', write: true});
|
|
await createStateAsync(NSPanel_Path + 'ScreensaverInfo.entityChangeTime', 60, {type: 'number', write: true});
|
|
}
|
|
//Create Alias weatherForecast
|
|
setObject(AliasPath + 'ScreensaverInfo.weatherForecast', {type: 'channel', common: {role: 'socket', name: 'weatherForecast'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'ScreensaverInfo.weatherForecast.ACTUAL', NSPanel_Path + 'ScreensaverInfo.weatherForecast', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'ScreensaverInfo.weatherForecast.SET', NSPanel_Path + 'ScreensaverInfo.weatherForecast', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'SET',
|
|
});
|
|
//Create Alias weatherForecastTimer
|
|
setObject(AliasPath + 'ScreensaverInfo.weatherForecastTimer', {type: 'channel', common: {role: 'socket', name: 'weatherForecastTimer'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'ScreensaverInfo.weatherForecastTimer.ACTUAL', NSPanel_Path + 'ScreensaverInfo.weatherForecastTimer', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'ScreensaverInfo.weatherForecastTimer.SET', NSPanel_Path + 'ScreensaverInfo.weatherForecastTimer', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'SET',
|
|
});
|
|
//Create Alias entityChangeTime
|
|
setObject(AliasPath + 'ScreensaverInfo.entityChangeTime', {type: 'channel', common: {role: 'slider', name: 'entityChangeTime'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'ScreensaverInfo.entityChangeTime.ACTUAL', NSPanel_Path + 'ScreensaverInfo.entityChangeTime', true, {
|
|
type: 'number',
|
|
role: 'value',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'ScreensaverInfo.entityChangeTime.SET', NSPanel_Path + 'ScreensaverInfo.entityChangeTime', true, {
|
|
type: 'number',
|
|
role: 'level',
|
|
name: 'SET',
|
|
});
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function InitWeatherForecast: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
InitWeatherForecast();
|
|
|
|
/**
|
|
* Initializes the states for the screensaver dimmode.
|
|
* This function creates a new state for NSPanel_Dimmode_brightnessDay, NSPanel_Dimmode_hourDay, NSPanel_Dimmode_brightnessNight and NSPanel_Dimmode_hourNight with a default value if they are not already present.
|
|
* It sets the state to a JSON string representing the default page navigation.
|
|
* Logs a warning message if an error occurs during the state creation or setting process.
|
|
* Additionally it schedules two timers: one for the day and one for the night to switch the brightness according to the settings.
|
|
*
|
|
* @returns {Promise<void>} - A Promise that resolves when the states are created and set successfully.
|
|
*/
|
|
async function InitDimmode () {
|
|
try {
|
|
if (isSetOptionActive) {
|
|
// Screensaver on dark at night ("brightnessNight: e.g. 2") or off ("brightnessNight:0")
|
|
if (!existsState(NSPanel_Path + 'NSPanel_Dimmode_brightnessDay')) {
|
|
await createStateAsync(NSPanel_Path + 'NSPanel_Dimmode_brightnessDay', {type: 'number', write: true});
|
|
await setStateAsync(NSPanel_Path + 'NSPanel_Dimmode_brightnessDay', {val: 8, ack: true});
|
|
setObject(AliasPath + 'Dimmode.brightnessDay', {type: 'channel', common: {role: 'slider', name: 'brightnessDay'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Dimmode.brightnessDay.ACTUAL', NSPanel_Path + 'NSPanel_Dimmode_brightnessDay', true, {
|
|
type: 'number',
|
|
role: 'value',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Dimmode.brightnessDay.SET', NSPanel_Path + 'NSPanel_Dimmode_brightnessDay', true, {
|
|
type: 'number',
|
|
role: 'level',
|
|
name: 'SET',
|
|
});
|
|
}
|
|
if (!existsState(NSPanel_Path + 'NSPanel_Dimmode_hourDay')) {
|
|
await createStateAsync(NSPanel_Path + 'NSPanel_Dimmode_hourDay', {type: 'number', write: true});
|
|
await setStateAsync(NSPanel_Path + 'NSPanel_Dimmode_hourDay', {val: 7, ack: true});
|
|
setObject(AliasPath + 'Dimmode.hourDay', {type: 'channel', common: {role: 'slider', name: 'hourDay'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Dimmode.hourDay.ACTUAL', NSPanel_Path + 'NSPanel_Dimmode_hourDay', true, {type: 'number', role: 'value', name: 'ACTUAL'});
|
|
await createAliasAsync(AliasPath + 'Dimmode.hourDay.SET', NSPanel_Path + 'NSPanel_Dimmode_hourDay', true, {type: 'number', role: 'level', name: 'SET'});
|
|
}
|
|
if (!existsState(NSPanel_Path + 'NSPanel_Dimmode_brightnessNight')) {
|
|
await createStateAsync(NSPanel_Path + 'NSPanel_Dimmode_brightnessNight', {type: 'number', write: true});
|
|
await setStateAsync(NSPanel_Path + 'NSPanel_Dimmode_brightnessNight', {val: 1, ack: true});
|
|
setObject(AliasPath + 'Dimmode.brightnessNight', {type: 'channel', common: {role: 'slider', name: 'brightnessNight'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Dimmode.brightnessNight.ACTUAL', NSPanel_Path + 'NSPanel_Dimmode_brightnessNight', true, {
|
|
type: 'number',
|
|
role: 'value',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Dimmode.brightnessNight.SET', NSPanel_Path + 'NSPanel_Dimmode_brightnessNight', true, {
|
|
type: 'number',
|
|
role: 'level',
|
|
name: 'SET',
|
|
});
|
|
}
|
|
if (!existsState(NSPanel_Path + 'NSPanel_Dimmode_hourNight')) {
|
|
await createStateAsync(NSPanel_Path + 'NSPanel_Dimmode_hourNight', {type: 'number', write: true});
|
|
await setStateAsync(NSPanel_Path + 'NSPanel_Dimmode_hourNight', {val: 22, ack: true});
|
|
setObject(AliasPath + 'Dimmode.hourNight', {type: 'channel', common: {role: 'slider', name: 'hourNight'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Dimmode.hourNight.ACTUAL', NSPanel_Path + 'NSPanel_Dimmode_hourNight', true, {type: 'number', role: 'value', name: 'ACTUAL'});
|
|
await createAliasAsync(AliasPath + 'Dimmode.hourNight.SET', NSPanel_Path + 'NSPanel_Dimmode_hourNight', true, {type: 'number', role: 'level', name: 'SET'});
|
|
}
|
|
const vTimeDay = getState(NSPanel_Path + 'NSPanel_Dimmode_hourDay').val;
|
|
const vTimeNight = getState(NSPanel_Path + 'NSPanel_Dimmode_hourNight').val;
|
|
const timeDimMode: NSPanel.DimMode = {
|
|
dimmodeOn: true,
|
|
brightnessDay: getState(NSPanel_Path + 'NSPanel_Dimmode_brightnessDay').val,
|
|
brightnessNight: getState(NSPanel_Path + 'NSPanel_Dimmode_brightnessNight').val,
|
|
timeDay: vTimeDay < 10 ? `0${vTimeDay}:00` : `${vTimeDay}:00`,
|
|
timeNight: vTimeNight < 10 ? `0${vTimeNight}:00` : `${vTimeNight}:00`,
|
|
};
|
|
// timeDimMode Day
|
|
scheduleInitDimModeDay = adapterSchedule({hour: getState(NSPanel_Path + 'NSPanel_Dimmode_hourDay').val, minute: 0}, 24 * 60 * 60, () => {
|
|
if (getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val != null && getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val == -1) {
|
|
ScreensaverDimmode(timeDimMode);
|
|
}
|
|
});
|
|
// timeDimMode Night
|
|
scheduleInitDimModeNight = adapterSchedule({hour: getState(NSPanel_Path + 'NSPanel_Dimmode_hourNight').val, minute: 0}, 24 * 60 * 60, () => {
|
|
if (getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val != null && getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val == -1) {
|
|
ScreensaverDimmode(timeDimMode);
|
|
}
|
|
});
|
|
if (getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val != null && getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val != -1) {
|
|
SendToPanel({
|
|
payload:
|
|
'dimmode~' + getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val + '~' + (getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val ?? '80') + '~' +
|
|
rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor)
|
|
});
|
|
} else {
|
|
if (isDimTimeInRange(timeDimMode.timeDay, timeDimMode.timeNight)) {
|
|
SendToPanel({
|
|
payload:
|
|
'dimmode~' + timeDimMode.brightnessDay + '~' + (getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val ??
|
|
'80') + '~' + rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor)
|
|
});
|
|
} else {
|
|
SendToPanel({
|
|
payload:
|
|
'dimmode~' + timeDimMode.brightnessNight + '~' + (getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val ??
|
|
'80') + '~' + rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor)
|
|
});
|
|
}
|
|
ScreensaverDimmode(timeDimMode);
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function InitDimmode: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
InitDimmode();
|
|
|
|
/**
|
|
* Returns a Date object that is set to the current date, but with the time set to 00:00:00.
|
|
* This is used to compare with the Dimmode dates.
|
|
* @returns {Date} The current date as a Date object.
|
|
*/
|
|
function currentDimDate () {
|
|
let d = new Date();
|
|
return new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
}
|
|
|
|
/**
|
|
* Takes a string in the format HH:MM:SS and adds it to the current date.
|
|
* @param {string} strTime - The time string to add to the current date.
|
|
* @returns {Date} The resulting date object.
|
|
*/
|
|
function addDimTime (strTime) {
|
|
let time = strTime.split(':');
|
|
let d = currentDimDate();
|
|
d.setHours(time[0]);
|
|
d.setMinutes(time[1]);
|
|
d.setSeconds(time[2]);
|
|
return d;
|
|
}
|
|
|
|
/**
|
|
* Checks if the current time is within the given range.
|
|
* The range is defined by two strings in the format HH:MM:SS.
|
|
* If the upper time is before the lower time, it is assumed that the range
|
|
* spans over midnight.
|
|
* @param {string} strLower - The lower bound of the range.
|
|
* @param {string} strUpper - The upper bound of the range.
|
|
* @returns {boolean} true if the current time is within the range, false otherwise.
|
|
*/
|
|
function isDimTimeInRange (strLower, strUpper) {
|
|
let now = new Date();
|
|
let lower = addDimTime(strLower);
|
|
let upper = addDimTime(strUpper);
|
|
let inRange = false;
|
|
if (upper > lower) {
|
|
// opens and closes in same day
|
|
inRange = now >= lower && now <= upper ? true : false;
|
|
} else {
|
|
// closes in the following day
|
|
inRange = now >= upper && now <= lower ? false : true;
|
|
}
|
|
return inRange;
|
|
}
|
|
|
|
//--------------------End Dimmode
|
|
|
|
//--------------------Begin Consumtion (with Dimmode and Relays On Off)
|
|
|
|
/**
|
|
* Calculates the mean linear consumption of the panel based on the given Brightness and number of Relays On.
|
|
* If Relays is undefined, it is assumed to be 0.
|
|
* @param {number} Brightness - The current brightness setting of the panel.
|
|
* @param {number|undefined} [Relays] - The number of relays that are currently on. If undefined, it is assumed to be 0.
|
|
* @returns {number|undefined} The calculated consumption in Watts, or undefined if an error occurs.
|
|
*/
|
|
async function Calc_Consumption (Brightness: number, Relays: number | undefined) {
|
|
try {
|
|
if (Relays == undefined) Relays = 0;
|
|
return parseFloat((Relays * 0.25 + (0.0086 * Brightness + 0.7429)).toFixed(2));
|
|
} catch (err: any) {
|
|
log('error at function Calc_Consumption: ' + err.message, 'warn');
|
|
return undefined
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Counts the number of Relays that are currently on in the given Path.
|
|
* @param {string} Path - The path to the Relays states.
|
|
* @returns {Promise<number>} The number of Relays that are currently on.
|
|
*/
|
|
async function CountRelaysOn (Path: string): Promise<number> {
|
|
try {
|
|
let r1: boolean = true;
|
|
let r2: boolean = true;
|
|
if (existsState(Path + 'Relay.1')) r1 = getState(Path + 'Relay.1').val;
|
|
if (existsState(Path + 'Relay.2')) r2 = getState(Path + 'Relay.2').val;
|
|
if (r1 && r2) {
|
|
return 2;
|
|
} else if (!r1 && !r2) {
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function CountRelaysOn: ' + err.message, 'warn');
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determines the current brightness based on the Dimmode settings and the current time of day.
|
|
* If the current page is the screensaver, it calls DetermineScreensaverDimmode to determine the brightness.
|
|
* If the current page is not the screensaver, it returns the active brightness.
|
|
* If the activeDimmodeBrightness is set to -1, it returns the active brightness.
|
|
* @param {string} Path - The path to the Dimmode settings and the ScreensaverInfo state.
|
|
* @returns {Promise<number|undefined>} The current brightness or undefined if an error occurs.
|
|
*/
|
|
async function DetermineDimBrightness (Path: string) {
|
|
if (existsState(NSPanel_Path + 'NSPanel_Dimmode_hourDay') &&
|
|
existsState(NSPanel_Path + 'NSPanel_Dimmode_hourNight') &&
|
|
existsState(NSPanel_Path + 'NSPanel_Dimmode_brightnessDay') &&
|
|
existsState(NSPanel_Path + 'NSPanel_Dimmode_brightnessNight') &&
|
|
existsState(NSPanel_Path + 'ScreensaverInfo.activeBrightness') &&
|
|
existsState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness') &&
|
|
existsState(NSPanel_Path + 'ActivePage.id0')
|
|
) {
|
|
try {
|
|
const vTimeDay = getState(Path + 'NSPanel_Dimmode_hourDay').val;
|
|
const vTimeNight = getState(Path + 'NSPanel_Dimmode_hourNight').val;
|
|
const timeDimMode: NSPanel.DimMode = {
|
|
dimmodeOn: true,
|
|
brightnessDay: getState(NSPanel_Path + 'NSPanel_Dimmode_brightnessDay').val,
|
|
brightnessNight: getState(NSPanel_Path + 'NSPanel_Dimmode_brightnessNight').val,
|
|
timeDay: vTimeDay < 10 ? `0${vTimeDay}:00` : `${vTimeDay}:00`,
|
|
timeNight: vTimeNight < 10 ? `0${vTimeNight}:00` : `${vTimeNight}:00`,
|
|
};
|
|
|
|
if (getState(Path + 'ScreensaverInfo.activeDimmodeBrightness').val == -1) {
|
|
if (getState(Path + 'ActivePage.id0').val == 'screensaver') {
|
|
return await DetermineScreensaverDimmode(timeDimMode);
|
|
} else {
|
|
return getState(Path + 'ScreensaverInfo.activeBrightness').val;
|
|
}
|
|
} else {
|
|
return getState(Path + 'ScreensaverInfo.activeDimmodeBrightness').val;
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function DetermineDimBrightness: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Determines the current brightness based on the Dimmode settings and the current time of day for the screensaver page.
|
|
* @param {NSPanel.DimMode} timeDimMode - The object containing the Dimmode settings.
|
|
* @returns {Promise<number>} The current brightness or 100 if an error occurs.
|
|
*/
|
|
async function DetermineScreensaverDimmode (timeDimMode: NSPanel.DimMode): Promise<number> {
|
|
try {
|
|
if (timeDimMode.dimmodeOn != undefined ? timeDimMode.dimmodeOn : false) {
|
|
if (compareTime(timeDimMode.timeNight != undefined ? timeDimMode.timeNight : '22:00', timeDimMode.timeDay != undefined ? timeDimMode.timeDay : '07:00', 'not between', undefined)) {
|
|
return timeDimMode.brightnessDay ?? 100;
|
|
} else {
|
|
return timeDimMode.brightnessNight ?? 100;
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function DetermineScreensaverDimmode: ' + err.message, 'warn');
|
|
}
|
|
return 100
|
|
}
|
|
|
|
/**
|
|
* Initializes the mean power consumption of the NSPanel.
|
|
*
|
|
* This function calculates the mean power consumption based on the brightness and the number of relays that are on.
|
|
* If the state for mean power consumption does not exist, it will be created.
|
|
* The calculated value is then written to the state.
|
|
*
|
|
* @async
|
|
* @function InitMeanPowerConsumption
|
|
* @returns {Promise<void>} A promise that resolves when the initialization is complete.
|
|
* @throws {Error} If an error occurs during initialization.
|
|
*/
|
|
async function InitMeanPowerConsumption () {
|
|
try {
|
|
const meanPower = NSPanel_Path + 'Consumption.MeanPower';
|
|
let meanConsumption: number | undefined = await Calc_Consumption(await DetermineDimBrightness(NSPanel_Path), await CountRelaysOn(NSPanel_Path));
|
|
if (meanConsumption === undefined) {
|
|
meanConsumption = 0; // Assign a default value if undefined
|
|
}
|
|
if (!existsState(meanPower)) {
|
|
await createStateAsync(meanPower, {type: 'number', write: true, unit: 'W'});
|
|
}
|
|
await setStateAsync(meanPower, {val: meanConsumption, ack: true});
|
|
if (Debug) log(meanConsumption + ' W', 'info');
|
|
} catch (err: any) {
|
|
log('error at function InitMeanPowerConsumption: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
InitMeanPowerConsumption();
|
|
|
|
/**
|
|
* Sets up a subscription to monitor changes in specific states and triggers the initialization of mean power consumption.
|
|
*
|
|
* This subscription listens for changes in the following states:
|
|
* - NSPanel_Dimmode_brightnessDay
|
|
* - NSPanel_Dimmode_brightnessNight
|
|
* - ScreensaverInfo.activeBrightness
|
|
* - ScreensaverInfo.activeDimmodeBrightness
|
|
* - Relay.1
|
|
* - Relay.2
|
|
* - ActivePage.id0
|
|
*
|
|
* When any of these states change, the `InitMeanPowerConsumption` function is called to recalculate and update the mean power consumption.
|
|
*
|
|
* @event
|
|
*/
|
|
on(
|
|
{
|
|
id: []
|
|
.concat(NSPanel_Path + 'NSPanel_Dimmode_brightnessDay')
|
|
.concat(NSPanel_Path + 'NSPanel_Dimmode_brightnessNight')
|
|
.concat(NSPanel_Path + 'ScreensaverInfo.activeBrightness')
|
|
.concat(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness')
|
|
.concat(NSPanel_Path + 'Relay.1')
|
|
.concat(NSPanel_Path + 'Relay.2')
|
|
.concat(NSPanel_Path + 'ActivePage.id0'),
|
|
change: 'any',
|
|
},
|
|
async (obj) => {
|
|
await InitMeanPowerConsumption();
|
|
}
|
|
);
|
|
|
|
//--------------------End Consumtion
|
|
|
|
// Data points for message to screensaver
|
|
const screensaverNotifyHeading = NSPanel_Path + 'ScreensaverInfo.popupNotifyHeading';
|
|
const screensaverNotifyText = NSPanel_Path + 'ScreensaverInfo.popupNotifyText';
|
|
|
|
// Data points for message popupNotify Page
|
|
const popupNotifyHeading = NSPanel_Path + 'popupNotify.popupNotifyHeading';
|
|
const popupNotifyHeadingColor = NSPanel_Path + 'popupNotify.popupNotifyHeadingColor';
|
|
const popupNotifyText = NSPanel_Path + 'popupNotify.popupNotifyText';
|
|
const popupNotifyTextColor = NSPanel_Path + 'popupNotify.popupNotifyTextColor';
|
|
const popupNotifyInternalName = NSPanel_Path + 'popupNotify.popupNotifyInternalName'; // Written back with button action
|
|
const popupNotifyButton1TextColor = NSPanel_Path + 'popupNotify.popupNotifyButton1TextColor';
|
|
const popupNotifyButton1Text = NSPanel_Path + 'popupNotify.popupNotifyButton1Text';
|
|
const popupNotifyButton2TextColor = NSPanel_Path + 'popupNotify.popupNotifyButton2TextColor';
|
|
const popupNotifyButton2Text = NSPanel_Path + 'popupNotify.popupNotifyButton2Text';
|
|
const popupNotifySleepTimeout = NSPanel_Path + 'popupNotify.popupNotifySleepTimeout'; // in sec. / if 0, then the message remains
|
|
const popupNotifyAction = NSPanel_Path + 'popupNotify.popupNotifyAction'; // Response from the panel true/false
|
|
const popupNotifyLayout = NSPanel_Path + 'popupNotify.popupNotifyLayout';
|
|
const popupNotifyFontIdText = NSPanel_Path + 'popupNotify.popupNotifyFontIdText'; // 1 - 5
|
|
const popupNotifyIcon = NSPanel_Path + 'popupNotify.popupNotifyIcon'; // 1 - 5
|
|
const popupNotifyIconColor = NSPanel_Path + 'popupNotify.popupNotifyIconColor'; // 1 - 5
|
|
const popupNotifyBuzzer = NSPanel_Path + 'popupNotify.popupNotifyBuzzer'; // 1,1,1 -> off 0
|
|
|
|
/**
|
|
* Initializes the popup notification system for the NSPanel.
|
|
*
|
|
* This function creates the necessary states for popup notifications and sets up event listeners to handle notifications.
|
|
* It handles notifications for both the screensaver and a separate popup page.
|
|
*
|
|
* @async
|
|
* @function InitPopupNotify
|
|
* @returns {Promise<void>} A promise that resolves when the initialization is complete.
|
|
* @throws {Error} If an error occurs during initialization.
|
|
*/
|
|
async function InitPopupNotify () {
|
|
try {
|
|
if (!existsState(screensaverNotifyHeading)) {
|
|
await createStateAsync(screensaverNotifyHeading, {type: 'string', write: true});
|
|
await setStateAsync(screensaverNotifyHeading, {val: '', ack: true});
|
|
}
|
|
|
|
if (!existsState(screensaverNotifyText)) {
|
|
await createStateAsync(screensaverNotifyText, {type: 'string', write: true});
|
|
await setStateAsync(screensaverNotifyText, {val: '', ack: true});
|
|
}
|
|
|
|
await createStateAsync(popupNotifyHeading, {type: 'string', write: true});
|
|
await createStateAsync(popupNotifyHeadingColor, {type: 'string', write: true});
|
|
await createStateAsync(popupNotifyText, {type: 'string', write: true});
|
|
await createStateAsync(popupNotifyTextColor, {type: 'string', write: true});
|
|
await createStateAsync(popupNotifyInternalName, {type: 'string', write: true});
|
|
await createStateAsync(popupNotifyButton1Text, {type: 'string', write: true});
|
|
await createStateAsync(popupNotifyButton1TextColor, {type: 'string', write: true});
|
|
await createStateAsync(popupNotifyButton2Text, {type: 'string', write: true});
|
|
await createStateAsync(popupNotifyButton2TextColor, {type: 'string', write: true});
|
|
await createStateAsync(popupNotifySleepTimeout, {type: 'number', write: true});
|
|
await createStateAsync(popupNotifyAction, {type: 'boolean', write: true});
|
|
await createStateAsync(popupNotifyLayout, {type: 'number', write: true});
|
|
await createStateAsync(popupNotifyFontIdText, {type: 'number', write: true});
|
|
await createStateAsync(popupNotifyIcon, {type: 'string', write: true});
|
|
await createStateAsync(popupNotifyIconColor, {type: 'string', write: true});
|
|
await createStateAsync(popupNotifyBuzzer, {type: 'string', def: '0', write: true});
|
|
|
|
// Notification to screensaver
|
|
on({id: [screensaverNotifyHeading, screensaverNotifyText], change: 'ne', ack: false}, async (obj) => {
|
|
let heading: string = '';
|
|
let text: string = '';
|
|
if (existsState(screensaverNotifyHeading)) {
|
|
heading = getState(screensaverNotifyHeading).val;
|
|
}
|
|
if (existsState(screensaverNotifyText)) {
|
|
text = getState(screensaverNotifyText).val;
|
|
}
|
|
|
|
if (screensaverEnabled && heading != '' && text != '') {
|
|
setIfExists(config.panelSendTopic, `notify~${heading}~${text}`);
|
|
}
|
|
|
|
if (obj.id) {
|
|
await setStateAsync(obj.id, {val: obj.state.val, ack: true}); // ack new value
|
|
}
|
|
});
|
|
|
|
// popupNotify - Notification to a separate page
|
|
on({id: [popupNotifyText], change: 'any'}, async () => {
|
|
let notification: string;
|
|
|
|
let v_popupNotifyHeadingColor = getState(popupNotifyHeadingColor).val != null ? getState(popupNotifyHeadingColor).val : '65504'; // Headline color - yellow 65504
|
|
let v_popupNotifyButton1TextColor = getState(popupNotifyButton1TextColor).val != null ? getState(popupNotifyButton1TextColor).val : '63488'; // Button 1 color - red 63488
|
|
let v_popupNotifyButton2TextColor = getState(popupNotifyButton2TextColor).val != null ? getState(popupNotifyButton2TextColor).val : '2016'; // Button 2 color - green 2016
|
|
let v_popupNotifyTextColor = getState(popupNotifyTextColor).val != null ? getState(popupNotifyTextColor).val : '65535'; // Text color - white 65535
|
|
let v_popupNotifyIconColor = getState(popupNotifyIconColor).val != null ? getState(popupNotifyIconColor).val : '65535'; // Icon color - white 65535
|
|
let v_popupNotifyFontIdText = getState(popupNotifyFontIdText).val != null ? getState(popupNotifyFontIdText).val : '1';
|
|
let v_popupNotifyIcon = getState(popupNotifyIcon).val != null ? getState(popupNotifyIcon).val : 'alert';
|
|
let v_popupNotifyBuzzer = getState(popupNotifyBuzzer).val != null ? getState(popupNotifyBuzzer).val : '0';
|
|
|
|
let heading: string = '';
|
|
let text: string = '';
|
|
if (existsState(popupNotifyHeading)) {
|
|
heading = getState(popupNotifyHeading).val;
|
|
}
|
|
if (existsState(popupNotifyText)) {
|
|
text = getState(popupNotifyText).val;
|
|
}
|
|
|
|
notification =
|
|
'entityUpdateDetail' +
|
|
'~' +
|
|
getState(popupNotifyInternalName).val +
|
|
'~' +
|
|
heading +
|
|
'~' +
|
|
v_popupNotifyHeadingColor +
|
|
'~' +
|
|
getState(popupNotifyButton1Text).val +
|
|
'~' +
|
|
v_popupNotifyButton1TextColor +
|
|
'~' +
|
|
getState(popupNotifyButton2Text).val +
|
|
'~' +
|
|
v_popupNotifyButton2TextColor +
|
|
'~' +
|
|
text +
|
|
'~' +
|
|
v_popupNotifyTextColor +
|
|
'~' +
|
|
getState(popupNotifySleepTimeout).val;
|
|
|
|
if (getState(popupNotifyLayout).val == 2) {
|
|
notification = notification + '~' + v_popupNotifyFontIdText + '~' + Icons.GetIcon(v_popupNotifyIcon) + '~' + v_popupNotifyIconColor;
|
|
}
|
|
|
|
if (heading != '' && text != '') {
|
|
setIfExists(config.panelSendTopic, 'pageType~popupNotify');
|
|
setIfExists(config.panelSendTopic, notification);
|
|
// Set ActivePage
|
|
setIfExists(NSPanel_Path + 'ActivePage.type', 'popupNotify', null, true);
|
|
setIfExists(NSPanel_Path + 'ActivePage.heading', heading, null, true);
|
|
setIfExists(NSPanel_Path + 'ActivePage.id0', '', null, true);
|
|
}
|
|
|
|
//------ Tasmota Buzzer ------
|
|
|
|
if (v_popupNotifyBuzzer != '0') {
|
|
if (Debug) {
|
|
log('Tasmota Buzzer enabled. Value: ' + v_popupNotifyBuzzer, 'info');
|
|
}
|
|
let urlString = `http://${get_current_tasmota_ip_address()}/cm?cmnd=Buzzer ${v_popupNotifyBuzzer}`;
|
|
if (tasmota_web_admin_password != '') {
|
|
urlString = `http://${get_current_tasmota_ip_address()}/cm?user=${tasmota_web_admin_user}&password=${tasmota_web_admin_password}&cmnd=Buzzer ${v_popupNotifyBuzzer}`;
|
|
}
|
|
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
log('Axios Data: ' + JSON.stringify(response.data), 'info');
|
|
} else {
|
|
log('Axios Status - Tasmota Buzzer: ' + response.state, 'warn');
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
if (error.code === 'EHOSTUNREACH') {
|
|
log(`Can't connect to display!`, 'warn');
|
|
} else log(error, 'warn');
|
|
});
|
|
} else {
|
|
if (Debug) {
|
|
log('Tasmota Buzzer disabled', 'info');
|
|
}
|
|
}
|
|
//---- Tasmota Buzzer -----
|
|
});
|
|
} catch (err: any) {
|
|
log('error at function InitPopupNotify: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
InitPopupNotify();
|
|
|
|
let subscriptions: any = {};
|
|
let screensaverEnabled: boolean = false;
|
|
let pageId = 0;
|
|
let activePage: PageType | undefined = undefined;
|
|
|
|
//Send time to NSPanel
|
|
/**
|
|
* Schedules a task to send the current time and handle screensaver updates every 60 seconds.
|
|
*
|
|
* This scheduled task calls the `SendTime` and `HandleScreensaverUpdate` functions every minute.
|
|
* If an error occurs during the execution of these functions, it is logged.
|
|
*
|
|
* @constant
|
|
* @type {Object}
|
|
*/
|
|
let scheduleSendTime = adapterSchedule(new Date().setSeconds(0, 0), 60, () => {
|
|
try {
|
|
SendTime();
|
|
HandleScreensaverUpdate();
|
|
} catch (err: any) {
|
|
log('error at schedule SendTime: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
//Switch between Screensaver Entities and WeatherForecast
|
|
let screensaverChangeTime = 60;
|
|
if (existsState(NSPanel_Path + 'ScreensaverInfo.entityChangeTime')) {
|
|
screensaverChangeTime = parseInt(getState(NSPanel_Path + 'ScreensaverInfo.entityChangeTime').val);
|
|
}
|
|
|
|
|
|
/**
|
|
* Schedules a task to switch the screensaver state based on certain conditions.
|
|
*
|
|
* This scheduled task checks various states related to the screensaver and weather forecast.
|
|
* It toggles the weather forecast state with a delay based on the entity change time.
|
|
* If an error occurs during the execution of this task, it is logged.
|
|
*
|
|
* @constant
|
|
* @type {Object}
|
|
*/
|
|
let scheduleSwichScreensaver = adapterSchedule(undefined, screensaverChangeTime, () => {
|
|
try {
|
|
// WeatherForecast true/false Switchover delayed
|
|
let heading: string = '';
|
|
let text: string = '';
|
|
let wForecast: boolean = true;
|
|
let wForecastTimer: boolean = true;
|
|
let changeTime: number = 60;
|
|
if (existsState(NSPanel_Path + 'ScreensaverInfo.popupNotifyHeading')) {
|
|
heading = getState(NSPanel_Path + 'ScreensaverInfo.popupNotifyHeading').val;
|
|
}
|
|
if (existsState(NSPanel_Path + 'ScreensaverInfo.popupNotifyText')) {
|
|
text = getState(NSPanel_Path + 'ScreensaverInfo.popupNotifyText').val;
|
|
}
|
|
if (existsState(NSPanel_Path + 'ScreensaverInfo.weatherForecast')) {
|
|
wForecast = getState(NSPanel_Path + 'ScreensaverInfo.weatherForecast').val;
|
|
}
|
|
if (existsState(NSPanel_Path + 'ScreensaverInfo.weatherForecastTimer')) {
|
|
wForecastTimer = getState(NSPanel_Path + 'ScreensaverInfo.weatherForecastTimer').val;
|
|
}
|
|
if (existsState(NSPanel_Path + 'ScreensaverInfo.entityChangeTime')) {
|
|
changeTime = getState(NSPanel_Path + 'ScreensaverInfo.entityChangeTime').val;
|
|
}
|
|
if (
|
|
heading == '' &&
|
|
text == '' &&
|
|
wForecast == true &&
|
|
wForecastTimer == true
|
|
) {
|
|
setStateDelayed(NSPanel_Path + 'ScreensaverInfo.weatherForecast', {val: false, ack: true}, (changeTime / 2) * 1000, false);
|
|
} else if (
|
|
heading == '' &&
|
|
text == '' &&
|
|
wForecast == false &&
|
|
wForecastTimer == true
|
|
) {
|
|
setStateDelayed(NSPanel_Path + 'ScreensaverInfo.weatherForecast', {val: true, ack: true}, (changeTime / 2) * 1000, false);
|
|
}
|
|
} catch (err: any) {
|
|
log('error at schedule entityChangeTime: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
function InitHWButton1Color () {
|
|
try {
|
|
if ([null, undefined, ''].indexOf(config.mrIcon1ScreensaverEntity.ScreensaverEntity) == -1) {
|
|
on({id: config.mrIcon1ScreensaverEntity.ScreensaverEntity, change: 'ne'}, async function () {
|
|
HandleScreensaverUpdate();
|
|
});
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function InitHWButton1Color: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
InitHWButton1Color();
|
|
|
|
/**
|
|
* Initializes the hardware button 2 color by setting up an event listener
|
|
* that triggers when the specified screensaver entity changes.
|
|
*
|
|
* This function checks if the `ScreensaverEntity` property of
|
|
* `config.mrIcon2ScreensaverEntity` is not null or undefined. If it is valid,
|
|
* it sets up an event listener using the `on` function to listen for changes
|
|
* (with change type 'ne') on the specified entity. When a change is detected,
|
|
* the `HandleScreensaverUpdate` function is called.
|
|
*
|
|
* If an error occurs during the execution of this function, it is caught and
|
|
* logged with a warning message.
|
|
*
|
|
* @throws Will log a warning message if an error occurs during execution.
|
|
*/
|
|
function InitHWButton2Color () {
|
|
try {
|
|
if ([null, undefined, ''].indexOf(config.mrIcon2ScreensaverEntity.ScreensaverEntity) == -1) {
|
|
on({id: config.mrIcon2ScreensaverEntity.ScreensaverEntity, change: 'ne'}, async function () {
|
|
HandleScreensaverUpdate();
|
|
});
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function InitHWButton2Color: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
InitHWButton2Color();
|
|
|
|
//Switch between data points and weather forecast in the screensaver
|
|
/**
|
|
* Sets up a subscription to monitor changes in the weather forecast state.
|
|
*
|
|
* This subscription listens for changes in the `ScreensaverInfo.weatherForecast` state.
|
|
* When the state changes, the `HandleScreensaverUpdate` function is called to update the screensaver.
|
|
*
|
|
* @event
|
|
* @param {Object} obj - The object containing the state change information.
|
|
* @throws {Error} If an error occurs during the state change handling.
|
|
*/
|
|
on({id: [NSPanel_Path + 'ScreensaverInfo.weatherForecast'], change: 'ne'}, async function (obj) {
|
|
try {
|
|
weatherForecast = obj.state.val;
|
|
HandleScreensaverUpdate();
|
|
} catch (err: any) {
|
|
log('error at trigger weatherForecast: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
//Update if Changing Values on Wheather Alias
|
|
/**
|
|
* Sets up a subscription to monitor changes in the weather entity's temperature and icon states.
|
|
*
|
|
* This subscription listens for changes in the `TEMP` and `ICON` states of the configured weather entity.
|
|
* When either state changes, the `HandleScreensaverUpdate` function is called to update the screensaver.
|
|
*
|
|
* @event
|
|
* @param {Object} obj - The object containing the state change information.
|
|
* @throws {Error} If an error occurs during the state change handling.
|
|
*/
|
|
on({id: [config.weatherEntity + '.TEMP', config.weatherEntity + '.ICON'], change: 'ne'}, async function (obj) {
|
|
try {
|
|
HandleScreensaverUpdate();
|
|
} catch (err: any) {
|
|
log('error at trigger weatherForecast .TEMP + .ICON: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
//send new Screensavertimeout if Changing of 'timeoutScreensaver'
|
|
/**
|
|
* Sets up a subscription to monitor changes in the screensaver timeout configuration.
|
|
*
|
|
* This subscription listens for changes in the `Config.Screensaver.timeoutScreensaver` state.
|
|
* When the state changes, the new timeout value is sent to the panel.
|
|
*
|
|
* @event
|
|
* @param {Object} obj - The object containing the state change information.
|
|
* @throws {Error} If an error occurs during the state change handling.
|
|
*/
|
|
on({id: [NSPanel_Path + 'Config.Screensaver.timeoutScreensaver'], change: 'ne'}, async function (obj) {
|
|
try {
|
|
let timeout = obj.state.val;
|
|
SendToPanel({payload: 'timeout~' + timeout});
|
|
} catch (err: any) {
|
|
log('error at trigger timeoutScreensaver: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
let scheduleSendDate = adapterSchedule(new Date().setMinutes(0, 0), 60 * 60, () => {
|
|
SendDate();
|
|
});
|
|
|
|
// Check for updates with Start
|
|
get_locales();
|
|
get_locales_servicemenu();
|
|
|
|
// setIfExists(config.panelSendTopic, 'pageType~pageStartup');
|
|
// get_tasmota_status0();
|
|
// get_panel_update_data();
|
|
// check_updates();
|
|
|
|
renameChannel();
|
|
// Updates currently compare and every 12 hours
|
|
let scheduleCheckUpdates = adapterSchedule(undefined, 60 * 60 * 12, () => {
|
|
get_tasmota_status0();
|
|
get_panel_update_data();
|
|
check_updates();
|
|
});
|
|
|
|
// force manual restart after object initialization
|
|
/*
|
|
if (firstRun) {
|
|
stopScript(scriptName);
|
|
}
|
|
*/
|
|
|
|
setTimeout(async function () {
|
|
if (firstRun) {
|
|
stopScript(scriptName);
|
|
}
|
|
}, 20000);
|
|
|
|
//------------------Begin Update Functions
|
|
|
|
/**
|
|
* Retrieves the locale setting for Moment.js.
|
|
*
|
|
* This function checks the `Config.locale` state and returns the appropriate locale string for Moment.js.
|
|
* If the locale is 'hy-AM', 'zh-CN', or 'zh-TW', it returns the locale in lowercase.
|
|
* Otherwise, it returns the first two characters of the locale string.
|
|
* If an error occurs, it logs the error and returns 'en' as the default locale.
|
|
*
|
|
* @function getMomentjsLocale
|
|
* @returns {String} The locale string for Moment.js.
|
|
* @throws {Error} If an error occurs during the locale retrieval.
|
|
*/
|
|
function getMomentjsLocale (): String {
|
|
try {
|
|
let locale = 'en-US';
|
|
if (existsState(NSPanel_Path + 'Config.locale')) {
|
|
locale = getState(NSPanel_Path + 'Config.locale').val;
|
|
}
|
|
if (locale == 'hy-AM' || locale == 'zh-CN' || locale == 'zh-TW') {
|
|
return locale.toLowerCase();
|
|
} else {
|
|
return locale.substring(0, 2);
|
|
}
|
|
} catch (err: any) {
|
|
log('error in function getMomentjsLocale: ' + err.message, 'warn');
|
|
return 'en';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetches the locales JSON from a remote URL and stores it in a state.
|
|
*
|
|
* This function sends a GET request to a specified URL to retrieve the locales JSON.
|
|
* If the request is successful, the JSON data is stored in the `NSPanel_locales_json` state.
|
|
* If an error occurs during the request, it is logged.
|
|
*
|
|
* @async
|
|
* @function get_locales
|
|
* @returns {Promise<void>} A promise that resolves when the locales have been fetched and stored.
|
|
* @throws {Error} If an error occurs during the request or state update.
|
|
*/
|
|
async function get_locales () {
|
|
try {
|
|
if (Debug) {
|
|
log('Requesting locales', 'info');
|
|
}
|
|
let urlString: string = 'https://raw.githubusercontent.com/joBr99/nspanel-lovelace-ui/main/ioBroker/ioBroker_NSPanel_locales.json';
|
|
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(JSON.stringify(response.data), 'info');
|
|
}
|
|
await createStateAsync(NSPanel_Path + 'NSPanel_locales_json', {type: 'string', role: 'json', write: false});
|
|
await setStateAsync(NSPanel_Path + 'NSPanel_locales_json', {val: JSON.stringify(response.data), ack: true});
|
|
} else {
|
|
log('Axios Status - Requesting locales: ' + response.state, 'warn');
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
log(error, 'warn');
|
|
});
|
|
} catch (err: any) {
|
|
log('error requesting locales in function get_locales: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetches the locales JSON for the service menu from a remote URL and stores it in a state.
|
|
*
|
|
* This function sends a GET request to a specified URL to retrieve the locales JSON for the service menu.
|
|
* If the request is successful, the JSON data is stored in the `NSPanel_locales_service_json` state.
|
|
* If an error occurs during the request, it is logged.
|
|
*
|
|
* @async
|
|
* @function get_locales_servicemenu
|
|
* @returns {Promise<void>} A promise that resolves when the locales have been fetched and stored.
|
|
* @throws {Error} If an error occurs during the request or state update.
|
|
*/
|
|
async function get_locales_servicemenu () {
|
|
try {
|
|
if (Debug) {
|
|
log('Requesting locales Service Menu', 'info');
|
|
}
|
|
let urlString: string = 'https://raw.githubusercontent.com/joBr99/nspanel-lovelace-ui/main/ioBroker/ioBroker_NSPanel_locales_service.json';
|
|
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(JSON.stringify(response.data), 'info');
|
|
}
|
|
await createStateAsync(NSPanel_Path + 'NSPanel_locales_service_json', {type: 'string', role: 'json', write: false});
|
|
await setStateAsync(NSPanel_Path + 'NSPanel_locales_service_json', {val: JSON.stringify(response.data), ack: true});
|
|
} else {
|
|
log('Axios Status - Requesting locales Service Menu: ' + response.state, 'warn');
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
log(error, 'warn');
|
|
});
|
|
} catch (err: any) {
|
|
log('error requesting locales in function get_locales_servicemenu: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks for updates to the NSPanel firmware or configuration.
|
|
*
|
|
* This function performs a check for updates and handles the update process if any updates are found.
|
|
*
|
|
* @async
|
|
* @function check_updates
|
|
* @returns {Promise<void>} A promise that resolves when the update check is complete.
|
|
* @throws {Error} If an error occurs during the update check.
|
|
*/
|
|
async function check_updates () {
|
|
try {
|
|
if (Debug) log('Check-Updates', 'info');
|
|
|
|
let Update: boolean = false;
|
|
|
|
let InternalName: string = '';
|
|
let Headline: string = '';
|
|
let Text: string = '';
|
|
|
|
const HeadlineColor: string = '63488'; // Farbe Rot
|
|
const Button1: string = 'Nein';
|
|
const Button1Color: string = '63488'; // Farbe Rot
|
|
const Button2: string = 'Ja';
|
|
const Button2Color: string = '2016'; // Farbe Grün
|
|
const Timeout: number = 0;
|
|
const Layout: number = 1;
|
|
|
|
// Tasmota-Firmware-Vergleich
|
|
if (existsObject(NSPanel_Path + 'Tasmota_Firmware.currentVersion') && existsObject(NSPanel_Path + 'Tasmota_Firmware.onlineVersion')) {
|
|
let splitTasmotaVersion = getState(NSPanel_Path + 'Tasmota_Firmware.currentVersion').val.split('.');
|
|
let shortTasmoataVersion = splitTasmotaVersion[0] + '.' + splitTasmotaVersion[1] + '.' + splitTasmotaVersion[2];
|
|
if (shortTasmoataVersion !== getState(NSPanel_Path + 'Tasmota_Firmware.onlineVersion').val) {
|
|
if (existsState(NSPanel_Path + 'NSPanel_autoUpdate')) {
|
|
if (getState(NSPanel_Path + 'NSPanel_autoUpdate').val) {
|
|
log('Auto-Updates eingeschaltet - Update Tasmota wird durchgeführt', 'info');
|
|
|
|
// Perform Tasmota upgrade
|
|
update_tasmota_firmware();
|
|
// Current Tasmota version = online Tasmota version
|
|
|
|
await setStateAsync(NSPanel_Path + 'Tasmota_Firmware.currentVersion', {val: getState(NSPanel_Path + 'Tasmota_Firmware.onlineVersion').val, ack: true});
|
|
} else {
|
|
// Point out Tasmota updates
|
|
if (Debug) log('Tasmota-Firmware => Automatische Updates aus, manuelles Update nötig', 'info');
|
|
|
|
InternalName = 'TasmotaFirmwareUpdate';
|
|
Headline = 'Tasmota-Firmware Update';
|
|
Text = [
|
|
'Es ist eine neue Version der Tasmota-Firmware',
|
|
'\r\n',
|
|
'verfügbar',
|
|
'\r\n',
|
|
'\r\n',
|
|
'Installierte Version: ' + String(getState(String(NSPanel_Path) + 'Tasmota_Firmware.currentVersion').val),
|
|
'\r\n',
|
|
'Verfügbare Version: ' + String(getState(String(NSPanel_Path) + 'Tasmota_Firmware.onlineVersion').val),
|
|
'\r\n',
|
|
'\r\n',
|
|
'Upgrade durchführen?',
|
|
].join('');
|
|
Update = true;
|
|
}
|
|
}
|
|
} else {
|
|
if (Debug) log('Already the latest Tasmota version on NSPanel', 'info');
|
|
}
|
|
}
|
|
|
|
// Tasmota-Berry-Driver-Vergleich
|
|
if (existsObject(NSPanel_Path + 'Berry_Driver.currentVersion')) {
|
|
if (parseFloat(getState(NSPanel_Path + 'Berry_Driver.currentVersion').val) < berry_driver_version) {
|
|
if (existsState(NSPanel_Path + 'NSPanel_autoUpdate')) {
|
|
if (getState(NSPanel_Path + 'NSPanel_autoUpdate').val) {
|
|
log('Auto-updates switched on - Berry driver update is carried out', 'info');
|
|
|
|
// Tasmota Berry-Driver Update durchführen
|
|
update_berry_driver_version();
|
|
// Aktuelle Berry-Driver Version = Online Berry-Driver Version
|
|
await setStateAsync(NSPanel_Path + 'Berry_Driver.currentVersion', {val: getState(NSPanel_Path + 'Berry_Driver.onlineVersion').val, ack: true});
|
|
|
|
if (Debug) log('Berry driver updated automatically', 'info');
|
|
} else {
|
|
//Auf BerryDriver-Update hinweisen
|
|
if (Debug) log('Berry Driver => Automatic updates off, manual update required', 'info');
|
|
|
|
InternalName = 'BerryDriverUpdate';
|
|
Headline = 'Berry-Driver Update';
|
|
Text = [
|
|
'Es ist eine neue Version des Berry-Drivers',
|
|
'\r\n',
|
|
'(Tasmota) verfügbar',
|
|
'\r\n',
|
|
'\r\n',
|
|
'Installierte Version: ' + String(getState(String(NSPanel_Path) + 'Berry_Driver.currentVersion').val),
|
|
'\r\n',
|
|
'Verfügbare Version: ' + String(berry_driver_version),
|
|
'\r\n',
|
|
'\r\n',
|
|
'Upgrade durchführen?',
|
|
].join('');
|
|
Update = true;
|
|
}
|
|
}
|
|
} else {
|
|
if (Debug) log('Already the latest Berry driver on NSPanel', 'info');
|
|
}
|
|
}
|
|
|
|
// TFT-Firmware-Vergleich
|
|
if (existsObject(NSPanel_Path + 'Display_Firmware.currentVersion')) {
|
|
if (parseInt(getState(NSPanel_Path + 'Display_Firmware.currentVersion').val) == 0) {
|
|
log('Actual TFT-firmware version just not not initialized', 'info');
|
|
} else {
|
|
if (parseInt(getState(NSPanel_Path + 'Display_Firmware.currentVersion').val) < desired_display_firmware_version) {
|
|
if (existsState(NSPanel_Path + 'NSPanel_autoUpdate')) {
|
|
if (getState(NSPanel_Path + 'NSPanel_autoUpdate').val) {
|
|
log('Auto-updates switched on - update TFT firmware is carried out', 'info');
|
|
|
|
// TFT-Firmware Update durchführen
|
|
update_tft_firmware();
|
|
|
|
// Aktuelle TFT-Firmware Version = Online TFT-Firmware Version
|
|
await setStateAsync(NSPanel_Path + 'Display_Firmware.currentVersion', {val: getState(NSPanel_Path + 'Display_Firmware.onlineVersion').val, ack: true});
|
|
|
|
if (Debug) log('Display firmware updated automatically', 'info');
|
|
} else {
|
|
// Auf TFT-Firmware hinweisen
|
|
if (Debug) log('Display firmware => Automatic updates off, manual update required', 'info');
|
|
|
|
InternalName = 'TFTFirmwareUpdate';
|
|
Headline = 'TFT-Firmware Update';
|
|
Text = [
|
|
'Es ist eine neue Version der TFT-Firmware',
|
|
'\r\n',
|
|
'verfügbar',
|
|
'\r\n',
|
|
'\r\n',
|
|
'Installierte Version: ' + String(getState(String(NSPanel_Path) + 'Display_Firmware.currentVersion').val),
|
|
'\r\n',
|
|
'Verfügbare Version: ' + String(desired_display_firmware_version),
|
|
'\r\n',
|
|
'\r\n',
|
|
'Upgrade durchführen?',
|
|
].join('');
|
|
Update = true;
|
|
}
|
|
}
|
|
} else {
|
|
if (Debug) log('Already the latest display firmware on NSPanel', 'info');
|
|
}
|
|
}
|
|
}
|
|
let update_message: boolean = true;
|
|
if (existsState(NSPanel_Path + 'Config.Update.UpdateMessage')) {
|
|
update_message = getState(NSPanel_Path + 'Config.Update.UpdateMessage').val;
|
|
}
|
|
if (Update && update_message) {
|
|
await setStateAsync(popupNotifyHeading, {val: Headline, ack: false});
|
|
await setStateAsync(popupNotifyHeadingColor, {val: HeadlineColor, ack: false});
|
|
await setStateAsync(popupNotifyButton1Text, {val: Button1, ack: false});
|
|
await setStateAsync(popupNotifyButton1TextColor, {val: Button1Color, ack: false});
|
|
await setStateAsync(popupNotifyButton2Text, {val: Button2, ack: false});
|
|
await setStateAsync(popupNotifyButton2TextColor, {val: Button2Color, ack: false});
|
|
await setStateAsync(popupNotifySleepTimeout, {val: Timeout, ack: false});
|
|
await setStateAsync(popupNotifyInternalName, {val: InternalName, ack: false});
|
|
await setStateAsync(popupNotifyLayout, {val: Layout, ack: false});
|
|
await setStateAsync(popupNotifyText, {val: [formatDate(getDateObject(new Date().getTime()), 'TT.MM.JJJJ SS:mm:ss'), '\r\n', '\r\n', Text].join(''), ack: false});
|
|
} else if (Update && !update_message) {
|
|
log('Updates for NSPanel available', 'info');
|
|
} else {
|
|
log('No Updates for NSPanel available', 'info');
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function check_updates: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets up a subscription to monitor changes in the popup notification action state.
|
|
*
|
|
* This subscription listens for changes in the `popupNotify.popupNotifyAction` state.
|
|
* When the state changes, the specified callback function is executed.
|
|
*
|
|
* @event
|
|
* @param {Object} obj - The object containing the state change information.
|
|
* @throws {Error} If an error occurs during the state change handling.
|
|
*/
|
|
on({id: NSPanel_Path + 'popupNotify.popupNotifyAction', change: 'any'}, async function (obj) {
|
|
try {
|
|
const val = obj.state ? obj.state.val : false;
|
|
if (!val) {
|
|
if (Debug) {
|
|
log('Button1 was pressed', 'info');
|
|
}
|
|
} else if (val) {
|
|
const internalName: string = getState(NSPanel_Path + 'popupNotify.popupNotifyInternalName').val;
|
|
if (internalName.includes('Update')) {
|
|
if (internalName == 'TasmotaFirmwareUpdate') {
|
|
update_tasmota_firmware();
|
|
} else if (internalName == 'BerryDriverUpdate') {
|
|
update_berry_driver_version();
|
|
} else if (internalName == 'TFTFirmwareUpdate') {
|
|
update_tft_firmware();
|
|
}
|
|
}
|
|
if (Debug) {
|
|
log('Button2 was pressed', 'info');
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at Trigger popupNotifyAction: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Retrieves the update data for the NSPanel.
|
|
*
|
|
* This function fetches the latest update data for the NSPanel, including firmware and configuration updates.
|
|
*
|
|
* @async
|
|
* @function get_panel_update_data
|
|
* @returns {Promise<void>} A promise that resolves when the update data has been retrieved.
|
|
* @throws {Error} If an error occurs during the data retrieval.
|
|
*/
|
|
async function get_panel_update_data () {
|
|
try {
|
|
if (isSetOptionActive) {
|
|
await createStateAsync(NSPanel_Path + 'Config.Update.UpdateMessage', true, {read: true, write: true, name: 'Update-Message', type: 'boolean', def: true});
|
|
if (autoCreateAlias) {
|
|
setObject(AliasPath + 'Config.Update.UpdateMessage', {type: 'channel', common: {role: 'socket', name: 'UpdateMesssage'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Config.Update.UpdateMessage.ACTUAL', NSPanel_Path + 'Config.Update.UpdateMessage', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Config.Update.UpdateMessage.SET', NSPanel_Path + 'Config.Update.UpdateMessage', true, {
|
|
type: 'boolean',
|
|
role: 'switch',
|
|
name: 'SET',
|
|
});
|
|
}
|
|
await createStateAsync(NSPanel_Path + 'NSPanel_autoUpdate', false, {read: true, write: true, name: 'Auto-Update', type: 'boolean', def: false});
|
|
if (autoCreateAlias) {
|
|
setObject(AliasPath + 'autoUpdate', {type: 'channel', common: {role: 'socket', name: 'AutoUpdate'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'autoUpdate.ACTUAL', NSPanel_Path + 'NSPanel_autoUpdate', true, {type: 'boolean', role: 'switch', name: 'ACTUAL'});
|
|
await createAliasAsync(AliasPath + 'autoUpdate.SET', NSPanel_Path + 'NSPanel_autoUpdate', true, {type: 'boolean', role: 'switch', name: 'SET'});
|
|
}
|
|
await createStateAsync(NSPanel_Path + 'NSPanel_ipAddress', {type: 'string', write: false});
|
|
await setStateAsync(NSPanel_Path + 'NSPanel_ipAddress', {val: get_current_tasmota_ip_address(), ack: true});
|
|
if (autoCreateAlias) {
|
|
setObject(AliasPath + 'ipAddress', {type: 'channel', common: {role: 'info', name: 'ipAddress'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'ipAddress.ACTUAL', NSPanel_Path + 'NSPanel_ipAddress', true, {type: 'string', role: 'state', name: 'ACTUAL'});
|
|
}
|
|
get_online_tasmota_firmware_version();
|
|
get_current_berry_driver_version();
|
|
get_online_berry_driver_version();
|
|
check_version_tft_firmware();
|
|
check_online_display_firmware();
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function get_panel_update_data: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves the current IP address of the Tasmota device.
|
|
*
|
|
* This function returns the IP address of the Tasmota device currently in use.
|
|
*
|
|
* @function get_current_tasmota_ip_address
|
|
* @returns {string} The IP address of the Tasmota device.
|
|
*/
|
|
function get_current_tasmota_ip_address () {
|
|
try {
|
|
const infoObjId = config.panelRecvTopic.substring(0, config.panelRecvTopic.length - 'RESULT'.length) + 'INFO2';
|
|
const infoObj = JSON.parse(getState(infoObjId).val);
|
|
|
|
if (Debug) {
|
|
log(`get_current_tasmota_ip_address: ${infoObj.Info2.IPAddress}`, 'info');
|
|
}
|
|
|
|
return infoObj.Info2.IPAddress;
|
|
} catch (err: any) {
|
|
log('error at function get_current_tasmota_ip_address: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves the current IP address of the Tasmota device.
|
|
*
|
|
* This function returns the IP address of the Tasmota device currently in use.
|
|
*
|
|
* @function get_current_tasmota_ip_address
|
|
* @returns {string} The IP address of the Tasmota device.
|
|
* @throws {Error} If an error occurs during the IP address retrieval.
|
|
*/
|
|
function get_online_tasmota_firmware_version () {
|
|
try {
|
|
if (Debug) {
|
|
log('Requesting tasmota firmware version', 'info');
|
|
}
|
|
|
|
let urlString: string = 'https://api.github.com/repositories/80286288/releases/latest';
|
|
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(JSON.stringify(response.data), 'info');
|
|
}
|
|
if (isSetOptionActive) {
|
|
const Tasmota_JSON = JSON.parse(JSON.stringify(response.data)); // Write JSON result to variable
|
|
const TasmotaTagName = Tasmota_JSON.tag_name; // Filter JSON by "tag_name" and write to variable
|
|
const TasmotaVersionOnline = TasmotaTagName.replace(/v/i, ''); // Filter unnecessary "v" from variable and write to release variable
|
|
await createStateAsync(NSPanel_Path + 'Tasmota_Firmware.onlineVersion', {type: 'string', write: false});
|
|
setObject(AliasPath + 'Tasmota_Firmware.onlineVersion', {type: 'channel', common: {role: 'info', name: 'onlineVersion'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Tasmota_Firmware.onlineVersion.ACTUAL', NSPanel_Path + 'Tasmota_Firmware.onlineVersion', true, {
|
|
type: 'string',
|
|
role: 'state',
|
|
name: 'ACTUAL',
|
|
});
|
|
await setStateAsync(NSPanel_Path + 'Tasmota_Firmware.onlineVersion', {val: TasmotaVersionOnline, ack: true});
|
|
if (Debug) log('online tasmota firmware version => ' + TasmotaVersionOnline, 'info');
|
|
}
|
|
} else {
|
|
log('Axios Status - online tasmota firmware version: ' + response.state, 'warn');
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
log(error, 'warn');
|
|
});
|
|
} catch (err: any) {
|
|
log('error requesting firmware in function get_online_tasmota_firmware_version: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves the current Berry driver version.
|
|
*
|
|
* This function retrieves the current Berry driver version from the Tasmota device.
|
|
*
|
|
* @function get_current_berry_driver_version
|
|
* @returns {Promise<void>} A promise that resolves when the current Berry driver version has been retrieved.
|
|
* @throws {Error} If an error occurs during the version retrieval.
|
|
*/
|
|
function get_current_berry_driver_version () {
|
|
try {
|
|
if (Debug) {
|
|
log('Requesting current berry driver version', 'info');
|
|
}
|
|
|
|
let urlString = get_tasmot_url('GetDriverVersion')
|
|
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(JSON.stringify(response.data), 'info');
|
|
}
|
|
if (isSetOptionActive) {
|
|
const BerryDriverVersionCurrent: string = JSON.parse(JSON.stringify(response.data)).nlui_driver_version;
|
|
await createStateAsync(NSPanel_Path + 'Berry_Driver.currentVersion', {type: 'string', write: false});
|
|
await setStateAsync(NSPanel_Path + 'Berry_Driver.currentVersion', {val: JSON.parse(JSON.stringify(response.data)).nlui_driver_version, ack: true});
|
|
if (autoCreateAlias) {
|
|
setObject(AliasPath + 'Display.BerryDriver', {type: 'channel', common: {role: 'info', name: 'Berry Driver'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Display.BerryDriver.ACTUAL', NSPanel_Path + 'Berry_Driver.currentVersion', true, {
|
|
type: 'string',
|
|
role: 'state',
|
|
name: 'ACTUAL',
|
|
});
|
|
}
|
|
if (Debug) log('current berry driver version => ' + BerryDriverVersionCurrent, 'info');
|
|
}
|
|
} else {
|
|
log('Axios Status - current berry driver version: ' + response.state, 'info');
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
if (error.code === 'EHOSTUNREACH') {
|
|
log(`Can't connect to display!`, 'warn');
|
|
} else log(error, 'warn');
|
|
});
|
|
} catch (err: any) {
|
|
log('error requesting firmware in function get_current_berry_driver_version: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves the online Berry driver version.
|
|
*
|
|
* This function retrieves the latest online Berry driver version from the Tasmota device.
|
|
*
|
|
* @function get_online_berry_driver_version
|
|
* @returns {Promise<void>} A promise that resolves when the online Berry driver version has been retrieved.
|
|
* @throws {Error} If an error occurs during the version retrieval.
|
|
*/
|
|
function get_tasmota_status0 () {
|
|
try {
|
|
if (Debug) {
|
|
log('Requesting tasmota status0', 'info');
|
|
}
|
|
|
|
let urlString = `http://${get_current_tasmota_ip_address()}/cm?cmnd=Status0`;
|
|
if (tasmota_web_admin_password != '') {
|
|
urlString = `http://${get_current_tasmota_ip_address()}/cm?user=${tasmota_web_admin_user}&password=${tasmota_web_admin_password}&cmnd=Status0`;
|
|
}
|
|
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(JSON.stringify(response.data), 'info');
|
|
}
|
|
if (isSetOptionActive) {
|
|
await createStateAsync(NSPanel_Path + 'Tasmota_Firmware.currentVersion', {type: 'string', write: false});
|
|
await createStateAsync(NSPanel_Path + 'Tasmota.Uptime', {type: 'string', write: false});
|
|
await createStateAsync(NSPanel_Path + 'Tasmota.Version', {type: 'string', write: false});
|
|
await createStateAsync(NSPanel_Path + 'Tasmota.Hardware', {type: 'string', write: false});
|
|
await createStateAsync(NSPanel_Path + 'Tasmota.Wifi.AP', {type: 'number', write: false});
|
|
await createStateAsync(NSPanel_Path + 'Tasmota.Wifi.SSId', {type: 'string', write: false});
|
|
await createStateAsync(NSPanel_Path + 'Tasmota.Wifi.BSSId', {type: 'string', write: false});
|
|
await createStateAsync(NSPanel_Path + 'Tasmota.Wifi.Channel', {type: 'number', write: false});
|
|
await createStateAsync(NSPanel_Path + 'Tasmota.Wifi.Mode', {type: 'string', write: false});
|
|
await createStateAsync(NSPanel_Path + 'Tasmota.Wifi.RSSI', {type: 'number', write: false});
|
|
await createStateAsync(NSPanel_Path + 'Tasmota.Wifi.Signal', {type: 'number', write: false});
|
|
await createStateAsync(NSPanel_Path + 'Tasmota.Product', {type: 'string', write: false});
|
|
|
|
try {
|
|
const Tasmota_JSON = JSON.parse(JSON.stringify(response.data));
|
|
const tasmoVersion = Tasmota_JSON.StatusFWR.Version.indexOf('(') > -1 ? Tasmota_JSON.StatusFWR.Version.split('(')[0] : Tasmota_JSON.StatusFWR.Version;
|
|
|
|
await setStateAsync(NSPanel_Path + 'Tasmota_Firmware.currentVersion', {val: tasmoVersion, ack: true});
|
|
await setStateAsync(NSPanel_Path + 'Tasmota.Uptime', {val: Tasmota_JSON.StatusPRM.Uptime, ack: true});
|
|
await setStateAsync(NSPanel_Path + 'Tasmota.Version', {val: Tasmota_JSON.StatusFWR.Version, ack: true});
|
|
let TasmotaHardware: string = Tasmota_JSON.StatusFWR.Hardware.split(' ');
|
|
await setStateAsync(NSPanel_Path + 'Tasmota.Hardware', {val: TasmotaHardware[0] + '\r\n' + TasmotaHardware[1], ack: true});
|
|
await setStateAsync(NSPanel_Path + 'Tasmota.Wifi.AP', {val: Tasmota_JSON.StatusSTS.Wifi.AP, ack: true});
|
|
await setStateAsync(NSPanel_Path + 'Tasmota.Wifi.SSId', {val: Tasmota_JSON.StatusSTS.Wifi.SSId, ack: true});
|
|
await setStateAsync(NSPanel_Path + 'Tasmota.Wifi.BSSId', {val: Tasmota_JSON.StatusSTS.Wifi.BSSId, ack: true});
|
|
await setStateAsync(NSPanel_Path + 'Tasmota.Wifi.Channel', {val: Tasmota_JSON.StatusSTS.Wifi.Channel, ack: true});
|
|
await setStateAsync(NSPanel_Path + 'Tasmota.Wifi.Mode', {val: Tasmota_JSON.StatusSTS.Wifi.Mode, ack: true});
|
|
await setStateAsync(NSPanel_Path + 'Tasmota.Wifi.RSSI', {val: Tasmota_JSON.StatusSTS.Wifi.RSSI, ack: true});
|
|
await setStateAsync(NSPanel_Path + 'Tasmota.Wifi.Signal', {val: Tasmota_JSON.StatusSTS.Wifi.Signal, ack: true});
|
|
await setStateAsync(NSPanel_Path + 'Tasmota.Product', {val: 'SONOFF NSPanel', ack: true});
|
|
if (Debug) log('current tasmota firmware version => ' + tasmoVersion, 'info');
|
|
} catch (err: any) {
|
|
log('error setState in function get_tasmota_status0' + err.message, 'warn');
|
|
}
|
|
if (autoCreateAlias) {
|
|
setObject(AliasPath + 'Tasmota.Uptime', {type: 'channel', common: {role: 'info', name: 'Uptime'}, native: {}});
|
|
setObject(AliasPath + 'Tasmota.Version', {type: 'channel', common: {role: 'info', name: 'Version'}, native: {}});
|
|
setObject(AliasPath + 'Tasmota.Hardware', {type: 'channel', common: {role: 'info', name: 'Hardware'}, native: {}});
|
|
setObject(AliasPath + 'Tasmota.Wifi.AP', {type: 'channel', common: {role: 'info', name: 'AP'}, native: {}});
|
|
setObject(AliasPath + 'Tasmota.Wifi.SSId', {type: 'channel', common: {role: 'info', name: 'SSId'}, native: {}});
|
|
setObject(AliasPath + 'Tasmota.Wifi.BSSId', {type: 'channel', common: {role: 'info', name: 'BSSId'}, native: {}});
|
|
setObject(AliasPath + 'Tasmota.Wifi.Channel', {type: 'channel', common: {role: 'info', name: 'Channel'}, native: {}});
|
|
setObject(AliasPath + 'Tasmota.Wifi.Mode', {type: 'channel', common: {role: 'info', name: 'Mode'}, native: {}});
|
|
setObject(AliasPath + 'Tasmota.Wifi.RSSI', {type: 'channel', common: {role: 'info', name: 'RSSI'}, native: {}});
|
|
setObject(AliasPath + 'Tasmota.Wifi.Signal', {type: 'channel', common: {role: 'info', name: 'Signal'}, native: {}});
|
|
setObject(AliasPath + 'Tasmota.Product', {type: 'channel', common: {role: 'info', name: 'Product'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Tasmota.Uptime.ACTUAL', NSPanel_Path + 'Tasmota.Uptime', true, {type: 'string', role: 'state', name: 'ACTUAL'});
|
|
await createAliasAsync(AliasPath + 'Tasmota.Version.ACTUAL', NSPanel_Path + 'Tasmota.Version', true, {type: 'string', role: 'state', name: 'ACTUAL'});
|
|
await createAliasAsync(AliasPath + 'Tasmota.Hardware.ACTUAL', NSPanel_Path + 'Tasmota.Hardware', true, {
|
|
type: 'string',
|
|
role: 'state',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Tasmota.Wifi.AP.ACTUAL', NSPanel_Path + 'Tasmota.Wifi.AP', true, {type: 'number', role: 'state', name: 'ACTUAL'});
|
|
await createAliasAsync(AliasPath + 'Tasmota.Wifi.SSId.ACTUAL', NSPanel_Path + 'Tasmota.Wifi.SSId', true, {
|
|
type: 'string',
|
|
role: 'state',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Tasmota.Wifi.BSSId.ACTUAL', NSPanel_Path + 'Tasmota.Wifi.BSSId', true, {
|
|
type: 'string',
|
|
role: 'state',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Tasmota.Wifi.Channel.ACTUAL', NSPanel_Path + 'Tasmota.Wifi.Channel', true, {
|
|
type: 'number',
|
|
role: 'state',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Tasmota.Wifi.Mode.ACTUAL', NSPanel_Path + 'Tasmota.Wifi.Mode', true, {
|
|
type: 'string',
|
|
role: 'state',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Tasmota.Wifi.RSSI.ACTUAL', NSPanel_Path + 'Tasmota.Wifi.RSSI', true, {
|
|
type: 'number',
|
|
role: 'state',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Tasmota.Wifi.Signal.ACTUAL', NSPanel_Path + 'Tasmota.Wifi.Signal', true, {
|
|
type: 'number',
|
|
role: 'state',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Tasmota.Product.ACTUAL', NSPanel_Path + 'Tasmota.Product', true, {type: 'string', role: 'state', name: 'ACTUAL'});
|
|
}
|
|
}
|
|
} else {
|
|
log('Axios Status - get_tasmota_status0: ' + response.state, 'warn');
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
if (error.code === 'EHOSTUNREACH') {
|
|
log(`Can't connect to display!`, 'warn');
|
|
} else log(error, 'error');
|
|
});
|
|
} catch (err: any) {
|
|
log('error requesting firmware in function get_tasmota_status0: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
function renameChannel () {
|
|
const urlString = get_tasmot_url('DeviceName');
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(JSON.stringify(response.data), 'info');
|
|
}
|
|
if (isSetOptionActive) {
|
|
try {
|
|
if (response.data && response.data.DeviceName && response.data.DeviceName !== '') {
|
|
await extendObjectAsync(NSPanel_Path.slice(0, -1), {common: {name: response.data.DeviceName}})
|
|
await extendObjectAsync(AliasPath.slice(0, -1), {common: {name: response.data.DeviceName}})
|
|
}
|
|
} catch (e) {
|
|
//nothing
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
if (error.code === 'EHOSTUNREACH') {
|
|
log(`Can't connect to display!`, 'warn');
|
|
} else log(error, 'error');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get url to tasmota api
|
|
* @param cmd tasmota command
|
|
* @returns url
|
|
*/
|
|
function get_tasmot_url (cmd: string): string {
|
|
if (tasmota_web_admin_password != '') {
|
|
return `http://${get_current_tasmota_ip_address()}/cm?user=${tasmota_web_admin_user}&password=${tasmota_web_admin_password}&cmnd=${cmd}`;
|
|
}
|
|
return `http://${get_current_tasmota_ip_address()}/cm?cmnd=${cmd}`;
|
|
}
|
|
|
|
/**
|
|
* Updates the Tasmota firmware.
|
|
*
|
|
* This function updates the Tasmota firmware on the NSPanel.
|
|
*
|
|
* @function update_tasmota_firmware
|
|
* @returns {Promise<void>} A promise that resolves when the firmware update has been completed.
|
|
* @throws {Error} If an error occurs during the firmware update.
|
|
*/
|
|
async function get_online_berry_driver_version () {
|
|
try {
|
|
if (NSPanel_Path + 'Config.Update.activ') {
|
|
if (Debug) {
|
|
log('Requesting online berry driver version', 'info');
|
|
}
|
|
|
|
//Use version.json in Future
|
|
/*
|
|
let urlString = 'https://raw.githubusercontent.com/joBr99/nspanel-lovelace-ui/main/tasmota/autoexec.be';
|
|
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(response.data, 'info');
|
|
}
|
|
if (isSetOptionActive) {
|
|
const BerryDriverVersionOnline = response.data
|
|
.substring(response.data.indexOf('version_of_this_script = ') + 24, response.data.indexOf('version_of_this_script = ') + 27)
|
|
.replace(/\s+/g, '');
|
|
await createStateAsync(NSPanel_Path + 'Berry_Driver.onlineVersion', {type: 'string', write: false});
|
|
setObject(AliasPath + 'Berry_Driver.onlineVersion', {type: 'channel', common: {role: 'info', name: 'onlineVersion'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Berry_Driver.onlineVersion.ACTUAL', NSPanel_Path + 'Berry_Driver.onlineVersion', true, {
|
|
type: 'string',
|
|
role: 'state',
|
|
name: 'ACTUAL',
|
|
});
|
|
await setStateAsync(NSPanel_Path + 'Berry_Driver.onlineVersion', {val: BerryDriverVersionOnline, ack: true});
|
|
if (Debug) log('online berry driver version => ' + BerryDriverVersionOnline, 'info');
|
|
}
|
|
} else {
|
|
log('Axios Status - get_online_berry_driver_version: ' + response.state, 'warn');
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
log(error, 'warn');
|
|
});
|
|
*/
|
|
|
|
if (isSetOptionActive) {
|
|
await createStateAsync(NSPanel_Path + 'Berry_Driver.onlineVersion', {type: 'string', write: false});
|
|
setObject(AliasPath + 'Berry_Driver.onlineVersion', {type: 'channel', common: {role: 'info', name: 'onlineVersion'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Berry_Driver.onlineVersion.ACTUAL', NSPanel_Path + 'Berry_Driver.onlineVersion', true, {
|
|
type: 'string',
|
|
role: 'state',
|
|
name: 'ACTUAL',
|
|
});
|
|
await setStateAsync(NSPanel_Path + 'Berry_Driver.onlineVersion', {val: String(berry_driver_version), ack: true});
|
|
if (Debug) log('online berry driver version => ' + String(berry_driver_version), 'info');
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error requesting firmware in function get_online_berry_driver_version: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the Berry driver version.
|
|
*
|
|
* This function updates the Berry driver version on the NSPanel.
|
|
*
|
|
* @function update_berry_driver_version
|
|
* @returns {Promise<void>} A promise that resolves when the Berry driver update has been completed.
|
|
* @throws {Error} If an error occurs during the update.
|
|
*/
|
|
function check_version_tft_firmware () {
|
|
try {
|
|
if (Debug) {
|
|
log('Requesting online TFT version', 'info');
|
|
}
|
|
|
|
let urlString = 'https://api.github.com/repos/joBr99/nspanel-lovelace-ui/releases/latest';
|
|
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(JSON.stringify(response.data), 'info');
|
|
}
|
|
let NSPanel_JSON = JSON.parse(JSON.stringify(response.data)); // Write JSON result to variable
|
|
let NSPanelTagName = NSPanel_JSON.tag_name; // created_at; published_at; name ; draft ; prerelease
|
|
let NSPanelVersion = NSPanelTagName.replace(/v/i, ''); // Filter unnecessary "v" from variable and write to release variable
|
|
|
|
await createStateAsync(NSPanel_Path + 'TFT_Firmware.onlineVersion', {type: 'string', write: false});
|
|
await setStateAsync(NSPanel_Path + 'TFT_Firmware.onlineVersion', {val: NSPanelVersion, ack: true});
|
|
if (Debug) log('online TFT firmware version => ' + NSPanelVersion, 'info');
|
|
} else {
|
|
log('Axios Status - check_version_tft_firmware: ' + response.state, 'warn');
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
log(error, 'warn');
|
|
});
|
|
} catch (err: any) {
|
|
log('error requesting firmware in function check_version_tft_firmware: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks for online display firmware updates.
|
|
*
|
|
* This function checks for online display firmware updates for the NSPanel.
|
|
*
|
|
* @function check_online_display_firmware
|
|
* @returns {Promise<void>} A promise that resolves when the display firmware update data has been retrieved.
|
|
* @throws {Error} If an error occurs during the update data retrieval.
|
|
*/
|
|
function check_online_display_firmware () {
|
|
try {
|
|
if (Debug) {
|
|
log('Requesting online firmware version', 'info');
|
|
}
|
|
|
|
let urlString = 'https://raw.githubusercontent.com/joBr99/nspanel-lovelace-ui/main/apps/nspanel-lovelace-ui/nspanel-lovelace-ui.py';
|
|
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(response.data, 'info');
|
|
}
|
|
let desired_display_firmware_version = response.data
|
|
.substring(response.data.indexOf('desired_display_firmware_version =') + 34, response.data.indexOf('desired_display_firmware_version =') + 38)
|
|
.replace(/\s+/g, '');
|
|
|
|
await createStateAsync(NSPanel_Path + 'Display_Firmware.onlineVersion', {type: 'string', write: false});
|
|
await setStateAsync(NSPanel_Path + 'Display_Firmware.onlineVersion', {val: desired_display_firmware_version, ack: true});
|
|
if (Debug) log('online display firmware version => ' + desired_display_firmware_version, 'info');
|
|
} else {
|
|
log('Axios Status - check_online_display_firmware: ' + response.state, 'warn');
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
log(error, 'warn');
|
|
});
|
|
} catch (err: any) {
|
|
log('error requesting firmware in function check_online_display_firmware: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
//mqttCallback (topic: string, message: string): Promise<void> {
|
|
/**
|
|
* Handles incoming MQTT messages.
|
|
*
|
|
* This function handles incoming MQTT messages from the NSPanel.
|
|
*
|
|
* @function mqttCallback
|
|
* @param {string} topic - The incoming message topic.
|
|
* @param {string} message - The incoming message.
|
|
* @returns {Promise<void>} A promise that resolves when the message has been processed.
|
|
* @throws {Error} If an error occurs during the message processing.
|
|
*/
|
|
on({id: config.panelRecvTopic}, async (obj) => {
|
|
if (obj.state.val.startsWith('{"CustomRecv":')) {
|
|
try {
|
|
const json = JSON.parse(obj.state.val);
|
|
const split = json.CustomRecv.split(',');
|
|
if (isSetOptionActive) {
|
|
if (split[0] == 'event' && split[1] == 'startup') {
|
|
await createStateAsync(NSPanel_Path + 'Display_Firmware.currentVersion', {type: 'string', write: false});
|
|
await createStateAsync(NSPanel_Path + 'Display_Firmware.currentRelease', {type: 'string', write: false});
|
|
await createStateAsync(NSPanel_Path + 'NSPanel_Version', {type: 'string', write: false});
|
|
|
|
await setStateAsync(NSPanel_Path + 'Display_Firmware.currentVersion', {val: split[2], ack: true});
|
|
await setStateAsync(NSPanel_Path + 'Display_Firmware.currentRelease', {val: split[4], ack: true});
|
|
await setStateAsync(NSPanel_Path + 'NSPanel_Version', {val: split[3], ack: true});
|
|
|
|
if (autoCreateAlias) {
|
|
setObject(AliasPath + 'Display.TFTVersion', {type: 'channel', common: {role: 'info', name: 'Display.TFTVersion'}, native: {}});
|
|
setObject(AliasPath + 'Display.TFTRelease', {type: 'channel', common: {role: 'info', name: 'Display.TFTRelease'}, native: {}});
|
|
setObject(AliasPath + 'Display.Model', {type: 'channel', common: {role: 'info', name: 'Display.Model'}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Display.TFTVersion.ACTUAL', NSPanel_Path + 'Display_Firmware.currentVersion', true, {
|
|
type: 'string',
|
|
role: 'state',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Display.TFTRelease.ACTUAL', NSPanel_Path + 'Display_Firmware.currentRelease', true, {
|
|
type: 'string',
|
|
role: 'state',
|
|
name: 'ACTUAL',
|
|
});
|
|
await createAliasAsync(AliasPath + 'Display.Model.ACTUAL', NSPanel_Path + 'NSPanel_Version', true, {type: 'string', role: 'state', name: 'ACTUAL'});
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isEventMethod(split[1])) HandleMessage(split[0], split[1], parseInt(split[2]), split);
|
|
} catch (err: any) {
|
|
log('error at trigger rceiving CustomRecv: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
/**
|
|
* Updates the Berry driver version on the NSPanel.
|
|
*
|
|
* This function handles the process of updating the Berry driver version on the NSPanel.
|
|
*
|
|
* @function update_berry_driver_version
|
|
* @throws {Error} If an error occurs during the update process.
|
|
*/
|
|
function update_berry_driver_version () {
|
|
try {
|
|
let urlString = `http://${get_current_tasmota_ip_address()}/cm?cmnd=Backlog UpdateDriverVersion https://raw.githubusercontent.com/ticaki/ioBroker.nspanel-lovelace-ui/refs/heads/main/tasmota/berry/${berry_driver_version}/autoexec.be; Restart 1`;
|
|
if (tasmota_web_admin_password != '') {
|
|
urlString = `http://${get_current_tasmota_ip_address()}/cm?user=${tasmota_web_admin_user}&password=${tasmota_web_admin_password}&cmnd=Backlog UpdateDriverVersion https://raw.githubusercontent.com/ticaki/ioBroker.nspanel-lovelace-ui/refs/heads/main/tasmota/berry/${berry_driver_version}/autoexec.be; Restart 1`;
|
|
}
|
|
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(response.data, 'info');
|
|
}
|
|
} else {
|
|
log('Axios Status - update_berry_driver_version: ' + response.state, 'warn');
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
log(error, 'warn');
|
|
});
|
|
} catch (err: any) {
|
|
log('error at function update_berry_driver_version: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the display firmware on the NSPanel.
|
|
*
|
|
* This function updates the display firmware on the NSPanel.
|
|
*
|
|
* @function update_display_firmware
|
|
* @throws {Error} If an error occurs during the firmware update.
|
|
*/
|
|
function update_tft_firmware () {
|
|
if ((existsObject(NSPanel_Path + 'Config.Update.activ') != false) && (existsObject(NSPanel_Path + 'Display_Firmware.TFT.currentVersion') != false)) {
|
|
let id = getState(NSPanel_Path + 'Display_Firmware.TFT.currentVersion').val;
|
|
let currentVersion = id.split('/');
|
|
let version = parseInt(currentVersion[0]);
|
|
if (!isNaN(version)) {
|
|
if ((getState(NSPanel_Path + 'Config.Update.activ').val == 0) && (version != 0)) {
|
|
if (existsState(NSPanel_Path + 'NSPanel_Version')) {
|
|
let desired_display_firmware_url = '';
|
|
|
|
if (getState(NSPanel_Path + 'NSPanel_Version').val == 'us-l') {
|
|
desired_display_firmware_url = `http://nspanel.de/nspanel-us-l-${tft_version}.tft`;
|
|
} else if (getState(NSPanel_Path + 'NSPanel_Version').val == 'us-p') {
|
|
desired_display_firmware_url = `http://nspanel.de/nspanel-us-p-${tft_version}.tft`;
|
|
} else {
|
|
desired_display_firmware_url = `http://nspanel.de/nspanel-${tft_version}.tft`;
|
|
}
|
|
|
|
log('Start TFT-Upgrade for: ' + getState(NSPanel_Path + 'NSPanel_Version').val + ' Version', 'info');
|
|
log('Install NextionTFT: ' + desired_display_firmware_url, 'info');
|
|
|
|
try {
|
|
let urlString = `http://${get_current_tasmota_ip_address()}/cm?cmnd=FlashNextion ${desired_display_firmware_url}`;
|
|
if (tasmota_web_admin_password != '') {
|
|
urlString = `http://${get_current_tasmota_ip_address()}/cm?user=${tasmota_web_admin_user}&password=${tasmota_web_admin_password}&cmnd=FlashNextion ${desired_display_firmware_url}`;
|
|
}
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(response.data, 'info');
|
|
}
|
|
await createStateAsync(NSPanel_Path + 'TFT_Firmware.onlineVersion', {type: 'string', write: false});
|
|
await setStateAsync(NSPanel_Path + 'TFT_Firmware.onlineVersion', {val: tft_version, ack: true});
|
|
Init_Release();
|
|
} else {
|
|
log('Axios Status - update_tft_firmware: ' + response.state, 'warn');
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
log(error, 'warn');
|
|
});
|
|
} catch (err: any) {
|
|
log('error request in function update_tft_firmware: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks for updates.
|
|
*
|
|
* This function checks for updates for the Tasmota Firmware.
|
|
*
|
|
* @function check_updates
|
|
* @throws {Error} If an error occurs during the update check.
|
|
*/
|
|
function update_tasmota_firmware () {
|
|
if (existsObject(NSPanel_Path + 'Config.Update.activ') != false) {
|
|
try {
|
|
if (getState(NSPanel_Path + 'Config.Update.activ').val == 0) {
|
|
let urlString = `http://${get_current_tasmota_ip_address()}/cm?cmnd=OtaUrl ${tasmotaOtaUrl}${tasmotaOtaVersion}`;
|
|
if (tasmota_web_admin_password != '') {
|
|
urlString = `http://${get_current_tasmota_ip_address()}/cm?user=${tasmota_web_admin_user}&password=${tasmota_web_admin_password}&cmnd=OtaUrl ${tasmotaOtaUrl}${tasmotaOtaVersion}`;
|
|
}
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(response.data, 'info');
|
|
}
|
|
} else {
|
|
log('Axios Status - update_tasmota_firmware ==> set OTA: ' + response.state, 'warn');
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
log(error, 'warn');
|
|
});
|
|
|
|
urlString = `http://${get_current_tasmota_ip_address()}/cm?cmnd=Upgrade 1`;
|
|
if (tasmota_web_admin_password != '') {
|
|
urlString = `http://${get_current_tasmota_ip_address()}/cm?user=${tasmota_web_admin_user}&password=${tasmota_web_admin_password}&cmnd=Upgrade 1`;
|
|
}
|
|
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(response.data, 'info');
|
|
}
|
|
} else {
|
|
log('Axios Status - update_tasmota_firmware: ' + response.state, 'warn');
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
log(error, 'warn');
|
|
});
|
|
}
|
|
} catch (err: any) {
|
|
log('error request in function update_tasmota_firmware: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets up a subscription to monitor changes in the INFO1 state of the panel.
|
|
*
|
|
* This subscription listens for changes in the `INFO1` state of the panel.
|
|
* When the state changes, the specified callback function is executed.
|
|
*
|
|
* @event
|
|
* @param {Object} obj - The object containing the state change information.
|
|
* @throws {Error} If an error occurs during the state change handling.
|
|
*/
|
|
on({id: config.panelRecvTopic.substring(0, config.panelRecvTopic.length - 'RESULT'.length) + 'INFO1', change: 'ne'}, async (obj) => {
|
|
try {
|
|
if (getState(NSPanel_Path + 'Config.Update.activ').val == 0) {
|
|
let Tasmota_JSON: any = JSON.parse(obj.state.val);
|
|
if (Tasmota_JSON.Info1.Version.indexOf('safeboot') != -1) {
|
|
log('Tasmota in Safeboot - Please wait while upgrading', 'warn');
|
|
update_tasmota_firmware();
|
|
} else {
|
|
log('Tasmota upgrade complete. New Version: ' + Tasmota_JSON.Info1.Version, 'info');
|
|
get_tasmota_status0();
|
|
//check_updates();
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at trigger with reading senor-data: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
//------------------End Update Functions
|
|
|
|
/**
|
|
* Send Payload to Panel
|
|
* @param {NSPanel.Payload | NSPanel.Payload[]} val Payload or Array of Payload to send
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async function SendToPanel (val: NSPanel.Payload | NSPanel.Payload[]) {
|
|
try {
|
|
if (Array.isArray(val)) {
|
|
val.forEach(function (id) {
|
|
setIfExists(config.panelSendTopic, id.payload);
|
|
if (Debug) {
|
|
log('function SendToPanel payload: ' + id.payload, 'info');
|
|
}
|
|
});
|
|
} else {
|
|
setIfExists(config.panelSendTopic, val.payload);
|
|
if (Debug) {
|
|
log('function SendToPanel val-payload: ' + val.payload, 'info');
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function SendToPanel: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets up a subscription to monitor changes in the alarm state.
|
|
*
|
|
* This subscription listens for changes in the `Alarm.AlarmState` state.
|
|
* When the state changes, the specified callback function is executed.
|
|
*
|
|
* @event
|
|
* @param {Object} obj - The object containing the state change information.
|
|
* @throws {Error} If an error occurs during the state change handling.
|
|
*/
|
|
on({id: NSPanel_Alarm_Path + 'Alarm.AlarmState', change: 'ne'}, async (obj) => {
|
|
try {
|
|
if ((obj.state ? obj.state.val : '') == 'armed' || (obj.state ? obj.state.val : '') == 'disarmed' || (obj.state ? obj.state.val : '') == 'triggered') {
|
|
if (Debug) {
|
|
log('Trigger AlarmState aktivePage: ' + activePage, 'info');
|
|
}
|
|
if (NSPanel_Path == getState(NSPanel_Alarm_Path + 'Alarm.PANEL').val) {
|
|
if (activePage != undefined) GeneratePage(activePage!);
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at Trigger AlarmState: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Handles incoming messages and performs actions based on the message type and method.
|
|
*
|
|
* This function processes incoming messages, determines the type and method, and performs the appropriate actions.
|
|
*
|
|
* @function HandleMessage
|
|
* @param {string} typ - The type of the message.
|
|
* @param {NSPanel.EventMethod} method - The method associated with the event.
|
|
* @param {number | undefined} page - The page number associated with the event, if applicable.
|
|
* @param {string[] | undefined} words - Additional words or parameters associated with the event, if applicable.
|
|
*/
|
|
function HandleMessage (typ: string, method: NSPanel.EventMethod, page: number | undefined, words: string[] | undefined): void {
|
|
try {
|
|
if (typ == 'event') {
|
|
switch (method as NSPanel.EventMethod) {
|
|
case 'startup':
|
|
screensaverEnabled = false;
|
|
UnsubscribeWatcher();
|
|
HandleStartupProcess();
|
|
InitDimmode();
|
|
pageId = 0;
|
|
GeneratePage(config.pages[0]);
|
|
if (Debug) log('HandleMessage -> Startup', 'info');
|
|
Init_Release();
|
|
break;
|
|
case 'sleepReached':
|
|
useMediaEvents = false;
|
|
screensaverEnabled = true;
|
|
if (pageId < 0) pageId = 0;
|
|
HandleScreensaver();
|
|
if (Debug) log('HandleMessage -> sleepReached', 'info');
|
|
break;
|
|
case 'pageOpenDetail':
|
|
if (words != undefined) {
|
|
screensaverEnabled = false;
|
|
UnsubscribeWatcher();
|
|
if (Debug) {
|
|
log('HandleMessage -> pageOpenDetail ' + words[0] + ' - ' + words[1] + ' - ' + words[2] + ' - ' + words[3] + ' - ' + words[4], 'info');
|
|
}
|
|
let tempId: PageItem['id'];
|
|
let tempPageItem = words[3].split('?');
|
|
let placeId: number | undefined = undefined;
|
|
if (!isNaN(parseInt(tempPageItem[0]))) {
|
|
tempId = activePage!.items[tempPageItem[0]].id;
|
|
placeId = parseInt(tempPageItem[0]);
|
|
if (tempId == undefined) {
|
|
throw new Error(`Missing id in HandleMessage!`);
|
|
}
|
|
} else {
|
|
tempId = tempPageItem[0];
|
|
}
|
|
let pageItem: PageItem = findPageItem(tempId);
|
|
if (pageItem !== undefined && isPopupType(words[2])) {
|
|
let temp: string | NSPanel.mediaOptional | undefined = tempPageItem[1];
|
|
if (isMediaOptional(temp)) SendToPanel(GenerateDetailPage(words[2], temp, pageItem, placeId));
|
|
else SendToPanel(GenerateDetailPage(words[2], undefined, pageItem, placeId));
|
|
}
|
|
}
|
|
break;
|
|
case 'buttonPress2':
|
|
screensaverEnabled = false;
|
|
HandleButtonEvent(words);
|
|
if (Debug) {
|
|
if (words != undefined) log('HandleMessage -> buttonPress2 ' + words[0] + ' - ' + words[1] + ' - ' + words[2] + ' - ' + words[3] + ' - ' + words[4], 'info');
|
|
}
|
|
break;
|
|
case 'renderCurrentPage':
|
|
// Event only for HA at this Moment
|
|
if (Debug) log('renderCurrentPage', 'info');
|
|
break;
|
|
case 'button1':
|
|
case 'button2':
|
|
screensaverEnabled = false;
|
|
HandleHardwareButton(method);
|
|
if (Debug) log('HandleMessage -> button1 / button2', 'info');
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function HandleMessage: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Finds and returns a PageItem by its ID from the active page or subpages.
|
|
*
|
|
* @param {String} searching - The ID of the PageItem to search for.
|
|
* @returns {NSPanel.PageItem} The PageItem with the specified ID, or the first item of the active page if an error occurs.
|
|
*
|
|
* The function searches for a PageItem with the specified ID within the active page's items.
|
|
* If not found, it searches through each subPage's items. Logs the found PageItem details if in debug mode.
|
|
* Returns the PageItem if found, otherwise returns the first item of the active page in case of an error.
|
|
*/
|
|
function findPageItem (searching: String): NSPanel.PageItem {
|
|
try {
|
|
let pageItem = activePage!.items.find((e) => e && e.id === searching);
|
|
|
|
if (pageItem !== undefined) {
|
|
if (Debug) log('findPageItem -> pageItem ' + JSON.stringify(pageItem), 'info');
|
|
return pageItem;
|
|
}
|
|
|
|
config.subPages.every((sp) => {
|
|
pageItem = sp.items.find((e) => e && e.id === searching);
|
|
|
|
return pageItem === undefined;
|
|
});
|
|
|
|
if (Debug) log('findPageItem -> pageItem SubPage ' + JSON.stringify(pageItem), 'info');
|
|
//@ts-ignore ticaki bitte lösen, pageItem kann undefined sein.
|
|
return pageItem;
|
|
} catch (err: any) {
|
|
log('error at function findPageItem: ' + err.message, 'error');
|
|
return activePage!.items[0]!;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates a page based on the specified PageType.
|
|
*
|
|
* This function generates a page based on the specified PageType.
|
|
*
|
|
* @function GeneratePage
|
|
* @param {NSPanel.PageType} page - The page type to generate.
|
|
*/
|
|
function GeneratePage (page: PageType): void {
|
|
try {
|
|
activePage = page;
|
|
setIfExists(NSPanel_Path + 'ActivePage.type', activePage.type, null, true);
|
|
setIfExists(NSPanel_Path + 'ActivePage.heading', activePage.heading, null, true);
|
|
setIfExists(NSPanel_Path + 'ActivePage.id0', activePage.items[0] !== undefined ? activePage.items[0].id : '', null, true);
|
|
switch (page.type) {
|
|
case 'cardEntities':
|
|
SendToPanel(GenerateEntitiesPage(page));
|
|
break;
|
|
case 'cardSchedule':
|
|
SendToPanel(GenerateSchedulePage(page));
|
|
break;
|
|
case 'cardThermo':
|
|
SendToPanel(GenerateThermoPage(page));
|
|
break;
|
|
case 'cardThermo2':
|
|
SendToPanel(GenerateThermo2Page(page));
|
|
break;
|
|
case 'cardGrid':
|
|
SendToPanel(GenerateGridPage(page));
|
|
break;
|
|
case 'cardGrid2':
|
|
SendToPanel(GenerateGridPage2(page));
|
|
break;
|
|
case 'cardGrid3':
|
|
SendToPanel(GenerateGridPage3(page));
|
|
break;
|
|
case 'cardMedia':
|
|
useMediaEvents = true;
|
|
SendToPanel(GenerateMediaPage(page));
|
|
break;
|
|
case 'cardAlarm':
|
|
SendToPanel(GenerateAlarmPage(page));
|
|
break;
|
|
case 'cardQR':
|
|
SendToPanel(GenerateQRPage(page));
|
|
break;
|
|
case 'cardPower':
|
|
SendToPanel(GeneratePowerPage(page));
|
|
break;
|
|
case 'cardChart':
|
|
SendToPanel(GenerateChartPage(page));
|
|
break;
|
|
case 'cardLChart':
|
|
SendToPanel(GenerateChartPage(page));
|
|
break;
|
|
case 'cardUnlock':
|
|
SendToPanel(GenerateUnlockPage(page));
|
|
break;
|
|
}
|
|
} catch (err: any) {
|
|
if (err.message == "Cannot read properties of undefined (reading 'type')") {
|
|
log('Please wait a few seconds longer when launching the NSPanel. Not all parameters are loaded yet.', 'warn');
|
|
} else {
|
|
log('error at function GeneratePage: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles actions triggered by hardware button events.
|
|
*
|
|
* This function processes events triggered by hardware button interactions and performs the appropriate actions based on the event method.
|
|
*
|
|
* @function HandleHardwareButton
|
|
* @param {NSPanel.EventMethod} method - The method associated with the hardware button event.
|
|
*/
|
|
function HandleHardwareButton (method: NSPanel.EventMethod): void {
|
|
try {
|
|
let buttonConfig: NSPanel.ConfigButtonFunction = config[method];
|
|
if (buttonConfig.mode === null) {
|
|
return;
|
|
}
|
|
switch (buttonConfig.mode) {
|
|
case 'page':
|
|
if (Debug) log('HandleHardwareButton -> Mode Page', 'info');
|
|
if (buttonConfig.page) {
|
|
if (method == 'button1') {
|
|
pageId = -1;
|
|
} else if (method == 'button2') {
|
|
pageId = -2;
|
|
}
|
|
GeneratePage(buttonConfig.page);
|
|
break;
|
|
}
|
|
case 'toggle':
|
|
if (Debug) log('HandleHardwareButton -> Mode Toggle', 'info');
|
|
if (buttonConfig.entity) {
|
|
let current = getState(buttonConfig.entity).val;
|
|
setState(buttonConfig.entity, !current);
|
|
}
|
|
screensaverEnabled = true;
|
|
break;
|
|
case 'set':
|
|
if (Debug) log('HandleHardwareButton -> Mode Set', 'info');
|
|
if (buttonConfig.setOn && existsState(buttonConfig.setOn.dp) && !buttonToggleState[method]) {
|
|
setState(buttonConfig.setOn.dp, buttonConfig.setOn.val);
|
|
buttonToggleState[method] = true;
|
|
} else if (buttonConfig.setOff && existsState(buttonConfig.setOff.dp) && buttonToggleState[method]) {
|
|
setState(buttonConfig.setOff.dp, buttonConfig.setOff.val);
|
|
buttonToggleState[method] = false;
|
|
} else if (buttonConfig.entity && existsState(buttonConfig.entity)) {
|
|
setState(buttonConfig.entity, buttonConfig.setValue);
|
|
}
|
|
screensaverEnabled = true;
|
|
break;
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function HandleHardwareButton: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles the startup process for the NSPanel.
|
|
*
|
|
* This function performs the necessary initialization steps and configurations required during the startup of the NSPanel.
|
|
*
|
|
* @function HandleStartupProcess
|
|
*/
|
|
function HandleStartupProcess (): void {
|
|
let timeout: number = 10;
|
|
SendDate();
|
|
SendTime();
|
|
if (existsState(NSPanel_Path + 'Config.Screensaver.timeoutScreensaver')) {
|
|
timeout = getState(NSPanel_Path + 'Config.Screensaver.timeoutScreensaver').val;
|
|
}
|
|
SendToPanel({payload: 'timeout~' + timeout});
|
|
}
|
|
|
|
/**
|
|
* Sends the current date to the NSPanel.
|
|
*
|
|
* This function retrieves the current date and sends it to the NSPanel for display or processing.
|
|
*
|
|
* @function SendDate
|
|
*/
|
|
function SendDate (): void {
|
|
try {
|
|
if (existsObject(NSPanel_Path + 'Config.locale')) {
|
|
let dpWeekday = existsObject(NSPanel_Path + 'Config.Dateformat.weekday') ? getState(NSPanel_Path + 'Config.Dateformat.weekday').val : 'short';
|
|
let dpMonth = existsObject(NSPanel_Path + 'Config.Dateformat.month') ? getState(NSPanel_Path + 'Config.Dateformat.month').val : 'short';
|
|
let dpCustomFormat = existsObject(NSPanel_Path + 'Config.Dateformat.customFormat') ? getState(NSPanel_Path + 'Config.Dateformat.customFormat').val : '';
|
|
|
|
const date = new Date();
|
|
const options: any = {weekday: dpWeekday, year: 'numeric', month: dpMonth, day: 'numeric'};
|
|
const _SendDate = dpCustomFormat != '' ? moment().format(dpCustomFormat) : date.toLocaleDateString(getState(NSPanel_Path + 'Config.locale').val, options);
|
|
|
|
SendToPanel({payload: 'date~' + _SendDate});
|
|
}
|
|
} catch (err: any) {
|
|
if ((err.message = 'Cannot convert undefined or null to object')) {
|
|
log('Datumsformat noch nicht initialisiert', 'info');
|
|
} else {
|
|
log('error at function SendDate: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sends the current time to the NSPanel.
|
|
*
|
|
* This function retrieves the current time and sends it to the NSPanel for display or processing.
|
|
*
|
|
* @function SendTime
|
|
*/
|
|
function SendTime (): void {
|
|
try {
|
|
/*const d = new Date();
|
|
const hr = (d.getHours() < 10 ? '0' : '') + d.getHours();
|
|
const min = (d.getMinutes() < 10 ? '0' : '') + d.getMinutes();
|
|
|
|
SendToPanel({ payload: 'time~' + hr + ':' + min });*/
|
|
SendToPanel({payload: `time~${new Date().toLocaleTimeString('de-DE', {hour: '2-digit', minute: '2-digit'})}`});
|
|
} catch (err: any) {
|
|
log('error at function SendTime: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates the payload for an entities page on the NSPanel.
|
|
*
|
|
* This function creates and returns the payload required to display an entities page on the NSPanel.
|
|
*
|
|
* @function GenerateEntitiesPage
|
|
* @param {NSPanel.PageEntities} page - The entities page configuration.
|
|
* @returns {NSPanel.Payload[]} The payload array for the entities page.
|
|
*/
|
|
function GenerateEntitiesPage (page: NSPanel.PageEntities): NSPanel.Payload[] {
|
|
try {
|
|
let out_msgs: NSPanel.Payload[];
|
|
out_msgs = [{payload: 'pageType~cardEntities'}];
|
|
out_msgs.push({payload: GeneratePageElements(page)});
|
|
return out_msgs;
|
|
} catch (err: any) {
|
|
log('error at function GenerateEntitiesPage: ' + err.message, 'warn');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates the payload for an Schedule page on the NSPanel.
|
|
*
|
|
* This function creates and returns the payload required to display an schedule page on the NSPanel.
|
|
*
|
|
* @function GenerateSchedulePage
|
|
* @param {NSPanel.PageSchedule} page - The entities page configuration.
|
|
* @returns {NSPanel.Payload[]} The payload array for the schedule page.
|
|
*/
|
|
function GenerateSchedulePage (page: NSPanel.PageSchedule): NSPanel.Payload[] {
|
|
try {
|
|
let out_msgs: NSPanel.Payload[];
|
|
out_msgs = [{payload: 'pageType~cardSchedule'}];
|
|
out_msgs.push({payload: GeneratePageElements(page)});
|
|
return out_msgs;
|
|
} catch (err: any) {
|
|
log('error at function GenerateSchedulePage: ' + err.message, 'warn');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates the payload for a grid page on the NSPanel.
|
|
*
|
|
* This function creates and returns the payload required to display a grid page on the NSPanel.
|
|
*
|
|
* @function GenerateGridPage
|
|
* @param {NSPanel.PageGrid} page - The grid page configuration.
|
|
* @returns {NSPanel.Payload[]} The payload array for the grid page.
|
|
*/
|
|
function GenerateGridPage (page: NSPanel.PageGrid): NSPanel.Payload[] {
|
|
try {
|
|
let out_msgs: NSPanel.Payload[] = [{payload: 'pageType~cardGrid'}];
|
|
out_msgs.push({payload: GeneratePageElements(page)});
|
|
return out_msgs;
|
|
} catch (err: any) {
|
|
log('error at function GenerateGridPage: ' + err.message, 'warn');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates the payload for a secondary grid page on the NSPanel.
|
|
*
|
|
* This function creates and returns the payload required to display a secondary grid page on the NSPanel.
|
|
*
|
|
* @function GenerateGridPage2
|
|
* @param {NSPanel.PageGrid2} page - The secondary grid page configuration.
|
|
* @returns {NSPanel.Payload[]} The payload array for the secondary grid page.
|
|
*/
|
|
function GenerateGridPage2 (page: NSPanel.PageGrid2): NSPanel.Payload[] {
|
|
try {
|
|
let out_msgs: NSPanel.Payload[] = [{payload: 'pageType~cardGrid2'}];
|
|
out_msgs.push({payload: GeneratePageElements(page)});
|
|
return out_msgs;
|
|
} catch (err: any) {
|
|
log('error at function GenerateGridPage2: ' + err.message, 'warn');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates the payload for a secondary grid page on the NSPanel.
|
|
*
|
|
* This function creates and returns the payload required to display a secondary grid page on the NSPanel.
|
|
*
|
|
* @function GenerateGridPage3
|
|
* @param {NSPanel.PageGrid3} page - The secondary grid page configuration.
|
|
* @returns {NSPanel.Payload[]} The payload array for the secondary grid page.
|
|
*/
|
|
function GenerateGridPage3 (page: NSPanel.PageGrid3): NSPanel.Payload[] {
|
|
try {
|
|
let out_msgs: NSPanel.Payload[] = [{payload: 'pageType~cardGrid3'}];
|
|
out_msgs.push({payload: GeneratePageElements(page)});
|
|
return out_msgs;
|
|
} catch (err: any) {
|
|
log('error at function GenerateGridPage3: ' + err.message, 'warn');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates the page elements for a given page type on the NSPanel.
|
|
*
|
|
* This function creates and returns the string representation of the page elements for the specified page type.
|
|
*
|
|
* @function GeneratePageElements
|
|
* @param {PageType} page - The page type configuration.
|
|
* @returns {string} The string representation of the page elements.
|
|
*/
|
|
function GeneratePageElements (page: PageType): string {
|
|
try {
|
|
activePage = page;
|
|
let maxItems = 0;
|
|
switch (page.type) {
|
|
case 'cardThermo':
|
|
maxItems = 1;
|
|
break;
|
|
case 'cardThermo2':
|
|
maxItems = 9;
|
|
break;
|
|
case 'cardAlarm':
|
|
maxItems = 1;
|
|
break;
|
|
case 'cardUnlock':
|
|
maxItems = 1;
|
|
break;
|
|
case 'cardMedia':
|
|
maxItems = 1;
|
|
break;
|
|
case 'cardQR':
|
|
maxItems = 1;
|
|
break;
|
|
case 'cardPower':
|
|
maxItems = 1;
|
|
break;
|
|
case 'cardChart':
|
|
maxItems = 1;
|
|
break;
|
|
case 'cardEntities':
|
|
if (existsState(NSPanel_Path + 'NSPanel_Version')) {
|
|
if (getState(NSPanel_Path + 'NSPanel_Version').val == 'eu') {
|
|
maxItems = 4;
|
|
} else {
|
|
maxItems = 5;
|
|
}
|
|
} else {
|
|
maxItems = 4;
|
|
}
|
|
break;
|
|
case 'cardSchedule':
|
|
maxItems = 6;
|
|
break;
|
|
case 'cardGrid':
|
|
maxItems = 6;
|
|
break;
|
|
case 'cardGrid2':
|
|
if (existsState(NSPanel_Path + 'NSPanel_Version')) {
|
|
if (getState(NSPanel_Path + 'NSPanel_Version').val == 'us-p') {
|
|
maxItems = 9;
|
|
} else {
|
|
maxItems = 8;
|
|
}
|
|
} else {
|
|
maxItems = 8;
|
|
}
|
|
break;
|
|
case 'cardGrid3':
|
|
maxItems = 4;
|
|
break;
|
|
}
|
|
|
|
if (activePage.type != 'cardThermo2') {
|
|
let pageData = 'entityUpd~' + page.heading + '~' + getNavigationString(pageId);
|
|
|
|
for (let index = 0; index < maxItems; index++) {
|
|
if (page.items[index] !== undefined) {
|
|
pageData += CreateEntity(page.items[index], index, 'useColor' in page ? page.useColor : false);
|
|
}
|
|
}
|
|
if (Debug) log('GeneratePageElements pageData ' + pageData, 'info');
|
|
return pageData;
|
|
} else {
|
|
setTimeout(async function () {
|
|
pageCounter = 1;
|
|
GeneratePage(activePage!);
|
|
}, 500);
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function GeneratePageElements: ' + err.message, 'warn');
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates an entity for the NSPanel.
|
|
*
|
|
* @param {PageItem} pageItem - The object containing information about the entity.
|
|
* @param {number} placeId - The position of the entity on the NSPanel.
|
|
* @param {boolean} [useColors=false] - Whether the entity should contain color information.
|
|
* @returns {string} The string representing the entity.
|
|
*/
|
|
function CreateEntity (pageItem: PageItem, placeId: number, useColors: boolean = false): string {
|
|
|
|
if (Debug) log(JSON.stringify(pageItem) + ' - ' + placeId, 'info');
|
|
|
|
try {
|
|
let iconId = '0';
|
|
let iconId2 = '0';
|
|
if (pageItem.id == 'delete') {
|
|
return '~delete~~~~~';
|
|
}
|
|
|
|
let name: string;
|
|
let buttonText: string = 'PRESS';
|
|
let type: NSPanel.SerialType;
|
|
|
|
// ioBroker
|
|
if ((pageItem.id && existsObject(pageItem.id)) || pageItem.navigate === true) {
|
|
let iconColor: number = rgb_dec565(config.defaultColor);
|
|
let optVal: string = '0';
|
|
let val: any = null;
|
|
|
|
let o: any = undefined;
|
|
if (pageItem.id != null && existsObject(pageItem.id)) {
|
|
o = getObject(pageItem.id);
|
|
}
|
|
|
|
// Fallback if no name is given
|
|
name = pageItem.name !== undefined ? pageItem.name : o.common.name.de == undefined ? o.common.name : o.common.name.de;
|
|
const prefix = pageItem.prefixName !== undefined ? pageItem.prefixName : '';
|
|
const suffix = pageItem.suffixName !== undefined ? pageItem.suffixName : '';
|
|
|
|
// If name is used with changing values
|
|
if ((name || '').indexOf('getState(') != -1) {
|
|
let dpName: string = name.slice(10, name.length - 6);
|
|
name = getState(dpName).val;
|
|
RegisterEntityWatcher(dpName);
|
|
} else if ((name || '').split('.').length > 3 && existsState(name)) {
|
|
name = getState(name).val;
|
|
RegisterEntityWatcher(name);
|
|
}
|
|
name = prefix + name + suffix;
|
|
|
|
if (existsState(pageItem.id + '.GET')) {
|
|
val = getState(pageItem.id + '.GET').val;
|
|
RegisterEntityWatcher(pageItem.id + '.GET');
|
|
}
|
|
if (pageItem.monobutton != undefined && pageItem.monobutton == true) {
|
|
if (existsState(pageItem.id + '.ACTUAL')) {
|
|
val = getState(pageItem.id + '.ACTUAL').val;
|
|
RegisterEntityWatcher(pageItem.id + '.ACTUAL');
|
|
}
|
|
} else {
|
|
if (existsState(pageItem.id + '.ACTUAL')) {
|
|
val = getState(pageItem.id + '.ACTUAL').val;
|
|
RegisterEntityWatcher(pageItem.id + '.ACTUAL');
|
|
}
|
|
if (existsState(pageItem.id + '.SET')) {
|
|
val = getState(pageItem.id + '.SET').val;
|
|
RegisterEntityWatcher(pageItem.id + '.SET');
|
|
}
|
|
}
|
|
if (existsState(pageItem.id + '.ON_ACTUAL')) {
|
|
val = getState(pageItem.id + '.ON_ACTUAL').val;
|
|
RegisterEntityWatcher(pageItem.id + '.ON_ACTUAL');
|
|
}
|
|
if (existsState(pageItem.id + '.ON_SET')) {
|
|
val = getState(pageItem.id + '.ON_SET').val;
|
|
RegisterEntityWatcher(pageItem.id + '.ON_SET');
|
|
}
|
|
if (existsState(pageItem.id + '.ON')) {
|
|
val = getState(pageItem.id + '.ON').val;
|
|
RegisterEntityWatcher(pageItem.id + '.ON');
|
|
}
|
|
|
|
if (pageItem.navigate) {
|
|
if (pageItem.id == null && pageItem.targetPage != undefined) {
|
|
buttonText = pageItem.buttonText !== undefined ? pageItem.buttonText : 'PRESS';
|
|
type = 'button';
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('gesture-tap-button');
|
|
iconColor = GetIconColor(pageItem, true, useColors);
|
|
|
|
if (Debug)
|
|
log('CreateEntity statisch Icon Navi ~' + type + '~' + 'navigate.' + pageItem.targetPage + '~' + iconId + '~' + iconColor + '~' + pageItem.name + '~' + buttonText, 'info');
|
|
return '~' + type + '~' + 'navigate.' + pageItem.targetPage + '~' + iconId + '~' + iconColor + '~' + name + '~' + buttonText;
|
|
} else if (pageItem.id != null && pageItem.targetPage != undefined) {
|
|
type = 'button';
|
|
const role = o.common.role as NSPanel.roles;
|
|
switch (role) {
|
|
case 'socket':
|
|
case 'light':
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : role == 'socket' ? Icons.GetIcon('power-socket-de') : Icons.GetIcon('lightbulb');
|
|
iconId2 = pageItem.icon2 !== undefined ? Icons.GetIcon(pageItem.icon2) : iconId;
|
|
|
|
buttonText = pageItem.buttonText !== undefined ? pageItem.buttonText : existsState(pageItem.id + '.BUTTONTEXT') ? getState(pageItem.id + '.BUTTONTEXT').val : 'PRESS';
|
|
if (existsState(pageItem.id + '.COLORDEC')) {
|
|
iconColor = getState(pageItem.id + '.COLORDEC').val;
|
|
} else {
|
|
if (val === true || val === 'true') {
|
|
iconColor = GetIconColor(pageItem, true, useColors);
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, false, useColors);
|
|
}
|
|
}
|
|
if (val === true || val === 'true') {
|
|
iconId = iconId2;
|
|
}
|
|
break;
|
|
|
|
case 'blind':
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('window-open');
|
|
/**
|
|
* The same extension can be found below in blind. vval=0 means closed / val=100 means open. If val is in between, icon3 is used.
|
|
* Icons in this part can be states and strings. The specifications are based on German shutters.
|
|
*/
|
|
iconId = determinePageItemStatusIcon(pageItem, val, iconId, ['window-shutter', 'window-shutter-open', 'window-shutter-alert']);
|
|
iconColor = existsState(pageItem.id + '.COLORDEC')
|
|
? getState(pageItem.id + '.COLORDEC').val
|
|
: GetIconColor(pageItem, existsState(pageItem.id + '.ACTUAL') ? getState(pageItem.id + '.ACTUAL').val : true, useColors);
|
|
buttonText = pageItem.buttonText !== undefined ? pageItem.buttonText : existsState(pageItem.id + '.BUTTONTEXT') ? getState(pageItem.id + '.BUTTONTEXT').val : 'PRESS';
|
|
break;
|
|
|
|
case 'door':
|
|
case 'window':
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : role == 'door' ? Icons.GetIcon('door-closed') : Icons.GetIcon('window-closed-variant');
|
|
iconId2 = pageItem.icon2 !== undefined ? Icons.GetIcon(pageItem.icon2) : role == 'door' ? Icons.GetIcon('door-open') : Icons.GetIcon('window-open-variant');
|
|
|
|
buttonText = pageItem.buttonText !== undefined ? pageItem.buttonText : existsState(pageItem.id + '.BUTTONTEXT') ? getState(pageItem.id + '.BUTTONTEXT').val : 'PRESS';
|
|
if (existsState(pageItem.id + '.COLORDEC')) {
|
|
iconColor = getState(pageItem.id + '.COLORDEC').val;
|
|
} else {
|
|
if (val === true || val === 'true') {
|
|
iconColor = GetIconColor(pageItem, true, useColors);
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, false, useColors);
|
|
}
|
|
}
|
|
if (val === true || val === 'true') {
|
|
iconId = iconId2;
|
|
}
|
|
break;
|
|
|
|
|
|
case 'info':
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('gesture-tap-button');
|
|
iconId2 = pageItem.icon2 !== undefined ? Icons.GetIcon(pageItem.icon2) : iconId;
|
|
|
|
buttonText = pageItem.buttonText !== undefined ? pageItem.buttonText : existsState(pageItem.id + '.BUTTONTEXT') ? getState(pageItem.id + '.BUTTONTEXT').val : 'PRESS';
|
|
if (existsState(pageItem.id + '.COLORDEC')) {
|
|
iconColor = getState(pageItem.id + '.COLORDEC').val;
|
|
} else {
|
|
if (val === true || val === 'true') {
|
|
iconColor = GetIconColor(pageItem, false, useColors);
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, true, useColors);
|
|
}
|
|
}
|
|
|
|
if (pageItem.colorScale != undefined) {
|
|
let iconvalmin = pageItem.colorScale.val_min != undefined ? pageItem.colorScale.val_min : 0;
|
|
let iconvalmax = pageItem.colorScale.val_max != undefined ? pageItem.colorScale.val_max : 100;
|
|
let iconvalbest = pageItem.colorScale.val_best != undefined ? pageItem.colorScale.val_best : iconvalmin;
|
|
let valueScale = val;
|
|
|
|
if (iconvalmin == 0 && iconvalmax == 1) {
|
|
iconColor = getState(pageItem.id).val == 1 ? rgb_dec565(colorScale0) : rgb_dec565(colorScale10);
|
|
} else {
|
|
if (iconvalbest == iconvalmin) {
|
|
valueScale = scale(valueScale, iconvalmin, iconvalmax, 10, 0);
|
|
} else {
|
|
if (valueScale < iconvalbest) {
|
|
valueScale = scale(valueScale, iconvalmin, iconvalbest, 0, 10);
|
|
} else if (valueScale > iconvalbest || iconvalbest != iconvalmin) {
|
|
valueScale = scale(valueScale, iconvalbest, iconvalmax, 10, 0);
|
|
} else {
|
|
valueScale = scale(valueScale, iconvalmin, iconvalmax, 10, 0);
|
|
}
|
|
}
|
|
let valueScaletemp = Math.round(valueScale).toFixed();
|
|
iconColor = HandleColorScale(valueScaletemp);
|
|
}
|
|
}
|
|
|
|
if (val === true || val === 'true') {
|
|
iconId = iconId2;
|
|
}
|
|
break;
|
|
|
|
case 'humidity':
|
|
|
|
case 'temperature':
|
|
|
|
case 'illuminance':
|
|
|
|
case 'value.temperature':
|
|
|
|
case 'value.humidity':
|
|
|
|
case 'sensor.door':
|
|
|
|
case 'sensor.window':
|
|
|
|
case 'thermostat':
|
|
iconId =
|
|
pageItem.icon !== undefined
|
|
? Icons.GetIcon(pageItem.icon)
|
|
: role == 'temperature' || role == 'value.temperature' || role == 'thermostat'
|
|
? Icons.GetIcon('thermometer')
|
|
: Icons.GetIcon('information-outline');
|
|
|
|
let unit = '';
|
|
optVal = '0';
|
|
|
|
if (existsState(pageItem.id + '.ON_ACTUAL')) {
|
|
optVal = getState(pageItem.id + '.ON_ACTUAL').val;
|
|
unit = pageItem.unit !== undefined ? pageItem.unit : GetUnitOfMeasurement(pageItem.id + '.ON_ACTUAL');
|
|
} else if (existsState(pageItem.id + '.ACTUAL')) {
|
|
optVal = getState(pageItem.id + '.ACTUAL').val;
|
|
unit = pageItem.unit !== undefined ? pageItem.unit : GetUnitOfMeasurement(pageItem.id + '.ACTUAL');
|
|
}
|
|
|
|
if (role == 'temperature' || role == 'value.temperature' || role == 'thermostat') {
|
|
iconId = determinePageItemStatusIcon(pageItem, val, iconId, ['snowflake-thermometer', 'sun-thermometer', 'thermometer']);
|
|
} else if (role == 'humidity' || role == 'value.humidity') {
|
|
iconId = determinePageItemStatusIcon(pageItem, val, iconId, ['water-off', 'water-percent-alert', 'water-percent']);
|
|
}
|
|
iconColor = GetIconColor(pageItem, parseInt(optVal), useColors);
|
|
|
|
if (pageItem.colorScale != undefined) {
|
|
let iconvalmin = pageItem.colorScale.val_min != undefined ? pageItem.colorScale.val_min : 0;
|
|
let iconvalmax = pageItem.colorScale.val_max != undefined ? pageItem.colorScale.val_max : 100;
|
|
let iconvalbest = pageItem.colorScale.val_best != undefined ? pageItem.colorScale.val_best : iconvalmin;
|
|
let valueScale = val;
|
|
|
|
if (iconvalmin == 0 && iconvalmax == 1) {
|
|
iconColor = getState(pageItem.id).val == 1 ? rgb_dec565(colorScale0) : rgb_dec565(colorScale10);
|
|
} else {
|
|
if (iconvalbest == iconvalmin) {
|
|
valueScale = scale(valueScale, iconvalmin, iconvalmax, 10, 0);
|
|
} else {
|
|
if (valueScale < iconvalbest) {
|
|
valueScale = scale(valueScale, iconvalmin, iconvalbest, 0, 10);
|
|
} else if (valueScale > iconvalbest || iconvalbest != iconvalmin) {
|
|
valueScale = scale(valueScale, iconvalbest, iconvalmax, 10, 0);
|
|
} else {
|
|
valueScale = scale(valueScale, iconvalmin, iconvalmax, 10, 0);
|
|
}
|
|
}
|
|
let valueScaletemp = Math.round(valueScale).toFixed();
|
|
iconColor = HandleColorScale(valueScaletemp);
|
|
}
|
|
}
|
|
|
|
if (existsState(pageItem.id + '.USERICON')) {
|
|
iconId = Icons.GetIcon(getState(pageItem.id + '.USERICON').val);
|
|
if (Debug) log('iconid von ' + pageItem.id + '.USERICON: ' + getState(pageItem.id + '.USERICON').val, 'info');
|
|
RegisterEntityWatcher(pageItem.id + '.USERICON');
|
|
}
|
|
|
|
if (pageItem.useValue) {
|
|
if (pageItem.fontSize != undefined) {
|
|
iconId = optVal + '¬' + pageItem.fontSize;
|
|
} else {
|
|
iconId = optVal;
|
|
}
|
|
}
|
|
|
|
buttonText = pageItem.buttonText !== undefined ? pageItem.buttonText : existsState(pageItem.id + '.BUTTONTEXT') ? getState(pageItem.id + '.BUTTONTEXT').val : 'PRESS';
|
|
break;
|
|
|
|
case 'warning':
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('gesture-tap-button');
|
|
iconColor = pageItem.onColor !== undefined ? GetIconColor(pageItem, true, useColors) : getState(pageItem.id + '.LEVEL').val;
|
|
name = pageItem.name !== undefined ? pageItem.name : getState(pageItem.id + '.INFO').val;
|
|
break;
|
|
|
|
default:
|
|
buttonText = pageItem.buttonText !== undefined ? pageItem.buttonText : existsState(pageItem.id + '.BUTTONTEXT') ? getState(pageItem.id + '.BUTTONTEXT').val : 'PRESS';
|
|
iconColor =
|
|
pageItem.onColor !== undefined ? GetIconColor(pageItem, true, useColors) : existsState(pageItem.id + '.COLORDEC') ? getState(pageItem.id + '.COLORDEC').val : 65535;
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('gesture-tap-button');
|
|
break;
|
|
// return '~delete~~~~~';
|
|
}
|
|
|
|
if (Debug) log('CreateEntity Dynamische Icon Navi ~' + type + '~' + 'navigate.' + pageItem.targetPage + '~' + iconId + '~' + iconColor + '~' + name + '~' + buttonText, 'info');
|
|
return '~' + type + '~' + 'navigate.' + pageItem.targetPage + '~' + iconId + '~' + iconColor + '~' + name + '~' + buttonText;
|
|
} else {
|
|
type = 'button';
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('gesture-tap-button');
|
|
iconColor = GetIconColor(pageItem, true, useColors);
|
|
buttonText = pageItem.buttonText !== undefined ? pageItem.buttonText : 'PRESS';
|
|
|
|
if (Debug) log('CreateEntity Standard ~' + type + '~' + 'navigate.' + pageItem.id + '~' + iconId + '~' + iconColor + '~' + name + '~' + buttonText, 'info');
|
|
return '~' + type + '~' + 'navigate.' + pageItem.id + '~' + iconId + '~' + iconColor + '~' + name + '~' + buttonText;
|
|
}
|
|
}
|
|
const role = o.common.role as NSPanel.roles;
|
|
switch (role) {
|
|
case 'socket':
|
|
case 'light':
|
|
type = 'light';
|
|
if (pageItem.popupVersion !== undefined) {
|
|
if (pageItem.popupVersion == 1) {
|
|
type = 'light';
|
|
} else if (pageItem.popupVersion == 2) {
|
|
type = 'light2';
|
|
}
|
|
}
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : role == 'socket' ? Icons.GetIcon('power-socket-de') : Icons.GetIcon('lightbulb');
|
|
iconId2 = pageItem.icon2 !== undefined ? Icons.GetIcon(pageItem.icon2) : role == 'socket' ? Icons.GetIcon('power-socket-de') : Icons.GetIcon('lightbulb-outline');
|
|
optVal = '0';
|
|
|
|
if (val === true || val === 'true') {
|
|
optVal = '1';
|
|
iconColor = GetIconColor(pageItem, true, useColors);
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, false, useColors);
|
|
if (pageItem.icon2 !== undefined) {
|
|
iconId = iconId2;
|
|
}
|
|
}
|
|
|
|
if (Debug) log('CreateEntity Icon role socket/light ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal, 'info');
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal;
|
|
|
|
case 'hue':
|
|
type = 'light';
|
|
if (pageItem.popupVersion !== undefined) {
|
|
if (pageItem.popupVersion == 1) {
|
|
type = 'light';
|
|
} else if (pageItem.popupVersion == 2) {
|
|
type = 'light2';
|
|
}
|
|
}
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('lightbulb');
|
|
iconId2 = pageItem.icon2 !== undefined ? Icons.GetIcon(pageItem.icon2) : Icons.GetIcon('lightbulb-outline');
|
|
optVal = '0';
|
|
|
|
if (val === true || val === 'true') {
|
|
optVal = '1';
|
|
iconColor = GetIconColor(pageItem, existsState(pageItem.id + '.DIMMER') ? getState(pageItem.id + '.DIMMER').val : 100, useColors);
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, false, useColors);
|
|
if (pageItem.icon2 !== undefined) {
|
|
iconId = iconId2;
|
|
}
|
|
}
|
|
|
|
//Calculate color for icon based on color, color temperature and brightness
|
|
//Check last Change of DP HUE or CT for Icon in GUI
|
|
if (existsState(pageItem.id + '.HUE') && existsState(pageItem.id + '.TEMPERATURE') && existsState(pageItem.id + '.DIMMER') && pageItem.interpolateColor) {
|
|
let brightness = getState(pageItem.id + '.DIMMER').val;
|
|
//@ts-ignore
|
|
if (getState(pageItem.id + '.TEMPERATURE').ts < getState(pageItem.id + '.HUE').ts) {
|
|
if (Debug) log('HUE wurde zuletzt geändert - Lampe ist Color-Mode');
|
|
let huecolor = hsv2rgb(getState(pageItem.id + '.HUE').val, 1, 1);
|
|
let rgb: RGB = {red: Math.round(huecolor[0]), green: Math.round(huecolor[1]), blue: Math.round(huecolor[2])};
|
|
let cRGB: RGB = lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1);
|
|
iconColor = rgb_dec565(pageItem.interpolateColor !== undefined ? cRGB : config.defaultOnColor);
|
|
} else {
|
|
if (Debug) log('TEMPERATURE wurde zuletzt geändert - Lampe ist CT-Mode');
|
|
let rgb: RGB = kelvinToRGB(getState(pageItem.id + '.TEMPERATURE').val);
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
}
|
|
}
|
|
|
|
if (Debug) log('CreateEntity Icon role hue ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal, 'info');
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal;
|
|
|
|
case 'ct':
|
|
type = 'light';
|
|
if (pageItem.popupVersion !== undefined) {
|
|
if (pageItem.popupVersion == 1) {
|
|
type = 'light';
|
|
} else if (pageItem.popupVersion == 2) {
|
|
type = 'light2';
|
|
}
|
|
}
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('lightbulb');
|
|
iconId2 = pageItem.icon2 !== undefined ? Icons.GetIcon(pageItem.icon2) : Icons.GetIcon('lightbulb-outline');
|
|
optVal = '0';
|
|
|
|
if (val === true || val === 'true') {
|
|
optVal = '1';
|
|
iconColor = GetIconColor(pageItem, existsState(pageItem.id + '.DIMMER') ? getState(pageItem.id + '.DIMMER').val : 100, useColors);
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, false, useColors);
|
|
if (pageItem.icon2 !== undefined) {
|
|
iconId = iconId2;
|
|
}
|
|
}
|
|
|
|
//Calculate color for icon based on color temperature and brightness
|
|
if (existsState(pageItem.id + '.TEMPERATURE') && existsState(pageItem.id + '.DIMMER') && pageItem.interpolateColor) {
|
|
let brightness = getState(pageItem.id + '.DIMMER').val;
|
|
if (getState(pageItem.id + '.TEMPERATURE').val > 1000) {
|
|
//Color-Temperatur in Kelvin
|
|
let rgb: RGB = kelvinToRGB(getState(pageItem.id + '.TEMPERATURE').val);
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
} else {
|
|
//Color-Temperatur in mired
|
|
let rgb: RGB = kelvinToRGB(1000000 / (getState(pageItem.id + '.TEMPERATURE').val));
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
}
|
|
}
|
|
|
|
if (Debug) log('CreateEntity Icon role ct ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal, 'info');
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal;
|
|
|
|
case 'rgb':
|
|
type = 'light';
|
|
if (pageItem.popupVersion !== undefined) {
|
|
if (pageItem.popupVersion == 1) {
|
|
type = 'light';
|
|
} else if (pageItem.popupVersion == 2) {
|
|
type = 'light2';
|
|
}
|
|
}
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('lightbulb');
|
|
iconId2 = pageItem.icon2 !== undefined ? Icons.GetIcon(pageItem.icon2) : Icons.GetIcon('lightbulb-outline');
|
|
optVal = '0';
|
|
|
|
if (val === true || val === 'true') {
|
|
optVal = '1';
|
|
iconColor = GetIconColor(pageItem, existsState(pageItem.id + '.DIMMER') ? getState(pageItem.id + '.DIMMER').val : 100, useColors);
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, false, useColors);
|
|
if (pageItem.icon2 !== undefined) {
|
|
iconId = iconId2;
|
|
}
|
|
}
|
|
|
|
//Calculate color for icon based on color, color temperature and brightness
|
|
//Check last Change of DP RGB or CT for Icon in GUI
|
|
if (existsState(pageItem.id + '.RED') && existsState(pageItem.id + '.GREEN') && existsState(pageItem.id + '.BLUE') && existsState(pageItem.id + '.TEMPERATURE') && existsState(pageItem.id + '.DIMMER') && pageItem.interpolateColor) {
|
|
let brightness = getState(pageItem.id + '.DIMMER').val;
|
|
//@ts-ignore
|
|
if (getState(pageItem.id + '.TEMPERATURE').ts < getState(pageItem.id + '.RED').ts) {
|
|
if (Debug) log('RED wurde zuletzt geändert - Lampe ist Color-Mode');
|
|
if (getState(pageItem.id + '.RED').val != null) {
|
|
let rgbRed = getState(pageItem.id + '.RED').val;
|
|
let rgbGreen = getState(pageItem.id + '.GREEN').val;
|
|
let rgbBlue = getState(pageItem.id + '.BLUE').val;
|
|
let rgb: RGB = {red: Math.round(rgbRed), green: Math.round(rgbGreen), blue: Math.round(rgbBlue)};
|
|
let cRGB: RGB = lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1);
|
|
iconColor = rgb_dec565(pageItem.interpolateColor !== undefined ? cRGB : config.defaultOnColor);
|
|
}
|
|
} else {
|
|
if (Debug) log('TEMPERATURE wurde zuletzt geändert - Lampe ist CT-Mode');
|
|
if (getState(pageItem.id + '.TEMPERATURE').val > 1000) {
|
|
//Color-Temperatur in Kelvin
|
|
let rgb: RGB = kelvinToRGB(getState(pageItem.id + '.TEMPERATURE').val);
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
} else {
|
|
//Color-Temperatur in mired
|
|
let rgb: RGB = kelvinToRGB(1000000 / (getState(pageItem.id + '.TEMPERATURE').val));
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Debug) log('CreateEntity Icon role rgb ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal, 'info');
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal;
|
|
|
|
case 'cie':
|
|
type = 'light';
|
|
if (pageItem.popupVersion !== undefined) {
|
|
if (pageItem.popupVersion == 1) {
|
|
type = 'light';
|
|
} else if (pageItem.popupVersion == 2) {
|
|
type = 'light2';
|
|
}
|
|
}
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('lightbulb');
|
|
iconId2 = pageItem.icon2 !== undefined ? Icons.GetIcon(pageItem.icon2) : Icons.GetIcon('lightbulb-outline');
|
|
optVal = '0';
|
|
|
|
if (val === true || val === 'true') {
|
|
optVal = '1';
|
|
iconColor = GetIconColor(pageItem, existsState(pageItem.id + '.DIMMER') ? getState(pageItem.id + '.DIMMER').val : 100, useColors);
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, false, useColors);
|
|
if (pageItem.icon2 !== undefined) {
|
|
iconId = iconId2;
|
|
}
|
|
}
|
|
|
|
//Calculate color for icon based on color, color temperature and brightness
|
|
//Check last Change of DP CIE or CT for Icon in GUI
|
|
if (existsState(pageItem.id + '.CIE') && existsState(pageItem.id + '.TEMPERATURE') && existsState(pageItem.id + '.DIMMER') && pageItem.interpolateColor) {
|
|
let brightness = getState(pageItem.id + '.DIMMER').val;
|
|
//@ts-ignore
|
|
if (getState(pageItem.id + '.TEMPERATURE').ts < getState(pageItem.id + '.CIE').ts) {
|
|
if (Debug) log('CIE wurde zuletzt geändert - Lampe ist Color-Mode');
|
|
if (getState(pageItem.id + '.CIE').val != null) {
|
|
let cie: string = getState(pageItem.id + '.CIE').val;
|
|
let cieArray = (cie.substring(1, cie.length - 1)).split(',');
|
|
let rgb: RGB = cie_to_rgb(parseFloat(cieArray[0]), parseFloat(cieArray[1]), 254);
|
|
let cRGB: RGB = lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1);
|
|
iconColor = rgb_dec565(pageItem.interpolateColor !== undefined ? cRGB : config.defaultOnColor);
|
|
}
|
|
} else {
|
|
if (Debug) log('TEMPERATURE wurde zuletzt geändert - Lampe ist CT-Mode');
|
|
if (getState(pageItem.id + '.TEMPERATURE').val > 1000) {
|
|
//Color-Temperatur in Kelvin
|
|
let rgb: RGB = kelvinToRGB(getState(pageItem.id + '.TEMPERATURE').val);
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
} else {
|
|
//Color-Temperatur in mired
|
|
let rgb: RGB = kelvinToRGB(1000000 / (getState(pageItem.id + '.TEMPERATURE').val));
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Debug) log('CreateEntity Icon role cie ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal, 'info');
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal;
|
|
|
|
case 'rgbSingle':
|
|
type = 'light';
|
|
if (pageItem.popupVersion !== undefined) {
|
|
if (pageItem.popupVersion == 1) {
|
|
type = 'light';
|
|
} else if (pageItem.popupVersion == 2) {
|
|
type = 'light2';
|
|
}
|
|
}
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('lightbulb');
|
|
iconId2 = pageItem.icon2 !== undefined ? Icons.GetIcon(pageItem.icon2) : Icons.GetIcon('lightbulb-outline');
|
|
optVal = '0';
|
|
|
|
if (val === true || val === 'true') {
|
|
optVal = '1';
|
|
iconColor = GetIconColor(pageItem, existsState(pageItem.id + '.DIMMER') ? getState(pageItem.id + '.DIMMER').val : 100, useColors);
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, false, useColors);
|
|
if (pageItem.icon2 !== undefined) {
|
|
iconId = iconId2;
|
|
}
|
|
}
|
|
|
|
//Calculate color for icon based on color, color temperature and brightness
|
|
//Check last Change of DP RGB or CT for Icon in GUI
|
|
if (existsState(pageItem.id + '.RGB') && existsState(pageItem.id + '.TEMPERATURE') && existsState(pageItem.id + '.DIMMER') && pageItem.interpolateColor) {
|
|
let brightness = getState(pageItem.id + '.DIMMER').val;
|
|
//@ts-ignore
|
|
if (getState(pageItem.id + '.TEMPERATURE').ts < getState(pageItem.id + '.RGB').ts) {
|
|
if (Debug) log('RGB wurde zuletzt geändert - Lampe ist Color-Mode');
|
|
if (getState(pageItem.id + '.RGB').val != null) {
|
|
let hex = getState(pageItem.id + '.RGB').val;
|
|
let hexRed = parseInt(hex[1] + hex[2], 16);
|
|
let hexGreen = parseInt(hex[3] + hex[4], 16);
|
|
let hexBlue = parseInt(hex[5] + hex[6], 16);
|
|
let rgb: RGB = {red: Math.round(hexRed), green: Math.round(hexGreen), blue: Math.round(hexBlue)};
|
|
let cRGB: RGB = lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1);
|
|
iconColor = rgb_dec565(pageItem.interpolateColor !== undefined ? cRGB : config.defaultOnColor);
|
|
}
|
|
} else {
|
|
if (Debug) log('TEMPERATURE wurde zuletzt geändert - Lampe ist CT-Mode');
|
|
if (getState(pageItem.id + '.TEMPERATURE').val > 1000) {
|
|
//Color-Temperatur in Kelvin
|
|
let rgb: RGB = kelvinToRGB(getState(pageItem.id + '.TEMPERATURE').val);
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
} else {
|
|
//Color-Temperatur in mired
|
|
let rgb: RGB = kelvinToRGB(1000000 / (getState(pageItem.id + '.TEMPERATURE').val));
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Debug) log('CreateEntity Icon role rgbSingle ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal, 'info');
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal;
|
|
|
|
case 'dimmer':
|
|
type = 'light';
|
|
if (pageItem.popupVersion !== undefined) {
|
|
if (pageItem.popupVersion == 1) {
|
|
type = 'light';
|
|
} else if (pageItem.popupVersion == 2) {
|
|
type = 'light2';
|
|
}
|
|
}
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('lightbulb');
|
|
iconId2 = pageItem.icon2 !== undefined ? Icons.GetIcon(pageItem.icon2) : Icons.GetIcon('lightbulb-outline');
|
|
optVal = '0';
|
|
|
|
if (val === true || val === 'true') {
|
|
optVal = '1';
|
|
iconColor = GetIconColor(pageItem, existsState(pageItem.id + '.ACTUAL') ? getState(pageItem.id + '.ACTUAL').val : 100, useColors);
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, false, useColors);
|
|
if (pageItem.icon2 !== undefined) {
|
|
iconId = iconId2;
|
|
}
|
|
}
|
|
|
|
if (Debug) log('CreateEntity Icon role dimmer ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal, 'info');
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal;
|
|
|
|
case 'blind':
|
|
type = 'shutter';
|
|
if (pageItem.popupVersion !== undefined) {
|
|
if (pageItem.popupVersion == 1) {
|
|
type = 'shutter';
|
|
} else if (pageItem.popupVersion == 2) {
|
|
type = 'shutter2';
|
|
}
|
|
}
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('window-open');
|
|
iconColor = GetIconColor(pageItem, existsState(pageItem.id + '.ACTUAL') ? getState(pageItem.id + '.ACTUAL').val : true, useColors);
|
|
// only if icon3 is set go into 3 icons
|
|
iconId = determinePageItemStatusIcon(pageItem, val, iconId, ['window-shutter', 'window-shutter-open', 'window-shutter-alert']);
|
|
|
|
let min_Level: number = 0;
|
|
let max_Level: number = 100;
|
|
if (pageItem.minValueLevel !== undefined && pageItem.maxValueLevel !== undefined) {
|
|
min_Level = pageItem.minValueLevel;
|
|
max_Level = pageItem.maxValueLevel;
|
|
val = Math.trunc(scale(getState(pageItem.id + '.ACTUAL').val, pageItem.minValueLevel, pageItem.maxValueLevel, 100, 0));
|
|
}
|
|
|
|
let icon_up = Icons.GetIcon('arrow-up');
|
|
let icon_stop = Icons.GetIcon('stop');
|
|
let icon_down = Icons.GetIcon('arrow-down');
|
|
|
|
if (Debug) log('pageItem.id: ' + getState(pageItem.id + '.ACTUAL').val, 'info');
|
|
if (Debug) log('min_Level: ' + min_Level, 'info');
|
|
if (Debug) log('max_Level: ' + max_Level, 'info');
|
|
|
|
let icon_up_status = 'enable';
|
|
let icon_down_status = 'enable';
|
|
|
|
let tempVal: number = getState(pageItem.id + '.ACTUAL').val;
|
|
|
|
//Disabled Status while bug in updating origin adapter data points of lift values
|
|
//let icon_up_status = tempVal === min_Level ? 'disable' : 'enable';
|
|
let icon_stop_status = 'enable';
|
|
if (tempVal === min_Level || tempVal === max_Level || checkBlindActive === false) {
|
|
//icon_stop_status = 'disable';
|
|
}
|
|
//let icon_down_status = tempVal === max_Level ? 'disable' : 'enable';
|
|
let value = icon_up + '|' + icon_stop + '|' + icon_down + '|' + icon_up_status + '|' + icon_stop_status + '|' + icon_down_status;
|
|
|
|
if (Debug) log('CreateEntity Icon role blind ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + value, 'info');
|
|
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + value;
|
|
|
|
case 'gate':
|
|
type = 'text';
|
|
let gateState: string | undefined = undefined;
|
|
if (existsState(pageItem.id + '.ACTUAL')) {
|
|
if (getState(pageItem.id + '.ACTUAL').val == 0 || getState(pageItem.id + '.ACTUAL').val === false) {
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('garage');
|
|
iconColor = GetIconColor(pageItem, false, useColors);
|
|
gateState = findLocale('window', 'closed');
|
|
} else {
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('garage-open');
|
|
iconId = pageItem.icon2 !== undefined ? Icons.GetIcon(pageItem.icon2) : Icons.GetIcon('garage-open');
|
|
iconColor = GetIconColor(pageItem, true, useColors);
|
|
gateState = findLocale('window', 'opened');
|
|
}
|
|
}
|
|
if (gateState == undefined) {
|
|
throw new Error(`Missing ${pageItem.id}.ACTUAL for type ${type}`);
|
|
}
|
|
|
|
if (Debug) log('CreateEntity Icon role gate ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + gateState, 'info');
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + gateState;
|
|
|
|
case 'door':
|
|
case 'window':
|
|
type = 'text';
|
|
let windowState;
|
|
|
|
if (existsState(pageItem.id + '.ACTUAL')) {
|
|
if (getState(pageItem.id + '.ACTUAL').val) {
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : role == 'door' ? Icons.GetIcon('door-open') : Icons.GetIcon('window-open-variant');
|
|
iconColor = GetIconColor(pageItem, true, useColors);
|
|
windowState = findLocale('window', 'opened');
|
|
} else {
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : role == 'door' ? Icons.GetIcon('door-closed') : Icons.GetIcon('window-closed-variant');
|
|
iconId = pageItem.icon2 !== undefined ? Icons.GetIcon(pageItem.icon2) : iconId;
|
|
iconColor = GetIconColor(pageItem, false, useColors);
|
|
windowState = findLocale('window', 'closed');
|
|
}
|
|
}
|
|
|
|
if (Debug) log('CreateEntity Icon role door/window ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + windowState, 'info');
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + windowState;
|
|
|
|
case 'motion':
|
|
type = 'text';
|
|
if (val === true) {
|
|
optVal = 'On';
|
|
iconColor = GetIconColor(pageItem, true, useColors);
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('motion-sensor');
|
|
} else {
|
|
optVal = 'Off';
|
|
iconColor = GetIconColor(pageItem, false, useColors);
|
|
iconId = pageItem.icon2 !== undefined ? Icons.GetIcon(pageItem.icon2) : Icons.GetIcon('motion-sensor');
|
|
}
|
|
|
|
if (Debug) log('CreateEntity Icon role motion ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal, 'info');
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal;
|
|
|
|
case 'info':
|
|
|
|
case 'humidity':
|
|
|
|
case 'illuminance':
|
|
|
|
case 'temperature':
|
|
|
|
case 'value.temperature':
|
|
|
|
case 'value.humidity':
|
|
|
|
case 'sensor.door':
|
|
|
|
case 'sensor.window':
|
|
|
|
case 'thermostat':
|
|
type = 'text';
|
|
|
|
iconId =
|
|
pageItem.icon !== undefined
|
|
? Icons.GetIcon(pageItem.icon)
|
|
: role == 'temperature' || role == 'value.temperature' || role == 'thermostat'
|
|
? Icons.GetIcon('thermometer')
|
|
: Icons.GetIcon('information-outline');
|
|
|
|
let unit = '';
|
|
optVal = '0';
|
|
|
|
if (existsState(pageItem.id + '.ON_ACTUAL')) {
|
|
optVal = getState(pageItem.id + '.ON_ACTUAL').val;
|
|
unit = pageItem.unit !== undefined ? pageItem.unit : GetUnitOfMeasurement(pageItem.id + '.ON_ACTUAL');
|
|
} else if (existsState(pageItem.id + '.ACTUAL')) {
|
|
optVal = getState(pageItem.id + '.ACTUAL').val;
|
|
unit = pageItem.unit !== undefined ? pageItem.unit : GetUnitOfMeasurement(pageItem.id + '.ACTUAL');
|
|
}
|
|
|
|
if (role == 'temperature' || role == 'value.temperature' || role == 'thermostat') {
|
|
iconId = determinePageItemStatusIcon(pageItem, val, iconId, ['snowflake-thermometer', 'sun-thermometer', 'thermometer']);
|
|
} else if (role == 'humidity' || role == 'value.humidity') {
|
|
iconId = determinePageItemStatusIcon(pageItem, val, iconId, ['water-off', 'water-percent-alert', 'water-percent']);
|
|
}
|
|
|
|
iconColor = GetIconColor(pageItem, parseInt(optVal), useColors);
|
|
|
|
if (pageItem.colorScale != undefined) {
|
|
let iconvalmin = pageItem.colorScale.val_min != undefined ? pageItem.colorScale.val_min : 0;
|
|
let iconvalmax = pageItem.colorScale.val_max != undefined ? pageItem.colorScale.val_max : 100;
|
|
let iconvalbest = pageItem.colorScale.val_best != undefined ? pageItem.colorScale.val_best : iconvalmin;
|
|
let valueScale = val;
|
|
|
|
if (iconvalmin == 0 && iconvalmax == 1) {
|
|
iconColor = !pageItem.id || getState(pageItem.id).val == 1 ? rgb_dec565(colorScale0) : rgb_dec565(colorScale10);
|
|
} else {
|
|
if (iconvalbest == iconvalmin) {
|
|
valueScale = scale(valueScale, iconvalmin, iconvalmax, 10, 0);
|
|
} else {
|
|
if (valueScale < iconvalbest) {
|
|
valueScale = scale(valueScale, iconvalmin, iconvalbest, 0, 10);
|
|
} else if (valueScale > iconvalbest || iconvalbest != iconvalmin) {
|
|
valueScale = scale(valueScale, iconvalbest, iconvalmax, 10, 0);
|
|
} else {
|
|
valueScale = scale(valueScale, iconvalmin, iconvalmax, 10, 0);
|
|
}
|
|
}
|
|
let valueScaletemp = Math.round(valueScale).toFixed();
|
|
iconColor = HandleColorScale(valueScaletemp);
|
|
}
|
|
}
|
|
|
|
if (existsState(pageItem.id + '.USERICON')) {
|
|
iconId = Icons.GetIcon(getState(pageItem.id + '.USERICON').val);
|
|
if (Debug) log('iconid von ' + pageItem.id + '.USERICON: ' + getState(pageItem.id + '.USERICON').val, 'info');
|
|
RegisterEntityWatcher(pageItem.id + '.USERICON');
|
|
}
|
|
|
|
if (pageItem.useValue) {
|
|
if (pageItem.fontSize != undefined) {
|
|
if (pageItem.suffixName == undefined) {
|
|
iconId = optVal + '¬' + pageItem.fontSize;
|
|
} else {
|
|
iconId = optVal + pageItem.suffixName + '¬' + pageItem.fontSize;
|
|
}
|
|
} else {
|
|
iconId = optVal;
|
|
}
|
|
}
|
|
|
|
if (existsState(pageItem.id + '.ACTUAL') && (pageItem.icon2 != undefined || pageItem.useValue)) {
|
|
// Read Alias Datapoint Objectdata
|
|
let obj = getObject(pageItem.id + ".ACTUAL");
|
|
// Read origin Datapoint Objectdata
|
|
if (existsState(obj.common.alias.id)) {
|
|
let obj2 = getObject(obj.common.alias.id);
|
|
// Register Origin Datapoint
|
|
RegisterEntityWatcher(obj.common.alias.id);
|
|
// Check Data-Type (This must be boolean)
|
|
if (obj2.type === 'state' && obj2.common.type == "boolean") {
|
|
if (Debug) log(getState(obj.common.alias.id).val, 'info');
|
|
if (getState(obj.common.alias.id).val) {
|
|
if (!pageItem.useValue) iconId = pageItem.icon != undefined ? Icons.GetIcon(pageItem.icon) : iconId;
|
|
iconColor = pageItem.onColor != undefined ? rgb_dec565(pageItem.onColor) : iconColor;
|
|
} else {
|
|
if (!pageItem.useValue) iconId = pageItem.icon2 != undefined ? Icons.GetIcon(pageItem.icon2) : iconId;
|
|
iconColor = pageItem.offColor != undefined ? rgb_dec565(pageItem.offColor) : iconColor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (existsState(pageItem.id + '.COLORDEC')) {
|
|
if (Debug) log('iconcolor von ' + pageItem.id + '.COLORDEC: ' + getState(pageItem.id + '.COLORDEC').val, 'info');
|
|
RegisterEntityWatcher(pageItem.id + '.COLORDEC');
|
|
iconColor = getState(pageItem.id + '.COLORDEC').val;
|
|
}
|
|
|
|
if (Debug) log('CreateEntity Icon role info, humidity, temperature, value.temperature, value.humidity, sensor.door, sensor.window, thermostat', 'info');
|
|
if (Debug) log('CreateEntity ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal + ' ' + unit, 'info');
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal + ' ' + unit;
|
|
|
|
case 'buttonSensor':
|
|
type = 'input_sel';
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('gesture-tap-button');
|
|
iconColor = GetIconColor(pageItem, true, useColors);
|
|
let inSelText = pageItem.buttonText !== undefined ? pageItem.buttonText : 'PRESS';
|
|
|
|
if (Debug) log('CreateEntity Icon role buttonSensor ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + inSelText, 'info');
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + inSelText;
|
|
|
|
case 'button':
|
|
type = 'button';
|
|
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('gesture-tap-button');
|
|
iconColor = GetIconColor(pageItem, true, useColors);
|
|
if (val === false) {
|
|
iconColor = GetIconColor(pageItem, false, useColors);
|
|
}
|
|
|
|
let buttonText = pageItem.buttonText !== undefined ? pageItem.buttonText : 'PRESS';
|
|
|
|
if (Debug) log('CreateEntity Icon role button ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + buttonText, 'info');
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + buttonText;
|
|
case 'value.time':
|
|
case 'level.timer':
|
|
type = 'timer';
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('gesture-tap-button');
|
|
iconColor = GetIconColor(pageItem, true, useColors);
|
|
let timerText = pageItem.buttonText !== undefined ? pageItem.buttonText : 'PRESS';
|
|
|
|
if (existsState(pageItem.id + '.STATE')) {
|
|
val = getState(pageItem.id + '.STATE').val;
|
|
RegisterEntityWatcher(pageItem.id + '.STATE');
|
|
}
|
|
|
|
if (Debug) log('CreateEntity Icon role level.timer ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + timerText, 'info');
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + timerText;
|
|
|
|
case 'value.alarmtime':
|
|
type = 'timer';
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('timer-outline');
|
|
let alarmtimerText = pageItem.buttonText !== undefined ? pageItem.buttonText : 'PRESS';
|
|
|
|
if (existsState(pageItem.id + '.STATE')) {
|
|
val = getState(pageItem.id + '.STATE').val;
|
|
iconColor = val == 'paused' ? rgb_dec565(colorScale10) : rgb_dec565(colorScale0);
|
|
}
|
|
|
|
if (existsState(pageItem.id + '.ACTUAL')) {
|
|
let timer_actual = getState(pageItem.id + '.ACTUAL').val;
|
|
name = ('0' + String(Math.floor(timer_actual / 60))).slice(-2) + ':' + ('0' + String(timer_actual % 60)).slice(-2);
|
|
}
|
|
|
|
if (Debug) log('CreateEntity Icon role value.alarmtime ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + alarmtimerText + ' ' + val, 'info');
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + alarmtimerText;
|
|
|
|
case 'level.mode.fan':
|
|
type = 'fan';
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('fan');
|
|
optVal = '0';
|
|
|
|
if (val === true || val === 'true') {
|
|
optVal = '1';
|
|
iconColor = GetIconColor(pageItem, true, useColors);
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, false, useColors);
|
|
if (pageItem.icon !== undefined) {
|
|
if (pageItem.icon2 !== undefined) {
|
|
iconId = iconId2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Debug) log('CreateEntity Icon role level.mode.fan ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal, 'info');
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + optVal;
|
|
|
|
case 'lock':
|
|
type = 'button';
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('lock');
|
|
iconColor = GetIconColor(pageItem, true, useColors);
|
|
let lockState;
|
|
|
|
if (existsState(pageItem.id + '.ACTUAL')) {
|
|
if (getState(pageItem.id + '.ACTUAL').val) {
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('lock');
|
|
iconColor = GetIconColor(pageItem, true, useColors);
|
|
lockState = findLocale('lock', 'UNLOCK');
|
|
} else {
|
|
iconId = pageItem.icon2 !== undefined ? Icons.GetIcon(pageItem.icon2) : Icons.GetIcon('lock-open-variant');
|
|
iconColor = GetIconColor(pageItem, false, useColors);
|
|
lockState = findLocale('lock', 'LOCK');
|
|
}
|
|
lockState = pageItem.buttonText !== undefined ? pageItem.buttonText : lockState;
|
|
}
|
|
|
|
if (Debug) log('CreateEntity Icon role lock ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + lockState, 'info');
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + lockState;
|
|
|
|
case 'slider':
|
|
type = 'number';
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('plus-minus-variant');
|
|
|
|
let minValueSlider: number = pageItem.minValue !== undefined ? pageItem.minValue : 0;
|
|
let maxValueSlider: number = pageItem.maxValue !== undefined ? pageItem.maxValue : 100;
|
|
|
|
iconColor = GetIconColor(pageItem, false, useColors);
|
|
|
|
if (pageItem.colorScale != undefined) {
|
|
let iconvalmin = pageItem.colorScale.val_min != undefined ? pageItem.colorScale.val_min : 0;
|
|
let iconvalmax = pageItem.colorScale.val_max != undefined ? pageItem.colorScale.val_max : 100;
|
|
let iconvalbest = pageItem.colorScale.val_best != undefined ? pageItem.colorScale.val_best : iconvalmin;
|
|
let valueScale = val;
|
|
|
|
if (iconvalmin == 0 && iconvalmax == 1) {
|
|
iconColor = !pageItem.id || getState(pageItem.id).val == 1 ? rgb_dec565(colorScale0) : rgb_dec565(colorScale10);
|
|
} else {
|
|
if (iconvalbest == iconvalmin) {
|
|
valueScale = scale(valueScale, iconvalmin, iconvalmax, 10, 0);
|
|
} else {
|
|
if (valueScale < iconvalbest) {
|
|
valueScale = scale(valueScale, iconvalmin, iconvalbest, 0, 10);
|
|
} else if (valueScale > iconvalbest || iconvalbest != iconvalmin) {
|
|
valueScale = scale(valueScale, iconvalbest, iconvalmax, 10, 0);
|
|
} else {
|
|
valueScale = scale(valueScale, iconvalmin, iconvalmax, 10, 0);
|
|
}
|
|
}
|
|
let valueScaletemp = Math.round(valueScale).toFixed();
|
|
iconColor = HandleColorScale(valueScaletemp);
|
|
}
|
|
}
|
|
|
|
if (existsState(pageItem.id + '.USERICON')) {
|
|
iconId = Icons.GetIcon(getState(pageItem.id + '.USERICON').val);
|
|
if (Debug) log('iconid von ' + pageItem.id + '.USERICON: ' + getState(pageItem.id + '.USERICON').val, 'info');
|
|
RegisterEntityWatcher(pageItem.id + '.USERICON');
|
|
}
|
|
|
|
if (Debug)
|
|
log(
|
|
'CreateEntity Icon role slider ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + val + '|' + minValueSlider + '|' + maxValueSlider,
|
|
'info'
|
|
);
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + val + '|' + minValueSlider + '|' + maxValueSlider;
|
|
|
|
case 'volumeGroup':
|
|
case 'volume':
|
|
type = 'number';
|
|
iconColor = GetIconColor(pageItem, false, useColors);
|
|
if (existsState(pageItem.id + '.MUTE')) {
|
|
getState(pageItem.id + '.MUTE').val ? (iconColor = GetIconColor(pageItem, false, useColors)) : (iconColor = GetIconColor(pageItem, true, useColors));
|
|
RegisterEntityWatcher(pageItem.id + '.MUTE');
|
|
}
|
|
|
|
if (val > 0 && val <= 33 && !getState(pageItem.id + '.MUTE').val) {
|
|
iconId = Icons.GetIcon('volume-low');
|
|
} else if (val > 33 && val <= 66 && !getState(pageItem.id + '.MUTE').val) {
|
|
iconId = Icons.GetIcon('volume-medium');
|
|
} else if (val > 66 && val <= 100 && !getState(pageItem.id + '.MUTE').val) {
|
|
iconId = Icons.GetIcon('volume-high');
|
|
} else {
|
|
iconId = Icons.GetIcon('volume-mute');
|
|
}
|
|
|
|
let minValueVolume: number = pageItem.minValue !== undefined ? pageItem.minValue : 0;
|
|
let maxValueVolume: number = pageItem.maxValue !== undefined ? pageItem.maxValue : 100;
|
|
|
|
if (Debug)
|
|
log(
|
|
'CreateEntity Icon role volumeGroup/volume ~' +
|
|
type +
|
|
'~' +
|
|
placeId +
|
|
'~' +
|
|
iconId +
|
|
'~' +
|
|
iconColor +
|
|
'~' +
|
|
name +
|
|
'~' +
|
|
val +
|
|
'|' +
|
|
minValueVolume +
|
|
'|' +
|
|
maxValueVolume,
|
|
'info'
|
|
);
|
|
return '~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + name + '~' + val + '|' + minValueVolume + '|' + maxValueVolume;
|
|
|
|
case 'warning':
|
|
type = 'text';
|
|
iconId = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('alert-outline');
|
|
iconColor = getState([pageItem.id, '.LEVEL'].join('')).val;
|
|
let itemName = getState([pageItem.id, '.TITLE'].join('')).val;
|
|
let itemInfo = getState([pageItem.id, '.INFO'].join('')).val;
|
|
|
|
RegisterEntityWatcher(pageItem.id + '.LEVEL');
|
|
RegisterEntityWatcher(pageItem.id + '.INFO');
|
|
|
|
if (pageItem.useValue) {
|
|
iconId = itemInfo;
|
|
}
|
|
|
|
if (Debug) log('CreateEntity Icon role warning ~' + type + '~' + placeId + '~' + iconId + '~' + iconColor + '~' + itemName + '~' + itemInfo, 'info');
|
|
return '~' + type + '~' + itemName + '~' + iconId + '~' + iconColor + '~' + itemName + '~' + itemInfo;
|
|
|
|
case 'timeTable':
|
|
type = 'text';
|
|
let itemFahrzeug: string = getState(pageItem.id + '.VEHICLE').val;
|
|
let itemUhrzeit: string = getState(pageItem.id + '.ACTUAL').val;
|
|
let itemRichtung: string = getState(pageItem.id + '.DIRECTION').val;
|
|
let itemVerspaetung: boolean = getState(pageItem.id + '.DELAY').val;
|
|
|
|
if (Icons.GetIcon(itemFahrzeug) != '') {
|
|
iconId = Icons.GetIcon(itemFahrzeug);
|
|
} else {
|
|
iconId = '';
|
|
}
|
|
|
|
iconColor = !itemVerspaetung ? rgb_dec565(colorScale0) : rgb_dec565(colorScale10);
|
|
|
|
if (Debug) log('CreateEntity Icon role timeTable ~' + type + '~' + itemRichtung + '~' + iconId + '~' + iconColor + '~' + itemRichtung + '~' + itemUhrzeit, 'info');
|
|
return '~' + type + '~' + itemRichtung + '~' + iconId + '~' + iconColor + '~' + itemRichtung + '~' + itemUhrzeit;
|
|
|
|
default:
|
|
if (Debug) log('CreateEntity Icon keine passende Rolle gefunden', 'warn');
|
|
return '~delete~~~~~';
|
|
}
|
|
}
|
|
if (Debug) log('CreateEntity return ~delete~~~~~', 'info');
|
|
return '~delete~~~~~';
|
|
} catch (err: any) {
|
|
if (err.message == "Cannot read properties of undefined (reading 'common')") {
|
|
log('Found Alias without channel: ' + pageItem.id + '! Please correct the Alias', 'warn');
|
|
return '';
|
|
} else {
|
|
log('error at function CreateEntity: ' + err.message, 'warn');
|
|
return '';
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Finds the locale string for a given controls object and state.
|
|
*
|
|
* This function retrieves the locale string based on the specified controls object and state.
|
|
*
|
|
* @function findLocale
|
|
* @param {string} controlsObject - The controls object identifier.
|
|
* @param {string} controlsState - The controls state identifier.
|
|
* @returns {string} The locale string for the specified controls object and state.
|
|
*/
|
|
function findLocale (controlsObject: string, controlsState: string): string {
|
|
if (!existsState(NSPanel_Path + 'Config.locale')) {
|
|
if (Debug) {
|
|
log('findLocaleServMenu missing object: ' + NSPanel_Path + 'Config.locale' + ' -> ' + controlsState, 'warn');
|
|
}
|
|
return controlsState;
|
|
}
|
|
if (!existsState(NSPanel_Path + 'NSPanel_locales_json')) {
|
|
if (Debug) {
|
|
log('findLocaleServMenu missing object: ' + NSPanel_Path + 'NSPanel_locales_json' + ' -> ' + controlsState, 'warn');
|
|
}
|
|
return controlsState;
|
|
}
|
|
|
|
const locale = getState(NSPanel_Path + 'Config.locale').val;
|
|
const strJson = getState(NSPanel_Path + 'NSPanel_locales_json').val;
|
|
|
|
try {
|
|
const obj = JSON.parse(strJson);
|
|
const strLocale = obj[controlsObject][controlsState][locale];
|
|
|
|
if (strLocale != undefined) {
|
|
if (Debug) {
|
|
log('findLocale: ' + controlsObject + ' - ' + controlsState + ' - ' + strLocale, 'info');
|
|
}
|
|
return strLocale;
|
|
} else {
|
|
if (Debug) {
|
|
log('findLocale missing locale: ' + controlsObject + ' - ' + controlsState, 'info');
|
|
}
|
|
return controlsState;
|
|
}
|
|
} catch (err: any) {
|
|
if (err.message.substring(0, 35) == 'Cannot read properties of undefined') {
|
|
log('function findLocale: missing translation: ' + controlsObject + ' - ' + controlsState, 'info');
|
|
} else {
|
|
log('error at function findLocale: ' + controlsObject + ' - ' + controlsState + ' : ' + err.message, 'warn');
|
|
}
|
|
return controlsState;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Finds the locale string for a given service menu controls state.
|
|
*
|
|
* This function retrieves the locale string based on the specified service menu controls state.
|
|
*
|
|
* @function findLocaleServMenu
|
|
* @param {string} controlsState - The service menu controls state identifier.
|
|
* @returns {string} The locale string for the specified service menu controls state.
|
|
*/
|
|
function findLocaleServMenu (controlsState: string): string {
|
|
if (!existsState(NSPanel_Path + 'Config.locale')) {
|
|
if (Debug) {
|
|
log('findLocaleServMenu missing object: ' + NSPanel_Path + 'Config.locale' + ' -> ' + controlsState, 'warn');
|
|
}
|
|
return controlsState;
|
|
}
|
|
if (!existsState(NSPanel_Path + 'NSPanel_locales_service_json')) {
|
|
if (Debug) {
|
|
log('findLocaleServMenu missing object: ' + NSPanel_Path + 'NSPanel_locales_service_json' + ' -> ' + controlsState, 'warn');
|
|
}
|
|
return controlsState;
|
|
}
|
|
|
|
const locale = getState(NSPanel_Path + 'Config.locale').val;
|
|
const strJson = getState(NSPanel_Path + 'NSPanel_locales_service_json').val;
|
|
|
|
try {
|
|
const obj = JSON.parse(strJson);
|
|
const strLocale = obj[controlsState][locale];
|
|
|
|
if (strLocale != undefined) {
|
|
if (Debug) {
|
|
log('findLocaleServMenu: ' + controlsState + ' - ' + locale + ' -> ' + strLocale, 'info');
|
|
}
|
|
return strLocale;
|
|
} else {
|
|
if (obj[controlsState]['en-US'] != undefined) {
|
|
if (Debug) {
|
|
log('findLocaleServMenu: ' + controlsState + ' - ' + locale + ' -> ' + obj[controlsState]['en-US'], 'info');
|
|
}
|
|
return obj[controlsState]['en-US'];
|
|
} else {
|
|
if (Debug) {
|
|
log('findLocaleServMenu missing entry: ' + controlsState + ' - en-US', 'info');
|
|
}
|
|
return controlsState;
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
if (err.message.substring(0, 35) == 'Cannot read properties of undefined') {
|
|
log('function findLocaleServMenu: missing translation: ' + controlsState + ' - ' + locale, 'info');
|
|
} else {
|
|
log('error at function findLocaleServMenu: ' + controlsState + ' - ' + locale + ' : ' + err.message, 'warn');
|
|
}
|
|
return controlsState;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves the icon color for a given page item based on its value and color usage settings.
|
|
*
|
|
* This function determines the appropriate icon color for the specified page item, considering its value and whether colors should be used.
|
|
*
|
|
* @function GetIconColor
|
|
* @param {PageItem} pageItem - The page item configuration.
|
|
* @param {boolean | number} value - The value associated with the page item.
|
|
* @param {boolean} useColors - A flag indicating whether colors should be used.
|
|
* @returns {number} The color code for the icon.
|
|
*/
|
|
function GetIconColor (pageItem: PageItem, value: boolean | number, useColors: boolean): number {
|
|
try {
|
|
// dimmer
|
|
if ((pageItem.useColor || useColors) && pageItem.interpolateColor && typeof value === 'number') {
|
|
let maxValue = pageItem.maxValueBrightness !== undefined ? pageItem.maxValueBrightness : 100;
|
|
let minValue = pageItem.minValueBrightness !== undefined ? pageItem.minValueBrightness : 0;
|
|
if (pageItem.maxValue !== undefined) maxValue = pageItem.maxValue;
|
|
if (pageItem.minValue !== undefined) minValue = pageItem.minValue;
|
|
value = value > maxValue ? maxValue : value;
|
|
value = value < minValue ? minValue : value;
|
|
|
|
return rgb_dec565(
|
|
Interpolate(
|
|
pageItem.offColor !== undefined ? pageItem.offColor : config.defaultOffColor,
|
|
pageItem.onColor !== undefined ? pageItem.onColor : config.defaultOnColor,
|
|
scale(100 - value, minValue, maxValue, 0, 1)
|
|
)
|
|
);
|
|
}
|
|
|
|
if (
|
|
((pageItem.useColor || useColors) && typeof value === 'boolean' && value) ||
|
|
(typeof value === 'number' && value > (pageItem.minValueBrightness !== undefined ? pageItem.minValueBrightness : 0))
|
|
) {
|
|
return rgb_dec565(pageItem.onColor !== undefined ? pageItem.onColor : config.defaultOnColor);
|
|
}
|
|
|
|
return rgb_dec565(pageItem.offColor !== undefined ? pageItem.offColor : config.defaultOffColor);
|
|
} catch (err: any) {
|
|
log('error at function GetIconColor: ' + err.message, 'warn');
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registers an entity watcher for the specified entity ID.
|
|
*
|
|
* This function sets up a watcher to monitor changes in the specified entity and perform actions when changes occur.
|
|
*
|
|
* @function RegisterEntityWatcher
|
|
* @param {string} id - The ID of the entity to watch.
|
|
*/
|
|
function RegisterEntityWatcher (id: string): void {
|
|
try {
|
|
if (subscriptions.hasOwnProperty(id)) {
|
|
return;
|
|
}
|
|
|
|
subscriptions[id] = on({id: id, change: 'any'}, (obj) => {
|
|
//@ts-ignore
|
|
if (obj.oldState && obj.oldState.val === obj.state.val && obj.state.ack) {
|
|
return;
|
|
}
|
|
if (pageId == -1 && config.button1.page) {
|
|
SendToPanel({payload: GeneratePageElements(config.button1.page)});
|
|
}
|
|
if (pageId == -2 && config.button2.page) {
|
|
SendToPanel({payload: GeneratePageElements(config.button2.page)});
|
|
}
|
|
if (activePage !== undefined) {
|
|
SendToPanel({payload: GeneratePageElements(activePage!)});
|
|
}
|
|
});
|
|
} catch (err: any) {
|
|
log('error at function RegisterEntityWatcher: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registers a detailed entity watcher for the specified entity ID and page item.
|
|
*
|
|
* This function sets up a watcher to monitor changes in the specified entity and perform actions when changes occur,
|
|
* considering the page item configuration, popup type, and place ID.
|
|
*
|
|
* @function RegisterDetailEntityWatcher
|
|
* @param {string} id - The ID of the entity to watch.
|
|
* @param {PageItem} pageItem - The page item configuration.
|
|
* @param {NSPanel.PopupType} type - The type of popup to display.
|
|
* @param {number | undefined} placeId - The place ID associated with the entity, if applicable.
|
|
*/
|
|
function RegisterDetailEntityWatcher (id: string, pageItem: PageItem, type: NSPanel.PopupType, placeId: number | undefined): void {
|
|
try {
|
|
if (subscriptions.hasOwnProperty(id)) {
|
|
return;
|
|
}
|
|
|
|
if (Debug) log('id: ' + id + ' - pageItem: ' + JSON.stringify(pageItem) + ' - type: ' + type + ' - placeId: ' + placeId, 'info');
|
|
|
|
subscriptions[id] = on({id: id, change: 'any'}, (obj) => {
|
|
//@ts-ignore
|
|
if (obj.oldState && obj.oldState.val === obj.state.val && obj.state.ack) {
|
|
return;
|
|
}
|
|
SendToPanel(GenerateDetailPage(type, undefined, pageItem, placeId));
|
|
});
|
|
} catch (err: any) {
|
|
log('error at function RegisterDetailEntityWatcher: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves the unit of measurement for the specified entity ID.
|
|
*
|
|
* This function returns the unit of measurement associated with the given entity ID.
|
|
*
|
|
* @function GetUnitOfMeasurement
|
|
* @param {string} id - The ID of the entity.
|
|
* @returns {string} The unit of measurement for the entity.
|
|
*/
|
|
function GetUnitOfMeasurement (id: string): string {
|
|
try {
|
|
if (!existsObject(id)) return '';
|
|
|
|
let obj = getObject(id);
|
|
if (typeof obj.common.unit !== 'undefined') {
|
|
return obj.common.unit;
|
|
}
|
|
|
|
if (typeof obj.common.alias !== 'undefined' && typeof obj.common.alias.id !== 'undefined') {
|
|
return GetUnitOfMeasurement(obj.common.alias.id);
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function GetUnitOfMeasurement: ' + err.message, 'warn');
|
|
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Generates the payload for a thermostat page on the NSPanel.
|
|
*
|
|
* This function creates and returns the payload required to display a thermostat page on the NSPanel.
|
|
*
|
|
* @function GenerateThermoPage
|
|
* @param {NSPanel.PageThermo} page - The thermostat page configuration.
|
|
* @returns {NSPanel.Payload[]} The payload array for the thermostat page.
|
|
*/
|
|
function GenerateThermoPage (page: NSPanel.PageThermo): NSPanel.Payload[] {
|
|
try {
|
|
UnsubscribeWatcher();
|
|
let id = page.items[0].id;
|
|
let out_msgs: NSPanel.Payload[] = [];
|
|
|
|
// Leave the display on if the alwaysOnDisplay parameter is specified (true)
|
|
if (page.type == 'cardThermo' && pageCounter == 0 && page.items[0].alwaysOnDisplay != undefined) {
|
|
out_msgs.push({payload: 'pageType~cardThermo'});
|
|
if (page.items[0].alwaysOnDisplay != undefined) {
|
|
if (page.items[0].alwaysOnDisplay) {
|
|
pageCounter = 1;
|
|
if (id && existsObject(id) && alwaysOn == false) {
|
|
alwaysOn = true;
|
|
SendToPanel({payload: 'timeout~0'});
|
|
subscribePowerSubscriptions(id);
|
|
}
|
|
}
|
|
}
|
|
} else if (id && existsObject(id) && page.type == 'cardThermo' && pageCounter == 1) {
|
|
subscribePowerSubscriptions(id);
|
|
|
|
} else {
|
|
out_msgs.push({payload: 'pageType~cardThermo'});
|
|
}
|
|
|
|
// ioBroker
|
|
if (id && existsObject(id)) {
|
|
let o = getObject(id);
|
|
let name = page.heading !== undefined ? page.heading : o.common.name && typeof o.common.name === 'object' ? o.common.name.de : o.common.name;
|
|
let currentTemp = 0;
|
|
if (existsState(id + '.ACTUAL')) {
|
|
currentTemp = Math.round(parseFloat(getState(id + '.ACTUAL').val) * 10) / 10;
|
|
}
|
|
|
|
let minTemp = page.items[0].minValue !== undefined ? page.items[0].minValue : 50; //Min Temp 5°C
|
|
let maxTemp = page.items[0].maxValue !== undefined ? page.items[0].maxValue : 300; //Max Temp 30°C
|
|
let stepTemp = page.items[0].stepValue !== undefined ? page.items[0].stepValue : 5; //Default 0,5° Schritte
|
|
|
|
let destTemp = 0;
|
|
if (existsState(id + '.SET')) {
|
|
// using minValue, if .SET is null (e.g. for tado AWAY or tado is off)
|
|
let setValue = getState(id + '.SET').val;
|
|
if (setValue == null) {
|
|
setValue = minTemp;
|
|
}
|
|
|
|
destTemp = setValue.toFixed(2) * 10;
|
|
}
|
|
let statusStr: String = 'MANU';
|
|
|
|
if (existsState(id + '.MODE') && getState(id + '.MODE').val != null) {
|
|
if (getState(id + '.MODE').val === 1) {
|
|
statusStr = 'MANU';
|
|
} else {
|
|
statusStr = 'AUTO';
|
|
}
|
|
}
|
|
|
|
//Add attributes if defined in alias
|
|
let i_list = Array.prototype.slice.apply($('[state.id="' + id + '.*"]'));
|
|
let bt = ['~~~~', '~~~~', '~~~~', '~~~~', '~~~~', '~~~~', '~~~~', '~~~~', '~~~~'];
|
|
|
|
let tempIcon: string = '';
|
|
|
|
let iconsObj: any;
|
|
if (page.items[0].customIcons != undefined) {
|
|
iconsObj = page.items[0].customIcons[0];
|
|
}
|
|
|
|
let tempIconOnColor: number = 35921;
|
|
let tempIconOffColor: number = 35921;
|
|
|
|
if (i_list.length - 3 != 0) {
|
|
let i = 0;
|
|
switch (o.common.role as NSPanel.roles) {
|
|
case 'thermostat':
|
|
{
|
|
if (existsState(id + '.AUTOMATIC') && getState(id + '.AUTOMATIC').val != null) {
|
|
if (getState(id + '.AUTOMATIC').val) {
|
|
bt[i++] = Icons.GetIcon('alpha-a-circle') + '~' + rgb_dec565(On) + '~1~' + 'AUTT' + '~';
|
|
statusStr = 'AUTO';
|
|
} else {
|
|
bt[i++] = Icons.GetIcon('alpha-a-circle') + '~33840~1~' + 'AUTT' + '~';
|
|
}
|
|
}
|
|
if (existsState(id + '.MANUAL') && getState(id + '.MANUAL').val != null) {
|
|
if (getState(id + '.MANUAL').val) {
|
|
bt[i++] = Icons.GetIcon('alpha-m-circle') + '~' + rgb_dec565(On) + '~1~' + 'MANT' + '~';
|
|
statusStr = 'MANU';
|
|
} else {
|
|
bt[i++] = Icons.GetIcon('alpha-m-circle') + '~33840~1~' + 'MANT' + '~';
|
|
}
|
|
}
|
|
if (existsState(id + '.PARTY') && getState(id + '.PARTY').val != null) {
|
|
if (getState(id + '.PARTY').val) {
|
|
bt[i++] = Icons.GetIcon('party-popper') + '~' + rgb_dec565(On) + '~1~' + 'PART' + '~';
|
|
statusStr = 'PARTY';
|
|
} else {
|
|
bt[i++] = Icons.GetIcon('party-popper') + '~33840~1~' + 'PART' + '~';
|
|
}
|
|
}
|
|
if (existsState(id + '.VACATION') && getState(id + '.VACATION').val != null) {
|
|
if (getState(id + '.VACATION').val) {
|
|
bt[i++] = Icons.GetIcon('palm-tree') + '~' + rgb_dec565(On) + '~1~' + 'VACT' + '~';
|
|
statusStr = 'VAC';
|
|
} else {
|
|
bt[i++] = Icons.GetIcon('palm-tree') + '~33840~1~' + 'VACT' + '~';
|
|
}
|
|
}
|
|
if (existsState(id + '.BOOST') && getState(id + '.BOOST').val != null) {
|
|
if (getState(id + '.BOOST').val) {
|
|
bt[i++] = Icons.GetIcon('fast-forward-60') + '~' + rgb_dec565(On) + '~1~' + 'BOOT' + '~';
|
|
statusStr = 'BOOST';
|
|
} else {
|
|
bt[i++] = Icons.GetIcon('fast-forward-60') + '~33840~1~' + 'BOOT' + '~';
|
|
}
|
|
}
|
|
|
|
for (let i_index in i_list) {
|
|
let thermostatState = i_list[i_index].split('.');
|
|
if (
|
|
thermostatState[thermostatState.length - 1] != 'SET' &&
|
|
thermostatState[thermostatState.length - 1] != 'ACTUAL' &&
|
|
thermostatState[thermostatState.length - 1] != 'MODE'
|
|
) {
|
|
i++;
|
|
|
|
switch (thermostatState[thermostatState.length - 1]) {
|
|
case 'HUMIDITY':
|
|
if (existsState(id + '.HUMIDITY') && getState(id + '.HUMIDITY').val != null) {
|
|
if (parseInt(getState(id + '.HUMIDITY').val) < 40) {
|
|
bt[i - 1] = Icons.GetIcon('water-percent') + '~65504~1~' + 'HUM' + '~';
|
|
} else if (parseInt(getState(id + '.HUMIDITY').val) < 30) {
|
|
bt[i - 1] = Icons.GetIcon('water-percent') + '~63488~1~' + 'HUM' + '~';
|
|
} else if (parseInt(getState(id + '.HUMIDITY').val) >= 40) {
|
|
bt[i - 1] = Icons.GetIcon('water-percent') + '~2016~1~' + 'HUM' + '~';
|
|
} else if (parseInt(getState(id + '.HUMIDITY').val) > 65) {
|
|
bt[i - 1] = Icons.GetIcon('water-percent') + '~65504~1~' + 'HUM' + '~';
|
|
} else if (parseInt(getState(id + '.HUMIDITY').val) > 75) {
|
|
bt[i - 1] = Icons.GetIcon('water-percent') + '~63488~1~' + 'HUM' + '~';
|
|
}
|
|
} else i--;
|
|
break;
|
|
case 'LOWBAT':
|
|
if (existsState(id + '.LOWBAT') && getState(id + '.LOWBAT').val != null) {
|
|
if (getState(id + '.LOWBAT').val) {
|
|
bt[i - 1] = Icons.GetIcon('battery-low') + '~63488~1~' + 'LBAT' + '~';
|
|
} else {
|
|
bt[i - 1] = Icons.GetIcon('battery-high') + '~2016~1~' + 'LBAT' + '~';
|
|
}
|
|
} else i--;
|
|
break;
|
|
case 'MAINTAIN':
|
|
if (existsState(id + '.MAINTAIN') && getState(id + '.MAINTAIN').val != null) {
|
|
if (getState(id + '.MAINTAIN').val >> 0.1) {
|
|
bt[i - 1] = Icons.GetIcon('account-wrench') + '~60897~1~' + 'MAIN' + '~';
|
|
} else {
|
|
bt[i - 1] = Icons.GetIcon('account-wrench') + '~33840~1~' + 'MAIN' + '~';
|
|
}
|
|
} else i--;
|
|
break;
|
|
case 'UNREACH':
|
|
if (existsState(id + '.UNREACH') && getState(id + '.UNREACH').val != null) {
|
|
if (getState(id + '.UNREACH').val) {
|
|
bt[i - 1] = Icons.GetIcon('wifi-off') + '~63488~1~' + 'WLAN' + '~';
|
|
} else {
|
|
bt[i - 1] = Icons.GetIcon('wifi') + '~2016~1~' + 'WLAN' + '~';
|
|
}
|
|
} else i--;
|
|
break;
|
|
case 'POWER':
|
|
if (existsState(id + '.POWER') && getState(id + '.POWER').val != null) {
|
|
if (getState(id + '.POWER').val) {
|
|
bt[i - 1] = Icons.GetIcon('power-standby') + '~2016~1~' + 'POWER' + '~';
|
|
} else {
|
|
bt[i - 1] = Icons.GetIcon('power-standby') + '~33840~1~' + 'POWER' + '~';
|
|
}
|
|
} else i--;
|
|
break;
|
|
case 'ERROR':
|
|
if (existsState(id + '.ERROR') && getState(id + '.ERROR').val != null) {
|
|
if (getState(id + '.ERROR').val) {
|
|
bt[i - 1] = Icons.GetIcon('alert-circle') + '~63488~1~' + 'ERR' + '~';
|
|
} else {
|
|
bt[i - 1] = Icons.GetIcon('alert-circle') + '~33840~1~' + 'ERR' + '~';
|
|
}
|
|
} else i--;
|
|
break;
|
|
case 'WORKING':
|
|
if (existsState(id + '.WORKING') && getState(id + '.WORKING').val != null) {
|
|
if (getState(id + '.WORKING').val) {
|
|
bt[i - 1] = Icons.GetIcon('briefcase-check') + '~2016~1~' + 'WORK' + '~';
|
|
} else {
|
|
bt[i - 1] = Icons.GetIcon('briefcase-check') + '~33840~1~' + 'WORK' + '~';
|
|
}
|
|
} else i--;
|
|
break;
|
|
case 'WINDOWOPEN':
|
|
if (existsState(id + '.WINDOWOPEN') && getState(id + '.WINDOWOPEN').val != null) {
|
|
if (getState(id + '.WINDOWOPEN').val) {
|
|
bt[i - 1] = Icons.GetIcon('window-open-variant') + '~63488~1~' + 'WIN' + '~';
|
|
} else {
|
|
bt[i - 1] = Icons.GetIcon('window-closed-variant') + '~2016~1~' + 'WIN' + '~';
|
|
}
|
|
} else i--;
|
|
break;
|
|
default:
|
|
i--;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (let j = i; j < 9; j++) {
|
|
bt[j] = '~~~~';
|
|
}
|
|
}
|
|
break;
|
|
case 'airCondition':
|
|
{
|
|
if (existsState(id + '.MODE') && getState(id + '.MODE').val != null) {
|
|
let Mode = getState(id + '.MODE').val;
|
|
let States = getObject(id + '.MODE').common.states;
|
|
|
|
let iconIndex: number = 1;
|
|
for (const statekey in States) {
|
|
let stateName: string = States[statekey];
|
|
let stateKeyNumber: number = parseInt(statekey);
|
|
if (stateName == 'OFF' || stateKeyNumber > 6) {
|
|
continue;
|
|
}
|
|
if (stateKeyNumber == Mode) {
|
|
statusStr = stateName.replace('_', ' ');
|
|
}
|
|
|
|
switch (stateName) {
|
|
case 'AUTO':
|
|
if (page.items[0].iconArray !== undefined && page.items[0].iconArray[1] !== '') {
|
|
tempIcon = page.items[0].iconArray[1];
|
|
tempIconOnColor = 1024;
|
|
} else {
|
|
tempIcon = iconsObj != undefined ? iconsObj['AUTO']['iconName'] : 'air-conditioner';
|
|
tempIconOnColor = iconsObj != undefined ? rgb_dec565(iconsObj['AUTO']['iconOnColor']) : 1024;
|
|
tempIconOffColor = iconsObj != undefined ? rgb_dec565(iconsObj['AUTO']['iconOffColor']) : 35921;
|
|
}
|
|
if (stateKeyNumber == Mode) {
|
|
bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOnColor + '~1~' + 'AUTO' + '~';
|
|
} else {
|
|
//bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOffColor + '~0~' + 'AUTO' + '~'; bis HMI Fix
|
|
bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOffColor + '~1~' + 'AUTO' + '~';
|
|
}
|
|
break;
|
|
case 'COOL':
|
|
if (page.items[0].iconArray !== undefined && page.items[0].iconArray[2] !== '') {
|
|
tempIcon = page.items[0].iconArray[2];
|
|
tempIconOnColor = 11487;
|
|
} else {
|
|
tempIcon = iconsObj != undefined ? iconsObj['COOL']["iconName"] : 'snowflake';
|
|
tempIconOnColor = iconsObj != undefined ? rgb_dec565(iconsObj['COOL']["iconOnColor"]) : 11487;
|
|
tempIconOffColor = iconsObj != undefined ? rgb_dec565(iconsObj['COOL']['iconOffColor']) : 35921;
|
|
}
|
|
if (stateKeyNumber == Mode) {
|
|
bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOnColor + '~1~' + 'COOL' + '~';
|
|
} else {
|
|
//bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOffColor + '~0~' + 'COOL' + '~'; bis HMI Fix
|
|
bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOffColor + '~1~' + 'COOL' + '~';
|
|
}
|
|
break;
|
|
case 'HEAT':
|
|
if (page.items[0].iconArray !== undefined && page.items[0].iconArray[3] !== '') {
|
|
tempIcon = page.items[0].iconArray[3];
|
|
tempIconOnColor = 64512;
|
|
} else {
|
|
tempIcon = iconsObj != undefined ? iconsObj['HEAT']["iconName"] : 'fire';
|
|
tempIconOnColor = iconsObj != undefined ? rgb_dec565(iconsObj['HEAT']["iconOnColor"]) : 64512;
|
|
tempIconOffColor = iconsObj != undefined ? rgb_dec565(iconsObj['HEAT']['iconOffColor']) : 35921;
|
|
}
|
|
if (stateKeyNumber == Mode) {
|
|
bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOnColor + '~1~' + 'HEAT' + '~';
|
|
} else {
|
|
//bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOffColor + '~0~' + 'HEAT' + '~'; bis HMI Fix
|
|
bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOffColor + '~1~' + 'HEAT' + '~';
|
|
}
|
|
break;
|
|
case 'ECO':
|
|
if (page.items[0].iconArray !== undefined && page.items[0].iconArray[4] !== '') {
|
|
tempIcon = page.items[0].iconArray[4];
|
|
tempIconOnColor = 2016;
|
|
} else {
|
|
tempIcon = iconsObj != undefined ? iconsObj['ECO']["iconName"] : 'alpha-e-circle-outline';
|
|
tempIconOnColor = iconsObj != undefined ? rgb_dec565(iconsObj['ECO']["iconOnColor"]) : 2016;
|
|
tempIconOffColor = iconsObj != undefined ? rgb_dec565(iconsObj['ECO']['iconOffColor']) : 35921;
|
|
}
|
|
if (stateKeyNumber == Mode) {
|
|
bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOnColor + '~1~' + 'ECO' + '~';
|
|
} else {
|
|
//bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOffColor + '~0~' + 'ECO' + '~'; bis HMI Fix
|
|
bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOffColor + '~1~' + 'ECO' + '~';
|
|
}
|
|
break;
|
|
case 'FAN_ONLY':
|
|
if (page.items[0].iconArray !== undefined && page.items[0].iconArray[5] !== '') {
|
|
tempIcon = page.items[0].iconArray[5];
|
|
tempIconOnColor = 11487;
|
|
} else {
|
|
tempIcon = iconsObj != undefined ? iconsObj['FAN_ONLY']['iconName'] : 'fan';
|
|
tempIconOnColor = iconsObj != undefined ? rgb_dec565(iconsObj['FAN_ONLY']['iconOnColor']) : 11487;
|
|
tempIconOffColor = iconsObj != undefined ? rgb_dec565(iconsObj['FAN_ONLY']['iconOffColor']) : 35921;
|
|
}
|
|
if (stateKeyNumber == Mode) {
|
|
bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOnColor + '~1~' + 'FAN_ONLY' + '~';
|
|
} else {
|
|
//bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOffColor + '~0~' + 'FAN_ONLY' + '~'; bis HMI Fix
|
|
bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOffColor + '~1~' + 'FAN_ONLY' + '~';
|
|
}
|
|
break;
|
|
case 'DRY':
|
|
if (page.items[0].iconArray !== undefined && page.items[0].iconArray[6] !== '') {
|
|
tempIcon = page.items[0].iconArray[6];
|
|
tempIconOnColor = 60897;
|
|
} else {
|
|
tempIcon = iconsObj != undefined ? iconsObj["DRY"]["iconName"] : 'water-percent';
|
|
tempIconOnColor = iconsObj != undefined ? rgb_dec565(iconsObj["DRY"]["iconOnColor"]) : 60897;
|
|
tempIconOffColor = iconsObj != undefined ? rgb_dec565(iconsObj["DRY"]["iconOffColor"]) : 35921;
|
|
}
|
|
if (stateKeyNumber == Mode) {
|
|
bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOnColor + '~1~' + 'DRY' + '~';
|
|
} else {
|
|
//bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOffColor + '~0~' + 'DRY' + '~'; // bis HMI Fix
|
|
bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOffColor + '~1~' + 'DRY' + '~';
|
|
}
|
|
break;
|
|
}
|
|
iconIndex++;
|
|
}
|
|
|
|
if (iconIndex <= 7 && existsState(id + '.ECO') && getState(id + '.ECO').val != null) {
|
|
if (page.items[0].iconArray !== undefined && page.items[0].iconArray[4] !== '') {
|
|
tempIcon = page.items[0].iconArray[4];
|
|
tempIconOnColor = 2016;
|
|
} else {
|
|
tempIcon = iconsObj != undefined ? iconsObj["ECO"]["iconName"] : 'alpha-e-circle-outline';
|
|
tempIconOnColor = iconsObj != undefined ? rgb_dec565(iconsObj["ECO"]["iconOnColor"]) : 2016;
|
|
tempIconOffColor = iconsObj != undefined ? rgb_dec565(iconsObj["ECO"]["iconOffColor"]) : 35921;
|
|
}
|
|
if (getState(id + '.ECO').val && getState(id + '.ECO').val == 1) {
|
|
bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOnColor + '~1~' + 'ECO' + '~';
|
|
statusStr = 'ECO';
|
|
} else {
|
|
//bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOffColor + '~0~' + 'ECO' + '~'; // bis HMI Fix
|
|
bt[iconIndex] = Icons.GetIcon(tempIcon) + '~' + tempIconOffColor + '~0~' + 'ECO' + '~';
|
|
}
|
|
iconIndex++;
|
|
}
|
|
|
|
if (iconIndex <= 7 && existsState(id + '.SWING') && getState(id + '.SWING').val != null) {
|
|
if (page.items[0].iconArray !== undefined && page.items[0].iconArray[7] !== '') {
|
|
tempIcon = page.items[0].iconArray[7];
|
|
tempIconOnColor = 2016;
|
|
} else {
|
|
tempIcon = iconsObj != undefined ? iconsObj["SWING"]["iconName"] : 'swap-vertical-bold';
|
|
tempIconOnColor = iconsObj != undefined ? rgb_dec565(iconsObj["SWING"]["iconOnColor"]) : 2016;
|
|
tempIconOffColor = iconsObj != undefined ? rgb_dec565(iconsObj["SWING"]["iconOffColor"]) : 35921;
|
|
}
|
|
if (getState(id + '.POWER').val && getState(id + '.SWING').val == 1) {
|
|
//0=ON oder .SWING = true
|
|
bt[7] = Icons.GetIcon(tempIcon) + '~' + tempIconOnColor + '~1~' + 'SWING' + '~';
|
|
} else {
|
|
//bt[7] = Icons.GetIcon(tempIcon) + '~' + tempIconOffColor + '~0~' + 'SWING' + '~'; // bis HMI Fix
|
|
bt[7] = Icons.GetIcon(tempIcon) + '~' + tempIconOffColor + '~1~' + 'SWING' + '~';
|
|
}
|
|
iconIndex++;
|
|
}
|
|
|
|
// Power Icon zuletzt pruefen, damit der Mode ggf. mit OFF ueberschrieben werden kann
|
|
if (existsState(id + '.POWER') && getState(id + '.POWER').val != null) {
|
|
if (page.items[0].iconArray !== undefined && page.items[0].iconArray[0] !== '') {
|
|
tempIcon = page.items[0].iconArray[0];
|
|
tempIconOnColor = 2016;
|
|
} else {
|
|
tempIcon = iconsObj != undefined ? iconsObj["POWER"]["iconName"] : 'power-standby';
|
|
tempIconOnColor = iconsObj != undefined ? rgb_dec565(iconsObj["POWER"]["iconOnColor"]) : 2016;
|
|
tempIconOffColor = iconsObj != undefined ? rgb_dec565(iconsObj["POWER"]["iconOffColor"]) : 35921;
|
|
}
|
|
if (States[Mode] == 'OFF' || !getState(id + '.POWER').val) {
|
|
//bt[0] = Icons.GetIcon(tempIcon) + '~' + tempIconOffColor + '~0~' + 'POWER' + '~'; // bis HMI Fix
|
|
bt[0] = Icons.GetIcon(tempIcon) + '~' + tempIconOffColor + '~1~' + 'POWER' + '~';
|
|
statusStr = 'OFF';
|
|
} else {
|
|
bt[0] = Icons.GetIcon(tempIcon) + '~' + tempIconOnColor + '~1~' + 'POWER' + '~';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
let destTemp2 = '';
|
|
if (page.items[0].setThermoDestTemp2 != undefined) {
|
|
let setValue2 = getState(id + '.' + page.items[0].setThermoDestTemp2).val;
|
|
destTemp2 = '' + setValue2.toFixed(2) * 10;
|
|
}
|
|
|
|
let thermoPopup = 1;
|
|
if (page.items[0].popupThermoMode1 != undefined) {
|
|
thermoPopup = 0;
|
|
}
|
|
|
|
let temperatureUnit = getState(NSPanel_Path + 'Config.temperatureUnit').val;
|
|
|
|
let icon_res = bt[0] + bt[1] + bt[2] + bt[3] + bt[4] + bt[5] + bt[6] + bt[7];
|
|
|
|
if (statusStr == 'OFF') {
|
|
stepTemp = 0;
|
|
icon_res = bt[0] + '~~~~~~~~~~~~~~~~~~~~~~~~~~~~';
|
|
thermoPopup = 1;
|
|
}
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpd~' +
|
|
name +
|
|
'~' + // Heading
|
|
getNavigationString(pageId) +
|
|
'~' + // Page Navigation
|
|
id +
|
|
'~' + // internalNameEntity
|
|
currentTemp +
|
|
temperatureUnit +
|
|
'~' + // Actual temperature (string)
|
|
destTemp +
|
|
'~' + // Target temperature (numeric without comma)
|
|
statusStr +
|
|
'~' + // Mode
|
|
minTemp +
|
|
'~' + // Thermostat min temperature
|
|
maxTemp +
|
|
'~' + // Thermostat max temperatur
|
|
stepTemp +
|
|
'~' + // Steps for Target (5°C)
|
|
icon_res + // Icons Status
|
|
findLocale('thermostat', 'Currently') +
|
|
'~' + // Identifier in front of Current room temperature
|
|
findLocale('thermostat', 'State') +
|
|
'~~' + // Bezeichner vor State
|
|
temperatureUnit +
|
|
'~' + // iconTemperature dstTempTwoTempMode
|
|
destTemp2 +
|
|
'~' + // dstTempTwoTempMode --> Wenn Wert, dann 2 Temp
|
|
thermoPopup, // PopUp
|
|
});
|
|
}
|
|
|
|
if (Debug) {
|
|
log('GenerateThermoPage payload: ' + out_msgs, 'info');
|
|
}
|
|
return out_msgs;
|
|
} catch (err: any) {
|
|
log('error at function GenerateThermoPage: ' + err.message, 'warn');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unsubscribes from all thermo2-related subscriptions.
|
|
*
|
|
* This function removes all active subscriptions related to thermo2 entities.
|
|
*
|
|
* @function unsubscribeThermo2Subscriptions
|
|
*/
|
|
function unsubscribeThermo2Subscriptions (): void {
|
|
for (let i = 0; i < config.pages.length; i++) {
|
|
const page: NSPanel.PageType = config.pages[i];
|
|
if (isPageThermoItem(page)) {
|
|
let thermo2ID = page.thermoItems[0].id;
|
|
unsubscribe(thermo2ID + '.ACTUAL');
|
|
unsubscribe(thermo2ID + '.SET');
|
|
unsubscribe(thermo2ID + '.MODE');
|
|
unsubscribe(thermo2ID + '.HUMIDITY');
|
|
}
|
|
}
|
|
for (let i = 0; i < config.subPages.length; i++) {
|
|
const page: NSPanel.PageType = config.subPages[i];
|
|
if (isPageThermoItem(page)) {
|
|
let thermo2ID = page.thermoItems[0].id;
|
|
unsubscribe(thermo2ID + '.ACTUAL');
|
|
unsubscribe(thermo2ID + '.SET');
|
|
unsubscribe(thermo2ID + '.MODE');
|
|
unsubscribe(thermo2ID + '.HUMIDITY');
|
|
}
|
|
}
|
|
if (Debug) log('unsubscribeMediaSubscriptions gestartet', 'info');
|
|
}
|
|
|
|
/**
|
|
* Subscribes to thermo2-related subscriptions for the specified entity ID.
|
|
*
|
|
* This function sets up subscriptions to monitor changes in media entities and perform actions when changes occur.
|
|
*
|
|
* @function subscribeThermo2Subscriptions
|
|
* @param {string} id - The ID of the media entity to subscribe to.
|
|
*/
|
|
function subscribeThermo2Subscriptions (id: string): void {
|
|
on(
|
|
{id: [id + '.ACTUAL', id + '.MODE', id + '.SET', id + '.HUMIDITY'], change: 'any', ack: true},
|
|
async function () {
|
|
|
|
setTimeout(async function () {
|
|
//pageCounter = 1;
|
|
GeneratePage(activePage!);
|
|
}, 500);
|
|
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Generates the payload for a thermostat page on the NSPanel.
|
|
*
|
|
* This function creates and returns the payload required to display a thermostat page on the NSPanel.
|
|
*
|
|
* @function GenerateThermo2Page
|
|
* @param {NSPanel.PageThermo2} page - The thermostat page configuration.
|
|
* @returns {NSPanel.Payload[]} The payload array for the cardThermo2.
|
|
*/
|
|
function GenerateThermo2Page (page: NSPanel.PageThermo2): NSPanel.Payload[] {
|
|
try {
|
|
|
|
UnsubscribeWatcher();
|
|
activePage = page;
|
|
unsubscribeThermo2Subscriptions;
|
|
|
|
let id = page.thermoItems[0].id;
|
|
let out_msgs: NSPanel.Payload[] = [];
|
|
|
|
// Leave the display on if the alwaysOnDisplay parameter is specified (true)
|
|
if (page.type == 'cardThermo2' && pageCounter == 0 && page.alwaysOnDisplay != undefined) {
|
|
out_msgs.push({payload: 'pageType~cardThermo2'});
|
|
if (page.alwaysOnDisplay != undefined) {
|
|
if (page.alwaysOnDisplay) {
|
|
pageCounter = 1;
|
|
if (existsObject(id) && alwaysOn == false) {
|
|
alwaysOn = true;
|
|
SendToPanel({payload: 'timeout~0'});
|
|
subscribeThermo2Subscriptions(id);
|
|
}
|
|
}
|
|
}
|
|
} else if (id && existsObject(id) && page.type == 'cardThermo2' && pageCounter == 1) {
|
|
subscribeThermo2Subscriptions(id);
|
|
} else {
|
|
out_msgs.push({payload: 'pageType~cardThermo2'});
|
|
}
|
|
|
|
if (id && existsObject(id)) {
|
|
let o = getObject(id);
|
|
let name = page.heading !== undefined ? page.heading : o.common.name && typeof o.common.name === 'object' ? o.common.name.de : o.common.name;
|
|
let currentTemp = 0;
|
|
if (existsState(page.thermoItems[1].id)) {
|
|
currentTemp = Math.round(parseFloat(getState(page.thermoItems[1].id).val) * 10);
|
|
}
|
|
let tempUnit = page.thermoItems[1].unit !== undefined ? page.thermoItems[1].unit : "?";
|
|
let tempColor = page.thermoItems[1].unit !== undefined ? rgb_dec565(page.thermoItems[1].onColor) : '64512';
|
|
|
|
let currentHumidity = 0;
|
|
if (existsState(page.thermoItems[2].id)) {
|
|
currentHumidity = Math.round(parseFloat(getState(page.thermoItems[2].id).val) * 10);
|
|
}
|
|
let humidityUnit = page.thermoItems[2].unit !== undefined ? page.thermoItems[2].unit : "?";
|
|
let humColor = page.thermoItems[2].unit !== undefined ? rgb_dec565(page.thermoItems[2].onColor) : '1048';
|
|
|
|
let obj = getObject(page.thermoItems[3].id);
|
|
let actualModeState = getState(page.thermoItems[3].id).val;
|
|
let modeStatus = obj.common['states'][getState(page.thermoItems[3].id).val] ?? ''
|
|
let textStateColor = page.thermoItems[3].unit !== undefined ? rgb_dec565(page.thermoItems[3].onColor) : '64512';
|
|
|
|
let minTemp: number = page.thermoItems[0].minValue !== undefined ? page.thermoItems[0].minValue * 10 : 45; //Min Temp 4,5°C
|
|
let maxTemp: number = page.thermoItems[0].maxValue !== undefined ? page.thermoItems[0].maxValue * 10 : 305; //Max Temp 30,5°C
|
|
let stepTemp: number = page.thermoItems[0].stepValue !== undefined ? page.thermoItems[0].stepValue * 10 : 5; //Default 0,5° Schritte
|
|
let unit: string = page.thermoItems[0].unit !== undefined ? page.thermoItems[0].unit : '°C'; //Default 0,5° Schritte
|
|
|
|
let destTemp = 0;
|
|
if (existsState(id + '.SET')) {
|
|
let setValue = getState(id + '.SET').val;
|
|
if (setValue == null) {
|
|
setValue = minTemp;
|
|
}
|
|
|
|
destTemp = setValue.toFixed(2) * 10;
|
|
}
|
|
|
|
let message: string =
|
|
'entityUpd~' +
|
|
name + // Heading 1
|
|
'~' +
|
|
getNavigationString(pageId) + // 2-13 Page Navigation
|
|
/*-Temp Control-----------------------------------*/
|
|
'~' + id + '~' + destTemp + '~' + minTemp + '~' + maxTemp + '~' + stepTemp + '~' + unit + '~' + /* 20 */ actualModeState +
|
|
/* Entity 1 - Actual Temperature (Icon) */
|
|
'~text~' + pageId + '?1~' + Icons.GetIcon('thermometer') + '~' + tempColor + '~~' +
|
|
/* Entity 2 - Actual Temperature (Temp) */
|
|
'~text~' + pageId + '?2~' + currentTemp + '~' + tempColor + '~~' +
|
|
/* Entity 3 - Actual Temperature (Unit) */
|
|
'~text~' + pageId + '?3~' + tempUnit + '~' + tempColor + '~~' +
|
|
/* Entity 4 - Actual Humidity (Icon) */
|
|
'~text~' + pageId + '?4~' + Icons.GetIcon('water-percent') + '~' + humColor + '~~' +
|
|
/* Entity 5 - Actual Humidity (Hum) */
|
|
'~text~' + pageId + '?5~' + currentHumidity + '~' + humColor + '~~' +
|
|
/* Entity 6 - Actual Humidity (Unit) */
|
|
'~text~' + pageId + '?6~' + humidityUnit + '~' + humColor + '~~' +
|
|
/* Entity 7 - Text-State */
|
|
'~text~' + pageId + '?7~' + modeStatus + '~' + textStateColor + '~~' + /* 62 */ actualModeState;
|
|
|
|
for (let i=0; i<9; i++) {
|
|
if(page.items[i] != undefined) {
|
|
id = page.items[i];
|
|
message = message + CreateEntity(id, i, true);
|
|
} else {
|
|
id = 'delete'
|
|
message = message + CreateEntity(id, i);
|
|
}
|
|
}
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
message
|
|
});
|
|
|
|
}
|
|
|
|
if (Debug) {
|
|
log('GenerateThermo2Page payload: ' + JSON.stringify(out_msgs), 'info');
|
|
}
|
|
return out_msgs;
|
|
} catch (err: any) {
|
|
log('error at function GenerateThermo2Page: ' + err.message, 'warn');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unsubscribes from all media-related subscriptions.
|
|
*
|
|
* This function removes all active subscriptions related to media entities.
|
|
*
|
|
* @function unsubscribeMediaSubscriptions
|
|
*/
|
|
function unsubscribeMediaSubscriptions (): void {
|
|
for (let i = 0; i < config.pages.length; i++) {
|
|
const page: NSPanel.PageType = config.pages[i];
|
|
if (isPageMedia(page)) {
|
|
let mediaID = page.items[0].id;
|
|
unsubscribe(mediaID + '.STATE');
|
|
unsubscribe(mediaID + '.ARTIST');
|
|
unsubscribe(mediaID + '.TITLE');
|
|
unsubscribe(mediaID + '.ALBUM');
|
|
unsubscribe(mediaID + '.VOLUME');
|
|
unsubscribe(mediaID + '.REPEAT');
|
|
unsubscribe(mediaID + '.SHUFFLE');
|
|
unsubscribe(mediaID + '.QUEUE');
|
|
unsubscribe(mediaID + '.DURATION');
|
|
unsubscribe(mediaID + '.ELAPSED');
|
|
}
|
|
}
|
|
for (let i = 0; i < config.subPages.length; i++) {
|
|
const page: NSPanel.PageType = config.subPages[i];
|
|
if (isPageMedia(page)) {
|
|
let mediaID = page.items[0].id;
|
|
unsubscribe(mediaID + '.STATE');
|
|
unsubscribe(mediaID + '.ARTIST');
|
|
unsubscribe(mediaID + '.TITLE');
|
|
unsubscribe(mediaID + '.ALBUM');
|
|
unsubscribe(mediaID + '.VOLUME');
|
|
unsubscribe(mediaID + '.REPEAT');
|
|
unsubscribe(mediaID + '.SHUFFLE');
|
|
unsubscribe(mediaID + '.QUEUE');
|
|
unsubscribe(mediaID + '.DURATION');
|
|
unsubscribe(mediaID + '.ELAPSED');
|
|
}
|
|
}
|
|
if (Debug) log('unsubscribeMediaSubscriptions gestartet', 'info');
|
|
}
|
|
|
|
/**
|
|
* Subscribes to media-related subscriptions for the specified entity ID.
|
|
*
|
|
* This function sets up subscriptions to monitor changes in media entities and perform actions when changes occur.
|
|
*
|
|
* @function subscribeMediaSubscriptions
|
|
* @param {string} id - The ID of the media entity to subscribe to.
|
|
*/
|
|
function subscribeMediaSubscriptions (id: string): void {
|
|
on(
|
|
{id: [id + '.STATE', id + '.ARTIST', id + '.TITLE', id + '.ALBUM', id + '.VOLUME', id + '.REPEAT', id + '.SHUFFLE', id + '.DURATION', id + '.ELAPSED'], change: 'any', ack: true},
|
|
async function () {
|
|
if (useMediaEvents && pageCounter == 1) {
|
|
GeneratePage(activePage!);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Subscribes to Sonos media-related subscriptions for the specified entity ID.
|
|
*
|
|
* This function sets up subscriptions to monitor changes in Sonos media entities and perform actions when changes occur.
|
|
*
|
|
* @function subscribeMediaSubscriptionsSonosAdd
|
|
* @param {string} id - The ID of the Sonos media entity to subscribe to.
|
|
*/
|
|
function subscribeMediaSubscriptionsSonosAdd (id: string): void {
|
|
on({id: [id + '.QUEUE'], change: 'any', ack: true}, async function () {
|
|
if (useMediaEvents && pageCounter == 1) {
|
|
GeneratePage(activePage!);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Creates media aliases for a specific media device and adapter player instance.
|
|
*
|
|
* @param id - The unique identifier for the alias to be created.
|
|
* @param mediaDevice - The media device name for which aliases will be created.
|
|
* @param adapterPlayerInstance - The type of adapter player instance, e.g., 'alexa2.0.', 'sonos.0.', etc.
|
|
*
|
|
* This function automatically creates aliases for media controls such as volume, play, pause, next, previous,
|
|
* album, artist, title, and more, based on the adapter player instance. It checks if the alias already exists
|
|
* and creates it if not. Supported adapters include Alexa, Sonos, Spotify, Volumio, Squeezebox, and Bose SoundTouch.
|
|
* Logs errors if alias creation fails.
|
|
*/
|
|
async function createAutoMediaAlias (id: string, mediaDevice: string, adapterPlayerInstance: NSPanel.adapterPlayerInstanceType) {
|
|
if (autoCreateAlias) {
|
|
if (isSetOptionActive) {
|
|
switch (adapterPlayerInstance) {
|
|
case 'alexa2.0.':
|
|
case 'alexa2.1.':
|
|
case 'alexa2.2.':
|
|
case 'alexa2.3.':
|
|
case 'alexa2.4.':
|
|
case 'alexa2.5.':
|
|
case 'alexa2.6.':
|
|
case 'alexa2.7.':
|
|
case 'alexa2.8.':
|
|
case 'alexa2.9.':
|
|
{
|
|
if (existsObject(id) == false) {
|
|
log('Alexa Alias ' + id + ' does not exist - will be created now', 'info');
|
|
|
|
const dpPath: string = adapterPlayerInstance + 'Echo-Devices.' + mediaDevice;
|
|
try {
|
|
setObject(id, {_id: id, type: 'channel', common: {role: 'media', name: 'media'}, native: {}});
|
|
await createAliasAsync(id + '.ACTUAL', dpPath + '.Player.volume', true, {type: 'number', role: 'value.volume', name: 'ACTUAL'});
|
|
await createAliasAsync(id + '.ALBUM', dpPath + '.Player.currentAlbum', true, {type: 'string', role: 'media.album', name: 'ALBUM'});
|
|
await createAliasAsync(id + '.ARTIST', dpPath + '.Player.currentArtist', true, {type: 'string', role: 'media.artist', name: 'ARTIST'});
|
|
await createAliasAsync(id + '.TITLE', dpPath + '.Player.currentTitle', true, {type: 'string', role: 'media.title', name: 'TITLE'});
|
|
await createAliasAsync(id + '.NEXT', dpPath + '.Player.controlNext', true, {type: 'boolean', role: 'button.next', name: 'NEXT'});
|
|
await createAliasAsync(id + '.PREV', dpPath + '.Player.controlPrevious', true, {type: 'boolean', role: 'button.prev', name: 'PREV'});
|
|
await createAliasAsync(id + '.PLAY', dpPath + '.Player.controlPlay', true, {type: 'boolean', role: 'button.play', name: 'PLAY'});
|
|
await createAliasAsync(id + '.PAUSE', dpPath + '.Player.controlPause', true, {type: 'boolean', role: 'button.pause', name: 'PAUSE'});
|
|
await createAliasAsync(id + '.STOP', dpPath + '.Commands.deviceStop', true, {type: 'boolean', role: 'button.stop', name: 'STOP'});
|
|
await createAliasAsync(id + '.STATE', dpPath + '.Player.currentState', true, {type: 'boolean', role: 'media.state', name: 'STATE'});
|
|
await createAliasAsync(id + '.VOLUME', dpPath + '.Player.volume', true, {type: 'number', role: 'level.volume', name: 'VOLUME'});
|
|
await createAliasAsync(id + '.REPEAT', dpPath + '.Player.controlRepeat', true, {type: 'boolean', role: 'media.mode.repeat', name: 'REPEAT'});
|
|
await createAliasAsync(id + '.SHUFFLE', dpPath + '.Player.controlShuffle', true, {type: 'boolean', role: 'media.mode.shuffle', name: 'SHUFFLE'});
|
|
} catch (err: any) {
|
|
log('error at function createAutoMediaAlias Adapter Alexa2: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
//Add Alexa Datapoints > v4.3.3.18
|
|
if (existsObject(id + '.DURATION') == false) {
|
|
let dpPath: string = adapterPlayerInstance + 'Echo-Devices.' + mediaDevice;
|
|
await createAliasAsync(id + '.DURATION', dpPath + '.Player.mediaLength', true, {type: 'string', role: 'media.duration.text', name: 'DURATION'});
|
|
await createAliasAsync(id + '.ELAPSED', dpPath + '.Player.mediaProgressStr', true, {type: 'string', role: 'media.elapsed.text', name: 'ELAPSED'});
|
|
}
|
|
}
|
|
break;
|
|
case 'sonos.0.':
|
|
case 'sonos.1.':
|
|
case 'sonos.2.':
|
|
case 'sonos.3.':
|
|
case 'sonos.4.':
|
|
case 'sonos.5.':
|
|
case 'sonos.6.':
|
|
case 'sonos.7.':
|
|
case 'sonos.8.':
|
|
case 'sonos.9.':
|
|
{
|
|
if (existsObject(id) == false) {
|
|
log('Sonos Alias ' + id + ' does not exist - will be created now', 'info');
|
|
|
|
const dpPath: string = adapterPlayerInstance + 'root.' + mediaDevice;
|
|
try {
|
|
setObject(id, {_id: id, type: 'channel', common: {role: 'media', name: 'media'}, native: {}});
|
|
await createAliasAsync(id + '.ACTUAL', dpPath + '.volume', true, {type: 'number', role: 'value.volume', name: 'ACTUAL'});
|
|
await createAliasAsync(id + '.ALBUM', dpPath + '.current_album', true, {type: 'string', role: 'media.album', name: 'ALBUM'});
|
|
await createAliasAsync(id + '.ARTIST', dpPath + '.current_artist', true, {type: 'string', role: 'media.artist', name: 'ARTIST'});
|
|
await createAliasAsync(id + '.TITLE', dpPath + '.current_title', true, {type: 'string', role: 'media.title', name: 'TITLE'});
|
|
await createAliasAsync(id + '.CONTEXT_DESCRIPTION', dpPath + '.current_station', true, {
|
|
type: 'string',
|
|
role: 'media.station',
|
|
name: 'CONTEXT_DESCRIPTION',
|
|
});
|
|
await createAliasAsync(id + '.NEXT', dpPath + '.next', true, {type: 'boolean', role: 'button.next', name: 'NEXT'});
|
|
await createAliasAsync(id + '.PREV', dpPath + '.prev', true, {type: 'boolean', role: 'button.prev', name: 'PREV'});
|
|
await createAliasAsync(id + '.PLAY', dpPath + '.play', true, {type: 'boolean', role: 'button.play', name: 'PLAY'});
|
|
await createAliasAsync(id + '.PAUSE', dpPath + '.pause', true, {type: 'boolean', role: 'button.pause', name: 'PAUSE'});
|
|
await createAliasAsync(id + '.STOP', dpPath + '.stop', true, {type: 'boolean', role: 'button.stop', name: 'STOP'});
|
|
await createAliasAsync(id + '.STATE', dpPath + '.state_simple', true, {type: 'boolean', role: 'media.state', name: 'STATE'});
|
|
await createAliasAsync(id + '.VOLUME', dpPath + '.volume', true, {type: 'number', role: 'level.volume', name: 'VOLUME'});
|
|
await createAliasAsync(id + '.REPEAT', dpPath + '.repeat', true, {type: 'number', role: 'media.mode.repeat', name: 'REPEAT'});
|
|
await createAliasAsync(id + '.SHUFFLE', dpPath + '.shuffle', true, {type: 'boolean', role: 'media.mode.shuffle', name: 'SHUFFLE'});
|
|
} catch (err: any) {
|
|
log('error at function createAutoMediaAlias Adapter sonos: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
//Add Sonos Datapoints > v4.3.3.15
|
|
if (existsObject(id + '.QUEUE') == false) {
|
|
let dpPath: string = adapterPlayerInstance + 'root.' + mediaDevice;
|
|
await createAliasAsync(id + '.QUEUE', dpPath + '.queue', true, {type: 'string', role: 'state', name: 'QUEUE'});
|
|
await createAliasAsync(id + '.DURATION', dpPath + '.current_duration_s', true, {type: 'string', role: 'media.duration.text', name: 'DURATION'});
|
|
await createAliasAsync(id + '.ELAPSED', dpPath + '.current_elapsed_s', true, {type: 'string', role: 'media.elapsed.text', name: 'ELAPSED'});
|
|
}
|
|
}
|
|
break;
|
|
case 'spotify-premium.0.':
|
|
case 'spotify-premium.1.':
|
|
case 'spotify-premium.2.':
|
|
case 'spotify-premium.3.':
|
|
case 'spotify-premium.4.':
|
|
case 'spotify-premium.5.':
|
|
case 'spotify-premium.6.':
|
|
case 'spotify-premium.7.':
|
|
case 'spotify-premium.8.':
|
|
case 'spotify-premium.9.':
|
|
{
|
|
if (existsObject(id) == false) {
|
|
log('Spotify Alias ' + id + ' does not exist - will be created now', 'info');
|
|
|
|
const dpPath: string = adapterPlayerInstance;
|
|
try {
|
|
setObject(id, {_id: id + 'player', type: 'channel', common: {role: 'media', name: 'media'}, native: {}});
|
|
await createAliasAsync(id + '.ACTUAL', dpPath + 'player.volume', true, {type: 'number', role: 'value.volume', name: 'ACTUAL'});
|
|
await createAliasAsync(id + '.ALBUM', dpPath + 'player.album', true, {type: 'string', role: 'media.album', name: 'ALBUM'});
|
|
await createAliasAsync(id + '.ARTIST', dpPath + 'player.artistName', true, {type: 'string', role: 'media.artist', name: 'ARTIST'});
|
|
await createAliasAsync(id + '.TITLE', dpPath + 'player.trackName', true, {type: 'string', role: 'media.title', name: 'TITLE'});
|
|
await createAliasAsync(id + '.CONTEXT_DESCRIPTION', dpPath + 'player.contextDescription', true, {
|
|
type: 'string',
|
|
role: 'media.station',
|
|
name: 'CONTEXT_DESCRIPTION',
|
|
});
|
|
await createAliasAsync(id + '.NEXT', dpPath + 'player.skipPlus', true, {type: 'boolean', role: 'button.next', name: 'NEXT'});
|
|
await createAliasAsync(id + '.PREV', dpPath + 'player.skipMinus', true, {type: 'boolean', role: 'button.prev', name: 'PREV'});
|
|
await createAliasAsync(id + '.PLAY', dpPath + 'player.play', true, {type: 'boolean', role: 'button.play', name: 'PLAY'});
|
|
await createAliasAsync(id + '.PAUSE', dpPath + 'player.pause', true, {type: 'boolean', role: 'button.pause', name: 'PAUSE'});
|
|
await createAliasAsync(id + '.STOP', dpPath + 'player.pause', true, {type: 'boolean', role: 'button.stop', name: 'STOP'});
|
|
await createAliasAsync(id + '.STATE', dpPath + 'player.isPlaying', true, {type: 'boolean', role: 'media.state', name: 'STATE'});
|
|
await createAliasAsync(id + '.VOLUME', dpPath + 'player.volume', true, {type: 'number', role: 'level.volume', name: 'VOLUME'});
|
|
await createAliasAsync(id + '.REPEAT', dpPath + 'player.repeat', true, {type: 'string', role: 'value', name: 'REPEAT'});
|
|
await createAliasAsync(id + '.SHUFFLE', dpPath + 'player.shuffle', true, {type: 'string', role: 'value', name: 'SHUFFLE'});
|
|
} catch (err: any) {
|
|
log('error at function createAutoMediaAlias Adapter spotify-premium: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
//Add Spotify Datapoints > v4.3.3.42
|
|
//Spotify-Premium has Role value and a known Bug in player.progress
|
|
if (existsObject(id + '.DURATION') == false) {
|
|
const dpPath: string = adapterPlayerInstance;
|
|
await createAliasAsync(id + '.DURATION', dpPath + 'player.duration', true, {type: 'string', role: 'media.duration.text', name: 'DURATION'});
|
|
await createAliasAsync(id + '.ELAPSED', dpPath + 'player.progress', true, {type: 'string', role: 'media.elapsed.text', name: 'ELAPSED'});
|
|
}
|
|
}
|
|
break;
|
|
case 'volumio.0.':
|
|
case 'volumio.1.':
|
|
case 'volumio.2.':
|
|
case 'volumio.3.':
|
|
case 'volumio.4.':
|
|
case 'volumio.5.':
|
|
case 'volumio.6.':
|
|
case 'volumio.7.':
|
|
case 'volumio.8.':
|
|
case 'volumio.9.':
|
|
{
|
|
if (existsObject(id) == false) {
|
|
log('Volumio Alias ' + id + ' does not exist - will be created now', 'info');
|
|
|
|
const dpPath: string = adapterPlayerInstance;
|
|
try {
|
|
setObject(id, {_id: id, type: 'channel', common: {role: 'media', name: 'media'}, native: {}});
|
|
await createAliasAsync(id + '.ACTUAL', dpPath + 'playbackInfo.volume', true, {type: 'number', role: 'value.volume', name: 'ACTUAL'});
|
|
await createAliasAsync(id + '.ALBUM', dpPath + 'playbackInfo.album', true, {type: 'string', role: 'media.album', name: 'ALBUM'});
|
|
await createAliasAsync(id + '.ARTIST', dpPath + 'playbackInfo.artist', true, {type: 'string', role: 'media.artist', name: 'ARTIST'});
|
|
await createAliasAsync(id + '.TITLE', dpPath + 'playbackInfo.title', true, {type: 'string', role: 'media.title', name: 'TITLE'});
|
|
await createAliasAsync(id + '.NEXT', dpPath + 'player.next', true, {type: 'boolean', role: 'button.next', name: 'NEXT'});
|
|
await createAliasAsync(id + '.PREV', dpPath + 'player.prev', true, {type: 'boolean', role: 'button.prev', name: 'PREV'});
|
|
await createAliasAsync(id + '.PLAY', dpPath + 'player.play', true, {type: 'boolean', role: 'button.play', name: 'PLAY'});
|
|
await createAliasAsync(id + '.PAUSE', dpPath + 'player.toggle', true, {type: 'boolean', role: 'button.pause', name: 'PAUSE'});
|
|
await createAliasAsync(id + '.STOP', dpPath + 'player.stop', true, {type: 'boolean', role: 'button.stop', name: 'STOP'});
|
|
await createAliasAsync(id + '.STATE', dpPath + 'playbackInfo.status', true, {type: 'boolean', role: 'media.state', name: 'STATE'});
|
|
await createAliasAsync(id + '.VOLUME', dpPath + 'playbackInfo.volume', true, {type: 'number', role: 'level.volume', name: 'VOLUME'});
|
|
await createAliasAsync(id + '.REPEAT', dpPath + 'playbackInfo.repeat', true, {type: 'number', role: 'media.mode.repeat', name: 'REPEAT'});
|
|
await createAliasAsync(id + '.SHUFFLE', dpPath + 'queue.shuffle', true, {type: 'boolean', role: 'media.mode.shuffle', name: 'SHUFFLE'});
|
|
await createAliasAsync(id + '.status', dpPath + 'playbackInfo.status', true, {type: 'string', role: 'media.state', name: 'status'});
|
|
} catch (err: any) {
|
|
log('error function createAutoMediaAlias Adapter volumio: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
//Add Volumio Datapoints > v4.3.3.42
|
|
if (existsObject(id + '.DURATION') == false) {
|
|
const dpPath: string = adapterPlayerInstance;
|
|
await createAliasAsync(id + '.DURATION', dpPath + 'playbackInfo.duration', true, {type: 'string', role: 'media.duration', name: 'DURATION'});
|
|
//await createAliasAsync(id + '.ELAPSED', dpPath + 'player.progress', true, {type: 'string', role: 'media.elapsed.text', name: 'ELAPSED'});
|
|
}
|
|
}
|
|
break;
|
|
case 'squeezeboxrpc.0.':
|
|
case 'squeezeboxrpc.1.':
|
|
case 'squeezeboxrpc.2.':
|
|
case 'squeezeboxrpc.3.':
|
|
case 'squeezeboxrpc.4.':
|
|
case 'squeezeboxrpc.5.':
|
|
case 'squeezeboxrpc.6.':
|
|
case 'squeezeboxrpc.7.':
|
|
case 'squeezeboxrpc.8.':
|
|
case 'squeezeboxrpc.9.':
|
|
{
|
|
if (existsObject(id) == false) {
|
|
log('Squeezebox Alias ' + id + ' does not exist - will be created now', 'info');
|
|
|
|
const dpPath: string = adapterPlayerInstance + 'Players.' + mediaDevice;
|
|
try {
|
|
setObject(id, {_id: id, type: 'channel', common: {role: 'media', name: 'media'}, native: {}});
|
|
await createAliasAsync(id + '.ALBUM', dpPath + '.Album', true, {type: 'string', role: 'media.album', name: 'ALBUM'});
|
|
await createAliasAsync(id + '.ARTIST', dpPath + '.Artist', true, {type: 'string', role: 'media.artist', name: 'ARTIST'});
|
|
await createAliasAsync(id + '.TITLE', dpPath + '.Title', true, {type: 'string', role: 'media.title', name: 'TITLE'});
|
|
await createAliasAsync(id + '.NEXT', dpPath + '.btnForward', true, {type: 'boolean', role: 'button.forward', name: 'NEXT'});
|
|
await createAliasAsync(id + '.PREV', dpPath + '.btnRewind', true, {type: 'boolean', role: 'button.reverse', name: 'PREV'});
|
|
await createAliasAsync(id + '.PLAY', dpPath + '.state', true, {
|
|
type: 'boolean',
|
|
role: 'media.state',
|
|
name: 'PLAY',
|
|
alias: {id: dpPath + '.state', read: 'val === 1 ? true : false'},
|
|
});
|
|
await createAliasAsync(id + '.PAUSE', dpPath + '.state', true, {
|
|
type: 'boolean',
|
|
role: 'media.state',
|
|
name: 'PAUSE',
|
|
alias: {id: dpPath + '.state', read: 'val === 0 ? true : false'},
|
|
});
|
|
await createAliasAsync(id + '.STOP', dpPath + '.state', true, {
|
|
type: 'boolean',
|
|
role: 'media.state',
|
|
name: 'STOP',
|
|
alias: {id: dpPath + '.state', read: 'val === 0 ? true : false'},
|
|
});
|
|
await createAliasAsync(id + '.STATE', dpPath + '.Power', true, {type: 'number', role: 'switch', name: 'STATE'});
|
|
await createAliasAsync(id + '.VOLUME', dpPath + '.Volume', true, {type: 'number', role: 'level.volume', name: 'VOLUME'});
|
|
await createAliasAsync(id + '.VOLUME_ACTUAL', dpPath + '.Volume', true, {type: 'number', role: 'value.volume', name: 'VOLUME_ACTUAL'});
|
|
await createAliasAsync(id + '.SHUFFLE', dpPath + '.PlaylistShuffle', true, {type: 'string', role: 'media.mode.shuffle', name: 'SHUFFLE'});
|
|
await createAliasAsync(id + '.REPEAT', dpPath + '.PlaylistRepeat', true, {type: 'number', role: 'media.mode.repeat', name: 'REPEAT'});
|
|
await createAliasAsync(id + '.DURATION', dpPath + '.Duration', true, {type: 'string', role: 'media.duration', name: 'DURATION'});
|
|
await createAliasAsync(id + '.ELAPSED', dpPath + '.Time', true, {type: 'string', role: 'media.elapsed', name: 'ELAPSED'});
|
|
} catch (err: any) {
|
|
log('error at function createAutoMediaAlias Adapter Squeezebox: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
}
|
|
break
|
|
case 'mpd.0.':
|
|
case 'mpd.1.':
|
|
case 'mpd.2.':
|
|
case 'mpd.3.':
|
|
case 'mpd.4.':
|
|
case 'mpd.5.':
|
|
case 'mpd.6.':
|
|
case 'mpd.7.':
|
|
case 'mpd.8.':
|
|
case 'mpd.9.':
|
|
{
|
|
if (existsObject(id) == false) {
|
|
log('MPD Alias ' + id + ' does not exist - will be created now', 'info');
|
|
|
|
const dpPath: string = adapterPlayerInstance;
|
|
|
|
try {
|
|
setObject(id, {_id: id, type: 'channel', common: {role: 'media', name: 'media'}, native: {}});
|
|
await createAliasAsync(id + '.ACTUAL', dpPath + 'setvol', true, {type: 'number', role: 'value.volume', name: 'ACTUAL'});
|
|
await createAliasAsync(id + '.ALBUM', dpPath + 'album', true, {type: 'string', role: 'media.album', name: 'ALBUM'});
|
|
await createAliasAsync(id + '.ARTIST', dpPath + 'artist', true, {type: 'string', role: 'media.artist', name: 'ARTIST'});
|
|
await createAliasAsync(id + '.TITLE', dpPath + 'title', true, {type: 'string', role: 'media.title', name: 'TITLE'});
|
|
await createAliasAsync(id + '.NEXT', dpPath + 'next', true, {type: 'boolean', role: 'button.next', name: 'NEXT'});
|
|
await createAliasAsync(id + '.PREV', dpPath + 'previous', true, {type: 'boolean', role: 'button.prev', name: 'PREV'});
|
|
await createAliasAsync(id + '.PLAY', dpPath + 'play', true, {type: 'boolean', role: 'button.play', name: 'PLAY'});
|
|
await createAliasAsync(id + '.PAUSE', dpPath + 'pause', true, {type: 'boolean', role: 'button.pause', name: 'PAUSE'});
|
|
await createAliasAsync(id + '.STOP', dpPath + 'stop', true, {type: 'boolean', role: 'button.stop', name: 'STOP'});
|
|
await createAliasAsync(id + '.STATE', dpPath + 'state', true, {type: 'string', role: 'media.state', name: 'STATE'});
|
|
await createAliasAsync(id + '.VOLUME', dpPath + 'volume', true, {type: 'number', role: 'level.volume', name: 'VOLUME'});
|
|
await createAliasAsync(id + '.REPEAT', dpPath + 'repeat', true, {type: 'boolean', role: 'media.mode.repeat', name: 'REPEAT'});
|
|
await createAliasAsync(id + '.SINGLE', dpPath + 'single', true, {type: 'number', role: 'media', name: 'SINGLE'});
|
|
await createAliasAsync(id + '.SHUFFLE', dpPath + 'random', true, {type: 'boolean', role: 'media.mode.shuffle', name: 'SHUFFLE'});
|
|
await createAliasAsync(id + '.DURATION', dpPath + 'current_duration', true, {type: 'string', role: 'media.duration.text', name: 'DURATION'});
|
|
await createAliasAsync(id + '.ELAPSED', dpPath + 'current_elapsed', true, {type: 'string', role: 'media.elapsed.text', name: 'ELAPSED'});
|
|
} catch (err: any) {
|
|
log('error at function createAutoMediaAlias Adapter mpd: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'bosesoundtouch.0.':
|
|
case 'bosesoundtouch.1.':
|
|
case 'bosesoundtouch.2.':
|
|
case 'bosesoundtouch.3.':
|
|
case 'bosesoundtouch.4.':
|
|
case 'bosesoundtouch.5.':
|
|
case 'bosesoundtouch.6.':
|
|
case 'bosesoundtouch.7.':
|
|
case 'bosesoundtouch.8.':
|
|
case 'bosesoundtouch.9.': {
|
|
if (existsObject(id) == false) {
|
|
log('bosesoundtouch Alias ' + id + ' does not exist - will be created now', 'info');
|
|
|
|
try {
|
|
let dpPath: string = adapterPlayerInstance;
|
|
await extendObjectAsync(id, {_id: id, type: 'channel', common: {role: 'media', name: 'media'}, native: {}});
|
|
await createAliasAsync(id + '.ACTUAL', dpPath + 'volume', true, {type: 'number', role: 'value.volume', name: 'ACTUAL'});
|
|
await createAliasAsync(id + '.VOLUME', dpPath + 'volume', true, {type: 'number', role: 'level.volume', name: 'VOLUME'});
|
|
await createAliasAsync(id + '.STATE', dpPath + 'on', true, {type: 'boolean', role: 'media.state', name: 'STATE'});
|
|
|
|
dpPath = adapterPlayerInstance + 'nowPlaying';
|
|
await createAliasAsync(id + '.ALBUM', dpPath + '.album', true, {type: 'string', role: 'media.album', name: 'ALBUM'});
|
|
await createAliasAsync(id + '.ARTIST', dpPath + '.artist', true, {type: 'string', role: 'media.artist', name: 'ARTIST'});
|
|
await createAliasAsync(id + '.TITLE', dpPath + '.track', true, {type: 'string', role: 'media.title', name: 'TITLE'});
|
|
await createAliasAsync(id + '.DURATION', dpPath + '.total', true, {type: 'string', role: 'media.duration.text', name: 'DURATION'});
|
|
await createAliasAsync(id + '.ELAPSED', dpPath + '.time', true, {type: 'string', role: 'media.elapsed.text', name: 'ELAPSED'});
|
|
await createAliasAsync(id + '.REPEAT', dpPath + '.repeat', true, {type: 'boolean', role: 'media.mode.repeat', name: 'REPEAT'});
|
|
await createAliasAsync(id + '.SHUFFLE', dpPath + '.shuffle', true, {type: 'boolean', role: 'media.mode.shuffle', name: 'SHUFFLE'});
|
|
|
|
dpPath = adapterPlayerInstance + 'keys';
|
|
await createAliasAsync(id + '.NEXT', dpPath + '.NEXT_TRACK', true, {type: 'boolean', role: 'button.next', name: 'NEXT'});
|
|
await createAliasAsync(id + '.PREV', dpPath + '.PREV_TRACK', true, {type: 'boolean', role: 'button.prev', name: 'PREV'});
|
|
await createAliasAsync(id + '.PLAY', dpPath + '.PLAY', true, {type: 'boolean', role: 'button.play', name: 'PLAY'});
|
|
await createAliasAsync(id + '.PAUSE', dpPath + '.PAUSE', true, {type: 'boolean', role: 'button.pause', name: 'PAUSE'});
|
|
await createAliasAsync(id + '.STOP', dpPath + '.STOP', true, {type: 'boolean', role: 'button.stop', name: 'STOP'});
|
|
} catch (err: any) {
|
|
log('error at function createAutoMediaAlias Adapter bosesoundtouch: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
default: {
|
|
log(`Dont find adapterPlayerInstance: ${adapterPlayerInstance}!`, 'warn');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates the payload for a media page on the NSPanel.
|
|
*
|
|
* This function creates and returns the payload required to display a media page on the NSPanel.
|
|
*
|
|
* @function GenerateMediaPage
|
|
* @param {NSPanel.PageMedia} page - The media page configuration.
|
|
* @returns {NSPanel.Payload[]} The payload array for the media page.
|
|
*/
|
|
function GenerateMediaPage (page: NSPanel.PageMedia): NSPanel.Payload[] {
|
|
try {
|
|
unsubscribeMediaSubscriptions();
|
|
|
|
if (!page.items[0].id) throw new Error('Missing page id for cardMedia!');
|
|
|
|
let id = page.items[0].id;
|
|
let tid = 0;
|
|
let out_msgs: NSPanel.Payload[] = [];
|
|
|
|
if (!page.items[0].adapterPlayerInstance!) throw new Error('page.items[0].adapterPlayerInstance is undefined!');
|
|
let vInstance = page.items[0].adapterPlayerInstance!;
|
|
let v1Adapter = vInstance.split('.');
|
|
let v2Adapter: NSPanel.PlayerType = v1Adapter[0] as NSPanel.PlayerType;
|
|
|
|
let vMediaDevice = page.items[0].mediaDevice != undefined ? page.items[0].mediaDevice : '';
|
|
|
|
if (isPlayerWithMediaDevice(v2Adapter)) {
|
|
if (!vMediaDevice) throw new Error(`Error in cardMedia! mediaDevice is empty! Page: ${JSON.stringify(page)}`);
|
|
}
|
|
createAutoMediaAlias(id, vMediaDevice, page.items[0].adapterPlayerInstance!);
|
|
|
|
// Leave the display on if the alwaysOnDisplay parameter is specified (true)
|
|
if (page.type == 'cardMedia' && pageCounter == 0 && page.items[0].alwaysOnDisplay != undefined) {
|
|
out_msgs.push({payload: 'pageType~cardMedia'});
|
|
if (page.items[0].alwaysOnDisplay != undefined) {
|
|
if (page.items[0].alwaysOnDisplay) {
|
|
pageCounter = 1;
|
|
if (alwaysOn == false) {
|
|
alwaysOn = true;
|
|
SendToPanel({payload: 'timeout~0'});
|
|
subscribeMediaSubscriptions(page.items[0].id);
|
|
if (v2Adapter == 'sonos') {
|
|
subscribeMediaSubscriptionsSonosAdd(page.items[0].id);
|
|
} else if (v2Adapter == 'spotify-premium') {
|
|
setState(vInstance + 'getDevices', true);
|
|
setState(vInstance + 'getPlaybackInfo', true);
|
|
setState(vInstance + 'getPlaylists', true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (page.type == 'cardMedia' && pageCounter == 1) {
|
|
alwaysOn = true;
|
|
subscribeMediaSubscriptions(page.items[0].id);
|
|
if (v2Adapter == 'sonos') {
|
|
subscribeMediaSubscriptionsSonosAdd(page.items[0].id);
|
|
}
|
|
} else if (page.type == 'cardMedia' && pageCounter == -1) {
|
|
//Do Nothing
|
|
} else {
|
|
out_msgs.push({payload: 'pageType~cardMedia'});
|
|
}
|
|
|
|
if (existsObject(id)) {
|
|
let name = getState(id + '.ALBUM').val;
|
|
let title = getState(id + '.TITLE').val;
|
|
if (title.length > 24) {
|
|
title = title.slice(0, 24) + '...';
|
|
}
|
|
if (existsObject(id + '.DURATION') && existsObject(id + '.ELAPSED')) {
|
|
if (v2Adapter == 'alexa2') {
|
|
if (Debug) log(getState(id + '.DURATION').val, 'info');
|
|
let Seconds = parseInt(getState(id + '.DURATION').val) % 60 < 10 ? '0' : '';
|
|
let Duration = Math.floor(getState(id + '.DURATION').val / 60) + ':' + Seconds + (getState(id + '.DURATION').val % 60);
|
|
let vElapsed = getState(id + '.ELAPSED').val;
|
|
if (vElapsed.length == 5) {
|
|
if (parseInt(vElapsed.slice(0, 2)) < 9) {
|
|
vElapsed = vElapsed.slice(1);
|
|
}
|
|
}
|
|
if (vElapsed == 0) {
|
|
vElapsed = '0:00';
|
|
}
|
|
let vDuration = Duration;
|
|
if (vDuration.length == 5) {
|
|
if (parseInt(vDuration.slice(0, 2)) < 9) {
|
|
vDuration = vDuration.slice(1);
|
|
}
|
|
}
|
|
if (vDuration != '0:00') {
|
|
title = title + ' (' + vElapsed + '|' + vDuration + ')';
|
|
} else {
|
|
title = title + ' (' + vElapsed + ')';
|
|
}
|
|
if (title == ' (0:00)') {
|
|
title = '';
|
|
}
|
|
} else if (v2Adapter == 'sonos' && getState(page.items[0].adapterPlayerInstance + 'root.' + page.items[0].mediaDevice + '.current_type').val == 0) {
|
|
let vElapsed = getState(id + '.ELAPSED').val;
|
|
if (vElapsed.length == 5) {
|
|
if (parseInt(vElapsed.slice(0, 2)) < 9) {
|
|
vElapsed = vElapsed.slice(1);
|
|
}
|
|
} else if (vElapsed.length == 8) {
|
|
vElapsed = vElapsed.slice(4);
|
|
}
|
|
let vDuration = getState(id + '.DURATION').val;
|
|
if (vDuration.length == 5) {
|
|
if (parseInt(vDuration.slice(0, 2)) < 9) {
|
|
vDuration = vDuration.slice(1);
|
|
}
|
|
} else if (vDuration.length == 8) {
|
|
vDuration = vDuration.slice(4);
|
|
}
|
|
title = title + ' (' + vElapsed + '|' + vDuration + ')';
|
|
} else if (v2Adapter == 'bosesoundtouch') {
|
|
if (Debug) log(getState(id + '.ELAPSED').val, 'info');
|
|
let elapsedSeconds = parseInt(getState(id + '.ELAPSED').val) % 60 < 10 ? '0' : '';
|
|
let vElapsed = Math.floor(getState(id + '.ELAPSED').val / 60) + ':' + elapsedSeconds + (getState(id + '.ELAPSED').val % 60);
|
|
if (Debug) log(getState(id + '.DURATION').val, 'info');
|
|
let durationSeconds = parseInt(getState(id + '.DURATION').val) % 60 < 10 ? '0' : '';
|
|
let vDuration = Math.floor(getState(id + '.DURATION').val / 60) + ':' + durationSeconds + (getState(id + '.DURATION').val % 60);
|
|
title = title + ' (' + vElapsed + '|' + vDuration + ')';
|
|
} else if (v2Adapter == 'mpd') {
|
|
let vElapsed: string = getState(id + '.ELAPSED').val;
|
|
let vDuration: string = getState(id + '.DURATION').val;
|
|
title = title + ' (' + vElapsed + '|' + vDuration + ')';
|
|
if (getState(id + '.STATE').val === 'stop') {
|
|
title = '(00:00|00:00)';
|
|
}
|
|
}
|
|
}
|
|
|
|
let author = getState(id + '.ARTIST').val;
|
|
|
|
if (v2Adapter == 'squeezeboxrpc' && author.length == 0) {
|
|
if (existsObject([page.items[0].adapterPlayerInstance, 'Players.', page.items[0].mediaDevice, '.Playlist'].join(''))) {
|
|
if (existsObject([page.items[0].adapterPlayerInstance, 'Players.', page.items[0].mediaDevice, '.Playlist'].join(''))) {
|
|
let lmstracklist = JSON.parse(getState([page.items[0].adapterPlayerInstance, 'Players.', page.items[0].mediaDevice, '.Playlist'].join('')).val);
|
|
let currentIndex: number = parseInt(getState([page.items[0].adapterPlayerInstance, 'Players.', page.items[0].mediaDevice, '.PlaylistCurrentIndex'].join('')).val);
|
|
author = lmstracklist[currentIndex].Artist + '|' + lmstracklist[currentIndex].Album;
|
|
if (author.length > 37) {
|
|
author = author.slice(0, 37) + '...';
|
|
}
|
|
let elapsedTime: number = parseInt(getState([page.items[0].adapterPlayerInstance, 'Players.', page.items[0].mediaDevice, '.Time'].join('')).val);
|
|
let elapsedSeconds = elapsedTime % 60 < 10 ? '0' : '';
|
|
let vElapsed = Math.floor(elapsedTime / 60) + ':' + elapsedSeconds + (elapsedTime % 60);
|
|
|
|
let durationSeconds = parseInt(lmstracklist[currentIndex].Duration) % 60 < 10 ? '0' : '';
|
|
let vDuration = Math.floor(parseInt(lmstracklist[currentIndex].Duration) / 60) + ':' + durationSeconds + (parseInt(lmstracklist[currentIndex].Duration) % 60);
|
|
title = lmstracklist[currentIndex].title;
|
|
if (title.length > 25) {
|
|
title = title.slice(0, 25) + '...';
|
|
}
|
|
title = title + ' (' + vElapsed + '|' + vDuration + ')';
|
|
}
|
|
}
|
|
}
|
|
|
|
// Settings >>Aktualisierungsintervall für Statusinformationen<< = 1 !
|
|
// If the refresh time is set to 1 second in the spotify-premium.X instance,
|
|
// the elapsed refresh bug '00:00' is not visible
|
|
if (v2Adapter == 'spotify-premium') {
|
|
let vElapsed: string = getState(id + '.ELAPSED').val;
|
|
if (vElapsed.substring(0, 1) == '0') {
|
|
vElapsed = vElapsed.slice(1);
|
|
}
|
|
let vDuration: string = getState(id + '.DURATION').val;
|
|
if (vDuration.substring(0, 1) == '0') {
|
|
vDuration = vDuration.slice(1);
|
|
}
|
|
title = title + ' (' + vElapsed + '|' + vDuration + ')';
|
|
if (title == ' (0:00|0:00)') {
|
|
title = '';
|
|
}
|
|
}
|
|
|
|
let shuffle = getState(id + '.SHUFFLE').val;
|
|
|
|
//New Adapter/Player
|
|
let media_icon = Icons.GetIcon('playlist-music');
|
|
|
|
//Spotify-Premium
|
|
if (v2Adapter == 'spotify-premium') {
|
|
media_icon = Icons.GetIcon('spotify');
|
|
if (page.items[0].playerMediaIcon !== undefined) {
|
|
if (page.items[0].playerMediaIcon == 'logo-spotify') {
|
|
media_icon = page.items[0].playerMediaIcon;
|
|
} else {
|
|
media_icon = Icons.GetIcon(page.items[0].playerMediaIcon);
|
|
}
|
|
}
|
|
name = getState(id + '.CONTEXT_DESCRIPTION').val;
|
|
let nameLength = name.length;
|
|
if (name.substring(0, 17) == 'Playlist: This Is') {
|
|
name = name.slice(18, 34) + '...';
|
|
} else if (name.substring(0, 9) == 'Playlist:') {
|
|
name = name.slice(10, 26) + '...';
|
|
} else if (name.substring(0, 6) == 'Album:') {
|
|
name = name.slice(7, 23) + '...';
|
|
} else if (name.substring(0, 6) == 'Track:') {
|
|
name = name.slice(7, 23) + '...';
|
|
} else if (name.substring(0, 7) == 'Artist:') {
|
|
name = name.slice(8, 24) + '...';
|
|
}
|
|
if (nameLength == 0) {
|
|
name = 'Spotify-Premium';
|
|
}
|
|
author = getState(id + '.ARTIST').val + ' | ' + getState(id + '.ALBUM').val;
|
|
if (author.length > 37) {
|
|
author = author.slice(0, 37) + '...';
|
|
}
|
|
if (getState(id + '.ARTIST').val.length == 0) {
|
|
author = findLocale('media', 'no_music_to_control');
|
|
}
|
|
}
|
|
|
|
//Sonos
|
|
if (v2Adapter == 'sonos') {
|
|
media_icon = Icons.GetIcon('alpha-s-circle');
|
|
if (page.items[0].playerMediaIcon !== undefined) {
|
|
if (page.items[0].playerMediaIcon == 'logo-sonos') {
|
|
media_icon = page.items[0].playerMediaIcon;
|
|
} else {
|
|
media_icon = Icons.GetIcon(page.items[0].playerMediaIcon);
|
|
}
|
|
}
|
|
name = getState(id + '.CONTEXT_DESCRIPTION').val;
|
|
let nameLenght = name.length;
|
|
if (nameLenght == 0) {
|
|
name = page.heading;
|
|
} else if (nameLenght > 16) {
|
|
name = name.slice(0, 16) + '...';
|
|
}
|
|
if (getState(id + '.ALBUM').val.length > 0) {
|
|
author = getState(id + '.ARTIST').val + ' | ' + getState(id + '.ALBUM').val;
|
|
if (author.length > 37) {
|
|
author = author.slice(0, 37) + '...';
|
|
}
|
|
} else {
|
|
author = getState(id + '.ARTIST').val;
|
|
}
|
|
if (getState(id + '.ARTIST').val.length == 0) {
|
|
author = findLocale('media', 'no_music_to_control');
|
|
}
|
|
}
|
|
|
|
//Bose Soundtouch
|
|
if (v2Adapter == 'bosesoundtouch') {
|
|
media_icon = Icons.GetIcon('alpha-b-circle');
|
|
if (page.items[0].playerMediaIcon !== undefined) {
|
|
if (page.items[0].playerMediaIcon == 'logo-bose') {
|
|
media_icon = page.items[0].playerMediaIcon;
|
|
} else {
|
|
media_icon = Icons.GetIcon(page.items[0].playerMediaIcon);
|
|
}
|
|
}
|
|
name = page.heading;
|
|
|
|
if (getState(id + '.ALBUM').val.length > 0) {
|
|
author = getState(id + '.ARTIST').val + ' | ' + getState(id + '.ALBUM').val;
|
|
if (author.length > 37) {
|
|
author = author.slice(0, 37) + '...';
|
|
}
|
|
} else {
|
|
author = getState(id + '.ARTIST').val;
|
|
}
|
|
if (getState(id + '.ARTIST').val.length == 0) {
|
|
author = findLocale('media', 'no_music_to_control');
|
|
}
|
|
}
|
|
|
|
//Logitech Squeezebox RPC
|
|
if (v2Adapter == 'squeezeboxrpc') {
|
|
media_icon = Icons.GetIcon('dlna');
|
|
if (page.items[0].playerMediaIcon !== undefined) {
|
|
if (page.items[0].playerMediaIcon == 'logo-dnla') {
|
|
media_icon = page.items[0].playerMediaIcon;
|
|
} else {
|
|
media_icon = Icons.GetIcon(page.items[0].playerMediaIcon);
|
|
}
|
|
}
|
|
if (name.length == 0) {
|
|
name = page.heading;
|
|
} else if (name.length > 16) {
|
|
name = name.slice(0, 16) + '...';
|
|
}
|
|
}
|
|
|
|
//Alexa2
|
|
if (v2Adapter == 'alexa2') {
|
|
media_icon = Icons.GetIcon('alpha-a-circle');
|
|
if (page.items[0].playerMediaIcon !== undefined) {
|
|
if (page.items[0].playerMediaIcon == 'logo-alexa') {
|
|
media_icon = page.items[0].playerMediaIcon;
|
|
} else {
|
|
media_icon = Icons.GetIcon(page.items[0].playerMediaIcon);
|
|
}
|
|
}
|
|
name = getState(id + '.ALBUM').val;
|
|
let nameLength = name.length;
|
|
if (name.substring(0, 9) == 'Playlist:') {
|
|
name = name.slice(10, 26) + '...';
|
|
} else if (name.substring(0, 6) == 'Album:') {
|
|
name = name.slice(7, 23) + '...';
|
|
} else if (name.substring(0, 6) == 'Track') {
|
|
name = 'Alexa Player';
|
|
}
|
|
if (nameLength == 0) {
|
|
name = 'Alexa Player';
|
|
} else {
|
|
name = name.slice(0, 16) + '...';
|
|
}
|
|
author = getState(id + '.ARTIST').val + ' | ' + getState(id + '.ALBUM').val;
|
|
if (author.length > 30) {
|
|
author = getState(id + '.ARTIST').val;
|
|
}
|
|
if (getState(id + '.ARTIST').val.length == 0) {
|
|
author = findLocale('media', 'no_music_to_control');
|
|
}
|
|
}
|
|
|
|
//Volumio
|
|
if (v2Adapter == 'volumio') {
|
|
media_icon = Icons.GetIcon('clock-time-twelve-outline');
|
|
if (page.items[0].playerMediaIcon !== undefined) {
|
|
if (page.items[0].playerMediaIcon == 'logo-volumio') {
|
|
media_icon = page.items[0].playerMediaIcon;
|
|
} else {
|
|
media_icon = Icons.GetIcon(page.items[0].playerMediaIcon);
|
|
}
|
|
}
|
|
if (name != undefined) {
|
|
author = author + ' | ' + name;
|
|
}
|
|
name = page.heading;
|
|
}
|
|
|
|
//MPD
|
|
if (v2Adapter == 'mpd') {
|
|
media_icon = Icons.GetIcon('alpha-m-circle');
|
|
if (page.items[0].playerMediaIcon !== undefined) {
|
|
if (page.items[0].playerMediaIcon == 'logo-mpd') {
|
|
media_icon = page.items[0].playerMediaIcon;
|
|
} else {
|
|
media_icon = Icons.GetIcon(page.items[0].playerMediaIcon);
|
|
}
|
|
}
|
|
if (getState(id + '.ALBUM').val.length > 0) {
|
|
author = getState(id + '.ARTIST').val + ' | ' + getState(id + '.ALBUM').val;
|
|
if (author.length > 37) {
|
|
author = author.slice(0, 37) + '...';
|
|
}
|
|
} else {
|
|
author = getState(id + '.ARTIST').val;
|
|
}
|
|
if (getState(id + '.ARTIST').val.length == 0) {
|
|
author = findLocale('media', 'no_music_to_control');
|
|
}
|
|
name = page.heading;
|
|
}
|
|
|
|
let volume = scale(getState(id + '.VOLUME').val, activePage!.items[0]!.minValue ?? 0, activePage!.items[0]!.maxValue ?? 100, 100, 0);
|
|
let iconplaypause = Icons.GetIcon('pause'); //pause
|
|
let shuffle_icon = Icons.GetIcon('shuffle-variant'); //shuffle
|
|
let onoffbutton = 1374;
|
|
|
|
if (shuffle == 'off' || shuffle == false || shuffle == 0 || shuffle == 'false') {
|
|
shuffle_icon = Icons.GetIcon('shuffle-disabled'); //shuffle
|
|
}
|
|
|
|
// Todo: Refresh automatisieren und dafür wieder Shuffle nutzen
|
|
//if (v2Adapter == 'volumio') { shuffle_icon = Icons.GetIcon('shuffle-disabled'); } //Volumio: refresh playlist
|
|
|
|
//For all players
|
|
if (getState(id + '.STATE').val) {
|
|
onoffbutton = 65535;
|
|
iconplaypause = Icons.GetIcon('pause'); //pause
|
|
} else {
|
|
iconplaypause = Icons.GetIcon('play'); //play
|
|
}
|
|
|
|
//Ausnahme für squeezebox, da State = Power
|
|
if (v2Adapter == 'squeezeboxrpc') {
|
|
if (getState(id + '.PAUSE').val === false) {
|
|
onoffbutton = 65535;
|
|
iconplaypause = Icons.GetIcon('pause'); //pause
|
|
} else {
|
|
iconplaypause = Icons.GetIcon('play'); //play
|
|
}
|
|
}
|
|
|
|
//Ausnahme für mpd, da State = play, pause,....
|
|
if (v2Adapter == 'mpd') {
|
|
if (getState(id + '.STATE').val === 'play') {
|
|
onoffbutton = 65535;
|
|
iconplaypause = Icons.GetIcon('pause'); //pause
|
|
} else if (getState(id + '.STATE').val === 'pause') {
|
|
iconplaypause = Icons.GetIcon('play'); //play
|
|
} else if (getState(id + '.STATE').val === 'stop') {
|
|
onoffbutton = 1374;
|
|
iconplaypause = Icons.GetIcon('play'); //play
|
|
}
|
|
|
|
}
|
|
|
|
//Ausnahme Volumio: status = string: play, pause, stop usw.
|
|
if (v2Adapter == 'volumio') {
|
|
if (getState(id + '.status').val == 'play') {
|
|
onoffbutton = 65535;
|
|
iconplaypause = Icons.GetIcon('pause'); //pause
|
|
} else {
|
|
iconplaypause = Icons.GetIcon('play'); //play
|
|
}
|
|
}
|
|
|
|
let currentSpeaker: string = findLocale('media', 'no_speaker_found');
|
|
|
|
if (v2Adapter == 'alexa2') {
|
|
currentSpeaker = getState([page.items[0].adapterPlayerInstance, 'Echo-Devices.', page.items[0].mediaDevice, '.Info.name'].join('')).val;
|
|
} else if (v2Adapter == 'spotify-premium') {
|
|
currentSpeaker = getState([page.items[0].adapterPlayerInstance, 'player.device.name'].join('')).val;
|
|
} else if (v2Adapter == 'sonos') {
|
|
currentSpeaker = getState([page.items[0].adapterPlayerInstance, 'root.', page.items[0].mediaDevice, '.members'].join('')).val;
|
|
} else if (v2Adapter == 'squeezeboxrpc') {
|
|
currentSpeaker = getState([page.items[0].adapterPlayerInstance, 'Players.', page.items[0].mediaDevice, '.Playername'].join('')).val;
|
|
} else if (v2Adapter == 'bosesoundtouch') {
|
|
currentSpeaker = getState([page.items[0].adapterPlayerInstance, 'deviceInfo.name'].join('')).val;
|
|
} else if (v2Adapter == 'volumio') {
|
|
currentSpeaker = getState([page.items[0].adapterPlayerInstance, 'info.name'].join('')).val;
|
|
} else if (v2Adapter == 'mpd') {
|
|
currentSpeaker = v1Adapter[0] + '.' + v1Adapter[1];
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------------
|
|
// All Alexa devices (the online / player and commands directory is available) are listed and linked below
|
|
// If the constant alexaSpeakerList contains at least one entry, the constant is used - otherwise all devices from the Alexa adapter
|
|
let speakerListArray: string[] = [];
|
|
if (page.items[0].speakerList && page.items[0].speakerList.length > 0) {
|
|
for (let i_index in page.items[0].speakerList) {
|
|
speakerListArray.push(page.items[0].speakerList[i_index]);
|
|
}
|
|
} else if (v2Adapter == 'squeezeboxrpc') {
|
|
// Beim Squeezeboxrpc ist jeder Player ein eigener Knoten im Objektbaum. Somit werden einzelne Aliase benötigt.
|
|
const squeezeboxPlayerQuery: iobJS.QueryResult = $('channel[state.id=' + page.items[0].adapterPlayerInstance + '.Players.*.Playername]');
|
|
squeezeboxPlayerQuery.each((playerId: string, playerIndex: number) => {
|
|
speakerListArray.push(getState(playerId).val);
|
|
page.items[0].speakerList = speakerListArray;
|
|
});
|
|
} else if (v2Adapter == 'spotify-premium') {
|
|
// All possible Devices if page.items[0].speakerList empty
|
|
if (Debug) log(getState(page.items[0].adapterPlayerInstance + 'devices.availableDeviceListString').val);
|
|
speakerListArray = getState(page.items[0].adapterPlayerInstance + 'devices.availableDeviceListString').val.split(';');
|
|
page.items[0].speakerList = speakerListArray;
|
|
} else if (v2Adapter == 'mpd') {
|
|
// All possible Devices if page.items[0].speakerList empty
|
|
page.items[0].speakerList[0] = v1Adapter[0] + '.' + v1Adapter[1];
|
|
} else {
|
|
let i_list = Array.prototype.slice.apply($('[state.id="' + page.items[0].adapterPlayerInstance + 'Echo-Devices.*.Info.name"]'));
|
|
for (let i_index in i_list) {
|
|
let i = i_list[i_index];
|
|
let deviceId = i;
|
|
deviceId = deviceId.split('.');
|
|
if (
|
|
getState([page.items[0].adapterPlayerInstance, 'Echo-Devices.', deviceId[3], '.online'].join('')).val &&
|
|
existsObject([page.items[0].adapterPlayerInstance, 'Echo-Devices.', deviceId[3], '.Player'].join('')) &&
|
|
existsObject([page.items[0].adapterPlayerInstance, 'Echo-Devices.', deviceId[3], '.Commands'].join(''))
|
|
) {
|
|
speakerListArray.push(getState(i).val);
|
|
}
|
|
}
|
|
}
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
|
|
let colMediaIcon = page.items[0].colorMediaIcon != undefined ? page.items[0].colorMediaIcon : White;
|
|
let colMediaTitle = page.items[0].colorMediaTitle != undefined ? page.items[0].colorMediaTitle : White;
|
|
let colMediaArtist = page.items[0].colorMediaArtist != undefined ? page.items[0].colorMediaArtist : White;
|
|
|
|
//InSel Speaker
|
|
let speakerListString: string = '~~~~~~';
|
|
let speakerListIconCol = rgb_dec565(HMIOff);
|
|
if (speakerListArray.length > 0) {
|
|
speakerListIconCol = rgb_dec565(HMIOn);
|
|
speakerListString = 'input_sel' + '~' + tid + '?speakerlist' + '~' + Icons.GetIcon('speaker') + '~' + speakerListIconCol + '~' + findLocale('media', 'speaker') + '~' + 'media0~';
|
|
}
|
|
|
|
//InSel Playlist
|
|
let playListString: string = '~~~~~~';
|
|
let playListIconCol = rgb_dec565(HMIOff);
|
|
if (page.items[0].playList != undefined) {
|
|
/* Volumio: get actual playlist if empty */
|
|
if (v2Adapter == 'volumio') {
|
|
if (page.items[0].playList.length == 0) {
|
|
let urlString: string = `${getState(vInstance + 'info.host').val}/api/listplaylists`;
|
|
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(JSON.stringify(response.data), 'info');
|
|
}
|
|
page.items[0].playList = JSON.parse(JSON.stringify(response.data));
|
|
if (Debug) log('volumio-playlist: ' + page.items[0].playList, 'info');
|
|
} else {
|
|
log('Axios Status - get_volumio-playlist: ' + response.state, 'warn');
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
log(error, 'warn');
|
|
});
|
|
}
|
|
/* Spotify: get all playlists if empty */
|
|
} else if (v2Adapter == 'spotify-premium') {
|
|
page.items[0].playList = getState(page.items[0].adapterPlayerInstance + 'playlists.playlistListString').val.split(';');
|
|
} else if (v2Adapter == 'mpd') {
|
|
let tempPL = getState(page.items[0].adapterPlayerInstance + 'listplaylists').val;
|
|
tempPL = tempPL.replace('[', '');
|
|
tempPL = tempPL.replace(']', '');
|
|
tempPL = tempPL.replaceAll('"', '');
|
|
page.items[0].playList = tempPL.split(',');
|
|
}
|
|
playListIconCol = rgb_dec565(HMIOn);
|
|
playListString =
|
|
'input_sel' +
|
|
'~' +
|
|
tid +
|
|
'?playlist' +
|
|
'~' +
|
|
Icons.GetIcon('playlist-play') +
|
|
'~' +
|
|
playListIconCol +
|
|
'~' +
|
|
//'PlayL ' + page.heading + '~' +
|
|
findLocale('media', 'playlist') +
|
|
'~' +
|
|
'media1~';
|
|
}
|
|
|
|
//InSel Tracklist
|
|
globalTracklist = '';
|
|
|
|
let trackListString: string = '~~~~~~';
|
|
let trackListIconCol = rgb_dec565(HMIOff);
|
|
if (v2Adapter == 'volumio') {
|
|
/* Volumio: get queue */
|
|
setTimeout(async function () {
|
|
let urlString: string = `${getState(vInstance + 'info.host').val}/api/getQueue`;
|
|
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(JSON.stringify(response.data), 'info');
|
|
}
|
|
const QUEUELIST = JSON.parse(JSON.stringify(response.data));
|
|
page.items[0].globalTracklist = QUEUELIST.queue;
|
|
if (Debug) {
|
|
for (let i_index in QUEUELIST.queue) {
|
|
log('volumio-queue: ' + QUEUELIST.queue[i_index], 'info');
|
|
}
|
|
}
|
|
} else {
|
|
log('Axios Status - get_volumio-queue: ' + response.state, 'warn');
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
log(error, 'warn');
|
|
});
|
|
}, 2000);
|
|
globalTracklist = page.items[0].globalTracklist;
|
|
} else if (v2Adapter == 'squeezeboxrpc' && existsObject([page.items[0].adapterPlayerInstance, 'Players.', page.items[0].mediaDevice, '.Playlist'].join(''))) {
|
|
let lmstracklist = JSON.parse(getState([page.items[0].adapterPlayerInstance, 'Players.', page.items[0].mediaDevice, '.Playlist'].join('')).val);
|
|
globalTracklist = lmstracklist;
|
|
} else if (v2Adapter == 'sonos' && existsObject([page.items[0].adapterPlayerInstance, 'root.', page.items[0].mediaDevice, '.playlist_set'].join(''))) {
|
|
let lmstracklist = getState([page.items[0].adapterPlayerInstance, 'root.', page.items[0].mediaDevice, '.queue'].join('')).val;
|
|
lmstracklist = lmstracklist.replace(/\s*[\[{(].*?[)}\]]\s*/g, '');
|
|
let lmstracklistTemp = lmstracklist.split(', ');
|
|
let trackList: string = '[';
|
|
if (getState(page.items[0].adapterPlayerInstance + 'root.' + page.items[0].mediaDevice + '.current_type').val == 0) {
|
|
for (let i = 0; i < lmstracklistTemp.length; i++) {
|
|
let trackTemp = lmstracklistTemp[i].split(' - ');
|
|
trackList = trackList + '{"id":"' + i + '","name":"' + trackTemp[0] + '","title":"' + trackTemp[1] + '"}';
|
|
if (i < lmstracklistTemp.length - 1) {
|
|
trackList = trackList + ',';
|
|
}
|
|
}
|
|
}
|
|
trackList = trackList + ']';
|
|
if (Debug) log(trackList, 'info');
|
|
globalTracklist = trackList;
|
|
} else if (v2Adapter == 'spotify-premium') {
|
|
try {
|
|
let tempTrackList = JSON.parse(getState(page.items[0].adapterPlayerInstance + 'player.playlist.trackListArray').val);
|
|
globalTracklist = tempTrackList;
|
|
} catch {
|
|
log('Hello Mr. Developer something went wrong in tracklist!', 'debug');
|
|
}
|
|
} else if (v2Adapter == 'mpd') {
|
|
try {
|
|
let tempTrackList = JSON.parse(getState(page.items[0].adapterPlayerInstance + 'playlist_list').val);
|
|
globalTracklist = tempTrackList;
|
|
} catch {
|
|
log('Hello Mr. Developer something went wrong in tracklist!', 'debug');
|
|
}
|
|
}
|
|
|
|
if (globalTracklist != null && globalTracklist.length != 0) {
|
|
trackListIconCol = rgb_dec565(HMIOn);
|
|
trackListString =
|
|
'input_sel' + '~' + tid + '?tracklist' + '~' + Icons.GetIcon('animation-play-outline') + '~' + trackListIconCol + '~' + findLocale('media', 'tracklist') + '~' + 'media2~';
|
|
}
|
|
|
|
// InSel/Slider EQ
|
|
let equalizerListString: string = '~~~~~~';
|
|
let equalizerListIconCol = rgb_dec565(HMIOff);
|
|
|
|
if (page.items[0].equalizerList != undefined) {
|
|
equalizerListIconCol = rgb_dec565(HMIOn);
|
|
equalizerListString =
|
|
'input_sel' + '~' + tid + '?equalizer' + '~' + Icons.GetIcon('equalizer-outline') + '~' + equalizerListIconCol + '~' + findLocale('media', 'equalizer') + '~' + 'media3~';
|
|
} else if (page.items[0].equalizerSlider != undefined && v2Adapter == 'alexa2') {
|
|
equalizerListIconCol = rgb_dec565(HMIOn);
|
|
equalizerListString =
|
|
'slider' + '~' + tid + '~' + Icons.GetIcon('equalizer-outline') + '~' + equalizerListIconCol + '~' + findLocale('media', 'equalizer') + '~' + 'media3~';
|
|
} else if (page.items[0].equalizerList == undefined && v2Adapter == 'sonos') {
|
|
let equalizerListIconCol = rgb_dec565(HMIOn);
|
|
//equalizerListString is used for favariteList
|
|
equalizerListString =
|
|
'input_sel' + '~' + tid + '?favorites' + '~' + Icons.GetIcon('playlist-star') + '~' + equalizerListIconCol + '~' + findLocale('media', 'favorites') + '~' + 'media3~';
|
|
}
|
|
|
|
//Repeat Control Button
|
|
let repeatIcon = Icons.GetIcon('repeat-variant');
|
|
let repeatIconCol = rgb_dec565(HMIOff);
|
|
let repeatButtonString: string = '~~~~~~';
|
|
|
|
if (v2Adapter == 'spotify-premium') {
|
|
if (getState(id + '.REPEAT').val == 'context') {
|
|
repeatIcon = Icons.GetIcon('repeat-variant');
|
|
repeatIconCol = rgb_dec565(HMIOn);
|
|
} else if (getState(id + '.REPEAT').val == 'track') {
|
|
repeatIcon = Icons.GetIcon('repeat-once');
|
|
repeatIconCol = rgb_dec565(HMIOn);
|
|
}
|
|
} else if (v2Adapter == 'alexa2') {
|
|
if (getState(id + '.REPEAT').val == true) {
|
|
repeatIcon = Icons.GetIcon('repeat-variant');
|
|
repeatIconCol = rgb_dec565(HMIOn);
|
|
}
|
|
} else if (v2Adapter == 'sonos') {
|
|
if (getState(id + '.REPEAT').val == 1) {
|
|
repeatIcon = Icons.GetIcon('repeat-variant');
|
|
repeatIconCol = rgb_dec565(HMIOn);
|
|
} else if (getState(id + '.REPEAT').val == 2) {
|
|
repeatIcon = Icons.GetIcon('repeat-once');
|
|
repeatIconCol = rgb_dec565(HMIOn);
|
|
}
|
|
} else if (v2Adapter == 'bosesoundtouch') {
|
|
if (getState(id + '.REPEAT').val == 'REPEAT_ALL') {
|
|
repeatIcon = Icons.GetIcon('repeat-variant');
|
|
repeatIconCol = rgb_dec565(HMIOn);
|
|
} else if (getState(id + '.REPEAT').val == 'REPEAT_ONE') {
|
|
repeatIcon = Icons.GetIcon('repeat-once');
|
|
repeatIconCol = rgb_dec565(HMIOn);
|
|
}
|
|
} else if (v2Adapter == 'squeezeboxrpc') {
|
|
if (getState(id + '.REPEAT').val == 1) {
|
|
repeatIcon = Icons.GetIcon('repeat-once');
|
|
repeatIconCol = rgb_dec565(HMIOn);
|
|
} else if (getState(id + '.REPEAT').val == 2) {
|
|
repeatIcon = Icons.GetIcon('repeat');
|
|
repeatIconCol = rgb_dec565(HMIOn);
|
|
}
|
|
} else if (v2Adapter == 'volumio') {
|
|
/* Volumio: only Repeat true/false with API */
|
|
if (getState(id + '.REPEAT').val == true) {
|
|
repeatIcon = Icons.GetIcon('repeat-variant');
|
|
repeatIconCol = rgb_dec565(HMIOn);
|
|
}
|
|
} else if (v2Adapter == 'mpd') {
|
|
if (getState(id + '.REPEAT').val == true && getState(id + '.SINGLE').val == 0) {
|
|
repeatIcon = Icons.GetIcon('repeat');
|
|
repeatIconCol = rgb_dec565(HMIOn);
|
|
} else if (getState(id + '.REPEAT').val == true && getState(id + '.SINGLE').val == 1) {
|
|
repeatIcon = Icons.GetIcon('repeat-once');
|
|
repeatIconCol = rgb_dec565(HMIOn);
|
|
}
|
|
}
|
|
|
|
if (v2Adapter == 'spotify-premium' || v2Adapter == 'alexa2' || v2Adapter == 'sonos' || v2Adapter == 'bosesoundtouch' || v2Adapter == 'volumio' || v2Adapter == 'squeezeboxrpc' || v2Adapter == 'mpd') {
|
|
repeatButtonString = 'button' + '~' + tid + '?repeat' + '~' + repeatIcon + '~' + repeatIconCol + '~' + 'Repeat' + '~' + 'media4';
|
|
}
|
|
|
|
//popUp Tools
|
|
let toolsString: string = '~~~~~~';
|
|
let toolsIconCol = rgb_dec565(colMediaIcon);
|
|
if (v2Adapter == 'sonos' || v2Adapter == 'mpd') {
|
|
if (page.items[0].crossfade == undefined || page.items[0].crossfade == false) {
|
|
toolsString = 'input_sel' + '~' + tid + '?seek' + '~' + media_icon + '~' + toolsIconCol + '~' + findLocale('media', 'seek') + '~' + 'media5~';
|
|
} else {
|
|
toolsString = 'input_sel' + '~' + tid + '?crossfade' + '~' + media_icon + '~' + toolsIconCol + '~' + findLocale('media', 'crossfade') + '~' + 'media5~';
|
|
}
|
|
} else if (v2Adapter == 'squeezeboxrpc') {
|
|
if (page.items[0].crossfade == undefined || page.items[0].crossfade == false) {
|
|
toolsString = 'input_sel' + '~' + tid + '?seek' + '~' + media_icon + '~' + toolsIconCol + '~' + findLocale('media', 'seek') + '~' + 'media5~';
|
|
}
|
|
} else if (v2Adapter == 'spotify-premium') {
|
|
if (page.items[0].crossfade == undefined || page.items[0].crossfade == false) {
|
|
toolsString = 'input_sel' + '~' + tid + '?seek' + '~' + media_icon + '~' + toolsIconCol + '~' + findLocale('media', 'seek') + '~' + 'media5~';
|
|
}
|
|
} else {
|
|
toolsString = 'button' + '~' + tid + '' + '~' + media_icon + '~' + toolsIconCol + '~' + findLocale('media', 'tools') + '~' + 'media5~';
|
|
}
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpd~' + //entityUpd
|
|
name +
|
|
'~' + //heading
|
|
getNavigationString(pageId) +
|
|
'~' + //navigation
|
|
tid +
|
|
'~' + //internalNameEntiy
|
|
title +
|
|
'~' + //title
|
|
rgb_dec565(colMediaTitle) +
|
|
'~' + //titleColor
|
|
author +
|
|
'~' + //author
|
|
rgb_dec565(colMediaArtist) +
|
|
'~' + //authorColor
|
|
volume +
|
|
'~' + //volume
|
|
iconplaypause +
|
|
'~' + //playpauseicon
|
|
onoffbutton +
|
|
'~' + //On/Off Button Color
|
|
shuffle_icon +
|
|
'~' + //iconShuffle --> Code
|
|
toolsString +
|
|
speakerListString +
|
|
playListString +
|
|
trackListString +
|
|
equalizerListString +
|
|
repeatButtonString,
|
|
});
|
|
}
|
|
if (Debug) {
|
|
log('GenerateMediaPage payload: ' + JSON.stringify(out_msgs), 'info');
|
|
}
|
|
return out_msgs;
|
|
} catch (err: any) {
|
|
log('error at function GenerateMediaPage: ' + err.message, 'warn');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates an automatic alarm alias for the specified entity ID and namespace path.
|
|
*
|
|
* This function sets up an alias for the specified entity ID within the given namespace path to handle automatic alarm configurations.
|
|
*
|
|
* @async
|
|
* @function createAutoAlarmAlias
|
|
* @param {string} id - The ID of the entity to create an alias for.
|
|
* @param {string} nsPath - The namespace path where the alias will be created.
|
|
* @returns {Promise<void>} A promise that resolves when the alias has been created.
|
|
* @throws {Error} If an error occurs during the alias creation.
|
|
*/
|
|
async function createAutoAlarmAlias (id: string, nsPath: string) {
|
|
try {
|
|
if (Debug) {
|
|
log('Alarm Alias Path: ' + id, 'info');
|
|
log('Alarm 0_userdata Path: ' + nsPath, 'info');
|
|
}
|
|
if (autoCreateAlias) {
|
|
if (isSetOptionActive) {
|
|
if (
|
|
existsState(nsPath + '.AlarmPin') == false ||
|
|
existsState(nsPath + '.AlarmState') == false ||
|
|
existsState(nsPath + '.AlarmType') == false ||
|
|
existsState(nsPath + '.PIN_Failed') == false ||
|
|
existsState(nsPath + '.PANEL') == false
|
|
) {
|
|
await createStateAsync(nsPath + '.AlarmPin', '0000', {type: 'string', write: true});
|
|
await createStateAsync(nsPath + '.AlarmState', 'disarmed', {type: 'string', write: false});
|
|
await createStateAsync(nsPath + '.AlarmType', 'D1', {type: 'string', write: false});
|
|
await createStateAsync(nsPath + '.PIN_Failed', 0, {type: 'number', write: false});
|
|
await createStateAsync(nsPath + '.PANEL', NSPanel_Path, {type: 'string', write: false});
|
|
setObject(id, {_id: id, type: 'channel', common: {role: 'sensor.fire.alarm', name: 'alarm'}, native: {}});
|
|
await createAliasAsync(id + '.ACTUAL', nsPath + '.AlarmState', true, {type: 'string', role: 'state', name: 'ACTUAL'});
|
|
await createAliasAsync(id + '.PIN', nsPath + '.AlarmPin', true, {type: 'string', role: 'state', name: 'PIN'});
|
|
await createAliasAsync(id + '.TYPE', nsPath + '.AlarmType', true, {type: 'string', role: 'state', name: 'TYPE'});
|
|
await createAliasAsync(id + '.PIN_Failed', nsPath + '.PIN_Failed', true, {type: 'number', role: 'state', name: 'PIN_Failed'});
|
|
await createAliasAsync(id + '.PANEL', nsPath + '.PANEL', true, {type: 'string', role: 'state', name: 'PANEL'});
|
|
}
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function createAutoAlarmAlias: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates the payload for an alarm page on the NSPanel.
|
|
*
|
|
* This function creates and returns the payload required to display an alarm page on the NSPanel.
|
|
*
|
|
* @function GenerateAlarmPage
|
|
* @param {NSPanel.PageAlarm} page - The alarm page configuration.
|
|
* @returns {NSPanel.Payload[]} The payload array for the alarm page.
|
|
*/
|
|
function GenerateAlarmPage (page: NSPanel.PageAlarm): NSPanel.Payload[] {
|
|
try {
|
|
activePage = page;
|
|
|
|
let id = page.items[0].id;
|
|
let name = page.heading;
|
|
|
|
let out_msgs: NSPanel.Payload[] = [];
|
|
out_msgs.push({payload: 'pageType~cardAlarm'});
|
|
let nsPath = NSPanel_Alarm_Path + 'Alarm';
|
|
|
|
if (page.items[0].autoCreateALias) {
|
|
if (!id) throw new Error('Missing pageItem.id for cardAlarm! Property autoCreateAlias is true!');
|
|
createAutoAlarmAlias(id, nsPath);
|
|
}
|
|
|
|
type AlarmEntityType = 'triggered' | 'armed' | 'disarmed' | 'pending' | 'arming';
|
|
const AlarmEntityElements: AlarmEntityType[] = ['triggered', 'armed', 'disarmed', 'pending', 'arming'];
|
|
|
|
if (existsState(nsPath + '.AlarmPin') && existsState(nsPath + '.AlarmState') && existsState(nsPath + '.AlarmType')) {
|
|
//let entityPin = getState(nsPath + 'AlarmPin').val;
|
|
let entityState: AlarmEntityType = getState(nsPath + '.AlarmState').val as AlarmEntityType;
|
|
if (AlarmEntityElements.indexOf(entityState) == -1) {
|
|
throw new Error(`Invalid value in state ${nsPath}.AlarmPin!`);
|
|
}
|
|
//let entityType = getState(nsPath + 'AlarmType').val;
|
|
let arm1: string, arm2: string, arm3: string, arm4: string;
|
|
let arm1ActionName: NSPanel.ButtonActionType | '',
|
|
arm2ActionName: NSPanel.ButtonActionType | '',
|
|
arm3ActionName: NSPanel.ButtonActionType | '',
|
|
arm4ActionName: NSPanel.ButtonActionType | '';
|
|
let icon = '0';
|
|
let iconcolor = 63488;
|
|
let numpadStatus = 'disable';
|
|
let flashing = 'disable';
|
|
|
|
if (Debug) {
|
|
log('GenerateAlarmPage pageid: ' + id, 'info');
|
|
}
|
|
|
|
if (entityState == 'armed' || entityState == 'triggered') {
|
|
if (page.items[0].actionStringArray !== undefined && page.items[0].actionStringArray[4] !== '') {
|
|
arm1 = page.items[0].actionStringArray[4];
|
|
} else {
|
|
arm1 = findLocale('alarm_control_panel', 'disarm'); //'Deactivate'; //arm1*~*
|
|
}
|
|
arm1ActionName = 'D1'; //arm1ActionName*~*
|
|
arm2 = ''; //arm2*~*
|
|
arm2ActionName = ''; //arm2ActionName*~*
|
|
arm3 = ''; //arm3*~*
|
|
arm3ActionName = ''; //arm3ActionName*~*
|
|
arm4 = ''; //arm4*~*
|
|
arm4ActionName = ''; //arm4ActionName*~*
|
|
} /* if (entityState == 'disarmed' || entityState == 'arming' || entityState == 'pending')*/ else {
|
|
if (page.items[0].actionStringArray !== undefined && page.items[0].actionStringArray[0] !== '') {
|
|
arm1 = page.items[0].actionStringArray[0];
|
|
} else {
|
|
arm1 = formatInSelText(findLocale('alarm_control_panel', 'arm_away')); //'Vollschutz' //arm1*~*
|
|
}
|
|
arm1ActionName = 'A1'; //arm1ActionName*~*
|
|
if (page.items[0].actionStringArray !== undefined && page.items[0].actionStringArray[1] !== '') {
|
|
arm2 = page.items[0].actionStringArray[1];
|
|
} else {
|
|
arm2 = formatInSelText(findLocale('alarm_control_panel', 'arm_home')); //'Zuhause'; //arm2*~*
|
|
}
|
|
arm2ActionName = 'A2'; //arm2ActionName*~*
|
|
if (page.items[0].actionStringArray !== undefined && page.items[0].actionStringArray[2] !== '') {
|
|
arm3 = page.items[0].actionStringArray[2];
|
|
} else {
|
|
arm3 = formatInSelText(findLocale('alarm_control_panel', 'arm_night')); //'Nacht'; //arm3*~*
|
|
}
|
|
arm3ActionName = 'A3'; //arm3ActionName*~*
|
|
if (page.items[0].actionStringArray !== undefined && page.items[0].actionStringArray[3] !== '') {
|
|
arm4 = page.items[0].actionStringArray[3];
|
|
} else {
|
|
arm4 = formatInSelText(findLocale('alarm_control_panel', 'arm_vacation')); //'Besuch'; //arm4*~*
|
|
}
|
|
arm4ActionName = 'A4'; //arm4ActionName*~*
|
|
if (Debug) {
|
|
log('GenerateAlarmPage String for arm1: ' + arm1 + ', arm2: ' + arm2 + ', arm3: ' + arm3 + ', arm4: ' + arm4, 'info');
|
|
}
|
|
}
|
|
|
|
if (entityState == 'armed') {
|
|
icon = Icons.GetIcon('shield-home'); //icon*~*
|
|
iconcolor = 63488; //iconcolor*~*
|
|
numpadStatus = 'enable'; //numpadStatus*~*
|
|
flashing = 'disable'; //flashing*
|
|
}
|
|
if (entityState == 'disarmed') {
|
|
icon = Icons.GetIcon('shield-off'); //icon*~*
|
|
iconcolor = 2016; //iconcolor*~*
|
|
numpadStatus = 'enable'; //numpadStatus*~*
|
|
flashing = 'disable'; //flashing*
|
|
}
|
|
if (entityState == 'arming' || entityState == 'pending') {
|
|
icon = Icons.GetIcon('shield'); //icon*~*
|
|
iconcolor = rgb_dec565({red: 243, green: 179, blue: 0}); //iconcolor*~*
|
|
numpadStatus = 'disable'; //numpadStatus*~*
|
|
flashing = 'enable'; //flashing*
|
|
}
|
|
if (entityState == 'triggered') {
|
|
iconcolor = rgb_dec565({red: 223, green: 76, blue: 30}); //icon*~*
|
|
icon = Icons.GetIcon('bell-ring'); //iconcolor*~*
|
|
numpadStatus = 'enable'; //numpadStatus*~*
|
|
flashing = 'enable'; //flashing*
|
|
}
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpd~' + //entityUpd~*
|
|
name +
|
|
'~' + //heading
|
|
getNavigationString(pageId) +
|
|
'~' + //navigation*~* --> hiddenCardsv
|
|
id +
|
|
'~' + //internalNameEntity*~*
|
|
arm1 +
|
|
'~' + //arm1*~*
|
|
arm1ActionName +
|
|
'~' + //arm1ActionName*~*
|
|
arm2 +
|
|
'~' + //arm2*~*
|
|
arm2ActionName +
|
|
'~' + //arm2ActionName*~*
|
|
arm3 +
|
|
'~' + //arm3*~*
|
|
arm3ActionName +
|
|
'~' + //arm3ActionName*~*
|
|
arm4 +
|
|
'~' + //arm4*~*
|
|
arm4ActionName +
|
|
'~' + //arm4ActionName*~*
|
|
icon +
|
|
'~' + //icon*~*
|
|
iconcolor +
|
|
'~' + //iconcolor*~*
|
|
numpadStatus +
|
|
'~' + //numpadStatus*~*
|
|
flashing, //flashing*
|
|
});
|
|
|
|
if (Debug) {
|
|
log('GenerateAlarmPage payload: ' + JSON.stringify(out_msgs), 'info');
|
|
}
|
|
return out_msgs;
|
|
}
|
|
return [];
|
|
} catch (err: any) {
|
|
log('error at function GenerateAlarmPage: ' + err.message, 'warn');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates an automatic unlock alias for the specified entity ID and datapoint path.
|
|
*
|
|
* This function sets up an alias for the specified entity ID within the given datapoint path to handle automatic unlock configurations.
|
|
*
|
|
* @async
|
|
* @function createAutoUnlockAlias
|
|
* @param {string} id - The ID of the entity to create an alias for.
|
|
* @param {string} dpPath - The datapoint path where the alias will be created.
|
|
* @returns {Promise<void>} A promise that resolves when the alias has been created.
|
|
* @throws {Error} If an error occurs during the alias creation.
|
|
*/
|
|
async function createAutoUnlockAlias (id: string, dpPath: string) {
|
|
try {
|
|
if (Debug) {
|
|
log('Unlock Alias Path: ' + id, 'info');
|
|
log('Unlock 0_userdata Path: ' + dpPath, 'info');
|
|
}
|
|
if (autoCreateAlias) {
|
|
if (isSetOptionActive) {
|
|
if (existsState(dpPath + 'UnlockPin') == false || existsState(dpPath + 'Access') == false) {
|
|
await createStateAsync(dpPath + 'UnlockPin', '0000', {type: 'string', write: true});
|
|
await createStateAsync(dpPath + 'Access', 'false', {type: 'boolean', write: false});
|
|
setObject(id, {_id: id, type: 'channel', common: {role: 'sensor.fire.alarm', name: 'sensor.fire.alarm'}, native: {}});
|
|
await createAliasAsync(id + '.PIN', dpPath + 'UnlockPin', true, {type: 'string', role: 'state', name: 'PIN'});
|
|
await createAliasAsync(id + '.ACTUAL', dpPath + 'Access', true, {type: 'boolean', role: 'sensor.fire.alarm', name: 'ACTUAL'});
|
|
}
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function createAutoUnlockAlias: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates the payload for an unlock page on the NSPanel.
|
|
*
|
|
* This function creates and returns the payload required to display an unlock page on the NSPanel.
|
|
*
|
|
* @function GenerateUnlockPage
|
|
* @param {NSPanel.PageUnlock} page - The unlock page configuration.
|
|
* @returns {NSPanel.Payload[]} The payload array for the unlock page.
|
|
*/
|
|
function GenerateUnlockPage (page: NSPanel.PageUnlock): NSPanel.Payload[] {
|
|
try {
|
|
activePage = page;
|
|
let id = page.items[0].id;
|
|
let name = page.heading;
|
|
|
|
let out_msgs: NSPanel.Payload[] = [];
|
|
out_msgs.push({payload: 'pageType~cardAlarm'});
|
|
|
|
let dpPath: string = '';
|
|
let dpTempPath: any = NSPanel_Path.split('.');
|
|
for (let i = 0; i < dpTempPath.length - 2; i++) {
|
|
dpPath = dpPath + dpTempPath[i] + '.';
|
|
}
|
|
dpPath = dpPath + 'Unlock.';
|
|
|
|
if (page.items[0].autoCreateALias) {
|
|
if (!id) throw new Error('Missing pageItem.id for cardUnlock! Property autoCreateAlias is true!');
|
|
createAutoUnlockAlias(id, dpPath);
|
|
}
|
|
|
|
let unlock1 = findLocale('lock', 'UNLOCK'); //unlock1*~*
|
|
let unlock1ActionName: NSPanel.ButtonActionType | '' = 'U1'; //unlock1ActionName*~*
|
|
|
|
let iconcolor = rgb_dec565({red: 223, green: 76, blue: 30}); //icon*~*
|
|
let icon = Icons.GetIcon('lock-remove'); //iconcolor*~*
|
|
let numpadStatus = 'enable'; //numpadStatus*~*
|
|
let flashing = 'disable'; //flashing*
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpd~' + //entityUpd~*
|
|
name +
|
|
'~' + //heading
|
|
getNavigationString(pageId) +
|
|
'~' + //navigation*~* --> hiddenCardsv
|
|
id +
|
|
'~' + //internalNameEntity*~*
|
|
unlock1 +
|
|
'~' + //unlock1*~*
|
|
unlock1ActionName +
|
|
'~' + //unlock1ActionName*~*
|
|
'~' +
|
|
'~' +
|
|
'~' +
|
|
'~' +
|
|
'~' +
|
|
'~' +
|
|
icon +
|
|
'~' + //icon*~*
|
|
iconcolor +
|
|
'~' + //iconcolor*~*
|
|
numpadStatus +
|
|
'~' + //numpadStatus*~*
|
|
flashing, //flashing*
|
|
});
|
|
|
|
if (Debug) {
|
|
log('GenerateUnlockPage payload: ' + JSON.stringify(out_msgs), 'info');
|
|
}
|
|
return out_msgs;
|
|
} catch (err: any) {
|
|
log('error at function GenerateUnlockPage: ' + err.message, 'warn');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates an automatic QR alias for the specified entity ID and datapoint path.
|
|
*
|
|
* This function sets up an alias for the specified entity ID within the given datapoint path to handle automatic QR configurations.
|
|
*
|
|
* @async
|
|
* @function createAutoQRAlias
|
|
* @param {string} id - The ID of the entity to create an alias for.
|
|
* @param {string} dpPath - The datapoint path where the alias will be created.
|
|
* @returns {Promise<void>} A promise that resolves when the alias has been created.
|
|
* @throws {Error} If an error occurs during the alias creation.
|
|
*/
|
|
async function createAutoQRAlias (id: string, dpPath: string) {
|
|
try {
|
|
if (Debug) {
|
|
log('QRPage Alias Path: ' + id, 'info');
|
|
log('QRPage 0_userdata Path: ' + dpPath, 'info');
|
|
}
|
|
if (autoCreateAlias) {
|
|
if (isSetOptionActive) {
|
|
if (existsState(dpPath + 'Daten') == false) {
|
|
await createStateAsync(dpPath + 'Daten', 'WIFI:T:undefined;S:undefined;P:undefined;H:undefined;', {type: 'string', write: true});
|
|
await createStateAsync(dpPath + 'Switch', false, {type: 'boolean', write: true});
|
|
setObject(id, {_id: id, type: 'channel', common: {role: 'switch.mode.wlan', name: 'QR Page'}, native: {}});
|
|
await createAliasAsync(id + '.ACTUAL', dpPath + 'Daten', true, {type: 'string', role: 'state', name: 'ACTUAL'});
|
|
await createAliasAsync(id + '.SWITCH', dpPath + 'Switch', true, {type: 'boolean', role: 'state', name: 'SWITCH'});
|
|
log('Adjust data for the QR page under ' + dpPath + 'data. Follow the instructions in the wiki.', 'warn');
|
|
}
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function createAutoQRkAlias: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates the payload for a QR page on the NSPanel.
|
|
*
|
|
* This function creates and returns the payload required to display a QR page on the NSPanel.
|
|
*
|
|
* @function GenerateQRPage
|
|
* @param {NSPanel.PageQR} page - The QR page configuration.
|
|
* @returns {NSPanel.Payload[]} The payload array for the QR page.
|
|
*/
|
|
function GenerateQRPage (page: NSPanel.PageQR): NSPanel.Payload[] {
|
|
try {
|
|
activePage = page;
|
|
if (!page.items[0].id) throw new Error('Missing pageItem.id for cardQRPage!');
|
|
let id = page.items[0].id;
|
|
let out_msgs: NSPanel.Payload[] = [];
|
|
out_msgs.push({payload: 'pageType~cardQR'});
|
|
|
|
let dpPath: string = '';
|
|
let dpTempPath: any = NSPanel_Path.split('.');
|
|
for (let i = 0; i < dpTempPath.length - 2; i++) {
|
|
dpPath = dpPath + dpTempPath[i] + '.';
|
|
}
|
|
dpPath = dpPath + 'GuestWiFi.';
|
|
|
|
if (page.items[0].autoCreateALias) {
|
|
createAutoQRAlias(id, dpPath);
|
|
}
|
|
|
|
let o = getObject(id);
|
|
|
|
let heading = page.heading !== undefined ? page.heading : typeof o.common.name === 'object' ? o.common.name.de : o.common.name;
|
|
let textQR = page.items[0].id + '.ACTUAL' !== undefined ? getState(page.items[0].id + '.ACTUAL').val : 'WIFI:T:undefined;S:undefined;P:undefined;H:undefined;';
|
|
let hiddenPWD = false;
|
|
if (page.items[0].hidePassword !== undefined && page.items[0].hidePassword == true) {
|
|
hiddenPWD = true;
|
|
}
|
|
let hiddenSwitch = false;
|
|
if (page.items[0].hideEntity2 !== undefined && page.items[0].hideEntity2 == true) {
|
|
hiddenSwitch = true;
|
|
}
|
|
|
|
const tempstr = textQR.split(';');
|
|
let optionalValue1: any;
|
|
let optionalValue2: any;
|
|
for (let w = 0; w < tempstr.length - 1; w++) {
|
|
if (tempstr[w].substring(5, 6) == 'T') {
|
|
tempstr[w].slice(7) == 'undefined' ? log('Adjust data (T) for the QR page under ' + dpPath + 'data. Follow the instructions in the wiki.', 'warn') : '';
|
|
}
|
|
if (tempstr[w].substring(0, 1) == 'S') {
|
|
tempstr[w].slice(2) == 'undefined'
|
|
? log('Adjust data (S) for the QR page under ' + dpPath + 'data. Follow the instructions in the wiki.', 'warn')
|
|
: (optionalValue1 = tempstr[w].slice(2));
|
|
}
|
|
if (tempstr[w].substring(0, 1) == 'P') {
|
|
optionalValue2 = tempstr[w].slice(2);
|
|
}
|
|
}
|
|
|
|
let type1 = 'text';
|
|
let internalName1 = findLocale('qr', 'ssid');
|
|
let iconId1 = Icons.GetIcon('wifi');
|
|
let iconColor1 = 65535
|
|
let displayName1 = findLocale('qr', 'ssid');
|
|
let type2 = 'text';
|
|
let internalName2 = findLocale('qr', 'password');
|
|
let iconColor2 = 65535;
|
|
let iconId2 = Icons.GetIcon('key');
|
|
let displayName2 = findLocale('qr', 'password');
|
|
|
|
if (existsState(page.items[0].id + '.SWITCH')) {
|
|
iconColor1 = getState(page.items[0].id + '.SWITCH').val ? rgb_dec565(colorScale0) : rgb_dec565(colorScale10);
|
|
}
|
|
|
|
if (hiddenPWD) {
|
|
type2 = 'switch';
|
|
internalName2 = id;
|
|
iconId2 = getState(page.items[0].id + '.SWITCH').val ? Icons.GetIcon('router-wireless') : Icons.GetIcon('router-wireless-off');
|
|
displayName2 = getState(page.items[0].id + '.SWITCH').val ? findLocale('qr', 'Wlan enabled') : findLocale('qr', 'Wlan disabled');
|
|
optionalValue2 = getState(page.items[0].id + '.SWITCH').val ? 1 : 0;
|
|
}
|
|
|
|
if (hiddenSwitch) {
|
|
iconColor2 = defaultBackgroundColorParam;
|
|
type2 = 'text';
|
|
internalName2 = id;
|
|
iconId2 = '';
|
|
displayName2 = '';
|
|
optionalValue2 = '';
|
|
}
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpd~' + //entityUpd
|
|
heading +
|
|
'~' + //heading
|
|
getNavigationString(pageId) +
|
|
'~' + //navigation
|
|
textQR +
|
|
'~' + //textQR
|
|
type1 +
|
|
'~' + //type
|
|
internalName1 +
|
|
'~' + //internalName
|
|
iconId1 +
|
|
'~' + //iconId
|
|
iconColor1 +
|
|
'~' + //iconColor
|
|
displayName1 +
|
|
'~' + //displayName
|
|
optionalValue1 +
|
|
'~' + //optionalValue
|
|
type2 +
|
|
'~' + //type
|
|
internalName2 +
|
|
'~' + //internalName
|
|
iconId2 +
|
|
'~' + //iconId
|
|
iconColor2 +
|
|
'~' + //iconColor
|
|
displayName2 +
|
|
'~' + //displayName
|
|
optionalValue2
|
|
});
|
|
|
|
if (Debug) {
|
|
log('GenerateQRPage payload: ' + JSON.stringify(out_msgs), 'info');
|
|
}
|
|
return out_msgs;
|
|
} catch (err: any) {
|
|
log('error at function GenerateQRPage: ' + err.message, 'warn');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Unsubscribes from all power-related subscriptions.
|
|
*
|
|
* This function removes all active subscriptions related to power entities.
|
|
*
|
|
* @function unsubscribePowerSubscriptions
|
|
*/
|
|
function unsubscribePowerSubscriptions (): void {
|
|
for (let i = 0; i < config.pages.length; i++) {
|
|
const page: NSPanel.PageType = config.pages[i];
|
|
if (isPagePower(page)) {
|
|
let powerID = page.items[0].id;
|
|
unsubscribe(powerID + '.ACTUAL');
|
|
}
|
|
}
|
|
for (let i = 0; i < config.subPages.length; i++) {
|
|
const page: NSPanel.PageType = config.subPages[i];
|
|
if (isPagePower(page)) {
|
|
let powerID = page.items[0].id;
|
|
unsubscribe(powerID + '.ACTUAL');
|
|
}
|
|
}
|
|
if (Debug) log('unsubscribePowerSubscriptions getstartet', 'info');
|
|
}
|
|
|
|
|
|
/**
|
|
* @function subscribePowerSubscriptions
|
|
* @description Subscribes to the power state and registers a change listener.
|
|
* When the power state changes, it triggers a page update after 25 ms.
|
|
* @param {string} id - The ID of the page for which the power state is to be subscribed to.
|
|
* @returns {void}
|
|
*/
|
|
function subscribePowerSubscriptions (id: string): void {
|
|
const subscriptionKey = 'power_' + id + '.ACTUAL';
|
|
if (subscriptions.hasOwnProperty(subscriptionKey)) {
|
|
return;
|
|
}
|
|
subscriptions[subscriptionKey] = on({id: id + '.ACTUAL', change: 'ne'}, async function () {
|
|
(function () {
|
|
if (timeoutPower) {
|
|
clearTimeout(timeoutPower);
|
|
timeoutPower = null;
|
|
}
|
|
})();
|
|
timeoutPower = setTimeout(async function () {
|
|
GeneratePage(activePage!);
|
|
}, 25);
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* @function GeneratePowerPage
|
|
* @description Generates a page with power state and energy usage information.
|
|
* @param {NSPanel.PagePower} page - The page configuration with the power state and energy usage information.
|
|
* @returns {NSPanel.Payload[]} An array of payloads to be sent to the panel.
|
|
*/
|
|
function GeneratePowerPage (page: NSPanel.PagePower): NSPanel.Payload[] {
|
|
try {
|
|
let obj: object = {};
|
|
let demoMode = false;
|
|
if (page.items[0].id === 'DEMO') {
|
|
log('No PageItem defined - cardPower demo mode active', 'info');
|
|
demoMode = true;
|
|
}
|
|
|
|
activePage = page;
|
|
if (Debug) {
|
|
log('GeneratePowerPage PageItem.id = ' + page.items[0].id, 'info');
|
|
}
|
|
|
|
let heading = 'cardPower Example';
|
|
if (!page.items[0].id || typeof page.items[0].id !== 'string') {throw Error('Id ist empty or not a string')}
|
|
if (demoMode != true) {
|
|
let id = page.items[0].id;
|
|
unsubscribePowerSubscriptions();
|
|
|
|
let o = getObject(id);
|
|
heading = page.heading !== undefined ? page.heading : typeof o.common.name === 'object' ? o.common.name.de : o.common.name;
|
|
|
|
obj = JSON.parse(getState(page.items[0].id + '.ACTUAL').val);
|
|
}
|
|
|
|
let out_msgs: NSPanel.Payload[] = [];
|
|
|
|
// Leave the display on if the alwaysOnDisplay parameter is specified (true)
|
|
if (page.type == 'cardPower' && pageCounter == 0 && page.items[0].alwaysOnDisplay != undefined) {
|
|
out_msgs.push({payload: 'pageType~cardPower'});
|
|
if (page.items[0].alwaysOnDisplay != undefined) {
|
|
if (page.items[0].alwaysOnDisplay) {
|
|
pageCounter = 1;
|
|
if (alwaysOn == false) {
|
|
alwaysOn = true;
|
|
SendToPanel({payload: 'timeout~0'});
|
|
subscribePowerSubscriptions(page.items[0].id);
|
|
}
|
|
}
|
|
}
|
|
} else if (page.type == 'cardPower' && pageCounter == 1) {
|
|
subscribePowerSubscriptions(page.items[0].id);
|
|
} else {
|
|
out_msgs.push({payload: 'pageType~cardPower'});
|
|
}
|
|
|
|
if (Debug) {
|
|
log('GeneratePowerPage PageItem.id = ' + page.items[0].id, 'info');
|
|
}
|
|
|
|
//Demo Data if no pageItem present
|
|
let array_icon_color = [White, MSGreen, MSYellow, MSGreen, MSYellow, MSGreen, MSRed];
|
|
let array_icon = ['home', 'battery-charging-60', 'solar-power-variant', 'wind-turbine', 'shape', 'transmission-tower', 'car'];
|
|
let array_powerspeed = ['', '10', '-20', '-40', '-10', '-10', '-50'];
|
|
let array_powerstate = ['', '0,5 kW', '0,9 kW', '2,8 kW', '0,2 kW', '0,1 kW', '4,6 kW'];
|
|
|
|
let arrayColorScale = [colorScale0, colorScale1, colorScale2, colorScale3, colorScale4, colorScale5, colorScale6, colorScale7, colorScale8, colorScale9, colorScale10, White];
|
|
|
|
if (!demoMode) {
|
|
for (let obji = 1; obji < 7; obji++) {
|
|
const color = obj[obji].iconColor !== '' ? obj[obji].iconColor : 0;
|
|
array_icon_color[obji] = arrayColorScale[color];
|
|
array_icon[obji] = obj[obji].icon;
|
|
array_powerspeed[obji] = obj[obji].speed;
|
|
array_powerstate[obji] = obj[obji].value + ' ' + obj[obji].unit;
|
|
}
|
|
array_icon[0] = obj[0].icon;
|
|
array_powerstate[0] = obj[0].value + ' ' + obj[0].unit;
|
|
array_icon_color[0] = arrayColorScale[obj[0].iconColor];
|
|
}
|
|
|
|
let power_string: any = '';
|
|
|
|
for (let i = 0; i < 6; i++) {
|
|
power_string = power_string + '~'; // type (ignored)
|
|
power_string = power_string + '~'; // intNameEntity (ignored)
|
|
power_string = power_string + Icons.GetIcon(array_icon[i + 1]) + '~'; // icon~
|
|
power_string = power_string + rgb_dec565(array_icon_color[i + 1]) + '~'; // icon_color~
|
|
power_string = power_string + '~'; // display (ignored in TS)
|
|
power_string = power_string + array_powerstate[i + 1] + '~'; // optionalValue~
|
|
power_string = power_string + array_powerspeed[i + 1] + '~'; // speed~
|
|
|
|
if (Debug) log(power_string, 'info');
|
|
}
|
|
|
|
power_string = power_string.substring(0, power_string.length - 1);
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpd~' + //entityUpd~*
|
|
heading +
|
|
'~' + //internalNameEntity*~*
|
|
getNavigationString(pageId) +
|
|
'~' + //navigation*~*
|
|
// Home Icon / Value below Home Icon
|
|
'' +
|
|
'~' + // type (ignored)
|
|
'' +
|
|
'~' + // intNameEntity (ignored)
|
|
Icons.GetIcon(array_icon[0]) +
|
|
'~' + // icon
|
|
rgb_dec565(array_icon_color[0]) +
|
|
'~' + // icon_color
|
|
'' +
|
|
'~' + // display (ignored in TS)
|
|
array_powerstate[0] +
|
|
'~' + // optionalValue
|
|
'' +
|
|
'~' + // speed
|
|
// Value above Home Icon
|
|
'' +
|
|
'~' + // type (ignored)
|
|
'' +
|
|
'~' + // intNameEntity (ignored)
|
|
'' +
|
|
'~' + // icon
|
|
'' +
|
|
'~' + // icon_color
|
|
'' +
|
|
'~' + // display (ignored in TS)
|
|
'' +
|
|
'~' + // optionalValue
|
|
'' +
|
|
'~' + // speed~
|
|
// 1st to 6th Item
|
|
power_string,
|
|
});
|
|
if (Debug) log('GeneratePowerPage payload: ' + JSON.stringify(out_msgs), 'info');
|
|
return out_msgs;
|
|
} catch (err: any) {
|
|
log('error at function GeneratePowerPage: ' + err.message, 'warn');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Regular expression pattern to match time values in the format "~<number>:<number>".
|
|
*
|
|
* The pattern matches a tilde (~) followed by one or more digits, a colon (:), and then one or more digits.
|
|
* The second group of digits is captured and can be accessed using the index 1.
|
|
*
|
|
* @type {RegExp}
|
|
* @constant
|
|
*/
|
|
const timeValueRegEx = /\~\d+:(\d+)/g;
|
|
|
|
/**
|
|
* @function GenerateChartPage
|
|
* @description generates a chart page with given page data
|
|
* @param {NSPanel.PageChart} page - the page data
|
|
* @returns {NSPanel.Payload[]} - an array of payloads
|
|
*/
|
|
function GenerateChartPage (page: NSPanel.PageChart): NSPanel.Payload[] {
|
|
try {
|
|
activePage = page;
|
|
|
|
let id = page.items[0].id;
|
|
let out_msgs: NSPanel.Payload[] = [];
|
|
out_msgs.push({payload: 'pageType~' + page.type});
|
|
|
|
let heading = page.heading !== undefined ? page.heading : 'Chart...';
|
|
|
|
const txt = getState(id + '.ACTUAL')?.val;
|
|
if (!txt) {
|
|
throw new Error(`Unable to get the state of ${id}.ACTUAL`);
|
|
}
|
|
|
|
let yAxisTicks: number[] = [];
|
|
|
|
if (!page.items[0].yAxisTicks) {
|
|
const sorted = [...String(txt).matchAll(timeValueRegEx)].map((x) => Number(x[1])).sort((x, y) => (x < y ? -1 : 1));
|
|
if (sorted.length === 0) {
|
|
throw new Error(`Page item ${id} yAxisTicks is undefined and unable to be calculated!`);
|
|
}
|
|
const minValue = sorted[0];
|
|
const maxValue = sorted[sorted.length - 1];
|
|
const tick = Math.max(Number(((maxValue - minValue) / 5).toFixed()), 10);
|
|
|
|
let currentTick = minValue - tick;
|
|
while (currentTick < maxValue + tick) {
|
|
yAxisTicks.push(currentTick);
|
|
currentTick += tick;
|
|
}
|
|
|
|
if (Debug) {
|
|
log(`Calculated yAxisTicks for ${id} (Min: ${minValue}, Max: ${maxValue}, Tick: ${tick}): ${yAxisTicks}`);
|
|
}
|
|
} else {
|
|
yAxisTicks = typeof page.items[0].yAxisTicks === 'string' ? JSON.parse(getState(page.items[0].yAxisTicks).val) : page.items[0].yAxisTicks;
|
|
}
|
|
|
|
if (!page.items[0].onColor) {
|
|
throw new Error(`Page item ${id} onColor is undefined!`);
|
|
}
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpd~' + //entityUpd
|
|
heading +
|
|
'~' + //heading
|
|
getNavigationString(pageId) +
|
|
'~' + //navigation
|
|
rgb_dec565(page.items[0].onColor) +
|
|
'~' + //color
|
|
page.items[0].yAxis +
|
|
'~' +
|
|
yAxisTicks.join(':') +
|
|
'~' +
|
|
txt,
|
|
});
|
|
|
|
if (Debug) log('GenerateChartPage payload: ' + JSON.stringify(out_msgs), 'info');
|
|
return out_msgs;
|
|
} catch (err: any) {
|
|
log('error at function GenerateChartPage: ' + err.message, 'warn');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the value of a state if it exists.
|
|
*
|
|
* This function checks if the specified state exists and sets its value if it does.
|
|
* Optionally, the type and acknowledgment flag can be specified.
|
|
*
|
|
* @function setIfExists
|
|
* @param {string} id - The ID of the state to set.
|
|
* @param {any} value - The value to set for the state.
|
|
* @param {string | null} [type=null] - The type of the state (optional).
|
|
* @param {boolean} [ack=false] - The acknowledgment flag (optional).
|
|
* @returns {boolean} True if the state exists and the value was set, false otherwise.
|
|
*/
|
|
function setIfExists (id: string, value: any, type: string | null = null, ack: boolean = false): boolean {
|
|
try {
|
|
if (type === null) {
|
|
if (existsState(id)) {
|
|
setState(id, value, ack);
|
|
return true;
|
|
}
|
|
} else {
|
|
const obj = getObject(id);
|
|
if (existsState(id) && obj.common.type !== undefined && obj.common.type === type) {
|
|
setState(id, value, ack);
|
|
return true;
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function setIfExists: ' + err.message, 'warn');
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Toggles the state of the specified entity.
|
|
*
|
|
* This function retrieves the current state of the specified entity and toggles its value.
|
|
*
|
|
* @function toggleState
|
|
* @param {string} id - The ID of the entity to toggle.
|
|
* @returns {boolean} True if the state was successfully toggled, false otherwise.
|
|
*/
|
|
function toggleState (id: string): boolean {
|
|
try {
|
|
const obj = getObject(id);
|
|
if (existsState(id) && obj.common.type !== undefined && obj.common.type === 'boolean') {
|
|
setIfExists(id, !getState(id).val);
|
|
return true;
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function toggleState: ' + err.message, 'warn');
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Begin Monobutton
|
|
/**
|
|
* Triggers a button action for the specified entity.
|
|
*
|
|
* This function simulates a button press action for the specified entity by toggling its state.
|
|
*
|
|
* @function triggerButton
|
|
* @param {string} id - The ID of the entity to trigger.
|
|
* @returns {boolean} True if the button action was successfully triggered, false otherwise.
|
|
*/
|
|
function triggerButton (id: string): boolean {
|
|
try {
|
|
let obj = getObject(id);
|
|
if (existsState(id) && obj.common.type !== undefined && obj.common.type === 'boolean') {
|
|
setState(id, true);
|
|
setTimeout(function () {
|
|
setState(id, false);
|
|
}, 250);
|
|
return true;
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function triggerButton: ' + err.message, 'warn');
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// End Monobutton
|
|
|
|
/**
|
|
* Handles button events based on the provided words.
|
|
*
|
|
* This function processes button events by interpreting the provided words and performing the appropriate actions.
|
|
*
|
|
* @function HandleButtonEvent
|
|
* @param {any} words - The words or parameters associated with the button event.
|
|
*/
|
|
function HandleButtonEvent (words: any): void {
|
|
try {
|
|
// Turn off the display if the alwaysOnDisplay parameter was specified
|
|
if (alwaysOn == true) {
|
|
unsubscribePowerSubscriptions();
|
|
unsubscribeMediaSubscriptions();
|
|
}
|
|
|
|
let tempid = words[2].split('?');
|
|
let id = tempid[0];
|
|
let buttonAction: NSPanel.ButtonActionType = words[3] as NSPanel.ButtonActionType;
|
|
let pageItemID: string = '';
|
|
|
|
if (!isNaN(id)) {
|
|
if (activePage!.items[id].id == undefined) throw new Error('Missing pageItem.id in HandleButtonEvent!');
|
|
pageItemID = activePage!.items[id].id!;
|
|
if (Debug) {
|
|
log('HandleButtonEvent activePage: ' + activePage!.items.length + ' id: ' + id + ' tempid: ' + tempid + ' pageItemId: ' + pageItemID);
|
|
}
|
|
id = pageItemID;
|
|
}
|
|
|
|
if (Debug) {
|
|
log('HandleButtonEvent übergebene Werte ' + words[0] + ' - ' + words[1] + ' - ' + words[2] + ' - ' + words[3] + ' - ' + words[4] + ' - PageId: ' + pageId, 'info');
|
|
}
|
|
|
|
if (words[2].substring(0, 8) == 'navigate') {
|
|
let temppage: PageType = eval(words[2].substring(9, words[2].length));
|
|
if (temppage.hiddenByTrigger && valueHiddenCards) {
|
|
log(`Subpage ${words[2].substring(9, words[2].length)} is hidden`);
|
|
return;
|
|
}
|
|
GeneratePage(temppage);
|
|
return;
|
|
}
|
|
|
|
if (words[2] == 'bNext' || words[2] == 'bPrev' || words[2] == 'bUp' || words[2] == 'bHome' || words[2] == 'bSubNext' || words[2] == 'bSubPrev') {
|
|
buttonAction = words[2];
|
|
pageCounter = 0;
|
|
alwaysOn = false;
|
|
SendToPanel({payload: 'timeout~' + getState(NSPanel_Path + 'Config.Screensaver.timeoutScreensaver').val});
|
|
}
|
|
|
|
setOrCreate(NSPanel_Path + 'Event.Button.Action', buttonAction ?? words[2], false, {name: 'Incoming button acion', type: 'string', role: 'text', write: false, read: true});
|
|
setOrCreate(NSPanel_Path + 'Event.Button.Value', words[4] != undefined ? words[4] : '', false, {name: 'Incoming button value', type: 'string', role: 'text', write: false, read: true});
|
|
setOrCreate(NSPanel_Path + 'Event.Button.Id', id, false, {name: 'Incoming button id', type: 'string', role: 'text', write: false, read: true});
|
|
|
|
if (Debug) {
|
|
log('HandleButtonEvent buttonAction: ' + buttonAction, 'info');
|
|
}
|
|
|
|
if (buttonAction.startsWith('swipe')) {
|
|
buttonAction = 'bExit';
|
|
}
|
|
|
|
let pageNum: number = 0;
|
|
|
|
switch (buttonAction) {
|
|
case 'bUp':
|
|
if (pageId < 0) {
|
|
// Check whether button1page or button2page
|
|
pageId = 0;
|
|
UnsubscribeWatcher();
|
|
GeneratePage(config.pages[pageId]);
|
|
} else {
|
|
pageNum = (((pageId - 1) % config.pages.length) + config.pages.length) % config.pages.length;
|
|
pageId = pageNum;
|
|
UnsubscribeWatcher();
|
|
if (activePage != undefined && activePage!.parent != undefined) {
|
|
//update pageID
|
|
for (let i = 0; i < config.pages.length; i++) {
|
|
if (config.pages[i] == activePage!.parent) {
|
|
pageId = i;
|
|
break;
|
|
}
|
|
}
|
|
GeneratePage(activePage!.parent);
|
|
} else {
|
|
GeneratePage(config.pages[pageId]);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 'bNext':
|
|
pageNum = (((pageId + 1) % config.pages.length) + config.pages.length) % config.pages.length;
|
|
pageId = pageNum;
|
|
UnsubscribeWatcher();
|
|
//-Serching for next unhidden Page----------------------
|
|
if (config.pages[pageId].hiddenByTrigger && valueHiddenCards) {
|
|
for (let i = pageId; i <= config.pages.length; i++) {
|
|
if (i == config.pages.length) {
|
|
i = 0;
|
|
}
|
|
if (!config.pages[i].hiddenByTrigger) {
|
|
pageId = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
GeneratePage(config.pages[pageId]);
|
|
break;
|
|
case 'bSubNext':
|
|
UnsubscribeWatcher();
|
|
// check this please
|
|
GeneratePage(eval(activePage!.next!));
|
|
break;
|
|
case 'bPrev':
|
|
pageNum = (((pageId - 1) % config.pages.length) + config.pages.length) % config.pages.length;
|
|
pageId = pageNum;
|
|
UnsubscribeWatcher();
|
|
//-Searching for previous unhidden Page----------------------
|
|
if (config.pages[pageId].hiddenByTrigger && valueHiddenCards) {
|
|
for (let i = pageId; i >= 0; i--) {
|
|
if (config.pages[i].hiddenByTrigger == false || config.pages[i].hiddenByTrigger == undefined) {
|
|
pageId = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (activePage != undefined && activePage!.parent != undefined) {
|
|
//update pageID
|
|
for (let i = 0; i < config.pages.length; i++) {
|
|
if (config.pages[i] == activePage!.parent) {
|
|
pageId = i;
|
|
break;
|
|
}
|
|
}
|
|
GeneratePage(activePage!.parent);
|
|
} else {
|
|
GeneratePage(config.pages[pageId]);
|
|
}
|
|
break;
|
|
case 'bSubPrev':
|
|
UnsubscribeWatcher();
|
|
// check this please
|
|
GeneratePage(eval(activePage!.prev!));
|
|
break;
|
|
case 'bExit':
|
|
if (Debug) {
|
|
log('HandleButtonEvent -> bExit: ' + words[2] + ' - ' + words[4] + ' - ' + pageId, 'info');
|
|
}
|
|
if (words[2] == 'screensaver') {
|
|
if (getState(NSPanel_Path + 'Config.Screensaver.screenSaverDoubleClick').val) {
|
|
if (words[4] >= 2) {
|
|
if (
|
|
existsObject(NSPanel_Path + 'ScreensaverInfo.bExitPage') &&
|
|
getState(NSPanel_Path + 'ScreensaverInfo.bExitPage').val != null &&
|
|
getState(NSPanel_Path + 'ScreensaverInfo.bExitPage').val != -1
|
|
) {
|
|
pageId = getState(NSPanel_Path + 'ScreensaverInfo.bExitPage').val;
|
|
}
|
|
} else {
|
|
if (getState(NSPanel_Path + 'ScreensaverInfo.popupNotifyHeading').val != '') {
|
|
setIfExists(NSPanel_Path + 'ScreensaverInfo.popupNotifyHeading', '');
|
|
}
|
|
if (getState(NSPanel_Path + 'ScreensaverInfo.popupNotifyText').val != '') {
|
|
setIfExists(NSPanel_Path + 'ScreensaverInfo.popupNotifyText', '');
|
|
}
|
|
screensaverEnabled = true;
|
|
break;
|
|
}
|
|
} else {
|
|
if (getState(NSPanel_Path + 'ScreensaverInfo.popupNotifyHeading').val != '') {
|
|
setIfExists(NSPanel_Path + 'ScreensaverInfo.popupNotifyHeading', '');
|
|
}
|
|
if (getState(NSPanel_Path + 'ScreensaverInfo.popupNotifyText').val != '') {
|
|
setIfExists(NSPanel_Path + 'ScreensaverInfo.popupNotifyText', '');
|
|
}
|
|
if (
|
|
existsObject(NSPanel_Path + 'ScreensaverInfo.bExitPage') &&
|
|
getState(NSPanel_Path + 'ScreensaverInfo.bExitPage').val != null &&
|
|
getState(NSPanel_Path + 'ScreensaverInfo.bExitPage').val != -1
|
|
) {
|
|
pageId = getState(NSPanel_Path + 'ScreensaverInfo.bExitPage').val;
|
|
}
|
|
screensaverEnabled = true; // Activating screensaver also on One-Time click
|
|
}
|
|
activePage = config.pages[pageId];
|
|
}
|
|
if (words[2] == 'popupInSel' && activePage!.type == 'cardMedia') {
|
|
if (Debug) log('Leave popupInsel without any action', 'info');
|
|
pageCounter = 0;
|
|
GeneratePage(activePage!);
|
|
setTimeout(async function () {
|
|
pageCounter = 1;
|
|
GeneratePage(activePage!);
|
|
}, 3000);
|
|
} else {
|
|
pageCounter = 0;
|
|
GeneratePage(activePage!);
|
|
}
|
|
if (words[2] == 'popupSlider' && activePage!.type == 'cardMedia') {
|
|
if (Debug) log('Leave popupSlider without any action', 'info');
|
|
pageCounter = 0;
|
|
GeneratePage(activePage!);
|
|
setTimeout(async function () {
|
|
pageCounter = 1;
|
|
GeneratePage(activePage!);
|
|
}, 3000);
|
|
} else {
|
|
pageCounter = 0;
|
|
GeneratePage(activePage!);
|
|
}
|
|
break;
|
|
case 'bHome':
|
|
if (Debug) {
|
|
log('HandleButtonEvent -> bHome: ' + words[4] + ' - ' + pageId, 'info');
|
|
}
|
|
UnsubscribeWatcher();
|
|
const home = activePage!.home;
|
|
if (home !== undefined) {
|
|
pageId = config.pages.findIndex((a) => a == eval(home));
|
|
pageId = pageId === -1 ? 0 : pageId;
|
|
GeneratePage(eval(home));
|
|
} else {
|
|
pageId = 0;
|
|
GeneratePage(config.pages[0]);
|
|
}
|
|
break;
|
|
case 'notifyAction':
|
|
if (words[4] == 'yes') {
|
|
setState(popupNotifyInternalName, {val: words[2], ack: true});
|
|
setState(popupNotifyAction, {val: true, ack: true});
|
|
} else if (words[4] == 'no') {
|
|
setState(popupNotifyInternalName, {val: words[2], ack: true});
|
|
setState(popupNotifyAction, {val: false, ack: true});
|
|
}
|
|
|
|
setIfExists(config.panelSendTopic, 'exitPopup');
|
|
|
|
break;
|
|
case 'OnOff':
|
|
if (existsObject(id)) {
|
|
let action = false;
|
|
if (words[4] == '1') action = true;
|
|
let o = getObject(id);
|
|
if (Debug) {
|
|
log('HandleButtonEvent -> OnOff: ' + words[4] + ' - ' + id + ' - Role - ' + o.common.role, 'info');
|
|
}
|
|
const role = o.common.role as NSPanel.roles;
|
|
switch (role) {
|
|
case 'level.mode.fan':
|
|
case 'socket':
|
|
case 'light':
|
|
let pageItem = findPageItem(id);
|
|
if (pageItem.monobutton != undefined && pageItem.monobutton == true) {
|
|
triggerButton(id + '.SET');
|
|
} else {
|
|
setIfExists(id + '.SET', action);
|
|
}
|
|
break;
|
|
case 'dimmer':
|
|
setIfExists(id + '.ON_SET', action) ? true : setIfExists(id + '.ON_ACTUAL', action);
|
|
break;
|
|
case 'ct':
|
|
setIfExists(id + '.ON', action);
|
|
break;
|
|
case 'rgb':
|
|
case 'rgbSingle':
|
|
case 'cie':
|
|
case 'hue':
|
|
setIfExists(id + '.ON', action);
|
|
break;
|
|
case 'switch.mode.wlan':
|
|
setIfExists(id + '.SWITCH', action);
|
|
GeneratePage(activePage!);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 'button':
|
|
if (['f1Icon', 'f2Icon', 'f3Icon', 'f4Icon', 'f5Icon'].indexOf(words[2]) != -1) {
|
|
const fNumber = parseInt(words[2].substring(1, 2)) - 1;
|
|
const indicatorScreensaverEntity = config.indicatorScreensaverEntity[fNumber];
|
|
if (indicatorScreensaverEntity != null && indicatorScreensaverEntity !== undefined && indicatorScreensaverEntity.ScreensaverEntityNaviToPage !== undefined) {
|
|
if (Debug) log('NaviToPage: ' + words[2]);
|
|
GeneratePage(indicatorScreensaverEntity.ScreensaverEntityNaviToPage);
|
|
} else {
|
|
const value = ['event', 'buttonPress2', 'screensaver', 'bExit', '2'];
|
|
HandleButtonEvent(value);
|
|
}
|
|
}
|
|
|
|
if (existsObject(id)) {
|
|
let action = false;
|
|
if (words[4] == '1') action = true;
|
|
let o = getObject(id);
|
|
|
|
if (Debug) log(o.common.role)
|
|
|
|
switch (o.common.role as NSPanel.roles) {
|
|
case 'level.mode.fan':
|
|
toggleState(id + '.SET') ? true : toggleState(id + '.ACTUAL');
|
|
break;
|
|
case 'lock':
|
|
case 'button':
|
|
toggleState(id + '.SET') ? true : toggleState(id + '.ON_SET');
|
|
break;
|
|
case 'buttonSensor':
|
|
if (existsObject(id + '.ACTUAL')) {
|
|
toggleState(id + '.ACTUAL');
|
|
}
|
|
break;
|
|
case 'socket':
|
|
case 'light':
|
|
// Change for monobutton
|
|
let pageItem = findPageItem(id);
|
|
if (pageItem.monobutton != undefined && pageItem.monobutton == true) {
|
|
triggerButton(id + '.SET');
|
|
} else {
|
|
toggleState(id + '.SET') ? true : toggleState(id + '.ON_SET');
|
|
}
|
|
break;
|
|
case 'dimmer':
|
|
toggleState(id + '.ON_SET') ? true : toggleState(id + '.ON_ACTUAL');
|
|
break;
|
|
case 'ct':
|
|
toggleState(id + '.ON');
|
|
break;
|
|
case 'rgb':
|
|
case 'rgbSingle':
|
|
case 'cie':
|
|
case 'hue':
|
|
toggleState(id + '.ON');
|
|
break;
|
|
case 'media':
|
|
if (!activePage || activePage.type != 'cardMedia') {
|
|
if (activePage) throw new Error(`Found channel role media for card: ${activePage.type} not allowed`);
|
|
else throw new Error(`Something went wrong! Active Page is empty!`);
|
|
}
|
|
if (tempid[1] == undefined) {
|
|
if (Debug) log('Logo click', 'info');
|
|
GeneratePage(activePage!);
|
|
} else if (tempid[1] == 'repeat') {
|
|
let pageItemRepeat = findPageItem(id);
|
|
if (isPageMediaItem(pageItemRepeat)) {
|
|
let adapterInstanceRepeat = pageItemRepeat.adapterPlayerInstance;
|
|
let adapterRepeat = adapterInstanceRepeat.split('.');
|
|
const deviceAdapterRP: NSPanel.PlayerType = adapterRepeat[0] as NSPanel.PlayerType;
|
|
|
|
switch (deviceAdapterRP) {
|
|
case 'spotify-premium':
|
|
if (Debug) log(getState(id + '.REPEAT').val);
|
|
let stateSpotifyRepeat = getState(id + '.REPEAT').val;
|
|
if (stateSpotifyRepeat == 'off') {
|
|
setIfExists(id + '.REPEAT', 'context');
|
|
} else if (stateSpotifyRepeat == 'context') {
|
|
setIfExists(id + '.REPEAT', 'track');
|
|
} else if (stateSpotifyRepeat == 'track') {
|
|
setIfExists(id + '.REPEAT', 'off');
|
|
}
|
|
GeneratePage(activePage!);
|
|
break;
|
|
case 'bosesoundtouch':
|
|
if (Debug) log(adapterInstanceRepeat);
|
|
let stateBoseRepeat = getState(id + '.REPEAT').val;
|
|
if (stateBoseRepeat == 'REPEAT_OFF') {
|
|
setIfExists(adapterInstanceRepeat + 'key', 'REPEAT_ALL');
|
|
} else if (stateBoseRepeat == 'REPEAT_ALL') {
|
|
setIfExists(adapterInstanceRepeat + 'key', 'REPEAT_ONE');
|
|
} else if (stateBoseRepeat == 'REPEAT_ONE') {
|
|
setIfExists(adapterInstanceRepeat + 'key', 'REPEAT_OFF');
|
|
}
|
|
GeneratePage(activePage!);
|
|
break;
|
|
case 'sonos':
|
|
let stateSonosRepeat = getState(id + '.REPEAT').val;
|
|
if (stateSonosRepeat == 0) {
|
|
setIfExists(id + '.REPEAT', 1);
|
|
} else if (stateSonosRepeat == 1) {
|
|
setIfExists(id + '.REPEAT', 2);
|
|
} else if (stateSonosRepeat == 2) {
|
|
setIfExists(id + '.REPEAT', 0);
|
|
}
|
|
GeneratePage(activePage!);
|
|
break;
|
|
case 'alexa2':
|
|
try {
|
|
setIfExists(id + '.REPEAT', !getState(id + '.REPEAT').val);
|
|
} catch (err: any) {
|
|
log('ALEXA2: Repeat kann nicht verändert werden', 'warn');
|
|
}
|
|
GeneratePage(activePage!);
|
|
break;
|
|
case 'mpd':
|
|
if (getState(id + '.REPEAT').val == false) {
|
|
setIfExists(id + '.REPEAT', true);
|
|
setIfExists(id + '.SINGLE', 0);
|
|
} else if (getState(id + '.REPEAT').val == true && getState(id + '.SINGLE').val == 0) {
|
|
setIfExists(id + '.REPEAT', true);
|
|
setIfExists(id + '.SINGLE', 1);
|
|
} else if (getState(id + '.REPEAT').val == true && getState(id + '.SINGLE').val == 1) {
|
|
setIfExists(id + '.REPEAT', false);
|
|
setIfExists(id + '.SINGLE', 0);
|
|
}
|
|
GeneratePage(activePage!);
|
|
break;
|
|
case 'volumio':
|
|
let urlString: string = `${getState(adapterInstanceRepeat + 'info.host').val}/api/commands/?cmd=repeat`;
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(response.data, 'info');
|
|
}
|
|
GeneratePage(activePage!);
|
|
} else {
|
|
log('Axios Status - adapterInstanceRepeat: ' + response.state, 'warn');
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
log(error, 'warn');
|
|
});
|
|
break;
|
|
case 'squeezeboxrpc':
|
|
try {
|
|
switch (getState(id + '.REPEAT').val) {
|
|
case 0:
|
|
setIfExists(id + '.REPEAT', 1);
|
|
GeneratePage(activePage!);
|
|
break;
|
|
case 1:
|
|
setIfExists(id + '.REPEAT', 2);
|
|
GeneratePage(activePage!);
|
|
break;
|
|
case 2:
|
|
setIfExists(id + '.REPEAT', 0);
|
|
GeneratePage(activePage!);
|
|
break;
|
|
}
|
|
} catch (err: any) {
|
|
log('Squeezebox: Repeat kann nicht verändert werden', 'warn');
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'up':
|
|
setIfExists(id + '.OPEN', true);
|
|
checkBlindActive = true;
|
|
break;
|
|
case 'stop':
|
|
setIfExists(id + '.STOP', true);
|
|
checkBlindActive = false;
|
|
break;
|
|
case 'down':
|
|
setIfExists(id + '.CLOSE', true);
|
|
checkBlindActive = true;
|
|
break;
|
|
case 'button1Press':
|
|
let pageItemShutterButton1 = findPageItem(id);
|
|
if (pageItemShutterButton1.shutterIcons[0].buttonType != undefined && pageItemShutterButton1.shutterIcons[0].buttonType == 'toggle') {
|
|
toggleState(pageItemShutterButton1.shutterIcons[0].id);
|
|
} else if (pageItemShutterButton1.shutterIcons[0].buttonType != undefined && pageItemShutterButton1.shutterIcons[0].buttonType == 'press') {
|
|
setIfExists(pageItemShutterButton1.shutterIcons[0].id, true);
|
|
} else {
|
|
//do nothing
|
|
}
|
|
if (Debug) log("Shutter2 - Button1 Touch Press Event", 'info');
|
|
break;
|
|
case 'button2Press':
|
|
let pageItemShutterButton2 = findPageItem(id);
|
|
if (pageItemShutterButton2.shutterIcons[1].buttonType != undefined && pageItemShutterButton2.shutterIcons[1].buttonType == 'toggle') {
|
|
toggleState(pageItemShutterButton2.shutterIcons[1].id);
|
|
} else if (pageItemShutterButton2.shutterIcons[1].buttonType != undefined && pageItemShutterButton2.shutterIcons[1].buttonType == 'press') {
|
|
setIfExists(pageItemShutterButton2.shutterIcons[1].id, true);
|
|
} else {
|
|
//do nothing
|
|
}
|
|
if (Debug) log("Shutter2 - Button2 Touch Press Event", 'info');
|
|
break;
|
|
case 'button3Press':
|
|
let pageItemShutterButton3 = findPageItem(id);
|
|
if (pageItemShutterButton3.shutterIcons[2].buttonType != undefined && pageItemShutterButton3.shutterIcons[2].buttonType == 'toggle') {
|
|
toggleState(pageItemShutterButton3.shutterIcons[2].id);
|
|
} else if (pageItemShutterButton3.shutterIcons[2].buttonType != undefined && pageItemShutterButton3.shutterIcons[2].buttonType == 'press') {
|
|
setIfExists(pageItemShutterButton3.shutterIcons[2].id, true);
|
|
} else {
|
|
//do nothing
|
|
}
|
|
if (Debug) log("Shutter2 - Button3 Touch Press Event", 'info');
|
|
break;
|
|
case 'positionSlider':
|
|
(function () {
|
|
if (timeoutSlider) {
|
|
clearTimeout(timeoutSlider);
|
|
timeoutSlider = null;
|
|
}
|
|
})();
|
|
timeoutSlider = setTimeout(async function () {
|
|
let pageItem = findPageItem(id);
|
|
if (pageItem.minValueLevel != undefined && pageItem.maxValueLevel != undefined) {
|
|
let sliderPos = Math.trunc(scale(parseInt(words[4]), 0, 100, pageItem.maxValueLevel, pageItem.minValueLevel));
|
|
setIfExists(id + '.SET', sliderPos) ? true : setIfExists(id + '.ACTUAL', sliderPos);
|
|
checkBlindActive = true;
|
|
} else {
|
|
setIfExists(id + '.SET', parseInt(words[4])) ? true : setIfExists(id + '.ACTUAL', parseInt(words[4]));
|
|
checkBlindActive = true;
|
|
}
|
|
}, 250);
|
|
break;
|
|
case 'tiltOpen':
|
|
setIfExists(id + '.TILT_OPEN', true);
|
|
break;
|
|
case 'tiltStop':
|
|
setIfExists(id + '.TILT_STOP', true);
|
|
break;
|
|
case 'tiltClose':
|
|
setIfExists(id + '.TILT_CLOSE', true);
|
|
break;
|
|
case 'tiltSlider':
|
|
(function () {
|
|
if (timeoutSlider) {
|
|
clearTimeout(timeoutSlider);
|
|
timeoutSlider = null;
|
|
}
|
|
})();
|
|
timeoutSlider = setTimeout(async function () {
|
|
let pageItem = findPageItem(id);
|
|
if (pageItem.minValueTilt != undefined && pageItem.maxValueTilt != undefined) {
|
|
let sliderPos = Math.trunc(scale(parseInt(words[4]), 0, 100, pageItem.maxValueTilt, pageItem.minValueTilt));
|
|
setIfExists(id + '.TILT_SET', sliderPos) ? true : setIfExists(id + '.TILT_ACTUAL', sliderPos);
|
|
} else {
|
|
setIfExists(id + '.TILT_SET', parseInt(words[4])) ? true : setIfExists(id + '.TILT_ACTUAL', parseInt(words[4]));
|
|
}
|
|
}, 250);
|
|
break;
|
|
case 'brightnessSlider':
|
|
(function () {
|
|
if (timeoutSlider) {
|
|
clearTimeout(timeoutSlider);
|
|
timeoutSlider = null;
|
|
}
|
|
})();
|
|
timeoutSlider = setTimeout(async function () {
|
|
if (existsObject(id)) {
|
|
let o = getObject(id);
|
|
let pageItem = findPageItem(id);
|
|
const role = o.common.role as NSPanel.roles;
|
|
switch (role) {
|
|
case 'dimmer':
|
|
if (pageItem.minValueBrightness != undefined && pageItem.maxValueBrightness != undefined) {
|
|
let sliderPos = Math.trunc(scale(parseInt(words[4]), 0, 100, pageItem.maxValueBrightness, pageItem.minValueBrightness));
|
|
setIfExists(id + '.SET', sliderPos) ? true : setIfExists(id + '.ACTUAL', sliderPos);
|
|
} else {
|
|
setIfExists(id + '.SET', parseInt(words[4])) ? true : setIfExists(id + '.ACTUAL', parseInt(words[4]));
|
|
}
|
|
break;
|
|
case 'rgb':
|
|
case 'ct':
|
|
case 'rgbSingle':
|
|
case 'cie':
|
|
case 'hue':
|
|
if (pageItem.minValueBrightness != undefined && pageItem.maxValueBrightness != undefined) {
|
|
let sliderPos = Math.trunc(scale(parseInt(words[4]), 0, 100, pageItem.minValueBrightness, pageItem.maxValueBrightness));
|
|
setIfExists(id + '.DIMMER', sliderPos);
|
|
} else {
|
|
setIfExists(id + '.DIMMER', parseInt(words[4]));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}, 250);
|
|
break;
|
|
case 'colorTempSlider':
|
|
(function () {
|
|
if (timeoutSlider) {
|
|
clearTimeout(timeoutSlider);
|
|
timeoutSlider = null;
|
|
}
|
|
})();
|
|
timeoutSlider = setTimeout(async function () {
|
|
let pageItem = findPageItem(id);
|
|
if (pageItem.minValueColorTemp !== undefined && pageItem.maxValueColorTemp !== undefined) {
|
|
let colorTempK = Math.trunc(scale(parseInt(words[4]), 100, 0, pageItem.maxValueColorTemp, pageItem.minValueColorTemp));
|
|
setIfExists(id + '.TEMPERATURE', colorTempK);
|
|
} else {
|
|
setIfExists(id + '.TEMPERATURE', parseInt(words[4]));
|
|
}
|
|
}, 250);
|
|
break;
|
|
case 'colorWheel':
|
|
let colorCoordinates = words[4].split('|');
|
|
let rgb = pos_to_color(colorCoordinates[0], colorCoordinates[1]);
|
|
if (Debug) {
|
|
log('HandleButtonEvent colorWeel -> rgb-Wert: ' + rgb, 'info');
|
|
}
|
|
if (Debug) {
|
|
log('HandleButtonEvent colorWeel -> getHue-Werte: ' + getHue(rgb.red, rgb.green, rgb.blue), 'info');
|
|
}
|
|
let o = getObject(id);
|
|
switch (o.common.role as NSPanel.roles) {
|
|
case 'hue':
|
|
setIfExists(id + '.HUE', getHue(rgb.red, rgb.green, rgb.blue));
|
|
break;
|
|
case 'rgb':
|
|
setIfExists(id + '.RED', rgb.red);
|
|
setIfExists(id + '.GREEN', rgb.green);
|
|
setIfExists(id + '.BLUE', rgb.blue);
|
|
break;
|
|
case 'cie':
|
|
setIfExists(id + '.CIE', rgb_to_cie(rgb.red, rgb.green, rgb.blue));
|
|
case 'rgbSingle':
|
|
let pageItem = findPageItem(id);
|
|
if (pageItem.colormode == 'xy') {
|
|
//For e.g. Deconz XY
|
|
setIfExists(id + '.RGB', rgb_to_cie(rgb.red, rgb.green, rgb.blue));
|
|
if (Debug) {
|
|
log('HandleButtonEvent colorWeel colorMode=xy -> rgb_to_cie Wert: ' + rgb_to_cie(rgb.red, rgb.green, rgb.blue), 'info');
|
|
}
|
|
} else {
|
|
//For RGB
|
|
setIfExists(id + '.RGB', ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue));
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 'tempUpd':
|
|
setIfExists(id + '.SET', parseInt(words[4]) / 10);
|
|
break;
|
|
case 'tempUpdHighLow':
|
|
let temps = words[4].split('|');
|
|
if (getState(id + '.ACTUAL2').val * 10 != parseInt(temps[1])) {
|
|
// avoid writing if not needed
|
|
setIfExists(id + '.ACTUAL2', parseInt(temps[1]) / 10);
|
|
}
|
|
if (getState(id + '.SET').val * 10 != parseInt(temps[0])) {
|
|
setIfExists(id + '.SET', parseInt(temps[0]) / 10);
|
|
}
|
|
break;
|
|
case 'media-back': {
|
|
const tempPage = findPageItem(id);
|
|
if (isPageMediaItem(tempPage)) {
|
|
if (tempPage.adapterPlayerInstance.startsWith('bosesoundtouch')) {
|
|
setIfExists(tempPage.adapterPlayerInstance + 'key', 'PREV_TRACK');
|
|
} else {
|
|
setIfExists(id + '.PREV', true);
|
|
}
|
|
GeneratePage(activePage!);
|
|
}
|
|
break;
|
|
}
|
|
case 'media-pause':
|
|
let pageItemTemp = findPageItem(id);
|
|
if (isPageMediaItem(pageItemTemp)) {
|
|
let adaInstanceSplit = pageItemTemp.adapterPlayerInstance!.split('.');
|
|
if (adaInstanceSplit[0] == 'squeezeboxrpc') {
|
|
let adapterPlayerInstanceStateSeceltor: string = pageItemTemp.adapterPlayerInstance + 'Players.' + pageItemTemp.mediaDevice + '.state';
|
|
if (Debug) log('HandleButtonEvent media-pause Squeezebox-> adapterPlayerInstanceStateSeceltor: ' + adapterPlayerInstanceStateSeceltor, 'info');
|
|
let stateVal = getState(adapterPlayerInstanceStateSeceltor).val;
|
|
if (stateVal == 0) {
|
|
setState(adapterPlayerInstanceStateSeceltor, 1);
|
|
} else if (stateVal == 1) {
|
|
setState(adapterPlayerInstanceStateSeceltor, 0);
|
|
} else if (stateVal == null) {
|
|
setState(adapterPlayerInstanceStateSeceltor, 1);
|
|
}
|
|
} else if (adaInstanceSplit[0] == 'mpd') {
|
|
if (getState(id + '.STATE').val === 'play') {
|
|
setIfExists(id + '.PAUSE', true);
|
|
} else {
|
|
setIfExists(id + '.PLAY', true);
|
|
}
|
|
} else if (pageItemTemp.adapterPlayerInstance.startsWith('bosesoundtouch')) {
|
|
setIfExists(pageItemTemp.adapterPlayerInstance + 'key', 'PLAY_PAUSE');
|
|
} else {
|
|
if (Debug) log('HandleButtonEvent media-pause -> .STATE Value: ' + getState(id + '.STATE').val, 'info');
|
|
if (getState(id + '.STATE').val === true) {
|
|
setIfExists(id + '.PAUSE', true);
|
|
} else {
|
|
setIfExists(id + '.PLAY', true);
|
|
}
|
|
}
|
|
GeneratePage(activePage!);
|
|
}
|
|
break;
|
|
case 'media-next': {
|
|
const tempPage = findPageItem(id);
|
|
if (isPageMediaItem(tempPage)) {
|
|
if (tempPage.adapterPlayerInstance.startsWith('bosesoundtouch')) {
|
|
setIfExists(tempPage.adapterPlayerInstance + 'key', 'NEXT_TRACK');
|
|
} else {
|
|
setIfExists(id + '.NEXT', true);
|
|
}
|
|
GeneratePage(activePage!);
|
|
}
|
|
break;
|
|
}
|
|
case 'media-shuffle': {
|
|
const tempPage = findPageItem(id);
|
|
if (isPageMediaItem(tempPage)) {
|
|
if (tempPage.adapterPlayerInstance.startsWith('volumio')) {
|
|
const item = findPageItem(id);
|
|
if (isPageMediaItem(item)) item.playList = [];
|
|
break;
|
|
} //Volumio: empty playlist $uha-20230103
|
|
if (tempPage.adapterPlayerInstance.startsWith('mpd')) {
|
|
if (getState(id + '.SHUFFLE').val == false) {
|
|
setIfExists(id + '.SHUFFLE', true);
|
|
} else {
|
|
setIfExists(id + '.SHUFFLE', false);
|
|
}
|
|
}
|
|
if (tempPage.adapterPlayerInstance.startsWith('spotify')) {
|
|
if (getState(id + '.SHUFFLE').val == 'off') {
|
|
setIfExists(id + '.SHUFFLE', 'on');
|
|
} else {
|
|
setIfExists(id + '.SHUFFLE', 'off');
|
|
}
|
|
}
|
|
if (tempPage.adapterPlayerInstance.startsWith('alexa')) {
|
|
if (getState(id + '.SHUFFLE').val == false) {
|
|
setIfExists(id + '.SHUFFLE', true);
|
|
} else {
|
|
setIfExists(id + '.SHUFFLE', false);
|
|
}
|
|
}
|
|
if (tempPage.adapterPlayerInstance.startsWith('sonos')) {
|
|
if (getState(id + '.SHUFFLE').val == false) {
|
|
setIfExists(id + '.SHUFFLE', true);
|
|
} else {
|
|
setIfExists(id + '.SHUFFLE', false);
|
|
}
|
|
}
|
|
if (tempPage.adapterPlayerInstance.startsWith('squeezeboxrpc')) {
|
|
if (getState(tempPage.adapterPlayerInstance + 'Players.' + tempPage.mediaDevice + '.PlaylistShuffle').val == 1) {
|
|
setIfExists(tempPage.adapterPlayerInstance + 'Players.' + tempPage.mediaDevice + '.PlaylistShuffle', 0);
|
|
} else {
|
|
setIfExists(tempPage.adapterPlayerInstance + 'Players.' + tempPage.mediaDevice + '.PlaylistShuffle', 1);
|
|
}
|
|
}
|
|
if (tempPage.adapterPlayerInstance.startsWith('bosesoundtouch')) {
|
|
if (Debug) log(tempPage.adapterPlayerInstance + 'nowPlaying.shuffle');
|
|
if (getState(tempPage.adapterPlayerInstance + 'nowPlaying.shuffle').val == 'false') {
|
|
setIfExists(tempPage.adapterPlayerInstance + 'key', 'SHUFFLE_ON');
|
|
} else {
|
|
setIfExists(tempPage.adapterPlayerInstance + 'key', 'SHUFFLE_OFF');
|
|
}
|
|
}
|
|
GeneratePage(activePage!);
|
|
}
|
|
break;
|
|
}
|
|
case 'volumeSlider':
|
|
subscribeMediaSubscriptions(id);
|
|
useMediaEvents = true;
|
|
pageCounter = 1;
|
|
let vVolume = scale(parseInt(words[4]), 100, 0, activePage!.items[0]!.minValue ?? 0, activePage!.items[0]!.maxValue ?? 100);
|
|
setIfExists(id + '.VOLUME', Math.floor(vVolume));
|
|
break;
|
|
case 'mode-speakerlist':
|
|
let pageItem = findPageItem(id);
|
|
if (isPageMediaItem(pageItem)) {
|
|
let adapterInstance = pageItem.adapterPlayerInstance!;
|
|
let adapter = adapterInstance!.split('.');
|
|
const deviceAdapter: NSPanel.PlayerType = adapter[0] as NSPanel.PlayerType;
|
|
|
|
switch (deviceAdapter) {
|
|
case 'spotify-premium':
|
|
let strDevicePI = pageItem.speakerList![words[4]];
|
|
let strDeviceID = spotifyGetDeviceID(strDevicePI);
|
|
setState(adapterInstance + 'devices.' + strDeviceID + '.useForPlayback', true);
|
|
break;
|
|
case 'alexa2':
|
|
let i_list = Array.prototype.slice.apply($('[state.id="' + adapterInstance + 'Echo-Devices.*.Info.name"]'));
|
|
for (let i_index in i_list) {
|
|
let i = i_list[i_index];
|
|
if (getState(i).val === pageItem.speakerList![words[4]]) {
|
|
if (Debug) log('HandleButtonEvent mode-Speakerlist Alexa2: ' + getState(i).val + ' - ' + pageItem.speakerList![words[4]], 'info');
|
|
let deviceId = i;
|
|
deviceId = deviceId.split('.');
|
|
setIfExists(adapterInstance + 'Echo-Devices.' + pageItem.mediaDevice + '.Commands.textCommand', 'Schiebe meine Musik auf ' + pageItem.speakerList![words[4]]);
|
|
pageItem.mediaDevice = deviceId[3];
|
|
}
|
|
}
|
|
break;
|
|
case 'sonos':
|
|
break;
|
|
/*case 'chromecast':
|
|
break;*/
|
|
case 'squeezeboxrpc':
|
|
pageItem.mediaDevice = pageItem.speakerList![words[4]];
|
|
break;
|
|
case 'volumio':
|
|
break;
|
|
case 'bosesoundtouch':
|
|
break;
|
|
}
|
|
pageCounter = 0;
|
|
GeneratePage(activePage!);
|
|
setTimeout(async function () {
|
|
pageCounter = 1;
|
|
GeneratePage(activePage!);
|
|
}, 3000);
|
|
}
|
|
break;
|
|
case 'mode-playlist':
|
|
let pageItemPL = findPageItem(id);
|
|
if (!isPageMediaItem(pageItemPL)) break;
|
|
let adapterInstancePL = pageItemPL.adapterPlayerInstance!;
|
|
let adapterPL = adapterInstancePL.split('.');
|
|
const deviceAdapterPL: NSPanel.PlayerType = adapterPL[0] as NSPanel.PlayerType;
|
|
|
|
switch (deviceAdapterPL) {
|
|
case 'spotify-premium':
|
|
let strDevicePI = pageItemPL.playList![words[4]];
|
|
if (Debug) log('HandleButtonEvent mode-playlist Spotify -> strDevicePI: ' + strDevicePI, 'info');
|
|
let playlistListString = getState(adapterInstancePL + 'playlists.playlistListString').val.split(';');
|
|
let playlistListIds = getState(adapterInstancePL + 'playlists.playlistListIds').val.split(';');
|
|
let playlistIndex = playlistListString.indexOf(strDevicePI);
|
|
setState(adapterInstancePL + 'playlists.playlistList', playlistListIds[playlistIndex]);
|
|
setTimeout(async function () {
|
|
globalTracklist = (function () {
|
|
try {
|
|
return JSON.parse(getState(adapterInstancePL + 'player.playlist.trackListArray').val);
|
|
} catch (e) {
|
|
return {};
|
|
}
|
|
})();
|
|
}, 2000);
|
|
break;
|
|
case 'alexa2':
|
|
let tempListItem = pageItemPL.playList![words[4]].split('.');
|
|
setState(adapterInstancePL + 'Echo-Devices.' + pageItemPL.mediaDevice + '.Music-Provider.' + tempListItem[0], tempListItem[1]);
|
|
break;
|
|
case 'sonos':
|
|
let strDevicePLSonos = pageItemPL.playList![words[4]].split('.');
|
|
if (Debug) log(adapterInstancePL + 'root.' + pageItemPL.mediaDevice + '.playlist_set', 'info');
|
|
setState(adapterInstancePL + 'root.' + pageItemPL.mediaDevice + '.playlist_set', strDevicePLSonos[0]);
|
|
break;
|
|
case 'volumio':
|
|
let strDevicePL = pageItemPL.playList![words[4]];
|
|
let urlString: string = `${getState(adapterInstancePL + 'info.host').val}/api/commands/?cmd=playplaylist&name=${strDevicePL}`;
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(JSON.stringify(response.data), 'info');
|
|
}
|
|
} else {
|
|
log('Axios Status - mode-playlist: ' + response.state, 'warn');
|
|
}
|
|
})
|
|
.catch(function (error) {
|
|
log(error, 'warn');
|
|
});
|
|
break;
|
|
case 'squeezeboxrpc':
|
|
setState([adapterInstancePL, 'Players.', pageItemPL.mediaDevice, '.cmdPlayFavorite'].join(''), words[4]);
|
|
break;
|
|
case 'bosesoundtouch':
|
|
if (Debug) log('bosesoundtouch - playlist ' + pageItemPL.adapterPlayerInstance + ' - ' + words[4]);
|
|
if (Debug) log(adapterInstancePL + 'key');
|
|
if (words[4] < 6) {
|
|
setState(adapterInstancePL + 'key', 'PRESET_' + (parseInt(words[4]) + 1));
|
|
} else if (words[4] == 6) {
|
|
setState(adapterInstancePL + 'key', 'AUX_INPUT');
|
|
}
|
|
break;
|
|
case 'mpd':
|
|
if (Debug) log('mpd - playlist ' + pageItemPL.adapterPlayerInstance + ' - ' + words[4]);
|
|
if (Debug) log(pageItemPL.playList![words[4]]);
|
|
setState(adapterInstancePL + 'clear',true); // Clear current Queue
|
|
setState(adapterInstancePL + 'load', pageItemPL.playList![words[4]]); // Load stored Playlist
|
|
setState(adapterInstancePL + 'play',true); // Start new Queue with play
|
|
break;
|
|
default:
|
|
log('Hello Mr. Developer u miss in mode-playlist something!', 'warn');
|
|
}
|
|
pageCounter = 0;
|
|
GeneratePage(activePage!);
|
|
setTimeout(async function () {
|
|
pageCounter = 1;
|
|
GeneratePage(activePage!);
|
|
}, 3000);
|
|
break;
|
|
case 'mode-tracklist':
|
|
let pageItemTL = findPageItem(id);
|
|
if (!isPageMediaItem(pageItemTL)) break;
|
|
let adapterInstanceTL = pageItemTL.adapterPlayerInstance!;
|
|
let adapterTL = adapterInstanceTL.split('.');
|
|
const deviceAdapterTL: NSPanel.PlayerType = adapterTL[0] as NSPanel.PlayerType;
|
|
|
|
switch (deviceAdapterTL) {
|
|
case 'spotify-premium':
|
|
//let trackArray = (function () { try {return JSON.parse(getState(pageItemTL.adapterPlayerInstance + 'player.playlist.trackListArray').val);} catch(e) {return {};}})();
|
|
//setState(adapterInstanceTL + 'player.trackId', getAttr(trackArray, words[4] + '.id'));
|
|
setState(adapterInstanceTL + 'player.playlist.trackNo', parseInt(words[4]) + 1);
|
|
break;
|
|
case 'mpd':
|
|
setState(adapterInstanceTL + 'playid', parseInt(words[4]));
|
|
break;
|
|
case 'sonos':
|
|
setState(adapterInstanceTL + 'root.' + pageItemTL.mediaDevice + '.current_track_number', parseInt(words[4]) + 1);
|
|
case 'alexa2':
|
|
if (Debug) log('Aktuell hat alexa2 keine Tracklist', 'info');
|
|
break;
|
|
case 'volumio':
|
|
let urlString: string = `${getState(adapterInstanceTL + 'info.host').val}/api/commands/?cmd=play&N=${words[4]}`;
|
|
axios
|
|
.get(urlString, {headers: {'User-Agent': 'ioBroker'}})
|
|
.then(async function (response) {
|
|
if (response.status === 200) {
|
|
if (Debug) {
|
|
log(JSON.stringify(response.data), 'info');
|
|
}
|
|
} else {
|
|
log('Axios Status - mode-tracklist: ' + response.state, 'warn');
|
|
}
|
|
})
|
|
.catch(function (error: any) {
|
|
log(error, 'warn');
|
|
});
|
|
break;
|
|
case 'squeezeboxrpc':
|
|
//@ts-ignore Fehler kommt von findPageItem in vscode
|
|
setState([adapterInstanceTL, 'Players.', pageItemTL.mediaDevice, '.PlaylistCurrentIndex'].join(''), words[4]);
|
|
break;
|
|
case 'bosesoundtouch':
|
|
break;
|
|
default:
|
|
log('Hello Mr. Developer u miss in mode-tracklist something!', 'warn');
|
|
}
|
|
pageCounter = 0;
|
|
GeneratePage(activePage!);
|
|
setTimeout(async function () {
|
|
pageCounter = 1;
|
|
GeneratePage(activePage!);
|
|
}, 3000);
|
|
break;
|
|
case 'mode-repeat':
|
|
let pageItemRP = findPageItem(id);
|
|
if (!isPageMediaItem(pageItemRP)) break;
|
|
let adapterInstanceRP = pageItemRP.adapterPlayerInstance!;
|
|
let adapterRP = adapterInstanceRP.split('.');
|
|
let deviceAdapterRP: NSPanel.PlayerType = adapterRP[0] as NSPanel.PlayerType;
|
|
|
|
if (Debug) log(pageItemRP.repeatList![words[4]], 'warn');
|
|
switch (deviceAdapterRP) {
|
|
case 'spotify-premium':
|
|
setIfExists(id + '.REPEAT', pageItemRP.repeatList![words[4]]);
|
|
GeneratePage(activePage!);
|
|
break;
|
|
case 'mpd':
|
|
if (getState(id + '.REPEAT').val == false) {
|
|
setIfExists(id + '.REPEAT', true);
|
|
setIfExists(id + '.SINGLE', 0);
|
|
} else if (getState(id + '.REPEAT').val == true && getState(id + '.SINGLE').val == 0) {
|
|
setIfExists(id + '.REPEAT', true);
|
|
setIfExists(id + '.SINGLE', 1);
|
|
} else if (getState(id + '.REPEAT').val == true && getState(id + '.SINGLE').val == 1) {
|
|
setIfExists(id + '.REPEAT', false);
|
|
setIfExists(id + '.SINGLE', 0);
|
|
}
|
|
GeneratePage(activePage!);
|
|
break;
|
|
case 'alexa2':
|
|
GeneratePage(activePage!);
|
|
break;
|
|
}
|
|
break;
|
|
case 'mode-equalizer':
|
|
let pageItemEQ = findPageItem(id);
|
|
if (!isPageMediaItem(pageItemEQ)) break;
|
|
if (Debug) log('HandleButtonEvent mode-equalizer -> id: ' + id, 'info');
|
|
let lastIndex = id.split('.').pop();
|
|
setState(NSPanel_Path + 'Media.Player.' + lastIndex + '.EQ.activeMode', pageItemEQ.equalizerList![words[4]]);
|
|
pageCounter = 0;
|
|
GeneratePage(activePage!);
|
|
setTimeout(async function () {
|
|
pageCounter = 1;
|
|
GeneratePage(activePage!);
|
|
}, 3000);
|
|
break;
|
|
case 'positionSlider1':
|
|
case 'positionSlider2':
|
|
case 'positionSlider3':
|
|
let newSliderVal: number = 0
|
|
let pageItemSlider = findPageItem(id);
|
|
if (isPageMediaItem(pageItemSlider)) {
|
|
let adaInstanceSplit = pageItemSlider.adapterPlayerInstance!.split('.');
|
|
if (adaInstanceSplit[0] == 'alexa2') {
|
|
if (words[4] > 6) {
|
|
newSliderVal = words[4] - 6;
|
|
} else if (words[4] < 6) {
|
|
newSliderVal = (6 - words[4]) * -1;
|
|
}
|
|
if (Debug) log(words[3] + ': ' + newSliderVal);
|
|
switch (words[3]) {
|
|
case 'positionSlider1':
|
|
if (Debug) log(pageItemSlider.adapterPlayerInstance + 'Echo-Devices.' + pageItemSlider.mediaDevice + '.Preferences.equalizerBass');
|
|
setState(pageItemSlider.adapterPlayerInstance + 'Echo-Devices.' + pageItemSlider.mediaDevice + '.Preferences.equalizerBass', newSliderVal);
|
|
break;
|
|
case 'positionSlider2':
|
|
if (Debug) log(pageItemSlider.adapterPlayerInstance + 'Echo-Devices.' + pageItemSlider.mediaDevice + '.Preferences.equalizerMidRange');
|
|
setState(pageItemSlider.adapterPlayerInstance + 'Echo-Devices.' + pageItemSlider.mediaDevice + '.Preferences.equalizerMidRange', newSliderVal);
|
|
break;
|
|
case 'positionSlider3':
|
|
if (Debug) log(pageItemSlider.adapterPlayerInstance + 'Echo-Devices.' + pageItemSlider.mediaDevice + '.Preferences.equalizerTreble');
|
|
setState(pageItemSlider.adapterPlayerInstance + 'Echo-Devices.' + pageItemSlider.mediaDevice + '.Preferences.equalizerTreble', newSliderVal);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'mode-seek':
|
|
let pageItemSeek = findPageItem(id);
|
|
if (!isPageMediaItem(pageItemSeek)) break;
|
|
let adapterInstanceSK = pageItemSeek.adapterPlayerInstance!;
|
|
let adapterSK = adapterInstanceSK.split('.');
|
|
let deviceAdapterSK: NSPanel.PlayerType = adapterSK[0] as NSPanel.PlayerType;
|
|
switch (deviceAdapterSK) {
|
|
case 'spotify-premium':
|
|
setState(adapterInstanceSK + 'player.progressPercentage', parseInt(words[4]) * 10);
|
|
break;
|
|
case 'mpd':
|
|
setState(adapterInstanceSK + 'seek', parseInt(words[4]) * 10);
|
|
break;
|
|
case 'squeezeboxrpc':
|
|
const vDuration: number = getState(adapterInstanceSK + 'Players.' + pageItemSeek.mediaDevice + '.Duration').val;
|
|
const vSeekPercentage: number = words[4] * 10;
|
|
const setSeekSeconds: number = (vSeekPercentage * vDuration) / 100;
|
|
if (Debug) log(adapterInstanceSK + 'Players.' + pageItemSeek.mediaDevice + '.cmdGoTime' + ': ' + setSeekSeconds + ' sec.');
|
|
setState(adapterInstanceSK + 'Players.' + pageItemSeek.mediaDevice + '.cmdGoTime', String(setSeekSeconds.toFixed(0)));
|
|
break;
|
|
case 'sonos':
|
|
if (Debug) log('HandleButtonEvent mode-seek -> id: ' + id, 'info');
|
|
setState(adapterInstanceSK + 'root.' + pageItemSeek.mediaDevice + '.seek', parseInt(words[4]) * 10);
|
|
break;
|
|
}
|
|
pageCounter = 0;
|
|
GeneratePage(activePage!);
|
|
setTimeout(async function () {
|
|
pageCounter = 1;
|
|
GeneratePage(activePage!);
|
|
}, 3000);
|
|
break;
|
|
case 'mode-crossfade':
|
|
let pageItemCrossfade = findPageItem(id);
|
|
if (!isPageMediaItem(pageItemCrossfade)) break;
|
|
let adapterInstanceCF = pageItemCrossfade.adapterPlayerInstance!;
|
|
let adapterCF = adapterInstanceCF.split('.');
|
|
let deviceAdapterCF: NSPanel.PlayerType = adapterCF[0] as NSPanel.PlayerType;
|
|
switch (deviceAdapterCF) {
|
|
case 'spotify-premium':
|
|
break;
|
|
case 'sonos':
|
|
if (Debug) log('HandleButtonEvent mode-crossfade -> id: ' + id, 'info');
|
|
let cfState: boolean = false;
|
|
if (parseInt(words[4]) == 0) {
|
|
cfState = true;
|
|
}
|
|
setState(adapterInstanceCF + 'root.' + pageItemCrossfade.mediaDevice + '.crossfade', cfState);
|
|
break;
|
|
case 'mpd':
|
|
if (Debug) log('HandleButtonEvent mode-crossfade -> id: ' + id, 'info');
|
|
setState(adapterInstanceCF + 'crossfade', parseInt(words[4]));
|
|
break;
|
|
}
|
|
pageCounter = 0;
|
|
GeneratePage(activePage!);
|
|
setTimeout(async function () {
|
|
pageCounter = 1;
|
|
GeneratePage(activePage!);
|
|
}, 3000);
|
|
break;
|
|
case 'mode-favorites':
|
|
let pageItemFav = findPageItem(id);
|
|
if (!isPageMediaItem(pageItemFav)) break;
|
|
if (Debug) log(getState(pageItemFav.adapterPlayerInstance + 'root.' + pageItemFav.mediaDevice + '.favorites_set').val, 'info');
|
|
let favListArray = getState(pageItemFav.adapterPlayerInstance + 'root.' + pageItemFav.mediaDevice + '.favorites_list_array').val;
|
|
setState(pageItemFav.adapterPlayerInstance + 'root.' + pageItemFav.mediaDevice + '.favorites_set', favListArray[words[4]]);
|
|
pageCounter = 0;
|
|
GeneratePage(activePage!);
|
|
setTimeout(async function () {
|
|
pageCounter = 1;
|
|
GeneratePage(activePage!);
|
|
}, 3000);
|
|
break;
|
|
case 'mode-insel':
|
|
setIfExists(id + '.VALUE', parseInt(words[4]));
|
|
break;
|
|
case 'media-OnOff': {
|
|
let pageItemTemp = findPageItem(id);
|
|
if (!isPageMediaItem(pageItemTemp)) break;
|
|
let adapterInstance = pageItemTemp.adapterPlayerInstance.split('.');
|
|
if (adapterInstance[0] == 'squeezeboxrpc') {
|
|
let adapterPlayerInstancePowerSelector: string = [pageItemTemp.adapterPlayerInstance, 'Players.', pageItemTemp.mediaDevice, '.Power'].join('');
|
|
let stateVal = getState(adapterPlayerInstancePowerSelector).val;
|
|
if (stateVal === 0) {
|
|
setState(adapterPlayerInstancePowerSelector, 1);
|
|
setIfExists(id + '.STOP', false);
|
|
setIfExists(id + '.STATE', 1);
|
|
} else {
|
|
setState(adapterPlayerInstancePowerSelector, 0);
|
|
setIfExists(id + '.STOP', true);
|
|
setIfExists(id + '.STATE', 0);
|
|
}
|
|
} else if (adapterInstance[0] == 'bosesoundtouch') {
|
|
setState(pageItemTemp.adapterPlayerInstance + 'key', 'POWER');
|
|
} else {
|
|
setIfExists(id + '.STOP', true);
|
|
}
|
|
GeneratePage(activePage!);
|
|
break;
|
|
}
|
|
case 'timer-start':
|
|
if (words[4] != undefined) {
|
|
let timer_panel = words[4].split(':');
|
|
setIfExists(id + '.ACTUAL', parseInt(timer_panel[1]) * 60 + parseInt(timer_panel[2]));
|
|
}
|
|
setIfExists(id + '.STATE', 'active');
|
|
break;
|
|
case 'timer-pause':
|
|
setIfExists(id + '.STATE', 'paused');
|
|
break;
|
|
case 'timer-cancle':
|
|
setIfExists(id + '.STATE', 'idle');
|
|
setIfExists(id + '.ACTUAL', 0);
|
|
break;
|
|
case 'timer-finish':
|
|
setIfExists(id + '.STATE', 'idle');
|
|
setIfExists(id + '.ACTUAL', 0);
|
|
break;
|
|
case 'hvac_action':
|
|
if (words[4] == 'BOOT' || words[4] == 'PART' || words[4] == 'AUTT' || words[4] == 'MANT' || words[4] == 'VACT') {
|
|
switch (words[4]) {
|
|
case 'BOOT':
|
|
setIfExists(words[2] + '.' + 'BOOST', !getState(words[2] + '.' + 'BOOST').val);
|
|
break;
|
|
case 'PART':
|
|
setIfExists(words[2] + '.' + 'PARTY', !getState(words[2] + '.' + 'PARTY').val);
|
|
break;
|
|
case 'AUTT':
|
|
setIfExists(words[2] + '.' + 'AUTOMATIC', !getState(words[2] + '.' + 'AUTOMATIC').val);
|
|
break;
|
|
case 'MANT':
|
|
setIfExists(words[2] + '.' + 'MANUAL', !getState(words[2] + '.' + 'MANUAL').val);
|
|
break;
|
|
case 'VACT':
|
|
setIfExists(words[2] + '.' + 'VACATION', !getState(words[2] + '.' + 'VACATION').val);
|
|
break;
|
|
}
|
|
let modes = ['BOOT', 'PART', 'AUTT', 'MANT', 'VACT'];
|
|
let modesDP = ['BOOST', 'PARTY', 'AUTOMATIC', 'MANUAL', 'VACATION'];
|
|
for (let mode = 0; mode < 5; mode++) {
|
|
if (words[4] != modes[mode]) {
|
|
setIfExists(words[2] + '.' + modesDP[mode], false);
|
|
}
|
|
}
|
|
pageCounter = 1;
|
|
GeneratePage(activePage!);
|
|
} else {
|
|
let HVACMode = getState(words[2] + '.MODE').val;
|
|
|
|
// Event is bound to its own object
|
|
if (existsObject(words[2] + '.' + words[4])) {
|
|
switch (words[4]) {
|
|
case 'SWING':
|
|
if (getState(words[2] + '.SWING').val == 0) {
|
|
setIfExists(words[2] + '.SWING', 1);
|
|
} else {
|
|
setIfExists(words[2] + '.' + 'SWING', 0);
|
|
}
|
|
break;
|
|
default: // Power and Eco can easily be toggled
|
|
setIfExists(words[2] + '.' + words[4], !getState(words[2] + '.' + words[4]).val);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Event is a mode of the list (mode change)
|
|
let HVACModeList = getObject(words[2] + '.MODE').common.states;
|
|
for (const statekey in HVACModeList) {
|
|
if (HVACModeList[statekey] == words[4]) {
|
|
HVACMode = parseInt(statekey);
|
|
break;
|
|
}
|
|
}
|
|
|
|
setIfExists(words[2] + '.' + 'MODE', HVACMode);
|
|
pageCounter = 1;
|
|
GeneratePage(activePage!);
|
|
}
|
|
break;
|
|
case 'mode-modus1':
|
|
let pageItemT1 = findPageItem(id);
|
|
if (isPageThermoItem(pageItemT1)) setIfExists(id + '.' + pageItemT1.setThermoAlias![0], pageItemT1.popupThermoMode1![parseInt(words[4])]);
|
|
break;
|
|
case 'mode-modus2':
|
|
let pageItemT2 = findPageItem(id);
|
|
if (isPageThermoItem(pageItemT2)) setIfExists(id + '.' + pageItemT2.setThermoAlias![1], pageItemT2.popupThermoMode2![parseInt(words[4])]);
|
|
break;
|
|
case 'mode-modus3':
|
|
let pageItemT3 = findPageItem(id);
|
|
if (isPageThermoItem(pageItemT3)) setIfExists(id + '.' + pageItemT3.setThermoAlias![2], pageItemT3.popupThermoMode3![parseInt(words[4])]);
|
|
break;
|
|
case 'number-set':
|
|
let nobj = getObject(id);
|
|
switch (nobj.common.role as NSPanel.roles) {
|
|
case 'level.mode.fan':
|
|
(function () {
|
|
if (timeoutSlider) {
|
|
clearTimeout(timeoutSlider);
|
|
timeoutSlider = null;
|
|
}
|
|
})();
|
|
timeoutSlider = setTimeout(async function () {
|
|
setIfExists(id + '.SPEED', parseInt(words[4]));
|
|
}, 250);
|
|
break;
|
|
case 'slider':
|
|
(function () {
|
|
if (timeoutSlider) {
|
|
clearTimeout(timeoutSlider);
|
|
timeoutSlider = null;
|
|
}
|
|
})();
|
|
timeoutSlider = setTimeout(async function () {
|
|
setIfExists(id + '.SET', parseInt(words[4])) ? true : setIfExists(id + '.ACTUAL', parseInt(words[4]));
|
|
}, 250);
|
|
default:
|
|
(function () {
|
|
if (timeoutSlider) {
|
|
clearTimeout(timeoutSlider);
|
|
timeoutSlider = null;
|
|
}
|
|
})();
|
|
timeoutSlider = setTimeout(async function () {
|
|
setIfExists(id + '.SET', parseInt(words[4])) ? true : setIfExists(id + '.ACTUAL', parseInt(words[4]));
|
|
}, 250);
|
|
break;
|
|
}
|
|
break;
|
|
case 'mode-preset_modes':
|
|
setIfExists(id + '.MODE', parseInt(words[4]));
|
|
break;
|
|
case 'A1': // Alarm page - activate alarm 1
|
|
if (words[4] != '') {
|
|
setIfExists(id + '.TYPE', 'A1');
|
|
setIfExists(id + '.PIN', words[4]);
|
|
setIfExists(id + '.ACTUAL', 'arming');
|
|
setIfExists(id + '.PANEL', NSPanel_Path);
|
|
}
|
|
setTimeout(function () {
|
|
GeneratePage(activePage!);
|
|
}, 250);
|
|
break;
|
|
case 'A2': // Alarm page - activate alarm 2
|
|
if (words[4] != '') {
|
|
setIfExists(id + '.TYPE', 'A2');
|
|
setIfExists(id + '.PIN', words[4]);
|
|
setIfExists(id + '.ACTUAL', 'arming');
|
|
setIfExists(id + '.PANEL', NSPanel_Path);
|
|
}
|
|
setTimeout(function () {
|
|
GeneratePage(activePage!);
|
|
}, 250);
|
|
break;
|
|
case 'A3': // Alarm page - activate alarm 3
|
|
if (words[4] != '') {
|
|
setIfExists(id + '.TYPE', 'A3');
|
|
setIfExists(id + '.PIN', words[4]);
|
|
setIfExists(id + '.ACTUAL', 'arming');
|
|
setIfExists(id + '.PANEL', NSPanel_Path);
|
|
}
|
|
setTimeout(function () {
|
|
GeneratePage(activePage!);
|
|
}, 250);
|
|
break;
|
|
case 'A4': // Alarm page - activate alarm 4
|
|
if (words[4] != '') {
|
|
setIfExists(id + '.TYPE', 'A4');
|
|
setIfExists(id + '.PIN', words[4]);
|
|
setIfExists(id + '.ACTUAL', 'arming');
|
|
setIfExists(id + '.PANEL', NSPanel_Path);
|
|
}
|
|
setTimeout(function () {
|
|
GeneratePage(activePage!);
|
|
}, 250);
|
|
break;
|
|
case 'D1': // Alarm page - deactivate alarm 4
|
|
if (Debug) {
|
|
log('HandleButtonEvent Alarmpage D1 -> PIN: ' + getState(id + '.PIN').val, 'info');
|
|
}
|
|
if (Debug) {
|
|
log('HandleButtonEvent Alarmpage D1 -> words[4]: ' + words[4], 'info');
|
|
}
|
|
if (words[4] != '') {
|
|
if (getState(id + '.PIN').val == words[4]) {
|
|
setIfExists(id + '.PIN', '0000');
|
|
setIfExists(id + '.TYPE', 'D1');
|
|
setIfExists(id + '.ACTUAL', 'pending');
|
|
setIfExists(id + '.PIN_Failed', 0);
|
|
} else {
|
|
setIfExists(id + '.PIN_Failed', getState(id + '.PIN_Failed').val + 1);
|
|
setIfExists(id + '.ACTUAL', 'triggered');
|
|
}
|
|
setIfExists(id + '.PANEL', NSPanel_Path);
|
|
setTimeout(function () {
|
|
GeneratePage(activePage!);
|
|
}, 500);
|
|
}
|
|
break;
|
|
case 'U1': // Unlock-Page
|
|
let pageItemUnlock = findPageItem(id);
|
|
if (words[4] == getState(id + '.PIN').val) {
|
|
UnsubscribeWatcher();
|
|
GeneratePage(eval(pageItemUnlock.targetPage!));
|
|
setIfExists(id + '.ACTUAL', true);
|
|
} else {
|
|
setIfExists(id + '.ACTUAL', false);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function HandleButtonEvent: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the value of a state or creates it if it does not exist.
|
|
*
|
|
* This function checks if the specified state exists and sets its value if it does.
|
|
* If the state does not exist, it creates the state with the provided common properties and sets its value.
|
|
* Optionally, a callback function can be provided.
|
|
*
|
|
* @function setOrCreate
|
|
* @param {string} id - The ID of the state to set or create.
|
|
* @param {any} value - The value to set for the state.
|
|
* @param {boolean} [forceCreation=true] - Whether to force the creation of the state if it does not exist.
|
|
* @param {Partial<iobJS.StateCommon>} [common={}] - The common properties for the state (optional).
|
|
* @param {iobJS.SetStateCallback} [callback] - The callback function to execute after setting or creating the state (optional).
|
|
*/
|
|
function setOrCreate (id: string, value: any, forceCreation: boolean = true, common: Partial<iobJS.StateCommon> = {}, callback?: iobJS.SetStateCallback) {
|
|
if (!existsState(id)) {
|
|
extendObject(id.split('.').slice(0, -2).join('.'), {type: 'channel', common: {name: 'channel'}, native: {}});
|
|
extendObject(id.split('.').slice(0, -1).join('.'), {type: 'channel', common: {name: 'channel'}, native: {}});
|
|
createState(id, value, forceCreation, common, callback);
|
|
} else {
|
|
setState(id, value, true);
|
|
}
|
|
}
|
|
|
|
//Determination of page navigation (CustomSend-Payload)
|
|
/**
|
|
* Retrieves the navigation string for the specified page ID.
|
|
*
|
|
* This function returns the navigation string associated with the given page ID.
|
|
*
|
|
* @function getNavigationString
|
|
* @param {number} pageId - The ID of the page.
|
|
* @returns {string} The navigation string for the specified page ID.
|
|
*/
|
|
function getNavigationString (pageId: number): string {
|
|
try {
|
|
if (Debug) {
|
|
log('getNavigationString Übergabe pageId: ' + pageId, 'info');
|
|
}
|
|
|
|
let navigationString: string = '';
|
|
|
|
if (activePage && activePage.subPage) {
|
|
//Left icon
|
|
if (activePage.prev == undefined) {
|
|
if (activePage.parentIcon != undefined) {
|
|
navigationString = 'button~bUp~' + Icons.GetIcon(activePage.parentIcon);
|
|
if (activePage.parentIconColor != undefined) {
|
|
navigationString += '~' + rgb_dec565(activePage!.parentIconColor);
|
|
} else {
|
|
navigationString += '~' + rgb_dec565(White);
|
|
}
|
|
} else {
|
|
navigationString = 'button~bUp~' + Icons.GetIcon('arrow-up-bold') + '~' + rgb_dec565(White);
|
|
}
|
|
} else {
|
|
if (activePage.prevIcon != undefined) {
|
|
navigationString = 'button~bSubPrev~' + Icons.GetIcon(activePage.prevIcon);
|
|
if (activePage.prevIconColor != undefined) {
|
|
navigationString += '~' + rgb_dec565(activePage!.prevIconColor);
|
|
} else {
|
|
navigationString += '~' + rgb_dec565(White);
|
|
}
|
|
} else {
|
|
navigationString = 'button~bSubPrev~' + Icons.GetIcon('arrow-left-bold') + '~' + rgb_dec565(White);
|
|
}
|
|
}
|
|
|
|
//Right icon
|
|
if (activePage.next == undefined) {
|
|
if (activePage.homeIcon != undefined) {
|
|
navigationString += '~~~button~bHome~' + Icons.GetIcon(activePage!.homeIcon);
|
|
if (activePage.homeIconColor != undefined) {
|
|
navigationString += '~' + rgb_dec565(activePage.homeIconColor) + '~~';
|
|
} else {
|
|
navigationString += '~' + rgb_dec565(White) + '~~';
|
|
}
|
|
} else {
|
|
navigationString += '~~~button~bHome~' + Icons.GetIcon('home') + '~' + rgb_dec565(White) + '~~';
|
|
}
|
|
} else {
|
|
if (activePage.nextIcon != undefined) {
|
|
navigationString += '~~~button~bSubNext~' + Icons.GetIcon(activePage.nextIcon);
|
|
if (activePage.nextIconColor != undefined) {
|
|
navigationString += '~' + rgb_dec565(activePage!.nextIconColor) + '~~';
|
|
} else {
|
|
navigationString += '~' + rgb_dec565(White) + '~~';
|
|
}
|
|
} else {
|
|
navigationString += '~~~button~bSubNext~' + Icons.GetIcon('arrow-right-bold') + '~' + rgb_dec565(White) + '~~';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (activePage && activePage.subPage && navigationString != '') {
|
|
return navigationString;
|
|
}
|
|
|
|
const getNavigationStringForPage = (icon: string, color: number) => `button~bUp~${Icons.GetIcon(icon)}~${color} ~~~delete~~~~~`;
|
|
|
|
switch (pageId) {
|
|
case -1:
|
|
case -2:
|
|
return getNavigationStringForPage('arrow-up-bold', rgb_dec565(White));
|
|
default: {
|
|
const prevIcon = activePage && activePage.prevIcon ? Icons.GetIcon(activePage!.prevIcon) : Icons.GetIcon('arrow-left-bold');
|
|
const prevIconColor = activePage && activePage.prevIconColor ? rgb_dec565(activePage!.prevIconColor) : rgb_dec565(White);
|
|
const nextIcon = activePage && activePage.nextIcon ? Icons.GetIcon(activePage!.nextIcon) : Icons.GetIcon('arrow-right-bold');
|
|
const nextIconColor = activePage && activePage.nextIconColor ? rgb_dec565(activePage!.nextIconColor) : rgb_dec565(White);
|
|
|
|
return `button~bPrev~${prevIcon}~${prevIconColor}~~~button~bNext~${nextIcon}~${nextIconColor}~~`;
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function getNavigationString: ' + err.message, 'warn');
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Generates the payload for a detail page on the NSPanel.
|
|
*
|
|
* This function creates and returns the payload required to display a detail page on the NSPanel.
|
|
*
|
|
* @function GenerateDetailPage
|
|
* @param {NSPanel.PopupType} type - The type of popup to display.
|
|
* @param {NSPanel.mediaOptional | undefined} optional - Optional media configuration for the detail page.
|
|
* @param {PageItem} pageItem - The page item configuration.
|
|
* @param {number | undefined} placeId - The place ID associated with the detail page, if applicable.
|
|
* @returns {NSPanel.Payload[]} The payload array for the detail page.
|
|
*/
|
|
function GenerateDetailPage (type: NSPanel.PopupType, optional: NSPanel.mediaOptional | undefined, pageItem: PageItem, placeId: number | undefined): NSPanel.Payload[] {
|
|
if (Debug) log('GenerateDetailPage Übergabe Type: ' + type + ' - optional: ' + optional + ' - pageItem.id: ' + pageItem.id, 'info');
|
|
try {
|
|
let out_msgs: NSPanel.Payload[] = [];
|
|
let id = pageItem.id;
|
|
|
|
if (id && existsObject(id)) {
|
|
const o = getObject(id);
|
|
let val: boolean | number = 0;
|
|
//let icon = Icons.GetIcon('lightbulb');
|
|
let icon = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('lightbulb');
|
|
let icon2 = pageItem.icon2 !== undefined ? Icons.GetIcon(pageItem.icon2) : Icons.GetIcon('lightbulb-outline');
|
|
|
|
let iconColor = rgb_dec565(config.defaultColor);
|
|
const role = o.common.role as NSPanel.roles;
|
|
|
|
if (type == 'popupLight') {
|
|
let switchVal = '0';
|
|
let brightness = 0;
|
|
switch (role) {
|
|
case 'light':
|
|
case 'socket':
|
|
{
|
|
if (existsState(id + '.GET')) {
|
|
val = getState(id + '.GET').val;
|
|
RegisterDetailEntityWatcher(id + '.GET', pageItem, type, placeId);
|
|
} else if (existsState(id + '.SET')) {
|
|
if (pageItem.monobutton != undefined && pageItem.monobutton == true) {
|
|
val = getState(id + '.STATE').val;
|
|
RegisterDetailEntityWatcher(id + '.STATE', pageItem, type, placeId);
|
|
} else {
|
|
val = getState(id + '.SET').val;
|
|
RegisterDetailEntityWatcher(id + '.SET', pageItem, type, placeId);
|
|
}
|
|
}
|
|
|
|
icon = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : role == 'socket' ? Icons.GetIcon('power-socket-de') : Icons.GetIcon('lightbulb');
|
|
|
|
if (val === true) {
|
|
iconColor = GetIconColor(pageItem, 100, true);
|
|
switchVal = '1';
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, false, true);
|
|
icon = icon2;
|
|
}
|
|
|
|
let effect_supported = 'disable';
|
|
if (pageItem.modeList != undefined) {
|
|
effect_supported = 'enable';
|
|
}
|
|
|
|
let tempId = placeId != undefined ? placeId : id;
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpdateDetail' +
|
|
'~' + // entityUpdateDetail
|
|
tempId +
|
|
'~' +
|
|
icon +
|
|
'~' + // iconId
|
|
iconColor +
|
|
'~' + // iconColor
|
|
switchVal +
|
|
'~' + // buttonState
|
|
'disable' +
|
|
'~' + // sliderBrightnessPos
|
|
'disable' +
|
|
'~' + // sliderColorTempPos
|
|
'disable' +
|
|
'~' + // colorMode
|
|
'' +
|
|
'~' + // Color identifier
|
|
findLocale('lights', 'Temperature') +
|
|
'~' + // Temperature identifier
|
|
findLocale('lights', 'Brightness') +
|
|
'~' + // Brightness identifier
|
|
effect_supported,
|
|
});
|
|
}
|
|
break;
|
|
// Dimmer
|
|
case 'dimmer':
|
|
{
|
|
if (existsState(id + '.ON_ACTUAL')) {
|
|
val = getState(id + '.ON_ACTUAL').val;
|
|
RegisterDetailEntityWatcher(id + '.ON_ACTUAL', pageItem, type, placeId);
|
|
} else if (existsState(id + '.ON_SET')) {
|
|
val = getState(id + '.ON_SET').val;
|
|
RegisterDetailEntityWatcher(id + '.ON_SET', pageItem, type, placeId);
|
|
}
|
|
|
|
if (existsState(id + '.ACTUAL')) {
|
|
if (pageItem.minValueBrightness != undefined && pageItem.maxValueBrightness != undefined) {
|
|
brightness = Math.trunc(scale(getState(id + '.ACTUAL').val, pageItem.minValueBrightness, pageItem.maxValueBrightness, 100, 0));
|
|
} else {
|
|
brightness = getState(id + '.ACTUAL').val;
|
|
}
|
|
} else {
|
|
log('function GenerateDetailPage role:dimmer -> Alias-Datenpoint: ' + id + '.ACTUAL could not be read', 'warn');
|
|
}
|
|
|
|
if (val === true) {
|
|
iconColor = GetIconColor(pageItem, brightness, true);
|
|
switchVal = '1';
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, false, true);
|
|
icon = icon2;
|
|
}
|
|
|
|
RegisterDetailEntityWatcher(id + '.ACTUAL', pageItem, type, placeId);
|
|
|
|
let effect_supported = 'disable';
|
|
if (pageItem.modeList != undefined) {
|
|
effect_supported = 'enable';
|
|
}
|
|
|
|
let tempId = placeId != undefined ? placeId : id;
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpdateDetail' +
|
|
'~' + //entityUpdateDetail
|
|
tempId +
|
|
'~' +
|
|
icon +
|
|
'~' + //iconId
|
|
iconColor +
|
|
'~' + //iconColor
|
|
switchVal +
|
|
'~' + //buttonState
|
|
brightness +
|
|
'~' + //sliderBrightnessPos
|
|
'disable' +
|
|
'~' + //sliderColorTempPos
|
|
'disable' +
|
|
'~' + //colorMod
|
|
'' +
|
|
'~' + //Color-identifier
|
|
findLocale('lights', 'Temperature') +
|
|
'~' + //Temperature-identifier
|
|
findLocale('lights', 'Brightness') +
|
|
'~' + //Brightness-identifier
|
|
effect_supported,
|
|
});
|
|
}
|
|
break;
|
|
// HUE-Licht
|
|
case 'hue':
|
|
{
|
|
if (existsState(id + '.ON_ACTUAL')) {
|
|
val = getState(id + '.ON_ACTUAL').val;
|
|
RegisterDetailEntityWatcher(id + '.ON_ACTUAL', pageItem, type, placeId);
|
|
}
|
|
|
|
if (existsState(id + '.DIMMER')) {
|
|
if (pageItem.minValueBrightness != undefined && pageItem.maxValueBrightness != undefined) {
|
|
brightness = Math.trunc(scale(getState(id + '.DIMMER').val, pageItem.minValueBrightness, pageItem.maxValueBrightness, 100, 0));
|
|
} else {
|
|
brightness = getState(id + '.DIMMER').val;
|
|
}
|
|
RegisterDetailEntityWatcher(id + '.DIMMER', pageItem, type, placeId);
|
|
} else {
|
|
log('function GenerateDetailPage role:hue -> Alias-Datenpunkt: ' + id + '.DIMMER could not be read', 'warn');
|
|
}
|
|
|
|
if (val === true) {
|
|
iconColor = GetIconColor(pageItem, brightness, true);
|
|
switchVal = '1';
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, false, true);
|
|
icon = icon2;
|
|
}
|
|
|
|
//Calculate color for icon based on color, color temperature and brightness
|
|
//Check last Change of DP HUE or CT for Icon in GUI
|
|
if (existsState(id + '.HUE') && existsState(id + '.TEMPERATURE') && pageItem.interpolateColor) {
|
|
RegisterDetailEntityWatcher(id + '.HUE', pageItem, type, placeId);
|
|
RegisterDetailEntityWatcher(id + '.TEMPERATURE', pageItem, type, placeId);
|
|
//@ts-ignore
|
|
if (getState(id + '.TEMPERATURE').ts < getState(id + '.HUE').ts) {
|
|
if (Debug) log('HUE wurde zuletzt geändert - Lampe ist Color-Mode')
|
|
let huecolor = hsv2rgb(getState(id + '.HUE').val, 1, 1);
|
|
let rgb: RGB = {red: Math.round(huecolor[0]), green: Math.round(huecolor[1]), blue: Math.round(huecolor[2])};
|
|
let cRGB: RGB = lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1)
|
|
iconColor = rgb_dec565(pageItem.interpolateColor !== undefined ? cRGB : config.defaultOnColor);
|
|
} else {
|
|
if (Debug) log('TEMPERATURE wurde zuletzt geändert - Lampe ist CT-Mode');
|
|
if (getState(id + '.TEMPERATURE').val > 1000) {
|
|
//Color-Temperatur in Kelvin
|
|
let rgb: RGB = kelvinToRGB(getState(id + '.TEMPERATURE').val);
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
} else {
|
|
//Color-Temperatur in Mired
|
|
let rgb: RGB = kelvinToRGB(1000000 / (getState(id + '.TEMPERATURE').val));
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
}
|
|
}
|
|
}
|
|
|
|
let colorMode = 'disable';
|
|
if (existsState(id + '.HUE')) {
|
|
if (getState(id + '.HUE').val != null) {
|
|
colorMode = 'enable';
|
|
}
|
|
}
|
|
|
|
let colorTemp: any;
|
|
if (existsState(id + '.TEMPERATURE')) {
|
|
colorTemp = 'enable';
|
|
if (getState(id + '.TEMPERATURE').val != null) {
|
|
if (pageItem.minValueColorTemp !== undefined && pageItem.maxValueColorTemp !== undefined) {
|
|
colorTemp = Math.trunc(scale(getState(id + '.TEMPERATURE').val, pageItem.maxValueColorTemp, pageItem.minValueColorTemp, 100, 0));
|
|
} else {
|
|
colorTemp = getState(id + '.TEMPERATURE').val;
|
|
}
|
|
}
|
|
} else {
|
|
colorTemp = 'disable';
|
|
}
|
|
|
|
let effect_supported = 'disable';
|
|
if (pageItem.modeList != undefined) {
|
|
effect_supported = 'enable';
|
|
}
|
|
|
|
let tempId = placeId != undefined ? placeId : id;
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpdateDetail' +
|
|
'~' + //entityUpdateDetail
|
|
tempId +
|
|
'~' +
|
|
icon +
|
|
'~' + //iconId
|
|
iconColor +
|
|
'~' + //iconColor
|
|
switchVal +
|
|
'~' + //buttonState
|
|
brightness +
|
|
'~' + //sliderBrightnessPos
|
|
colorTemp +
|
|
'~' + //sliderColorTempPos
|
|
colorMode +
|
|
'~' + //colorMode (if hue-alias without hue-datapoint, then disable)
|
|
'Color' +
|
|
'~' + //Color-identifier
|
|
findLocale('lights', 'Temperature') +
|
|
'~' + //Temperature-identifier
|
|
findLocale('lights', 'Brightness') +
|
|
'~' + //Brightness-identifier
|
|
effect_supported,
|
|
});
|
|
}
|
|
break;
|
|
// RGB-Licht
|
|
case 'rgb':
|
|
{
|
|
if (existsState(id + '.ON_ACTUAL')) {
|
|
val = getState(id + '.ON_ACTUAL').val;
|
|
RegisterDetailEntityWatcher(id + '.ON_ACTUAL', pageItem, type, placeId);
|
|
}
|
|
|
|
if (existsState(id + '.DIMMER')) {
|
|
if (pageItem.minValueBrightness != undefined && pageItem.maxValueBrightness != undefined) {
|
|
brightness = Math.trunc(scale(getState(id + '.DIMMER').val, pageItem.minValueBrightness, pageItem.maxValueBrightness, 100, 0));
|
|
} else {
|
|
brightness = getState(id + '.DIMMER').val;
|
|
}
|
|
RegisterDetailEntityWatcher(id + '.DIMMER', pageItem, type, placeId);
|
|
} else {
|
|
log('function GenerateDetailPage role:rgb -> Alias-Datenpunkt: ' + id + '.DIMMER could not be read', 'warn');
|
|
}
|
|
|
|
if (val === true) {
|
|
iconColor = GetIconColor(pageItem, brightness, true);
|
|
switchVal = '1';
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, false, true);
|
|
icon = icon2;
|
|
}
|
|
|
|
let colorMode = 'disable';
|
|
if (existsState(id + '.RED') && existsState(id + '.GREEN') && existsState(id + '.BLUE')) {
|
|
if (getState(id + '.RED').val != null && getState(id + '.GREEN').val != null && getState(id + '.BLUE').val != null) {
|
|
colorMode = 'enable';
|
|
let rgb: RGB = {red: Math.round(getState(id + '.RED').val), green: Math.round(getState(id + '.GREEN').val), blue: Math.round(getState(id + '.BLUE').val)};
|
|
iconColor = rgb_dec565(pageItem.interpolateColor !== undefined ? rgb : config.defaultOnColor);
|
|
}
|
|
}
|
|
|
|
//Calculate color for icon based on color, color temperature and brightness
|
|
//Check last Change of DP RGB or CT for Icon in GUI
|
|
if (existsState(id + '.RED') && existsState(id + '.TEMPERATURE') && pageItem.interpolateColor) {
|
|
RegisterDetailEntityWatcher(id + '.RED', pageItem, type, placeId);
|
|
RegisterDetailEntityWatcher(id + '.TEMPERATURE', pageItem, type, placeId);
|
|
//@ts-ignore
|
|
if (getState(id + '.TEMPERATURE').ts < getState(id + '.RED').ts) {
|
|
if (Debug) log('RGB wurde zuletzt geändert - Lampe ist Color-Mode')
|
|
let rgb: RGB = {red: Math.round(getState(id + '.RED').val), green: Math.round(getState(id + '.GREEN').val), blue: Math.round(getState(id + '.BLUE').val)};
|
|
let cRGB: RGB = lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1)
|
|
iconColor = rgb_dec565(pageItem.interpolateColor !== undefined ? cRGB : config.defaultOnColor);
|
|
} else {
|
|
if (Debug) log('TEMPERATURE wurde zuletzt geändert - Lampe ist CT-Mode');
|
|
if (getState(id + '.TEMPERATURE').val > 1000) {
|
|
//Color-Temperatur in Kelvin
|
|
let rgb: RGB = kelvinToRGB(getState(id + '.TEMPERATURE').val);
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
} else {
|
|
//Color-Temperatur in Mired
|
|
let rgb: RGB = kelvinToRGB(1000000 / (getState(id + '.TEMPERATURE').val));
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
}
|
|
}
|
|
}
|
|
|
|
let colorTemp: any;
|
|
if (existsState(id + '.TEMPERATURE')) {
|
|
colorTemp = 0;
|
|
if (getState(id + '.TEMPERATURE').val != null) {
|
|
if (pageItem.minValueColorTemp !== undefined && pageItem.maxValueColorTemp !== undefined) {
|
|
colorTemp = Math.trunc(scale(getState(id + '.TEMPERATURE').val, pageItem.maxValueColorTemp, pageItem.minValueColorTemp!, 100, 0));
|
|
} else {
|
|
colorTemp = getState(id + '.TEMPERATURE').val;
|
|
}
|
|
}
|
|
} else {
|
|
colorTemp = 'disable';
|
|
}
|
|
|
|
let effect_supported = 'disable';
|
|
if (pageItem.modeList != undefined) {
|
|
effect_supported = 'enable';
|
|
}
|
|
|
|
let tempId = placeId != undefined ? placeId : id;
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpdateDetail' +
|
|
'~' + //entityUpdateDetail
|
|
tempId +
|
|
'~' +
|
|
icon +
|
|
'~' + //iconId
|
|
iconColor +
|
|
'~' + //iconColor
|
|
switchVal +
|
|
'~' + //buttonState
|
|
brightness +
|
|
'~' + //sliderBrightnessPos
|
|
colorTemp +
|
|
'~' + //sliderColorTempPos
|
|
colorMode +
|
|
'~' + //colorMode (if hue-alias without hue-datapoint, then disable)
|
|
'Color' +
|
|
'~' + //Color-identifier
|
|
findLocale('lights', 'Temperature') +
|
|
'~' + //Temperature-identifier
|
|
findLocale('lights', 'Brightness') +
|
|
'~' + //Brightness-identifier
|
|
effect_supported,
|
|
});
|
|
}
|
|
break;
|
|
// RGB-Licht-einzeln (HEX)
|
|
case 'rgbSingle':
|
|
{
|
|
if (existsState(id + '.ON_ACTUAL')) {
|
|
val = getState(id + '.ON_ACTUAL').val;
|
|
RegisterDetailEntityWatcher(id + '.ON_ACTUAL', pageItem, type, placeId);
|
|
}
|
|
|
|
if (existsState(id + '.DIMMER')) {
|
|
if (pageItem.minValueBrightness != undefined && pageItem.maxValueBrightness != undefined) {
|
|
brightness = Math.trunc(scale(getState(id + '.DIMMER').val, pageItem.minValueBrightness, pageItem.maxValueBrightness, 100, 0));
|
|
} else {
|
|
brightness = getState(id + '.DIMMER').val;
|
|
}
|
|
RegisterDetailEntityWatcher(id + '.DIMMER', pageItem, type, placeId);
|
|
} else {
|
|
log('function GenerateDetailPage role:rgbSingle -> Alias-Datenpunkt: ' + id + '.DIMMER could not be read', 'warn');
|
|
}
|
|
|
|
if (val === true) {
|
|
iconColor = GetIconColor(pageItem, brightness, true);
|
|
switchVal = '1';
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, false, true);
|
|
icon = icon2;
|
|
}
|
|
|
|
let colorMode = 'disable';
|
|
if (existsState(id + '.RGB')) {
|
|
if (getState(id + '.RGB').val != null) {
|
|
colorMode = 'enable';
|
|
let hex = getState(id + '.RGB').val;
|
|
let hexRed = parseInt(hex[1] + hex[2], 16);
|
|
let hexGreen = parseInt(hex[3] + hex[4], 16);
|
|
let hexBlue = parseInt(hex[5] + hex[6], 16);
|
|
let rgb: RGB = {red: Math.round(hexRed), green: Math.round(hexGreen), blue: Math.round(hexBlue)};
|
|
iconColor = rgb_dec565(pageItem.interpolateColor !== undefined ? rgb : config.defaultOnColor);
|
|
}
|
|
}
|
|
|
|
//Calculate color for icon based on color, color temperature and brightness
|
|
//Check last Change of DP RGB or CT for Icon in GUI
|
|
if (existsState(id + '.RGB') && existsState(id + '.TEMPERATURE') && pageItem.interpolateColor) {
|
|
RegisterDetailEntityWatcher(id + '.RGB', pageItem, type, placeId);
|
|
RegisterDetailEntityWatcher(id + '.TEMPERATURE', pageItem, type, placeId);
|
|
//@ts-ignore
|
|
if (getState(id + '.TEMPERATURE').ts < getState(id + '.RGB').ts) {
|
|
if (Debug) log('RGB wurde zuletzt geändert - Lampe ist Color-Mode')
|
|
let hex = getState(id + '.RGB').val;
|
|
let hexRed = parseInt(hex[1] + hex[2], 16);
|
|
let hexGreen = parseInt(hex[3] + hex[4], 16);
|
|
let hexBlue = parseInt(hex[5] + hex[6], 16);
|
|
let rgb: RGB = {red: Math.round(hexRed), green: Math.round(hexGreen), blue: Math.round(hexBlue)};
|
|
let cRGB: RGB = lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1)
|
|
iconColor = rgb_dec565(pageItem.interpolateColor !== undefined ? cRGB : config.defaultOnColor);
|
|
} else {
|
|
if (Debug) log('TEMPERATURE wurde zuletzt geändert - Lampe ist CT-Mode');
|
|
if (getState(id + '.TEMPERATURE').val > 1000) {
|
|
//Color-Temperatur in Kelvin
|
|
let rgb: RGB = kelvinToRGB(getState(id + '.TEMPERATURE').val);
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
} else {
|
|
//Color-Temperatur in Mired
|
|
let rgb: RGB = kelvinToRGB(1000000 / (getState(id + '.TEMPERATURE').val));
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
}
|
|
}
|
|
}
|
|
|
|
let colorTemp: any;
|
|
if (existsState(id + '.TEMPERATURE')) {
|
|
colorTemp = 0;
|
|
if (getState(id + '.TEMPERATURE').val != null) {
|
|
if (pageItem.minValueColorTemp !== undefined && pageItem.maxValueColorTemp !== undefined) {
|
|
colorTemp = Math.trunc(scale(getState(id + '.TEMPERATURE').val, pageItem.maxValueColorTemp, pageItem.minValueColorTemp, 100, 0));
|
|
} else {
|
|
colorTemp = getState(id + '.TEMPERATURE').val;
|
|
}
|
|
}
|
|
} else {
|
|
colorTemp = 'disable';
|
|
}
|
|
|
|
let effect_supported = 'disable';
|
|
if (pageItem.modeList != undefined) {
|
|
effect_supported = 'enable';
|
|
}
|
|
|
|
let tempId = placeId != undefined ? placeId : id;
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpdateDetail' +
|
|
'~' + //entityUpdateDetail
|
|
tempId +
|
|
'~' +
|
|
icon +
|
|
'~' + //iconId
|
|
iconColor +
|
|
'~' + //iconColor
|
|
switchVal +
|
|
'~' + //buttonState
|
|
brightness +
|
|
'~' + //sliderBrightnessPos
|
|
colorTemp +
|
|
'~' + //sliderColorTempPos
|
|
colorMode +
|
|
'~' + //colorMode (if hue-alias without hue-datapoint, then disable)
|
|
'Color' +
|
|
'~' + //Color-identifier
|
|
findLocale('lights', 'Temperature') +
|
|
'~' + //Temperature-identifier
|
|
findLocale('lights', 'Brightness') +
|
|
'~' + //Brightness-identifier
|
|
effect_supported,
|
|
});
|
|
}
|
|
break;
|
|
|
|
//CIE (XY)
|
|
case 'cie':
|
|
{
|
|
if (existsState(id + '.ON')) {
|
|
val = getState(id + '.ON').val;
|
|
RegisterDetailEntityWatcher(id + '.ON', pageItem, type, placeId);
|
|
}
|
|
|
|
if (existsState(id + '.DIMMER')) {
|
|
if (pageItem.minValueBrightness != undefined && pageItem.maxValueBrightness != undefined) {
|
|
brightness = Math.trunc(scale(getState(id + '.DIMMER').val, pageItem.minValueBrightness, pageItem.maxValueBrightness, 100, 0));
|
|
} else {
|
|
brightness = getState(id + '.DIMMER').val;
|
|
}
|
|
RegisterDetailEntityWatcher(id + '.DIMMER', pageItem, type, placeId);
|
|
} else {
|
|
log('function GenerateDetailPage role:ct -> Alias-Datenpunkt: ' + id + '.DIMMER could not be read', 'warn');
|
|
}
|
|
|
|
if (val === true) {
|
|
iconColor = GetIconColor(pageItem, brightness, true);
|
|
switchVal = '1';
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, false, true);
|
|
icon = icon2;
|
|
}
|
|
|
|
let colorMode = 'disable';
|
|
if (existsState(id + '.CIE')) {
|
|
if (getState(id + '.CIE').val != null) {
|
|
colorMode = 'enable';
|
|
let cie: string = getState(id + '.CIE').val;
|
|
let cieArray = (cie.substring(1, cie.length -1)).split(',');
|
|
let rgb: RGB = cie_to_rgb(parseFloat(cieArray[0]), parseFloat(cieArray[1]), 254);
|
|
iconColor = rgb_dec565(pageItem.interpolateColor !== undefined ? rgb : config.defaultOnColor);
|
|
log(iconColor)
|
|
}
|
|
}
|
|
|
|
//Calculate color for icon based on color, color temperature and brightness
|
|
//Check last Change of DP CIE or CT for Icon in GUI
|
|
if (existsState(id + '.CIE') && existsState(id + '.TEMPERATURE') && pageItem.interpolateColor) {
|
|
RegisterDetailEntityWatcher(id + '.CIE', pageItem, type, placeId);
|
|
RegisterDetailEntityWatcher(id + '.TEMPERATURE', pageItem, type, placeId);
|
|
//@ts-ignore
|
|
if (getState(id + '.TEMPERATURE').ts < getState(id + '.CIE').ts) {
|
|
if (Debug) log('CIE wurde zuletzt geändert - Lampe ist Color-Mode')
|
|
let cie: string = getState(id + '.CIE').val;
|
|
let cieArray = (cie.substring(1, cie.length -1)).split(',');
|
|
let rgb: RGB = cie_to_rgb(parseFloat(cieArray[0]), parseFloat(cieArray[1]), 254);
|
|
let cRGB: RGB = lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1)
|
|
iconColor = rgb_dec565(pageItem.interpolateColor !== undefined ? cRGB : config.defaultOnColor);
|
|
} else {
|
|
if (Debug) log('TEMPERATURE wurde zuletzt geändert - Lampe ist CT-Mode');
|
|
if (getState(id + '.TEMPERATURE').val > 1000) {
|
|
//Color-Temperatur in Kelvin
|
|
let rgb: RGB = kelvinToRGB(getState(id + '.TEMPERATURE').val);
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
} else {
|
|
//Color-Temperatur in Mired
|
|
let rgb: RGB = kelvinToRGB(1000000 / (getState(id + '.TEMPERATURE').val));
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
}
|
|
}
|
|
}
|
|
|
|
let colorTemp = 0;
|
|
if (existsState(id + '.TEMPERATURE')) {
|
|
if (getState(id + '.TEMPERATURE').val != null) {
|
|
if (pageItem.minValueColorTemp !== undefined && pageItem.maxValueColorTemp !== undefined) {
|
|
colorTemp = Math.trunc(scale(getState(id + '.TEMPERATURE').val, pageItem.maxValueColorTemp, pageItem.minValueColorTemp, 100, 0));
|
|
} else {
|
|
colorTemp = getState(id + '.TEMPERATURE').val;
|
|
}
|
|
}
|
|
} else {
|
|
log('function GenerateDetailPage role:ct -> Alias-Datenpunkt: ' + id + '.TEMPERATURE could not be read', 'warn');
|
|
}
|
|
|
|
let effect_supported = 'disable';
|
|
if (pageItem.modeList != undefined) {
|
|
effect_supported = 'enable';
|
|
}
|
|
|
|
let tempId = placeId != undefined ? placeId : id;
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpdateDetail' +
|
|
'~' + //entityUpdateDetail
|
|
tempId +
|
|
'~' +
|
|
icon +
|
|
'~' + //iconId
|
|
iconColor +
|
|
'~' + //iconColor
|
|
switchVal +
|
|
'~' + //buttonState
|
|
brightness +
|
|
'~' + //sliderBrightnessPos
|
|
colorTemp +
|
|
'~' + //sliderColorTempPos
|
|
colorMode +
|
|
'~' + //colorMode (if hue-alias without hue-datapoint, then disable)
|
|
'Color' +
|
|
'~' + //Color-identifier
|
|
findLocale('lights', 'Temperature') +
|
|
'~' + //Temperature-identifier
|
|
findLocale('lights', 'Brightness') +
|
|
'~' + //Brightness-identifier
|
|
effect_supported,
|
|
});
|
|
}
|
|
break;
|
|
// Farbtemperatur (CT)
|
|
case 'ct':
|
|
{
|
|
if (existsState(id + '.ON')) {
|
|
val = getState(id + '.ON').val;
|
|
RegisterDetailEntityWatcher(id + '.ON', pageItem, type, placeId);
|
|
}
|
|
|
|
if (existsState(id + '.DIMMER')) {
|
|
if (pageItem.minValueBrightness != undefined && pageItem.maxValueBrightness != undefined) {
|
|
brightness = Math.trunc(scale(getState(id + '.DIMMER').val, pageItem.minValueBrightness, pageItem.maxValueBrightness, 100, 0));
|
|
} else {
|
|
brightness = getState(id + '.DIMMER').val;
|
|
}
|
|
RegisterDetailEntityWatcher(id + '.DIMMER', pageItem, type, placeId);
|
|
} else {
|
|
log('function GenerateDetailPage role:ct -> Alias-Datenpunkt: ' + id + '.DIMMER could not be read', 'warn');
|
|
}
|
|
|
|
if (val === true) {
|
|
iconColor = GetIconColor(pageItem, brightness, true);
|
|
switchVal = '1';
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, false, true);
|
|
icon = icon2;
|
|
}
|
|
|
|
let colorMode = 'disable';
|
|
|
|
//Calculate color for icon based on color temperature and brightness
|
|
if (existsState(id + '.TEMPERATURE') && pageItem.interpolateColor) {
|
|
RegisterDetailEntityWatcher(id + '.TEMPERATURE', pageItem, type, placeId);
|
|
if (getState(id + '.TEMPERATURE').val > 1000) {
|
|
//Color-Temperatur in Kelvin
|
|
let rgb: RGB = kelvinToRGB(getState(id + '.TEMPERATURE').val);
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
} else {
|
|
//Color-Temperatur in Mired
|
|
let rgb: RGB = kelvinToRGB(1000000 / (getState(id + '.TEMPERATURE').val));
|
|
iconColor = rgb_dec565(lightenDarkenColor(ConvertRGBtoHex(rgb.red, rgb.green, rgb.blue), (100 - brightness) * -1));
|
|
}
|
|
}
|
|
|
|
let colorTemp = 0;
|
|
if (existsState(id + '.TEMPERATURE')) {
|
|
if (getState(id + '.TEMPERATURE').val != null) {
|
|
if (pageItem.minValueColorTemp !== undefined && pageItem.maxValueColorTemp !== undefined) {
|
|
colorTemp = Math.trunc(scale(getState(id + '.TEMPERATURE').val, pageItem.maxValueColorTemp, pageItem.minValueColorTemp, 100, 0));
|
|
} else {
|
|
colorTemp = getState(id + '.TEMPERATURE').val;
|
|
}
|
|
}
|
|
} else {
|
|
log('function GenerateDetailPage role:ct -> Alias-Datenpunkt: ' + id + '.TEMPERATURE could not be read', 'warn');
|
|
}
|
|
|
|
let effect_supported = 'disable';
|
|
if (pageItem.modeList != undefined) {
|
|
effect_supported = 'enable';
|
|
}
|
|
|
|
let tempId = placeId != undefined ? placeId : id;
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpdateDetail' +
|
|
'~' + //entityUpdateDetail
|
|
tempId +
|
|
'~' +
|
|
icon +
|
|
'~' + //iconId
|
|
iconColor +
|
|
'~' + //iconColor
|
|
switchVal +
|
|
'~' + //buttonState
|
|
brightness +
|
|
'~' + //sliderBrightnessPos
|
|
colorTemp +
|
|
'~' + //sliderColorTempPos
|
|
colorMode +
|
|
'~' + //colorMode (if hue-alias without hue-datapoint, then disable)
|
|
'Color' +
|
|
'~' + //Color-identifier
|
|
findLocale('lights', 'Temperature') +
|
|
'~' + //Temperature-identifier
|
|
findLocale('lights', 'Brightness') +
|
|
'~' + //Brightness-identifier
|
|
effect_supported,
|
|
});
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (type == 'popupSlider') {
|
|
|
|
let tempId = placeId != undefined ? placeId : id;
|
|
|
|
if (isPageMediaItem(pageItem)) {
|
|
|
|
const vTempAdapter = pageItem.adapterPlayerInstance!.split('.');
|
|
const vAdapter: NSPanel.PlayerType = vTempAdapter[0] as NSPanel.PlayerType;
|
|
|
|
if (vAdapter == 'alexa2') {
|
|
|
|
let tSlider1: string = pageItem.equalizerSlider[0].Slider1.heading ?? "Bass";
|
|
let tIconS1M: string = Icons.GetIcon(pageItem.equalizerSlider[0].Slider1.icon1 ?? "minus-box");
|
|
let tIconS1P: string = Icons.GetIcon(pageItem.equalizerSlider[0].Slider1.icon2 ?? "plus-box");
|
|
let hSlider1MinVal: number = pageItem.equalizerSlider[0].Slider1.minValue ?? 0;
|
|
let hSlider1MaxVal: number = pageItem.equalizerSlider[0].Slider1.maxValue ?? 12;
|
|
let hSlider1ZeroVal: number = pageItem.equalizerSlider[0].Slider1.zeroValue ?? 6;
|
|
let hSlider1CurVal: number = getState(pageItem.adapterPlayerInstance! + 'Echo-Devices.' + pageItem.mediaDevice! + '.Preferences.equalizerBass').val + hSlider1ZeroVal;
|
|
let hSlider1Step: number = pageItem.equalizerSlider[0].Slider1.stepValue ?? 1;
|
|
let hSlider1Visibility: string = "enable";
|
|
|
|
let tSlider2: string = pageItem.equalizerSlider[0].Slider2.heading ?? "MidRange";
|
|
let tIconS2M: string = Icons.GetIcon(pageItem.equalizerSlider[0].Slider2.icon1 ?? "minus-box");
|
|
let tIconS2P: string = Icons.GetIcon(pageItem.equalizerSlider[0].Slider2.icon2 ?? "plus-box");
|
|
let hSlider2MinVal: number = pageItem.equalizerSlider[0].Slider2.minValue ?? 0;
|
|
let hSlider2MaxVal: number = pageItem.equalizerSlider[0].Slider2.maxValue ?? 12;
|
|
let hSlider2ZeroVal: number = pageItem.equalizerSlider[0].Slider2.zeroValue ?? 6;
|
|
let hSlider2CurVal: number = getState(pageItem.adapterPlayerInstance! + 'Echo-Devices.' + pageItem.mediaDevice! + '.Preferences.equalizerMidRange').val + hSlider2ZeroVal;
|
|
let hSlider2Step: number = pageItem.equalizerSlider[0].Slider2.stepValue ?? 1;
|
|
let hSlider2Visibility: string = "enable";
|
|
|
|
let tSlider3: string = pageItem.equalizerSlider[0].Slider3.heading ?? "Treble";
|
|
let tIconS3M: string = Icons.GetIcon(pageItem.equalizerSlider[0].Slider3.icon1 ?? "minus-box");
|
|
let tIconS3P: string = Icons.GetIcon(pageItem.equalizerSlider[0].Slider3.icon2 ?? "plus-box");
|
|
let hSlider3MinVal: number = pageItem.equalizerSlider[0].Slider3.minValue ?? 0;
|
|
let hSlider3MaxVal: number = pageItem.equalizerSlider[0].Slider3.maxValue ?? 12;
|
|
let hSlider3ZeroVal: number = pageItem.equalizerSlider[0].Slider3.zeroValue ?? 6;
|
|
let hSlider3CurVal: number = getState(pageItem.adapterPlayerInstance! + 'Echo-Devices.' + pageItem.mediaDevice! + '.Preferences.equalizerTreble').val + hSlider3ZeroVal;
|
|
let hSlider3Step: number = pageItem.equalizerSlider[0].Slider3.stepValue ?? 1;
|
|
let hSlider3Visibility: string = "enable";
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpdateDetail' +
|
|
'~' + //entityUpdateDetail
|
|
tempId +
|
|
'~' +
|
|
// Slider1
|
|
tSlider1 + // Slider1 Headline --> tmSerial 2
|
|
'~' +
|
|
tIconS1M + // Slider1 Left Icon --> tmSerial 3
|
|
'~' +
|
|
tIconS1P + // Slider1 Right Icon --> tmSerial 4
|
|
'~' +
|
|
hSlider1CurVal + // Slider1 Current Slider Value --> tmSerial 5
|
|
'~' +
|
|
hSlider1MinVal + // Slider1 Minimal Slider Value --> tmSerial 6
|
|
'~' +
|
|
hSlider1MaxVal + // Slider1 Maximal Slider Value --> tmSerial 7
|
|
'~' +
|
|
hSlider1ZeroVal + // If Slider 0 is betweeb Min and Max --> tmSerial 8
|
|
'~' +
|
|
hSlider1Step + // If Slider Tap > --> tmSerial 9
|
|
'~' +
|
|
hSlider1Visibility // If Slider Tap > --> tmSerial 10
|
|
// Slider2
|
|
+ '~' +
|
|
tSlider2 + // Slider2 Headline --> tmSerial 11
|
|
'~' +
|
|
tIconS2M + // Slider2 Left Icon --> tmSerial 12
|
|
'~' +
|
|
tIconS2P + // Slider2 Right Icon --> tmSerial 13
|
|
'~' +
|
|
hSlider2CurVal + // Slider2 Current Slider Value --> tmSerial 14
|
|
'~' +
|
|
hSlider2MinVal + // Slider2 Minimal Slider Value --> tmSerial 15
|
|
'~' +
|
|
hSlider2MaxVal + // Slider2 Maximal Slider Value --> tmSerial 16
|
|
'~' +
|
|
hSlider2ZeroVal + // If Slider2 0 is betweeb Min and Max --> tmSerial 17
|
|
'~' +
|
|
hSlider2Step + // If Slider2 Tap > 1 --> tmSerial 18
|
|
'~' +
|
|
hSlider2Visibility // If Slider Tap > --> tmSerial 19
|
|
// Slider3
|
|
+ '~' +
|
|
tSlider3 + // Slider3 Headline --> tmSerial 20
|
|
'~' +
|
|
tIconS3M + // Slider3 Left Icon --> tmSerial 21
|
|
'~' +
|
|
tIconS3P + // Slider3 Right Icon --> tmSerial 22
|
|
'~' +
|
|
hSlider3CurVal + // Slider3 Current Slider Value --> tmSerial 23
|
|
'~' +
|
|
hSlider3MinVal + // Slider2 Minimal Slider Value --> tmSerial 24
|
|
'~' +
|
|
hSlider3MaxVal + // Slider2 Maximal Slider Value --> tmSerial 25
|
|
'~' +
|
|
hSlider3ZeroVal + // If Slider3 0 is betweeb Min and Max --> tmSerial 26
|
|
'~' +
|
|
hSlider3Step + // If Slider3 Tap > 1 --> tmSerial 27
|
|
'~' +
|
|
hSlider3Visibility // If Slider Tap > --> tmSerial 28
|
|
});
|
|
}
|
|
} else { // no Media Item
|
|
|
|
let tSlider2: string = "";
|
|
let tIconS2M: string = Icons.GetIcon("minus-box");
|
|
let tIconS2P: string = Icons.GetIcon("plus-box");
|
|
let hSlider2MinVal: number = pageItem.minValue ?? 0;
|
|
let hSlider2MaxVal: number = pageItem.maxValue ?? 100;
|
|
let hSlider2ZeroVal: number = 0;
|
|
let hSlider2CurVal: number = getState(id + '.ACTUAL').val;
|
|
let hSlider2Step: number = 1;
|
|
let hSlider2Visibility: string = "enable";
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpdateDetail' +
|
|
'~' + //entityUpdateDetail
|
|
tempId +
|
|
// Slider1
|
|
'~~~~~~~~~disable' +
|
|
// Slider2
|
|
'~' +
|
|
tSlider2 + // Slider2 Headline --> tmSerial 11
|
|
'~' +
|
|
tIconS2M + // Slider2 Left Icon --> tmSerial 12
|
|
'~' +
|
|
tIconS2P + // Slider2 Right Icon --> tmSerial 13
|
|
'~' +
|
|
hSlider2CurVal + // Slider2 Current Slider Value --> tmSerial 14
|
|
'~' +
|
|
hSlider2MinVal + // Slider2 Minimal Slider Value --> tmSerial 15
|
|
'~' +
|
|
hSlider2MaxVal + // Slider2 Maximal Slider Value --> tmSerial 16
|
|
'~' +
|
|
hSlider2ZeroVal + // If Slider2 0 is betweeb Min and Max --> tmSerial 17
|
|
'~' +
|
|
hSlider2Step + // If Slider2 Tap > 1 --> tmSerial 18
|
|
'~' +
|
|
hSlider2Visibility + // If Slider Tap > --> tmSerial 19
|
|
// Slider3
|
|
'~~~~~~~~~disable'
|
|
});
|
|
|
|
|
|
}
|
|
}
|
|
|
|
if (type == 'popupShutter') {
|
|
icon = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('window-open');
|
|
if (existsState(id + '.ACTUAL')) {
|
|
val = getState(id + '.ACTUAL').val;
|
|
RegisterDetailEntityWatcher(id + '.ACTUAL', pageItem, type, placeId);
|
|
} else if (existsState(id + '.SET')) {
|
|
val = getState(id + '.SET').val;
|
|
//RegisterDetailEntityWatcher(id + '.SET', pageItem, type);
|
|
}
|
|
let tilt_position: any = 'disabled';
|
|
if (existsState(id + '.TILT_ACTUAL')) {
|
|
tilt_position = getState(id + '.TILT_ACTUAL').val;
|
|
RegisterDetailEntityWatcher(id + '.TILT_ACTUAL', pageItem, type, placeId);
|
|
} else if (existsState(id + '.TILT_SET')) {
|
|
tilt_position = getState(id + '.TILT_SET').val;
|
|
//RegisterDetailEntityWatcher(id + '.TILT_SET', pageItem, type);
|
|
}
|
|
|
|
let min_Level: number = 0;
|
|
let max_Level: number = 100;
|
|
if (pageItem.minValueLevel !== undefined && pageItem.maxValueLevel !== undefined) {
|
|
min_Level = pageItem.minValueLevel;
|
|
max_Level = pageItem.maxValueLevel;
|
|
val = Math.trunc(scale(getState(id + '.ACTUAL').val, pageItem.minValueLevel, pageItem.maxValueLevel, 100, 0));
|
|
}
|
|
let min_Tilt: number = 0;
|
|
let max_Tilt: number = 100;
|
|
if (pageItem.minValueTilt !== undefined && pageItem.maxValueTilt !== undefined) {
|
|
min_Tilt = pageItem.minValueTilt;
|
|
max_Tilt = pageItem.maxValueTilt;
|
|
tilt_position = Math.trunc(scale(getState(id + '.TILT_ACTUAL').val, pageItem.minValueTilt, pageItem.maxValueTilt, 100, 0));
|
|
}
|
|
|
|
if (Debug) log('minLevel ' + min_Level + ' maxLevel ' + max_Level + ' Level ' + val, 'info');
|
|
if (Debug) log('minTilt ' + min_Tilt + ' maxTilt ' + max_Tilt + ' TiltPosition ' + tilt_position, 'info');
|
|
|
|
let textSecondRow = '';
|
|
let icon_id = icon;
|
|
let icon_up = Icons.GetIcon('arrow-up');
|
|
let icon_stop = Icons.GetIcon('stop');
|
|
let icon_down = Icons.GetIcon('arrow-down');
|
|
let tempVal: number = getState(pageItem.id + '.ACTUAL').val;
|
|
|
|
//Disabled Status while bug in updating origin adapter data points of lift values
|
|
let icon_up_status = 'enable';
|
|
//let icon_up_status = tempVal === min_Level ? 'disable' : 'enable';
|
|
let icon_stop_status = 'enable';
|
|
if (tempVal === min_Level || tempVal === max_Level || checkBlindActive === false) {
|
|
//icon_stop_status = 'disable';
|
|
}
|
|
let icon_down_status = 'enable';
|
|
//let icon_down_status = tempVal === max_Level ? 'disable' : 'enable';
|
|
|
|
let textTilt = '';
|
|
let iconTiltLeft = '';
|
|
let iconTiltStop = '';
|
|
let iconTiltRight = '';
|
|
let iconTiltLeftStatus = 'disable';
|
|
let iconTiltStopStatus = 'disable';
|
|
let iconTiltRightStatus = 'disable';
|
|
let tilt_pos = 'disable';
|
|
|
|
if (existsState(id + '.TILT_SET')) {
|
|
textTilt = findLocale('blinds', 'Tilt');
|
|
iconTiltLeft = Icons.GetIcon('arrow-top-right');
|
|
iconTiltStop = Icons.GetIcon('stop');
|
|
iconTiltRight = Icons.GetIcon('arrow-bottom-left');
|
|
iconTiltLeftStatus = getState(id + '.TILT_ACTUAL').val != max_Tilt ? 'enable' : 'disable';
|
|
iconTiltStopStatus = 'enable';
|
|
iconTiltRightStatus = getState(id + '.TILT_ACTUAL').val != min_Tilt ? 'enable' : 'disable';
|
|
tilt_pos = tilt_position;
|
|
}
|
|
|
|
if (pageItem.secondRow != undefined) {
|
|
textSecondRow = pageItem.secondRow;
|
|
}
|
|
|
|
let tempId = placeId != undefined ? placeId : id;
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpdateDetail' +
|
|
'~' + //entityUpdateDetail
|
|
tempId +
|
|
'~' + //entity_id
|
|
val +
|
|
'~' + //Shutterposition
|
|
textSecondRow +
|
|
'~' + //pos_status 2.line
|
|
findLocale('blinds', 'Position') +
|
|
'~' + //pos_translation
|
|
icon_id +
|
|
'~' + //{icon_id}~
|
|
icon_up +
|
|
'~' + //{icon_up}~
|
|
icon_stop +
|
|
'~' + //{icon_stop}~
|
|
icon_down +
|
|
'~' + //{icon_down}~
|
|
icon_up_status +
|
|
'~' + //{icon_up_status}~
|
|
icon_stop_status +
|
|
'~' + //{icon_stop_status}~
|
|
icon_down_status +
|
|
'~' + //{icon_down_status}~
|
|
textTilt +
|
|
'~' + //{textTilt}~
|
|
iconTiltLeft +
|
|
'~' + //{iconTiltLeft}~
|
|
iconTiltStop +
|
|
'~' + //{iconTiltStop}~
|
|
iconTiltRight +
|
|
'~' + //{iconTiltRight}~
|
|
iconTiltLeftStatus +
|
|
'~' + //{iconTiltLeftStatus}~
|
|
iconTiltStopStatus +
|
|
'~' + //{iconTiltStopStatus}~
|
|
iconTiltRightStatus +
|
|
'~' + //{iconTiltRightStatus}~
|
|
tilt_pos, //{tilt_pos}")
|
|
});
|
|
}
|
|
|
|
if (type == 'popupShutter2') {
|
|
icon = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : Icons.GetIcon('window-open');
|
|
if (existsState(id + '.ACTUAL')) {
|
|
val = getState(id + '.ACTUAL').val;
|
|
RegisterDetailEntityWatcher(id + '.ACTUAL', pageItem, type, placeId);
|
|
} else if (existsState(id + '.SET')) {
|
|
val = getState(id + '.SET').val;
|
|
}
|
|
|
|
let min_Level: number = 0;
|
|
let max_Level: number = 100;
|
|
|
|
if (pageItem.minValueLevel !== undefined && pageItem.maxValueLevel !== undefined) {
|
|
min_Level = pageItem.minValueLevel;
|
|
max_Level = pageItem.maxValueLevel;
|
|
val = Math.trunc(scale(getState(id + '.ACTUAL').val, pageItem.minValueLevel, pageItem.maxValueLevel, 100, 0));
|
|
}
|
|
|
|
if (Debug) log('minLevel ' + min_Level + ' maxLevel ' + max_Level + ' Level ' + val, 'info');
|
|
|
|
let textSecondRow = '';
|
|
let icon_id = icon;
|
|
let icon_up = Icons.GetIcon('arrow-up');
|
|
let icon_stop = Icons.GetIcon('stop');
|
|
let icon_down = Icons.GetIcon('arrow-down');
|
|
let tempVal: number = getState(pageItem.id + '.ACTUAL').val;
|
|
|
|
//Disabled Status while bug in updating origin adapter data points of lift values
|
|
let icon_up_status = 'enable';
|
|
//let icon_up_status = tempVal === min_Level ? 'disable' : 'enable';
|
|
let icon_stop_status = 'enable';
|
|
if (tempVal === min_Level || tempVal === max_Level || checkBlindActive === false) {
|
|
//icon_stop_status = 'disable';
|
|
}
|
|
let icon_down_status = 'enable';
|
|
//let icon_down_status = tempVal === max_Level ? 'disable' : 'enable';
|
|
|
|
if (pageItem.secondRow != undefined) {
|
|
textSecondRow = pageItem.secondRow;
|
|
}
|
|
|
|
let tempId = placeId != undefined ? placeId : id;
|
|
|
|
//shutterIcons - Button1
|
|
let bEntity1State : boolean = false;
|
|
let bEntity1Icon : string = 'power';
|
|
let bEntity1Color : number = rgb_dec565(White);
|
|
let bEntity1Visibility : string = 'disable';
|
|
if (pageItem.shutterIcons && pageItem.shutterIcons[0] != undefined) {
|
|
if (pageItem.shutterIcons[0].id != undefined) {
|
|
bEntity1Visibility = 'enable';
|
|
RegisterDetailEntityWatcher(pageItem.shutterIcons[0].id, pageItem, type, placeId);
|
|
bEntity1State = getState(pageItem.shutterIcons[0].id).val;
|
|
if (bEntity1State) {
|
|
bEntity1Icon = Icons.GetIcon(pageItem.shutterIcons[0].icon) ?? bEntity1Icon;
|
|
bEntity1Color = rgb_dec565(pageItem.shutterIcons[0].iconOnColor) ?? bEntity1Color;
|
|
} else {
|
|
bEntity1Icon = Icons.GetIcon(pageItem.shutterIcons[0].icon2) ?? bEntity1Icon;
|
|
bEntity1Color = rgb_dec565(pageItem.shutterIcons[0].iconOffColor) ?? bEntity1Color;
|
|
}
|
|
}
|
|
}
|
|
|
|
//shutterIcons - Button2
|
|
let bEntity2State : boolean = false;
|
|
let bEntity2Icon : string = 'power';
|
|
let bEntity2Color : number = rgb_dec565(White);
|
|
let bEntity2Visibility : string = 'disable';
|
|
if (pageItem.shutterIcons && pageItem.shutterIcons[1] != undefined) {
|
|
if (pageItem.shutterIcons[1].id != undefined) {
|
|
bEntity2Visibility = 'enable';
|
|
RegisterDetailEntityWatcher(pageItem.shutterIcons[1].id, pageItem, type, placeId);
|
|
bEntity2State = getState(pageItem.shutterIcons[1].id).val;
|
|
if (bEntity2State) {
|
|
bEntity2Icon = Icons.GetIcon(pageItem.shutterIcons[1].icon) ?? bEntity2Icon;
|
|
bEntity2Color = rgb_dec565(pageItem.shutterIcons[1].iconOnColor) ?? bEntity2Color;
|
|
} else {
|
|
bEntity2Icon = Icons.GetIcon(pageItem.shutterIcons[1].icon2) ?? bEntity2Icon;
|
|
bEntity2Color = rgb_dec565(pageItem.shutterIcons[1].iconOffColor) ?? bEntity2Color;
|
|
}
|
|
}
|
|
}
|
|
|
|
//shutterIcons - Button3
|
|
let bEntity3State : boolean = false;
|
|
let bEntity3Icon : string = 'power';
|
|
let bEntity3Color : number = rgb_dec565(White);
|
|
let bEntity3Visibility : string = 'disable';
|
|
if (pageItem.shutterIcons && pageItem.shutterIcons[2] != undefined) {
|
|
if (pageItem.shutterIcons[2].id != undefined) {
|
|
bEntity3Visibility = 'enable';
|
|
RegisterDetailEntityWatcher(pageItem.shutterIcons[2].id, pageItem, type, placeId);
|
|
bEntity3State = getState(pageItem.shutterIcons[2].id).val;
|
|
if (bEntity3State) {
|
|
bEntity3Icon = Icons.GetIcon(pageItem.shutterIcons[2].icon) ?? bEntity3Icon;
|
|
bEntity3Color = rgb_dec565(pageItem.shutterIcons[2].iconOnColor) ?? bEntity3Color;
|
|
} else {
|
|
bEntity3Icon = Icons.GetIcon(pageItem.shutterIcons[2].icon2) ?? bEntity3Icon;
|
|
bEntity3Color = rgb_dec565(pageItem.shutterIcons[2].iconOffColor) ?? bEntity3Color;
|
|
}
|
|
}
|
|
}
|
|
|
|
let shutterTyp = 'shutter';
|
|
if (pageItem.shutterType != undefined) {
|
|
shutterTyp = pageItem.shutterType;
|
|
}
|
|
|
|
let shutterZeroIsClosed:string = "0";
|
|
if (pageItem.shutterZeroIsClosed != undefined) {
|
|
if(pageItem.shutterZeroIsClosed==true) {
|
|
shutterZeroIsClosed = "1";
|
|
}
|
|
}
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpdateDetail' +
|
|
'~' + //entityUpdateDetail
|
|
tempId +
|
|
'~' + //entity_id
|
|
val +
|
|
'~' + //Shutterposition
|
|
textSecondRow +
|
|
'~' + //pos_status 2.line
|
|
findLocale('blinds', 'Position') +
|
|
'~' + //pos_translation
|
|
icon_id +
|
|
'~' + //{icon_id}~
|
|
icon_up +
|
|
'~' + //{icon_up}~
|
|
icon_stop +
|
|
'~' + //{icon_stop}~
|
|
icon_down +
|
|
'~' + //{icon_down}~
|
|
icon_up_status +
|
|
'~' + //{icon_up_status}~
|
|
icon_stop_status +
|
|
'~' + //{icon_stop_status}~
|
|
icon_down_status +
|
|
//shutterIcons
|
|
//bEntity1
|
|
'~' +
|
|
bEntity1Icon + //12
|
|
'~' +
|
|
bEntity1Color + //13
|
|
'~' +
|
|
bEntity1Visibility + //14
|
|
//bEntity2
|
|
'~' +
|
|
bEntity2Icon + //15
|
|
'~' +
|
|
bEntity2Color + //16
|
|
'~' +
|
|
bEntity2Visibility + //17
|
|
//bEntity3
|
|
'~' +
|
|
bEntity3Icon + //18
|
|
'~' +
|
|
bEntity3Color + //19
|
|
'~' +
|
|
bEntity3Visibility + //20
|
|
'~' +
|
|
shutterTyp +//21 for Future
|
|
'~' +
|
|
shutterZeroIsClosed //21 for Future
|
|
});
|
|
}
|
|
|
|
if (type == 'popupThermo') {
|
|
let vIcon = pageItem.icon != undefined ? pageItem.icon : 'fan';
|
|
let mode1 = isPageThermoItem(pageItem) && pageItem.popupThermoMode1 != undefined ? pageItem.popupThermoMode1.join('?') : '';
|
|
let mode2 = isPageThermoItem(pageItem) && pageItem.popupThermoMode2 != undefined ? pageItem.popupThermoMode2.join('?') : '';
|
|
let mode3 = isPageThermoItem(pageItem) && pageItem.popupThermoMode3 != undefined ? pageItem.popupThermoMode3.join('?') : '';
|
|
|
|
let payloadParameters1 = '~~~~';
|
|
if (isPageThermoItem(pageItem) && pageItem.popupThermoMode1 != undefined) {
|
|
RegisterDetailEntityWatcher(pageItem.id + '.' + pageItem.setThermoAlias![0], pageItem, type, placeId);
|
|
payloadParameters1 =
|
|
pageItem.popUpThermoName![0] +
|
|
'~' + //{heading}~ Mode 1
|
|
'modus1' +
|
|
'~' + //{id}~ Mode 1
|
|
getState(pageItem.id + '.' + pageItem.setThermoAlias![0]).val +
|
|
'~' + //{ACTUAL}~ Mode 1
|
|
mode1 +
|
|
'~'; //{possible values} Mode 1 (1-n)
|
|
}
|
|
|
|
let payloadParameters2 = '~~~~';
|
|
if (isPageThermoItem(pageItem) && pageItem.popupThermoMode2 != undefined) {
|
|
RegisterDetailEntityWatcher(pageItem.id + '.' + pageItem.setThermoAlias![1], pageItem, type, placeId);
|
|
payloadParameters2 =
|
|
pageItem.popUpThermoName![1] +
|
|
'~' + //{heading}~ Mode 2
|
|
'modus2' +
|
|
'~' + //{id}~ Mode 2
|
|
getState(pageItem.id + '.' + pageItem.setThermoAlias![1]).val +
|
|
'~' + //{ACTUAL}~ Mode 2
|
|
mode2 +
|
|
'~'; //{possible values}
|
|
}
|
|
|
|
let payloadParameters3 = '~~~~';
|
|
if (isPageThermoItem(pageItem) && pageItem.popupThermoMode3 != undefined) {
|
|
RegisterDetailEntityWatcher(pageItem.id + '.' + pageItem.setThermoAlias![2], pageItem, type, placeId);
|
|
payloadParameters3 =
|
|
pageItem.popUpThermoName![2] +
|
|
'~' + //{heading}~ Mode 3
|
|
'modus3' +
|
|
'~' + //{id}~ Mode 3
|
|
getState(pageItem.id + '.' + pageItem.setThermoAlias![2]).val +
|
|
'~' + //{ACTUAL}~ Mode 3
|
|
mode3; //{possible values} Mode 3 (1-n)
|
|
}
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpdateDetail' +
|
|
'~' + //entityUpdateDetail
|
|
id +
|
|
'~' + //{entity_id}
|
|
Icons.GetIcon(vIcon) +
|
|
'~' + //{icon_id}~
|
|
11487 +
|
|
'~' + //{icon_color}~
|
|
payloadParameters1 +
|
|
payloadParameters2 +
|
|
payloadParameters3,
|
|
});
|
|
}
|
|
|
|
if (type == 'popupTimer') {
|
|
let timer_actual: number = 0;
|
|
|
|
if (existsState(id + '.ACTUAL')) {
|
|
RegisterDetailEntityWatcher(id + '.ACTUAL', pageItem, type, placeId);
|
|
timer_actual = getState(id + '.ACTUAL').val;
|
|
}
|
|
|
|
if (existsState(id + '.STATE')) {
|
|
RegisterDetailEntityWatcher(id + '.STATE', pageItem, type, placeId);
|
|
}
|
|
|
|
let editable = 1;
|
|
let action1 = '';
|
|
let action2 = '';
|
|
let action3 = '';
|
|
let label1 = '';
|
|
let label2 = '';
|
|
let label3 = '';
|
|
let min_remaining = 0;
|
|
let sec_remaining = 0;
|
|
if (existsState(id + '.STATE')) {
|
|
if (role == 'value.time') {
|
|
if (getState(id + '.STATE').val == 'idle' || getState(id + '.STATE').val == 'paused') {
|
|
min_remaining = Math.floor(timer_actual / 60);
|
|
sec_remaining = timer_actual % 60;
|
|
editable = 1;
|
|
} else {
|
|
min_remaining = Math.floor(timer_actual / 60);
|
|
sec_remaining = timer_actual % 60;
|
|
editable = 1;
|
|
}
|
|
} else if (role == 'level.timer') {
|
|
if (getState(id + '.STATE').val == 'idle' || getState(id + '.STATE').val == 'paused') {
|
|
min_remaining = Math.floor(timer_actual / 60);
|
|
sec_remaining = timer_actual % 60;
|
|
editable = 1;
|
|
action2 = 'start';
|
|
label2 = findLocale('timer', 'start');
|
|
} else {
|
|
min_remaining = Math.floor(timer_actual / 60);
|
|
sec_remaining = timer_actual % 60;
|
|
editable = 0;
|
|
action1 = 'pause';
|
|
action2 = 'cancle';
|
|
action3 = 'finish';
|
|
label1 = findLocale('timer', 'pause');
|
|
label2 = findLocale('timer', 'cancel');
|
|
label3 = findLocale('timer', 'finish');
|
|
}
|
|
} else if (role == 'value.alarmtime') {
|
|
if (getState(id + '.STATE').val == 'paused') {
|
|
min_remaining = Math.floor(timer_actual / 60);
|
|
sec_remaining = timer_actual % 60;
|
|
editable = 1;
|
|
action2 = 'start';
|
|
label2 = findLocale('timer', 'on');
|
|
} else {
|
|
min_remaining = Math.floor(timer_actual / 60);
|
|
sec_remaining = timer_actual % 60;
|
|
editable = 0;
|
|
action2 = 'pause';
|
|
label2 = findLocale('timer', 'off');
|
|
}
|
|
}
|
|
|
|
let tempId = placeId != undefined ? placeId : id;
|
|
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpdateDetail' +
|
|
'~' + //entityUpdateDetail
|
|
tempId +
|
|
'~~' + //{entity_id}
|
|
rgb_dec565(White) +
|
|
'~' + //{icon_color}~
|
|
tempId +
|
|
'~' +
|
|
min_remaining +
|
|
'~' +
|
|
sec_remaining +
|
|
'~' +
|
|
editable +
|
|
'~' +
|
|
action1 +
|
|
'~' +
|
|
action2 +
|
|
'~' +
|
|
action3 +
|
|
'~' +
|
|
label1 +
|
|
'~' +
|
|
label2 +
|
|
'~' +
|
|
label3,
|
|
});
|
|
}
|
|
}
|
|
|
|
if (type == 'popupFan') {
|
|
let switchVal = '0';
|
|
if (role == 'level.mode.fan') {
|
|
if (existsState(id + '.SET')) {
|
|
val = getState(id + '.SET').val;
|
|
RegisterDetailEntityWatcher(id + '.SET', pageItem, type, placeId);
|
|
}
|
|
if (existsState(id + '.MODE')) {
|
|
RegisterDetailEntityWatcher(id + '.MODE', pageItem, type, placeId);
|
|
}
|
|
|
|
icon = pageItem.icon !== undefined ? Icons.GetIcon(pageItem.icon) : 'fan';
|
|
|
|
if (val) {
|
|
switchVal = '1';
|
|
iconColor = GetIconColor(pageItem, true, true);
|
|
} else {
|
|
iconColor = GetIconColor(pageItem, false, true);
|
|
}
|
|
|
|
let actualSpeed = getState(id + '.SPEED').val;
|
|
let maxSpeed = pageItem.maxValue != undefined ? pageItem.maxValue : 100;
|
|
|
|
let modeList = pageItem.modeList!.join('?');
|
|
let actualMode = pageItem.modeList![getState(id + '.MODE').val];
|
|
|
|
let tempId = placeId != undefined ? placeId : id;
|
|
// {tempid | icon | iconColor | switchVal | actualSpeed | maxSpeed: | findLocale | actualMode | modeList}
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpdateDetail' +
|
|
'~' + // entityUpdateDetail
|
|
tempId +
|
|
'~' +
|
|
icon +
|
|
'~' + // iconId
|
|
iconColor +
|
|
'~' + // iconColor
|
|
switchVal +
|
|
'~' + // buttonState
|
|
actualSpeed +
|
|
'~' +
|
|
maxSpeed +
|
|
'~' +
|
|
findLocale('fan', 'speed') +
|
|
'~' +
|
|
actualMode +
|
|
'~' +
|
|
modeList,
|
|
});
|
|
}
|
|
}
|
|
|
|
if (type == 'popupInSel') {
|
|
if (role == 'media') {
|
|
let actualState: any = '';
|
|
let optionalString: string = 'Kein Eintrag';
|
|
let mode: NSPanel.mediaOptional | '' = '';
|
|
if (isPageMediaItem(pageItem)) {
|
|
const vTempAdapter = pageItem.adapterPlayerInstance!.split('.');
|
|
const vAdapter: NSPanel.PlayerType = vTempAdapter[0] as NSPanel.PlayerType;
|
|
if (optional == 'seek') {
|
|
if (vAdapter == 'sonos') {
|
|
const actualStateTemp: number = getState(pageItem.adapterPlayerInstance + 'root.' + pageItem.mediaDevice + '.seek').val;
|
|
actualState = Math.round(actualStateTemp / 10) * 10 + '%';
|
|
optionalString = '0%?10%?20%?30%?40%?50%?60%?70%?80%?90%?100%';
|
|
}
|
|
if (vAdapter == 'mpd') {
|
|
const actualStateTemp: number = getState(pageItem.adapterPlayerInstance + 'seek').val;
|
|
actualState = Math.round(actualStateTemp / 10) * 10 + '%';
|
|
optionalString = '0%?10%?20%?30%?40%?50%?60%?70%?80%?90%?100%';
|
|
}
|
|
if (vAdapter == 'spotify-premium') {
|
|
const actualStateTemp: number = getState(pageItem.adapterPlayerInstance + 'player.progressPercentage').val;
|
|
actualState = Math.round(actualStateTemp / 10) * 10 + '%';
|
|
optionalString = '0%?10%?20%?30%?40%?50%?60%?70%?80%?90%?100%';
|
|
}
|
|
if (vAdapter == 'squeezeboxrpc') {
|
|
const actualStateTime: number = parseInt(getState(pageItem.adapterPlayerInstance + 'Players.' + pageItem.mediaDevice + '.Time').val);
|
|
const actualStateDuration: number = parseInt(getState(pageItem.adapterPlayerInstance + 'Players.' + pageItem.mediaDevice + '.Duration').val);
|
|
const actualStateTemp: number = (actualStateTime * 100) / actualStateDuration;
|
|
actualState = Math.round(actualStateTemp / 10) * 10 + '%';
|
|
optionalString = '0%?10%?20%?30%?40%?50%?60%?70%?80%?90%?100%';
|
|
}
|
|
mode = 'seek';
|
|
} else if (optional == 'crossfade') {
|
|
//Sonos is using bool Auto-Crossfading 2 Songs
|
|
if (vAdapter == 'sonos') {
|
|
if (existsObject(pageItem.adapterPlayerInstance + 'root.' + pageItem.mediaDevice + '.crossfade')) {
|
|
let actualStateTemp: boolean = getState(pageItem.adapterPlayerInstance + 'root.' + pageItem.mediaDevice + '.crossfade').val;
|
|
if (actualStateTemp) {
|
|
actualState = findLocale('media', 'on');
|
|
} else {
|
|
actualState = findLocale('media', 'off');
|
|
}
|
|
}
|
|
optionalString = findLocale('media', 'on') + '?' + findLocale('media', 'off');
|
|
}
|
|
if (vAdapter == 'mpd') {
|
|
//MPD is using numeric X seconds for crossfading 2 Songs
|
|
if (existsObject(pageItem.adapterPlayerInstance + 'crossfade')) {
|
|
actualState = getState(pageItem.adapterPlayerInstance + 'crossfade').val + ' Sec';
|
|
}
|
|
optionalString = '0 Sec?1 Sec?2 Sec?3 Sec?4 Sec?5 Sec?6 Sec?7 Sec?8 Sec?9 Sec?10 Sec';
|
|
}
|
|
mode = 'crossfade';
|
|
} else if (optional == 'speakerlist') {
|
|
if (vAdapter == 'spotify-premium') {
|
|
if (existsObject(pageItem.adapterPlayerInstance + 'player.device.name')) {
|
|
actualState = formatInSelText(getState(pageItem.adapterPlayerInstance + 'player.device.name').val);
|
|
}
|
|
} else if (vAdapter == 'alexa2') {
|
|
if (existsObject(pageItem.adapterPlayerInstance + 'player.device.name')) {
|
|
//Todo Richtiges Device finden
|
|
actualState = formatInSelText(getState(pageItem.adapterPlayerInstance + 'Echo-Devices.' + pageItem.mediaDevice + '.Info.name').val);
|
|
}
|
|
} else if (vAdapter == 'bosesoundtouch') {
|
|
if (existsObject(pageItem.adapterPlayerInstance + 'deviceInfo.name')) {
|
|
actualState = formatInSelText(getState(pageItem.adapterPlayerInstance + 'deviceInfo.name').val);
|
|
}
|
|
} else if (vAdapter == 'squeezeboxrpc') {
|
|
actualState = pageItem.mediaDevice;
|
|
}
|
|
let tempSpeakerList: string[] = [];
|
|
for (let i = 0; i < pageItem.speakerList!.length; i++) {
|
|
tempSpeakerList[i] = formatInSelText(pageItem.speakerList![i]).trim();
|
|
}
|
|
optionalString = pageItem.speakerList != undefined ? tempSpeakerList.join('?') : '';
|
|
mode = 'speakerlist';
|
|
} else if (optional == 'playlist') {
|
|
if (vAdapter == 'spotify-premium') {
|
|
if (existsObject(pageItem.adapterPlayerInstance + 'player.playlist.name')) {
|
|
actualState = formatInSelText(getState(pageItem.adapterPlayerInstance + 'player.playlist.name').val);
|
|
}
|
|
let tempPlayList: string[] = [];
|
|
for (let i = 0; i < pageItem.playList!.length; i++) {
|
|
tempPlayList[i] = formatInSelText(pageItem.playList![i]);
|
|
}
|
|
optionalString = pageItem.playList != undefined ? tempPlayList.join('?') : '';
|
|
} else if (vAdapter == 'mpd') {
|
|
if (existsObject(pageItem.adapterPlayerInstance + 'listplaylist')) {
|
|
actualState = formatInSelText(getState(pageItem.adapterPlayerInstance + 'listplaylist').val);
|
|
}
|
|
let tempPlayList: string[] = [];
|
|
for (let i = 0; i < pageItem.playList!.length; i++) {
|
|
tempPlayList[i] = formatInSelText(pageItem.playList![i]);
|
|
}
|
|
optionalString = pageItem.playList != undefined ? tempPlayList.join('?') : '';
|
|
} else if (vAdapter == 'alexa2') {
|
|
//Todo Richtiges Device finden
|
|
actualState = formatInSelText(getState(pageItem.adapterPlayerInstance + 'Echo-Devices.' + pageItem.mediaDevice + '.Player.currentAlbum').val);
|
|
|
|
let tPlayList: any = [];
|
|
for (let i = 0; i < pageItem.playList!.length; i++) {
|
|
if (Debug) log('function GenerateDetailPage role:media -> Playlist ' + pageItem.playList![i], 'info');
|
|
let tempItem = pageItem.playList![i].split('.');
|
|
tPlayList[i] = tempItem[1];
|
|
}
|
|
|
|
let tempPlayList: string[] = [];
|
|
for (let i = 0; i < tPlayList.length; i++) {
|
|
tempPlayList[i] = formatInSelText(tPlayList[i]);
|
|
}
|
|
optionalString = pageItem.playList != undefined ? tempPlayList.join('?') : '';
|
|
} else if (vAdapter == 'bosesoundtouch') {
|
|
if (existsObject(pageItem.adapterPlayerInstance + 'deviceInfo.name')) {
|
|
actualState = formatInSelText(getState(pageItem.adapterPlayerInstance + 'deviceInfo.name').val);
|
|
}
|
|
let tempPlayList: string[] = [];
|
|
let vPreset: string = 'No Entry';
|
|
for (let i = 1; i < 7; i++) {
|
|
if (getState(pageItem.adapterPlayerInstance + 'presets.' + i + '.source').val !== null) {
|
|
vPreset = getState(pageItem.adapterPlayerInstance + 'presets.' + i + '.source').val;
|
|
} else {
|
|
vPreset = 'Preset ' + i.toFixed;
|
|
}
|
|
tempPlayList[i - 1] = formatInSelText(vPreset.replace('_', ' '));
|
|
if (Debug) log(formatInSelText(vPreset.replace('_', ' ')));
|
|
}
|
|
tempPlayList[6] = 'AUX INPUT';
|
|
optionalString = pageItem.playList != undefined ? tempPlayList.join('?') : '';
|
|
} else if (vAdapter == 'sonos') {
|
|
if (Debug) log(pageItem.adapterPlayerInstance + 'root.' + pageItem.mediaDevice + '.playlist_set', 'info');
|
|
if (existsObject(pageItem.adapterPlayerInstance + 'root.' + pageItem.mediaDevice + '.playlist_set')) {
|
|
actualState = formatInSelText(getState(pageItem.adapterPlayerInstance + 'root.' + pageItem.mediaDevice + '.playlist_set').val);
|
|
}
|
|
let tempPlayList: string[] = [];
|
|
for (let i = 0; i < pageItem.playList!.length; i++) {
|
|
tempPlayList[i] = formatInSelText(pageItem.playList![i]);
|
|
}
|
|
optionalString = pageItem.playList != undefined ? tempPlayList.join('?') : '';
|
|
} else if (vAdapter == 'volumio') {
|
|
/* Volumio: limit 900 chars */
|
|
actualState = ''; //todo: no actual playlistname saving
|
|
let tempPlayList: string[] = [];
|
|
let tempPll = 0;
|
|
for (let i = 0; i < pageItem.playList!.length; i++) {
|
|
tempPll += pageItem.playList![i].length;
|
|
if (tempPll > 900) break;
|
|
tempPlayList[i] = formatInSelText(pageItem.playList![i]);
|
|
}
|
|
optionalString = pageItem.playList != undefined ? tempPlayList.join('?') : '';
|
|
} else if (vAdapter == 'squeezeboxrpc') {
|
|
// Playlist browsing not supported by squeezeboxrpc adapter. But Favorites can be used
|
|
actualState = ''; // Not supported by squeezeboxrpc adapter
|
|
let tempPlayList: string[] = [];
|
|
let pathParts: string[] = pageItem.adapterPlayerInstance!.split('.');
|
|
for (let favorite_index = 0; favorite_index < 45; favorite_index++) {
|
|
let favorite_name_selector: string = [pathParts[0], pathParts[1], 'Favorites', favorite_index, 'Name'].join('.');
|
|
if (!existsObject(favorite_name_selector)) {
|
|
break;
|
|
}
|
|
let favoritename: string = getState(favorite_name_selector).val;
|
|
tempPlayList.push(formatInSelText(favoritename));
|
|
}
|
|
optionalString = tempPlayList.length > 0 ? tempPlayList.join('?') : '';
|
|
}
|
|
mode = 'playlist';
|
|
} else if (optional == 'tracklist') {
|
|
actualState = '';
|
|
/* Volumio: works for files */
|
|
if (vAdapter == 'volumio') {
|
|
actualState = getState(pageItem.id + '.TITLE').val;
|
|
globalTracklist = pageItem.globalTracklist;
|
|
} else if (vAdapter == 'squeezeboxrpc') {
|
|
actualState = getState(pageItem.id + '.TITLE').val;
|
|
} else if (vAdapter == 'mpd') {
|
|
actualState = getState(pageItem.id + '.TITLE').val;
|
|
} else if (vAdapter == 'sonos') {
|
|
actualState = getState(pageItem.id + '.TITLE').val;
|
|
} else {
|
|
actualState = getState(pageItem.adapterPlayerInstance + 'player.trackName').val;
|
|
}
|
|
actualState = actualState.replace('?', '').split(' -');
|
|
actualState = actualState[0].split(' (');
|
|
actualState = formatInSelText(actualState[0]);
|
|
if (Debug) log(actualState, 'info');
|
|
if (Debug) log(globalTracklist, 'info');
|
|
//Limit 900 characters, then memory overflow --> Shorten as much as possible
|
|
let temp_array: any[] = [];
|
|
let temp_cut_array: any;
|
|
//let trackArray = (function () { try {return JSON.parse(getState(pageItem.adapterPlayerInstance + 'player.playlist.trackListArray').val);} catch(e) {return {};}})();
|
|
for (let track_index = 0; track_index < 48; track_index++) {
|
|
if (vAdapter == 'mpd') {
|
|
temp_cut_array = getAttr(globalTracklist, track_index + '.Title');
|
|
} else {
|
|
temp_cut_array = getAttr(globalTracklist, track_index + '.title');
|
|
}
|
|
/* Volumio: @local/NAS no title -> name */
|
|
if (temp_cut_array == undefined) {
|
|
temp_cut_array = getAttr(globalTracklist, track_index + '.name');
|
|
}
|
|
if (Debug) log('function GenerateDetailPage role:media tracklist -> ' + temp_cut_array, 'info');
|
|
if (temp_cut_array != undefined) {
|
|
temp_cut_array = temp_cut_array.replace('?', '').split(' -');
|
|
temp_cut_array = temp_cut_array[0].split(' (');
|
|
temp_cut_array = temp_cut_array[0];
|
|
if (String(temp_cut_array[0]).length >= 22) {
|
|
temp_array[track_index] = temp_cut_array.substring(0, 20) + '..';
|
|
} else {
|
|
temp_array[track_index] = temp_cut_array.substring(0, 23);
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
let tempTrackList: string[] = [];
|
|
for (let i = 0; i < temp_array.length; i++) {
|
|
tempTrackList[i] = formatInSelText(temp_array[i]);
|
|
}
|
|
optionalString = pageItem.playList != undefined ? tempTrackList.join('?') : '';
|
|
mode = 'tracklist';
|
|
} else if (optional == 'equalizer') {
|
|
if (pageItem.id == undefined) throw new Error('Missing pageItem.id in equalizer!');
|
|
let lastIndex = pageItem.id.split('.').pop();
|
|
|
|
if (
|
|
existsObject(NSPanel_Path + 'Media.Player.' + lastIndex + '.EQ.activeMode') == false ||
|
|
existsObject(NSPanel_Path + 'Media.Player.' + lastIndex + '.Speaker') == false
|
|
) {
|
|
createState(NSPanel_Path + 'Media.Player.' + lastIndex + '.EQ.activeMode', {type: 'string', write: false});
|
|
createState(NSPanel_Path + 'Media.Player.' + lastIndex + '.Speaker', {type: 'string', write: false});
|
|
}
|
|
|
|
actualState = '';
|
|
if (getState(NSPanel_Path + 'Media.Player.' + lastIndex + '.EQ.activeMode').val != null) {
|
|
actualState = formatInSelText(getState(NSPanel_Path + 'Media.Player.' + lastIndex + '.EQ.activeMode').val);
|
|
}
|
|
|
|
let tempEQList: string[] = [];
|
|
for (let i = 0; i < pageItem.equalizerList!.length; i++) {
|
|
tempEQList[i] = formatInSelText(pageItem!.equalizerList![i]);
|
|
}
|
|
|
|
optionalString = pageItem.equalizerList != undefined ? tempEQList.join('?') : '';
|
|
mode = 'equalizer';
|
|
} else if (optional == 'repeat') {
|
|
actualState = getState(pageItem.adapterPlayerInstance + 'player.repeat').val;
|
|
optionalString = pageItem.repeatList!.join('?');
|
|
mode = 'repeat';
|
|
} else if (optional == 'favorites') {
|
|
if (Debug) log(getState(pageItem.adapterPlayerInstance + 'root.' + pageItem.mediaDevice + '.favorites_set').val, 'info');
|
|
actualState = formatInSelText(getState(pageItem.adapterPlayerInstance + 'root.' + pageItem.mediaDevice + '.favorites_set').val);
|
|
|
|
let tempFavList: string[] = [];
|
|
let favList = getState(pageItem.adapterPlayerInstance + 'root.' + pageItem.mediaDevice + '.favorites_list_array').val;
|
|
for (let i = 0; i < favList.length; i++) {
|
|
tempFavList[i] = formatInSelText(favList[i]);
|
|
}
|
|
optionalString = tempFavList != undefined ? tempFavList.join('?') : '';
|
|
mode = 'favorites';
|
|
}
|
|
|
|
let tempId = placeId != undefined ? placeId : id;
|
|
// {tempid | color | NSPanel.mediaOptional | actualState | optionalString}
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpdateDetail2' +
|
|
'~' + //entityUpdateDetail
|
|
tempId +
|
|
'?' +
|
|
optional +
|
|
'~~' + //{entity_id}
|
|
rgb_dec565(HMIOn) +
|
|
'~' + //{icon_color}~
|
|
mode +
|
|
'~' +
|
|
actualState +
|
|
'~' +
|
|
optionalString,
|
|
|
|
|
|
});
|
|
|
|
GeneratePage(activePage!);
|
|
}
|
|
} else if (role == 'buttonSensor') {
|
|
let actualValue: string = '';
|
|
|
|
if (pageItem.inSel_ChoiceState || pageItem.inSel_ChoiceState == undefined) {
|
|
if (existsObject(pageItem.id + '.VALUE')) {
|
|
actualValue = formatInSelText(pageItem.modeList![getState(pageItem.id + '.VALUE').val]);
|
|
RegisterDetailEntityWatcher(id + '.VALUE', pageItem, type, placeId);
|
|
}
|
|
}
|
|
|
|
let tempModeList: string[] = [];
|
|
for (let i = 0; i < pageItem.modeList!.length; i++) {
|
|
tempModeList[i] = formatInSelText(pageItem.modeList![i]);
|
|
}
|
|
let valueList = pageItem.modeList != undefined ? tempModeList.join('?') : '';
|
|
|
|
let tempId = placeId != undefined ? placeId : id;
|
|
// {tempid | color | NSPanel.mediaOptional | actualValue | valueList}
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpdateDetail2' +
|
|
'~' + //entityUpdateDetail2
|
|
tempId +
|
|
'~~' + //{entity_id}
|
|
rgb_dec565(White) +
|
|
'~' + //{icon_color}~
|
|
'insel' +
|
|
'~' +
|
|
actualValue +
|
|
'~' +
|
|
valueList,
|
|
});
|
|
} else if (role == 'light' || role == 'dimmer' || role == 'hue' || role == 'rgb' || role == 'rgbSingle' || role == 'ct') {
|
|
//log(pageItem.id, 'info');
|
|
if (pageItem.modeList != undefined) {
|
|
let actualValue: string = '';
|
|
|
|
if (pageItem.inSel_ChoiceState || pageItem.inSel_ChoiceState == undefined) {
|
|
if (existsObject(pageItem.id + '.VALUE')) {
|
|
actualValue = formatInSelText(pageItem.modeList[getState(pageItem.id + '.VALUE').val]);
|
|
RegisterDetailEntityWatcher(id + '.VALUE', pageItem, type, placeId);
|
|
}
|
|
}
|
|
|
|
let tempModeList: string[] = [];
|
|
for (let i = 0; i < pageItem.modeList.length; i++) {
|
|
tempModeList[i] = formatInSelText(pageItem.modeList[i]);
|
|
}
|
|
let valueList = pageItem.modeList != undefined ? tempModeList.join('?') : '';
|
|
|
|
//log(valueList);
|
|
let tempId = placeId != undefined ? placeId : id;
|
|
// {tempid | color | 'insel' | actualValue | valueList}
|
|
out_msgs.push({
|
|
payload:
|
|
'entityUpdateDetail2' +
|
|
'~' + //entityUpdateDetail2
|
|
tempId +
|
|
'~~' + //{entity_id}
|
|
rgb_dec565(White) +
|
|
'~' + //{icon_color}~
|
|
'insel' +
|
|
'~' +
|
|
actualValue +
|
|
'~' +
|
|
valueList,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (Debug) log('GenerateDetailPage -> payload: ' + JSON.stringify(out_msgs), 'info');
|
|
return out_msgs;
|
|
} catch (err: any) {
|
|
log('error at function GenerateDetailPage: ' + err.message, 'warn');
|
|
}
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* Scales a number from one range to another.
|
|
*
|
|
* This function takes a number and scales it from the input range [inMin, inMax] to the output range [outMin, outMax].
|
|
*
|
|
* @function scale
|
|
* @param {number} number - The number to scale.
|
|
* @param {number} inMin - The minimum value of the input range.
|
|
* @param {number} inMax - The maximum value of the input range.
|
|
* @param {number} outMin - The minimum value of the output range.
|
|
* @param {number} outMax - The maximum value of the output range.
|
|
* @returns {number} The scaled number.
|
|
*/
|
|
function scale (number: number, inMin: number, inMax: number, outMin: number, outMax: number): number {
|
|
try {
|
|
return outMax + outMin - (((number - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin);
|
|
} catch (err: any) {
|
|
log('error at function scale: ' + err.message, 'warn');
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Unsubscribes from all active watchers.
|
|
*
|
|
* This function removes all active watchers that have been set up to monitor changes in entities.
|
|
*
|
|
* @function UnsubscribeWatcher
|
|
*/
|
|
function UnsubscribeWatcher (): void {
|
|
//log(Object.entries(subscriptions));
|
|
try {
|
|
for (const [key, value] of Object.entries(subscriptions)) {
|
|
//@ts-ignore
|
|
unsubscribe(value);
|
|
delete subscriptions[key];
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function UnsubscribeWatcher: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles the screensaver functionality for the NSPanel.
|
|
*
|
|
* This function manages the screensaver behavior, including activation, updates, and deactivation.
|
|
*
|
|
* @function HandleScreensaver
|
|
*/
|
|
function HandleScreensaver (): void {
|
|
setIfExists(NSPanel_Path + 'ActivePage.type', 'screensaver', null, true);
|
|
setIfExists(NSPanel_Path + 'ActivePage.id0', 'screensaver', null, true);
|
|
setIfExists(NSPanel_Path + 'ActivePage.heading', 'Screensaver', null, true);
|
|
if (existsObject(`${NSPanel_Path}${ScreensaverAdvancedEndPath}`) && getState(`${NSPanel_Path}${ScreensaverAdvancedEndPath}`).val) {
|
|
SendToPanel({payload: 'pageType~screensaver2'});
|
|
} else if (existsObject(`${NSPanel_Path}${ScreensaverEasyViewEndPath}`) && getState(`${NSPanel_Path}${ScreensaverEasyViewEndPath}`).val) {
|
|
SendToPanel({payload: 'pageType~screensaver3'});
|
|
} else {
|
|
SendToPanel({payload: 'pageType~screensaver'});
|
|
}
|
|
weatherForecast = getState(NSPanel_Path + 'ScreensaverInfo.weatherForecast').val;
|
|
HandleScreensaverUpdate();
|
|
HandleScreensaverStatusIcons();
|
|
HandleScreensaverColors();
|
|
}
|
|
|
|
/**
|
|
* Updates the screensaver state and content on the NSPanel.
|
|
*
|
|
* This function handles the updates to the screensaver, including refreshing the displayed content and managing state changes.
|
|
*
|
|
* @function HandleScreensaverUpdate
|
|
*/
|
|
function HandleScreensaverUpdate (): void {
|
|
try {
|
|
if (screensaverEnabled) {
|
|
UnsubscribeWatcher();
|
|
|
|
let payloadString: string = '';
|
|
let temperatureUnit = getState(NSPanel_Path + 'Config.temperatureUnit').val;
|
|
let screensaverAdvanced = getState(`${NSPanel_Path}${ScreensaverAdvancedEndPath}`).val;
|
|
|
|
//Create Weather MainIcon
|
|
if (screensaverEnabled && config.weatherEntity != null && existsObject(config.weatherEntity)) {
|
|
let icon = getState(config.weatherEntity + '.ICON').val;
|
|
RegisterScreensaverEntityWatcher(config.weatherEntity + '.ICON');
|
|
let temperature = '0';
|
|
if (existsState(config.weatherEntity + '.ACTUAL')) {
|
|
temperature = getState(config.weatherEntity + '.ACTUAL').val;
|
|
RegisterScreensaverEntityWatcher(config.weatherEntity + '.ACTUAL');
|
|
} else {
|
|
if (existsState(config.weatherEntity + '.TEMP')) {
|
|
temperature = String(Math.round(getState(config.weatherEntity + '.TEMP').val * 10)/10);
|
|
} else {
|
|
('null');
|
|
}
|
|
}
|
|
let optionalValue = temperature + ' ' + temperatureUnit;
|
|
let entityIcon = '';
|
|
let entityIconCol = 0;
|
|
if (weatherAdapterInstance == 'daswetter.' + weatherAdapterInstanceNumber + '.') {
|
|
entityIcon = Icons.GetIcon(GetDasWetterIcon(parseInt(icon)));
|
|
entityIconCol = GetDasWetterIconColor(parseInt(icon));
|
|
} else if (weatherAdapterInstance == 'accuweather.' + weatherAdapterInstanceNumber + '.') {
|
|
entityIcon = Icons.GetIcon(GetAccuWeatherIcon(parseInt(icon)));
|
|
entityIconCol = GetAccuWeatherIconColor(parseInt(icon));
|
|
} else if (weatherAdapterInstance == 'openweathermap.' + weatherAdapterInstanceNumber + '.') {
|
|
entityIcon = Icons.GetIcon(GetOpenWeatherMapIcon(icon));
|
|
entityIconCol = GetOpenWeatherMapIconColor(icon);
|
|
} else if (weatherAdapterInstance == 'pirate-weather.' + weatherAdapterInstanceNumber + '.' || weatherAdapterInstance == 'brightsky.' + weatherAdapterInstanceNumber + '.') {
|
|
entityIcon = Icons.GetIcon(GetPirateWeatherIcon(icon));
|
|
entityIconCol = GetPirateWeatherIconColor(icon);
|
|
}
|
|
|
|
payloadString += '~' + '~' + entityIcon + '~' + entityIconCol + '~' + '~' + optionalValue + '~';
|
|
}
|
|
|
|
// 3 leftScreensaverEntities
|
|
if (screensaverAdvanced) {
|
|
let checkpoint = true;
|
|
let i = 0;
|
|
if (config.leftScreensaverEntity && Array.isArray(config.leftScreensaverEntity) && config.leftScreensaverEntity.length > 0) {
|
|
for (i = 0; i < 3 && i < config.leftScreensaverEntity.length; i++) {
|
|
const leftScreensaverEntity = config.leftScreensaverEntity[i];
|
|
if (leftScreensaverEntity === null || leftScreensaverEntity === undefined) {
|
|
checkpoint = false;
|
|
break;
|
|
}
|
|
RegisterScreensaverEntityWatcher(leftScreensaverEntity.ScreensaverEntity);
|
|
|
|
let val = getState(leftScreensaverEntity.ScreensaverEntity).val;
|
|
let iconColor = rgb_dec565(White);
|
|
let icon;
|
|
if (typeof leftScreensaverEntity.ScreensaverEntityIconOn == 'string' && existsObject(leftScreensaverEntity.ScreensaverEntityIconOn as string)) {
|
|
let iconName = getState(leftScreensaverEntity.ScreensaverEntityIconOn!).val;
|
|
icon = Icons.GetIcon(iconName);
|
|
} else {
|
|
icon = Icons.GetIcon(leftScreensaverEntity.ScreensaverEntityIconOn);
|
|
}
|
|
|
|
if (parseFloat(val + '') == val) {
|
|
val = parseFloat(val);
|
|
}
|
|
|
|
if (typeof val == 'number') {
|
|
val = val * (leftScreensaverEntity.ScreensaverEntityFactor ? leftScreensaverEntity.ScreensaverEntityFactor! : 0)
|
|
icon = determineScreensaverStatusIcon(leftScreensaverEntity,val,icon)
|
|
val = val.toFixed(
|
|
leftScreensaverEntity.ScreensaverEntityDecimalPlaces
|
|
) + leftScreensaverEntity.ScreensaverEntityUnitText;
|
|
iconColor = GetScreenSaverEntityColor(leftScreensaverEntity);
|
|
} else if (typeof val == 'boolean') {
|
|
iconColor = GetScreenSaverEntityColor(leftScreensaverEntity);
|
|
if (!val && leftScreensaverEntity.ScreensaverEntityIconOff != null) {
|
|
icon = Icons.GetIcon(leftScreensaverEntity.ScreensaverEntityIconOff);
|
|
}
|
|
} else if (typeof val == 'string') {
|
|
iconColor = GetScreenSaverEntityColor(leftScreensaverEntity);
|
|
let pformat = parseFormat(val);
|
|
if (Debug) log('moments.js --> Datum ' + val + ' valid?: ' + moment(val, pformat, true).isValid(), 'info');
|
|
if (moment(val, pformat, true).isValid()) {
|
|
let DatumZeit = moment(val, pformat).unix(); // Umwandlung in Unix Time-Stamp
|
|
if (leftScreensaverEntity.ScreensaverEntityDateFormat !== undefined) {
|
|
val = new Date(DatumZeit * 1000).toLocaleString(getState(NSPanel_Path + 'Config.locale').val, leftScreensaverEntity.ScreensaverEntityDateFormat);
|
|
} else {
|
|
val = new Date(DatumZeit * 1000).toLocaleString(getState(NSPanel_Path + 'Config.locale').val);
|
|
}
|
|
}
|
|
}
|
|
const temp = leftScreensaverEntity.ScreensaverEntityIconColor;
|
|
if (temp && typeof temp == 'string' && existsObject(temp)) {
|
|
iconColor = getState(temp).val;
|
|
}
|
|
|
|
payloadString += '~' + '~' + icon + '~' + iconColor + '~' + leftScreensaverEntity.ScreensaverEntityText + '~' + val + '~';
|
|
}
|
|
}
|
|
|
|
if (i < 3) {
|
|
checkpoint = false;
|
|
}
|
|
|
|
if (checkpoint == false) {
|
|
for (let j = i; j < 3; j++) {
|
|
payloadString += '~~~~~~';
|
|
}
|
|
}
|
|
}
|
|
|
|
// 6 bottomScreensaverEntities
|
|
let maxEntities: number = 7;
|
|
if (screensaverAdvanced == false) {
|
|
maxEntities = 5;
|
|
if (getState(NSPanel_Path + 'Config.Screensaver.alternativeScreensaverLayout').val) {
|
|
maxEntities = 6;
|
|
} else if (getState(NSPanel_Path + ScreensaverEasyViewEndPath).val) {
|
|
//us-p needs 5 Items - us-l and eu only 4
|
|
maxEntities = 5;
|
|
}
|
|
}
|
|
|
|
if (weatherForecast) {
|
|
if (getState(NSPanel_Path + 'Config.Screensaver.alternativeScreensaverLayout').val) {
|
|
maxEntities = 5;
|
|
}
|
|
|
|
for (let i = 1; i < maxEntities; i++) {
|
|
|
|
let TempMin = 0;
|
|
let TempMax = 0;
|
|
let DayOfWeek: any = 0;
|
|
let WeatherIcon = '0';
|
|
let WheatherColor: any = 0;
|
|
|
|
if (weatherAdapterInstance == 'daswetter.' + weatherAdapterInstanceNumber + '.') {
|
|
TempMin = getState('daswetter.' + weatherAdapterInstanceNumber + '.NextDays.Location_1.Day_' + i + '.Minimale_Temperatur_value').val;
|
|
TempMax = getState('daswetter.' + weatherAdapterInstanceNumber + '.NextDays.Location_1.Day_' + i + '.Maximale_Temperatur_value').val;
|
|
DayOfWeek = getState('daswetter.' + weatherAdapterInstanceNumber + '.NextDays.Location_1.Day_' + i + '.Tag_value').val.substring(0, 2);
|
|
WeatherIcon = GetDasWetterIcon(getState('daswetter.' + weatherAdapterInstanceNumber + '.NextDays.Location_1.Day_' + i + '.Wetter_Symbol_id').val);
|
|
WheatherColor = GetDasWetterIconColor(getState('daswetter.' + weatherAdapterInstanceNumber + '.NextDays.Location_1.Day_' + i + '.Wetter_Symbol_id').val);
|
|
|
|
RegisterScreensaverEntityWatcher('daswetter.' + weatherAdapterInstanceNumber + '.NextDays.Location_1.Day_' + i + '.Minimale_Temperatur_value');
|
|
RegisterScreensaverEntityWatcher('daswetter.' + weatherAdapterInstanceNumber + '.NextDays.Location_1.Day_' + i + '.Maximale_Temperatur_value');
|
|
RegisterScreensaverEntityWatcher('daswetter.' + weatherAdapterInstanceNumber + '.NextDays.Location_1.Day_' + i + '.Tag_value');
|
|
RegisterScreensaverEntityWatcher('daswetter.' + weatherAdapterInstanceNumber + '.NextDays.Location_1.Day_' + i + '.Wetter_Symbol_id');
|
|
} else if (weatherAdapterInstance == 'accuweather.' + weatherAdapterInstanceNumber + '.') {
|
|
if (i < 6) {
|
|
//Maximal 5 Tage bei accuweather
|
|
TempMin = existsObject('accuweather.' + weatherAdapterInstanceNumber + '.Summary.TempMin_d' + i)
|
|
? getState('accuweather.' + weatherAdapterInstanceNumber + '.Summary.TempMin_d' + i).val
|
|
: 0;
|
|
TempMax = existsObject('accuweather.' + weatherAdapterInstanceNumber + '.Summary.TempMax_d' + i)
|
|
? getState('accuweather.' + weatherAdapterInstanceNumber + '.Summary.TempMax_d' + i).val
|
|
: 0;
|
|
DayOfWeek = existsObject('accuweather.' + weatherAdapterInstanceNumber + '.Summary.DayOfWeek_d' + i)
|
|
? getState('accuweather.' + weatherAdapterInstanceNumber + '.Summary.DayOfWeek_d' + i).val
|
|
: 0;
|
|
WeatherIcon = existsObject('accuweather.' + weatherAdapterInstanceNumber + '.Summary.WeatherIcon_d' + i)
|
|
? GetAccuWeatherIcon(getState('accuweather.' + weatherAdapterInstanceNumber + '.Summary.WeatherIcon_d' + i).val)
|
|
: '';
|
|
WheatherColor = existsObject('accuweather.' + weatherAdapterInstanceNumber + '.Summary.WeatherIcon_d' + i)
|
|
? GetAccuWeatherIconColor(getState('accuweather.' + weatherAdapterInstanceNumber + '.Summary.WeatherIcon_d' + i).val)
|
|
: 0;
|
|
|
|
RegisterScreensaverEntityWatcher('accuweather.' + weatherAdapterInstanceNumber + '.Summary.TempMin_d' + i);
|
|
RegisterScreensaverEntityWatcher('accuweather.' + weatherAdapterInstanceNumber + '.Summary.TempMax_d' + i);
|
|
RegisterScreensaverEntityWatcher('accuweather.' + weatherAdapterInstanceNumber + '.Summary.DayOfWeek_d' + i);
|
|
RegisterScreensaverEntityWatcher('accuweather.' + weatherAdapterInstanceNumber + '.Summary.WeatherIcon_d' + i);
|
|
}
|
|
} else if (weatherAdapterInstance == 'openweathermap.' + weatherAdapterInstanceNumber + '.') {
|
|
if (i < 6) {
|
|
|
|
//Maximal 6 Tage bei openweathermap
|
|
TempMin = existsObject('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.day' + String(i-1) + '.temperatureMin')
|
|
? Math.round(getState('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.day' + String(i-1) + '.temperatureMin').val * 10) / 10
|
|
: 0;
|
|
TempMax = existsObject('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.day' + String(i-1) + '.temperatureMax')
|
|
? Math.round(getState('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.day' + String(i-1) + '.temperatureMax').val * 10) / 10
|
|
: 0;
|
|
//openweathermap.0.forecast.day0.date
|
|
//log(formatDate(getDateObject(getState('openweathermap.0.forecast.day0.date').val), 'W', 'de'),'info');
|
|
DayOfWeek = existsObject('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.day' + String(i-1) + '.date')
|
|
? formatDate(getDateObject(getState('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.day' + String(i-1) + '.date').val), 'W', 'de')
|
|
: 0;
|
|
WeatherIcon = existsObject('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.day' + String(i-1) + '.icon')
|
|
? GetOpenWeatherMapIcon((getState('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.day' + String(i-1) + '.icon').val).split('/').pop().split('.').shift())
|
|
: '';
|
|
WheatherColor = existsObject('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.day' + String(i-1) + '.icon')
|
|
? GetOpenWeatherMapIconColor(String(getState('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.day' + String(i-1) + '.icon').val).split('/').pop().split('.').shift())
|
|
: 0;
|
|
|
|
RegisterScreensaverEntityWatcher('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.day' + String(i-1) + '.temperatureMin');
|
|
RegisterScreensaverEntityWatcher('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.day' + String(i-1) + '.temperatureMax');
|
|
RegisterScreensaverEntityWatcher('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.day' + String(i-1) + '.date');
|
|
RegisterScreensaverEntityWatcher('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.day' + String(i-1) + '.icon');
|
|
}
|
|
} else if (weatherAdapterInstance == 'pirate-weather.' + weatherAdapterInstanceNumber + '.') {
|
|
if (i < 6) {
|
|
//Maximal 8 Tage bei openweathermap - pirate-weather.0.weather.daily.00.icon
|
|
TempMin = existsObject('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.0' + String(i-1) + '.temperatureMin')
|
|
? Math.round(getState('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.0' + String(i-1) + '.temperatureMin').val * 10) / 10
|
|
: 0;
|
|
TempMax = existsObject('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.0' + String(i-1) + '.temperatureMax')
|
|
? Math.round(getState('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.0' + String(i-1) + '.temperatureMax').val * 10) / 10
|
|
: 0;
|
|
DayOfWeek = existsObject('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.0' + String(i-1) + '.time')
|
|
? formatDate(getDateObject((getState('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.0' + String(i-1) + '.time').val)), 'W', 'de')
|
|
: 0;
|
|
WeatherIcon = existsObject('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.0' + String(i-1) + '.icon')
|
|
? GetPirateWeatherIcon(getState('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.0' + String(i-1) + '.icon').val)
|
|
: '';
|
|
WheatherColor = existsObject('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.0' + String(i-1) + '.icon')
|
|
? GetPirateWeatherIconColor(String(getState('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.0' + String(i-1) + '.icon').val))
|
|
: 0;
|
|
|
|
RegisterScreensaverEntityWatcher('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.0' + String(i-1) + '.temperatureMin');
|
|
RegisterScreensaverEntityWatcher('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.0' + String(i-1) + '.temperatureMax');
|
|
RegisterScreensaverEntityWatcher('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.0' + String(i-1) + '.time');
|
|
RegisterScreensaverEntityWatcher('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.0' + String(i-1) + '.icon');
|
|
}
|
|
} else if (weatherAdapterInstance == 'brightsky.' + weatherAdapterInstanceNumber + '.') {
|
|
if (i < 6) {
|
|
//Maximal 8 Tage bei openweathermap - pirate-weather.0.weather.daily.00.icon
|
|
TempMin = existsObject('brightsky.' + weatherAdapterInstanceNumber + '.daily.0' + String(i-1) + '.temperature_min')
|
|
? Math.round(getState('brightsky.' + weatherAdapterInstanceNumber + '.daily.0' + String(i-1) + '.temperature_min').val * 10) / 10
|
|
: 0;
|
|
TempMax = existsObject('brightsky.' + weatherAdapterInstanceNumber + '.daily.0' + String(i-1) + '.temperature_max')
|
|
? Math.round(getState('brightsky.' + weatherAdapterInstanceNumber + '.daily.0' + String(i-1) + '.temperature_max').val * 10) / 10
|
|
: 0;
|
|
DayOfWeek = existsObject('brightsky.' + weatherAdapterInstanceNumber + '.daily.0' + String(i-1) + '.timestamp')
|
|
? formatDate(getDateObject((getState('brightsky.' + weatherAdapterInstanceNumber + '.daily.0' + String(i-1) + '.timestamp').val)), 'W', 'de')
|
|
: 0;
|
|
WeatherIcon = existsObject('brightsky.' + weatherAdapterInstanceNumber + '.daily.0' + String(i-1) + '.icon')
|
|
? GetPirateWeatherIcon(getState('brightsky.' + weatherAdapterInstanceNumber + '.daily.0' + String(i-1) + '.icon').val)
|
|
: '';
|
|
WheatherColor = existsObject('brightsky.' + weatherAdapterInstanceNumber + '.daily.0' + String(i-1) + '.icon')
|
|
? GetPirateWeatherIconColor(String(getState('brightsky.' + weatherAdapterInstanceNumber + '.daily.0' + String(i-1) + '.icon').val))
|
|
: 0;
|
|
|
|
RegisterScreensaverEntityWatcher('brightsky.' + weatherAdapterInstanceNumber + '.daily.0' + String(i-1) + '.temperature_min');
|
|
RegisterScreensaverEntityWatcher('brightsky.' + weatherAdapterInstanceNumber + '.daily.0' + String(i-1) + '.temperature_max');
|
|
RegisterScreensaverEntityWatcher('brightsky.' + weatherAdapterInstanceNumber + '.daily.0' + String(i-1) + '.timestamp');
|
|
RegisterScreensaverEntityWatcher('brightsky.' + weatherAdapterInstanceNumber + '.daily.0' + String(i-1) + '.icon');
|
|
}
|
|
}
|
|
|
|
let tempMinMaxString: string = '';
|
|
if (weatherScreensaverTempMinMax == 'Min') {
|
|
tempMinMaxString = TempMin + temperatureUnit;
|
|
} else if (weatherScreensaverTempMinMax == 'Max') {
|
|
tempMinMaxString = TempMax + temperatureUnit;
|
|
} else if (weatherScreensaverTempMinMax == 'MinMax') {
|
|
tempMinMaxString = Math.round(TempMin) + '° ' + Math.round(TempMax) + '°';
|
|
}
|
|
|
|
if (weatherAdapterInstance == 'accuweather.' + weatherAdapterInstanceNumber + '.' && i == 6) {
|
|
let nextSunEvent = 0;
|
|
let valDateNow = new Date().getTime();
|
|
let arraySunEvent: number[] = [];
|
|
|
|
arraySunEvent[0] = getDateObject(getState('accuweather.' + weatherAdapterInstanceNumber + '.Daily.Day1.Sunrise').val).getTime();
|
|
arraySunEvent[1] = getDateObject(getState('accuweather.' + weatherAdapterInstanceNumber + '.Daily.Day1.Sunset').val).getTime();
|
|
arraySunEvent[2] = getDateObject(getState('accuweather.' + weatherAdapterInstanceNumber + '.Daily.Day2.Sunrise').val).getTime();
|
|
|
|
let j = 0;
|
|
for (j = 0; j < 3; j++) {
|
|
if (arraySunEvent[j] > valDateNow) {
|
|
nextSunEvent = j;
|
|
break;
|
|
}
|
|
}
|
|
let sun = '';
|
|
if (j == 1) {
|
|
sun = 'weather-sunset-down';
|
|
} else {
|
|
sun = 'weather-sunset-up';
|
|
}
|
|
|
|
payloadString += '~' + '~' + Icons.GetIcon(sun) + '~' + rgb_dec565(MSYellow) + '~' + 'Sonne' + '~' + formatDate(getDateObject(arraySunEvent[nextSunEvent]), 'hh:mm') + '~';
|
|
} else if (weatherAdapterInstance == 'openweathermap.' + weatherAdapterInstanceNumber + '.' && i == 6) {
|
|
let nextSunEvent = 0;
|
|
let valDateNow = new Date().getTime();
|
|
let arraySunEvent: number[] = [];
|
|
//openweathermap.0.forecast.current.sunrise
|
|
//openweathermap.0.forecast.current.sunset
|
|
//no Sunset for Next day
|
|
arraySunEvent[0] = getDateObject(getState('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.current.sunrise').val).getTime();
|
|
arraySunEvent[1] = getDateObject(getState('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.current.sunset').val).getTime();
|
|
arraySunEvent[2] = getDateObject(getState('openweathermap.' + weatherAdapterInstanceNumber + '.forecast.current.sunrise').val).getTime();
|
|
|
|
let j = 0;
|
|
for (j = 0; j < 3; j++) {
|
|
if (arraySunEvent[j] > valDateNow) {
|
|
nextSunEvent = j;
|
|
break;
|
|
}
|
|
}
|
|
let sun = '';
|
|
if (j == 1) {
|
|
sun = 'weather-sunset-down';
|
|
} else {
|
|
sun = 'weather-sunset-up';
|
|
}
|
|
|
|
payloadString += '~' + '~' + Icons.GetIcon(sun) + '~' + rgb_dec565(MSYellow) + '~' + 'Sonne' + '~' + formatDate(getDateObject(arraySunEvent[nextSunEvent]), 'hh:mm') + '~';
|
|
} else if (weatherAdapterInstance == 'pirate-weather.' + weatherAdapterInstanceNumber + '.' && i == 6) {
|
|
let nextSunEvent = 0;
|
|
let valDateNow = getDateObject((new Date().getTime())).getTime();
|
|
let arraySunEvent: number[] = [];
|
|
|
|
arraySunEvent[0] = getDateObject(getState('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.00.sunriseTime').val).getTime();
|
|
arraySunEvent[1] = getDateObject(getState('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.00.sunsetTime').val).getTime();
|
|
arraySunEvent[2] = getDateObject(getState('pirate-weather.' + weatherAdapterInstanceNumber + '.weather.daily.01.sunriseTime').val).getTime();
|
|
|
|
let j = 0;
|
|
for (j = 0; j < 3; j++) {
|
|
if (arraySunEvent[j] > valDateNow) {
|
|
nextSunEvent = j;
|
|
break;
|
|
}
|
|
}
|
|
let sun = '';
|
|
if (j == 1) {
|
|
sun = 'weather-sunset-down';
|
|
} else {
|
|
sun = 'weather-sunset-up';
|
|
}
|
|
|
|
payloadString += '~' + '~' + Icons.GetIcon(sun) + '~' + rgb_dec565(MSYellow) + '~' + 'Sonne' + '~' + formatDate(getDateObject(arraySunEvent[nextSunEvent]), 'hh:mm') + '~';
|
|
} else if (weatherAdapterInstance == 'brightsky.' + weatherAdapterInstanceNumber + '.' && i == 6) {
|
|
let nextSunEvent = 0;
|
|
let valDateNow = getDateObject((new Date().getTime())).getTime();
|
|
let arraySunEvent: number[] = [];
|
|
|
|
arraySunEvent[0] = getDateObject(getState('brightsky.' + weatherAdapterInstanceNumber + '.daily.00.sunrise').val).getTime();
|
|
arraySunEvent[1] = getDateObject(getState('brightsky.' + weatherAdapterInstanceNumber + '.daily.00.sunset').val).getTime();
|
|
arraySunEvent[2] = getDateObject(getState('brightsky.' + weatherAdapterInstanceNumber + '.daily.01.sunrise').val).getTime();
|
|
|
|
let j = 0;
|
|
for (j = 0; j < 3; j++) {
|
|
if (arraySunEvent[j] > valDateNow) {
|
|
nextSunEvent = j;
|
|
break;
|
|
}
|
|
}
|
|
let sun = '';
|
|
if (j == 1) {
|
|
sun = 'weather-sunset-down';
|
|
} else {
|
|
sun = 'weather-sunset-up';
|
|
}
|
|
|
|
payloadString += '~' + '~' + Icons.GetIcon(sun) + '~' + rgb_dec565(MSYellow) + '~' + 'Sonne' + '~' + formatDate(getDateObject(arraySunEvent[nextSunEvent]), 'hh:mm') + '~';
|
|
} else {
|
|
payloadString += '~' + '~' + Icons.GetIcon(WeatherIcon) + '~' + WheatherColor + '~' + DayOfWeek + '~' + tempMinMaxString + '~';
|
|
}
|
|
}
|
|
|
|
//Alternativ Layout bekommt zusätzlichen Status
|
|
if (config.bottomScreensaverEntity[4] && getState(NSPanel_Path + 'Config.Screensaver.alternativeScreensaverLayout').val) {
|
|
let val = getState(config.bottomScreensaverEntity[4].ScreensaverEntity).val;
|
|
if (parseFloat(val + '') == val) {
|
|
val = parseFloat(val);
|
|
}
|
|
let iconColor = rgb_dec565(White);
|
|
if (typeof val == 'number') {
|
|
val =
|
|
(val * (config.bottomScreensaverEntity[4].ScreensaverEntityFactor ? config.bottomScreensaverEntity[4].ScreensaverEntityFactor : 0)).toFixed(
|
|
config.bottomScreensaverEntity[4].ScreensaverEntityDecimalPlaces
|
|
) + config.bottomScreensaverEntity[4].ScreensaverEntityUnitText;
|
|
iconColor = GetScreenSaverEntityColor(config.bottomScreensaverEntity[4]);
|
|
} else if (typeof val == 'boolean') {
|
|
iconColor = GetScreenSaverEntityColor(config.bottomScreensaverEntity[4]);
|
|
} else if (typeof val == 'string') {
|
|
iconColor = GetScreenSaverEntityColor(config.bottomScreensaverEntity[4]);
|
|
|
|
let pformat = parseFormat(val);
|
|
if (Debug) log('moments.js --> Datum ' + val + ' valid?: ' + moment(val, pformat, true).isValid(), 'info');
|
|
if (moment(val, pformat, true).isValid()) {
|
|
let DatumZeit = moment(val, pformat).unix(); // Conversion to Unix time stamp
|
|
if (config.bottomScreensaverEntity[4].ScreensaverEntityDateFormat !== undefined) {
|
|
val = new Date(DatumZeit * 1000).toLocaleString(getState(NSPanel_Path + 'Config.locale').val, config.bottomScreensaverEntity[4].ScreensaverEntityDateFormat);
|
|
} else {
|
|
val = new Date(DatumZeit * 1000).toLocaleString(getState(NSPanel_Path + 'Config.locale').val);
|
|
}
|
|
}
|
|
}
|
|
const temp = config.bottomScreensaverEntity[4].ScreensaverEntityIconColor;
|
|
if (temp && typeof temp == 'string' && existsObject(temp)) {
|
|
iconColor = getState(temp).val;
|
|
}
|
|
payloadString +=
|
|
'~' +
|
|
'~' +
|
|
Icons.GetIcon(config.bottomScreensaverEntity[4].ScreensaverEntityIconOn) +
|
|
'~' +
|
|
iconColor +
|
|
'~' +
|
|
config.bottomScreensaverEntity[4].ScreensaverEntityText +
|
|
'~' +
|
|
val;
|
|
} // Ende zusätzlichen Status Alternativ Layout
|
|
} else {
|
|
// USER definierte Bottom Entities
|
|
let checkpoint = true;
|
|
let i = 0;
|
|
for (i = 0; i < maxEntities - 1 && i < config.bottomScreensaverEntity.length; i++) {
|
|
const entity = config.bottomScreensaverEntity[i]
|
|
if (entity == null || entity === undefined) {
|
|
checkpoint = false;
|
|
break;
|
|
}
|
|
RegisterScreensaverEntityWatcher(entity.ScreensaverEntity);
|
|
|
|
let val = getState(entity.ScreensaverEntity).val;
|
|
if (parseFloat(val + '') == val) {
|
|
val = parseFloat(val);
|
|
}
|
|
let iconColor = rgb_dec565(White);
|
|
let icon;
|
|
if (entity.ScreensaverEntityIconOn && existsObject(entity.ScreensaverEntityIconOn!)) {
|
|
let iconName = getState(entity.ScreensaverEntityIconOn!).val;
|
|
icon = Icons.GetIcon(iconName);
|
|
} else {
|
|
icon = Icons.GetIcon(entity.ScreensaverEntityIconOn);
|
|
}
|
|
|
|
if (typeof val == 'number') {
|
|
val = val * (entity.ScreensaverEntityFactor ? entity.ScreensaverEntityFactor! : 0)
|
|
icon = determineScreensaverStatusIcon(entity,val,icon)
|
|
val = val.toFixed(
|
|
entity.ScreensaverEntityDecimalPlaces
|
|
) + entity.ScreensaverEntityUnitText;
|
|
iconColor = GetScreenSaverEntityColor(entity);
|
|
} else if (typeof val == 'boolean') {
|
|
iconColor = GetScreenSaverEntityColor(entity);
|
|
if (!val && entity.ScreensaverEntityIconOff != null) {
|
|
icon = Icons.GetIcon(entity.ScreensaverEntityIconOff);
|
|
}
|
|
if (val && entity.ScreensaverEntityOnText != undefined) {
|
|
val = entity.ScreensaverEntityOnText;
|
|
}
|
|
if (!val && entity.ScreensaverEntityOffText != undefined) {
|
|
val = entity.ScreensaverEntityOffText;
|
|
}
|
|
} else if (typeof val == 'string') {
|
|
iconColor = GetScreenSaverEntityColor(entity);
|
|
let pformat = parseFormat(val);
|
|
if (Debug) log('moments.js --> Datum ' + val + ' valid?: ' + moment(val, pformat, true).isValid(), 'info');
|
|
if (moment(val, pformat, true).isValid()) {
|
|
let DatumZeit = moment(val, pformat).unix(); // Conversion to Unix time stamp
|
|
if (entity.ScreensaverEntityDateFormat !== undefined) {
|
|
val = new Date(DatumZeit * 1000).toLocaleString(getState(NSPanel_Path + 'Config.locale').val, entity.ScreensaverEntityDateFormat);
|
|
} else {
|
|
val = new Date(DatumZeit * 1000).toLocaleString(getState(NSPanel_Path + 'Config.locale').val);
|
|
}
|
|
}
|
|
}
|
|
const text = entity.ScreensaverEntityText && existsState(entity.ScreensaverEntityText) && getState(entity.ScreensaverEntityText).val
|
|
|| entity.ScreensaverEntityText || '';
|
|
|
|
const temp = entity.ScreensaverEntityIconColor;
|
|
if (temp && typeof temp == 'string' && existsObject(temp)) {
|
|
iconColor = getState(temp).val;
|
|
}
|
|
if (i < maxEntities - 1) {
|
|
val = val + '~';
|
|
}
|
|
payloadString += '~' + '~' + icon + '~' + iconColor + '~' + text + '~' + val;
|
|
}
|
|
if (checkpoint == false) {
|
|
for (let j = i; j < maxEntities - 1; j++) {
|
|
payloadString += '~~~~~~';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (screensaverAdvanced) {
|
|
// 5 indicatorScreensaverEntities
|
|
for (let i = 0; i < 5 && i < config.indicatorScreensaverEntity.length; i++) {
|
|
const indicatorScreensaverEntity: NSPanel.ScreenSaverElementWithUndefined = config.indicatorScreensaverEntity[i];
|
|
if (indicatorScreensaverEntity === null || indicatorScreensaverEntity === undefined) {
|
|
break;
|
|
}
|
|
RegisterScreensaverEntityWatcher(indicatorScreensaverEntity.ScreensaverEntity);
|
|
|
|
let val = getState(indicatorScreensaverEntity.ScreensaverEntity).val;
|
|
if (parseFloat(val + '') == val) {
|
|
val = parseFloat(val);
|
|
}
|
|
let iconColor = rgb_dec565(White);
|
|
|
|
let icon;
|
|
if (indicatorScreensaverEntity.ScreensaverEntityIconOn && existsObject(indicatorScreensaverEntity.ScreensaverEntityIconOn!)) {
|
|
let iconName = getState(indicatorScreensaverEntity.ScreensaverEntityIconOn!).val;
|
|
icon = Icons.GetIcon(iconName);
|
|
} else {
|
|
icon = Icons.GetIcon(indicatorScreensaverEntity.ScreensaverEntityIconOn);
|
|
}
|
|
|
|
if (typeof val == 'number') {
|
|
val = val * (indicatorScreensaverEntity.ScreensaverEntityFactor ? indicatorScreensaverEntity.ScreensaverEntityFactor! : 0)
|
|
icon = determineScreensaverStatusIcon(indicatorScreensaverEntity,val,icon)
|
|
val = val.toFixed(
|
|
indicatorScreensaverEntity.ScreensaverEntityDecimalPlaces
|
|
) + indicatorScreensaverEntity.ScreensaverEntityUnitText;
|
|
iconColor = GetScreenSaverEntityColor(indicatorScreensaverEntity);
|
|
} else if (typeof val == 'boolean') {
|
|
iconColor = GetScreenSaverEntityColor(indicatorScreensaverEntity);
|
|
if (!val && indicatorScreensaverEntity.ScreensaverEntityIconOff != null) {
|
|
icon = Icons.GetIcon(indicatorScreensaverEntity.ScreensaverEntityIconOff);
|
|
}
|
|
}
|
|
const temp = indicatorScreensaverEntity.ScreensaverEntityIconColor;
|
|
if (temp && typeof temp == 'string' && existsObject(temp)) {
|
|
iconColor = getState(temp).val;
|
|
}
|
|
payloadString += '~' + 'f' + (i + 1) + 'Icon~' + icon + '~' + iconColor + '~' + indicatorScreensaverEntity.ScreensaverEntityText + '~' + val + '~';
|
|
}
|
|
}
|
|
if (Debug) log('HandleScreensaverUpdate payload: weatherUpdate~' + payloadString, 'info');
|
|
|
|
SendToPanel({payload: 'weatherUpdate~' + payloadString});
|
|
|
|
HandleScreensaverStatusIcons();
|
|
|
|
HandleScreensaverColors();
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function HandleScreensaverUpdate: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registers a watcher for the specified screensaver entity.
|
|
*
|
|
* This function sets up a watcher to monitor changes in the specified screensaver entity and perform actions when changes occur.
|
|
*
|
|
* @function RegisterScreensaverEntityWatcher
|
|
* @param {string} id - The ID of the screensaver entity to watch.
|
|
*/
|
|
function RegisterScreensaverEntityWatcher (id: string): void {
|
|
try {
|
|
if (subscriptions.hasOwnProperty(id)) {
|
|
return;
|
|
}
|
|
|
|
subscriptions[id] = on({id: id, change: 'any'}, () => {
|
|
HandleScreensaverUpdate();
|
|
});
|
|
} catch (err: any) {
|
|
log('function RegisterEntityWatcher: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles the status icons for the screensaver on the NSPanel.
|
|
*
|
|
* This function manages the display and updates of status icons on the screensaver.
|
|
*
|
|
* @function HandleScreensaverStatusIcons
|
|
*/
|
|
function HandleScreensaverStatusIcons (): void {
|
|
try {
|
|
let payloadString = '';
|
|
const iconData: Record<'mrIcon1' | 'mrIcon2', NSPanel.ScreenSaverMRDataElement> = {
|
|
mrIcon1: {
|
|
ScreensaverEntity:
|
|
config.mrIcon1ScreensaverEntity.ScreensaverEntity != null && existsState(config.mrIcon1ScreensaverEntity.ScreensaverEntity)
|
|
? getState(config.mrIcon1ScreensaverEntity.ScreensaverEntity).val
|
|
: null,
|
|
ScreensaverEntityIconOn: config.mrIcon1ScreensaverEntity.ScreensaverEntityIconOn ? Icons.GetIcon(config.mrIcon1ScreensaverEntity.ScreensaverEntityIconOn) : '',
|
|
ScreensaverEntityIconOff: config.mrIcon1ScreensaverEntity.ScreensaverEntityIconOff ? Icons.GetIcon(config.mrIcon1ScreensaverEntity.ScreensaverEntityIconOff) : '',
|
|
ScreensaverEntityOnColor: config.mrIcon1ScreensaverEntity.ScreensaverEntityOnColor,
|
|
ScreensaverEntityOffColor: config.mrIcon1ScreensaverEntity.ScreensaverEntityOffColor,
|
|
ScreensaverEntityValue: config.mrIcon1ScreensaverEntity.ScreensaverEntityValue === null ? null : getState(config.mrIcon1ScreensaverEntity.ScreensaverEntityValue).val,
|
|
ScreensaverEntityValueDecimalPlace: config.mrIcon1ScreensaverEntity.ScreensaverEntityValueDecimalPlace,
|
|
ScreensaverEntityValueUnit: config.mrIcon1ScreensaverEntity.ScreensaverEntityValueUnit,
|
|
ScreensaverEntityIconSelect:
|
|
config.mrIcon1ScreensaverEntity.ScreensaverEntityIconSelect && typeof config.mrIcon1ScreensaverEntity.ScreensaverEntityIconSelect === 'object'
|
|
? config.mrIcon1ScreensaverEntity.ScreensaverEntityIconSelect
|
|
: null,
|
|
},
|
|
mrIcon2: {
|
|
ScreensaverEntity:
|
|
config.mrIcon2ScreensaverEntity.ScreensaverEntity != null && existsState(config.mrIcon2ScreensaverEntity.ScreensaverEntity)
|
|
? getState(config.mrIcon2ScreensaverEntity.ScreensaverEntity).val
|
|
: null,
|
|
ScreensaverEntityIconOn: config.mrIcon2ScreensaverEntity.ScreensaverEntityIconOn ? Icons.GetIcon(config.mrIcon2ScreensaverEntity.ScreensaverEntityIconOn) : '',
|
|
ScreensaverEntityIconOff: config.mrIcon2ScreensaverEntity.ScreensaverEntityIconOff ? Icons.GetIcon(config.mrIcon2ScreensaverEntity.ScreensaverEntityIconOff) : '',
|
|
ScreensaverEntityOnColor: config.mrIcon2ScreensaverEntity.ScreensaverEntityOnColor,
|
|
ScreensaverEntityOffColor: config.mrIcon2ScreensaverEntity.ScreensaverEntityOffColor,
|
|
ScreensaverEntityValue: config.mrIcon2ScreensaverEntity.ScreensaverEntityValue === null ? null : getState(config.mrIcon2ScreensaverEntity.ScreensaverEntityValue).val,
|
|
ScreensaverEntityValueDecimalPlace: config.mrIcon2ScreensaverEntity.ScreensaverEntityValueDecimalPlace,
|
|
ScreensaverEntityValueUnit: config.mrIcon2ScreensaverEntity.ScreensaverEntityValueUnit,
|
|
ScreensaverEntityIconSelect:
|
|
config.mrIcon2ScreensaverEntity.ScreensaverEntityIconSelect && typeof config.mrIcon2ScreensaverEntity.ScreensaverEntityIconSelect === 'object'
|
|
? config.mrIcon2ScreensaverEntity.ScreensaverEntityIconSelect
|
|
: null,
|
|
},
|
|
};
|
|
for (const a in iconData) {
|
|
if (iconData[a].ScreensaverEntityValue !== null) {
|
|
switch (typeof iconData[a].ScreensaverEntityValue) {
|
|
case 'string':
|
|
if (iconData[a].ScreensaverEntityValue === '' || isNaN(iconData[a].ScreensaverEntityValue)) break;
|
|
case 'number':
|
|
case 'bigint':
|
|
iconData[a].ScreensaverEntityValue = Number(iconData[a].ScreensaverEntityValue).toFixed(iconData[a].ScreensaverEntityValueDecimalPlace);
|
|
break;
|
|
case 'boolean':
|
|
break;
|
|
case 'symbol':
|
|
case 'undefined':
|
|
case 'object':
|
|
case 'function':
|
|
iconData[a].ScreensaverEntityValue = null;
|
|
}
|
|
}
|
|
let hwBtn1Col: RGB = iconData[a].ScreensaverEntityOffColor;
|
|
if (iconData[a].ScreensaverEntity != null || iconData[a].ScreensaverEntityValue != null) {
|
|
// Prüfung ob ScreensaverEntity vom Typ String ist
|
|
if (iconData[a].ScreensaverEntity != null) {
|
|
if (typeof iconData[a].ScreensaverEntity == 'string') {
|
|
if (Debug) log('Entity ist String', 'info');
|
|
switch (String(iconData[a].ScreensaverEntity).toUpperCase()) {
|
|
case 'ON':
|
|
case 'OK':
|
|
case 'AN':
|
|
case 'YES':
|
|
case 'TRUE':
|
|
case 'ONLINE':
|
|
hwBtn1Col = iconData[a].ScreensaverEntityOnColor;
|
|
break;
|
|
default:
|
|
}
|
|
if (Debug) log('Value: ' + iconData[a].ScreensaverEntity + ' Color: ' + JSON.stringify(hwBtn1Col), 'info');
|
|
// Alles was kein String ist in Boolean umwandeln
|
|
} else {
|
|
if (Debug) log('Entity ist kein String', 'info');
|
|
if (!!iconData[a].ScreensaverEntity) {
|
|
hwBtn1Col = iconData[a].ScreensaverEntityOnColor;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Icon ermitteln
|
|
if (iconData[a].ScreensaverEntityIconSelect && iconData[a].ScreensaverEntity != null) {
|
|
const icon = iconData[a].ScreensaverEntityIconSelect[iconData[a].ScreensaverEntity];
|
|
if (icon !== undefined) {
|
|
payloadString += Icons.GetIcon(icon);
|
|
if (Debug) log('SelectIcon: ' + payloadString, 'info');
|
|
}
|
|
} else if (iconData[a].ScreensaverEntity) {
|
|
payloadString += iconData[a].ScreensaverEntityIconOn;
|
|
if (Debug) log('Icon if true ' + payloadString, 'info');
|
|
} else {
|
|
if (iconData[a].ScreensaverEntityIconOff) {
|
|
payloadString += iconData[a].ScreensaverEntityIconOff;
|
|
if (Debug) log('Icon1 else true ' + payloadString, 'info');
|
|
} else {
|
|
payloadString += iconData[a].ScreensaverEntityIconOn;
|
|
if (Debug) log('Icon1 else false ' + payloadString, 'info');
|
|
}
|
|
}
|
|
|
|
if (iconData[a].ScreensaverEntityValue != null) {
|
|
payloadString += iconData[a].ScreensaverEntityValue;
|
|
payloadString += iconData[a].ScreensaverEntityValueUnit == null ? '' : iconData[a].ScreensaverEntityValueUnit;
|
|
}
|
|
|
|
payloadString += '~' + rgb_dec565(hwBtn1Col) + '~';
|
|
} else {
|
|
hwBtn1Col = Black;
|
|
payloadString += '~~';
|
|
}
|
|
}
|
|
|
|
let alternateScreensaverMFRIcon1Size: boolean = true;
|
|
let alternateScreensaverMFRIcon2Size: boolean = true;
|
|
if (existsState(NSPanel_Path + 'Config.MRIcons.alternateMRIconSize.1')) {
|
|
alternateScreensaverMFRIcon1Size = getState(NSPanel_Path + 'Config.MRIcons.alternateMRIconSize.1').val;
|
|
}
|
|
if (existsState(NSPanel_Path + 'Config.MRIcons.alternateMRIconSize.2')) {
|
|
alternateScreensaverMFRIcon2Size = getState(NSPanel_Path + 'Config.MRIcons.alternateMRIconSize.2').val;
|
|
}
|
|
|
|
//Alternate MRIcon Size
|
|
if (alternateScreensaverMFRIcon1Size) {
|
|
payloadString += '1~';
|
|
} else {
|
|
payloadString += '~';
|
|
}
|
|
if (alternateScreensaverMFRIcon2Size) {
|
|
payloadString += '1~';
|
|
} else {
|
|
payloadString += '~';
|
|
}
|
|
// statusUpdate~icon1~icon1Color~icon1font~icon2~icon2color~icon2font~icon2font
|
|
SendToPanel({payload: 'statusUpdate~' + payloadString});
|
|
} catch (err: any) {
|
|
log('error at function HandleScreensaverStatusIcons: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles the color scale conversion for a given temperature value.
|
|
*
|
|
* This function converts a temperature value to a corresponding color scale value.
|
|
*
|
|
* @function HandleColorScale
|
|
* @param {string} valueScaletemp - The temperature value to convert.
|
|
* @returns {number} The corresponding color scale value.
|
|
*/
|
|
function HandleColorScale (valueScaletemp: string): number {
|
|
switch (valueScaletemp) {
|
|
case '0':
|
|
return rgb_dec565(colorScale0);
|
|
case '1':
|
|
return rgb_dec565(colorScale1);
|
|
case '2':
|
|
return rgb_dec565(colorScale2);
|
|
case '3':
|
|
return rgb_dec565(colorScale3);
|
|
case '4':
|
|
return rgb_dec565(colorScale4);
|
|
case '5':
|
|
return rgb_dec565(colorScale5);
|
|
case '6':
|
|
return rgb_dec565(colorScale6);
|
|
case '7':
|
|
return rgb_dec565(colorScale7);
|
|
case '8':
|
|
return rgb_dec565(colorScale8);
|
|
case '9':
|
|
return rgb_dec565(colorScale9);
|
|
case '10':
|
|
return rgb_dec565(colorScale10);
|
|
default:
|
|
return rgb_dec565(colorScale10);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles the color settings for the screensaver on the NSPanel.
|
|
*
|
|
* This function manages the color configurations and updates for the screensaver.
|
|
*
|
|
* @function HandleScreensaverColors
|
|
*/
|
|
function HandleScreensaverColors (): void {
|
|
try {
|
|
let vwIcon: number[] = [];
|
|
if (getState(NSPanel_Path + 'Config.Screensaver.autoWeatherColorScreensaverLayout').val) {
|
|
vwIcon[0] = vwIconColor[0];
|
|
vwIcon[1] = vwIconColor[1];
|
|
vwIcon[2] = vwIconColor[2];
|
|
vwIcon[3] = vwIconColor[3];
|
|
vwIcon[4] = vwIconColor[4];
|
|
} else {
|
|
if (weatherForecast) {
|
|
vwIcon[0] = rgb_dec565(sctMainIcon);
|
|
vwIcon[1] = rgb_dec565(sctF1Icon);
|
|
vwIcon[2] = rgb_dec565(sctF2Icon);
|
|
vwIcon[3] = rgb_dec565(sctF3Icon);
|
|
vwIcon[4] = rgb_dec565(sctF4Icon);
|
|
} else {
|
|
vwIcon[0] = rgb_dec565(sctMainIcon);
|
|
vwIcon[1] = vwIconColor[1];
|
|
vwIcon[2] = vwIconColor[2];
|
|
vwIcon[3] = vwIconColor[3];
|
|
vwIcon[4] = vwIconColor[4];
|
|
}
|
|
}
|
|
|
|
let scrSvrBGCol: any;
|
|
|
|
if (bgColorScrSaver == 0) {
|
|
scrSvrBGCol = rgb_dec565(scbackground);
|
|
} else if (bgColorScrSaver == 1) {
|
|
scrSvrBGCol = rgb_dec565(scbackgroundInd1);
|
|
} else if (bgColorScrSaver == 2) {
|
|
scrSvrBGCol = rgb_dec565(scbackgroundInd2);
|
|
} else if (bgColorScrSaver == 3) {
|
|
scrSvrBGCol = rgb_dec565(scbackgroundInd3);
|
|
} else if (bgColorScrSaver == 4) {
|
|
scrSvrBGCol = rgb_dec565({red: 255, green: 16, blue: 240});
|
|
} else if (bgColorScrSaver == 5) {
|
|
scrSvrBGCol = rgb_dec565({red: 100, green: 0, blue: 0});
|
|
}
|
|
|
|
let payloadString = buildNSPanelString(
|
|
'color',
|
|
scrSvrBGCol, //background
|
|
rgb_dec565(sctime), //time
|
|
rgb_dec565(sctimeAMPM), //timeAMPM~
|
|
rgb_dec565(scdate), //date~
|
|
rgb_dec565(sctMainText), //tMainText~
|
|
rgb_dec565(sctForecast1), //tForecast1~
|
|
rgb_dec565(sctForecast2), //tForecast2~
|
|
rgb_dec565(sctForecast3), //tForecast3~
|
|
rgb_dec565(sctForecast4), //tForecast4~
|
|
rgb_dec565(sctForecast1Val), //tForecast1Val~
|
|
rgb_dec565(sctForecast2Val), //tForecast2Val~
|
|
rgb_dec565(sctForecast3Val), //tForecast3Val~
|
|
rgb_dec565(sctForecast4Val), //tForecast4Val~
|
|
rgb_dec565(scbar), //bar~
|
|
rgb_dec565(sctMainTextAlt), //tMainTextAlt
|
|
rgb_dec565(sctTimeAdd) //tTimeAdd
|
|
);
|
|
|
|
SendToPanel({payload: payloadString});
|
|
} catch (err: any) {
|
|
log('error at function HandleScreensaverColors: ' + err.message, 'warn');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves the color for a screensaver entity based on its configuration.
|
|
*
|
|
* This function determines and returns the color for the specified screensaver entity configuration.
|
|
*
|
|
* @function GetScreenSaverEntityColor
|
|
* @param {NSPanel.ScreenSaverElement | null} configElement - The configuration element for the screensaver entity.
|
|
* @returns {number} The color code for the screensaver entity.
|
|
*/
|
|
function GetScreenSaverEntityColor (configElement: NSPanel.ScreenSaverElement | null): number {
|
|
try {
|
|
let colorReturn: number;
|
|
if (configElement && configElement.ScreensaverEntityIconColor != undefined) {
|
|
const ScreensaverEntityIconColor = configElement.ScreensaverEntityIconColor as NSPanel.IconScaleElement;
|
|
if (typeof getState(configElement.ScreensaverEntity).val == 'boolean') {
|
|
let iconvalbest = typeof ScreensaverEntityIconColor == 'object' && ScreensaverEntityIconColor.val_best !== undefined ? ScreensaverEntityIconColor.val_best : false;
|
|
colorReturn = getState(configElement.ScreensaverEntity).val == iconvalbest ? rgb_dec565(colorScale0) : rgb_dec565(colorScale10);
|
|
} else if (typeof ScreensaverEntityIconColor == 'object') {
|
|
const iconvalmin: number = ScreensaverEntityIconColor.val_min != undefined ? ScreensaverEntityIconColor.val_min : 0;
|
|
const iconvalmax: number = ScreensaverEntityIconColor.val_max != undefined ? ScreensaverEntityIconColor.val_max : 100;
|
|
const iconvalbest: number = ScreensaverEntityIconColor.val_best != undefined ? ScreensaverEntityIconColor.val_best : iconvalmin;
|
|
let valueScale = getState(configElement.ScreensaverEntity).val * configElement.ScreensaverEntityFactor!;
|
|
|
|
if (iconvalmin == 0 && iconvalmax == 1) {
|
|
colorReturn = getState(configElement.ScreensaverEntity).val == 1 ? rgb_dec565(colorScale0) : rgb_dec565(colorScale10);
|
|
} else {
|
|
if (iconvalbest == iconvalmin) {
|
|
valueScale = scale(valueScale, iconvalmin, iconvalmax, 10, 0);
|
|
} else {
|
|
if (valueScale < iconvalbest) {
|
|
valueScale = scale(valueScale, iconvalmin, iconvalbest, 0, 10);
|
|
} else if (valueScale > iconvalbest || iconvalbest != iconvalmin) {
|
|
valueScale = scale(valueScale, iconvalbest, iconvalmax, 10, 0);
|
|
} else {
|
|
valueScale = scale(valueScale, iconvalmin, iconvalmax, 10, 0);
|
|
}
|
|
}
|
|
//limit if valueScale is smaller/larger than 0-10
|
|
if (valueScale > 10) valueScale = 10;
|
|
if (valueScale < 0) valueScale = 0;
|
|
|
|
let valueScaletemp = Math.round(valueScale).toFixed();
|
|
colorReturn = HandleColorScale(valueScaletemp);
|
|
}
|
|
if (ScreensaverEntityIconColor.val_min == undefined) {
|
|
colorReturn = rgb_dec565(configElement.ScreensaverEntityIconColor as RGB);
|
|
}
|
|
} else {
|
|
colorReturn = rgb_dec565(White);
|
|
}
|
|
} else {
|
|
const value: number | boolean = configElement ? getState(configElement.ScreensaverEntity).val : 0;
|
|
if (configElement && typeof value == 'boolean') {
|
|
if (value) {
|
|
if (configElement.ScreensaverEntityOnColor != undefined) {
|
|
colorReturn = rgb_dec565(configElement.ScreensaverEntityOnColor);
|
|
} else {
|
|
colorReturn = rgb_dec565(White);
|
|
}
|
|
} else {
|
|
if (configElement.ScreensaverEntityOffColor != undefined) {
|
|
colorReturn = rgb_dec565(configElement.ScreensaverEntityOffColor);
|
|
} else {
|
|
colorReturn = rgb_dec565(White);
|
|
}
|
|
}
|
|
} else {
|
|
colorReturn = rgb_dec565(White);
|
|
}
|
|
}
|
|
return colorReturn;
|
|
} catch (err: any) {
|
|
log('error at function GetScreenSaverEntityColor: ' + err.message, 'warn');
|
|
}
|
|
return rgb_dec565(White);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the AccuWeather icon string based on the provided icon number.
|
|
*
|
|
* This function maps the given AccuWeather icon number to its corresponding icon string representation.
|
|
*
|
|
* @function GetAccuWeatherIcon
|
|
* @param {number} icon - The AccuWeather icon number.
|
|
* @returns {string} The corresponding icon string.
|
|
*/
|
|
function GetAccuWeatherIcon (icon: number): string {
|
|
try {
|
|
switch (icon) {
|
|
case 30: // Hot
|
|
return 'weather-sunny-alert'; // exceptional
|
|
|
|
case 24: // Ice
|
|
case 31: // Cold
|
|
return 'snowflake-alert'; // exceptional
|
|
|
|
case 7: // Cloudy
|
|
case 8: // Dreary (Overcast)
|
|
case 38: // Mostly Cloudy
|
|
return 'weather-cloudy'; // cloudy
|
|
|
|
case 11: // fog
|
|
return 'weather-fog'; // fog
|
|
|
|
case 25: // Sleet
|
|
return 'weather-hail'; // Hail
|
|
|
|
case 15: // T-Storms
|
|
return 'weather-lightning'; // lightning
|
|
|
|
case 16: // Mostly Cloudy w/ T-Storms
|
|
case 17: // Partly Sunny w/ T-Storms
|
|
case 41: // Partly Cloudy w/ T-Storms
|
|
case 42: // Mostly Cloudy w/ T-Storms
|
|
return 'weather-lightning-rainy'; // lightning-rainy
|
|
|
|
case 33: // Clear
|
|
case 34: // Mostly Clear
|
|
case 37: // Hazy Moonlight
|
|
return 'weather-night';
|
|
|
|
case 3: // Partly Sunny
|
|
case 4: // Intermittent Clouds
|
|
case 6: // Mostly Cloudy
|
|
case 35: // Partly Cloudy
|
|
case 36: // Intermittent Clouds
|
|
return 'weather-partly-cloudy'; // partlycloudy
|
|
|
|
case 18: // pouring
|
|
return 'weather-pouring'; // pouring
|
|
|
|
case 12: // Showers
|
|
case 13: // Mostly Cloudy w/ Showers
|
|
case 14: // Partly Sunny w/ Showers
|
|
case 26: // Freezing Rain
|
|
case 39: // Partly Cloudy w/ Showers
|
|
case 40: // Mostly Cloudy w/ Showers
|
|
return 'weather-rainy'; // rainy
|
|
|
|
case 19: // Flurries
|
|
case 20: // Mostly Cloudy w/ Flurries
|
|
case 21: // Partly Sunny w/ Flurries
|
|
case 22: // Snow
|
|
case 23: // Mostly Cloudy w/ Snow
|
|
case 43: // Mostly Cloudy w/ Flurries
|
|
case 44: // Mostly Cloudy w/ Snow
|
|
return 'weather-snowy'; // snowy
|
|
|
|
case 29: // Rain and Snow
|
|
return 'weather-snowy-rainy'; // snowy-rainy
|
|
|
|
case 1: // Sunny
|
|
case 2: // Mostly Sunny
|
|
case 5: // Hazy Sunshine
|
|
return 'weather-sunny'; // sunny
|
|
|
|
case 32: // windy
|
|
return 'weather-windy'; // windy
|
|
|
|
default:
|
|
return 'alert-circle-outline';
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function GetAccuWeatherIcon: ' + err.message, 'warn');
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Retrieves the color code for a given AccuWeather icon number.
|
|
*
|
|
* This function maps the provided AccuWeather icon number to its corresponding color code.
|
|
*
|
|
* @function GetAccuWeatherIconColor
|
|
* @param {number} icon - The AccuWeather icon number.
|
|
* @returns {number} The corresponding color code.
|
|
*/
|
|
function GetAccuWeatherIconColor (icon: number): number {
|
|
try {
|
|
switch (icon) {
|
|
case 24: // Ice
|
|
case 30: // Hot
|
|
case 31: // Cold
|
|
return rgb_dec565(swExceptional); // exceptional
|
|
|
|
case 7: // Cloudy
|
|
case 8: // Dreary (Overcast)
|
|
case 38: // Mostly Cloudy
|
|
return rgb_dec565(swCloudy); // cloudy
|
|
|
|
case 11: // fog
|
|
return rgb_dec565(swFog); // fog
|
|
|
|
case 25: // Sleet
|
|
return rgb_dec565(swHail); // Hail
|
|
|
|
case 15: // T-Storms
|
|
return rgb_dec565(swLightning); // lightning
|
|
|
|
case 16: // Mostly Cloudy w/ T-Storms
|
|
case 17: // Partly Sunny w/ T-Storms
|
|
case 41: // Partly Cloudy w/ T-Storms
|
|
case 42: // Mostly Cloudy w/ T-Storms
|
|
return rgb_dec565(swLightningRainy); // lightning-rainy
|
|
|
|
case 33: // Clear
|
|
case 34: // Mostly Clear
|
|
case 37: // Hazy Moonlight
|
|
return rgb_dec565(swClearNight);
|
|
|
|
case 3: // Partly Sunny
|
|
case 4: // Intermittent Clouds
|
|
case 6: // Mostly Cloudy
|
|
case 35: // Partly Cloudy
|
|
case 36: // Intermittent Clouds
|
|
return rgb_dec565(swPartlycloudy); // partlycloudy
|
|
|
|
case 18: // pouring
|
|
return rgb_dec565(swPouring); // pouring
|
|
|
|
case 12: // Showers
|
|
case 13: // Mostly Cloudy w/ Showers
|
|
case 14: // Partly Sunny w/ Showers
|
|
case 26: // Freezing Rain
|
|
case 39: // Partly Cloudy w/ Showers
|
|
case 40: // Mostly Cloudy w/ Showers
|
|
return rgb_dec565(swRainy); // rainy
|
|
|
|
case 19: // Flurries
|
|
case 20: // Mostly Cloudy w/ Flurries
|
|
case 21: // Partly Sunny w/ Flurries
|
|
case 22: // Snow
|
|
case 23: // Mostly Cloudy w/ Snow
|
|
case 43: // Mostly Cloudy w/ Flurries
|
|
case 44: // Mostly Cloudy w/ Snow
|
|
return rgb_dec565(swSnowy); // snowy
|
|
|
|
case 29: // Rain and Snow
|
|
return rgb_dec565(swSnowyRainy); // snowy-rainy
|
|
|
|
case 1: // Sunny
|
|
case 2: // Mostly Sunny
|
|
case 5: // Hazy Sunshine
|
|
return rgb_dec565(swSunny); // sunny
|
|
|
|
case 32: // windy
|
|
return rgb_dec565(swWindy); // windy
|
|
|
|
default:
|
|
return rgb_dec565(White);
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function GetAccuWeatherIconColor: ' + err.message, 'warn');
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the DasWetter icon string based on the provided icon number.
|
|
*
|
|
* This function maps the given DasWetter icon number to its corresponding icon string representation.
|
|
*
|
|
* @function GetDasWetterIcon
|
|
* @param {number} icon - The DasWetter icon number.
|
|
* @returns {string} The corresponding icon string.
|
|
*/
|
|
function GetDasWetterIcon (icon: number): string {
|
|
try {
|
|
switch (icon) {
|
|
case 1: // Sonnig
|
|
return 'weather-sunny'; // sunny
|
|
|
|
case 2: // Teils bewölkt
|
|
case 3: // Bewölkt
|
|
return 'weather-partly-cloudy'; // partlycloudy
|
|
|
|
case 4: // Bedeckt
|
|
return 'weather-cloudy'; // cloudy
|
|
|
|
case 5: // Teils bewölkt mit leichtem Regen
|
|
case 6: // Bewölkt mit leichtem Regen
|
|
case 8: // Teils bewölkt mit mäßigem Regen
|
|
case 9: // Bewölkt mit mäßigem Regen
|
|
return 'weather-partly-rainy'; // partly-rainy
|
|
|
|
case 7: // Bedeckt mit leichtem Regen
|
|
return 'weather-rainy'; // rainy
|
|
|
|
case 10: // Bedeckt mit mäßigem Regen
|
|
return 'weather-pouring'; // pouring
|
|
|
|
case 11: // Teils bewölkt mit starken Regenschauern
|
|
case 12: // Bewölkt mit stürmischen Regenschauern
|
|
return 'weather-partly-lightning'; // partlylightning
|
|
|
|
case 13: // Bedeckt mit stürmischen Regenschauern
|
|
return 'weather-lightning'; // lightning
|
|
|
|
case 14: // Teils bewölkt mit stürmischen Regenschauern und Hagel
|
|
case 15: // Bewölkt mit stürmischen Regenschauern und Hagel
|
|
case 16: // Bedeckt mit stürmischen Regenschauern und Hagel
|
|
return 'weather-hail'; // Hail
|
|
|
|
case 17: // Teils bewölkt mit Schnee
|
|
case 18: // Bewölkt mit Schnee
|
|
return 'weather-partly-snowy'; // partlysnowy
|
|
|
|
case 19: // Bedeckt mit Schneeschauern
|
|
return 'weather-snowy'; // snowy
|
|
|
|
case 20: // Teils bewölkt mit Schneeregen
|
|
case 21: // Bewölkt mit Schneeregen
|
|
return 'weather-partly-snowy-rainy';
|
|
|
|
case 22: // Bedeckt mit Schneeregen
|
|
return 'weather-snowy-rainy'; // snowy-rainy
|
|
|
|
default:
|
|
return 'alert-circle-outline';
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function GetDasWetterIcon: ' + err.message, 'warn');
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Retrieves the color code for a given DasWetter icon number.
|
|
*
|
|
* This function maps the provided DasWetter icon number to its corresponding color code.
|
|
*
|
|
* @function GetDasWetterIconColor
|
|
* @param {number} icon - The DasWetter icon number.
|
|
* @returns {number} The corresponding color code.
|
|
*/
|
|
function GetDasWetterIconColor (icon: number): number {
|
|
try {
|
|
switch (icon) {
|
|
case 1: // Sonnig
|
|
return rgb_dec565(swSunny);
|
|
|
|
case 2: // Teils bewölkt
|
|
case 3: // Bewölkt
|
|
return rgb_dec565(swPartlycloudy);
|
|
|
|
case 4: // Bedeckt
|
|
return rgb_dec565(swCloudy);
|
|
|
|
case 5: // Teils bewölkt mit leichtem Regen
|
|
case 6: // Bewölkt mit leichtem Regen
|
|
case 8: // Teils bewölkt mit mäßigem Regen
|
|
case 9: // Bewölkt mit mäßigem Regen
|
|
return rgb_dec565(swRainy);
|
|
|
|
case 7: // Bedeckt mit leichtem Regen
|
|
return rgb_dec565(swRainy);
|
|
|
|
case 10: // Bedeckt mit mäßigem Regen
|
|
return rgb_dec565(swPouring);
|
|
|
|
case 11: // Teils bewölkt mit starken Regenschauern
|
|
case 12: // Bewölkt mit stürmischen Regenschauern
|
|
return rgb_dec565(swLightningRainy);
|
|
|
|
case 13: // Bedeckt mit stürmischen Regenschauern
|
|
return rgb_dec565(swLightning);
|
|
|
|
case 14: // Teils bewölkt mit stürmischen Regenschauern und Hagel
|
|
case 15: // Bewölkt mit stürmischen Regenschauern und Hagel
|
|
case 16: // Bedeckt mit stürmischen Regenschauern und Hagel
|
|
return rgb_dec565(swHail);
|
|
|
|
case 17: // Teils bewölkt mit Schnee
|
|
case 18: // Bewölkt mit Schnee
|
|
return rgb_dec565(swSnowy);
|
|
|
|
case 19: // Bedeckt mit Schneeschauern
|
|
return rgb_dec565(swSnowy);
|
|
|
|
case 20: // Teils bewölkt mit Schneeregen
|
|
case 21: // Bewölkt mit Schneeregen
|
|
return rgb_dec565(swSnowyRainy); // snowy-rainy
|
|
|
|
case 22: // Bedeckt mit Schneeregen
|
|
return rgb_dec565(swSnowyRainy);
|
|
|
|
default:
|
|
return rgb_dec565(White);
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function GetDasWetterIconColor: ' + err.message, 'warn');
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the OpenWeatherMap icon string based on the provided icon string.
|
|
*
|
|
* This function maps the given OpenWeatherMap icon string to its corresponding icon string representation.
|
|
*
|
|
* @function GetOpenWeatherMapIcon
|
|
* @param {string} icon - The AccuWeather icon string.
|
|
* @returns {string} The corresponding icon string.
|
|
*/
|
|
function GetOpenWeatherMapIcon (icon: string): string {
|
|
try {
|
|
switch (icon) {
|
|
/*
|
|
01d.png return rgb_dec565(swSunny);
|
|
01n.png return rgb_dec565(swClearNight);
|
|
02d.png 02n.png few clouds*
|
|
03d.png 03n.png scattered clouds
|
|
04d.png 04n.png broken clouds
|
|
09d.png 09n.png shower rain
|
|
10d.png 10n.png rain*
|
|
11d.png 11n.png thunderstorm
|
|
13d.png 13n.png snow
|
|
50d.png 50n.png mist
|
|
*/
|
|
|
|
case "01d":
|
|
return 'weather-sunny';
|
|
case "01n":
|
|
return 'weather-night';
|
|
case "02d": //few clouds day
|
|
return 'weather-partly-cloudy';
|
|
case "02n": //few clouds night
|
|
return 'weather-night-partly-cloudy';
|
|
case "03d": //scattered clouds
|
|
case "03n":
|
|
return 'weather-cloudy';
|
|
case "04d": // cloudy
|
|
case "04n":
|
|
return 'weather-cloudy';
|
|
case "09d": //shower rain
|
|
case "09n":
|
|
return 'weather-rainy';
|
|
case "10d": //rain
|
|
case "10n":
|
|
return 'weather-pouring';
|
|
case "11d": //Thunderstorm
|
|
case "11n":
|
|
return 'weather-lightning';
|
|
case "13d": //snow
|
|
case "13n":
|
|
return 'weather-snowy';
|
|
case "50d": //mist
|
|
case "50n":
|
|
return 'weather-fog';
|
|
default:
|
|
return 'alert-circle-outline';
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function GetOpenWeatherMapIcon: ' + err.message, 'warn');
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Retrieves the color code for a given OpenWeatherMap icon string.
|
|
*
|
|
* This function maps the provided OpenWeatherMap icon string to its corresponding color code.
|
|
*
|
|
* @function GetOpenWeatherMapIconColor
|
|
* @param {string} icon - The OpenWeatherMap icon string.
|
|
* @returns {number} The corresponding color code.
|
|
*/
|
|
function GetOpenWeatherMapIconColor (icon: string): number {
|
|
try {
|
|
switch (icon) {
|
|
case "01d": //clear sky day
|
|
return rgb_dec565(swSunny);
|
|
case "01n": //clear sky night
|
|
return rgb_dec565(swClearNight);
|
|
case "02d": //few clouds day
|
|
case "02n": //few clouds night
|
|
return rgb_dec565(swPartlycloudy);
|
|
case "03d": //scattered clouds
|
|
case "03n":
|
|
return rgb_dec565(swCloudy);
|
|
case "04d": //broken clouds
|
|
case "04n":
|
|
return rgb_dec565(swCloudy);
|
|
case "09d": //shower rain
|
|
case "09n":
|
|
return rgb_dec565(swRainy);
|
|
case "10d": //rain
|
|
case "10n":
|
|
return rgb_dec565(swPouring);
|
|
case "11d": //Thunderstorm
|
|
case "11n":
|
|
return rgb_dec565(swLightningRainy);
|
|
case "13d": //snow
|
|
case "13n":
|
|
return rgb_dec565(swSnowy);
|
|
case "50d": //mist
|
|
case "50n":
|
|
return rgb_dec565(swFog);
|
|
default:
|
|
return rgb_dec565(White);
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function GetOpenWeatherMapIconColor: ' + err.message, 'warn');
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the PirateWeather icon string based on the provided icon string.
|
|
*
|
|
* This function maps the given PirateWeather icon string to its corresponding icon string representation.
|
|
*
|
|
* @function GetPirateWeatherIcon
|
|
* @param {string} icon - The PirateWeather icon string.
|
|
* @returns {string} The corresponding icon string.
|
|
*/
|
|
function GetPirateWeatherIcon (icon: string): string {
|
|
try {
|
|
switch (icon) {
|
|
|
|
case 'cloudy':
|
|
case 'mostly-cloudy-day':
|
|
case 'mostly-cloudy-night':
|
|
return 'weather-cloudy';
|
|
|
|
case 'fog':
|
|
case 'mist':
|
|
case 'smoke':
|
|
return 'weather-fog';
|
|
|
|
case 'hail':
|
|
return 'weather-hail';
|
|
|
|
case 'haze':
|
|
return 'weather-hazy'
|
|
|
|
case 'thunderstorm':
|
|
return 'weather-lightning';
|
|
|
|
case 'possible-precipitation-day':
|
|
case 'possible-precipitation-night':
|
|
return 'weather-lightning-rainy';
|
|
|
|
case 'clear-night':
|
|
case 'mostly-clear-night':
|
|
return 'weather-night';
|
|
|
|
case 'partly-cloudy-night':
|
|
return 'weather-night-partly-cloudy';
|
|
|
|
case 'partly-cloudy-day':
|
|
return 'weather-partly-cloudy';
|
|
|
|
case 'possible-rain-day':
|
|
case 'possible-rain-night':
|
|
return 'weather-partly-rainy';
|
|
|
|
case 'possible-snow-night':
|
|
case 'possible-snow-day':
|
|
return 'weather-partly-snowy';
|
|
|
|
case 'possible-sleet-day':
|
|
case 'possible-sleet-night':
|
|
return 'weather-partly-snowy-rainy';
|
|
|
|
case 'rain':
|
|
case 'heavy-rain':
|
|
case 'precipitation':
|
|
return 'weather-pouring';
|
|
|
|
case 'drizzle':
|
|
case 'light-rain':
|
|
return 'weather-rainy';
|
|
|
|
case 'light-snow':
|
|
case 'snow':
|
|
return 'weather-snowy';
|
|
|
|
case 'heavy-sleet':
|
|
case 'heavy-snow':
|
|
case 'flurries':
|
|
return 'weather-snowy-heavy';
|
|
|
|
case 'sleet':
|
|
case 'light-sleet':
|
|
case 'very-light-sleet':
|
|
return 'weather-snowy-rainy';
|
|
|
|
case 'clear-day':
|
|
case 'mostly-clear-day':
|
|
return 'weather-sunny';
|
|
|
|
case 'dangerous-wind':
|
|
return 'weather-tornado';
|
|
|
|
case 'wind':
|
|
return 'weather-windy';
|
|
|
|
case 'breezy':
|
|
return 'weather-windy-variant';
|
|
|
|
default:
|
|
return 'alert-circle-outline';
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function GetPirateWeatherIcon: ' + err.message, 'warn');
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Retrieves the color code for a given AccuWeather icon number.
|
|
*
|
|
* This function maps the provided AccuWeather icon number to its corresponding color code.
|
|
*
|
|
* @function GetPirateWeatherIconColor
|
|
* @param {string} icon - The PirateWeather icon string.
|
|
* @returns {number} The corresponding color code.
|
|
*/
|
|
function GetPirateWeatherIconColor (icon: string): number {
|
|
try {
|
|
switch (icon) {
|
|
case 'cloudy':
|
|
case 'mostly-cloudy-day':
|
|
case 'mostly-cloudy-night':
|
|
return rgb_dec565(swCloudy); // cloudy
|
|
|
|
case 'fog':
|
|
case 'mist':
|
|
case 'haze':
|
|
case 'smoke':
|
|
return rgb_dec565(swFog);
|
|
|
|
case 'hail':
|
|
return rgb_dec565(swHail);
|
|
|
|
case 'thunderstorm': // T-Storms
|
|
return rgb_dec565(swLightning);
|
|
|
|
case 'clear-night':
|
|
case 'mostly-clear-night':
|
|
return rgb_dec565(swClearNight);
|
|
|
|
case 'partly-cloudy-day':
|
|
return rgb_dec565(swPartlycloudy);
|
|
|
|
case 'partly-cloudy-night':
|
|
return rgb_dec565(swPartlycloudy);
|
|
|
|
case 'rain':
|
|
case 'heavy-rain':
|
|
case 'precipitation':
|
|
return rgb_dec565(swPouring);
|
|
|
|
case 'possible-rain-day':
|
|
case 'possible-rain-night':
|
|
case 'possible-precipitation-night':
|
|
case 'possible-precipitation-day':
|
|
case 'drizzle':
|
|
case 'light-rain':
|
|
return rgb_dec565(swRainy);
|
|
|
|
case 'light-snow':
|
|
case 'snow':
|
|
case 'heavy-sleet':
|
|
case 'heavy-snow':
|
|
case 'flurries':
|
|
case 'possible-snow-day':
|
|
case 'possible-snow-night':
|
|
case 'possible-sleet-day':
|
|
case 'possible-sleet-night':
|
|
return rgb_dec565(swSnowy)
|
|
|
|
case 'sleet':
|
|
case 'light-sleet':
|
|
case 'very-light-sleet':
|
|
return rgb_dec565(swSnowyRainy);
|
|
|
|
case 'clear-day':
|
|
case 'mostly-clear-day':
|
|
return rgb_dec565(swSunny);
|
|
|
|
case 'dangerous-wind':
|
|
case 'breezy':
|
|
case 'wind':
|
|
return rgb_dec565(swWindy);
|
|
|
|
default:
|
|
return rgb_dec565(White);
|
|
}
|
|
} catch (err: any) {
|
|
log('error at function GetPirateWeatherIconColor: ' + err.message, 'warn');
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//------------------Begin Read Internal Sensor Data
|
|
//mqttCallback (topic: string, message: string): Promise<void> {
|
|
/**
|
|
* Sets up a subscription to monitor changes in the SENSOR state of the panel.
|
|
*
|
|
* This subscription listens for changes in the `SENSOR` state of the panel.
|
|
* When the state changes, the specified callback function is executed.
|
|
*
|
|
* @event
|
|
* @param {Object} obj - The object containing the state change information.
|
|
* @throws {Error} If an error occurs during the state change handling.
|
|
*/
|
|
on({id: config.panelRecvTopic.substring(0, config.panelRecvTopic.length - 'RESULT'.length) + 'SENSOR'}, async (obj) => {
|
|
try {
|
|
const Tasmota_Sensor = JSON.parse(obj.state.val);
|
|
|
|
await createStateAsync(NSPanel_Path + 'Sensor.Time', {type: 'string', write: false});
|
|
await createStateAsync(NSPanel_Path + 'Sensor.TempUnit', {type: 'string', write: false});
|
|
await createStateAsync(NSPanel_Path + 'Sensor.ANALOG.Temperature', {type: 'number', unit: '°C', write: false});
|
|
await createStateAsync(NSPanel_Path + 'Sensor.ESP32.Temperature', {type: 'number', unit: '°C', write: false});
|
|
let dateTime: string = Tasmota_Sensor.Time.split('T');
|
|
await setStateAsync(NSPanel_Path + 'Sensor.Time', {val: dateTime[0] + '\r\n' + dateTime[1], ack: true});
|
|
await setStateAsync(NSPanel_Path + 'Sensor.TempUnit', {val: '°' + Tasmota_Sensor.TempUnit, ack: true});
|
|
|
|
/* Some messages do not include temperature values, so catch ecxeption for them separately */
|
|
try {
|
|
await setStateAsync(NSPanel_Path + 'Sensor.ANALOG.Temperature', {val: parseFloat(Tasmota_Sensor.ANALOG.Temperature1), ack: true});
|
|
await setStateAsync(NSPanel_Path + 'Sensor.ESP32.Temperature', {val: parseFloat(Tasmota_Sensor.ESP32.Temperature), ack: true});
|
|
} catch (e) {
|
|
/* Nothing to do */
|
|
}
|
|
|
|
if (autoCreateAlias) {
|
|
setObject(AliasPath + 'Sensor.ANALOG.Temperature', {type: 'channel', common: {role: 'info', name: ''}, native: {}});
|
|
setObject(AliasPath + 'Sensor.ESP32.Temperature', {type: 'channel', common: {role: 'info', name: ''}, native: {}});
|
|
setObject(AliasPath + 'Sensor.Time', {type: 'channel', common: {role: 'info', name: ''}, native: {}});
|
|
setObject(AliasPath + 'Sensor.TempUnit', {type: 'channel', common: {role: 'info', name: ''}, native: {}});
|
|
await createAliasAsync(AliasPath + 'Sensor.ANALOG.Temperature.ACTUAL', NSPanel_Path + 'Sensor.ANALOG.Temperature', true, {type: 'number', unit: '°C'});
|
|
await createAliasAsync(AliasPath + 'Sensor.ESP32.Temperature.ACTUAL', NSPanel_Path + 'Sensor.ESP32.Temperature', true, {type: 'number', unit: '°C'});
|
|
await createAliasAsync(AliasPath + 'Sensor.Time.ACTUAL', NSPanel_Path + 'Sensor.Time', true, {type: 'string'});
|
|
await createAliasAsync(AliasPath + 'Sensor.TempUnit.ACTUAL', NSPanel_Path + 'Sensor.TempUnit', true, {type: 'string'});
|
|
}
|
|
} catch (err: any) {
|
|
log('error Trigger reading senor-data: ' + err.message, 'warn');
|
|
}
|
|
});
|
|
|
|
//------------------End Read Internal Sensor Data
|
|
|
|
/**
|
|
* Formats the input text for selection display.
|
|
*
|
|
* This function processes the input text and formats it for display in a selection context.
|
|
*
|
|
* @function formatInSelText
|
|
* @param {string} Text - The input text to format.
|
|
* @returns {string} The formatted text.
|
|
*/
|
|
function formatInSelText (Text: string): string {
|
|
let splitText = Text.split(' ');
|
|
let lengthLineOne = 0;
|
|
let arrayLineOne: string[] = [];
|
|
for (let i = 0; i < splitText.length; i++) {
|
|
lengthLineOne = lengthLineOne + splitText[i].length + 1;
|
|
if (lengthLineOne > 12) {
|
|
break;
|
|
} else {
|
|
arrayLineOne[i] = splitText[i];
|
|
}
|
|
}
|
|
let textLineOne = arrayLineOne.join(' ');
|
|
let arrayLineTwo: string[] = [];
|
|
for (let i = arrayLineOne.length; i < splitText.length; i++) {
|
|
arrayLineTwo[i] = splitText[i];
|
|
}
|
|
let textLineTwo = arrayLineTwo.join(' ');
|
|
if (textLineTwo.length > 12) {
|
|
textLineTwo = textLineTwo.substring(0, 9) + '...';
|
|
}
|
|
if (textLineOne.length != 0) {
|
|
return textLineOne + '\r\n' + textLineTwo.trim();
|
|
} else {
|
|
return textLineTwo.trim();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Interpolates between two RGB colors based on the given fraction.
|
|
*
|
|
* This function calculates and returns an interpolated color between two RGB colors based on the specified fraction.
|
|
*
|
|
* @function Interpolate
|
|
* @param {RGB} color1 - The first RGB color.
|
|
* @param {RGB} color2 - The second RGB color.
|
|
* @param {number} fraction - The fraction to determine the interpolation (0.0 to 1.0).
|
|
* @returns {RGB} The interpolated RGB color.
|
|
*/
|
|
function Interpolate (color1: RGB, color2: RGB, fraction: number): RGB {
|
|
let r: number = InterpolateNum(color1.red, color2.red, fraction);
|
|
let g: number = InterpolateNum(color1.green, color2.green, fraction);
|
|
let b: number = InterpolateNum(color1.blue, color2.blue, fraction);
|
|
return {red: Math.round(r), green: Math.round(g), blue: Math.round(b)};
|
|
}
|
|
|
|
/**
|
|
* Interpolates between two numbers based on the given fraction.
|
|
*
|
|
* This function calculates and returns an interpolated value between two numbers based on the specified fraction.
|
|
*
|
|
* @function InterpolateNum
|
|
* @param {number} d1 - The first number.
|
|
* @param {number} d2 - The second number.
|
|
* @param {number} fraction - The fraction to determine the interpolation (0.0 to 1.0).
|
|
* @returns {number} The interpolated value.
|
|
*/
|
|
function InterpolateNum (d1: number, d2: number, fraction: number): number {
|
|
return d1 + (d2 - d1) * fraction;
|
|
}
|
|
|
|
/**
|
|
* Converts an RGB color to a 16-bit 565 color format.
|
|
*
|
|
* This function takes an RGB color object and converts it to a 16-bit 565 color format.
|
|
*
|
|
* @function rgb_dec565
|
|
* @param {RGB} rgb - The RGB color object.
|
|
* @returns {number} The 16-bit 565 color value.
|
|
*/
|
|
function rgb_dec565 (rgb: RGB): number { //return ((Math.floor(rgb.red / 255 * 31) << 11) | (Math.floor(rgb.green / 255 * 63) << 5) | (Math.floor(rgb.blue / 255 * 31)));
|
|
return ((rgb.red >> 3) << 11) | ((rgb.green >> 2) << 5) | (rgb.blue >> 3);
|
|
}
|
|
|
|
function lightenDarkenColor(color: any, amount: number): RGB { // #FFF not supportet rather use #FFFFFF
|
|
const clamp = (val: any) => Math.min(Math.max(val, 0), 0xFF);
|
|
const num: number = parseInt(color.substr(1), 16);
|
|
const cRed: number = clamp((num >> 16) + amount);
|
|
const cGreen: number = clamp(((num >> 8) & 0x00FF) + amount);
|
|
const cBlue: number = clamp((num & 0x0000FF) + amount);
|
|
return {red: cRed, green: cGreen, blue: cBlue};
|
|
}
|
|
|
|
function kelvinToRGB (colorTemperature: number): RGB {
|
|
colorTemperature = colorTemperature / 100;
|
|
let red: number;
|
|
let blue: number;
|
|
let green: number;
|
|
//Calculate Red
|
|
if (colorTemperature <= 66) {
|
|
red = 255
|
|
} else {
|
|
red = colorTemperature - 60
|
|
red = 329.698727466 * Math.pow(red, -0.1332047592)
|
|
if (red < 0) {
|
|
red = 0
|
|
}
|
|
if (red > 255) {
|
|
red = 255
|
|
}
|
|
}
|
|
//Calculate Green
|
|
if (colorTemperature <= 66) {
|
|
green = colorTemperature
|
|
green = 99.4708025861 * Math.log(green) - 161.1195681661
|
|
if (green < 0) {
|
|
green = 0
|
|
}
|
|
if (green > 255) {
|
|
green = 255
|
|
}
|
|
} else {
|
|
green = colorTemperature - 60
|
|
green = 288.1221695283 * Math.pow(green, -0.0755148492)
|
|
if (green < 0) {
|
|
green = 0
|
|
}
|
|
if (green > 255) {
|
|
green = 255
|
|
}
|
|
}
|
|
//Calculate Blue
|
|
if (colorTemperature >= 66) {
|
|
blue = 255
|
|
} else {
|
|
if (colorTemperature <= 19) {
|
|
blue = 0
|
|
} else {
|
|
blue = colorTemperature - 10
|
|
blue = 138.5177312231 * Math.log(blue) - 305.0447927307
|
|
if (blue < 0) {
|
|
blue = 0
|
|
}
|
|
if (blue > 255) {
|
|
blue = 255
|
|
}
|
|
}
|
|
}
|
|
return {red: Math.floor(red), green: Math.floor(green), blue: Math.floor(blue)};
|
|
}
|
|
|
|
/**
|
|
* Convert radians to degrees
|
|
* @param rad radians to convert, expects rad in range +/- PI per Math.atan2
|
|
* @returns {number} degrees equivalent of rad
|
|
*/
|
|
function rad2deg (rad): number {
|
|
return (360 + (180 * rad) / Math.PI) % 360;
|
|
}
|
|
|
|
/**
|
|
* Converts a color value to its hexadecimal string representation.
|
|
*
|
|
* This function takes a color value and converts it to a hexadecimal string.
|
|
*
|
|
* @function ColorToHex
|
|
* @param {number} color - The color value to convert.
|
|
* @returns {string} The hexadecimal string representation of the color.
|
|
*/
|
|
function ColorToHex (color): string {
|
|
let hexadecimal: string = color.toString(16);
|
|
return hexadecimal.length == 1 ? '0' + hexadecimal : hexadecimal;
|
|
}
|
|
|
|
/**
|
|
* Converts RGB color values to a hexadecimal string representation.
|
|
*
|
|
* This function takes red, green, and blue color values and converts them to a hexadecimal string.
|
|
*
|
|
* @function ConvertRGBtoHex
|
|
* @param {number} red - The red color value.
|
|
* @param {number} green - The green color value.
|
|
* @param {number} blue - The blue color value.
|
|
* @returns {string} The hexadecimal string representation of the RGB color.
|
|
*/
|
|
function ConvertRGBtoHex (red: number, green: number, blue: number): string {
|
|
return '#' + ColorToHex(red) + ColorToHex(green) + ColorToHex(blue);
|
|
}
|
|
|
|
/**
|
|
* Convert h,s,v values to r,g,b
|
|
* @param hue in range [0, 360]
|
|
* @param saturation in range 0 to 1
|
|
* @param value in range 0 to 1
|
|
* @returns {[number, number, number]} [r, g,b] in range 0 to 255
|
|
*/
|
|
function hsv2rgb (hue: number, saturation: number, value: number): [number, number, number] {
|
|
hue /= 60;
|
|
let chroma = value * saturation;
|
|
let x = chroma * (1 - Math.abs((hue % 2) - 1));
|
|
let rgb: [number, number, number] = hue <= 1 ? [chroma, x, 0] : hue <= 2 ? [x, chroma, 0] : hue <= 3 ? [0, chroma, x] : hue <= 4 ? [0, x, chroma] : hue <= 5 ? [x, 0, chroma] : [chroma, 0, x];
|
|
|
|
return rgb.map((v) => (v + value - chroma) * 255) as [number, number, number];
|
|
}
|
|
|
|
/**
|
|
* Calculates the hue value from RGB color values.
|
|
*
|
|
* This function takes red, green, and blue color values and calculates the corresponding hue value.
|
|
*
|
|
* @function getHue
|
|
* @param {number} red - The red color value.
|
|
* @param {number} green - The green color value.
|
|
* @param {number} blue - The blue color value.
|
|
* @returns {number} The hue value.
|
|
*/
|
|
function getHue (red: number, green: number, blue: number): number {
|
|
let min = Math.min(Math.min(red, green), blue);
|
|
let max = Math.max(Math.max(red, green), blue);
|
|
|
|
if (min == max) {
|
|
return 0;
|
|
}
|
|
|
|
let hue = 0;
|
|
if (max == red) {
|
|
hue = (green - blue) / (max - min);
|
|
} else if (max == green) {
|
|
hue = 2 + (blue - red) / (max - min);
|
|
} else {
|
|
hue = 4 + (red - green) / (max - min);
|
|
}
|
|
|
|
hue = hue * 60;
|
|
if (hue < 0) hue = hue + 360;
|
|
|
|
return Math.round(hue);
|
|
}
|
|
|
|
/**
|
|
* Converts a position (x, y) to an RGB color.
|
|
*
|
|
* This function takes x and y coordinates, calculates the corresponding hue, saturation, and value (HSV),
|
|
* and converts them to an RGB color.
|
|
*
|
|
* @function pos_to_color
|
|
* @param {number} x - The x-coordinate of the position.
|
|
* @param {number} y - The y-coordinate of the position.
|
|
* @returns {RGB} The RGB color corresponding to the position.
|
|
*/
|
|
function pos_to_color (x: number, y: number): RGB {
|
|
let r = 160 / 2;
|
|
x = Math.round(((x - r) / r) * 100) / 100;
|
|
y = Math.round(((r - y) / r) * 100) / 100;
|
|
|
|
r = Math.sqrt(x * x + y * y);
|
|
let sat = 0;
|
|
if (r > 1) {
|
|
sat = 0;
|
|
} else {
|
|
sat = r;
|
|
}
|
|
|
|
let hsv = rad2deg(Math.atan2(y, x));
|
|
let rgb = hsv2rgb(hsv, sat, 1);
|
|
|
|
return {red: Math.round(rgb[0]), green: Math.round(rgb[1]), blue: Math.round(rgb[2])};
|
|
}
|
|
|
|
|
|
/**
|
|
* Converts RGB color values to CIE 1931 color space coordinates.
|
|
*
|
|
* This function applies gamma correction to the RGB values and converts them to CIE 1931 color space coordinates.
|
|
*
|
|
* @function rgb_to_cie
|
|
* @param {number} red - The red color value.
|
|
* @param {number} green - The green color value.
|
|
* @param {number} blue - The blue color value.
|
|
* @returns {string} The CIE 1931 color space coordinates as a string.
|
|
*/
|
|
function rgb_to_cie (red: number, green: number, blue: number): string {
|
|
// Apply a gamma correction to the RGB values, which makes the color more vivid and more the like the color displayed on the screen of your device
|
|
let vred = red > 0.04045 ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : red / 12.92;
|
|
let vgreen = green > 0.04045 ? Math.pow((green + 0.055) / (1.0 + 0.055), 2.4) : green / 12.92;
|
|
let vblue = blue > 0.04045 ? Math.pow((blue + 0.055) / (1.0 + 0.055), 2.4) : blue / 12.92;
|
|
|
|
// RGB values to XYZ using the Wide RGB D65 conversion formula
|
|
let X = vred * 0.664511 + vgreen * 0.154324 + vblue * 0.162028;
|
|
let Y = vred * 0.283881 + vgreen * 0.668433 + vblue * 0.047685;
|
|
let Z = vred * 0.000088 + vgreen * 0.07231 + vblue * 0.986039;
|
|
|
|
// Calculate the xy values from the XYZ values
|
|
let ciex = (X / (X + Y + Z)).toFixed(4);
|
|
let ciey = (Y / (X + Y + Z)).toFixed(4);
|
|
let cie = '[' + ciex + ',' + ciey + ']';
|
|
|
|
return cie;
|
|
}
|
|
|
|
function perc2color(percent: number) {
|
|
let red: number = 0;
|
|
let green: number = 0;
|
|
let blue: number = 0;
|
|
if(percent < 50) {
|
|
red = 255;
|
|
green = Math.round(5.1 * percent);
|
|
}
|
|
else {
|
|
green = 255;
|
|
red = Math.round(510 - 5.10 * percent);
|
|
}
|
|
var h = red * 0x10000 + green * 0x100 + blue * 0x1;
|
|
return '#' + ('000000' + h.toString(16)).slice(-6);
|
|
}
|
|
|
|
function cie_to_rgb(x: number, y: number, brightness: number): RGB {
|
|
//Set to maximum brightness if no custom value was given (Not the slick ECMAScript 6 way for compatibility reasons)
|
|
if (brightness === undefined) {
|
|
brightness = 254;
|
|
}
|
|
|
|
let z: number = 1.0 - x - y;
|
|
let Y: number = parseFloat((brightness / 254).toFixed(2));
|
|
let X: number = (Y / y) * x;
|
|
let Z: number = (Y / y) * z;
|
|
|
|
//Convert to RGB using Wide RGB D65 conversion
|
|
let red: number = X * 1.656492 - Y * 0.354851 - Z * 0.255038;
|
|
let green: number = -X * 0.707196 + Y * 1.655397 + Z * 0.036152;
|
|
let blue: number = X * 0.051713 - Y * 0.121364 + Z * 1.011530;
|
|
|
|
//If red, green or blue is larger than 1.0 set it back to the maximum of 1.0
|
|
if (red > blue && red > green && red > 1.0) {
|
|
green = green / red;
|
|
blue = blue / red;
|
|
red = 1.0;
|
|
}
|
|
else if (green > blue && green > red && green > 1.0) {
|
|
red = red / green;
|
|
blue = blue / green;
|
|
green = 1.0;
|
|
}
|
|
else if (blue > red && blue > green && blue > 1.0) {
|
|
red = red / blue;
|
|
green = green / blue;
|
|
blue = 1.0;
|
|
}
|
|
|
|
//Reverse gamma correction
|
|
red = red <= 0.0031308 ? 12.92 * red : (1.0 + 0.055) * Math.pow(red, (1.0 / 2.4)) - 0.055;
|
|
green = green <= 0.0031308 ? 12.92 * green : (1.0 + 0.055) * Math.pow(green, (1.0 / 2.4)) - 0.055;
|
|
blue = blue <= 0.0031308 ? 12.92 * blue : (1.0 + 0.055) * Math.pow(blue, (1.0 / 2.4)) - 0.055;
|
|
|
|
//Convert normalized decimal to decimal
|
|
red = Math.round(red * 255);
|
|
green = Math.round(green * 255);
|
|
blue = Math.round(blue * 255);
|
|
|
|
if (isNaN(red))
|
|
red = 0;
|
|
|
|
if (isNaN(green))
|
|
green = 0;
|
|
|
|
if (isNaN(blue))
|
|
blue = 0;
|
|
|
|
return {red: red, green: green, blue: blue};
|
|
}
|
|
|
|
/**
|
|
* Determines the icon ID for a given page item based on the provided value thresholds.
|
|
* If the icon3 property is missing or invalid, it immediately returns the fallback icon ID.
|
|
* Otherwise, it delegates to determineStatusIcon, passing the relevant parameters.
|
|
*
|
|
* @param {PageItem} pageItem - Contains icon references (icon, icon2, icon3) and threshold values.
|
|
* @param {number} val - The current value used to evaluate thresholds.
|
|
* @param {string} iconId - The fallback icon ID if no matching icon is found.
|
|
* @param {[string, string, string]} [def] - Optional array of default icons corresponding to threshold levels. [low, high, between]
|
|
* @returns {string} The icon ID determined by value, thresholds, and defaults.
|
|
*/
|
|
function determinePageItemStatusIcon (pageItem: PageItem, val: number, iconId: string, def?: [string, string, string]): string {
|
|
if (!pageItem.icon3 || typeof pageItem.icon3 !== 'string') {
|
|
return iconId;
|
|
}
|
|
const max = pageItem.maxValueLevel ?? pageItem.maxValue ?? 100;
|
|
const min = pageItem.minValueLevel ?? pageItem.minValue ?? 0;
|
|
return determineStatusIcon(pageItem.icon, pageItem.icon2, pageItem.icon3, val, min, max, iconId, def);
|
|
}
|
|
|
|
/**
|
|
* Determines an icon for the screensaver based on the provided value and icon configuration.
|
|
*
|
|
* @param {NSPanel.ScreenSaverElement} ss - Object containing the ScreensaverEntityIconSelect array.
|
|
* @param {number} val - The current value used to determine which icon to select.
|
|
* @param {string} iconId - A fallback icon ID if no suitable icon can be retrieved.
|
|
* @returns {string} The icon ID that matches the value thresholds, or the fallback icon ID.
|
|
*/
|
|
function determineScreensaverStatusIcon(ss: NSPanel.ScreenSaverElement, val: number, iconId: string): string {
|
|
if (!ss) {
|
|
return iconId;
|
|
}
|
|
if (!ss.ScreensaverEntityIconSelect || !Array.isArray(ss.ScreensaverEntityIconSelect)) {
|
|
return iconId;
|
|
}
|
|
ss.ScreensaverEntityIconSelect = ss.ScreensaverEntityIconSelect.filter((item) => item);
|
|
ss.ScreensaverEntityIconSelect = ss.ScreensaverEntityIconSelect.sort((a, b) => a.value - b.value);
|
|
for (const item of ss.ScreensaverEntityIconSelect) {
|
|
if (val <= item.value) {
|
|
return Icons.GetIcon(item.icon) || iconId;
|
|
}
|
|
}
|
|
return Icons.GetIcon(ss.ScreensaverEntityIconSelect[ss.ScreensaverEntityIconSelect.length - 1].icon) || iconId;
|
|
}
|
|
|
|
/**
|
|
* Determines which icon to use based on the given value range (min and max) and available icon references.
|
|
*
|
|
* @param {string | undefined} icon1 - The icon reference for the lower threshold condition.
|
|
* @param {string | undefined} icon2 - The icon reference for the higher threshold condition.
|
|
* @param {string | undefined} icon3 - The icon reference for the midrange condition.
|
|
* @param {number} val - The current value to evaluate.
|
|
* @param {number | undefined} min - The minimum threshold.
|
|
* @param {number | undefined} max - The maximum threshold.
|
|
* @param {string} iconId - The fallback icon identifier.
|
|
* @param {[string, string, string]} [def] - Optional array of default icons for each threshold level.
|
|
*
|
|
* @returns {string} The icon identifier determined by the value, thresholds, and defaults.
|
|
*/
|
|
function determineStatusIcon (
|
|
icon1: string|undefined,
|
|
icon2: string|undefined,
|
|
icon3: string|undefined,
|
|
val: number,
|
|
min: number|undefined,
|
|
max: number|undefined,
|
|
iconId: string,
|
|
def?: [string, string, string]
|
|
): string {
|
|
if (!icon3 && typeof icon3 !== 'string') {
|
|
return iconId;
|
|
}
|
|
if (min === undefined || max === undefined) {
|
|
return iconId;
|
|
}
|
|
|
|
function pickIcon(iconKey?: string, defIndex?: number): string {
|
|
return (
|
|
(iconKey && existsState(iconKey) && Icons.GetIcon(getState(iconKey).val)) ||
|
|
Icons.GetIcon(iconKey) ||
|
|
(def && defIndex !== undefined ? Icons.GetIcon(def[defIndex]) : undefined) ||
|
|
iconId
|
|
);
|
|
}
|
|
|
|
if ((min > max && val >= min) || (min <= max && val <= min)) {
|
|
iconId = pickIcon(icon1, 0);
|
|
} else if ((min > max && val <= max) || (min <= max && val >= max)) {
|
|
iconId = pickIcon(icon2, 1);
|
|
} else {
|
|
// The original code used the entire def array for icon3; here we assume the third index
|
|
iconId = pickIcon(icon3, 2);
|
|
}
|
|
|
|
return iconId;
|
|
}
|
|
|
|
|
|
/**
|
|
* Retrieves the Spotify device ID for a given device name.
|
|
*
|
|
* This function takes a device name string, searches the available Spotify devices, and returns the corresponding device ID.
|
|
*
|
|
* @function spotifyGetDeviceID
|
|
* @param {string} vDeviceString - The name of the Spotify device.
|
|
* @returns {string} The ID of the Spotify device.
|
|
*/
|
|
function spotifyGetDeviceID (vDeviceString: string): string {
|
|
const availableDeviceIDs: string = getState('spotify-premium.0.devices.availableDeviceListIds').val;
|
|
const availableDeviceNames: string = getState('spotify-premium.0.devices.availableDeviceListString').val;
|
|
let arrayDeviceListIds: string[] = availableDeviceIDs.split(';');
|
|
let arrayDeviceListSting: string[] = availableDeviceNames.split(';');
|
|
let indexPos: number = arrayDeviceListSting.indexOf(vDeviceString);
|
|
let strDevID = arrayDeviceListIds[indexPos];
|
|
return strDevID;
|
|
}
|
|
|
|
/**
|
|
* Join arguments with ~ and return the string;
|
|
* @param tokens unlimited numbers of strings
|
|
* @returns
|
|
*/
|
|
function buildNSPanelString (...tokens: (string | number)[]): string {
|
|
return tokens.join('~');
|
|
}
|
|
|
|
type RGB = NSPanel.RGB;
|
|
type PageItem = NSPanel.PageItem;
|
|
type PageType = NSPanel.PageType;
|
|
type Config = NSPanel.Config;
|
|
type PageEntities = NSPanel.PageEntities;
|
|
type PageSchedule = NSPanel.PageSchedule;
|
|
type PageChart = NSPanel.PageChart;
|
|
type PagePower = NSPanel.PagePower;
|
|
type PageGrid = NSPanel.PageGrid;
|
|
type PageGrid2 = NSPanel.PageGrid2;
|
|
type PageGrid3 = NSPanel.PageGrid3;
|
|
type PageQR = NSPanel.PageQR;
|
|
type PageMedia = NSPanel.PageMedia;
|
|
type PageThermo = NSPanel.PageThermo;
|
|
type PageThermo2 = NSPanel.PageThermo2;
|
|
type PageUnlock = NSPanel.PageUnlock;
|
|
type PageAlarm = NSPanel.PageAlarm;
|
|
|
|
/**
|
|
*
|
|
* @param time object { hour: number, minutes: number } | number: Time as number in ms
|
|
* @param repeatTime in seconds
|
|
* @param callback what todo
|
|
* @returns
|
|
*/
|
|
function adapterSchedule (time: {hour?: number; minute?: number} | undefined | number, repeatTime: number, callback: () => void): number | null {
|
|
if (typeof callback !== 'function') return null;
|
|
const ref = Math.random() + 1;
|
|
(scheduleList[ref] = setTimeout(_schedule, 1, time, ref, repeatTime, callback)), true;
|
|
return ref;
|
|
}
|
|
|
|
/**
|
|
* Schedules a recurring task based on the specified time and repeat interval.
|
|
*
|
|
* This function sets up a recurring task that executes the provided callback function at the specified time and repeats at the given interval.
|
|
*
|
|
* @function _schedule
|
|
* @param {Object | undefined | number} time - The time to schedule the task. Can be an object with hour and minute properties, undefined, or a timestamp.
|
|
* @param {number} ref - The reference ID for the scheduled task.
|
|
* @param {number} repeatTime - The repeat interval in seconds.
|
|
* @param {Function} callback - The callback function to execute.
|
|
* @param {boolean} [init=false] - Whether to initialize the task immediately.
|
|
*/
|
|
function _schedule (time: {hour?: number; minute?: number} | undefined | number, ref: number, repeatTime: number, callback, init: boolean = false) {
|
|
if (!scheduleList[ref]) return;
|
|
if (!init) callback();
|
|
let targetTime: number;
|
|
if (time === undefined) {
|
|
targetTime = new Date().setMilliseconds(0) + repeatTime * 1000;
|
|
time = targetTime;
|
|
} else if (typeof time === 'number') {
|
|
targetTime = time + repeatTime * 1000;
|
|
time = targetTime;
|
|
} else {
|
|
time.hour = time.hour !== undefined ? time.hour : -1;
|
|
time.minute = time.minute !== undefined ? time.minute : 0;
|
|
targetTime = time.hour !== -1 ? new Date().setHours(time.hour, time.minute, 0, 0) : new Date().setMinutes(time.minute, 0, 0);
|
|
if (new Date().getTime() >= targetTime) {
|
|
targetTime += repeatTime * 1000;
|
|
targetTime = time.hour !== -1 ? new Date(targetTime).setHours(time.hour, time.minute, 0, 0) : new Date(targetTime).setMinutes(time.minute, 0, 0);
|
|
}
|
|
}
|
|
const timeout = targetTime - new Date().getTime();
|
|
scheduleList[ref] = setTimeout(_schedule, timeout, time, ref, repeatTime, callback);
|
|
}
|
|
|
|
/**
|
|
* Clears a scheduled task based on the reference ID.
|
|
*
|
|
* This function cancels the scheduled task associated with the provided reference ID and removes it from the schedule list.
|
|
*
|
|
* @function _clearSchedule
|
|
* @param {number} ref - The reference ID of the scheduled task to clear.
|
|
* @returns {null} Returns null after clearing the scheduled task.
|
|
*/
|
|
function _clearSchedule (ref: number): null {
|
|
if (scheduleList[ref]) clearTimeout(scheduleList[ref]);
|
|
delete scheduleList[ref];
|
|
return null;
|
|
}
|
|
|
|
const ArrayPlayerTypeWithMediaDevice = ['alexa2', 'sonos', 'squeezeboxrpc'] as const;
|
|
const ArrayPlayerTypeWithOutMediaDevice = ['spotify-premium', 'volumio', 'bosesoundtouch', 'mpd'] as const;
|
|
|
|
/**
|
|
* Checks if the given player type is a player with a media device.
|
|
*
|
|
* This function determines if the provided player type is included in the list of player types with media devices.
|
|
*
|
|
* @function isPlayerWithMediaDevice
|
|
* @param {string | NSPanel._PlayerTypeWithMediaDevice} F - The player type to check.
|
|
* @returns {boolean} True if the player type is a player with a media device, false otherwise.
|
|
*/
|
|
function isPlayerWithMediaDevice (F: string | NSPanel._PlayerTypeWithMediaDevice): F is NSPanel._PlayerTypeWithMediaDevice {
|
|
return ArrayPlayerTypeWithMediaDevice.indexOf(F as NSPanel._PlayerTypeWithMediaDevice) != -1;
|
|
}
|
|
|
|
/** check if NSPanel.adapterPlayerInstanceType has all Playertypes */
|
|
function checkSortedPlayerType (F: NSPanel.notSortedPlayerType) {
|
|
const test: NSPanel.adapterPlayerInstanceType = F;
|
|
}
|
|
|
|
/**
|
|
* Checks if the given string is a valid media optional type.
|
|
*
|
|
* This function determines if the provided string is included in the list of valid media optional types.
|
|
*
|
|
* @function isMediaOptional
|
|
* @param {string | NSPanel.mediaOptional} F - The string to check.
|
|
* @returns {boolean} True if the string is a valid media optional type, false otherwise.
|
|
*/
|
|
function isMediaOptional (F: string | NSPanel.mediaOptional): F is NSPanel.mediaOptional {
|
|
switch (F as NSPanel.mediaOptional) {
|
|
case 'seek':
|
|
case 'crossfade':
|
|
case 'speakerlist':
|
|
case 'playlist':
|
|
case 'tracklist':
|
|
case 'equalizer':
|
|
case 'repeat':
|
|
case 'favorites':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if the given string is a valid event method.
|
|
*
|
|
* This function determines if the provided string is included in the list of valid event methods.
|
|
* If the event method is unknown, it logs a warning message.
|
|
*
|
|
* @function isEventMethod
|
|
* @param {string | NSPanel.EventMethod} F - The string to check.
|
|
* @returns {boolean} True if the string is a valid event method, false otherwise.
|
|
*/
|
|
function isEventMethod (F: string | NSPanel.EventMethod): F is NSPanel.EventMethod {
|
|
switch (F as NSPanel.EventMethod) {
|
|
case 'startup':
|
|
case 'sleepReached':
|
|
case 'pageOpenDetail':
|
|
case 'buttonPress2':
|
|
case 'renderCurrentPage':
|
|
case 'button1':
|
|
case 'button2':
|
|
return true;
|
|
default:
|
|
// Have to talk about this.
|
|
log(`Please report to developer: Unknown NSPanel.EventMethod: ${F} `, 'warn');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if the given string is a valid popup type.
|
|
*
|
|
* This function determines if the provided string is included in the list of valid popup types.
|
|
* If the popup type is unknown, it logs a warning message.
|
|
*
|
|
* @function isPopupType
|
|
* @param {NSPanel.PopupType | string} F - The string to check.
|
|
* @returns {boolean} True if the string is a valid popup type, false otherwise.
|
|
*/
|
|
function isPopupType (F: NSPanel.PopupType | string): F is NSPanel.PopupType {
|
|
switch (F as NSPanel.PopupType) {
|
|
case 'popupFan':
|
|
case 'popupInSel':
|
|
case 'popupLight':
|
|
case 'popupNotify':
|
|
case 'popupShutter':
|
|
case 'popupShutter2':
|
|
case 'popupThermo':
|
|
case 'popupTimer':
|
|
case 'popupSlider':
|
|
case 'popupColor':
|
|
return true;
|
|
default:
|
|
log(`Please report to developer: Unknown NSPanel.PopupType: ${F} `, 'warn');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// If u get a error here u forgot something in PagetypeType or PageType
|
|
function checkPageType (F: NSPanel.PagetypeType, A: NSPanel.PageType) {
|
|
A.type = F;
|
|
}
|
|
|
|
function isPageMediaItem (F: NSPanel.PageItem | NSPanel.PageMediaItem): F is NSPanel.PageMediaItem {
|
|
return 'adapterPlayerInstance' in F;
|
|
}
|
|
|
|
function isPageThermoItem (F: PageItem | NSPanel.PageThermoItem): F is NSPanel.PageThermoItem {
|
|
return 'popupThermoMode1' in F;
|
|
}
|
|
|
|
function isPageMedia (F: NSPanel.PageType | NSPanel.PageMedia): F is NSPanel.PageMedia {
|
|
return F.type == 'cardMedia';
|
|
}
|
|
|
|
function isPagePower (F: NSPanel.PageType | NSPanel.PagePower): F is NSPanel.PagePower {
|
|
return F.type == 'cardPower';
|
|
}
|
|
|
|
namespace NSPanel {
|
|
export type PopupType = 'popupFan' | 'popupInSel' | 'popupLight' | 'popupNotify' | 'popupShutter' | 'popupShutter2' | 'popupSlider' | 'popupThermo' | 'popupTimer' | 'popupColor';
|
|
|
|
export type EventMethod = 'startup' | 'sleepReached' | 'pageOpenDetail' | 'buttonPress2' | 'renderCurrentPage' | 'button1' | 'button2';
|
|
export type panelRecvType = {
|
|
event: 'event';
|
|
method: EventMethod;
|
|
};
|
|
|
|
export type SerialType = 'button' | 'light' | 'light2' | 'shutter' | 'shutter2' | 'text' | 'input_sel' | 'timer' | 'number' | 'fan' | 'slider';
|
|
|
|
/**
|
|
* Defines the possible roles for entities in the NSPanel.
|
|
*
|
|
* This type represents the various roles that entities can have within the NSPanel system.
|
|
*
|
|
* @typedef {string} roles
|
|
* @enum {string}
|
|
*/
|
|
export type roles = | 'light'
|
|
| 'socket'
|
|
| 'dimmer'
|
|
| 'hue'
|
|
| 'rgb'
|
|
| 'rgbSingle'
|
|
| 'ct'
|
|
| 'blind'
|
|
| 'door'
|
|
| 'window'
|
|
| 'volumeGroup'
|
|
| 'volume'
|
|
| 'info'
|
|
| 'humidity'
|
|
| 'temperature'
|
|
| 'value.temperature'
|
|
| 'value.humidity'
|
|
| 'sensor.door'
|
|
| 'sensor.window'
|
|
| 'thermostat'
|
|
| 'warning'
|
|
| 'cie'
|
|
| 'gate'
|
|
| 'motion'
|
|
| 'buttonSensor'
|
|
| 'button'
|
|
| 'value.time'
|
|
| 'level.timer'
|
|
| 'value.alarmtime'
|
|
| 'level.mode.fan'
|
|
| 'lock'
|
|
| 'slider'
|
|
| 'switch.mode.wlan'
|
|
| 'media'
|
|
| 'timeTable'
|
|
| 'airCondition'
|
|
| 'illuminance';
|
|
|
|
export type ButtonActionType =
|
|
| 'bExit'
|
|
| 'bUp'
|
|
| 'bNext'
|
|
| 'bSubNext'
|
|
| 'bPrev'
|
|
| 'bSubPrev'
|
|
| 'bHome'
|
|
| 'notifyAction'
|
|
| 'OnOff'
|
|
| 'button'
|
|
| 'up'
|
|
| 'stop'
|
|
| 'down'
|
|
| 'button1Press'
|
|
| 'button2Press'
|
|
| 'button3Press'
|
|
| 'positionSlider'
|
|
| 'positionSlider1'
|
|
| 'positionSlider2'
|
|
| 'positionSlider3'
|
|
| 'tiltOpen'
|
|
| 'tiltStop'
|
|
| 'tiltSlider'
|
|
| 'tiltClose'
|
|
| 'brightnessSlider'
|
|
| 'colorTempSlider'
|
|
| 'colorWheel'
|
|
| 'tempUpd'
|
|
| 'tempUpdHighLow'
|
|
| 'media-back'
|
|
| 'media-pause'
|
|
| 'media-next'
|
|
| 'media-shuffle'
|
|
| 'volumeSlider'
|
|
| 'mode-speakerlist'
|
|
| 'mode-playlist'
|
|
| 'mode-tracklist'
|
|
| 'mode-repeat'
|
|
| 'mode-equalizer'
|
|
| 'mode-seek'
|
|
| 'mode-crossfade'
|
|
| 'mode-favorites'
|
|
| 'mode-insel'
|
|
| 'media-OnOff'
|
|
| 'timer-start'
|
|
| 'timer-pause'
|
|
| 'timer-cancle'
|
|
| 'timer-finish'
|
|
| 'hvac_action'
|
|
| 'mode-modus1'
|
|
| 'mode-modus2'
|
|
| 'mode-modus3'
|
|
| 'number-set'
|
|
| 'mode-preset_modes'
|
|
| 'A1'
|
|
| 'A2'
|
|
| 'A3'
|
|
| 'A4'
|
|
| 'D1'
|
|
| 'U1'
|
|
| 'f1Icon'
|
|
| 'f2Icon'
|
|
| 'f3Icon'
|
|
| 'f4Icon'
|
|
| 'f5Icon';
|
|
|
|
export type RGB = {
|
|
red: number;
|
|
green: number;
|
|
blue: number;
|
|
};
|
|
|
|
export type Payload = {
|
|
payload: string;
|
|
};
|
|
|
|
export type PageBaseType = {
|
|
type: PagetypeType;
|
|
uniqueName?: string;
|
|
heading: string;
|
|
items: PageItem[];
|
|
useColor: boolean;
|
|
subPage?: boolean;
|
|
parent?: PageType;
|
|
parentIcon?: string;
|
|
parentIconColor?: RGB;
|
|
prev?: string;
|
|
prevIcon?: string;
|
|
prevIconColor?: RGB;
|
|
next?: string;
|
|
nextIcon?: string;
|
|
nextIconColor?: RGB;
|
|
home?: string;
|
|
homeIcon?: string;
|
|
homeIconColor?: RGB;
|
|
hiddenByTrigger?: boolean;
|
|
thermoItems?: any;
|
|
alwaysOnDisplay?: boolean;
|
|
};
|
|
|
|
export type PagetypeType = 'cardChart' | 'cardLChart' | 'cardEntities' | 'cardSchedule' | 'cardGrid' | 'cardGrid2' | 'cardGrid3' | 'cardThermo' | 'cardThermo2' | 'cardMedia' | 'cardUnlock' | 'cardQR' | 'cardAlarm' | 'cardPower'; //| 'cardBurnRec'
|
|
|
|
export type PageType = PageChart | PageEntities | PageSchedule | PageGrid | PageGrid2 | PageGrid3 | PageThermo | PageThermo2 | PageMedia | PageUnlock | PageQR | PageAlarm | PagePower;
|
|
|
|
export type PageEntities = {
|
|
type: 'cardEntities';
|
|
items: [PageItem?, PageItem?, PageItem?, PageItem?, PageItem?];
|
|
} & PageBaseType;
|
|
|
|
export type PageSchedule = {
|
|
type: 'cardSchedule';
|
|
items: [PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?];
|
|
} & PageBaseType;
|
|
|
|
export type PageGrid = {
|
|
type: 'cardGrid';
|
|
items: [PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?];
|
|
} & PageBaseType;
|
|
|
|
export type PageGrid2 = {
|
|
type: 'cardGrid2';
|
|
items: [PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?];
|
|
} & PageBaseType;
|
|
|
|
export type PageGrid3 = {
|
|
type: 'cardGrid3';
|
|
items: [PageItem?, PageItem?, PageItem?, PageItem?];
|
|
} & PageBaseType;
|
|
|
|
export type PageThermo = {
|
|
type: 'cardThermo';
|
|
items: [PageThermoItem];
|
|
} & Omit<PageBaseType, 'useColor'>;
|
|
|
|
export type PageThermo2 = {
|
|
type: 'cardThermo2';
|
|
items: [PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?];
|
|
} & Omit<PageBaseType, 'useColor'>;
|
|
|
|
export type PageMedia = {
|
|
type: 'cardMedia';
|
|
items: [PageMediaItem];
|
|
} & Omit<PageBaseType, 'useColor' | 'autoCreateAlias'>;
|
|
|
|
export type PageAlarm = {
|
|
type: 'cardAlarm';
|
|
items: [PageItem];
|
|
} & Omit<PageBaseType, 'useColor'>;
|
|
|
|
export type PageUnlock = {
|
|
type: 'cardUnlock';
|
|
items: [PageItem];
|
|
} & Omit<PageBaseType, 'useColor'> &
|
|
Partial<Pick<PageBaseType, 'useColor'>>;
|
|
|
|
export type PageQR = {
|
|
type: 'cardQR';
|
|
items: [PageItem];
|
|
} & Omit<PageBaseType, 'useColor'>;
|
|
|
|
export type PagePower = {
|
|
type: 'cardPower';
|
|
items: [PageItem];
|
|
} & Omit<PageBaseType, 'useColor'>;
|
|
|
|
export type PageChart = {
|
|
type: 'cardChart' | 'cardLChart';
|
|
items: PageItem[];
|
|
} & Omit<PageBaseType, 'useColor'>;
|
|
|
|
export type PageItem = PageBaseItem | PageMediaItem | PageThermoItem;
|
|
|
|
export type PageMediaItem = {
|
|
adapterPlayerInstance: adapterPlayerInstanceType;
|
|
mediaDevice?: string;
|
|
playerMediaIcon?: string;
|
|
colorMediaIcon?: RGB;
|
|
colorMediaArtist?: RGB;
|
|
colorMediaTitle?: RGB;
|
|
speakerList?: string[];
|
|
playList?: string[];
|
|
equalizerList?: string[];
|
|
equalizerSlider?: any[];
|
|
repeatList?: string[];
|
|
globalTracklist?: string[];
|
|
crossfade?: boolean;
|
|
} & PageBaseItem;
|
|
|
|
export type PageThermoItem =
|
|
| ({
|
|
popupThermoMode1?: string[];
|
|
popupThermoMode2?: string[];
|
|
popupThermoMode3?: string[];
|
|
popUpThermoName?: string[];
|
|
setThermoAlias?: string[];
|
|
setThermoDestTemp2?: string;
|
|
} & PageBaseItem)
|
|
| ({
|
|
popupThermoMode1?: string[];
|
|
popupThermoMode2?: string[];
|
|
popupThermoMode3?: string[];
|
|
popUpThermoName?: string[];
|
|
setThermoAlias?: string[];
|
|
setThermoDestTemp2?: string;
|
|
} & PageBaseItem);
|
|
// mean string start with getState(' and end with ').val
|
|
type getStateID = string;
|
|
export type PageBaseItem = {
|
|
uniqueName?: string;
|
|
role?: string;
|
|
/**
|
|
* The data point with the data to be used.
|
|
*/
|
|
id?: string | null;
|
|
/**
|
|
* The icon that is used in the standard case or if ID is true
|
|
*/
|
|
icon?: string;
|
|
/**
|
|
* The icon that is used when id is false
|
|
*/
|
|
icon2?: string;
|
|
/**
|
|
* Used with blinds for partially open.
|
|
*/
|
|
icon3?: string
|
|
/**
|
|
* The color that is used in the standard case or if ID is true
|
|
*/
|
|
onColor?: RGB;
|
|
/**
|
|
* The color that is used when id is false
|
|
*/
|
|
offColor?: RGB;
|
|
/**
|
|
*
|
|
*/
|
|
useColor?: boolean;
|
|
/**
|
|
* Interpolate the icon colour by ID
|
|
*/
|
|
interpolateColor?: boolean;
|
|
minValueBrightness?: number;
|
|
maxValueBrightness?: number;
|
|
minValueColorTemp?: number;
|
|
maxValueColorTemp?: number;
|
|
minValueLevel?: number;
|
|
maxValueLevel?: number;
|
|
minValueTilt?: number;
|
|
maxValueTilt?: number;
|
|
minValue?: number;
|
|
maxValue?: number;
|
|
stepValue?: number;
|
|
prefixName?: string;
|
|
suffixName?: string;
|
|
name?: string | getStateID;
|
|
secondRow?: string;
|
|
buttonText?: string;
|
|
unit?: string;
|
|
navigate?: boolean;
|
|
colormode?: string;
|
|
colorScale?: IconScaleElement;
|
|
targetPage?: string;
|
|
modeList?: string[];
|
|
hidePassword?: boolean;
|
|
hideEntity2?: boolean;
|
|
autoCreateALias?: boolean;
|
|
yAxis?: string;
|
|
yAxisTicks?: number[] | string;
|
|
xAxisDecorationId?: string;
|
|
useValue?: boolean;
|
|
monobutton?: boolean;
|
|
inSel_ChoiceState?: boolean;
|
|
iconArray?: string[];
|
|
customIcons?: any[];
|
|
shutterIcons?: [ shutterIcons?, shutterIcons?, shutterIcons?] | null;
|
|
fontSize?: number;
|
|
actionStringArray?: string[];
|
|
alwaysOnDisplay?: boolean;
|
|
popupVersion?: number;
|
|
shutterType?: string;
|
|
shutterZeroIsClosed?: boolean;
|
|
sliderItems?: [sliderItems?, sliderItems?, sliderItems?] | null;
|
|
};
|
|
|
|
type sliderItems = {
|
|
heading: string;
|
|
icon1?: string;
|
|
icon2?: string;
|
|
minValue?: number;
|
|
maxValue?: number;
|
|
stepValue?: number;
|
|
zeroValue?: boolean;
|
|
id?: string; // writeable overwrite actual and set
|
|
};
|
|
|
|
type shutterIcons = {
|
|
id: string;
|
|
icon: string;
|
|
icon2?: string;
|
|
iconOnColor?: RGB;
|
|
iconOffColor?: RGB;
|
|
buttonType: string;
|
|
};
|
|
|
|
export type DimMode = {
|
|
dimmodeOn: boolean | undefined;
|
|
brightnessDay: number | undefined;
|
|
brightnessNight: number | undefined;
|
|
timeDay: string | undefined;
|
|
timeNight: string | undefined;
|
|
};
|
|
|
|
export type ConfigButtonFunction = {
|
|
mode: 'page' | 'toggle' | 'set' | null;
|
|
page: PageThermo | PageThermo2 | PageMedia | PageAlarm | PageQR | PageEntities | PageSchedule | PageGrid | PageGrid2 | PageGrid3 | PagePower | PageChart | PageUnlock | null;
|
|
entity: string | null;
|
|
setValue: string | number | boolean | null;
|
|
setOn?: {dp: string; val: any};
|
|
setOff?: {dp: string; val: any};
|
|
};
|
|
|
|
export type Config = {
|
|
panelRecvTopic: string;
|
|
panelSendTopic: string;
|
|
weatherEntity: string;
|
|
leftScreensaverEntity: leftScreensaverEntityType;
|
|
bottomScreensaverEntity: ScreenSaverElement[];
|
|
indicatorScreensaverEntity: indicatorScreensaverEntityType;
|
|
mrIcon1ScreensaverEntity: ScreenSaverMRElement;
|
|
mrIcon2ScreensaverEntity: ScreenSaverMRElement;
|
|
defaultColor: RGB;
|
|
defaultOnColor: RGB;
|
|
defaultOffColor: RGB;
|
|
defaultBackgroundColor: RGB;
|
|
pages: PageType[];
|
|
subPages: PageType[];
|
|
button1: ConfigButtonFunction;
|
|
button2: ConfigButtonFunction;
|
|
};
|
|
export type leftScreensaverEntityType = [ScreenSaverElementWithUndefined?, ScreenSaverElementWithUndefined?, ScreenSaverElementWithUndefined?] | [];
|
|
export type indicatorScreensaverEntityType =
|
|
| [ScreenSaverElementWithUndefined?, ScreenSaverElementWithUndefined?, ScreenSaverElementWithUndefined?, ScreenSaverElementWithUndefined?, ScreenSaverElementWithUndefined?]
|
|
| [];
|
|
export type ScreenSaverElementWithUndefined = null | undefined | ScreenSaverElement;
|
|
export type ScreenSaverElement = {
|
|
ScreensaverEntity: string;
|
|
ScreensaverEntityText: string;
|
|
/**
|
|
* Value wird mit diesem Factor multipliziert.
|
|
*/
|
|
ScreensaverEntityFactor?: number;
|
|
ScreensaverEntityDecimalPlaces?: number;
|
|
ScreensaverEntityDateFormat?: Intl.DateTimeFormatOptions;
|
|
ScreensaverEntityIconOn?: string | null;
|
|
ScreensaverEntityIconOff?: string | null;
|
|
ScreensaverEntityUnitText?: string;
|
|
ScreensaverEntityIconColor?: RGB | IconScaleElement | string;
|
|
ScreensaverEntityOnColor?: RGB;
|
|
ScreensaverEntityOffColor?: RGB;
|
|
ScreensaverEntityOnText?: string | null;
|
|
ScreensaverEntityOffText?: string | null;
|
|
ScreensaverEntityNaviToPage?: PageType;
|
|
/**
|
|
* To show different icons for different values in the screensaver
|
|
*
|
|
* Value is the threshold for the icon. Lower values are first.
|
|
* Example:
|
|
* [
|
|
{icon: 'sun-thermometer', value:40},
|
|
{icon: 'sun-thermometer-outline', value: 35},
|
|
{icon: 'thermometer-high', value: 30},
|
|
{icon: 'thermometer', value: 25},
|
|
{icon: 'thermometer-low', value: 15},
|
|
{icon: 'snowflake-alert', value: 2},
|
|
{icon: 'snowflake-thermometer', value: -2},
|
|
{icon: 'snowflake', value: -10},
|
|
]
|
|
*/
|
|
ScreensaverEntityIconSelect?: {icon:string, value: number}[] | null;
|
|
};
|
|
|
|
export type ScreenSaverMRElement = {
|
|
ScreensaverEntity: string | null;
|
|
ScreensaverEntityIconOn: string | null;
|
|
ScreensaverEntityIconSelect?: {[key: string]: string} | null | undefined;
|
|
ScreensaverEntityIconOff: string | null;
|
|
ScreensaverEntityValue: string | null;
|
|
ScreensaverEntityValueDecimalPlace: number | null;
|
|
ScreensaverEntityValueUnit: string | null;
|
|
ScreensaverEntityOnColor: RGB;
|
|
ScreensaverEntityOffColor: RGB;
|
|
};
|
|
export type ScreenSaverMRDataElement = {
|
|
ScreensaverEntity: string | number | boolean | null;
|
|
ScreensaverEntityIconOn: string | null;
|
|
ScreensaverEntityIconOff: string | null;
|
|
ScreensaverEntityValue: string | number | boolean | null;
|
|
ScreensaverEntityValueDecimalPlace: number | null;
|
|
ScreensaverEntityValueUnit: string | null;
|
|
ScreensaverEntityOnColor: RGB;
|
|
ScreensaverEntityOffColor: RGB;
|
|
ScreensaverEntityIconSelect: {[key: string]: string} | null;
|
|
};
|
|
|
|
export type IconScaleElement = {
|
|
val_min: number;
|
|
val_max: number;
|
|
val_best?: number;
|
|
};
|
|
/** we need this to have a nice order when using switch() */
|
|
export type adapterPlayerInstanceType =
|
|
| 'alexa2.0.'
|
|
| 'alexa2.1.'
|
|
| 'alexa2.2.'
|
|
| 'alexa2.3.'
|
|
| 'alexa2.4.'
|
|
| 'alexa2.5.'
|
|
| 'alexa2.6.'
|
|
| 'alexa2.7.'
|
|
| 'alexa2.8.'
|
|
| 'alexa2.9.'
|
|
| 'sonos.0.'
|
|
| 'sonos.1.'
|
|
| 'sonos.2.'
|
|
| 'sonos.3.'
|
|
| 'sonos.4.'
|
|
| 'sonos.5.'
|
|
| 'sonos.6.'
|
|
| 'sonos.7.'
|
|
| 'sonos.8.'
|
|
| 'sonos.9.'
|
|
| 'spotify-premium.0.'
|
|
| 'spotify-premium.1.'
|
|
| 'spotify-premium.2.'
|
|
| 'spotify-premium.3.'
|
|
| 'spotify-premium.4.'
|
|
| 'spotify-premium.5.'
|
|
| 'spotify-premium.6.'
|
|
| 'spotify-premium.7.'
|
|
| 'spotify-premium.8.'
|
|
| 'spotify-premium.9.'
|
|
| 'volumio.0.'
|
|
| 'volumio.1.'
|
|
| 'volumio.2.'
|
|
| 'volumio.3.'
|
|
| 'volumio.4.'
|
|
| 'volumio.5.'
|
|
| 'volumio.6.'
|
|
| 'volumio.7.'
|
|
| 'volumio.8.'
|
|
| 'volumio.9.'
|
|
| 'squeezeboxrpc.0.'
|
|
| 'squeezeboxrpc.1.'
|
|
| 'squeezeboxrpc.2.'
|
|
| 'squeezeboxrpc.3.'
|
|
| 'squeezeboxrpc.4.'
|
|
| 'squeezeboxrpc.5.'
|
|
| 'squeezeboxrpc.6.'
|
|
| 'squeezeboxrpc.7.'
|
|
| 'squeezeboxrpc.8.'
|
|
| 'squeezeboxrpc.9.'
|
|
| 'mpd.0.'
|
|
| 'mpd.1.'
|
|
| 'mpd.2.'
|
|
| 'mpd.3.'
|
|
| 'mpd.4.'
|
|
| 'mpd.5.'
|
|
| 'mpd.6.'
|
|
| 'mpd.7.'
|
|
| 'mpd.8.'
|
|
| 'mpd.9.'
|
|
| 'bosesoundtouch.0.'
|
|
| 'bosesoundtouch.1.'
|
|
| 'bosesoundtouch.2.'
|
|
| 'bosesoundtouch.3.'
|
|
| 'bosesoundtouch.4.'
|
|
| 'bosesoundtouch.5.'
|
|
| 'bosesoundtouch.6.'
|
|
| 'bosesoundtouch.7.'
|
|
| 'bosesoundtouch.8.'
|
|
| 'bosesoundtouch.9.';
|
|
|
|
export type PlayerType = _PlayerTypeWithMediaDevice | _PlayerTypeWithOutMediaDevice;
|
|
|
|
export type _PlayerTypeWithOutMediaDevice = (typeof ArrayPlayerTypeWithOutMediaDevice)[number];
|
|
export type _PlayerTypeWithMediaDevice = (typeof ArrayPlayerTypeWithMediaDevice)[number];
|
|
|
|
export type notSortedPlayerType =
|
|
| `${PlayerType}.0.`
|
|
| `${PlayerType}.1.`
|
|
| `${PlayerType}.2.`
|
|
| `${PlayerType}.3.`
|
|
| `${PlayerType}.4.`
|
|
| `${PlayerType}.5.`
|
|
| `${PlayerType}.6.`
|
|
| `${PlayerType}.7.`
|
|
| `${PlayerType}.8.`
|
|
| `${PlayerType}.9.`;
|
|
|
|
export type mediaOptional = 'seek' | 'crossfade' | 'speakerlist' | 'playlist' | 'tracklist' | 'equalizer' | 'repeat' | 'favorites';
|
|
}
|