mirror of
https://github.com/joBr99/nspanel-lovelace-ui.git
synced 2025-12-20 14:37:01 +01:00
Compare commits
89 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f78e03e5ec | ||
|
|
33afd31d8f | ||
|
|
4dccdfb014 | ||
|
|
2d6b8634bf | ||
|
|
f11177c5c1 | ||
|
|
645aef4cfa | ||
|
|
d5fe05f806 | ||
|
|
64cb8563aa | ||
|
|
b91629421e | ||
|
|
814cd4a295 | ||
|
|
9c2d91e3d9 | ||
|
|
028ddb7896 | ||
|
|
afdc05a239 | ||
|
|
f63f7f7861 | ||
|
|
c9d8f622cb | ||
|
|
e6bb202823 | ||
|
|
7080facb10 | ||
|
|
dcce889b3e | ||
|
|
2e15482ed1 | ||
|
|
adb99623e9 | ||
|
|
27f48819a4 | ||
|
|
1b52c885f6 | ||
|
|
1f937f75f2 | ||
|
|
269b983c2a | ||
|
|
29dfa5671d | ||
|
|
74a3860aa0 | ||
|
|
1bc69128a9 | ||
|
|
4529d5e34e | ||
|
|
0db8983b4a | ||
|
|
dc5e732e04 | ||
|
|
7e7d14a999 | ||
|
|
7ebc29b4a4 | ||
|
|
8b24f70ae0 | ||
|
|
edfe79e3c4 | ||
|
|
93d223141b | ||
|
|
853de19a40 | ||
|
|
da2b4565a4 | ||
|
|
18d360d339 | ||
|
|
af494ada10 | ||
|
|
05d6ee1f5f | ||
|
|
a4344ade18 | ||
|
|
1470ee5c82 | ||
|
|
9ae898c30f | ||
|
|
ec65eebb74 | ||
|
|
f7a768de7c | ||
|
|
e7aa27128c | ||
|
|
11bbad78d1 | ||
|
|
95e3dfa0ea | ||
|
|
70557ab748 | ||
|
|
2ecc7caedd | ||
|
|
6fd8553450 | ||
|
|
83396c283f | ||
|
|
a388efb5b5 | ||
|
|
77b05b8079 | ||
|
|
0aae22118b | ||
|
|
4a0a2d3620 | ||
|
|
cfd0d2cbe8 | ||
|
|
39575935b6 | ||
|
|
9f8900bf78 | ||
|
|
a054f7a7b3 | ||
|
|
464e625eaa | ||
|
|
505db42064 | ||
|
|
36459145e4 | ||
|
|
32b84bba9f | ||
|
|
952398a827 | ||
|
|
ff106be9cb | ||
|
|
0ab7900ec0 | ||
|
|
f328107f75 | ||
|
|
8deb0fd35e | ||
|
|
ed207b6e1a | ||
|
|
83e3316458 | ||
|
|
53c4ed370b | ||
|
|
050a8e6fd2 | ||
|
|
907f814ac7 | ||
|
|
e91c514fe1 | ||
|
|
5566f20e47 | ||
|
|
4810679ac5 | ||
|
|
48ed2daa33 | ||
|
|
482cee0552 | ||
|
|
8813331299 | ||
|
|
abf4868281 | ||
|
|
420905db04 | ||
|
|
4151aba6ea | ||
|
|
35b15c92e0 | ||
|
|
c40906fdfe | ||
|
|
66a65c03e8 | ||
|
|
20e6f21307 | ||
|
|
342a97df0f | ||
|
|
0d343d4919 |
@@ -71,6 +71,8 @@ change the page type:
|
||||
|
||||
`pageType,popupNotify`
|
||||
|
||||
`pageType,screensaver`
|
||||
|
||||
### screensaver page
|
||||
|
||||
`weatherUpdate,? tMainIcon? tMainText? tMRIcon? tMR? tForecast1? tF1Icon? tForecast1Val? tForecast2? tF2Icon? tForecast2Val`
|
||||
@@ -117,7 +119,7 @@ The following message can be used to update the content on the cardEntities Page
|
||||
|
||||
`entityUpdateDetail,*internalName*,*tHeading*,*tHeadingColor*,*b1*,*tB1Color*,*b2*,*tB2Color*,*tText*,*tTextColor*,*sleepTimeout*`
|
||||
|
||||
`popupExit`
|
||||
`exitPopup`
|
||||
|
||||
### cardThermo Page
|
||||
|
||||
@@ -136,20 +138,27 @@ The following message can be used to update the content on the cardEntities Page
|
||||
|
||||
## Messages from Nextion Display
|
||||
|
||||
`event,buttonPress2,pageName,bNext`
|
||||
|
||||
`event,buttonPress2,pageName,bPrev`
|
||||
|
||||
`event,buttonPress2,pageName,bExit`
|
||||
|
||||
### startup page
|
||||
|
||||
`event,startup,version`
|
||||
|
||||
### screensaver page
|
||||
|
||||
`event,screensaverOpen`
|
||||
`event,buttonPress2,screensaver,exit` - Touch Event on Screensaver
|
||||
|
||||
`event,screensaverOpen` - Screensaver has opened
|
||||
|
||||
|
||||
### cardEntities Page
|
||||
|
||||
`event,*eventName*,*entityName*,*actionName*,*optionalValue*`
|
||||
|
||||
`event,pageOpen,0`
|
||||
|
||||
`event,buttonPress2,internalNameEntity,up`
|
||||
|
||||
`event,buttonPress2,internalNameEntity,down`
|
||||
@@ -186,8 +195,6 @@ The following message can be used to update the content on the cardEntities Page
|
||||
|
||||
### cardThermo Page
|
||||
|
||||
`event,pageOpen,0`
|
||||
|
||||
`event,buttonPress2,*entityName*,tempUpd,*temperature*`
|
||||
|
||||
`event,buttonPress2,*entityName*,hvac_action,*hvac_action*`
|
||||
|
||||
@@ -5,24 +5,11 @@ Program.s
|
||||
int recvCrc=0
|
||||
int payloadLength=0
|
||||
int par0=0,par1=0
|
||||
// landsspace orientation x has 480px and y has 320px xy limits todo: adjust xy values to something that fit's resulution
|
||||
//Maximum values in directional change for Swipes beeing detected as swipe (diagonal swipes are invalid) (for one axis at a time)
|
||||
int xLimit=125,yLimit=125
|
||||
int ixLimit=-125,iyLimit=-125
|
||||
//Minimum values for swipes, directional changes below theese values are ignored, because they could be unintended swipes
|
||||
int xLimitMin=80,yLimitMin=80
|
||||
int ixLimitMin=-80,iyLimitMin=-80
|
||||
// Swipe Result Vars
|
||||
int ycR=0,xcR=0
|
||||
// Start End Swipe Touch Locations
|
||||
int yc1=0,xc1=0,yc2=0,xc2=0
|
||||
// sleep timeout in s
|
||||
int sleepTimeout=20
|
||||
int sleepValue=0
|
||||
// dim value
|
||||
int dimValue=40
|
||||
// current page
|
||||
int nPage=0
|
||||
// fix touch offset
|
||||
lcd_dev fffb 0002 0000 0020
|
||||
page pageStartup
|
||||
|
||||
@@ -324,14 +324,7 @@ Button bNext
|
||||
|
||||
Events
|
||||
Touch Press Event
|
||||
nPage=nPage+1
|
||||
nPageDisp.val=nPage
|
||||
//page open event
|
||||
// event,pageOpen,cardEntities,pageNumber
|
||||
// craft command
|
||||
// convert pageNumber and write to tTmp
|
||||
covx nPage,tTmp.txt,0,0
|
||||
tSend.txt="event,pageOpen,"+tTmp.txt
|
||||
tSend.txt="event,buttonPress2,cardAlarm,bNext"
|
||||
//send calc crc
|
||||
btlen tSend.txt,sys0
|
||||
crcrest 1,0xffff // reset CRC
|
||||
@@ -378,14 +371,7 @@ Button bPrev
|
||||
|
||||
Events
|
||||
Touch Press Event
|
||||
nPage=nPage-1
|
||||
nPageDisp.val=nPage
|
||||
//page open event
|
||||
// event,pageOpen,cardEntities,pageNumber
|
||||
// craft command
|
||||
// convert pageNumber and write to tTmp
|
||||
covx nPage,tTmp.txt,0,0
|
||||
tSend.txt="event,pageOpen,"+tTmp.txt
|
||||
tSend.txt="event,buttonPress2,cardAlarm,bPrev"
|
||||
//send calc crc
|
||||
btlen tSend.txt,sys0
|
||||
crcrest 1,0xffff // reset CRC
|
||||
|
||||
@@ -1131,13 +1131,14 @@ Button bPrev
|
||||
|
||||
Events
|
||||
Touch Press Event
|
||||
nPage=nPage-1
|
||||
//page open event
|
||||
// event,pageOpen,cardEntities,pageNumber
|
||||
// craft command
|
||||
// convert pageNumber and write to tTmp
|
||||
covx nPage,tTmp.txt,0,0
|
||||
tSend.txt="event,pageOpen,"+tTmp.txt
|
||||
tSend.txt="event,buttonPress2,cardEntities,"
|
||||
if(bPrev.isbr==1)
|
||||
{
|
||||
tSend.txt+="bBack"
|
||||
}else
|
||||
{
|
||||
tSend.txt+="bPrev"
|
||||
}
|
||||
//send calc crc
|
||||
btlen tSend.txt,sys0
|
||||
crcrest 1,0xffff // reset CRC
|
||||
@@ -1184,13 +1185,7 @@ Button bNext
|
||||
|
||||
Events
|
||||
Touch Press Event
|
||||
nPage=nPage+1
|
||||
//page open event
|
||||
// event,pageOpen,cardEntities,pageNumber
|
||||
// craft command
|
||||
// convert pageNumber and write to tTmp
|
||||
covx nPage,tTmp.txt,0,0
|
||||
tSend.txt="event,pageOpen,"+tTmp.txt
|
||||
tSend.txt="event,buttonPress2,cardEntities,bNext"
|
||||
//send calc crc
|
||||
btlen tSend.txt,sys0
|
||||
crcrest 1,0xffff // reset CRC
|
||||
@@ -1776,6 +1771,27 @@ Timer tmSerial
|
||||
if(tInstruction.txt=="entityUpdHeading")
|
||||
{
|
||||
spstr strCommand.txt,tHeading.txt,",",1
|
||||
spstr strCommand.txt,tTmp.txt,",",2
|
||||
if(tTmp.txt=="0")
|
||||
{
|
||||
vis bPrev,0
|
||||
}else
|
||||
{
|
||||
vis bPrev,1
|
||||
if(tTmp.txt=="2")
|
||||
{
|
||||
bPrev.txt="î¶"
|
||||
bPrev.isbr=1
|
||||
}
|
||||
}
|
||||
spstr strCommand.txt,tTmp.txt,",",3
|
||||
if(tTmp.txt=="0")
|
||||
{
|
||||
vis bNext,0
|
||||
}else
|
||||
{
|
||||
vis bNext,1
|
||||
}
|
||||
}
|
||||
if(tInstruction.txt=="entityUpd")
|
||||
{
|
||||
@@ -1784,7 +1800,7 @@ Timer tmSerial
|
||||
spstr strCommand.txt,type1.txt,",",1
|
||||
// get internal name
|
||||
spstr strCommand.txt,entn1.txt,",",2
|
||||
if(type1.txt=="delete")
|
||||
if(type1.txt=="delete"||type1.txt=="")
|
||||
{
|
||||
vis bUp1,0
|
||||
vis bStop1,0
|
||||
@@ -1871,7 +1887,7 @@ Timer tmSerial
|
||||
spstr strCommand.txt,type2.txt,",",7
|
||||
// get internal name
|
||||
spstr strCommand.txt,entn2.txt,",",8
|
||||
if(type2.txt=="delete")
|
||||
if(type2.txt=="delete"||type2.txt=="")
|
||||
{
|
||||
vis bUp2,0
|
||||
vis bStop2,0
|
||||
@@ -1958,7 +1974,7 @@ Timer tmSerial
|
||||
spstr strCommand.txt,type3.txt,",",13
|
||||
// get internal name
|
||||
spstr strCommand.txt,entn3.txt,",",14
|
||||
if(type3.txt=="delete")
|
||||
if(type3.txt=="delete"||type3.txt=="")
|
||||
{
|
||||
vis bUp3,0
|
||||
vis bStop3,0
|
||||
@@ -2045,7 +2061,7 @@ Timer tmSerial
|
||||
spstr strCommand.txt,type4.txt,",",19
|
||||
// get internal name
|
||||
spstr strCommand.txt,entn4.txt,",",20
|
||||
if(type4.txt=="delete")
|
||||
if(type4.txt=="delete"||type4.txt=="")
|
||||
{
|
||||
vis bUp4,0
|
||||
vis bStop4,0
|
||||
|
||||
@@ -515,13 +515,7 @@ Button bPrev
|
||||
|
||||
Events
|
||||
Touch Press Event
|
||||
nPage=nPage-1
|
||||
//page open event
|
||||
// event,pageOpen,cardEntities,pageNumber
|
||||
// craft command
|
||||
// convert pageNumber and write to tTmp
|
||||
covx nPage,tTmp.txt,0,0
|
||||
tSend.txt="event,pageOpen,"+tTmp.txt
|
||||
tSend.txt="event,buttonPress2,cardGrid,bPrev"
|
||||
//send calc crc
|
||||
btlen tSend.txt,sys0
|
||||
crcrest 1,0xffff // reset CRC
|
||||
@@ -568,13 +562,7 @@ Button bNext
|
||||
|
||||
Events
|
||||
Touch Press Event
|
||||
nPage=nPage+1
|
||||
//page open event
|
||||
// event,pageOpen,cardEntities,pageNumber
|
||||
// craft command
|
||||
// convert pageNumber and write to tTmp
|
||||
covx nPage,tTmp.txt,0,0
|
||||
tSend.txt="event,pageOpen,"+tTmp.txt
|
||||
tSend.txt="event,buttonPress2,cardGrid,bNext"
|
||||
//send calc crc
|
||||
btlen tSend.txt,sys0
|
||||
crcrest 1,0xffff // reset CRC
|
||||
|
||||
@@ -23,55 +23,24 @@ Page cardMedia
|
||||
vis p0,0
|
||||
vis tSend,0
|
||||
vis tInstruction,0
|
||||
vis nPageDisp,0
|
||||
vis tTmp,0
|
||||
vis tId,0
|
||||
//vis nPageDisp,0
|
||||
|
||||
Variable (string) strCommand
|
||||
Attributes
|
||||
ID : 8
|
||||
ID : 7
|
||||
Scope : local
|
||||
Text :
|
||||
Max. Text Size: 200
|
||||
|
||||
Variable (string) entn
|
||||
Attributes
|
||||
ID : 19
|
||||
ID : 18
|
||||
Scope : local
|
||||
Text :
|
||||
Max. Text Size: 50
|
||||
|
||||
Number nPageDisp
|
||||
Attributes
|
||||
ID : 6
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
Send Component ID : disabled
|
||||
Opacity : 127
|
||||
x coordinate : 426
|
||||
y coordinate : 0
|
||||
Width : 42
|
||||
Height : 24
|
||||
Effect : load
|
||||
Effect Priority : 0
|
||||
Effect Time : 300
|
||||
Fill : solid color
|
||||
Style : flat
|
||||
Associated Keyboard : none
|
||||
Font ID : 0
|
||||
Back. Color : 65535
|
||||
Font Color : 0
|
||||
Horizontal Alignment : center
|
||||
Vertical Alignment : center
|
||||
Value : 0
|
||||
Significant digits shown : all
|
||||
Format : decimal
|
||||
Word wrap : enabled
|
||||
Horizontal Spacing : 0
|
||||
Vertical Spacing : 0
|
||||
|
||||
Text tSend
|
||||
Attributes
|
||||
ID : 2
|
||||
@@ -134,7 +103,7 @@ Text tTmp
|
||||
|
||||
Text tInstruction
|
||||
Attributes
|
||||
ID : 9
|
||||
ID : 8
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
@@ -164,7 +133,7 @@ Text tInstruction
|
||||
|
||||
Text tId
|
||||
Attributes
|
||||
ID : 10
|
||||
ID : 9
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
@@ -194,7 +163,7 @@ Text tId
|
||||
|
||||
Text tHeading
|
||||
Attributes
|
||||
ID : 11
|
||||
ID : 10
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
@@ -224,7 +193,7 @@ Text tHeading
|
||||
|
||||
Text tTitle
|
||||
Attributes
|
||||
ID : 12
|
||||
ID : 11
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
@@ -254,7 +223,7 @@ Text tTitle
|
||||
|
||||
Text tAuthor
|
||||
Attributes
|
||||
ID : 13
|
||||
ID : 12
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
@@ -284,7 +253,7 @@ Text tAuthor
|
||||
|
||||
Text t2
|
||||
Attributes
|
||||
ID : 14
|
||||
ID : 13
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
@@ -333,7 +302,7 @@ Text t2
|
||||
|
||||
Text tPlayPause
|
||||
Attributes
|
||||
ID : 15
|
||||
ID : 14
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
@@ -382,7 +351,7 @@ Text tPlayPause
|
||||
|
||||
Text t0
|
||||
Attributes
|
||||
ID : 16
|
||||
ID : 15
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
@@ -431,7 +400,7 @@ Text t0
|
||||
|
||||
Text tIcon
|
||||
Attributes
|
||||
ID : 18
|
||||
ID : 17
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
@@ -459,6 +428,106 @@ Text tIcon
|
||||
Horizontal Spacing : 0
|
||||
Vertical Spacing : 0
|
||||
|
||||
Text t1
|
||||
Attributes
|
||||
ID : 22
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
Send Component ID : disabled
|
||||
Opacity : 127
|
||||
x coordinate : 5
|
||||
y coordinate : 222
|
||||
Width : 50
|
||||
Height : 50
|
||||
Effect : load
|
||||
Effect Priority : 0
|
||||
Effect Time : 300
|
||||
Fill : solid color
|
||||
Style : flat
|
||||
Associated Keyboard : none
|
||||
Font ID : 2
|
||||
Back. Color : 6371
|
||||
Font Color : 50712
|
||||
Horizontal Alignment : center
|
||||
Vertical Alignment : center
|
||||
Input Type : character
|
||||
Text : î
|
||||
Max. Text Size : 10
|
||||
Word wrap : disabled
|
||||
Horizontal Spacing : 0
|
||||
Vertical Spacing : 0
|
||||
|
||||
Events
|
||||
Touch Press Event
|
||||
hVolume.val=hVolume.val-5
|
||||
// event,buttonPress2,internalNameEntity,volumeSlider,50
|
||||
//craft command
|
||||
tSend.txt="event,buttonPress2,"+entn.txt+",volumeSlider,"
|
||||
covx hVolume.val,tTmp.txt,0,0
|
||||
tSend.txt+=tTmp.txt
|
||||
//send calc crc
|
||||
btlen tSend.txt,sys0
|
||||
crcrest 1,0xffff // reset CRC
|
||||
crcputh 55 bb
|
||||
crcputs sys0,1
|
||||
crcputs tSend.txt,0
|
||||
//send cmd
|
||||
printh 55 bb
|
||||
prints sys0,2
|
||||
prints tSend.txt,0
|
||||
prints crcval,2
|
||||
|
||||
Text t3
|
||||
Attributes
|
||||
ID : 23
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
Send Component ID : disabled
|
||||
Opacity : 127
|
||||
x coordinate : 397
|
||||
y coordinate : 222
|
||||
Width : 50
|
||||
Height : 50
|
||||
Effect : load
|
||||
Effect Priority : 0
|
||||
Effect Time : 300
|
||||
Fill : solid color
|
||||
Style : flat
|
||||
Associated Keyboard : none
|
||||
Font ID : 2
|
||||
Back. Color : 6371
|
||||
Font Color : 50712
|
||||
Horizontal Alignment : center
|
||||
Vertical Alignment : center
|
||||
Input Type : character
|
||||
Text : î
|
||||
Max. Text Size : 10
|
||||
Word wrap : disabled
|
||||
Horizontal Spacing : 0
|
||||
Vertical Spacing : 0
|
||||
|
||||
Events
|
||||
Touch Press Event
|
||||
hVolume.val=hVolume.val+5
|
||||
// event,buttonPress2,internalNameEntity,volumeSlider,50
|
||||
//craft command
|
||||
tSend.txt="event,buttonPress2,"+entn.txt+",volumeSlider,"
|
||||
covx hVolume.val,tTmp.txt,0,0
|
||||
tSend.txt+=tTmp.txt
|
||||
//send calc crc
|
||||
btlen tSend.txt,sys0
|
||||
crcrest 1,0xffff // reset CRC
|
||||
crcputh 55 bb
|
||||
crcputs sys0,1
|
||||
crcputs tSend.txt,0
|
||||
//send cmd
|
||||
printh 55 bb
|
||||
prints sys0,2
|
||||
prints tSend.txt,0
|
||||
prints crcval,2
|
||||
|
||||
Picture p0
|
||||
Attributes
|
||||
ID : 1
|
||||
@@ -478,13 +547,13 @@ Picture p0
|
||||
|
||||
Slider hVolume
|
||||
Attributes
|
||||
ID : 17
|
||||
ID : 16
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
Send Component ID : disabled
|
||||
Opacity : 127
|
||||
x coordinate : 61
|
||||
x coordinate : 59
|
||||
y coordinate : 227
|
||||
Width : 335
|
||||
Height : 43
|
||||
@@ -503,6 +572,8 @@ Slider hVolume
|
||||
|
||||
Events
|
||||
Touch Release Event
|
||||
tmCooldown.en=1
|
||||
tmCooldown.tim=100
|
||||
// event,buttonPress2,internalNameEntity,volumeSlider,50
|
||||
//craft command
|
||||
tSend.txt="event,buttonPress2,"+entn.txt+",volumeSlider,"
|
||||
@@ -554,14 +625,7 @@ Button bNext
|
||||
|
||||
Events
|
||||
Touch Press Event
|
||||
nPage=nPage+1
|
||||
nPageDisp.val=nPage
|
||||
//page open event
|
||||
// event,pageOpen,cardEntities,pageNumber
|
||||
// craft command
|
||||
// convert pageNumber and write to tTmp
|
||||
covx nPage,tTmp.txt,0,0
|
||||
tSend.txt="event,pageOpen,"+tTmp.txt
|
||||
tSend.txt="event,buttonPress2,cardMedia,bNext"
|
||||
//send calc crc
|
||||
btlen tSend.txt,sys0
|
||||
crcrest 1,0xffff // reset CRC
|
||||
@@ -608,14 +672,7 @@ Button bPrev
|
||||
|
||||
Events
|
||||
Touch Press Event
|
||||
nPage=nPage-1
|
||||
nPageDisp.val=nPage
|
||||
//page open event
|
||||
// event,pageOpen,cardEntities,pageNumber
|
||||
// craft command
|
||||
// convert pageNumber and write to tTmp
|
||||
covx nPage,tTmp.txt,0,0
|
||||
tSend.txt="event,pageOpen,"+tTmp.txt
|
||||
tSend.txt="event,buttonPress2,cardMedia,bPrev"
|
||||
//send calc crc
|
||||
btlen tSend.txt,sys0
|
||||
crcrest 1,0xffff // reset CRC
|
||||
@@ -630,7 +687,7 @@ Button bPrev
|
||||
|
||||
Timer tmSerial
|
||||
Attributes
|
||||
ID : 7
|
||||
ID : 6
|
||||
Scope : local
|
||||
Period (ms): 50
|
||||
Enabled : yes
|
||||
@@ -791,7 +848,7 @@ Timer tmSerial
|
||||
|
||||
Timer tmSleep
|
||||
Attributes
|
||||
ID : 20
|
||||
ID : 19
|
||||
Scope : local
|
||||
Period (ms): 1000
|
||||
Enabled : yes
|
||||
@@ -809,9 +866,20 @@ Timer tmSleep
|
||||
}
|
||||
}
|
||||
|
||||
Timer tmCooldown
|
||||
Attributes
|
||||
ID : 21
|
||||
Scope : local
|
||||
Period (ms): 100
|
||||
Enabled : no
|
||||
|
||||
Events
|
||||
Timer Event
|
||||
tmCooldown.en=0
|
||||
|
||||
TouchCap tc0
|
||||
Attributes
|
||||
ID : 21
|
||||
ID : 20
|
||||
Scope: local
|
||||
Value: 0
|
||||
|
||||
|
||||
@@ -559,14 +559,7 @@ Button bNext
|
||||
|
||||
Events
|
||||
Touch Press Event
|
||||
nPage=nPage+1
|
||||
nPageDisp.val=nPage
|
||||
//page open event
|
||||
// event,pageOpen,cardEntities,pageNumber
|
||||
// craft command
|
||||
// convert pageNumber and write to tTmp
|
||||
covx nPage,tTmp.txt,0,0
|
||||
tSend.txt="event,pageOpen,"+tTmp.txt
|
||||
tSend.txt="event,buttonPress2,cardThermo,bNext"
|
||||
//send calc crc
|
||||
btlen tSend.txt,sys0
|
||||
crcrest 1,0xffff // reset CRC
|
||||
@@ -721,14 +714,7 @@ Button bPrev
|
||||
|
||||
Events
|
||||
Touch Press Event
|
||||
nPage=nPage-1
|
||||
nPageDisp.val=nPage
|
||||
//page open event
|
||||
// event,pageOpen,cardEntities,pageNumber
|
||||
// craft command
|
||||
// convert pageNumber and write to tTmp
|
||||
covx nPage,tTmp.txt,0,0
|
||||
tSend.txt="event,pageOpen,"+tTmp.txt
|
||||
tSend.txt="event,buttonPress2,cardThermo,bPrev"
|
||||
//send calc crc
|
||||
btlen tSend.txt,sys0
|
||||
crcrest 1,0xffff // reset CRC
|
||||
|
||||
@@ -1,66 +1,62 @@
|
||||
Program.s
|
||||
0 Component(s)
|
||||
17 Line(s) of event code
|
||||
17 Unique line(s) of event code
|
||||
10 Line(s) of event code
|
||||
10 Unique line(s) of event code
|
||||
pageIcons
|
||||
6 Component(s)
|
||||
0 Line(s) of event code
|
||||
0 Unique line(s) of event code
|
||||
pageTest
|
||||
13 Component(s)
|
||||
13 Line(s) of event code
|
||||
13 Unique line(s) of event code
|
||||
pageSerialTest
|
||||
13 Component(s)
|
||||
48 Line(s) of event code
|
||||
43 Unique line(s) of event code
|
||||
popupNotify
|
||||
17 Component(s)
|
||||
180 Line(s) of event code
|
||||
119 Unique line(s) of event code
|
||||
pageStartup
|
||||
19 Component(s)
|
||||
146 Line(s) of event code
|
||||
111 Unique line(s) of event code
|
||||
cardMedia
|
||||
22 Component(s)
|
||||
200 Line(s) of event code
|
||||
115 Unique line(s) of event code
|
||||
pageSwipeTest
|
||||
18 Component(s)
|
||||
62 Line(s) of event code
|
||||
44 Unique line(s) of event code
|
||||
popupShutter
|
||||
19 Component(s)
|
||||
180 Line(s) of event code
|
||||
103 Unique line(s) of event code
|
||||
pageTest
|
||||
14 Component(s)
|
||||
14 Line(s) of event code
|
||||
14 Unique line(s) of event code
|
||||
popupLight
|
||||
26 Component(s)
|
||||
304 Line(s) of event code
|
||||
167 Unique line(s) of event code
|
||||
screensaver
|
||||
25 Component(s)
|
||||
173 Line(s) of event code
|
||||
124 Unique line(s) of event code
|
||||
cardThermo
|
||||
42 Component(s)
|
||||
412 Line(s) of event code
|
||||
221 Unique line(s) of event code
|
||||
cardGrid
|
||||
39 Component(s)
|
||||
382 Line(s) of event code
|
||||
221 Unique line(s) of event code
|
||||
cardEntities
|
||||
54 Component(s)
|
||||
728 Line(s) of event code
|
||||
317 Unique line(s) of event code
|
||||
179 Line(s) of event code
|
||||
102 Unique line(s) of event code
|
||||
popupNotify
|
||||
17 Component(s)
|
||||
179 Line(s) of event code
|
||||
118 Unique line(s) of event code
|
||||
pageStartup
|
||||
19 Component(s)
|
||||
150 Line(s) of event code
|
||||
113 Unique line(s) of event code
|
||||
cardAlarm
|
||||
35 Component(s)
|
||||
259 Line(s) of event code
|
||||
163 Unique line(s) of event code
|
||||
253 Line(s) of event code
|
||||
160 Unique line(s) of event code
|
||||
popupLight
|
||||
27 Component(s)
|
||||
323 Line(s) of event code
|
||||
173 Unique line(s) of event code
|
||||
cardGrid
|
||||
39 Component(s)
|
||||
378 Line(s) of event code
|
||||
219 Unique line(s) of event code
|
||||
cardThermo
|
||||
42 Component(s)
|
||||
406 Line(s) of event code
|
||||
218 Unique line(s) of event code
|
||||
cardEntities
|
||||
54 Component(s)
|
||||
752 Line(s) of event code
|
||||
330 Unique line(s) of event code
|
||||
cardMedia
|
||||
24 Component(s)
|
||||
222 Line(s) of event code
|
||||
116 Unique line(s) of event code
|
||||
screensaver
|
||||
26 Component(s)
|
||||
176 Line(s) of event code
|
||||
127 Unique line(s) of event code
|
||||
|
||||
Total
|
||||
14 Page(s)
|
||||
349 Component(s)
|
||||
3105 Line(s) of event code
|
||||
827 Unique line(s) of event code
|
||||
13 Page(s)
|
||||
334 Component(s)
|
||||
3089 Line(s) of event code
|
||||
817 Unique line(s) of event code
|
||||
|
||||
@@ -402,7 +402,7 @@ Text tVersion
|
||||
Horizontal Alignment : center
|
||||
Vertical Alignment : center
|
||||
Input Type : character
|
||||
Text : 16
|
||||
Text : 19
|
||||
Max. Text Size : 10
|
||||
Word wrap : disabled
|
||||
Horizontal Spacing : 0
|
||||
@@ -592,6 +592,10 @@ Timer tmSerial
|
||||
{
|
||||
page cardThermo
|
||||
}
|
||||
if(tId.txt=="screensaver")
|
||||
{
|
||||
page screensaver
|
||||
}
|
||||
if(tId.txt=="popupLight")
|
||||
{
|
||||
pageIcons.tTmp1.txt=tTmp.txt
|
||||
|
||||
@@ -111,45 +111,9 @@ Button b1
|
||||
Touch Press Event
|
||||
page pageSerialTest
|
||||
|
||||
Button b2
|
||||
Attributes
|
||||
ID : 4
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
Send Component ID : disabled
|
||||
Opacity : 127
|
||||
x coordinate : 7
|
||||
y coordinate : 265
|
||||
Width : 100
|
||||
Height : 50
|
||||
Effect : load
|
||||
Effect Priority : 0
|
||||
Effect Time : 300
|
||||
Fill : solid color
|
||||
Style : 3D auto
|
||||
Font ID : 4
|
||||
Back. Color : 50712
|
||||
Back. Picture ID (Pressed) : 65535
|
||||
Back. Color (Pressed) : 1024
|
||||
Font Color (Unpressed) : 0
|
||||
Font Color (Pressed) : 65535
|
||||
Horizontal Alignment : center
|
||||
Vertical Alignment : center
|
||||
State : unpressed
|
||||
Text : swipe
|
||||
Max. Text Size : 10
|
||||
Word wrap : disabled
|
||||
Horizontal Spacing : 0
|
||||
Vertical Spacing : 0
|
||||
|
||||
Events
|
||||
Touch Press Event
|
||||
page pageSwipeTest
|
||||
|
||||
Button b3
|
||||
Attributes
|
||||
ID : 5
|
||||
ID : 4
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
@@ -185,7 +149,7 @@ Button b3
|
||||
|
||||
Button b6
|
||||
Attributes
|
||||
ID : 6
|
||||
ID : 5
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
@@ -221,7 +185,7 @@ Button b6
|
||||
|
||||
Button b4
|
||||
Attributes
|
||||
ID : 7
|
||||
ID : 6
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
@@ -257,7 +221,7 @@ Button b4
|
||||
|
||||
Button b5
|
||||
Attributes
|
||||
ID : 8
|
||||
ID : 7
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
@@ -293,7 +257,7 @@ Button b5
|
||||
|
||||
Button b7
|
||||
Attributes
|
||||
ID : 9
|
||||
ID : 8
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
@@ -329,7 +293,7 @@ Button b7
|
||||
|
||||
Button b8
|
||||
Attributes
|
||||
ID : 10
|
||||
ID : 9
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
@@ -366,7 +330,7 @@ Button b8
|
||||
|
||||
Button b9
|
||||
Attributes
|
||||
ID : 11
|
||||
ID : 10
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
@@ -402,7 +366,7 @@ Button b9
|
||||
|
||||
Button b10
|
||||
Attributes
|
||||
ID : 12
|
||||
ID : 11
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
@@ -438,7 +402,7 @@ Button b10
|
||||
|
||||
Button b11
|
||||
Attributes
|
||||
ID : 13
|
||||
ID : 12
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
|
||||
@@ -500,6 +500,8 @@ Slider hBrightness
|
||||
|
||||
Events
|
||||
Touch Release Event
|
||||
tmCooldown.en=1
|
||||
tmCooldown.tim=200
|
||||
//craft command
|
||||
tSend.txt="event,buttonPress2,"+entn.txt+",brightnessSlider,"
|
||||
covx hBrightness.val,tTmp.txt,0,0
|
||||
@@ -543,6 +545,8 @@ Slider hTempSlider
|
||||
|
||||
Events
|
||||
Touch Release Event
|
||||
tmCooldown.en=1
|
||||
tmCooldown.tim=200
|
||||
//craft command
|
||||
tSend.txt="event,buttonPress2,"+entn.txt+",colorTempSlider,"
|
||||
covx hTempSlider.val,tTmp.txt,0,0
|
||||
@@ -593,12 +597,7 @@ Button b0
|
||||
|
||||
Events
|
||||
Touch Press Event
|
||||
//page open event
|
||||
// event,pageOpen,cardEntities,pageNumber
|
||||
// craft command
|
||||
// convert pageNumber and write to tTmp
|
||||
covx nPage,tTmp.txt,0,0
|
||||
tSend.txt="event,pageOpen,"+tTmp.txt
|
||||
tSend.txt="event,buttonPress2,popupLight,bExit"
|
||||
//send calc crc
|
||||
btlen tSend.txt,sys0
|
||||
crcrest 1,0xffff // reset CRC
|
||||
@@ -815,7 +814,10 @@ Timer tmSerial
|
||||
vis t2,1
|
||||
vis hBrightness,1
|
||||
covx tTmp.txt,sys0,0,0
|
||||
hBrightness.val=sys0
|
||||
if(tmCooldown.en==0)
|
||||
{
|
||||
hBrightness.val=sys0
|
||||
}
|
||||
}
|
||||
// get ColorTemp value
|
||||
spstr strCommand.txt,tTmp.txt,",",5
|
||||
@@ -844,10 +846,15 @@ Timer tmSerial
|
||||
}
|
||||
}else
|
||||
{
|
||||
mode_temp.val=0
|
||||
mode_temp.val=1
|
||||
vis hTempSlider,1
|
||||
vis t3,1
|
||||
vis t4,1
|
||||
if(tmCooldown.en==0)
|
||||
{
|
||||
covx tTmp.txt,sys0,0,0
|
||||
hTempSlider.val=sys0
|
||||
}
|
||||
// mode == 0 is rgb controls currently shown
|
||||
if(mode.val==0)
|
||||
{
|
||||
@@ -867,7 +874,11 @@ Timer tmSerial
|
||||
}else
|
||||
{
|
||||
vis t0,1
|
||||
vis bColor,1
|
||||
//only enable bColor button if color temp is also supported
|
||||
if(mode_temp.val==1)
|
||||
{
|
||||
vis bColor,1
|
||||
}
|
||||
if(mode.val==0)
|
||||
{
|
||||
vis pColorWheel,1
|
||||
@@ -945,6 +956,17 @@ Timer tmSerial
|
||||
}
|
||||
}
|
||||
|
||||
Timer tmCooldown
|
||||
Attributes
|
||||
ID : 26
|
||||
Scope : local
|
||||
Period (ms): 100
|
||||
Enabled : no
|
||||
|
||||
Events
|
||||
Timer Event
|
||||
tmCooldown.en=0
|
||||
|
||||
TouchCap tc0
|
||||
Attributes
|
||||
ID : 14
|
||||
|
||||
@@ -278,12 +278,7 @@ Button b0
|
||||
Events
|
||||
Touch Press Event
|
||||
sleepTimeout=vaOldSleepT.val
|
||||
//page open event
|
||||
// event,pageOpen,cardEntities,pageNumber
|
||||
// craft command
|
||||
// convert pageNumber and write to tTmp
|
||||
covx nPage,tTmp.txt,0,0
|
||||
tSend.txt="event,pageOpen,"+tTmp.txt
|
||||
tSend.txt="event,buttonPress2,popupNotify,bExit"
|
||||
//send calc crc
|
||||
btlen tSend.txt,sys0
|
||||
crcrest 1,0xffff // reset CRC
|
||||
|
||||
@@ -364,12 +364,7 @@ Button b0
|
||||
|
||||
Events
|
||||
Touch Press Event
|
||||
//page open event
|
||||
// event,pageOpen,cardEntities,pageNumber
|
||||
// craft command
|
||||
// convert pageNumber and write to tTmp
|
||||
covx nPage,tTmp.txt,0,0
|
||||
tSend.txt="event,pageOpen,"+tTmp.txt
|
||||
tSend.txt="event,buttonPress2,popupShutter,bExit"
|
||||
//send calc crc
|
||||
btlen tSend.txt,sys0
|
||||
crcrest 1,0xffff // reset CRC
|
||||
|
||||
@@ -27,12 +27,28 @@ Page screensaver
|
||||
vis tTmp,0
|
||||
vis p0,0
|
||||
//load gloabl time string from pageIcons
|
||||
tTime.txt=vaTime.txt
|
||||
//tTime.txt=vaTime.txt
|
||||
spstr vaTime.txt,tTime.txt,"?",0
|
||||
spstr vaTime.txt,tAMPM.txt,"?",1
|
||||
if(tAMPM.txt=="")
|
||||
{
|
||||
vis tAMPM,0
|
||||
}
|
||||
tDate.txt=vaDate.txt
|
||||
dim=dimValue
|
||||
vis tSend,0
|
||||
//page open event
|
||||
// craft command
|
||||
// clear weather elements, to keep example content in HMI
|
||||
tMainIcon.txt=""
|
||||
tMainText.txt=""
|
||||
tMRIcon.txt=""
|
||||
tMR.txt=""
|
||||
tForecast1.txt=""
|
||||
tF1Icon.txt=""
|
||||
tForecast1Val.txt=""
|
||||
tForecast2.txt=""
|
||||
tF2Icon.txt=""
|
||||
tForecast2Val.txt=""
|
||||
tSend.txt="event,screensaverOpen"
|
||||
//send calc crc
|
||||
btlen tSend.txt,sys0
|
||||
@@ -45,17 +61,6 @@ Page screensaver
|
||||
prints sys0,2
|
||||
prints tSend.txt,0
|
||||
prints crcval,2
|
||||
// clear weather elements, to keep example content in HMI
|
||||
tMainIcon.txt=""
|
||||
tMainText.txt=""
|
||||
tMRIcon.txt=""
|
||||
tMR.txt=""
|
||||
tForecast1.txt=""
|
||||
tF1Icon.txt=""
|
||||
tForecast1Val.txt=""
|
||||
tForecast2.txt=""
|
||||
tF2Icon.txt=""
|
||||
tForecast2Val.txt=""
|
||||
|
||||
Variable (string) strCommand
|
||||
Attributes
|
||||
@@ -69,7 +74,7 @@ Variable (string) vaTime
|
||||
ID : 10
|
||||
Scope : global
|
||||
Text :
|
||||
Max. Text Size: 10
|
||||
Max. Text Size: 15
|
||||
|
||||
Variable (string) vaDate
|
||||
Attributes
|
||||
@@ -199,8 +204,8 @@ Text tTime
|
||||
Horizontal Alignment : center
|
||||
Vertical Alignment : center
|
||||
Input Type : character
|
||||
Text : 21:32
|
||||
Max. Text Size : 10
|
||||
Text : 21:32
|
||||
Max. Text Size : 15
|
||||
Word wrap : disabled
|
||||
Horizontal Spacing : 0
|
||||
Vertical Spacing : 0
|
||||
@@ -260,7 +265,7 @@ Text tSend
|
||||
Vertical Alignment : center
|
||||
Input Type : character
|
||||
Text :
|
||||
Max. Text Size : 25
|
||||
Max. Text Size : 50
|
||||
Word wrap : disabled
|
||||
Horizontal Spacing : 0
|
||||
Vertical Spacing : 0
|
||||
@@ -595,6 +600,36 @@ Text t10
|
||||
Horizontal Spacing : 0
|
||||
Vertical Spacing : 0
|
||||
|
||||
Text tAMPM
|
||||
Attributes
|
||||
ID : 25
|
||||
Scope : local
|
||||
Dragging : 0
|
||||
Disable release event after dragging: 0
|
||||
Send Component ID : disabled
|
||||
Opacity : 127
|
||||
x coordinate : 343
|
||||
y coordinate : 48
|
||||
Width : 104
|
||||
Height : 57
|
||||
Effect : load
|
||||
Effect Priority : 0
|
||||
Effect Time : 300
|
||||
Fill : solid color
|
||||
Style : flat
|
||||
Associated Keyboard : none
|
||||
Font ID : 3
|
||||
Back. Color : 0
|
||||
Font Color : 65535
|
||||
Horizontal Alignment : center
|
||||
Vertical Alignment : center
|
||||
Input Type : character
|
||||
Text : PM
|
||||
Max. Text Size : 10
|
||||
Word wrap : disabled
|
||||
Horizontal Spacing : 0
|
||||
Vertical Spacing : 0
|
||||
|
||||
Picture p0
|
||||
Attributes
|
||||
ID : 1
|
||||
@@ -680,7 +715,12 @@ Timer tmSerial
|
||||
{
|
||||
//get set time to global variable
|
||||
spstr strCommand.txt,vaTime.txt,",",1
|
||||
tTime.txt=vaTime.txt
|
||||
spstr vaTime.txt,tTime.txt,"?",0
|
||||
spstr vaTime.txt,tAMPM.txt,"?",1
|
||||
if(tAMPM.txt=="")
|
||||
{
|
||||
vis tAMPM,0
|
||||
}
|
||||
}
|
||||
if(tInstruction.txt=="date")
|
||||
{
|
||||
@@ -725,14 +765,6 @@ Timer tmSerial
|
||||
//tForecast2Val
|
||||
spstr strCommand.txt,tForecast2Val.txt,"?",10
|
||||
}
|
||||
if(tInstruction.txt=="page")
|
||||
{
|
||||
//pagenumber
|
||||
spstr strCommand.txt,tTmp.txt,",",1
|
||||
covx tTmp.txt,sys0,0,0
|
||||
nPage=sys0
|
||||
//don't send current page number, wake will do
|
||||
}
|
||||
if(tInstruction.txt=="pageType")
|
||||
{
|
||||
dim=100
|
||||
@@ -804,12 +836,7 @@ TouchCap tc0
|
||||
|
||||
Events
|
||||
Touch Press Event
|
||||
//page open event
|
||||
// event,pageOpen,cardEntities,pageNumber
|
||||
// craft command
|
||||
// convert pageNumber and write to tTmp
|
||||
covx nPage,tTmp.txt,0,0
|
||||
tSend.txt="event,pageOpen,"+tTmp.txt
|
||||
tSend.txt="event,buttonPress2,screensaver,bExit"
|
||||
//send calc crc
|
||||
btlen tSend.txt,sys0
|
||||
crcrest 1,0xffff // reset CRC
|
||||
|
||||
BIN
HMI/nspanel.HMI
BIN
HMI/nspanel.HMI
Binary file not shown.
BIN
HMI/nspanel.tft
BIN
HMI/nspanel.tft
Binary file not shown.
48
README.md
48
README.md
@@ -202,7 +202,7 @@ Due the limitations of Berry, it's not possible to download the tft file directl
|
||||
|
||||
The following Link has always the latest version from this repository, just execute the following Command in Tasmota:
|
||||
|
||||
`FlashNextion http://nspanel.pky.eu/lui.tft`
|
||||
`FlashNextion http://nspanel.pky.eu/lui-release.tft`
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -244,26 +244,25 @@ Confiure your NSPanel as you like, you need to edit the `apps.yaml` inside of yo
|
||||
You can have multiple nspanel sections.
|
||||
|
||||
```yaml
|
||||
---
|
||||
nspanel-1:
|
||||
module: nspanel-lovelace-ui
|
||||
class: NsPanelLovelaceUIManager
|
||||
config:
|
||||
panelRecvTopic: "tele/tasmota_your_mqtt_topic/RESULT"
|
||||
panelSendTopic: "cmnd/tasmota_your_mqtt_topic/CustomSend"
|
||||
updateMode: auto-notify # possible values are auto, auto-notify and manual
|
||||
timeoutScreensaver: 15 #in seconds
|
||||
timeoutScreensaver: 20
|
||||
#brightnessScreensaver: 10
|
||||
brightnessScreensaver:
|
||||
- time: "7:00:00"
|
||||
value: 10
|
||||
- time: "23:00:00"
|
||||
value: 0
|
||||
locale: "de_DE" # only used if babel python package is installed
|
||||
dateFormatBabel: "full" # only used if babel python package is installed
|
||||
# formatting options on https://babel.pocoo.org/en/latest/dates.html?highlight=name%20of%20day#date-fields
|
||||
locale: "de_DE"
|
||||
dateFormatBabel: "full"
|
||||
timeFormat: "%H:%M"
|
||||
dateFormat: "%A, %d. %B %Y" # ignored if babel python package is installed
|
||||
weatherEntity: weather.example
|
||||
weather: weather.example
|
||||
pages:
|
||||
- type: cardEntities
|
||||
heading: Example Page 1
|
||||
@@ -309,9 +308,39 @@ key | optional | type | default | description
|
||||
`class` | False | string | | The name of the Class.
|
||||
`config` | False | complex | | Config/Mapping between Homeassistant and your NsPanel
|
||||
|
||||
### Override Icons
|
||||
Possible configuration values for config key:
|
||||
|
||||
To override Icons of entities you can configure an icon name in your configuration, please see the following example.
|
||||
key | optional | type | default | description
|
||||
-- | -- | -- | -- | --
|
||||
`panelRecvTopic` | False | string | `tele/tasmota_your_mqtt_topic/RESULT` | The mqtt topic used to receive messages.
|
||||
`panelSendTopic` | False | string | `cmnd/tasmota_your_mqtt_topic/CustomSend` | The mqtt topic used to send messages.
|
||||
`timeoutScreensaver` | True | integer | `20` | Timeout for the screen to enter screensaver, to disable screensaver use 0
|
||||
`brightnessScreensaver` | True | integer/complex | `20` | Brightness for the screen to enter screensaver, see example below for complex/scheduled config.
|
||||
`brightnessScreensaverTracking` | True | string | None | Forces screensaver brightness to 0 in case entity state is not_home, can be a group, person or device_tracker entity.
|
||||
`locale` | True | string | `en_US` | Used by babel to determinante Date format on screensaver, also used for localization.
|
||||
`dateFormatBabel` | True | string | `full` | formatting options on https://babel.pocoo.org/en/latest/dates.html?highlight=name%20of%20day#date-fields
|
||||
`timeFormat` | True | string | `%H:%M` | Time Format on screensaver. Substring after `?` is displayed in a seperate smaller textbox. Useful for 12h time format with AM/PM `"%I:%M ?%p"`
|
||||
`dateFormat` | True | string | `%A, %d. %B %Y` | date format used if babel is not installed
|
||||
`weather` | True | string | `weather.example` | weather entity from homeassistant
|
||||
`weatherOverrideForecast1` | True | string | `None` | sensor entity from home assistant here to override the first weather forecast item on the screensaver
|
||||
`weatherOverrideForecast2` | True | string | `None` | sensor entity from home assistant here to override the second weather forecast item on the screensaver
|
||||
`pages` | False | complex | | configuration for pages on panel
|
||||
|
||||
#### Schedule screensaver brightness
|
||||
|
||||
It is possible to schedule a brightness change for the screen at specific times.
|
||||
|
||||
```yaml
|
||||
brightnessScreensaver:
|
||||
- time: "7:00:00"
|
||||
value: 10
|
||||
- time: "23:00:00"
|
||||
value: 0
|
||||
```
|
||||
|
||||
#### Override Icons or Names
|
||||
|
||||
To override Icons or Names of entities you can configure an icon and/or name in your configuration, please see the following example.
|
||||
Only the icons listed in the [Icon Table](HMI#icons-ids) are useable.
|
||||
|
||||
```yaml
|
||||
@@ -322,6 +351,7 @@ Only the icons listed in the [Icon Table](HMI#icons-ids) are useable.
|
||||
- light.schreibtischlampe
|
||||
- switch.deckenbeleuchtung_hinten:
|
||||
icon: lightbulb
|
||||
name: Lampe
|
||||
- delete
|
||||
- delete
|
||||
- type: cardMedia
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
nspanel:
|
||||
nspanel-1:
|
||||
module: nspanel-lovelace-ui
|
||||
class: NsPanelLovelaceUIManager
|
||||
config:
|
||||
panelRecvTopic: "tele/tasmota_your_mqtt_topic/RESULT"
|
||||
panelSendTopic: "cmnd/tasmota_your_mqtt_topic/CustomSend"
|
||||
updateMode: auto-notify # possible values are auto, auto-notify and manual
|
||||
timeoutScreensaver: 15 #in seconds
|
||||
updateMode: "auto-notify"
|
||||
timeoutScreensaver: 20
|
||||
#brightnessScreensaver: 10
|
||||
brightnessScreensaver:
|
||||
- time: "7:00:00"
|
||||
@@ -18,7 +18,7 @@ nspanel:
|
||||
# formatting options on https://babel.pocoo.org/en/latest/dates.html?highlight=name%20of%20day#date-fields
|
||||
timeFormat: "%H:%M"
|
||||
dateFormat: "%A, %d. %B %Y" # ignored if babel python package is installed
|
||||
weatherEntity: weather.example
|
||||
weather: weather.example
|
||||
pages:
|
||||
- type: cardEntities
|
||||
heading: Example Page 1
|
||||
@@ -55,5 +55,4 @@ nspanel:
|
||||
item: climate.example_climate
|
||||
- type: cardMedia
|
||||
heading: Exampe Media
|
||||
item: media_player.spotify_user
|
||||
|
||||
item: media_player.spotify_user
|
||||
157
apps/nspanel-lovelace-ui/luibackend/config.py
Normal file
157
apps/nspanel-lovelace-ui/luibackend/config.py
Normal file
@@ -0,0 +1,157 @@
|
||||
import logging
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
class PageNode(object):
|
||||
def __init__(self, data, parent=None):
|
||||
self.data = data
|
||||
self.name = None
|
||||
self.childs = []
|
||||
self.parent = parent
|
||||
self.pos = None
|
||||
|
||||
if "items" in data:
|
||||
childs = data.pop("items")
|
||||
for page in childs:
|
||||
self.add_child(PageNode(page, self))
|
||||
|
||||
name = self.data.get("heading", "unkown") if type(self.data) is dict else self.data
|
||||
ptype = self.data.get("type", "unkown") if type(self.data) is dict else "leaf"
|
||||
|
||||
self.name = f"{ptype}.{name}" if type(self.data) is dict else self.data
|
||||
self.name = self.name.replace(".","_")
|
||||
self.name = self.name.replace(",","_")
|
||||
self.name = self.name.replace(" ","_")
|
||||
|
||||
def add_child(self, obj):
|
||||
obj.pos = len(self.childs)
|
||||
self.childs.append(obj)
|
||||
|
||||
def next(self):
|
||||
if self.parent is not None:
|
||||
pos = self.pos
|
||||
length = len(self.parent.childs)
|
||||
return self.parent.childs[(pos+1)%length]
|
||||
else:
|
||||
return self
|
||||
def prev(self):
|
||||
if self.parent is not None:
|
||||
pos = self.pos
|
||||
length = len(self.parent.childs)
|
||||
return self.parent.childs[(pos-1)%length]
|
||||
else:
|
||||
return self
|
||||
|
||||
def search_page_by_name(self, name):
|
||||
name = name.replace("navigate.", "")
|
||||
pages = []
|
||||
for i in self.childs:
|
||||
# compare name of current page
|
||||
if i.name == name:
|
||||
pages.append(i)
|
||||
# current pages has also childs
|
||||
if len(i.childs) > 0:
|
||||
pages.extend(i.search_page_by_name(name))
|
||||
return pages
|
||||
|
||||
return items
|
||||
|
||||
def dump(self, indent=0):
|
||||
"""dump tree to string"""
|
||||
tab = ' '*(indent-1) + ' |- ' if indent > 0 else ''
|
||||
name = self.name
|
||||
parent = self.parent.name if self.parent is not None else "root"
|
||||
dumpstring = f"{tab}{self.pos}:{name} -> {parent} \n"
|
||||
for obj in self.childs:
|
||||
dumpstring += obj.dump(indent + 1)
|
||||
return dumpstring
|
||||
|
||||
def get_items(self):
|
||||
items = []
|
||||
for i in self.childs:
|
||||
if len(i.childs) > 0:
|
||||
items.append(f"navigate.{i.name}")
|
||||
else:
|
||||
items.append(i.data)
|
||||
return items
|
||||
|
||||
def get_all_item_names(self, recursive=True):
|
||||
items = []
|
||||
# current page
|
||||
if type(self.data) is dict:
|
||||
items.append(self.data.get("item", next(iter(self.data))))
|
||||
else:
|
||||
items.append(self.data)
|
||||
# childs of page
|
||||
for i in self.childs:
|
||||
if len(i.childs) > 0:
|
||||
if recursive:
|
||||
items.extend(i.get_all_item_names())
|
||||
else:
|
||||
if type(i.data) is dict:
|
||||
items.append(i.data.get("item", next(iter(i.data))))
|
||||
else:
|
||||
items.append(i.data)
|
||||
return items
|
||||
|
||||
class LuiBackendConfig(object):
|
||||
|
||||
_DEFAULT_CONFIG = {
|
||||
'panelRecvTopic': "tele/tasmota_your_mqtt_topic/RESULT",
|
||||
'panelSendTopic': "cmnd/tasmota_your_mqtt_topic/CustomSend",
|
||||
'updateMode': "auto-notify",
|
||||
'timeoutScreensaver': 20,
|
||||
'brightnessScreensaver': 20,
|
||||
'brightnessScreensaverTracking': None,
|
||||
'locale': "en_US",
|
||||
'timeFormat': "%H:%M",
|
||||
'dateFormatBabel': "full",
|
||||
'dateFormat': "%A, %d. %B %Y",
|
||||
'weather': 'weather.example',
|
||||
'weatherOverrideForecast1': None,
|
||||
'weatherOverrideForecast2': None,
|
||||
'pages': [{
|
||||
'type': 'cardEntities',
|
||||
'heading': 'Test Entities 1',
|
||||
'items': ['switch.test_item', 'switch.test_item', 'switch.test_item']
|
||||
}, {
|
||||
'type': 'cardGrid',
|
||||
'heading': 'Test Grid 1',
|
||||
'items': ['switch.test_item', 'switch.test_item', 'switch.test_item']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def __init__(self, args=None, check=True):
|
||||
self._config = {}
|
||||
self._page_config = None
|
||||
|
||||
if args:
|
||||
self.load(args)
|
||||
|
||||
if check:
|
||||
self.check()
|
||||
|
||||
def load(self, args):
|
||||
for k, v in args.items():
|
||||
if k in self._DEFAULT_CONFIG:
|
||||
self._config[k] = v
|
||||
LOGGER.info(f"Loaded config: {self._config}")
|
||||
|
||||
root_page = {"items": self.get("pages"), "type": "internal", "heading": "root"}
|
||||
self._page_config = PageNode(root_page)
|
||||
|
||||
LOGGER.info(f"Parsed Page config to the following Tree: \n {self._page_config.dump()}")
|
||||
|
||||
def check(self):
|
||||
return
|
||||
|
||||
def get(self, name):
|
||||
value = self._config.get(name)
|
||||
if value is None:
|
||||
value = self._DEFAULT_CONFIG.get(name)
|
||||
return value
|
||||
|
||||
def get_root_page(self):
|
||||
return self._page_config
|
||||
|
||||
209
apps/nspanel-lovelace-ui/luibackend/controller.py
Normal file
209
apps/nspanel-lovelace-ui/luibackend/controller.py
Normal file
@@ -0,0 +1,209 @@
|
||||
import logging
|
||||
import datetime
|
||||
from helper import scale, pos_to_color
|
||||
|
||||
from pages import LuiPagesGen
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
class LuiController(object):
|
||||
|
||||
def __init__(self, ha_api, config, send_mqtt_msg):
|
||||
self._ha_api = ha_api
|
||||
self._config = config
|
||||
self._send_mqtt_msg = send_mqtt_msg
|
||||
|
||||
# first child of root page (default, after startup)
|
||||
self._current_page = self._config._page_config.childs[0]
|
||||
|
||||
self._pages_gen = LuiPagesGen(ha_api, config, send_mqtt_msg)
|
||||
|
||||
# send panel back to startup page on restart of this script
|
||||
self._pages_gen.page_type("pageStartup")
|
||||
|
||||
# time update callback
|
||||
time = datetime.time(0, 0, 0)
|
||||
ha_api.run_minutely(self._pages_gen.update_time, time)
|
||||
|
||||
# weather callback
|
||||
weather_interval = 15 * 60 # 15 minutes
|
||||
ha_api.run_every(self.weather_update, "now", weather_interval)
|
||||
|
||||
# register callbacks
|
||||
self.register_callbacks()
|
||||
|
||||
# register callbacks for each time
|
||||
if type(self._config.get("brightnessScreensaver")) == list:
|
||||
for index, timeset in enumerate(self._config.get("brightnessScreensaver")):
|
||||
self._ha_api.run_daily(self.update_screensaver_brightness, timeset["time"], value=timeset["value"])
|
||||
|
||||
# calculate current brightness
|
||||
self.current_screensaver_brightness = self.calc_current_screensaver_brightness()
|
||||
|
||||
# call update_screensaver_brightness on changes of entity configured in brightnessScreensaverTracking
|
||||
bst = self._config.get("brightnessScreensaverTracking")
|
||||
if bst is not None and self._ha_api.entity_exists(bst):
|
||||
self._ha_api.listen_state(self.update_screensaver_brightness, entity_id=bst, value=self.current_screensaver_brightness)
|
||||
|
||||
def startup(self):
|
||||
LOGGER.info(f"Startup Event")
|
||||
# send time and date on startup
|
||||
self._pages_gen.update_time("")
|
||||
self._pages_gen.update_date("")
|
||||
|
||||
# set screensaver timeout
|
||||
timeout = self._config.get("timeoutScreensaver")
|
||||
self._send_mqtt_msg(f"timeout,{timeout}")
|
||||
|
||||
# set current screensaver brightness
|
||||
self.update_screensaver_brightness(kwargs={"value": self.current_screensaver_brightness})
|
||||
|
||||
# send panel to screensaver
|
||||
self._pages_gen.page_type("screensaver")
|
||||
self.weather_update("")
|
||||
|
||||
def update_screensaver_brightness(self, kwargs):
|
||||
bst = self._config.get("brightnessScreensaverTracking")
|
||||
if bst is not None and self._ha_api.entity_exists(bst) and self._ha_api.get_entity(bst).state == "not_home":
|
||||
self.current_screensaver_brightness = 0
|
||||
else:
|
||||
self.current_screensaver_brightness = kwargs['value']
|
||||
self._send_mqtt_msg(f"dimmode,{self.current_screensaver_brightness}")
|
||||
|
||||
def calc_current_screensaver_brightness(self):
|
||||
current_screensaver_brightness = 20
|
||||
# set brightness of screensaver
|
||||
if type(self._config.get("brightnessScreensaver")) == int:
|
||||
current_screensaver_brightness = self._config.get("brightnessScreensaver")
|
||||
elif type(self._config.get("brightnessScreensaver")) == list:
|
||||
sorted_timesets = sorted(self._config.get("brightnessScreensaver"), key=lambda d: self._ha_api.parse_time(d['time']))
|
||||
# calc current screensaver brightness
|
||||
found_current_dim_value = False
|
||||
for index, timeset in enumerate(sorted_timesets):
|
||||
LOGGER.info("Current time %s", self._ha_api.get_now().time())
|
||||
if self._ha_api.parse_time(timeset["time"]) > self._ha_api.get_now().time() and not found_current_dim_value:
|
||||
# first time after current time, set dim value
|
||||
current_screensaver_brightness = sorted_timesets[index-1]["value"]
|
||||
LOGGER.info("Setting dim value to %s", sorted_timesets[index-1])
|
||||
found_current_dim_value = True
|
||||
# still no dim value
|
||||
if not found_current_dim_value:
|
||||
self.current_screensaver_brightness = sorted_timesets[-1]["value"]
|
||||
return current_screensaver_brightness
|
||||
|
||||
def weather_update(self, kwargs):
|
||||
we_name = self._config.get("weather")
|
||||
unit = "°C"
|
||||
self._pages_gen.update_screensaver_weather(kwargs={"weather": we_name, "unit": unit})
|
||||
|
||||
def register_callbacks(self):
|
||||
items = self._config.get_root_page().get_all_item_names()
|
||||
LOGGER.debug(f"Registering callbacks for the following items: {items}")
|
||||
for item in items:
|
||||
if self._ha_api.entity_exists(item):
|
||||
self._ha_api.listen_state(self.state_change_callback, entity_id=item, attribute="all")
|
||||
|
||||
def state_change_callback(self, entity, attribute, old, new, kwargs):
|
||||
LOGGER.debug(f"Got callback for: {entity}")
|
||||
LOGGER.debug(f"Current page has the following items: {self._current_page.get_items()}")
|
||||
if entity in self._current_page.get_all_item_names(recursive=False):
|
||||
LOGGER.debug(f"Callback Entity is on current page: {entity}")
|
||||
self._pages_gen.render_page(self._current_page, send_page_type=False)
|
||||
# send detail page update, just in case
|
||||
if self._current_page.data.get("type", "unknown") in ["cardGrid", "cardEntities"]:
|
||||
if entity.startswith("light"):
|
||||
self._pages_gen.generate_light_detail_page(entity)
|
||||
if entity.startswith("cover"):
|
||||
self._pages_gen.generate_shutter_detail_page(entity)
|
||||
|
||||
|
||||
def detail_open(self, detail_type, entity_id):
|
||||
if detail_type == "popupShutter":
|
||||
self._pages_gen.generate_shutter_detail_page(entity_id)
|
||||
if detail_type == "popupLight":
|
||||
self._pages_gen.generate_light_detail_page(entity_id)
|
||||
|
||||
def button_press(self, entity_id, button_type, value):
|
||||
LOGGER.info(f"Button Press Event; entity_id: {entity_id}; button_type: {button_type}; value: {value} ")
|
||||
# internal buttons
|
||||
if entity_id == "screensaver" and button_type == "enter":
|
||||
self._pages_gen.render_page(self._current_page)
|
||||
if button_type == "bExit":
|
||||
self._pages_gen.render_page(self._current_page)
|
||||
|
||||
if button_type == "bNext":
|
||||
self._current_page = self._current_page.next()
|
||||
self._pages_gen.render_page(self._current_page)
|
||||
if button_type == "bPrev":
|
||||
self._current_page = self._current_page.prev()
|
||||
self._pages_gen.render_page(self._current_page)
|
||||
|
||||
elif entity_id == "updateDisplayNoYes" and value == "no":
|
||||
self._pages_gen.render_page(self._current_page)
|
||||
|
||||
# buttons with actions on HA
|
||||
if button_type == "OnOff":
|
||||
if value == "1":
|
||||
self._ha_api.turn_on(entity_id)
|
||||
else:
|
||||
self._ha_api.turn_off(entity_id)
|
||||
|
||||
# for shutter / covers
|
||||
if button_type == "up":
|
||||
self._ha_api.get_entity(entity_id).call_service("open_cover")
|
||||
if button_type == "stop":
|
||||
self._ha_api.get_entity(entity_id).call_service("stop_cover")
|
||||
if button_type == "down":
|
||||
self._ha_api.get_entity(entity_id).call_service("close_cover")
|
||||
if button_type == "positionSlider":
|
||||
pos = int(value)
|
||||
self._ha_api.get_entity(entity_id).call_service("set_cover_position", position=pos)
|
||||
|
||||
if button_type == "button":
|
||||
if entity_id.startswith('navigate'):
|
||||
# internal for navigation to nested pages
|
||||
self._current_page = self._config.get_root_page().search_page_by_name(entity_id)[0]
|
||||
self._pages_gen.render_page(self._current_page)
|
||||
elif entity_id.startswith('scene'):
|
||||
self._ha_api.get_entity(entity_id).call_service("turn_on")
|
||||
elif entity_id.startswith('light') or entity_id.startswith('switch') or entity_id.startswith('input_boolean'):
|
||||
self._ha_api.get_entity(entity_id).call_service("toggle")
|
||||
else:
|
||||
self._ha_api.get_entity(entity_id).call_service("press")
|
||||
|
||||
# for media page
|
||||
if button_type == "media-next":
|
||||
self._ha_api.get_entity(entity_id).call_service("media_next_track")
|
||||
if button_type == "media-back":
|
||||
self._ha_api.get_entity(entity_id).call_service("media_previous_track")
|
||||
if button_type == "media-pause":
|
||||
self._ha_api.get_entity(entity_id).call_service("media_play_pause")
|
||||
if button_type == "hvac_action":
|
||||
self._ha_api.get_entity(entity_id).call_service("set_hvac_mode", hvac_mode=value)
|
||||
if button_type == "volumeSlider":
|
||||
pos = int(value)
|
||||
# HA wants this value between 0 and 1 as float
|
||||
pos = pos/100
|
||||
self._ha_api.get_entity(entity_id).call_service("volume_set", volume_level=pos)
|
||||
|
||||
# for light detail page
|
||||
if button_type == "brightnessSlider":
|
||||
# scale 0-100 to ha brightness range
|
||||
brightness = int(scale(int(value),(0,100),(0,255)))
|
||||
self._ha_api.get_entity(entity_id).call_service("turn_on", brightness=brightness)
|
||||
if button_type == "colorTempSlider":
|
||||
entity = self._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))
|
||||
self._ha_api.get_entity(entity_id).call_service("turn_on", color_temp=color_val)
|
||||
if button_type == "colorWheel":
|
||||
self._ha_api.log(value)
|
||||
value = value.split('|')
|
||||
color = pos_to_color(int(value[0]), int(value[1]))
|
||||
self._ha_api.log(color)
|
||||
self._ha_api.get_entity(entity_id).call_service("turn_on", rgb_color=color)
|
||||
|
||||
# for climate page
|
||||
if button_type == "tempUpd":
|
||||
temp = int(value)/10
|
||||
self._ha_api.get_entity(entity_id).call_service("set_temperature", temperature=temp)
|
||||
@@ -10,6 +10,7 @@ def scale(val, src, dst):
|
||||
def hsv2rgb(h, s, v):
|
||||
hsv = colorsys.hsv_to_rgb(h,s,v)
|
||||
return tuple(round(i * 255) for i in hsv)
|
||||
|
||||
def pos_to_color(x, y):
|
||||
r = 160/2
|
||||
x = round((x - r) / r * 100) / 100
|
||||
@@ -26,10 +27,13 @@ def pos_to_color(x, y):
|
||||
return rgb
|
||||
|
||||
def rgb_brightness(rgb_color, brightness):
|
||||
# brightness values are in range 0-255
|
||||
# to make sure that the color is not completly lost we need to rescale this to 70-255
|
||||
brightness = int(scale(brightness,(0,255),(70,255)))
|
||||
red = rgb_color[0]/255*brightness
|
||||
green = rgb_color[1]/255*brightness
|
||||
blue = rgb_color[2]/255*brightness
|
||||
return [red, green, blue]
|
||||
return [int(red), int(green), int(blue)]
|
||||
|
||||
def rgb_dec565(rgb_color):
|
||||
red = rgb_color[0]
|
||||
@@ -37,4 +41,4 @@ def rgb_dec565(rgb_color):
|
||||
blue = rgb_color[2]
|
||||
# take in the red, green and blue values (0-255) as 8 bit values and then combine
|
||||
# and shift them to make them a 16 bit dec value in 565 format.
|
||||
return ((int(red / 255 * 31) << 11) | (int(green / 255 * 63) << 5) | (int(blue / 255 * 31)))
|
||||
return ((int(red / 255 * 31) << 11) | (int(green / 255 * 63) << 5) | (int(blue / 255 * 31)))
|
||||
205
apps/nspanel-lovelace-ui/luibackend/localization.py
Normal file
205
apps/nspanel-lovelace-ui/luibackend/localization.py
Normal file
@@ -0,0 +1,205 @@
|
||||
translations = {
|
||||
'af_ZA': {
|
||||
'ACTIVATE': "Aktiveer",
|
||||
'aux_heat': "Aanvullende hitte",
|
||||
'cooling': "Koel Af",
|
||||
'drying': "Droog Uit",
|
||||
'fan': "Waaier",
|
||||
'idle': "Onaktief",
|
||||
'off': "Af",
|
||||
},
|
||||
'ca_ES': {
|
||||
'PRESS': "Prem",
|
||||
'ACTIVATE': "Activar",
|
||||
'aux_heat': "Calefactor auxiliar",
|
||||
'cooling': "Refredant",
|
||||
'drying': "Assecant",
|
||||
'fan': "Ventilació",
|
||||
'idle': "Inactiu",
|
||||
'off': "OFF",
|
||||
},
|
||||
'cs_CZ': {
|
||||
'PRESS': "Stisknutí",
|
||||
'ACTIVATE': "Aktivovat",
|
||||
'aux_heat': "Pomocné teplo",
|
||||
'cooling': "Chlazení",
|
||||
'drying': "Sušení",
|
||||
'fan': "Ventilátor",
|
||||
'idle': "Nečinný",
|
||||
'off': "Vypnuto",
|
||||
},
|
||||
'cy_GB': {
|
||||
'ACTIVATE': "Actifadu",
|
||||
'off': "I ffwrdd",
|
||||
},
|
||||
'da_DK': {
|
||||
'PRESS': "Tryk",
|
||||
'ACTIVATE': "Aktiver",
|
||||
'aux_heat': "Støtte-varme",
|
||||
'cooling': "Køling",
|
||||
'drying': "Tørring",
|
||||
'fan': "Blæser",
|
||||
'idle': "Inaktiv",
|
||||
'off': "Fra",
|
||||
},
|
||||
'de_DE': {
|
||||
'PRESS': "Drücken",
|
||||
'ACTIVATE': "Aktivieren",
|
||||
'aux_heat': "Hilfswärme",
|
||||
'cooling': "Kühlung",
|
||||
'drying': "Trocknen",
|
||||
'fan': "Ventilator",
|
||||
'idle': "Leerlauf",
|
||||
'off': "Aus",
|
||||
},
|
||||
'en_GB': {
|
||||
},
|
||||
'en_US': {
|
||||
'PRESS': "Press",
|
||||
'ACTIVATE': "Activate",
|
||||
'aux_heat': "Aux heat",
|
||||
'cooling': "Cooling",
|
||||
'drying': "Drying",
|
||||
'fan': "Fan",
|
||||
'idle': "Idle",
|
||||
'off': "Off",
|
||||
},
|
||||
'es_ES': {
|
||||
'PRESS': "Pulsa",
|
||||
'ACTIVATE': "Activar",
|
||||
'aux_heat': "Calor auxiliar",
|
||||
'cooling': "Enfriando",
|
||||
'drying': "Secando",
|
||||
'fan': "Ventilador",
|
||||
'idle': "Inactivo",
|
||||
'off': "Apagado",
|
||||
},
|
||||
'et_EE': {
|
||||
'PRESS': "Vajuta nuppu",
|
||||
'ACTIVATE': "Aktiveeri",
|
||||
'aux_heat': "Abiküte",
|
||||
'cooling': "Jahutamine",
|
||||
'drying': "Kuivatamine",
|
||||
'fan': "Ventilaator",
|
||||
'idle': "Ootel",
|
||||
'off': "Väljas",
|
||||
},
|
||||
'eu_ES': {
|
||||
'ACTIVATE': "Aktibatu",
|
||||
'off': "Itzalita",
|
||||
},
|
||||
'fi_FI': {
|
||||
'PRESS': "Paina",
|
||||
'ACTIVATE': "Aktivoi",
|
||||
'aux_heat': "Lisälämpö",
|
||||
'cooling': "Jäähdytys",
|
||||
'drying': "Kuivaus",
|
||||
'fan': "Tuuletin",
|
||||
'idle': "Lepotilassa",
|
||||
'off': "Pois",
|
||||
},
|
||||
'fr_FR': {
|
||||
'PRESS': "Appui",
|
||||
'ACTIVATE': "Activer",
|
||||
'aux_heat': "Chauffage d'appoint",
|
||||
'cooling': "Refroidissement",
|
||||
'drying': "Séchage",
|
||||
'fan': "Ventilateur",
|
||||
'idle': "Inactif",
|
||||
'off': "Off",
|
||||
},
|
||||
'fy_DE': {
|
||||
'off': "Út",
|
||||
},
|
||||
'gl_ES': {
|
||||
'off': "Apagado",
|
||||
},
|
||||
'hr_HR': {
|
||||
'ACTIVATE': "Aktivirati",
|
||||
'aux_heat': "Pomoćno grijanje",
|
||||
'cooling': "Hlađenje",
|
||||
'drying': "Sušenje",
|
||||
'fan': "Ventilator",
|
||||
'idle': "Neaktivan",
|
||||
'off': "Isključen",
|
||||
},
|
||||
'id_ID': {
|
||||
'PRESS': "Tekan",
|
||||
'ACTIVATE': "Aktifkan",
|
||||
'aux_heat': "Pemanasan tambahan",
|
||||
'cooling': "Mendinginkan",
|
||||
'drying': "Mengeringkan",
|
||||
'fan': "Kipas",
|
||||
'idle': "Diam",
|
||||
'off': "Mati",
|
||||
},
|
||||
'is_IS': {
|
||||
'PRESS': "Ýttu á",
|
||||
'ACTIVATE': "Virkja",
|
||||
'cooling': "Kæling",
|
||||
'drying': "Þurrkun",
|
||||
'fan': "Vifta",
|
||||
'idle': "Aðgerðalaus",
|
||||
'off': "Slökkt",
|
||||
},
|
||||
'it_IT': {
|
||||
'PRESS': "Premi",
|
||||
'ACTIVATE': "Attiva",
|
||||
'aux_heat': "Riscaldamento ausiliario",
|
||||
'cooling': "Raffreddamento",
|
||||
'drying': "Deumidificazione",
|
||||
'fan': "Ventilatore",
|
||||
'idle': "Inattivo",
|
||||
'off': "Spento",
|
||||
},
|
||||
'nl_NL': {
|
||||
'PRESS': "Klik",
|
||||
'ACTIVATE': "Activeren",
|
||||
'aux_heat': "Extra warmte",
|
||||
'cooling': "Koelen",
|
||||
'drying': "Ontvochtigen",
|
||||
'fan': "Ventilator",
|
||||
'idle': "Inactief",
|
||||
'off': "Uit",
|
||||
},
|
||||
'nn_NO': {
|
||||
'ACTIVATE': "Aktiver",
|
||||
'aux_heat': "Aux-varme",
|
||||
'cooling': "Nedkjøling",
|
||||
'drying': "Tørkar",
|
||||
'fan': "Vifte",
|
||||
'idle': "Tomgang",
|
||||
'off': "Av",
|
||||
},
|
||||
'pt_PT': {
|
||||
'PRESS': "Pressione",
|
||||
'ACTIVATE': "Ativar",
|
||||
'aux_heat': "Calor auxiliar",
|
||||
'cooling': "Resfriar",
|
||||
'drying': "Secagem",
|
||||
'fan': "Ventoinha",
|
||||
'idle': "Em espera",
|
||||
'off': "Desligado",
|
||||
},
|
||||
'sr_RS': {
|
||||
'PRESS': "Pritisnite taster",
|
||||
'ACTIVATE': "Aktiviraj",
|
||||
'off': "Isključen",
|
||||
},
|
||||
'sv_SE': {
|
||||
'PRESS': "Tryck",
|
||||
'ACTIVATE': "Aktivera",
|
||||
'aux_heat': "Underhållsvärme",
|
||||
'cooling': "Kyler",
|
||||
'drying': "Avfuktar",
|
||||
'fan': "Fläkt",
|
||||
'idle': "Inaktiv",
|
||||
'off': "Av",
|
||||
},
|
||||
}
|
||||
|
||||
def get_translation(locale, input):
|
||||
if locale in translations:
|
||||
return translations.get(locale).get(input, input)
|
||||
else:
|
||||
return translations.get("en_US").get(input, input)
|
||||
54
apps/nspanel-lovelace-ui/luibackend/mqttListener.py
Normal file
54
apps/nspanel-lovelace-ui/luibackend/mqttListener.py
Normal file
@@ -0,0 +1,54 @@
|
||||
import json
|
||||
|
||||
import logging
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
class LuiMqttListener(object):
|
||||
|
||||
def __init__(self, mqtt_api, topic, controller, updater):
|
||||
self._controller = controller
|
||||
self._updater = updater
|
||||
# Setup, mqtt subscription and callback
|
||||
mqtt_api.mqtt_subscribe(topic=topic)
|
||||
mqtt_api.listen_event(self.mqtt_event_callback, "MQTT_MESSAGE", topic=topic, namespace='mqtt')
|
||||
|
||||
|
||||
def mqtt_event_callback(self, event_name, data, kwargs):
|
||||
LOGGER.debug(f'MQTT callback for: {data}')
|
||||
# Parse Json Message from Tasmota and strip out message from nextion display
|
||||
data = json.loads(data["payload"])
|
||||
if("nlui_driver_version" in data):
|
||||
msg = data["nlui_driver_version"]
|
||||
self._updater.set_tasmota_driver_version(int(msg))
|
||||
self._updater.check_updates()
|
||||
if("CustomRecv" not in data):
|
||||
return
|
||||
msg = data["CustomRecv"]
|
||||
LOGGER.info(f"Received Message from Screen: {msg}")
|
||||
# Split message into parts seperated by ","
|
||||
msg = msg.split(",")
|
||||
# run action based on received command
|
||||
if msg[0] == "event":
|
||||
if msg[1] == "startup":
|
||||
display_firmware_version = int(msg[2])
|
||||
self._updater.set_current_display_firmware_version(display_firmware_version)
|
||||
# check for updates
|
||||
msg_send = self._updater.check_updates()
|
||||
# send messages for current page
|
||||
if not msg_send:
|
||||
self._controller.startup()
|
||||
if msg[1] == "screensaverOpen":
|
||||
self._controller.weather_update("")
|
||||
if msg[1] == "buttonPress2":
|
||||
entity_id = msg[2]
|
||||
btype = msg[3]
|
||||
value = msg[4] if len(msg) > 4 else None
|
||||
|
||||
if entity_id == "updateDisplayNoYes" and value == "yes":
|
||||
self._updater.update_panel_driver()
|
||||
|
||||
self._controller.button_press(entity_id, btype, value)
|
||||
if msg[1] == "pageOpenDetail":
|
||||
self._controller.detail_open(msg[2], msg[3])
|
||||
|
||||
320
apps/nspanel-lovelace-ui/luibackend/pages.py
Normal file
320
apps/nspanel-lovelace-ui/luibackend/pages.py
Normal file
@@ -0,0 +1,320 @@
|
||||
import logging
|
||||
import datetime
|
||||
|
||||
from icon_mapping import get_icon_id
|
||||
from icons import get_icon_id_ha
|
||||
from helper import scale, rgb_dec565, rgb_brightness
|
||||
from localization import get_translation
|
||||
|
||||
# check Babel
|
||||
import importlib
|
||||
babel_spec = importlib.util.find_spec("babel")
|
||||
if babel_spec is not None:
|
||||
import babel.dates
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
class LuiPagesGen(object):
|
||||
|
||||
def __init__(self, ha_api, config, send_mqtt_msg):
|
||||
self._ha_api = ha_api
|
||||
self._config = config
|
||||
self._locale = config.get("locale")
|
||||
self._send_mqtt_msg = send_mqtt_msg
|
||||
|
||||
def get_entity_color(self, entity):
|
||||
attr = entity.attributes
|
||||
default_color_on = rgb_dec565([253, 216, 53])
|
||||
default_color_off = rgb_dec565([68, 115, 158])
|
||||
icon_color = default_color_on if entity.state == "on" else default_color_off
|
||||
|
||||
if "rgb_color" in attr:
|
||||
color = attr.rgb_color
|
||||
if "brightness" in attr:
|
||||
color = rgb_brightness(color, attr.brightness)
|
||||
icon_color = rgb_dec565(color)
|
||||
elif "brightness" in attr:
|
||||
color = rgb_brightness([253, 216, 53], attr.brightness)
|
||||
icon_color = rgb_dec565(color)
|
||||
return icon_color
|
||||
|
||||
def update_time(self, kwargs):
|
||||
time = datetime.datetime.now().strftime(self._config.get("timeFormat"))
|
||||
self._send_mqtt_msg(f"time,{time}")
|
||||
|
||||
def update_date(self, kwargs):
|
||||
global babel_spec
|
||||
if babel_spec is not None:
|
||||
dateformat = self._config.get("dateFormatBabel")
|
||||
date = babel.dates.format_date(datetime.datetime.now(), dateformat, locale=self._locale)
|
||||
else:
|
||||
dateformat = self._config.get("dateFormat")
|
||||
date = datetime.datetime.now().strftime(dateformat)
|
||||
self._send_mqtt_msg(f"date,?{date}")
|
||||
|
||||
def page_type(self, target_page):
|
||||
self._send_mqtt_msg(f"pageType,{target_page}")
|
||||
|
||||
def update_screensaver_weather(self, kwargs):
|
||||
global babel_spec
|
||||
we_name = kwargs['weather']
|
||||
unit = kwargs['unit']
|
||||
|
||||
if self._ha_api.entity_exists(we_name):
|
||||
we = self._ha_api.get_entity(we_name)
|
||||
else:
|
||||
LOGGER.error("Skipping Weather Update, entitiy not found")
|
||||
return
|
||||
|
||||
icon_cur = get_icon_id_ha("weather", state=we.state)
|
||||
text_cur = f"{we.attributes.temperature}{unit}"
|
||||
icon_cur_detail = get_icon_id("water-percent")
|
||||
text_cur_detail = f"{we.attributes.humidity} %"
|
||||
|
||||
wOF1 = self._config.get("weatherOverrideForecast1")
|
||||
if wOF1 is None:
|
||||
up1 = we.attributes.forecast[0]['datetime']
|
||||
up1 = datetime.datetime.fromisoformat(up1)
|
||||
if babel_spec is not None:
|
||||
up1 = babel.dates.format_date(up1, "E", locale=self._locale)
|
||||
else:
|
||||
up1 = up1.strftime("%a")
|
||||
icon1 = get_icon_id_ha("weather", state=we.attributes.forecast[0]['condition'])
|
||||
down1 = f"{we.attributes.forecast[0]['temperature']} {unit}"
|
||||
else:
|
||||
LOGGER.info(f"Forecast 1 is overrriden with {wOF1}")
|
||||
icon = None
|
||||
name = None
|
||||
if type(wOF1) is dict:
|
||||
icon = next(iter(wOF1.items()))[1].get('icon')
|
||||
name = next(iter(wOF1.items()))[1].get('name')
|
||||
wOF1 = next(iter(wOF1.items()))[0]
|
||||
entity = self._ha_api.get_entity(wOF1)
|
||||
up1 = name if name is not None else entity.attributes.friendly_name
|
||||
icon1 = get_icon_id_ha("sensor", state=entity.state, device_class=entity.attributes.get("device_class", ""), overwrite=icon)
|
||||
unit_of_measurement = entity.attributes.get("unit_of_measurement", "")
|
||||
down1 = f"{entity.state} {unit_of_measurement}"
|
||||
|
||||
|
||||
wOF2 = self._config.get("weatherOverrideForecast2")
|
||||
if wOF2 is None:
|
||||
up2 = we.attributes.forecast[1]['datetime']
|
||||
up2 = datetime.datetime.fromisoformat(up2)
|
||||
if babel_spec is not None:
|
||||
up2 = babel.dates.format_date(up2, "E", locale=self._locale)
|
||||
else:
|
||||
up2 = up2.strftime("%a")
|
||||
icon2 = get_icon_id_ha("weather", state=we.attributes.forecast[1]['condition'])
|
||||
down2 = f"{we.attributes.forecast[1]['temperature']} {unit}"
|
||||
|
||||
else:
|
||||
LOGGER.info(f"Forecast 2 is overrriden with {wOF2}")
|
||||
icon = None
|
||||
name = None
|
||||
if type(wOF2) is dict:
|
||||
icon = next(iter(wOF2.items()))[1].get('icon')
|
||||
name = next(iter(wOF2.items()))[1].get('name')
|
||||
wOF2 = next(iter(wOF2.items()))[0]
|
||||
entity = self._ha_api.get_entity(wOF2)
|
||||
up2 = name if name is not None else entity.attributes.friendly_name
|
||||
icon2 = get_icon_id_ha("sensor", state=entity.state, device_class=entity.attributes.get("device_class", ""), overwrite=icon)
|
||||
unit_of_measurement = entity.attributes.get("unit_of_measurement", "")
|
||||
down2 = f"{entity.state} {unit_of_measurement}"
|
||||
|
||||
|
||||
self._send_mqtt_msg(f"weatherUpdate,?{icon_cur}?{text_cur}?{icon_cur_detail}?{text_cur_detail}?{up1}?{icon1}?{down1}?{up2}?{icon2}?{down2}")
|
||||
|
||||
def generate_entities_item(self, item):
|
||||
icon = None
|
||||
name = None
|
||||
if type(item) is dict:
|
||||
icon = next(iter(item.items()))[1].get('icon')
|
||||
name = next(iter(item.items()))[1].get('name')
|
||||
item = next(iter(item.items()))[0]
|
||||
# type of the item is the string before the "." in the item name
|
||||
item_type = item.split(".")[0]
|
||||
LOGGER.debug(f"Generating item command for {item} with type {item_type}",)
|
||||
# Internal Entities
|
||||
if item_type == "delete":
|
||||
return f",{item_type},,,,,"
|
||||
if item_type == "navigate":
|
||||
page_search = self._config.get_root_page().search_page_by_name(item)
|
||||
if len(page_search) > 0:
|
||||
page_data = page_search[0].data
|
||||
name = page_data.get("heading")
|
||||
text = get_translation(self._locale,"PRESS")
|
||||
icon_id = get_icon_id(page_data.get("icon", "gesture-tap-button"))
|
||||
return f",button,{item},{icon_id},17299,{name},{text}"
|
||||
else:
|
||||
return f",text,{item},{get_icon_id('alert-circle-outline')},17299,page not found,"
|
||||
if not self._ha_api.entity_exists(item):
|
||||
return f",text,{item},{get_icon_id('alert-circle-outline')},17299,Not found check, apps.yaml"
|
||||
|
||||
# HA Entities
|
||||
entity = self._ha_api.get_entity(item)
|
||||
name = name if name is not None else entity.attributes.friendly_name
|
||||
if item_type == "cover":
|
||||
icon_id = get_icon_id_ha("cover", state=entity.state, overwrite=icon)
|
||||
return f",shutter,{item},{icon_id},17299,{name},"
|
||||
if item_type in "light":
|
||||
switch_val = 1 if entity.state == "on" else 0
|
||||
icon_color = self.get_entity_color(entity)
|
||||
icon_id = get_icon_id_ha("light", overwrite=icon)
|
||||
return f",{item_type},{item},{icon_id},{icon_color},{name},{switch_val}"
|
||||
if item_type in ["switch", "input_boolean"]:
|
||||
switch_val = 1 if entity.state == "on" else 0
|
||||
icon_color = self.get_entity_color(entity)
|
||||
icon_id = get_icon_id_ha(item_type, state=entity.state, overwrite=icon)
|
||||
return f",switch,{item},{icon_id},{icon_color},{name},{switch_val}"
|
||||
if item_type in ["sensor", "binary_sensor"]:
|
||||
device_class = entity.attributes.get("device_class", "")
|
||||
icon_id = get_icon_id_ha("sensor", state=entity.state, device_class=device_class, overwrite=icon)
|
||||
unit_of_measurement = entity.attributes.get("unit_of_measurement", "")
|
||||
value = entity.state + " " + unit_of_measurement
|
||||
icon_color = self.get_entity_color(entity)
|
||||
return f",text,{item},{icon_id},{icon_color},{name},{value}"
|
||||
if item_type in ["button", "input_button"]:
|
||||
icon_id = get_icon_id_ha("button", overwrite=icon)
|
||||
text = get_translation(self._locale,"PRESS")
|
||||
return f",button,{item},{icon_id},17299,{name},{text}"
|
||||
if item_type == "scene":
|
||||
icon_id = get_icon_id_ha("scene", overwrite=icon)
|
||||
text = get_translation(self._locale,"ACTIVATE")
|
||||
return f",button,{item},{icon_id},17299,{name},{text}"
|
||||
|
||||
|
||||
def generate_entities_page(self, heading, items):
|
||||
# Set Heading of Page
|
||||
self._send_mqtt_msg(f"entityUpdHeading,{heading}")
|
||||
# Get items and construct cmd string
|
||||
command = "entityUpd"
|
||||
for item in items:
|
||||
command += self.generate_entities_item(item)
|
||||
self._send_mqtt_msg(command)
|
||||
|
||||
|
||||
def generate_thermo_page(self, item):
|
||||
if not self._ha_api.entity_exists(item):
|
||||
command = f"entityUpd,{item},Not found,220,220,Not found,150,300,5"
|
||||
else:
|
||||
entity = self._ha_api.get_entity(item)
|
||||
heading = entity.attributes.friendly_name
|
||||
current_temp = int(entity.attributes.get("current_temperature", 0)*10)
|
||||
dest_temp = int(entity.attributes.get("temperature", 0)*10)
|
||||
status = entity.attributes.get("hvac_action", "")
|
||||
status = get_translation(self._locale,status)
|
||||
min_temp = int(entity.attributes.get("min_temp", 0)*10)
|
||||
max_temp = int(entity.attributes.get("max_temp", 0)*10)
|
||||
step_temp = int(entity.attributes.get("target_temp_step", 0.5)*10)
|
||||
icon_res = ""
|
||||
hvac_modes = entity.attributes.get("hvac_modes", [])
|
||||
for mode in hvac_modes:
|
||||
icon_id = get_icon_id('alert-circle-outline')
|
||||
color_on = 64512
|
||||
if mode == "auto":
|
||||
icon_id = get_icon_id("calendar-sync")
|
||||
color_on = 1024
|
||||
if mode == "heat":
|
||||
icon_id = get_icon_id("fire")
|
||||
color_on = 64512
|
||||
if mode == "off":
|
||||
icon_id = get_icon_id("power")
|
||||
color_on = 35921
|
||||
if mode == "cool":
|
||||
icon_id = get_icon_id("snowflake")
|
||||
color_on = 11487
|
||||
if mode == "dry":
|
||||
icon_id = get_icon_id("water-percent")
|
||||
color_on = 60897
|
||||
if mode == "fan_only":
|
||||
icon_id = get_icon_id("fan")
|
||||
color_on = 35921
|
||||
state = 0
|
||||
if(mode == entity.state):
|
||||
state = 1
|
||||
icon_res += f",{icon_id},{color_on},{state},{mode}"
|
||||
|
||||
len_hvac_modes = len(hvac_modes)
|
||||
if len_hvac_modes%2 == 0:
|
||||
# even
|
||||
padding_len = int((4-len_hvac_modes)/2)
|
||||
icon_res = ","*4*padding_len + icon_res + ","*4*padding_len
|
||||
# use last 4 icons
|
||||
icon_res = ","*4*5 + icon_res
|
||||
else:
|
||||
# uneven
|
||||
padding_len = int((5-len_hvac_modes)/2)
|
||||
icon_res = ","*4*padding_len + icon_res + ","*4*padding_len
|
||||
# use first 5 icons
|
||||
icon_res = icon_res + ","*4*4
|
||||
command = f"entityUpd,{item},{heading},{current_temp},{dest_temp},{status},{min_temp},{max_temp},{step_temp}{icon_res}"
|
||||
self._send_mqtt_msg(command)
|
||||
|
||||
def generate_media_page(self, item):
|
||||
if not self._ha_api.entity_exists(item):
|
||||
command = f"entityUpd,|{item}|Not found|{get_icon_id('alert-circle-outline')}|Please check your|apps.yaml in AppDaemon|50|{get_icon_id('alert-circle-outline')}"
|
||||
else:
|
||||
entity = self._ha_api.get_entity(item)
|
||||
heading = entity.attributes.friendly_name
|
||||
icon = 0
|
||||
title = entity.attributes.get("media_title", "")
|
||||
author = entity.attributes.get("media_artist", "")
|
||||
volume = int(entity.attributes.get("volume_level", 0)*100)
|
||||
iconplaypause = get_icon_id("pause") if entity.state == "playing" else get_icon_id("play")
|
||||
if "media_content_type" in entity.attributes:
|
||||
if entity.attributes.media_content_type == "music":
|
||||
icon = get_icon_id("music")
|
||||
command = f"entityUpd,|{item}|{heading}|{icon}|{title}|{author}|{volume}|{iconplaypause}"
|
||||
self._send_mqtt_msg(command)
|
||||
|
||||
def render_page(self, page, send_page_type=True):
|
||||
config = page.data
|
||||
page_type = config["type"]
|
||||
LOGGER.info(f"Started rendering of page {page.pos} with type {page_type}")
|
||||
# Switch to page
|
||||
if send_page_type:
|
||||
self.page_type(page_type)
|
||||
if page_type in ["cardEntities", "cardGrid"]:
|
||||
heading = config.get("heading", "unknown")
|
||||
self.generate_entities_page(heading, page.get_items())
|
||||
return
|
||||
if page_type == "cardThermo":
|
||||
self.generate_thermo_page(page.data.get("item"))
|
||||
if page_type == "cardMedia":
|
||||
self.generate_media_page(page.data.get("item"))
|
||||
|
||||
def generate_light_detail_page(self, entity):
|
||||
entity = self._ha_api.get_entity(entity)
|
||||
switch_val = 1 if entity.state == "on" else 0
|
||||
icon_color = self.get_entity_color(entity)
|
||||
brightness = "disable"
|
||||
color_temp = "disable"
|
||||
color = "disable"
|
||||
if entity.state == "on":
|
||||
if "brightness" in entity.attributes:
|
||||
# scale 0-255 brightness from ha to 0-100
|
||||
brightness = int(scale(entity.attributes.brightness,(0,255),(0,100)))
|
||||
else:
|
||||
brightness = "disable"
|
||||
if "color_temp" in entity.attributes.supported_color_modes:
|
||||
if "color_temp" in entity.attributes:
|
||||
# scale ha color temp range to 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 entity.attributes.supported_color_modes):
|
||||
color = "enable"
|
||||
else:
|
||||
color = "disable"
|
||||
self._send_mqtt_msg(f"entityUpdateDetail,{get_icon_id('lightbulb')},{icon_color},{switch_val},{brightness},{color_temp},{color}")
|
||||
|
||||
def generate_shutter_detail_page(self, entity):
|
||||
pos = 100-int(entity.attributes.get("current_position", 50))
|
||||
self._send_mqtt_msg(f"entityUpdateDetail,{pos}")
|
||||
|
||||
def send_message_page(self, id, heading, msg, b1, b2):
|
||||
self._send_mqtt_msg(f"pageType,popupNotify")
|
||||
self._send_mqtt_msg(f"entityUpdateDetail,|{id}|{heading}|65535|{b1}|65535|{b2}|65535|{msg}|65535|0")
|
||||
77
apps/nspanel-lovelace-ui/luibackend/updater.py
Normal file
77
apps/nspanel-lovelace-ui/luibackend/updater.py
Normal file
@@ -0,0 +1,77 @@
|
||||
import logging
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
class Updater:
|
||||
def __init__(self, send_mqtt_msg, topic_send, mode, desired_display_firmware_version, desired_display_firmware_url, desired_tasmota_driver_version, desired_tasmota_driver_url):
|
||||
self.desired_display_firmware_version = desired_display_firmware_version
|
||||
self.desired_display_firmware_url = desired_display_firmware_url
|
||||
self.desired_tasmota_driver_version = desired_tasmota_driver_version
|
||||
self.desired_tasmota_driver_url = desired_tasmota_driver_url
|
||||
|
||||
self.mode = mode
|
||||
self._send_mqtt_msg = send_mqtt_msg
|
||||
self.topic_send = topic_send
|
||||
self.current_tasmota_driver_version = None
|
||||
self.current_display_firmware_version = None
|
||||
|
||||
def set_tasmota_driver_version(self, driver_version):
|
||||
self.current_tasmota_driver_version = driver_version
|
||||
def set_current_display_firmware_version(self, panel_version):
|
||||
self.current_display_firmware_version = panel_version
|
||||
|
||||
def check_pre_req(self):
|
||||
# we need to know both versions to continue
|
||||
if self.current_tasmota_driver_version is not None and self.current_display_firmware_version is not None:
|
||||
# tasmota driver has to be at least version 2 for Update command
|
||||
# and panel has to be at version 11 for notify commands
|
||||
# version 16 for new button cmd format
|
||||
if self.current_tasmota_driver_version >= 2 and self.current_display_firmware_version >= 16:
|
||||
return True
|
||||
return False
|
||||
|
||||
def send_message_page(self, id, heading, msg, b1, b2):
|
||||
self._send_mqtt_msg(f"pageType,popupNotify")
|
||||
self._send_mqtt_msg(f"entityUpdateDetail,|{id}|{heading}|65535|{b1}|65535|{b2}|65535|{msg}|65535|0")
|
||||
|
||||
def check_updates(self):
|
||||
# return's true if a notification was send to the panel
|
||||
# run pre req check
|
||||
if self.check_pre_req():
|
||||
LOGGER.info("Update Pre-Check sucessful Tasmota Driver Version: %s Panel Version: %s", self.current_tasmota_driver_version, self.current_display_firmware_version)
|
||||
# check if tasmota driver needs update
|
||||
if self.current_tasmota_driver_version < self.desired_tasmota_driver_version:
|
||||
LOGGER.info("Update of Tasmota Driver needed")
|
||||
# in auto mode just do the update
|
||||
if self.mode == "auto":
|
||||
self.update_berry_driver()
|
||||
return False
|
||||
# send notification about the update
|
||||
if self.mode == "auto-notify":
|
||||
update_msg = "There's an update available for the Tasmota Berry driver, do you want to start the update now? If you encounter issues after the update or this message appears frequently, please check the manual and repeat the installation steps for the Tasmota Berry driver. "
|
||||
self.send_message_page("updateBerryNoYes", "Driver Update available!", update_msg, "Dismiss", "Yes")
|
||||
return True
|
||||
return False
|
||||
# check if display firmware needs an update
|
||||
if self.current_display_firmware_version < self.desired_display_firmware_version:
|
||||
LOGGER.info("Update of Display Firmware needed")
|
||||
# in auto mode just do the update
|
||||
if self.mode == "auto":
|
||||
self.update_panel_driver()
|
||||
return False
|
||||
# send notification about the update
|
||||
if self.mode == "auto-notify":
|
||||
update_msg = "There's a firmware update available for the Nextion screen of the NSPanel. Do you want tostart the update now? If the update fails check the installation manual and flash again over the Tasmota console. Be patient, the update will take a while."
|
||||
self.send_message_page("updateDisplayNoYes", "Display Update available!", update_msg, "Dismiss", "Yes")
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
LOGGER.info("Update Pre-Check failed Tasmota Driver Version: %s Panel Version: %s", self.current_tasmota_driver_version, self.current_display_firmware_version)
|
||||
return False
|
||||
|
||||
def update_berry_driver(self):
|
||||
topic = self.topic_send.replace("CustomSend", "UpdateDriverVersion")
|
||||
self._send_mqtt_msg(self.desired_tasmota_driver_url, topic=topic)
|
||||
def update_panel_driver(self):
|
||||
topic = self.topic_send.replace("CustomSend", "FlashNextion")
|
||||
self._send_mqtt_msg(self.desired_display_firmware_url, topic=topic)
|
||||
@@ -1,627 +1,79 @@
|
||||
import json
|
||||
import datetime
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
import hassapi as hass
|
||||
from helper import scale, pos_to_color, rgb_dec565, rgb_brightness
|
||||
from icon_mapping import get_icon_id
|
||||
from icons import get_icon_id_ha
|
||||
# check Babel
|
||||
import importlib
|
||||
babel_spec = importlib.util.find_spec("babel")
|
||||
if babel_spec is not None:
|
||||
import babel.dates
|
||||
|
||||
from luibackend.config import LuiBackendConfig
|
||||
from luibackend.controller import LuiController
|
||||
from luibackend.mqttListener import LuiMqttListener
|
||||
from luibackend.updater import Updater
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AppDaemonLoggingHandler(logging.Handler):
|
||||
def __init__(self, app):
|
||||
super().__init__()
|
||||
self._app = app
|
||||
|
||||
def emit(self, record):
|
||||
message = record.getMessage()
|
||||
if record.exc_info:
|
||||
message += '\nTraceback (most recent call last):\n'
|
||||
message += '\n'.join(traceback.format_tb(record.exc_info[2]))
|
||||
message += f'{record.exc_info[0].__name__}: {record.exc_info[1]}'
|
||||
self._app.log(message, level=record.levelname)
|
||||
|
||||
|
||||
class NsPanelLovelaceUIManager(hass.Hass):
|
||||
def initialize(self):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._redirect_logging()
|
||||
|
||||
data = self.args["config"]
|
||||
LovelaceUIPanel(self, data)
|
||||
def _redirect_logging(self):
|
||||
# Add a handler for the logging module that will convert the
|
||||
# calls to AppDaemon's logger with the self instance, so that
|
||||
# we can simply use logging in the rest of the application
|
||||
rlogger = logging.getLogger()
|
||||
rlogger.handlers = [
|
||||
h for h in rlogger.handlers
|
||||
if type(h).__name__ != AppDaemonLoggingHandler.__name__
|
||||
]
|
||||
rlogger.addHandler(AppDaemonLoggingHandler(self))
|
||||
|
||||
class Updater:
|
||||
def __init__(self, nsplui, mode):
|
||||
self.desired_display_firmware_version = 16
|
||||
self.desired_display_firmware_url = "http://nspanel.pky.eu/lovelace-ui/github/nspanel-v1.7.0.tft"
|
||||
self.desired_tasmota_driver_version = 3
|
||||
self.desired_tasmota_driver_url = "https://raw.githubusercontent.com/joBr99/nspanel-lovelace-ui/main/tasmota/autoexec.be"
|
||||
# We want to grab all the logs, AppDaemon will
|
||||
# then care about filtering those we asked for
|
||||
rlogger.setLevel(logging.DEBUG)
|
||||
|
||||
self.mode = mode
|
||||
self.nsplui = nsplui
|
||||
self.current_tasmota_driver_version = None
|
||||
self.current_display_firmware_version = None
|
||||
|
||||
def set_tasmota_driver_version(self, driver_version):
|
||||
self.current_tasmota_driver_version = driver_version
|
||||
def set_current_display_firmware_version(self, panel_version):
|
||||
self.current_display_firmware_version = panel_version
|
||||
def check_pre_req(self):
|
||||
# we need to know both versions to continue
|
||||
if self.current_tasmota_driver_version is not None and self.current_display_firmware_version is not None:
|
||||
# tasmota driver has to be at least version 2 for Update command and panel has to be at version 11 for notify commands
|
||||
if self.current_tasmota_driver_version >= 2 and self.current_display_firmware_version >= 11:
|
||||
return True
|
||||
return False
|
||||
def check_updates(self):
|
||||
# return's true if a notification was send to the panel
|
||||
# run pre req check
|
||||
if self.check_pre_req():
|
||||
self.nsplui.api.log("Update Pre-Check sucessful Tasmota Driver Version: %s Panel Version: %s", self.current_tasmota_driver_version, self.current_display_firmware_version, level="DEBUG")
|
||||
# check if tasmota driver needs update
|
||||
if self.current_tasmota_driver_version < self.desired_tasmota_driver_version:
|
||||
self.nsplui.api.log("Update of Tasmota Driver needed")
|
||||
# in auto mode just do the update
|
||||
if self.mode == "auto":
|
||||
self.update_berry_driver()
|
||||
return False
|
||||
# send notification about the update
|
||||
if self.mode == "auto-notify":
|
||||
update_msg = "There's an update avalible for the tasmota berry driver, do you want to start the update now? If you encounter issues after the update or this message appears frequently, please checkthe manual and repeat the installation steps for the tasmota berry driver. "
|
||||
self.nsplui.send_message_page("updateBerryNoYes", "Driver Update available!", update_msg, "Dismiss", "Yes")
|
||||
return True
|
||||
return False
|
||||
# check if display firmware needs an update
|
||||
if self.current_display_firmware_version < self.desired_display_firmware_version:
|
||||
self.nsplui.api.log("Update of Display Firmware needed")
|
||||
# in auto mode just do the update
|
||||
if self.mode == "auto":
|
||||
self.update_panel_driver()
|
||||
return False
|
||||
# send notification about the update
|
||||
if self.mode == "auto-notify":
|
||||
update_msg = "There's a firmware update avalible for the nextion sceen inside of nspanel, do you want to start the update now? If the update fails check the installation manual and flash again over the tasmota console. Be pationed the update will take a while."
|
||||
self.nsplui.send_message_page("updateDisplayNoYes", "Display Update available!", update_msg, "Dismiss", "Yes")
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
self.nsplui.api.log("Update Pre-Check failed Tasmota Driver Version: %s Panel Version: %s", self.current_tasmota_driver_version, self.current_display_firmware_version)
|
||||
return False
|
||||
def update_berry_driver(self):
|
||||
self.nsplui.mqtt.mqtt_publish(self.nsplui.config["panelSendTopic"].replace("CustomSend", "UpdateDriverVersion"), self.desired_tasmota_driver_url)
|
||||
def update_panel_driver(self):
|
||||
self.nsplui.mqtt.mqtt_publish(self.nsplui.config["panelSendTopic"].replace("CustomSend", "FlashNextion"), self.desired_display_firmware_url)
|
||||
|
||||
class LovelaceUIPanel:
|
||||
def __init__(self, api, config):
|
||||
self.api = api
|
||||
self.config = config
|
||||
self.current_page_nr = 0
|
||||
self.current_screensaver_brightness = 10
|
||||
|
||||
# check configured items
|
||||
self.check_items()
|
||||
|
||||
# Setup, mqtt subscription and callback
|
||||
self.mqtt = self.api.get_plugin_api("MQTT")
|
||||
self.mqtt.mqtt_subscribe(topic=self.config["panelRecvTopic"])
|
||||
self.mqtt.listen_event(self.handle_mqtt_incoming_message, "MQTT_MESSAGE", topic=self.config["panelRecvTopic"], namespace='mqtt')
|
||||
|
||||
# Read updateMode and use "auto-notify" as default
|
||||
update_mode = self.config["updateMode"] if "updateMode" in self.config else "auto-notify"
|
||||
self.updater = Updater(self, update_mode)
|
||||
|
||||
# Request Tasmota Driver Version
|
||||
self.mqtt.mqtt_publish(self.config["panelSendTopic"].replace("CustomSend", "GetDriverVersion"), "x")
|
||||
|
||||
# send panel back to startup page on restart of this script
|
||||
self.send_mqtt_msg("pageType,pageStartup")
|
||||
|
||||
# Setup time callback
|
||||
time = datetime.time(0, 0, 0)
|
||||
self.api.run_minutely(self.update_time, time)
|
||||
|
||||
# Setup date callback
|
||||
time = datetime.time(0, 0, 0)
|
||||
self.api.run_daily(self.update_date, time)
|
||||
# send date update in case config has been changed
|
||||
self.update_date("")
|
||||
|
||||
# Setup weather callback
|
||||
weather_interval = 15 * 60 # 15 minutes
|
||||
self.api.run_every(self.update_screensaver_weather, "now", weather_interval)
|
||||
|
||||
# set brightness of screensaver
|
||||
if type(self.config["brightnessScreensaver"]) == int:
|
||||
self.current_screensaver_brightness = self.config["brightnessScreensaver"]
|
||||
elif type(self.config["brightnessScreensaver"]) == list:
|
||||
sorted_timesets = sorted(self.config["brightnessScreensaver"], key=lambda d: self.api.parse_time(d['time']))
|
||||
found_current_dim_value = False
|
||||
for index, timeset in enumerate(sorted_timesets):
|
||||
self.api.run_daily(self.update_screensaver_brightness, timeset["time"], value=timeset["value"])
|
||||
self.api.log("Current time %s", self.api.get_now().time(), level="DEBUG")
|
||||
if self.api.parse_time(timeset["time"]) > self.api.get_now().time() and not found_current_dim_value:
|
||||
# first time after current time, set dim value
|
||||
self.current_screensaver_brightness = sorted_timesets[index-1]["value"]
|
||||
self.api.log("Setting dim value to %s", sorted_timesets[index-1]) #level="DEBUG"
|
||||
found_current_dim_value = True
|
||||
# still no dim value
|
||||
if not found_current_dim_value:
|
||||
self.current_screensaver_brightness = sorted_timesets[-1]["value"]
|
||||
# send screensaver brightness in case config has changed
|
||||
self.update_screensaver_brightness(kwargs={"value": self.current_screensaver_brightness})
|
||||
|
||||
# register callbacks
|
||||
self.register_callbacks()
|
||||
|
||||
def filter_dict_from_item_list(self, items):
|
||||
# remove all dicts from list
|
||||
cleaned_list = []
|
||||
for item in items:
|
||||
# in case item is a dict, grab the item name
|
||||
if type(item) is dict:
|
||||
cleaned_list.append(next(iter(item)))
|
||||
else:
|
||||
cleaned_list.append(item)
|
||||
return cleaned_list
|
||||
|
||||
def get_all_configured_items(self):
|
||||
items = []
|
||||
for page in self.config["pages"]:
|
||||
if "item" in page:
|
||||
items.append(page["item"])
|
||||
if "items" in page:
|
||||
items.extend(page["items"])
|
||||
return self.filter_dict_from_item_list(items)
|
||||
|
||||
def check_items(self):
|
||||
items = self.get_all_configured_items()
|
||||
for item in items:
|
||||
if self.api.entity_exists(item) or item == "delete":
|
||||
self.api.log("Found configured item in Home Assistant %s", item, level="DEBUG")
|
||||
else:
|
||||
self.api.error("The following item does not exist in Home Assistant, configuration error: %s", item)
|
||||
|
||||
def register_callbacks(self):
|
||||
items = self.get_all_configured_items()
|
||||
for item in items:
|
||||
self.api.log("Enable state callback for %s", item, level="DEBUG")
|
||||
self.api.handle = self.api.listen_state(self.state_change_callback, entity_id=item, attribute="all")
|
||||
|
||||
def state_change_callback(self, entity, attribute, old, new, kwargs):
|
||||
current_page_config = self.config["pages"][self.current_page_nr]
|
||||
page_type = current_page_config["type"]
|
||||
self.api.log(f"Got state_callback from {entity}", level="DEBUG")
|
||||
|
||||
if page_type in ["cardEntities", "cardGrid"]:
|
||||
items = current_page_config["items"]
|
||||
items_filtered = self.filter_dict_from_item_list(items)
|
||||
if entity in items_filtered:
|
||||
self.api.log(f"State change on current page for {entity}", level="DEBUG")
|
||||
# send update of the page
|
||||
self.generate_entities_page(items)
|
||||
# send detail pages in case they are open
|
||||
if(entity.startswith("cover")):
|
||||
self.generate_shutter_detail_page(entity)
|
||||
if(entity.startswith("light")):
|
||||
self.generate_light_detail_page(entity)
|
||||
return
|
||||
|
||||
if page_type in ["cardThermo", "cardMedia"]:
|
||||
if entity == current_page_config["item"]:
|
||||
self.api.log(f"State change on current page for {entity}", level="DEBUG")
|
||||
# send update of the whole page
|
||||
if page_type == "cardThermo":
|
||||
self.generate_thermo_page(entity)
|
||||
return
|
||||
if page_type == "cardMedia":
|
||||
self.generate_media_page(entity)
|
||||
return
|
||||
return
|
||||
|
||||
def send_mqtt_msg(self,msg):
|
||||
self.api.log("Send Message to Tasmota: %s", msg) #, level="DEBUG"
|
||||
self.mqtt.mqtt_publish(self.config["panelSendTopic"], msg)
|
||||
|
||||
def handle_mqtt_incoming_message(self, event_name, data, kwargs):
|
||||
# Parse Json Message from Tasmota and strip out message from nextion display
|
||||
data = json.loads(data["payload"])
|
||||
# pass tasmota driver version to updater class
|
||||
if("nlui_driver_version" in data):
|
||||
msg = data["nlui_driver_version"]
|
||||
self.api.log("Received Driver Version from Tasmota: %s", int(msg), level="DEBUG")
|
||||
self.updater.set_tasmota_driver_version(int(msg))
|
||||
return
|
||||
if("CustomRecv" not in data):
|
||||
self.api.log("Received Message from Tasmota, but not from nextion screen: %s", data, level="DEBUG")
|
||||
return
|
||||
msg = data["CustomRecv"]
|
||||
self.api.log("Received Message from Tasmota: %s", msg) #, level="DEBUG"
|
||||
|
||||
# Split message into parts seperated by ","
|
||||
msg = msg.split(",")
|
||||
|
||||
# run action based on received command
|
||||
if msg[0] == "event":
|
||||
if msg[1] == "startup":
|
||||
self.api.log("Handling startup event", level="DEBUG")
|
||||
# grab version from screen and pass to updater class
|
||||
self.updater.set_current_display_firmware_version(int(msg[2]))
|
||||
# send date and time
|
||||
self.update_time("")
|
||||
self.update_date("")
|
||||
# set screensaver timeout
|
||||
timeout = self.config["timeoutScreensaver"]
|
||||
self.send_mqtt_msg(f"timeout,{timeout}")
|
||||
# send screensaver brightness
|
||||
self.update_screensaver_brightness(kwargs={"value": self.current_screensaver_brightness})
|
||||
# check for updates
|
||||
msg_send = self.updater.check_updates()
|
||||
# send messages for current page
|
||||
if not msg_send:
|
||||
self.generate_page(self.current_page_nr)
|
||||
|
||||
if msg[1] == "pageOpen":
|
||||
# Calculate current page
|
||||
recv_page = int(msg[2])
|
||||
self.current_page_nr = recv_page % len(self.config["pages"])
|
||||
self.api.log("Received pageOpen command, raw page: %i, calc page: %i", recv_page, self.current_page_nr, level="DEBUG")
|
||||
# generate commands for current page
|
||||
self.generate_page(self.current_page_nr)
|
||||
def initialize(self):
|
||||
LOGGER.info('Starting')
|
||||
mqtt_api = self._mqtt_api = self.get_plugin_api("MQTT")
|
||||
cfg = self._cfg = LuiBackendConfig(self.args["config"])
|
||||
|
||||
if msg[1] == "buttonPress":
|
||||
entity_id = msg[4]
|
||||
btype = msg[6]
|
||||
if len(msg) > 7:
|
||||
value = msg[7]
|
||||
else:
|
||||
value = None
|
||||
self.handle_button_press(entity_id, btype, value)
|
||||
topic_send = cfg.get("panelSendTopic")
|
||||
def send_mqtt_msg(msg, topic=None):
|
||||
if topic is None:
|
||||
topic = topic_send
|
||||
LOGGER.info(f"Sending MQTT Message: {msg}")
|
||||
mqtt_api.mqtt_publish(topic, msg)
|
||||
|
||||
# Request Tasmota Driver Version
|
||||
mqtt_api.mqtt_publish(topic_send.replace("CustomSend", "GetDriverVersion"), "x")
|
||||
|
||||
controller = LuiController(self, cfg, send_mqtt_msg)
|
||||
|
||||
desired_display_firmware_version = 19
|
||||
desired_display_firmware_url = "http://nspanel.pky.eu/lovelace-ui/github/nspanel-v1.8.3.tft"
|
||||
desired_tasmota_driver_version = 3
|
||||
desired_tasmota_driver_url = "https://raw.githubusercontent.com/joBr99/nspanel-lovelace-ui/main/tasmota/autoexec.be"
|
||||
|
||||
if msg[1] == "buttonPress2":
|
||||
entity_id = msg[2]
|
||||
btype = msg[3]
|
||||
if len(msg) > 4:
|
||||
value = msg[4]
|
||||
else:
|
||||
value = None
|
||||
self.handle_button_press(entity_id, btype, value)
|
||||
mode = cfg.get("updateMode")
|
||||
topic_send = cfg.get("panelSendTopic")
|
||||
updater = Updater(send_mqtt_msg, topic_send, mode, desired_display_firmware_version, desired_display_firmware_url, desired_tasmota_driver_version, desired_tasmota_driver_url)
|
||||
|
||||
if msg[1] == "pageOpenDetail":
|
||||
self.api.log("Received pageOpenDetail command", level="DEBUG")
|
||||
if msg[2] == "popupShutter":
|
||||
self.generate_shutter_detail_page(msg[3])
|
||||
if msg[2] == "popupLight":
|
||||
self.generate_light_detail_page(msg[3])
|
||||
topic_recv = cfg.get("panelRecvTopic")
|
||||
LuiMqttListener(mqtt_api, topic_recv, controller, updater)
|
||||
|
||||
if msg[1] == "screensaverOpen":
|
||||
self.update_screensaver_weather("")
|
||||
|
||||
def update_time(self, kwargs):
|
||||
time = datetime.datetime.now().strftime(self.config["timeFormat"])
|
||||
self.send_mqtt_msg(f"time,{time}")
|
||||
|
||||
def update_date(self, kwargs):
|
||||
global babel_spec
|
||||
if babel_spec is not None:
|
||||
self.api.log("Babel package found", level="DEBUG")
|
||||
if "dateFormatBabel" in self.config:
|
||||
dateformat = self.config["dateFormatBabel"]
|
||||
else:
|
||||
dateformat = "full"
|
||||
date = babel.dates.format_date(datetime.datetime.now(), dateformat, locale=self.config["locale"])
|
||||
self.send_mqtt_msg(f"date,?{date}")
|
||||
else:
|
||||
self.api.log("Babel package not found", level="DEBUG")
|
||||
date = datetime.datetime.now().strftime(self.config["dateFormat"])
|
||||
self.send_mqtt_msg(f"date,?{date}")
|
||||
|
||||
def update_screensaver_brightness(self, kwargs):
|
||||
self.current_screensaver_brightness = kwargs['value']
|
||||
self.send_mqtt_msg(f"dimmode,{self.current_screensaver_brightness}")
|
||||
|
||||
def update_screensaver_weather(self, kwargs):
|
||||
if not ("weatherEntity" in self.config and self.api.entity_exists(self.config["weatherEntity"])):
|
||||
return
|
||||
we = self.api.get_entity(self.config["weatherEntity"])
|
||||
unit = "°C"
|
||||
|
||||
icon_cur = get_icon_id_ha("weather", state=we.state)
|
||||
text_cur = f"{we.attributes.temperature}{unit}"
|
||||
icon_cur_detail = get_icon_id("water-percent")
|
||||
text_cur_detail = f"{we.attributes.humidity} %"
|
||||
|
||||
up1 = we.attributes.forecast[0]['datetime']
|
||||
up1 = datetime.datetime.fromisoformat(up1)
|
||||
icon1 = get_icon_id_ha("weather", state=we.attributes.forecast[0]['condition'])
|
||||
down1 = we.attributes.forecast[0]['temperature']
|
||||
|
||||
up2 = we.attributes.forecast[1]['datetime']
|
||||
up2 = datetime.datetime.fromisoformat(up2)
|
||||
icon2 = get_icon_id_ha("weather", state=we.attributes.forecast[1]['condition'])
|
||||
down2 = we.attributes.forecast[1]['temperature']
|
||||
|
||||
global babel_spec
|
||||
if babel_spec is not None:
|
||||
up1 = babel.dates.format_date(up1, "E", locale=self.config["locale"])
|
||||
up2 = babel.dates.format_date(up2, "E", locale=self.config["locale"])
|
||||
else:
|
||||
up1 = up1.strftime("%a")
|
||||
up2 = up2.strftime("%a")
|
||||
|
||||
self.send_mqtt_msg(f"weatherUpdate,?{icon_cur}?{text_cur}?{icon_cur_detail}?{text_cur_detail}?{up1}?{icon1}?{down1}?{up2}?{icon2}?{down2}")
|
||||
|
||||
|
||||
def handle_button_press(self, entity_id, btype, optVal=None):
|
||||
if entity_id == "updateBerryNoYes" and optVal == "yes":
|
||||
# go back to main page before starting the update
|
||||
self.generate_page(self.current_page_nr)
|
||||
self.updater.update_berry_driver()
|
||||
elif entity_id == "updateBerryNoYes" and optVal == "no":
|
||||
self.generate_page(self.current_page_nr)
|
||||
|
||||
if entity_id == "updateDisplayNoYes" and optVal == "yes":
|
||||
self.updater.update_panel_driver()
|
||||
elif entity_id == "updateDisplayNoYes" and optVal == "no":
|
||||
self.generate_page(self.current_page_nr)
|
||||
|
||||
if btype == "OnOff":
|
||||
if optVal == "1":
|
||||
self.api.turn_on(entity_id)
|
||||
else:
|
||||
self.api.turn_off(entity_id)
|
||||
if btype == "up":
|
||||
self.api.get_entity(entity_id).call_service("open_cover")
|
||||
if btype == "stop":
|
||||
self.api.get_entity(entity_id).call_service("stop_cover")
|
||||
if btype == "down":
|
||||
self.api.get_entity(entity_id).call_service("close_cover")
|
||||
|
||||
if btype == "button":
|
||||
if entity_id.startswith('scene'):
|
||||
self.api.get_entity(entity_id).call_service("turn_on")
|
||||
elif entity_id.startswith('light') or entity_id.startswith('switch') or entity_id.startswith('input_boolean'):
|
||||
self.api.get_entity(entity_id).call_service("toggle")
|
||||
else:
|
||||
self.api.get_entity(entity_id).call_service("press")
|
||||
|
||||
if btype == "media-next":
|
||||
self.api.get_entity(entity_id).call_service("media_next_track")
|
||||
if btype == "media-back":
|
||||
self.api.get_entity(entity_id).call_service("media_previous_track")
|
||||
if btype == "media-pause":
|
||||
self.api.get_entity(entity_id).call_service("media_play_pause")
|
||||
|
||||
if btype == "hvac_action":
|
||||
self.api.get_entity(entity_id).call_service("set_hvac_mode", hvac_mode=optVal)
|
||||
|
||||
|
||||
if btype == "brightnessSlider":
|
||||
# scale 0-100 to ha brightness range
|
||||
brightness = int(scale(int(optVal),(0,100),(0,255)))
|
||||
self.api.get_entity(entity_id).call_service("turn_on", brightness=brightness)
|
||||
|
||||
if btype == "colorTempSlider":
|
||||
entity = self.api.get_entity(entity_id)
|
||||
#scale 0-100 from slider to color range of lamp
|
||||
color_val = scale(int(optVal), (0, 100), (entity.attributes.min_mireds, entity.attributes.max_mireds))
|
||||
self.api.get_entity(entity_id).call_service("turn_on", color_temp=color_val)
|
||||
|
||||
if btype == "colorWheel":
|
||||
self.api.log(optVal)
|
||||
optVal = optVal.split('|')
|
||||
color = pos_to_color(int(optVal[0]), int(optVal[1]))
|
||||
self.api.log(color)
|
||||
self.api.get_entity(entity_id).call_service("turn_on", rgb_color=color)
|
||||
|
||||
if btype == "positionSlider":
|
||||
pos = int(optVal)
|
||||
self.api.get_entity(entity_id).call_service("set_cover_position", position=pos)
|
||||
|
||||
if btype == "volumeSlider":
|
||||
pos = int(optVal)
|
||||
# HA wants this value between 0 and 1 as float
|
||||
pos = pos/100
|
||||
self.api.get_entity(entity_id).call_service("volume_set", volume_level=pos)
|
||||
|
||||
if btype == "tempUpd":
|
||||
temp = int(optVal)/10
|
||||
self.api.get_entity(msg[3]).call_service("set_temperature", temperature=temp)
|
||||
|
||||
def generate_page(self, page_number):
|
||||
# get type of page
|
||||
page_type = self.config["pages"][self.current_page_nr]["type"]
|
||||
self.api.log("Generating page commands for page %i with type %s", self.current_page_nr, page_type, level="DEBUG")
|
||||
|
||||
# Send page type
|
||||
self.send_mqtt_msg(f"pageType,{page_type}")
|
||||
|
||||
if page_type in ["cardEntities", "cardGrid"]:
|
||||
self.generate_entities_page(self.config["pages"][self.current_page_nr]["items"])
|
||||
|
||||
if page_type == "cardThermo":
|
||||
self.generate_thermo_page(self.config["pages"][self.current_page_nr]["item"])
|
||||
|
||||
if page_type == "cardMedia":
|
||||
self.generate_media_page(self.config["pages"][self.current_page_nr]["item"])
|
||||
|
||||
def generate_entities_item(self, item):
|
||||
icon = None
|
||||
if type(item) is dict:
|
||||
icon = next(iter(item.items()))[1]['icon']
|
||||
item = next(iter(item.items()))[0]
|
||||
|
||||
# type of the item is the string before the "." in the item name
|
||||
item_type = item.split(".")[0]
|
||||
|
||||
self.api.log("Generating item command for %s with type %s", item, item_type, level="DEBUG")
|
||||
|
||||
if item_type == "delete":
|
||||
return f",{item_type},,,,,"
|
||||
|
||||
if not self.api.entity_exists(item):
|
||||
return f",text,{item},{get_icon_id('alert-circle-outline')},17299,Not found check, apps.yaml"
|
||||
|
||||
entity = self.api.get_entity(item)
|
||||
name = entity.attributes.friendly_name
|
||||
|
||||
if item_type == "cover":
|
||||
icon_id = get_icon_id_ha("cover", state=entity.state, overwrite=icon)
|
||||
return f",shutter,{item},{icon_id},17299,{name},"
|
||||
|
||||
if item_type == "light":
|
||||
switch_val = 1 if entity.state == "on" else 0
|
||||
icon_color = self.getEntityColor(entity)
|
||||
icon_id = get_icon_id_ha("light", overwrite=icon)
|
||||
return f",{item_type},{item},{icon_id},{icon_color},{name},{switch_val}"
|
||||
|
||||
if item_type == "switch" or item_type == "input_boolean":
|
||||
icon_id = get_icon_id_ha(item_type, state=entity.state, overwrite=icon)
|
||||
switch_val = 1 if entity.state == "on" else 0
|
||||
icon_color = self.getEntityColor(entity)
|
||||
return f",switch,{item},{icon_id},{icon_color},{name},{switch_val}"
|
||||
|
||||
if item_type in ["sensor", "binary_sensor"]:
|
||||
device_class = self.get_safe_ha_attribute(entity.attributes, "device_class", "")
|
||||
icon_id = get_icon_id_ha("sensor", state=entity.state, device_class=device_class, overwrite=icon)
|
||||
unit_of_measurement = self.get_safe_ha_attribute(entity.attributes, "unit_of_measurement", "")
|
||||
value = entity.state + " " + unit_of_measurement
|
||||
return f",text,{item},{icon_id},17299,{name},{value}"
|
||||
|
||||
if item_type in ["button", "input_button"]:
|
||||
icon_id = get_icon_id_ha("button", overwrite=icon)
|
||||
return f",button,{item},{icon_id},17299,{name},PRESS"
|
||||
|
||||
if item_type == "scene":
|
||||
icon_id = get_icon_id_ha("scene", overwrite=icon)
|
||||
return f",button,{item},{icon_id},17299,{name},ACTIVATE"
|
||||
|
||||
def generate_entities_page(self, items):
|
||||
# Set Heading of Page
|
||||
self.send_mqtt_msg(f"entityUpdHeading,{self.config['pages'][self.current_page_nr]['heading']}")
|
||||
# Get items and construct cmd string
|
||||
command = "entityUpd"
|
||||
for item in items:
|
||||
command += self.generate_entities_item(item)
|
||||
self.send_mqtt_msg(command)
|
||||
|
||||
def get_safe_ha_attribute(self, eattr, attr, default):
|
||||
return eattr[attr] if attr in eattr else default
|
||||
|
||||
def generate_thermo_page(self, item):
|
||||
if not self.api.entity_exists(item):
|
||||
command = f"entityUpd,{item},Not found,220,220,Not found,150,300,5"
|
||||
else:
|
||||
entity = self.api.get_entity(item)
|
||||
heading = entity.attributes.friendly_name
|
||||
current_temp = int(self.get_safe_ha_attribute(entity.attributes, "current_temperature", 0)*10)
|
||||
dest_temp = int(self.get_safe_ha_attribute(entity.attributes, "temperature", 0)*10)
|
||||
status = self.get_safe_ha_attribute(entity.attributes, "hvac_action", "")
|
||||
min_temp = int(self.get_safe_ha_attribute(entity.attributes, "min_temp", 0)*10)
|
||||
max_temp = int(self.get_safe_ha_attribute(entity.attributes, "max_temp", 0)*10)
|
||||
step_temp = int(self.get_safe_ha_attribute(entity.attributes, "target_temp_step", 0.5)*10)
|
||||
|
||||
icon_res = ""
|
||||
hvac_modes = self.get_safe_ha_attribute(entity.attributes, "hvac_modes", [])
|
||||
for mode in hvac_modes:
|
||||
icon_id = get_icon_id('alert-circle-outline')
|
||||
color_on = 64512
|
||||
if mode == "auto":
|
||||
icon_id = get_icon_id("calendar-sync")
|
||||
color_on = 1024
|
||||
if mode == "heat":
|
||||
icon_id = get_icon_id("fire")
|
||||
color_on = 64512
|
||||
if mode == "off":
|
||||
icon_id = get_icon_id("power")
|
||||
color_on = 35921
|
||||
if mode == "cool":
|
||||
icon_id = get_icon_id("snowflake")
|
||||
color_on = 11487
|
||||
if mode == "dry":
|
||||
icon_id = get_icon_id("water-percent")
|
||||
color_on = 60897
|
||||
if mode == "fan_only":
|
||||
icon_id = get_icon_id("fan")
|
||||
color_on = 35921
|
||||
state = 0
|
||||
if(mode == entity.state):
|
||||
state = 1
|
||||
icon_res += f",{icon_id},{color_on},{state},{mode}"
|
||||
|
||||
len_hvac_modes = len(hvac_modes)
|
||||
if len_hvac_modes%2 == 0:
|
||||
# even
|
||||
padding_len = int((4-len_hvac_modes)/2)
|
||||
icon_res = ","*4*padding_len + icon_res + ","*4*padding_len
|
||||
# use last 4 icons
|
||||
icon_res = ","*4*5 + icon_res
|
||||
else:
|
||||
# uneven
|
||||
padding_len = int((5-len_hvac_modes)/2)
|
||||
icon_res = ","*4*padding_len + icon_res + ","*4*padding_len
|
||||
# use first 5 icons
|
||||
icon_res = icon_res + ","*4*4
|
||||
command = f"entityUpd,{item},{heading},{current_temp},{dest_temp},{status},{min_temp},{max_temp},{step_temp}{icon_res}"
|
||||
self.send_mqtt_msg(command)
|
||||
|
||||
def generate_media_page(self, item):
|
||||
|
||||
if not self.api.entity_exists(item):
|
||||
command = f"entityUpd,|{item}|Not found|{get_icon_id('alert-circle-outline')}|Please check your|apps.yaml in AppDaemon|50|11"
|
||||
else:
|
||||
entity = self.api.get_entity(item)
|
||||
heading = entity.attributes.friendly_name
|
||||
icon = 0
|
||||
title = self.get_safe_ha_attribute(entity.attributes, "media_title", "")
|
||||
author = self.get_safe_ha_attribute(entity.attributes, "media_artist", "")
|
||||
volume = int(self.get_safe_ha_attribute(entity.attributes, "volume_level", 0)*100)
|
||||
iconplaypause = get_icon_id("pause") if entity.state == "playing" else get_icon_id("play")
|
||||
if "media_content_type" in entity.attributes:
|
||||
if entity.attributes.media_content_type == "music":
|
||||
icon = get_icon_id("music")
|
||||
command = f"entityUpd,|{item}|{heading}|{icon}|{title}|{author}|{volume}|{iconplaypause}"
|
||||
|
||||
self.send_mqtt_msg(command)
|
||||
|
||||
def getEntityColor(self, entity):
|
||||
attr = entity.attributes
|
||||
default_color_on = rgb_dec565([253, 216, 53])
|
||||
default_color_off = rgb_dec565([68, 115, 158])
|
||||
icon_color = default_color_on if entity.state == "on" else default_color_off
|
||||
|
||||
if "rgb_color" in attr:
|
||||
color = attr.rgb_color
|
||||
if "brightness" in attr:
|
||||
color = rgb_brightness(color, attr.brightness)
|
||||
icon_color = rgb_dec565(color)
|
||||
elif "brightness" in attr:
|
||||
color = rgb_brightness([253, 216, 53], attr.brightness)
|
||||
icon_color = rgb_dec565(color)
|
||||
return icon_color
|
||||
|
||||
def generate_light_detail_page(self, entity):
|
||||
entity = self.api.get_entity(entity)
|
||||
switch_val = 1 if entity.state == "on" else 0
|
||||
icon_color = self.getEntityColor(entity)
|
||||
brightness = "disable"
|
||||
color_temp = "disable"
|
||||
color = "disable"
|
||||
# scale 0-255 brightness from ha to 0-100
|
||||
if entity.state == "on":
|
||||
if "brightness" in entity.attributes:
|
||||
brightness = int(scale(entity.attributes.brightness,(0,255),(0,100)))
|
||||
else:
|
||||
brightness = "disable"
|
||||
if "color_temp" in entity.attributes.supported_color_modes:
|
||||
if "color_temp" in entity.attributes:
|
||||
# scale ha color temp range to 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 entity.attributes.supported_color_modes):
|
||||
color = "enable"
|
||||
else:
|
||||
color = "disable"
|
||||
self.send_mqtt_msg(f"entityUpdateDetail,{get_icon_id('lightbulb')},{icon_color},{switch_val},{brightness},{color_temp},{color}")
|
||||
|
||||
def generate_shutter_detail_page(self, entity):
|
||||
pos = int(self.get_safe_ha_attribute(entity.attributes, "current_position", 50))
|
||||
# reverse position for slider
|
||||
pos = 100-pos
|
||||
self.send_mqtt_msg(f"entityUpdateDetail,{pos}")
|
||||
|
||||
def send_message_page(self, id, heading, msg, b1, b2):
|
||||
self.send_mqtt_msg(f"pageType,popupNotify")
|
||||
self.send_mqtt_msg(f"entityUpdateDetail,|{id}|{heading}|65535|{b1}|65535|{b2}|65535|{msg}|65535|0")
|
||||
|
||||
LOGGER.info('Started')
|
||||
|
||||
7
info.md
7
info.md
@@ -5,14 +5,15 @@ Checkout [README](https://github.com/joBr99/nspanel-lovelace-ui/blob/main/README
|
||||
### App Configuration
|
||||
|
||||
```yaml
|
||||
---
|
||||
nspanel-1:
|
||||
module: nspanel-lovelace-ui
|
||||
class: NsPanelLovelaceUIManager
|
||||
config:
|
||||
panelRecvTopic: "tele/tasmota_your_mqtt_topic/RESULT"
|
||||
panelSendTopic: "cmnd/tasmota_your_mqtt_topic/CustomSend"
|
||||
updateMode: auto-notify # possible values are auto, auto-notify and manual
|
||||
timeoutScreensaver: 15 #in seconds
|
||||
updateMode: "auto-notify"
|
||||
timeoutScreensaver: 20
|
||||
#brightnessScreensaver: 10
|
||||
brightnessScreensaver:
|
||||
- time: "7:00:00"
|
||||
@@ -24,7 +25,7 @@ nspanel-1:
|
||||
# formatting options on https://babel.pocoo.org/en/latest/dates.html?highlight=name%20of%20day#date-fields
|
||||
timeFormat: "%H:%M"
|
||||
dateFormat: "%A, %d. %B %Y" # ignored if babel python package is installed
|
||||
weatherEntity: weather.example
|
||||
weather: weather.example
|
||||
pages:
|
||||
- type: cardEntities
|
||||
heading: Example Page 1
|
||||
|
||||
@@ -92,8 +92,6 @@ export const config: Config = {
|
||||
button2Page: button2Page
|
||||
};
|
||||
|
||||
|
||||
|
||||
var subscriptions: any = {};
|
||||
|
||||
var pageId = 0;
|
||||
@@ -149,28 +147,29 @@ function SendToPanel(val: Payload | Payload[]): void {
|
||||
|
||||
function HandleMessage(typ: string, method: string, page: number, words: Array<string>): void {
|
||||
if (typ == "event") {
|
||||
var pageNum = (page % config.pages.length);
|
||||
pageId = Math.abs(pageNum);
|
||||
|
||||
if (method == 'pageOpen' || method == 'startup') {
|
||||
UnsubscribeWatcher();
|
||||
|
||||
if (method == 'startup')
|
||||
switch (method) {
|
||||
case "pageOpen":
|
||||
var pageNum = (page % config.pages.length);
|
||||
pageId = Math.abs(pageNum);
|
||||
UnsubscribeWatcher();
|
||||
GeneratePage(config.pages[pageId]);
|
||||
break;
|
||||
case "startup":
|
||||
UnsubscribeWatcher();
|
||||
HandleStartupProcess();
|
||||
|
||||
GeneratePage(config.pages[pageId]);
|
||||
}
|
||||
|
||||
if (method == 'buttonPress' || method == "tempUpd") {
|
||||
HandleButtonEvent(words)
|
||||
}
|
||||
|
||||
if (method == 'screensaverOpen') {
|
||||
HandleScreensaver()
|
||||
}
|
||||
|
||||
if (method == 'button1' || method == 'button2') {
|
||||
HandleHardwareButton(method);
|
||||
break;
|
||||
case "buttonPress2":
|
||||
HandleButtonEvent(words);
|
||||
break;
|
||||
case "screensaverOpen":
|
||||
HandleScreensaver();
|
||||
break;
|
||||
case "button1":
|
||||
case "button2":
|
||||
HandleHardwareButton(method);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,11 +275,11 @@ function CreateEntity(pageItem: PageItem, placeId: number, useColors: boolean =
|
||||
|
||||
if (existsState(pageItem.id + ".GET")) {
|
||||
val = getState(pageItem.id + ".GET").val;
|
||||
RegisterEntityWatcher(pageItem.id + ".GET", pageItem.id, placeId);
|
||||
RegisterEntityWatcher(pageItem.id + ".GET");
|
||||
}
|
||||
else if (existsState(pageItem.id + ".SET")) {
|
||||
val = getState(pageItem.id + ".SET").val;
|
||||
RegisterEntityWatcher(pageItem.id + ".SET", pageItem.id, placeId);
|
||||
RegisterEntityWatcher(pageItem.id + ".SET");
|
||||
}
|
||||
var iconColor = rgb_dec565(config.defaultColor);
|
||||
|
||||
@@ -303,11 +302,11 @@ function CreateEntity(pageItem: PageItem, placeId: number, useColors: boolean =
|
||||
var optVal = "0"
|
||||
if (existsState(pageItem.id + ".ON_ACTUAL")) {
|
||||
val = getState(pageItem.id + ".ON_ACTUAL").val;
|
||||
RegisterEntityWatcher(pageItem.id + ".ON_ACTUAL", pageItem.id, placeId);
|
||||
RegisterEntityWatcher(pageItem.id + ".ON_ACTUAL");
|
||||
}
|
||||
else if (existsState(pageItem.id + ".ON_SET")) {
|
||||
val = getState(pageItem.id + ".ON_SET").val;
|
||||
RegisterEntityWatcher(pageItem.id + ".ON_SET", pageItem.id, placeId);
|
||||
RegisterEntityWatcher(pageItem.id + ".ON_SET");
|
||||
}
|
||||
if (val === true || val === "true") {
|
||||
optVal = "1"
|
||||
@@ -331,12 +330,12 @@ function CreateEntity(pageItem: PageItem, placeId: number, useColors: boolean =
|
||||
if (existsState(pageItem.id + ".ON_ACTUAL")) {
|
||||
optVal = getState(pageItem.id + ".ON_ACTUAL").val;
|
||||
unit = GetUnitOfMeasurement(pageItem.id + ".ON_ACTUAL");
|
||||
RegisterEntityWatcher(pageItem.id + ".ON_ACTUAL", pageItem.id, placeId);
|
||||
RegisterEntityWatcher(pageItem.id + ".ON_ACTUAL");
|
||||
}
|
||||
else if (existsState(pageItem.id + ".ACTUAL")) {
|
||||
optVal = getState(pageItem.id + ".ACTUAL").val;
|
||||
unit = GetUnitOfMeasurement(pageItem.id + ".ACTUAL");
|
||||
RegisterEntityWatcher(pageItem.id + ".ACTUAL", pageItem.id, placeId);
|
||||
RegisterEntityWatcher(pageItem.id + ".ACTUAL");
|
||||
}
|
||||
|
||||
if (o.common.role == "value.temperature") {
|
||||
@@ -384,11 +383,12 @@ function GetIconColor(pageItem: PageItem, value: (boolean | number), useColors:
|
||||
return rgb_dec565(pageItem.offColor !== undefined ? pageItem.offColor : config.defaultOffColor);
|
||||
}
|
||||
|
||||
function RegisterEntityWatcher(id: string, entityId: string, placeId: number): void {
|
||||
function RegisterEntityWatcher(id: string): void {
|
||||
if (subscriptions.hasOwnProperty(id)) {
|
||||
return;
|
||||
}
|
||||
subscriptions[id] = (on({ id: id, change: 'any' }, function (data) {
|
||||
log("RegisterEntityWatcher PageId:" + pageId.toString())
|
||||
GeneratePage(config.pages[pageId]);
|
||||
}))
|
||||
}
|
||||
@@ -477,11 +477,11 @@ function toggleState(id: string): boolean {
|
||||
}
|
||||
|
||||
function HandleButtonEvent(words): void {
|
||||
let id = words[4]
|
||||
let id = words[2]
|
||||
|
||||
if (words[6] == "OnOff" && existsObject(id)) {
|
||||
if (words[3] == "OnOff" && existsObject(id)) {
|
||||
var action = false
|
||||
if (words[7] == "1")
|
||||
if (words[4] == "1")
|
||||
action = true
|
||||
let o = getObject(id)
|
||||
switch (o.common.role) {
|
||||
@@ -496,31 +496,28 @@ function HandleButtonEvent(words): void {
|
||||
}
|
||||
}
|
||||
|
||||
if (words[6] == "up")
|
||||
if (words[3] == "up")
|
||||
setState(id + ".OPEN", true)
|
||||
if (words[6] == "stop")
|
||||
if (words[3] == "stop")
|
||||
setState(id + ".STOP", true)
|
||||
if (words[6] == "down")
|
||||
if (words[3] == "down")
|
||||
setState(id + ".CLOSE", true)
|
||||
if (words[6] == "button") {
|
||||
let switchOn = true;
|
||||
if (words[5] !== "1")
|
||||
switchOn = false;
|
||||
if (words[3] == "button") {
|
||||
toggleState(id + ".SET") ? true : toggleState(id + ".ON_SET")
|
||||
}
|
||||
if (words[6] == "positionSlider")
|
||||
setState(id + ".SET", parseInt(words[7]))
|
||||
if (words[3] == "positionSlider")
|
||||
setState(id + ".SET", parseInt(words[4]))
|
||||
|
||||
if (words[6] == "brightnessSlider")
|
||||
if (words[3] == "brightnessSlider")
|
||||
if (existsState(id + ".SET"))
|
||||
setState(id + ".SET", parseInt(words[7]));
|
||||
setState(id + ".SET", parseInt(words[4]));
|
||||
else if (existsState(id + ".ACTUAL"))
|
||||
setState(id + ".ACTUAL", parseInt(words[7]));
|
||||
setState(id + ".ACTUAL", parseInt(words[4]));
|
||||
// out_msgs.push({ payload: id, action: "turn_on", domain: "lightBrightness", brightness: parseInt(words[7]) })
|
||||
// if (words[6] == "colorTempSlider")
|
||||
// out_msgs.push({ payload: id, action: "turn_on", domain: "lightTemperature", temperature: parseInt(words[7]) })
|
||||
if (words[1] == "tempUpd") {
|
||||
setState(words[3] + ".SET", parseInt(words[4]) / 10)
|
||||
if (words[3] == "tempUpd") {
|
||||
setState(id + ".SET", parseInt(words[4]) / 10)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -811,4 +808,4 @@ type Config = {
|
||||
pages: (PageThermo | PageEntities | PageGrid)[],
|
||||
button1Page: (PageThermo | PageEntities | PageGrid | null),
|
||||
button2Page: (PageThermo | PageEntities | PageGrid | null),
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user