mirror of
https://github.com/joBr99/nspanel-lovelace-ui.git
synced 2025-12-20 14:37:01 +01:00
Compare commits
244 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a5d76e2f4 | ||
|
|
745445af19 | ||
|
|
1f7a3d513d | ||
|
|
24365f7824 | ||
|
|
7996393afb | ||
|
|
5903239456 | ||
|
|
32aa35d9c3 | ||
|
|
bb0108dc7a | ||
|
|
cf6359dea5 | ||
|
|
8d4c1904f1 | ||
|
|
3111ce15a2 | ||
|
|
85d290c3f4 | ||
|
|
f8a72a55a9 | ||
|
|
b9123ed2e9 | ||
|
|
b9d2c08cab | ||
|
|
0d460af0c6 | ||
|
|
6eefab4ed7 | ||
|
|
41c90f68ce | ||
|
|
18516917b9 | ||
|
|
bd64ba702a | ||
|
|
429f023d3d | ||
|
|
6264f22d12 | ||
|
|
fc4fb72818 | ||
|
|
23814a7bf2 | ||
|
|
ef6877b2ae | ||
|
|
9b5ca37673 | ||
|
|
acc2e859bb | ||
|
|
481f5fc074 | ||
|
|
ff96c8fce6 | ||
|
|
5e74db2d85 | ||
|
|
74a2cfd369 | ||
|
|
e780bf302d | ||
|
|
54c8b7d2ab | ||
|
|
88a2ad46ec | ||
|
|
18855840d8 | ||
|
|
2cbcb098e6 | ||
|
|
e2c20b8c2d | ||
|
|
a895a85477 | ||
|
|
d1165efbd8 | ||
|
|
a2a2e221d4 | ||
|
|
23d0514339 | ||
|
|
d66cfc6cd8 | ||
|
|
07fd51543f | ||
|
|
8d0feefb01 | ||
|
|
b5afc7d427 | ||
|
|
c0dd81f967 | ||
|
|
91273bb6f2 | ||
|
|
4ce16095b5 | ||
|
|
9bd6925f75 | ||
|
|
0b58f16a0e | ||
|
|
6983ff1532 | ||
|
|
f7f63d00a4 | ||
|
|
b0027d4c3c | ||
|
|
622f9bf0c7 | ||
|
|
b6b0249bfe | ||
|
|
27251104ee | ||
|
|
7822782fc6 | ||
|
|
763dbb6ef8 | ||
|
|
45b59d4d01 | ||
|
|
ae515df4c9 | ||
|
|
498f8bf234 | ||
|
|
3f531da6f0 | ||
|
|
264e9cd61c | ||
|
|
fc0f24a794 | ||
|
|
f9549a7dc6 | ||
|
|
b698d67f79 | ||
|
|
d0d456c49d | ||
|
|
e3909e457c | ||
|
|
b1768f2b80 | ||
|
|
6643d6c284 | ||
|
|
c683a9bc7c | ||
|
|
721a41ce3e | ||
|
|
94891bf2cb | ||
|
|
0de9d83b50 | ||
|
|
25f3913010 | ||
|
|
a023d2e04a | ||
|
|
741ff125f3 | ||
|
|
0d883983fa | ||
|
|
646ec6cf92 | ||
|
|
411ef851a7 | ||
|
|
65886ca9fc | ||
|
|
a789c39fa1 | ||
|
|
3930408ebf | ||
|
|
17554fd57d | ||
|
|
5d6f59df71 | ||
|
|
818405fa73 | ||
|
|
2451c353f8 | ||
|
|
d3475a948e | ||
|
|
0b7fec2f99 | ||
|
|
81487e580b | ||
|
|
6347297d1f | ||
|
|
6e3dbc4057 | ||
|
|
8849e18e48 | ||
|
|
5e6348dc70 | ||
|
|
b1399aad81 | ||
|
|
fe3e0c882e | ||
|
|
59691cc591 | ||
|
|
0d85dac1e1 | ||
|
|
f08010d74b | ||
|
|
470a891f65 | ||
|
|
860f4a3dd0 | ||
|
|
64cb6c58e3 | ||
|
|
21d1ad9a1e | ||
|
|
7a55c1c876 | ||
|
|
ae9254eda2 | ||
|
|
2b0e16d633 | ||
|
|
330744ca9c | ||
|
|
7584516ac9 | ||
|
|
7937368f73 | ||
|
|
1aca308970 | ||
|
|
5a9ffc1889 | ||
|
|
6a9b4d4f0e | ||
|
|
0809443b5d | ||
|
|
9ca085e296 | ||
|
|
5120a87472 | ||
|
|
8c3df900fe | ||
|
|
3af3fd5172 | ||
|
|
13d1e95e74 | ||
|
|
36ab2eb09c | ||
|
|
66f3e2955b | ||
|
|
e01d4a8bc8 | ||
|
|
63e7ecd60a | ||
|
|
2ad0ad4c35 | ||
|
|
a1665075b5 | ||
|
|
d095f7ef28 | ||
|
|
9a4fef98d3 | ||
|
|
1e9a8df3f8 | ||
|
|
2f21594289 | ||
|
|
63778275dc | ||
|
|
ff43743ae3 | ||
|
|
a7d710bb6f | ||
|
|
ee66eb13f6 | ||
|
|
441bedf5a2 | ||
|
|
85b0ed4154 | ||
|
|
890236f683 | ||
|
|
18c96ee0f8 | ||
|
|
00442c82b0 | ||
|
|
dc3062eba0 | ||
|
|
cfdfb9404c | ||
|
|
246c03ee4b | ||
|
|
940ee10fc4 | ||
|
|
ba887cf60c | ||
|
|
e5706d848f | ||
|
|
eac1095bbd | ||
|
|
1f6cd2d1aa | ||
|
|
1201142cb5 | ||
|
|
dddff76d11 | ||
|
|
e74de74ede | ||
|
|
e859c338b3 | ||
|
|
0921dd81b8 | ||
|
|
413401b1f9 | ||
|
|
fcc8d44bfd | ||
|
|
d4ff842dee | ||
|
|
4cd0ecbe56 | ||
|
|
cfba107246 | ||
|
|
e5b4fb4743 | ||
|
|
a35467830f | ||
|
|
625bddc894 | ||
|
|
6124a24887 | ||
|
|
39e44bc820 | ||
|
|
f3351af4e5 | ||
|
|
0cc735566f | ||
|
|
90e2b1622b | ||
|
|
9f8be27835 | ||
|
|
b852703aa1 | ||
|
|
cce36c281e | ||
|
|
9267e7be4e | ||
|
|
91434bfdd3 | ||
|
|
9153b602d7 | ||
|
|
7c7241bc98 | ||
|
|
9a111720e5 | ||
|
|
a93681f262 | ||
|
|
dfe82dcd45 | ||
|
|
6f792443ad | ||
|
|
4b21961707 | ||
|
|
699ba7855b | ||
|
|
0b740fb14d | ||
|
|
19764eb220 | ||
|
|
8fee0af87f | ||
|
|
ccd914c1a7 | ||
|
|
49f6631d5a | ||
|
|
6fd1ddb432 | ||
|
|
3fae02ebe2 | ||
|
|
6e073f7181 | ||
|
|
9ea6c218fa | ||
|
|
f48510e03e | ||
|
|
b5e3133d77 | ||
|
|
fc1614e243 | ||
|
|
2d39e15b9d | ||
|
|
025a3bd700 | ||
|
|
93393ac877 | ||
|
|
b28c4e46eb | ||
|
|
48e17ed986 | ||
|
|
f0857aeec7 | ||
|
|
36dddb0dc2 | ||
|
|
e1ac4d4cec | ||
|
|
d82c4127b7 | ||
|
|
991c882373 | ||
|
|
e35b9d4a82 | ||
|
|
0cdbfc5708 | ||
|
|
21e8300191 | ||
|
|
d534ba30ac | ||
|
|
491ad5ea25 | ||
|
|
fb5145561f | ||
|
|
a65b8cb2df | ||
|
|
4d27f2f485 | ||
|
|
864f5b80d0 | ||
|
|
6b1e0f0499 | ||
|
|
13919eba01 | ||
|
|
5965a089f1 | ||
|
|
5ea3557ffb | ||
|
|
c8e3960003 | ||
|
|
9aec695f89 | ||
|
|
14bc505c79 | ||
|
|
7504120e83 | ||
|
|
c1d15385c3 | ||
|
|
a49b12c204 | ||
|
|
96b136c194 | ||
|
|
292c372060 | ||
|
|
9c79197a59 | ||
|
|
ac167a36ea | ||
|
|
989f085649 | ||
|
|
f231ea690b | ||
|
|
d18fbe269d | ||
|
|
27ff4f0581 | ||
|
|
b9e70f7b89 | ||
|
|
bea91b01a9 | ||
|
|
0bcacd6ba6 | ||
|
|
e3f5aad46b | ||
|
|
f367758445 | ||
|
|
9f0f670db5 | ||
|
|
36d0685f3d | ||
|
|
f0ee420d52 | ||
|
|
daa1b1ee0f | ||
|
|
35d97fa0bd | ||
|
|
0e76fc37de | ||
|
|
0cb06c752f | ||
|
|
cf9f14f12a | ||
|
|
a3a6487e13 | ||
|
|
bd9709fda6 | ||
|
|
ac237ff2ed | ||
|
|
6353d67335 | ||
|
|
c9b73303cf | ||
|
|
580681eb73 |
11
.github/workflows/codeql-analysis.yml
vendored
11
.github/workflows/codeql-analysis.yml
vendored
@@ -14,13 +14,14 @@ name: "CodeQL"
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
paths:
|
# paths:
|
||||||
- '**.py'
|
# - '**.py'
|
||||||
|
# - '**.ts'
|
||||||
pull_request:
|
pull_request:
|
||||||
# The branches below must be a subset of the branches above
|
# The branches below must be a subset of the branches above
|
||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
paths:
|
# paths:
|
||||||
- '**.py'
|
# - '**.py'
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '36 14 * * 3'
|
- cron: '36 14 * * 3'
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
language: [ 'python' ]
|
language: [ 'python', 'typescript' ]
|
||||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||||
|
|
||||||
|
|||||||
@@ -77,33 +77,35 @@ change the page type:
|
|||||||
|
|
||||||
`weatherUpdate,?0?2,3 C?0?0 mm?Mi?0?9,3 C?Do?0?11,5 C`
|
`weatherUpdate,?0?2,3 C?0?0 mm?Mi?0?9,3 C?Do?0?11,5 C`
|
||||||
|
|
||||||
|
`page,0`
|
||||||
|
|
||||||
### cardEntities Page
|
### cardEntities Page
|
||||||
|
|
||||||
The following message can be used to update the content on the cardEntities Page
|
The following message can be used to update the content on the cardEntities Page
|
||||||
|
|
||||||
`entityUpdHeading,heading1337`
|
`entityUpdHeading,heading1337`
|
||||||
|
|
||||||
`entityUpd,*id*,*type*,*internalNameEntity*,*iconId*,*displayNameEntity*,*optionalValue*`
|
`entityUpd[,*type*,*internalNameEntity*,*iconId*,*iconColor*,*displayNameEntity*,*optionalValue*]x4`
|
||||||
|
|
||||||
`entityUpd,1,light,light.entityName,1,Light1,0`
|
`,light,light.entityName,1,17299,Light1,0`
|
||||||
|
|
||||||
`entityUpd,2,shutter,cover.entityName,0,Shutter2`
|
`,shutter,cover.entityName,0,17299,Shutter2,`
|
||||||
|
|
||||||
`entityUpd,3,delete`
|
`,delete,,,,,`
|
||||||
|
|
||||||
`entityUpd,4,text,sensor.entityName,3,Temperature,content`
|
`,text,sensor.entityName,3,17299,Temperature,content`
|
||||||
|
|
||||||
`entityUpd,4,button,button.entityName,3,bt-name,bt-text`
|
`,button,button.entityName,3,17299,bt-name,bt-text`
|
||||||
|
|
||||||
`entityUpd,1,switch,switch.entityName,4,Switch1,0`
|
`,switch,switch.entityName,4,17299,Switch1,0`
|
||||||
|
|
||||||
### popupLight Page
|
### popupLight Page
|
||||||
|
|
||||||
`entityUpdateDetail,*buttonState*,*sliderBrightnessPos*,*sliderColorTempPos*,*colorMode*`
|
`entityUpdateDetail,*iconId*,*iconColor*,*buttonState*,*sliderBrightnessPos*,*sliderColorTempPos*,*colorMode*`
|
||||||
|
|
||||||
`entityUpdateDetail,1,100,78,enable`
|
`entityUpdateDetail,1,17299,1,100,78,enable`
|
||||||
|
|
||||||
`entityUpdateDetail,1,100,disable`
|
`entityUpdateDetail,1,17299,1,100,disable`
|
||||||
|
|
||||||
### popupShutter Page
|
### popupShutter Page
|
||||||
|
|
||||||
@@ -113,16 +115,25 @@ The following message can be used to update the content on the cardEntities Page
|
|||||||
|
|
||||||
### popupNotify Page
|
### popupNotify Page
|
||||||
|
|
||||||
`entityUpdateDetail,*tHeading*,*b1*,*b2*,*tText*`
|
`entityUpdateDetail,*internalName*,*tHeading*,*tHeadingColor*,*b1*,*tB1Color*,*b2*,*tB2Color*,*tText*,*tTextColor*,*sleepTimeout*`
|
||||||
|
|
||||||
|
`popupExit`
|
||||||
|
|
||||||
### cardThermo Page
|
### cardThermo Page
|
||||||
|
|
||||||
`entityUpd,*internalNameEntiy*,*heading*,*currentTemp*,*destTemp*,*status*,*minTemp*,*maxTemp*,*stepTemp*`
|
`entityUpd,*internalNameEntiy*,*heading*,*currentTemp*,*destTemp*,*status*,*minTemp*,*maxTemp*,*stepTemp*[[,*iconId*,*activeColor*,*state*,*hvac_action*]]`
|
||||||
|
|
||||||
|
`[[]]` are not part of the command, this part repeats 9 times for the buttons
|
||||||
|
|
||||||
### cardMedia Page
|
### cardMedia Page
|
||||||
|
|
||||||
`entityUpd,|*internalNameEntiy*|*heading*|*icon*|*title*|*author*|*volume*|*playpauseicon*`
|
`entityUpd,|*internalNameEntiy*|*heading*|*icon*|*title*|*author*|*volume*|*playpauseicon*`
|
||||||
|
|
||||||
|
### cardAlarm Page
|
||||||
|
|
||||||
|
`entityUpd,*internalNameEntity*,*arm1*,*arm1ActionName*,*arm2*,*arm2ActionName*,*arm3*,*arm3ActionName*,*arm4*,*arm4ActionName*,*icon*,*numpadStatus*`
|
||||||
|
|
||||||
|
|
||||||
## Messages from Nextion Display
|
## Messages from Nextion Display
|
||||||
|
|
||||||
### startup page
|
### startup page
|
||||||
@@ -169,9 +180,9 @@ The following message can be used to update the content on the cardEntities Page
|
|||||||
|
|
||||||
### popupNotify Page
|
### popupNotify Page
|
||||||
|
|
||||||
`event,buttonPress,D,D,D,1,notifyAction,yes`
|
`event,buttonPress,D,D,*internalName*,1,notifyAction,yes`
|
||||||
|
|
||||||
`event,buttonPress,D,D,D,1,notifyAction,no`
|
`event,buttonPress,D,D,*internalName*,1,notifyAction,no`
|
||||||
|
|
||||||
### cardThermo Page
|
### cardThermo Page
|
||||||
|
|
||||||
@@ -179,6 +190,8 @@ The following message can be used to update the content on the cardEntities Page
|
|||||||
|
|
||||||
`event,tempUpd,*pageNumber*,*entityName*,*temperature*`
|
`event,tempUpd,*pageNumber*,*entityName*,*temperature*`
|
||||||
|
|
||||||
|
`event,buttonPress,*pageNumber*,*tHeading*,*entityName,1,hvac_action,*hvac_action*
|
||||||
|
|
||||||
### cardMedia Page
|
### cardMedia Page
|
||||||
|
|
||||||
`event,buttonPress,1,tHeading,internalNameEntity,1,media-back`
|
`event,buttonPress,1,tHeading,internalNameEntity,1,media-back`
|
||||||
@@ -189,39 +202,14 @@ The following message can be used to update the content on the cardEntities Page
|
|||||||
|
|
||||||
`event,buttonPress,1,tHeading,internalNameEntity,1,volumeSlider,75`
|
`event,buttonPress,1,tHeading,internalNameEntity,1,volumeSlider,75`
|
||||||
|
|
||||||
|
### cardAlarm Page
|
||||||
|
|
||||||
|
`event,buttonPress2,internalNameEntity,actionName,code`
|
||||||
|
|
||||||
|
|
||||||
# Icons IDs
|
# Icons IDs
|
||||||
|
|
||||||
ID | Icon
|
Please see Icon's int the [icons.md file](icons.md)
|
||||||
-- | ----
|
|
||||||
0 | 
|
|
||||||
1 | 
|
|
||||||
2 | 
|
|
||||||
3 | 
|
|
||||||
4 | 
|
|
||||||
5 | 
|
|
||||||
6 | 
|
|
||||||
7 | 
|
|
||||||
8 | 
|
|
||||||
9 | 
|
|
||||||
10 | 
|
|
||||||
11 | 
|
|
||||||
12 | 
|
|
||||||
13 | 
|
|
||||||
14 | 
|
|
||||||
15 | 
|
|
||||||
16 | 
|
|
||||||
17 | 
|
|
||||||
18 | 
|
|
||||||
19 | 
|
|
||||||
20 | 
|
|
||||||
21 | 
|
|
||||||
22 | 
|
|
||||||
23 | 
|
|
||||||
24 | 
|
|
||||||
25 | 
|
|
||||||
26 | 
|
|
||||||
|
|
||||||
# Design Guidelines for Nextion HMI Project
|
# Design Guidelines for Nextion HMI Project
|
||||||
|
|
||||||
|
|||||||
25592
HMI/code_gen/icons/icons.json
Normal file
25592
HMI/code_gen/icons/icons.json
Normal file
File diff suppressed because it is too large
Load Diff
94
HMI/code_gen/icons/icons.py
Normal file
94
HMI/code_gen/icons/icons.py
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
icons = [
|
||||||
|
"alert-circle-outline",
|
||||||
|
"lightbulb",
|
||||||
|
"thermometer",
|
||||||
|
"gesture-tap-button",
|
||||||
|
"flash",
|
||||||
|
"music",
|
||||||
|
"check-circle-outline",
|
||||||
|
"close-circle-outline",
|
||||||
|
"pause",
|
||||||
|
"play",
|
||||||
|
"palette",
|
||||||
|
"window-open",
|
||||||
|
"weather-cloudy",
|
||||||
|
"weather-fog",
|
||||||
|
"weather-hail",
|
||||||
|
"weather-lightning",
|
||||||
|
"weather-lightning-rainy",
|
||||||
|
"weather-night",
|
||||||
|
"weather-partly-cloudy",
|
||||||
|
"weather-pouring",
|
||||||
|
"weather-rainy",
|
||||||
|
"weather-snowy",
|
||||||
|
"weather-snowy-rainy",
|
||||||
|
"weather-sunny",
|
||||||
|
"weather-windy",
|
||||||
|
"weather-windy-variant",
|
||||||
|
"water-percent",
|
||||||
|
"power",
|
||||||
|
"fire",
|
||||||
|
"calendar-sync",
|
||||||
|
"fan",
|
||||||
|
"snowflake",
|
||||||
|
"solar-power",
|
||||||
|
"battery-charging-medium",
|
||||||
|
"battery-medium",
|
||||||
|
"shield-home"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
__location__ = os.path.realpath(
|
||||||
|
os.path.join(os.getcwd(), os.path.dirname(__file__)))
|
||||||
|
|
||||||
|
with open(os.path.join(__location__, "icons.json"),'r') as f:
|
||||||
|
icon_metadata = json.load(f)
|
||||||
|
|
||||||
|
icon_nextion_string = ""
|
||||||
|
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:
|
||||||
|
print(f"WARNING ICON NOT FOUND: {icon_name}")
|
||||||
|
else:
|
||||||
|
hex = icon['hex']
|
||||||
|
s = int(hex, 16)
|
||||||
|
#print(chr(s), end = '')
|
||||||
|
icon_nextion_string += chr(s)
|
||||||
|
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:
|
||||||
|
f.write("icons = {\n")
|
||||||
|
for idx, val in enumerate(icon_name_list):
|
||||||
|
f.write(f" '{val}': {idx},\n")
|
||||||
|
f.write("}\n")
|
||||||
|
f.write("""
|
||||||
|
def get_icon_id(ma_name):
|
||||||
|
if ma_name in icons:
|
||||||
|
return icons[ma_name]
|
||||||
|
else:
|
||||||
|
return icons["alert-circle-outline"]
|
||||||
|
""")
|
||||||
|
|
||||||
|
# write documentation file
|
||||||
|
with open(os.path.join(__location__, "../..","icons.md"), 'w') as f:
|
||||||
|
f.write("""
|
||||||
|
# Icons IDs
|
||||||
|
This file contains the Icons IDs included in the display firmware, addressable via serial.
|
||||||
|
|
||||||
|
ID | MD Icon Name | Icon
|
||||||
|
-- | ------------ | ----
|
||||||
|
""")
|
||||||
|
for idx, val in enumerate(icon_name_list):
|
||||||
|
f.write(f"{idx} | {val} | \n")
|
||||||
|
|
||||||
|
|
||||||
|
print("=== STRING for HMI Project ===")
|
||||||
|
print("=== Put the following string into the txt field in nextion ===")
|
||||||
|
print(icon_nextion_string)
|
||||||
42
HMI/icons.md
Normal file
42
HMI/icons.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
|
||||||
|
# Icons IDs
|
||||||
|
This file contains the Icons IDs included in the display firmware, addressable via serial.
|
||||||
|
|
||||||
|
ID | MD Icon Name | Icon
|
||||||
|
-- | ------------ | ----
|
||||||
|
0 | alert-circle-outline | 
|
||||||
|
1 | lightbulb | 
|
||||||
|
2 | thermometer | 
|
||||||
|
3 | gesture-tap-button | 
|
||||||
|
4 | flash | 
|
||||||
|
5 | music | 
|
||||||
|
6 | check-circle-outline | 
|
||||||
|
7 | close-circle-outline | 
|
||||||
|
8 | pause | 
|
||||||
|
9 | play | 
|
||||||
|
10 | palette | 
|
||||||
|
11 | window-open | 
|
||||||
|
12 | weather-cloudy | 
|
||||||
|
13 | weather-fog | 
|
||||||
|
14 | weather-hail | 
|
||||||
|
15 | weather-lightning | 
|
||||||
|
16 | weather-lightning-rainy | 
|
||||||
|
17 | weather-night | 
|
||||||
|
18 | weather-partly-cloudy | 
|
||||||
|
19 | weather-pouring | 
|
||||||
|
20 | weather-rainy | 
|
||||||
|
21 | weather-snowy | 
|
||||||
|
22 | weather-snowy-rainy | 
|
||||||
|
23 | weather-sunny | 
|
||||||
|
24 | weather-windy | 
|
||||||
|
25 | weather-windy-variant | 
|
||||||
|
26 | water-percent | 
|
||||||
|
27 | power | 
|
||||||
|
28 | fire | 
|
||||||
|
29 | calendar-sync | 
|
||||||
|
30 | fan | 
|
||||||
|
31 | snowflake | 
|
||||||
|
32 | solar-power | 
|
||||||
|
33 | battery-charging-medium | 
|
||||||
|
34 | battery-medium | 
|
||||||
|
35 | shield-home | 
|
||||||
1247
HMI/n2t-out/cardAlarm.txt
Normal file
1247
HMI/n2t-out/cardAlarm.txt
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1323
HMI/n2t-out/cardGrid.txt
Normal file
1323
HMI/n2t-out/cardGrid.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -27,25 +27,6 @@ Page cardMedia
|
|||||||
vis tTmp,0
|
vis tTmp,0
|
||||||
vis tId,0
|
vis tId,0
|
||||||
//vis nPageDisp,0
|
//vis nPageDisp,0
|
||||||
//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
|
|
||||||
//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
|
|
||||||
//display current page from global var
|
|
||||||
nPageDisp.val=nPage
|
|
||||||
|
|
||||||
Variable (string) strCommand
|
Variable (string) strCommand
|
||||||
Attributes
|
Attributes
|
||||||
@@ -573,7 +554,7 @@ Button bNext
|
|||||||
Font ID : 2
|
Font ID : 2
|
||||||
Back. Color : 6371
|
Back. Color : 6371
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 1024
|
Back. Color (Pressed) : 14823
|
||||||
Font Color (Unpressed) : 65535
|
Font Color (Unpressed) : 65535
|
||||||
Font Color (Pressed) : 65535
|
Font Color (Pressed) : 65535
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -627,7 +608,7 @@ Button bPrev
|
|||||||
Font ID : 2
|
Font ID : 2
|
||||||
Back. Color : 6371
|
Back. Color : 6371
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 1024
|
Back. Color (Pressed) : 14823
|
||||||
Font Color (Unpressed) : 65535
|
Font Color (Unpressed) : 65535
|
||||||
Font Color (Pressed) : 65535
|
Font Color (Pressed) : 65535
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -768,15 +749,9 @@ Timer tmSerial
|
|||||||
spstr strCommand.txt,tTmp.txt,",",2
|
spstr strCommand.txt,tTmp.txt,",",2
|
||||||
//save third arg if there's one
|
//save third arg if there's one
|
||||||
spstr strCommand.txt,pageIcons.tTmp2.txt,",",3
|
spstr strCommand.txt,pageIcons.tTmp2.txt,",",3
|
||||||
if(tId.txt=="cardMedia")
|
//we are going to exit this page with this command, so we have to clear the buffer, so we are not getting into a stupid loop ...
|
||||||
{
|
udelete payloadLength-1
|
||||||
//yay, we are already on the correct page
|
bufferPos=0
|
||||||
}else
|
|
||||||
{
|
|
||||||
//we are going to exit this page with this command, so we have to clear the buffer, so we are not getting into a stupid loop ...
|
|
||||||
udelete payloadLength-1
|
|
||||||
bufferPos=0
|
|
||||||
}
|
|
||||||
if(tId.txt=="popupLight")
|
if(tId.txt=="popupLight")
|
||||||
{
|
{
|
||||||
pageIcons.tTmp1.txt=tTmp.txt
|
pageIcons.tTmp1.txt=tTmp.txt
|
||||||
@@ -794,10 +769,18 @@ Timer tmSerial
|
|||||||
{
|
{
|
||||||
page pageStartup
|
page pageStartup
|
||||||
}
|
}
|
||||||
if(tId.txt=="pageNotify")
|
if(tId.txt=="popupNotify")
|
||||||
{
|
{
|
||||||
page popupNotify
|
page popupNotify
|
||||||
}
|
}
|
||||||
|
if(tId.txt=="cardGrid")
|
||||||
|
{
|
||||||
|
page cardGrid
|
||||||
|
}
|
||||||
|
if(tId.txt=="cardAlarm")
|
||||||
|
{
|
||||||
|
page cardAlarm
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// end of user code
|
// end of user code
|
||||||
udelete payloadLength-1
|
udelete payloadLength-1
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -10,49 +10,57 @@ pageSerialTest
|
|||||||
13 Component(s)
|
13 Component(s)
|
||||||
48 Line(s) of event code
|
48 Line(s) of event code
|
||||||
43 Unique line(s) of event code
|
43 Unique line(s) of event code
|
||||||
pageTest
|
cardMedia
|
||||||
12 Component(s)
|
22 Component(s)
|
||||||
12 Line(s) of event code
|
210 Line(s) of event code
|
||||||
12 Unique line(s) of event code
|
|
||||||
screensaver
|
|
||||||
25 Component(s)
|
|
||||||
146 Line(s) of event code
|
|
||||||
116 Unique line(s) of event code
|
116 Unique line(s) of event code
|
||||||
|
popupNotify
|
||||||
|
17 Component(s)
|
||||||
|
179 Line(s) of event code
|
||||||
|
118 Unique line(s) of event code
|
||||||
pageSwipeTest
|
pageSwipeTest
|
||||||
18 Component(s)
|
18 Component(s)
|
||||||
62 Line(s) of event code
|
62 Line(s) of event code
|
||||||
44 Unique line(s) of event code
|
44 Unique line(s) of event code
|
||||||
popupNotify
|
|
||||||
15 Component(s)
|
|
||||||
143 Line(s) of event code
|
|
||||||
95 Unique line(s) of event code
|
|
||||||
cardMedia
|
|
||||||
22 Component(s)
|
|
||||||
219 Line(s) of event code
|
|
||||||
114 Unique line(s) of event code
|
|
||||||
pageStartup
|
|
||||||
19 Component(s)
|
|
||||||
135 Line(s) of event code
|
|
||||||
105 Unique line(s) of event code
|
|
||||||
popupLight
|
popupLight
|
||||||
27 Component(s)
|
27 Component(s)
|
||||||
296 Line(s) of event code
|
338 Line(s) of event code
|
||||||
152 Unique 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
|
popupShutter
|
||||||
19 Component(s)
|
19 Component(s)
|
||||||
148 Line(s) of event code
|
194 Line(s) of event code
|
||||||
84 Unique 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
|
cardThermo
|
||||||
27 Component(s)
|
42 Component(s)
|
||||||
201 Line(s) of event code
|
438 Line(s) of event code
|
||||||
120 Unique line(s) of event code
|
221 Unique line(s) of event code
|
||||||
cardEntities
|
cardEntities
|
||||||
55 Component(s)
|
54 Component(s)
|
||||||
824 Line(s) of event code
|
808 Line(s) of event code
|
||||||
313 Unique 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
|
||||||
|
cardAlarm
|
||||||
|
35 Component(s)
|
||||||
|
259 Line(s) of event code
|
||||||
|
163 Unique line(s) of event code
|
||||||
|
|
||||||
Total
|
Total
|
||||||
12 Page(s)
|
14 Page(s)
|
||||||
258 Component(s)
|
350 Component(s)
|
||||||
2251 Line(s) of event code
|
3292 Line(s) of event code
|
||||||
578 Unique line(s) of event code
|
842 Unique line(s) of event code
|
||||||
|
|||||||
@@ -72,8 +72,8 @@ Text tIcons
|
|||||||
Horizontal Alignment : left
|
Horizontal Alignment : left
|
||||||
Vertical Alignment : top
|
Vertical Alignment : top
|
||||||
Input Type : character
|
Input Type : character
|
||||||
Text : î°î´îï§îîî î
î£îîîîîîîî½îîîîîî¾îîîî
|
Text : îî´îï§îîî î
î£îîî°îîîîî½îîîîîî¾îîîîî¤î·îºîîï¤ï¡î
|
||||||
Max. Text Size : 100
|
Max. Text Size : 120
|
||||||
Word wrap : enabled
|
Word wrap : enabled
|
||||||
Horizontal Spacing : 0
|
Horizontal Spacing : 0
|
||||||
Vertical Spacing : 0
|
Vertical Spacing : 0
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ Number nCrcRecv
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -72,7 +72,7 @@ Number nCrcCalc
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -102,7 +102,7 @@ Number nIt
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -162,7 +162,7 @@ Text tStatus
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -192,7 +192,7 @@ Text tCmd
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -222,7 +222,7 @@ Text tBuffer
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -252,7 +252,7 @@ Text tDesc
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -298,7 +298,7 @@ Button b0
|
|||||||
Effect Time : 300
|
Effect Time : 300
|
||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : 3D auto
|
Style : 3D auto
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 50712
|
Back. Color : 50712
|
||||||
Back. Picture ID (Pressed) : 0
|
Back. Picture ID (Pressed) : 0
|
||||||
Back. Color (Pressed) : 1024
|
Back. Color (Pressed) : 1024
|
||||||
@@ -335,7 +335,7 @@ Button b1
|
|||||||
Effect Time : 300
|
Effect Time : 300
|
||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : 3D auto
|
Style : 3D auto
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 50712
|
Back. Color : 50712
|
||||||
Back. Picture ID (Pressed) : 0
|
Back. Picture ID (Pressed) : 0
|
||||||
Back. Color (Pressed) : 1024
|
Back. Color (Pressed) : 1024
|
||||||
|
|||||||
@@ -236,9 +236,9 @@ Text tInstruction
|
|||||||
Disable release event after dragging: 0
|
Disable release event after dragging: 0
|
||||||
Send Component ID : disabled
|
Send Component ID : disabled
|
||||||
Opacity : 127
|
Opacity : 127
|
||||||
x coordinate : 246
|
x coordinate : 163
|
||||||
y coordinate : 0
|
y coordinate : 0
|
||||||
Width : 100
|
Width : 119
|
||||||
Height : 30
|
Height : 30
|
||||||
Effect : load
|
Effect : load
|
||||||
Effect Priority : 0
|
Effect Priority : 0
|
||||||
@@ -266,9 +266,9 @@ Text tId
|
|||||||
Disable release event after dragging: 0
|
Disable release event after dragging: 0
|
||||||
Send Component ID : disabled
|
Send Component ID : disabled
|
||||||
Opacity : 127
|
Opacity : 127
|
||||||
x coordinate : 355
|
x coordinate : 282
|
||||||
y coordinate : 0
|
y coordinate : 0
|
||||||
Width : 39
|
Width : 125
|
||||||
Height : 30
|
Height : 30
|
||||||
Effect : load
|
Effect : load
|
||||||
Effect Priority : 0
|
Effect Priority : 0
|
||||||
@@ -402,7 +402,7 @@ Text tVersion
|
|||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
Vertical Alignment : center
|
Vertical Alignment : center
|
||||||
Input Type : character
|
Input Type : character
|
||||||
Text : 4
|
Text : 15
|
||||||
Max. Text Size : 10
|
Max. Text Size : 10
|
||||||
Word wrap : disabled
|
Word wrap : disabled
|
||||||
Horizontal Spacing : 0
|
Horizontal Spacing : 0
|
||||||
@@ -459,6 +459,7 @@ Button bSendStartup
|
|||||||
|
|
||||||
Events
|
Events
|
||||||
Touch Press Event
|
Touch Press Event
|
||||||
|
recmod=1
|
||||||
bauds=115200
|
bauds=115200
|
||||||
// startup event
|
// startup event
|
||||||
tSend.txt="event,startup,"+tVersion.txt
|
tSend.txt="event,startup,"+tVersion.txt
|
||||||
@@ -474,7 +475,7 @@ Button bSendStartup
|
|||||||
prints tSend.txt,0
|
prints tSend.txt,0
|
||||||
prints crcval,2
|
prints crcval,2
|
||||||
|
|
||||||
Hotspot mSpecial
|
Hotspot mSpinner
|
||||||
Attributes
|
Attributes
|
||||||
ID : 16
|
ID : 16
|
||||||
Scope : local
|
Scope : local
|
||||||
@@ -482,10 +483,10 @@ Hotspot mSpecial
|
|||||||
Disable release event after dragging: 0
|
Disable release event after dragging: 0
|
||||||
Send Component ID : disabled
|
Send Component ID : disabled
|
||||||
Opacity : 127
|
Opacity : 127
|
||||||
x coordinate : 87
|
x coordinate : 154
|
||||||
y coordinate : 33
|
y coordinate : 49
|
||||||
Width : 288
|
Width : 140
|
||||||
Height : 187
|
Height : 140
|
||||||
Effect : load
|
Effect : load
|
||||||
Effect Priority : 0
|
Effect Priority : 0
|
||||||
Effect Time : 300
|
Effect Time : 300
|
||||||
@@ -576,6 +577,7 @@ Timer tmSerial
|
|||||||
}
|
}
|
||||||
if(tInstruction.txt=="pageType")
|
if(tInstruction.txt=="pageType")
|
||||||
{
|
{
|
||||||
|
dim=100
|
||||||
//command format pageType,specialPageName
|
//command format pageType,specialPageName
|
||||||
//write name of speical page to tId
|
//write name of speical page to tId
|
||||||
spstr strCommand.txt,tId.txt,",",1
|
spstr strCommand.txt,tId.txt,",",1
|
||||||
@@ -603,10 +605,22 @@ Timer tmSerial
|
|||||||
{
|
{
|
||||||
page pageStartup
|
page pageStartup
|
||||||
}
|
}
|
||||||
if(tId.txt=="pageNotify")
|
if(tId.txt=="popupNotify")
|
||||||
{
|
{
|
||||||
page popupNotify
|
page popupNotify
|
||||||
}
|
}
|
||||||
|
if(tId.txt=="cardMedia")
|
||||||
|
{
|
||||||
|
page cardMedia
|
||||||
|
}
|
||||||
|
if(tId.txt=="cardGrid")
|
||||||
|
{
|
||||||
|
page cardGrid
|
||||||
|
}
|
||||||
|
if(tId.txt=="cardAlarm")
|
||||||
|
{
|
||||||
|
page cardAlarm
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// end of user code
|
// end of user code
|
||||||
udelete payloadLength-1
|
udelete payloadLength-1
|
||||||
@@ -618,15 +632,6 @@ Timer tmSerial
|
|||||||
// next character
|
// next character
|
||||||
bufferPos++
|
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 tmSpinner
|
Timer tmSpinner
|
||||||
@@ -638,7 +643,7 @@ Timer tmSpinner
|
|||||||
|
|
||||||
Events
|
Events
|
||||||
Timer Event
|
Timer Event
|
||||||
xpic 154,49,spinner_w.val,140,frame_ptr.val,0,11 // draw the current frame
|
xpic mSpinner.x,mSpinner.y,spinner_w.val,140,frame_ptr.val,0,11 // draw the current frame
|
||||||
frame_ptr.val+=spinner_w.val%frapic_w.val // advance the pointer to the next frame in the pic resource and roll over at the end
|
frame_ptr.val+=spinner_w.val%frapic_w.val // advance the pointer to the next frame in the pic resource and roll over at the end
|
||||||
doevents // finish drawing before next timer event triggers
|
doevents // finish drawing before next timer event triggers
|
||||||
if(frame_ptr.val==1820)
|
if(frame_ptr.val==1820)
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ Number nXS
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -66,7 +66,7 @@ Number nYE
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -96,7 +96,7 @@ Number nYS
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -126,7 +126,7 @@ Number nXE
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -156,7 +156,7 @@ Number nYR
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -186,7 +186,7 @@ Number nXR
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -216,7 +216,7 @@ Text tDesc1
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -246,7 +246,7 @@ Text t2
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -276,7 +276,7 @@ Text tDirection
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -306,7 +306,7 @@ Text t0
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -336,7 +336,7 @@ Text tStatusNew
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -366,7 +366,7 @@ Text t3
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -396,7 +396,7 @@ Text tDirNew
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 65535
|
Back. Color : 65535
|
||||||
Font Color : 0
|
Font Color : 0
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -442,7 +442,7 @@ Button b8
|
|||||||
Effect Time : 300
|
Effect Time : 300
|
||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : 3D auto
|
Style : 3D auto
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 50712
|
Back. Color : 50712
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 1024
|
Back. Color (Pressed) : 1024
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ Button b0
|
|||||||
Effect Time : 300
|
Effect Time : 300
|
||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : 3D auto
|
Style : 3D auto
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 50712
|
Back. Color : 50712
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 1024
|
Back. Color (Pressed) : 1024
|
||||||
@@ -92,7 +92,7 @@ Button b1
|
|||||||
Effect Time : 300
|
Effect Time : 300
|
||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : 3D auto
|
Style : 3D auto
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 50712
|
Back. Color : 50712
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 1024
|
Back. Color (Pressed) : 1024
|
||||||
@@ -128,7 +128,7 @@ Button b2
|
|||||||
Effect Time : 300
|
Effect Time : 300
|
||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : 3D auto
|
Style : 3D auto
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 50712
|
Back. Color : 50712
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 1024
|
Back. Color (Pressed) : 1024
|
||||||
@@ -164,7 +164,7 @@ Button b3
|
|||||||
Effect Time : 300
|
Effect Time : 300
|
||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : 3D auto
|
Style : 3D auto
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 50712
|
Back. Color : 50712
|
||||||
Back. Picture ID (Pressed) : 0
|
Back. Picture ID (Pressed) : 0
|
||||||
Back. Color (Pressed) : 1024
|
Back. Color (Pressed) : 1024
|
||||||
@@ -200,7 +200,7 @@ Button b6
|
|||||||
Effect Time : 300
|
Effect Time : 300
|
||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : 3D auto
|
Style : 3D auto
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 50712
|
Back. Color : 50712
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 1024
|
Back. Color (Pressed) : 1024
|
||||||
@@ -236,7 +236,7 @@ Button b4
|
|||||||
Effect Time : 300
|
Effect Time : 300
|
||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : 3D auto
|
Style : 3D auto
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 50712
|
Back. Color : 50712
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 1024
|
Back. Color (Pressed) : 1024
|
||||||
@@ -272,7 +272,7 @@ Button b5
|
|||||||
Effect Time : 300
|
Effect Time : 300
|
||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : 3D auto
|
Style : 3D auto
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 50712
|
Back. Color : 50712
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 1024
|
Back. Color (Pressed) : 1024
|
||||||
@@ -308,7 +308,7 @@ Button b7
|
|||||||
Effect Time : 300
|
Effect Time : 300
|
||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : 3D auto
|
Style : 3D auto
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 50712
|
Back. Color : 50712
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 1024
|
Back. Color (Pressed) : 1024
|
||||||
@@ -344,7 +344,7 @@ Button b8
|
|||||||
Effect Time : 300
|
Effect Time : 300
|
||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : 3D auto
|
Style : 3D auto
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 50712
|
Back. Color : 50712
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 1024
|
Back. Color (Pressed) : 1024
|
||||||
@@ -381,7 +381,7 @@ Button b9
|
|||||||
Effect Time : 300
|
Effect Time : 300
|
||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : 3D auto
|
Style : 3D auto
|
||||||
Font ID : 5
|
Font ID : 4
|
||||||
Back. Color : 50712
|
Back. Color : 50712
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 1024
|
Back. Color (Pressed) : 1024
|
||||||
@@ -400,3 +400,75 @@ Button b9
|
|||||||
Touch Press Event
|
Touch Press Event
|
||||||
page popupNotify
|
page popupNotify
|
||||||
|
|
||||||
|
Button b10
|
||||||
|
Attributes
|
||||||
|
ID : 12
|
||||||
|
Scope : local
|
||||||
|
Dragging : 0
|
||||||
|
Disable release event after dragging: 0
|
||||||
|
Send Component ID : disabled
|
||||||
|
Opacity : 127
|
||||||
|
x coordinate : 115
|
||||||
|
y coordinate : 128
|
||||||
|
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 : alarm
|
||||||
|
Max. Text Size : 10
|
||||||
|
Word wrap : disabled
|
||||||
|
Horizontal Spacing : 0
|
||||||
|
Vertical Spacing : 0
|
||||||
|
|
||||||
|
Events
|
||||||
|
Touch Press Event
|
||||||
|
page cardAlarm
|
||||||
|
|
||||||
|
Button b11
|
||||||
|
Attributes
|
||||||
|
ID : 13
|
||||||
|
Scope : local
|
||||||
|
Dragging : 0
|
||||||
|
Disable release event after dragging: 0
|
||||||
|
Send Component ID : disabled
|
||||||
|
Opacity : 127
|
||||||
|
x coordinate : 117
|
||||||
|
y coordinate : 184
|
||||||
|
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 : grid
|
||||||
|
Max. Text Size : 10
|
||||||
|
Word wrap : disabled
|
||||||
|
Horizontal Spacing : 0
|
||||||
|
Vertical Spacing : 0
|
||||||
|
|
||||||
|
Events
|
||||||
|
Touch Press Event
|
||||||
|
page cardGrid
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ Page popupLight
|
|||||||
vis tSend,0
|
vis tSend,0
|
||||||
vis tTmp,0
|
vis tTmp,0
|
||||||
vis tInstruction,0
|
vis tInstruction,0
|
||||||
vis tItem,0
|
vis tId,0
|
||||||
//page open event
|
//page open event
|
||||||
// event,pageOpenDetail,typeOfPage,tEntityName
|
// event,pageOpenDetail,typeOfPage,tEntityName
|
||||||
// craft command
|
// craft command
|
||||||
@@ -54,15 +54,16 @@ Page popupLight
|
|||||||
vis hTempSlider,0
|
vis hTempSlider,0
|
||||||
//disable color wheel components by default
|
//disable color wheel components by default
|
||||||
vis bColor,0
|
vis bColor,0
|
||||||
vis pColorWheel,0
|
|
||||||
vis t0,0
|
vis t0,0
|
||||||
|
vis pColorWheel,0
|
||||||
|
//vis t0,0
|
||||||
|
|
||||||
Variable (string) strCommand
|
Variable (string) strCommand
|
||||||
Attributes
|
Attributes
|
||||||
ID : 17
|
ID : 17
|
||||||
Scope : local
|
Scope : local
|
||||||
Text :
|
Text :
|
||||||
Max. Text Size: 50
|
Max. Text Size: 100
|
||||||
|
|
||||||
Variable (string) entn
|
Variable (string) entn
|
||||||
Attributes
|
Attributes
|
||||||
@@ -97,8 +98,8 @@ Text tEntity
|
|||||||
Disable release event after dragging: 0
|
Disable release event after dragging: 0
|
||||||
Send Component ID : disabled
|
Send Component ID : disabled
|
||||||
Opacity : 127
|
Opacity : 127
|
||||||
x coordinate : 63
|
x coordinate : 92
|
||||||
y coordinate : 37
|
y coordinate : 21
|
||||||
Width : 263
|
Width : 263
|
||||||
Height : 30
|
Height : 30
|
||||||
Effect : load
|
Effect : load
|
||||||
@@ -127,8 +128,8 @@ Text tIcon1
|
|||||||
Disable release event after dragging: 0
|
Disable release event after dragging: 0
|
||||||
Send Component ID : disabled
|
Send Component ID : disabled
|
||||||
Opacity : 127
|
Opacity : 127
|
||||||
x coordinate : 10
|
x coordinate : 11
|
||||||
y coordinate : 38
|
y coordinate : 21
|
||||||
Width : 45
|
Width : 45
|
||||||
Height : 30
|
Height : 30
|
||||||
Effect : load
|
Effect : load
|
||||||
@@ -158,7 +159,7 @@ Text t1
|
|||||||
Send Component ID : disabled
|
Send Component ID : disabled
|
||||||
Opacity : 127
|
Opacity : 127
|
||||||
x coordinate : 10
|
x coordinate : 10
|
||||||
y coordinate : 133
|
y coordinate : 226
|
||||||
Width : 100
|
Width : 100
|
||||||
Height : 30
|
Height : 30
|
||||||
Effect : load
|
Effect : load
|
||||||
@@ -188,7 +189,7 @@ Text t2
|
|||||||
Send Component ID : disabled
|
Send Component ID : disabled
|
||||||
Opacity : 127
|
Opacity : 127
|
||||||
x coordinate : 10
|
x coordinate : 10
|
||||||
y coordinate : 173
|
y coordinate : 267
|
||||||
Width : 45
|
Width : 45
|
||||||
Height : 30
|
Height : 30
|
||||||
Effect : load
|
Effect : load
|
||||||
@@ -218,7 +219,7 @@ Text t3
|
|||||||
Send Component ID : disabled
|
Send Component ID : disabled
|
||||||
Opacity : 127
|
Opacity : 127
|
||||||
x coordinate : 10
|
x coordinate : 10
|
||||||
y coordinate : 251
|
y coordinate : 181
|
||||||
Width : 45
|
Width : 45
|
||||||
Height : 30
|
Height : 30
|
||||||
Effect : load
|
Effect : load
|
||||||
@@ -248,7 +249,7 @@ Text t4
|
|||||||
Send Component ID : disabled
|
Send Component ID : disabled
|
||||||
Opacity : 127
|
Opacity : 127
|
||||||
x coordinate : 9
|
x coordinate : 9
|
||||||
y coordinate : 213
|
y coordinate : 143
|
||||||
Width : 116
|
Width : 116
|
||||||
Height : 30
|
Height : 30
|
||||||
Effect : load
|
Effect : load
|
||||||
@@ -280,7 +281,7 @@ Text tSend
|
|||||||
x coordinate : 0
|
x coordinate : 0
|
||||||
y coordinate : 0
|
y coordinate : 0
|
||||||
Width : 348
|
Width : 348
|
||||||
Height : 22
|
Height : 7
|
||||||
Effect : load
|
Effect : load
|
||||||
Effect Priority : 0
|
Effect Priority : 0
|
||||||
Effect Time : 300
|
Effect Time : 300
|
||||||
@@ -359,7 +360,7 @@ Text tInstruction
|
|||||||
Horizontal Spacing : 0
|
Horizontal Spacing : 0
|
||||||
Vertical Spacing : 0
|
Vertical Spacing : 0
|
||||||
|
|
||||||
Text tItem
|
Text tId
|
||||||
Attributes
|
Attributes
|
||||||
ID : 19
|
ID : 19
|
||||||
Scope : local
|
Scope : local
|
||||||
@@ -397,8 +398,8 @@ Text t0
|
|||||||
Disable release event after dragging: 0
|
Disable release event after dragging: 0
|
||||||
Send Component ID : disabled
|
Send Component ID : disabled
|
||||||
Opacity : 127
|
Opacity : 127
|
||||||
x coordinate : 76
|
x coordinate : 9
|
||||||
y coordinate : 89
|
y coordinate : 80
|
||||||
Width : 64
|
Width : 64
|
||||||
Height : 30
|
Height : 30
|
||||||
Effect : load
|
Effect : load
|
||||||
@@ -444,10 +445,10 @@ Picture pColorWheel
|
|||||||
Disable release event after dragging: 0
|
Disable release event after dragging: 0
|
||||||
Send Component ID : disabled
|
Send Component ID : disabled
|
||||||
Opacity : 127
|
Opacity : 127
|
||||||
x coordinate : 142
|
x coordinate : 167
|
||||||
y coordinate : 87
|
y coordinate : 69
|
||||||
Width : 213
|
Width : 160
|
||||||
Height : 213
|
Height : 160
|
||||||
Effect : load
|
Effect : load
|
||||||
Effect Priority : 0
|
Effect Priority : 0
|
||||||
Effect Time : 300
|
Effect Time : 300
|
||||||
@@ -491,7 +492,7 @@ Slider hBrightness
|
|||||||
Send Component ID : disabled
|
Send Component ID : disabled
|
||||||
Opacity : 127
|
Opacity : 127
|
||||||
x coordinate : 61
|
x coordinate : 61
|
||||||
y coordinate : 167
|
y coordinate : 261
|
||||||
Width : 335
|
Width : 335
|
||||||
Height : 43
|
Height : 43
|
||||||
Effect : load
|
Effect : load
|
||||||
@@ -559,7 +560,7 @@ Slider hTempSlider
|
|||||||
Send Component ID : disabled
|
Send Component ID : disabled
|
||||||
Opacity : 127
|
Opacity : 127
|
||||||
x coordinate : 61
|
x coordinate : 61
|
||||||
y coordinate : 248
|
y coordinate : 178
|
||||||
Width : 335
|
Width : 335
|
||||||
Height : 44
|
Height : 44
|
||||||
Effect : load
|
Effect : load
|
||||||
@@ -638,7 +639,7 @@ Button b0
|
|||||||
Font ID : 1
|
Font ID : 1
|
||||||
Back. Color : 6371
|
Back. Color : 6371
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 6371
|
Back. Color (Pressed) : 14823
|
||||||
Font Color (Unpressed) : 65535
|
Font Color (Unpressed) : 65535
|
||||||
Font Color (Pressed) : 65535
|
Font Color (Pressed) : 65535
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -652,7 +653,23 @@ Button b0
|
|||||||
|
|
||||||
Events
|
Events
|
||||||
Touch Press Event
|
Touch Press Event
|
||||||
page cardEntities
|
//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
|
||||||
|
//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
|
||||||
|
|
||||||
Button bColor
|
Button bColor
|
||||||
Attributes
|
Attributes
|
||||||
@@ -662,8 +679,8 @@ Button bColor
|
|||||||
Disable release event after dragging: 0
|
Disable release event after dragging: 0
|
||||||
Send Component ID : disabled
|
Send Component ID : disabled
|
||||||
Opacity : 127
|
Opacity : 127
|
||||||
x coordinate : 9
|
x coordinate : 89
|
||||||
y coordinate : 78
|
y coordinate : 68
|
||||||
Width : 50
|
Width : 50
|
||||||
Height : 50
|
Height : 50
|
||||||
Effect : load
|
Effect : load
|
||||||
@@ -674,7 +691,7 @@ Button bColor
|
|||||||
Font ID : 1
|
Font ID : 1
|
||||||
Back. Color : 6371
|
Back. Color : 6371
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 6371
|
Back. Color (Pressed) : 14823
|
||||||
Font Color (Unpressed) : 65535
|
Font Color (Unpressed) : 65535
|
||||||
Font Color (Pressed) : 65535
|
Font Color (Pressed) : 65535
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -693,11 +710,11 @@ Button bColor
|
|||||||
mode.val=0
|
mode.val=0
|
||||||
//enable color wheel
|
//enable color wheel
|
||||||
vis pColorWheel,1
|
vis pColorWheel,1
|
||||||
vis t0,1
|
//vis t0,1
|
||||||
//disable brightness
|
//disable brightness
|
||||||
vis t1,0
|
//vis t1,0
|
||||||
vis t2,0
|
//vis t2,0
|
||||||
vis hBrightness,0
|
//vis hBrightness,0
|
||||||
//disable color temp
|
//disable color temp
|
||||||
vis t4,0
|
vis t4,0
|
||||||
vis t3,0
|
vis t3,0
|
||||||
@@ -707,14 +724,14 @@ Button bColor
|
|||||||
mode.val=1
|
mode.val=1
|
||||||
//disable color wheel
|
//disable color wheel
|
||||||
vis pColorWheel,0
|
vis pColorWheel,0
|
||||||
vis t0,0
|
//vis t0,0
|
||||||
if(mode_bright.val==1)
|
//if(mode_bright.val==1)
|
||||||
{
|
//{
|
||||||
//enable brightness
|
// //enable brightness
|
||||||
vis t1,1
|
// vis t1,1
|
||||||
vis t2,1
|
// vis t2,1
|
||||||
vis hBrightness,1
|
// vis hBrightness,1
|
||||||
}
|
//}
|
||||||
if(mode_temp.val==1)
|
if(mode_temp.val==1)
|
||||||
{
|
{
|
||||||
//enable color temp
|
//enable color temp
|
||||||
@@ -733,7 +750,7 @@ Dual-state Button btOnOff1
|
|||||||
Send Component ID : disabled
|
Send Component ID : disabled
|
||||||
Opacity : 127
|
Opacity : 127
|
||||||
x coordinate : 370
|
x coordinate : 370
|
||||||
y coordinate : 75
|
y coordinate : 79
|
||||||
Width : 50
|
Width : 50
|
||||||
Height : 50
|
Height : 50
|
||||||
Effect : load
|
Effect : load
|
||||||
@@ -790,7 +807,7 @@ Timer tmSleep
|
|||||||
{
|
{
|
||||||
screensaver.originPage.val=dp
|
screensaver.originPage.val=dp
|
||||||
sleepValue=0
|
sleepValue=0
|
||||||
page cardEntities
|
click b0,1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -848,32 +865,45 @@ Timer tmSerial
|
|||||||
spstr strCommand.txt,tInstruction.txt,",",0
|
spstr strCommand.txt,tInstruction.txt,",",0
|
||||||
if(tInstruction.txt=="entityUpdateDetail")
|
if(tInstruction.txt=="entityUpdateDetail")
|
||||||
{
|
{
|
||||||
// get Button State
|
// change icon
|
||||||
spstr strCommand.txt,tTmp.txt,",",1
|
spstr strCommand.txt,tTmp.txt,",",1
|
||||||
covx tTmp.txt,sys0,0,0
|
covx tTmp.txt,sys0,0,0
|
||||||
|
substr pageIcons.tIcons.txt,tIcon1.txt,sys0,1
|
||||||
|
vis tIcon1,1
|
||||||
|
// change icon color
|
||||||
|
spstr strCommand.txt,tTmp.txt,",",2
|
||||||
|
covx tTmp.txt,sys0,0,0
|
||||||
|
tIcon1.pco=sys0
|
||||||
|
// get Button State
|
||||||
|
spstr strCommand.txt,tTmp.txt,",",3
|
||||||
|
covx tTmp.txt,sys0,0,0
|
||||||
btOnOff1.val=sys0
|
btOnOff1.val=sys0
|
||||||
|
// get Brightness value
|
||||||
|
spstr strCommand.txt,tTmp.txt,",",4
|
||||||
|
if(tTmp.txt=="disable")
|
||||||
|
{
|
||||||
|
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
|
||||||
|
hBrightness.val=sys0
|
||||||
|
}
|
||||||
if(mode.val==1)
|
if(mode.val==1)
|
||||||
{
|
{
|
||||||
// get Brightness value
|
|
||||||
spstr strCommand.txt,tTmp.txt,",",2
|
|
||||||
if(tTmp.txt=="disable")
|
|
||||||
{
|
|
||||||
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
|
|
||||||
hBrightness.val=sys0
|
|
||||||
}
|
|
||||||
// get ColorTemp value
|
// get ColorTemp value
|
||||||
spstr strCommand.txt,tTmp.txt,",",3
|
spstr strCommand.txt,tTmp.txt,",",5
|
||||||
if(tTmp.txt=="disable")
|
if(tTmp.txt=="unknown")
|
||||||
|
{
|
||||||
|
click bColor,1
|
||||||
|
mode_temp.val=1
|
||||||
|
}else if(tTmp.txt=="disable")
|
||||||
{
|
{
|
||||||
vis t4,0
|
vis t4,0
|
||||||
vis t3,0
|
vis t3,0
|
||||||
@@ -890,10 +920,11 @@ Timer tmSerial
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// get ColorWheel value
|
// get ColorWheel value
|
||||||
spstr strCommand.txt,tTmp.txt,",",4
|
spstr strCommand.txt,tTmp.txt,",",6
|
||||||
if(tTmp.txt!="disable")
|
if(tTmp.txt!="disable")
|
||||||
{
|
{
|
||||||
vis bColor,1
|
vis bColor,1
|
||||||
|
vis t0,1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(tInstruction.txt=="time")
|
if(tInstruction.txt=="time")
|
||||||
@@ -911,7 +942,6 @@ Timer tmSerial
|
|||||||
// get value
|
// get value
|
||||||
spstr strCommand.txt,tTmp.txt,",",1
|
spstr strCommand.txt,tTmp.txt,",",1
|
||||||
covx tTmp.txt,dimValue,0,0
|
covx tTmp.txt,dimValue,0,0
|
||||||
dim=dimValue
|
|
||||||
}
|
}
|
||||||
if(tInstruction.txt=="timeout")
|
if(tInstruction.txt=="timeout")
|
||||||
{
|
{
|
||||||
@@ -923,6 +953,43 @@ Timer tmSerial
|
|||||||
udelete payloadLength-1
|
udelete payloadLength-1
|
||||||
bufferPos=0
|
bufferPos=0
|
||||||
}
|
}
|
||||||
|
if(tInstruction.txt=="pageType")
|
||||||
|
{
|
||||||
|
//command format pageType,specialPageName
|
||||||
|
//write name of speical page to tId
|
||||||
|
spstr strCommand.txt,tId.txt,",",1
|
||||||
|
//save second arg if there's one
|
||||||
|
spstr strCommand.txt,tTmp.txt,",",2
|
||||||
|
//save third arg if there's one
|
||||||
|
spstr strCommand.txt,pageIcons.tTmp2.txt,",",3
|
||||||
|
//we are going to exit this page with this command, so we have to clear the buffer, so we are not getting into a stupid loop ...
|
||||||
|
udelete payloadLength-1
|
||||||
|
bufferPos=0
|
||||||
|
if(tId.txt=="cardEntities")
|
||||||
|
{
|
||||||
|
page cardEntities
|
||||||
|
}
|
||||||
|
if(tId.txt=="cardMedia")
|
||||||
|
{
|
||||||
|
page cardMedia
|
||||||
|
}
|
||||||
|
if(tId.txt=="pageStartup")
|
||||||
|
{
|
||||||
|
page pageStartup
|
||||||
|
}
|
||||||
|
if(tId.txt=="popupNotify")
|
||||||
|
{
|
||||||
|
page popupNotify
|
||||||
|
}
|
||||||
|
if(tId.txt=="cardGrid")
|
||||||
|
{
|
||||||
|
page cardGrid
|
||||||
|
}
|
||||||
|
if(tId.txt=="cardAlarm")
|
||||||
|
{
|
||||||
|
page cardAlarm
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,28 +25,26 @@ Page popupNotify
|
|||||||
vis tTmp,0
|
vis tTmp,0
|
||||||
vis tInstruction,0
|
vis tInstruction,0
|
||||||
vis tId,0
|
vis tId,0
|
||||||
//page open event
|
|
||||||
// event,pageOpenDetail,typeOfPage,tEntityName
|
|
||||||
// craft command
|
|
||||||
tSend.txt="event,pageOpenDetail,notify"
|
|
||||||
//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
|
|
||||||
|
|
||||||
Variable (string) strCommand
|
Variable (string) strCommand
|
||||||
Attributes
|
Attributes
|
||||||
ID : 8
|
ID : 8
|
||||||
Scope : local
|
Scope : local
|
||||||
Text :
|
Text :
|
||||||
Max. Text Size: 500
|
Max. Text Size: 1000
|
||||||
|
|
||||||
|
Variable (string) entn
|
||||||
|
Attributes
|
||||||
|
ID : 15
|
||||||
|
Scope : local
|
||||||
|
Text :
|
||||||
|
Max. Text Size: 30
|
||||||
|
|
||||||
|
Variable (int32) vaOldSleepT
|
||||||
|
Attributes
|
||||||
|
ID : 16
|
||||||
|
Scope: local
|
||||||
|
Value: 0
|
||||||
|
|
||||||
Text tSend
|
Text tSend
|
||||||
Attributes
|
Attributes
|
||||||
@@ -223,7 +221,7 @@ Text tText
|
|||||||
Vertical Alignment : top
|
Vertical Alignment : top
|
||||||
Input Type : character
|
Input Type : character
|
||||||
Text :
|
Text :
|
||||||
Max. Text Size : 300
|
Max. Text Size : 500
|
||||||
Word wrap : enabled
|
Word wrap : enabled
|
||||||
Horizontal Spacing : 0
|
Horizontal Spacing : 0
|
||||||
Vertical Spacing : 0
|
Vertical Spacing : 0
|
||||||
@@ -265,7 +263,7 @@ Button b0
|
|||||||
Font ID : 1
|
Font ID : 1
|
||||||
Back. Color : 6371
|
Back. Color : 6371
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 6371
|
Back. Color (Pressed) : 14823
|
||||||
Font Color (Unpressed) : 65535
|
Font Color (Unpressed) : 65535
|
||||||
Font Color (Pressed) : 65535
|
Font Color (Pressed) : 65535
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -279,7 +277,24 @@ Button b0
|
|||||||
|
|
||||||
Events
|
Events
|
||||||
Touch Press Event
|
Touch Press Event
|
||||||
page cardEntities
|
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
|
||||||
|
//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
|
||||||
|
|
||||||
Button b2
|
Button b2
|
||||||
Attributes
|
Attributes
|
||||||
@@ -315,8 +330,9 @@ Button b2
|
|||||||
|
|
||||||
Events
|
Events
|
||||||
Touch Press Event
|
Touch Press Event
|
||||||
|
sleepTimeout=vaOldSleepT.val
|
||||||
//craft command
|
//craft command
|
||||||
tSend.txt="event,buttonPress,D,D,D,1,notifyAction,yes"
|
tSend.txt="event,buttonPress,D,D,"+entn.txt+",1,notifyAction,yes"
|
||||||
//send calc crc
|
//send calc crc
|
||||||
btlen tSend.txt,sys0
|
btlen tSend.txt,sys0
|
||||||
crcrest 1,0xffff // reset CRC
|
crcrest 1,0xffff // reset CRC
|
||||||
@@ -363,8 +379,9 @@ Button b1
|
|||||||
|
|
||||||
Events
|
Events
|
||||||
Touch Press Event
|
Touch Press Event
|
||||||
|
sleepTimeout=vaOldSleepT.val
|
||||||
//craft command
|
//craft command
|
||||||
tSend.txt="event,buttonPress,D,D,D,1,notifyAction,no"
|
tSend.txt="event,buttonPress,D,D,"+entn.txt+",1,notifyAction,no"
|
||||||
//send calc crc
|
//send calc crc
|
||||||
btlen tSend.txt,sys0
|
btlen tSend.txt,sys0
|
||||||
crcrest 1,0xffff // reset CRC
|
crcrest 1,0xffff // reset CRC
|
||||||
@@ -393,7 +410,7 @@ Timer tmSleep
|
|||||||
{
|
{
|
||||||
screensaver.originPage.val=dp
|
screensaver.originPage.val=dp
|
||||||
sleepValue=0
|
sleepValue=0
|
||||||
page cardEntities
|
click b0,1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -451,14 +468,46 @@ Timer tmSerial
|
|||||||
spstr strCommand.txt,tInstruction.txt,",",0
|
spstr strCommand.txt,tInstruction.txt,",",0
|
||||||
if(tInstruction.txt=="entityUpdateDetail")
|
if(tInstruction.txt=="entityUpdateDetail")
|
||||||
{
|
{
|
||||||
|
// get entn
|
||||||
|
spstr strCommand.txt,entn.txt,"|",1
|
||||||
// get Heading
|
// get Heading
|
||||||
spstr strCommand.txt,tHeading.txt,",",1
|
spstr strCommand.txt,tHeading.txt,"|",2
|
||||||
|
// heading color
|
||||||
|
spstr strCommand.txt,tTmp.txt,"|",3
|
||||||
|
covx tTmp.txt,sys0,0,0
|
||||||
|
tHeading.pco=sys0
|
||||||
// get b1
|
// get b1
|
||||||
spstr strCommand.txt,b1.txt,",",2
|
spstr strCommand.txt,b1.txt,"|",4
|
||||||
|
if(b1.txt!="")
|
||||||
|
{
|
||||||
|
vis b1,1
|
||||||
|
}
|
||||||
|
// b1 color
|
||||||
|
spstr strCommand.txt,tTmp.txt,"|",5
|
||||||
|
covx tTmp.txt,sys0,0,0
|
||||||
|
b1.pco=sys0
|
||||||
// get b2
|
// get b2
|
||||||
spstr strCommand.txt,b2.txt,",",3
|
spstr strCommand.txt,b2.txt,"|",6
|
||||||
|
if(b2.txt!="")
|
||||||
|
{
|
||||||
|
vis b2,1
|
||||||
|
}
|
||||||
|
// b2 color
|
||||||
|
spstr strCommand.txt,tTmp.txt,"|",7
|
||||||
|
covx tTmp.txt,sys0,0,0
|
||||||
|
b2.pco=sys0
|
||||||
// get tText
|
// get tText
|
||||||
spstr strCommand.txt,tText.txt,",",4
|
spstr strCommand.txt,tText.txt,"|",8
|
||||||
|
// tText color
|
||||||
|
spstr strCommand.txt,tTmp.txt,"|",9
|
||||||
|
covx tTmp.txt,sys0,0,0
|
||||||
|
tText.pco=sys0
|
||||||
|
//preserve old sleepTimeout
|
||||||
|
vaOldSleepT.val=sleepTimeout
|
||||||
|
// sleep timeout
|
||||||
|
spstr strCommand.txt,tTmp.txt,"|",10
|
||||||
|
covx tTmp.txt,sys0,0,0
|
||||||
|
sleepTimeout=sys0
|
||||||
}
|
}
|
||||||
if(tInstruction.txt=="time")
|
if(tInstruction.txt=="time")
|
||||||
{
|
{
|
||||||
@@ -475,7 +524,6 @@ Timer tmSerial
|
|||||||
// get value
|
// get value
|
||||||
spstr strCommand.txt,tTmp.txt,",",1
|
spstr strCommand.txt,tTmp.txt,",",1
|
||||||
covx tTmp.txt,dimValue,0,0
|
covx tTmp.txt,dimValue,0,0
|
||||||
dim=dimValue
|
|
||||||
}
|
}
|
||||||
if(tInstruction.txt=="timeout")
|
if(tInstruction.txt=="timeout")
|
||||||
{
|
{
|
||||||
@@ -483,22 +531,23 @@ Timer tmSerial
|
|||||||
spstr strCommand.txt,tTmp.txt,",",1
|
spstr strCommand.txt,tTmp.txt,",",1
|
||||||
covx tTmp.txt,sleepTimeout,0,0
|
covx tTmp.txt,sleepTimeout,0,0
|
||||||
}
|
}
|
||||||
|
if(tInstruction.txt=="exitPopup")
|
||||||
|
{
|
||||||
|
click b0,1
|
||||||
|
}
|
||||||
if(tInstruction.txt=="pageType")
|
if(tInstruction.txt=="pageType")
|
||||||
{
|
{
|
||||||
|
sleepTimeout=vaOldSleepT.val
|
||||||
//command format pageType,specialPageName
|
//command format pageType,specialPageName
|
||||||
//write name of speical page to tId
|
//write name of speical page to tId
|
||||||
spstr strCommand.txt,tId.txt,",",1
|
spstr strCommand.txt,tId.txt,",",1
|
||||||
//save second arg if there's one
|
//save second arg if there's one
|
||||||
spstr strCommand.txt,tTmp.txt,",",2
|
spstr strCommand.txt,tTmp.txt,",",2
|
||||||
if(tId.txt=="pageNotify")
|
//save third arg if there's one
|
||||||
{
|
spstr strCommand.txt,pageIcons.tTmp2.txt,",",3
|
||||||
//yay, we are already on the correct page
|
//we are going to exit this page with this command, so we have to clear the buffer, so we are not getting into a stupid loop ...
|
||||||
}else
|
udelete payloadLength-1
|
||||||
{
|
bufferPos=0
|
||||||
//we are going to exit this page with this command, so we have to clear the buffer, so we are not getting into a stupid loop ...
|
|
||||||
udelete payloadLength-1
|
|
||||||
bufferPos=0
|
|
||||||
}
|
|
||||||
if(tId.txt=="popupLight")
|
if(tId.txt=="popupLight")
|
||||||
{
|
{
|
||||||
pageIcons.tTmp1.txt=tTmp.txt
|
pageIcons.tTmp1.txt=tTmp.txt
|
||||||
@@ -512,13 +561,21 @@ Timer tmSerial
|
|||||||
{
|
{
|
||||||
page cardMedia
|
page cardMedia
|
||||||
}
|
}
|
||||||
|
if(tId.txt=="cardThermo")
|
||||||
|
{
|
||||||
|
page cardThermo
|
||||||
|
}
|
||||||
if(tId.txt=="pageStartup")
|
if(tId.txt=="pageStartup")
|
||||||
{
|
{
|
||||||
page pageStartup
|
page pageStartup
|
||||||
}
|
}
|
||||||
if(tId.txt=="pageThermo")
|
if(tId.txt=="cardGrid")
|
||||||
{
|
{
|
||||||
page cardThermo
|
page cardGrid
|
||||||
|
}
|
||||||
|
if(tId.txt=="cardAlarm")
|
||||||
|
{
|
||||||
|
page cardAlarm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// end of user code
|
// end of user code
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ Page popupShutter
|
|||||||
vis tSend,0
|
vis tSend,0
|
||||||
vis tTmp,0
|
vis tTmp,0
|
||||||
vis tInstruction,0
|
vis tInstruction,0
|
||||||
vis tItem,0
|
vis tId,0
|
||||||
//page open event
|
//page open event
|
||||||
// event,pageOpenDetail,typeOfPage,tEntityName
|
// event,pageOpenDetail,typeOfPage,tEntityName
|
||||||
// craft command
|
// craft command
|
||||||
@@ -209,7 +209,7 @@ Text tInstruction
|
|||||||
Horizontal Spacing : 0
|
Horizontal Spacing : 0
|
||||||
Vertical Spacing : 0
|
Vertical Spacing : 0
|
||||||
|
|
||||||
Text tItem
|
Text tId
|
||||||
Attributes
|
Attributes
|
||||||
ID : 12
|
ID : 12
|
||||||
Scope : local
|
Scope : local
|
||||||
@@ -353,7 +353,7 @@ Button b0
|
|||||||
Font ID : 1
|
Font ID : 1
|
||||||
Back. Color : 6371
|
Back. Color : 6371
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 6371
|
Back. Color (Pressed) : 14823
|
||||||
Font Color (Unpressed) : 65535
|
Font Color (Unpressed) : 65535
|
||||||
Font Color (Pressed) : 65535
|
Font Color (Pressed) : 65535
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -367,7 +367,23 @@ Button b0
|
|||||||
|
|
||||||
Events
|
Events
|
||||||
Touch Press Event
|
Touch Press Event
|
||||||
page cardEntities
|
//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
|
||||||
|
//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
|
||||||
|
|
||||||
Button bDown1
|
Button bDown1
|
||||||
Attributes
|
Attributes
|
||||||
@@ -389,7 +405,7 @@ Button bDown1
|
|||||||
Font ID : 1
|
Font ID : 1
|
||||||
Back. Color : 6371
|
Back. Color : 6371
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 1024
|
Back. Color (Pressed) : 14823
|
||||||
Font Color (Unpressed) : 65535
|
Font Color (Unpressed) : 65535
|
||||||
Font Color (Pressed) : 65535
|
Font Color (Pressed) : 65535
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -443,7 +459,7 @@ Button bStop1
|
|||||||
Font ID : 1
|
Font ID : 1
|
||||||
Back. Color : 6371
|
Back. Color : 6371
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 1024
|
Back. Color (Pressed) : 14823
|
||||||
Font Color (Unpressed) : 65535
|
Font Color (Unpressed) : 65535
|
||||||
Font Color (Pressed) : 65535
|
Font Color (Pressed) : 65535
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -497,7 +513,7 @@ Button bUp1
|
|||||||
Font ID : 1
|
Font ID : 1
|
||||||
Back. Color : 6371
|
Back. Color : 6371
|
||||||
Back. Picture ID (Pressed) : 65535
|
Back. Picture ID (Pressed) : 65535
|
||||||
Back. Color (Pressed) : 1024
|
Back. Color (Pressed) : 14823
|
||||||
Font Color (Unpressed) : 65535
|
Font Color (Unpressed) : 65535
|
||||||
Font Color (Pressed) : 65535
|
Font Color (Pressed) : 65535
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -547,7 +563,7 @@ Timer tmSleep
|
|||||||
{
|
{
|
||||||
screensaver.originPage.val=dp
|
screensaver.originPage.val=dp
|
||||||
sleepValue=0
|
sleepValue=0
|
||||||
page cardEntities
|
click b0,1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,7 +631,6 @@ Timer tmSerial
|
|||||||
// get value
|
// get value
|
||||||
spstr strCommand.txt,tTmp.txt,",",1
|
spstr strCommand.txt,tTmp.txt,",",1
|
||||||
covx tTmp.txt,dimValue,0,0
|
covx tTmp.txt,dimValue,0,0
|
||||||
dim=dimValue
|
|
||||||
}
|
}
|
||||||
if(tInstruction.txt=="time")
|
if(tInstruction.txt=="time")
|
||||||
{
|
{
|
||||||
@@ -633,6 +648,48 @@ Timer tmSerial
|
|||||||
spstr strCommand.txt,tTmp.txt,",",1
|
spstr strCommand.txt,tTmp.txt,",",1
|
||||||
covx tTmp.txt,sleepTimeout,0,0
|
covx tTmp.txt,sleepTimeout,0,0
|
||||||
}
|
}
|
||||||
|
if(tInstruction.txt=="pageType")
|
||||||
|
{
|
||||||
|
//command format pageType,specialPageName
|
||||||
|
//write name of speical page to tId
|
||||||
|
spstr strCommand.txt,tId.txt,",",1
|
||||||
|
//save second arg if there's one
|
||||||
|
spstr strCommand.txt,tTmp.txt,",",2
|
||||||
|
//save third arg if there's one
|
||||||
|
spstr strCommand.txt,pageIcons.tTmp2.txt,",",3
|
||||||
|
//we are going to exit this page with this command, so we have to clear the buffer, so we are not getting into a stupid loop ...
|
||||||
|
udelete payloadLength-1
|
||||||
|
bufferPos=0
|
||||||
|
if(tId.txt=="popupLight")
|
||||||
|
{
|
||||||
|
pageIcons.tTmp1.txt=tTmp.txt
|
||||||
|
page popupLight
|
||||||
|
}
|
||||||
|
if(tId.txt=="cardEntities")
|
||||||
|
{
|
||||||
|
page cardEntities
|
||||||
|
}
|
||||||
|
if(tId.txt=="cardMedia")
|
||||||
|
{
|
||||||
|
page cardMedia
|
||||||
|
}
|
||||||
|
if(tId.txt=="pageStartup")
|
||||||
|
{
|
||||||
|
page pageStartup
|
||||||
|
}
|
||||||
|
if(tId.txt=="popupNotify")
|
||||||
|
{
|
||||||
|
page popupNotify
|
||||||
|
}
|
||||||
|
if(tId.txt=="cardGrid")
|
||||||
|
{
|
||||||
|
page cardGrid
|
||||||
|
}
|
||||||
|
if(tId.txt=="cardAlarm")
|
||||||
|
{
|
||||||
|
page cardAlarm
|
||||||
|
}
|
||||||
|
}
|
||||||
// end of user code
|
// end of user code
|
||||||
udelete payloadLength-1
|
udelete payloadLength-1
|
||||||
bufferPos=0
|
bufferPos=0
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ Text tTime
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 6
|
Font ID : 5
|
||||||
Back. Color : 0
|
Back. Color : 0
|
||||||
Font Color : 65535
|
Font Color : 65535
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -283,7 +283,7 @@ Text tMainIcon
|
|||||||
Fill : solid color
|
Fill : solid color
|
||||||
Style : flat
|
Style : flat
|
||||||
Associated Keyboard : none
|
Associated Keyboard : none
|
||||||
Font ID : 4
|
Font ID : 3
|
||||||
Back. Color : 0
|
Back. Color : 0
|
||||||
Font Color : 65535
|
Font Color : 65535
|
||||||
Horizontal Alignment : center
|
Horizontal Alignment : center
|
||||||
@@ -666,8 +666,8 @@ Timer tmSerial
|
|||||||
spstr strCommand.txt,tInstruction.txt,",",0
|
spstr strCommand.txt,tInstruction.txt,",",0
|
||||||
if(tInstruction.txt=="wake")
|
if(tInstruction.txt=="wake")
|
||||||
{
|
{
|
||||||
dim=100
|
//dim=100
|
||||||
page originPage.val
|
click tc0,1
|
||||||
}
|
}
|
||||||
if(tInstruction.txt=="dimmode")
|
if(tInstruction.txt=="dimmode")
|
||||||
{
|
{
|
||||||
@@ -725,8 +725,17 @@ Timer tmSerial
|
|||||||
//tForecast2Val
|
//tForecast2Val
|
||||||
spstr strCommand.txt,tForecast2Val.txt,"?",10
|
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")
|
if(tInstruction.txt=="pageType")
|
||||||
{
|
{
|
||||||
|
dim=100
|
||||||
//command format pageType,specialPageName
|
//command format pageType,specialPageName
|
||||||
//write name of speical page to tId
|
//write name of speical page to tId
|
||||||
spstr strCommand.txt,tId.txt,",",1
|
spstr strCommand.txt,tId.txt,",",1
|
||||||
@@ -758,10 +767,22 @@ Timer tmSerial
|
|||||||
{
|
{
|
||||||
page pageStartup
|
page pageStartup
|
||||||
}
|
}
|
||||||
if(tId.txt=="pageNotify")
|
if(tId.txt=="popupNotify")
|
||||||
{
|
{
|
||||||
page popupNotify
|
page popupNotify
|
||||||
}
|
}
|
||||||
|
if(tId.txt=="cardGrid")
|
||||||
|
{
|
||||||
|
page cardGrid
|
||||||
|
}
|
||||||
|
if(tId.txt=="cardEntities")
|
||||||
|
{
|
||||||
|
page cardEntities
|
||||||
|
}
|
||||||
|
if(tId.txt=="cardAlarm")
|
||||||
|
{
|
||||||
|
page cardAlarm
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// end of user code
|
// end of user code
|
||||||
udelete payloadLength-1
|
udelete payloadLength-1
|
||||||
@@ -783,6 +804,23 @@ TouchCap tc0
|
|||||||
|
|
||||||
Events
|
Events
|
||||||
Touch Press Event
|
Touch Press Event
|
||||||
dim=100
|
//page open event
|
||||||
page originPage.val
|
// event,pageOpen,cardEntities,pageNumber
|
||||||
|
// craft command
|
||||||
|
// convert pageNumber and write to tTmp
|
||||||
|
covx nPage,tTmp.txt,0,0
|
||||||
|
tSend.txt="event,pageOpen,"+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
|
||||||
|
//dim=100
|
||||||
|
//page originPage.val
|
||||||
|
|
||||||
|
|||||||
BIN
HMI/nspanel.HMI
BIN
HMI/nspanel.HMI
Binary file not shown.
BIN
HMI/nspanel.tft
BIN
HMI/nspanel.tft
Binary file not shown.
54
README.md
54
README.md
@@ -9,8 +9,9 @@ NsPanel Lovelace UI is a Firmware for the nextion screen inside of NSPanel in th
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Entities Page with support for cover, switch, input_boolean, sensor, button, input_button and light
|
- Entities Page with support for cover, switch, input_boolean, sensor, button, scenes, input_button and light
|
||||||
- Detail Pages for Lights (Brightness and Temperature of the Light) and for Covers (Position)
|
- 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
|
- Thermostat Page
|
||||||
- Media Player Card
|
- Media Player Card
|
||||||
- Screensaver Page with Time, Date and Weather Information
|
- Screensaver Page with Time, Date and Weather Information
|
||||||
@@ -33,7 +34,7 @@ For more detailed Instructions see the following Sections:
|
|||||||
- [How It Works](#how-it-works)
|
- [How It Works](#how-it-works)
|
||||||
- [Requirements](#requirements)
|
- [Requirements](#requirements)
|
||||||
|
|
||||||
- [Installation - Home Automation Part](#installation---home-automation-part)
|
- [Installation - Home Automation Part (Homeassistant)](#installation---home-automation-part-home-assistant)
|
||||||
- [Installing AppDaemon](#installing-appdaemon)
|
- [Installing AppDaemon](#installing-appdaemon)
|
||||||
- [Installing Studio Code Server (optional, recommended)](#installing-studio-code-server-optional-recommended)
|
- [Installing Studio Code Server (optional, recommended)](#installing-studio-code-server-optional-recommended)
|
||||||
- [Installing HACS (optional, recommended)](#installing-hacs-optional-recommended)
|
- [Installing HACS (optional, recommended)](#installing-hacs-optional-recommended)
|
||||||
@@ -42,6 +43,8 @@ For more detailed Instructions see the following Sections:
|
|||||||
- [Manually](#manually)
|
- [Manually](#manually)
|
||||||
- [Installing Tasmota to your NSPanel](#installing---tasmota-to-your-nspanel)
|
- [Installing Tasmota to your NSPanel](#installing---tasmota-to-your-nspanel)
|
||||||
|
|
||||||
|
- [Installation - Home Automation Part (IoBroker)](#installation---home-automation-part-iobroker)
|
||||||
|
|
||||||
- [Installation - NSPanel Part](#installation-nspanel-part)
|
- [Installation - NSPanel Part](#installation-nspanel-part)
|
||||||
- [Flash Tasmota to your NSPanel](#flash-tasmota-to-your-nspanel)
|
- [Flash Tasmota to your NSPanel](#flash-tasmota-to-your-nspanel)
|
||||||
- [Configure Tasmota Template for NSPanel](#configure-tasmota-template-for-nspanel)
|
- [Configure Tasmota Template for NSPanel](#configure-tasmota-template-for-nspanel)
|
||||||
@@ -71,9 +74,7 @@ For more details on how the display firmware works see the [README File in the H
|
|||||||
- Running [Home Assistant Instance](https://www.home-assistant.io/installation/)
|
- Running [Home Assistant Instance](https://www.home-assistant.io/installation/)
|
||||||
- Installed [MQTT Broker](https://www.home-assistant.io/docs/mqtt/broker) alongside Homeassistant
|
- Installed [MQTT Broker](https://www.home-assistant.io/docs/mqtt/broker) alongside Homeassistant
|
||||||
|
|
||||||
## Installation - Home Automation Part
|
## Installation - Home Automation Part (Home Assistant)
|
||||||
|
|
||||||
### Installing Home Assistant
|
|
||||||
|
|
||||||
### Installing AppDaemon
|
### Installing AppDaemon
|
||||||
|
|
||||||
@@ -84,6 +85,13 @@ The easiest way to install it is through Home Assistant's Supervisor Add-on Stor
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
#### Add babel package to AppDaemon Container (Optional)
|
||||||
|
|
||||||
|
For localisation (date in your local language) you need to add the python package babel to your AppDaemon Installation.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
### Installing Studio Code Server (optional, recommended)
|
### Installing Studio Code Server (optional, recommended)
|
||||||
|
|
||||||
You will need a way to edit the `apps.yaml` config file in the Appdaemon folder.
|
You will need a way to edit the `apps.yaml` config file in the Appdaemon folder.
|
||||||
@@ -136,6 +144,11 @@ Installing the Backend Application manually can be summarized by putting the con
|
|||||||
directory of your AppDaemon installation.
|
directory of your AppDaemon installation.
|
||||||
|
|
||||||
|
|
||||||
|
## Installation - Home Automation Part (IoBroker)
|
||||||
|
|
||||||
|
If you are looking for an ioBroker Integration instead of HomeAssistant take a look into the [Readme](ioBroker/README.md) of the iobroker folder.
|
||||||
|
Thanks to [britzelpuf](https://github.com/britzelpuf) for this integration.
|
||||||
|
|
||||||
## Installation - NSPanel Part
|
## Installation - NSPanel Part
|
||||||
|
|
||||||
This section describes how to free your nspanel from stock firmware and get it ready for Lovelace UI 🎉
|
This section describes how to free your nspanel from stock firmware and get it ready for Lovelace UI 🎉
|
||||||
@@ -179,8 +192,6 @@ See Tasmota [MQTT Documentation](https://tasmota.github.io/docs/MQTT/) for more
|
|||||||
|
|
||||||
Upload the nspanel.tft from the lastest release to a Webserver (for example www folder of Home Assistant) and execute the following command in Tasmota Console. (Development Version: [tft file from HMI folder](HMI/nspanel.tft))
|
Upload the nspanel.tft from the lastest release to a Webserver (for example www folder of Home Assistant) and execute the following command in Tasmota Console. (Development Version: [tft file from HMI folder](HMI/nspanel.tft))
|
||||||
|
|
||||||
**Webserver needs to support HTTP Range Header Requests, python2/3 http server doesn't work**
|
|
||||||
|
|
||||||
**Webserver must be HTTP, HTTPS is not supported, due to limitations of berry lang on tasmota**
|
**Webserver must be HTTP, HTTPS is not supported, due to limitations of berry lang on tasmota**
|
||||||
|
|
||||||
`FlashNextion http://ip-address-of-your-homeassistant:8123/local/nspanel.tft`
|
`FlashNextion http://ip-address-of-your-homeassistant:8123/local/nspanel.tft`
|
||||||
@@ -239,16 +250,19 @@ nspanel-1:
|
|||||||
config:
|
config:
|
||||||
panelRecvTopic: "tele/tasmota_your_mqtt_topic/RESULT"
|
panelRecvTopic: "tele/tasmota_your_mqtt_topic/RESULT"
|
||||||
panelSendTopic: "cmnd/tasmota_your_mqtt_topic/CustomSend"
|
panelSendTopic: "cmnd/tasmota_your_mqtt_topic/CustomSend"
|
||||||
timeoutScreensaver: 15 #in seconds, values between 5 and 60 are allowed
|
updateMode: auto-notify # possible values are auto, auto-notify and manual
|
||||||
|
timeoutScreensaver: 15 #in seconds
|
||||||
#brightnessScreensaver: 10
|
#brightnessScreensaver: 10
|
||||||
brightnessScreensaver:
|
brightnessScreensaver:
|
||||||
- time: "7:00:00"
|
- time: "7:00:00"
|
||||||
value: 10
|
value: 10
|
||||||
- time: "23:00:00"
|
- time: "23:00:00"
|
||||||
value: 0
|
value: 0
|
||||||
locale: "de_DE"
|
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
|
||||||
timeFormat: "%H:%M"
|
timeFormat: "%H:%M"
|
||||||
dateFormat: "%A, %d. %B %Y"
|
dateFormat: "%A, %d. %B %Y" # ignored if babel python package is installed
|
||||||
weatherEntity: weather.example
|
weatherEntity: weather.example
|
||||||
pages:
|
pages:
|
||||||
- type: cardEntities
|
- type: cardEntities
|
||||||
@@ -264,14 +278,23 @@ nspanel-1:
|
|||||||
- button.example_button
|
- button.example_button
|
||||||
- input_button.example_input_button
|
- input_button.example_input_button
|
||||||
- light.light_example
|
- light.light_example
|
||||||
- delete # To make sure we don't keep buttons from previous page (read this as 'empty')
|
- delete # (read this as 'empty')
|
||||||
- type: cardEntities
|
- type: cardEntities
|
||||||
heading: Example Page 3
|
heading: Example Page 3
|
||||||
items:
|
items:
|
||||||
- scene.some_scene
|
- scene.example_scene
|
||||||
- scene.moodlights
|
|
||||||
- delete
|
- delete
|
||||||
- delete
|
- delete
|
||||||
|
- delete
|
||||||
|
- type: cardGrid
|
||||||
|
heading: Example Page 4
|
||||||
|
items:
|
||||||
|
- light.light_example
|
||||||
|
- button.example_button
|
||||||
|
- cover.example_cover
|
||||||
|
- scene.example_scene
|
||||||
|
- switch.example_switch
|
||||||
|
- delete
|
||||||
- type: cardThermo
|
- type: cardThermo
|
||||||
heading: Exmaple Thermostat
|
heading: Exmaple Thermostat
|
||||||
item: climate.example_climate
|
item: climate.example_climate
|
||||||
@@ -322,8 +345,7 @@ Since release 1.1 you can update the berry driver directly from the Tasmota Cons
|
|||||||
### Flashing of the Display Firmware with FlashNextion doesn't work
|
### Flashing of the Display Firmware with FlashNextion doesn't work
|
||||||
|
|
||||||
1. Make sure to use the [tasmota32-nspanel.bin](https://github.com/tasmota/install/raw/main/firmware/unofficial/tasmota32-nspanel.bin) Tasmota build.
|
1. Make sure to use the [tasmota32-nspanel.bin](https://github.com/tasmota/install/raw/main/firmware/unofficial/tasmota32-nspanel.bin) Tasmota build.
|
||||||
2. Make sure to use an WebServer which supports http range requests like HomeAssistant, apache2 or nginx for exmaple.
|
2. Make sure to use HTTP and **not HTTPS**
|
||||||
3. Make sure to use HTTP and **not HTTPS**
|
|
||||||
|
|
||||||
### My flashing doesn't start at all
|
### My flashing doesn't start at all
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,19 @@ nspanel:
|
|||||||
config:
|
config:
|
||||||
panelRecvTopic: "tele/tasmota_your_mqtt_topic/RESULT"
|
panelRecvTopic: "tele/tasmota_your_mqtt_topic/RESULT"
|
||||||
panelSendTopic: "cmnd/tasmota_your_mqtt_topic/CustomSend"
|
panelSendTopic: "cmnd/tasmota_your_mqtt_topic/CustomSend"
|
||||||
timeoutScreensaver: 15 #in seconds, values between 5 and 60 are allowed
|
updateMode: auto-notify # possible values are auto, auto-notify and manual
|
||||||
|
timeoutScreensaver: 15 #in seconds
|
||||||
#brightnessScreensaver: 10
|
#brightnessScreensaver: 10
|
||||||
brightnessScreensaver:
|
brightnessScreensaver:
|
||||||
- time: "7:00:00"
|
- time: "7:00:00"
|
||||||
value: 10
|
value: 10
|
||||||
- time: "23:00:00"
|
- time: "23:00:00"
|
||||||
value: 0
|
value: 0
|
||||||
locale: "de_DE"
|
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
|
||||||
timeFormat: "%H:%M"
|
timeFormat: "%H:%M"
|
||||||
dateFormat: "%A, %d. %B %Y"
|
dateFormat: "%A, %d. %B %Y" # ignored if babel python package is installed
|
||||||
weatherEntity: weather.example
|
weatherEntity: weather.example
|
||||||
pages:
|
pages:
|
||||||
- type: cardEntities
|
- type: cardEntities
|
||||||
@@ -30,14 +33,23 @@ nspanel:
|
|||||||
- button.example_button
|
- button.example_button
|
||||||
- input_button.example_input_button
|
- input_button.example_input_button
|
||||||
- light.light_example
|
- light.light_example
|
||||||
- delete # To make sure we don't keep buttons from previous page (read this as 'empty')
|
- delete # (read this as 'empty')
|
||||||
- type: cardEntities
|
- type: cardEntities
|
||||||
heading: Example Page 3
|
heading: Example Page 3
|
||||||
items:
|
items:
|
||||||
- scene.some_scene
|
- scene.example_scene
|
||||||
- scene.moodlights
|
|
||||||
- delete
|
- delete
|
||||||
- delete
|
- delete
|
||||||
|
- delete
|
||||||
|
- type: cardGrid
|
||||||
|
heading: Example Page 4
|
||||||
|
items:
|
||||||
|
- light.light_example
|
||||||
|
- button.example_button
|
||||||
|
- cover.example_cover
|
||||||
|
- scene.example_scene
|
||||||
|
- switch.example_switch
|
||||||
|
- delete
|
||||||
- type: cardThermo
|
- type: cardThermo
|
||||||
heading: Exmaple Thermostat
|
heading: Exmaple Thermostat
|
||||||
item: climate.example_climate
|
item: climate.example_climate
|
||||||
|
|||||||
40
apps/nspanel-lovelace-ui/helper.py
Normal file
40
apps/nspanel-lovelace-ui/helper.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import colorsys
|
||||||
|
import math
|
||||||
|
|
||||||
|
def scale(val, src, dst):
|
||||||
|
"""
|
||||||
|
Scale the given value from the scale of src to the scale of dst.
|
||||||
|
"""
|
||||||
|
return ((val - src[0]) / (src[1]-src[0])) * (dst[1]-dst[0]) + dst[0]
|
||||||
|
|
||||||
|
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
|
||||||
|
y = round((r - y) / r * 100) / 100
|
||||||
|
|
||||||
|
r = math.sqrt(x*x + y*y)
|
||||||
|
sat = 0
|
||||||
|
if (r > 1):
|
||||||
|
sat = 0
|
||||||
|
else:
|
||||||
|
sat = r
|
||||||
|
hsv = (math.degrees(math.atan2(y, x))%360/360, sat, 1)
|
||||||
|
rgb = hsv2rgb(hsv[0],hsv[1],hsv[2])
|
||||||
|
return rgb
|
||||||
|
|
||||||
|
def rgb_brightness(rgb_color, brightness):
|
||||||
|
red = rgb_color[0]/255*brightness
|
||||||
|
green = rgb_color[1]/255*brightness
|
||||||
|
blue = rgb_color[2]/255*brightness
|
||||||
|
return [red, green, blue]
|
||||||
|
|
||||||
|
def rgb_dec565(rgb_color):
|
||||||
|
red = rgb_color[0]
|
||||||
|
green = rgb_color[1]
|
||||||
|
blue = rgb_color[2]
|
||||||
|
# take in the red, green and blue values (0-255) as 8 bit values and then combine
|
||||||
|
# and shift them to make them a 16 bit dec value in 565 format.
|
||||||
|
return ((int(red / 255 * 31) << 11) | (int(green / 255 * 63) << 5) | (int(blue / 255 * 31)))
|
||||||
45
apps/nspanel-lovelace-ui/icon_mapper.py
Normal file
45
apps/nspanel-lovelace-ui/icon_mapper.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
icons = {
|
||||||
|
'alert-circle-outline': 0,
|
||||||
|
'lightbulb': 1,
|
||||||
|
'thermometer': 2,
|
||||||
|
'gesture-tap-button': 3,
|
||||||
|
'flash': 4,
|
||||||
|
'music': 5,
|
||||||
|
'check-circle-outline': 6,
|
||||||
|
'close-circle-outline': 7,
|
||||||
|
'pause': 8,
|
||||||
|
'play': 9,
|
||||||
|
'palette': 10,
|
||||||
|
'window-open': 11,
|
||||||
|
'weather-cloudy': 12,
|
||||||
|
'weather-fog': 13,
|
||||||
|
'weather-hail': 14,
|
||||||
|
'weather-lightning': 15,
|
||||||
|
'weather-lightning-rainy': 16,
|
||||||
|
'weather-night': 17,
|
||||||
|
'weather-partly-cloudy': 18,
|
||||||
|
'weather-pouring': 19,
|
||||||
|
'weather-rainy': 20,
|
||||||
|
'weather-snowy': 21,
|
||||||
|
'weather-snowy-rainy': 22,
|
||||||
|
'weather-sunny': 23,
|
||||||
|
'weather-windy': 24,
|
||||||
|
'weather-windy-variant': 25,
|
||||||
|
'water-percent': 26,
|
||||||
|
'power': 27,
|
||||||
|
'fire': 28,
|
||||||
|
'calendar-sync': 29,
|
||||||
|
'fan': 30,
|
||||||
|
'snowflake': 31,
|
||||||
|
'solar-power': 32,
|
||||||
|
'battery-charging-medium': 33,
|
||||||
|
'battery-medium': 34,
|
||||||
|
'shield-home': 35,
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_icon_id(ma_name):
|
||||||
|
if ma_name in icons:
|
||||||
|
return icons[ma_name]
|
||||||
|
else:
|
||||||
|
return icons["alert-circle-outline"]
|
||||||
|
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
import json
|
import json
|
||||||
import datetime
|
import datetime
|
||||||
import hassapi as hass
|
import hassapi as hass
|
||||||
|
from helper import scale, pos_to_color, rgb_dec565, rgb_brightness
|
||||||
|
from icon_mapper import get_icon_id
|
||||||
|
|
||||||
import math
|
# check Babel
|
||||||
import colorsys
|
import importlib
|
||||||
|
babel_spec = importlib.util.find_spec("babel")
|
||||||
|
if babel_spec is not None:
|
||||||
|
import babel.dates
|
||||||
|
|
||||||
class NsPanelLovelaceUIManager(hass.Hass):
|
class NsPanelLovelaceUIManager(hass.Hass):
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
@@ -11,6 +16,68 @@ class NsPanelLovelaceUIManager(hass.Hass):
|
|||||||
data = self.args["config"]
|
data = self.args["config"]
|
||||||
NsPanelLovelaceUI(self, data)
|
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:
|
class NsPanelLovelaceUI:
|
||||||
def __init__(self, api, config):
|
def __init__(self, api, config):
|
||||||
self.api = api
|
self.api = api
|
||||||
@@ -26,10 +93,19 @@ class NsPanelLovelaceUI:
|
|||||||
self.mqtt.mqtt_subscribe(topic=self.config["panelRecvTopic"])
|
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')
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
# Request Tasmota Driver Version
|
||||||
|
self.mqtt.mqtt_publish(self.config["panelSendTopic"].replace("CustomSend", "GetDriverVersion"), "x")
|
||||||
|
|
||||||
# send panel back to startup page on restart of this script
|
# send panel back to startup page on restart of this script
|
||||||
self.send_mqtt_msg("pageType,pageStartup")
|
self.send_mqtt_msg("pageType,pageStartup")
|
||||||
|
|
||||||
|
|
||||||
# Setup time callback
|
# Setup time callback
|
||||||
time = datetime.time(0, 0, 0)
|
time = datetime.time(0, 0, 0)
|
||||||
self.api.run_minutely(self.update_time, time)
|
self.api.run_minutely(self.update_time, time)
|
||||||
@@ -37,6 +113,7 @@ class NsPanelLovelaceUI:
|
|||||||
# Setup date callback
|
# Setup date callback
|
||||||
time = datetime.time(0, 0, 0)
|
time = datetime.time(0, 0, 0)
|
||||||
self.api.run_daily(self.update_date, time)
|
self.api.run_daily(self.update_date, time)
|
||||||
|
# send date update in case config has been changed
|
||||||
self.update_date("")
|
self.update_date("")
|
||||||
|
|
||||||
# Setup weather callback
|
# Setup weather callback
|
||||||
@@ -60,32 +137,40 @@ class NsPanelLovelaceUI:
|
|||||||
# send screensaver brightness in case config has changed
|
# send screensaver brightness in case config has changed
|
||||||
self.update_screensaver_brightness(kwargs={"value": self.current_screensaver_brightness})
|
self.update_screensaver_brightness(kwargs={"value": self.current_screensaver_brightness})
|
||||||
|
|
||||||
# send date update in case config has been changed
|
|
||||||
self.update_date("")
|
|
||||||
|
|
||||||
|
|
||||||
# register callbacks
|
# register callbacks
|
||||||
self.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):
|
def handle_mqtt_incoming_message(self, event_name, data, kwargs):
|
||||||
# Parse Json Message from Tasmota and strip out message from nextion display
|
# Parse Json Message from Tasmota and strip out message from nextion display
|
||||||
data = json.loads(data["payload"])
|
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):
|
if("CustomRecv" not in data):
|
||||||
self.api.log("Received Message from Tasmota: %s", data, level="DEBUG")
|
self.api.log("Received Message from Tasmota, but not from nextion screen: %s", data, level="DEBUG")
|
||||||
return
|
return
|
||||||
msg = data["CustomRecv"]
|
msg = data["CustomRecv"]
|
||||||
self.api.log("Received Message from Tasmota: %s", msg, level="DEBUG")
|
self.api.log("Received Message from Tasmota: %s", msg) #, level="DEBUG"
|
||||||
|
|
||||||
# Split message into parts seperated by ","
|
# Split message into parts seperated by ","
|
||||||
msg = msg.split(",")
|
msg = msg.split(",")
|
||||||
|
|
||||||
# run action based on received command
|
# run action based on received command
|
||||||
# TODO: replace with match case after appdeamon container swiched to python 3.10 - https://pakstech.com/blog/python-switch-case/ - https://www.python.org/dev/peps/pep-0636/
|
|
||||||
if msg[0] == "event":
|
if msg[0] == "event":
|
||||||
|
|
||||||
if msg[1] == "startup":
|
if msg[1] == "startup":
|
||||||
self.api.log("Handling startup event", level="DEBUG")
|
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
|
# send date and time
|
||||||
self.update_time("")
|
self.update_time("")
|
||||||
self.update_date("")
|
self.update_date("")
|
||||||
@@ -97,19 +182,20 @@ class NsPanelLovelaceUI:
|
|||||||
# send screensaver brightness
|
# send screensaver brightness
|
||||||
self.update_screensaver_brightness(kwargs={"value": self.current_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
|
# send messages for current page
|
||||||
page_type = self.config["pages"][self.current_page_nr]["type"]
|
if not msg_send:
|
||||||
self.generate_page(self.current_page_nr, page_type)
|
self.generate_page(self.current_page_nr)
|
||||||
|
|
||||||
if msg[1] == "pageOpen":
|
if msg[1] == "pageOpen":
|
||||||
# Calculate current page
|
# Calculate current page
|
||||||
recv_page = int(msg[2])
|
recv_page = int(msg[2])
|
||||||
self.current_page_nr = recv_page % len(self.config["pages"])
|
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")
|
self.api.log("Received pageOpen command, raw page: %i, calc page: %i", recv_page, self.current_page_nr, level="DEBUG")
|
||||||
# get type of current page
|
|
||||||
page_type = self.config["pages"][self.current_page_nr]["type"]
|
|
||||||
# generate commands for current page
|
# generate commands for current page
|
||||||
self.generate_page(self.current_page_nr, page_type)
|
self.generate_page(self.current_page_nr)
|
||||||
|
|
||||||
if msg[1] == "buttonPress":
|
if msg[1] == "buttonPress":
|
||||||
entity_id = msg[4]
|
entity_id = msg[4]
|
||||||
@@ -119,6 +205,14 @@ class NsPanelLovelaceUI:
|
|||||||
else:
|
else:
|
||||||
value = None
|
value = None
|
||||||
self.handle_button_press(entity_id, btype, value)
|
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":
|
if msg[1] == "pageOpenDetail":
|
||||||
self.api.log("Received pageOpenDetail command", level="DEBUG")
|
self.api.log("Received pageOpenDetail command", level="DEBUG")
|
||||||
@@ -132,18 +226,24 @@ class NsPanelLovelaceUI:
|
|||||||
if msg[1] == "screensaverOpen":
|
if msg[1] == "screensaverOpen":
|
||||||
self.update_screensaver_weather("")
|
self.update_screensaver_weather("")
|
||||||
|
|
||||||
def send_mqtt_msg(self,msg):
|
|
||||||
self.api.log("Send Message from Tasmota: %s", msg, level="DEBUG")
|
|
||||||
self.mqtt.mqtt_publish(self.config["panelSendTopic"], msg)
|
|
||||||
|
|
||||||
def update_time(self, kwargs):
|
def update_time(self, kwargs):
|
||||||
time = datetime.datetime.now().strftime(self.config["timeFormat"])
|
time = datetime.datetime.now().strftime(self.config["timeFormat"])
|
||||||
self.send_mqtt_msg("time,{0}".format(time))
|
self.send_mqtt_msg(f"time,{time}")
|
||||||
|
|
||||||
def update_date(self, kwargs):
|
def update_date(self, kwargs):
|
||||||
# TODO: implement localization of date
|
global babel_spec
|
||||||
date = datetime.datetime.now().strftime(self.config["dateFormat"])
|
if babel_spec is not None:
|
||||||
self.send_mqtt_msg("date,?{0}".format(date))
|
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):
|
def update_screensaver_brightness(self, kwargs):
|
||||||
self.current_screensaver_brightness = kwargs['value']
|
self.current_screensaver_brightness = kwargs['value']
|
||||||
@@ -155,92 +255,114 @@ class NsPanelLovelaceUI:
|
|||||||
we = self.api.get_entity(self.config["weatherEntity"])
|
we = self.api.get_entity(self.config["weatherEntity"])
|
||||||
unit = "°C"
|
unit = "°C"
|
||||||
|
|
||||||
|
# this maps possible states from ha to material design icon names
|
||||||
weathericons = {
|
weathericons = {
|
||||||
'clear-night': 17,
|
'clear-night': 'weather-night',
|
||||||
'cloudy': 12,
|
'cloudy': 'weather-cloudy',
|
||||||
'exceptional': 11,
|
'exceptional': 'alert-circle-outline',
|
||||||
'fog': 13,
|
'fog': 'weather-fog',
|
||||||
'hail': 14,
|
'hail': 'weather-hail',
|
||||||
'lightning': 15,
|
'lightning': 'weather-lightning',
|
||||||
'lightning-rainy': 16,
|
'lightning-rainy': 'weather-lightning-rainy',
|
||||||
'partlycloudy': 18,
|
'partlycloudy': 'weather-partly-cloudy',
|
||||||
'pouring': 19,
|
'pouring': 'weather-pouring',
|
||||||
'rainy': 20,
|
'rainy': 'weather-rainy',
|
||||||
'snowy': 21,
|
'snowy': 'weather-snowy',
|
||||||
'snowy-rainy': 22,
|
'snowy-rainy': 'weather-snowy-rainy',
|
||||||
'sunny': 23,
|
'sunny': 'weather-sunny',
|
||||||
'windy': 24,
|
'windy': 'weather-windy',
|
||||||
'windy-variant': 25
|
'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 = we.attributes.forecast[0]['datetime']
|
||||||
o1 = datetime.datetime.fromisoformat(o1)
|
o1 = datetime.datetime.fromisoformat(o1)
|
||||||
o1 = o1.strftime("%a")
|
|
||||||
i1 = weathericons[we.attributes.forecast[0]['condition']]
|
|
||||||
u1 = we.attributes.forecast[0]['temperature']
|
|
||||||
o2 = we.attributes.forecast[1]['datetime']
|
o2 = we.attributes.forecast[1]['datetime']
|
||||||
o2 = datetime.datetime.fromisoformat(o2)
|
o2 = datetime.datetime.fromisoformat(o2)
|
||||||
o2 = o2.strftime("%a")
|
global babel_spec
|
||||||
i2 = weathericons[we.attributes.forecast[1]['condition']]
|
if babel_spec is not None:
|
||||||
u2 = we.attributes.forecast[1]['temperature']
|
o1 = babel.dates.format_date(o1, "E", locale=self.config["locale"])
|
||||||
self.send_mqtt_msg(f"weatherUpdate,?{weathericons[we.state]}?{we.attributes.temperature}{unit}?{26}?{we.attributes.humidity} %?{o1}?{i1}?{u1}?{o2}?{i2}?{u2}")
|
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 scale(self, val, src, dst):
|
|
||||||
"""
|
|
||||||
Scale the given value from the scale of src to the scale of dst.
|
|
||||||
"""
|
|
||||||
return ((val - src[0]) / (src[1]-src[0])) * (dst[1]-dst[0]) + dst[0]
|
|
||||||
|
|
||||||
def handle_button_press(self, entity_id, btype, optVal=None):
|
def handle_button_press(self, entity_id, btype, optVal=None):
|
||||||
if(btype == "OnOff"):
|
|
||||||
if(optVal == "1"):
|
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)
|
self.api.turn_on(entity_id)
|
||||||
else:
|
else:
|
||||||
self.api.turn_off(entity_id)
|
self.api.turn_off(entity_id)
|
||||||
if(btype == "up"):
|
if btype == "up":
|
||||||
self.api.get_entity(entity_id).call_service("open_cover")
|
self.api.get_entity(entity_id).call_service("open_cover")
|
||||||
if(btype == "stop"):
|
if btype == "stop":
|
||||||
self.api.get_entity(entity_id).call_service("stop_cover")
|
self.api.get_entity(entity_id).call_service("stop_cover")
|
||||||
if(btype == "down"):
|
if btype == "down":
|
||||||
self.api.get_entity(entity_id).call_service("close_cover")
|
self.api.get_entity(entity_id).call_service("close_cover")
|
||||||
|
|
||||||
if(btype == "button"):
|
if btype == "button":
|
||||||
if(entity_id.startswith('scene')):
|
if entity_id.startswith('scene'):
|
||||||
self.api.get_entity(entity_id).call_service("turn_on")
|
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:
|
else:
|
||||||
self.api.get_entity(entity_id).call_service("press")
|
self.api.get_entity(entity_id).call_service("press")
|
||||||
|
|
||||||
if(btype == "media-next"):
|
if btype == "media-next":
|
||||||
self.api.get_entity(entity_id).call_service("media_next_track")
|
self.api.get_entity(entity_id).call_service("media_next_track")
|
||||||
if(btype == "media-back"):
|
if btype == "media-back":
|
||||||
self.api.get_entity(entity_id).call_service("media_previous_track")
|
self.api.get_entity(entity_id).call_service("media_previous_track")
|
||||||
if(btype == "media-pause"):
|
if btype == "media-pause":
|
||||||
self.api.get_entity(entity_id).call_service("media_play_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"):
|
|
||||||
|
if btype == "brightnessSlider":
|
||||||
# scale 0-100 to ha brightness range
|
# scale 0-100 to ha brightness range
|
||||||
brightness = int(self.scale(int(optVal),(0,100),(0,255)))
|
brightness = int(scale(int(optVal),(0,100),(0,255)))
|
||||||
self.api.get_entity(entity_id).call_service("turn_on", brightness=brightness)
|
self.api.get_entity(entity_id).call_service("turn_on", brightness=brightness)
|
||||||
|
|
||||||
if(btype == "colorTempSlider"):
|
if btype == "colorTempSlider":
|
||||||
entity = self.api.get_entity(entity_id)
|
entity = self.api.get_entity(entity_id)
|
||||||
#scale 0-100 from slider to color range of lamp
|
#scale 0-100 from slider to color range of lamp
|
||||||
color_val = self.scale(int(optVal), (0, 100), (entity.attributes.min_mireds, entity.attributes.max_mireds))
|
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)
|
self.api.get_entity(entity_id).call_service("turn_on", color_temp=color_val)
|
||||||
|
|
||||||
if(btype == "colorWheel"):
|
if btype == "colorWheel":
|
||||||
self.api.log(optVal)
|
self.api.log(optVal)
|
||||||
optVal = optVal.split('|')
|
optVal = optVal.split('|')
|
||||||
color = self.pos_to_color(int(optVal[0]), int(optVal[1]))
|
color = pos_to_color(int(optVal[0]), int(optVal[1]))
|
||||||
self.api.log(color)
|
self.api.log(color)
|
||||||
self.api.get_entity(entity_id).call_service("turn_on", rgb_color=color)
|
self.api.get_entity(entity_id).call_service("turn_on", rgb_color=color)
|
||||||
|
|
||||||
if(btype == "positionSlider"):
|
if btype == "positionSlider":
|
||||||
pos = int(optVal)
|
pos = int(optVal)
|
||||||
self.api.get_entity(entity_id).call_service("set_cover_position", position=pos)
|
self.api.get_entity(entity_id).call_service("set_cover_position", position=pos)
|
||||||
|
|
||||||
if(btype == "volumeSlider"):
|
if btype == "volumeSlider":
|
||||||
pos = int(optVal)
|
pos = int(optVal)
|
||||||
# HA wants this value between 0 and 1 as float
|
# HA wants this value between 0 and 1 as float
|
||||||
pos = pos/100
|
pos = pos/100
|
||||||
@@ -279,14 +401,14 @@ class NsPanelLovelaceUI:
|
|||||||
|
|
||||||
page_type = current_page_config["type"]
|
page_type = current_page_config["type"]
|
||||||
|
|
||||||
self.api.log("Got state_callback from {0}".format(entity), level="DEBUG")
|
self.api.log(f"Got state_callback from {entity}", level="DEBUG")
|
||||||
|
|
||||||
if page_type == "cardEntities":
|
if page_type in ["cardEntities", "cardGrid"]:
|
||||||
items = current_page_config["items"]
|
items = current_page_config["items"]
|
||||||
if entity in items:
|
if entity in items:
|
||||||
self.api.log("State change on current page for {0}".format(entity), level="DEBUG")
|
self.api.log(f"State change on current page for {entity}", level="DEBUG")
|
||||||
# send update of the item on page
|
# send update of the page
|
||||||
command = self.generate_entities_item(entity, items.index(entity)+1)
|
command = self.generate_entities_page(items)
|
||||||
self.send_mqtt_msg(command)
|
self.send_mqtt_msg(command)
|
||||||
if(entity.startswith("cover")):
|
if(entity.startswith("cover")):
|
||||||
self.generate_detail_page("popupShutter", entity)
|
self.generate_detail_page("popupShutter", entity)
|
||||||
@@ -297,7 +419,7 @@ class NsPanelLovelaceUI:
|
|||||||
|
|
||||||
if page_type == "cardThermo" or page_type == "cardMedia":
|
if page_type == "cardThermo" or page_type == "cardMedia":
|
||||||
if entity == current_page_config["item"]:
|
if entity == current_page_config["item"]:
|
||||||
self.api.log("State change on current page for {0}".format(entity), level="DEBUG")
|
self.api.log(f"State change on current page for {entity}", level="DEBUG")
|
||||||
# send update of the whole page
|
# send update of the whole page
|
||||||
if page_type == "cardThermo":
|
if page_type == "cardThermo":
|
||||||
self.send_mqtt_msg(self.generate_thermo_page(entity))
|
self.send_mqtt_msg(self.generate_thermo_page(entity))
|
||||||
@@ -307,131 +429,31 @@ class NsPanelLovelaceUI:
|
|||||||
return
|
return
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def generate_page(self, page_number):
|
||||||
|
# get type of current page
|
||||||
# TODO: Call Method for refresh of the item/page of the current entity
|
page_type = self.config["pages"][self.current_page_nr]["type"]
|
||||||
|
|
||||||
def generate_entities_item(self, item, item_nr):
|
|
||||||
|
|
||||||
# type of 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 "entityUpd,{0},{1}".format(item_nr, item_type)
|
|
||||||
|
|
||||||
if not self.api.entity_exists(item):
|
|
||||||
return f"entityUpd,{item_nr},text,{item},11,Not found check, apps.yaml"
|
|
||||||
entity = self.api.get_entity(item)
|
|
||||||
name = entity.attributes.friendly_name
|
|
||||||
|
|
||||||
if item_type == "cover":
|
|
||||||
return "entityUpd,{0},{1},{2},{3},{4}".format(item_nr, "shutter", item, 0, name)
|
|
||||||
|
|
||||||
if item_type == "light":
|
|
||||||
switch_val = 1 if entity.state == "on" else 0
|
|
||||||
return "entityUpd,{0},{1},{2},{3},{4},{5}".format(item_nr, item_type, item, 1, name, switch_val)
|
|
||||||
|
|
||||||
if item_type == "switch" or item_type == "input_boolean":
|
|
||||||
switch_val = 1 if entity.state == "on" else 0
|
|
||||||
icon_id = 4
|
|
||||||
if item_type == "input_boolean" and switch_val == 1:
|
|
||||||
icon_id = 6
|
|
||||||
else:
|
|
||||||
icon_id = 7
|
|
||||||
|
|
||||||
return "entityUpd,{0},{1},{2},{3},{4},{5}".format(item_nr, "switch", item, icon_id, name, switch_val)
|
|
||||||
|
|
||||||
if item_type == "sensor":
|
|
||||||
icon_id = 0
|
|
||||||
unit_of_measurement = ""
|
|
||||||
icon_mapping = {
|
|
||||||
"temperature": 2,
|
|
||||||
"power": 4
|
|
||||||
}
|
|
||||||
if "device_class" in entity.attributes:
|
|
||||||
if entity.attributes.device_class in icon_mapping:
|
|
||||||
icon_id = icon_mapping[entity.attributes.device_class]
|
|
||||||
|
|
||||||
if "unit_of_measurement" in entity.attributes:
|
|
||||||
unit_of_measurement = entity.attributes.unit_of_measurement
|
|
||||||
|
|
||||||
value = entity.state + " " + unit_of_measurement
|
|
||||||
return "entityUpd,{0},{1},{2},{3},{4},{5}".format(item_nr, "text", item, icon_id, name, value)
|
|
||||||
|
|
||||||
if item_type == "button" or item_type == "input_button":
|
|
||||||
return "entityUpd,{0},{1},{2},{3},{4},{5}".format(item_nr, "button", item, 3, name, "PRESS")
|
|
||||||
|
|
||||||
if item_type == "scene":
|
|
||||||
return "entityUpd,{0},{1},{2},{3},{4},{5}".format(item_nr, "button", item, 10, name, "ACTIVATE")
|
|
||||||
|
|
||||||
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(entity.attributes.current_temperature*10)
|
|
||||||
dest_temp = int(entity.attributes.temperature*10)
|
|
||||||
status = ""
|
|
||||||
if "hvac_action" in entity.attributes:
|
|
||||||
status = entity.attributes.hvac_action
|
|
||||||
min_temp = int(entity.attributes.min_temp*10)
|
|
||||||
max_temp = int(entity.attributes.max_temp*10)
|
|
||||||
step_temp = int(0.5*10)
|
|
||||||
|
|
||||||
return f"entityUpd,{item},{heading},{current_temp},{dest_temp},{status},{min_temp},{max_temp},{step_temp}"
|
|
||||||
|
|
||||||
def generate_media_page(self, item):
|
|
||||||
|
|
||||||
if not self.api.entity_exists(item):
|
|
||||||
return f"entityUpd,|{item}|Not found|11|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
|
|
||||||
#iconplaypause = 9
|
|
||||||
if "media_content_type" in entity.attributes:
|
|
||||||
if entity.attributes.media_content_type == "music":
|
|
||||||
icon = 5
|
|
||||||
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 = 8
|
|
||||||
else:
|
|
||||||
iconplaypause = 9
|
|
||||||
|
|
||||||
return f"entityUpd,|{item}|{heading}|{icon}|{title}|{author}|{volume}|{iconplaypause}"
|
|
||||||
|
|
||||||
|
|
||||||
def generate_page(self, page_number, page_type):
|
|
||||||
self.api.log("Generating page commands for page %i with type %s", self.current_page_nr, page_type, level="DEBUG")
|
self.api.log("Generating page commands for page %i with type %s", self.current_page_nr, page_type, level="DEBUG")
|
||||||
if page_type == "cardEntities":
|
if page_type == "cardEntities":
|
||||||
# Send page type
|
# Send page type
|
||||||
self.send_mqtt_msg("pageType,{0}".format(page_type))
|
self.send_mqtt_msg(f"pageType,{page_type}")
|
||||||
# Set Heading of Page
|
# Set Heading of Page
|
||||||
self.send_mqtt_msg("entityUpdHeading,{0}".format(self.config["pages"][self.current_page_nr]["heading"]))
|
self.send_mqtt_msg(f"entityUpdHeading,{self.config['pages'][self.current_page_nr]['heading']}")
|
||||||
|
|
||||||
# Set Items of Page
|
command = self.generate_entities_page(self.config["pages"][self.current_page_nr]["items"])
|
||||||
current_item_nr = 0
|
self.send_mqtt_msg(command)
|
||||||
for item in self.config["pages"][self.current_page_nr]["items"]:
|
|
||||||
current_item_nr += 1
|
if page_type == "cardGrid":
|
||||||
command = self.generate_entities_item(item, current_item_nr)
|
# Send page type
|
||||||
self.send_mqtt_msg(command)
|
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":
|
if page_type == "cardThermo":
|
||||||
# Send page type
|
# Send page type
|
||||||
self.send_mqtt_msg("pageType,{0}".format(page_type))
|
self.send_mqtt_msg(f"pageType,{page_type}")
|
||||||
command = self.generate_thermo_page(self.config["pages"][self.current_page_nr]["item"])
|
command = self.generate_thermo_page(self.config["pages"][self.current_page_nr]["item"])
|
||||||
self.send_mqtt_msg(command)
|
self.send_mqtt_msg(command)
|
||||||
|
|
||||||
@@ -441,54 +463,222 @@ class NsPanelLovelaceUI:
|
|||||||
command = self.generate_media_page(self.config["pages"][self.current_page_nr]["item"])
|
command = self.generate_media_page(self.config["pages"][self.current_page_nr]["item"])
|
||||||
self.send_mqtt_msg(command)
|
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):
|
def generate_detail_page(self, page_type, entity):
|
||||||
if(page_type == "popupLight"):
|
if page_type == "popupLight":
|
||||||
entity = self.api.get_entity(entity)
|
entity = self.api.get_entity(entity)
|
||||||
switch_val = 1 if entity.state == "on" else 0
|
switch_val = 1 if entity.state == "on" else 0
|
||||||
|
|
||||||
|
icon_color = self.getEntityColor(entity)
|
||||||
|
|
||||||
brightness = "disable"
|
brightness = "disable"
|
||||||
color_temp = "disable"
|
color_temp = "disable"
|
||||||
color = "disable"
|
color = "disable"
|
||||||
# scale 0-255 brightness from ha to 0-100
|
# scale 0-255 brightness from ha to 0-100
|
||||||
if entity.state == "on":
|
if entity.state == "on":
|
||||||
if "brightness" in entity.attributes:
|
if "brightness" in entity.attributes:
|
||||||
brightness = int(self.scale(entity.attributes.brightness,(0,255),(0,100)))
|
brightness = int(scale(entity.attributes.brightness,(0,255),(0,100)))
|
||||||
else:
|
else:
|
||||||
brightness = "disable"
|
brightness = "disable"
|
||||||
if "color_temp" in entity.attributes.supported_color_modes:
|
if "color_temp" in entity.attributes.supported_color_modes:
|
||||||
if "color_temp" in entity.attributes:
|
if "color_temp" in entity.attributes:
|
||||||
# scale ha color temp range to 0-100
|
# scale ha color temp range to 0-100
|
||||||
color_temp = int(self.scale(entity.attributes.color_temp,(entity.attributes.min_mireds, entity.attributes.max_mireds),(0,100)))
|
color_temp = int(scale(entity.attributes.color_temp,(entity.attributes.min_mireds, entity.attributes.max_mireds),(0,100)))
|
||||||
else:
|
else:
|
||||||
color_temp = 0
|
color_temp = "unknown"
|
||||||
else:
|
else:
|
||||||
color_temp = "disable"
|
color_temp = "disable"
|
||||||
|
|
||||||
if "xy" in entity.attributes.supported_color_modes or "rgb" in entity.attributes.supported_color_modes or "rgbw" in entity.attributes.supported_color_modes or "hs" in entity.attributes.supported_color_modes:
|
list_color_modes = ["xy", "rgb", "rgbw", "hs"]
|
||||||
|
if any(item in list_color_modes for item in entity.attributes.supported_color_modes):
|
||||||
color = "enable"
|
color = "enable"
|
||||||
else:
|
else:
|
||||||
color = "disable"
|
color = "disable"
|
||||||
self.send_mqtt_msg(f"entityUpdateDetail,{switch_val},{brightness},{color_temp},{color}")
|
self.send_mqtt_msg(f"entityUpdateDetail,{get_icon_id('lightbulb')},{icon_color},{switch_val},{brightness},{color_temp},{color}")
|
||||||
|
|
||||||
if(page_type == "popupShutter"):
|
if page_type == "popupShutter":
|
||||||
pos = self.api.get_entity(msg[3]).attributes.current_position
|
pos = self.api.get_entity(entity).attributes.current_position
|
||||||
# reverse position for slider
|
# reverse position for slider
|
||||||
pos = 100-pos
|
pos = 100-pos
|
||||||
self.send_mqtt_msg("entityUpdateDetail,{0}".format(pos))
|
self.send_mqtt_msg(f"entityUpdateDetail,{pos}")
|
||||||
|
|
||||||
def hsv2rgb(self, h, s, v):
|
def send_message_page(self, id, heading, msg, b1, b2):
|
||||||
hsv = colorsys.hsv_to_rgb(h,s,v)
|
self.send_mqtt_msg(f"pageType,popupNotify")
|
||||||
return tuple(round(i * 255) for i in hsv)
|
self.send_mqtt_msg(f"entityUpdateDetail,|{id}|{heading}|65535|{b1}|65535|{b2}|65535|{msg}|65535|0")
|
||||||
def pos_to_color(self, x, y):
|
|
||||||
r = 213/2
|
|
||||||
x = round((x - r) / r * 100) / 100
|
|
||||||
y = round((r - y) / r * 100) / 100
|
|
||||||
|
|
||||||
r = math.sqrt(x*x + y*y)
|
|
||||||
sat = 0
|
|
||||||
if (r > 1):
|
|
||||||
sat = 0
|
|
||||||
else:
|
|
||||||
sat = r
|
|
||||||
hsv = (math.degrees(math.atan2(y, x))%360/360, sat, 1)
|
|
||||||
rgb = self.hsv2rgb(hsv[0],hsv[1],hsv[2])
|
|
||||||
return rgb
|
|
||||||
|
|||||||
BIN
doc-pics/appdaemon-babel.png
Normal file
BIN
doc-pics/appdaemon-babel.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
doc-pics/nodered-remote-control.png
Normal file
BIN
doc-pics/nodered-remote-control.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 206 KiB After Width: | Height: | Size: 246 KiB |
24
info.md
24
info.md
@@ -11,16 +11,19 @@ nspanel-1:
|
|||||||
config:
|
config:
|
||||||
panelRecvTopic: "tele/tasmota_your_mqtt_topic/RESULT"
|
panelRecvTopic: "tele/tasmota_your_mqtt_topic/RESULT"
|
||||||
panelSendTopic: "cmnd/tasmota_your_mqtt_topic/CustomSend"
|
panelSendTopic: "cmnd/tasmota_your_mqtt_topic/CustomSend"
|
||||||
timeoutScreensaver: 15 #in seconds, values between 5 and 60 are allowed
|
updateMode: auto-notify # possible values are auto, auto-notify and manual
|
||||||
|
timeoutScreensaver: 15 #in seconds
|
||||||
#brightnessScreensaver: 10
|
#brightnessScreensaver: 10
|
||||||
brightnessScreensaver:
|
brightnessScreensaver:
|
||||||
- time: "7:00:00"
|
- time: "7:00:00"
|
||||||
value: 10
|
value: 10
|
||||||
- time: "23:00:00"
|
- time: "23:00:00"
|
||||||
value: 0
|
value: 0
|
||||||
locale: "de_DE"
|
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
|
||||||
timeFormat: "%H:%M"
|
timeFormat: "%H:%M"
|
||||||
dateFormat: "%A, %d. %B %Y"
|
dateFormat: "%A, %d. %B %Y" # ignored if babel python package is installed
|
||||||
weatherEntity: weather.example
|
weatherEntity: weather.example
|
||||||
pages:
|
pages:
|
||||||
- type: cardEntities
|
- type: cardEntities
|
||||||
@@ -36,14 +39,23 @@ nspanel-1:
|
|||||||
- button.example_button
|
- button.example_button
|
||||||
- input_button.example_input_button
|
- input_button.example_input_button
|
||||||
- light.light_example
|
- light.light_example
|
||||||
- delete # To make sure we don't keep buttons from previous page (read this as 'empty')
|
- delete # (read this as 'empty')
|
||||||
- type: cardEntities
|
- type: cardEntities
|
||||||
heading: Example Page 3
|
heading: Example Page 3
|
||||||
items:
|
items:
|
||||||
- scene.some_scene
|
- scene.example_scene
|
||||||
- scene.moodlights
|
|
||||||
- delete
|
- delete
|
||||||
- delete
|
- delete
|
||||||
|
- delete
|
||||||
|
- type: cardGrid
|
||||||
|
heading: Example Page 4
|
||||||
|
items:
|
||||||
|
- light.light_example
|
||||||
|
- button.example_button
|
||||||
|
- cover.example_cover
|
||||||
|
- scene.example_scene
|
||||||
|
- switch.example_switch
|
||||||
|
- delete
|
||||||
- type: cardThermo
|
- type: cardThermo
|
||||||
heading: Exmaple Thermostat
|
heading: Exmaple Thermostat
|
||||||
item: climate.example_climate
|
item: climate.example_climate
|
||||||
|
|||||||
783
ioBroker/NsPanelTs.ts
Normal file
783
ioBroker/NsPanelTs.ts
Normal file
@@ -0,0 +1,783 @@
|
|||||||
|
type RGB = {
|
||||||
|
red: number,
|
||||||
|
green: number,
|
||||||
|
blue: number
|
||||||
|
};
|
||||||
|
|
||||||
|
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 };
|
||||||
|
|
||||||
|
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 =
|
||||||
|
{
|
||||||
|
"type": "cardEntities",
|
||||||
|
"heading": "Haus",
|
||||||
|
"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 }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
var page2: PageEntities =
|
||||||
|
{
|
||||||
|
"type": "cardEntities",
|
||||||
|
"heading": "Strom",
|
||||||
|
"items": [
|
||||||
|
<PageItem>{ id: "alias.0.Netz" },
|
||||||
|
<PageItem>{ id: "alias.0.Hausverbrauch" },
|
||||||
|
<PageItem>{ id: "alias.0.Pv" },
|
||||||
|
<PageItem>{ id: "alias.0.Batterie" }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
var button1Page: PageGrid =
|
||||||
|
{
|
||||||
|
"type": "cardGrid",
|
||||||
|
"heading": "Radio",
|
||||||
|
"items": [
|
||||||
|
<PageItem>{ id: "alias.0.Radio.NJoy" },
|
||||||
|
<PageItem>{ id: "alias.0.Radio.Delta_Radio" },
|
||||||
|
<PageItem>{ id: "alias.0.Radio.NDR2" },
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var button2Page: PageEntities =
|
||||||
|
{
|
||||||
|
"type": "cardEntities",
|
||||||
|
"heading": "Knopf2",
|
||||||
|
"items": [
|
||||||
|
<PageItem>{ id: "alias.0.Schlafen" },
|
||||||
|
<PageItem>{ id: "alias.0.Stern" }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
var config: Config = {
|
||||||
|
panelRecvTopic: "mqtt.0.tele.WzDisplay.RESULT",
|
||||||
|
panelSendTopic: "mqtt.0.cmnd.WzDisplay.CustomSend",
|
||||||
|
leftEntity: "alias.0.Batterie.ACTUAL",
|
||||||
|
leftEntityIcon: 34,
|
||||||
|
leftEntityText: "Batterie",
|
||||||
|
leftEntityUnitText: "%",
|
||||||
|
rightEntity: "alias.0.Pv.ACTUAL",
|
||||||
|
rightEntityIcon: 32,
|
||||||
|
rightEntityText: "PV",
|
||||||
|
rightEntityUnitText: "W",
|
||||||
|
timeoutScreensaver: 15,
|
||||||
|
dimmode: 8,
|
||||||
|
locale: "de_DE",
|
||||||
|
timeFormat: "%H:%M",
|
||||||
|
dateFormat: "%A, %d. %B %Y",
|
||||||
|
weatherEntity: "alias.0.Wetter",
|
||||||
|
gridPageOffColor: Blue,
|
||||||
|
gridPageOnColor: White,
|
||||||
|
defaultColor: Blue,
|
||||||
|
temperatureUnit: "°C",
|
||||||
|
pages: [page1, page2,
|
||||||
|
{
|
||||||
|
"type": "cardThermo",
|
||||||
|
"heading": "Thermostat",
|
||||||
|
"item": <PageItem>{ id: "alias.0.WzNsPanel" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
button1Page: button1Page,
|
||||||
|
button2Page: button2Page
|
||||||
|
};
|
||||||
|
|
||||||
|
schedule("* * * * *", function () {
|
||||||
|
SendTime();
|
||||||
|
});
|
||||||
|
schedule("0 * * * *", function () {
|
||||||
|
SendDate();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Only monitor the extra nodes if one or both are present
|
||||||
|
var updateArray: string[] = [];
|
||||||
|
if (config.rightEntity !== null && existsState(config.rightEntity)) {
|
||||||
|
updateArray.push(config.rightEntity)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.leftEntity !== null && existsState(config.leftEntity)) {
|
||||||
|
updateArray.push(config.leftEntity)
|
||||||
|
}
|
||||||
|
if (updateArray.length > 0) {
|
||||||
|
on(updateArray, function () {
|
||||||
|
HandleScreensaverUpdate();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
on({ id: config.panelRecvTopic }, function (obj) {
|
||||||
|
if (obj.state.val.startsWith('\{"CustomRecv":')) {
|
||||||
|
var json = JSON.parse(obj.state.val);
|
||||||
|
|
||||||
|
var split = json.CustomRecv.split(",");
|
||||||
|
if (split[1] == "pageOpenDetail") {
|
||||||
|
UnsubscribeWatcher();
|
||||||
|
SendToPanel(GenerateDetailPage(split[2], split[3]));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
HandleMessage(split[0], split[1], parseInt(split[2]), split);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function SendToPanel(val: Payload | Payload[]): void {
|
||||||
|
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
val.forEach(function (id, i) {
|
||||||
|
setState(config.panelSendTopic, id.payload);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
setState(config.panelSendTopic, val.payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
function HandleMessage(typ: string, method: string, page: number, words: Array<string>): void {
|
||||||
|
if (typ == "event") {
|
||||||
|
var pageNum = (page % config.pages.length);
|
||||||
|
pageId = Math.abs(pageNum);
|
||||||
|
|
||||||
|
if (method == 'pageOpen' || method == 'startup') {
|
||||||
|
UnsubscribeWatcher();
|
||||||
|
|
||||||
|
if (method == 'startup')
|
||||||
|
HandleStartupProcess();
|
||||||
|
|
||||||
|
GeneratePage(config.pages[pageId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method == 'buttonPress' || method == "tempUpd") {
|
||||||
|
HandleButtonEvent(words)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method == 'screensaverOpen') {
|
||||||
|
HandleScreensaver()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method == 'button1' || method == 'button2') {
|
||||||
|
HandleHardwareButton(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function GeneratePage(page: Page): void {
|
||||||
|
|
||||||
|
switch (page.type) {
|
||||||
|
case "cardEntities":
|
||||||
|
SendToPanel(GenerateEntitiesPage(<PageEntities>page));
|
||||||
|
break;
|
||||||
|
case "cardThermo":
|
||||||
|
SendToPanel(GenerateThermoPage(<PageThermo>page));
|
||||||
|
break;
|
||||||
|
case "cardGrid":
|
||||||
|
SendToPanel(GenerateGridPage(<PageGrid>page));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function HandleHardwareButton(method: string): void {
|
||||||
|
let page: (PageThermo | PageEntities | PageGrid);
|
||||||
|
if (config.button1Page !== null && method == "button1") {
|
||||||
|
page = config.button1Page;
|
||||||
|
}
|
||||||
|
else if (config.button2Page !== null && method == "button2") {
|
||||||
|
page = config.button2Page;
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
SendDate();
|
||||||
|
SendTime();
|
||||||
|
SendToPanel({ payload: "timeout," + config.timeoutScreensaver });
|
||||||
|
SendToPanel({ payload: "dimmode," + config.dimmode });
|
||||||
|
}
|
||||||
|
|
||||||
|
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 date = d.getDate();
|
||||||
|
var month = months[d.getMonth()];
|
||||||
|
var year = d.getFullYear();
|
||||||
|
var _sendDate = "date,?" + day + " " + date + " " + month + " " + year;
|
||||||
|
SendToPanel(<Payload>{ payload: _sendDate });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function SendTime(): void {
|
||||||
|
var d = new Date();
|
||||||
|
var hr = d.getHours().toString();
|
||||||
|
var min = d.getMinutes().toString();
|
||||||
|
|
||||||
|
if (d.getHours() < 10) {
|
||||||
|
hr = "0" + d.getHours().toString();
|
||||||
|
}
|
||||||
|
if (d.getMinutes() < 10) {
|
||||||
|
min = "0" + d.getMinutes().toString();
|
||||||
|
}
|
||||||
|
SendToPanel(<Payload>{ payload: "time," + hr + ":" + min });
|
||||||
|
}
|
||||||
|
|
||||||
|
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) });
|
||||||
|
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) });
|
||||||
|
return out_msgs
|
||||||
|
}
|
||||||
|
|
||||||
|
function GeneratePageElements(pageItems: PageItem[], maxItems: number, useColors: boolean = false): string {
|
||||||
|
let pageData = "entityUpd";
|
||||||
|
for (let index = 0; index < maxItems; index++) {
|
||||||
|
if (pageItems[index] !== undefined) {
|
||||||
|
pageData += CreateEntity(pageItems[index], index + 1, useColors);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pageData += CreateEntity(<PageItem>{ id: "delete" }, index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CreateEntity(pageItem: PageItem, placeId: number, useColors: boolean = false): string {
|
||||||
|
var iconId = 0
|
||||||
|
if (pageItem.id == "delete") {
|
||||||
|
return ",delete,,,,,"
|
||||||
|
}
|
||||||
|
var name: string;
|
||||||
|
var type: string;
|
||||||
|
// ioBroker
|
||||||
|
if (existsObject(pageItem.id)) {
|
||||||
|
let o = getObject(pageItem.id)
|
||||||
|
var val = null;
|
||||||
|
name = o.common.name.de
|
||||||
|
|
||||||
|
if (existsState(pageItem.id + ".GET")) {
|
||||||
|
val = getState(pageItem.id + ".GET").val;
|
||||||
|
RegisterEntityWatcher(pageItem.id + ".GET", pageItem.id, placeId);
|
||||||
|
}
|
||||||
|
else if (existsState(pageItem.id + ".SET")) {
|
||||||
|
val = getState(pageItem.id + ".SET").val;
|
||||||
|
RegisterEntityWatcher(pageItem.id + ".SET", pageItem.id, placeId);
|
||||||
|
}
|
||||||
|
var iconColor = rgb_dec565(useColors ? config.gridPageOffColor : config.defaultColor);
|
||||||
|
switch (o.common.role) {
|
||||||
|
case "light":
|
||||||
|
type = "light"
|
||||||
|
iconId = 1
|
||||||
|
var optVal = "0"
|
||||||
|
|
||||||
|
if (val === true || val === "true") {
|
||||||
|
optVal = "1"
|
||||||
|
iconColor = rgb_dec565(useColors ? config.gridPageOnColor : config.defaultColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "," + type + "," + pageItem.id + "," + iconId + "," + iconColor + "," + name + "," + optVal
|
||||||
|
|
||||||
|
case "dimmer":
|
||||||
|
type = "light"
|
||||||
|
iconId = 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);
|
||||||
|
}
|
||||||
|
else if (existsState(pageItem.id + ".ON_SET")) {
|
||||||
|
val = getState(pageItem.id + ".ON_SET").val;
|
||||||
|
RegisterEntityWatcher(pageItem.id + ".ON_SET", pageItem.id, placeId);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "," + type + "," + pageItem.id + "," + iconId + "," + iconColor + "," + name + "," + optVal
|
||||||
|
|
||||||
|
case "blind":
|
||||||
|
type = "shutter"
|
||||||
|
iconId = 0
|
||||||
|
return "," + type + "," + pageItem.id + "," + iconId + "," + iconColor + "," + name + ","
|
||||||
|
|
||||||
|
case "info":
|
||||||
|
case "value.temperature":
|
||||||
|
type = "text"
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
else if (existsState(pageItem.id + ".ACTUAL")) {
|
||||||
|
optVal = getState(pageItem.id + ".ACTUAL").val;
|
||||||
|
RegisterEntityWatcher(pageItem.id + ".ACTUAL", pageItem.id, placeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o.common.role == "value.temperature") {
|
||||||
|
iconId = 2;
|
||||||
|
optVal += config.temperatureUnit;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
optVal += GetUnitOfMeasurement(pageItem.id + ".ACTUAL");
|
||||||
|
}
|
||||||
|
return "," + type + "," + pageItem.id + "," + iconId + "," + iconColor + "," + name + "," + optVal;
|
||||||
|
|
||||||
|
case "button":
|
||||||
|
type = "button";
|
||||||
|
iconId = 3;
|
||||||
|
var optVal = "PRESS";
|
||||||
|
return "," + type + "," + pageItem.id + "," + iconId + "," + + iconColor + "," + name + "," + optVal;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ",delete,,,,"
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ",delete,,,,,"
|
||||||
|
}
|
||||||
|
|
||||||
|
function RegisterEntityWatcher(id: string, entityId: string, placeId: number): void {
|
||||||
|
if (subscriptions.hasOwnProperty(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
subscriptions[id] = (on({ id: id, change: 'any' }, function (data) {
|
||||||
|
GeneratePage(config.pages[pageId]);
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function RegisterDetailEntityWatcher(id: string, entityId: string, type: string): void {
|
||||||
|
if (subscriptions.hasOwnProperty(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
subscriptions[id] = (on({ id: id, change: 'any' }, function () {
|
||||||
|
SendToPanel(GenerateDetailPage(type, entityId));
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetUnitOfMeasurement(id: string): string {
|
||||||
|
if (!existsObject(id))
|
||||||
|
return "";
|
||||||
|
|
||||||
|
let obj = getObject(id);
|
||||||
|
if (typeof obj.common.unit !== 'undefined') {
|
||||||
|
return obj.common.unit
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof obj.common.alias !== 'undefined' && typeof obj.common.alias.id !== 'undefined') {
|
||||||
|
return GetUnitOfMeasurement(obj.common.alias.id);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function GenerateThermoPage(page: PageThermo): Payload[] {
|
||||||
|
var id = page.item.id
|
||||||
|
var out_msgs: Array<Payload> = [];
|
||||||
|
out_msgs.push({ payload: "pageType,cardThermo" });
|
||||||
|
|
||||||
|
// ioBroker
|
||||||
|
if (existsObject(id)) {
|
||||||
|
let o = getObject(id)
|
||||||
|
let name = o.common.name.de
|
||||||
|
let currentTemp = 0;
|
||||||
|
if (existsState(id + ".ACTUAL"))
|
||||||
|
currentTemp = parseInt(getState(id + ".ACTUAL").val) * 10;
|
||||||
|
|
||||||
|
let destTemp = 0;
|
||||||
|
if (existsState(id + ".SET"))
|
||||||
|
destTemp = parseInt(getState(id + ".SET").val) * 10;
|
||||||
|
|
||||||
|
let status = ""
|
||||||
|
if (existsState(id + ".MODE"))
|
||||||
|
status = destTemp = getState(id + ".MODE").val;
|
||||||
|
let minTemp = 180
|
||||||
|
let maxTemp = 300
|
||||||
|
let stepTemp = 5
|
||||||
|
|
||||||
|
out_msgs.push({ payload: "entityUpd," + id + "," + name + "," + currentTemp + "," + destTemp + "," + status + "," + minTemp + "," + maxTemp + "," + stepTemp })
|
||||||
|
}
|
||||||
|
|
||||||
|
return out_msgs
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function setIfExists(id: string, value: any, type: string | null = null): boolean {
|
||||||
|
if (type === null) {
|
||||||
|
if (existsState(id)) {
|
||||||
|
setState(id, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let obj = getObject(id);
|
||||||
|
if (existsState(id) && obj.common.type !== undefined && obj.common.type === type) {
|
||||||
|
log(id)
|
||||||
|
setState(id, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleState(id: string): boolean {
|
||||||
|
let obj = getObject(id);
|
||||||
|
if (existsState(id) && obj.common.type !== undefined && obj.common.type === "boolean") {
|
||||||
|
setState(id, !getState(id).val);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function HandleButtonEvent(words): void {
|
||||||
|
let id = words[4]
|
||||||
|
|
||||||
|
if (words[6] == "OnOff" && existsObject(id)) {
|
||||||
|
var action = false
|
||||||
|
if (words[7] == "1")
|
||||||
|
action = true
|
||||||
|
let o = getObject(id)
|
||||||
|
switch (o.common.role) {
|
||||||
|
case "light":
|
||||||
|
setState(id + ".SET", action);
|
||||||
|
break;
|
||||||
|
case "dimmer":
|
||||||
|
if (existsState(id + ".ON_SET"))
|
||||||
|
setState(id + ".ON_SET", action);
|
||||||
|
else if (existsState(id + ".ON_ACTUAL"))
|
||||||
|
setState(id + ".ON_ACTUAL", action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (words[6] == "up")
|
||||||
|
setState(id + ".OPEN", true)
|
||||||
|
if (words[6] == "stop")
|
||||||
|
setState(id + ".STOP", true)
|
||||||
|
if (words[6] == "down")
|
||||||
|
setState(id + ".CLOSE", true)
|
||||||
|
if (words[6] == "button") {
|
||||||
|
let switchOn = true;
|
||||||
|
if (words[5] !== "1")
|
||||||
|
switchOn = false;
|
||||||
|
toggleState(id + ".SET") ? true : toggleState(id + ".ON_SET")
|
||||||
|
}
|
||||||
|
if (words[6] == "positionSlider")
|
||||||
|
setState(id + ".SET", parseInt(words[7]))
|
||||||
|
|
||||||
|
if (words[6] == "brightnessSlider")
|
||||||
|
if (existsState(id + ".SET"))
|
||||||
|
setState(id + ".SET", parseInt(words[7]));
|
||||||
|
else if (existsState(id + ".ACTUAL"))
|
||||||
|
setState(id + ".ACTUAL", parseInt(words[7]));
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function GenerateDetailPage(type: string, entityId: string): Payload[] {
|
||||||
|
|
||||||
|
var out_msgs: Array<Payload> = [];
|
||||||
|
let id = entityId
|
||||||
|
if (existsObject(id)) {
|
||||||
|
var o = getObject(id)
|
||||||
|
var val = null;
|
||||||
|
let icon = 1;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
else if (existsState(id + ".SET")) {
|
||||||
|
val = getState(id + ".SET").val;
|
||||||
|
RegisterDetailEntityWatcher(id + ".SET", id, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val)
|
||||||
|
switchVal = "1"
|
||||||
|
|
||||||
|
out_msgs.push({ payload: "entityUpdateDetail," + icon + "," + "17299," + 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (existsState(id + ".ON_SET")) {
|
||||||
|
val = getState(id + ".ON_SET").val;
|
||||||
|
RegisterDetailEntityWatcher(id + ".ON_SET", id, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val === true || val === "true")
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
let colortemp = "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 })
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == "popupShutter") {
|
||||||
|
if (existsState(id + ".ACTUAL"))
|
||||||
|
val = getState(id + ".ACTUAL").val;
|
||||||
|
else if (existsState(id + ".SET"))
|
||||||
|
val = getState(id + ".SET").val;
|
||||||
|
out_msgs.push({ payload: "entityUpdateDetail," + val })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out_msgs
|
||||||
|
}
|
||||||
|
|
||||||
|
function scale(number: number, inMin: number, inMax: number, outMin: number, outMax: number): number {
|
||||||
|
return (number - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
function UnsubscribeWatcher(): void {
|
||||||
|
for (const [key, value] of Object.entries(subscriptions)) {
|
||||||
|
unsubscribe(value);
|
||||||
|
delete subscriptions[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function HandleScreensaver(): void {
|
||||||
|
UnsubscribeWatcher();
|
||||||
|
HandleScreensaverUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
function HandleScreensaverUpdate(): void {
|
||||||
|
if (config.weatherEntity != null && existsObject(config.weatherEntity)) {
|
||||||
|
var icon = getState(config.weatherEntity + ".ICON").val;
|
||||||
|
|
||||||
|
let temperature: string =
|
||||||
|
existsState(config.weatherEntity + ".ACTUAL") ? getState(config.weatherEntity + ".ACTUAL").val :
|
||||||
|
existsState(config.weatherEntity + ".TEMP") ? getState(config.weatherEntity + ".TEMP").val : "null";
|
||||||
|
let humidity = getState(config.weatherEntity + ".HUMIDITY").val;
|
||||||
|
|
||||||
|
|
||||||
|
let payloadString =
|
||||||
|
"weatherUpdate,?" + GetAccuWeatherIcon(parseInt(icon)) + "?"
|
||||||
|
+ temperature + " " + config.temperatureUnit + "?26?"
|
||||||
|
+ humidity + " %?";
|
||||||
|
|
||||||
|
if (existsState(config.leftEntity)) {
|
||||||
|
let u1 = getState(config.leftEntity).val;
|
||||||
|
payloadString += config.leftEntityText + "?" + config.leftEntityIcon + "?" + u1 + " " + config.leftEntityUnitText + "?";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
payloadString += "???";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existsState(config.rightEntity)) {
|
||||||
|
let u2 = getState(config.rightEntity).val;
|
||||||
|
payloadString += config.rightEntityText + "?" + config.rightEntityIcon + "?" + u2 + " " + config.rightEntityUnitText;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
payloadString += "??";
|
||||||
|
}
|
||||||
|
SendToPanel(<Payload>{ payload: payloadString });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetAccuWeatherIcon(icon: number): number {
|
||||||
|
switch (icon) {
|
||||||
|
case 24: // Ice
|
||||||
|
case 30: // Hot
|
||||||
|
case 31: // Cold
|
||||||
|
return 11; // exceptional
|
||||||
|
|
||||||
|
case 7: // Cloudy
|
||||||
|
case 8: // Dreary (Overcast)
|
||||||
|
case 38: // Mostly Cloudy
|
||||||
|
return 12; // cloudy
|
||||||
|
|
||||||
|
case 11: // fog
|
||||||
|
return 13; // fog
|
||||||
|
|
||||||
|
case 25: // Sleet
|
||||||
|
return 14; // Hail
|
||||||
|
|
||||||
|
case 15: // T-Storms
|
||||||
|
return 15; // lightning
|
||||||
|
|
||||||
|
case 16: // Mostly Cloudy w/ T-Storms
|
||||||
|
case 17: // Partly Sunny w/ T-Storms
|
||||||
|
case 41: // Partly Cloudy w/ T-Storms
|
||||||
|
case 42: // Mostly Cloudy w/ T-Storms
|
||||||
|
return 16; // lightning-rainy
|
||||||
|
|
||||||
|
case 33: // Clear
|
||||||
|
case 34: // Mostly Clear
|
||||||
|
case 37: // Hazy Moonlight
|
||||||
|
return 17;
|
||||||
|
|
||||||
|
case 3: // Partly Sunny
|
||||||
|
case 4: // Intermittent Clouds
|
||||||
|
case 6: // Mostly Cloudy
|
||||||
|
case 35: // Partly Cloudy
|
||||||
|
case 36: // Intermittent Clouds
|
||||||
|
return 18; // partlycloudy
|
||||||
|
|
||||||
|
case 18: // pouring
|
||||||
|
return 19; // pouring
|
||||||
|
|
||||||
|
case 12: // Showers
|
||||||
|
case 13: // Mostly Cloudy w/ Showers
|
||||||
|
case 14: // Partly Sunny w/ Showers
|
||||||
|
case 26: // Freezing Rain
|
||||||
|
case 39: // Partly Cloudy w/ Showers
|
||||||
|
case 40: // Mostly Cloudy w/ Showers
|
||||||
|
return 20; // rainy
|
||||||
|
|
||||||
|
case 19: // Flurries
|
||||||
|
case 20: // Mostly Cloudy w/ Flurries
|
||||||
|
case 21: // Partly Sunny w/ Flurries
|
||||||
|
case 22: // Snow
|
||||||
|
case 23: // Mostly Cloudy w/ Snow
|
||||||
|
case 43: // Mostly Cloudy w/ Flurries
|
||||||
|
case 44: // Mostly Cloudy w/ Snow
|
||||||
|
return 21; // snowy
|
||||||
|
|
||||||
|
case 29: // Rain and Snow
|
||||||
|
return 22; // snowy-rainy
|
||||||
|
|
||||||
|
case 1: // Sunny
|
||||||
|
case 2: // Mostly Sunny
|
||||||
|
case 5: // Hazy Sunshine
|
||||||
|
return 23; // sunny
|
||||||
|
|
||||||
|
case 32: // windy
|
||||||
|
return 24; // windy
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetBlendedColor(percentage: number): RGB {
|
||||||
|
if (percentage < 50)
|
||||||
|
return Interpolate(config.gridPageOffColor, config.gridPageOnColor, percentage / 50.0);
|
||||||
|
return Interpolate(Red, White, (percentage - 50) / 50.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Interpolate(color1: RGB, color2: RGB, fraction: number): RGB {
|
||||||
|
var r: number = InterpolateNum(color1.red, color2.red, fraction);
|
||||||
|
var g: number = InterpolateNum(color1.green, color2.green, fraction);
|
||||||
|
var b: number = InterpolateNum(color1.blue, color2.blue, fraction);
|
||||||
|
return <RGB>{ red: Math.round(r), green: Math.round(g), blue: Math.round(b) };
|
||||||
|
}
|
||||||
|
|
||||||
|
function InterpolateNum(d1: number, d2: number, fraction: number): number {
|
||||||
|
return d1 + (d2 - d1) * fraction;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)))
|
||||||
|
}
|
||||||
156
ioBroker/README.md
Normal file
156
ioBroker/README.md
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
# NSPanel ioBroker Integration
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Thermostat Card
|
||||||
|
- Entity Card (Temperature, Switches and sensors, the script tries to figure the unit of measurement automatically)
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
- ioBroker
|
||||||
|
- MQTT Broker/Client
|
||||||
|
- Javascript
|
||||||
|
- devices (default)
|
||||||
|
- all devices needs to be defined in the devices panel
|
||||||
|
- supported device roles are light, dimmer, blind, thermostat
|
||||||
|
|
||||||
|
## Note
|
||||||
|
Currently the names are pulled from the objects data field common.name.de.
|
||||||
|
If you use a different language please search and replace the "common.name.de" with your language.
|
||||||
|
You can find this in the device raw settings.
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
- Import this script into the ioBroker javascript instance and choose Typescript.
|
||||||
|
- Make sure the version of the adapter is not to old.
|
||||||
|
- Find the config variable and update to your needs.
|
||||||
|
- The format strings are not used right now.
|
||||||
|
- Make sure your device is connected with the mqtt instance. I didn't get it working with the sonoff adapter, but I didn't tried it too long.
|
||||||
|
- Create a state with a mqtt client or create one per hand. The mqtt adapter will not create the state CustomSend
|
||||||
|
- you only need to send a dummy message to cmnd/<yourPanel>/CustomSend
|
||||||
|
- then the state will be created
|
||||||
|
|
||||||
|
## Update the screensaver string
|
||||||
|
The screensaver string which is send to the display looks something like this:
|
||||||
|
weatherUpdate,?23?11 °C?26?54%?Batterie?4?12 %?PV?23?123W
|
||||||
|
All fields are seperated by a question mark. In detail the fields are:
|
||||||
|
weatherUpdate,?Icon?Text?Icon (default humidity)?Text next to the last icon?Text for the left icon on the right side?Icon?Text under the icon?Text for the right icon on the left side?Icon?Text under the icon
|
||||||
|
|
||||||
|
See the icons currently usable in the following table:
|
||||||
|
|
||||||
|
[Icon Table](../HMI#icons-ids)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
First you need to add this rule to Tasmota:
|
||||||
|
|
||||||
|
```
|
||||||
|
Rule2 on Button1#state do Publish tele/%topic%/RESULT {"CustomRecv":"event,button1"} endon on Button2#state do Publish tele/%topic%/RESULT {"CustomRecv":"event,button2"} endon
|
||||||
|
Rule2
|
||||||
|
```
|
||||||
|
|
||||||
|
## The config element in the script which needs to be configured
|
||||||
|
```
|
||||||
|
var config: Config = {
|
||||||
|
panelRecvTopic: "mqtt.0.tele.WzDisplay.RESULT", // This is the object where the panel send the data to.
|
||||||
|
panelSendTopic: "mqtt.0.cmnd.WzDisplay.CustomSend", // This is the object where data is send to the panel.
|
||||||
|
leftEntity: "alias.0.Batterie.ACTUAL", // This is a state will be displayed on the left side.
|
||||||
|
leftEntityIcon: 34, // This is a icon which will be displayed on the left side.
|
||||||
|
leftEntityText: "Batterie", // The label for the left side.
|
||||||
|
leftEntityUnitText: "%", // The unit which will be appendon the left side.
|
||||||
|
rightEntity: "alias.0.Pv.ACTUAL", // The same but for the right side.
|
||||||
|
rightEntityIcon: 32,
|
||||||
|
rightEntityText: "PV",
|
||||||
|
rightEntityUnitText: "W",
|
||||||
|
timeoutScreensaver: 15, // Timeout for screensaver
|
||||||
|
dimmode: 8, // Display dim
|
||||||
|
locale: "de_DE", // not used right now
|
||||||
|
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
|
||||||
|
temperatureUnit: "°C", // Unit to append on temperature sensors
|
||||||
|
pages: [
|
||||||
|
{
|
||||||
|
"type": "cardEntities", // card type (cardEntities, cardThermo)
|
||||||
|
"heading": "Testseite", // heading
|
||||||
|
"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" },
|
||||||
|
<PageItem>{ id: "alias.0.Küche", useColor: true },
|
||||||
|
<PageItem>{ id: "alias.0.Wand", useColor: true }
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "cardEntities",
|
||||||
|
"heading": "Strom",
|
||||||
|
"items": [
|
||||||
|
<PageItem>{ id: "alias.0.Netz" },
|
||||||
|
<PageItem>{ id: "alias.0.Hausverbrauch" },
|
||||||
|
<PageItem>{ id: "alias.0.Pv" },
|
||||||
|
<PageItem>{ id: "alias.0.Batterie" }
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "cardThermo",
|
||||||
|
"heading": "Thermostat",
|
||||||
|
"item": "alias.0.WzNsPanel" // Needs to be a thermostat in the device panel
|
||||||
|
}
|
||||||
|
],
|
||||||
|
button1Page: button1Page, // A cardEntities, cardThermo or nothing. This will be opened when pressing button1
|
||||||
|
button2Page: button2Page // you guess it
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
The pageItem element:
|
||||||
|
```
|
||||||
|
type PageItem = {
|
||||||
|
id: string, // the element in ioBroker devices
|
||||||
|
icon: (string | undefined), // the icon which should be displayed instead of the default detected. (not implemented)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
If you want you can create dedicated objects, so you don't need to declare them again. Then you can use tehm in the pages array and button pages.
|
||||||
|
|
||||||
|
```
|
||||||
|
var button1Page: PageGrid =
|
||||||
|
{
|
||||||
|
"type": "cardGrid",
|
||||||
|
"heading": "Radio",
|
||||||
|
"items": [
|
||||||
|
<PageItem>{ id: "alias.0.Radio.NJoy" },
|
||||||
|
<PageItem>{ id: "alias.0.Radio.Delta_Radio" },
|
||||||
|
<PageItem>{ id: "alias.0.Radio.NDR2" },
|
||||||
|
]
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Pages array can look like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
pages: [
|
||||||
|
button1Page,
|
||||||
|
{
|
||||||
|
"type": "cardEntities",
|
||||||
|
"heading": "Strom",
|
||||||
|
"items": [
|
||||||
|
<PageItem>{ id: "alias.0.Netz" },
|
||||||
|
<PageItem>{ id: "alias.0.Hausverbrauch" },
|
||||||
|
<PageItem>{ id: "alias.0.Pv" },
|
||||||
|
<PageItem>{ id: "alias.0.Batterie" }
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
```
|
||||||
@@ -1,11 +1,6 @@
|
|||||||
# Node-Red Flow
|
# Node-Red Flow
|
||||||
|
|
||||||
## Note:
|
There is no node-red backend, but for some advanced scenarios nodered can become handy, like remote controlling your nspanel.
|
||||||
**This flow has been deprecated in favour of the AppDaemon Backend.**
|
|
||||||
**It's still functioning with a limited feature set.**
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
This is the exmaple node red flow which an be used to control the screen over MQTT.
|
|
||||||
|
|
||||||
Import the example node-red flow from "node-red-example-flow.json" file and adjust to your needs.
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,44 @@
|
|||||||
# Nextion Berry Driver
|
# Nextion Berry Driver
|
||||||
|
|
||||||
|
This berry driver is intended for the usage with a custom HMI/TFT firmware on nspanel and is a customisted version form [peepshow-21's ns-flash](https://github.com/peepshow-21/ns-flash)
|
||||||
|
|
||||||
|
It adds the following commands to Tasmota:
|
||||||
|
|
||||||
|
- `Nextion Payload`
|
||||||
|
|
||||||
|
Send's normal Nextion Commands to the Screen (suffixed by 0xFFFFFF)
|
||||||
|
|
||||||
|
|
||||||
|
- `CustomSend Payload`
|
||||||
|
|
||||||
|
Send's normal Custom Commands to the Screen in the following format:
|
||||||
|
`55 BB [payload length] [payload] [crc] [crc]`
|
||||||
|
|
||||||
|
- `FlashNextion URL`
|
||||||
|
|
||||||
|
Start's flashing a tft file to the nextion screen via Nextion Upload Protocol 1.2
|
||||||
|
|
||||||
|
Webserver must be reachable via HTTP
|
||||||
|
|
||||||
|
Example: `FlashNextion http://192.168.75.30:8123/local/nspanel.tft`
|
||||||
|
|
||||||
|
- `GetDriverVersion`
|
||||||
|
|
||||||
|
Returns the version currently defined in the berry script
|
||||||
|
|
||||||
|
- `UpdateDriverVersion URL`
|
||||||
|
|
||||||
|
Downloads the autoexec.be script from the specified URL and loads it.
|
||||||
|
|
||||||
|
|
||||||
|
Besides the commands, serial input will be published on 'RESULT' Topic, depending on the input in one of the following formats:
|
||||||
|
- `{"CustomRecv":%s}`
|
||||||
|
- `{"nextion":%s}`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Nextion Berry Driver Legacy Range (Old version with HTTP Range Method)
|
||||||
|
|
||||||
This berry driver is intended for the usage with a custom HMI/TFT firmware on nspanel.
|
This berry driver is intended for the usage with a custom HMI/TFT firmware on nspanel.
|
||||||
|
|
||||||
It adds the following commands to Tasmota:
|
It adds the following commands to Tasmota:
|
||||||
|
|||||||
409
tasmota/autoexec-legacy-range.be
Normal file
409
tasmota/autoexec-legacy-range.be
Normal file
@@ -0,0 +1,409 @@
|
|||||||
|
# Nextion Serial Protocol driver by joBr99 + nextion upload protocol 1.2 (the fast one yay) implementation using http range and tcpclient
|
||||||
|
# based on;
|
||||||
|
# Sonoff NSPanel Tasmota driver v0.47 | code by blakadder and s-hadinger
|
||||||
|
|
||||||
|
class TftDownloader
|
||||||
|
var tcp
|
||||||
|
|
||||||
|
var host
|
||||||
|
var port
|
||||||
|
var file
|
||||||
|
|
||||||
|
var s
|
||||||
|
var b
|
||||||
|
var tft_file_size
|
||||||
|
var current_chunk
|
||||||
|
var current_chunk_start
|
||||||
|
var download_range
|
||||||
|
|
||||||
|
|
||||||
|
def init(host, port, file, download_range)
|
||||||
|
self.tft_file_size = 0
|
||||||
|
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.file = file
|
||||||
|
self.download_range = download_range #32768
|
||||||
|
end
|
||||||
|
|
||||||
|
def download_chunk(b_start, b_length)
|
||||||
|
import string
|
||||||
|
self.tcp = tcpclient()
|
||||||
|
self.tcp.connect(self.host, self.port)
|
||||||
|
print("connected:", self.tcp.connected())
|
||||||
|
self.s = "GET " + self.file + " HTTP/1.0\r\n"
|
||||||
|
self.s += "HOST: " + self.host + "\r\n"
|
||||||
|
self.s += string.format("Range: bytes=%d-%d\r\n", b_start, (b_start+b_length-1))
|
||||||
|
print(string.format("Downloading Byte %d - %d", b_start, (b_start+b_length-1)))
|
||||||
|
self.s += "\r\n"
|
||||||
|
self.tcp.write(self.s)
|
||||||
|
|
||||||
|
#read one char after another until we reached end of http header
|
||||||
|
var end_of_header = false
|
||||||
|
var header = ""
|
||||||
|
while !end_of_header
|
||||||
|
if self.tcp.available() > 0
|
||||||
|
header += self.tcp.read(1)
|
||||||
|
if(string.find(header, '\r\n\r\n') != -1)
|
||||||
|
end_of_header = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
var content_length = 0
|
||||||
|
|
||||||
|
# check for 206 status code
|
||||||
|
if(string.find(header, '206 Partial Content') != -1)
|
||||||
|
# download was sucessful
|
||||||
|
else
|
||||||
|
print("Error while downloading")
|
||||||
|
print(header)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# convert header to list
|
||||||
|
header = string.split(header, '\r\n')
|
||||||
|
for i : header.iter()
|
||||||
|
#print(i)
|
||||||
|
if(string.find(i, 'Content-Range:') != -1)
|
||||||
|
if self.tft_file_size == 0
|
||||||
|
print(i)
|
||||||
|
self.tft_file_size = number(string.split(i, '/')[1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if(string.find(i, 'Content-Length:') != -1)
|
||||||
|
content_length = number(string.split(i, 16)[1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
#print(content_length)
|
||||||
|
# read bytes until content_length is reached
|
||||||
|
var content = bytes()
|
||||||
|
while content.size() != content_length
|
||||||
|
if self.tcp.available() > 0
|
||||||
|
content += self.tcp.readbytes()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
#print(content.size())
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_file_size()
|
||||||
|
self.download_chunk(0, 1)
|
||||||
|
return self.tft_file_size
|
||||||
|
end
|
||||||
|
|
||||||
|
# returns the next 4096 bytes after pos of the tft file
|
||||||
|
def next_chunk(pos)
|
||||||
|
if(self.current_chunk == nil)
|
||||||
|
print("current chunk empty")
|
||||||
|
self.current_chunk = self.download_chunk(pos, self.download_range)
|
||||||
|
self.current_chunk_start = pos
|
||||||
|
end
|
||||||
|
if(pos < self.current_chunk_start)
|
||||||
|
print("Requested pos is below start point of chunk in memory, not implemented")
|
||||||
|
end
|
||||||
|
if(pos >= (self.current_chunk_start+self.download_range))
|
||||||
|
print("Requested pos is after the end of chunk in memory, downloading new range")
|
||||||
|
self.current_chunk = self.download_chunk(pos, self.download_range)
|
||||||
|
self.current_chunk_start = pos
|
||||||
|
end
|
||||||
|
var start_within_current_chunk = pos - self.current_chunk_start
|
||||||
|
return self.current_chunk[start_within_current_chunk..(start_within_current_chunk+4095)]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Nextion : Driver
|
||||||
|
|
||||||
|
var ser
|
||||||
|
var flash_size
|
||||||
|
var flash_mode
|
||||||
|
var flash_version
|
||||||
|
var flash_skip
|
||||||
|
var flash_current_byte
|
||||||
|
var tftd
|
||||||
|
var progress_percentage_last
|
||||||
|
static header = bytes('55BB')
|
||||||
|
|
||||||
|
def init()
|
||||||
|
log("NSP: Initializing Driver")
|
||||||
|
self.ser = serial(17, 16, 115200, serial.SERIAL_8N1)
|
||||||
|
self.flash_mode = 0
|
||||||
|
self.flash_version = 1
|
||||||
|
self.flash_skip = false
|
||||||
|
tasmota.add_driver(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
def crc16(data, poly)
|
||||||
|
if !poly poly = 0xA001 end
|
||||||
|
# CRC-16 MODBUS HASHING ALGORITHM
|
||||||
|
var crc = 0xFFFF
|
||||||
|
for i:0..size(data)-1
|
||||||
|
crc = crc ^ data[i]
|
||||||
|
for j:0..7
|
||||||
|
if crc & 1
|
||||||
|
crc = (crc >> 1) ^ poly
|
||||||
|
else
|
||||||
|
crc = crc >> 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return crc
|
||||||
|
end
|
||||||
|
|
||||||
|
def split_55(b)
|
||||||
|
var ret = []
|
||||||
|
var s = size(b)
|
||||||
|
var i = s-2 # start from last-1
|
||||||
|
while i > 0
|
||||||
|
if b[i] == 0x55 && b[i+1] == 0xBB
|
||||||
|
ret.push(b[i..s-1]) # push last msg to list
|
||||||
|
b = b[(0..i-1)] # write the rest back to b
|
||||||
|
end
|
||||||
|
i -= 1
|
||||||
|
end
|
||||||
|
ret.push(b)
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
# encode using custom protocol 55 BB [payload length] [payload length] [payload] [crc] [crc]
|
||||||
|
def encode(payload)
|
||||||
|
var b = bytes()
|
||||||
|
b += self.header
|
||||||
|
b.add(size(payload), 2) # add size as 2 bytes, little endian
|
||||||
|
b += bytes().fromstring(payload)
|
||||||
|
var msg_crc = self.crc16(b)
|
||||||
|
b.add(msg_crc, 2) # crc 2 bytes, little endian
|
||||||
|
return b
|
||||||
|
end
|
||||||
|
|
||||||
|
# send a nextion payload
|
||||||
|
def encodenx(payload)
|
||||||
|
var b = bytes().fromstring(payload)
|
||||||
|
b += bytes('FFFFFF')
|
||||||
|
return b
|
||||||
|
end
|
||||||
|
|
||||||
|
def sendnx(payload)
|
||||||
|
var payload_bin = self.encodenx(payload)
|
||||||
|
self.ser.write(payload_bin)
|
||||||
|
print("NSP: Sent =", payload_bin)
|
||||||
|
log("NSP: Nextion command sent = " + str(payload_bin), 3)
|
||||||
|
end
|
||||||
|
|
||||||
|
def send(payload)
|
||||||
|
var payload_bin = self.encode(payload)
|
||||||
|
if self.flash_mode==1
|
||||||
|
log("NSP: skipped command becuase still flashing", 3)
|
||||||
|
else
|
||||||
|
self.ser.write(payload_bin)
|
||||||
|
log("NSP: payload sent = " + str(payload_bin), 3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def start_flash(url)
|
||||||
|
import string
|
||||||
|
var host
|
||||||
|
var port
|
||||||
|
var s1 = string.split(url,7)[1]
|
||||||
|
var i = string.find(s1,":")
|
||||||
|
var sa
|
||||||
|
if i<0
|
||||||
|
port = 80
|
||||||
|
i = string.find(s1,"/")
|
||||||
|
sa = string.split(s1,i)
|
||||||
|
host = sa[0]
|
||||||
|
else
|
||||||
|
sa = string.split(s1,i)
|
||||||
|
host = sa[0]
|
||||||
|
s1 = string.split(sa[1],1)[1]
|
||||||
|
i = string.find(s1,"/")
|
||||||
|
sa = string.split(s1,i)
|
||||||
|
port = int(sa[0])
|
||||||
|
end
|
||||||
|
var file = sa[1]
|
||||||
|
#print(host,port,file)
|
||||||
|
|
||||||
|
self.tftd = TftDownloader(host, port, file, 32768)
|
||||||
|
|
||||||
|
# get size of tft file
|
||||||
|
self.flash_size = self.tftd.get_file_size()
|
||||||
|
|
||||||
|
self.flash_mode = 1
|
||||||
|
self.sendnx('DRAKJHSUYDGBNCJHGJKSHBDN')
|
||||||
|
self.sendnx('recmod=0')
|
||||||
|
self.sendnx('recmod=0')
|
||||||
|
self.sendnx("connect")
|
||||||
|
self.sendnx("connect")
|
||||||
|
|
||||||
|
self.flash_current_byte = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_chunk(b_start)
|
||||||
|
var chunk = self.tftd.next_chunk(b_start)
|
||||||
|
#import string
|
||||||
|
#print(string.format("Sending Byte %d - %d with size of %d", b_start, b_start+4095, chunk.size()))
|
||||||
|
self.ser.write(chunk)
|
||||||
|
return chunk.size()
|
||||||
|
end
|
||||||
|
|
||||||
|
def flash_nextion()
|
||||||
|
import string
|
||||||
|
var x = self.write_chunk(self.flash_current_byte)
|
||||||
|
self.flash_current_byte = self.flash_current_byte + x
|
||||||
|
var progress_percentage = (self.flash_current_byte*100/self.flash_size)
|
||||||
|
if (self.progress_percentage_last!=progress_percentage)
|
||||||
|
print(string.format("Flashing Progress ( %d / %d ) [ %d ]", self.flash_current_byte, self.flash_size, progress_percentage))
|
||||||
|
self.progress_percentage_last = progress_percentage
|
||||||
|
tasmota.publish_result(string.format("{\"Flashing\":{\"complete\": %d}}",progress_percentage), "RESULT")
|
||||||
|
end
|
||||||
|
if (self.flash_current_byte==self.flash_size)
|
||||||
|
log("NSP: Flashing complete")
|
||||||
|
self.flash_mode = 0
|
||||||
|
end
|
||||||
|
tasmota.yield()
|
||||||
|
end
|
||||||
|
|
||||||
|
def every_100ms()
|
||||||
|
import string
|
||||||
|
if self.ser.available() > 0
|
||||||
|
var msg = self.ser.read()
|
||||||
|
if size(msg) > 0
|
||||||
|
print("NSP: Received Raw =", msg)
|
||||||
|
if self.flash_mode==1
|
||||||
|
var str = msg[0..-4].asstring()
|
||||||
|
log(str, 3)
|
||||||
|
# TODO: add check for firmware versions < 126 and send proto 1.1 command for thoose
|
||||||
|
if (string.find(str,"comok 2")==0)
|
||||||
|
if self.flash_version==1
|
||||||
|
log("NSP: Flashing 1.1")
|
||||||
|
self.sendnx(string.format("whmi-wri %d,115200,1",self.flash_size)) # Nextion Upload Protocol 1.1
|
||||||
|
else
|
||||||
|
log("NSP: Flashing 1.2")
|
||||||
|
self.sendnx(string.format("whmi-wris %d,115200,1",self.flash_size)) # Nextion Upload Protocol 1.2
|
||||||
|
end
|
||||||
|
|
||||||
|
# skip to byte (upload protocol 1.2)
|
||||||
|
elif (size(msg)==1 && msg[0]==0x08)
|
||||||
|
self.flash_skip = true
|
||||||
|
print("rec 0x08")
|
||||||
|
elif (size(msg)==4 && self.flash_skip)
|
||||||
|
var skip_to_byte = msg[0..4].get(0,4)
|
||||||
|
if(skip_to_byte == 0)
|
||||||
|
print("don't skip, offset is 0")
|
||||||
|
else
|
||||||
|
print("skip to ", skip_to_byte)
|
||||||
|
self.flash_current_byte = skip_to_byte
|
||||||
|
end
|
||||||
|
self.flash_nextion()
|
||||||
|
# send next 4096 bytes (proto 1.1/1.2)
|
||||||
|
elif (size(msg)==1 && msg[0]==0x05)
|
||||||
|
print("rec 0x05")
|
||||||
|
self.flash_nextion()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
# Recive messages using custom protocol 55 BB [payload length] [payload length] [payload] [crc] [crc]
|
||||||
|
if msg[0..1] == self.header
|
||||||
|
var lst = self.split_55(msg)
|
||||||
|
for i:0..size(lst)-1
|
||||||
|
msg = lst[i]
|
||||||
|
#var j = msg[2]+2
|
||||||
|
var j = size(msg) - 3
|
||||||
|
msg = msg[4..j]
|
||||||
|
if size(msg) > 2
|
||||||
|
var jm = string.format("{\"CustomRecv\":\"%s\"}",msg.asstring())
|
||||||
|
tasmota.publish_result(jm, "RESULT")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elif msg == bytes('000000FFFFFF88FFFFFF')
|
||||||
|
log("NSP: Screen Initialized")
|
||||||
|
else
|
||||||
|
var jm = string.format("{\"nextion\":\"%s\"}",str(msg[0..-4]))
|
||||||
|
tasmota.publish_result(jm, "RESULT")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_current_version(cmd, idx, payload, payload_json)
|
||||||
|
import string
|
||||||
|
var version_of_this_script = 2
|
||||||
|
var jm = string.format("{\"nlui_driver_version\":\"%s\"}", version_of_this_script)
|
||||||
|
tasmota.publish_result(jm, "RESULT")
|
||||||
|
end
|
||||||
|
|
||||||
|
tasmota.add_cmd('GetDriverVersion', get_current_version)
|
||||||
|
|
||||||
|
def update_berry_driver(cmd, idx, payload, payload_json)
|
||||||
|
def task()
|
||||||
|
import string
|
||||||
|
var cl = webclient()
|
||||||
|
cl.begin(payload)
|
||||||
|
var r = cl.GET()
|
||||||
|
if r == 200
|
||||||
|
print("Sucessfully downloaded nspanel-lovelace-ui berry driver")
|
||||||
|
else
|
||||||
|
print("Error while downloading nspanel-lovelace-ui berry driver")
|
||||||
|
end
|
||||||
|
r = cl.write_file("autoexec.be")
|
||||||
|
if r < 0
|
||||||
|
print("Error while writeing nspanel-lovelace-ui berry driver")
|
||||||
|
else
|
||||||
|
print("Scucessfully written nspanel-lovelace-ui berry driver")
|
||||||
|
var s = load('autoexec.be')
|
||||||
|
if s == true
|
||||||
|
var jm = string.format("{\"nlui_driver_update\":\"%s\"}", "succeeded")
|
||||||
|
tasmota.publish_result(jm, "RESULT")
|
||||||
|
else
|
||||||
|
var jm = string.format("{\"nlui_driver_update\":\"%s\"}", "failed")
|
||||||
|
tasmota.publish_result(jm, "RESULT")
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
tasmota.set_timer(0,task)
|
||||||
|
tasmota.resp_cmnd_done()
|
||||||
|
end
|
||||||
|
|
||||||
|
tasmota.add_cmd('UpdateDriverVersion', update_berry_driver)
|
||||||
|
|
||||||
|
var nextion = Nextion()
|
||||||
|
|
||||||
|
def flash_nextion(cmd, idx, payload, payload_json)
|
||||||
|
def task()
|
||||||
|
nextion.flash_version = 1
|
||||||
|
nextion.start_flash(payload)
|
||||||
|
end
|
||||||
|
tasmota.set_timer(0,task)
|
||||||
|
tasmota.resp_cmnd_done()
|
||||||
|
end
|
||||||
|
|
||||||
|
tasmota.add_cmd('FlashNextion', flash_nextion)
|
||||||
|
|
||||||
|
def flash_nextion_1_2(cmd, idx, payload, payload_json)
|
||||||
|
def task()
|
||||||
|
nextion.flash_version = 2
|
||||||
|
nextion.start_flash(payload)
|
||||||
|
end
|
||||||
|
tasmota.set_timer(0,task)
|
||||||
|
tasmota.resp_cmnd_done()
|
||||||
|
end
|
||||||
|
|
||||||
|
tasmota.add_cmd('FlashNextionFast', flash_nextion_1_2)
|
||||||
|
|
||||||
|
def send_cmd(cmd, idx, payload, payload_json)
|
||||||
|
nextion.sendnx(payload)
|
||||||
|
tasmota.resp_cmnd_done()
|
||||||
|
end
|
||||||
|
|
||||||
|
tasmota.add_cmd('Nextion', send_cmd)
|
||||||
|
|
||||||
|
def send_cmd2(cmd, idx, payload, payload_json)
|
||||||
|
nextion.send(payload)
|
||||||
|
tasmota.resp_cmnd_done()
|
||||||
|
end
|
||||||
|
|
||||||
|
tasmota.add_cmd('CustomSend', send_cmd2)
|
||||||
@@ -1,138 +1,43 @@
|
|||||||
# Nextion Serial Protocol driver by joBr99 + nextion upload protocol 1.2 (the fast one yay) implementation using http range and tcpclient
|
# Sonoff NSPanel Tasmota Lovelace UI Berry Driver | code by joBr99
|
||||||
|
# based on;
|
||||||
|
# Sonoff NSPanel Tasmota (Nextion with Flashing) driver | code by peepshow-21
|
||||||
# based on;
|
# based on;
|
||||||
# Sonoff NSPanel Tasmota driver v0.47 | code by blakadder and s-hadinger
|
# Sonoff NSPanel Tasmota driver v0.47 | code by blakadder and s-hadinger
|
||||||
|
|
||||||
class TftDownloader
|
# Example Flash
|
||||||
var tcp
|
# FlashNextion http://ip-address-of-your-homeassistant:8123/local/nspanel.tft
|
||||||
|
# FlashNextion http://nspanel.pky.eu/lui.tft
|
||||||
var host
|
|
||||||
var port
|
|
||||||
var file
|
|
||||||
|
|
||||||
var s
|
|
||||||
var b
|
|
||||||
var tft_file_size
|
|
||||||
var current_chunk
|
|
||||||
var current_chunk_start
|
|
||||||
var download_range
|
|
||||||
|
|
||||||
|
|
||||||
def init(host, port, file, download_range)
|
|
||||||
self.tft_file_size = 0
|
|
||||||
|
|
||||||
self.host = host
|
|
||||||
self.port = port
|
|
||||||
self.file = file
|
|
||||||
self.download_range = download_range #32768
|
|
||||||
end
|
|
||||||
|
|
||||||
def download_chunk(b_start, b_length)
|
|
||||||
import string
|
|
||||||
self.tcp = tcpclient()
|
|
||||||
self.tcp.connect(self.host, self.port)
|
|
||||||
print("connected:", self.tcp.connected())
|
|
||||||
self.s = "GET " + self.file + " HTTP/1.0\r\n"
|
|
||||||
self.s += "HOST: " + self.host + "\r\n"
|
|
||||||
self.s += string.format("Range: bytes=%d-%d\r\n", b_start, (b_start+b_length-1))
|
|
||||||
print(string.format("Downloading Byte %d - %d", b_start, (b_start+b_length-1)))
|
|
||||||
self.s += "\r\n"
|
|
||||||
self.tcp.write(self.s)
|
|
||||||
|
|
||||||
#read one char after another until we reached end of http header
|
|
||||||
var end_of_header = false
|
|
||||||
var header = ""
|
|
||||||
while !end_of_header
|
|
||||||
if self.tcp.available() > 0
|
|
||||||
header += self.tcp.read(1)
|
|
||||||
if(string.find(header, '\r\n\r\n') != -1)
|
|
||||||
end_of_header = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
var content_length = 0
|
|
||||||
|
|
||||||
# check for 206 status code
|
|
||||||
if(string.find(header, '206 Partial Content') != -1)
|
|
||||||
# download was sucessful
|
|
||||||
else
|
|
||||||
print("Error while downloading")
|
|
||||||
print(header)
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# convert header to list
|
|
||||||
header = string.split(header, '\r\n')
|
|
||||||
for i : header.iter()
|
|
||||||
#print(i)
|
|
||||||
if(string.find(i, 'Content-Range:') != -1)
|
|
||||||
if self.tft_file_size == 0
|
|
||||||
print(i)
|
|
||||||
self.tft_file_size = number(string.split(i, '/')[1])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if(string.find(i, 'Content-Length:') != -1)
|
|
||||||
content_length = number(string.split(i, 16)[1])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
#print(content_length)
|
|
||||||
# read bytes until content_length is reached
|
|
||||||
var content = bytes()
|
|
||||||
while content.size() != content_length
|
|
||||||
if self.tcp.available() > 0
|
|
||||||
content += self.tcp.readbytes()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
#print(content.size())
|
|
||||||
return content
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_file_size()
|
|
||||||
self.download_chunk(0, 1)
|
|
||||||
return self.tft_file_size
|
|
||||||
end
|
|
||||||
|
|
||||||
# returns the next 4096 bytes after pos of the tft file
|
|
||||||
def next_chunk(pos)
|
|
||||||
if(self.current_chunk == nil)
|
|
||||||
print("current chunk empty")
|
|
||||||
self.current_chunk = self.download_chunk(pos, self.download_range)
|
|
||||||
self.current_chunk_start = pos
|
|
||||||
end
|
|
||||||
if(pos < self.current_chunk_start)
|
|
||||||
print("Requested pos is below start point of chunk in memory, not implemented")
|
|
||||||
end
|
|
||||||
if(pos >= (self.current_chunk_start+self.download_range))
|
|
||||||
print("Requested pos is after the end of chunk in memory, downloading new range")
|
|
||||||
self.current_chunk = self.download_chunk(pos, self.download_range)
|
|
||||||
self.current_chunk_start = pos
|
|
||||||
end
|
|
||||||
var start_within_current_chunk = pos - self.current_chunk_start
|
|
||||||
return self.current_chunk[start_within_current_chunk..(start_within_current_chunk+4095)]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Nextion : Driver
|
class Nextion : Driver
|
||||||
|
|
||||||
var ser
|
static VERSION = "1.1.3"
|
||||||
var flash_size
|
static header = bytes('55BB')
|
||||||
var flash_mode
|
|
||||||
var flash_version
|
|
||||||
var flash_skip
|
|
||||||
var flash_current_byte
|
|
||||||
var tftd
|
|
||||||
var progress_percentage_last
|
|
||||||
static header = bytes('55BB')
|
|
||||||
|
|
||||||
def init()
|
static flash_block_size = 4096
|
||||||
log("NSP: Initializing Driver")
|
|
||||||
self.ser = serial(17, 16, 115200, serial.SERIAL_8N1)
|
var flash_mode
|
||||||
self.flash_mode = 0
|
var flash_size
|
||||||
self.flash_version = 1
|
var flash_written
|
||||||
self.flash_skip = false
|
var flash_buff
|
||||||
tasmota.add_driver(self)
|
var flash_offset
|
||||||
|
var awaiting_offset
|
||||||
|
var tcp
|
||||||
|
var ser
|
||||||
|
var last_per
|
||||||
|
|
||||||
|
def split_55(b)
|
||||||
|
var ret = []
|
||||||
|
var s = size(b)
|
||||||
|
var i = s-2 # start from last-1
|
||||||
|
while i > 0
|
||||||
|
if b[i] == 0x55 && b[i+1] == 0xBB
|
||||||
|
ret.push(b[i..s-1]) # push last msg to list
|
||||||
|
b = b[(0..i-1)] # write the rest back to b
|
||||||
|
end
|
||||||
|
i -= 1
|
||||||
|
end
|
||||||
|
ret.push(b)
|
||||||
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
def crc16(data, poly)
|
def crc16(data, poly)
|
||||||
@@ -152,21 +57,6 @@ class Nextion : Driver
|
|||||||
return crc
|
return crc
|
||||||
end
|
end
|
||||||
|
|
||||||
def split_55(b)
|
|
||||||
var ret = []
|
|
||||||
var s = size(b)
|
|
||||||
var i = s-2 # start from last-1
|
|
||||||
while i > 0
|
|
||||||
if b[i] == 0x55 && b[i+1] == 0xBB
|
|
||||||
ret.push(b[i..s-1]) # push last msg to list
|
|
||||||
b = b[(0..i-1)] # write the rest back to b
|
|
||||||
end
|
|
||||||
i -= 1
|
|
||||||
end
|
|
||||||
ret.push(b)
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
|
|
||||||
# encode using custom protocol 55 BB [payload length] [payload length] [payload] [crc] [crc]
|
# encode using custom protocol 55 BB [payload length] [payload length] [payload] [crc] [crc]
|
||||||
def encode(payload)
|
def encode(payload)
|
||||||
var b = bytes()
|
var b = bytes()
|
||||||
@@ -178,32 +68,143 @@ class Nextion : Driver
|
|||||||
return b
|
return b
|
||||||
end
|
end
|
||||||
|
|
||||||
# send a nextion payload
|
def encodenx(payload)
|
||||||
def encodenx(payload)
|
var b = bytes().fromstring(payload)
|
||||||
var b = bytes().fromstring(payload)
|
b += bytes('FFFFFF')
|
||||||
b += bytes('FFFFFF')
|
return b
|
||||||
return b
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def sendnx(payload)
|
def sendnx(payload)
|
||||||
var payload_bin = self.encodenx(payload)
|
import string
|
||||||
self.ser.write(payload_bin)
|
var payload_bin = self.encodenx(payload)
|
||||||
print("NSP: Sent =", payload_bin)
|
self.ser.write(payload_bin)
|
||||||
log("NSP: Nextion command sent = " + str(payload_bin), 3)
|
log(string.format("NXP: Nextion command sent = %s",str(payload_bin)), 3)
|
||||||
end
|
end
|
||||||
|
|
||||||
def send(payload)
|
def send(payload)
|
||||||
var payload_bin = self.encode(payload)
|
var payload_bin = self.encode(payload)
|
||||||
if self.flash_mode==1
|
if self.flash_mode==1
|
||||||
log("NSP: skipped command becuase still flashing", 3)
|
log("NXP: skipped command becuase still flashing", 3)
|
||||||
else
|
else
|
||||||
self.ser.write(payload_bin)
|
self.ser.write(payload_bin)
|
||||||
log("NSP: payload sent = " + str(payload_bin), 3)
|
log("NXP: payload sent = " + str(payload_bin), 3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def start_flash(url)
|
def write_to_nextion(b)
|
||||||
import string
|
self.ser.write(b)
|
||||||
|
end
|
||||||
|
|
||||||
|
def screeninit()
|
||||||
|
log("NXP: Screen Initialized")
|
||||||
|
self.sendnx("recmod=1")
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_block()
|
||||||
|
|
||||||
|
import string
|
||||||
|
log("FLH: Read block",3)
|
||||||
|
while size(self.flash_buff)<self.flash_block_size && self.tcp.connected()
|
||||||
|
if self.tcp.available()>0
|
||||||
|
self.flash_buff += self.tcp.readbytes()
|
||||||
|
else
|
||||||
|
tasmota.delay(50)
|
||||||
|
log("FLH: Wait for available...",3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
log("FLH: Buff size "+str(size(self.flash_buff)),3)
|
||||||
|
var to_write
|
||||||
|
if size(self.flash_buff)>self.flash_block_size
|
||||||
|
to_write = self.flash_buff[0..self.flash_block_size-1]
|
||||||
|
self.flash_buff = self.flash_buff[self.flash_block_size..]
|
||||||
|
else
|
||||||
|
to_write = self.flash_buff
|
||||||
|
self.flash_buff = bytes()
|
||||||
|
end
|
||||||
|
log("FLH: Writing "+str(size(to_write)),3)
|
||||||
|
var per = (self.flash_written*100)/self.flash_size
|
||||||
|
if (self.last_per!=per)
|
||||||
|
self.last_per = per
|
||||||
|
tasmota.publish_result(string.format("{\"Flashing\":{\"complete\": %d}}",per), "RESULT")
|
||||||
|
end
|
||||||
|
if size(to_write)>0
|
||||||
|
self.flash_written += size(to_write)
|
||||||
|
if self.flash_offset==0 || self.flash_written>self.flash_offset
|
||||||
|
self.ser.write(to_write)
|
||||||
|
self.flash_offset = 0
|
||||||
|
else
|
||||||
|
tasmota.set_timer(10,/->self.write_block())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
log("FLH: Total "+str(self.flash_written),3)
|
||||||
|
if (self.flash_written==self.flash_size)
|
||||||
|
log("FLH: Flashing complete")
|
||||||
|
self.flash_mode = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def every_100ms()
|
||||||
|
import string
|
||||||
|
if self.ser.available() > 0
|
||||||
|
var msg = self.ser.read()
|
||||||
|
if size(msg) > 0
|
||||||
|
log(string.format("NXP: Received Raw = %s",str(msg)), 3)
|
||||||
|
if (self.flash_mode==1)
|
||||||
|
var strv = msg[0..-4].asstring()
|
||||||
|
if string.find(strv,"comok 2")>=0
|
||||||
|
log("FLH: Send (High Speed) flash start")
|
||||||
|
self.sendnx(string.format("whmi-wris %d,115200,res0",self.flash_size))
|
||||||
|
elif size(msg)==1 && msg[0]==0x08
|
||||||
|
log("FLH: Waiting offset...",3)
|
||||||
|
self.awaiting_offset = 1
|
||||||
|
elif size(msg)==4 && self.awaiting_offset==1
|
||||||
|
self.awaiting_offset = 0
|
||||||
|
self.flash_offset = msg.get(0,4)
|
||||||
|
log("FLH: Flash offset marker "+str(self.flash_offset),3)
|
||||||
|
self.write_block()
|
||||||
|
elif size(msg)==1 && msg[0]==0x05
|
||||||
|
self.write_block()
|
||||||
|
else
|
||||||
|
log("FLH: Something has gone wrong flashing display firmware ["+str(msg)+"]",2)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
var msg_list = self.split_55(msg)
|
||||||
|
for i:0..size(msg_list)-1
|
||||||
|
msg = msg_list[i]
|
||||||
|
if size(msg) > 0
|
||||||
|
if msg == bytes('000000FFFFFF88FFFFFF')
|
||||||
|
self.screeninit()
|
||||||
|
elif size(msg)>=2 && msg[0]==0x55 && msg[1]==0xBB
|
||||||
|
var jm = string.format("{\"CustomRecv\":\"%s\"}",msg[4..-3].asstring())
|
||||||
|
tasmota.publish_result(jm, "RESULT")
|
||||||
|
elif msg[0]==0x07 && size(msg)==1 # BELL/Buzzer
|
||||||
|
tasmota.cmd("buzzer 1,1")
|
||||||
|
else
|
||||||
|
var jm = string.format("{\"nextion\":\"%s\"}",str(msg[0..-4]))
|
||||||
|
tasmota.publish_result(jm, "RESULT")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def begin_nextion_flash()
|
||||||
|
self.flash_written = 0
|
||||||
|
self.awaiting_offset = 0
|
||||||
|
self.flash_offset = 0
|
||||||
|
self.sendnx('DRAKJHSUYDGBNCJHGJKSHBDN')
|
||||||
|
self.sendnx('recmod=0')
|
||||||
|
self.sendnx('recmod=0')
|
||||||
|
self.flash_mode = 1
|
||||||
|
self.sendnx("connect")
|
||||||
|
end
|
||||||
|
|
||||||
|
def open_url(url)
|
||||||
|
|
||||||
|
import string
|
||||||
var host
|
var host
|
||||||
var port
|
var port
|
||||||
var s1 = string.split(url,7)[1]
|
var s1 = string.split(url,7)[1]
|
||||||
@@ -222,115 +223,109 @@ class Nextion : Driver
|
|||||||
sa = string.split(s1,i)
|
sa = string.split(s1,i)
|
||||||
port = int(sa[0])
|
port = int(sa[0])
|
||||||
end
|
end
|
||||||
var file = sa[1]
|
var get = sa[1]
|
||||||
#print(host,port,file)
|
log(string.format("FLH: host: %s, port: %s, get: %s",host,port,get))
|
||||||
|
self.tcp = tcpclient()
|
||||||
self.tftd = TftDownloader(host, port, file, 32768)
|
self.tcp.connect(host,port)
|
||||||
|
log("FLH: Connected:"+str(self.tcp.connected()),3)
|
||||||
# get size of tft file
|
var get_req = "GET "+get+" HTTP/1.0\r\n"
|
||||||
self.flash_size = self.tftd.get_file_size()
|
get_req += string.format("HOST: %s:%s\r\n\r\n",host,port)
|
||||||
|
self.tcp.write(get_req)
|
||||||
self.flash_mode = 1
|
var a = self.tcp.available()
|
||||||
self.sendnx('DRAKJHSUYDGBNCJHGJKSHBDN')
|
i = 1
|
||||||
self.sendnx('recmod=0')
|
while a==0 && i<5
|
||||||
self.sendnx('recmod=0')
|
tasmota.delay(100*i)
|
||||||
self.sendnx("connect")
|
tasmota.yield()
|
||||||
self.sendnx("connect")
|
i += 1
|
||||||
|
log("FLH: Retry "+str(i),3)
|
||||||
self.flash_current_byte = 0
|
a = self.tcp.available()
|
||||||
end
|
|
||||||
|
|
||||||
def write_chunk(b_start)
|
|
||||||
var chunk = self.tftd.next_chunk(b_start)
|
|
||||||
#import string
|
|
||||||
#print(string.format("Sending Byte %d - %d with size of %d", b_start, b_start+4095, chunk.size()))
|
|
||||||
self.ser.write(chunk)
|
|
||||||
return chunk.size()
|
|
||||||
end
|
|
||||||
|
|
||||||
def flash_nextion()
|
|
||||||
import string
|
|
||||||
var x = self.write_chunk(self.flash_current_byte)
|
|
||||||
self.flash_current_byte = self.flash_current_byte + x
|
|
||||||
var progress_percentage = (self.flash_current_byte*100/self.flash_size)
|
|
||||||
if (self.progress_percentage_last!=progress_percentage)
|
|
||||||
print(string.format("Flashing Progress ( %d / %d ) [ %d ]", self.flash_current_byte, self.flash_size, progress_percentage))
|
|
||||||
self.progress_percentage_last = progress_percentage
|
|
||||||
tasmota.publish_result(string.format("{\"Flashing\":{\"complete\": %d}}",progress_percentage), "RESULT")
|
|
||||||
end
|
end
|
||||||
if (self.flash_current_byte==self.flash_size)
|
if a==0
|
||||||
log("NSP: Flashing complete")
|
log("FLH: Nothing available to read!",3)
|
||||||
self.flash_mode = 0
|
return
|
||||||
end
|
end
|
||||||
tasmota.yield()
|
var b = self.tcp.readbytes()
|
||||||
end
|
i = 0
|
||||||
|
var end_headers = false;
|
||||||
def every_100ms()
|
var headers
|
||||||
import string
|
while i<size(b) && headers==nil
|
||||||
if self.ser.available() > 0
|
if b[i..(i+3)]==bytes().fromstring("\r\n\r\n")
|
||||||
var msg = self.ser.read()
|
headers = b[0..(i+3)].asstring()
|
||||||
if size(msg) > 0
|
self.flash_buff = b[(i+4)..]
|
||||||
print("NSP: Received Raw =", msg)
|
else
|
||||||
if self.flash_mode==1
|
i += 1
|
||||||
var str = msg[0..-4].asstring()
|
|
||||||
log(str, 3)
|
|
||||||
# TODO: add check for firmware versions < 126 and send proto 1.1 command for thoose
|
|
||||||
if (string.find(str,"comok 2")==0)
|
|
||||||
if self.flash_version==1
|
|
||||||
log("NSP: Flashing 1.1")
|
|
||||||
self.sendnx(string.format("whmi-wri %d,115200,1",self.flash_size)) # Nextion Upload Protocol 1.1
|
|
||||||
else
|
|
||||||
log("NSP: Flashing 1.2")
|
|
||||||
self.sendnx(string.format("whmi-wris %d,115200,1",self.flash_size)) # Nextion Upload Protocol 1.2
|
|
||||||
end
|
|
||||||
|
|
||||||
# skip to byte (upload protocol 1.2)
|
|
||||||
elif (size(msg)==1 && msg[0]==0x08)
|
|
||||||
self.flash_skip = true
|
|
||||||
print("rec 0x08")
|
|
||||||
elif (size(msg)==4 && self.flash_skip)
|
|
||||||
var skip_to_byte = msg[0..4].get(0,4)
|
|
||||||
if(skip_to_byte == 0)
|
|
||||||
print("don't skip, offset is 0")
|
|
||||||
else
|
|
||||||
print("skip to ", skip_to_byte)
|
|
||||||
self.flash_current_byte = skip_to_byte
|
|
||||||
end
|
|
||||||
self.flash_nextion()
|
|
||||||
# send next 4096 bytes (proto 1.1/1.2)
|
|
||||||
elif (size(msg)==1 && msg[0]==0x05)
|
|
||||||
print("rec 0x05")
|
|
||||||
self.flash_nextion()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
# Recive messages using custom protocol 55 BB [payload length] [payload length] [payload] [crc] [crc]
|
|
||||||
if msg[0..1] == self.header
|
|
||||||
var lst = self.split_55(msg)
|
|
||||||
for i:0..size(lst)-1
|
|
||||||
msg = lst[i]
|
|
||||||
#var j = msg[2]+2
|
|
||||||
var j = size(msg) - 3
|
|
||||||
msg = msg[4..j]
|
|
||||||
if size(msg) > 2
|
|
||||||
var jm = string.format("{\"CustomRecv\":\"%s\"}",msg.asstring())
|
|
||||||
tasmota.publish_result(jm, "RESULT")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elif msg == bytes('000000FFFFFF88FFFFFF')
|
|
||||||
log("NSP: Screen Initialized")
|
|
||||||
else
|
|
||||||
var jm = string.format("{\"nextion\":\"%s\"}",str(msg[0..-4]))
|
|
||||||
tasmota.publish_result(jm, "RESULT")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
#print(headers)
|
||||||
|
# check http respose for code 200
|
||||||
|
var tag = "200 OK"
|
||||||
|
i = string.find(headers,tag)
|
||||||
|
if (i>0)
|
||||||
|
log("FLH: HTTP Respose is 200 OK",3)
|
||||||
|
else
|
||||||
|
log("FLH: HTTP Respose is not 200 OK",3)
|
||||||
|
print(headers)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
# check http respose for content-length
|
||||||
|
tag = "Content-Length: "
|
||||||
|
i = string.find(headers,tag)
|
||||||
|
if (i>0)
|
||||||
|
var i2 = string.find(headers,"\r\n",i)
|
||||||
|
var s = headers[i+size(tag)..i2-1]
|
||||||
|
self.flash_size=int(s)
|
||||||
|
end
|
||||||
|
if self.flash_size==0
|
||||||
|
log("FLH: No size header, counting ...",3)
|
||||||
|
self.flash_size = size(self.flash_buff)
|
||||||
|
#print("counting start ...")
|
||||||
|
while self.tcp.connected()
|
||||||
|
while self.tcp.available()>0
|
||||||
|
self.flash_size += size(self.tcp.readbytes())
|
||||||
|
end
|
||||||
|
tasmota.delay(50)
|
||||||
|
end
|
||||||
|
#print("counting end ...",self.flash_size)
|
||||||
|
self.tcp.close()
|
||||||
|
self.open_url(url)
|
||||||
|
else
|
||||||
|
log("FLH: Size found in header, skip count",3)
|
||||||
|
end
|
||||||
|
log("FLH: Flash file size: "+str(self.flash_size),3)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def flash_nextion(url)
|
||||||
|
|
||||||
|
self.flash_size = 0
|
||||||
|
self.open_url(url)
|
||||||
|
self.begin_nextion_flash()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def version_number(str)
|
||||||
|
import string
|
||||||
|
var i1 = string.find(str,".",0)
|
||||||
|
var i2 = string.find(str,".",i1+1)
|
||||||
|
var num = int(str[0..i1-1])*10000+int(str[i1+1..i2-1])*100+int(str[i2+1..])
|
||||||
|
return num
|
||||||
|
end
|
||||||
|
|
||||||
|
def init()
|
||||||
|
log("NXP: Initializing Driver")
|
||||||
|
self.ser = serial(17, 16, 115200, serial.SERIAL_8N1)
|
||||||
|
self.flash_mode = 0
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
var nextion = Nextion()
|
||||||
|
|
||||||
|
tasmota.add_driver(nextion)
|
||||||
|
|
||||||
def get_current_version(cmd, idx, payload, payload_json)
|
def get_current_version(cmd, idx, payload, payload_json)
|
||||||
import string
|
import string
|
||||||
var version_of_this_script = 2
|
var version_of_this_script = 3
|
||||||
var jm = string.format("{\"nlui_driver_version\":\"%s\"}", version_of_this_script)
|
var jm = string.format("{\"nlui_driver_version\":\"%s\"}", version_of_this_script)
|
||||||
tasmota.publish_result(jm, "RESULT")
|
tasmota.publish_result(jm, "RESULT")
|
||||||
end
|
end
|
||||||
@@ -352,7 +347,7 @@ def update_berry_driver(cmd, idx, payload, payload_json)
|
|||||||
if r < 0
|
if r < 0
|
||||||
print("Error while writeing nspanel-lovelace-ui berry driver")
|
print("Error while writeing nspanel-lovelace-ui berry driver")
|
||||||
else
|
else
|
||||||
print("Scucessfully written nspanel-lovelace-ui berry driver")
|
print("Sucessfully written nspanel-lovelace-ui berry driver")
|
||||||
var s = load('autoexec.be')
|
var s = load('autoexec.be')
|
||||||
if s == true
|
if s == true
|
||||||
var jm = string.format("{\"nlui_driver_update\":\"%s\"}", "succeeded")
|
var jm = string.format("{\"nlui_driver_update\":\"%s\"}", "succeeded")
|
||||||
@@ -370,40 +365,24 @@ end
|
|||||||
|
|
||||||
tasmota.add_cmd('UpdateDriverVersion', update_berry_driver)
|
tasmota.add_cmd('UpdateDriverVersion', update_berry_driver)
|
||||||
|
|
||||||
var nextion = Nextion()
|
|
||||||
|
|
||||||
def flash_nextion(cmd, idx, payload, payload_json)
|
def flash_nextion(cmd, idx, payload, payload_json)
|
||||||
def task()
|
def task()
|
||||||
nextion.flash_version = 1
|
nextion.flash_nextion(payload)
|
||||||
nextion.start_flash(payload)
|
|
||||||
end
|
end
|
||||||
tasmota.set_timer(0,task)
|
tasmota.set_timer(0,task)
|
||||||
tasmota.resp_cmnd_done()
|
tasmota.resp_cmnd_done()
|
||||||
end
|
end
|
||||||
|
|
||||||
tasmota.add_cmd('FlashNextion', flash_nextion)
|
|
||||||
|
|
||||||
def flash_nextion_1_2(cmd, idx, payload, payload_json)
|
|
||||||
def task()
|
|
||||||
nextion.flash_version = 2
|
|
||||||
nextion.start_flash(payload)
|
|
||||||
end
|
|
||||||
tasmota.set_timer(0,task)
|
|
||||||
tasmota.resp_cmnd_done()
|
|
||||||
end
|
|
||||||
|
|
||||||
tasmota.add_cmd('FlashNextionFast', flash_nextion_1_2)
|
|
||||||
|
|
||||||
def send_cmd(cmd, idx, payload, payload_json)
|
def send_cmd(cmd, idx, payload, payload_json)
|
||||||
nextion.sendnx(payload)
|
nextion.sendnx(payload)
|
||||||
tasmota.resp_cmnd_done()
|
tasmota.resp_cmnd_done()
|
||||||
end
|
end
|
||||||
|
|
||||||
tasmota.add_cmd('Nextion', send_cmd)
|
|
||||||
|
|
||||||
def send_cmd2(cmd, idx, payload, payload_json)
|
def send_cmd2(cmd, idx, payload, payload_json)
|
||||||
nextion.send(payload)
|
nextion.send(payload)
|
||||||
tasmota.resp_cmnd_done()
|
tasmota.resp_cmnd_done()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
tasmota.add_cmd('Nextion', send_cmd)
|
||||||
tasmota.add_cmd('CustomSend', send_cmd2)
|
tasmota.add_cmd('CustomSend', send_cmd2)
|
||||||
|
tasmota.add_cmd('FlashNextion', flash_nextion)
|
||||||
|
|||||||
Reference in New Issue
Block a user