Compare commits

..

2 Commits

Author SHA1 Message Date
slajob
f31a8a7f38 Merge 127671047f into 760b25b7a2 2024-01-20 23:14:50 -07:00
slajob
127671047f missing friday entity from example photo 2024-01-10 18:22:05 +01:00
42 changed files with 9133 additions and 18451 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.4.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@2025.03.0
uses: home-assistant/builder@2024.01.0
with:
args: |
${{ env.BUILD_ARGS }} \

View File

@@ -36,6 +36,6 @@ jobs:
uses: actions/checkout@v4
- name: 🚀 Run Home Assistant Add-on Lint
uses: frenck/action-addon-linter@v2.18
uses: frenck/action-addon-linter@v2.15
with:
path: "./${{ matrix.path }}"

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 @@
@@ -336,7 +314,7 @@
│ Value: 0
│ Variable (int32) xc1
│ @@ -370,165 +336,50 @@
│ @@ -370,145 +336,50 @@
│ Scope : local
│ Dragging : 0
│ Send Component ID : disabled
@@ -347,10 +325,6 @@
│ - Events
│ - Touch Press Event
│ - tSend.txt="event,buttonPress2,"+entn1.txt+",button"
│ - if(entn1.txt=="")
│ - {
│ - tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
│ - }
│ - //send calc crc
│ - btlen tSend.txt,sys0
│ - crcrest 1,0xffff // reset CRC
@@ -375,10 +349,6 @@
│ - Events
│ - Touch Press Event
│ - tSend.txt="event,buttonPress2,"+entn2.txt+",button"
│ - if(entn2.txt=="")
│ - {
│ - tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
│ - }
│ - //send calc crc
│ - btlen tSend.txt,sys0
│ - crcrest 1,0xffff // reset CRC
@@ -403,10 +373,6 @@
│ - Events
│ - Touch Press Event
│ - tSend.txt="event,buttonPress2,"+entn3.txt+",button"
│ - if(entn3.txt=="")
│ - {
│ - tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
│ - }
│ - //send calc crc
│ - btlen tSend.txt,sys0
│ - crcrest 1,0xffff // reset CRC
@@ -431,10 +397,6 @@
│ - Events
│ - Touch Press Event
│ - tSend.txt="event,buttonPress2,"+entn4.txt+",button"
│ - if(entn4.txt=="")
│ - {
│ - tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
│ - }
│ - //send calc crc
│ - btlen tSend.txt,sys0
│ - crcrest 1,0xffff // reset CRC
@@ -459,10 +421,6 @@
│ - Events
│ - Touch Press Event
│ - tSend.txt="event,buttonPress2,"+entn5.txt+",button"
│ - if(entn5.txt=="")
│ - {
│ - tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
│ - }
│ - //send calc crc
│ - btlen tSend.txt,sys0
│ - crcrest 1,0xffff // reset CRC
@@ -502,7 +460,7 @@
│ Send Component ID : disabled
│ Associated Keyboard: none
│ Text : PM
│ @@ -710,15 +561,14 @@
│ @@ -690,15 +561,14 @@
│ if(tTmp.txt!="")
│ {
│ covx tTmp.txt,defaultFontColor,0,0
@@ -518,7 +476,7 @@
│ if(tAMPM.txt=="")
│ {
│ vis tAMPM,0
│ @@ -839,44 +689,34 @@
│ @@ -819,44 +689,34 @@
│ //e6Val
│ spstr strCommand.txt,e6Val.txt,"~",60
│ //f1Icon
@@ -563,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 @@
@@ -2279,7 +2259,7 @@
│ Value: 0
│ Variable (int32) xc1
│ @@ -370,165 +324,50 @@
│ @@ -370,145 +324,50 @@
│ Scope : local
│ Dragging : 0
│ Send Component ID : disabled
@@ -2290,10 +2270,6 @@
│ - Events
│ - Touch Press Event
│ - tSend.txt="event,buttonPress2,"+entn1.txt+",button"
│ - if(entn1.txt=="")
│ - {
│ - tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
│ - }
│ - //send calc crc
│ - btlen tSend.txt,sys0
│ - crcrest 1,0xffff // reset CRC
@@ -2318,10 +2294,6 @@
│ - Events
│ - Touch Press Event
│ - tSend.txt="event,buttonPress2,"+entn2.txt+",button"
│ - if(entn2.txt=="")
│ - {
│ - tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
│ - }
│ - //send calc crc
│ - btlen tSend.txt,sys0
│ - crcrest 1,0xffff // reset CRC
@@ -2346,10 +2318,6 @@
│ - Events
│ - Touch Press Event
│ - tSend.txt="event,buttonPress2,"+entn3.txt+",button"
│ - if(entn3.txt=="")
│ - {
│ - tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
│ - }
│ - //send calc crc
│ - btlen tSend.txt,sys0
│ - crcrest 1,0xffff // reset CRC
@@ -2374,10 +2342,6 @@
│ - Events
│ - Touch Press Event
│ - tSend.txt="event,buttonPress2,"+entn4.txt+",button"
│ - if(entn4.txt=="")
│ - {
│ - tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
│ - }
│ - //send calc crc
│ - btlen tSend.txt,sys0
│ - crcrest 1,0xffff // reset CRC
@@ -2402,10 +2366,6 @@
│ - Events
│ - Touch Press Event
│ - tSend.txt="event,buttonPress2,"+entn5.txt+",button"
│ - if(entn5.txt=="")
│ - {
│ - tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
│ - }
│ - //send calc crc
│ - btlen tSend.txt,sys0
│ - crcrest 1,0xffff // reset CRC
@@ -2445,7 +2405,7 @@
│ Send Component ID : disabled
│ Associated Keyboard: none
│ Text : PM
│ @@ -710,15 +549,14 @@
│ @@ -690,15 +549,14 @@
│ if(tTmp.txt!="")
│ {
│ covx tTmp.txt,defaultFontColor,0,0
@@ -2461,7 +2421,7 @@
│ if(tAMPM.txt=="")
│ {
│ vis tAMPM,0
│ @@ -839,44 +677,34 @@
│ @@ -819,44 +677,34 @@
│ //e6Val
│ spstr strCommand.txt,e6Val.txt,"~",60
│ //f1Icon
@@ -2506,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-11-22 20:00:11.734673876 +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 @@
@@ -1821,7 +1801,7 @@
+ Value: 0
+
+e (int32) xc1
+165 +324,50 @@
+145 +324,50 @@
+ Scope : local
+ Dragging : 0
+ Send Component ID : disabled
@@ -1832,10 +1812,6 @@
+nts
+ Touch Press Event
+ tSend.txt="event,buttonPress2,"+entn1.txt+",button"
+ if(entn1.txt=="")
+ {
+ tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
+ }
+ //send calc crc
+ btlen tSend.txt,sys0
+ crcrest 1,0xffff // reset CRC
@@ -1860,10 +1836,6 @@
+nts
+ Touch Press Event
+ tSend.txt="event,buttonPress2,"+entn2.txt+",button"
+ if(entn2.txt=="")
+ {
+ tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
+ }
+ //send calc crc
+ btlen tSend.txt,sys0
+ crcrest 1,0xffff // reset CRC
@@ -1888,10 +1860,6 @@
+nts
+ Touch Press Event
+ tSend.txt="event,buttonPress2,"+entn3.txt+",button"
+ if(entn3.txt=="")
+ {
+ tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
+ }
+ //send calc crc
+ btlen tSend.txt,sys0
+ crcrest 1,0xffff // reset CRC
@@ -1916,10 +1884,6 @@
+nts
+ Touch Press Event
+ tSend.txt="event,buttonPress2,"+entn4.txt+",button"
+ if(entn4.txt=="")
+ {
+ tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
+ }
+ //send calc crc
+ btlen tSend.txt,sys0
+ crcrest 1,0xffff // reset CRC
@@ -1944,10 +1908,6 @@
+nts
+ Touch Press Event
+ tSend.txt="event,buttonPress2,"+entn5.txt+",button"
+ if(entn5.txt=="")
+ {
+ tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
+ }
+ //send calc crc
+ btlen tSend.txt,sys0
+ crcrest 1,0xffff // reset CRC
@@ -2048,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)
448 Line(s) of event code
272 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)
10798 Line(s) of event code
2472 Unique 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

@@ -903,10 +903,6 @@ Text f1Icon
Events
Touch Press Event
tSend.txt="event,buttonPress2,"+entn1.txt+",button"
if(entn1.txt=="")
{
tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
}
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -951,10 +947,6 @@ Text f2Icon
Events
Touch Press Event
tSend.txt="event,buttonPress2,"+entn2.txt+",button"
if(entn2.txt=="")
{
tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
}
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -999,10 +991,6 @@ Text f3Icon
Events
Touch Press Event
tSend.txt="event,buttonPress2,"+entn3.txt+",button"
if(entn3.txt=="")
{
tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
}
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -1047,10 +1035,6 @@ Text f4Icon
Events
Touch Press Event
tSend.txt="event,buttonPress2,"+entn4.txt+",button"
if(entn4.txt=="")
{
tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
}
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -1095,10 +1079,6 @@ Text f5Icon
Events
Touch Press Event
tSend.txt="event,buttonPress2,"+entn5.txt+",button"
if(entn5.txt=="")
{
tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
}
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -1814,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

@@ -377,10 +377,6 @@ Text f1Icon
Events
Touch Press Event
tSend.txt="event,buttonPress2,"+entn1.txt+",button"
if(entn1.txt=="")
{
tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
}
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -405,10 +401,6 @@ Text f2Icon
Events
Touch Press Event
tSend.txt="event,buttonPress2,"+entn2.txt+",button"
if(entn2.txt=="")
{
tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
}
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -433,10 +425,6 @@ Text f3Icon
Events
Touch Press Event
tSend.txt="event,buttonPress2,"+entn3.txt+",button"
if(entn3.txt=="")
{
tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
}
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -461,10 +449,6 @@ Text f4Icon
Events
Touch Press Event
tSend.txt="event,buttonPress2,"+entn4.txt+",button"
if(entn4.txt=="")
{
tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
}
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -489,10 +473,6 @@ Text f5Icon
Events
Touch Press Event
tSend.txt="event,buttonPress2,"+entn5.txt+",button"
if(entn5.txt=="")
{
tSend.txt="event,buttonPress2,screensaver,bExit,"+tTmp.txt
}
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -976,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

@@ -5,7 +5,7 @@ If you like this project consider buying me a pizza 🍕 <a href="https://paypal
[![hacs_badge](https://img.shields.io/badge/HACS-Default-41BDF5.svg)](https://github.com/hacs/integration)
![hacs validation](https://github.com/joBr99/nspanel-lovelace-ui/actions/workflows/hacs-validation.yaml/badge.svg)
[![GitHub Release](https://img.shields.io/github/release/joBr99/nspanel-lovelace-ui.svg)](https://github.com/joBr99/nspanel-lovelace-ui/releases)
![Project Maintenance](https://img.shields.io/maintenance/yes/2024.svg)
![Project Maintenance](https://img.shields.io/maintenance/yes/2023.svg)
[![GitHub Activity](https://img.shields.io/github/commit-activity/y/joBr99/nspanel-lovelace-ui.svg)](https://github.com/joBr99/nspanel-lovelace-ui/commits/main)
@@ -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):
@@ -334,11 +333,6 @@ class LuiController(object):
apis.ha_api.get_entity(entity_id).call_service("return_to_base")
elif entity_id.startswith('service'):
apis.ha_api.call_service(entity_id.replace('service.', '', 1).replace('.','/', 1), **entity_config.data)
elif entity_id.startswith('valve'):
if apis.ha_api.get_entity(entity_id).state == "open":
apis.ha_api.get_entity(entity_id).call_service("close_valve")
else:
apis.ha_api.get_entity(entity_id).call_service("open_valve")
# for media page
if button_type == "media-next":
@@ -353,8 +347,8 @@ class LuiController(object):
else:
apis.ha_api.get_entity(entity_id).call_service("turn_off")
if button_type == "media-shuffle":
shuffle = not apis.ha_api.get_entity(entity_id).attributes['shuffle']
apis.ha_api.get_entity(entity_id).call_service("shuffle_set", shuffle=shuffle)
suffle = not apis.ha_api.get_entity(entity_id).attributes.shuffle
apis.ha_api.get_entity(entity_id).call_service("shuffle_set", shuffle=suffle)
if button_type == "volumeSlider":
pos = int(value)
# HA wants this value between 0 and 1 as float
@@ -371,7 +365,7 @@ class LuiController(object):
if button_type == "colorTempSlider":
entity = apis.ha_api.get_entity(entity_id)
#scale 0-100 from slider to color range of lamp
color_val = scale(int(value), (0, 100), (entity.attributes['min_mireds'], entity.attributes['max_mireds']))
color_val = scale(int(value), (0, 100), (entity.attributes.min_mireds, entity.attributes.max_mireds))
apis.ha_api.get_entity(entity_id).call_service("turn_on", color_temp=color_val)
if button_type == "colorWheel":
apis.ha_api.log(value)
@@ -398,9 +392,9 @@ class LuiController(object):
if button_type == "opnSensorNotify":
msg = ""
entity = apis.ha_api.get_entity(entity_id)
if open_sensors := entity.attributes.get("open_sensors") is not None:
for e in open_sensors:
msg += f"- {apis.ha_api.get_entity(e).attributes['friendly_name']}\r\n"
if "open_sensors" in entity.attributes and entity.attributes.open_sensors is not None:
for e in entity.attributes.open_sensors:
msg += f"- {apis.ha_api.get_entity(e).attributes.friendly_name}\r\n"
self._pages_gen.send_message_page("opnSensorNotifyRes", "", msg, "", "")
# for cardUnlock
@@ -417,22 +411,22 @@ class LuiController(object):
if button_type == "mode-preset_modes":
entity = apis.ha_api.get_entity(entity_id)
preset_mode = entity.attributes['preset_modes'][int(value)]
preset_mode = entity.attributes.preset_modes[int(value)]
entity.call_service("set_preset_mode", preset_mode=preset_mode)
if button_type == "mode-swing_modes":
entity = apis.ha_api.get_entity(entity_id)
swing_mode = entity.attributes['swing_modes'][int(value)]
swing_mode = entity.attributes.swing_modes[int(value)]
entity.call_service("set_swing_mode", swing_mode=swing_mode)
if button_type == "mode-fan_modes":
entity = apis.ha_api.get_entity(entity_id)
fan_mode = entity.attributes['fan_modes'][int(value)]
fan_mode = entity.attributes.fan_modes[int(value)]
entity.call_service("set_fan_mode", fan_mode=fan_mode)
if button_type in ["mode-input_select", "mode-select"]:
entity = apis.ha_api.get_entity(entity_id)
option = entity.attributes['options'][int(value)]
option = entity.attributes.options[int(value)]
entity.call_service("select_option", option=option)
if button_type == "mode-light":
@@ -444,12 +438,12 @@ class LuiController(object):
if options_list is not None:
option = options_list[int(value)]
else:
option = entity.attributes['effect_list'][int(value)]
option = entity.attributes.effect_list[int(value)]
entity.call_service("turn_on", effect=option)
if button_type == "mode-media_player":
entity = apis.ha_api.get_entity(entity_id)
option = entity.attributes['source_list'][int(value)]
option = entity.attributes.source_list[int(value)]
entity.call_service("select_source", source=option)
# timer detail page
@@ -464,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

@@ -159,8 +159,8 @@ alarm_control_panel_mapping = {
}
climate_mapping = {
'auto': 'fan-auto',
'heat_cool': 'sun-snowflake-variant',
'auto': 'calendar-sync',
'heat_cool': 'calendar-sync',
'heat': 'fire',
'off': 'power',
'cool': 'snowflake',
@@ -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)
@@ -266,8 +263,8 @@ def get_icon_ha(entity_id, overwrite=None, stateOverwrite=None):
# based on media_content_type
elif ha_type == "media_player":
result_icon = "speaker-off"
if media_content_type := entity.attributes.get("media_content_type"):
if media_content_type in media_content_type_mapping:
result_icon = media_content_type_mapping[media_content_type]
if "media_content_type" in entity.attributes:
if entity.attributes.media_content_type in media_content_type_mapping:
result_icon = media_content_type_mapping[entity.attributes.media_content_type]
return get_icon_char(result_icon)

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

@@ -25,7 +25,7 @@ class LuiPagesGen(object):
self._config = config
self._locale = config.get("locale")
self._send_mqtt_msg = send_mqtt_msg
def get_entity_color(self, entity, ha_type=None, stateOverwrite=None, overwrite=None):
if overwrite is not None:
if type(overwrite) in [str, list]:
@@ -82,7 +82,7 @@ class LuiPagesGen(object):
icon_color = 63878 #red
if state == "fog":
icon_color = 38066 #75% grey
if state in ["hail", "snowy"]:
if state in ["hail", "snowy"]:
icon_color = 65535 #white
if state == "lightning":
icon_color = 65120 #golden-yellow
@@ -97,12 +97,13 @@ class LuiPagesGen(object):
if state == "sunny":
icon_color = 65504 #bright-yellow
if color := attr.get("rgb_color"):
if brightness := attr.get("brightness"):
color = rgb_brightness(color, brightness)
if "rgb_color" in attr and attr.rgb_color:
color = attr.rgb_color
if "brightness" in attr and attr.brightness:
color = rgb_brightness(color, attr.brightness)
icon_color = rgb_dec565(color)
elif brightness := attr.get("brightness"):
color = rgb_brightness([253, 216, 53], brightness)
elif "brightness" in attr and attr.brightness:
color = rgb_brightness([253, 216, 53], attr.brightness)
icon_color = rgb_dec565(color)
return icon_color
@@ -128,7 +129,7 @@ class LuiPagesGen(object):
else:
dateformat = self._config.get("dateFormat")
date = datetime.datetime.now().strftime(dateformat)
addTemplate = self._config.get("dateAdditionalTemplate")
addDateText = apis.ha_api.render_template(addTemplate)
self._send_mqtt_msg(f"date~{date}{addDateText}")
@@ -137,7 +138,7 @@ class LuiPagesGen(object):
if target_page == "cardUnlock":
target_page = "cardAlarm"
self._send_mqtt_msg(f"pageType~{target_page}")
def update_screensaver_weather(self, theme):
entities = self._config._config_screensaver.entities
@@ -150,7 +151,7 @@ class LuiPagesGen(object):
item_str = ""
for item in entities:
item_str += self.generate_entities_item(item, "cardEntities", mask=["type", "entityId"])
self._send_mqtt_msg(f"weatherUpdate{item_str}")
# send color if configured in screensaver
if theme is not None:
@@ -180,20 +181,17 @@ class LuiPagesGen(object):
colorOverride = item.colorOverride
name = item.nameOverride
uuid = item.uuid
# check ha template for name
if item.nameOverride is not None and ("{" in item.nameOverride and "}" in item.nameOverride):
name = apis.ha_api.render_template(item.nameOverride)
# type of the item is the string before the "." in the entityId
if entityId is not None:
entityType = entityId.split(".")[0]
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
@@ -223,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]
@@ -247,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]
@@ -256,7 +254,7 @@ class LuiPagesGen(object):
if entity is None:
return f"~text~{entityId}~{get_icon_id('alert-circle-outline')}~17299~Not found check~ apps.yaml"
# HA Entities
# common res vars
entityTypePanel = "text"
@@ -275,7 +273,7 @@ class LuiPagesGen(object):
icon_up_status = "disable"
icon_stop_status = "disable"
icon_down_status = "disable"
bits = entity.attributes.get('supported_features')
bits = entity.attributes.supported_features
pos = entity.attributes.get("current_position")
if pos is None:
pos_status = entity.state
@@ -308,24 +306,19 @@ class LuiPagesGen(object):
device_class = entity.attributes.get("device_class", "")
unit_of_measurement = entity.attributes.get("unit_of_measurement", "")
value = entity.state
try:
value = str(round(float(value), 1))
except:
print("An exception occurred")
# limit value to 4 chars on us-p
if self._config.get("model") == "us-p" and cardType == "cardEntities":
value = entity.state[:4]
if value[-1] == ".":
value = value[:-1]
if device_class != "temperature":
value = value + " "
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]
@@ -387,50 +380,20 @@ class LuiPagesGen(object):
elif entityType == "weather":
entityTypePanel = "text"
unit = get_attr_safe(entity, "temperature_unit", "")
rt = None
if type(item.stype) == str:
spintstr = item.stype.split(":")
rt = spintstr[0]
item.stype = int(spintstr[1])
if type(item.stype) == int:
bits = get_attr_safe(entity, "supported_features", 0b0)
if not rt:
rt = "daily"
if bits & 0b001: #FORECAST_DAILY
rt = "daily"
elif bits & 0b010: #FORECAST_HOURLY
rt = "hourly"
elif bits & 0b100: #FORECAST_TWICE_DAILY
rt = "twice_daily"
results = apis.ha_api.call_service(
"weather/get_forecasts", target={"entity_id": entityId}, service_data={"type": rt}
)
forecast = results.get("result", {}).get("response", {}).get(entityId, {}).get('forecast') or entity.attributes.get('forecast', [])
if len(forecast) >= item.stype:
day_forecast = forecast[item.stype]
fdate = dp.parse(day_forecast['datetime'])
global babel_spec
if babel_spec is not None:
dateformat = "E" if item.nameOverride is None else item.nameOverride
name = babel.dates.format_datetime(fdate.astimezone(), dateformat, locale=self._locale)
else:
dateformat = "%a" if item.nameOverride is None else item.nameOverride
name = fdate.astimezone().strftime(dateformat)
icon_id = get_icon_ha(entityId, stateOverwrite=day_forecast['condition'])
value = f'{day_forecast.get("temperature", "")}{unit}'
color = self.get_entity_color(entity, ha_type=entityType, stateOverwrite=day_forecast['condition'], overwrite=colorOverride)
if type(item.stype) == int and len(entity.attributes.forecast) >= item.stype:
fdate = dp.parse(entity.attributes.forecast[item.stype]['datetime'])
global babel_spec
if babel_spec is not None:
dateformat = "E" if item.nameOverride is None else item.nameOverride
name = babel.dates.format_datetime(fdate.astimezone(), dateformat, locale=self._locale)
else:
value = f'{get_attr_safe(entity, "temperature", "")}{unit}'
dateformat = "%a" if item.nameOverride is None else item.nameOverride
name = fdate.astimezone().strftime(dateformat)
icon_id = get_icon_ha(entityId, stateOverwrite=entity.attributes.forecast[item.stype]['condition'])
value = f'{entity.attributes.forecast[item.stype].get("temperature", "")}{unit}'
color = self.get_entity_color(entity, ha_type=entityType, stateOverwrite=entity.attributes.forecast[item.stype]['condition'], overwrite=colorOverride)
else:
value = f'{get_attr_safe(entity, "temperature", "")}{unit}'
elif entityType == "valve":
entityTypePanel = "valve"
value = get_translation(self._locale, f"backend.component.binary_sensor.state.door.{entity.state}")
if entity.state == "open":
icon_id = get_icon_id("valve-open")
else:
icon_id = get_icon_id("valve-closed")
else:
name = "unsupported"
# Overwrite for value
@@ -489,7 +452,7 @@ class LuiPagesGen(object):
command = f"entityUpd~Not found~{navigation}~{item}~check~220~apps.yaml~150~300~5~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Please~your~~"
else:
entity = apis.ha_api.get_entity(item)
heading = title if title != "unknown" else entity.attributes['friendly_name']
heading = title if title != "unknown" else entity.attributes.friendly_name
current_temp = get_attr_safe(entity, "current_temperature", "")
dest_temp = get_attr_safe(entity, "temperature", None)
dest_temp2 = ""
@@ -510,10 +473,10 @@ class LuiPagesGen(object):
state_value += get_translation(self._locale, f"backend.component.climate.state._.{entity.state}")
if hvac_action != "":
state_value += ")"
min_temp = int(get_attr_safe(entity, "min_temp", 0)*10)
max_temp = int(get_attr_safe(entity, "max_temp", 0)*10)
step_temp = int(get_attr_safe(entity, "target_temp_step", 0.5)*10)
step_temp = int(get_attr_safe(entity, "target_temp_step", 0.5)*10)
icon_res_list = []
icon_res = ""
@@ -538,7 +501,7 @@ class LuiPagesGen(object):
state = 0
if(mode == entity.state):
state = 1
icon_res_list.append(f"~{icon_id}~{color_on}~{state}~{mode}")
icon_res = "".join(icon_res_list)
@@ -553,11 +516,11 @@ class LuiPagesGen(object):
icon_res = "~"*4 + icon_res_list[0] + "~"*4 + icon_res_list[1] + "~"*4 + icon_res_list[2] + "~"*4 + icon_res_list[3]
elif len(icon_res_list) >= 5 or self._config.get("model") == "us-p":
icon_res = "".join(icon_res_list) + "~"*4*(8-len(icon_res_list))
currently_translation = get_translation(self._locale, "frontend.ui.card.climate.currently")
state_translation = get_translation(self._locale, "frontend.ui.panel.config.devices.entities.state")
action_translation = get_translation(self._locale, "frontend.ui.card.climate.operation").replace(' ','\r\n')
detailPage = "1"
if any(x in ["preset_modes", "swing_modes", "fan_modes"] for x in entity.attributes):
detailPage = "0"
@@ -571,7 +534,7 @@ class LuiPagesGen(object):
command = f"entityUpd~Not found~{navigation}"
else:
entity = apis.ha_api.get_entity(item)
heading = title if title != "unknown" else entity.attributes['friendly_name']
heading = title if title != "unknown" else entity.attributes.friendly_name
# get data from homeassistant
data_raw = apis.ha_api.get_history(entity_id = item, days = 7)
@@ -618,12 +581,12 @@ class LuiPagesGen(object):
else:
media_icon = self.generate_entities_item(entity, "cardGrid")
ha_entity = apis.ha_api.get_entity(entityId)
heading = title if title != "unknown" else ha_entity.attributes['friendly_name']
heading = title if title != "unknown" else ha_entity.attributes.friendly_name
title = get_attr_safe(ha_entity, "media_title", "")
author = get_attr_safe(ha_entity, "media_artist", "")
volume = int(get_attr_safe(ha_entity, "volume_level", 0)*100)
iconplaypause = get_icon_id("pause") if ha_entity.state == "playing" else get_icon_id("play")
bits = ha_entity.attributes['supported_features']
bits = ha_entity.attributes.supported_features
onoffbutton = "disable"
if bits & 0b10000000:
if ha_entity.state == "off":
@@ -641,7 +604,7 @@ class LuiPagesGen(object):
item_str = ""
for item in entities:
item_str += self.generate_entities_item(item, "cardGrid")
bck_override = entity.iconOverride
if entity.status is not None:
bck_entity = entity.entityId
@@ -649,14 +612,14 @@ class LuiPagesGen(object):
entity.iconOverride = "mdi:speaker"
item_str += self.generate_entities_item(entity, "cardGrid")
entity.iconOverride = bck_override
if entity.status is not None:
entity.entityId = bck_entity
command = f"entityUpd~{heading}~{navigation}~{entityId}~{title}~~{author}~~{volume}~{iconplaypause}~{onoffbutton}~{shuffleBtn}{media_icon}{item_str}"
self._send_mqtt_msg(command)
def generate_alarm_page(self, navigation, title, entity, overwrite_supported_modes, alarmBtn):
item = entity.entityId
if not apis.ha_api.entity_exists(item):
@@ -673,7 +636,7 @@ class LuiPagesGen(object):
if not entity.attributes.get("code_arm_required", False):
numpad = "disable"
if overwrite_supported_modes is None:
bits = entity.attributes['supported_features']
bits = entity.attributes.supported_features
if bits & 0b000001:
supported_modes.append("arm_home")
if bits & 0b000010:
@@ -712,7 +675,7 @@ class LuiPagesGen(object):
#add button to show sensor state
add_btn = ""
if entity.attributes.get("open_sensors") is not None:
if "open_sensors" in entity.attributes and entity.attributes.open_sensors is not None:
add_btn=f"{get_icon_id('progress-alert')}~{rgb_dec565([243,179,0])}~"
if alarmBtn is not None and type(alarmBtn) is dict:
entity = alarmBtn.get("entity")
@@ -723,8 +686,8 @@ class LuiPagesGen(object):
else:
icon_color = rgb_dec565([243,179,0])
add_btn=f"{iconnav}~{icon_color}~{entity}"
# add padding to arm buttons
arm_buttons = ""
for b in supported_modes:
@@ -734,12 +697,12 @@ class LuiPagesGen(object):
arm_buttons += "~"*((4-len(supported_modes))*2)
command = f"entityUpd~{title}~{navigation}~{item}{arm_buttons}~{icon}~{color}~{numpad}~{flashing}~{add_btn}"
self._send_mqtt_msg(command)
def generate_unlock_page(self, navigation, item, title, destination, pin):
color = rgb_dec565([255,0,0])
icon = get_icon_id("lock")
supported_modes = ["cardUnlock-unlock"]
# add padding to arm buttons
arm_buttons = ""
for b in supported_modes:
@@ -778,8 +741,8 @@ class LuiPagesGen(object):
if (time.time()-card.last_update) < card.cooldown:
return
card.last_update = time.time()
leftBtn = "delete~~~~~"
if card.uuid_prev is not None:
leftBtn = self.generate_entities_item(Entity(
@@ -816,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
@@ -825,9 +786,9 @@ class LuiPagesGen(object):
self._send_mqtt_msg(f"timeout~{card.sleepTimeout}")
else:
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":
@@ -879,26 +840,25 @@ class LuiPagesGen(object):
color_temp = "disable"
color = "disable"
effect_supported = "disable"
supported_color_modes = entity.attributes['supported_color_modes']
if "onoff" not in supported_color_modes:
if "onoff" not in entity.attributes.supported_color_modes:
brightness = 0
if entity.state == "on":
if brightness := entity.attributes.get("brightness"):
if "brightness" in entity.attributes and entity.attributes.brightness:
# scale 0-255 brightness from ha to 0-100
brightness = int(scale(brightness, (0,255), (0,100)))
brightness = int(scale(entity.attributes.brightness,(0,255),(0,100)))
else:
brightness = "disable"
if "color_temp" in supported_color_modes:
if color_temp := entity.attributes.get("color_temp"):
if "color_temp" in entity.attributes.supported_color_modes and entity.attributes.supported_color_modes:
if "color_temp" in entity.attributes and entity.attributes.color_temp:
# scale ha color temp range to 0-100
color_temp = int(scale(color_temp, (entity.attributes['min_mireds'], entity.attributes['max_mireds']),(0, 100)))
color_temp = int(scale(entity.attributes.color_temp,(entity.attributes.min_mireds, entity.attributes.max_mireds),(0,100)))
else:
color_temp = "unknown"
else:
color_temp = "disable"
list_color_modes = ["xy", "rgb", "rgbw", "hs"]
if any(item in list_color_modes for item in supported_color_modes):
if any(item in list_color_modes for item in entity.attributes.supported_color_modes):
color = "enable"
else:
color = "disable"
@@ -908,20 +868,20 @@ class LuiPagesGen(object):
brightness_translation = get_translation(self._locale, "frontend.ui.card.light.brightness")
color_temp_translation = get_translation(self._locale, "frontend.ui.card.light.color_temperature")
self._send_mqtt_msg(f"entityUpdateDetail~{entity_id}~~{icon_color}~{switch_val}~{brightness}~{color_temp}~{color}~{color_translation}~{color_temp_translation}~{brightness_translation}~{effect_supported}", force=is_open_detail)
def generate_shutter_detail_page(self, entity_id, is_open_detail=False):
entity = apis.ha_api.get_entity(entity_id)
entityType = "cover"
device_class = entity.attributes.get("device_class", "window")
icon_id = get_icon_ha(entity_id)
pos = entity.attributes.get("current_position")
if pos is None:
pos_status = entity.state
pos = "disable"
else:
pos_status = pos
pos_translation = ""
icon_up = ""
icon_stop = ""
@@ -938,7 +898,7 @@ class LuiPagesGen(object):
iconTiltRightStatus = "disable"
tilt_pos = "disable"
bits = entity.attributes['supported_features']
bits = entity.attributes.supported_features
# position supported
if bits & 0b00001111:
@@ -1025,7 +985,7 @@ class LuiPagesGen(object):
if modes:
modes_out += f"{heading}~{mode}~{cur_mode}~{modes_res}~"
self._send_mqtt_msg(f"entityUpdateDetail~{entity_id}~{icon_id}~{icon_color}~{modes_out}", force=is_open_detail)
self._send_mqtt_msg(f"entityUpdateDetail~{entity_id}~{icon_id}~{icon_color}~{modes_out}", force=is_open_detail)
def generate_input_select_detail_page(self, entity_id, is_open_detail=False):
options_list = None
@@ -1089,8 +1049,8 @@ class LuiPagesGen(object):
label2 = get_translation(self._locale, "frontend.ui.card.timer.actions.cancel")
label3 = get_translation(self._locale, "frontend.ui.card.timer.actions.finish")
self._send_mqtt_msg(f"entityUpdateDetail~{entity_id}~~{icon_color}~{entity_id}~{min_remaining}~{sec_remaining}~{editable}~{action1}~{action2}~{action3}~{label1}~{label2}~{label3}", force=is_open_detail)
def send_message_page(self, ident, heading, msg, b1, b2):
self._send_mqtt_msg(f"pageType~popupNotify")
self._send_mqtt_msg(f"entityUpdateDetail~{ident}~{heading}~65535~{b1}~65535~{b2}~65535~{msg}~65535~0")

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,15 +22,14 @@ 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
version = "v4.7.3"
version = "v4.3.3"
model = cfg.get("model")
if model == "us-l":
@@ -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,4 +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

View File

@@ -0,0 +1,74 @@
const idAbfalliCal = 'ical.1'; // iCal Instanz zum Abfallkalender
const idZeichenLoeschen = 14; // x Zeichen links vom String abziehen, wenn vor dem Eventname noch Text steht z.B. Strassenname; Standard = 0
const idRestmuellName ='Hausmüll'; // Schwarze Tonne
const idWertstoffName = 'Gelber Sack'; // Gelbe Tonne / Sack
const idPappePapierName = 'Papier'; // Blaue Tonne
const idBioabfaelleName = 'Biomüll'; // Braune Tonne
var i, Muell_JSON, Event2, Color = 0;
for (i = 1; i <= 4; i++) {
if (!existsState('0_userdata.0.Abfallkalender.' + parseFloat(i) + '.date')) {
log(i + '.date nicht vorhanden, wurde erstellt');
createState('0_userdata.0.Abfallkalender.' + parseFloat(i) + '.date', '',
{
name: parseFloat(i) + '.date',
role: 'state',
type: 'string',
read: true,
write: true,
def: ''
});
};
if (!existsState('0_userdata.0.Abfallkalender.' + parseFloat(i) + '.event')) {
log(i + '.event nicht vorhanden, wurde erstellt');
createState('0_userdata.0.Abfallkalender.' + parseFloat(i) + '.event', '',
{
name: parseFloat(i) + '.event',
role: 'state',
type: 'string',
read: true,
write: true,
def: ''
});
};
if (!existsState('0_userdata.0.Abfallkalender.' + parseFloat(i) + '.color')) {
log(i + '.color nicht vorhanden, wurde erstellt');
createState('0_userdata.0.Abfallkalender.' + parseFloat(i) + '.color', 0,
{
name: parseFloat(i) + '.color',
role: 'state',
type: 'number',
read: true,
write: true,
def: 0
});
};
}
function subsequenceFromStartLast(sequence, at1) {
var start = at1;
var end = sequence.length;
return sequence.slice(start, end);
}
on({ id: idAbfalliCal + '.data.table', change: "ne" }, async function () {
for (i = 0; i <= 3; i++) {
Muell_JSON = getState(idAbfalliCal + '.data.table').val;
setStateDelayed((['0_userdata.0.Abfallkalender.', parseFloat(i) + 1, '.date'].join('')), getAttr(Muell_JSON, (String(i) + '.date')), false, parseInt(((0) || "").toString(), 10), false);
Event2 = subsequenceFromStartLast(getAttr(Muell_JSON, (String(i) + '.event')), idZeichenLoeschen);
setStateDelayed((['0_userdata.0.Abfallkalender.', parseFloat(i) + 1, '.event'].join('')), Event2, false, parseInt(((0) || "").toString(), 10), false);
if (Event2 == idRestmuellName) {
Color = 33840;
} else if (Event2 == idBioabfaelleName) {
Color = 2016;
} else if (Event2 == idPappePapierName) {
Color = 31;
} else if (Event2 == idWertstoffName) {
Color = 65504;
}
setStateDelayed((['0_userdata.0.Abfallkalender.', parseFloat(i) + 1, '.color'].join('')), Color, false, parseInt(((0) || "").toString(), 10), false);
}
});

View File

@@ -1,224 +0,0 @@
/*
* @author 2023 @tt-tom
*
* Version 5.1.1
*
* Das Script erstellt die Datenpunkte und Alias für den Abfallkalender im Sonoff NSPanel
* Es wird der iCal Adapter benötigt und eine URL mit Terminen vom Entsorger bzw. eine .ics-Datei mit den Terminen.
* Das Script triggert auf dem bereitgestellten JSON im iCal adapter und füllt die 0_userdata.0 Datenpunkte
* Weitere Informationen findest du in der FAQ auf Github https://github.com/joBr99/nspanel-lovelace-ui/wiki
*
* changelog
* - 06.12.2023 - v5.0.2 add custom name for trashtype
* - 06.12.2023 - v5.1.0 Refactoring
* - 22.01.2024 - v5.1.1 Add tow Events more
*
*
*/
const idTrashData: string = 'ical.0.data.table'; // Datenpunkt mit Daten im JSON Format
const idUserdataAbfallVerzeichnis: string = '0_userdata.0.Abfallkalender'; // Name des Datenpunktverzeichnis unter 0_userdata.0 -> Strandard = 0_userdata.0.Abfallkalender
const idAliasPanelVerzeichnis: string = 'alias.0.NSPanel.allgemein'; //Name PanelVerzeichnis unter alias.0. Standard = alias.0.NSPanel.1
const idAliasAbfallVerzeichnis: string = 'Abfall'; //Name Verzeichnis unterhalb der idPanelverzeichnis Standard = Abfall
const anzahlZeichenLoeschen: number = 14; // x Zeichen links vom String abziehen, wenn vor dem Eventname noch Text steht z.B. Strassenname; Standard = 0
const jsonEventName1: string = 'Hausmüll'; // Vergleichstring für Schwarze Tonne
const customEventName1: string = ''; // benutzerdefinierter Text für schwarze Tonne
const jsonEventName2: string = 'Gelber Sack'; // Vergleichstring für Gelbe Tonne / Sack
const customEventName2: string = ''; // benutzerdefinierter Text für gelbe Tonne
const jsonEventName3: string = 'Papier'; // Vergleichstring für Blaue Tonne
const customEventName3: string = ''; // benutzerdefinierter Text für blaue Tonne
const jsonEventName4: string = 'Biomüll'; // Vergleichstring für Braune Tonne
const customEventName4: string = ''; // benutzerdefinierter Text für braune Tonne
const jsonEventName5: string = 'Treppe'; // Vergleichstring für Event 5
const customEventName5: string = 'Besen schwingen'; // benutzerdefinierter Text für Event 5
const jsonEventName6: string = ''; // Vergleichstring für Event 6
const customEventName6: string = ''; // benutzerdefinierter Text für Event 6
const Debug: boolean = false;
// ------------------------- Trigger zum füllen der 0_userdata Datenpunkte aus dem json vom ical Adapter -------------------------------
// Trigger auf JSON Datenpunkt
on({ id: idTrashData, change: 'ne' }, async function () {
JSON_auswerten();
});
// ------------------------------------- Ende Trigger ------------------------------------
// ------------------------------------- Funktion JSON auswerten und DP füllen -------------------------------
async function JSON_auswerten() {
try {
let trashJSON: any;
let instanzName: any;
let eventName: string;
let eventDatum: string;
let eventStartdatum: string;
let farbNummer: number = 0;
let farbString: string;
let abfallNummer: number = 1;
trashJSON = getState(idTrashData).val;
instanzName = idTrashData.split('.');
if (Debug) log('Rohdaten von Instanz ' + instanzName[0] + ': ' + JSON.stringify(trashJSON), 'info')
if (Debug) log('Anzahl Trash - Daten: ' + trashJSON.length, 'info');
for (let i = 0; i < trashJSON.length; i++) {
if (abfallNummer === 7) {
if (Debug) log('Alle Abfall-Datenpunkte gefüllt', 'warn');
break;
}
log('Daten vom ical Adapter werden ausgewertet', 'info');
eventName = getAttr(trashJSON, (String(i) + '.event')).slice(anzahlZeichenLoeschen, getAttr(trashJSON, (String(i) + '.event')).length);
// Leerzeichen vorne und hinten löschen
eventName = eventName.trimEnd();
eventName = eventName.trimStart();
eventDatum = getAttr(trashJSON, (String(i) + '.date'));
eventStartdatum = getAttr(trashJSON, (String(i) + '._date'));
let d: Date = currentDate();
let d1: Date = new Date(eventStartdatum);
if (Debug) log('--------- Nächster Termin wird geprüft ---------', 'info');
//if (Debug) log(d + ' ' + d1, 'info');
if (Debug) log('Startdatum UTC: ' + eventStartdatum, 'info');
if (Debug) log('Datum: ' + eventDatum, 'info');
if (Debug) log('Event: ' + eventName, 'info');
if (Debug) log('Kontrolle Leerzeichen %' + eventName + '%', 'info');
if (d.getTime() <= d1.getTime()) {
if ((eventName == jsonEventName1) || (eventName == jsonEventName2) || (eventName == jsonEventName3) || (eventName == jsonEventName4) || (eventName == jsonEventName5) || (eventName == jsonEventName6)) {
switch (eventName) {
case jsonEventName1:
farbNummer = 33840;
if (customEventName1 != '') {
eventName = customEventName1;
if (Debug) log('Event customName: ' + eventName, 'info');
};
break;
case jsonEventName2:
farbNummer = 65504;
if (customEventName2 != '') {
eventName = customEventName2;
if (Debug) log('Event customName: ' + eventName, 'info');
};
break;
case jsonEventName3:
farbNummer = 31;
if (customEventName3 != '') {
eventName = customEventName3
if (Debug) log('Event customName: ' + eventName, 'info');
};
break;
case jsonEventName4:
farbNummer = 2016;
if (customEventName4 != '') {
eventName = customEventName4;
if (Debug) log('Event customName: ' + eventName, 'info');
};
break;
case jsonEventName5:
farbNummer = 2016;
if (customEventName5 != '') {
eventName = customEventName5;
if (Debug) log('Event customName: ' + eventName, 'info');
};
break;
case jsonEventName6:
farbNummer = 2016;
if (customEventName6 != '') {
eventName = customEventName6
if (Debug) log('Event customName: ' + eventName, 'info');
};
break;
}
//if (farbString != undefined) farbNummer = rgb_dec565(hex_rgb(farbString));
setState(idUserdataAbfallVerzeichnis + '.' + String(abfallNummer) + '.date', eventDatum);
setState(idUserdataAbfallVerzeichnis + '.' + String(abfallNummer) + '.event', eventName);
setState(idUserdataAbfallVerzeichnis + '.' + String(abfallNummer) + '.color', farbNummer);
//if (Debug) log('farbString: ' + farbString + ' farbNummer: ' + farbNummer, 'info');
if (Debug) log('Abfallnummer: ' + abfallNummer, 'info');
abfallNummer += 1
} else {
if (Debug) log('Kein Abfalltermin => Event passt mit keinem Abfallnamen überein.', 'warn');
}
} else {
if (Debug) log('Termin liegt vor dem heutigen Tag', 'warn');
}
}
} catch (err) {
log('error at subscrption: ' + err.message, 'warn');
}
};
// ------------------------------------- Ende Funktion JSON ------------------------------
// ------------------------------------- Funktion zur Prüfung und Erstellung der Datenpunkte in 0_userdata.0 und alias.0 -----------------------
async function Init_Datenpunkte() {
try {
for (let i = 1; i <= 6; i++) {
if (existsObject(idUserdataAbfallVerzeichnis + '.' + String(i)) == false) {
log('Datenpunkt ' + idUserdataAbfallVerzeichnis + '.' + String(i) + ' werden angelegt', 'info')
await createStateAsync(idUserdataAbfallVerzeichnis + '.' + String(i) + '.date', '', { type: 'string' });
await createStateAsync(idUserdataAbfallVerzeichnis + '.' + String(i) + '.event', '', { type: 'string' });
await createStateAsync(idUserdataAbfallVerzeichnis + '.' + String(i) + '.color', 0, { type: 'number' });
setObject(idAliasPanelVerzeichnis + '.' + idAliasAbfallVerzeichnis, { type: 'device', common: { name: { de: 'Abfall', en: 'Trash' } }, native: {} });
setObject(idAliasPanelVerzeichnis + '.' + idAliasAbfallVerzeichnis + '.event' + String(i), { type: 'channel', common: { role: 'warning', name: { de: 'Ereignis ' + String(i), en: 'Event' + String(i) } }, native: {} });
await createAliasAsync(idAliasPanelVerzeichnis + '.' + idAliasAbfallVerzeichnis + '.event' + String(i) + '.TITLE', idUserdataAbfallVerzeichnis + '.' + String(i) + '.event', true, <iobJS.StateCommon>{ type: 'string', role: 'weather.title.short', name: { de: 'TITEL', en: 'TITLE' } });
await createAliasAsync(idAliasPanelVerzeichnis + '.' + idAliasAbfallVerzeichnis + '.event' + String(i) + '.LEVEL', idUserdataAbfallVerzeichnis + '.' + String(i) + '.color', true, <iobJS.StateCommon>{ type: 'number', role: 'value.warning', name: { de: 'LEVEL', en: 'LEVEL' } });
await createAliasAsync(idAliasPanelVerzeichnis + '.' + idAliasAbfallVerzeichnis + '.event' + String(i) + '.INFO', idUserdataAbfallVerzeichnis + '.' + String(i) + '.date', true, <iobJS.StateCommon>{ type: 'string', role: 'weather.title', name: { de: 'INFO', en: 'INFO' } });
log('Fertig', 'info')
} else {
log('Datenpunkt ' + idUserdataAbfallVerzeichnis + '.' + String(i) + ' vorhanden', 'info')
}
}
log('Startabfrage der Daten', 'info');
JSON_auswerten();
} catch (err) {
log('error at function Init_Datenpunkte: ' + err.message, 'warn');
}
}
Init_Datenpunkte();
// --------------------------- Ende Funktion Datenpunkte ------------------------------------------------
// --------------------------- Zusatzfuktionen -------------------------------------------------------------
function currentDate() {
let d: Date = new Date();
return new Date(d.getFullYear(), d.getMonth(), d.getDate());
}
function rgb_dec565(rgb: RGB): number {
//return ((Math.floor(rgb.red / 255 * 31) << 11) | (Math.floor(rgb.green / 255 * 63) << 5) | (Math.floor(rgb.blue / 255 * 31)));
return ((rgb.red >> 3) << 11) | ((rgb.green >> 2)) << 5 | ((rgb.blue) >> 3);
}
function hex_rgb(colorhex: string): RGB {
let r = parseInt(colorhex.substring(1, 3), 16);
let g = parseInt(colorhex.substring(3, 5), 16);
let b = parseInt(colorhex.substring(5, 7), 16);
return { red: r, green: g, blue: b };
}
type RGB = {
red: number,
green: number,
blue: number
};
// -------------------- Ende Zudatzfunktionen --------------------------------------------------------------------------

View File

@@ -1,51 +0,0 @@
const sourceDP = 'alias.0.Wohnzimmer.Heizung.ACTUAL';
const targetDP = '0_userdata.0.Test.chartTest';
const rangeHours = 24;
const maxXAchsisTicks = 6;
const historyInstance = 'history.0';
const factor = 1; // Bei zu großen Werten und negativen Anzeigen im Panel um das 10fache erhöhen
on({id: sourceDP, change: "any"}, async function (obj) {
sendTo(historyInstance, 'getHistory', {
id: sourceDP,
options: {
start: Date.now() - (60 * 60 * 1000 * rangeHours),
end: Date.now(),
count: rangeHours,
limit: rangeHours,
aggregate: 'average'
}
}, function (result) {
var cardChartString = "";
var stepXAchsis = rangeHours / maxXAchsisTicks;
for (var i = 0; i < rangeHours; i++){
var deltaHour = rangeHours - i;
var targetDate = new Date(Date.now() - (deltaHour * 60 * 60 * 1000));
//Check history items for requested hours
for (var j = 0, targetValue = 0; j < result.result.length; j++) {
var valueDate = new Date(result.result[j].ts);
var value = Math.round(result.result[j].val / factor * 10);
if (valueDate > targetDate){
if ((targetDate.getHours() % stepXAchsis) == 0){
cardChartString += targetValue + '^' + targetDate.getHours() + ':00' + '~';
} else {
cardChartString += targetValue + '~';
}
break;
} else {
targetValue = value;
}
}
}
cardChartString = cardChartString.substring(0,cardChartString.length-1);
if (existsState(targetDP) == false ) {
createState(targetDP, cardChartString, true, { type: 'string' });
} else {
setState(targetDP, cardChartString, true);
}
});
});

View File

@@ -1,78 +0,0 @@
const sourceDP = 'alias.0.Wohnzimmer.Heizung.ACTUAL';
const targetDP = '0_userdata.0.Test.chartTest';
const numberOfHoursAgo = 24; // Period of time in hours which shall be visualized
const xAxisTicksEveryM = 60; // Time after x axis gets a tick in minutes
const xAxisLabelEveryM = 240; // Time after x axis is labeled in minutes
const historyInstance = 'history.0';
const Debug = false;
const maxX = 1420;
const limitMeasurements = 35;
createState(targetDP, "", {
name: 'SensorGrid',
desc: 'Sensor Values [~<time>:<value>]*',
type: 'string',
role: 'value',
});
on({id: sourceDP, change: "any"}, async function (obj) {
sendTo(historyInstance, 'getHistory', {
id: sourceDP,
options: {
start: Date.now() - (numberOfHoursAgo * 60 * 60 * 1000 ), //Time in ms: hours * 60m * 60s * 1000ms
end: Date.now(),
count: limitMeasurements,
limit: limitMeasurements,
aggregate: 'average'
}
}, function (result) {
var ticksAndLabels = ""
var coordinates = "";
var cardLChartString = "";
let ticksAndLabelsList = []
var date = new Date();
date.setMinutes(0, 0, 0);
var ts = Math.round(date.getTime() / 1000);
var tsYesterday = ts - (numberOfHoursAgo * 3600);
for (var x = tsYesterday, i = 0; x < ts; x += (xAxisTicksEveryM * 60), i += xAxisTicksEveryM)
{
if (i % xAxisLabelEveryM)
{
ticksAndLabelsList.push(i);
} else
{
var currentDate = new Date(x * 1000);
// Hours part from the timestamp
var hours = "0" + currentDate.getHours();
// Minutes part from the timestamp
var minutes = "0" + currentDate.getMinutes();
// Seconds part from the timestamp
var seconds = "0" + currentDate.getSeconds();
var formattedTime = hours.slice(-2) + ':' + minutes.slice(-2);
ticksAndLabelsList.push(String(i) + "^" + formattedTime);
}
}
ticksAndLabels = ticksAndLabelsList.join("+");
let list = [];
let offSetTime = Math.round(result.result[0].ts / 1000);
let counter = Math.round((result.result[result.result.length -1 ].ts / 1000 - offSetTime) / maxX);
for (var i = 0; i < result.result.length; i++)
{
var time = Math.round(((result.result[i].ts / 1000) - offSetTime) / counter);
var value = Math.round(result.result[i].val * 10);
if ((value != null) && (value != 0)){
list.push(time + ":" + value)
}
}
coordinates = list.join("~");
cardLChartString = ticksAndLabels + '~' + coordinates
setState(targetDP, cardLChartString, true);
if (Debug) console.log(cardLChartString);
});
});

View File

@@ -1,130 +0,0 @@
/**
* 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; // true für erweiterte Ausgaben im Log
const NSPanel_Path = '0_userdata.0.NSPanel.';
const Path = NSPanel_Path + 'Influx2NSPanel.cardLChart.';
const InfluxInstance = 'influxdb.0';
const influxDbBucket = 'storage_short';
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':....}
*/
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);
});
// 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);
});
//__________________________
// 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);
}
//__________________________
// 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);
}
}

View File

@@ -1,54 +0,0 @@
/**
* generate an JSON for display Power-Card on NSPanel
* Source: https://github.com/joBr99/nspanel-lovelace-ui/wiki/ioBroker-Card-Definitionen-(Seiten)#cardpower-ab-ts-script-v341
* Version: 0.1 - L4rs
*/
schedule("* * * * *", function () {
// Definition der Datenpunkte für das JSON der POWER-Card und der anzuzeigenden Leistungswerte
var powerCardJson = "0_userdata.0.NSPanel.Energie.PowerCard",
pwr1 = "", // Batterie
pwr2 = Math.round(getState("mqtt.0.SmartHome.Energie.PV.openDTU.114180710360.0.power").val), // Solar
pwr3 = "", // Wind
pwr4 = "", // Verbraucher
pwr5 = Math.round(getState("hm-rpc.0.MEQ0706303.1.POWER").val), // Stromnetz
pwr6 = 0, // Auto
pwrHome = Math.round(pwr5 - pwr2); // Berechnung des Energiefluss anstelle eines Datenpunktes
// Definition der Keys im JSON
var keys = ["id", "value", "unit", "icon", "iconColor", "speed"];
// Definition der "Kacheln", inkl. StandardIcon. Es können alle Icon aus dem Iconmapping genutzt werden.
// Kacheln die nicht genutzt werden sollen, müssen wie z.b. item1 formatiert sein
var home = [0, pwrHome, "W", "home-lightning-bolt-outline", 0]; // Icon home
var item1 = [1, pwr1, "", "", 0, ""]; // Icon battery-charging-60
var item2 = [2, pwr2, "W", "solar-power-variant-outline", 3, pwr2 > 0 ? -2 : 0]; // Icon solar-power-variant
var item3 = [3, pwr3, "", "", 0, ""]; // Icon wind-turbine
var item4 = [4, pwr4, "", "", 0, ""]; // Icon shape
var item5 = [5, pwr5, "W", "transmission-tower", 10, 10]; // Icon transmission-tower
var item6 = [6, pwr6, "kW", "car-electric-outline", 5, 0]; // Icon car
/**
* JSON generieren und in den Datenpunkt schreiben,
*
* --- ab hier keine Änderungen mehr ---
*/
function func(tags, values) {
return Object.assign(
...tags.map((element, index) => ({ [element]: values[index] }))
);
}
setState(
powerCardJson,
JSON.stringify([
func(keys, home),
func(keys, item1),
func(keys, item2),
func(keys, item3),
func(keys, item4),
func(keys, item5),
func(keys, item6),
])
);
});

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -2564,11 +2564,7 @@
"crossfade":{
"en-US":"Crossfade",
"de-DE":"Überblenden"
},
"tools":{
"en-US":"Tools",
"de-DE":"Tools"
},
},
"speaker":{
"en-US":"Speakerlist",
"de-DE":"Wiedergabegeräte",

View File

@@ -2701,13 +2701,5 @@
"scriptname":{
"en-US":"Script name",
"de-DE":"Skriptname"
},
"hiddencards_offon":{
"en-US":"hidden Cards (on/off)",
"de-DE":"ausgeblendete Seiten (an/aus)"
},
"easyview_layout":{
"en-US":"Easyview Layout",
"de-DE":"Einfaches Layout"
}
}

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

@@ -228,8 +228,6 @@ class LovelaceUIPanel:
self.render_current_page(requested=True)
if msg[1] == "buttonPress2":
entity_id = msg[2]
if entity_id == "":
return
btype = msg[3]
value = msg[4] if len(msg) > 4 else None
if btype == "bExit":
@@ -280,8 +278,6 @@ class LovelaceUIPanel:
self.privious_cards.append(self.current_card)
self.current_card = self.searchCard(card_iid)
self.render_current_page(switchPages=True)
case 'mode-light':
ha_control.handle_buttons(entity_id, btype, value, entity_config=entity_config)
case _:
ha_control.handle_buttons(entity_id, btype, value)

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