Compare commits

..

146 Commits

Author SHA1 Message Date
Johannes
f78e03e5ec fixes #82 2022-03-27 11:34:54 +02:00
Johannes
33afd31d8f Check that page is existent #55 2022-03-26 21:06:59 +01:00
joBr99
4dccdfb014 Merge branch 'main' of https://github.com/joBr99/nspanel-lovelace-ui (add nextion2text) 2022-03-26 15:41:05 +00:00
Johannes
2d6b8634bf Merge branch 'main' of https://github.com/joBr99/nspanel-lovelace-ui 2022-03-26 16:40:43 +01:00
Johannes
f11177c5c1 fix updater msg and bump to v1.8.3 2022-03-26 16:40:36 +01:00
joBr99
645aef4cfa fix bump for 1.8.2 (add nextion2text) 2022-03-26 15:26:46 +00:00
Johannes
d5fe05f806 fix bump for 1.8.2 2022-03-26 16:26:25 +01:00
Johannes
64cb8563aa bump to 18 for v1.8.1 2022-03-26 16:21:31 +01:00
Johannes
b91629421e Add name and icon to navigation buttons 2022-03-26 14:15:26 +01:00
Johannes
814cd4a295 implements #78 2022-03-26 13:44:48 +01:00
Johannes
9c2d91e3d9 fix brightness scale 2022-03-26 11:23:19 +01:00
Johannes
028ddb7896 Update pages.py 2022-03-26 11:16:39 +01:00
Johannes
afdc05a239 Add unit to forecast 2022-03-26 11:12:07 +01:00
Johannes
f63f7f7861 fix weather override 2022-03-26 11:08:32 +01:00
Johannes
c9d8f622cb fix weather override 2022-03-26 11:05:25 +01:00
Johannes
e6bb202823 Merge pull request #77 from gerard33/patch-2
Fix weather override
2022-03-26 11:00:01 +01:00
joBr99
7080facb10 added volume buttons to media page (add nextion2text) 2022-03-26 09:56:34 +00:00
Johannes
dcce889b3e added volume buttons to media page 2022-03-26 10:56:14 +01:00
Johannes
2e15482ed1 limit lower bound of color brightness scale 2022-03-26 10:45:22 +01:00
joBr99
adb99623e9 Merge branch 'main' of https://github.com/joBr99/nspanel-lovelace-ui (add nextion2text) 2022-03-26 09:31:24 +00:00
Johannes
27f48819a4 Merge branch 'main' of https://github.com/joBr99/nspanel-lovelace-ui 2022-03-26 10:31:02 +01:00
Johannes
1b52c885f6 added cooldown to sliders on light detail page 2022-03-26 10:30:54 +01:00
Gerard
1f937f75f2 Fix weather override 2022-03-26 10:10:58 +01:00
Johannes
269b983c2a fix current_page on nested page nav 2022-03-26 09:57:37 +01:00
Johannes
29dfa5671d Update README.md 2022-03-26 09:55:15 +01:00
Johannes
74a3860aa0 Merge pull request #76 from gerard33/patch-2
Fix weather forecast
2022-03-26 09:47:39 +01:00
Gerard
1bc69128a9 Fix weather forecast
Show the correct day and icon for day 2
2022-03-26 09:46:26 +01:00
Johannes
4529d5e34e Update README.md 2022-03-26 09:14:49 +01:00
Johannes
0db8983b4a Update README.md 2022-03-26 09:12:21 +01:00
Johannes
dc5e732e04 Update README.md 2022-03-26 09:11:47 +01:00
Johannes
7e7d14a999 add overwrite for forecast's on screensaver 2022-03-26 08:51:21 +01:00
joBr99
7ebc29b4a4 allow empty type as alternative for delete (add nextion2text) 2022-03-26 00:28:30 +00:00
Johannes
8b24f70ae0 allow empty type as alternative for delete 2022-03-26 01:28:10 +01:00
Johannes
edfe79e3c4 implemented navigation within tree 2022-03-26 01:23:41 +01:00
Johannes
93d223141b fixes #72 at least for now 2022-03-25 22:58:34 +01:00
Johannes
853de19a40 fixes #69 2022-03-25 22:34:31 +01:00
Johannes
da2b4565a4 fixes #74 2022-03-25 22:30:00 +01:00
Johannes
18d360d339 Merge branch 'main' of https://github.com/joBr99/nspanel-lovelace-ui 2022-03-25 22:23:36 +01:00
Johannes
af494ada10 fixes #73 2022-03-25 22:23:27 +01:00
Johannes
05d6ee1f5f fixed localization for buttons 2022-03-25 20:58:04 +01:00
Johannes
a4344ade18 send brightness and timeout on startup 2022-03-25 20:50:45 +01:00
Johannes
1470ee5c82 Update localization.py 2022-03-25 20:44:19 +01:00
Johannes
9ae898c30f Update localization.py 2022-03-25 20:43:07 +01:00
Johannes
ec65eebb74 Add localization to thermo page status 2022-03-25 20:39:26 +01:00
Johannes
f7a768de7c Update localization.py 2022-03-25 20:37:40 +01:00
Johannes
e7aa27128c Merge pull request #70 from gerard33/patch-2
Some textual updates
2022-03-25 20:26:51 +01:00
Johannes
11bbad78d1 Update localization.py 2022-03-25 20:25:28 +01:00
Gerard
95e3dfa0ea Some textual updates 2022-03-25 20:23:28 +01:00
Johannes
70557ab748 Merge branch 'main' of https://github.com/joBr99/nspanel-lovelace-ui 2022-03-25 16:57:17 +01:00
Johannes
2ecc7caedd update config 2022-03-25 16:57:11 +01:00
joBr99
6fd8553450 added am/pm on screensaver (add nextion2text) 2022-03-25 15:31:46 +00:00
Johannes
83396c283f added am/pm on screensaver 2022-03-25 16:31:28 +01:00
Johannes
a388efb5b5 Merge pull request #65 from gerard33/patch-3
Rename `getEntityColor` to `get_entity_color`
2022-03-25 15:46:22 +01:00
Johannes
77b05b8079 add min brightness 2022-03-25 15:46:03 +01:00
Johannes
0aae22118b fix ext update 2022-03-25 15:44:12 +01:00
Johannes
4a0a2d3620 merged main into pr 2022-03-25 15:29:40 +01:00
Johannes Braun
cfd0d2cbe8 fixed erron in gen items 2022-03-25 15:11:33 +01:00
Johannes
39575935b6 Merge pull request #68 from joBr99/newConfigFormat
new config parseing
2022-03-25 15:05:09 +01:00
Johannes Braun
9f8900bf78 fixed altert 2022-03-25 14:43:33 +01:00
joBr99
a054f7a7b3 fixed alerts (add nextion2text) 2022-03-25 13:34:26 +00:00
Johannes Braun
464e625eaa fixed alerts 2022-03-25 14:34:06 +01:00
Johannes
505db42064 Merge branch 'main' into newConfigFormat 2022-03-25 14:13:07 +01:00
Johannes Braun
36459145e4 added localization fixed updater 2022-03-25 14:06:40 +01:00
Johannes Braun
32b84bba9f upd docs 2022-03-25 12:11:53 +01:00
Johannes Braun
952398a827 added updater 2022-03-25 12:09:53 +01:00
Johannes Braun
ff106be9cb added state change callback 2022-03-25 00:26:43 +01:00
Johannes Braun
0ab7900ec0 basic navigation and event handling 2022-03-24 22:51:51 +01:00
Johannes
f328107f75 Merge pull request #66 from gerard33/patch-2
Show alert icon instead of shutter icon
2022-03-24 22:37:07 +01:00
Gerard
8deb0fd35e Show alert icon instead of shutter icon
When no mediaplayer is found
2022-03-24 20:20:14 +01:00
Gerard
ed207b6e1a Rename getEntityColor to get_entity_color
I have also set a minimum brightness for the icon otherwise it's almost not visible anymore when the brightness value is very low. What do you think?
2022-03-24 20:14:15 +01:00
Johannes
83e3316458 Merge pull request #63 from gerard33/patch-2
Add language settings
2022-03-24 20:10:45 +01:00
Johannes
53c4ed370b Merge pull request #64 from gerard33/patch-3
Add icon_color for binary sensor
2022-03-24 20:10:10 +01:00
Gerard
050a8e6fd2 Add icon_color for binary sensor
Blue when `off` and yellow when `on`
Not sure about the colors though, but at least it's nice to show different icon colors based on the state
2022-03-24 20:07:36 +01:00
Gerard
907f814ac7 Add config options for buttons and scenes 2022-03-24 19:45:18 +01:00
Gerard
e91c514fe1 Add language settings
Make `PRESS` and `ACTIVATE` text configurable
2022-03-24 19:42:48 +01:00
Johannes
5566f20e47 fixes #61 2022-03-24 17:24:47 +01:00
Johannes
4810679ac5 Update README.md 2022-03-24 16:18:35 +01:00
Johannes
48ed2daa33 Merge pull request #59 closes #58
Add name as config item
2022-03-24 16:14:46 +01:00
Johannes Braun
482cee0552 redesign of config format 2022-03-24 16:12:42 +01:00
Gerard
8813331299 Update README.md 2022-03-24 16:11:06 +01:00
Gerard
abf4868281 Update README.md 2022-03-24 16:10:06 +01:00
Gerard
420905db04 Add name as config item
See FR #58
2022-03-24 16:08:21 +01:00
Johannes
4151aba6ea Update README.md 2022-03-24 14:56:48 +01:00
Johannes
35b15c92e0 Merge pull request #54 from joBr99/patch-buttonPress2
Migrate to new buttonPress cmd format
2022-03-23 22:11:48 +01:00
Johannes
c40906fdfe Merge pull request #56 from britzelpuf/patch-buttonPress2
Update NsPanelTs.ts
2022-03-23 22:11:23 +01:00
britzelpuf
66a65c03e8 Update NsPanelTs.ts
Update HandleMessage function to switch
Fix tempupd
2022-03-23 22:08:25 +01:00
Johannes
20e6f21307 Migrate to new buttonPress cmd format 2022-03-23 11:25:21 +01:00
joBr99
342a97df0f don't show palette icon if color temp isn't supported (add nextion2text) 2022-03-23 10:13:32 +00:00
Johannes Braun
0d343d4919 don't show palette icon if color temp isn't supported 2022-03-23 11:13:06 +01:00
Johannes Braun
0111a9bb21 updated screens picture 2022-03-23 09:37:25 +01:00
Johannes Braun
9ac5c8d748 fix input_boolean icon 2022-03-23 09:37:00 +01:00
Johannes
a725305cd4 Update nspanel-lovelace-ui.py 2022-03-22 23:56:24 +01:00
joBr99
0746221fe7 Merge branch 'main' of https://github.com/joBr99/nspanel-lovelace-ui (add nextion2text) 2022-03-22 22:44:56 +00:00
Johannes
dbfd5e7da4 Merge branch 'main' of https://github.com/joBr99/nspanel-lovelace-ui 2022-03-22 23:44:37 +01:00
Johannes
6d80a53e9c bump version for next release 2022-03-22 23:44:28 +01:00
joBr99
fb93f96a94 update doc (add nextion2text) 2022-03-22 22:35:23 +00:00
Johannes
5dd7dd2c77 update doc 2022-03-22 23:35:01 +01:00
Johannes
f11c870f47 removed old buttonPress format 2022-03-22 23:32:46 +01:00
joBr99
e4727822a9 moved entities page to new button format (add nextion2text) 2022-03-22 22:27:44 +00:00
Johannes
a75e7abc32 moved entities page to new button format 2022-03-22 23:27:25 +01:00
joBr99
d3e308ccaf added input boolean to button command (add nextion2text) 2022-03-22 22:13:43 +00:00
Johannes
f5d785298f added input boolean to button command 2022-03-22 23:13:23 +01:00
Johannes
89cd951e70 migrated some more pages to button v2 cmd format 2022-03-22 23:12:57 +01:00
Johannes
9278258684 cleanup 2022-03-22 22:36:36 +01:00
Johannes
327be4bca2 change override config format 2022-03-22 22:11:08 +01:00
Johannes
27f492ba6a Fix indent 2022-03-22 19:46:53 +01:00
Johannes Braun
c66e595e65 add docs about upcoming icon feature 2022-03-22 19:45:34 +01:00
Johannes Braun
29613e11cc remove import 2022-03-22 19:35:59 +01:00
Johannes Braun
2bf396f7af Merge branch 'main' of github.com:joBr99/nspanel-lovelace-ui 2022-03-22 19:30:23 +01:00
Johannes Braun
9dd34abac4 fix cover and input boolean 2022-03-22 19:30:13 +01:00
Johannes Braun
aae3aa9e62 fix import 2022-03-22 19:29:12 +01:00
joBr99
ff3208d5fa fix (add nextion2text) 2022-03-22 18:21:02 +00:00
Johannes Braun
da3ae4b78d fix 2022-03-22 19:20:39 +01:00
Johannes Braun
256b3e5fe6 Merge branch 'main' of github.com:joBr99/nspanel-lovelace-ui 2022-03-22 19:20:28 +01:00
Johannes Braun
0629f70d30 inital support for alternating icons 2022-03-22 19:00:22 +01:00
Johannes Braun
3ed71d725d upadte readme 2022-03-22 18:27:43 +01:00
joBr99
35e4252a6c added some icons (add nextion2text) 2022-03-22 17:24:49 +00:00
Johannes Braun
9ba4395e6c added some icons 2022-03-22 18:24:26 +01:00
Johannes Braun
909101d726 Merge branch 'main' of github.com:joBr99/nspanel-lovelace-ui 2022-03-22 17:11:15 +01:00
Johannes Braun
f99fa640fe inital support for binary_sensors closes #52 2022-03-22 17:11:00 +01:00
joBr99
d2434ca626 changed timeout page exit on popup pages (add nextion2text) 2022-03-22 15:34:05 +00:00
Johannes Braun
65f4dd3970 changed timeout page exit on popup pages 2022-03-22 16:33:40 +01:00
Johannes Braun
fac86624ae changed behaviour of light detail page 2022-03-22 16:32:02 +01:00
joBr99
277b2ccfd6 . (add nextion2text) 2022-03-22 15:09:16 +00:00
Johannes
cbfd803a65 . 2022-03-22 16:08:53 +01:00
Johannes Braun
ae691d298f fix testing for none 2022-03-22 11:42:22 +01:00
joBr99
ee187ae9cd migrated thero page to new button cmd format (add nextion2text) 2022-03-22 10:37:28 +00:00
Johannes Braun
35e1b4cd66 migrated thero page to new button cmd format 2022-03-22 11:37:04 +01:00
Johannes
f6912ad3ca Update nspanel-lovelace-ui.py 2022-03-22 10:22:32 +01:00
joBr99
71a5830db4 migrate media page to new buttonPress cmd format (add nextion2text) 2022-03-22 09:08:02 +00:00
Johannes
8ada5ad9b9 migrate media page to new buttonPress cmd format 2022-03-22 10:07:39 +01:00
Johannes
9a59b453d2 some more cleanup 2022-03-22 00:17:04 +01:00
Johannes
e52091b274 some more cleanup 2022-03-22 00:09:04 +01:00
Johannes
95de956e77 fix dimmode 2022-03-22 00:02:48 +01:00
Johannes
358a5f8b72 Update nspanel-lovelace-ui.py 2022-03-21 23:50:58 +01:00
Johannes
4292a34c11 fix 2022-03-21 23:48:49 +01:00
Johannes
60c40d56e5 some cleanup 2022-03-21 23:44:17 +01:00
Johannes
97fed8c52c Fixed input boolean, broken ealier :) 2022-03-21 22:12:07 +01:00
Johannes
bf06931a2e Fixed Callbacks for customized items 2022-03-21 21:51:37 +01:00
Johannes
cec43eeb16 Fix input_boolean and switch 2022-03-21 21:38:51 +01:00
Johannes
20c02007fa cleanup get all items 2022-03-21 21:31:42 +01:00
Johannes
2be33d723a Added support for custom icons on cover, light, switch, button and scene 2022-03-21 21:02:26 +01:00
Johannes
ea121ff9a9 Merge pull request #50 from britzelpuf/main
Colors and color interpolation
2022-03-21 20:30:20 +01:00
britzelpuf
601de7b47f added a feature information 2022-03-21 20:05:05 +01:00
britzelpuf
0c33184e5c Update README.md 2022-03-21 20:04:16 +01:00
britzelpuf
979d37281c Cleanup, Restructure
Added colors for missing items and cards
Added color interpolation
2022-03-21 19:57:07 +01:00
36 changed files with 2045 additions and 1735 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
custom: ['https://paypal.me/joBr99']

View File

@@ -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,71 +138,76 @@ 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*,*PageNumber*,*PageHeading*,*entityName*,*buttonId*,*actionName*,*optionalValue*`
`event,*eventName*,*entityName*,*actionName*,*optionalValue*`
`event,pageOpen,0`
`event,buttonPress2,internalNameEntity,up`
`event,buttonPress,1,tHeading,internalNameEntity,1,up`
`event,buttonPress2,internalNameEntity,down`
`event,buttonPress,1,tHeading,internalNameEntity,1,down`
`event,buttonPress2,internalNameEntity,stop`
`event,buttonPress,1,tHeading,internalNameEntity,1,stop`
`event,buttonPress2,internalNameEntity,OnOff,1`
`event,buttonPress,1,tHeading,internalNameEntity,1,OnOff,1`
`event,buttonPress,1,tHeading,internalNameEntity,1,button`
`event,buttonPress2,internalNameEntity,button`
### popupLight Page
`event,pageOpenDetail,popupLight,internalNameEntity`
`event,buttonPress,D,nameEntity,internalNameEntity,1,OnOff,1`
`event,buttonPress2,internalNameEntity,OnOff,1`
`event,buttonPress,D,nameEntity,internalNameEntity,1,brightnessSlider,50`
`event,buttonPress2,internalNameEntity,brightnessSlider,50`
`event,buttonPress,D,nameEntity,internalNameEntity,1,colorTempSlider,50`
`event,buttonPress2,internalNameEntity,colorTempSlider,50`
`event,buttonPress,D,nameEntity,internalNameEntity,1,colorWheel,x|y`
`event,buttonPress2,internalNameEntity,colorWheel,x|y`
### popupShutter Page
`event,pageOpenDetail,popupShutter,internalNameEntity`
`event,buttonPress,D,nameEntity,internalNameEntity,1,positionSlider,50`
`event,buttonPress2,internalNameEntity,positionSlider,50`
### popupNotify Page
`event,buttonPress,D,D,*internalName*,1,notifyAction,yes`
`event,buttonPress2,*internalName*,notifyAction,yes`
`event,buttonPress,D,D,*internalName*,1,notifyAction,no`
`event,buttonPress2,*internalName*,notifyAction,no`
### cardThermo Page
`event,pageOpen,0`
`event,buttonPress2,*entityName*,tempUpd,*temperature*`
`event,tempUpd,*pageNumber*,*entityName*,*temperature*`
`event,buttonPress,*pageNumber*,*tHeading*,*entityName,1,hvac_action,*hvac_action*
`event,buttonPress2,*entityName*,hvac_action,*hvac_action*`
### cardMedia Page
`event,buttonPress,1,tHeading,internalNameEntity,1,media-back`
`event,buttonPress2,internalNameEntity,media-back`
`event,buttonPress,1,tHeading,internalNameEntity,1,media-pause`
`event,buttonPress2,internalNameEntity,media-pause`
`event,buttonPress,1,tHeading,internalNameEntity,1,media-next`
`event,buttonPress2,internalNameEntity,media-next`
`event,buttonPress,1,tHeading,internalNameEntity,1,volumeSlider,75`
`event,buttonPress2,internalNameEntity,volumeSlider,75`
### cardAlarm Page

View File

@@ -37,7 +37,10 @@ icons = [
"solar-power",
"battery-charging-medium",
"battery-medium",
"shield-home"
"shield-home",
"door-open",
"door-closed",
"window-closed"
]
@@ -53,7 +56,7 @@ icon_name_list = []
for icon_name in icons:
#print(icon_name)
icon = next((item for item in icon_metadata if item["name"] == icon_name), None)
if icon == None:
if icon is None:
print(f"WARNING ICON NOT FOUND: {icon_name}")
else:
hex = icon['hex']
@@ -63,7 +66,7 @@ for icon_name in icons:
icon_name_list.append(icon_name)
# write mapping lib for python
with open(os.path.join(__location__, "../../../apps/nspanel-lovelace-ui", "icon_mapper.py"), 'w') as f:
with open(os.path.join(__location__, "../../../apps/nspanel-lovelace-ui", "icon_mapping.py"), 'w') as f:
f.write("icons = {\n")
for idx, val in enumerate(icon_name_list):
f.write(f" '{val}': {idx},\n")

View File

@@ -40,3 +40,6 @@ ID | MD Icon Name | Icon
33 | battery-charging-medium | ![battery-charging-medium](https://raw.githubusercontent.com/Templarian/MaterialDesign-SVG/0aeb4d612644d80d9d1fe242f705f362985de5dc/svg/battery-charging-medium.svg)
34 | battery-medium | ![battery-medium](https://raw.githubusercontent.com/Templarian/MaterialDesign-SVG/0aeb4d612644d80d9d1fe242f705f362985de5dc/svg/battery-medium.svg)
35 | shield-home | ![shield-home](https://raw.githubusercontent.com/Templarian/MaterialDesign-SVG/0aeb4d612644d80d9d1fe242f705f362985de5dc/svg/shield-home.svg)
36 | door-open | ![door-open](https://raw.githubusercontent.com/Templarian/MaterialDesign-SVG/0aeb4d612644d80d9d1fe242f705f362985de5dc/svg/door-open.svg)
37 | door-closed | ![door-closed](https://raw.githubusercontent.com/Templarian/MaterialDesign-SVG/0aeb4d612644d80d9d1fe242f705f362985de5dc/svg/door-closed.svg)
38 | window-closed | ![window-closed](https://raw.githubusercontent.com/Templarian/MaterialDesign-SVG/0aeb4d612644d80d9d1fe242f705f362985de5dc/svg/window-closed.svg)

View File

@@ -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

View File

@@ -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

View File

@@ -541,8 +541,8 @@ Button bDown1
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 379
y coordinate : 76
x coordinate : 377
y coordinate : 75
Width : 50
Height : 50
Effect : load
@@ -567,14 +567,7 @@ Button bDown1
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn1.txt+","
tSend.txt+="1,down"
tSend.txt="event,buttonPress2,"+entn1.txt+",down"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -596,7 +589,7 @@ Button bUp1
Send Component ID : disabled
Opacity : 127
x coordinate : 275
y coordinate : 76
y coordinate : 75
Width : 50
Height : 50
Effect : load
@@ -621,14 +614,7 @@ Button bUp1
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn1.txt+","
tSend.txt+="1,up"
tSend.txt="event,buttonPress2,"+entn1.txt+",up"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -649,8 +635,8 @@ Button bStop1
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 327
y coordinate : 76
x coordinate : 325
y coordinate : 75
Width : 50
Height : 50
Effect : load
@@ -675,14 +661,7 @@ Button bStop1
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn1.txt+","
tSend.txt+="1,stop"
tSend.txt="event,buttonPress2,"+entn1.txt+",stop"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -703,8 +682,8 @@ Button bStop2
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 327
y coordinate : 133
x coordinate : 326
y coordinate : 132
Width : 50
Height : 50
Effect : load
@@ -729,14 +708,7 @@ Button bStop2
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn2.txt+","
tSend.txt+="2,stop"
tSend.txt="event,buttonPress2,"+entn2.txt+",stop"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -757,8 +729,8 @@ Button bDown2
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 379
y coordinate : 133
x coordinate : 377
y coordinate : 131
Width : 50
Height : 50
Effect : load
@@ -783,14 +755,7 @@ Button bDown2
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn2.txt+","
tSend.txt+="2,down"
tSend.txt="event,buttonPress2,"+entn2.txt+",down"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -812,7 +777,7 @@ Button bUp2
Send Component ID : disabled
Opacity : 127
x coordinate : 275
y coordinate : 133
y coordinate : 132
Width : 50
Height : 50
Effect : load
@@ -837,14 +802,7 @@ Button bUp2
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn2.txt+","
tSend.txt+="2,up"
tSend.txt="event,buttonPress2,"+entn2.txt+",up"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -865,8 +823,8 @@ Button bStop3
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 327
y coordinate : 186
x coordinate : 326
y coordinate : 185
Width : 50
Height : 50
Effect : load
@@ -891,14 +849,7 @@ Button bStop3
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn3.txt+","
tSend.txt+="3,stop"
tSend.txt="event,buttonPress2,"+entn3.txt+",stop"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -919,8 +870,8 @@ Button bDown3
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 379
y coordinate : 186
x coordinate : 378
y coordinate : 185
Width : 50
Height : 50
Effect : load
@@ -945,14 +896,7 @@ Button bDown3
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn3.txt+","
tSend.txt+="3,down"
tSend.txt="event,buttonPress2,"+entn3.txt+",down"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -999,14 +943,7 @@ Button bUp3
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn3.txt+","
tSend.txt+="3,up"
tSend.txt="event,buttonPress2,"+entn3.txt+",up"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -1027,8 +964,8 @@ Button bStop4
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 327
y coordinate : 242
x coordinate : 326
y coordinate : 241
Width : 50
Height : 50
Effect : load
@@ -1053,14 +990,7 @@ Button bStop4
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn4.txt+","
tSend.txt+="4,stop"
tSend.txt="event,buttonPress2,"+entn4.txt+",stop"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -1081,8 +1011,8 @@ Button bDown4
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 378
y coordinate : 242
x coordinate : 377
y coordinate : 241
Width : 50
Height : 50
Effect : load
@@ -1107,14 +1037,7 @@ Button bDown4
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn4.txt+","
tSend.txt+="4,down"
tSend.txt="event,buttonPress2,"+entn4.txt+",down"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -1161,14 +1084,7 @@ Button bUp4
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn4.txt+","
tSend.txt+="4,up"
tSend.txt="event,buttonPress2,"+entn4.txt+",up"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -1215,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
@@ -1268,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
@@ -1321,14 +1232,7 @@ Button bText1
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn1.txt+","
tSend.txt+="1,button"
tSend.txt="event,buttonPress2,"+entn1.txt+",button"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -1349,8 +1253,8 @@ Button bText2
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 277
y coordinate : 141
x coordinate : 276
y coordinate : 140
Width : 145
Height : 30
Effect : load
@@ -1375,14 +1279,7 @@ Button bText2
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn2.txt+","
tSend.txt+="1,button"
tSend.txt="event,buttonPress2,"+entn2.txt+",button"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -1429,14 +1326,7 @@ Button bText3
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn3.txt+","
tSend.txt+="1,button"
tSend.txt="event,buttonPress2,"+entn3.txt+",button"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -1483,14 +1373,7 @@ Button bText4
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn4.txt+","
tSend.txt+="1,button"
tSend.txt="event,buttonPress2,"+entn4.txt+",button"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -1511,8 +1394,8 @@ Dual-state Button btOnOff1
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 374
y coordinate : 76
x coordinate : 373
y coordinate : 75
Width : 50
Height : 50
Effect : load
@@ -1533,14 +1416,7 @@ Dual-state Button btOnOff1
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn1.txt+","
tSend.txt+="1,OnOff,"
tSend.txt="event,buttonPress2,"+entn1.txt+",OnOff,"
covx btOnOff1.val,tTmp.txt,0,0
tSend.txt+=tTmp.txt
//send calc crc
@@ -1563,8 +1439,8 @@ Dual-state Button btOnOff2
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 374
y coordinate : 133
x coordinate : 373
y coordinate : 132
Width : 50
Height : 50
Effect : load
@@ -1585,14 +1461,7 @@ Dual-state Button btOnOff2
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn2.txt+","
tSend.txt+="2,OnOff,"
tSend.txt="event,buttonPress2,"+entn2.txt+",OnOff,"
covx btOnOff2.val,tTmp.txt,0,0
tSend.txt+=tTmp.txt
//send calc crc
@@ -1637,14 +1506,7 @@ Dual-state Button btOnOff3
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn3.txt+","
tSend.txt+="3,OnOff,"
tSend.txt="event,buttonPress2,"+entn3.txt+",OnOff,"
covx btOnOff3.val,tTmp.txt,0,0
tSend.txt+=tTmp.txt
//send calc crc
@@ -1689,14 +1551,7 @@ Dual-state Button btOnOff4
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn4.txt+","
tSend.txt+="4,OnOff,"
tSend.txt="event,buttonPress2,"+entn4.txt+",OnOff,"
covx btOnOff4.val,tTmp.txt,0,0
tSend.txt+=tTmp.txt
//send calc crc
@@ -1916,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")
{
@@ -1924,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
@@ -2011,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
@@ -2098,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
@@ -2185,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

View File

@@ -172,9 +172,9 @@ Text tEntity1
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 33
x coordinate : 6
y coordinate : 155
Width : 120
Width : 140
Height : 30
Effect : load
Effect Priority : 0
@@ -322,9 +322,9 @@ Text tEntity2
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 167
x coordinate : 157
y coordinate : 155
Width : 120
Width : 140
Height : 30
Effect : load
Effect Priority : 0
@@ -352,9 +352,9 @@ Text tEntity3
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 296
x coordinate : 307
y coordinate : 155
Width : 120
Width : 140
Height : 30
Effect : load
Effect Priority : 0
@@ -382,9 +382,9 @@ Text tEntity4
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 33
x coordinate : 6
y coordinate : 274
Width : 120
Width : 140
Height : 30
Effect : load
Effect Priority : 0
@@ -412,9 +412,9 @@ Text tEntity5
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 167
x coordinate : 157
y coordinate : 274
Width : 120
Width : 140
Height : 30
Effect : load
Effect Priority : 0
@@ -442,9 +442,9 @@ Text tEntity6
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 296
x coordinate : 307
y coordinate : 274
Width : 120
Width : 140
Height : 30
Effect : load
Effect Priority : 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
@@ -595,7 +583,7 @@ Button bEntity1
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 53
x coordinate : 36
y coordinate : 75
Width : 80
Height : 80
@@ -632,14 +620,7 @@ Button bEntity1
Touch Release Event
tmLP.en=0
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn1.txt+","
tSend.txt+="1,button"
tSend.txt="event,buttonPress2,"+entn1.txt+",button"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -697,14 +678,7 @@ Button bEntity2
Touch Release Event
tmLP.en=0
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn2.txt+","
tSend.txt+="1,button"
tSend.txt="event,buttonPress2,"+entn2.txt+",button"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -725,7 +699,7 @@ Button bEntity3
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 316
x coordinate : 337
y coordinate : 75
Width : 80
Height : 80
@@ -762,14 +736,7 @@ Button bEntity3
Touch Release Event
tmLP.en=0
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn3.txt+","
tSend.txt+="1,button"
tSend.txt="event,buttonPress2,"+entn3.txt+",button"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -790,7 +757,7 @@ Button bEntity4
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 53
x coordinate : 36
y coordinate : 194
Width : 80
Height : 80
@@ -827,14 +794,7 @@ Button bEntity4
Touch Release Event
tmLP.en=0
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn4.txt+","
tSend.txt+="1,button"
tSend.txt="event,buttonPress2,"+entn4.txt+",button"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -892,14 +852,7 @@ Button bEntity5
Touch Release Event
tmLP.en=0
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn5.txt+","
tSend.txt+="1,button"
tSend.txt="event,buttonPress2,"+entn5.txt+",button"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -920,7 +873,7 @@ Button bEntity6
Disable release event after dragging: 0
Send Component ID : disabled
Opacity : 127
x coordinate : 316
x coordinate : 337
y coordinate : 194
Width : 80
Height : 80
@@ -957,14 +910,7 @@ Button bEntity6
Touch Release Event
tmLP.en=0
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn6.txt+","
tSend.txt+="1,button"
tSend.txt="event,buttonPress2,"+entn6.txt+",button"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC

View File

@@ -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
@@ -314,14 +283,11 @@ Text t2
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
// event,buttonPress2,entn,media-next
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt="event,buttonPress2,"
tSend.txt+=entn.txt+","
tSend.txt+="1,media-next"
tSend.txt+="media-next"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -336,7 +302,7 @@ Text t2
Text tPlayPause
Attributes
ID : 15
ID : 14
Scope : local
Dragging : 0
Disable release event after dragging: 0
@@ -366,14 +332,11 @@ Text tPlayPause
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
// event,buttonPress2,entn,media-pause
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt="event,buttonPress2,"
tSend.txt+=entn.txt+","
tSend.txt+="1,media-pause"
tSend.txt+="media-pause"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -388,7 +351,7 @@ Text tPlayPause
Text t0
Attributes
ID : 16
ID : 15
Scope : local
Dragging : 0
Disable release event after dragging: 0
@@ -418,14 +381,11 @@ Text t0
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
// event,buttonPress2,entn,media-back
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt="event,buttonPress2,"
tSend.txt+=entn.txt+","
tSend.txt+="1,media-back"
tSend.txt+="media-back"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -440,7 +400,7 @@ Text t0
Text tIcon
Attributes
ID : 18
ID : 17
Scope : local
Dragging : 0
Disable release event after dragging: 0
@@ -468,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
@@ -487,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
@@ -512,14 +572,11 @@ Slider hVolume
Events
Touch Release Event
// event,buttonPress,D,nameEntity,internalNameEntity,1,positionSlider,50
tmCooldown.en=1
tmCooldown.tim=100
// event,buttonPress2,internalNameEntity,volumeSlider,50
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,volumeSlider,"
tSend.txt="event,buttonPress2,"+entn.txt+",volumeSlider,"
covx hVolume.val,tTmp.txt,0,0
tSend.txt+=tTmp.txt
//send calc crc
@@ -568,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
@@ -622,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
@@ -644,7 +687,7 @@ Button bPrev
Timer tmSerial
Attributes
ID : 7
ID : 6
Scope : local
Period (ms): 50
Enabled : yes
@@ -805,7 +848,7 @@ Timer tmSerial
Timer tmSleep
Attributes
ID : 20
ID : 19
Scope : local
Period (ms): 1000
Enabled : yes
@@ -823,9 +866,20 @@ Timer tmSleep
}
}
TouchCap tc0
Timer tmCooldown
Attributes
ID : 21
Scope : local
Period (ms): 100
Enabled : no
Events
Timer Event
tmCooldown.en=0
TouchCap tc0
Attributes
ID : 20
Scope: local
Value: 0

View File

@@ -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
@@ -613,13 +606,11 @@ Button bDown
{
xTempDest.val=xTempDest.val-xTempStep.val
}
// temp chane event
// event,tempUpd,pageNumber,entityName,temperature
// event,buttonPress2,entn,tempUpd,X
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,tempUpd,"+tTmp.txt+","
tSend.txt="event,buttonPress2,"
tSend.txt+=entn.txt+","
tSend.txt+="tempUpd,"
//convert temperature
covx xTempDest.val,tTmp.txt,0,0
tSend.txt+=tTmp.txt
@@ -669,11 +660,11 @@ Button bUp
{
xTempDest.val=xTempDest.val+xTempStep.val
}
// temp chane event
// event,tempUpd,pageNumber,entityName,temperature
// event,buttonPress2,entn,tempUpd,X
//craft command
tSend.txt="event,tempUpd,"+tTmp.txt+","
tSend.txt="event,buttonPress2,"
tSend.txt+=entn.txt+","
tSend.txt+="tempUpd,"
//convert temperature
covx xTempDest.val,tTmp.txt,0,0
tSend.txt+=tTmp.txt
@@ -723,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
@@ -778,12 +762,8 @@ Dual-state Button bt0
Events
Touch Press Event
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,hvac_action,"+va0.txt
tSend.txt="event,buttonPress2,"+entn.txt
tSend.txt+=",hvac_action,"+va0.txt
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -831,12 +811,8 @@ Dual-state Button bt1
Events
Touch Press Event
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,hvac_action,"+va1.txt
tSend.txt="event,buttonPress2,"+entn.txt
tSend.txt+=",hvac_action,"+va1.txt
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -884,12 +860,8 @@ Dual-state Button bt2
Events
Touch Press Event
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,hvac_action,"+va2.txt
tSend.txt="event,buttonPress2,"+entn.txt
tSend.txt+=",hvac_action,"+va2.txt
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -937,12 +909,8 @@ Dual-state Button bt3
Events
Touch Press Event
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,hvac_action,"+va3.txt
tSend.txt="event,buttonPress2,"+entn.txt
tSend.txt+=",hvac_action,"+va3.txt
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -990,12 +958,8 @@ Dual-state Button bt4
Events
Touch Press Event
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,hvac_action,"+va4.txt
tSend.txt="event,buttonPress2,"+entn.txt
tSend.txt+=",hvac_action,"+va4.txt
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -1043,12 +1007,8 @@ Dual-state Button bt5
Events
Touch Press Event
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,hvac_action,"+va5.txt
tSend.txt="event,buttonPress2,"+entn.txt
tSend.txt+=",hvac_action,"+va5.txt
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -1096,12 +1056,8 @@ Dual-state Button bt6
Events
Touch Press Event
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,hvac_action,"+va6.txt
tSend.txt="event,buttonPress2,"+entn.txt
tSend.txt+=",hvac_action,"+va6.txt
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -1149,12 +1105,8 @@ Dual-state Button bt7
Events
Touch Press Event
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,hvac_action,"+va7.txt
tSend.txt="event,buttonPress2,"+entn.txt
tSend.txt+=",hvac_action,"+va7.txt
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -1202,12 +1154,8 @@ Dual-state Button bt8
Events
Touch Press Event
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tHeading.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,hvac_action,"+va8.txt
tSend.txt="event,buttonPress2,"+entn.txt
tSend.txt+=",hvac_action,"+va8.txt
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC

View File

@@ -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
cardMedia
22 Component(s)
210 Line(s) of event code
116 Unique line(s) of event code
popupShutter
19 Component(s)
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
pageSwipeTest
18 Component(s)
62 Line(s) of event code
44 Unique line(s) of event code
popupLight
27 Component(s)
338 Line(s) of event code
170 Unique line(s) of event code
pageTest
14 Component(s)
14 Line(s) of event code
14 Unique line(s) of event code
pageStartup
19 Component(s)
146 Line(s) of event code
111 Unique line(s) of event code
popupShutter
19 Component(s)
194 Line(s) of event code
106 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)
438 Line(s) of event code
221 Unique line(s) of event code
cardEntities
54 Component(s)
808 Line(s) of event code
320 Unique line(s) of event code
cardGrid
39 Component(s)
406 Line(s) of event code
224 Unique line(s) of event code
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)
350 Component(s)
3292 Line(s) of event code
842 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

View File

@@ -72,7 +72,7 @@ Text tIcons
Horizontal Alignment : left
Vertical Alignment : top
Input Type : character
Text : 
Text : 
Max. Text Size : 120
Word wrap : enabled
Horizontal Spacing : 0

View File

@@ -402,7 +402,7 @@ Text tVersion
Horizontal Alignment : center
Vertical Alignment : center
Input Type : character
Text : 15
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

View File

@@ -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

View File

@@ -78,15 +78,9 @@ Variable (int32) mode
Scope: local
Value: 1
Variable (int32) mode_bright
Attributes
ID : 24
Scope: local
Value: 0
Variable (int32) mode_temp
Attributes
ID : 25
ID : 24
Scope: local
Value: 0
@@ -392,7 +386,7 @@ Text tId
Text t0
Attributes
ID : 26
ID : 25
Scope : local
Dragging : 0
Disable release event after dragging: 0
@@ -456,12 +450,8 @@ Picture pColorWheel
Events
Touch Press Event
// event,buttonPress,D,nameEntity,internalNameEntity,1,brightnessSlider,50
//craft command
tSend.txt="event,buttonPress,D,"
tSend.txt+=tEntity.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,colorWheel,"
tSend.txt="event,buttonPress2,"+entn.txt+",colorWheel,"
sys0=tch0
sys0=sys0-pColorWheel.x
covx sys0,tTmp.txt,0,0
@@ -509,34 +499,11 @@ Slider hBrightness
Lower range limit : 0
Events
Touch Press Event
// event,buttonPress,D,nameEntity,internalNameEntity,1,brightnessSlider,50
//craft command
tSend.txt="event,buttonPress,D,"
tSend.txt+=tEntity.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,brightnessSlider,"
covx hBrightness.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
Touch Release Event
// event,buttonPress,D,nameEntity,internalNameEntity,1,brightnessSlider,50
tmCooldown.en=1
tmCooldown.tim=200
//craft command
tSend.txt="event,buttonPress,D,"
tSend.txt+=tEntity.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,brightnessSlider,"
tSend.txt="event,buttonPress2,"+entn.txt+",brightnessSlider,"
covx hBrightness.val,tTmp.txt,0,0
tSend.txt+=tTmp.txt
//send calc crc
@@ -577,34 +544,11 @@ Slider hTempSlider
Lower range limit : 0
Events
Touch Press Event
// event,buttonPress,D,nameEntity,internalNameEntity,1,colorTempSlider,50
//craft command
tSend.txt="event,buttonPress,D,"
tSend.txt+=tEntity.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,colorTempSlider,"
covx hTempSlider.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
Touch Release Event
// event,buttonPress,D,nameEntity,internalNameEntity,1,colorTempSlider,50
tmCooldown.en=1
tmCooldown.tim=200
//craft command
tSend.txt="event,buttonPress,D,"
tSend.txt+=tEntity.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,colorTempSlider,"
tSend.txt="event,buttonPress2,"+entn.txt+",colorTempSlider,"
covx hTempSlider.val,tTmp.txt,0,0
tSend.txt+=tTmp.txt
//send calc crc
@@ -653,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
@@ -710,11 +649,6 @@ Button bColor
mode.val=0
//enable color wheel
vis pColorWheel,1
//vis t0,1
//disable brightness
//vis t1,0
//vis t2,0
//vis hBrightness,0
//disable color temp
vis t4,0
vis t3,0
@@ -724,14 +658,6 @@ Button bColor
mode.val=1
//disable color wheel
vis pColorWheel,0
//vis t0,0
//if(mode_bright.val==1)
//{
// //enable brightness
// vis t1,1
// vis t2,1
// vis hBrightness,1
//}
if(mode_temp.val==1)
{
//enable color temp
@@ -771,12 +697,8 @@ Dual-state Button btOnOff1
Events
Touch Press Event
// event,buttonPress,D,nameEntity,internalNameEntity,1,OnOff,1
//craft command
tSend.txt="event,buttonPress,D,"
tSend.txt+=tEntity.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,OnOff,"
tSend.txt="event,buttonPress2,"+entn.txt+","+"OnOff,"
covx btOnOff1.val,tTmp.txt,0,0
tSend.txt+=tTmp.txt
//send calc crc
@@ -808,6 +730,7 @@ Timer tmSleep
screensaver.originPage.val=dp
sleepValue=0
click b0,1
click b0,0
}
}
@@ -885,46 +808,81 @@ Timer tmSerial
vis t1,0
vis t2,0
vis hBrightness,0
mode_bright.val=0
}else
{
vis t1,1
vis t2,1
vis hBrightness,1
mode_bright.val=1
covx tTmp.txt,sys0,0,0
if(tmCooldown.en==0)
{
hBrightness.val=sys0
}
if(mode.val==1)
{
}
// get ColorTemp value
spstr strCommand.txt,tTmp.txt,",",5
if(tTmp.txt=="unknown")
if(tTmp.txt=="disable")
{
click bColor,1
mode_temp.val=1
}else if(tTmp.txt=="disable")
{
vis t4,0
vis t3,0
vis hTempSlider,0
vis t3,0
vis t4,0
mode_temp.val=0
// mode == 1 is temp controls currently shown
if(mode.val==1)
{
// switch mode
click bColor,1
click bColor,0
}
}else if(tTmp.txt=="unknown") // temp supported, but not in color mode
{
mode_temp.val=1
// mode == 1 is temp controls currently shown
if(mode.val==1)
{
// switch mode
click bColor,1
click bColor,0
}
}else
{
vis t4,1
vis t3,1
vis hTempSlider,1
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)
{
// switch mode
click bColor,1
click bColor,0
}
// get ColorWheel value
}
// get Color value
spstr strCommand.txt,tTmp.txt,",",6
if(tTmp.txt!="disable")
// disable -> isn't supported
if(tTmp.txt=="disable")
{
vis t0,0
vis bColor,0
vis pColorWheel,0
}else
{
vis t0,1
//only enable bColor button if color temp is also supported
if(mode_temp.val==1)
{
vis bColor,1
vis t0,1
}
if(mode.val==0)
{
vis pColorWheel,1
}
}
}
if(tInstruction.txt=="time")
@@ -949,10 +907,6 @@ Timer tmSerial
spstr strCommand.txt,tTmp.txt,",",1
covx tTmp.txt,sleepTimeout,0,0
}
// end of user code
udelete payloadLength-1
bufferPos=0
}
if(tInstruction.txt=="pageType")
{
//command format pageType,specialPageName
@@ -990,23 +944,29 @@ Timer tmSerial
page cardAlarm
}
}
// end of user code
udelete payloadLength-1
bufferPos=0
}
}
}
}
// next character
bufferPos++
}
if(bufferPos==usize)
{
// copy whole buffer to t1.txt, for debugging
//ucopy t2.txt,0,usize,0
// ucopy n2.val,0,usize,0
// clear whole buffer
//code_c
//bufferPos=0
}
}
Timer tmCooldown
Attributes
ID : 26
Scope : local
Period (ms): 100
Enabled : no
Events
Timer Event
tmCooldown.en=0
TouchCap tc0
Attributes
ID : 14

View File

@@ -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
@@ -332,7 +327,7 @@ Button b2
Touch Press Event
sleepTimeout=vaOldSleepT.val
//craft command
tSend.txt="event,buttonPress,D,D,"+entn.txt+",1,notifyAction,yes"
tSend.txt="event,buttonPress2,"+entn.txt+",notifyAction,yes"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -381,7 +376,7 @@ Button b1
Touch Press Event
sleepTimeout=vaOldSleepT.val
//craft command
tSend.txt="event,buttonPress,D,D,"+entn.txt+",1,notifyAction,no"
tSend.txt="event,buttonPress2,"+entn.txt+",notifyAction,no"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -411,6 +406,7 @@ Timer tmSleep
screensaver.originPage.val=dp
sleepValue=0
click b0,1
click b0,0
}
}

View File

@@ -313,12 +313,9 @@ Slider hPosition
Events
Touch Release Event
// event,buttonPress,D,nameEntity,internalNameEntity,1,positionSlider,50
// event,buttonPress2,internalNameEntity,positionSlider,50
//craft command
tSend.txt="event,buttonPress,D,"
tSend.txt+=tEntity.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,positionSlider,"
tSend.txt="event,buttonPress2,"+entn.txt+",positionSlider,"
covx hPosition.val,tTmp.txt,0,0
tSend.txt+=tTmp.txt
//send calc crc
@@ -367,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
@@ -419,14 +411,7 @@ Button bDown1
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tEntity.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,down"
tSend.txt="event,buttonPress2,"+entn.txt+",down"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -473,14 +458,7 @@ Button bStop1
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tEntity.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,stop"
tSend.txt="event,buttonPress2,"+entn.txt+",stop"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -527,14 +505,7 @@ Button bUp1
Events
Touch Press Event
// event,1,tHeading,tEntityName,1,up
//craft command
// convert pageNumber and write to tTmp
covx nPage,tTmp.txt,0,0
tSend.txt="event,buttonPress,"+tTmp.txt+","
tSend.txt+=tEntity.txt+","
tSend.txt+=entn.txt+","
tSend.txt+="1,up"
tSend.txt="event,buttonPress2,"+entn.txt+",up"
//send calc crc
btlen tSend.txt,sys0
crcrest 1,0xffff // reset CRC
@@ -564,6 +535,7 @@ Timer tmSleep
screensaver.originPage.val=dp
sleepValue=0
click b0,1
click b0,0
}
}

View File

@@ -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
@@ -200,7 +205,7 @@ Text tTime
Vertical Alignment : center
Input Type : character
Text : 21:32
Max. Text Size : 10
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

Binary file not shown.

Binary file not shown.

View File

@@ -9,7 +9,7 @@ NsPanel Lovelace UI is a Firmware for the nextion screen inside of NSPanel in th
## Features
- Entities Page with support for cover, switch, input_boolean, sensor, button, scenes, input_button and light
- Entities Page with support for cover, switch, input_boolean, binary_sensor, sensor, button, scenes, input_button and light
- Grid Page with support for cover, switch, input_boolean, button, scenes, and light
- Detail Pages for Lights (Brightness, Temperature and Color of the Light) and for Covers (Position)
- Thermostat Page
@@ -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,6 +308,55 @@ key | optional | type | default | description
`class` | False | string | | The name of the Class.
`config` | False | complex | | Config/Mapping between Homeassistant and your NsPanel
Possible configuration values for config key:
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
- type: cardGrid
heading: Lights
items:
- light.wled
- light.schreibtischlampe
- switch.deckenbeleuchtung_hinten:
icon: lightbulb
name: Lampe
- delete
- delete
- type: cardMedia
```
## How to update
Updating involves mainly already descriped steps from installation, so this is a short summary.

View File

@@ -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
@@ -56,4 +56,3 @@ nspanel:
- type: cardMedia
heading: Exampe Media
item: media_player.spotify_user

View 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

View 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)

View File

@@ -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]

View File

@@ -35,6 +35,9 @@ icons = {
'battery-charging-medium': 33,
'battery-medium': 34,
'shield-home': 35,
'door-open': 36,
'door-closed': 37,
'window-closed': 38,
}
def get_icon_id(ma_name):

View File

@@ -0,0 +1,64 @@
from icon_mapping import get_icon_id
weather_mapping = {
'clear-night': 'weather-night',
'cloudy': 'weather-cloudy',
'exceptional': 'alert-circle-outline',
'fog': 'weather-fog',
'hail': 'weather-hail',
'lightning': 'weather-lightning',
'lightning-rainy': 'weather-lightning-rainy',
'partlycloudy': 'weather-partly-cloudy',
'pouring': 'weather-pouring',
'rainy': 'weather-rainy',
'snowy': 'weather-snowy',
'snowy-rainy': 'weather-snowy-rainy',
'sunny': 'weather-sunny',
'windy': 'weather-windy',
'windy-variant': 'weather-windy-variant'
}
sensor_mapping_on = {
"door": "door-open",
}
sensor_mapping_off = {
"door": "door-closed",
}
sensor_mapping = {
"temperature": "thermometer",
"power": "flash"
}
def map_to_mdi_name(ha_type, state=None, device_class=None):
if ha_type == "weather":
return weather_mapping[state] if state in weather_mapping else "alert-circle-outline"
if ha_type == "button":
return "gesture-tap-button"
if ha_type == "scene":
return "palette"
if ha_type == "switch":
return "flash"
if ha_type == "light":
return "lightbulb"
if ha_type == "input_boolean":
return "check-circle-outline" if state == "on" else "close-circle-outline"
if ha_type == "cover":
return "window-open" if state == "open" else "window-closed"
elif ha_type == "sensor":
if state == "on":
return sensor_mapping_on[device_class] if device_class in sensor_mapping_on else "alert-circle-outline"
elif state == "off":
return sensor_mapping_off[device_class] if device_class in sensor_mapping_off else "alert-circle-outline"
else:
return sensor_mapping[device_class] if device_class in sensor_mapping else "alert-circle-outline"
return "alert-circle-outline"
def get_icon_id_ha(ha_name, state=None, device_class=None, overwrite=None):
if overwrite is not None:
return get_icon_id(overwrite)
return get_icon_id(map_to_mdi_name(ha_name, state, device_class))

View 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)

View 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])

View 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")

View 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)

View File

@@ -1,684 +1,79 @@
import json
import datetime
import hassapi as hass
from helper import scale, pos_to_color, rgb_dec565, rgb_brightness
from icon_mapper import get_icon_id
import logging
import traceback
import hassapi as hass
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)
# check Babel
import importlib
babel_spec = importlib.util.find_spec("babel")
if babel_spec is not None:
import babel.dates
class NsPanelLovelaceUIManager(hass.Hass):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._redirect_logging()
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))
# We want to grab all the logs, AppDaemon will
# then care about filtering those we asked for
rlogger.setLevel(logging.DEBUG)
def initialize(self):
LOGGER.info('Starting')
mqtt_api = self._mqtt_api = self.get_plugin_api("MQTT")
cfg = self._cfg = LuiBackendConfig(self.args["config"])
data = self.args["config"]
NsPanelLovelaceUI(self, data)
class Updater:
def __init__(self, nsplui, mode):
self.desired_display_firmware_version = 15
self.desired_display_firmware_url = "http://nspanel.pky.eu/lovelace-ui/github/nspanel-1f7a3d5.tft"
self.desired_tasmota_driver_version = 3
self.desired_tasmota_driver_url = "https://raw.githubusercontent.com/joBr99/nspanel-lovelace-ui/main/tasmota/autoexec.be"
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 NsPanelLovelaceUI:
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')
if "updateMode" in self.config:
update_mode = self.config["updateMode"]
else:
update_mode = "auto-notify"
self.updater = Updater(self, update_mode)
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
self.mqtt.mqtt_publish(self.config["panelSendTopic"].replace("CustomSend", "GetDriverVersion"), "x")
mqtt_api.mqtt_publish(topic_send.replace("CustomSend", "GetDriverVersion"), "x")
# send panel back to startup page on restart of this script
self.send_mqtt_msg("pageType,pageStartup")
controller = LuiController(self, cfg, send_mqtt_msg)
# Setup time callback
time = datetime.time(0, 0, 0)
self.api.run_minutely(self.update_time, time)
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"
# 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("")
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)
# 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
# send screensaver brightness in case config has changed
self.update_screensaver_brightness(kwargs={"value": self.current_screensaver_brightness})
# register callbacks
self.register_callbacks()
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)
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)
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)
if msg[1] == "pageOpenDetail":
self.api.log("Received pageOpenDetail command", level="DEBUG")
self.generate_detail_page(msg[2], msg[3])
if msg[1] == "tempUpd":
self.api.log("Received tempUpd command", level="DEBUG")
temp = int(msg[4])/10
self.api.get_entity(msg[3]).call_service("set_temperature", temperature=temp)
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"
# this maps possible states from ha to material design icon names
weathericons = {
'clear-night': 'weather-night',
'cloudy': 'weather-cloudy',
'exceptional': 'alert-circle-outline',
'fog': 'weather-fog',
'hail': 'weather-hail',
'lightning': 'weather-lightning',
'lightning-rainy': 'weather-lightning-rainy',
'partlycloudy': 'weather-partly-cloudy',
'pouring': 'weather-pouring',
'rainy': 'weather-rainy',
'snowy': 'weather-snowy',
'snowy-rainy': 'weather-snowy-rainy',
'sunny': 'weather-sunny',
'windy': 'weather-windy',
'windy-variant': 'weather-windy-variant'
}
i1 = get_icon_id(weathericons[we.attributes.forecast[0]['condition']])
u1 = we.attributes.forecast[0]['temperature']
i2 = get_icon_id(weathericons[we.attributes.forecast[1]['condition']])
u2 = we.attributes.forecast[1]['temperature']
o1 = we.attributes.forecast[0]['datetime']
o1 = datetime.datetime.fromisoformat(o1)
o2 = we.attributes.forecast[1]['datetime']
o2 = datetime.datetime.fromisoformat(o2)
global babel_spec
if babel_spec is not None:
o1 = babel.dates.format_date(o1, "E", locale=self.config["locale"])
o2 = babel.dates.format_date(o2, "E", locale=self.config["locale"])
else:
o1 = o1.strftime("%a")
o2 = o2.strftime("%a")
self.send_mqtt_msg(f"weatherUpdate,?{get_icon_id(weathericons[we.state])}?{we.attributes.temperature}{unit}?{26}?{we.attributes.humidity} %?{o1}?{i1}?{u1}?{o2}?{i2}?{u2}")
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")
if entity_id.startswith('light') or entity_id.startswith('switch'):
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)
def check_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"])
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 = []
for page in self.config["pages"]:
if "item" in page:
items.append(page["item"])
if "items" in page:
items.extend(page["items"])
for item in items:
if not self.api.entity_exists(item):
continue
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"]
if entity in items:
self.api.log(f"State change on current page for {entity}", level="DEBUG")
# send update of the page
command = self.generate_entities_page(items)
self.send_mqtt_msg(command)
if(entity.startswith("cover")):
self.generate_detail_page("popupShutter", entity)
if(entity.startswith("light")):
self.generate_detail_page("popupLight", entity)
return
if page_type == "cardThermo" or page_type == "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.send_mqtt_msg(self.generate_thermo_page(entity))
return
if page_type == "cardMedia":
self.send_mqtt_msg(self.generate_media_page(entity))
return
return
def generate_page(self, page_number):
# get type of current 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")
if page_type == "cardEntities":
# Send page type
self.send_mqtt_msg(f"pageType,{page_type}")
# Set Heading of Page
self.send_mqtt_msg(f"entityUpdHeading,{self.config['pages'][self.current_page_nr]['heading']}")
command = self.generate_entities_page(self.config["pages"][self.current_page_nr]["items"])
self.send_mqtt_msg(command)
if page_type == "cardGrid":
# Send page type
self.send_mqtt_msg(f"pageType,{page_type}")
# Set Heading of Page
self.send_mqtt_msg(f"entityUpdHeading,{self.config['pages'][self.current_page_nr]['heading']}")
command = self.generate_entities_page(self.config["pages"][self.current_page_nr]["items"])
self.send_mqtt_msg(command)
if page_type == "cardThermo":
# Send page type
self.send_mqtt_msg(f"pageType,{page_type}")
command = self.generate_thermo_page(self.config["pages"][self.current_page_nr]["item"])
self.send_mqtt_msg(command)
if page_type == "cardMedia":
# Send page type
self.send_mqtt_msg("pageType,{0}".format(page_type))
command = self.generate_media_page(self.config["pages"][self.current_page_nr]["item"])
self.send_mqtt_msg(command)
def generate_entities_item(self, item):
# 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":
return f",shutter,{item},{get_icon_id('window-open')},17299,{name},"
if item_type == "light":
switch_val = 1 if entity.state == "on" else 0
icon_color = self.getEntityColor(entity)
return f",{item_type},{item},{get_icon_id('lightbulb')},{icon_color},{name},{switch_val}"
if item_type == "switch" or item_type == "input_boolean":
switch_val = 1 if entity.state == "on" else 0
icon_id = get_icon_id("flash")
icon_color = self.getEntityColor(entity)
if item_type == "input_boolean":
if switch_val == 1:
icon_id = get_icon_id("check-circle-outline")
else:
icon_id = get_icon_id("close-circle-outline")
return f",switch,{item},{icon_id},{icon_color},{name},{switch_val}"
if item_type == "sensor":
# maps ha device classes to material design icons
icon_mapping = {
"temperature": "thermometer",
"power": "flash"
}
if "device_class" in entity.attributes:
if entity.attributes.device_class in icon_mapping:
icon_id = icon_mapping[entity.attributes.device_class]
else:
self.api.log("No Icon found for device_class: %s. Please open a issue on github to report the missing mapping.", entity.attributes.device_class)
icon_id = get_icon_id('alert-circle-outline')
else:
icon_id = get_icon_id('alert-circle-outline')
unit_of_measurement = ""
if "unit_of_measurement" in entity.attributes:
unit_of_measurement = entity.attributes.unit_of_measurement
value = entity.state + " " + unit_of_measurement
return f",text,{item},{icon_id},17299,{name},{value}"
if item_type == "button" or item_type == "input_button":
return f",button,{item},{get_icon_id('gesture-tap-button')},17299,{name},PRESS"
if item_type == "scene":
return f",button,{item},{get_icon_id('palette')},17299,{name},ACTIVATE"
def generate_entities_page(self, items):
# Get items and construct cmd string
command = "entityUpd"
for item in items:
command += self.generate_entities_item(item)
return 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):
return f"entityUpd,{item},Not found,220,220,Not found,150,300,5"
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
return f"entityUpd,{item},{heading},{current_temp},{dest_temp},{status},{min_temp},{max_temp},{step_temp}{icon_res}"
def generate_media_page(self, item):
if not self.api.entity_exists(item):
return f"entityUpd,|{item}|Not found|{get_icon_id('alert-circle-outline')}|Please check your|apps.yaml in AppDaemon|50|11"
entity = self.api.get_entity(item)
heading = entity.attributes.friendly_name
icon = 0
title = ""
author = ""
volume = 0
if "media_content_type" in entity.attributes:
if entity.attributes.media_content_type == "music":
icon = get_icon_id("music")
if "media_title" in entity.attributes:
title = entity.attributes.media_title
if "media_artist" in entity.attributes:
author = entity.attributes.media_artist
if "volume_level" in entity.attributes:
volume = int(entity.attributes.volume_level*100)
if entity.state == "playing":
iconplaypause = get_icon_id("pause")
else:
iconplaypause = get_icon_id("play")
return f"entityUpd,|{item}|{heading}|{icon}|{title}|{author}|{volume}|{iconplaypause}"
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_detail_page(self, page_type, entity):
if page_type == "popupLight":
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}")
if page_type == "popupShutter":
pos = self.api.get_entity(entity).attributes.current_position
# 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")
topic_recv = cfg.get("panelRecvTopic")
LuiMqttListener(mqtt_api, topic_recv, controller, updater)
LOGGER.info('Started')

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 KiB

After

Width:  |  Height:  |  Size: 269 KiB

View File

@@ -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

View File

@@ -1,101 +1,45 @@
type RGB = {
red: number,
green: number,
blue: number
};
const Months = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"];
const Days = ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"];
const Red: RGB = { red: 255, green: 0, blue: 0 };
const White: RGB = { red: 255, green: 255, blue: 255 };
const Blue: RGB = { red: 68, green: 115, blue: 158 };
const Off: RGB = { red: 68, green: 115, blue: 158 };
const On: RGB = { red: 253, green: 216, blue: 53 };
const BatteryFull: RGB = { red: 96, green: 176, blue: 62 }
const BatteryEmpty: RGB = { red: 179, green: 45, blue: 25 }
type Payload = {
payload: string;
};
type Page = {
type: string,
heading: string
};
interface PageEntities extends Page {
type: "cardEntities",
items: PageItem[]
};
interface PageGrid extends Page {
type: "cardGrid",
items: PageItem[]
};
interface PageThermo extends Page {
type: "cardThermo",
item: PageItem
};
type Config = {
panelRecvTopic: string,
panelSendTopic: string,
timeoutScreensaver: number,
dimmode: number,
//brightnessScreensaver:
locale: string,
timeFormat: string,
dateFormat: string,
weatherEntity: string | null,
temperatureUnit: string,
leftEntity: string,
leftEntityIcon: number,
leftEntityText: string,
leftEntityUnitText: string | null,
rightEntity: string,
rightEntityIcon: number,
rightEntityText: string,
rightEntityUnitText: string | null,
defaultColor: RGB,
gridPageOnColor: RGB,
gridPageOffColor: RGB,
pages: (PageThermo | PageEntities | PageGrid)[],
button1Page: (PageThermo | PageEntities | PageGrid | null),
button2Page: (PageThermo | PageEntities | PageGrid | null),
};
type PageItem = {
id: string,
icon: (string | undefined),
onColor: (RGB | undefined),
offColor: (RGB | undefined),
useColor: (boolean | undefined)
}
var subscriptions: any = {};
var pageId = 0;
var page1: PageEntities =
var Wohnen: PageEntities =
{
"type": "cardEntities",
"heading": "Haus",
"useColor": true,
"items": [
<PageItem>{ id: "alias.0.Rolladen_Eltern" },
<PageItem>{ id: "alias.0.Erker" },
<PageItem>{ id: "alias.0.Küche", useColor: true },
<PageItem>{ id: "alias.0.Wand", useColor: true }
<PageItem>{ id: "alias.0.Stern"},
<PageItem>{ id: "alias.0.Erker"},
<PageItem>{ id: "alias.0.Küche", interpolateColor: true },
<PageItem>{ id: "alias.0.Wand" }
]
};
var page2: PageEntities =
var Strom: PageEntities =
{
"type": "cardEntities",
"heading": "Strom",
"useColor": true,
"items": [
<PageItem>{ id: "alias.0.Netz" },
<PageItem>{ id: "alias.0.Hausverbrauch" },
<PageItem>{ id: "alias.0.Pv" },
<PageItem>{ id: "alias.0.Batterie" }
<PageItem>{ id: "alias.0.Netz", icon: 4, interpolateColor: true, offColor: BatteryFull, onColor: Red , minValue: -1000, maxValue: 1000 },
<PageItem>{ id: "alias.0.Hausverbrauch", icon: 4, interpolateColor: true, offColor: BatteryFull, onColor: Red , maxValue: 1000 },
<PageItem>{ id: "alias.0.Pv", icon: 4, interpolateColor: true, offColor: Off, onColor: BatteryFull , maxValue: 1000 },
<PageItem>{ id: "alias.0.Batterie", icon: 34, interpolateColor: true, offColor: BatteryEmpty, onColor: BatteryFull }
]
};
var button1Page: PageGrid =
{
"type": "cardGrid",
"heading": "Radio",
"useColor": true,
"items": [
<PageItem>{ id: "alias.0.Radio.NJoy" },
<PageItem>{ id: "alias.0.Radio.Delta_Radio" },
@@ -108,13 +52,14 @@ var button2Page: PageEntities =
{
"type": "cardEntities",
"heading": "Knopf2",
"useColor": true,
"items": [
<PageItem>{ id: "alias.0.Schlafen" },
<PageItem>{ id: "alias.0.Stern" }
]
};
var config: Config = {
export const config: Config = {
panelRecvTopic: "mqtt.0.tele.WzDisplay.RESULT",
panelSendTopic: "mqtt.0.cmnd.WzDisplay.CustomSend",
leftEntity: "alias.0.Batterie.ACTUAL",
@@ -131,21 +76,26 @@ var config: Config = {
timeFormat: "%H:%M",
dateFormat: "%A, %d. %B %Y",
weatherEntity: "alias.0.Wetter",
gridPageOffColor: Blue,
gridPageOnColor: White,
defaultColor: Blue,
defaultOffColor: Off,
defaultOnColor: On,
defaultColor: Off,
temperatureUnit: "°C",
pages: [page1, page2,
pages: [Wohnen, Strom,
{
"type": "cardThermo",
"heading": "Thermostat",
"item": <PageItem>{ id: "alias.0.WzNsPanel" }
"useColor": true,
"items": [<PageItem>{ id: "alias.0.WzNsPanel" }]
}
],
button1Page: button1Page,
button2Page: button2Page
};
var subscriptions: any = {};
var pageId = 0;
schedule("* * * * *", function () {
SendTime();
});
@@ -154,8 +104,6 @@ schedule("0 * * * *", function () {
});
// Only monitor the extra nodes if one or both are present
var updateArray: string[] = [];
if (config.rightEntity !== null && existsState(config.rightEntity)) {
@@ -177,7 +125,9 @@ on({ id: config.panelRecvTopic }, function (obj) {
var split = json.CustomRecv.split(",");
if (split[1] == "pageOpenDetail") {
UnsubscribeWatcher();
SendToPanel(GenerateDetailPage(split[2], split[3]));
let pageItem = config.pages[pageId].items.find(e => e.id === split[3]);
if (pageItem !== undefined)
SendToPanel(GenerateDetailPage(split[2], pageItem));
}
else {
HandleMessage(split[0], split[1], parseInt(split[2]), split);
@@ -186,7 +136,6 @@ on({ id: config.panelRecvTopic }, function (obj) {
});
function SendToPanel(val: Payload | Payload[]): void {
if (Array.isArray(val)) {
val.forEach(function (id, i) {
setState(config.panelSendTopic, id.payload);
@@ -198,34 +147,34 @@ function SendToPanel(val: Payload | Payload[]): void {
function HandleMessage(typ: string, method: string, page: number, words: Array<string>): void {
if (typ == "event") {
switch (method) {
case "pageOpen":
var pageNum = (page % config.pages.length);
pageId = Math.abs(pageNum);
if (method == 'pageOpen' || method == 'startup') {
UnsubscribeWatcher();
if (method == 'startup')
HandleStartupProcess();
GeneratePage(config.pages[pageId]);
}
if (method == 'buttonPress' || method == "tempUpd") {
HandleButtonEvent(words)
}
if (method == 'screensaverOpen') {
HandleScreensaver()
}
if (method == 'button1' || method == 'button2') {
break;
case "startup":
UnsubscribeWatcher();
HandleStartupProcess();
break;
case "buttonPress2":
HandleButtonEvent(words);
break;
case "screensaverOpen":
HandleScreensaver();
break;
case "button1":
case "button2":
HandleHardwareButton(method);
default:
break;
}
}
}
function GeneratePage(page: Page): void {
switch (page.type) {
case "cardEntities":
SendToPanel(GenerateEntitiesPage(<PageEntities>page));
@@ -250,18 +199,7 @@ function HandleHardwareButton(method: string): void {
else {
return;
}
log("button1");
GeneratePage(page);
//SendToPanel({ payload: "wake" });
// switch (page.type) {
// case "cardEntities":
// SendToPanel(GenerateEntitiesPage(page));
// break;
// case "cardThermo":
// SendToPanel(GenerateThermoPage(0, page));
// break;
// }
}
function HandleStartupProcess(): void {
@@ -272,16 +210,13 @@ function HandleStartupProcess(): void {
}
function SendDate(): void {
var months = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"];
var days = ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"];
var d = new Date();
var day = days[d.getDay()];
var day = Days[d.getDay()];
var date = d.getDate();
var month = months[d.getMonth()];
var month = Months[d.getMonth()];
var year = d.getFullYear();
var _sendDate = "date,?" + day + " " + date + " " + month + " " + year;
SendToPanel(<Payload>{ payload: _sendDate });
}
function SendTime(): void {
@@ -301,14 +236,14 @@ function SendTime(): void {
function GenerateEntitiesPage(page: PageEntities): Payload[] {
var out_msgs: Array<Payload> = [];
out_msgs = [{ payload: "pageType,cardEntities" }, { payload: "entityUpdHeading," + page.heading }]
out_msgs.push({ payload: GeneratePageElements(page.items, 4) });
out_msgs.push({ payload: GeneratePageElements(page.items, 4,page.useColor) });
return out_msgs
}
function GenerateGridPage(page: PageGrid): Payload[] {
var out_msgs: Array<Payload> = [];
out_msgs = [{ payload: "pageType,cardGrid" }, { payload: "entityUpdHeading," + page.heading }]
out_msgs.push({ payload: GeneratePageElements(page.items, 6, true) });
out_msgs.push({ payload: GeneratePageElements(page.items, 6, page.useColor) });
return out_msgs
}
@@ -340,113 +275,131 @@ 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(useColors ? config.gridPageOffColor : config.defaultColor);
var iconColor = rgb_dec565(config.defaultColor);
switch (o.common.role) {
case "light":
type = "light"
iconId = 1
iconId = pageItem.icon !== undefined ? pageItem.icon : 1;
var optVal = "0"
if (val === true || val === "true") {
optVal = "1"
iconColor = rgb_dec565(useColors ? config.gridPageOnColor : config.defaultColor);
iconColor = GetIconColor(pageItem, true, useColors);
}
return "," + type + "," + pageItem.id + "," + iconId + "," + iconColor + "," + name + "," + optVal
return "," + type + "," + pageItem.id + "," + iconId + "," + iconColor + "," + name + "," + optVal;
case "dimmer":
type = "light"
iconId = 1
iconId = pageItem.icon !== undefined ? pageItem.icon : 1;
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"
if ((pageItem.useColor || useColors) && existsState(pageItem.id + ".ACTUAL")) {
let iconColorRgb =
Interpolate(
pageItem.onColor !== undefined ? pageItem.onColor : config.gridPageOffColor,
pageItem.offColor !== undefined ? pageItem.offColor : config.gridPageOnColor,
(getState(pageItem.id + ".ACTUAL").val / 100)
);
iconColor = rgb_dec565(iconColorRgb);
}
iconColor = GetIconColor(pageItem, existsState(pageItem.id + ".ACTUAL") ? getState(pageItem.id + ".ACTUAL").val : true, useColors);
}
return "," + type + "," + pageItem.id + "," + iconId + "," + iconColor + "," + name + "," + optVal
return "," + type + "," + pageItem.id + "," + iconId + "," + iconColor + "," + name + "," + optVal;
case "blind":
type = "shutter"
iconId = 0
iconId = pageItem.icon !== undefined ? pageItem.icon : 0;
iconColor = GetIconColor(pageItem, existsState(pageItem.id + ".ACTUAL") ? getState(pageItem.id + ".ACTUAL").val : true, useColors);
return "," + type + "," + pageItem.id + "," + iconId + "," + iconColor + "," + name + ","
case "info":
case "value.temperature":
type = "text"
type = "text";
iconId = pageItem.icon !== undefined ? pageItem.icon : 11;
let unit = "";
var optVal = "0"
if (existsState(pageItem.id + ".ON_ACTUAL")) {
optVal = getState(pageItem.id + ".ON_ACTUAL").val + " " + GetUnitOfMeasurement(pageItem.id + ".ON_ACTUAL");
RegisterEntityWatcher(pageItem.id + ".ON_ACTUAL", pageItem.id, placeId);
optVal = getState(pageItem.id + ".ON_ACTUAL").val;
unit = GetUnitOfMeasurement(pageItem.id + ".ON_ACTUAL");
RegisterEntityWatcher(pageItem.id + ".ON_ACTUAL");
}
else if (existsState(pageItem.id + ".ACTUAL")) {
optVal = getState(pageItem.id + ".ACTUAL").val;
RegisterEntityWatcher(pageItem.id + ".ACTUAL", pageItem.id, placeId);
unit = GetUnitOfMeasurement(pageItem.id + ".ACTUAL");
RegisterEntityWatcher(pageItem.id + ".ACTUAL");
}
if (o.common.role == "value.temperature") {
iconId = 2;
optVal += config.temperatureUnit;
iconId = pageItem.icon !== undefined ? pageItem.icon : 2;
}
else {
optVal += GetUnitOfMeasurement(pageItem.id + ".ACTUAL");
}
return "," + type + "," + pageItem.id + "," + iconId + "," + iconColor + "," + name + "," + optVal;
iconColor = GetIconColor(pageItem, parseInt(optVal), useColors);
return "," + type + "," + pageItem.id + "," + iconId + "," + iconColor + "," + name + "," + optVal + " " + unit;
case "button":
type = "button";
iconId = 3;
var optVal = "PRESS";
return "," + type + "," + pageItem.id + "," + iconId + "," + + iconColor + "," + name + "," + optVal;
iconId = pageItem.icon !== undefined ? pageItem.icon : 3;
let buttonText = pageItem.buttonText !== undefined ? pageItem.buttonText : "PRESS";
iconColor = GetIconColor(pageItem, true, useColors);
return "," + type + "," + pageItem.id + "," + iconId + "," + + iconColor + "," + name + "," + buttonText;
default:
return ",delete,,,,"
return ",delete,,,,";
}
}
return ",delete,,,,,"
}
function RegisterEntityWatcher(id: string, entityId: string, placeId: number): void {
function GetIconColor(pageItem: PageItem, value: (boolean | number), useColors: boolean): number {
// dimmer
if ((pageItem.useColor || useColors) && pageItem.interpolateColor && typeof (value) === "number") {
let maxValue = pageItem.maxValue !== undefined ? pageItem.maxValue : 100;
let minValue = pageItem.minValue !== undefined ? pageItem.minValue : 0;
value = value > maxValue ? maxValue : value;
value = value < minValue ? minValue : value;
return rgb_dec565(
Interpolate(
pageItem.offColor !== undefined ? pageItem.offColor : config.defaultOffColor,
pageItem.onColor !== undefined ? pageItem.onColor : config.defaultOnColor,
scale(value, minValue, maxValue, 0, 1)
));
}
if ((pageItem.useColor || useColors) && ((typeof (value) === "boolean" && value) || value > (pageItem.minValue !== undefined ? pageItem.minValue : 0))) {
return rgb_dec565(pageItem.onColor !== undefined ? pageItem.onColor : config.defaultOnColor)
}
return rgb_dec565(pageItem.offColor !== undefined ? pageItem.offColor : config.defaultOffColor);
}
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]);
}))
}
function RegisterDetailEntityWatcher(id: string, entityId: string, type: string): void {
function RegisterDetailEntityWatcher(id: string, pageItem: PageItem, type: string): void {
if (subscriptions.hasOwnProperty(id)) {
return;
}
subscriptions[id] = (on({ id: id, change: 'any' }, function () {
SendToPanel(GenerateDetailPage(type, entityId));
SendToPanel(GenerateDetailPage(type, pageItem));
}))
}
@@ -466,7 +419,7 @@ function GetUnitOfMeasurement(id: string): string {
}
function GenerateThermoPage(page: PageThermo): Payload[] {
var id = page.item.id
var id = page.items[0].id
var out_msgs: Array<Payload> = [];
out_msgs.push({ payload: "pageType,cardThermo" });
@@ -524,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) {
@@ -543,84 +496,88 @@ 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)
}
}
function GenerateDetailPage(type: string, entityId: string): Payload[] {
function GenerateDetailPage(type: string, pageItem: PageItem): Payload[] {
var out_msgs: Array<Payload> = [];
let id = entityId
let id = pageItem.id
if (existsObject(id)) {
var o = getObject(id)
var val = null;
var val: (boolean | number) = 0;
let icon = 1;
var iconColor = rgb_dec565(config.defaultColor);
if (type == "popupLight") {
let switchVal = "0"
if (o.common.role == "light") {
if (existsState(id + ".GET")) {
val = getState(id + ".GET").val;
RegisterDetailEntityWatcher(id + ".GET", id, type);
RegisterDetailEntityWatcher(id + ".GET", pageItem, type);
}
else if (existsState(id + ".SET")) {
val = getState(id + ".SET").val;
RegisterDetailEntityWatcher(id + ".SET", id, type);
RegisterDetailEntityWatcher(id + ".SET", pageItem, type);
}
if (val)
switchVal = "1"
if (val) {
switchVal = "1";
iconColor = GetIconColor(pageItem, true, false);
}
out_msgs.push({ payload: "entityUpdateDetail," + icon + "," + "17299," + switchVal + ",disable,disable,disable" })
out_msgs.push({ payload: "entityUpdateDetail," + icon + "," + + iconColor + "," + switchVal + ",disable,disable,disable" })
}
if (o.common.role == "dimmer") {
if (existsState(id + ".ON_ACTUAL")) {
val = getState(id + ".ON_ACTUAL").val;
RegisterDetailEntityWatcher(id + ".ON_ACTUAL", id, type);
RegisterDetailEntityWatcher(id + ".ON_ACTUAL", pageItem, type);
}
else if (existsState(id + ".ON_SET")) {
val = getState(id + ".ON_SET").val;
RegisterDetailEntityWatcher(id + ".ON_SET", id, type);
RegisterDetailEntityWatcher(id + ".ON_SET", pageItem, type);
}
if (val === true || val === "true")
if (val === true) {
var iconColor = GetIconColor(pageItem, val, false);
switchVal = "1"
}
let brightness = 0;
if (existsState(id + ".ACTUAL")) {
brightness = Math.trunc(scale(getState(id + ".ACTUAL").val, 0, 100, 0, 100))
RegisterDetailEntityWatcher(id + ".ACTUAL", id, type);
iconColor = GetIconColor(pageItem, brightness, false);
RegisterDetailEntityWatcher(id + ".ACTUAL", pageItem, type);
}
let colortemp = "disable"
let colorTemp = "disable"
let colorMode = "disable"
//let attr_support_color = attr.supported_color_modes
//if (attr_support_color.includes("color_temp"))
// colortemp = Math.trunc(scale(attr.color_temp, attr.min_mireds, attr.max_mireds, 0, 100))
out_msgs.push({ payload: "entityUpdateDetail," + icon + "," + "17299," + switchVal + "," + brightness + "," + colortemp })
out_msgs.push({ payload: "entityUpdateDetail," + icon + "," + iconColor + "," + switchVal + "," + brightness + "," + colorTemp + "," + colorMode })
}
}
@@ -763,7 +720,7 @@ function GetAccuWeatherIcon(icon: number): number {
function GetBlendedColor(percentage: number): RGB {
if (percentage < 50)
return Interpolate(config.gridPageOffColor, config.gridPageOnColor, percentage / 50.0);
return Interpolate(config.defaultOffColor, config.defaultOnColor, percentage / 50.0);
return Interpolate(Red, White, (percentage - 50) / 50.0);
}
@@ -779,5 +736,76 @@ function InterpolateNum(d1: number, d2: number, fraction: number): number {
}
function rgb_dec565(rgb: RGB): number {
return ((Math.floor(rgb.red / 255 * 31) << 11) | (Math.floor(rgb.green / 255 * 63) << 5) | (Math.floor(rgb.blue / 255 * 31)))
return ((Math.floor(rgb.red / 255 * 31) << 11) | (Math.floor(rgb.green / 255 * 63) << 5) | (Math.floor(rgb.blue / 255 * 31)));
}
type RGB = {
red: number,
green: number,
blue: number
};
type Payload = {
payload: string;
};
type Page = {
type: string,
heading: string,
items: PageItem[],
useColor: boolean
};
interface PageEntities extends Page {
type: "cardEntities",
items: PageItem[],
};
interface PageGrid extends Page {
type: "cardGrid",
items: PageItem[],
};
interface PageThermo extends Page {
type: "cardThermo",
items: PageItem[],
};
type PageItem = {
id: string,
icon: (number | undefined),
onColor: (RGB | undefined),
offColor: (RGB | undefined),
useColor: (boolean | undefined),
interpolateColor: (boolean | undefined),
minValue: (number | undefined),
maxValue: (number | undefined),
buttonText: (string | undefined)
}
type Config = {
panelRecvTopic: string,
panelSendTopic: string,
timeoutScreensaver: number,
dimmode: number,
//brightnessScreensaver:
locale: string,
timeFormat: string,
dateFormat: string,
weatherEntity: string | null,
temperatureUnit: string,
leftEntity: string,
leftEntityIcon: number,
leftEntityText: string,
leftEntityUnitText: string | null,
rightEntity: string,
rightEntityIcon: number,
rightEntityText: string,
rightEntityUnitText: string | null,
defaultColor: RGB,
defaultOnColor: RGB,
defaultOffColor: RGB,
pages: (PageThermo | PageEntities | PageGrid)[],
button1Page: (PageThermo | PageEntities | PageGrid | null),
button2Page: (PageThermo | PageEntities | PageGrid | null),
};

View File

@@ -4,6 +4,7 @@
- Thermostat Card
- Entity Card (Temperature, Switches and sensors, the script tries to figure the unit of measurement automatically)
- Grid Card
- Detail Card (only switch and normal dimmer)
- Live update (when value was changed in the backend and the page is currently open)
- Screensaver Page with Time, Date and Weather Information.
@@ -44,8 +45,8 @@ See the icons currently usable in the following table:
You can change the string and devices in the config object.
## Buttons
If you like you can add special pages for the buttons, but there is a problem currently which will open the last page again. But if you press the button again, the correct page will open.
## Hardware buttons
If you like you can add special pages for the buttons.
First you need to add this rule to Tasmota:
@@ -54,6 +55,12 @@ Rule2 on Button1#state do Publish tele/%topic%/RESULT {"CustomRecv":"event,butto
Rule2
```
## Colors
You can define colors this way and use them later in the PageItem element
```
const BatteryFull: RGB = { red: 96, green: 176, blue: 62 }
const BatteryEmpty: RGB = { red: 179, green: 45, blue: 25 }
```
## The config element in the script which needs to be configured
```
var config: Config = {
@@ -73,14 +80,15 @@ var config: Config = {
timeFormat: "%H:%M", // not used right now
dateFormat: "%A, %d. %B %Y", // not used right now
weatherEntity: "alias.0.Wetter",
defaultColor: RGB, // Default color of all elements
gridPageOnColor: RGB, // Default on color on grid page
gridPageOffColor: RGB, // Default off color on grid page
defaultColor: Off, // Default color of all elements
defaultOnColor: RGB, // Default on state color for items
defaultOffColor: RGB, // Default off state color for page
temperatureUnit: "°C", // Unit to append on temperature sensors
pages: [
{
"type": "cardEntities", // card type (cardEntities, cardThermo)
"heading": "Testseite", // heading
"useColor": false, // should colors be enabled on this page, can be overridden in PageItem
"items": [ // items array (up to 4 on cardEntities, 1 for cardThermo)
<PageItem>{ id: "alias.0.Rolladen_Eltern" }, // device which must be configured in the device panel. Use only the folder for the device, not the set, get states ...
<PageItem>{ id: "alias.0.Erker" },
@@ -92,17 +100,19 @@ var config: Config = {
{
"type": "cardEntities",
"heading": "Strom",
"useColor": true, // should colors be enabled on this page, can be overridden in PageItem
"items": [
<PageItem>{ id: "alias.0.Netz" },
<PageItem>{ id: "alias.0.Hausverbrauch" },
<PageItem>{ id: "alias.0.Hausverbrauch", icon: 4, interpolateColor: true, offColor: BatteryFull, onColor: Red , maxValue: 1000 },
<PageItem>{ id: "alias.0.Pv" },
<PageItem>{ id: "alias.0.Batterie" }
<PageItem>{ id: "alias.0.Batterie", icon: 34, interpolateColor: true, offColor: BatteryEmpty, onColor: BatteryFull }
]
},
{
"type": "cardThermo",
"heading": "Thermostat",
"useColor": false, // should colors be enabled on this page, can be overridden in PageItem
"item": "alias.0.WzNsPanel" // Needs to be a thermostat in the device panel
}
],
@@ -119,6 +129,10 @@ type PageItem = {
onColor: (RGB | undefined), // the color the item will get when active
offColor: (RGB | undefined), // the color the item will get when inactive
useColor: (boolean | undefined) // override colors, only Grid pages has colors enabled per default
interpolateColor: (boolean | undefined),// fade between color on and off, useColor on Page or PageItem must be enabled
minValue: (number | undefined), // the minimum value for the fade calculation, if smaller the minimum value will be used
maxValue: (number | undefined), // the maximum value for the fade calculation, if larger the maximum value will be used
buttonText: (string | undefined) // the Button Text, default is "Press"
}
```
@@ -130,6 +144,7 @@ var button1Page: PageGrid =
{
"type": "cardGrid",
"heading": "Radio",
"useColor": true, // should colors be enabled on this page, can be overridden in PageItem
"items": [
<PageItem>{ id: "alias.0.Radio.NJoy" },
<PageItem>{ id: "alias.0.Radio.Delta_Radio" },
@@ -138,7 +153,7 @@ var button1Page: PageGrid =
};
```
Pages array can look like this:
Pages array can look like this, so you can add the pages as object or define them in the array itself. This is up to you.
```
pages: [
@@ -146,6 +161,7 @@ pages: [
{
"type": "cardEntities",
"heading": "Strom",
"useColor": true, // should colors be enabled on this page, can be overridden in PageItem
"items": [
<PageItem>{ id: "alias.0.Netz" },
<PageItem>{ id: "alias.0.Hausverbrauch" },