Add new files for icons, images, documentation, configuration, and initial code structure
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
10
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
]
|
||||
}
|
||||
20
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
// ...existing code...
|
||||
|
||||
// Live Server Configuration
|
||||
"liveServer.settings.root": "/data",
|
||||
"liveServer.settings.port": 5500,
|
||||
"liveServer.settings.cors": true,
|
||||
"liveServer.settings.mount": [
|
||||
["/", "./data"]
|
||||
],
|
||||
"liveServer.settings.CustomBrowser": "chrome",
|
||||
"liveServer.settings.NoBrowser": false,
|
||||
"liveServer.settings.ignoreFiles": [
|
||||
".vscode/**",
|
||||
"**/*.scss",
|
||||
"**/*.sass"
|
||||
],
|
||||
"liveServer.settings.wait": 100,
|
||||
"liveServer.settings.host": "localhost"
|
||||
}
|
||||
1
Backup/data/Test1.txt
Normal file
@ -0,0 +1 @@
|
||||
asdfada
|
||||
8
Backup/data/Test2.txt
Normal file
@ -0,0 +1,8 @@
|
||||
adfasdfadfa
|
||||
adfasdfadfaadfa
|
||||
sd
|
||||
|
||||
adfasdfadfaadfaa
|
||||
|
||||
|
||||
afas
|
||||
499
Backup/data/anim-profiles.json
Normal file
@ -0,0 +1,499 @@
|
||||
{
|
||||
"countdown": {
|
||||
"min": "10",
|
||||
"max": "75",
|
||||
"hold": "700",
|
||||
"ramp": "650"
|
||||
},
|
||||
"profile-index": 0,
|
||||
"profiles": [
|
||||
{
|
||||
"name": "Profile test",
|
||||
"events": [
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": "5",
|
||||
"speed": "10"
|
||||
},
|
||||
{
|
||||
"anim": 1,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": "25",
|
||||
"speed": "50"
|
||||
},
|
||||
{
|
||||
"anim": 2,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": "35",
|
||||
"speed": "65"
|
||||
},
|
||||
{
|
||||
"anim": 3,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": "45",
|
||||
"speed": "75"
|
||||
},
|
||||
{
|
||||
"anim": 4,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": "5",
|
||||
"speed": "10"
|
||||
},
|
||||
{
|
||||
"anim": 4,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": "5",
|
||||
"speed": "10"
|
||||
},
|
||||
{
|
||||
"anim": 4,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": "5",
|
||||
"speed": "10"
|
||||
},
|
||||
{
|
||||
"anim": 4,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": "5",
|
||||
"speed": "70"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Profile 1",
|
||||
"events": [
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Profile 2",
|
||||
"events": [
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Profile 3",
|
||||
"events": [
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Profile 4",
|
||||
"events": [
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Profile 5",
|
||||
"events": [
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Profile 6",
|
||||
"events": [
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Profile 7",
|
||||
"events": [
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 1,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 70
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
15
Backup/data/cfg/!instructions.txt
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
Choose Control App:
|
||||
1) Open app-events.json
|
||||
2) Change index to corresponding app
|
||||
a. 0=DSLRBooth, 1=...., 2=.....
|
||||
|
||||
|
||||
Front Constant Light: ( to enable the light)
|
||||
1) Open led-devices.json
|
||||
2) Change "front-light", "en" to true
|
||||
|
||||
|
||||
Rear Constant Light:
|
||||
1) Open led-devices.json
|
||||
2) Change "rear-light", "en" to true
|
||||
25
Backup/data/cfg/anim-list.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"whitefills":[
|
||||
"Bottom/Up Fill",
|
||||
"Snake Fill",
|
||||
"Tick Fill",
|
||||
"Smooth Brighten",
|
||||
"Cycle All",
|
||||
"Random Select"
|
||||
],
|
||||
"animations":[
|
||||
"Rainbow",
|
||||
"Hue Spectrum Mirrored",
|
||||
"Meteors Hue",
|
||||
"Dashes",
|
||||
"Sectors",
|
||||
"Fire (red) mirrored",
|
||||
"Fire (blue) mirrored",
|
||||
"Fire (green) mirrored",
|
||||
"Strobe",
|
||||
"Twinkle",
|
||||
"Rain",
|
||||
"Stacking",
|
||||
"Snake (Hue Range)"
|
||||
]
|
||||
}
|
||||
499
Backup/data/cfg/anim-profiles.json
Normal file
@ -0,0 +1,499 @@
|
||||
{
|
||||
"countdown":{
|
||||
"min": 10,
|
||||
"max": 75,
|
||||
"hold": 700,
|
||||
"ramp": 650
|
||||
},
|
||||
"profile-index": 0,
|
||||
"profiles":[
|
||||
{
|
||||
"name": "Profile test",
|
||||
"events":[
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 1,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 25,
|
||||
"speed": 50
|
||||
},
|
||||
{
|
||||
"anim": 2,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 35,
|
||||
"speed": 65
|
||||
},
|
||||
{
|
||||
"anim": 3,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 45,
|
||||
"speed": 75
|
||||
},
|
||||
{
|
||||
"anim": 4,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 4,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 4,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 4,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 70
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Profile 1",
|
||||
"events":[
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Profile 2",
|
||||
"events":[
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Profile 3",
|
||||
"events":[
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Profile 4",
|
||||
"events":[
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Profile 5",
|
||||
"events":[
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Profile 6",
|
||||
"events":[
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Profile 7",
|
||||
"events":[
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 0,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 10
|
||||
},
|
||||
{
|
||||
"anim": 1,
|
||||
"colmain": "#000020",
|
||||
"colbase": "#000020",
|
||||
"density": 5,
|
||||
"speed": 70
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
97
Backup/data/cfg/anim-props.json
Normal file
@ -0,0 +1,97 @@
|
||||
{
|
||||
"twinkle":{
|
||||
"col1": "#000000",
|
||||
"col2": "#000000",
|
||||
"density": 1,
|
||||
"speed": 25,
|
||||
"pwr": 255
|
||||
},
|
||||
"Strobe":{
|
||||
"col1": "#000000",
|
||||
"col2": "#000000",
|
||||
"density": 1,
|
||||
"speed": 25,
|
||||
"pwr": 255
|
||||
},
|
||||
"Solid":{
|
||||
"col1": "#000000",
|
||||
"col2": "#000000",
|
||||
"density": 1,
|
||||
"pwr": 255
|
||||
},
|
||||
"Fade":{
|
||||
"col1": "#000000",
|
||||
"range": 128,
|
||||
"speed": 25,
|
||||
"pwr": 255
|
||||
},
|
||||
"HueSwirl":{
|
||||
"col1": "#000000",
|
||||
"range": 128,
|
||||
"speed": 25,
|
||||
"pwr": 255
|
||||
},
|
||||
"Meteors":{
|
||||
"col1": "#000000",
|
||||
"col2": "#000000",
|
||||
"range": 1,
|
||||
"density": 1,
|
||||
"speed": 25,
|
||||
"pwr": 255
|
||||
},
|
||||
"Dashes":{
|
||||
"col1": "#000000",
|
||||
"col2": "#000000",
|
||||
"col3": "#000000",
|
||||
"segments": 1,
|
||||
"density": 1,
|
||||
"speed": 25,
|
||||
"pwr": 255
|
||||
},
|
||||
"DashesRange":{
|
||||
"col1": "#000000",
|
||||
"col2": "#000000",
|
||||
"range": 128,
|
||||
"segments": 1,
|
||||
"density": 1,
|
||||
"speed": 25,
|
||||
"pwr": 255
|
||||
},
|
||||
"Fire":{
|
||||
"col1": "#000000",
|
||||
"cool": 25,
|
||||
"spark": 25,
|
||||
"speed": 25,
|
||||
"pwr": 255
|
||||
},
|
||||
"Rain":{
|
||||
"col1": "#000000",
|
||||
"col2": "#000000",
|
||||
"range": 128,
|
||||
"density": 1,
|
||||
"speed": 25,
|
||||
"pwr": 255
|
||||
},
|
||||
"Stacking":{
|
||||
"col1": "#000000",
|
||||
"col2": "#000000",
|
||||
"range": 128,
|
||||
"speed": 25,
|
||||
"pwr": 255
|
||||
},
|
||||
"Snake":{
|
||||
"col1": "#000000",
|
||||
"col2": "#000000",
|
||||
"range": 128,
|
||||
"speed": 25,
|
||||
"pwr": 255
|
||||
},
|
||||
"Theater":{
|
||||
"col1": "#000000",
|
||||
"col2": "#000000",
|
||||
"range": 128,
|
||||
"speed": 25,
|
||||
"step": 20,
|
||||
"pwr": 255
|
||||
}
|
||||
}
|
||||
70
Backup/data/cfg/app-events.json
Normal file
@ -0,0 +1,70 @@
|
||||
{
|
||||
"index": 0,
|
||||
"apps":[
|
||||
{
|
||||
"name": "DSLR Booth Configuration",
|
||||
"events":[
|
||||
"White Fill / Countdown",
|
||||
"Experience Selection",
|
||||
"Select Sharing Screen",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "FotoFliqs Configuration",
|
||||
"events":[
|
||||
"White Fill / Countdown",
|
||||
"Experience Selection",
|
||||
"Preview",
|
||||
"Notice!",
|
||||
"Idle / Pause",
|
||||
"Advertisement",
|
||||
"Printing",
|
||||
"Phone Input / QR Code / Apple Drop"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "TouchPics Configuration",
|
||||
"events":[
|
||||
"White Fill / Countdown",
|
||||
"Experience Selection",
|
||||
"Preview",
|
||||
"Notice!",
|
||||
"Idle / Pause",
|
||||
"Advertisement",
|
||||
"Printing",
|
||||
"Phone Input / QR Code / Apple Drop"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "FotoZap Configuration",
|
||||
"events":[
|
||||
"White Fill / Countdown",
|
||||
"Experience Selection",
|
||||
"Select Sharing Screen",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Twineit Configuration",
|
||||
"events":[
|
||||
"White Fill / Countdown",
|
||||
"Experience Selection",
|
||||
"Select Sharing Screen",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
9
Backup/data/cfg/ble.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"en": "true",
|
||||
"core": 1,
|
||||
"device-name": "ATA_COMM",
|
||||
"key": "123456",
|
||||
"service_uuid": "6E400001-B5A3-F393-E0A9-E50E24DCCA9E",
|
||||
"char-uuid-rx": "6E400002-B5A3-F393-E0A9-E50E24DCCA9E",
|
||||
"char-uuid-tx": "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
}
|
||||
81
Backup/data/cfg/buzzer.json
Normal file
@ -0,0 +1,81 @@
|
||||
{
|
||||
"en":true,
|
||||
"boot":
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Boot:d=16,o=5,b=112:32p,f,g,a,b,b#"
|
||||
},
|
||||
"restart":
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Boot:d=16,o=5,b=112:32p,f,g,a,b,b#"
|
||||
},
|
||||
"wifi-conn":
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "WifiConnected:d=16,o=5,b=112:32p,f,a#,c6,f,a#,c6,f,a#,c6,f,a#,c6,f,a#,c6,f,a#,c6"
|
||||
},
|
||||
"wifi-disc":
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Boot:d=16,o=5,b=112:32p,f,g,a,b,b#"
|
||||
},
|
||||
"ble-conn":
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause":0,
|
||||
"tune": "Boot:d=16,o=5,b=112:32p,f,g,a,b,b#"
|
||||
},
|
||||
"ble-disc":
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Boot:d=16,o=5,b=112:32p,f,g,a,b,b#"
|
||||
},
|
||||
"click":
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Click:d=16,o=5,b=112:32p,f,g,a,b,b#"
|
||||
},
|
||||
"error":
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Click:d=16,o=5,b=112:32p,f,g,a,b,b#"
|
||||
},
|
||||
"success":
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Click:d=16,o=5,b=112:32p,f,g,a,b,b#"
|
||||
},
|
||||
"download":
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Click:d=16,o=5,b=112:32p,f,g,a,b,b#"
|
||||
},
|
||||
"waiting":
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Click:d=16,o=5,b=112:32p,f,g,a,b,b#"
|
||||
},
|
||||
"beep":
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Click:d=16,o=5,b=112:32p,f,g,a,b,b#"
|
||||
},
|
||||
"test":
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Boot:d=16,o=5,b=112:32p,f,g,a,b,b#"
|
||||
}
|
||||
}
|
||||
8
Backup/data/cfg/firmware.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"firm-index": 0,
|
||||
"firm-locations":[
|
||||
"http://mylocation1",
|
||||
"http://mylocation2",
|
||||
"http://mylocation3"
|
||||
]
|
||||
}
|
||||
39
Backup/data/cfg/led-devices.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"strip1": {
|
||||
"en": true,
|
||||
"size": 138,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "gbr",
|
||||
"shift":0,
|
||||
"offset": 0,
|
||||
"bright": 200,
|
||||
"pin": 3,
|
||||
"i2s-ch": 0,
|
||||
"core": 0
|
||||
},
|
||||
"strip2": {
|
||||
"en": false,
|
||||
"size": 20,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "grb",
|
||||
"shift":0,
|
||||
"offset": 0,
|
||||
"bright": 200,
|
||||
"pin": 46,
|
||||
"i2s-ch": 1,
|
||||
"core": 0
|
||||
},
|
||||
"front-light": {
|
||||
"en": false,
|
||||
"relay": 0,
|
||||
"min": 20,
|
||||
"max": 100,
|
||||
"core": 1
|
||||
},
|
||||
"rear-light": {
|
||||
"en": false,
|
||||
"relay": 1,
|
||||
"min": 20,
|
||||
"max": 100
|
||||
}
|
||||
}
|
||||
41
Backup/data/cfg/relays.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"relays":
|
||||
[
|
||||
{
|
||||
"pin": 45,
|
||||
"freq": 500,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 5,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"pin": 48,
|
||||
"freq": 500,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 10,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"pin": 47,
|
||||
"freq": 500,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 20,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"pin": 21,
|
||||
"freq": 500,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 40,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
37
Backup/data/cfg/system.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"buttons": [
|
||||
{
|
||||
"en": true,
|
||||
"pin": 8
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"pin": 19
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"pin": 0
|
||||
}
|
||||
],
|
||||
"oled": {
|
||||
"en": false,
|
||||
"height": 64,
|
||||
"width": 128
|
||||
},
|
||||
"fan": {
|
||||
"en": false,
|
||||
"relay": 3
|
||||
},
|
||||
"t-sensor": {
|
||||
"en": false,
|
||||
"addr": 72,
|
||||
"sp1": 85,
|
||||
"fan-pwr1": 50,
|
||||
"sp2": 90,
|
||||
"fan-pwr2": 90,
|
||||
"hyst": 1
|
||||
},
|
||||
"adc": {
|
||||
"ain1_factor": 1.0
|
||||
}
|
||||
}
|
||||
30
Backup/data/cfg/touch-pins.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"touchpins":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"pin": 23,
|
||||
"min": 100
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"pin": 23,
|
||||
"min": 100
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"pin": 23,
|
||||
"min": 100
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"pin": 23,
|
||||
"min": 100
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"pin": 23,
|
||||
"min": 100
|
||||
}
|
||||
]
|
||||
}
|
||||
10
Backup/data/cfg/trx433.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"rx433": {
|
||||
"en": "true",
|
||||
"pin": 38
|
||||
},
|
||||
"tx433": {
|
||||
"en": "true",
|
||||
"pin": 16
|
||||
}
|
||||
}
|
||||
20
Backup/data/cfg/wifi.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"wifi":
|
||||
{
|
||||
"en": true,
|
||||
"ap-ssid": "ATA-AP",
|
||||
"ap-pass": "123456",
|
||||
"mdns-name": "atadev",
|
||||
"host-name": "ATADeviceXX"
|
||||
},
|
||||
"cred1":
|
||||
{
|
||||
"ssid": "DPWifi",
|
||||
"pass": "dave3.14159"
|
||||
},
|
||||
"cred2":
|
||||
{
|
||||
"ssid": "Default",
|
||||
"pass": "password"
|
||||
}
|
||||
}
|
||||
BIN
Backup/data/favicon.ico
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Backup/data/img/atalogo.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
Backup/data/img/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
617
Backup/data/www/appcontrol.html
Normal file
@ -0,0 +1,617 @@
|
||||
{{NAVBAR}}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>App Control Configuration</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="global-style.css" rel="stylesheet">
|
||||
<style>
|
||||
#table{
|
||||
overflow: auto;
|
||||
}
|
||||
#first_td_th {
|
||||
width:325px;
|
||||
}
|
||||
#v2_td_th {
|
||||
width:200px;
|
||||
}
|
||||
#color-picker{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
background-color: transparent;
|
||||
}
|
||||
#select-anim {
|
||||
width:175px;
|
||||
}
|
||||
select{
|
||||
width:170px;
|
||||
}
|
||||
#slide-density{
|
||||
width: 70%;
|
||||
}
|
||||
#slide-speed{
|
||||
accent-color: rgb(181, 53, 53);
|
||||
width: 70%;
|
||||
}
|
||||
input::-webkit-slider-runnable-track {
|
||||
width: 450px;
|
||||
border: none;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 name="h1element">App Control Configuration</h1>
|
||||
|
||||
<fieldset>
|
||||
<legend>Saved Animation Profiles</legend>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="first_td_th">
|
||||
<label for="selSavedAnimProfiles">Profiles:</label>
|
||||
<select name= "selSavedAnimProfiles" id="selSaveddAnimProfiles" style="width:150px" onchange="OnSavedAnimProfilesChanged(this)">
|
||||
<option>(1)</option>
|
||||
<option>(2)</option>
|
||||
<option>(3)</option>
|
||||
<option>(4)</option>
|
||||
<option>(5)</option>
|
||||
<option>(6)</option>
|
||||
<option>(7)</option>
|
||||
<option>(8)</option>
|
||||
</select>
|
||||
  
|
||||
<label for="profileName">Name:</label>
|
||||
<input type="text" name="inputProfileName" id="profileName">
|
||||
   
|
||||
<button id="saveProfile" onclick="SaveProfilesToServer()">Save Profile</button>
|
||||
</td>
|
||||
</tr>
|
||||
<div ></div>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<tr><td>
|
||||
<div id="spacer-20"></div>
|
||||
</td></tr>
|
||||
|
||||
<fieldset>
|
||||
<legend id="legendLights">Countdown Animation ( White Fill )</legend>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="first_td_th">
|
||||
<label id="constLightMin-label" for="constlightMin">Light Min:</label> <br>
|
||||
<input type="range" name="constLightMin" id="constlightMin" min="0" max="99" value="25" step="1" onchange="updateLabel('Light Min: ', this)">
|
||||
</td>
|
||||
<td>
|
||||
<label id="constLightMax-label" for="constlightMax">Light Max:</label> <br>
|
||||
<input type="range" name="constLightMax" id="constlightMax" min="1" max="100" value="90" step="1" onchange="updateLabel('Light Max: ', this)">
|
||||
</td>
|
||||
<td>
|
||||
<!-- <button>Try</button> -->
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr><td>
|
||||
<div id="spacer-10"></div>
|
||||
</td></tr>
|
||||
|
||||
<tr>
|
||||
<td id="first_td_th">
|
||||
<label for="Holdtime">Hold time(ms):</label><br>
|
||||
<input type="number" name="holdTime" id="holdtime" min="0" max="5000" value="500">
|
||||
</td>
|
||||
<td>
|
||||
<label for="Ramptime">Ramp down(ms):</label><br>
|
||||
<input type="number" name="rampTime" id="Ramptime" min="0" max="5000" value="500">
|
||||
</td>
|
||||
<tr><td>
|
||||
<div id="spacer-10"></div>
|
||||
</td></tr>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<fieldset name="event-fieldset0">
|
||||
<legend name="legendAnim0">Event0</legend>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="v2_td_th">
|
||||
<label for="sel-count-anim">White Fill Animation: </label> <br>
|
||||
<select name="sel-anim0" id="select-anim0"></select>
|
||||
</td>
|
||||
<td>
|
||||
<label for="color-picker">Main:</label><br>
|
||||
<input type="color" name="main-color-anim0" id="color-picker" > 
|
||||
</td>
|
||||
<td>
|
||||
<label for="color-picker">Base:</label><br>
|
||||
<input type="color" name="base-color-anim0" id="color-picker"> 
|
||||
</td>
|
||||
<td>
|
||||
<label id="slide-density0-label" for="slide-density0">Density:</label> <br>
|
||||
<input type="range" name="slide-density0" id="slide-density" min="0" max="100" value="25" step="1" onchange="updateLabel('Density: ', this)">
|
||||
</td>
|
||||
<td>
|
||||
<label id="slide-speed0-label" for="slide-speed0">Speed:</label> <br>
|
||||
<input type="range" name="slide-speed0" id="slide-speed" min="0" max="100" value="25" step="1" onchange="updateLabel('Speed: ', this)">
|
||||
</td>
|
||||
<td>
|
||||
<button onclick="postPlayAnim(0)">Try</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<fieldset name="event-fieldset1">
|
||||
<legend name="legendAnim1">Event1</legend>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="v2_td_th">
|
||||
<label for="sel-count-anim">Animation: </label> <br>
|
||||
<select name="sel-anim1" id="select-anim1"></select>
|
||||
</td>
|
||||
<td>
|
||||
<label for="color-picker">Main:</label><br>
|
||||
<input type="color" name="main-color-anim1" id="color-picker"> 
|
||||
</td>
|
||||
<td>
|
||||
<label for="color-picker">Base:</label><br>
|
||||
<input type="color" name="base-color-anim1" id="color-picker"> 
|
||||
</td>
|
||||
<td>
|
||||
<label id="slide-density1-label" for="slide-density1">Density:</label> <br>
|
||||
<input type="range" name="slide-density1" id="slide-density" min="0" max="100" value="25" step="1" onchange="updateLabel('Density: ', this)">
|
||||
</td>
|
||||
<td>
|
||||
<label id="slide-speed1-label" for="slide-speed1">Speed:</label> <br>
|
||||
<input type="range" name="slide-speed1" id="slide-speed" min="0" max="100" value="25" step="1" onchange="updateLabel('Speed: ', this)">
|
||||
</td>
|
||||
<td>
|
||||
<button onclick="postPlayAnim(1)">Try</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<fieldset name="event-fieldset2">
|
||||
<legend name="legendAnim2">Event2</legend>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="v2_td_th">
|
||||
<label for="sel-count-anim">Animation: </label> <br>
|
||||
<select name="sel-anim2" id="select-anim2"></select>
|
||||
</td>
|
||||
<td>
|
||||
<label for="color-picker">Main:</label><br>
|
||||
<input type="color" name="main-color-anim2" id="color-picker"> 
|
||||
</td>
|
||||
<td>
|
||||
<label for="color-picker">Base:</label><br>
|
||||
<input type="color" name="base-color-anim2" id="color-picker"> 
|
||||
</td>
|
||||
<td>
|
||||
<label id="slide-density2-label" for="slide-density2">Density:</label> <br>
|
||||
<input type="range" name="slide-density2" id="slide-density" min="0" max="100" value="25" step="1" onchange="updateLabel('Density: ', this)">
|
||||
</td>
|
||||
<td>
|
||||
<label id="slide-speed2-label" for="slide-speed2">Speed:</label> <br>
|
||||
<input type="range" name="slide-speed2" id="slide-speed" min="0" max="100" value="25" step="1" onchange="updateLabel('Speed: ', this)">
|
||||
</td>
|
||||
<td>
|
||||
<button onclick="postPlayAnim(2)">Try</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<fieldset name="event-fieldset3">
|
||||
<legend name="legendAnim3">Event3</legend>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="v2_td_th">
|
||||
<label for="sel-count-anim">Animation: </label> <br>
|
||||
<select name="sel-anim3" id="select-anim3"></select>
|
||||
</td>
|
||||
<td>
|
||||
<label for="color-picker">Main:</label><br>
|
||||
<input type="color" name="main-color-anim3" id="color-picker"> 
|
||||
</td>
|
||||
<td>
|
||||
<label for="color-picker">Base:</label><br>
|
||||
<input type="color" name="base-color-anim3" id="color-picker"> 
|
||||
</td>
|
||||
<td>
|
||||
<label id="slide-density3-label" for="slide-density3">Density:</label> <br>
|
||||
<input type="range" name="slide-density3" id="slide-density" min="0" max="100" value="25" step="1" onchange="updateLabel('Density: ', this)">
|
||||
</td>
|
||||
<td>
|
||||
<label id="slide-speed3-label" for="slide-speed3">Speed:</label> <br>
|
||||
<input type="range" name="slide-speed3" id="slide-speed" min="0" max="100" value="25" step="1" onchange="updateLabel('Speed: ', this)">
|
||||
</td>
|
||||
<td>
|
||||
<button onclick="postPlayAnim(3)">Try</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<fieldset name="event-fieldset4">
|
||||
<legend name="legendAnim4">Event4</legend>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="v2_td_th">
|
||||
<label for="sel-count-anim">Animation: </label> <br>
|
||||
<select name="sel-anim4" id="select-anim4"></select>
|
||||
</td>
|
||||
<td>
|
||||
<label for="color-picker">Main:</label><br>
|
||||
<input type="color" name="main-color-anim4" id="color-picker"> 
|
||||
</td>
|
||||
<td>
|
||||
<label for="color-picker">Base:</label><br>
|
||||
<input type="color" name="base-color-anim4" id="color-picker"> 
|
||||
</td>
|
||||
<td>
|
||||
<label id="slide-density4-label" for="slide-density4">Density:</label> <br>
|
||||
<input type="range" name="slide-density4" id="slide-density" min="0" max="100" value="25" step="1" onchange="updateLabel('Density: ', this)">
|
||||
</td>
|
||||
<td>
|
||||
<label id="slide-speed4-label" for="slide-speed4">Speed:</label> <br>
|
||||
<input type="range" name="slide-speed4" id="slide-speed" min="0" max="100" value="25" step="1" onchange="updateLabel('Speed: ', this)">
|
||||
</td>
|
||||
<td>
|
||||
<button onclick="postPlayAnim(4)">Try</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<fieldset name="event-fieldset5">
|
||||
<legend name="legendAnim5">Event5</legend>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="v2_td_th">
|
||||
<label for="sel-count-anim">Animation: </label> <br>
|
||||
<select name="sel-anim5" id="select-anim5"></select>
|
||||
</td>
|
||||
<td>
|
||||
<label for="color-picker">Main:</label><br>
|
||||
<input type="color" name="main-color-anim5" id="color-picker"> 
|
||||
</td>
|
||||
<td>
|
||||
<label for="color-picker">Base:</label><br>
|
||||
<input type="color" name="base-color-anim5" id="color-picker"> 
|
||||
</td>
|
||||
<td>
|
||||
<label id="slide-density5-label" for="slide-density5">Density:</label> <br>
|
||||
<input type="range" name="slide-density5" id="slide-density" min="0" max="100" value="25" step="1" onchange="updateLabel('Density: ', this)">
|
||||
</td>
|
||||
<td>
|
||||
<label id="slide-speed5-label" for="slide-speed5">Speed:</label> <br>
|
||||
<input type="range" name="slide-speed5" id="slide-speed" min="0" max="100" value="25" step="1" onchange="updateLabel('Speed: ', this)">
|
||||
</td>
|
||||
<td>
|
||||
<button onclick="postPlayAnim(5)">Try</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<fieldset name="event-fieldset6">
|
||||
<legend name="legendAnim6">Event6</legend>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="v2_td_th">
|
||||
<label for="sel-count-anim">Animation: </label> <br>
|
||||
<select name="sel-anim6" id="select-anim6"></select>
|
||||
</td>
|
||||
<td>
|
||||
<label for="color-picker">Main:</label><br>
|
||||
<input type="color" name="main-color-anim6" id="color-picker"> 
|
||||
</td>
|
||||
<td>
|
||||
<label for="color-picker">Base:</label><br>
|
||||
<input type="color" name="base-color-anim6" id="color-picker"> 
|
||||
</td>
|
||||
<td>
|
||||
<label id="slide-density6-label" for="slide-density6">Density:</label> <br>
|
||||
<input type="range" name="slide-density6" id="slide-density" min="0" max="100" value="25" step="1" onchange="updateLabel('Density: ', this)">
|
||||
</td>
|
||||
<td>
|
||||
<label id="slide-speed6-label" for="slide-speed6">Speed:</label> <br>
|
||||
<input type="range" name="slide-speed6" id="slide-speed" min="0" max="100" value="25" step="1" onchange="updateLabel('Speed: ', this)">
|
||||
</td>
|
||||
<td>
|
||||
<button onclick="postPlayAnim(6)">Try</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<fieldset name="event-fieldset7">
|
||||
<legend name="legendAnim7">Event7</legend>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="v2_td_th">
|
||||
<label for="sel-count-anim">Animation: </label> <br>
|
||||
<select name="sel-anim7" id="select-anim7"></select>
|
||||
</td>
|
||||
<td>
|
||||
<label for="color-picker">Main:</label><br>
|
||||
<input type="color" name="main-color-anim7" id="color-picker"> 
|
||||
</td>
|
||||
<td>
|
||||
<label for="color-picker">Base:</label><br>
|
||||
<input type="color" name="base-color-anim7" id="color-picker"> 
|
||||
</td>
|
||||
<td>
|
||||
<label id="slide-density7-label" for="slide-density7">Density:</label> <br>
|
||||
<input type="range" name="slide-density7" id="slide-density" min="0" max="100" value="25" step="1" onchange="updateLabel('Density: ', this)">
|
||||
</td>
|
||||
<td>
|
||||
<label id="slide-speed7-label" for="slide-speed7">Speed:</label> <br>
|
||||
<input type="range" name="slide-speed7" id="slide-speed" min="0" max="100" value="25" step="1" onchange="updateLabel('Speed: ', this)">
|
||||
</td>
|
||||
<td>
|
||||
<button onclick="postPlayAnim(7)">Try</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<script>
|
||||
window.onload = function() { getProfilesAndEvents(); };
|
||||
|
||||
// Initialize Names
|
||||
const EVENTCOUNT = 8;
|
||||
const PROFILECOUNT = 8;
|
||||
var animEvent = [EVENTCOUNT];
|
||||
var profilesJson;
|
||||
var eventsJson;
|
||||
var animListJson;
|
||||
const inputProfileName = document.getElementsByName('inputProfileName')[0];
|
||||
const selSavedAnimProfiles = document.getElementsByName('selSavedAnimProfiles')[0];
|
||||
const constLightMin = document.getElementsByName('constLightMin')[0];
|
||||
const constLightMax = document.getElementsByName('constLightMax')[0];
|
||||
const holdTime = document.getElementsByName('holdTime')[0];
|
||||
const rampTime = document.getElementsByName('rampTime')[0];
|
||||
var eventFieldset = [EVENTCOUNT];
|
||||
var eventLegend = [EVENTCOUNT];
|
||||
|
||||
var h1element = document.getElementsByName("h1element")[0];
|
||||
|
||||
// set event names by app type and if they are visible
|
||||
for (let i = 0; i < EVENTCOUNT; i++) {
|
||||
eventFieldset[i] = document.getElementsByName("event-fieldset" + i)[0];
|
||||
eventLegend[i] = document.getElementsByName("legendAnim" + i)[0];
|
||||
}
|
||||
|
||||
for (let i = 0; i < EVENTCOUNT; i++) {
|
||||
animEvent[i] = {
|
||||
anim: document.getElementsByName('sel-anim' + i)[0],
|
||||
colmain: document.getElementsByName('main-color-anim' + i)[0],
|
||||
colbase: document.getElementsByName('base-color-anim' + i)[0],
|
||||
density: document.getElementsByName('slide-density' + i)[0],
|
||||
speed: document.getElementsByName('slide-speed' + i)[0]
|
||||
};
|
||||
}
|
||||
|
||||
// update the form with animation data when profile list item selected
|
||||
function setProfile(index){
|
||||
var changeEvent = new Event("change");
|
||||
|
||||
// update profiles
|
||||
inputProfileName.value = profilesJson.profiles[index].name;
|
||||
constLightMin.value = profilesJson.countdown.min;
|
||||
constLightMin.dispatchEvent(changeEvent);
|
||||
constLightMax.value = profilesJson.countdown.max;
|
||||
constLightMax.dispatchEvent(changeEvent);
|
||||
holdTime.value = profilesJson.countdown.hold;
|
||||
rampTime.value = profilesJson.countdown.ramp;
|
||||
|
||||
// Update Events
|
||||
for (let i = 0; i < EVENTCOUNT; i++) {
|
||||
animEvent[i].anim.selectedIndex = profilesJson.profiles[index].events[i].anim;
|
||||
animEvent[i].colmain.value = profilesJson.profiles[index].events[i].colmain;
|
||||
animEvent[i].colbase.value = profilesJson.profiles[index].events[i].colbase;
|
||||
animEvent[i].density.value = profilesJson.profiles[index].events[i].density;
|
||||
animEvent[i].density.dispatchEvent(changeEvent);
|
||||
animEvent[i].speed.value = profilesJson.profiles[index].events[i].speed;
|
||||
animEvent[i].speed.dispatchEvent(changeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
// When new profile is selected
|
||||
function OnSavedAnimProfilesChanged( event ){
|
||||
console.log('Selected value:', event.selectedIndex);
|
||||
setProfile(event.selectedIndex);
|
||||
}
|
||||
|
||||
//Update the profilesJson obj with updated values
|
||||
function updateProfilesJson(){
|
||||
try{
|
||||
let index = selSavedAnimProfiles.selectedIndex;
|
||||
profilesJson.profiles[index].name = inputProfileName.value;
|
||||
profilesJson.countdown.min = constLightMin.value;
|
||||
profilesJson.countdown.max = constLightMax.value;
|
||||
profilesJson.countdown.hold = holdTime.value;
|
||||
profilesJson.countdown.ramp = rampTime.value;
|
||||
|
||||
// Update Events
|
||||
for (let i = 0; i < EVENTCOUNT; i++) {
|
||||
profilesJson.profiles[index].events[i].anim = animEvent[i].anim.selectedIndex;
|
||||
profilesJson.profiles[index].events[i].colmain = animEvent[i].colmain.value;
|
||||
profilesJson.profiles[index].events[i].colbase = animEvent[i].colbase.value;
|
||||
profilesJson.profiles[index].events[i].density = animEvent[i].density.value;
|
||||
profilesJson.profiles[index].events[i].speed = animEvent[i].speed.value;
|
||||
}
|
||||
|
||||
}catch(e){
|
||||
debugger;
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
// save profilesJson
|
||||
function SaveProfilesToServer(){
|
||||
// update profilesJson obj with current settings before posting
|
||||
updateProfilesJson();
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.append('type', 'anim-profiles');
|
||||
const url = '/post?' + params.toString();
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'text/plain' } ,
|
||||
body: JSON.stringify(profilesJson) // convert to string
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) { console.log('Request successful');}
|
||||
else { throw new Error('Request failed');}
|
||||
})
|
||||
.catch(error => { console.error(error);});
|
||||
}
|
||||
|
||||
// send anim event to test out
|
||||
function postPlayAnim(animIndex){
|
||||
var tempAnimProps = profilesJson.profiles[0].events[0];
|
||||
tempAnimProps.anim = animEvent[animIndex].anim.selectedIndex;
|
||||
tempAnimProps.colmain = animEvent[animIndex].colmain.value
|
||||
tempAnimProps.colbase = animEvent[animIndex].colbase.value
|
||||
tempAnimProps.density = animEvent[animIndex].density.value;
|
||||
tempAnimProps.speed = animEvent[animIndex].speed.value;
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.append('type', 'play-anim');
|
||||
const url = '/post?' + params.toString();
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'text/plain'} ,
|
||||
body: JSON.stringify(tempAnimProps)
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) { throw new Error('Request failed'); }
|
||||
})
|
||||
.catch(error => { console.error(error); });
|
||||
}
|
||||
|
||||
// Get profiles
|
||||
function getProfilesAndEvents(){
|
||||
try{
|
||||
fetch_json_file('anim-profiles').then(result =>{
|
||||
profilesJson = result.data;
|
||||
//console.log({profilesJson});
|
||||
|
||||
fetch_json_file('app-events').then(result =>{
|
||||
eventsJson = result.data.apps[result.data.index]
|
||||
//console.log({evetnsJson});
|
||||
|
||||
fetch_json_file('anim-list').then(result =>{
|
||||
animListJson = result.data;
|
||||
//console.log({animListJson});
|
||||
|
||||
FillWhitefilList(animListJson.whitefills);
|
||||
FillAnimationsList(animListJson.animations);
|
||||
FillProfilesList(profilesJson);
|
||||
selSavedAnimProfiles.selectedIndex = profilesJson['profile-index'];
|
||||
const changeEvent = new Event('change');
|
||||
selSavedAnimProfiles.dispatchEvent(changeEvent);
|
||||
NameAndHideEvents();
|
||||
});
|
||||
});
|
||||
});
|
||||
}catch (error){
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
function fetch_json_file(fileName){
|
||||
const params = new URLSearchParams();
|
||||
params.append('type', fileName);
|
||||
const url = '/get?' + params.toString();
|
||||
|
||||
return fetch(url, {
|
||||
method: 'GET'
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) { return response.json(); }
|
||||
else { throw new Error('fetching ' + fileName + ' failed'); }
|
||||
})
|
||||
.then(data => { return{data:data}; })
|
||||
}
|
||||
|
||||
// Rename legends and or hide unused events property boxes
|
||||
function NameAndHideEvents(){
|
||||
h1element.textContent = eventsJson.name;
|
||||
for(let i = 0; i < EVENTCOUNT; i++){
|
||||
eventLegend[i].textContent = eventsJson.events[i];
|
||||
if(eventsJson.events[i] === ''){
|
||||
eventFieldset[i].style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fill Profiles List
|
||||
function FillProfilesList(profJson){
|
||||
for(let i = 0; i < PROFILECOUNT; i++){
|
||||
selSavedAnimProfiles.options[i].text = '(' + (i + 1) + ') ' + profJson.profiles[i].name;
|
||||
selSavedAnimProfiles.options[i].value = profJson.profiles[i].name;
|
||||
}
|
||||
}
|
||||
|
||||
function FillWhitefilList(whiteJson){
|
||||
for(let x = 0; x < whiteJson.length; x++){
|
||||
var op = document.createElement("option");
|
||||
op.text = whiteJson[x];
|
||||
animEvent[0].anim.appendChild(op);
|
||||
}
|
||||
}
|
||||
|
||||
// Fill Animations drop down lists
|
||||
function FillAnimationsList(animJson){
|
||||
for(let x = 0; x < animJson.length; x++){
|
||||
for(let i = 1; i < EVENTCOUNT; i++){
|
||||
var op = document.createElement("option");
|
||||
op.text = animJson[x];
|
||||
op.value = animJson[x];
|
||||
animEvent[i].anim.appendChild(op);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateLabel(labelText, slider) {
|
||||
try{
|
||||
let label = document.getElementById(slider.name + "-label");
|
||||
label.innerHTML = labelText + slider.value;
|
||||
}catch(e){
|
||||
debugger;
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
16
Backup/data/www/bleconfig.html
Normal file
@ -0,0 +1,16 @@
|
||||
{{NAVBAR}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>BLE Config</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="global-style.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>App Control - Bluetooth Configuration</h1>
|
||||
</body>
|
||||
</html>
|
||||
11
Backup/data/www/boothconfig.html
Normal file
@ -0,0 +1,11 @@
|
||||
{{NAVBAR}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Photobooth Configuration</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Photobooth Configuration</h1>
|
||||
</body>
|
||||
</html>
|
||||
144
Backup/data/www/edit.html
Normal file
@ -0,0 +1,144 @@
|
||||
{{NAVBAR}}
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Edit file</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
/*background-color: #f7f7f7;*/
|
||||
font-family: Tahoma, Arial, sans-serif;
|
||||
font-size: small;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column; /* Align contents vertically */
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
h1 {
|
||||
margin-top: 0; /* Remove top margin for h2 element */
|
||||
}
|
||||
#submit {
|
||||
width:100px;
|
||||
}
|
||||
button{
|
||||
width: 100px;
|
||||
}
|
||||
#spacer-50 {
|
||||
height: 20px;
|
||||
}
|
||||
#spacer-20 {
|
||||
height: 10px;
|
||||
}
|
||||
fieldset {
|
||||
width:700px;
|
||||
border-radius: 10px;
|
||||
background-color: lightgray;
|
||||
}
|
||||
td, th {
|
||||
text-align: center;
|
||||
padding: 1px;
|
||||
}
|
||||
legend{
|
||||
background-color:white;
|
||||
background-blend-mode: darken;
|
||||
border-radius: 5px;
|
||||
padding: 1px 6px 2px 6px;
|
||||
border-style:solid;
|
||||
border-width: 1.0;
|
||||
}
|
||||
textarea {
|
||||
width: 700px;
|
||||
height: 500px;
|
||||
box-sizing: border-box;
|
||||
border: 1.5px solid #000000;
|
||||
border-radius: 8px;
|
||||
resize: none;
|
||||
word-wrap: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Edit file</h2>
|
||||
<fieldset>
|
||||
<legend>Editing file: {{SAVE_PATH_INPUT}}</legend>
|
||||
<div id="spacer-20"></div>
|
||||
<table><tr><td colspan="2">
|
||||
<form name="edit-file" action="/save" onsubmit="return validateForm()">
|
||||
<textarea name="edit-textarea" id="edit-textarea" wrap="off" ></textarea>
|
||||
<div id="spacer-20"></div>
|
||||
</td></tr><tr><td>
|
||||
{{SAVE_PATH_INPUT}}
|
||||
<button type="submit" id="submit-edit" >Save</button>
|
||||
</form>
|
||||
</td><td>
|
||||
<button id="submit" onclick="window.location.href='/filemanager';">Cancel</button>
|
||||
</td></tr></table>
|
||||
<div id="spacer-50"></div>
|
||||
</fieldset>
|
||||
|
||||
<iframe style="display:none" name="self-page"></iframe>
|
||||
|
||||
<script>
|
||||
window.onload=loadEditFile();
|
||||
|
||||
function loadEditFile(){
|
||||
var savePath = document.getElementById('save-path').value;
|
||||
|
||||
if( savePath != "/new.txt"){ // skip if new.txt
|
||||
fetch(savePath)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch file');
|
||||
}
|
||||
return response.text();
|
||||
})
|
||||
.then(fileContents => {
|
||||
// Put the file contents into a textarea element
|
||||
document.getElementById('edit-textarea').value = fileContents;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function validateForm()
|
||||
{
|
||||
var allowedExtensions = "{{ALLOWED_EXTENSIONS_EDIT}}";
|
||||
var inputMessage = document.getElementById('save-path').value;
|
||||
var dotIndex = inputMessage.lastIndexOf(".")+1;
|
||||
var inputMessageExtension = inputMessage.substring(dotIndex);
|
||||
var extIndex = allowedExtensions.indexOf(inputMessageExtension);
|
||||
var isSlash = inputMessage.substring(0,1);
|
||||
|
||||
if(inputMessage == "")
|
||||
{
|
||||
alert("Enter the file name! \ne.g.: /new.txt");
|
||||
return false;
|
||||
}
|
||||
if(isSlash != "/")
|
||||
{
|
||||
alert("The slash at the beginning of the file is missing!");
|
||||
return false;
|
||||
}
|
||||
if(dotIndex == 0)
|
||||
{
|
||||
alert("The extension is missing at the end of the file!");
|
||||
return false;
|
||||
}
|
||||
if(inputMessageExtension == "")
|
||||
{
|
||||
alert("The extension is missing at the end of the file!");
|
||||
return false;
|
||||
}
|
||||
if(extIndex == -1)
|
||||
{
|
||||
alert("Extension not supported!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
24
Backup/data/www/failed.html
Normal file
@ -0,0 +1,24 @@
|
||||
{{NAVBAR}}
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Update Failed</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
#spacer-50 {
|
||||
height: 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<center>
|
||||
<h2>The update has failed.</h2>
|
||||
<div id="spacer-50"></div>
|
||||
<button onclick="window.location.href='/filemanager';">to homepage</button>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
297
Backup/data/www/filemanager.html
Normal file
@ -0,0 +1,297 @@
|
||||
{{NAVBAR}}
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>File Manager</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="global-style.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
#file-row:nth-child(odd) {
|
||||
background-color: #e8e8e8;
|
||||
}
|
||||
progress{
|
||||
width: 380px;
|
||||
}
|
||||
#submit-edit, #submit-upload, #submit-delete, #submit-update-local{
|
||||
width:120px;
|
||||
}
|
||||
#dir-path{
|
||||
width: 75px;
|
||||
}
|
||||
fieldset {
|
||||
width:500px;
|
||||
}
|
||||
table {
|
||||
width:540px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<h1>File Manager</h1>
|
||||
|
||||
<fieldset>
|
||||
<legend>File list</legend>
|
||||
<div id="spacer-20"></div>
|
||||
<p>  Total storage: {{FS_TOTAL_BYTES}}, Used: {{FS_USED_BYTES}}, Still available: {{FS_FREE_BYTES}}</p>
|
||||
<div id="spacer-20"></div>
|
||||
<table><tr><th id="first_td_th">Listing:</th> </th><th>Size:</th></tr>
|
||||
{{LISTED_FILES}}
|
||||
</table>
|
||||
<div id="spacer-20"></div>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<fieldset>
|
||||
<legend>File upload</legend>
|
||||
<div id="spacer-20"></div>
|
||||
<form action="/upload" method="POST" enctype="multipart/form-data">
|
||||
<table><tr>
|
||||
<td id="first_td_th">
|
||||
<label for="dir-path">Dir: </label>
|
||||
<select name="dir-path" id="dir-path">
|
||||
{{DIR_LIST}}
|
||||
</select>  
|
||||
<input type="file" id="upload-file" name="upload-file">
|
||||
</td>
|
||||
<td><input type="submit" id="submit-upload" value="File upload!" onclick="return validateFormUpload()"></td>
|
||||
</tr></table>
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<fieldset>
|
||||
<legend>Edit file</legend>
|
||||
<div id="spacer-20"></div>
|
||||
<form action="/edit" method="GET">
|
||||
<table><tr>
|
||||
<td id="first_td_th">
|
||||
<select name="edit-path" id="edit-path">
|
||||
<option value="choose">Select file to edit</option>
|
||||
<option value="new">New text file</option>
|
||||
{{EDIT-DEL_FILES}}
|
||||
</select></td>
|
||||
<td><input type="submit" id="submit-edit" value="Edit" onclick="return validateFormEdit()"></td>
|
||||
</tr></table>
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<fieldset>
|
||||
<legend>Delete file</legend>
|
||||
<div id="spacer-20"></div>
|
||||
<form action="/delete" method="GET">
|
||||
<table><tr>
|
||||
<td id="first_td_th">
|
||||
<select name="delete-path" id="select-files">
|
||||
<option value="choose">Select file to delete</option>
|
||||
{{EDIT-DEL_FILES}}
|
||||
</select></td>
|
||||
<td><input type="submit" id="submit-delete" value="Delete" onclick="return validateFormDelete()"></td>
|
||||
</tr></table>
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<!--<fieldset>
|
||||
<legend>Format File System</legend>
|
||||
<div id="spacer-20"></div>
|
||||
<form action="/format" method="POST" target="self-page">
|
||||
<table><tr>
|
||||
<td id="first_td_th">
|
||||
<p id="format-notice">Pressing the 'Format' button will immediately delete all data from File System!</p></td>
|
||||
<td><input type="submit" id="submit-format" value="Format" onclick="return confirmFormat()"></td>
|
||||
</tr></table>
|
||||
</form>
|
||||
<div id="spacer-20"></div>
|
||||
</fieldset>-->
|
||||
|
||||
<h2>Firmware/File System Update</h2>
|
||||
|
||||
<fieldset>
|
||||
<legend>Firmware Update (Local) | Firmware Ver: {{FIRM_VER}} | fwataVxxx.bin or lfsataVxxx.bin</legend>
|
||||
<div id="spacer-20"></div>
|
||||
<form action="/update" method="POST" enctype="multipart/form-data">
|
||||
<table>
|
||||
<tr>
|
||||
<td id="first_td_th">
|
||||
<input type="file" id="update-file" name="update-file">
|
||||
</td>
|
||||
<td>
|
||||
<input type="submit" id="submit-update-local" value="Update!" onclick="return validateFormUpdate()">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table>
|
||||
<tr><div id="spacer-20"></div>
|
||||
<td >
|
||||
<label for="firm-progress">Progress:</label>
|
||||
<progress id="firm-progress" value="0" max="100"></progress>
|
||||
<label id="lbl-firm-progress" for="firm-progress">---</label>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<fieldset hidden>
|
||||
<legend>Firmware Update (Web)</legend>
|
||||
<div id="spacer-20"></div>
|
||||
<form action="/update" method="POST" enctype="multipart/form-data">
|
||||
<table><tr>
|
||||
<td id="first_td_th">
|
||||
<button type="submit" id="submit-update-check" value="check" onclick="return validateFormUpdate()"></button>
|
||||
<label>Status:</label></td>
|
||||
<td><input type="submit" id="submit-update-web" value="Update!" onclick="return validateFormUpdate()"></td>
|
||||
</tr></table>
|
||||
</form>
|
||||
</fieldset>
|
||||
<div id="spacer-50"></div>
|
||||
<div id="spacer-50"></div>
|
||||
<div id="spacer-50"></div>
|
||||
<div id="spacer-50"></div>
|
||||
<div id="spacer-50"></div>
|
||||
<div id="spacer-50"></div>
|
||||
|
||||
<iframe style="display:none" name="self-page"></iframe>
|
||||
|
||||
|
||||
<script>
|
||||
function validateFormEdit()
|
||||
{
|
||||
var allowedExtensions = "{{ALLOWED_EXTENSIONS_EDIT}}";
|
||||
var editSelectValue = document.getElementById('edit-path').value;
|
||||
var dotIndex = editSelectValue.lastIndexOf(".")+1;
|
||||
var editSelectValueExtension = editSelectValue.substring(dotIndex);
|
||||
var extIndex = allowedExtensions.indexOf(editSelectValueExtension);
|
||||
|
||||
if(editSelectValue == "new"){
|
||||
return true;
|
||||
}
|
||||
if(editSelectValue == "choose" ){
|
||||
alert("You have not chosen a file!");
|
||||
return false;
|
||||
}
|
||||
if(extIndex == -1){
|
||||
alert("Editing of this file type is not supported!");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
function validateFormDelete()
|
||||
{
|
||||
var deleteSelectValue = document.getElementById('delete-path').value;
|
||||
|
||||
if(deleteSelectValue == "choose" || !deleteSelectValue.indexOf(".")){
|
||||
alert("You have not chosen a file!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function confirmFormat()
|
||||
{
|
||||
var text = 'Pressing the "OK" button immediately deletes all data from SPIFFS and restarts ESP32!';
|
||||
if (confirm(text) == true) {
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function validateFormUpload()
|
||||
{
|
||||
var inputElement = document.getElementById('upload-file');
|
||||
|
||||
var files = inputElement.files;
|
||||
if(files.length==0){
|
||||
alert("You have not chosen a file!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const fileInput = document.getElementById('update-file');
|
||||
const progressBar = document.getElementById('firm-progress');
|
||||
const progressLabel = document.getElementById('lbl-firm-progress');
|
||||
const submitButton = document.getElementById('submit-update-local');
|
||||
|
||||
submitButton.addEventListener('click', uploadFile);
|
||||
|
||||
function uploadFile(event)
|
||||
{
|
||||
event.preventDefault(); // Prevent the default form submission
|
||||
|
||||
const file = fileInput.files[0];
|
||||
const url = '/update'; // Replace with the actual upload URL
|
||||
|
||||
// File Checks
|
||||
//*********************************************************
|
||||
// Check file extension
|
||||
if (!file.name.toLowerCase().endsWith('.bin')) {
|
||||
alert('Please select a file with the ".bin" extension.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check filename prefix
|
||||
if (!file.name.toLowerCase().startsWith('fwata') && !file.name.toLowerCase().startsWith('lfsata')) {
|
||||
alert('Please select a file with a filename starting with "fwata" or "lfsata".');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check file size
|
||||
const maxSizeBytes = 2.7 * 1024 * 1024; // 2.75Mb in bytes
|
||||
if (file.size > maxSizeBytes) {
|
||||
alert('Please select a file with a size not exceeding 2.7Mb.');
|
||||
return;
|
||||
}
|
||||
//*********************************************************
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file-size', file.size); // Include the file size as a parameter
|
||||
formData.append('update-file', file);
|
||||
let s = "file-size: " + file.size;
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.upload.addEventListener('progress', (event) => {
|
||||
if (event.lengthComputable) {
|
||||
const progressPercent = Math.round((event.loaded * 100) / event.total);
|
||||
progressBar.value = progressPercent;
|
||||
progressLabel.innerHTML =progressBar.value + "%";
|
||||
}
|
||||
});
|
||||
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
console.log("received status: %d", xhr.status);
|
||||
if (xhr.status === 200) {
|
||||
// Upload completed successfully
|
||||
alert('Upload completed!');
|
||||
progressLabel.innerHTML = "Completed!";
|
||||
} else if (xhr.status === 500) {
|
||||
// Request was aborted (server-side)
|
||||
alert('Upload aborted by the server.');
|
||||
progressLabel.innerHTML = "Aborted!";
|
||||
} else {
|
||||
// Handle other error cases
|
||||
alert('An error occurred during the upload.');
|
||||
progressLabel.innerHTML = "Error!";
|
||||
}
|
||||
submitButton.disabled = false;
|
||||
progressBar.value = 0;
|
||||
progressLabel.innerHTML = "";
|
||||
}
|
||||
};
|
||||
|
||||
xhr.open('POST', url, true);
|
||||
xhr.send(formData);
|
||||
submitButton.disabled = true;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
74
Backup/data/www/global-style.css
Normal file
@ -0,0 +1,74 @@
|
||||
body {
|
||||
/*background-color: #f7f7f7;*/
|
||||
font-family: Tahoma, Arial, sans-serif;
|
||||
font-size: small;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
input{
|
||||
cursor:pointer;
|
||||
}
|
||||
input[type="number"]{
|
||||
width: 100px;
|
||||
}
|
||||
#submit {
|
||||
width:120px;
|
||||
}
|
||||
select{
|
||||
width:180px;
|
||||
}
|
||||
#select-path {
|
||||
width:250px;
|
||||
}
|
||||
#select-dir {
|
||||
width:90px;
|
||||
}
|
||||
#spacer-50 {
|
||||
height: 50px;
|
||||
}
|
||||
#spacer-20 {
|
||||
height: 20px;
|
||||
}
|
||||
#spacer-10 {
|
||||
height: 10px;
|
||||
}
|
||||
table {
|
||||
/*background-color: #dddddd;*/
|
||||
border-collapse: collapse;
|
||||
width:640px;
|
||||
margin: 0 auto;
|
||||
overflow: visible;
|
||||
}
|
||||
td, th {
|
||||
/*border: 1px solid #dddddd;*/
|
||||
text-align: left;
|
||||
padding: 2px;
|
||||
}
|
||||
#first_td_th {
|
||||
width:400px;
|
||||
}
|
||||
fieldset {
|
||||
width:700px;
|
||||
/*background-color: #f7f7f7;*/
|
||||
border-radius: 10px;
|
||||
border-color: blue;
|
||||
}
|
||||
#format-notice {
|
||||
color: #ff0000;
|
||||
}
|
||||
legend {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-color:white;
|
||||
background-blend-mode: darken;
|
||||
border-radius: 10px;
|
||||
padding: 1px 8px 2px 8px;
|
||||
border-style: solid;
|
||||
border-width: 1.0;
|
||||
}
|
||||
185
Backup/data/www/home.html
Normal file
@ -0,0 +1,185 @@
|
||||
{{NAVBAR}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="global-style.css" rel="stylesheet">
|
||||
|
||||
<title>Welcome</title>
|
||||
|
||||
<style>
|
||||
#first_td_th {
|
||||
width:240px;
|
||||
}
|
||||
#second_td_th {
|
||||
width:240px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>ATA Booth Summary</h1>
|
||||
|
||||
<fieldset>
|
||||
<legend>Booth Overview</legend>
|
||||
<div id="spacer-20"></div>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="first_td_th">
|
||||
Mode: Lumia
|
||||
</td>
|
||||
<td id="second_td_th">
|
||||
LED Strip1: Active
|
||||
</td>
|
||||
<td>
|
||||
Front Light: Active
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr><td>
|
||||
<div id="spacer-10"></div>
|
||||
</td></tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
App: DSLRBooth
|
||||
</td>
|
||||
<td>
|
||||
LED Strip2: Active
|
||||
</td>
|
||||
<td>
|
||||
Rear Light: Active
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr><td>
|
||||
<div id="spacer-10"></div>
|
||||
</td></tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
...
|
||||
</td>
|
||||
<td>
|
||||
...
|
||||
</td>
|
||||
<td>
|
||||
OLED: Active
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id="spacer-20"></div>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<fieldset>
|
||||
<legend>System Info:</legend>
|
||||
<div id="spacer-20"></div>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="first_td_th">
|
||||
Firmware Ver: 1.0
|
||||
</td>
|
||||
<td id="second_td_th">
|
||||
Flash Size:
|
||||
</td>
|
||||
<td>
|
||||
RAM Size:
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr><td>
|
||||
<div id="spacer-10"></div>
|
||||
</td></tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
Booth T°: 80.3F
|
||||
</td>
|
||||
<td>
|
||||
Flash Free:
|
||||
</td>
|
||||
<td>
|
||||
Used RAM:
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr><td>
|
||||
<div id="spacer-10"></div>
|
||||
</td></tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
Setpoint T°: 85F
|
||||
</td>
|
||||
<td>
|
||||
...
|
||||
</td>
|
||||
<td>
|
||||
Free RAM:
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id="spacer-20"></div>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<fieldset>
|
||||
<legend>Network / WiFi</legend>
|
||||
<div id="spacer-20"></div>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="first_td_th">
|
||||
IP Addr: 192.XXX.XXX.XXX
|
||||
</td>
|
||||
<td id="second_td_th">
|
||||
MAC Addr:
|
||||
</td>
|
||||
<td>
|
||||
WiFi SSID:
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr><td>
|
||||
<div id="spacer-10"></div>
|
||||
</td></tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
WiFi RSSi:
|
||||
</td>
|
||||
<td>
|
||||
WiFi Ch:
|
||||
</td>
|
||||
<td>
|
||||
Wifi Encryp:
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id="spacer-20"></div>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<fieldset>
|
||||
<legend>Bluetooth LE</legend>
|
||||
<div id="spacer-20"></div>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="first_td_th">
|
||||
Active: true
|
||||
</td>
|
||||
<td id="second_td_th">
|
||||
Connected: true
|
||||
</td>
|
||||
<td>
|
||||
Name: ATADevXX
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<div id="spacer-20"></div>
|
||||
</fieldset>
|
||||
</body>
|
||||
</html>
|
||||
11
Backup/data/www/info.html
Normal file
@ -0,0 +1,11 @@
|
||||
{{NAVBAR}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Welcome</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>System Information</h1>
|
||||
</body>
|
||||
</html>
|
||||
56
Backup/data/www/navbar.html
Normal file
@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items:flex-start;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.navbar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-color: rgb(113, 177, 216);
|
||||
padding: 1px;
|
||||
max-width: 64em;
|
||||
list-style-type: none; /* Remove the dot */
|
||||
margin: 0;
|
||||
}
|
||||
.navbar a {
|
||||
flex: 5;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #f2f2f2;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
padding: 5px;
|
||||
transition: background-color 0.3s;
|
||||
font-weight: bold;
|
||||
font-family: Tahoma, Arial, sans-serif;
|
||||
white-space: nowrap; /* Prevent text from wrapping */
|
||||
margin-left: 16px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
.navbar a:hover {
|
||||
background-color: #ddd;
|
||||
color: black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav>
|
||||
<ul class="navbar">
|
||||
<li></li><a href="/home" target="_top">HOME</a></li>
|
||||
<li></li><a href="/appcontrol" target="_top">APP CONTROL</a></li>
|
||||
<li></li><a href="/boothconfig" target="_top">BOOTH CONFIG</a></li>
|
||||
<li></li><a href="/filemanager" target="_top">FILES MANAGER</a></li>
|
||||
<li></li><a href="/wifiportal" target="_top">WIFI</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
</body>
|
||||
</html>
|
||||
24
Backup/data/www/ok.html
Normal file
@ -0,0 +1,24 @@
|
||||
{{NAVBAR}}
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Update Success</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
#spacer-50 {
|
||||
height: 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<center>
|
||||
<h2>The update was successful.</h2>
|
||||
<div id="spacer-50"></div>
|
||||
<button onclick="window.location.href='/filemanager';">to homepage</button>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
||||
128
Backup/data/www/wifiportal.html
Normal file
@ -0,0 +1,128 @@
|
||||
{{NAVBAR}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>WiFi Credentials</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f2f2f2;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
h1 {
|
||||
margin: 30px 0 20px;
|
||||
text-align: center;
|
||||
}
|
||||
form {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
|
||||
margin: auto;
|
||||
max-width: 500px;
|
||||
}
|
||||
input[type="text"], input[type="password"] {
|
||||
padding: 12px 20px;
|
||||
margin: 8px 0;
|
||||
box-sizing: border-box;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background-color: #f2f2f2;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
button {
|
||||
background-color: #2d2dfa;
|
||||
color: #fff;
|
||||
padding: 12px 20px;
|
||||
margin: 8px 0;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
img{
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right:auto;
|
||||
height: auto;
|
||||
width: auto;
|
||||
max-width: 150px;
|
||||
filter: drop-shadow(2px 2px 2px #666666);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Enter WiFi Credentials</h1>
|
||||
<form id="wifi-credentials-form">
|
||||
<label for="ssid">SSID:</label>
|
||||
<input type="text" id="ssid" name="ssid" placeholder="SSID" required>
|
||||
<label for="password">Password:</label>
|
||||
<input type="text" id="password" name="password" placeholder="Password" required>
|
||||
<button type="button" onclick="postCredentials(event)">Submit</button>
|
||||
<img src="img/atalogo.png" alt="Image not found" >
|
||||
</form>
|
||||
|
||||
<script>
|
||||
const form = document.querySelector('#wifi-credentials-form');
|
||||
window.onload = getSSID();
|
||||
|
||||
function getSSID(){
|
||||
const params = new URLSearchParams();
|
||||
params.append('type', 'wifi');
|
||||
const url = '/get?' + params.toString();
|
||||
|
||||
fetch(url, { method: 'GET'})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
console.log('Request successful');
|
||||
return response.json();
|
||||
}
|
||||
else { throw new Error('Request failed'); }
|
||||
})
|
||||
.then(data => {
|
||||
console.log(data);
|
||||
form.ssid.value = data.ssid;
|
||||
form.password.placeholder = '*'.repeat(data.pass);
|
||||
})
|
||||
.catch(error => { console.error(error); });
|
||||
}
|
||||
|
||||
function postCredentials(event){
|
||||
event.preventDefault();
|
||||
|
||||
if(!inputValidationOk()){ return; }
|
||||
|
||||
var creds = {
|
||||
ssid : form.ssid.value,
|
||||
pass : form.password.value
|
||||
};
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.append('type', 'wifi');
|
||||
const url = '/post?' + params.toString();
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify(creds)
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) { console.log('Request successful'); }
|
||||
else { throw new Error('Request failed'); }
|
||||
})
|
||||
.catch(error => { console.error(error); });
|
||||
}
|
||||
|
||||
function inputValidationOk(){
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
15
ToDo.txt
Normal file
@ -0,0 +1,15 @@
|
||||
1 - Second Strip Animation
|
||||
a - How to synchronize the 2 Animation
|
||||
b - Set Default directions?
|
||||
|
||||
2 - Wifi Server
|
||||
|
||||
3 - OTA Upgrade
|
||||
|
||||
4 - menu
|
||||
a - file manager & editor
|
||||
b - wifi credentials
|
||||
c - firmware update
|
||||
d - mode selection page
|
||||
i - booth config and reboot
|
||||
ii -
|
||||
51
boards/wroom-32S3-N8R2.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino":{
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"partitions": "default_8MB.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DARDUINO_ESP32S3_DEV",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1",
|
||||
"-DBOARD_HAS_PSRAM"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"hwids": [
|
||||
[
|
||||
"0x303A",
|
||||
"0x1001"
|
||||
]
|
||||
],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "esp32s3"
|
||||
},
|
||||
"connectivity": [
|
||||
"wifi"
|
||||
],
|
||||
"debug": {
|
||||
"default_tool": "esp-builtin",
|
||||
"onboard_tools": [
|
||||
"esp-builtin"
|
||||
],
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "My ESP32-S3-DevKitN8R2 (8 MB QD, 2M PSRAM)",
|
||||
"upload": {
|
||||
"flash_size": "8MB",
|
||||
"maximum_ram_size": 524288,
|
||||
"maximum_size": 8388608,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/esp32s3/user-guide-devkitc-1.html",
|
||||
"vendor": "Espressif"
|
||||
}
|
||||
37
boards/wroom-32d-8mb.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino":{
|
||||
"ldscript": "esp32_out.ld"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": "-DARDUINO_ESP32_DEV",
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
"flash_mode": "dio",
|
||||
"mcu": "esp32",
|
||||
"variant": "esp32"
|
||||
},
|
||||
"connectivity": [
|
||||
"wifi",
|
||||
"bluetooth",
|
||||
"ethernet",
|
||||
"can"
|
||||
],
|
||||
"debug": {
|
||||
"openocd_board": "esp-wroom-32.cfg"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino",
|
||||
"espidf"
|
||||
],
|
||||
"name": "WROOM-32D DEV MODULE",
|
||||
"upload": {
|
||||
"flash_size": "8MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 8388608,
|
||||
"require_upload_port": true,
|
||||
"speed": 460800
|
||||
},
|
||||
"url": "https://en.wikipedia.org/wiki/ESP32",
|
||||
"vendor": "Consius"
|
||||
}
|
||||
508
data/ata-boothifier-upgrade.html
Normal file
@ -0,0 +1,508 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ATA Firmware Update</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background-color: #f4f4f4;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 22px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.status-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: left;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.status-indicator-ble {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: gray;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.status-indicator-wifi {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: gray;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.status-indicator-internet {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: gray;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Adds space above the WiFi Connect button */
|
||||
.btn-container.wifi {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
max-width: 130px;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
button:hover:not(:disabled) {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ccc;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 90%;
|
||||
max-width: 300px;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
body {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: 14px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>ATA Firmware Update</h1>
|
||||
|
||||
<!-- Status Indicators -->
|
||||
<div class="status-container">
|
||||
<span class="status-indicator-ble"></span>
|
||||
<label id="status-ble-connection">Device: ...</label>
|
||||
</div>
|
||||
|
||||
<div class="status-container">
|
||||
<span class="status-indicator-wifi"></span>
|
||||
<label id="status-wifi-client">Wifi Client: ...</label>
|
||||
</div>
|
||||
|
||||
<div class="status-container">
|
||||
<span class="status-indicator-internet"></span>
|
||||
<label id="status-internet">Internet: ...</label>
|
||||
</div>
|
||||
|
||||
<div class="status-container">
|
||||
<label id="status-current-version">Curr Version: ...</label>
|
||||
</div>
|
||||
<div class="status-container">
|
||||
<label id="status-new-version">New Version: ...</label>
|
||||
</div>
|
||||
|
||||
<!-- Buttons -->
|
||||
<div class="btn-container">
|
||||
<button id="bleConnectBtn">Connect</button>
|
||||
<button id="checkStatusBtn" disabled>Check Status</button>
|
||||
</div>
|
||||
|
||||
<!-- Log Area -->
|
||||
<textarea id="logArea" readonly></textarea>
|
||||
|
||||
<div class="btn-container">
|
||||
<button id="checkVersionBtn" disabled>Check Version</button>
|
||||
<button id="startUpgradeBtn" disabled>Start Update</button>
|
||||
</div>
|
||||
|
||||
<!-- Wi-Fi Input Fields -->
|
||||
<div class="input-container">
|
||||
<input type="text" id="wifissid" name="wifissid" placeholder="Enter WiFi SSID" required>
|
||||
<input type="password" id="wifipassword" name="wifipassword" placeholder="Enter WiFi Password" required>
|
||||
<div style="display: flex; align-items: center; gap: 5px;">
|
||||
<input type="checkbox" id="showPassword" style="width: auto;">
|
||||
<label for="showPassword">Show Password</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Added margin-top above this button -->
|
||||
<div class="btn-container wifi">
|
||||
<button id="wifiConnectBtn" disabled>Connect Wifi</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Constants
|
||||
const BLE_SERVER_NAME = "ATALIGHTS"; // Replace with your server name
|
||||
const BLE_SERVICE_UUID = "abcdef01-2345-6789-1234-56789abcdef0"; // Replace with your service UUID
|
||||
const BLE_CHARACTERISTIC1_UUID = "abcdef01-2345-6789-1234-56789abcdef1"; // Replace with your characteristic UUID
|
||||
const BLE_CHARACTERISTIC2_UUID = "abcdef02-2345-6789-1234-56789abcdef1"; // Replace with your characteristic UUID
|
||||
|
||||
let bleDevice = null;
|
||||
let bleCharacteristic1 = null;
|
||||
let bleCharacteristic2 = null;
|
||||
let bleConnected = false;
|
||||
|
||||
const WIFI_STAT = { WIFI_DISCONNECTED:0, WIFI_BAD_CREDS:1, WIFI_NO_AP:2, WIFI_CONNECTED:3 };
|
||||
|
||||
let updatePacket = {
|
||||
wifiConnected: false,
|
||||
wifiOnline: false,
|
||||
wifiIP: [0, 0, 0, 0],
|
||||
currVersion: [0, 0, 0],
|
||||
newVersion: [0, 0, 0]
|
||||
};
|
||||
|
||||
// Log messages to the textarea
|
||||
function logMessage(message) {
|
||||
const logArea = document.getElementById('logArea');
|
||||
logArea.value += message + '\n';
|
||||
logArea.scrollTop = logArea.scrollHeight;
|
||||
}
|
||||
|
||||
// Function to scan for BLE devices
|
||||
async function scanForDevices() {
|
||||
logMessage('Scanning for BLE devices...');
|
||||
try {
|
||||
const device = await navigator.bluetooth.requestDevice({
|
||||
acceptAllDevices: true,
|
||||
optionalServices: [BLE_SERVICE_UUID]
|
||||
});
|
||||
|
||||
if (device) {
|
||||
logMessage(`Found device: ${device.name || "Unnamed"} (ID: ${device.id})`);
|
||||
} else {
|
||||
logMessage('No devices found.');
|
||||
}
|
||||
} catch (error) {
|
||||
logMessage(`Scan failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to connect to the BLE server
|
||||
async function connectToBle() {
|
||||
try {
|
||||
bleDevice = await navigator.bluetooth.requestDevice({
|
||||
filters: [{ name: BLE_SERVER_NAME }],
|
||||
optionalServices: [BLE_SERVICE_UUID]
|
||||
});
|
||||
|
||||
//logMessage(`Connecting to ${bleDevice.name}`);
|
||||
const server = await bleDevice.gatt.connect();
|
||||
//await server.setPreferredMtu(247); // Request larger MTU size
|
||||
|
||||
const service = await server.getPrimaryService(BLE_SERVICE_UUID);
|
||||
|
||||
bleCharacteristic1 = await service.getCharacteristic(BLE_CHARACTERISTIC1_UUID);
|
||||
|
||||
// Subscribe to notifications
|
||||
//await bleCharacteristic1.startNotifications();
|
||||
|
||||
// Add event listener for incoming notifications
|
||||
//bleCharacteristic1.addEventListener('characteristicvaluechanged', handleChar1Notifications);
|
||||
|
||||
|
||||
//logMessage('Getting characteristic...');
|
||||
bleCharacteristic2 = await service.getCharacteristic(BLE_CHARACTERISTIC2_UUID);
|
||||
|
||||
// Subscribe to notifications
|
||||
await bleCharacteristic2.startNotifications();
|
||||
|
||||
// Add event listener for incoming notifications
|
||||
bleCharacteristic2.addEventListener('characteristicvaluechanged', (event) => {
|
||||
const value = event.target.value;
|
||||
const decoder = new TextDecoder();
|
||||
const decodedValue = decoder.decode(value);
|
||||
logMessage('--> ' + decodedValue);
|
||||
});
|
||||
|
||||
bleConnected = true;
|
||||
document.getElementById('bleConnectBtn').disabled = true;
|
||||
document.querySelector('.status-indicator-ble').style.backgroundColor = 'green';
|
||||
document.getElementById('status-ble-connection').textContent = 'Device: Connected';
|
||||
document.getElementById('wifiConnectBtn').disabled = false;
|
||||
document.getElementById('checkStatusBtn').disabled = false;
|
||||
logMessage(`Connected to ${bleDevice.name}`);
|
||||
|
||||
await readPacket();
|
||||
processUpdatePacket(updatePacket);
|
||||
|
||||
} catch (error) {
|
||||
if (error.message.includes("cancelled")) {
|
||||
logMessage("Connection cancelled by user.");
|
||||
} else {
|
||||
logMessage(`Connection failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function sendPacket(packetMsg) {
|
||||
if (!bleCharacteristic1) {
|
||||
console.log("Cannot send packet: Not connected to BLE server.");
|
||||
return;
|
||||
}
|
||||
|
||||
const maxRetries = 3;
|
||||
const retryDelay = 1000; // 1 second
|
||||
let attempt = 0;
|
||||
|
||||
while (attempt < maxRetries) {
|
||||
try {
|
||||
//logMessage(`Sending request: ${packetMsg} (Attempt ${attempt + 1})`);
|
||||
const encoder = new TextEncoder();
|
||||
await bleCharacteristic1.writeValueWithResponse(encoder.encode(packetMsg));
|
||||
//console.log("Request sent successfully");
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error(`Failed to send packet: ${error.message}`);
|
||||
attempt++;
|
||||
if (attempt < maxRetries) {
|
||||
console.log(`Retrying in ${retryDelay / 1000} seconds...`);
|
||||
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||
} else {
|
||||
console.error("Max retries reached. Failed to send request.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function readPacket() {
|
||||
if (!bleCharacteristic1) {
|
||||
console.log("Cannot read packet: Not connected to BLE server.");
|
||||
return;
|
||||
}
|
||||
|
||||
const maxRetries = 3;
|
||||
const retryDelay = 1000; // 1 second
|
||||
let attempt = 0;
|
||||
|
||||
while (attempt < maxRetries) {
|
||||
try {
|
||||
const value = await bleCharacteristic1.readValue();
|
||||
const data = new Uint8Array(value.buffer);
|
||||
if (data.length === 12) {
|
||||
updatePacket.wifiConnected = data[0] !== 0;
|
||||
updatePacket.wifiOnline = data[1] !== 0;
|
||||
updatePacket.wifiIP = [data[2], data[3], data[4], data[5]];
|
||||
updatePacket.currVersion = [data[6], data[7], data[8]];
|
||||
updatePacket.newVersion = [data[9], data[10], data[11]];
|
||||
|
||||
//processUpdatePacket(updatePacket);
|
||||
return;
|
||||
}
|
||||
console.log("Invalid packet length");
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error(`Failed to read packet: ${error.message}`);
|
||||
attempt++;
|
||||
if (attempt < maxRetries) {
|
||||
console.log(`Retrying in ${retryDelay / 1000} seconds...`);
|
||||
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||
} else {
|
||||
console.error("Max retries reached. Failed to read packet.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process update packet
|
||||
function processUpdatePacket(packet) {
|
||||
// Process the packet data
|
||||
//console.log("Processing update packet:", packet);
|
||||
if(packet.wifiConnected === true) {
|
||||
if(packet.wifiConnected && packet.wifiIP[0] > 0) {
|
||||
document.getElementById('status-wifi-client').textContent = 'Wifi Client: Connected (' + packet.wifiIP.join('.') + ')';
|
||||
} else {
|
||||
document.getElementById('status-wifi-client').textContent = 'Wifi Client: Connected';
|
||||
}
|
||||
document.querySelector('.status-indicator-wifi').style.backgroundColor = 'green';
|
||||
} else {
|
||||
document.getElementById('status-wifi-client').textContent = 'Wifi Client: ...';
|
||||
document.querySelector('.status-indicator-wifi').style.backgroundColor = 'gray';
|
||||
}
|
||||
|
||||
if(packet.wifiOnline === true) {
|
||||
document.getElementById('status-internet').textContent = 'Online';
|
||||
document.querySelector('.status-indicator-internet').style.backgroundColor = 'green';
|
||||
document.getElementById('checkVersionBtn').disabled = false;
|
||||
|
||||
} else {
|
||||
document.getElementById('status-internet').textContent = 'Offline';
|
||||
document.querySelector('.status-indicator-internet').style.backgroundColor = 'gray';
|
||||
document.getElementById('checkVersionBtn').disabled = true;
|
||||
}
|
||||
|
||||
if (packet.currVersion[0] > 0) {
|
||||
document.getElementById('status-current-version').textContent = 'Curr Version: ' + packet.currVersion.join('.');
|
||||
} else {
|
||||
document.getElementById('status-current-version').textContent = 'Curr Version: ...';
|
||||
}
|
||||
|
||||
if (packet.newVersion[0] > 0) {
|
||||
document.getElementById('status-new-version').textContent = 'New Version: ' + packet.newVersion.join('.');
|
||||
document.getElementById('checkVersionBtn').disabled = true;
|
||||
|
||||
if(packet.wifiOnline && packet.newVersion[0] > packet.currVersion[0] ||
|
||||
(packet.newVersion[0] === packet.currVersion[0] && packet.newVersion[1] > packet.currVersion[1]) ||
|
||||
(packet.newVersion[0] === packet.currVersion[0] && packet.newVersion[1] === packet.currVersion[1] && packet.newVersion[2] > packet.currVersion[2])) {
|
||||
|
||||
//enable start upgrade button
|
||||
logMessage("New Version Available:");
|
||||
document.getElementById('startUpgradeBtn').disabled = false;
|
||||
} else {
|
||||
//disable start upgrade button
|
||||
document.getElementById('startUpgradeBtn').disabled = true;
|
||||
logMessage("New Version: Not Available");
|
||||
}
|
||||
} else {
|
||||
document.getElementById('status-new-version').textContent = 'New Version: ...';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//BLE_Characteristic.addEventListener('characteristicvaluechanged', handleNotifications);
|
||||
function handleChar1Notifications(event) {
|
||||
const data = new Uint8Array(event.data);
|
||||
|
||||
if (data.length !== 12) { // 1 byte for id, 4 bytes for booleans, 4 bytes for wifiIP, 3 bytes for currVersion, 3 bytes for newVersion
|
||||
console.log("Invalid packet length");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update existing updatePacket object instead of creating new one
|
||||
updatePacket.wifiConnected = data[0] !== 0;
|
||||
updatePacket.internetAvailable = data[1] !== 0;
|
||||
updatePacket.wifiIP = [data[2], data[3], data[4], data[5]];
|
||||
updatePacket.currVersion = [data[6], data[7], data[8]];
|
||||
updatePacket.newVersion = [data[9], data[10], data[11]];
|
||||
|
||||
processUpdatePacket(updatePacket);
|
||||
}
|
||||
|
||||
document.getElementById('showPassword').addEventListener('change', function() {
|
||||
const passwordInput = document.getElementById('wifipassword');
|
||||
passwordInput.type = this.checked ? 'text' : 'password';
|
||||
});
|
||||
|
||||
// Event listeners for buttons
|
||||
document.getElementById('bleConnectBtn').addEventListener('click', connectToBle);
|
||||
document.getElementById('checkStatusBtn').addEventListener('click', async () => {
|
||||
if (bleCharacteristic1) {
|
||||
await readPacket();
|
||||
processUpdatePacket(updatePacket);
|
||||
} else {
|
||||
logMessage('BLE device not connected.');
|
||||
}
|
||||
});
|
||||
document.getElementById('checkVersionBtn').addEventListener('click', async () => {
|
||||
await sendPacket('version-check');
|
||||
// loop and monitor the the updatePacket.newVersion
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
success = false;
|
||||
for (let i = 0; i < 20; i++) {
|
||||
await readPacket();
|
||||
if (updatePacket.newVersion[0] > 0) {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
if(success) {
|
||||
processUpdatePacket(updatePacket);
|
||||
logMessage("New Version: Available");
|
||||
} else {
|
||||
logMessage("New Version: Not Available");
|
||||
}
|
||||
|
||||
});
|
||||
document.getElementById('wifiConnectBtn').addEventListener('click', async () => {
|
||||
const ssid = document.getElementById('wifissid').value;
|
||||
const password = document.getElementById('wifipassword').value;
|
||||
if (ssid && password) {
|
||||
// Send credentials to the device
|
||||
jsonString = ' {"ssid":"' + ssid + '","pass":"' + password + '"} ';
|
||||
await sendPacket('wifi-connect' + jsonString);
|
||||
|
||||
await readPacket();
|
||||
processUpdatePacket(updatePacket);
|
||||
} else {
|
||||
alert('Please enter both SSID and password.');
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
document.getElementById('startUpgradeBtn').addEventListener('click', async () => {
|
||||
try {
|
||||
await sendPacket('upgrade-start');
|
||||
logMessage("Upgrade Starting... Please wait.");
|
||||
} catch (error) {
|
||||
logMessage(`Error starting upgrade: ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Initial call to process the update packet with defaults
|
||||
processUpdatePacket(updatePacket);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
30
data/boards/board14.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"rgb1": 3,
|
||||
"rgb2": 46,
|
||||
"btn1": 8,
|
||||
"btn2": 19,
|
||||
"btn3": 0,
|
||||
"buzzer": 37,
|
||||
"touch1": 9,
|
||||
"touch2": 10,
|
||||
"touch3": 11,
|
||||
"touch4": 12,
|
||||
"touch5": 13,
|
||||
"shield": 9,
|
||||
"relay1": 1,
|
||||
"relay2": 2,
|
||||
"relay3": 3,
|
||||
"relay4": 4,
|
||||
"stat1": 17,
|
||||
"stat2": 18,
|
||||
"adc1": 20,
|
||||
"oled_dc": 7,
|
||||
"oled_rst": 6,
|
||||
"oled_mosi": 5,
|
||||
"oled_sck": 4,
|
||||
"oled_cs": 15,
|
||||
"ext1": 35,
|
||||
"ext2": 36,
|
||||
"rf433tx": 38,
|
||||
"rf433rx": 16
|
||||
}
|
||||
30
data/boards/board15.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"rgb1": 3,
|
||||
"rgb2": 9,
|
||||
"btn1": 8,
|
||||
"btn2": 18,
|
||||
"btn3": 0,
|
||||
"buzzer": 42,
|
||||
"touch1": 11,
|
||||
"touch2": 12,
|
||||
"touch3": 13,
|
||||
"touch4": 21,
|
||||
"touch5": -1,
|
||||
"shield": 14,
|
||||
"relay1": 38,
|
||||
"relay2": 48,
|
||||
"relay3": 47,
|
||||
"relay4": 21,
|
||||
"stat1": 39,
|
||||
"stat2": 37,
|
||||
"adc1": 10,
|
||||
"oled_dc": 7,
|
||||
"oled_rst": 6,
|
||||
"oled_mosi": 5,
|
||||
"oled_sck": 4,
|
||||
"oled_cs": 15,
|
||||
"ext1": 41,
|
||||
"ext2": -1,
|
||||
"rf433tx": 40,
|
||||
"rf433rx": 16
|
||||
}
|
||||
30
data/boards/test.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"rgb1": 3,
|
||||
"rgb2": 38,
|
||||
"btn1": 8,
|
||||
"btn2": 19,
|
||||
"btn3": -1,
|
||||
"buzzer": -1,
|
||||
"touch1": -1,
|
||||
"touch2": -1,
|
||||
"touch3": -1,
|
||||
"touch4": -1,
|
||||
"touch5": -1,
|
||||
"shield": -1,
|
||||
"relay1": 1,
|
||||
"relay2": 2,
|
||||
"relay3": 3,
|
||||
"relay4": 4,
|
||||
"stat1": -1,
|
||||
"stat2": -1,
|
||||
"adc1": 10,
|
||||
"oled_dc": 7,
|
||||
"oled_rst": 6,
|
||||
"oled_mosi": 5,
|
||||
"oled_sck": 4,
|
||||
"oled_cs": 15,
|
||||
"ext1": -1,
|
||||
"ext2": -1,
|
||||
"rf433tx": -1,
|
||||
"rf433rx": -1
|
||||
}
|
||||
135
data/booths/custom.json
Normal file
@ -0,0 +1,135 @@
|
||||
{
|
||||
"mode": 0,
|
||||
"buttons":
|
||||
[
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
}
|
||||
],
|
||||
"pwmout":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
}
|
||||
],
|
||||
"ramp-lights":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 0,
|
||||
"button-index": 0,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 1,
|
||||
"button-index": 1,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
}
|
||||
],
|
||||
"oled": {
|
||||
"en": false,
|
||||
"height": 64,
|
||||
"width": 128
|
||||
},
|
||||
"t-sensor": {
|
||||
"en": true,
|
||||
"addr": 72,
|
||||
"sp1": 85.0,
|
||||
"fan-pwr1": 50.0,
|
||||
"sp2": 90.0,
|
||||
"fan-pwr2": 100.0,
|
||||
"hyst": 1.0,
|
||||
"relay": 3,
|
||||
"interval": 5000
|
||||
},
|
||||
"adc": {
|
||||
"ain1_factor": 1.0
|
||||
},
|
||||
"status-led":{
|
||||
"interval": 250
|
||||
},
|
||||
"strips":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"size": 138,
|
||||
"chip": "WS6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"size": 30,
|
||||
"chip": "WS6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
}
|
||||
],
|
||||
"rx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"tx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"ble":{
|
||||
"en": true,
|
||||
"name": "Ata_Lights",
|
||||
"core": 1
|
||||
}
|
||||
}
|
||||
137
data/booths/helio-flare.json
Normal file
@ -0,0 +1,137 @@
|
||||
{
|
||||
"mode": 0,
|
||||
"buttons":
|
||||
[
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
}
|
||||
],
|
||||
"pwmout":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
}
|
||||
],
|
||||
"ramp-lights":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 0,
|
||||
"button-index": 0,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 1,
|
||||
"button-index": 1,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
}
|
||||
],
|
||||
"oled": {
|
||||
"en": false,
|
||||
"height": 64,
|
||||
"width": 128
|
||||
},
|
||||
"t-sensor": {
|
||||
"en": true,
|
||||
"addr": 72,
|
||||
"sp1": 85.0,
|
||||
"fan-pwr1": 50.0,
|
||||
"sp2": 90.0,
|
||||
"fan-pwr2": 100.0,
|
||||
"hyst": 1.0,
|
||||
"relay": 3,
|
||||
"interval": 5000
|
||||
},
|
||||
"adc": {
|
||||
"ain1_factor": 1.0
|
||||
},
|
||||
"status-led":{
|
||||
"interval": 250
|
||||
},
|
||||
"strips":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"size": 168,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "RGB",
|
||||
"shift":-42,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1,
|
||||
"bright": 200
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"size": 20,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "RGB",
|
||||
"shift":0,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1,
|
||||
"bright": 200
|
||||
}
|
||||
],
|
||||
"rx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"tx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"ble":{
|
||||
"en": true,
|
||||
"name": "Ata_Lights",
|
||||
"core": 1
|
||||
}
|
||||
}
|
||||
134
data/booths/helio-posh.json
Normal file
@ -0,0 +1,134 @@
|
||||
{
|
||||
"buttons":
|
||||
[
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
}
|
||||
],
|
||||
"pwmout":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
}
|
||||
],
|
||||
"ramp-lights":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 0,
|
||||
"button-index": 0,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 1,
|
||||
"button-index": 1,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
}
|
||||
],
|
||||
"oled": {
|
||||
"en": false,
|
||||
"height": 64,
|
||||
"width": 128
|
||||
},
|
||||
"t-sensor": {
|
||||
"en": true,
|
||||
"addr": 72,
|
||||
"sp1": 85.0,
|
||||
"fan-pwr1": 50.0,
|
||||
"sp2": 90.0,
|
||||
"fan-pwr2": 100.0,
|
||||
"hyst": 1.0,
|
||||
"relay": 3,
|
||||
"interval": 5000
|
||||
},
|
||||
"adc": {
|
||||
"ain1_factor": 1.0
|
||||
},
|
||||
"status-led":{
|
||||
"interval": 250
|
||||
},
|
||||
"strips":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"size": 138,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"size": 30,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
}
|
||||
],
|
||||
"rx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"tx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"ble":{
|
||||
"en": true,
|
||||
"name": "Ata_Lights",
|
||||
"core": 1
|
||||
}
|
||||
}
|
||||
135
data/booths/helio-sport.json
Normal file
@ -0,0 +1,135 @@
|
||||
{
|
||||
"mode": 0,
|
||||
"buttons":
|
||||
[
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
}
|
||||
],
|
||||
"pwmout":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
}
|
||||
],
|
||||
"ramp-lights":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 0,
|
||||
"button-index": 0,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 1,
|
||||
"button-index": 1,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
}
|
||||
],
|
||||
"oled": {
|
||||
"en": false,
|
||||
"height": 64,
|
||||
"width": 128
|
||||
},
|
||||
"t-sensor": {
|
||||
"en": true,
|
||||
"addr": 72,
|
||||
"sp1": 85.0,
|
||||
"fan-pwr1": 50.0,
|
||||
"sp2": 90.0,
|
||||
"fan-pwr2": 100.0,
|
||||
"hyst": 1.0,
|
||||
"relay": 3,
|
||||
"interval": 5000
|
||||
},
|
||||
"adc": {
|
||||
"ain1_factor": 1.0
|
||||
},
|
||||
"status-led":{
|
||||
"interval": 250
|
||||
},
|
||||
"strips":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"size": 138,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"size": 30,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
}
|
||||
],
|
||||
"rx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"tx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"ble":{
|
||||
"en": true,
|
||||
"name": "Ata_Lights",
|
||||
"core": 1
|
||||
}
|
||||
}
|
||||
138
data/booths/light-stik.json
Normal file
@ -0,0 +1,138 @@
|
||||
{
|
||||
"mode": "stik",
|
||||
"buttons":
|
||||
[
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
}
|
||||
],
|
||||
"pwmout":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
}
|
||||
],
|
||||
"ramp-lights":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 0,
|
||||
"button-index": 0,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 1,
|
||||
"button-index": 1,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
}
|
||||
],
|
||||
"oled": {
|
||||
"en": false,
|
||||
"height": 64,
|
||||
"width": 128
|
||||
},
|
||||
"t-sensor": {
|
||||
"en": true,
|
||||
"addr": 72,
|
||||
"sp1": 85.0,
|
||||
"fan-pwr1": 50.0,
|
||||
"sp2": 90.0,
|
||||
"fan-pwr2": 100.0,
|
||||
"hyst": 1.0,
|
||||
"relay": 3,
|
||||
"interval": 5000
|
||||
},
|
||||
"adc": {
|
||||
"ain1_factor": 1.0
|
||||
},
|
||||
"status-led":{
|
||||
"interval": 250
|
||||
},
|
||||
"strips":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"size": 144,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "RGB",
|
||||
"shift":0,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1,
|
||||
"bright": 200
|
||||
},
|
||||
{
|
||||
"en": false,
|
||||
"size": 20,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "RGB",
|
||||
"shift":0,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1,
|
||||
"bright": 200
|
||||
}
|
||||
],
|
||||
"rx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"tx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"ble":{
|
||||
"en": true,
|
||||
"name": "FlashStik",
|
||||
"core": 1
|
||||
}
|
||||
}
|
||||
|
||||
140
data/booths/lumia-m.json
Normal file
@ -0,0 +1,140 @@
|
||||
{
|
||||
"mode": 0,
|
||||
"buttons":
|
||||
[
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
}
|
||||
],
|
||||
"pwmout":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
}
|
||||
],
|
||||
"ramp-lights":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 0,
|
||||
"button-index": 0,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 1,
|
||||
"button-index": 1,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
}
|
||||
],
|
||||
"oled": {
|
||||
"en": false,
|
||||
"height": 64,
|
||||
"width": 128
|
||||
},
|
||||
"t-sensor": {
|
||||
"en": true,
|
||||
"addr": 72,
|
||||
"sp1": 85.0,
|
||||
"fan-pwr1": 50.0,
|
||||
"sp2": 90.0,
|
||||
"fan-pwr2": 100.0,
|
||||
"hyst": 1.0,
|
||||
"relay": 3,
|
||||
"interval": 5000
|
||||
},
|
||||
"adc": {
|
||||
"ain1_factor": 1.0
|
||||
},
|
||||
"status-led":{
|
||||
"interval": 250
|
||||
},
|
||||
"buzzer":{
|
||||
"en": true
|
||||
},
|
||||
"strips":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"size": 168,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "RGB",
|
||||
"shift": 0,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1,
|
||||
"bright": 200
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"size": 30,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "RGB",
|
||||
"shift": 0,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1,
|
||||
"bright": 200
|
||||
}
|
||||
],
|
||||
"rx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"tx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"ble":{
|
||||
"en": true,
|
||||
"name": "Ata_Lights",
|
||||
"core": 1
|
||||
}
|
||||
}
|
||||
135
data/booths/lumia-spectra.json
Normal file
@ -0,0 +1,135 @@
|
||||
{
|
||||
"mode": 0,
|
||||
"buttons":
|
||||
[
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
}
|
||||
],
|
||||
"pwmout":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
}
|
||||
],
|
||||
"ramp-lights":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 0,
|
||||
"button-index": 0,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 1,
|
||||
"button-index": 1,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
}
|
||||
],
|
||||
"oled": {
|
||||
"en": false,
|
||||
"height": 64,
|
||||
"width": 128
|
||||
},
|
||||
"t-sensor": {
|
||||
"en": true,
|
||||
"addr": 72,
|
||||
"sp1": 85.0,
|
||||
"fan-pwr1": 50.0,
|
||||
"sp2": 90.0,
|
||||
"fan-pwr2": 100.0,
|
||||
"hyst": 1.0,
|
||||
"relay": 3,
|
||||
"interval": 5000
|
||||
},
|
||||
"adc": {
|
||||
"ain1_factor": 1.0
|
||||
},
|
||||
"status-led":{
|
||||
"interval": 250
|
||||
},
|
||||
"strips":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"size": 138,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"size": 30,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
}
|
||||
],
|
||||
"rx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"tx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"ble":{
|
||||
"en": true,
|
||||
"name": "Ata_Lights",
|
||||
"core": 1
|
||||
}
|
||||
}
|
||||
159
data/booths/lumia-xl.json
Normal file
@ -0,0 +1,159 @@
|
||||
{
|
||||
"mode": 0,
|
||||
"buttons":
|
||||
[
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
}
|
||||
],
|
||||
"pwmout":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
}
|
||||
],
|
||||
"ramp-lights":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 0,
|
||||
"button-index": 0,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 1,
|
||||
"button-index": 1,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
}
|
||||
],
|
||||
"oled": {
|
||||
"en": false,
|
||||
"height": 64,
|
||||
"width": 128
|
||||
},
|
||||
"t-sensor": {
|
||||
"en": true,
|
||||
"addr": 72,
|
||||
"sp1": 85.0,
|
||||
"fan-pwr1": 50.0,
|
||||
"sp2": 90.0,
|
||||
"fan-pwr2": 100.0,
|
||||
"hyst": 1.0,
|
||||
"relay": 3,
|
||||
"interval": 5000
|
||||
},
|
||||
"adc": {
|
||||
"ain1_factor": 1.0
|
||||
},
|
||||
"status-led":{
|
||||
"interval": 250
|
||||
},
|
||||
"strips":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"size": 138,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"size": 30,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
}
|
||||
],
|
||||
"rx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"tx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"ble":{
|
||||
"en": true,
|
||||
"name": "Ata_Lights",
|
||||
"core": 1
|
||||
},
|
||||
"wifi-client":{
|
||||
"en": true,
|
||||
"mdns-name": "atadev",
|
||||
"ssid": "padillaguest",
|
||||
"pass": "padillaguest"
|
||||
},
|
||||
"wifi-ap":{
|
||||
"ssid": "ATA_AP",
|
||||
"append-id": true,
|
||||
"pass": "12345678",
|
||||
"ip": [192,168,10.1],
|
||||
"gateway": [192,168,10,200],
|
||||
"subnet": [255,255,255,0]
|
||||
},
|
||||
"animation1":{
|
||||
"white-max-time": 30,
|
||||
"white-min": 20,
|
||||
"white-max": 100,
|
||||
"toggle-cycles": 2,
|
||||
"comet-size": 0.2,
|
||||
"comet-fade": 96,
|
||||
"fire-cooling": 66,
|
||||
"fire-sparking": 62
|
||||
}
|
||||
}
|
||||
135
data/booths/m1.json
Normal file
@ -0,0 +1,135 @@
|
||||
{
|
||||
"mode": 0,
|
||||
"buttons":
|
||||
[
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
}
|
||||
],
|
||||
"pwmout":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
}
|
||||
],
|
||||
"ramp-lights":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 0,
|
||||
"button-index": 0,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 1,
|
||||
"button-index": 1,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
}
|
||||
],
|
||||
"oled": {
|
||||
"en": false,
|
||||
"height": 64,
|
||||
"width": 128
|
||||
},
|
||||
"t-sensor": {
|
||||
"en": true,
|
||||
"addr": 72,
|
||||
"sp1": 85.0,
|
||||
"fan-pwr1": 50.0,
|
||||
"sp2": 90.0,
|
||||
"fan-pwr2": 100.0,
|
||||
"hyst": 1.0,
|
||||
"relay": 3,
|
||||
"interval": 5000
|
||||
},
|
||||
"adc": {
|
||||
"ain1_factor": 1.0
|
||||
},
|
||||
"status-led":{
|
||||
"interval": 250
|
||||
},
|
||||
"strips":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"size": 138,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"size": 30,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
}
|
||||
],
|
||||
"rx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"tx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"ble":{
|
||||
"en": true,
|
||||
"name": "Ata_Lights",
|
||||
"core": 1
|
||||
}
|
||||
}
|
||||
135
data/booths/marquee.json
Normal file
@ -0,0 +1,135 @@
|
||||
{
|
||||
"mode": 0,
|
||||
"buttons":
|
||||
[
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
}
|
||||
],
|
||||
"pwmout":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
}
|
||||
],
|
||||
"ramp-lights":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 0,
|
||||
"button-index": 0,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 1,
|
||||
"button-index": 1,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
}
|
||||
],
|
||||
"oled": {
|
||||
"en": false,
|
||||
"height": 64,
|
||||
"width": 128
|
||||
},
|
||||
"t-sensor": {
|
||||
"en": true,
|
||||
"addr": 72,
|
||||
"sp1": 85.0,
|
||||
"fan-pwr1": 50.0,
|
||||
"sp2": 90.0,
|
||||
"fan-pwr2": 100.0,
|
||||
"hyst": 1.0,
|
||||
"relay": 3,
|
||||
"interval": 5000
|
||||
},
|
||||
"adc": {
|
||||
"ain1_factor": 1.0
|
||||
},
|
||||
"status-led":{
|
||||
"interval": 250
|
||||
},
|
||||
"strips":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"size": 138,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"size": 30,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
}
|
||||
],
|
||||
"rx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"tx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"ble":{
|
||||
"en": true,
|
||||
"name": "Ata_Lights",
|
||||
"core": 1
|
||||
}
|
||||
}
|
||||
136
data/booths/roamer-big.json
Normal file
@ -0,0 +1,136 @@
|
||||
{
|
||||
"mode": "roamer",
|
||||
"button": 0,
|
||||
"buttons":
|
||||
[
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
}
|
||||
],
|
||||
"pwmout":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
}
|
||||
],
|
||||
"ramp-lights":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 0,
|
||||
"button-index": 0,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 1,
|
||||
"button-index": 1,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
}
|
||||
],
|
||||
"oled": {
|
||||
"en": false,
|
||||
"height": 64,
|
||||
"width": 128
|
||||
},
|
||||
"t-sensor": {
|
||||
"en": true,
|
||||
"addr": 72,
|
||||
"sp1": 85.0,
|
||||
"fan-pwr1": 50.0,
|
||||
"sp2": 90.0,
|
||||
"fan-pwr2": 100.0,
|
||||
"hyst": 1.0,
|
||||
"relay": 3,
|
||||
"interval": 5000
|
||||
},
|
||||
"adc": {
|
||||
"ain1_factor": 1.0
|
||||
},
|
||||
"status-led":{
|
||||
"interval": 250
|
||||
},
|
||||
"strips":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"size": 168,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-42,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
},
|
||||
{
|
||||
"en": false,
|
||||
"size": 30,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
}
|
||||
],
|
||||
"rx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"tx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"ble":{
|
||||
"en": true,
|
||||
"name": "Ata_Lights",
|
||||
"core": 1
|
||||
}
|
||||
}
|
||||
136
data/booths/roamer.json
Normal file
@ -0,0 +1,136 @@
|
||||
{
|
||||
"mode": "roamer",
|
||||
"button": 0,
|
||||
"buttons":
|
||||
[
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
}
|
||||
],
|
||||
"pwmout":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
}
|
||||
],
|
||||
"ramp-lights":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 0,
|
||||
"button-index": 0,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"relay-index": 1,
|
||||
"button-index": 1,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
}
|
||||
],
|
||||
"oled": {
|
||||
"en": false,
|
||||
"height": 64,
|
||||
"width": 128
|
||||
},
|
||||
"t-sensor": {
|
||||
"en": true,
|
||||
"addr": 72,
|
||||
"sp1": 85.0,
|
||||
"fan-pwr1": 50.0,
|
||||
"sp2": 90.0,
|
||||
"fan-pwr2": 100.0,
|
||||
"hyst": 1.0,
|
||||
"relay": 3,
|
||||
"interval": 5000
|
||||
},
|
||||
"adc": {
|
||||
"ain1_factor": 1.0
|
||||
},
|
||||
"status-led":{
|
||||
"interval": 250
|
||||
},
|
||||
"strips":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"size": 138,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
},
|
||||
{
|
||||
"en": false,
|
||||
"size": 30,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
}
|
||||
],
|
||||
"rx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"tx433": {
|
||||
"en": "true"
|
||||
},
|
||||
"ble":{
|
||||
"en": true,
|
||||
"name": "Ata_Lights",
|
||||
"core": 1
|
||||
}
|
||||
}
|
||||
135
data/booths/testbooth.json
Normal file
@ -0,0 +1,135 @@
|
||||
{
|
||||
"mode": 0,
|
||||
"buttons":
|
||||
[
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
},
|
||||
{
|
||||
"en": true
|
||||
}
|
||||
],
|
||||
"pwmout":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": false,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"freq": 250,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"default": 0,
|
||||
"vision": true,
|
||||
"deltarate": 0
|
||||
}
|
||||
],
|
||||
"ramp-lights":
|
||||
[
|
||||
{
|
||||
"en": false,
|
||||
"relay-index": 0,
|
||||
"button-index": 0,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
},
|
||||
{
|
||||
"en": false,
|
||||
"relay-index": 1,
|
||||
"button-index": 1,
|
||||
"min": 5.0,
|
||||
"max": 100.0,
|
||||
"step": 1.5,
|
||||
"skip-count": 5,
|
||||
"vision": true
|
||||
}
|
||||
],
|
||||
"oled": {
|
||||
"en": false,
|
||||
"height": 64,
|
||||
"width": 128
|
||||
},
|
||||
"t-sensor": {
|
||||
"en": false,
|
||||
"addr": 72,
|
||||
"sp1": 85.0,
|
||||
"fan-pwr1": 50.0,
|
||||
"sp2": 90.0,
|
||||
"fan-pwr2": 100.0,
|
||||
"hyst": 1.0,
|
||||
"relay": 3,
|
||||
"interval": 5000
|
||||
},
|
||||
"adc": {
|
||||
"ain1_factor": 1.0
|
||||
},
|
||||
"status-led":{
|
||||
"interval": 250
|
||||
},
|
||||
"strips":
|
||||
[
|
||||
{
|
||||
"en": true,
|
||||
"size": 138,
|
||||
"chip": "WS6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
},
|
||||
{
|
||||
"en": true,
|
||||
"size": 30,
|
||||
"chip": "WS6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-27,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
"core": 1
|
||||
}
|
||||
],
|
||||
"rx433": {
|
||||
"en": false
|
||||
},
|
||||
"tx433": {
|
||||
"en": false
|
||||
},
|
||||
"ble":{
|
||||
"en": true,
|
||||
"name": "Ata_Lights",
|
||||
"core": 1
|
||||
}
|
||||
}
|
||||
79
data/css/global-style.css
Normal file
@ -0,0 +1,79 @@
|
||||
body {
|
||||
/*background-color: #f7f7f7;*/
|
||||
font-family: Tahoma, Arial, sans-serif;
|
||||
font-size: small;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.main-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
}
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
input{
|
||||
cursor:pointer;
|
||||
}
|
||||
input[type="number"]{
|
||||
width: 100px;
|
||||
}
|
||||
#submit {
|
||||
width:120px;
|
||||
}
|
||||
select{
|
||||
width:160px;
|
||||
}
|
||||
#select-path {
|
||||
width:250px;
|
||||
}
|
||||
#select-dir {
|
||||
width:90px;
|
||||
}
|
||||
#spacer-50 {
|
||||
height: 50px;
|
||||
}
|
||||
#spacer-20 {
|
||||
height: 20px;
|
||||
}
|
||||
#spacer-10 {
|
||||
height: 10px;
|
||||
}
|
||||
table {
|
||||
/*background-color: #dddddd;*/
|
||||
border-collapse: collapse;
|
||||
width:600px;
|
||||
margin: 0 auto;
|
||||
overflow: visible;
|
||||
}
|
||||
td, th {
|
||||
/*border: 1px solid #dddddd;*/
|
||||
text-align: left;
|
||||
padding: 2px;
|
||||
}
|
||||
#first_td_th {
|
||||
width:400px;
|
||||
}
|
||||
fieldset {
|
||||
width:620px;
|
||||
/*background-color: #f7f7f7;*/
|
||||
border-radius: 10px;
|
||||
border-color: blue;
|
||||
}
|
||||
#format-notice {
|
||||
color: #ff0000;
|
||||
}
|
||||
legend {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-color:white;
|
||||
background-blend-mode: darken;
|
||||
border-radius: 10px;
|
||||
padding: 1px 8px 2px 8px;
|
||||
border-style: solid;
|
||||
border-width: 1.0;
|
||||
}
|
||||
72
data/css/nav.css
Normal file
@ -0,0 +1,72 @@
|
||||
.navbar {
|
||||
width: 100%;
|
||||
background-color: black;
|
||||
border-bottom: 2px solid white;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
.navbar-left {
|
||||
padding: 0 10px;
|
||||
}
|
||||
.navbar ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative; /* Ensure relative positioning for the submenu */
|
||||
}
|
||||
.navbar ul li {
|
||||
float: left;
|
||||
position: relative; /* Ensure relative positioning for the submenu */
|
||||
}
|
||||
.navbar ul li a {
|
||||
display: block;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 14px 16px;
|
||||
text-decoration: none;
|
||||
border-right: 1px solid white;
|
||||
}
|
||||
.navbar ul li a:hover {
|
||||
background-color: grey;
|
||||
}
|
||||
.navbar ul li a.disabled {
|
||||
color: grey;
|
||||
pointer-events: none;
|
||||
}
|
||||
.navbar ul .submenu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
background-color: black;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-top: 2px solid white;
|
||||
z-index: 1000;
|
||||
}
|
||||
.navbar ul .submenu li {
|
||||
float: none;
|
||||
border-right: none;
|
||||
}
|
||||
.navbar ul .submenu li a {
|
||||
padding: 10px 16px;
|
||||
border-bottom: 1px solid white;
|
||||
}
|
||||
.navbar ul .submenu li a:hover {
|
||||
background-color: grey;
|
||||
}
|
||||
.navbar ul li:hover > .submenu {
|
||||
display: block;
|
||||
}
|
||||
.nav-image {
|
||||
height: 40px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
BIN
data/favicon.ico
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
472
data/flashstik-reg.html
Normal file
@ -0,0 +1,472 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ATA Light Stick Reg</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background-color: #f4f4f4;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 22px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.status-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: left;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.status-indicator-ble {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: gray;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.status-indicator-wifi {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: gray;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.status-indicator-internet {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: gray;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Adds space above the WiFi Connect button */
|
||||
.btn-container.wifi {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
max-width: 130px;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
button:hover:not(:disabled) {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 97%;
|
||||
height: 100px;
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ccc;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 90%;
|
||||
max-width: 300px;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
body {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: 14px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>ATA Flash-Stick Link/Registration</h1>
|
||||
|
||||
<!-- Status Indicators -->
|
||||
<div class="status-container">
|
||||
<span class="status-indicator-ble"></span>
|
||||
<label id="status-ble-connection">Master: ...</label>
|
||||
</div>
|
||||
|
||||
<div class="status-container">
|
||||
<span class="status-indicator-registration"></span>
|
||||
<label id="status-registration">Registration: ...</label>
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<button id="bleConnectBtn">Connect</button>
|
||||
<button id="bleDisconnectBtn">Disconnect</button>
|
||||
<button id="bleSaveBtn">Save</button>
|
||||
</div>
|
||||
|
||||
<textarea id="logArea" readonly></textarea>
|
||||
|
||||
|
||||
<script>
|
||||
// Constants
|
||||
const BLE_SERVER_NAME = "ATALIGHTS"; // Replace with your server name
|
||||
const BLE_SERVICE_UUID = "abcdef01-2345-6789-1234-56789abcdef0"; // Replace with your service UUID
|
||||
const BLE_CHARACTERISTIC1_UUID = "abcdef01-2345-6789-1234-56789abcdef1"; // Replace with your characteristic UUID
|
||||
const BLE_CHARACTERISTIC2_UUID = "abcdef02-2345-6789-1234-56789abcdef1"; // Replace with your characteristic UUID
|
||||
|
||||
let bleDevice = null;
|
||||
let bleCharacteristic1 = null;
|
||||
let bleCharacteristic2 = null;
|
||||
let bleConnected = false;
|
||||
|
||||
const WIFI_STAT = { WIFI_DISCONNECTED:0, WIFI_BAD_CREDS:1, WIFI_NO_AP:2, WIFI_CONNECTED:3 };
|
||||
|
||||
let updatePacket = {
|
||||
name: "",
|
||||
};
|
||||
|
||||
// Log messages to the textarea
|
||||
function logMessage(message) {
|
||||
const logArea = document.getElementById('logArea');
|
||||
logArea.value += message + '\n';
|
||||
logArea.scrollTop = logArea.scrollHeight;
|
||||
}
|
||||
|
||||
// Function to scan for BLE devices
|
||||
async function scanForDevices() {
|
||||
logMessage('Scanning for BLE devices...');
|
||||
try {
|
||||
const device = await navigator.bluetooth.requestDevice({
|
||||
acceptAllDevices: true,
|
||||
optionalServices: [BLE_SERVICE_UUID]
|
||||
});
|
||||
|
||||
if (device) {
|
||||
logMessage(`Found device: ${device.name || "Unnamed"} (ID: ${device.id})`);
|
||||
} else {
|
||||
logMessage('No devices found.');
|
||||
}
|
||||
} catch (error) {
|
||||
logMessage(`Scan failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to connect to the BLE server
|
||||
async function connectToBle() {
|
||||
try {
|
||||
bleDevice = await navigator.bluetooth.requestDevice({
|
||||
filters: [{ name: BLE_SERVER_NAME }],
|
||||
optionalServices: [BLE_SERVICE_UUID]
|
||||
});
|
||||
|
||||
//logMessage(`Connecting to ${bleDevice.name}`);
|
||||
const server = await bleDevice.gatt.connect();
|
||||
//await server.setPreferredMtu(247); // Request larger MTU size
|
||||
|
||||
const service = await server.getPrimaryService(BLE_SERVICE_UUID);
|
||||
|
||||
bleCharacteristic1 = await service.getCharacteristic(BLE_CHARACTERISTIC1_UUID);
|
||||
|
||||
// Subscribe to notifications
|
||||
//await bleCharacteristic1.startNotifications();
|
||||
|
||||
// Add event listener for incoming notifications
|
||||
//bleCharacteristic1.addEventListener('characteristicvaluechanged', handleChar1Notifications);
|
||||
|
||||
|
||||
//logMessage('Getting characteristic...');
|
||||
bleCharacteristic2 = await service.getCharacteristic(BLE_CHARACTERISTIC2_UUID);
|
||||
|
||||
// Subscribe to notifications
|
||||
await bleCharacteristic2.startNotifications();
|
||||
|
||||
// Add event listener for incoming notifications
|
||||
bleCharacteristic2.addEventListener('characteristicvaluechanged', (event) => {
|
||||
const value = event.target.value;
|
||||
const decoder = new TextDecoder();
|
||||
const decodedValue = decoder.decode(value);
|
||||
logMessage('--> ' + decodedValue);
|
||||
});
|
||||
|
||||
bleConnected = true;
|
||||
document.getElementById('bleConnectBtn').disabled = true;
|
||||
document.querySelector('.status-indicator-ble').style.backgroundColor = 'green';
|
||||
document.getElementById('status-ble-connection').textContent = 'Device: Connected';
|
||||
document.getElementById('wifiConnectBtn').disabled = false;
|
||||
document.getElementById('checkStatusBtn').disabled = false;
|
||||
logMessage(`Connected to ${bleDevice.name}`);
|
||||
|
||||
await readPacket();
|
||||
processUpdatePacket(updatePacket);
|
||||
|
||||
} catch (error) {
|
||||
if (error.message.includes("cancelled")) {
|
||||
logMessage("Connection cancelled by user.");
|
||||
} else {
|
||||
logMessage(`Connection failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function sendPacket(packetMsg) {
|
||||
if (!bleCharacteristic1) {
|
||||
console.log("Cannot send packet: Not connected to BLE server.");
|
||||
return;
|
||||
}
|
||||
|
||||
const maxRetries = 3;
|
||||
const retryDelay = 1000; // 1 second
|
||||
let attempt = 0;
|
||||
|
||||
while (attempt < maxRetries) {
|
||||
try {
|
||||
//logMessage(`Sending request: ${packetMsg} (Attempt ${attempt + 1})`);
|
||||
const encoder = new TextEncoder();
|
||||
await bleCharacteristic1.writeValueWithResponse(encoder.encode(packetMsg));
|
||||
//console.log("Request sent successfully");
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error(`Failed to send packet: ${error.message}`);
|
||||
attempt++;
|
||||
if (attempt < maxRetries) {
|
||||
console.log(`Retrying in ${retryDelay / 1000} seconds...`);
|
||||
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||
} else {
|
||||
console.error("Max retries reached. Failed to send request.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function readPacket() {
|
||||
if (!bleCharacteristic1) {
|
||||
console.log("Cannot read packet: Not connected to BLE server.");
|
||||
return;
|
||||
}
|
||||
|
||||
const maxRetries = 3;
|
||||
const retryDelay = 1000; // 1 second
|
||||
let attempt = 0;
|
||||
|
||||
while (attempt < maxRetries) {
|
||||
try {
|
||||
const value = await bleCharacteristic1.readValue();
|
||||
const data = new Uint8Array(value.buffer);
|
||||
if (data.length === 12) {
|
||||
updatePacket.wifiConnected = data[0] !== 0;
|
||||
updatePacket.wifiOnline = data[1] !== 0;
|
||||
updatePacket.wifiIP = [data[2], data[3], data[4], data[5]];
|
||||
updatePacket.currVersion = [data[6], data[7], data[8]];
|
||||
updatePacket.newVersion = [data[9], data[10], data[11]];
|
||||
|
||||
//processUpdatePacket(updatePacket);
|
||||
return;
|
||||
}
|
||||
console.log("Invalid packet length");
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error(`Failed to read packet: ${error.message}`);
|
||||
attempt++;
|
||||
if (attempt < maxRetries) {
|
||||
console.log(`Retrying in ${retryDelay / 1000} seconds...`);
|
||||
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||
} else {
|
||||
console.error("Max retries reached. Failed to read packet.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process update packet
|
||||
function processUpdatePacket(packet) {
|
||||
// Process the packet data
|
||||
//console.log("Processing update packet:", packet);
|
||||
if(packet.wifiConnected === true) {
|
||||
if(packet.wifiConnected && packet.wifiIP[0] > 0) {
|
||||
document.getElementById('status-wifi-client').textContent = 'Wifi Client: Connected (' + packet.wifiIP.join('.') + ')';
|
||||
} else {
|
||||
document.getElementById('status-wifi-client').textContent = 'Wifi Client: Connected';
|
||||
}
|
||||
document.querySelector('.status-indicator-wifi').style.backgroundColor = 'green';
|
||||
} else {
|
||||
document.getElementById('status-wifi-client').textContent = 'Wifi Client: ...';
|
||||
document.querySelector('.status-indicator-wifi').style.backgroundColor = 'gray';
|
||||
}
|
||||
|
||||
if(packet.wifiOnline === true) {
|
||||
document.getElementById('status-internet').textContent = 'Online';
|
||||
document.querySelector('.status-indicator-internet').style.backgroundColor = 'green';
|
||||
document.getElementById('checkVersionBtn').disabled = false;
|
||||
|
||||
} else {
|
||||
document.getElementById('status-internet').textContent = 'Offline';
|
||||
document.querySelector('.status-indicator-internet').style.backgroundColor = 'gray';
|
||||
document.getElementById('checkVersionBtn').disabled = true;
|
||||
}
|
||||
|
||||
if (packet.currVersion[0] > 0) {
|
||||
document.getElementById('status-current-version').textContent = 'Curr Version: ' + packet.currVersion.join('.');
|
||||
} else {
|
||||
document.getElementById('status-current-version').textContent = 'Curr Version: ...';
|
||||
}
|
||||
|
||||
if (packet.newVersion[0] > 0) {
|
||||
document.getElementById('status-new-version').textContent = 'New Version: ' + packet.newVersion.join('.');
|
||||
document.getElementById('checkVersionBtn').disabled = true;
|
||||
|
||||
if(packet.wifiOnline && packet.newVersion[0] > packet.currVersion[0] ||
|
||||
(packet.newVersion[0] === packet.currVersion[0] && packet.newVersion[1] > packet.currVersion[1]) ||
|
||||
(packet.newVersion[0] === packet.currVersion[0] && packet.newVersion[1] === packet.currVersion[1] && packet.newVersion[2] > packet.currVersion[2])) {
|
||||
|
||||
//enable start upgrade button
|
||||
logMessage("New Version Available:");
|
||||
document.getElementById('startUpgradeBtn').disabled = false;
|
||||
} else {
|
||||
//disable start upgrade button
|
||||
document.getElementById('startUpgradeBtn').disabled = true;
|
||||
logMessage("New Version: Not Available");
|
||||
}
|
||||
} else {
|
||||
document.getElementById('status-new-version').textContent = 'New Version: ...';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//BLE_Characteristic.addEventListener('characteristicvaluechanged', handleNotifications);
|
||||
function handleChar1Notifications(event) {
|
||||
const data = new Uint8Array(event.data);
|
||||
|
||||
if (data.length !== 12) { // 1 byte for id, 4 bytes for booleans, 4 bytes for wifiIP, 3 bytes for currVersion, 3 bytes for newVersion
|
||||
console.log("Invalid packet length");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update existing updatePacket object instead of creating new one
|
||||
updatePacket.wifiConnected = data[0] !== 0;
|
||||
updatePacket.internetAvailable = data[1] !== 0;
|
||||
updatePacket.wifiIP = [data[2], data[3], data[4], data[5]];
|
||||
updatePacket.currVersion = [data[6], data[7], data[8]];
|
||||
updatePacket.newVersion = [data[9], data[10], data[11]];
|
||||
|
||||
processUpdatePacket(updatePacket);
|
||||
}
|
||||
|
||||
document.getElementById('showPassword').addEventListener('change', function() {
|
||||
const passwordInput = document.getElementById('wifipassword');
|
||||
passwordInput.type = this.checked ? 'text' : 'password';
|
||||
});
|
||||
|
||||
// Event listeners for buttons
|
||||
document.getElementById('bleConnectBtn').addEventListener('click', connectToBle);
|
||||
document.getElementById('checkStatusBtn').addEventListener('click', async () => {
|
||||
if (bleCharacteristic1) {
|
||||
await readPacket();
|
||||
processUpdatePacket(updatePacket);
|
||||
} else {
|
||||
logMessage('BLE device not connected.');
|
||||
}
|
||||
});
|
||||
document.getElementById('checkVersionBtn').addEventListener('click', async () => {
|
||||
await sendPacket('version-check');
|
||||
// loop and monitor the the updatePacket.newVersion
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
success = false;
|
||||
for (let i = 0; i < 20; i++) {
|
||||
await readPacket();
|
||||
if (updatePacket.newVersion[0] > 0) {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
if(success) {
|
||||
processUpdatePacket(updatePacket);
|
||||
logMessage("New Version: Available");
|
||||
} else {
|
||||
logMessage("New Version: Not Available");
|
||||
}
|
||||
|
||||
});
|
||||
document.getElementById('wifiConnectBtn').addEventListener('click', async () => {
|
||||
const ssid = document.getElementById('wifissid').value;
|
||||
const password = document.getElementById('wifipassword').value;
|
||||
if (ssid && password) {
|
||||
// Send credentials to the device
|
||||
jsonString = ' {"ssid":"' + ssid + '","pass":"' + password + '"} ';
|
||||
await sendPacket('wifi-connect' + jsonString);
|
||||
|
||||
await readPacket();
|
||||
processUpdatePacket(updatePacket);
|
||||
} else {
|
||||
alert('Please enter both SSID and password.');
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
document.getElementById('startUpgradeBtn').addEventListener('click', async () => {
|
||||
try {
|
||||
await sendPacket('upgrade-start');
|
||||
logMessage("Upgrade Starting... Please wait.");
|
||||
} catch (error) {
|
||||
logMessage(`Error starting upgrade: ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Initial call to process the update packet with defaults
|
||||
processUpdatePacket(updatePacket);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
data/images/atalogo.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
data/images/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
442
data/js/event-box.js
Normal file
@ -0,0 +1,442 @@
|
||||
class EventBox extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
this.shadowRoot.innerHTML = `
|
||||
<style>
|
||||
.outer-container {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
border: 1px solid #ccc;
|
||||
padding: 5px 20px 5px 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
|
||||
width:90%;
|
||||
}
|
||||
.select-container {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
.row > * {
|
||||
flex: 0 0 calc(50% - 20px);
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.row:last-child {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
select, input[type="range"], input[type="color"] {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
}
|
||||
select{
|
||||
width: 90%;
|
||||
font-size: large;
|
||||
height: auto;
|
||||
}
|
||||
input[type="color"] {
|
||||
height: 30px;
|
||||
padding: 0;
|
||||
width: 30px;
|
||||
}
|
||||
.checkbox-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: left;
|
||||
}
|
||||
.try-button-container {
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
width: 100%;
|
||||
}
|
||||
button {
|
||||
background-color: #007bff;
|
||||
color: #fff;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.center-text {
|
||||
text-align: center;
|
||||
font-size: larger;
|
||||
font-weight: bold;
|
||||
}
|
||||
input[type="checkbox"] {
|
||||
transform: scale(2.5);
|
||||
margin-left: 20px;
|
||||
}
|
||||
input[type="checkbox"].css-checkbox + label.css-label {
|
||||
display: inline-block;
|
||||
margin-left: 30px;
|
||||
}
|
||||
.label-check{
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
<div class="outer-container">
|
||||
<div class="container">
|
||||
<legend class="center-text" id="center-text">Event0</legend><br>
|
||||
<div class="row">
|
||||
<div>
|
||||
<label for="animation-list" id="list-label">Animations:</label>
|
||||
<select id="animation-list">
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<hue-select id="hue-selector"></hue-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>
|
||||
<label for="speed" id="speed-label">Speed:</label>
|
||||
<input type="range" id="speed" min="1" max="10">
|
||||
</div>
|
||||
<div>
|
||||
<label for="huerange" id="huerange-label">Hue Range:</label>
|
||||
<input type="range" id="huerange" min="0" max="100">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>
|
||||
<label for="param1" id="param1-label">Param1:</label>
|
||||
<input type="range" id="param1" min="0" max="50">
|
||||
</div>
|
||||
<div>
|
||||
<label for="param2" id="param2-label">Param2:</label>
|
||||
<input type="range" id="param2" min="0" max="100">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="checkbox-container">
|
||||
<input type="checkbox" id="check1">
|
||||
<label class="label-check" id="check1-label">Check1</label>
|
||||
</div>
|
||||
<div class="checkbox-container">
|
||||
<input type="checkbox" id="check3">
|
||||
<label class="label-check" id="check3-label">Check3</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="checkbox-container">
|
||||
<input type="checkbox" id="check2">
|
||||
<label class="label-check" id="check2-label">Check2</label>
|
||||
</div>
|
||||
<div class="checkbox-container">
|
||||
<input type="checkbox" id="check4">
|
||||
<label class="label-check" id="check4-label">50% Lum</label>
|
||||
<div class="try-button-container">
|
||||
<button id="try-button">Try</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
this.Title = this.shadowRoot.querySelector('#center-text');
|
||||
this.hueSelect = this.shadowRoot.querySelector('#hue-selector');
|
||||
this.AnimationList = this.shadowRoot.getElementById('animation-list');
|
||||
this.Speed = this.shadowRoot.getElementById('speed');
|
||||
this.HueRange = this.shadowRoot.querySelector('#huerange');
|
||||
this.Param1 = this.shadowRoot.querySelector('#param1');
|
||||
this.Param2 = this.shadowRoot.querySelector('#param2');
|
||||
this.Check1 = this.shadowRoot.querySelector('#check1');
|
||||
this.Check2 = this.shadowRoot.querySelector('#check2');
|
||||
this.Check3 = this.shadowRoot.querySelector('#check3');
|
||||
this.Check4 = this.shadowRoot.querySelector('#check4');
|
||||
|
||||
this.AnimationListLabel = this.shadowRoot.querySelector('#list-label');
|
||||
this.SpeedLabel = this.shadowRoot.querySelector('#speed-label');
|
||||
this.HueRangeLabel = this.shadowRoot.querySelector('#huerange-label');
|
||||
this.Param1Label = this.shadowRoot.getElementById('param1-label');
|
||||
this.Param2Label = this.shadowRoot.getElementById('param2-label');
|
||||
this.Check1Label = this.shadowRoot.getElementById('check1-label');
|
||||
this.Check2Label = this.shadowRoot.getElementById('check2-label');
|
||||
this.Check3Label = this.shadowRoot.getElementById('check3-label');
|
||||
this.Check4Label = this.shadowRoot.getElementById('check4-label');
|
||||
|
||||
this.AnimationPropsJson;
|
||||
|
||||
this.SpeedCaption = "";
|
||||
this.HueRangeCaption = "";
|
||||
this.Param1Caption = "";
|
||||
this.Param2Caption = "";
|
||||
this.Check1Caption = "";
|
||||
this.Check2Caption = "";
|
||||
this.Check3Caption = "";
|
||||
|
||||
this.setSpeedCaption(`Speed: `);
|
||||
this.Speed.min = 0;
|
||||
this.Speed.max = 100;
|
||||
|
||||
this.setHueRangeCaption(`Hue Range: `);
|
||||
this.HueRange.min = 0;
|
||||
this.HueRange.max = 360;
|
||||
|
||||
this.setParam1Caption(`Param1: `);
|
||||
this.Param1.min = 0;
|
||||
this.Param1.max = 100;
|
||||
|
||||
this.setParam2Caption(`Param2: `);
|
||||
this.Param2.min = 0;
|
||||
this.Param2.max = 100;
|
||||
|
||||
this.setCheck1Caption(`Check1`);
|
||||
this.setCheck2Caption(`Check2`);
|
||||
this.setCheck3Caption(`Check3`);
|
||||
this.setCheck4Caption(`50% Lum`);
|
||||
|
||||
this.Index = 0;
|
||||
|
||||
this.Speed.addEventListener('input', () => { this.updateSpeedLabel(); });
|
||||
this.HueRange.addEventListener('input', () => { this.updateHueRangeLabel(); });
|
||||
this.Param1.addEventListener('input', () => { this.updateParam1Label(); });
|
||||
this.Param2.addEventListener('input', () => { this.updateParam2Label(); });
|
||||
|
||||
this.TryButton = this.shadowRoot.querySelector('#try-button');
|
||||
this.TryButton.addEventListener('click', () => {
|
||||
this.handleTryButtonClick();
|
||||
});
|
||||
|
||||
this.AnimationList.addEventListener('change', this.handleAnimationListChange.bind(this));
|
||||
}
|
||||
|
||||
setIndex(i){ this.Index = i; }
|
||||
getIndex(){ return this.Index; }
|
||||
updateSpeedLabel(){
|
||||
if(!this.Speed.hidden){
|
||||
this.SpeedLabel.innerHTML = this.SpeedCaption + this.getSpeedValue();
|
||||
}
|
||||
}
|
||||
updateHueRangeLabel(){
|
||||
if(!this.HueRange.hidden){
|
||||
this.HueRangeLabel.innerHTML = this.HueRangeCaption + this.getHueRangeValue();
|
||||
}
|
||||
}
|
||||
updateParam1Label(){
|
||||
if(!this.Param1.hidden){
|
||||
this.Param1Label.innerHTML = this.Param1Caption + this.getParam1Value();
|
||||
}
|
||||
}
|
||||
updateParam2Label(){
|
||||
if(!this.Param2.hidden){
|
||||
this.Param2Label.innerHTML = this.Param2Caption + this.getParam2Value();
|
||||
}
|
||||
}
|
||||
setTitle(text){
|
||||
this.Title.textContent = text;
|
||||
}
|
||||
addOptionToList(value, text){
|
||||
const optionElement = document.createElement('option');
|
||||
optionElement.value = value;
|
||||
optionElement.textContent = text;
|
||||
this.AnimationList.appendChild(optionElement);
|
||||
}
|
||||
setHidden(hidden){ this.hidden = hidden; }
|
||||
getHidden(){ return this.hidden; }
|
||||
setHueValue(value){ this.hueSelect.setHue(value); }
|
||||
getHueValue(){ return this.hueSelect.getSelectedHue(); }
|
||||
setSpeedValue(value){
|
||||
this.Speed.value = value;
|
||||
this.updateSpeedLabel();
|
||||
}
|
||||
setSpeedCaption(caption){
|
||||
let hid = false;
|
||||
if(caption.trim() === ""){
|
||||
hid = true;
|
||||
}
|
||||
this.SpeedCaption = caption;
|
||||
this.SpeedLabel.innerHTML = caption;
|
||||
this.Speed.hidden = hid;
|
||||
this.SpeedLabel.hidden = hid;
|
||||
}
|
||||
getSpeedValue(){ return this.Speed.value; }
|
||||
setHueRangeValue(value){
|
||||
this.HueRange.value = value;
|
||||
this.updateHueRangeLabel()
|
||||
}
|
||||
setHueRangeCaption(caption){
|
||||
let hid = false;
|
||||
if(caption.trim() === ""){
|
||||
hid = true;
|
||||
}
|
||||
this.HueRangeCaption = caption;
|
||||
this.HueRangeLabel.innerHTML = caption;
|
||||
this.HueRange.hidden = hid;
|
||||
this.HueRangeLabel.hidden = hid;
|
||||
}
|
||||
getHueRangeValue(){ return this.HueRange.value; }
|
||||
setParam1Value(value){
|
||||
this.Param1.value = value;
|
||||
this.updateParam1Label()
|
||||
}
|
||||
setParam1Caption(caption){
|
||||
let hid = false;
|
||||
if(caption.trim() === ""){
|
||||
hid = true;
|
||||
}
|
||||
this.Param1Caption = caption;
|
||||
this.Param1Label.innerHTML = caption;
|
||||
this.Param1.hidden = hid;
|
||||
this.Param1Label.hidden = hid;
|
||||
}
|
||||
getParam1Value(){ return this.Param1.value; }
|
||||
setParam2Value(value){
|
||||
this.Param2.value = value;
|
||||
this.updateParam2Label()
|
||||
}
|
||||
setParam2Caption(caption){
|
||||
let hid = false;
|
||||
if(caption.trim() === ""){
|
||||
hid = true;
|
||||
}
|
||||
this.Param2Caption = caption;
|
||||
this.Param2Label.innerHTML = caption;
|
||||
this.Param2.hidden = hid;
|
||||
this.Param2Label.hidden = hid;
|
||||
}
|
||||
getParam2Value(){ return this.Param2.value; }
|
||||
|
||||
setCheck1Value(value){ this.Check1.checked = value; }
|
||||
setCheck1Caption(caption){
|
||||
let hid = false;
|
||||
if(caption.trim() === ""){
|
||||
hid = true;
|
||||
}
|
||||
this.Check1Label.innerHTML = caption;
|
||||
this.Check1.hidden = hid;
|
||||
this.Check1Label.hidden = hid;
|
||||
}
|
||||
getCheck1Value(){ return this.Check1.checked; }
|
||||
|
||||
setCheck2Value(value){ this.Check2.checked = value; }
|
||||
setCheck2Caption(caption){
|
||||
let hid = false;
|
||||
if(caption.trim() === ""){
|
||||
hid = true;
|
||||
}
|
||||
this.Check2Label.innerHTML = caption;
|
||||
this.Check2.hidden = hid;
|
||||
this.Check2Label.hidden = hid;
|
||||
}
|
||||
getCheck2Value(){ return this.Check2.checked; }
|
||||
|
||||
setCheck3Value(value){ this.Check3.checked = value; }
|
||||
setCheck3Caption(caption){
|
||||
let hid = false;
|
||||
if(caption.trim() === ""){
|
||||
hid = true;
|
||||
}
|
||||
this.Check3Label.innerHTML = caption;
|
||||
this.Check3.hidden = hid;
|
||||
this.Check3Label.hidden = hid;
|
||||
}
|
||||
getCheck3Value(){ return this.Check3.checked; }
|
||||
|
||||
setCheck4Value(value){ this.Check4.checked = value; }
|
||||
setCheck4Caption(caption){
|
||||
let hid = false;
|
||||
if(caption.trim() === ""){
|
||||
hid = true;
|
||||
}
|
||||
this.Check4Label.innerHTML = caption;
|
||||
this.Check4.hidden = hid;
|
||||
this.Check4Label.hidden = hid;
|
||||
}
|
||||
getCheck4Value(){ return this.Check4.checked; }
|
||||
|
||||
setAnimationIndex(index){
|
||||
this.AnimationList.selectedIndex = index;
|
||||
const changeEvent = new Event('change');
|
||||
this.AnimationList.dispatchEvent(changeEvent);
|
||||
}
|
||||
getAnimationIndex(){
|
||||
return this.AnimationList.selectedIndex;
|
||||
}
|
||||
|
||||
handleTryButtonClick() {
|
||||
const eventIndexValue = this.getIndex();
|
||||
const animIndexValue = this.getAnimationIndex();
|
||||
const hueValue = this.getHueValue();
|
||||
const speedValue = this.getSpeedValue();
|
||||
const colorRangeValue = this.getHueRangeValue();
|
||||
const param1Value = this.getParam1Value();
|
||||
const param2Value = this.getParam2Value();
|
||||
const check1Value = this.getCheck1Value();
|
||||
const check2Value = this.getCheck2Value();
|
||||
const check3Value = this.getCheck3Value();
|
||||
const check4Value = this.getCheck4Value();
|
||||
|
||||
this.dispatchEvent(new CustomEvent('tryClick', {
|
||||
detail: {
|
||||
eventIndex: eventIndexValue,
|
||||
animIndex: animIndexValue,
|
||||
hue: hueValue,
|
||||
speed: speedValue,
|
||||
colorRange: colorRangeValue,
|
||||
param1: param1Value,
|
||||
param2: param2Value,
|
||||
check1: check1Value,
|
||||
check2: check2Value,
|
||||
check3: check3Value,
|
||||
check4: check4Value
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
setAnimationCaptions(propsJson){
|
||||
this.AnimationPropsJson = propsJson;
|
||||
|
||||
//add options to list
|
||||
let x = 0;
|
||||
this.AnimationPropsJson.forEach(props => {
|
||||
this.addOptionToList(x, props.name);
|
||||
x++;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
handleAnimationListChange(){
|
||||
const selectedIndex = this.getAnimationIndex();
|
||||
const selectedProps = this.AnimationPropsJson[selectedIndex];
|
||||
|
||||
this.updateControlProps(selectedProps);
|
||||
}
|
||||
|
||||
updateControlProps(props){
|
||||
this.setSpeedCaption(props.speed);
|
||||
let s = props['hue-range'];
|
||||
this.setHueRangeCaption(s || "");
|
||||
this.setParam1Caption(props.param1 || "");
|
||||
this.setParam2Caption(props.param2 || "");
|
||||
this.setCheck1Caption(props.check1 || "");
|
||||
this.setCheck2Caption(props.check2 || "");
|
||||
this.setCheck3Caption(props.check3 || "");
|
||||
this.setCheck4Caption(props.check4 || "");
|
||||
|
||||
this.updateSpeedLabel();
|
||||
this.updateHueRangeLabel();
|
||||
this.updateParam1Label();
|
||||
this.updateParam2Label();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
customElements.define('event-box', EventBox);
|
||||
|
||||
64
data/js/fwUoload.js
Normal file
@ -0,0 +1,64 @@
|
||||
async function uploadFile(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const file = fileInput.files[0];
|
||||
if (!file) {
|
||||
alert("Please select a file.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Existing file validation code...
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
// Create a custom reader function
|
||||
const reader = file.stream().getReader();
|
||||
const totalLength = file.size;
|
||||
|
||||
let uploaded = 0;
|
||||
|
||||
// Create a readable stream and use the custom reader function
|
||||
const uploadStream = new ReadableStream({
|
||||
async start(controller) {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
uploaded += value.length;
|
||||
let percentUploaded = (uploaded / totalLength * 100).toFixed(2);
|
||||
|
||||
// Update progress bar and label
|
||||
progressBar.value = percentUploaded;
|
||||
progressLabel.innerHTML = `${percentUploaded}%`;
|
||||
|
||||
controller.enqueue(value);
|
||||
}
|
||||
controller.close();
|
||||
},
|
||||
});
|
||||
|
||||
// Create a new Request with the readable stream as body
|
||||
const req = new Request('/update', {
|
||||
method: 'POST',
|
||||
body: uploadStream,
|
||||
headers: {
|
||||
// Add any relevant headers here
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await fetch(req);
|
||||
|
||||
if (response.ok) {
|
||||
alert('Upload completed!');
|
||||
progressLabel.innerHTML = "Completed!";
|
||||
} else {
|
||||
alert('An error occurred during the upload.');
|
||||
progressLabel.innerHTML = "Error!";
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Upload failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
244
data/js/hue-select.js
Normal file
@ -0,0 +1,244 @@
|
||||
|
||||
class HueSelect extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
this.shadowRoot.innerHTML = `
|
||||
<style>
|
||||
.color-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.color-patch {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin-top: 18px;
|
||||
margin-right: 10px;
|
||||
border: 1px solid #000;
|
||||
}
|
||||
.hue-label {
|
||||
margin-top: 18px;
|
||||
width: 90px;
|
||||
text-align: left;
|
||||
}
|
||||
.label-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.dropdown-content {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: #f9f9f9;
|
||||
min-width: 180px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.dropdown-content .color-option {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
.dropdown:hover .dropdown-content {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<div class="dropdown">
|
||||
<div class="color-option" id="selectedColor">
|
||||
<span class="color-patch" style="background-color: hsl(0, 100%, 50%)"></span>
|
||||
<div class="label-container">
|
||||
<label class="hue-label" id="hue-label">0</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dropdown-content" id="colorPicker">
|
||||
<!-- The options will be added dynamically using JavaScript -->
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
this.currentHue = 0;
|
||||
this.colorList;
|
||||
this.hueLabel = this.shadowRoot.getElementById('hue-label');
|
||||
|
||||
const selectedColorDiv = this.shadowRoot.getElementById('selectedColor');
|
||||
const colorPatch = selectedColorDiv.querySelector('.color-patch');
|
||||
|
||||
selectedColorDiv.addEventListener('click', () => {
|
||||
const dropdownContent = this.shadowRoot.querySelector('.dropdown-content');
|
||||
dropdownContent.style.display = dropdownContent.style.display === 'block' ? 'none' : 'block';
|
||||
});
|
||||
|
||||
this.createColorOptions();
|
||||
this.setHue(0);
|
||||
}
|
||||
|
||||
generateColors() {
|
||||
const colors = [];
|
||||
for (let hue = 0; hue <= 360; hue += 10) {
|
||||
if (hue == 360) { hue = 359; }
|
||||
colors.push(hue);
|
||||
}
|
||||
|
||||
colors.push(-1);
|
||||
colors.push(-2);
|
||||
return colors;
|
||||
}
|
||||
|
||||
createColorOption(hue) {
|
||||
const colorOption = document.createElement('div');
|
||||
colorOption.classList.add('color-option');
|
||||
|
||||
const colorPatch = document.createElement('span');
|
||||
colorPatch.classList.add('color-patch');
|
||||
|
||||
const colorText = document.createElement('span');
|
||||
colorText.classList.add('color-text');
|
||||
colorText.textContent = hue;
|
||||
|
||||
const rgbHex = document.createElement('span');
|
||||
rgbHex.classList.add('rgb-hex');
|
||||
|
||||
if (hue === -2) {
|
||||
colorPatch.style.backgroundColor = 'rgb(0,0,0)';
|
||||
rgbHex.innerHTML = ' #000000';
|
||||
} else if (hue === -1) {
|
||||
colorPatch.style.backgroundColor = 'rgb(255,255,255)';
|
||||
rgbHex.innerHTML = ' #FFFFFF';
|
||||
} else {
|
||||
colorPatch.style.backgroundColor = `hsl(${hue}, 100%, 50%)`;
|
||||
const hexColor = this.hslToRgb(hue, 100, 50).toUpperCase();
|
||||
rgbHex.innerHTML = `<span> ${hexColor}</span>`;
|
||||
}
|
||||
|
||||
colorOption.appendChild(colorPatch);
|
||||
colorOption.appendChild(colorText);
|
||||
colorOption.appendChild(rgbHex);
|
||||
|
||||
colorOption.addEventListener('click', () => this.handleColorSelection(hue));
|
||||
|
||||
return colorOption;
|
||||
}
|
||||
|
||||
createColorOptions() {
|
||||
const dropdownContent = this.shadowRoot.querySelector('.dropdown-content');
|
||||
this.colorList = this.generateColors();
|
||||
|
||||
this.colorList.forEach(hue => {
|
||||
const colorOption = this.createColorOption(hue);
|
||||
dropdownContent.appendChild(colorOption);
|
||||
});
|
||||
}
|
||||
|
||||
// Function to convert HSL to RGB
|
||||
hslToRgb(h, s, l) {
|
||||
h /= 360;
|
||||
s /= 100;
|
||||
l /= 100;
|
||||
let r, g, b;
|
||||
if (s === 0) {
|
||||
r = g = b = l; // achromatic
|
||||
} else {
|
||||
const hue2rgb = (p, q, t) => {
|
||||
if (t < 0) t += 1;
|
||||
if (t > 1) t -= 1;
|
||||
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
||||
if (t < 1 / 2) return q;
|
||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
||||
return p;
|
||||
};
|
||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
const p = 2 * l - q;
|
||||
r = hue2rgb(p, q, h + 1 / 3);
|
||||
g = hue2rgb(p, q, h);
|
||||
b = hue2rgb(p, q, h - 1 / 3);
|
||||
}
|
||||
const toHex = (x) => {
|
||||
const hex = Math.round(x * 255).toString(16);
|
||||
return hex.length === 1 ? '0' + hex : hex;
|
||||
};
|
||||
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
||||
}
|
||||
|
||||
handleColorSelection(hue) {
|
||||
this.currentHue = hue;
|
||||
const selectedColorDiv = this.shadowRoot.getElementById('selectedColor');
|
||||
const colorPatch = selectedColorDiv.querySelector('.color-patch');
|
||||
|
||||
// Update the color patch and hue label
|
||||
const rgb = this.getRGBfromHue(hue);
|
||||
colorPatch.style.backgroundColor = rgb;
|
||||
this.setHueLabel(hue);
|
||||
this.hideDropdown();
|
||||
|
||||
this.dispatchEvent(new CustomEvent('change', { detail: { hue, rgb } }));
|
||||
}
|
||||
|
||||
// Method to get the hue value of the selected item
|
||||
getSelectedHue() {
|
||||
return this.currentHue;
|
||||
}
|
||||
|
||||
getSelectedRGB() {
|
||||
const selectedColorText = this.shadowRoot.getElementById('selectedColor').textContent.trim();
|
||||
return parseFloat(selectedColorText);
|
||||
}
|
||||
|
||||
getRGBfromHue(hue){
|
||||
if(hue == -1){
|
||||
return '#FFFFFF';
|
||||
}else if(hue == -2){
|
||||
return '#000000';
|
||||
}else{
|
||||
return this.hslToRgb(hue, 100, 50);;
|
||||
}
|
||||
}
|
||||
|
||||
// Method to get the RGB value of the selected item
|
||||
getSelectedRgb() {
|
||||
const selectedHue = this.getSelectedHue();
|
||||
return getRGBfromHue(selectedHue);
|
||||
}
|
||||
|
||||
setHue(hue) {
|
||||
// Round the input hue to the nearest 10th
|
||||
let roundedHue = hue;
|
||||
if(hue === -1 || hue === -2){
|
||||
roundedHue = hue;
|
||||
}else{
|
||||
roundedHue = Math.round(hue / 10) * 10;
|
||||
if (roundedHue >= 360) {
|
||||
roundedHue = 359;
|
||||
}else if (roundedHue < 0) {
|
||||
roundedHue = 0;
|
||||
}
|
||||
}
|
||||
|
||||
this.currentHue = roundedHue;
|
||||
|
||||
this.colorList.forEach(colorVal => {
|
||||
if (colorVal === roundedHue) {
|
||||
this.handleColorSelection(roundedHue);
|
||||
}
|
||||
});
|
||||
|
||||
this.hideDropdown();
|
||||
}
|
||||
|
||||
hideDropdown() {
|
||||
const dropdownContent = this.shadowRoot.querySelector('.dropdown-content');
|
||||
dropdownContent.style.display = 'none';
|
||||
}
|
||||
|
||||
setHueLabel(value){
|
||||
this.hueLabel.textContent = "Hue: " + value;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
customElements.define('hue-select', HueSelect);
|
||||
|
||||
8673
data/js/jquery-3.7.1.js
vendored
Normal file
16
data/system/luma-stiks.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"stiks":[
|
||||
{
|
||||
"en": true,
|
||||
"master": false,
|
||||
"encrypt": false,
|
||||
"ch": 1,
|
||||
"ssid": "lumastikXXXX",
|
||||
"mac": "6A:B6:B3:2D:E5:64",
|
||||
|
||||
"master-pass": "atapass",
|
||||
"slave-name": "FlashStick"
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
5
data/system/readme.txt
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
#Setting up /system/system.json
|
||||
1 - Choose the
|
||||
modes:
|
||||
booth, roamer, stick
|
||||
4
data/system/system.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"boardfile": "/boards/board15.json",
|
||||
"configfile": "/booths/helio-posh.json"
|
||||
}
|
||||
70
data/system/tunes.json
Normal file
@ -0,0 +1,70 @@
|
||||
{
|
||||
"tunes":
|
||||
[
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Boot:d=8,o=5,b=200:g,g,c6,e6,g6"
|
||||
},
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Shutdown:d=8,o=5,b=200:g6,e6,c6,g"
|
||||
},
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Connected:d=16,o=5,b=240:c,e,g,c6"
|
||||
},
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Disconnected:d=16,o=5,b=160:g,f,e,d,c"
|
||||
},
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Done:d=16,o=5,b=240:g,c6,e"
|
||||
},
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause":0,
|
||||
"tune": "Warning:d=16,o=6,b=300:e6,d6,e6,d6"
|
||||
},
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Error:d=16,o=4,b=100:f,f"
|
||||
},
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Blip:d=16,o=6,b=220:c6,g6"
|
||||
},
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Thump:d=8,o=4,b=180:c,c"
|
||||
},
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Ack:d=16,o=5,b=200:c,e,g"
|
||||
},
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "Waiting:d=16,o=5,b=112:b"
|
||||
},
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "LowBeep:d=4,o=5,b=240:c"
|
||||
},
|
||||
{
|
||||
"cycles": 1,
|
||||
"pause": 0,
|
||||
"tune": "HighBeep:d=4,o=5,b=240:b"
|
||||
}
|
||||
]
|
||||
}
|
||||
4
data/system/update.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"baseurl": "https://storage.googleapis.com/boothifier/",
|
||||
"folder": "latest/"
|
||||
}
|
||||
16
data/system/wifi.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"wifi-client":{
|
||||
"en": true,
|
||||
"mdns-name": "atadev",
|
||||
"ssid": "DPWifi",
|
||||
"pass": "dave3.14159"
|
||||
},
|
||||
"wifi-ap":{
|
||||
"ssid": "ATA_AP",
|
||||
"append-id": true,
|
||||
"pass": "12345678",
|
||||
"ip": "192.168.10.1",
|
||||
"gateway": "192.168.10.1",
|
||||
"subnet": "255.255.255.0"
|
||||
}
|
||||
}
|
||||
169
data/www/about.html
Normal file
@ -0,0 +1,169 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>About Printio</title>
|
||||
<link href="/css/nav.css" rel="stylesheet">
|
||||
<style>
|
||||
h1 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label, select, #media-sizes, button {
|
||||
margin: 5px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
select {
|
||||
padding: 3px;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
#media-sizes ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#media-sizes li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#media-sizes input[type="checkbox"] {
|
||||
transform: scale(1.5);
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
#media-sizes span {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: blue;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-size: 1.2em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: darkblue;
|
||||
}
|
||||
|
||||
p {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.license-section {
|
||||
margin-top: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.license-status {
|
||||
font-size: 1.1em;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.license-input {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.license-input input {
|
||||
padding: 10px;
|
||||
width: 200px;
|
||||
font-size: 1.1em;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.license-input button {
|
||||
background-color: #4CAF50;
|
||||
padding: 10px 20px;
|
||||
font-size: 1.1em;
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.license-input button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
/* New Styles for Layout */
|
||||
.content-wrapper {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.left-column {
|
||||
width: 280px;
|
||||
padding-right: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.right-column {
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.info-box {
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.license-box {
|
||||
padding: 15px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
}
|
||||
#p {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="navbar"></div>
|
||||
<div class="content-wrapper">
|
||||
<div class="left-column">
|
||||
<div class="info-box">
|
||||
<h3>System Info</h3>
|
||||
<p>Version: {{FIRM_VER}}
|
||||
<p>CPU: {{info.cpu}}</p>
|
||||
<p>CPU T: {{info.cpu_t}}</p>
|
||||
<p>Disk Size: {{FS_TOTAL_BYTES}}</p>
|
||||
<p>Disk Used: {{FS_USED_BYTES}}</p>
|
||||
<p>RAM Size: {{RAM_TOTAL_BYTES}}</p>
|
||||
<p>RAM Used: {{RAM_USED_BYTES}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right-column">
|
||||
<h1>About ATA Boothifier</h1>
|
||||
<p>
|
||||
ATA Boothifier is photobooth control board designed to work with ATA's line of photobooths and beyond.
|
||||
</p>
|
||||
<p>
|
||||
Warning: Unauthorized reproduction or distribution of this product
|
||||
is strictly prohibited and may result in severe civil and criminal
|
||||
penalties.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
fetch('/www/navbar.html')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
document.getElementById('navbar').innerHTML = data;
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
191
data/www/edit.html
Normal file
@ -0,0 +1,191 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Edit file</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="/css/nav.css" rel="stylesheet">
|
||||
<style>
|
||||
h1{
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #f0f0f0;
|
||||
width: 100%;
|
||||
max-width: auto;
|
||||
min-width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.outer-container {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
padding-top: 0;
|
||||
}
|
||||
.container {
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px; /* Adjust padding */
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
|
||||
width: 90%;
|
||||
min-width: 400px;
|
||||
height: calc(100vh - 120px); /* Adjust for navbar and margins */
|
||||
margin: auto; /* Center horizontally */
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.center-text {
|
||||
text-align: center;
|
||||
font-size: larger;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10;
|
||||
}
|
||||
input[type="text"] {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: nowrap;
|
||||
min-width: 400px;
|
||||
}
|
||||
.row > * {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.row label {
|
||||
flex: 0 0 auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.row div {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
input[type="number"], select {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
width: 50%;
|
||||
}
|
||||
button {
|
||||
background-color: #007bff;
|
||||
color: #fff;
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin: 20px 5px 10px;
|
||||
}
|
||||
textarea {
|
||||
width: 100%; /* Use full width */
|
||||
height: calc(100vh - 200px); /* Use vh unit for height */
|
||||
box-sizing: border-box;
|
||||
border: 1.5px solid gray;
|
||||
resize: vertical; /* Allow vertical resizing */
|
||||
word-wrap: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="navbar"></div>
|
||||
<h1>Edit file</h2>
|
||||
<div class="outer-container">
|
||||
<div class="container">
|
||||
<form name="edit-file" action="/files/save" onsubmit="return validateForm()">
|
||||
<div>
|
||||
<textarea name="edit-textarea" id="edit-textarea" wrap="off" ></textarea>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>File Name:</label>
|
||||
<input type="text" id="save-path" value="{{SAVE_PATH_INPUT}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>
|
||||
<button type="submit" id="submit-edit" >Save</button>
|
||||
<button id="cancel" onclick="window.location.href='/files';">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Load navbar
|
||||
fetch('/www/navbar.html')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
document.getElementById('navbar').innerHTML = data;
|
||||
});
|
||||
|
||||
window.onload=loadEditFile();
|
||||
|
||||
function loadEditFile(){
|
||||
var savePath = document.getElementById('save-path').value;
|
||||
|
||||
if( savePath != "/new.txt"){ // skip if new.txt
|
||||
fetch(savePath)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch file');
|
||||
}
|
||||
return response.text();
|
||||
})
|
||||
.then(fileContents => {
|
||||
// Put the file contents into a textarea element
|
||||
document.getElementById('edit-textarea').value = fileContents;
|
||||
document.getElementById('save-path').disabled = true;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function validateForm()
|
||||
{
|
||||
var allowedExtensions = "{{ALLOWED_EXTENSIONS_EDIT}}";
|
||||
var inputMessage = document.getElementById('save-path').value;
|
||||
var dotIndex = inputMessage.lastIndexOf(".")+1;
|
||||
var inputMessageExtension = inputMessage.substring(dotIndex);
|
||||
var extIndex = allowedExtensions.indexOf(inputMessageExtension);
|
||||
var isSlash = inputMessage.substring(0,1);
|
||||
|
||||
if(inputMessage == "")
|
||||
{
|
||||
alert("Enter the file name! \ne.g.: /new.txt");
|
||||
return false;
|
||||
}
|
||||
if(isSlash != "/")
|
||||
{
|
||||
alert("The slash at the beginning of the file is missing!");
|
||||
return false;
|
||||
}
|
||||
if(dotIndex == 0)
|
||||
{
|
||||
alert("The extension is missing at the end of the file!");
|
||||
return false;
|
||||
}
|
||||
if(inputMessageExtension == "")
|
||||
{
|
||||
alert("The extension is missing at the end of the file!");
|
||||
return false;
|
||||
}
|
||||
if(extIndex == -1)
|
||||
{
|
||||
alert("Extension not supported!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
151
data/www/edit_old.html
Normal file
@ -0,0 +1,151 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Edit file</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
font-family: Tahoma, Arial, sans-serif;
|
||||
font-size: small;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
#submit {
|
||||
width:100px;
|
||||
}
|
||||
button{
|
||||
width: 100px;
|
||||
}
|
||||
#spacer-50 {
|
||||
height: 20px;
|
||||
}
|
||||
#spacer-20 {
|
||||
height: 10px;
|
||||
}
|
||||
fieldset {
|
||||
width:700px;
|
||||
border-radius: 10px;
|
||||
background-color: lightgray;
|
||||
}
|
||||
td, th {
|
||||
text-align: center;
|
||||
padding: 1px;
|
||||
}
|
||||
legend{
|
||||
background-color:white;
|
||||
background-blend-mode: darken;
|
||||
border-radius: 5px;
|
||||
padding: 1px 6px 2px 6px;
|
||||
border-style:solid;
|
||||
border-width: 1.0;
|
||||
}
|
||||
textarea {
|
||||
width: 700px;
|
||||
height: 500px;
|
||||
box-sizing: border-box;
|
||||
border: 1.5px solid #000000;
|
||||
border-radius: 8px;
|
||||
resize: none;
|
||||
word-wrap: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="navbar"></div>
|
||||
<h2>Edit file</h2>
|
||||
<fieldset>
|
||||
<legend>Editing file: {{SAVE_PATH_INPUT}}</legend>
|
||||
<div id="spacer-20"></div>
|
||||
<table><tr><td colspan="2">
|
||||
<form name="edit-file" action="/save" onsubmit="return validateForm()">
|
||||
<textarea name="edit-textarea" id="edit-textarea" wrap="off" ></textarea>
|
||||
<div id="spacer-20"></div>
|
||||
</td></tr><tr><td>
|
||||
{{SAVE_PATH_INPUT}}
|
||||
<button type="submit" id="submit-edit" >Save</button>
|
||||
</form>
|
||||
</td><td>
|
||||
<button id="submit" onclick="window.location.href='/files';">Cancel</button>
|
||||
</td></tr></table>
|
||||
<div id="spacer-50"></div>
|
||||
</fieldset>
|
||||
|
||||
<iframe style="display:none" name="self-page"></iframe>
|
||||
|
||||
<script>
|
||||
|
||||
fetch('/www/navbar.html')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
document.getElementById('navbar').innerHTML = data;
|
||||
});
|
||||
|
||||
|
||||
window.onload=loadEditFile();
|
||||
|
||||
function loadEditFile(){
|
||||
var savePath = document.getElementById('save-path').value;
|
||||
|
||||
if( savePath != "/new.txt"){ // skip if new.txt
|
||||
fetch(savePath)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch file');
|
||||
}
|
||||
return response.text();
|
||||
})
|
||||
.then(fileContents => {
|
||||
// Put the file contents into a textarea element
|
||||
document.getElementById('edit-textarea').value = fileContents;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function validateForm()
|
||||
{
|
||||
var allowedExtensions = "{{ALLOWED_EXTENSIONS_EDIT}}";
|
||||
var inputMessage = document.getElementById('save-path').value;
|
||||
var dotIndex = inputMessage.lastIndexOf(".")+1;
|
||||
var inputMessageExtension = inputMessage.substring(dotIndex);
|
||||
var extIndex = allowedExtensions.indexOf(inputMessageExtension);
|
||||
var isSlash = inputMessage.substring(0,1);
|
||||
|
||||
if(inputMessage == "")
|
||||
{
|
||||
alert("Enter the file name! \ne.g.: /new.txt");
|
||||
return false;
|
||||
}
|
||||
if(isSlash != "/")
|
||||
{
|
||||
alert("The slash at the beginning of the file is missing!");
|
||||
return false;
|
||||
}
|
||||
if(dotIndex == 0)
|
||||
{
|
||||
alert("The extension is missing at the end of the file!");
|
||||
return false;
|
||||
}
|
||||
if(inputMessageExtension == "")
|
||||
{
|
||||
alert("The extension is missing at the end of the file!");
|
||||
return false;
|
||||
}
|
||||
if(extIndex == -1)
|
||||
{
|
||||
alert("Extension not supported!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
33
data/www/failed.html
Normal file
@ -0,0 +1,33 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Update Failed</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="/css/nav.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
#spacer-50 {
|
||||
height: 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="navbar"></div>
|
||||
<center>
|
||||
<h2>The update has failed.</h2>
|
||||
<div id="spacer-50"></div>
|
||||
<button onclick="window.location.href='/files';">to homepage</button>
|
||||
</center>
|
||||
|
||||
<script>
|
||||
fetch('/www/navbar.html')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
document.getElementById('navbar').innerHTML = data;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
276
data/www/files.html
Normal file
@ -0,0 +1,276 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>File Manager</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="/css/global-style.css" rel="stylesheet">
|
||||
<link href="/css/nav.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
#file-row:nth-child(odd) {
|
||||
background-color: #e8e8e8;
|
||||
}
|
||||
#first_td_th {
|
||||
width:300px;
|
||||
}
|
||||
progress{
|
||||
width: 290px;
|
||||
}
|
||||
#submit-edit, #submit-upload, #submit-delete, #submit-update-local{
|
||||
width:120px;
|
||||
}
|
||||
#dir-path{
|
||||
width: 75px;
|
||||
}
|
||||
fieldset {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
border-collapse: collapse;
|
||||
margin: 1rem 0;
|
||||
/*width:380px; */
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div id="navbar"></div>
|
||||
<div class="main-container">
|
||||
<h1>File Manager</h1>
|
||||
|
||||
<fieldset>
|
||||
<legend>File list</legend>
|
||||
<div id="spacer-20"></div>
|
||||
<p>  Total: {{FS_TOTAL_BYTES}}, Used: {{FS_USED_BYTES}}, Available: {{FS_FREE_BYTES}}</p>
|
||||
<div id="spacer-20"></div>
|
||||
<table><tr><th id="first_td_th">Listing:</th> </th><th>Size:</th></tr>
|
||||
{{LISTED_FILES}}
|
||||
</table>
|
||||
<div id="spacer-20"></div>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<fieldset>
|
||||
<legend>File upload</legend>
|
||||
<div id="spacer-20"></div>
|
||||
<form action="/upload" method="POST" enctype="multipart/form-data">
|
||||
<table>
|
||||
<tr>
|
||||
<td id="first_td_th">
|
||||
<label for="dir-path">Dir: </label><br>
|
||||
<select name="dir-path" id="dir-path">
|
||||
{{DIR_LIST}}
|
||||
</select>  
|
||||
|
||||
</td>
|
||||
<td><input type="submit" id="submit-upload" value="File upload!" onclick="return validateFormUpload()"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="spacer-20"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<input type="file" id="upload-file" name="upload-file">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<fieldset>
|
||||
<legend>Edit file</legend>
|
||||
<div id="spacer-20"></div>
|
||||
<form action="/files/edit" method="GET">
|
||||
<table><tr>
|
||||
<td id="first_td_th">
|
||||
<select name="edit-path" id="edit-path">
|
||||
<option value="choose">Select file to edit</option>
|
||||
<option value="new">New text file</option>
|
||||
{{EDIT-DEL_FILES}}
|
||||
</select></td>
|
||||
<td><input type="submit" id="submit-edit" value="Edit" onclick="return validateFormEdit()"></td>
|
||||
</tr></table>
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<fieldset>
|
||||
<legend>Delete file</legend>
|
||||
<div id="spacer-20"></div>
|
||||
<form action="/files/delete" method="GET">
|
||||
<table><tr>
|
||||
<td id="first_td_th">
|
||||
<select name="delete-path" id="select-files">
|
||||
<option value="choose">Select file to delete</option>
|
||||
{{EDIT-DEL_FILES}}
|
||||
</select></td>
|
||||
<td><input type="submit" id="submit-delete" value="Delete" onclick="return validateFormDelete()"></td>
|
||||
</tr></table>
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
<div id="spacer-20"></div>
|
||||
|
||||
<!--<fieldset>
|
||||
<legend>Format File System</legend>
|
||||
<div id="spacer-20"></div>
|
||||
<form action="/format" method="POST" target="self-page">
|
||||
<table><tr>
|
||||
<td id="first_td_th">
|
||||
<p id="format-notice">Pressing the 'Format' button will immediately delete all data from File System!</p></td>
|
||||
<td><input type="submit" id="submit-format" value="Format" onclick="return confirmFormat()"></td>
|
||||
</tr></table>
|
||||
</form>
|
||||
<div id="spacer-20"></div>
|
||||
</fieldset>-->
|
||||
|
||||
<!--<iframe style="display:none" name="self-page"></iframe>-->
|
||||
</div>
|
||||
<script>
|
||||
|
||||
fetch('/www/navbar.html')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
document.getElementById('navbar').innerHTML = data;
|
||||
});
|
||||
|
||||
|
||||
function validateFormEdit()
|
||||
{
|
||||
var allowedExtensions = "{{ALLOWED_EXTENSIONS_EDIT}}";
|
||||
var editSelectValue = document.getElementById('edit-path').value;
|
||||
var dotIndex = editSelectValue.lastIndexOf(".")+1;
|
||||
var editSelectValueExtension = editSelectValue.substring(dotIndex);
|
||||
var extIndex = allowedExtensions.indexOf(editSelectValueExtension);
|
||||
|
||||
if(editSelectValue == "new"){
|
||||
return true;
|
||||
}
|
||||
if(editSelectValue == "choose" ){
|
||||
alert("You have not chosen a file!");
|
||||
return false;
|
||||
}
|
||||
if(extIndex == -1){
|
||||
alert("Editing of this file type is not supported!");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
function validateFormDelete()
|
||||
{
|
||||
var deleteSelectValue = document.getElementById('delete-path').value;
|
||||
|
||||
if(deleteSelectValue == "choose" || !deleteSelectValue.indexOf(".")){
|
||||
alert("You have not chosen a file!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function confirmFormat()
|
||||
{
|
||||
var text = 'Pressing the "OK" button immediately deletes all data from SPIFFS and restarts ESP32!';
|
||||
if (confirm(text) == true) {
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function validateFormUpload()
|
||||
{
|
||||
var inputElement = document.getElementById('upload-file');
|
||||
var files = inputElement.files;
|
||||
|
||||
if(files.length==0){
|
||||
alert("You have not chosen a file!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const fileInput = document.getElementById('update-file');
|
||||
const progressBar = document.getElementById('firm-progress');
|
||||
const progressLabel = document.getElementById('lbl-firm-progress');
|
||||
const submitButton = document.getElementById('submit-update-local');
|
||||
|
||||
submitButton.addEventListener('click', uploadFile);
|
||||
|
||||
function uploadFile(event) {
|
||||
event.preventDefault(); // Prevent the default form submission
|
||||
|
||||
const file = fileInput.files[0];
|
||||
const url = '/update'; // Replace with the actual upload URL
|
||||
|
||||
// File Checks
|
||||
//*********************************************************
|
||||
// Check file extension
|
||||
if (!file.name.toLowerCase().endsWith('.bin')) {
|
||||
alert('Please select a file with the ".bin" extension.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check filename prefix
|
||||
if (!file.name.toLowerCase().startsWith('fwata') && !file.name.toLowerCase().startsWith('lfsata')) {
|
||||
alert('Please select a file with a filename starting with "fwata" or "lfsata".');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check file size
|
||||
const maxSizeBytes = 2.7 * 1024 * 1024; // 2.75Mb in bytes
|
||||
if (file.size > maxSizeBytes) {
|
||||
alert('Please select a file with a size not exceeding 2.7Mb.');
|
||||
return;
|
||||
}
|
||||
//*********************************************************
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file-size', file.size); // Include the file size as a parameter
|
||||
formData.append('update-file', file);
|
||||
let s = "file-size: " + file.size;
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.upload.addEventListener('progress', (event) => {
|
||||
if (event.lengthComputable) {
|
||||
const progressPercent = Math.round((event.loaded * 100) / event.total);
|
||||
progressBar.value = progressPercent;
|
||||
progressLabel.innerHTML =progressBar.value + "%";
|
||||
}
|
||||
});
|
||||
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
console.log("received status: %d", xhr.status);
|
||||
if (xhr.status === 200) {
|
||||
// Upload completed successfully
|
||||
alert('Upload completed!');
|
||||
progressLabel.innerHTML = "Completed!";
|
||||
} else if (xhr.status === 500) {
|
||||
// Request was aborted (server-side)
|
||||
alert('Upload aborted by the server.');
|
||||
progressLabel.innerHTML = "Aborted!";
|
||||
} else {
|
||||
// Handle other error cases
|
||||
alert('An error occurred during the upload.');
|
||||
progressLabel.innerHTML = "Error!";
|
||||
}
|
||||
submitButton.disabled = false;
|
||||
progressBar.value = 0;
|
||||
progressLabel.innerHTML = "";
|
||||
}
|
||||
};
|
||||
|
||||
xhr.open('POST', url, true);
|
||||
xhr.send(formData);
|
||||
submitButton.disabled = true;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
303
data/www/home.html
Normal file
@ -0,0 +1,303 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>System Summary</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="/css/nav.css" rel="stylesheet">
|
||||
<style>
|
||||
h1{
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
margin-bottom: 0;
|
||||
font-size: larger;
|
||||
}
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #f0f0f0;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
min-width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.outer-container {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 4 20px 4 20px
|
||||
}
|
||||
.container {
|
||||
border: 1px solid #ccc;
|
||||
padding: 5px 20px 10px 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
|
||||
width:90%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.center-text {
|
||||
text-align: center;
|
||||
font-size: medium;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.row > * {
|
||||
flex: 1;
|
||||
width: 0 1 calc(52% - 20px);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.row > *:last-child {
|
||||
flex: 0 1 calc(48% - 10px); /* 40% width with 10px gap */
|
||||
}
|
||||
input[type="number"], select {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
width: 50%;
|
||||
}
|
||||
button {
|
||||
background-color: #007bff;
|
||||
color: #fff;
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-top: auto;
|
||||
}
|
||||
.lbldata{
|
||||
font-style:italic;
|
||||
font-weight: bold;
|
||||
color: purple;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="navbar"></div>
|
||||
<h1 name="h1element">System Summary</h1>
|
||||
<div class="outer-container">
|
||||
|
||||
<div class="container">
|
||||
<legend class="center-text">Booth Overview</legend>
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>App:</label>
|
||||
<label class="lbldata">{{APP_NAME}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>OLED:</label>
|
||||
<label class="lbldata">{{OLED}}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>RGB Ch1:</label>
|
||||
<label class="lbldata">{{STRIP1}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>RGB Ch2:</label>
|
||||
<label class="lbldata">{{STRIP2}}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Front Light:</label>
|
||||
<label class="lbldata">{{FRONT_LIGHT}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Rear Light:</label>
|
||||
<label class="lbldata">{{REAR_LIGHT}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<legend class="center-text">System Information</legend>
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Firmware Ver:</label>
|
||||
<label class="lbldata">{{FIRMWARE}}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Booth T°:</label>
|
||||
<label class="lbldata">{{BOOTH_T}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Setpoint:</label>
|
||||
<label class="lbldata">{{SETPOINT}}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Flash Size:</label>
|
||||
<label class="lbldata">{{FLASH_SIZE}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Flash Free:</label>
|
||||
<label class="lbldata">{{FLASH_FREE}}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Heap Size:</label>
|
||||
<label class="lbldata">{{HEAP_SIZE}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Heap Free:</label>
|
||||
<label class="lbldata">{{HEAP_FREE}}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<label>CPU Freq:</label>
|
||||
<label class="lbldata">{{CPU_FREQ}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="container">
|
||||
<legend class="center-text">Network / WiFi</legend>
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>IP:</label>
|
||||
<label class="lbldata">{{IP}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>MAC:</label>
|
||||
<label class="lbldata">{{MAC}}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>SSID:</label>
|
||||
<label class="lbldata">{{SSID}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>RSSi:</label>
|
||||
<label class="lbldata">{{RSSI}}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Ch:</label>
|
||||
<label class="lbldata">{{WIFI_CH}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Encryp:</label>
|
||||
<label class="lbldata">{{ENCRYP}}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>AP SSID:</label>
|
||||
<label class="lbldata">{{AP_SSID}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>AP Clients:</label>
|
||||
<label class="lbldata">{{AP_CLIENTS}}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>AP MAC:</label>
|
||||
<label class="lbldata">{{AP_MAC}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>---</label>
|
||||
<label class="lbldata"></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<legend class="center-text">Bluetoth LE</legend>
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Active:</label>
|
||||
<label class="lbldata">{{BLE}}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>SSID:</label>
|
||||
<label class="lbldata">{{BLE_SSID}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Clients:</label>
|
||||
<label class="lbldata">{{BLE_CLIENTS}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<legend class="center-text">Luma Stiks</legend>
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Stik1:</label>
|
||||
<label class="lbldata">{{STIK1}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Stik2:</label>
|
||||
<label class="lbldata">{{STIK2}}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Stik3:</label>
|
||||
<label class="lbldata">{{STIK3}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Stik4:</label>
|
||||
<label class="lbldata">{{STIK4}}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Stik5:</label>
|
||||
<label class="lbldata">{{STIK5}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>Stik6:</label>
|
||||
<label class="lbldata">{{STIK6}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
fetch('/www/navbar.html')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
document.getElementById('navbar').innerHTML = data;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
29
data/www/index.html
Normal file
@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link href="/css/nav.css" rel="stylesheet">
|
||||
<title>ESP32 AP</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="navbar"></div>
|
||||
<h1>Welcome to ESP32 AP</h1>
|
||||
<p>This is a simple web server running on ESP32 in Access Point mode.</p>
|
||||
<div id="status"></div>
|
||||
<script>
|
||||
|
||||
fetch('/www/navbar.html')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
document.getElementById('navbar').innerHTML = data;
|
||||
});
|
||||
|
||||
// Fetch status
|
||||
fetch('/api/status')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
document.getElementById('status').innerHTML =
|
||||
`Status: ${data.status}`;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
527
data/www/lights.html
Normal file
@ -0,0 +1,527 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="/css/nav.css" rel="stylesheet">
|
||||
<script src="event-box.js"></script>
|
||||
<script src="hue-select.js"></script>
|
||||
|
||||
<style>
|
||||
|
||||
h1{
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
margin-bottom: 0;
|
||||
font-size: larger;
|
||||
}
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #f0f0f0;
|
||||
width: 100%;
|
||||
max-width: 700px;
|
||||
min-width: 350px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.outer-container {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 4 20px 20 20px
|
||||
}
|
||||
.container {
|
||||
border: 1px solid #ccc;
|
||||
padding: 5px 20px 5px 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
|
||||
width:90%;
|
||||
}
|
||||
.center-text {
|
||||
text-align: center;
|
||||
font-size: larger;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
.row > * {
|
||||
flex: 0 0 calc(50% - 20px);
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.row:last-child {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
input[type="range"] {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
}
|
||||
select{
|
||||
width: 50%;
|
||||
}
|
||||
button {
|
||||
background-color: #007bff;
|
||||
color: #fff;
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#set-countdown-button {
|
||||
margin-left: 30px; /* Adjust top margin if necessary */
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="navbar"></div>
|
||||
<div class="outer-container">
|
||||
<h1 name="h1element">Lights Configuration</h1>
|
||||
<div class="container">
|
||||
<legend class="center-text" id="center-text">Countdown ( Constant Light )</legend>
|
||||
<div class="row">
|
||||
<div>
|
||||
<label id="constLightMin-label" for="constlightMin">Light Min:</label>
|
||||
<input type="range" name="constLightMin" id="constlightMin" min="0" max="99" value="25" step="1" onchange="updateLabel('Light Min: ', this)">
|
||||
</div>
|
||||
<div>
|
||||
<label id="constLightMax-label" for="constlightMax">Light Max:</label>
|
||||
<input type="range" name="constLightMax" id="constlightMax" min="1" max="100" value="90" step="1" onchange="updateLabel('Light Max: ', this)">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>
|
||||
<label for="Holdtime">Hold time(ms):</label><br>
|
||||
<input type="number" name="holdTime" id="holdtime" min="0" max="5000" value="500">
|
||||
</div>
|
||||
<div>
|
||||
<label for="Ramptime">Ramp dn(ms):</label><br>
|
||||
<input type="number" name="rampTime" id="Ramptime" min="0" max="5000" value="500">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<br>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Active Profile:</label>
|
||||
<select name= "selActiveAnimProfiles" id="selActivedAnimProfiles" style="width:150px">
|
||||
<option>(1)</option>
|
||||
<option>(2)</option>
|
||||
<option>(3)</option>
|
||||
<option>(4)</option>
|
||||
<option>(5)</option>
|
||||
<option>(6)</option>
|
||||
<option>(7)</option>
|
||||
<option>(8)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<button id="testCommon" onclick="SaveProfileCommonToServer(true)">Try</button>
|
||||
|
||||
<button id="saveCommon" onclick="SaveProfileCommonToServer(false)">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="outer-container">
|
||||
<div class="container">
|
||||
<legend class="center-text" id="center-text">Saved Animation Profiles</legend>
|
||||
<div class="row">
|
||||
<div>
|
||||
<label for="selSavedAnimProfiles">Profiles:</label>
|
||||
<select name= "selSavedAnimProfiles" id="selSaveddAnimProfiles" style="width:150px" onchange="OnSavedAnimProfilesChanged(this)">
|
||||
<option>(1)</option>
|
||||
<option>(2)</option>
|
||||
<option>(3)</option>
|
||||
<option>(4)</option>
|
||||
<option>(5)</option>
|
||||
<option>(6)</option>
|
||||
<option>(7)</option>
|
||||
<option>(8)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="profileName">Rename:</label>
|
||||
<input type="text" name="inputProfileName" id="profileName" style="width:140px">
|
||||
<button id="saveProfile" onclick="SaveProfileToServer()">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="events-container"></div>
|
||||
|
||||
<script>
|
||||
|
||||
fetch('/www/navbar.html')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
document.getElementById('navbar').innerHTML = data;
|
||||
});
|
||||
|
||||
window.onload = function() { getProfilesAndEvents(); };
|
||||
|
||||
// Initialize Names
|
||||
const EVENTCOUNT = 12;
|
||||
const PROFILECOUNT = 8;
|
||||
var profileCommonJson;
|
||||
var profileJson = [PROFILECOUNT];
|
||||
var eventsJson;
|
||||
var animListJson;
|
||||
const inputProfileName = document.getElementsByName('inputProfileName')[0];
|
||||
const selSavedAnimProfiles = document.getElementsByName('selSavedAnimProfiles')[0];
|
||||
const selActiveAnimProfiles = document.getElementsByName('selActiveAnimProfiles')[0];
|
||||
const constLightMin = document.getElementsByName('constLightMin')[0];
|
||||
const constLightMax = document.getElementsByName('constLightMax')[0];
|
||||
const holdTime = document.getElementsByName('holdTime')[0];
|
||||
const rampTime = document.getElementsByName('rampTime')[0];
|
||||
var lastAnimProfileIndex = 0;
|
||||
var h1element = document.getElementsByName("h1element")[0];
|
||||
var eventBox = [EVENTCOUNT];
|
||||
|
||||
function createEventForms(count){
|
||||
const eventContainer = document.getElementById('events-container');
|
||||
|
||||
for(let i = 0; i < count; i++ ){
|
||||
const div = document.createElement('div');
|
||||
eventBox[i] = document.createElement('event-box');
|
||||
eventBox[i].id = `event${i}`;
|
||||
eventBox[i].setHidden(true);
|
||||
eventBox[i].setIndex(i);
|
||||
eventBox[i].addEventListener('tryClick', handleEventTryClick);
|
||||
|
||||
div.appendChild(eventBox[i]);
|
||||
eventContainer.appendChild(div);
|
||||
}
|
||||
}
|
||||
|
||||
// Get profiles
|
||||
// fetch anim-profile-common.json and anim-profile(1-8), events and anim-list
|
||||
function getProfilesAndEvents() {
|
||||
try {
|
||||
fetch_file('cfg/anim-profile-common.json').then(profilesResult => {
|
||||
profileCommonJson = profilesResult.data;
|
||||
|
||||
let profilePromises = [];
|
||||
for (let i = 1; i <= PROFILECOUNT; i++) {
|
||||
profilePromises.push(fetch_file(`cfg/anim-profile${i}.json`));
|
||||
}
|
||||
|
||||
Promise.all(profilePromises).then(results => {
|
||||
profileJson = results.map(result => result.data);
|
||||
|
||||
fetch_file('cfg/app-events.json').then(eventsResult => {
|
||||
eventsJson = eventsResult.data.apps[eventsResult.data.index];
|
||||
|
||||
fetch_file('cfg/anim-list.json').then(animListResult => {
|
||||
animListJson = animListResult.data;
|
||||
|
||||
// Title for page
|
||||
h1element.textContent = eventsJson.name;
|
||||
|
||||
// Event Boxes
|
||||
createEventForms(EVENTCOUNT);
|
||||
NameAndHideEvents(animListJson);
|
||||
|
||||
// Rest
|
||||
FillProfilesList();
|
||||
selSavedAnimProfiles.selectedIndex = profileCommonJson['profile-index'];
|
||||
lastAnimProfileIndex = selSavedAnimProfiles.selectedIndex;
|
||||
setProfile(selSavedAnimProfiles.selectedIndex);
|
||||
selActiveAnimProfiles.selectedIndex = profileCommonJson['profile-index'];
|
||||
});
|
||||
});
|
||||
});
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
// update the form with animation data when profile list item selected
|
||||
function setProfile(index){
|
||||
var changeEvent = new Event("change");
|
||||
|
||||
// update common
|
||||
inputProfileName.value = "";
|
||||
constLightMin.value = profileCommonJson.countdown.min;
|
||||
constLightMin.dispatchEvent(changeEvent);
|
||||
constLightMax.value = profileCommonJson.countdown.max;
|
||||
constLightMax.dispatchEvent(changeEvent);
|
||||
holdTime.value = profileCommonJson.countdown.hold;
|
||||
rampTime.value = profileCommonJson.countdown.ramp;
|
||||
|
||||
// Update Events
|
||||
for (let i = 0; i < EVENTCOUNT; i++) {
|
||||
if(!eventBox[i].getHidden()){
|
||||
let thisEvent = profileJson[index].events[i];
|
||||
eventBox[i].setAnimationIndex(thisEvent.anim);
|
||||
eventBox[i].setHueValue(thisEvent.hue);
|
||||
eventBox[i].setSpeedValue(thisEvent.speed);
|
||||
eventBox[i].setHueRangeValue(thisEvent["hue-range"]);
|
||||
eventBox[i].setParam1Value(thisEvent.param1);
|
||||
eventBox[i].setParam2Value(thisEvent.param2);
|
||||
eventBox[i].setCheck1Value(thisEvent.check1);
|
||||
eventBox[i].setCheck2Value(thisEvent.check2);
|
||||
eventBox[i].setCheck3Value(thisEvent.check3);
|
||||
eventBox[i].setCheck4Value(thisEvent.check4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When new profile is selected update
|
||||
function OnSavedAnimProfilesChanged( event ){
|
||||
console.log('Anim Profile Selected index:', event.selectedIndex);
|
||||
console.log('Anim Profile Updated index:', lastAnimProfileIndex);
|
||||
updateSingleProfileJson(lastAnimProfileIndex); // save current setting in ram but not in server yet
|
||||
setProfile(event.selectedIndex);
|
||||
lastAnimProfileIndex = event.selectedIndex;
|
||||
}
|
||||
|
||||
//Update the anim-profilesX.json obj with updated values
|
||||
function updateSingleProfileJson(index){
|
||||
if(inputProfileName.value != ""){
|
||||
profileJson[index].name = inputProfileName.value;
|
||||
selSavedAnimProfiles.options[index].text = '(' + (index + 1) + ') ' + inputProfileName.value;
|
||||
selSavedAnimProfiles.options[index].value = inputProfileName.value;
|
||||
}
|
||||
|
||||
// Update Events
|
||||
for (let i = 0; i < EVENTCOUNT; i++) {
|
||||
profileJson[index].events[i].anim = eventBox[i].getAnimationIndex();
|
||||
profileJson[index].events[i].hue = eventBox[i].getHueValue();
|
||||
profileJson[index].events[i].speed = eventBox[i].getSpeedValue();
|
||||
profileJson[index].events[i]["hue-range"] = eventBox[i].getHueRangeValue();
|
||||
profileJson[index].events[i].param1 = eventBox[i].getParam1Value();
|
||||
profileJson[index].events[i].param2 = eventBox[i].getParam2Value();
|
||||
profileJson[index].events[i].check1 = eventBox[i].getCheck1Value();
|
||||
profileJson[index].events[i].check2 = eventBox[i].getCheck2Value();
|
||||
profileJson[index].events[i].check3 = eventBox[i].getCheck3Value();
|
||||
profileJson[index].events[i].check4 = eventBox[i].getCheck4Value();
|
||||
}
|
||||
}
|
||||
|
||||
// save profileX.Json
|
||||
function SaveProfileToServer(){
|
||||
updateSingleProfileJson(selSavedAnimProfiles.selectedIndex);
|
||||
|
||||
const params = new URLSearchParams();
|
||||
let i = selSavedAnimProfiles.selectedIndex + 1;
|
||||
params.append('type', `anim-profile${i}`);
|
||||
const url = '/post?' + params.toString();
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'text/plain' } ,
|
||||
body: JSON.stringify(profileJson[selSavedAnimProfiles.selectedIndex]) // convert to string
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) { console.log('Request successful');}
|
||||
else { throw new Error('Request failed');}
|
||||
})
|
||||
.catch(error => { console.error(error);});
|
||||
}
|
||||
|
||||
// save profileCommonJson
|
||||
function SaveProfileCommonToServer(TestOnly){
|
||||
profileCommonJson.countdown.min = constLightMin.value;
|
||||
profileCommonJson.countdown.max = constLightMax.value;
|
||||
profileCommonJson.countdown.hold = holdTime.value;
|
||||
profileCommonJson.countdown.ramp = rampTime.value;
|
||||
profileCommonJson["profile-index"] = selActiveAnimProfiles.selectedIndex; // Set at active profile
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.append('type', TestOnly ? 'set-countdown' : 'anim-profile-common');
|
||||
const url = '/post?' + params.toString();
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'text/json' } ,
|
||||
body: JSON.stringify(profileCommonJson) // convert to string
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) { console.log('Request successful');}
|
||||
else { throw new Error('Request failed');}
|
||||
})
|
||||
.catch(error => { console.error(error);});
|
||||
}
|
||||
|
||||
//
|
||||
function fetch_file(filename){
|
||||
return fetch(filename)
|
||||
.then(response => {
|
||||
if (response.ok) { return response.json(); }
|
||||
else { throw new Error('fetching ' + fileName + ' failed'); }
|
||||
})
|
||||
.then(data => { return{data:data}; })
|
||||
}
|
||||
|
||||
//
|
||||
function fetch_json_file(fileName){
|
||||
const params = new URLSearchParams();
|
||||
params.append('type', fileName);
|
||||
const url = '/get?' + params.toString();
|
||||
|
||||
return fetch(url, {
|
||||
method: 'GET'
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) { return response.json(); }
|
||||
else { throw new Error('fetching ' + fileName + ' failed'); }
|
||||
})
|
||||
.then(data => { return{data:data}; })
|
||||
}
|
||||
|
||||
// Rename legends and or hide unused events property boxes
|
||||
function NameAndHideEvents(props){
|
||||
for(let i = 0; i < EVENTCOUNT; i++){
|
||||
if(eventsJson.events[i] === ''){
|
||||
break;
|
||||
}
|
||||
eventBox[i].setTitle(eventsJson.events[i]);
|
||||
eventBox[i].setHidden(false);
|
||||
}
|
||||
|
||||
// load captions for white fills box
|
||||
eventBox[0].setAnimationCaptions(props.whitefills);
|
||||
|
||||
// load captions for the rest of the eventBox
|
||||
for(let i = 1; i < EVENTCOUNT; i++){
|
||||
eventBox[i].setAnimationCaptions(props.animations);
|
||||
}
|
||||
}
|
||||
|
||||
// Fill Profiles List
|
||||
function FillProfilesList(){
|
||||
for(let i = 0; i < PROFILECOUNT; i++){
|
||||
selSavedAnimProfiles.options[i].text = '(' + (i + 1) + ') ' + profileJson[i].name;
|
||||
selSavedAnimProfiles.options[i].value = profileJson[i].name;
|
||||
|
||||
selActiveAnimProfiles.options[i].text = selSavedAnimProfiles.options[i].text;
|
||||
selActiveAnimProfiles.options[i].value = selSavedAnimProfiles.options[i].value;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill White Animation drop down lists
|
||||
function FillWhitefilList(whiteJson){
|
||||
for(let x = 0; x < whiteJson.length; x++){
|
||||
eventBox[0].addOptionToList(x, whiteJson[x])
|
||||
}
|
||||
}
|
||||
|
||||
// Fill Animations drop down lists
|
||||
function FillAnimationsList(animJson){
|
||||
for(let x = 0; x < animJson.length; x++){
|
||||
if(animJson[x].name == ""){break;}// stop if blank
|
||||
for(let i = 1; i < EVENTCOUNT; i++){
|
||||
eventBox[i].addOptionToList(i, animJson[x].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for const light props
|
||||
function updateLabel(labelText, slider) {
|
||||
let label = document.getElementById(slider.name + "-label");
|
||||
label.innerHTML = labelText + slider.value;
|
||||
}
|
||||
|
||||
// send anim event to test out
|
||||
function postPlayAnim(i){
|
||||
let tempAnimProps = {};
|
||||
tempAnimProps.event = eventIndex;
|
||||
tempAnimProps.anim = eventBox[i].getAnimationIndex();
|
||||
tempAnimProps.hue = eventBox[i].getHueValue();
|
||||
tempAnimProps.speed = eventBox[i].getSpeedValue();
|
||||
tempAnimProps["hue-range"] = eventBox[i].getHueRangeValue();
|
||||
tempAnimProps.param1 = eventBox[i].getParam1Value();
|
||||
tempAnimProps.param2 = eventBox[i].getParam2Value();
|
||||
tempAnimProps.check1 = eventBox[i].getCheck1Value();
|
||||
tempAnimProps.check2 = eventBox[i].getCheck2Value();
|
||||
tempAnimProps.check3 = eventBox[i].getCheck3Value();
|
||||
tempAnimProps.check4 = eventBox[i].getCheck4Value();
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.append('type', 'play-anim');
|
||||
const url = '/post?' + params.toString();
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'text/plain'} ,
|
||||
body: JSON.stringify(tempAnimProps)
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) { throw new Error('Request failed'); }
|
||||
})
|
||||
.catch(error => { console.error(error); });
|
||||
}
|
||||
|
||||
// send anim event to test out
|
||||
function postTestCountdown(){
|
||||
let tempCountdown = {};
|
||||
tempCountdown.min = constLightMin.value;
|
||||
tempCountdown.max = constLightMax.value;
|
||||
tempCountdown.hold = holdTime.value;
|
||||
tempCountdown.ramp = rampTime.value;
|
||||
tempCountdown.active_profile = selSavedAnimProfiles.selectedIndex;
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.append('type', 'set-countdown');
|
||||
const url = '/post?' + params.toString();
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'text/plain'} ,
|
||||
body: JSON.stringify(tempCountdown)
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) { throw new Error('Request failed'); }
|
||||
})
|
||||
.catch(error => { console.error(error); });
|
||||
}
|
||||
|
||||
// Post Play Anim
|
||||
function handleEventTryClick(event) {
|
||||
let tempAnimProps = {};
|
||||
tempAnimProps.index = event.detail.eventIndex;
|
||||
tempAnimProps.anim = event.detail.animIndex;
|
||||
tempAnimProps.hue = event.detail.hue;
|
||||
tempAnimProps.speed = event.detail.speed;
|
||||
tempAnimProps["hue-range"] = event.detail.colorRange;
|
||||
tempAnimProps.param1 = event.detail.param1;
|
||||
tempAnimProps.param2 = event.detail.param2;
|
||||
tempAnimProps.check1= event.detail.check1;
|
||||
tempAnimProps.check2 = event.detail.check2;
|
||||
tempAnimProps.check3 = event.detail.check3;
|
||||
tempAnimProps.check4 = event.detail.check4;
|
||||
//console.log(tempAnimProps);
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.append('type', 'play-anim');
|
||||
const url = '/post?' + params.toString();
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'text/plain'} ,
|
||||
body: JSON.stringify(tempAnimProps)
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) { throw new Error('Request failed'); }
|
||||
})
|
||||
.catch(error => { console.error(error); });
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
17
data/www/navbar.html
Normal file
@ -0,0 +1,17 @@
|
||||
<nav class="navbar">
|
||||
<div class="navbar-left">
|
||||
<img src="/images/atalogo.png" alt="Left Image" class="nav-image">
|
||||
</div>
|
||||
<ul>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/setup">Setup</a></li>
|
||||
<li>
|
||||
<a href="#"> System</a>
|
||||
<ul class="submenu">
|
||||
<li><a href="/www/wifi.html">Internet</a></li>
|
||||
<li><a href="/www/firmware.html">Update</a></li>
|
||||
<li><a href="/www/about.html">About</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
34
data/www/ok.html
Normal file
@ -0,0 +1,34 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Update Success</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="/css/nav.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
#spacer-50 {
|
||||
height: 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="navbar"></div>
|
||||
<center>
|
||||
<h2>The update was successful.</h2>
|
||||
<div id="spacer-50"></div>
|
||||
<button onclick="window.location.href='/files';">to homepage</button>
|
||||
</center>
|
||||
|
||||
<script>
|
||||
// Load navbar
|
||||
fetch('/www/navbar.html')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
document.getElementById('navbar').innerHTML = data;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
385
data/www/setup.html
Normal file
@ -0,0 +1,385 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Booth Configuration Tools</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="/css/nav.css" rel="stylesheet">
|
||||
<script src="hue-select.js"></script>
|
||||
<style>
|
||||
h1{
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
margin-bottom: 0;
|
||||
font-size: larger;
|
||||
}
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #f0f0f0;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
min-width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.outer-container {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 4 20px 4 20px
|
||||
}
|
||||
.container {
|
||||
border: 1px solid #ccc;
|
||||
padding: 5px 20px 10px 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
|
||||
width:90%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.center-text {
|
||||
text-align: center;
|
||||
font-size: medium;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.row > * {
|
||||
flex: 1;
|
||||
width: calc(50% - 20px);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
input[type="number"], select {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
width: 50%;
|
||||
}
|
||||
button {
|
||||
background-color: #007bff;
|
||||
color: #fff;
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="navbar"></div>
|
||||
<h1 name="h1element">System Setup</h1>
|
||||
<div class="outer-container">
|
||||
|
||||
<div class="container">
|
||||
<legend class="center-text" id="center-text">LED Strip #1 Settings</legend>
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Control App:</label><br>
|
||||
<select id="app-names-list" style="width: 80%;"></select>
|
||||
</div>
|
||||
<div>
|
||||
<button onclick="OnSaveAllClick()">Save All</button>
|
||||
</div>
|
||||
<div>
|
||||
<button onclick="OnReStartClick()">ReStart</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<legend class="center-text" id="center-text">LED Strip #1 Settings</legend>
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Enabled</label><br>
|
||||
<input type="checkbox" id="strip1-en">
|
||||
</div>
|
||||
<div>
|
||||
<label for="inputLEDS">LED Count:</label><br>
|
||||
<input type="number" id="strip1-led-count" min="1" max="200" step="1">
|
||||
</div>
|
||||
<div>
|
||||
<label for=" ">Shift:</label><br>
|
||||
<input type="number" id="strip1-shift" min="-199" max="199" step="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>
|
||||
<label for=" ">Offset:</label><br>
|
||||
<input type="number" id="strip1-offset" min="0" max="100" step="1">
|
||||
</div>
|
||||
<div>
|
||||
<label>RGB Order:</label><br>
|
||||
<select id="strip1-rgb">
|
||||
<option> RGB</option>
|
||||
<option> RBG</option>
|
||||
<option> GRB</option>
|
||||
<option> GBR</option>
|
||||
<option> BRG</option>
|
||||
<option> BGR</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Power:</label><br>
|
||||
<select id="strip1-power">
|
||||
<option> 100%</option>
|
||||
<option> 50%</option>
|
||||
<option> 25%</option>
|
||||
<option> 12.5%</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="container">
|
||||
<legend class="center-text" id="center-text">Test Tool (Single LED)</legend>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<input type="radio" id="test-radio-strip1" name="strip" value="0" checked>
|
||||
<label>Strip #1</label><br>
|
||||
</div>
|
||||
<div hidden>
|
||||
<input type="radio" name="strip" value="1">
|
||||
<label>Strip #2</label><br>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Pixel Index:</label><br>
|
||||
<input type="number" id="test-pixel-index" min="-199" max="199" step="1" style="width: 80px;">
|
||||
</div>
|
||||
<div>
|
||||
<hue-select id="test-hue"></hue-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<button onclick="OnSetPixelClick()">Set</button>
|
||||
</div>
|
||||
<div>
|
||||
<button onclick="OnClearClick()">Clear</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="container">
|
||||
<legend class="center-text" id="center-text">Luma Stiks Found</legend>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Stik #1 SSID:</label><br>
|
||||
<input type="checkbox" id="stick1-en">
|
||||
<input id="stick1" >
|
||||
</div>
|
||||
<div>
|
||||
<label>Stik #2 SSID:</label><br>
|
||||
<input type="checkbox" id="stick2-en">
|
||||
<input id="stick2" >
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Stik #3 SSID:</label><br>
|
||||
<input type="checkbox" id="stick3-en">
|
||||
<input id="stick3" >
|
||||
</div>
|
||||
<div>
|
||||
<label>Stik #4 SSID:</label><br>
|
||||
<input type="checkbox" id="stick4-en">
|
||||
<input id="stick4" >
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Stik #5 SSID:</label><br>
|
||||
<input type="checkbox" id="stick5-en">
|
||||
<input id="stick5" >
|
||||
</div>
|
||||
<div>
|
||||
<label>Stik #6 SSID:</label><br>
|
||||
<input type="checkbox" id="stick6-en">
|
||||
<input id="stick6" >
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Stik #7 SSID:</label><br>
|
||||
<input type="checkbox" id="stick7-en">
|
||||
<input id="stick7" >
|
||||
</div>
|
||||
<div>
|
||||
<label>Stik #8 SSID:</label><br>
|
||||
<input type="checkbox" id="stick8-en">
|
||||
<input id="stick8" >
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" >
|
||||
<div>
|
||||
<button id="save-stiks">Scan</button>
|
||||
</div>
|
||||
<div>
|
||||
<button id="save-stiks">Save</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
fetch('/www/navbar.html')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
document.getElementById('navbar').innerHTML = data;
|
||||
});
|
||||
|
||||
window.onload = function() { OnPageLoad(); };
|
||||
|
||||
const AppList = document.getElementById('app-names-list');
|
||||
|
||||
const Strip1Enabled = document.getElementById('strip1-en');
|
||||
const Strip1LedCount = document.getElementById('strip1-led-count');
|
||||
const Strip1Shift = document.getElementById('strip1-shift');
|
||||
const Strip1Offset = document.getElementById('strip1-offset');
|
||||
const Strip1RGB = document.getElementById('strip1-rgb');
|
||||
const Strip1Power = document.getElementById('strip1-power');
|
||||
|
||||
const TestHue = document.getElementById('test-hue');
|
||||
|
||||
const PixelIndex = document.getElementById('test-pixel-index');
|
||||
PixelIndex.value = 0;
|
||||
|
||||
function OnPageLoad(){
|
||||
fetch_json_file('cfg/app-events.json').then(result =>{
|
||||
jsAppEvents = result.data;
|
||||
|
||||
fetch_json_file('cfg/led-devices.json').then(result =>{
|
||||
jsLedDevices = result.data;
|
||||
|
||||
FillControlAppList(jsAppEvents);
|
||||
FillStripValues(jsLedDevices);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function FillStripValues(js){
|
||||
Strip1Enabled.checked = js.strip1.en;
|
||||
Strip1LedCount.value = js.strip1.size;
|
||||
Strip1Shift.value = js.strip1.shift;
|
||||
Strip1Offset.value = js.strip1.offset;
|
||||
Strip1RGB.selectedIndex = js.strip1['rgb-order'];
|
||||
Strip1Power.selectedIndex = js.strip1['power-div'];
|
||||
SelectRgbByText(js.strip1['rgb-order'].toLowerCase());
|
||||
}
|
||||
|
||||
function SelectRgbByText(rgbText){
|
||||
let x = 0;
|
||||
if(rgbText === "rgb"){x = 0;}
|
||||
else if(rgbText === "rbg"){x = 0;}
|
||||
else if(rgbText === "grb"){x = 0;}
|
||||
else if(rgbText === "gbr"){x = 0;}
|
||||
else if(rgbText === "brg"){x = 0;}
|
||||
else if(rgbText === "bgr"){x = 0;}
|
||||
Strip1RGB.selectedIndex = x;
|
||||
}
|
||||
|
||||
function FillControlAppList(js){
|
||||
jsApps = js.apps;
|
||||
let x = 0;
|
||||
jsApps.forEach(app => {
|
||||
AddOptionToList(x, app.name);
|
||||
x++;
|
||||
});
|
||||
|
||||
AppList.selectedIndex = js.index;
|
||||
}
|
||||
|
||||
function AddOptionToList(value, text){
|
||||
const optionElement = document.createElement('option');
|
||||
optionElement.value = value;
|
||||
optionElement.textContent = text;
|
||||
AppList.appendChild(optionElement);
|
||||
}
|
||||
|
||||
function OnSetPixelClick(){
|
||||
let jsPacket = {};
|
||||
jsPacket.strip = 0;
|
||||
i = PixelIndex.value;
|
||||
if( i < -200){
|
||||
PixelIndex.value = i;
|
||||
return;
|
||||
}
|
||||
|
||||
jsPacket.index = PixelIndex.value;
|
||||
jsPacket.hue = TestHue.getSelectedHue();
|
||||
SendJson(jsPacket, 'set-pixel');
|
||||
}
|
||||
|
||||
function SendJson(js, type){
|
||||
const params = new URLSearchParams();
|
||||
params.append('type', type);
|
||||
const url = '/post?' + params.toString();
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'text/plain'} ,
|
||||
body: JSON.stringify(js)
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) { throw new Error('Request failed'); }
|
||||
})
|
||||
.catch(error => { console.error(error); });
|
||||
}
|
||||
|
||||
function OnClearClick(){
|
||||
let jsPacket = {};
|
||||
jsPacket.strip = 0;
|
||||
SendJson(jsPacket, 'clear-strip');
|
||||
}
|
||||
|
||||
function OnSaveAllClick(){
|
||||
let jsPacket = {};
|
||||
jsPacket.appindex = AppList.selectedIndex;
|
||||
jsPacket.en1 = Strip1Enabled.checked;
|
||||
jsPacket.count1 = Strip1LedCount.value;
|
||||
jsPacket.shift1 = Strip1Shift.value;
|
||||
jsPacket.offset1 = Strip1Offset.value;
|
||||
jsPacket.rgb1 = Strip1RGB.value.toLowerCase();
|
||||
jsPacket.power1 = Strip1Power.selectedIndex;
|
||||
SendJson(jsPacket, 'setup-save');
|
||||
}
|
||||
|
||||
function OnReStartClick(){
|
||||
let jsPacket = {};
|
||||
jsPacket.test = "restart";
|
||||
SendJson(jsPacket, 'restart');
|
||||
}
|
||||
|
||||
function fetch_json_file(filename){
|
||||
return fetch(filename)
|
||||
.then(response => {
|
||||
if (response.ok) { return response.json(); }
|
||||
else { throw new Error('fetching ' + fileName + ' failed'); }
|
||||
})
|
||||
.then(data => { return{data:data}; })
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
234
data/www/upgrade.html
Normal file
@ -0,0 +1,234 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" type="text/css" href="/css/nav.css">
|
||||
<title>Firmware Upgrade</title>
|
||||
<style>
|
||||
.status-circle {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.connected { background-color: #4CAF50; }
|
||||
.disconnected { background-color: #f44336; }
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 20px auto;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.version-info {
|
||||
margin: 20px 0;
|
||||
padding: 10px;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.progress-box {
|
||||
height: calc(100vh - 500px);
|
||||
min-height: 200px;
|
||||
margin: 20px 0;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
overflow-y: auto;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #2196F3;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
background-color: #cccccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="navbar"></div>
|
||||
<div class="container">
|
||||
<h1>Firmware Upgrade</h1>
|
||||
|
||||
<div>
|
||||
<span class="status-circle disconnected" id="status-indicator"></span>
|
||||
<span id="connection-status">Disconnected</span>
|
||||
</div>
|
||||
|
||||
<div class="version-info">
|
||||
<p>Current Version: <span id="current-version">-</span></p>
|
||||
<p>Latest Version: <span id="latest-version">-</span></p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button id="check-upgrades" onclick="checkUpdates()">Check for Upgrades</button>
|
||||
<button id="start-upgrade" onclick="startUpdate()" disabled>Start upgrade</button>
|
||||
</div>
|
||||
|
||||
<div class="progress-box" id="progress-log"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Load the navigation bar from an external HTML file and insert it into the page
|
||||
fetch('/www/navbar.html')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
document.getElementById('navbar').innerHTML = data;
|
||||
});
|
||||
|
||||
// Flag to track if a firmware upgrade is available
|
||||
let updateAvailable = false;
|
||||
|
||||
// Updates the UI to show connection status
|
||||
// Changes the color and text of the status indicator
|
||||
function updateStatus(connected) {
|
||||
const indicator = document.getElementById('status-indicator');
|
||||
const status = document.getElementById('connection-status');
|
||||
|
||||
indicator.className = `status-circle ${connected ? 'connected' : 'disconnected'}`;
|
||||
status.textContent = connected ? 'Connected' : 'Disconnected';
|
||||
}
|
||||
|
||||
// Adds a message to the progress log box
|
||||
// Auto-scrolls to the bottom to show latest messages
|
||||
function log(message) {
|
||||
const logBox = document.getElementById('progress-log');
|
||||
logBox.innerHTML += `${message}<br>`;
|
||||
logBox.scrollTop = logBox.scrollHeight;
|
||||
}
|
||||
|
||||
// Checks for firmware updates by calling the server API
|
||||
// Updates the UI with version information and enables/disables upgrade button
|
||||
async function checkUpdates() {
|
||||
const checkButton = document.getElementById('check-upgrades');
|
||||
checkButton.disabled = true; // Disable button while checking
|
||||
|
||||
try {
|
||||
const response = await fetch('/upgrade/check');
|
||||
const data = await response.json();
|
||||
|
||||
// Upgrade version information in the UI
|
||||
document.getElementById('current-version').textContent = data.currentVersion;
|
||||
document.getElementById('latest-version').textContent = data.latestVersion;
|
||||
|
||||
// Enable/disable upgrade button based on availability
|
||||
updateAvailable = data.updateAvailable;
|
||||
document.getElementById('start-upgrade').disabled = !updateAvailable;
|
||||
|
||||
log(updateAvailable ? 'Upgrade available!' : 'No upgrades available');
|
||||
} catch (error) {
|
||||
log('Error checking for upgrades: ' + error);
|
||||
} finally {
|
||||
checkButton.disabled = false; // Re-enable button when done
|
||||
}
|
||||
}
|
||||
|
||||
// Initiates the firmware upgrade process
|
||||
// Uses Websocket to receive progress updates
|
||||
async function startUpdate() {
|
||||
const startButton = document.getElementById('start-upgrade');
|
||||
const checkButton = document.getElementById('check-upgrades');
|
||||
let retryCount = 0;
|
||||
const maxRetries = 3;
|
||||
|
||||
// Disable buttons during the update
|
||||
startButton.disabled = true;
|
||||
checkButton.disabled = true;
|
||||
|
||||
try {
|
||||
// Start the upgrade process with a timeout
|
||||
log('Starting update...');
|
||||
const timeout = new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error("Request timed out")), 10000)
|
||||
);
|
||||
|
||||
const response = await Promise.race([
|
||||
fetch('/upgrade/start', {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin'
|
||||
}),
|
||||
timeout
|
||||
]);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
log('Update initiated successfully.');
|
||||
|
||||
// Create SSE connection
|
||||
const eventSource = new EventSource('/upgrade-progress');
|
||||
log('Connecting to update server...');
|
||||
|
||||
eventSource.onopen = () => {
|
||||
console.log("EventSource connected");
|
||||
log('Update connection established.');
|
||||
};
|
||||
|
||||
eventSource.addEventListener('update', (event) => {
|
||||
try {
|
||||
console.log("Received message:", event.data);
|
||||
const data = JSON.parse(event.data);
|
||||
log(data.message); // Log the update message
|
||||
|
||||
// Handle completion
|
||||
if (data.complete) {
|
||||
eventSource.close();
|
||||
log('Upgrade complete! Rebooting...');
|
||||
//setTimeout(() => window.location.reload(), 5000);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Message parsing error:", error);
|
||||
log(`Error processing update message: ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
eventSource.onerror = (event) => {
|
||||
const errorDetails = event.error ? `Error: ${event.error}` : event.status ? `Status: ${event.status}` : 'Unknown error';
|
||||
log('Connection error occurred' + errorDetails);
|
||||
|
||||
if (retryCount++ >= maxRetries || eventSource.readyState === EventSource.CLOSED) {
|
||||
log('Max retries reached. Please refresh the page to retry.');
|
||||
eventSource.close();
|
||||
startButton.disabled = false;
|
||||
checkButton.disabled = false;
|
||||
} else {
|
||||
log(`Attempting to reconnect... (${retryCount}/${maxRetries})`);
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Update error:", error);
|
||||
log(`Update error: ${error.message}`);
|
||||
} finally {
|
||||
startButton.disabled = false;
|
||||
checkButton.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Poll server every 5 seconds to check if device is still connected
|
||||
// Updates the status indicator accordingly
|
||||
setInterval(async () => {
|
||||
try {
|
||||
const response = await fetch('/api/status');
|
||||
updateStatus(response.ok);
|
||||
} catch {
|
||||
updateStatus(false);
|
||||
}
|
||||
}, 5000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
159
data/www/wifi.html
Normal file
@ -0,0 +1,159 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" type="text/css" href="/css/nav.css">
|
||||
<title>Wifi Access</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f2f2f2;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
h1 {
|
||||
margin: 30px 0 20px;
|
||||
text-align: center;
|
||||
}
|
||||
form {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
|
||||
margin: auto;
|
||||
max-width: 500px;
|
||||
}
|
||||
input[type="text"], input[type="password"], select {
|
||||
padding: 12px 20px;
|
||||
margin: 8px 0;
|
||||
box-sizing: border-box;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background-color: #f2f2f2;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
button {
|
||||
background-color: #2d2dfa;
|
||||
color: #fff;
|
||||
padding: 12px 20px;
|
||||
margin: 8px 0;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
img{
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right:auto;
|
||||
height: auto;
|
||||
width: auto;
|
||||
max-width: 150px;
|
||||
filter: drop-shadow(2px 2px 2px #666666);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="navbar"></div>
|
||||
|
||||
<h1>Wifi Access</h1>
|
||||
<form id="wifi-credentials-form">
|
||||
<div style="text-align: center; margin-bottom: 15px;">
|
||||
<span style="display: inline-block; width: 25px; height: 25px; border-radius: 50%; background-color: gray; margin-right: 10px; vertical-align: middle;"></span>
|
||||
<label id="status-label">Status: Not Connected</label>
|
||||
</div>
|
||||
<label for="ssid">SSID:</label>
|
||||
<select id="ssid" name="ssid" required>
|
||||
<option value="">Select a network...</option>
|
||||
</select>
|
||||
<label for="password">Password:</label>
|
||||
<input type="text" id="password" name="password" placeholder="Password" required>
|
||||
<button type="button" onclick="postConnectToAP(event)">Connect</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
|
||||
fetch('/www/navbar.html')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
document.getElementById('navbar').innerHTML = data;
|
||||
});
|
||||
|
||||
const form = document.querySelector('#wifi-credentials-form');
|
||||
window.onload = getWifiNetworks();
|
||||
|
||||
function onStart(){
|
||||
getConnectionStatus();
|
||||
getWifiNetworks();
|
||||
}
|
||||
|
||||
async function getWifiNetworks() {
|
||||
try {
|
||||
const response = await fetch('/wifi/scans', { method: 'GET' });
|
||||
const data = await response.json();
|
||||
const ssidSelect = document.getElementById('ssid');
|
||||
ssidSelect.innerHTML = '<option value="">Select a network...</option>';
|
||||
|
||||
if (data.networks && Array.isArray(data.networks)) {
|
||||
data.networks.forEach(network => {
|
||||
const option = document.createElement('option');
|
||||
option.value = network.ssid;
|
||||
option.textContent = `${network.ssid} (${network.rssi}dBm)`;
|
||||
ssidSelect.appendChild(option);
|
||||
});
|
||||
} else {
|
||||
console.error('Network data is not in expected format');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('WiFi scan failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function postConnectToAP(event) {
|
||||
event.preventDefault();
|
||||
const connectButton = document.querySelector('button[onclick="postConnectToAP(event)"]');
|
||||
connectButton.disabled = true;
|
||||
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
ssid: document.getElementById('ssid').value,
|
||||
pass: document.getElementById('password').value
|
||||
});
|
||||
|
||||
await fetch(`/wifi/connect?${params.toString()}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Accept': 'application/json' }
|
||||
});
|
||||
document.getElementById('status-label').textContent = `Status: Disconnected`;
|
||||
const circle = document.querySelector('span');
|
||||
circle.style.backgroundColor = 'gray';
|
||||
|
||||
} catch (error) {
|
||||
console.error('Connection failed:', error);
|
||||
} finally {
|
||||
connectButton.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function getConnectionStatus() {
|
||||
try {
|
||||
const data = await fetch('/wifi/status', { method: 'GET'}).then(res => res.json());
|
||||
document.getElementById('status-label').textContent = `Status: ${data.status}`;
|
||||
const circle = document.querySelector('span');
|
||||
circle.style.backgroundColor = data.status === 'Connected' ? '#45a049' : 'gray';
|
||||
} catch (error) {
|
||||
console.error('Failed to get connection status:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Run getConnectionStatus every second
|
||||
setInterval(getConnectionStatus, 10000);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
8
diagram.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"version": 1,
|
||||
"author": "David P",
|
||||
"editor": "wokwi",
|
||||
"parts": ["board-esp32-s3-devkitc-1"],
|
||||
"connections": [ [ "esp:TX", "$serialMonitor:RX", "", [] ], [ "esp:RX", "$serialMonitor:TX", "", [] ] ],
|
||||
"dependencies": {}
|
||||
}
|
||||
18
docs/!instructions.txt
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
Choose Control App:
|
||||
1) Open app-events.json
|
||||
2) Change index to corresponding app
|
||||
a. 0=DSLRBooth, 1=...., 2=.....
|
||||
|
||||
Front Constant Light: ( to enable the light)
|
||||
1) Open led-devices.json
|
||||
2) Change "front-light", "en" to true
|
||||
|
||||
Rear Constant Light:
|
||||
1) Open led-devices.json
|
||||
2) Change "rear-light", "en" to true
|
||||
|
||||
|
||||
|
||||
|
||||
**anim-profiles
|
||||
2
docs/DSLRBooth Mode Wifi no bluetooth.md
Normal file
@ -0,0 +1,2 @@
|
||||
# DSLRBooth Mode Wifi no bluetooth
|
||||
|
||||
3
docs/Manual Mode.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Manual Mode
|
||||
|
||||
asdad
|
||||
51
docs/Sys Report.txt
Normal file
@ -0,0 +1,51 @@
|
||||
********** APP CONTROL **********
|
||||
App: DSLRBooth
|
||||
Mode:
|
||||
|
||||
********** PERIPHERALS **********
|
||||
RGB Strip1 Active: true, Size: 164, Core: 0, I2S_Ch: 0
|
||||
Shift: 0, Offset: 0, RGB Order: grb, Pin: 30
|
||||
RGB Strip2 Active: true, Size: 164, Core: 0, I2S_Ch: 0
|
||||
Shift: 0, Offset: 0, RGB Order: grb, Pin: 30
|
||||
Front Light Active: true, Relay: 0, Freq: 500
|
||||
Rear Light Active: false, Relay: 1, Freq: 500
|
||||
Buzzer: On
|
||||
OLED: Off
|
||||
|
||||
********** NETWORK **********
|
||||
Wifi Client Active: true, Status: Connected
|
||||
SSID: ATA, RSSi:-107, Ch:12, Encryp: asdasda,
|
||||
IP: 192.168.0.15, MAC: XX:XX:XX:XX:XX,
|
||||
Wifi AP Active: false, Status: Client connected,
|
||||
IP: 192.168.4.1
|
||||
|
||||
********** Chip **********
|
||||
Clock: 240Mhz, RAM: asdfas, Used: 23423, Free: 23423
|
||||
ID: 23:23:43:32:23:43
|
||||
|
||||
********** Bluetooth LE **********
|
||||
Active: true, Server SSID: ATA_COMMXX, Connected:
|
||||
SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
|
||||
********** FAN / T SENSOR **********
|
||||
Fan Active: true, Relay: 3
|
||||
TSensor Active: true, Setpoint1: 80, Setpoint2: 85
|
||||
T Now: 78.2F
|
||||
|
||||
********** 12V Monitor **********
|
||||
Volt: 12.2V
|
||||
********** Buttons **********
|
||||
|
||||
|
||||
********** Touch Sensors **********
|
||||
|
||||
********** 433 RX/TX **********
|
||||
RX Active: true
|
||||
TX Active: true
|
||||
|
||||
********** Tasks & CPU Usage **********
|
||||
Task1... CPU Usage: 23 Priority:3
|
||||
Task2...
|
||||
Task3...
|
||||
22
docs/Todo (add).doc
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
2) Make I2S buffer setting automatic based on pixel size ---------------- DONE
|
||||
|
||||
3) Add Address Input box for firmware web location
|
||||
|
||||
4) cpu / thread water mark diagnostics
|
||||
|
||||
5) ble multi client
|
||||
|
||||
6) Strip 2 simulataneous function Test
|
||||
|
||||
7) Add button to control Rear Lights
|
||||
|
||||
|
||||
**** Add LED Color Indication on BLE Connection established and disconnect
|
||||
|
||||
***
|
||||
|
||||
|
||||
|
||||
**** Saving from web to LittleFS.... Write to temporary file then rename.
|
||||
10
docs/Todo.(fixes)doc
Normal file
@ -0,0 +1,10 @@
|
||||
1) Helio and Lumia Lighting Logic
|
||||
|
||||
2) Rear Light Button Ramp control
|
||||
|
||||
3) Better Tunes
|
||||
|
||||
4) Save Wifi Creds in Flash...not LittleFS
|
||||
|
||||
5) Config page,
|
||||
Booth Type, Front active, Rear Active,
|
||||
23
docs/cli commands.txt
Normal file
@ -0,0 +1,23 @@
|
||||
1. set-relay -value -freq
|
||||
2. set-event -index
|
||||
3. set-front-light -value -freq
|
||||
4. playtune -index x
|
||||
playtune "dasdasafsdfasdf"
|
||||
5. readtemp -c
|
||||
6. restart
|
||||
7. set-bright
|
||||
8. readfile -f filename
|
||||
9. list directory
|
||||
10. trigger button
|
||||
11. echo
|
||||
12. ble-msg
|
||||
13. ping
|
||||
14. post ble command
|
||||
15. post tcp command
|
||||
16. set wifi creds
|
||||
17. start AP
|
||||
18. start ble
|
||||
19. test event anim colmain colbase density speed
|
||||
20. send rf
|
||||
21. get 12V value
|
||||
22.
|
||||
74
esp32s3_atabooth_8mb.code-workspace
Normal file
@ -0,0 +1,74 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
},
|
||||
{
|
||||
"path": "../esp32s3_module_8mb"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"files.associations": {
|
||||
"*.service": "ini",
|
||||
"new": "cpp",
|
||||
"array": "cpp",
|
||||
"atomic": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"bitset": "cpp",
|
||||
"cctype": "cpp",
|
||||
"chrono": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"deque": "cpp",
|
||||
"list": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"vector": "cpp",
|
||||
"exception": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"functional": "cpp",
|
||||
"iterator": "cpp",
|
||||
"map": "cpp",
|
||||
"memory": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"numeric": "cpp",
|
||||
"optional": "cpp",
|
||||
"random": "cpp",
|
||||
"ratio": "cpp",
|
||||
"regex": "cpp",
|
||||
"set": "cpp",
|
||||
"string": "cpp",
|
||||
"string_view": "cpp",
|
||||
"system_error": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"fstream": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"mutex": "cpp",
|
||||
"ostream": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"thread": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"typeinfo": "cpp"
|
||||
},
|
||||
"liveServer.settings.multiRootWorkspaceName": "esp32s3_atabooth_8mb"
|
||||
}
|
||||
}
|
||||
165
firmware_update/GenUpdate.py
Normal file
@ -0,0 +1,165 @@
|
||||
import os
|
||||
import shutil
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
def copy_folder_to_destination(src_path, dest_path, skip_dirs=None, skip_files=None):
|
||||
# Ensure the destination directory exists
|
||||
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
|
||||
|
||||
# Remove existing folder if present
|
||||
if os.path.exists(dest_path):
|
||||
shutil.rmtree(dest_path)
|
||||
|
||||
# Create destination directory
|
||||
os.makedirs(dest_path)
|
||||
|
||||
# Walk through source directory
|
||||
for root, dirs, files in os.walk(src_path):
|
||||
# Remove directories to skip from dirs list
|
||||
if skip_dirs:
|
||||
dirs[:] = [d for d in dirs if d not in skip_dirs]
|
||||
|
||||
# Calculate relative path
|
||||
rel_path = os.path.relpath(root, src_path)
|
||||
dest_dir = os.path.join(dest_path, rel_path)
|
||||
|
||||
# Create corresponding destination directory
|
||||
os.makedirs(dest_dir, exist_ok=True)
|
||||
|
||||
# Copy files that aren't in skip_files
|
||||
for file in files:
|
||||
if skip_files and file in skip_files:
|
||||
continue
|
||||
src_file = os.path.join(root, file)
|
||||
dest_file = os.path.join(dest_dir, file)
|
||||
shutil.copy2(src_file, dest_file)
|
||||
|
||||
def calculate_md5(file_path):
|
||||
hash_md5 = hashlib.md5()
|
||||
with open(file_path, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(4096), b""):
|
||||
hash_md5.update(chunk)
|
||||
return hash_md5.hexdigest()
|
||||
|
||||
def get_file_size(file_path):
|
||||
return os.path.getsize(file_path)
|
||||
|
||||
def update_json_file(json_array, folder_path):
|
||||
# Create new data array for files
|
||||
file_array = []
|
||||
|
||||
# Walk through the copied folder and collect file details
|
||||
for root, _, files in os.walk(folder_path):
|
||||
for file in files:
|
||||
file_path = os.path.join(root, file)
|
||||
relative_path = os.path.relpath(file_path, folder_path)
|
||||
|
||||
# Replace backslashes with forward slashes
|
||||
relative_path = relative_path.replace('\\', '/')
|
||||
|
||||
file_entry = {
|
||||
"remote": os.path.join("data/", relative_path),
|
||||
"local": os.path.join("/", relative_path),
|
||||
"md5": calculate_md5(file_path),
|
||||
"size": get_file_size(file_path)
|
||||
}
|
||||
file_array.append(file_entry)
|
||||
|
||||
# Replace the contents of the input json_array with new data
|
||||
json_array.clear()
|
||||
json_array.extend(file_array)
|
||||
|
||||
|
||||
def update_files(src_path, dest_path, skip_dirs=None, skip_files=None):
|
||||
# Check if the source folder exists
|
||||
if not os.path.isdir(src_path) or not os.path.isdir(dest_path):
|
||||
print("Invalid folder path!")
|
||||
return
|
||||
|
||||
# Copy all data contents
|
||||
copy_folder_to_destination(src_path, dest_path, skip_dirs, skip_files)
|
||||
print("Folder copied successfully.")
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
here_path = os.path.dirname(os.path.abspath(__file__))
|
||||
project_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
# Path of the folder to copy (you can modify this)
|
||||
src_folder_name = "data"
|
||||
src_path = os.path.join(project_path, src_folder_name)
|
||||
#print(f"source path: {src_path}")
|
||||
|
||||
# Path of the destination folder
|
||||
dest_folder_name = "firmware_update\\latest\\data"
|
||||
dest_path = os.path.join(project_path, dest_folder_name)
|
||||
#print(f"destination path: {dest_path}")
|
||||
|
||||
# Path of the firmware binary file
|
||||
bin_name = ".pio\\build\\esp32s3dev\\firmware.bin"
|
||||
|
||||
# Skip these directories
|
||||
skip_dirs = ["boards", "booths"]
|
||||
|
||||
# Skip these files
|
||||
skip_files = ["wifi.json", "system.json", "luma-stiks.json", ]
|
||||
|
||||
|
||||
update_files = input("Do you want to update the files? (y/n): ")
|
||||
|
||||
# *********************** Copy Data Files ***********************
|
||||
if update_files.lower() == "y":
|
||||
update_files(src_path, dest_path, skip_dirs, skip_files)
|
||||
|
||||
|
||||
# *********************** Copy Binary file ***********************
|
||||
update_firmware = input("Do you want to update the firmware? (y/n): ")
|
||||
if update_firmware.lower() == "y":
|
||||
# Copy firmware.bin to the destination
|
||||
bin_path = os.path.join(project_path, bin_name)
|
||||
shutil.copy(bin_path, here_path)
|
||||
#print(f"firmware path: {bin_path}")
|
||||
print("firmware.bin copied successfully.")
|
||||
|
||||
|
||||
|
||||
# *********************** Process update.json ***********************
|
||||
# Update the JSON file
|
||||
json_path = os.path.join(here_path, "update.json")
|
||||
print(f"json path: {json_path}")
|
||||
|
||||
# Read existing JSON
|
||||
with open(json_path, "r") as f:
|
||||
try:
|
||||
json_doc = json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
print("Invalid JSON file!")
|
||||
return
|
||||
|
||||
# process the files array
|
||||
#if update_files.lower() == "y":
|
||||
json_files_array = json_doc["files"]
|
||||
update_json_file(json_files_array, dest_path)
|
||||
#print(f"Folder {os.path.basename(src_path)} processed successfully.")
|
||||
|
||||
|
||||
# *********************** Process firmwware.bin in update.json ***********************
|
||||
# process the firmware
|
||||
if update_firmware.lower() == "y":
|
||||
json_firmware = json_doc["firmware"]
|
||||
firmware_path = os.path.join(here_path, "firmware.bin")
|
||||
json_firmware["md5"] = calculate_md5(firmware_path)
|
||||
json_firmware["size"] = get_file_size(firmware_path)
|
||||
|
||||
|
||||
# Write updated JSON
|
||||
with open(json_path, "w") as f:
|
||||
json.dump(json_doc, f, indent=4)
|
||||
|
||||
print("Update JSON files created successfully.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
152
firmware_update/UploadToGoogle.py
Normal file
@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import datetime
|
||||
from pathlib import Path
|
||||
from google.cloud import storage
|
||||
|
||||
# =============================================================================
|
||||
# CONFIGURATION CONSTANTS
|
||||
# =============================================================================
|
||||
|
||||
CREATE_BACKUP = False
|
||||
UPLOAD_FIRMWARE = True
|
||||
UPLOAD_MANIFEST = True
|
||||
UPLOAD_DATA = True
|
||||
|
||||
|
||||
# The name of your Google Cloud Storage bucket.
|
||||
GCS_BUCKET_NAME = 'boothifier' # <-- Change this
|
||||
|
||||
# The destination directory (prefix) inside your bucket.
|
||||
# For example, 'release' or 'prod'. Use an empty string ('') to use the bucket root.
|
||||
DESTINATION_DIR = 'latest' # <-- Change this (or leave '' for bucket root)
|
||||
BACKUPS_DIR = 'backups'
|
||||
|
||||
LOCAL_ROOT_PATH = Path(__file__).parent.resolve()
|
||||
|
||||
# Path to your Google Cloud credentials JSON file.
|
||||
GOOGLE_APPLICATION_CREDENTIALS_PATH = str(LOCAL_ROOT_PATH / 'loyal-column-439819-e3-8cddff2ee2c2.json')
|
||||
|
||||
|
||||
# Local path to the firmware file.
|
||||
LOCAL_FIRMWARE_PATH = str(LOCAL_ROOT_PATH / 'firmware.bin') # <-- Change this if needed
|
||||
LOCAL_MANIFEST_PATH = str(LOCAL_ROOT_PATH / 'update.json') # <-- Change this if needed
|
||||
|
||||
# Local path to the data directory.
|
||||
LOCAL_DATA_DIRECTORY = 'data' # <-- Change this if needed
|
||||
|
||||
# =============================================================================
|
||||
# SET UP GOOGLE CLOUD CREDENTIALS
|
||||
# =============================================================================
|
||||
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = GOOGLE_APPLICATION_CREDENTIALS_PATH
|
||||
|
||||
# =============================================================================
|
||||
# HELPER FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
def backup_existing_files(bucket, destination_prefix, backups_prefix, backup_folder):
|
||||
"""
|
||||
Copy every blob in the bucket with a name starting with destination_prefix
|
||||
(except any files already in a backup folder) into a backup folder under backups_prefix.
|
||||
|
||||
Files will be copied into:
|
||||
<backups_prefix>/<backup_folder>/<relative_path>
|
||||
"""
|
||||
blobs = bucket.list_blobs(prefix=destination_prefix)
|
||||
for blob in blobs:
|
||||
# Compute the file's relative path by removing the destination_prefix.
|
||||
if destination_prefix:
|
||||
# Remove the destination_prefix and any leading slashes.
|
||||
relative_path = blob.name[len(destination_prefix):].lstrip('/')
|
||||
else:
|
||||
relative_path = blob.name
|
||||
|
||||
# Construct the new blob name in the backup folder under backups_prefix.
|
||||
if backups_prefix:
|
||||
new_blob_name = f"{backups_prefix}/{backup_folder}/{relative_path}"
|
||||
else:
|
||||
new_blob_name = f"{backup_folder}/{relative_path}"
|
||||
|
||||
print(f"Backing up: copying '{blob.name}' to '{new_blob_name}'")
|
||||
bucket.copy_blob(blob, bucket, new_blob_name)
|
||||
|
||||
def upload_file(bucket, local_path, destination_blob_name):
|
||||
"""
|
||||
Upload a single file to the bucket with the specified blob name.
|
||||
Overwrites the blob if it already exists.
|
||||
"""
|
||||
print(f"Uploading file '{local_path}' to '{destination_blob_name}'")
|
||||
blob = bucket.blob(destination_blob_name)
|
||||
# Set Cache-Control header to force clients to always fetch fresh content.
|
||||
blob.cache_control = 'private, max-age=0, no-transform'
|
||||
blob.upload_from_filename(local_path)
|
||||
|
||||
def upload_directory(bucket, local_directory, destination_prefix):
|
||||
"""
|
||||
Recursively upload the contents of the local directory to the bucket under
|
||||
destination_prefix. The directory structure is preserved.
|
||||
"""
|
||||
for root, _, files in os.walk(local_directory):
|
||||
for file in files:
|
||||
file_local_path = os.path.join(root, file)
|
||||
# Compute the relative path of the file from the base directory.
|
||||
rel_path = os.path.relpath(file_local_path, local_directory)
|
||||
if destination_prefix:
|
||||
dest_blob_name = f"{destination_prefix}/{rel_path}"
|
||||
else:
|
||||
dest_blob_name = rel_path
|
||||
upload_file(bucket, file_local_path, dest_blob_name)
|
||||
|
||||
# =============================================================================
|
||||
# MAIN FUNCTION
|
||||
# =============================================================================
|
||||
|
||||
def main():
|
||||
# Initialize the Google Cloud Storage client.
|
||||
client = storage.Client()
|
||||
bucket = client.bucket(GCS_BUCKET_NAME)
|
||||
|
||||
# Normalize the destination prefix by stripping any trailing slashes.
|
||||
destination_prefix = DESTINATION_DIR.strip('/')
|
||||
backups_prefix = BACKUPS_DIR.strip('/')
|
||||
|
||||
if(CREATE_BACKUP):
|
||||
# Create a backup folder name with a timestamp.
|
||||
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_folder = f"backup_{timestamp}"
|
||||
print(f"Creating backup folder '{backup_folder}' inside '{destination_prefix}' and copying existing files...")
|
||||
backup_existing_files(bucket, destination_prefix, backups_prefix, backup_folder)
|
||||
|
||||
|
||||
if(UPLOAD_FIRMWARE):
|
||||
print("Uploading firmware...")
|
||||
# Upload the firmware file.
|
||||
if destination_prefix:
|
||||
firmware_destination = f"{destination_prefix}/firmware.bin"
|
||||
else:
|
||||
firmware_destination = "firmware.bin"
|
||||
upload_file(bucket, LOCAL_FIRMWARE_PATH, firmware_destination)
|
||||
|
||||
if(UPLOAD_MANIFEST):
|
||||
print("Uploading manifest...")
|
||||
# Upload the manifest.
|
||||
if destination_prefix:
|
||||
manifest_destination = f"{destination_prefix}/update.json"
|
||||
else:
|
||||
manifest_destination = "update.json"
|
||||
upload_file(bucket, LOCAL_MANIFEST_PATH, manifest_destination)
|
||||
|
||||
if(UPLOAD_DATA):
|
||||
print("Uploading data directory...")
|
||||
# Upload the data directory.
|
||||
if destination_prefix:
|
||||
data_destination = f"{destination_prefix}/data"
|
||||
else:
|
||||
data_destination = "data"
|
||||
upload_directory(bucket, LOCAL_DATA_DIRECTORY, data_destination)
|
||||
|
||||
|
||||
print("Upload complete.")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
501
firmware_update/latest/data/ata-boothifier-upgrade.html
Normal file
@ -0,0 +1,501 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ATA Firmware Update</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background-color: #f4f4f4;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 22px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.status-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: left;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.status-indicator-ble {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: gray;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.status-indicator-wifi {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: gray;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.status-indicator-internet {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: gray;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Adds space above the WiFi Connect button */
|
||||
.btn-container.wifi {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
max-width: 130px;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
button:hover:not(:disabled) {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ccc;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 90%;
|
||||
max-width: 300px;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
body {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: 14px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>ATA Firmware Update</h1>
|
||||
|
||||
<!-- Status Indicators -->
|
||||
<div class="status-container">
|
||||
<span class="status-indicator-ble"></span>
|
||||
<label id="status-ble-connection">Device: ...</label>
|
||||
</div>
|
||||
|
||||
<div class="status-container">
|
||||
<span class="status-indicator-wifi"></span>
|
||||
<label id="status-wifi-client">Wifi Client: ...</label>
|
||||
</div>
|
||||
|
||||
<div class="status-container">
|
||||
<span class="status-indicator-internet"></span>
|
||||
<label id="status-internet">Internet: ...</label>
|
||||
</div>
|
||||
|
||||
<div class="status-container">
|
||||
<label id="status-current-version">Curr Version: ...</label>
|
||||
</div>
|
||||
<div class="status-container">
|
||||
<label id="status-new-version">New Version: ...</label>
|
||||
</div>
|
||||
|
||||
<!-- Buttons -->
|
||||
<div class="btn-container">
|
||||
<button id="bleConnectBtn">Connect</button>
|
||||
<button id="checkStatusBtn" disabled>Check Status</button>
|
||||
</div>
|
||||
|
||||
<!-- Log Area -->
|
||||
<textarea id="logArea" readonly></textarea>
|
||||
|
||||
<div class="btn-container">
|
||||
<button id="checkVersionBtn" disabled>Check Version</button>
|
||||
<button id="startUpgradeBtn" disabled>Start Update</button>
|
||||
</div>
|
||||
|
||||
<!-- Wi-Fi Input Fields -->
|
||||
<div class="input-container">
|
||||
<input type="text" id="wifissid" name="wifissid" placeholder="Enter WiFi SSID" required>
|
||||
<input type="password" id="wifipassword" name="wifipassword" placeholder="Enter WiFi Password" required>
|
||||
<div style="display: flex; align-items: center; gap: 5px;">
|
||||
<input type="checkbox" id="showPassword" style="width: auto;">
|
||||
<label for="showPassword">Show Password</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Added margin-top above this button -->
|
||||
<div class="btn-container wifi">
|
||||
<button id="wifiConnectBtn" disabled>Connect Wifi</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Constants
|
||||
const BLE_SERVER_NAME = "ATALIGHTS"; // Replace with your server name
|
||||
const BLE_SERVICE_UUID = "abcdef01-2345-6789-1234-56789abcdef0"; // Replace with your service UUID
|
||||
const BLE_CHARACTERISTIC1_UUID = "abcdef01-2345-6789-1234-56789abcdef1"; // Replace with your characteristic UUID
|
||||
const BLE_CHARACTERISTIC2_UUID = "abcdef02-2345-6789-1234-56789abcdef1"; // Replace with your characteristic UUID
|
||||
|
||||
let bleDevice = null;
|
||||
let bleCharacteristic1 = null;
|
||||
let bleCharacteristic2 = null;
|
||||
let bleConnected = false;
|
||||
|
||||
const WIFI_STAT = { WIFI_DISCONNECTED:0, WIFI_BAD_CREDS:1, WIFI_NO_AP:2, WIFI_CONNECTED:3 };
|
||||
|
||||
let updatePacket = {
|
||||
wifiConnected: false,
|
||||
wifiOnline: false,
|
||||
wifiIP: [0, 0, 0, 0],
|
||||
currVersion: [0, 0, 0],
|
||||
newVersion: [0, 0, 0]
|
||||
};
|
||||
|
||||
// Log messages to the textarea
|
||||
function logMessage(message) {
|
||||
const logArea = document.getElementById('logArea');
|
||||
logArea.value += message + '\n';
|
||||
logArea.scrollTop = logArea.scrollHeight;
|
||||
}
|
||||
|
||||
// Function to scan for BLE devices
|
||||
async function scanForDevices() {
|
||||
logMessage('Scanning for BLE devices...');
|
||||
try {
|
||||
const device = await navigator.bluetooth.requestDevice({
|
||||
acceptAllDevices: true,
|
||||
optionalServices: [BLE_SERVICE_UUID]
|
||||
});
|
||||
|
||||
if (device) {
|
||||
logMessage(`Found device: ${device.name || "Unnamed"} (ID: ${device.id})`);
|
||||
} else {
|
||||
logMessage('No devices found.');
|
||||
}
|
||||
} catch (error) {
|
||||
logMessage(`Scan failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to connect to the BLE server
|
||||
async function connectToBle() {
|
||||
try {
|
||||
bleDevice = await navigator.bluetooth.requestDevice({
|
||||
filters: [{ name: BLE_SERVER_NAME }],
|
||||
optionalServices: [BLE_SERVICE_UUID]
|
||||
});
|
||||
|
||||
//logMessage(`Connecting to ${bleDevice.name}`);
|
||||
const server = await bleDevice.gatt.connect();
|
||||
//await server.setPreferredMtu(247); // Request larger MTU size
|
||||
|
||||
const service = await server.getPrimaryService(BLE_SERVICE_UUID);
|
||||
|
||||
bleCharacteristic1 = await service.getCharacteristic(BLE_CHARACTERISTIC1_UUID);
|
||||
|
||||
// Subscribe to notifications
|
||||
//await bleCharacteristic1.startNotifications();
|
||||
|
||||
// Add event listener for incoming notifications
|
||||
//bleCharacteristic1.addEventListener('characteristicvaluechanged', handleChar1Notifications);
|
||||
|
||||
|
||||
//logMessage('Getting characteristic...');
|
||||
bleCharacteristic2 = await service.getCharacteristic(BLE_CHARACTERISTIC2_UUID);
|
||||
|
||||
// Subscribe to notifications
|
||||
await bleCharacteristic2.startNotifications();
|
||||
|
||||
// Add event listener for incoming notifications
|
||||
bleCharacteristic2.addEventListener('characteristicvaluechanged', (event) => {
|
||||
const value = event.target.value;
|
||||
const decoder = new TextDecoder();
|
||||
const decodedValue = decoder.decode(value);
|
||||
logMessage('--> ' + decodedValue);
|
||||
});
|
||||
|
||||
bleConnected = true;
|
||||
document.getElementById('bleConnectBtn').disabled = true;
|
||||
document.querySelector('.status-indicator-ble').style.backgroundColor = 'green';
|
||||
document.getElementById('status-ble-connection').textContent = 'Device: Connected';
|
||||
document.getElementById('wifiConnectBtn').disabled = false;
|
||||
document.getElementById('checkStatusBtn').disabled = false;
|
||||
logMessage(`Connected to ${bleDevice.name}`);
|
||||
|
||||
await readPacket();
|
||||
processUpdatePacket(updatePacket);
|
||||
|
||||
} catch (error) {
|
||||
if (error.message.includes("cancelled")) {
|
||||
logMessage("Connection cancelled by user.");
|
||||
} else {
|
||||
logMessage(`Connection failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function sendPacket(packetMsg) {
|
||||
if (!bleCharacteristic1) {
|
||||
console.log("Cannot send packet: Not connected to BLE server.");
|
||||
return;
|
||||
}
|
||||
|
||||
const maxRetries = 3;
|
||||
const retryDelay = 1000; // 1 second
|
||||
let attempt = 0;
|
||||
|
||||
while (attempt < maxRetries) {
|
||||
try {
|
||||
//logMessage(`Sending request: ${packetMsg} (Attempt ${attempt + 1})`);
|
||||
const encoder = new TextEncoder();
|
||||
await bleCharacteristic1.writeValueWithResponse(encoder.encode(packetMsg));
|
||||
//console.log("Request sent successfully");
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error(`Failed to send packet: ${error.message}`);
|
||||
attempt++;
|
||||
if (attempt < maxRetries) {
|
||||
console.log(`Retrying in ${retryDelay / 1000} seconds...`);
|
||||
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||
} else {
|
||||
console.error("Max retries reached. Failed to send request.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function readPacket() {
|
||||
if (!bleCharacteristic1) {
|
||||
console.log("Cannot read packet: Not connected to BLE server.");
|
||||
return;
|
||||
}
|
||||
|
||||
const maxRetries = 3;
|
||||
const retryDelay = 1000; // 1 second
|
||||
let attempt = 0;
|
||||
|
||||
while (attempt < maxRetries) {
|
||||
try {
|
||||
const value = await bleCharacteristic1.readValue();
|
||||
const data = new Uint8Array(value.buffer);
|
||||
if (data.length === 12) {
|
||||
updatePacket.wifiConnected = data[0] !== 0;
|
||||
updatePacket.wifiOnline = data[1] !== 0;
|
||||
updatePacket.wifiIP = [data[2], data[3], data[4], data[5]];
|
||||
updatePacket.currVersion = [data[6], data[7], data[8]];
|
||||
updatePacket.newVersion = [data[9], data[10], data[11]];
|
||||
|
||||
//processUpdatePacket(updatePacket);
|
||||
return;
|
||||
}
|
||||
console.log("Invalid packet length");
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error(`Failed to read packet: ${error.message}`);
|
||||
attempt++;
|
||||
if (attempt < maxRetries) {
|
||||
console.log(`Retrying in ${retryDelay / 1000} seconds...`);
|
||||
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||
} else {
|
||||
console.error("Max retries reached. Failed to read packet.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process update packet
|
||||
function processUpdatePacket(packet) {
|
||||
// Process the packet data
|
||||
//console.log("Processing update packet:", packet);
|
||||
if(packet.wifiConnected === true) {
|
||||
if(packet.wifiConnected && packet.wifiIP[0] > 0) {
|
||||
document.getElementById('status-wifi-client').textContent = 'Wifi Client: Connected (' + packet.wifiIP.join('.') + ')';
|
||||
} else {
|
||||
document.getElementById('status-wifi-client').textContent = 'Wifi Client: Connected';
|
||||
}
|
||||
document.querySelector('.status-indicator-wifi').style.backgroundColor = 'green';
|
||||
} else {
|
||||
document.getElementById('status-wifi-client').textContent = 'Wifi Client: ...';
|
||||
document.querySelector('.status-indicator-wifi').style.backgroundColor = 'gray';
|
||||
}
|
||||
|
||||
if(packet.wifiOnline === true) {
|
||||
document.getElementById('status-internet').textContent = 'Online';
|
||||
document.querySelector('.status-indicator-internet').style.backgroundColor = 'green';
|
||||
document.getElementById('checkVersionBtn').disabled = false;
|
||||
|
||||
} else {
|
||||
document.getElementById('status-internet').textContent = 'Offline';
|
||||
document.querySelector('.status-indicator-internet').style.backgroundColor = 'gray';
|
||||
document.getElementById('checkVersionBtn').disabled = true;
|
||||
}
|
||||
|
||||
if (packet.currVersion[0] > 0) {
|
||||
document.getElementById('status-current-version').textContent = 'Curr Version: ' + packet.currVersion.join('.');
|
||||
} else {
|
||||
document.getElementById('status-current-version').textContent = 'Curr Version: ...';
|
||||
}
|
||||
|
||||
if (packet.newVersion[0] > 0) {
|
||||
document.getElementById('status-new-version').textContent = 'New Version: ' + packet.newVersion.join('.');
|
||||
|
||||
if(packet.wifiOnline && packet.newVersion[0] > packet.currVersion[0] ||
|
||||
(packet.newVersion[0] === packet.currVersion[0] && packet.newVersion[1] > packet.currVersion[1]) ||
|
||||
(packet.newVersion[0] === packet.currVersion[0] && packet.newVersion[1] === packet.currVersion[1] && packet.newVersion[2] > packet.currVersion[2])) {
|
||||
|
||||
//enable start upgrade button
|
||||
logMessage("New Version Available:");
|
||||
document.getElementById('checkVersionBtn').disabled = true;
|
||||
document.getElementById('startUpgradeBtn').disabled = false;
|
||||
} else {
|
||||
//disable start upgrade button
|
||||
document.getElementById('checkVersionBtn').disabled = false;
|
||||
document.getElementById('startUpgradeBtn').disabled = true;
|
||||
logMessage("New Version: Not Available");
|
||||
}
|
||||
} else {
|
||||
document.getElementById('status-new-version').textContent = 'New Version: ...';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//BLE_Characteristic.addEventListener('characteristicvaluechanged', handleNotifications);
|
||||
function handleChar1Notifications(event) {
|
||||
const data = new Uint8Array(event.data);
|
||||
|
||||
if (data.length !== 12) { // 1 byte for id, 4 bytes for booleans, 4 bytes for wifiIP, 3 bytes for currVersion, 3 bytes for newVersion
|
||||
console.log("Invalid packet length");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update existing updatePacket object instead of creating new one
|
||||
updatePacket.wifiConnected = data[0] !== 0;
|
||||
updatePacket.internetAvailable = data[1] !== 0;
|
||||
updatePacket.wifiIP = [data[2], data[3], data[4], data[5]];
|
||||
updatePacket.currVersion = [data[6], data[7], data[8]];
|
||||
updatePacket.newVersion = [data[9], data[10], data[11]];
|
||||
|
||||
processUpdatePacket(updatePacket);
|
||||
}
|
||||
|
||||
document.getElementById('showPassword').addEventListener('change', function() {
|
||||
const passwordInput = document.getElementById('wifipassword');
|
||||
passwordInput.type = this.checked ? 'text' : 'password';
|
||||
});
|
||||
|
||||
// Event listeners for buttons
|
||||
document.getElementById('bleConnectBtn').addEventListener('click', connectToBle);
|
||||
document.getElementById('checkStatusBtn').addEventListener('click', async () => {
|
||||
if (bleCharacteristic1) {
|
||||
await readPacket();
|
||||
processUpdatePacket(updatePacket);
|
||||
} else {
|
||||
logMessage('BLE device not connected.');
|
||||
}
|
||||
});
|
||||
document.getElementById('checkVersionBtn').addEventListener('click', async () => {
|
||||
await sendPacket('version-check');
|
||||
// loop and monitor the the updatePacket.newVersion
|
||||
success = false;
|
||||
for (let i = 0; i < 20; i++) {
|
||||
await readPacket();
|
||||
if (updatePacket.newVersion[0] > 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
if(success) {
|
||||
processUpdatePacket(updatePacket);
|
||||
logMessage("New Version: Available");
|
||||
} else {
|
||||
logMessage("New Version: Not Available");
|
||||
}
|
||||
|
||||
});
|
||||
document.getElementById('wifiConnectBtn').addEventListener('click', async () => {
|
||||
const ssid = document.getElementById('wifissid').value;
|
||||
const password = document.getElementById('wifipassword').value;
|
||||
if (ssid && password) {
|
||||
// Send credentials to the device
|
||||
jsonString = ' {"ssid":"' + ssid + '","pass":"' + password + '"} ';
|
||||
await sendPacket('wifi-connect' + jsonString);
|
||||
|
||||
await readPacket();
|
||||
processUpdatePacket(updatePacket);
|
||||
} else {
|
||||
alert('Please enter both SSID and password.');
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
document.getElementById('startUpgradeBtn').addEventListener('click', async () => {
|
||||
await sendPacket('upgrade-start');
|
||||
});
|
||||
|
||||
processUpdatePacket(updatePacket);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
79
firmware_update/latest/data/css/global-style.css
Normal file
@ -0,0 +1,79 @@
|
||||
body {
|
||||
/*background-color: #f7f7f7;*/
|
||||
font-family: Tahoma, Arial, sans-serif;
|
||||
font-size: small;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.main-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
}
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
input{
|
||||
cursor:pointer;
|
||||
}
|
||||
input[type="number"]{
|
||||
width: 100px;
|
||||
}
|
||||
#submit {
|
||||
width:120px;
|
||||
}
|
||||
select{
|
||||
width:160px;
|
||||
}
|
||||
#select-path {
|
||||
width:250px;
|
||||
}
|
||||
#select-dir {
|
||||
width:90px;
|
||||
}
|
||||
#spacer-50 {
|
||||
height: 50px;
|
||||
}
|
||||
#spacer-20 {
|
||||
height: 20px;
|
||||
}
|
||||
#spacer-10 {
|
||||
height: 10px;
|
||||
}
|
||||
table {
|
||||
/*background-color: #dddddd;*/
|
||||
border-collapse: collapse;
|
||||
width:600px;
|
||||
margin: 0 auto;
|
||||
overflow: visible;
|
||||
}
|
||||
td, th {
|
||||
/*border: 1px solid #dddddd;*/
|
||||
text-align: left;
|
||||
padding: 2px;
|
||||
}
|
||||
#first_td_th {
|
||||
width:400px;
|
||||
}
|
||||
fieldset {
|
||||
width:620px;
|
||||
/*background-color: #f7f7f7;*/
|
||||
border-radius: 10px;
|
||||
border-color: blue;
|
||||
}
|
||||
#format-notice {
|
||||
color: #ff0000;
|
||||
}
|
||||
legend {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-color:white;
|
||||
background-blend-mode: darken;
|
||||
border-radius: 10px;
|
||||
padding: 1px 8px 2px 8px;
|
||||
border-style: solid;
|
||||
border-width: 1.0;
|
||||
}
|
||||
72
firmware_update/latest/data/css/nav.css
Normal file
@ -0,0 +1,72 @@
|
||||
.navbar {
|
||||
width: 100%;
|
||||
background-color: black;
|
||||
border-bottom: 2px solid white;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
.navbar-left {
|
||||
padding: 0 10px;
|
||||
}
|
||||
.navbar ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative; /* Ensure relative positioning for the submenu */
|
||||
}
|
||||
.navbar ul li {
|
||||
float: left;
|
||||
position: relative; /* Ensure relative positioning for the submenu */
|
||||
}
|
||||
.navbar ul li a {
|
||||
display: block;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 14px 16px;
|
||||
text-decoration: none;
|
||||
border-right: 1px solid white;
|
||||
}
|
||||
.navbar ul li a:hover {
|
||||
background-color: grey;
|
||||
}
|
||||
.navbar ul li a.disabled {
|
||||
color: grey;
|
||||
pointer-events: none;
|
||||
}
|
||||
.navbar ul .submenu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
background-color: black;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-top: 2px solid white;
|
||||
z-index: 1000;
|
||||
}
|
||||
.navbar ul .submenu li {
|
||||
float: none;
|
||||
border-right: none;
|
||||
}
|
||||
.navbar ul .submenu li a {
|
||||
padding: 10px 16px;
|
||||
border-bottom: 1px solid white;
|
||||
}
|
||||
.navbar ul .submenu li a:hover {
|
||||
background-color: grey;
|
||||
}
|
||||
.navbar ul li:hover > .submenu {
|
||||
display: block;
|
||||
}
|
||||
.nav-image {
|
||||
height: 40px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
BIN
firmware_update/latest/data/favicon.ico
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
firmware_update/latest/data/images/atalogo.png
Normal file
|
After Width: | Height: | Size: 16 KiB |