Compare commits

..

2 Commits

Author SHA1 Message Date
slajob
f6450916f4 Merge 127671047f into 49577ddbb6 2024-01-30 23:15:09 -07:00
slajob
127671047f missing friday entity from example photo 2024-01-10 18:22:05 +01:00
30 changed files with 584 additions and 883 deletions

View File

@@ -58,5 +58,5 @@ _If applicable, add screenshots/pictures to help explain your problem._
_Add any other context about the problem here._
_Please note here in case you are using ioBroker_
### PANEL / FIRMWARE VERSION
### PANEL / FIRMWARE VERION
_Please add the Panel/Firmware Version you are using (EU, US-L or US-P)_

View File

@@ -24,5 +24,5 @@ _A clear and concise description of what the feature should do._
### ADDITIONAL CONTEXT
_Add any other context about the problem here._
### PANEL / FIRMWARE VERSION
### PANEL / FIRMWARE VERION
_Please add the Panel/Firmware Version you are using (EU, US-L or US-P)_

View File

@@ -92,7 +92,7 @@ jobs:
- name: Login to GitHub Container Registry
if: env.BUILD_ARGS != '--test'
uses: docker/login-action@v3.1.0
uses: docker/login-action@v3.0.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -100,7 +100,7 @@ jobs:
- name: Build ${{ matrix.addon }} add-on
if: steps.check.outputs.build_arch == 'true'
uses: home-assistant/builder@2024.03.5
uses: home-assistant/builder@2024.01.0
with:
args: |
${{ env.BUILD_ARGS }} \

View File

@@ -102,28 +102,6 @@
│ crcputs sys0,2
│ crcputs tSend.txt,0
│ //send cmd
│ --- HMI/n2t-out/popupLight.txt
├── +++ HMI/US/landscape/n2t-out/popupLight.txt
│ @@ -453,19 +453,14 @@
│ ucopy strCommand.txt,4,payloadLength-5,0
│ // write instruction to tInstuction (debug output, but used as variable here, ui elements will be disabled by default)
│ spstr strCommand.txt,tInstruction.txt,"~",0
│ spstr strCommand.txt,tTmp.txt,"~",1
│ if(tInstruction.txt=="entityUpdateDetail"&&entn.txt==tTmp.txt)
│ {
│ // change icon
│ - spstr strCommand.txt,tTmp.txt,"~",2
│ - if(tTmp.txt!="")
│ - {
│ - tIcon1.txt=tTmp.txt
│ - }
│ //spstr strCommand.txt,tIcon1.txt,"~",2
│ vis tIcon1,1
│ // change icon color
│ spstr strCommand.txt,tTmp.txt,"~",3
│ covx tTmp.txt,sys0,0,0
│ tIcon1.pco=sys0
│ // get Button State
│ --- HMI/n2t-out/popupNotify.txt
├── +++ HMI/US/landscape/n2t-out/popupNotify.txt
│ @@ -439,18 +439,14 @@
@@ -543,3 +521,22 @@
│ spstr strCommand.txt,tNotifyText.txt,"~",2
│ if(tNotifyHead.txt!=""||tNotifyText.txt!="")
│ {
│ @@ -952,14 +812,18 @@
│ {
│ page cardPower
│ }
│ if(tId.txt=="cardChart")
│ {
│ page cardChart
│ }
│ + if(tId.txt=="cardLChart")
│ + {
│ + page cardLChart
│ + }
│ }
│ if(tInstruction.txt=="timeout")
│ {
│ //set timeout to global var
│ spstr strCommand.txt,tTmp.txt,"~",1
│ covx tTmp.txt,sleepTimeout,0,0
│ }

View File

@@ -1985,26 +1985,6 @@
│ Variable (string) entn
│ Attributes
│ Scope : local
│ @@ -453,19 +453,14 @@
│ ucopy strCommand.txt,4,payloadLength-5,0
│ // write instruction to tInstuction (debug output, but used as variable here, ui elements will be disabled by default)
│ spstr strCommand.txt,tInstruction.txt,"~",0
│ spstr strCommand.txt,tTmp.txt,"~",1
│ if(tInstruction.txt=="entityUpdateDetail"&&entn.txt==tTmp.txt)
│ {
│ // change icon
│ - spstr strCommand.txt,tTmp.txt,"~",2
│ - if(tTmp.txt!="")
│ - {
│ - tIcon1.txt=tTmp.txt
│ - }
│ //spstr strCommand.txt,tIcon1.txt,"~",2
│ vis tIcon1,1
│ // change icon color
│ spstr strCommand.txt,tTmp.txt,"~",3
│ covx tTmp.txt,sys0,0,0
│ tIcon1.pco=sys0
│ // get Button State
│ --- HMI/n2t-out/popupNotify.txt
├── +++ HMI/US/portrait/n2t-out/popupNotify.txt
│ @@ -348,15 +348,15 @@
@@ -2486,3 +2466,22 @@
│ spstr strCommand.txt,tNotifyText.txt,"~",2
│ if(tNotifyHead.txt!=""||tNotifyText.txt!="")
│ {
│ @@ -952,14 +800,18 @@
│ {
│ page cardPower
│ }
│ if(tId.txt=="cardChart")
│ {
│ page cardChart
│ }
│ + if(tId.txt=="cardLChart")
│ + {
│ + page cardLChart
│ + }
│ }
│ if(tInstruction.txt=="timeout")
│ {
│ //set timeout to global var
│ spstr strCommand.txt,tTmp.txt,"~",1
│ covx tTmp.txt,sleepTimeout,0,0
│ }

View File

@@ -1,4 +1,4 @@
+++ /dev/fd/62 2024-02-25 11:03:09.634837907 +0000
+++ /dev/fd/62 2024-01-20 19:56:08.135834636 +0000
+I/n2t-out/Program.s.txt
++ HMI/US/portrait/n2t-out/Program.s.txt
+1 +12,11 @@
@@ -686,13 +686,6 @@
+ covx tTmp.txt,sys0,0,0
+ hSlider6.maxval=sys0
+ }
+ }
+ if(tInstruction.txt=="pageType")
+ {
+ sleepValue=0
+ //command format pageType,specialPageName
+ //write name of speical page to tId
+ spstr strCommand.txt,tId.txt,"~",1
+I/n2t-out/cardGrid.txt
++ HMI/US/portrait/n2t-out/cardGrid.txt
+ +7,14 @@
@@ -946,6 +939,13 @@
+ spstr strCommand.txt,tEntity9.txt,"~",66
+ vis tEntity9,1
+ }
+ }
+ if(tInstruction.txt=="pageType")
+ {
+ sleepValue=0
+ //command format pageType,specialPageName
+ //write name of speical page to tId
+ spstr strCommand.txt,tId.txt,"~",1
+I/n2t-out/cardLChart.txt
++ HMI/US/portrait/n2t-out/cardLChart.txt
+ +7,14 @@
@@ -1527,26 +1527,6 @@
+e (string) entn
+ributes
+ Scope : local
+19 +453,14 @@
+ ucopy strCommand.txt,4,payloadLength-5,0
+ // write instruction to tInstuction (debug output, but used as variable here, ui elements will be disabled by default)
+ spstr strCommand.txt,tInstruction.txt,"~",0
+ spstr strCommand.txt,tTmp.txt,"~",1
+ if(tInstruction.txt=="entityUpdateDetail"&&entn.txt==tTmp.txt)
+ {
+ // change icon
+ spstr strCommand.txt,tTmp.txt,"~",2
+ if(tTmp.txt!="")
+ {
+ tIcon1.txt=tTmp.txt
+ }
+ //spstr strCommand.txt,tIcon1.txt,"~",2
+ vis tIcon1,1
+ // change icon color
+ spstr strCommand.txt,tTmp.txt,"~",3
+ covx tTmp.txt,sys0,0,0
+ tIcon1.pco=sys0
+ // get Button State
+I/n2t-out/popupNotify.txt
++ HMI/US/portrait/n2t-out/popupNotify.txt
+15 +348,15 @@
@@ -2028,3 +2008,22 @@
+ spstr strCommand.txt,tNotifyText.txt,"~",2
+ if(tNotifyHead.txt!=""||tNotifyText.txt!="")
+ {
+14 +800,18 @@
+ {
+ page cardPower
+ }
+ if(tId.txt=="cardChart")
+ {
+ page cardChart
+ }
+ if(tId.txt=="cardLChart")
+ {
+ page cardLChart
+ }
+ }
+ if(tInstruction.txt=="timeout")
+ {
+ //set timeout to global var
+ spstr strCommand.txt,tTmp.txt,"~",1
+ covx tTmp.txt,sleepTimeout,0,0
+ }

View File

@@ -30,10 +30,6 @@ popupLightNew
23 Component(s)
412 Line(s) of event code
209 Unique line(s) of event code
popupLight
28 Component(s)
417 Line(s) of event code
228 Unique line(s) of event code
cardGrid2
52 Component(s)
703 Line(s) of event code
@@ -58,6 +54,10 @@ cardLChart
33 Component(s)
412 Line(s) of event code
267 Unique line(s) of event code
popupLight
28 Component(s)
412 Line(s) of event code
227 Unique line(s) of event code
cardPower
54 Component(s)
541 Line(s) of event code
@@ -66,6 +66,10 @@ cardThermo
57 Component(s)
550 Line(s) of event code
320 Unique line(s) of event code
screensaver2
64 Component(s)
424 Line(s) of event code
264 Unique line(s) of event code
popupInSel
34 Component(s)
621 Line(s) of event code
@@ -86,10 +90,6 @@ popupThermo
44 Component(s)
523 Line(s) of event code
276 Unique line(s) of event code
screensaver2
64 Component(s)
428 Line(s) of event code
266 Unique line(s) of event code
cardEntities
67 Component(s)
1205 Line(s) of event code
@@ -98,5 +98,5 @@ cardEntities
Total
23 Page(s)
881 Component(s)
10778 Line(s) of event code
10769 Line(s) of event code
2466 Unique line(s) of event code

View File

@@ -806,11 +806,6 @@ Timer tmSerial
if(tInstruction.txt=="entityUpdateDetail"&&entn.txt==tTmp.txt)
{
// change icon
spstr strCommand.txt,tTmp.txt,"~",2
if(tTmp.txt!="")
{
tIcon1.txt=tTmp.txt
}
//spstr strCommand.txt,tIcon1.txt,"~",2
vis tIcon1,1
// change icon color

View File

@@ -1794,10 +1794,6 @@ Timer tmSerial
{
page cardChart
}
if(tId.txt=="cardLChart")
{
page cardLChart
}
}
if(tInstruction.txt=="timeout")
{

View File

@@ -457,11 +457,6 @@ Timer tmSerial
if(tInstruction.txt=="entityUpdateDetail"&&entn.txt==tTmp.txt)
{
// change icon
spstr strCommand.txt,tTmp.txt,"~",2
if(tTmp.txt!="")
{
tIcon1.txt=tTmp.txt
}
//spstr strCommand.txt,tIcon1.txt,"~",2
vis tIcon1,1
// change icon color

View File

@@ -956,10 +956,6 @@ Timer tmSerial
{
page cardChart
}
if(tId.txt=="cardLChart")
{
page cardLChart
}
}
if(tInstruction.txt=="timeout")
{

Binary file not shown.

Binary file not shown.

View File

@@ -65,5 +65,3 @@ SmartHomeNG: https://github.com/sisamiwe/shng-nspanel-plugin
OpenHAB: https://github.com/donoo/o2n2l
NodeRed: https://github.com/laluz742/node-red-contrib-nspanel-lui
ESPHome without any Backend: https://github.com/olicooper/esphome-nspanel-lovelace-native

View File

@@ -1,3 +1,2 @@
ha_api = None
mqtt_api = None
ad_api = None
mqtt_api = None

View File

@@ -132,7 +132,6 @@ class LuiBackendConfig(object):
'sleepTrackingZones': ["not_home", "off"],
'sleepOverride': None,
'locale': "en_US",
'quiet': True,
'timeFormat': "%H:%M",
'dateFormatBabel': "full",
'dateAdditionalTemplate': "",

View File

@@ -3,7 +3,6 @@ import datetime
import apis
from helper import scale, pos_to_color, rgb_dec565
from pages import LuiPagesGen
from luibackend.config import Card
class LuiController(object):
@@ -459,9 +458,3 @@ class LuiController(object):
apis.ha_api.get_entity(entity_id).call_service("pause")
if button_type == "timer-finish":
apis.ha_api.get_entity(entity_id).call_service("finish")
@property
def current_card(self) -> Card:
"""Used to get the current card"""
return self._current_card

View File

@@ -213,9 +213,6 @@ def get_icon_ha(entity_id, overwrite=None, stateOverwrite=None):
entity = apis.ha_api.get_entity(entity_id)
state = entity.state if stateOverwrite is None else stateOverwrite
if entity_id in ["sensor.weather_forecast_daily", "sensor.weather_forecast_hourly"]:
ha_type = "weather"
if overwrite is not None:
if type(overwrite) is str:
return get_icon_char(overwrite)

View File

@@ -77,13 +77,12 @@ class LuiMqttListener(object):
self._controller.detail_open(msg[2], msg[3])
class LuiMqttSender(object):
def __init__(self, api, use_api, topic_send, api_panel_name, quiet):
def __init__(self, api, use_api, topic_send, api_panel_name):
self._ha_api = api
self._use_api = use_api
self._topic_send = topic_send
self._api_panel_name = api_panel_name
self._prev_msg = ""
self._quiet = quiet
def send_mqtt_msg(self, msg, topic=None, force=False):
if not force and self._prev_msg == msg:
@@ -91,9 +90,7 @@ class LuiMqttSender(object):
return
self._prev_msg = msg
if self._quiet is False:
apis.ha_api.log(f"Sending Message: {msg}")
apis.ha_api.log(f"Sending Message: {msg}")
if self._use_api:
apis.ha_api.call_service(service="esphome/" + self._api_panel_name + "_nspanelui_api_call", command=2, data=msg)
else:

View File

@@ -192,9 +192,6 @@ class LuiPagesGen(object):
else:
entityType = "delete"
if entityId in ["sensor.weather_forecast_daily", "sensor.weather_forecast_hourly"]:
entityType = "weather"
apis.ha_api.log(f"Generating item for {entityId} with type {entityType}", level="DEBUG")
status_entity = apis.ha_api.get_entity(item.status) if item.status and apis.ha_api.entity_exists(item.status) else None
@@ -224,7 +221,7 @@ class LuiPagesGen(object):
if status_entity:
icon_res = get_icon_ha(item.status, overwrite=icon)
icon_color = self.get_entity_color(status_entity, ha_type=item.status.split(".")[0], overwrite=colorOverride)
if item.status.startswith("sensor") and cardType in ["cardGrid", "cardGrid1", "cardGrid2"] and item.iconOverride is None:
if item.status.startswith("sensor") and (cardType == "cardGrid" or cardType == "cardGrid2") and item.iconOverride is None:
icon_res = status_entity.state[:4]
if icon_res[-1] == ".":
icon_res = icon_res[:-1]
@@ -248,7 +245,7 @@ class LuiPagesGen(object):
if status_entity:
icon_id = get_icon_ha(item.status, overwrite=icon)
icon_color = self.get_entity_color(status_entity, ha_type=item.status.split(".")[0], overwrite=colorOverride)
if item.status.startswith("sensor") and cardType in ["cardGrid", "cardGrid1", "cardGrid2"] and item.iconOverride is None:
if item.status.startswith("sensor") and (cardType == "cardGrid" or cardType == "cardGrid2") and item.iconOverride is None:
icon_id = status_entity.state[:4]
if icon_id[-1] == ".":
icon_id = icon_id[:-1]
@@ -321,7 +318,7 @@ class LuiPagesGen(object):
value = value + unit_of_measurement
if entityType == "binary_sensor":
value = get_translation(self._locale, f"backend.component.binary_sensor.state.{device_class}.{entity.state}")
if cardType in ["cardGrid", "cardGrid1", "cardGrid2"] and entityType == "sensor" and icon is None:
if (cardType == "cardGrid" or cardType == "cardGrid2") and entityType == "sensor" and icon is None:
icon_id = entity.state[:4]
if icon_id[-1] == ".":
icon_id = icon_id[:-1]
@@ -782,8 +779,6 @@ class LuiPagesGen(object):
if send_page_type:
if card.cardType == "cardGrid" and len(card.entities) > 6:
card.cardType = "cardGrid2"
if card.cardType == "cardGrid1":
card.cardType = "cardGrid"
self.page_type(card.cardType)
# send sleep timeout if there is one configured for the current card
@@ -793,7 +788,7 @@ class LuiPagesGen(object):
self._send_mqtt_msg(f'timeout~{self._config.get("sleepTimeout")}')
temp_unit = card.raw_config.get("temperatureUnit", "celsius")
if card.cardType in ["cardEntities", "cardGrid", "cardGrid1","cardGrid2"]:
if card.cardType in ["cardEntities", "cardGrid", "cardGrid2"]:
self.generate_entities_page(navigation, card.title, card.entities, card.cardType, temp_unit)
return
if card.cardType == "cardThermo":

View File

@@ -1,4 +1,4 @@
import adbase as ad
import hassapi as hass
from luibackend.config import LuiBackendConfig
from luibackend.controller import LuiController
@@ -6,19 +6,15 @@ from luibackend.mqtt import LuiMqttListener, LuiMqttSender
from luibackend.updater import Updater
import apis
import json
from typing import Literal
class NsPanelLovelaceUIManager(ad.ADBase):
class NsPanelLovelaceUIManager(hass.Hass):
def initialize(self):
self.adapi = self.get_ad_api()
self.adapi.log('Starting')
apis.ad_api = self.adapi
apis.ha_api = self.get_plugin_api("HASS")
self.log('Starting')
apis.ha_api = self
apis.mqtt_api = self.get_plugin_api("MQTT")
cfg = self._cfg = LuiBackendConfig(apis.ha_api, self.args["config"])
cfg = self._cfg = LuiBackendConfig(self, self.args["config"])
use_api = cfg.get("use_api") == True
@@ -26,11 +22,10 @@ class NsPanelLovelaceUIManager(ad.ADBase):
topic_recv = cfg.get("panelRecvTopic")
api_panel_name = cfg.get("panelName")
api_device_id = cfg.get("panelDeviceId")
quiet = cfg.get("quiet")
mqttsender = self._mqttsender = LuiMqttSender(apis.ha_api, use_api, topic_send, api_panel_name, quiet)
mqttsend = LuiMqttSender(self, use_api, topic_send, api_panel_name)
self._controller = LuiController(cfg, mqttsender.send_mqtt_msg)
controller = LuiController(cfg, mqttsend.send_mqtt_msg)
desired_tasmota_driver_version = 8
desired_display_firmware_version = 53
@@ -46,35 +41,11 @@ class NsPanelLovelaceUIManager(ad.ADBase):
desired_tasmota_driver_url = cfg._config.get("berryURL", "https://raw.githubusercontent.com/joBr99/nspanel-lovelace-ui/main/tasmota/autoexec.be")
mode = cfg.get("updateMode")
updater = Updater(self.adapi.log, mqttsender, topic_send, mode, desired_display_firmware_version, model, desired_display_firmware_url, desired_tasmota_driver_version, desired_tasmota_driver_url)
updater = Updater(self.log, mqttsend, topic_send, mode, desired_display_firmware_version, model, desired_display_firmware_url, desired_tasmota_driver_version, desired_tasmota_driver_url)
# Request Tasmota Driver Version
updater.request_berry_driver_version()
LuiMqttListener(use_api, topic_recv, api_panel_name, api_device_id, self._controller, updater)
LuiMqttListener(use_api, topic_recv, api_panel_name, api_device_id, controller, updater)
self.adapi.log(f'Started ({version})')
#
# helpers
#
def show_card(self, card_key: str) -> None:
"""Used to show card on panel"""
msg = json.dumps({"CustomRecv":f"event,buttonPress2,navigate.{card_key},button"})
topic = self._cfg.get("panelRecvTopic")
self._mqttsender.send_mqtt_msg(msg, topic)
def navigate(self, direction: Literal['up', 'prev', 'next']) -> None:
"""Used to navigate different directions on the panel"""
msg = json.dumps({"CustomRecv":f"event,buttonPress2,nav{direction.title()},button"})
topic = self._cfg.get("panelRecvTopic")
self._mqttsender.send_mqtt_msg(msg, topic)
@property
def current_card(self) -> str:
"""Used to get the panel's current card"""
return self._controller.current_card.key
self.log(f'Started ({version})')

View File

@@ -44,6 +44,8 @@ Using a 6th entity will automatically activate the alternative layout.
type: 0
- entity: weather.demo_weather_north
type: 1
- entity: weather.demo_weather_north
type: 2
- entity: sensor.energy_usage
- entity: delete
- entity: sensor.indoor_temp

View File

@@ -93,34 +93,3 @@ Now, to install NSPanel Lovelace UI Backend with HACS, follow these steps:
6. A confirmation panel will appear, click on `Download`, and wait for HACS to
proceed with the download
7. The Backend Application is now installed, and HACS will inform you when updates are available
# Workaround for HomeAssistant 2024.04
AppDaemon is using the old REST API that until AppDaemon moved on the the websocket API this woraround is needed to get weather forecast data from homeassistant. (https://github.com/AppDaemon/appdaemon/issues/1837)
To get the forecast data in appdaemon, there is a script needed in homeassistant's configuration.yaml:
```yaml
template:
- trigger:
- platform: time_pattern
hours: /1
action:
- service: weather.get_forecasts
data:
type: daily
target:
entity_id: weather.k3ll3r # change to your weather entity in this line
response_variable: daily
sensor:
- name: Weather Forecast Daily
unique_id: weather_forecast_daily
state: "{{ now().isoformat() }}"
attributes:
forecast: "{{ daily['weather.k3ll3r'].forecast }}" # change to your weather entity in this line
```
![image](https://github.com/joBr99/nspanel-lovelace-ui/assets/29555657/41f21db3-a6e2-4e4f-8dab-b9351ecd23e5)
Adjust the entities in your apps.yaml that are accessing the forecast to the newly created trigger template:
![image](https://github.com/joBr99/nspanel-lovelace-ui/assets/29555657/1cfd913d-88be-4cb0-9a68-0e864ee1ad4f)

View File

@@ -1,130 +1,109 @@
/**
* Dieses Script fragt eine influxDb ab, um Daten für die cardLcart (Liniendiagramm) zuberechnen und im richtigen Format bereitzustellen.
* Es erstellt automatisch einen Datenpunkt.
* Die Abfrage muss ggf. angepasst werden. Aktuell ermittelt sie Werte der letzten 24h, zu Stundenwerten zusammengefasst.
*/
const Debug = false;
const Debug = false; // true für erweiterte Ausgaben im Log
const NSPanel_Path = '0_userdata.0.NSPanel.';
const NSPanel_Path = '0_userdata.0.NSPanel.1.';
const Path = NSPanel_Path + 'Influx2NSPanel.cardLChart.';
const InfluxInstance = 'influxdb.0';
const influxDbBucket = 'storage_short';
const InfluxInstance = 'influxdb.1';
const influxDbBucket = 'iobroker';
const numberOfHoursAgo = 24;
const xAxisTicksEveryM = 60;
const xAxisLabelEveryM = 240;
//
const sensors : Record<string, Record <string, string>> = {};
/**
* Hier werden die Sensoren festgelegt nach flogendem Schema
*
* sensors[Datenpunkt(kompletter Pfad) des Messwertes'] = {'taget': 'Name des Datenpunkt für die Chartwerte', 'measurement': 'genutzter Alias in der Influxdb für den Messwert'};
*
* Wenn der Wert in der Datenbank keinen Alias hat bleibt der Wert 'measurement': weg.
* Jeder Messwert bekommt einen eigenen sensors[...] = {'target':....}
*/
// this records holds all sensors and their corresponding states which act as the data source for the charts
// add all sensors which are to be displayed in this script, there is no need to use multiple scripts
const sensors : Record<string, string> = {};
/* ↓ Id of the sensor ↓ Id of the data source for the charts */
sensors['deconz.0.Sensors.65.temperature'] = Path + 'buero_temperature.ACTUAL';
sensors['deconz.0.Sensors.65.humidity'] = Path + 'buero_luftfeuchte.ACTUAL';
sensors['netatmo-crawler.0.stationData.1.temperature'] = {'target':'AussenTemp', 'measurement':'wetter.temperatur'};
// ##### ab hier keine Änderungen mehr nötig #####
// create data source for NsPanel on script startup
Object.keys(sensors).forEach(async id => {
await generateDateAsync(id);
Object.keys(sensors).forEach(async x => {
await generateDateAsync(x, sensors[x]);
});
// then listen to the sensors and update the data source states accordingly
on({ id: Object.keys(sensors), change: 'any' }, async function (obj) {
if (!obj.id) {
return;
}
await generateDateAsync(obj.id);
if (!obj.id) {
return;
}
await generateDateAsync(obj.id, sensors[obj.id]);
});
//__________________________
// Beschreibe diese Funktion: Daten generieren
async function generateDateAsync(sensorId: string) {
let idMeasurement = sensors[sensorId].measurement;
if (idMeasurement =='' ||idMeasurement == undefined) {idMeasurement = sensorId};
const dataPointId:string = Path + sensors[sensorId].target +'.ACTUAL';
if (Debug) log(`(f) generateDateAsync: ${sensorId} ${dataPointId} > ${idMeasurement}`);
const query =[
'from(bucket: "' + influxDbBucket + '")',
'|> range(start: -' + numberOfHoursAgo + 'h)',
'|> filter(fn: (r) => r["_measurement"] == "' + idMeasurement + '")',
'|> filter(fn: (r) => r["_field"] == "value")',
'|> drop(columns: ["from", "ack", "q"])',
'|> aggregateWindow(every: 1h, fn: last, createEmpty: false)',
'|> map(fn: (r) => ({ r with _rtime: int(v: r._time) - int(v: r._start)}))',
'|> yield(name: "_result")'].join('');
if (Debug) console.log('Query: ' + query);
const result : any = await sendToAsync(InfluxInstance, 'query', query);
if (result.error) {
console.error(result.error);
return;
}
if (Debug) console.log(JSON.stringify(result));
const numResults = result.result.length;
let coordinates : string = '';
for (let r = 0; r < numResults; r++)
{
const list : string[] = [];
const numValues = result.result[r].length;
for (let i = 0; i < numValues; i++)
{
const time = Math.round(result.result[r][i]._rtime/1000/1000/1000/60);
const value = Math.round(result.result[r][i]._value * 10);
list.push(time + ":" + value);
}
coordinates = list.join("~");
if (Debug) console.log(coordinates);
}
const ticksAndLabelsList : string[] = []
const date = new Date();
date.setMinutes(0, 0, 0);
const ts = Math.round(date.getTime() / 1000);
const tsYesterday = ts - (numberOfHoursAgo * 3600);
if (Debug) console.log('Iterate from ' + tsYesterday + ' to ' + ts + ' stepsize=' + (xAxisTicksEveryM * 60));
for (let x = tsYesterday, i = 0; x < ts; x += (xAxisTicksEveryM * 60), i += xAxisTicksEveryM)
{
if ((i % xAxisLabelEveryM))
ticksAndLabelsList.push('' + i);
else
{
const currentDate = new Date(x * 1000);
// Hours part from the timestamp
const hours = "0" + String(currentDate.getHours());
// Minutes part from the timestamp
const minutes = "0" + String(currentDate.getMinutes());
const formattedTime = hours.slice(-2) + ':' + minutes.slice(-2);
ticksAndLabelsList.push(String(i) + "^" + formattedTime);
}
}
if (Debug) console.log('Ticks & Label: ' + ticksAndLabelsList);
if (Debug) console.log('Coordinates: ' + coordinates);
await setOrCreate(dataPointId, ticksAndLabelsList.join("+") + '~' + coordinates, true);
async function generateDateAsync(sensorId: string, dataPointId: string) {
const query =[
'from(bucket: "' + influxDbBucket + '")',
'|> range(start: -' + numberOfHoursAgo + 'h)',
'|> filter(fn: (r) => r["_measurement"] == "' + sensorId + '")',
'|> filter(fn: (r) => r["_field"] == "value")',
'|> drop(columns: ["from", "ack", "q"])',
'|> aggregateWindow(every: 1h, fn: last, createEmpty: false)',
'|> map(fn: (r) => ({ r with _rtime: int(v: r._time) - int(v: r._start)}))',
'|> yield(name: "_result")'].join('');
if (Debug) console.log('Query: ' + query);
const result : any = await sendToAsync(InfluxInstance, 'query', query);
if (result.error) {
console.error(result.error);
return;
}
if (Debug) console.log(result);
const numResults = result.result.length;
let coordinates : string = '';
for (let r = 0; r < numResults; r++)
{
const list : string[] = []
const numValues = result.result[r].length;
for (let i = 0; i < numValues; i++)
{
const time = Math.round(result.result[r][i]._rtime/1000/1000/1000/60);
const value = Math.round(result.result[r][i]._value * 10);
list.push(time + ":" + value);
}
coordinates = list.join("~");
if (Debug) console.log(coordinates);
}
const ticksAndLabelsList : string[] = []
const date = new Date();
date.setMinutes(0, 0, 0);
const ts = Math.round(date.getTime() / 1000);
const tsYesterday = ts - (numberOfHoursAgo * 3600);
if (Debug) console.log('Iterate from ' + tsYesterday + ' to ' + ts + ' stepsize=' + (xAxisTicksEveryM * 60));
for (let x = tsYesterday, i = 0; x < ts; x += (xAxisTicksEveryM * 60), i += xAxisTicksEveryM)
{
if ((i % xAxisLabelEveryM))
ticksAndLabelsList.push('' + i);
else
{
const currentDate = new Date(x * 1000);
// Hours part from the timestamp
const hours = "0" + String(currentDate.getHours());
// Minutes part from the timestamp
const minutes = "0" + String(currentDate.getMinutes());
const formattedTime = hours.slice(-2) + ':' + minutes.slice(-2);
ticksAndLabelsList.push(String(i) + "^" + formattedTime);
}
}
if (Debug) console.log('Ticks & Label: ' + ticksAndLabelsList);
if (Debug) console.log('Coordinates: ' + coordinates);
await setOrCreate(dataPointId, ticksAndLabelsList.join("+") + '~' + coordinates, true);
}
//__________________________
// Beschreibe diese Funktion: Datenpunkte anlegen bzw. schreiben
async function setOrCreate(id : string, value : any, ack : boolean) {
if (!(await existsStateAsync(id))) {
await createStateAsync(id, value, {
name: id.split('.').reverse()[0],
desc: 'Sensor Values [~<time>:<value>]*',
type: 'string',
role: 'value',
});
} else {
await setStateAsync(id, value, ack);
}
}
if (!(await existsStateAsync(id))) {
await createStateAsync(id, value, {
name: id.split('.').reverse()[0],
desc: 'Sensor Values [~<time>:<value>]*',
type: 'string',
role: 'value',
});
} else {
await setStateAsync(id, value, ack);
}
}

View File

@@ -1,5 +1,5 @@
/*-----------------------------------------------------------------------
TypeScript v4.3.3.43 zur Steuerung des SONOFF NSPanel mit dem ioBroker by @Armilar / @TT-Tom / @ticaki / @Britzelpuf / @Sternmiere / @ravenS0ne
TypeScript v4.3.3.39 zur Steuerung des SONOFF NSPanel mit dem ioBroker by @Armilar / @TT-Tom / @ticaki / @Britzelpuf / @Sternmiere / @ravenS0ne
- abgestimmt auf TFT 53 / v4.3.3 / BerryDriver 9 / Tasmota 13.3.0
@joBr99 Projekt: https://github.com/joBr99/nspanel-lovelace-ui/tree/main/ioBroker
NsPanelTs.ts (dieses TypeScript in ioBroker) Stable: https://github.com/joBr99/nspanel-lovelace-ui/blob/main/ioBroker/NsPanelTs.ts
@@ -107,16 +107,7 @@ ReleaseNotes:
- 20.01.2024 - v4.3.3.38 Add: click on indicatorIcon navigate to Page
- 23.01.2024 - v4.3.3.39 Add: Optional setOn & setOff for HW button with mode 'set'
- 28.01.2024 - v4.3.3.39 Fix: ack for read-only state
- 03.02.2024 - v4.3.3.40 Fix: RGB maxValueColorTemp
- 05.02.2024 - v4.3.3.40 Fix: SqueezeboxRPC-Media-Player and add some Functions
- 06.02.2024 - v4.3.3.41 Fix: activeBrightness -> null
- 06.02.2024 - v4.3.3.41 Fix: bHome -> corrected PageId
- 07.02.2024 - v4.3.3.42 Minor Fixes
- 09.02.2024 - v4.3.3.42 Change pageId with Alias in Communication with HMI
- 09.02.2024 - v4.3.3.42 Spotify Media-Player: Dynamic loading of the speaker list, playlist, tracklist, fix repeat, add seek, add elapsed/duration
- 10.02.2024 - v4.3.3.42 Spotify Minor Fixes; Add miValue / maxValue to Volume-Slider
- 10.02.2024 - v4.3.3.43 Fix: cardGrid2 => 9 Entities for Layout 'us-p' issue #1167
- 11.02.2024 - v4.3.3.43 Fix VolumeSlider
Todo:
- XX.XX.XXXX - v5.0.0 Change the bottomScreensaverEntity (rolling) if more than 6 entries are defined
@@ -138,8 +129,7 @@ Mögliche Seiten-Ansichten:
(die 4 kleineren Icons können als Wetter-Vorschau + 4Tage (Symbol + Höchsttemperatur) oder zur Anzeige definierter Infos konfiguriert werden)
cardEntities Page - 4 vertikale angeordnete 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 bzw. beim US-Modell im Portrait-Modus
9 horizontal angeordnete Steuerelemente in 3 Reihen a 3 Steuerelemente - auch als Subpage
cardGrid2 Page - 8 horizontal angeordnete Steuerelemente in 2 Reihen a 4 Steuerelemente - auch als Subpage
cardThermo Page - Thermostat 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
@@ -215,7 +205,6 @@ Upgrades in Konsole:
---------------------------------------------------------------------------------------
*/
/******************************* Begin CONFIG Parameter *******************************/
// DE: liefert bei true detailliertere Meldundgen im Log.
@@ -985,7 +974,7 @@ export const config: Config = {
// _________________________________ DE: Ab hier keine Konfiguration mehr _____________________________________
// _________________________________ EN: No more configuration from here _____________________________________
const scriptVersion: string = 'v4.3.3.43';
const scriptVersion: string = 'v4.3.3.39';
const tft_version: string = 'v4.3.3';
const desired_display_firmware_version = 53;
const berry_driver_version = 9;
@@ -1525,11 +1514,11 @@ InitActiveBrightness();
on({id: [NSPanel_Path + 'ScreensaverInfo.activeBrightness'], change: 'ne'}, async function (obj) {
try {
let active = getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val ?? -1;
let active = getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val;
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) + '~' + Sliders2 });
InitDimmode();
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) + '~' + Sliders2 });
InitDimmode();
}
} catch (err:any) {
log('error at trigger activeBrightness: ' + err.message, 'warn');
@@ -1538,7 +1527,7 @@ on({id: [NSPanel_Path + 'ScreensaverInfo.activeBrightness'], change: 'ne'}, asyn
on({id: [NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness'], change: "ne"}, async function (obj) {
try {
let active = getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val ?? 80;
let active = getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val;
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');
@@ -1568,7 +1557,7 @@ on({id: [NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness'], change: "ne"
on({id: String(NSPanel_Path) + 'ScreensaverInfo.Trigger_Dimmode', change: "ne"}, async function (obj) {
try {
let active = getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val ?? 80;
let active = getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val;
if (obj.state.val) {
SendToPanel({ payload: 'dimmode~' + 100 + '~' + active + '~' + rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor) + '~' + Sliders2 });
} else {
@@ -1919,8 +1908,8 @@ on({id: [NSPanel_Path + 'PageNavi'], change: "any"}, async function (obj) {
//----------------------Begin Dimmode
function ScreensaverDimmode(timeDimMode:NSPanel.DimMode) {
try {
let active = getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val ?? 80
let dimmode = getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val ?? -1
let active = getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val
let dimmode = getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val
if (Debug) {
log('function ScreensaverDimmode RGB-Wert HMIDark' + rgb_dec565(HMIDark), 'info');
}
@@ -2027,12 +2016,12 @@ async function InitDimmode() {
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) + '~' + Sliders2 });
SendToPanel({ payload: 'dimmode~' + getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val + '~' + getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val + '~' + rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor) + '~' + Sliders2 });
} 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) + '~' + Sliders2 });
SendToPanel({ payload: 'dimmode~' + timeDimMode.brightnessDay + '~' + getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val + '~' + rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor) + '~' + Sliders2 });
} else {
SendToPanel({ payload: 'dimmode~' + timeDimMode.brightnessNight + '~' + getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val ?? 80 + '~' + rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor) + '~' + Sliders2 });
SendToPanel({ payload: 'dimmode~' + timeDimMode.brightnessNight + '~' + getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val + '~' + rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor) + '~' + Sliders2 });
}
ScreensaverDimmode(timeDimMode);
}
@@ -3386,11 +3375,7 @@ function GeneratePageElements(page: PageType): string {
maxItems = 6;
break;
case 'cardGrid2':
if (getState(NSPanel_Path + 'NSPanel_Version').val == 'us-p') {
maxItems = 9;
} else {
maxItems = 8;
};
maxItems = 8;
break;
}
@@ -4769,25 +4754,67 @@ function unsubscribeMediaSubscriptions(): void {
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!);
}
id + '.VOLUME',
id + '.ARTIST',
id + '.ALBUM',
id + '.TITLE',
id + '.REPEAT',
id + '.SHUFFLE'], change: "any"}, async function () {
(function () { if (timeoutMedia) { clearTimeout(timeoutMedia); timeoutMedia = null; } })();
timeoutMedia = setTimeout(async function () {
if (useMediaEvents) {
GeneratePage(activePage!);
setTimeout(async function () {
GeneratePage(activePage!);
}, 3000);
}
},50)
});
}
function subscribeMediaSubscriptionsSonosAdd(id: string): void {
on({id: [id + '.QUEUE'], change: "any", ack: true}, async function () {
if (useMediaEvents && pageCounter == 1) {
GeneratePage(activePage!);
}
on({id: [id + '.QUEUE',
id + '.DURATION',
id + '.ELAPSED'], change: "any"}, async function () {
(function () { if (timeoutMedia) { clearTimeout(timeoutMedia); timeoutMedia = null; } })();
timeoutMedia = setTimeout(async function () {
if (useMediaEvents) {
GeneratePage(activePage!);
setTimeout(async function () {
GeneratePage(activePage!);
}, 50);
}
},50)
});
}
function subscribeMediaSubscriptionsAlexaAdd(id: string): void {
on({id: [id + '.DURATION',
id + '.ELAPSED'], change: "any"}, async function () {
(function () { if (timeoutMedia) { clearTimeout(timeoutMedia); timeoutMedia = null; } })();
timeoutMedia = setTimeout(async function () {
if (useMediaEvents) {
GeneratePage(activePage!);
setTimeout(async function () {
GeneratePage(activePage!);
}, 50);
}
},50)
});
}
function subscribeMediaSubscriptionsBoseAdd(id: string): void {
on({id: [id + '.DURATION',
id + '.ELAPSED'], change: "any"}, async function () {
(function () { if (timeoutMedia) { clearTimeout(timeoutMedia); timeoutMedia = null; } })();
timeoutMedia = setTimeout(async function () {
if (useMediaEvents) {
GeneratePage(activePage!);
setTimeout(async function () {
GeneratePage(activePage!);
}, 50);
}
},50)
});
}
@@ -4916,13 +4943,6 @@ async function createAutoMediaAlias (id: string, mediaDevice: string, adapterPla
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, <iobJS.StateCommon> {type: 'string', role: 'media.duration.text', name: 'DURATION'});
await createAliasAsync(id + '.ELAPSED', dpPath + 'player.progress', true, <iobJS.StateCommon> {type: 'string', role: 'media.elapsed.text', name: 'ELAPSED'});
}
}
break;
@@ -4960,12 +4980,7 @@ async function createAutoMediaAlias (id: string, mediaDevice: string, adapterPla
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, <iobJS.StateCommon> {type: 'string', role: 'media.duration', name: 'DURATION'});
//await createAliasAsync(id + '.ELAPSED', dpPath + 'player.progress', true, <iobJS.StateCommon> {type: 'string', role: 'media.elapsed.text', name: 'ELAPSED'});
}
}
break;
case "squeezeboxrpc.0.":
@@ -4995,14 +5010,13 @@ async function createAutoMediaAlias (id: string, mediaDevice: string, adapterPla
await createAliasAsync(id + '.STATE', dpPath + '.Power', true, <iobJS.StateCommon> {type: 'number', role: 'switch', name: 'STATE'});
await createAliasAsync(id + '.VOLUME', dpPath + '.Volume', true, <iobJS.StateCommon> {type: 'number', role: 'level.volume', name: 'VOLUME'});
await createAliasAsync(id + '.VOLUME_ACTUAL', dpPath + '.Volume', true, <iobJS.StateCommon> {type: 'number', role: 'value.volume', name: 'VOLUME_ACTUAL'});
await createAliasAsync(id + '.SHUFFLE', dpPath + '.PlaylistShuffle', true, <iobJS.StateCommon> {type: 'string', role: 'media.mode.shuffle', name: 'SHUFFLE'});
await createAliasAsync(id + '.SHUFFLE', dpPath + '.PlaylistShuffle', true, <iobJS.StateCommon> {type: 'string', role: 'media.mode.shuffle', name: 'SHUFFLE', alias: {id: dpPath + '.PlaylistShuffle', read: 'val !== 0 ? \'on\' : \'off\'', write: 'val === \'off\' ? 0 : 1'}});
await createAliasAsync(id + '.REPEAT', dpPath + '.PlaylistRepeat', true, <iobJS.StateCommon> {type: 'number', role: 'media.mode.repeat', name: 'REPEAT'});
await createAliasAsync(id + '.DURATION', dpPath + '.Duration', true, <iobJS.StateCommon> {type: 'string', role: 'media.duration', name: 'DURATION'});
await createAliasAsync(id + '.ELAPSED', dpPath + '.Time', true, <iobJS.StateCommon> {type: 'string', role: 'media.elapsed', name: 'ELAPSED'});
} catch (err: any) {
log('error at function createAutoMediaAlias Adapter Squeezebox: ' + err.message, 'warn');
}
}
}
break;
case "bosesoundtouch.0.":
@@ -5061,13 +5075,29 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
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;
// Some magic to change the ID of the alias, since speakers are not a property but separate objects
if(v2Adapter == 'squeezeboxrpc') {
if(id && getObject(id).type != 'channel') {
id = id + '.' + page.items[0].mediaDevice;
page.items[0].id = id;
page.heading = page.items[0].mediaDevice ?? '';
} else {
let idParts = id.split('.');
if(idParts[idParts.length-1] !== page.items[0].mediaDevice) {
idParts[idParts.length-1] = page.items[0].mediaDevice ?? '';
id = idParts.join('.');
page.items[0].id = id;
page.heading = page.items[0].mediaDevice ?? '';
}
}
}
let vMediaDevice = (page.items[0].mediaDevice != undefined) ? page.items[0].mediaDevice : '';
@@ -5075,7 +5105,7 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
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' });
@@ -5088,10 +5118,10 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
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 (v2Adapter == 'alexa2') {
subscribeMediaSubscriptionsAlexaAdd(page.items[0].id);
} else if (v2Adapter == 'bosesoundtouch') {
subscribeMediaSubscriptionsBoseAdd(page.items[0].id);
}
}
}
@@ -5100,7 +5130,11 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
alwaysOn = true;
subscribeMediaSubscriptions(page.items[0].id);
if (v2Adapter == 'sonos') {
subscribeMediaSubscriptionsSonosAdd(page.items[0].id);
subscribeMediaSubscriptionsSonosAdd(page.items[0].id);
} else if (v2Adapter == 'alexa2') {
subscribeMediaSubscriptionsAlexaAdd(page.items[0].id);
} else if (v2Adapter == 'bosesoundtouch') {
subscribeMediaSubscriptionsBoseAdd(page.items[0].id);
}
} else if (page.type == 'cardMedia' && pageCounter == -1) {
//Do Nothing
@@ -5111,8 +5145,8 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
if (existsObject(id)) {
let name = getState(id + '.ALBUM').val;
let title = getState(id + '.TITLE').val;
if (title.length > 26) {
title = title.slice(0, 26) + '...';
if (title.length > 27) {
title = title.slice(0, 27) + '...';
}
if (existsObject(id + '.DURATION') && existsObject(id + '.ELAPSED')) {
if (v2Adapter == 'alexa2') {
@@ -5133,15 +5167,8 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
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 = '';
}
}
title = title + ' (' + vElapsed + '|' + vDuration + ')';
} 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) {
@@ -5170,51 +5197,7 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
title = title + ' (' + vElapsed + '|' + vDuration + ')';
}
}
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
@@ -5225,23 +5208,19 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
media_icon = Icons.GetIcon('spotify');
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:') {
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) + '...';
} else if (name.substring(0,6) == 'Track') {
name = 'Spotify-Premium';
}
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 (author.length > 30) {
author = getState(id + '.ARTIST').val;
}
if ((getState(id + '.ARTIST').val).length == 0) {
author = findLocale('media','no_music_to_control');
@@ -5292,10 +5271,9 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
//Logitech Squeezebox RPC
if (v2Adapter == 'squeezeboxrpc') {
media_icon = Icons.GetIcon('dlna');
if (name.length == 0) {
name = page.heading;
} else if (name.length > 16) {
name = name.slice(0,16) + '...'
let nameLength = name.length;
if (nameLength == 0) {
name = page.items[0].mediaDevice;
}
}
@@ -5313,8 +5291,6 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
}
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) {
@@ -5327,12 +5303,12 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
//Volumio
if (v2Adapter == 'volumio') {
media_icon = Icons.GetIcon('clock-time-twelve-outline');
if (name != undefined) { author = author + " | " + name; }
name = page.heading;
if (name != undefined) { author = author + " [" + name + "]"; }
name = getState(vInstance + 'info.name').val; /* page.heading;
getState(id + '.TRACK').val; */
}
let volume = scale(getState(id + '.VOLUME').val, activePage!.items[0]!.minValue ?? 0, activePage!.items[0]!.maxValue ?? 100, 100, 0);
let volume = getState(id + '.VOLUME').val;
let iconplaypause = Icons.GetIcon('pause'); //pause
let shuffle_icon = Icons.GetIcon('shuffle-variant'); //shuffle
let onoffbutton = 1374;
@@ -5340,9 +5316,7 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
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
if (v2Adapter == 'volumio') { shuffle_icon = Icons.GetIcon('refresh'); } //Volumio: refresh playlist
//For all players
@@ -5373,7 +5347,7 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
}
}
let currentSpeaker: string = findLocale('media','no_speaker_found');
let currentSpeaker = findLocale('media','no_speaker_found');
if (v2Adapter == 'alexa2') {
currentSpeaker = getState(([page.items[0].adapterPlayerInstance, 'Echo-Devices.', page.items[0].mediaDevice, '.Info.name'].join(''))).val;
@@ -5385,8 +5359,6 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
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;
}
//-------------------------------------------------------------------------------------------------------------
// All Alexa devices (the online / player and commands directory is available) are listed and linked below
@@ -5403,11 +5375,6 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
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 {
let i_list = Array.prototype.slice.apply($('[state.id="' + page.items[0].adapterPlayerInstance + 'Echo-Devices.*.Info.name"]'));
for (let i_index in i_list) {
@@ -5433,7 +5400,7 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
if (speakerListArray.length > 0) {
speakerListIconCol = rgb_dec565(HMIOn);
speakerListString = 'input_sel' + '~' +
tid + '?speakerlist' + '~' +
id + '?speakerlist' + '~' +
Icons.GetIcon('speaker') + '~' +
speakerListIconCol + '~' +
findLocale('media','speaker') + '~' +
@@ -5467,13 +5434,10 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
});
}
/* Spotify: get all playlists if empty */
} else if (v2Adapter == 'spotify-premium') {
page.items[0].playList = (getState(page.items[0].adapterPlayerInstance + 'playlists.playlistListString').val).split(';');
}
playListIconCol = rgb_dec565(HMIOn);
playListString = 'input_sel' + '~' +
tid + '?playlist' + '~' +
id + '?playlist' + '~' +
Icons.GetIcon('playlist-play') + '~' +
playListIconCol + '~' +
//'PlayL ' + page.heading + '~' +
@@ -5483,7 +5447,9 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
//InSel Tracklist
globalTracklist = ''
if (v2Adapter == 'spotify-premium') {
globalTracklist = ' ' //Todo
}
let trackListString: string = '~~~~~~'
let trackListIconCol = rgb_dec565(HMIOff);
if (v2Adapter == 'volumio') { /* Volumio: get queue */
@@ -5514,8 +5480,8 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
}, 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);
} 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;
@@ -5534,19 +5500,12 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
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')
}
}
if (globalTracklist != null && globalTracklist.length != 0) {
trackListIconCol = rgb_dec565(HMIOn);
trackListString = 'input_sel' + '~' +
tid + '?tracklist' + '~' +
id + '?tracklist' + '~' +
Icons.GetIcon('animation-play-outline') + '~' +
trackListIconCol + '~' +
findLocale('media','tracklist') + '~' +
@@ -5559,7 +5518,7 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
if (page.items[0].equalizerList != undefined) {
equalizerListIconCol = rgb_dec565(HMIOn);
equalizerListString = 'input_sel' + '~' +
tid + '?equalizer' + '~' +
id + '?equalizer' + '~' +
Icons.GetIcon('equalizer-outline') + '~' +
equalizerListIconCol + '~' +
findLocale('media','equalizer') + '~' +
@@ -5568,7 +5527,7 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
let equalizerListIconCol = rgb_dec565(HMIOn);
//equalizerListString is used for favariteList
equalizerListString = 'input_sel' + '~' +
tid + '?favorites' + '~' +
id + '?favorites' + '~' +
Icons.GetIcon('playlist-star') + '~' +
equalizerListIconCol + '~' +
findLocale('media','favorites') + '~' +
@@ -5617,10 +5576,13 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
repeatIcon = Icons.GetIcon('repeat');
repeatIconCol = rgb_dec565(HMIOn);
}
else {
repeatIcon = Icons.GetIcon('repeat-off');
}
} 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);
repeatIconCol = rgb_dec565(colMediaIcon);
}
}
@@ -5631,7 +5593,7 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
v2Adapter == 'volumio' ||
v2Adapter == 'squeezeboxrpc') {
repeatButtonString = 'button' + '~' +
tid + '?repeat' + '~' +
id + '?repeat' + '~' +
repeatIcon + '~' +
repeatIconCol + '~' +
'Repeat' + '~' +
@@ -5644,40 +5606,22 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
if (v2Adapter == 'sonos') {
if (page.items[0].crossfade == undefined || page.items[0].crossfade == false) {
toolsString = 'input_sel' + '~' +
tid + '?seek' + '~' +
id + '?seek' + '~' +
media_icon + '~' +
toolsIconCol + '~' +
findLocale('media','seek') + '~' +
'media5~'
} else {
toolsString = 'input_sel' + '~' +
tid + '?crossfade' + '~' +
id + '?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 + '' + '~' +
id + '' + '~' +
media_icon + '~' +
toolsIconCol + '~' +
findLocale('media','tools') + '~' +
@@ -5688,7 +5632,7 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
payload: 'entityUpd~' + //entityUpd
name + '~' + //heading
GetNavigationString(pageId) + '~' + //navigation
tid + '~' + //internalNameEntiy
id + '~' + //internalNameEntiy
title + '~' + //title
rgb_dec565(colMediaTitle) + '~' + //titleColor
author + '~' + //author
@@ -6107,6 +6051,8 @@ function subscribePowerSubscriptions(id: string): void {
function GeneratePowerPage(page: NSPanel.PagePower): NSPanel.Payload[] {
try {
if (!page.items[0].id) throw new Error ('Missing pageItem.id for PowerPage!');
let obj:object = {};
let demoMode = false;
if (page.items[0].id == undefined){
@@ -6498,21 +6444,17 @@ function HandleButtonEvent(words: any): void {
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 'bHome':
if (Debug) {
log('HandleButtonEvent -> bHome: ' + words[4] + ' - ' + pageId, 'info');
}
UnsubscribeWatcher();
if (activePage!.home != undefined) {
GeneratePage(eval(activePage!.home));
} else {
GeneratePage(config.pages[0]);
}
break;
case 'notifyAction':
if (words[4] == 'yes') {
setState(popupNotifyInternalName, <iobJS.State>{ val: words[2], ack: true });
@@ -6633,20 +6575,19 @@ function HandleButtonEvent(words: any): void {
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');
let stateSpotifyRepeat = getState(id + '.REPEAT').val
if (stateSpotifyRepeat == 'none') {
setIfExists(id + '.REPEAT', 'all');
} else if (stateSpotifyRepeat == 'all') {
setIfExists(id + '.REPEAT', 'one');
} else if (stateSpotifyRepeat == 'one') {
setIfExists(id + '.REPEAT', 'none');
}
GeneratePage(activePage!);
break;
case 'bosesoundtouch':
if (Debug) log(adapterInstanceRepeat);
let stateBoseRepeat = getState(id + '.REPEAT').val;
let stateBoseRepeat = getState(id + '.REPEAT').val
if (stateBoseRepeat == 'REPEAT_OFF') {
setIfExists(adapterInstanceRepeat + 'key', 'REPEAT_ALL');
} else if (stateBoseRepeat == 'REPEAT_ALL') {
@@ -6657,7 +6598,7 @@ function HandleButtonEvent(words: any): void {
GeneratePage(activePage!);
break;
case 'sonos':
let stateSonosRepeat = getState(id + '.REPEAT').val;
let stateSonosRepeat = getState(id + '.REPEAT').val
if (stateSonosRepeat == 0) {
setIfExists(id + '.REPEAT', 1);
} else if (stateSonosRepeat == 1) {
@@ -6872,8 +6813,8 @@ function HandleButtonEvent(words: any): void {
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 adapterPlayerInstanceStateSeceltor: string = [pageItemTemp.adapterPlayerInstance, 'Players', pageItemTemp.mediaDevice, 'state'].join('.');
if (Debug) log('HandleButtonEvent media-pause Squeezebox-> adapterPlayerInstanceStateSeceltor: ' + adapterPlayerInstanceStateSeceltor, 'info');
let stateVal = getState(adapterPlayerInstanceStateSeceltor).val;
if (stateVal == 0) {
setState(adapterPlayerInstanceStateSeceltor, 1);
@@ -6936,13 +6877,6 @@ function HandleButtonEvent(words: any): void {
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') {
@@ -6956,11 +6890,15 @@ function HandleButtonEvent(words: any): void {
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));
pageCounter = -1;
(function () { if (timeoutSlider) { clearTimeout(timeoutSlider); timeoutSlider = null; } })();
timeoutSlider = setTimeout(async function () {
setIfExists(id + '.VOLUME', parseInt(words[4]));
setTimeout(async function () {
pageCounter = 1;
GeneratePage(activePage!);
}, 3000);
}, 20);
break;
case 'mode-speakerlist':
let pageItem = findPageItem(id);
@@ -7054,7 +6992,7 @@ function HandleButtonEvent(words: any): void {
});
break;
case 'squeezeboxrpc':
setState([adapterInstancePL, 'Players.', pageItemPL.mediaDevice, '.cmdPlayFavorite'].join(''), words[4]);
setState([pageItemPL.adapterPlayerInstance, 'Players', pageItemPL.mediaDevice, 'cmdPlayFavorite'].join('.'), words[4]);
break;
case "bosesoundtouch":
if (Debug) log('bosesoundtouch - playlist ' + pageItemPL.adapterPlayerInstance + ' - ' + words[4]);
@@ -7084,9 +7022,8 @@ function HandleButtonEvent(words: any): void {
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);
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'));
break;
case 'sonos':
setState(adapterInstanceTL + 'root.' + pageItemTL.mediaDevice + '.current_track_number', parseInt(words[4]) + 1);
@@ -7111,7 +7048,7 @@ function HandleButtonEvent(words: any): void {
break;
case 'squeezeboxrpc':
//@ts-ignore Fehler kommt von findPageItem in vscode
setState([adapterInstanceTL, 'Players.', pageItemTL.mediaDevice, '.PlaylistCurrentIndex'].join(''), words[4]);
setState([pageItemPL.adapterPlayerInstance, 'Players', pageItemPL.mediaDevice, 'PlaylistCurrentIndex'].join('.'), words[4]);
break;
case "bosesoundtouch":
break;
@@ -7164,14 +7101,6 @@ function HandleButtonEvent(words: any): void {
let deviceAdapterSK: NSPanel.PlayerType = adapterSK[0] as NSPanel.PlayerType;
switch (deviceAdapterSK) {
case 'spotify-premium':
setState(adapterInstanceSK + 'player.progressPercentage', 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');
@@ -7231,7 +7160,7 @@ function HandleButtonEvent(words: any): void {
if (!isPageMediaItem(pageItemTemp)) break;
let adapterInstance = pageItemTemp.adapterPlayerInstance.split('.');
if (adapterInstance[0] == 'squeezeboxrpc') {
let adapterPlayerInstancePowerSelector: string = [pageItemTemp.adapterPlayerInstance, 'Players.', pageItemTemp.mediaDevice, '.Power'].join('');
let adapterPlayerInstancePowerSelector: string = [pageItemTemp.adapterPlayerInstance, 'Players', pageItemTemp.mediaDevice, 'Power'].join('.');
let stateVal = getState(adapterPlayerInstancePowerSelector).val;
if (stateVal === 0) {
setState(adapterPlayerInstancePowerSelector, 1);
@@ -7793,7 +7722,7 @@ function GenerateDetailPage(type: NSPanel.PopupType, optional: NSPanel.mediaOpti
if (existsState(id + '.TEMPERATURE')) {
colorTemp = 0;
if (getState(id + '.TEMPERATURE').val != null) {
if (pageItem.minValueColorTemp !== undefined && pageItem.maxValueColorTemp !== undefined) {
if (pageItem.minValueColorTemp !== undefined && pageItem.minValueColorTemp !== undefined) {
colorTemp = Math.trunc(scale(getState(id + '.TEMPERATURE').val, pageItem.minValueColorTemp, pageItem.maxValueColorTemp!, 100, 0));
} else {
colorTemp = getState(id + '.TEMPERATURE').val;
@@ -8257,21 +8186,9 @@ function GenerateDetailPage(type: NSPanel.PopupType, optional: NSPanel.mediaOpti
const vTempAdapter = (pageItem.adapterPlayerInstance!).split('.');
const vAdapter: NSPanel.PlayerType = vTempAdapter[0] as NSPanel.PlayerType;
if (optional == 'seek') {
const actualStateTemp: number = getState(pageItem.adapterPlayerInstance + 'root.' + pageItem.mediaDevice + '.seek').val;
actualState = Math.round(actualStateTemp / 10) * 10 + '%';
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 == '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';
@@ -8409,7 +8326,7 @@ function GenerateDetailPage(type: NSPanel.PopupType, optional: NSPanel.mediaOpti
//Limit 900 characters, then memory overflow --> Shorten as much as possible
let temp_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++) {
for (let track_index = 0; track_index < 45; track_index++) {
let temp_cut_array = getAttr(globalTracklist, track_index + '.title');
/* Volumio: @local/NAS no title -> name */
if (temp_cut_array == undefined) {
@@ -8854,10 +8771,9 @@ function HandleScreensaverUpdate(): void {
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++) {
@@ -8911,7 +8827,7 @@ function HandleScreensaverUpdate(): void {
}
}
const temp = config.bottomScreensaverEntity[i].ScreensaverEntityIconColor
const temp = config.bottomScreensaverEntity[4].ScreensaverEntityIconColor
if (temp && typeof temp == 'string' && existsObject(temp)) {
iconColor = getState(temp).val;
}
@@ -8982,8 +8898,6 @@ function HandleScreensaverUpdate(): void {
SendToPanel({ payload: 'weatherUpdate~' + payloadString });
HandleScreensaverStatusIcons();
HandleScreensaverColors();
}
} catch (err: any) {
@@ -9977,8 +9891,8 @@ namespace NSPanel {
export type SerialType = 'button' | 'light' | 'shutter' | 'text' | 'input_sel' | 'timer' | 'number' | 'fan'
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'
export type roles = 'light' |'socket'|'dimmer'| 'hue' | 'rgb' | 'rgbSingle' | 'cd' | 'blind' | 'door' | 'window' | 'volumeGroup' | 'volume'
| 'info' | 'humidity' | 'temperature' | 'value.temperature' | 'value.humidity' | 'sensor.door' | 'sensor.window' | 'thermostat' | 'warning' | 'ct'
| 'cie' | 'gate' | 'motion' | 'buttonSensor' | 'button' | 'value.time' | 'level.timer' | 'value.alarmtime' | 'level.mode.fan' | 'lock' | 'slider'
| 'switch.mode.wlan' | 'media' | 'timeTable' | 'airCondition'
@@ -10038,7 +9952,7 @@ namespace NSPanel {
export type PageGrid2 = {
type: 'cardGrid2',
items: [PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?],
items: [PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?],
} & PageBaseType
export type PageThermo = {
@@ -10142,6 +10056,7 @@ namespace NSPanel {
navigate?: boolean,
colormode?: string,
colorScale?: IconScaleElement,
//adapterPlayerInstance?: adapterPlayerInstanceType,
targetPage?: string,
modeList?: string[],
hidePassword?: boolean,

View File

@@ -1,5 +1,5 @@
/*-----------------------------------------------------------------------
TypeScript v4.3.3.43 zur Steuerung des SONOFF NSPanel mit dem ioBroker by @Armilar / @TT-Tom / @ticaki / @Britzelpuf / @Sternmiere / @ravenS0ne
TypeScript v4.3.3.39 zur Steuerung des SONOFF NSPanel mit dem ioBroker by @Armilar / @TT-Tom / @ticaki / @Britzelpuf / @Sternmiere / @ravenS0ne
- abgestimmt auf TFT 53 / v4.3.3 / BerryDriver 9 / Tasmota 13.3.0
@joBr99 Projekt: https://github.com/joBr99/nspanel-lovelace-ui/tree/main/ioBroker
NsPanelTs.ts (dieses TypeScript in ioBroker) Stable: https://github.com/joBr99/nspanel-lovelace-ui/blob/main/ioBroker/NsPanelTs.ts
@@ -107,16 +107,7 @@ ReleaseNotes:
- 20.01.2024 - v4.3.3.38 Add: click on indicatorIcon navigate to Page
- 23.01.2024 - v4.3.3.39 Add: Optional setOn & setOff for HW button with mode 'set'
- 28.01.2024 - v4.3.3.39 Fix: ack for read-only state
- 03.02.2024 - v4.3.3.40 Fix: RGB maxValueColorTemp
- 05.02.2024 - v4.3.3.40 Fix: SqueezeboxRPC-Media-Player and add some Functions
- 06.02.2024 - v4.3.3.41 Fix: activeBrightness -> null
- 06.02.2024 - v4.3.3.41 Fix: bHome -> corrected PageId
- 07.02.2024 - v4.3.3.42 Minor Fixes
- 09.02.2024 - v4.3.3.42 Change pageId with Alias in Communication with HMI
- 09.02.2024 - v4.3.3.42 Spotify Media-Player: Dynamic loading of the speaker list, playlist, tracklist, fix repeat, add seek, add elapsed/duration
- 10.02.2024 - v4.3.3.42 Spotify Minor Fixes; Add miValue / maxValue to Volume-Slider
- 10.02.2024 - v4.3.3.43 Fix: cardGrid2 => 9 Entities for Layout 'us-p' issue #1167
- 11.02.2024 - v4.3.3.43 Fix VolumeSlider
Todo:
- XX.XX.XXXX - v5.0.0 Change the bottomScreensaverEntity (rolling) if more than 6 entries are defined
@@ -138,8 +129,7 @@ Mögliche Seiten-Ansichten:
(die 4 kleineren Icons können als Wetter-Vorschau + 4Tage (Symbol + Höchsttemperatur) oder zur Anzeige definierter Infos konfiguriert werden)
cardEntities Page - 4 vertikale angeordnete 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 bzw. beim US-Modell im Portrait-Modus
9 horizontal angeordnete Steuerelemente in 3 Reihen a 3 Steuerelemente - auch als Subpage
cardGrid2 Page - 8 horizontal angeordnete Steuerelemente in 2 Reihen a 4 Steuerelemente - auch als Subpage
cardThermo Page - Thermostat 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
@@ -215,7 +205,6 @@ Upgrades in Konsole:
---------------------------------------------------------------------------------------
*/
/******************************* Begin CONFIG Parameter *******************************/
// DE: liefert bei true detailliertere Meldundgen im Log.
@@ -985,7 +974,7 @@ export const config: Config = {
// _________________________________ DE: Ab hier keine Konfiguration mehr _____________________________________
// _________________________________ EN: No more configuration from here _____________________________________
const scriptVersion: string = 'v4.3.3.43';
const scriptVersion: string = 'v4.3.3.39';
const tft_version: string = 'v4.3.3';
const desired_display_firmware_version = 53;
const berry_driver_version = 9;
@@ -1525,11 +1514,11 @@ InitActiveBrightness();
on({id: [NSPanel_Path + 'ScreensaverInfo.activeBrightness'], change: 'ne'}, async function (obj) {
try {
let active = getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val ?? -1;
let active = getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val;
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) + '~' + Sliders2 });
InitDimmode();
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) + '~' + Sliders2 });
InitDimmode();
}
} catch (err:any) {
log('error at trigger activeBrightness: ' + err.message, 'warn');
@@ -1538,7 +1527,7 @@ on({id: [NSPanel_Path + 'ScreensaverInfo.activeBrightness'], change: 'ne'}, asyn
on({id: [NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness'], change: "ne"}, async function (obj) {
try {
let active = getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val ?? 80;
let active = getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val;
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');
@@ -1568,7 +1557,7 @@ on({id: [NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness'], change: "ne"
on({id: String(NSPanel_Path) + 'ScreensaverInfo.Trigger_Dimmode', change: "ne"}, async function (obj) {
try {
let active = getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val ?? 80;
let active = getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val;
if (obj.state.val) {
SendToPanel({ payload: 'dimmode~' + 100 + '~' + active + '~' + rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor) + '~' + Sliders2 });
} else {
@@ -1919,8 +1908,8 @@ on({id: [NSPanel_Path + 'PageNavi'], change: "any"}, async function (obj) {
//----------------------Begin Dimmode
function ScreensaverDimmode(timeDimMode:NSPanel.DimMode) {
try {
let active = getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val ?? 80
let dimmode = getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val ?? -1
let active = getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val
let dimmode = getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val
if (Debug) {
log('function ScreensaverDimmode RGB-Wert HMIDark' + rgb_dec565(HMIDark), 'info');
}
@@ -2027,12 +2016,12 @@ async function InitDimmode() {
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) + '~' + Sliders2 });
SendToPanel({ payload: 'dimmode~' + getState(NSPanel_Path + 'ScreensaverInfo.activeDimmodeBrightness').val + '~' + getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val + '~' + rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor) + '~' + Sliders2 });
} 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) + '~' + Sliders2 });
SendToPanel({ payload: 'dimmode~' + timeDimMode.brightnessDay + '~' + getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val + '~' + rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor) + '~' + Sliders2 });
} else {
SendToPanel({ payload: 'dimmode~' + timeDimMode.brightnessNight + '~' + getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val ?? 80 + '~' + rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor) + '~' + Sliders2 });
SendToPanel({ payload: 'dimmode~' + timeDimMode.brightnessNight + '~' + getState(NSPanel_Path + 'ScreensaverInfo.activeBrightness').val + '~' + rgb_dec565(config.defaultBackgroundColor) + '~' + rgb_dec565(globalTextColor) + '~' + Sliders2 });
}
ScreensaverDimmode(timeDimMode);
}
@@ -3386,11 +3375,7 @@ function GeneratePageElements(page: PageType): string {
maxItems = 6;
break;
case 'cardGrid2':
if (getState(NSPanel_Path + 'NSPanel_Version').val == 'us-p') {
maxItems = 9;
} else {
maxItems = 8;
};
maxItems = 8;
break;
}
@@ -4769,25 +4754,67 @@ function unsubscribeMediaSubscriptions(): void {
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!);
}
id + '.VOLUME',
id + '.ARTIST',
id + '.ALBUM',
id + '.TITLE',
id + '.REPEAT',
id + '.SHUFFLE'], change: "any"}, async function () {
(function () { if (timeoutMedia) { clearTimeout(timeoutMedia); timeoutMedia = null; } })();
timeoutMedia = setTimeout(async function () {
if (useMediaEvents) {
GeneratePage(activePage!);
setTimeout(async function () {
GeneratePage(activePage!);
}, 3000);
}
},50)
});
}
function subscribeMediaSubscriptionsSonosAdd(id: string): void {
on({id: [id + '.QUEUE'], change: "any", ack: true}, async function () {
if (useMediaEvents && pageCounter == 1) {
GeneratePage(activePage!);
}
on({id: [id + '.QUEUE',
id + '.DURATION',
id + '.ELAPSED'], change: "any"}, async function () {
(function () { if (timeoutMedia) { clearTimeout(timeoutMedia); timeoutMedia = null; } })();
timeoutMedia = setTimeout(async function () {
if (useMediaEvents) {
GeneratePage(activePage!);
setTimeout(async function () {
GeneratePage(activePage!);
}, 50);
}
},50)
});
}
function subscribeMediaSubscriptionsAlexaAdd(id: string): void {
on({id: [id + '.DURATION',
id + '.ELAPSED'], change: "any"}, async function () {
(function () { if (timeoutMedia) { clearTimeout(timeoutMedia); timeoutMedia = null; } })();
timeoutMedia = setTimeout(async function () {
if (useMediaEvents) {
GeneratePage(activePage!);
setTimeout(async function () {
GeneratePage(activePage!);
}, 50);
}
},50)
});
}
function subscribeMediaSubscriptionsBoseAdd(id: string): void {
on({id: [id + '.DURATION',
id + '.ELAPSED'], change: "any"}, async function () {
(function () { if (timeoutMedia) { clearTimeout(timeoutMedia); timeoutMedia = null; } })();
timeoutMedia = setTimeout(async function () {
if (useMediaEvents) {
GeneratePage(activePage!);
setTimeout(async function () {
GeneratePage(activePage!);
}, 50);
}
},50)
});
}
@@ -4916,13 +4943,6 @@ async function createAutoMediaAlias (id: string, mediaDevice: string, adapterPla
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, <iobJS.StateCommon> {type: 'string', role: 'media.duration.text', name: 'DURATION'});
await createAliasAsync(id + '.ELAPSED', dpPath + 'player.progress', true, <iobJS.StateCommon> {type: 'string', role: 'media.elapsed.text', name: 'ELAPSED'});
}
}
break;
@@ -4960,12 +4980,7 @@ async function createAutoMediaAlias (id: string, mediaDevice: string, adapterPla
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, <iobJS.StateCommon> {type: 'string', role: 'media.duration', name: 'DURATION'});
//await createAliasAsync(id + '.ELAPSED', dpPath + 'player.progress', true, <iobJS.StateCommon> {type: 'string', role: 'media.elapsed.text', name: 'ELAPSED'});
}
}
break;
case "squeezeboxrpc.0.":
@@ -4995,14 +5010,13 @@ async function createAutoMediaAlias (id: string, mediaDevice: string, adapterPla
await createAliasAsync(id + '.STATE', dpPath + '.Power', true, <iobJS.StateCommon> {type: 'number', role: 'switch', name: 'STATE'});
await createAliasAsync(id + '.VOLUME', dpPath + '.Volume', true, <iobJS.StateCommon> {type: 'number', role: 'level.volume', name: 'VOLUME'});
await createAliasAsync(id + '.VOLUME_ACTUAL', dpPath + '.Volume', true, <iobJS.StateCommon> {type: 'number', role: 'value.volume', name: 'VOLUME_ACTUAL'});
await createAliasAsync(id + '.SHUFFLE', dpPath + '.PlaylistShuffle', true, <iobJS.StateCommon> {type: 'string', role: 'media.mode.shuffle', name: 'SHUFFLE'});
await createAliasAsync(id + '.SHUFFLE', dpPath + '.PlaylistShuffle', true, <iobJS.StateCommon> {type: 'string', role: 'media.mode.shuffle', name: 'SHUFFLE', alias: {id: dpPath + '.PlaylistShuffle', read: 'val !== 0 ? \'on\' : \'off\'', write: 'val === \'off\' ? 0 : 1'}});
await createAliasAsync(id + '.REPEAT', dpPath + '.PlaylistRepeat', true, <iobJS.StateCommon> {type: 'number', role: 'media.mode.repeat', name: 'REPEAT'});
await createAliasAsync(id + '.DURATION', dpPath + '.Duration', true, <iobJS.StateCommon> {type: 'string', role: 'media.duration', name: 'DURATION'});
await createAliasAsync(id + '.ELAPSED', dpPath + '.Time', true, <iobJS.StateCommon> {type: 'string', role: 'media.elapsed', name: 'ELAPSED'});
} catch (err: any) {
log('error at function createAutoMediaAlias Adapter Squeezebox: ' + err.message, 'warn');
}
}
}
break;
case "bosesoundtouch.0.":
@@ -5061,13 +5075,29 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
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;
// Some magic to change the ID of the alias, since speakers are not a property but separate objects
if(v2Adapter == 'squeezeboxrpc') {
if(id && getObject(id).type != 'channel') {
id = id + '.' + page.items[0].mediaDevice;
page.items[0].id = id;
page.heading = page.items[0].mediaDevice ?? '';
} else {
let idParts = id.split('.');
if(idParts[idParts.length-1] !== page.items[0].mediaDevice) {
idParts[idParts.length-1] = page.items[0].mediaDevice ?? '';
id = idParts.join('.');
page.items[0].id = id;
page.heading = page.items[0].mediaDevice ?? '';
}
}
}
let vMediaDevice = (page.items[0].mediaDevice != undefined) ? page.items[0].mediaDevice : '';
@@ -5075,7 +5105,7 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
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' });
@@ -5088,10 +5118,10 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
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 (v2Adapter == 'alexa2') {
subscribeMediaSubscriptionsAlexaAdd(page.items[0].id);
} else if (v2Adapter == 'bosesoundtouch') {
subscribeMediaSubscriptionsBoseAdd(page.items[0].id);
}
}
}
@@ -5100,7 +5130,11 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
alwaysOn = true;
subscribeMediaSubscriptions(page.items[0].id);
if (v2Adapter == 'sonos') {
subscribeMediaSubscriptionsSonosAdd(page.items[0].id);
subscribeMediaSubscriptionsSonosAdd(page.items[0].id);
} else if (v2Adapter == 'alexa2') {
subscribeMediaSubscriptionsAlexaAdd(page.items[0].id);
} else if (v2Adapter == 'bosesoundtouch') {
subscribeMediaSubscriptionsBoseAdd(page.items[0].id);
}
} else if (page.type == 'cardMedia' && pageCounter == -1) {
//Do Nothing
@@ -5111,8 +5145,8 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
if (existsObject(id)) {
let name = getState(id + '.ALBUM').val;
let title = getState(id + '.TITLE').val;
if (title.length > 26) {
title = title.slice(0, 26) + '...';
if (title.length > 27) {
title = title.slice(0, 27) + '...';
}
if (existsObject(id + '.DURATION') && existsObject(id + '.ELAPSED')) {
if (v2Adapter == 'alexa2') {
@@ -5133,15 +5167,8 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
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 = '';
}
}
title = title + ' (' + vElapsed + '|' + vDuration + ')';
} 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) {
@@ -5170,51 +5197,7 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
title = title + ' (' + vElapsed + '|' + vDuration + ')';
}
}
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
@@ -5225,23 +5208,19 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
media_icon = Icons.GetIcon('spotify');
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:') {
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) + '...';
} else if (name.substring(0,6) == 'Track') {
name = 'Spotify-Premium';
}
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 (author.length > 30) {
author = getState(id + '.ARTIST').val;
}
if ((getState(id + '.ARTIST').val).length == 0) {
author = findLocale('media','no_music_to_control');
@@ -5292,10 +5271,9 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
//Logitech Squeezebox RPC
if (v2Adapter == 'squeezeboxrpc') {
media_icon = Icons.GetIcon('dlna');
if (name.length == 0) {
name = page.heading;
} else if (name.length > 16) {
name = name.slice(0,16) + '...'
let nameLength = name.length;
if (nameLength == 0) {
name = page.items[0].mediaDevice;
}
}
@@ -5313,8 +5291,6 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
}
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) {
@@ -5327,12 +5303,12 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
//Volumio
if (v2Adapter == 'volumio') {
media_icon = Icons.GetIcon('clock-time-twelve-outline');
if (name != undefined) { author = author + " | " + name; }
name = page.heading;
if (name != undefined) { author = author + " [" + name + "]"; }
name = getState(vInstance + 'info.name').val; /* page.heading;
getState(id + '.TRACK').val; */
}
let volume = scale(getState(id + '.VOLUME').val, activePage!.items[0]!.minValue ?? 0, activePage!.items[0]!.maxValue ?? 100, 100, 0);
let volume = getState(id + '.VOLUME').val;
let iconplaypause = Icons.GetIcon('pause'); //pause
let shuffle_icon = Icons.GetIcon('shuffle-variant'); //shuffle
let onoffbutton = 1374;
@@ -5340,9 +5316,7 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
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
if (v2Adapter == 'volumio') { shuffle_icon = Icons.GetIcon('refresh'); } //Volumio: refresh playlist
//For all players
@@ -5373,7 +5347,7 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
}
}
let currentSpeaker: string = findLocale('media','no_speaker_found');
let currentSpeaker = findLocale('media','no_speaker_found');
if (v2Adapter == 'alexa2') {
currentSpeaker = getState(([page.items[0].adapterPlayerInstance, 'Echo-Devices.', page.items[0].mediaDevice, '.Info.name'].join(''))).val;
@@ -5385,8 +5359,6 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
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;
}
//-------------------------------------------------------------------------------------------------------------
// All Alexa devices (the online / player and commands directory is available) are listed and linked below
@@ -5403,11 +5375,6 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
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 {
let i_list = Array.prototype.slice.apply($('[state.id="' + page.items[0].adapterPlayerInstance + 'Echo-Devices.*.Info.name"]'));
for (let i_index in i_list) {
@@ -5433,7 +5400,7 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
if (speakerListArray.length > 0) {
speakerListIconCol = rgb_dec565(HMIOn);
speakerListString = 'input_sel' + '~' +
tid + '?speakerlist' + '~' +
id + '?speakerlist' + '~' +
Icons.GetIcon('speaker') + '~' +
speakerListIconCol + '~' +
findLocale('media','speaker') + '~' +
@@ -5467,13 +5434,10 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
});
}
/* Spotify: get all playlists if empty */
} else if (v2Adapter == 'spotify-premium') {
page.items[0].playList = (getState(page.items[0].adapterPlayerInstance + 'playlists.playlistListString').val).split(';');
}
playListIconCol = rgb_dec565(HMIOn);
playListString = 'input_sel' + '~' +
tid + '?playlist' + '~' +
id + '?playlist' + '~' +
Icons.GetIcon('playlist-play') + '~' +
playListIconCol + '~' +
//'PlayL ' + page.heading + '~' +
@@ -5483,7 +5447,9 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
//InSel Tracklist
globalTracklist = ''
if (v2Adapter == 'spotify-premium') {
globalTracklist = ' ' //Todo
}
let trackListString: string = '~~~~~~'
let trackListIconCol = rgb_dec565(HMIOff);
if (v2Adapter == 'volumio') { /* Volumio: get queue */
@@ -5514,8 +5480,8 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
}, 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);
} 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;
@@ -5534,19 +5500,12 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
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')
}
}
if (globalTracklist != null && globalTracklist.length != 0) {
trackListIconCol = rgb_dec565(HMIOn);
trackListString = 'input_sel' + '~' +
tid + '?tracklist' + '~' +
id + '?tracklist' + '~' +
Icons.GetIcon('animation-play-outline') + '~' +
trackListIconCol + '~' +
findLocale('media','tracklist') + '~' +
@@ -5559,7 +5518,7 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
if (page.items[0].equalizerList != undefined) {
equalizerListIconCol = rgb_dec565(HMIOn);
equalizerListString = 'input_sel' + '~' +
tid + '?equalizer' + '~' +
id + '?equalizer' + '~' +
Icons.GetIcon('equalizer-outline') + '~' +
equalizerListIconCol + '~' +
findLocale('media','equalizer') + '~' +
@@ -5568,7 +5527,7 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
let equalizerListIconCol = rgb_dec565(HMIOn);
//equalizerListString is used for favariteList
equalizerListString = 'input_sel' + '~' +
tid + '?favorites' + '~' +
id + '?favorites' + '~' +
Icons.GetIcon('playlist-star') + '~' +
equalizerListIconCol + '~' +
findLocale('media','favorites') + '~' +
@@ -5617,10 +5576,13 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
repeatIcon = Icons.GetIcon('repeat');
repeatIconCol = rgb_dec565(HMIOn);
}
else {
repeatIcon = Icons.GetIcon('repeat-off');
}
} 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);
repeatIconCol = rgb_dec565(colMediaIcon);
}
}
@@ -5631,7 +5593,7 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
v2Adapter == 'volumio' ||
v2Adapter == 'squeezeboxrpc') {
repeatButtonString = 'button' + '~' +
tid + '?repeat' + '~' +
id + '?repeat' + '~' +
repeatIcon + '~' +
repeatIconCol + '~' +
'Repeat' + '~' +
@@ -5644,40 +5606,22 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
if (v2Adapter == 'sonos') {
if (page.items[0].crossfade == undefined || page.items[0].crossfade == false) {
toolsString = 'input_sel' + '~' +
tid + '?seek' + '~' +
id + '?seek' + '~' +
media_icon + '~' +
toolsIconCol + '~' +
findLocale('media','seek') + '~' +
'media5~'
} else {
toolsString = 'input_sel' + '~' +
tid + '?crossfade' + '~' +
id + '?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 + '' + '~' +
id + '' + '~' +
media_icon + '~' +
toolsIconCol + '~' +
findLocale('media','tools') + '~' +
@@ -5688,7 +5632,7 @@ function GenerateMediaPage(page: NSPanel.PageMedia): NSPanel.Payload[] {
payload: 'entityUpd~' + //entityUpd
name + '~' + //heading
GetNavigationString(pageId) + '~' + //navigation
tid + '~' + //internalNameEntiy
id + '~' + //internalNameEntiy
title + '~' + //title
rgb_dec565(colMediaTitle) + '~' + //titleColor
author + '~' + //author
@@ -6107,6 +6051,8 @@ function subscribePowerSubscriptions(id: string): void {
function GeneratePowerPage(page: NSPanel.PagePower): NSPanel.Payload[] {
try {
if (!page.items[0].id) throw new Error ('Missing pageItem.id for PowerPage!');
let obj:object = {};
let demoMode = false;
if (page.items[0].id == undefined){
@@ -6498,21 +6444,17 @@ function HandleButtonEvent(words: any): void {
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 'bHome':
if (Debug) {
log('HandleButtonEvent -> bHome: ' + words[4] + ' - ' + pageId, 'info');
}
UnsubscribeWatcher();
if (activePage!.home != undefined) {
GeneratePage(eval(activePage!.home));
} else {
GeneratePage(config.pages[0]);
}
break;
case 'notifyAction':
if (words[4] == 'yes') {
setState(popupNotifyInternalName, <iobJS.State>{ val: words[2], ack: true });
@@ -6633,20 +6575,19 @@ function HandleButtonEvent(words: any): void {
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');
let stateSpotifyRepeat = getState(id + '.REPEAT').val
if (stateSpotifyRepeat == 'none') {
setIfExists(id + '.REPEAT', 'all');
} else if (stateSpotifyRepeat == 'all') {
setIfExists(id + '.REPEAT', 'one');
} else if (stateSpotifyRepeat == 'one') {
setIfExists(id + '.REPEAT', 'none');
}
GeneratePage(activePage!);
break;
case 'bosesoundtouch':
if (Debug) log(adapterInstanceRepeat);
let stateBoseRepeat = getState(id + '.REPEAT').val;
let stateBoseRepeat = getState(id + '.REPEAT').val
if (stateBoseRepeat == 'REPEAT_OFF') {
setIfExists(adapterInstanceRepeat + 'key', 'REPEAT_ALL');
} else if (stateBoseRepeat == 'REPEAT_ALL') {
@@ -6657,7 +6598,7 @@ function HandleButtonEvent(words: any): void {
GeneratePage(activePage!);
break;
case 'sonos':
let stateSonosRepeat = getState(id + '.REPEAT').val;
let stateSonosRepeat = getState(id + '.REPEAT').val
if (stateSonosRepeat == 0) {
setIfExists(id + '.REPEAT', 1);
} else if (stateSonosRepeat == 1) {
@@ -6872,8 +6813,8 @@ function HandleButtonEvent(words: any): void {
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 adapterPlayerInstanceStateSeceltor: string = [pageItemTemp.adapterPlayerInstance, 'Players', pageItemTemp.mediaDevice, 'state'].join('.');
if (Debug) log('HandleButtonEvent media-pause Squeezebox-> adapterPlayerInstanceStateSeceltor: ' + adapterPlayerInstanceStateSeceltor, 'info');
let stateVal = getState(adapterPlayerInstanceStateSeceltor).val;
if (stateVal == 0) {
setState(adapterPlayerInstanceStateSeceltor, 1);
@@ -6936,13 +6877,6 @@ function HandleButtonEvent(words: any): void {
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') {
@@ -6956,11 +6890,15 @@ function HandleButtonEvent(words: any): void {
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));
pageCounter = -1;
(function () { if (timeoutSlider) { clearTimeout(timeoutSlider); timeoutSlider = null; } })();
timeoutSlider = setTimeout(async function () {
setIfExists(id + '.VOLUME', parseInt(words[4]));
setTimeout(async function () {
pageCounter = 1;
GeneratePage(activePage!);
}, 3000);
}, 20);
break;
case 'mode-speakerlist':
let pageItem = findPageItem(id);
@@ -7054,7 +6992,7 @@ function HandleButtonEvent(words: any): void {
});
break;
case 'squeezeboxrpc':
setState([adapterInstancePL, 'Players.', pageItemPL.mediaDevice, '.cmdPlayFavorite'].join(''), words[4]);
setState([pageItemPL.adapterPlayerInstance, 'Players', pageItemPL.mediaDevice, 'cmdPlayFavorite'].join('.'), words[4]);
break;
case "bosesoundtouch":
if (Debug) log('bosesoundtouch - playlist ' + pageItemPL.adapterPlayerInstance + ' - ' + words[4]);
@@ -7084,9 +7022,8 @@ function HandleButtonEvent(words: any): void {
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);
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'));
break;
case 'sonos':
setState(adapterInstanceTL + 'root.' + pageItemTL.mediaDevice + '.current_track_number', parseInt(words[4]) + 1);
@@ -7111,7 +7048,7 @@ function HandleButtonEvent(words: any): void {
break;
case 'squeezeboxrpc':
//@ts-ignore Fehler kommt von findPageItem in vscode
setState([adapterInstanceTL, 'Players.', pageItemTL.mediaDevice, '.PlaylistCurrentIndex'].join(''), words[4]);
setState([pageItemPL.adapterPlayerInstance, 'Players', pageItemPL.mediaDevice, 'PlaylistCurrentIndex'].join('.'), words[4]);
break;
case "bosesoundtouch":
break;
@@ -7164,14 +7101,6 @@ function HandleButtonEvent(words: any): void {
let deviceAdapterSK: NSPanel.PlayerType = adapterSK[0] as NSPanel.PlayerType;
switch (deviceAdapterSK) {
case 'spotify-premium':
setState(adapterInstanceSK + 'player.progressPercentage', 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');
@@ -7231,7 +7160,7 @@ function HandleButtonEvent(words: any): void {
if (!isPageMediaItem(pageItemTemp)) break;
let adapterInstance = pageItemTemp.adapterPlayerInstance.split('.');
if (adapterInstance[0] == 'squeezeboxrpc') {
let adapterPlayerInstancePowerSelector: string = [pageItemTemp.adapterPlayerInstance, 'Players.', pageItemTemp.mediaDevice, '.Power'].join('');
let adapterPlayerInstancePowerSelector: string = [pageItemTemp.adapterPlayerInstance, 'Players', pageItemTemp.mediaDevice, 'Power'].join('.');
let stateVal = getState(adapterPlayerInstancePowerSelector).val;
if (stateVal === 0) {
setState(adapterPlayerInstancePowerSelector, 1);
@@ -7793,7 +7722,7 @@ function GenerateDetailPage(type: NSPanel.PopupType, optional: NSPanel.mediaOpti
if (existsState(id + '.TEMPERATURE')) {
colorTemp = 0;
if (getState(id + '.TEMPERATURE').val != null) {
if (pageItem.minValueColorTemp !== undefined && pageItem.maxValueColorTemp !== undefined) {
if (pageItem.minValueColorTemp !== undefined && pageItem.minValueColorTemp !== undefined) {
colorTemp = Math.trunc(scale(getState(id + '.TEMPERATURE').val, pageItem.minValueColorTemp, pageItem.maxValueColorTemp!, 100, 0));
} else {
colorTemp = getState(id + '.TEMPERATURE').val;
@@ -8257,21 +8186,9 @@ function GenerateDetailPage(type: NSPanel.PopupType, optional: NSPanel.mediaOpti
const vTempAdapter = (pageItem.adapterPlayerInstance!).split('.');
const vAdapter: NSPanel.PlayerType = vTempAdapter[0] as NSPanel.PlayerType;
if (optional == 'seek') {
const actualStateTemp: number = getState(pageItem.adapterPlayerInstance + 'root.' + pageItem.mediaDevice + '.seek').val;
actualState = Math.round(actualStateTemp / 10) * 10 + '%';
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 == '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';
@@ -8409,7 +8326,7 @@ function GenerateDetailPage(type: NSPanel.PopupType, optional: NSPanel.mediaOpti
//Limit 900 characters, then memory overflow --> Shorten as much as possible
let temp_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++) {
for (let track_index = 0; track_index < 45; track_index++) {
let temp_cut_array = getAttr(globalTracklist, track_index + '.title');
/* Volumio: @local/NAS no title -> name */
if (temp_cut_array == undefined) {
@@ -8854,10 +8771,9 @@ function HandleScreensaverUpdate(): void {
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++) {
@@ -8911,7 +8827,7 @@ function HandleScreensaverUpdate(): void {
}
}
const temp = config.bottomScreensaverEntity[i].ScreensaverEntityIconColor
const temp = config.bottomScreensaverEntity[4].ScreensaverEntityIconColor
if (temp && typeof temp == 'string' && existsObject(temp)) {
iconColor = getState(temp).val;
}
@@ -8982,8 +8898,6 @@ function HandleScreensaverUpdate(): void {
SendToPanel({ payload: 'weatherUpdate~' + payloadString });
HandleScreensaverStatusIcons();
HandleScreensaverColors();
}
} catch (err: any) {
@@ -9977,8 +9891,8 @@ namespace NSPanel {
export type SerialType = 'button' | 'light' | 'shutter' | 'text' | 'input_sel' | 'timer' | 'number' | 'fan'
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'
export type roles = 'light' |'socket'|'dimmer'| 'hue' | 'rgb' | 'rgbSingle' | 'cd' | 'blind' | 'door' | 'window' | 'volumeGroup' | 'volume'
| 'info' | 'humidity' | 'temperature' | 'value.temperature' | 'value.humidity' | 'sensor.door' | 'sensor.window' | 'thermostat' | 'warning' | 'ct'
| 'cie' | 'gate' | 'motion' | 'buttonSensor' | 'button' | 'value.time' | 'level.timer' | 'value.alarmtime' | 'level.mode.fan' | 'lock' | 'slider'
| 'switch.mode.wlan' | 'media' | 'timeTable' | 'airCondition'
@@ -10038,7 +9952,7 @@ namespace NSPanel {
export type PageGrid2 = {
type: 'cardGrid2',
items: [PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?],
items: [PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?, PageItem?],
} & PageBaseType
export type PageThermo = {
@@ -10142,6 +10056,7 @@ namespace NSPanel {
navigate?: boolean,
colormode?: string,
colorScale?: IconScaleElement,
//adapterPlayerInstance?: adapterPlayerInstanceType,
targetPage?: string,
modeList?: string[],
hidePassword?: boolean,

View File

@@ -1,6 +1,6 @@
# https://developers.home-assistant.io/docs/add-ons/configuration#add-on-config
name: NSPanel Lovelace UI Addon
version: "4.7.80"
version: "4.7.74"
slug: nspanel-lovelace-ui
description: NSPanel Lovelace UI Addon
services:

View File

@@ -209,11 +209,11 @@ class HAEntity(panel_cards.Entity):
forecast = libs.home_assistant.execute_script(
entity_name=self.entity_id,
domain='weather',
service="get_forecasts",
service="get_forecast",
service_data={
'type': forecast_type
}
).get(self.entity_id,{}).get("forecast", [])
).get("forecast", [])
if len(forecast) > pos:
forcast_pos = forecast[pos]
forcast_condition = forcast_pos.get("condition", "")

View File

@@ -20,32 +20,27 @@ def calculate_dim_values(sleepTracking, sleepTrackingZones, sleepBrightness, scr
dimmode = sleepBrightness
elif isinstance(sleepBrightness, list):
logging.error("list style config for sleepBrightness no longer supported")
#elif sleepBrightness.startswith("ha:"):
# time.sleep(1)
# dimmode = int(float(libs.home_assistant.get_template(sleepBrightness)[3:]))
# involved_entities.extend(libs.home_assistant.get_template_listener_entities(sleepBrightness))
elif sleepBrightness.startswith("ha:"):
time.sleep(1)
dimmode = int(float(libs.home_assistant.get_template(sleepBrightness)[3:]))
involved_entities.extend(libs.home_assistant.get_template_listener_entities(sleepBrightness))
elif libs.home_assistant.is_existent(sleepBrightness):
involved_entities.append(sleepBrightness)
try:
dimmode = int(float(libs.home_assistant.get_entity_data(sleepBrightness).get('state', 10)))
except ValueError:
print("sleepBrightness entity invalid")
dimmode = int(float(libs.home_assistant.get_entity_data(sleepBrightness).get('state', 10)))
if screenBrightness:
if isinstance(screenBrightness, int):
dimValueNormal = screenBrightness
elif isinstance(screenBrightness, list):
logging.error("list style config for screenBrightness no longer supported")
#elif screenBrightness.startswith("ha:"):
# time.sleep(1)
# dimValueNormal = int(float(libs.home_assistant.get_template(screenBrightness)[3:]))
# involved_entities.extend(libs.home_assistant.get_template_listener_entities(screenBrightness))
elif screenBrightness.startswith("ha:"):
time.sleep(1)
dimValueNormal = int(float(libs.home_assistant.get_template(screenBrightness)[3:]))
involved_entities.extend(libs.home_assistant.get_template_listener_entities(screenBrightness))
elif libs.home_assistant.is_existent(screenBrightness):
involved_entities.append(screenBrightness)
try:
dimValueNormal = int(float(libs.home_assistant.get_entity_data(screenBrightness).get('state', 100)))
except ValueError:
print("screenBrightness entity invalid")
dimValueNormal = int(float(libs.home_assistant.get_entity_data(screenBrightness).get('state', 100)))
# force sleep brightness to zero in case sleepTracking is active
if sleepTracking:
if libs.home_assistant.is_existent(sleepTracking):

View File

@@ -1,4 +1,4 @@
paho-mqtt==1.6.1
paho-mqtt
pyyaml
websockets
websocket-client
@@ -7,4 +7,4 @@ python-dateutil
scheduler
babel
watchdog
jinja2
jinja2