This commit is contained in:
Jelmer
2023-12-30 11:50:42 +01:00
20 changed files with 171 additions and 83 deletions

View File

@@ -26,7 +26,7 @@ jobs:
- name: Build NRF firmware
run: |
cd ARM_Tag_FW/Newton_M3_nRF52811
pio run --environment Newton_M3_29_BWR
pio run --environment Newton_M3_Universal
- name: Build Simple_AP
run: |

1
.gitignore vendored
View File

@@ -25,3 +25,4 @@
*.o
sdcc/sdcc
ESP32_AP-Flasher/.vscode/extensions.json

Binary file not shown.

View File

@@ -35,7 +35,8 @@ bool getJsonTemplateFile(String &filename, String jsonfile, tagRecord *&taginfo,
extern bool getJsonTemplateFileExtractVariables(String &filename, String jsonfile, JsonDocument &variables, tagRecord *&taginfo, imgParam &imageParams);
int getJsonTemplateUrl(String &filename, String URL, time_t fetched, String MAC, tagRecord *&taginfo, imgParam &imageParams);
void drawJsonStream(Stream &stream, String &filename, tagRecord *&taginfo, imgParam &imageParams);
void drawElement(const JsonObject &element, TFT_eSprite &spr);
void rotateBuffer(uint8_t rotation, uint8_t &currentOrientation, TFT_eSprite &spr, imgParam &imageParams);
void drawElement(const JsonObject &element, TFT_eSprite &spr, imgParam &imageParams, uint8_t &currentOrientation);
uint16_t getColor(const String &color);
char *formatHttpDate(const time_t t);
String urlEncode(const char *msg);

View File

@@ -4,8 +4,6 @@
extern int defaultLanguage;
extern String languageList[];
/*EN English language section*/
extern String languageEnDaysShort[];
extern String languageEnDays[];

View File

@@ -69,6 +69,7 @@ struct Config {
uint8_t stopsleep;
uint8_t runStatus;
uint8_t preview;
uint8_t lock;
uint8_t wifiPower;
char timeZone[52];
uint8_t sleepTime1;

View File

@@ -1173,12 +1173,13 @@ void drawAPinfo(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgPa
TFT_eSprite spr = TFT_eSprite(&tft);
DynamicJsonDocument loc(2048);
uint8_t screenCurrentOrientation = 0;
getTemplate(loc, 21, taginfo->hwType);
initSprite(spr, imageParams.width, imageParams.height, imageParams);
const JsonArray jsonArray = loc.as<JsonArray>();
for (const JsonVariant &elem : jsonArray) {
drawElement(elem, spr);
drawElement(elem, spr, imageParams, screenCurrentOrientation);
}
spr2buffer(spr, filename, imageParams);
@@ -1371,8 +1372,11 @@ int getJsonTemplateUrl(String &filename, String URL, time_t fetched, String MAC,
}
void drawJsonStream(Stream &stream, String &filename, tagRecord *&taginfo, imgParam &imageParams) {
TFT_eSprite spr = TFT_eSprite(&tft);
TFT_eSprite spr = TFT_eSprite(&tft);
initSprite(spr, imageParams.width, imageParams.height, imageParams);
uint8_t screenCurrentOrientation = 0;
//spr.setRotation(2);
//imageParams.rotatebuffer = imageParams.rotatebuffer + 1;
DynamicJsonDocument doc(500);
if (stream.find("[")) {
do {
@@ -1381,7 +1385,7 @@ void drawJsonStream(Stream &stream, String &filename, tagRecord *&taginfo, imgPa
wsErr("json error " + String(error.c_str()));
break;
} else {
drawElement(doc.as<JsonObject>(), spr);
drawElement(doc.as<JsonObject>(), spr, imageParams, screenCurrentOrientation);
doc.clear();
}
} while (stream.findUntil(",", "]"));
@@ -1391,7 +1395,44 @@ void drawJsonStream(Stream &stream, String &filename, tagRecord *&taginfo, imgPa
spr.deleteSprite();
}
void drawElement(const JsonObject &element, TFT_eSprite &spr) {
void rotateBuffer(uint8_t rotation, uint8_t &currentOrientation, TFT_eSprite &spr, imgParam &imageParams){
rotation = rotation % 4; //First of all, let's be sure that the rotation have a valid value (0, 1, 2 or 3)
if(rotation != currentOrientation){ //If we have a rotation to do, let's do it
int stepToDo = currentOrientation - rotation; //rotation we have to do
//-2, 2: upside down
//-1, 3: 270° rotation
//-3, 1: 90° rotation
if(abs(stepToDo) == 2){ //If we have to do a 180° rotation:
TFT_eSprite sprCpy = TFT_eSprite(&tft); //We create a new sprite that will act as a buffer
initSprite(sprCpy, spr.width(), spr.height(), imageParams); //initialisation of the new sprite
spr.pushRotated(&sprCpy, 180, TFT_WHITE); //We fill the new sprite with the old one rotated by 180°
spr.fillSprite(TFT_WHITE); //We fill the old one in white as anything that's white will be ignored by the pushRotated function
sprCpy.pushRotated(&spr, 0, TFT_WHITE); //We copy the buffer sprite to the main one
sprCpy.deleteSprite(); //We delete the buffer sprite to avoid memory leak
}else{
int angle = 90;
if(stepToDo == -1 || stepToDo == 3){
angle = 270;
}
TFT_eSprite sprCpy = TFT_eSprite(&tft);
initSprite(sprCpy, spr.height(), spr.width(), imageParams);
spr.pushRotated(&sprCpy, angle, TFT_WHITE);
spr.deleteSprite();
initSprite(spr, sprCpy.width(), sprCpy.height(), imageParams);
sprCpy.pushRotated(&spr, 0, TFT_WHITE);
sprCpy.deleteSprite();
if(imageParams.rotatebuffer==1){
imageParams.rotatebuffer = 0;
}else{
imageParams.rotatebuffer = 1;
}
}
currentOrientation = rotation;
}
}
void drawElement(const JsonObject &element, TFT_eSprite &spr, imgParam &imageParams, uint8_t &currentOrientation) {
if (element.containsKey("text")) {
const JsonArray &textArray = element["text"];
const uint16_t align = textArray[5] | 0;
@@ -1414,6 +1455,9 @@ void drawElement(const JsonObject &element, TFT_eSprite &spr) {
} else if (element.containsKey("circle")) {
const JsonArray &circleArray = element["circle"];
spr.fillCircle(circleArray[0].as<int>(), circleArray[1].as<int>(), circleArray[2].as<int>(), getColor(circleArray[3]));
} else if (element.containsKey("rotate")) {
uint8_t rotation = element["rotate"].as<int>();
rotateBuffer(rotation, currentOrientation, spr, imageParams);
}
}

View File

@@ -7,8 +7,6 @@
int defaultLanguage = 0;
String languageList[] = {"EN - English", "NL - Nederlands", "DE - Deutsch", "NO - Norwegian", "FR - French"};
/*EN English language section*/
String languageEnDaysShort[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"};
String languageEnDays[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
@@ -33,21 +31,45 @@ String languageNoDays[] = {"Søndag", "Mandag", "Tirsdag", "Onsdag", "Torsdag",
String languageNoMonth[] = {"Januar", "Februar", "Mars", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Desember"};
/*END Norwegian language section END*/
/*CZ Czech language section*/
String languageCzDaysShort[] = {"NE", "PO", "ÚT", "ST", "ČT", "", "SO"};
String languageCzDays[] = {"Neděle", "Pondělí", "Úterý", "Středa", "Čtvrtek", "Pátek", "Sobota"};
String languageCzMonth[] = {"Leden", "Únor", "Březen", "Duben", "Květen", "Červen", "Červenec", "Srpen", "Září", "Říjen", "Listopad", "Prosinec"};
/*END Czech language section END*/
/*SK Slovak language section*/
String languageSkDaysShort[] = {"NE", "PO", "UT", "ST", "ŠT", "PI", "SO"};
String languageSkDays[] = {"Nedeľa", "Pondelok", "Utorok", "Streda", "Štvrtok", "Piatok", "Sobota"};
String languageSkMonth[] = {"Január", "Február", "Marec", "Apríl", "Máj", "Jún", "Júl", "August", "September", "Oktober", "November", "December"};
/*END Slovak language section END*/
/*PL Polish language section*/
String languagePlDaysShort[] = {"Ni", "Po", "Wt", "Śr", "Cz", "Pt", "So"};
String languagePlDays[] = {"Niedziela", "Poniedziałek", "Wtorek", "Środa", "Czwartek", "Piątek", "Sobota"};
String languagePlMonth[] = {"Styczeń", "Luty", "Marzec", "Kwiecień", "Maj", "Czerwiec", "Lipiec", "Sierpień", "Wrzesień", "Październik", "Listopad", "Grudzień"};
/*END Polish language section END*/
/*ES Spanish language section*/
String languageEsDaysShort[] = {"D", "L", "MA", "MI", "J", "V", "S"};
String languageEsDays[] = {"Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado"};
String languageEsMonth[] = {"Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"};
/*END Spanish language section END*/
/*FR French language section*/
String languageFrDaysShort[] = {"DI", "LU", "MA", "ME", "JE", "VE", "SA"};
String languageFrDays[] = {"Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"};
String languageFrMonth[] = {"Janvier", "Fevrier", "Mars", "Avril", "Mai", "Juin", "Juillet", "Aout", "Septembre", "Octobre", "Novembre", "Decembre"};
/*END French language section END*/
String* languageDaysShort[] = {languageEnDaysShort, languageNlDaysShort, languageDeDaysShort, languageNoDaysShort, languageFrDaysShort};
String* languageDays[] = {languageEnDays, languageNlDays, languageDeDays, languageNoDays, languageFrDays};
String* languageMonth[] = {languageEnMonth, languageNlMonth, languageDeMonth, languageNoMonth, languageFrMonth};
String* languageDaysShort[] = {languageEnDaysShort, languageNlDaysShort, languageDeDaysShort, languageNoDaysShort, languageFrDaysShort, languageCzDaysShort, languageSkDaysShort, languagePlDaysShort, languageEsDaysShort};
String* languageDays[] = {languageEnDays, languageNlDays, languageDeDays, languageNoDays, languageFrDays, languageCzDays, languageSkDays, languagePlDays, languageEsDays};
String* languageMonth[] = {languageEnMonth, languageNlMonth, languageDeMonth, languageNoMonth, languageFrMonth, languageCzMonth, languageSkMonth, languagePlMonth, languageEsMonth};
int currentLanguage = defaultLanguage;
void updateLanguageFromConfig() {
int tempLang = config.language;
if (tempLang < 0 || tempLang >= sizeof(languageList)) {
if (tempLang < 0 || tempLang > 8) {
Serial.println("Language not supported");
return;
}

View File

@@ -56,6 +56,7 @@ void prepareCancelPending(const uint8_t dst[8]) {
tagRecord* taginfo = tagRecord::findByMAC(dst);
if (taginfo == nullptr) {
if (config.lock) return;
wsErr("Tag not found, this shouldn't happen.");
return;
}
@@ -80,6 +81,7 @@ void prepareIdleReq(const uint8_t* dst, uint16_t nextCheckin) {
void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, const uint8_t* dst) {
tagRecord* taginfo = tagRecord::findByMAC(dst);
if (taginfo == nullptr) {
if (config.lock) return;
wsErr("Tag not found, this shouldn't happen.");
return;
}
@@ -129,6 +131,7 @@ bool prepareDataAvail(String& filename, uint8_t dataType, uint8_t dataTypeArgume
tagRecord* taginfo = tagRecord::findByMAC(dst);
if (taginfo == nullptr) {
if (config.lock) return true;
wsErr("Tag not found, this shouldn't happen.");
return true;
}
@@ -345,6 +348,7 @@ void processBlockRequest(struct espBlockRequest* br) {
tagRecord* taginfo = tagRecord::findByMAC(br->src);
if (taginfo == nullptr) {
if (config.lock) return;
prepareCancelPending(br->src);
Serial.printf("blockrequest: couldn't find taginfo %02X%02X%02X%02X%02X%02X%02X%02X\n", br->src[7], br->src[6], br->src[5], br->src[4], br->src[3], br->src[2], br->src[1], br->src[0]);
return;
@@ -458,6 +462,7 @@ void processDataReq(struct espAvailDataReq* eadr, bool local, IPAddress remoteIP
tagRecord* taginfo = tagRecord::findByMAC(eadr->src);
if (taginfo == nullptr) {
if (config.lock) return;
taginfo = new tagRecord;
memcpy(taginfo->mac, eadr->src, sizeof(taginfo->mac));
taginfo->pending = false;
@@ -657,6 +662,7 @@ void updateTaginfoitem(struct TagInfo* taginfoitem, IPAddress remoteIP) {
tagRecord* taginfo = tagRecord::findByMAC(taginfoitem->mac);
if (taginfo == nullptr) {
if (config.lock) return;
taginfo = new tagRecord;
memcpy(taginfo->mac, taginfoitem->mac, sizeof(taginfo->mac));
taginfo->pending = false;

View File

@@ -130,9 +130,17 @@ void saveDB(const String& filename) {
Storage.begin();
xSemaphoreTake(fsMutex, portMAX_DELAY);
fs::File existingFile = contentFS->open(filename, "r");
if (existingFile) {
existingFile.close();
String backupFilename = filename + ".bak";
contentFS->rename(filename.c_str(), backupFilename.c_str());
}
fs::File file = contentFS->open(filename, "w");
if (!file) {
Serial.println("saveDB: Failed to open file");
Serial.println("saveDB: Failed to open file for writing");
xSemaphoreGive(fsMutex);
return;
}
@@ -307,6 +315,7 @@ void initAPconfig() {
config.maxsleep = APconfig["maxsleep"] | 10;
config.stopsleep = APconfig["stopsleep"] | 1;
config.preview = APconfig["preview"] | 1;
config.lock = APconfig["lock"] | 0;
config.sleepTime1 = APconfig["sleeptime1"] | 0;
config.sleepTime2 = APconfig["sleeptime2"] | 0;
// default wifi power 8.5 dbM
@@ -333,6 +342,7 @@ void saveAPconfig() {
APconfig["maxsleep"] = config.maxsleep;
APconfig["stopsleep"] = config.stopsleep;
APconfig["preview"] = config.preview;
APconfig["lock"] = config.lock;
APconfig["wifipower"] = config.wifiPower;
APconfig["timezone"] = config.timeZone;
APconfig["sleeptime1"] = config.sleepTime1;

View File

@@ -471,6 +471,9 @@ void init_web() {
if (request->hasParam("preview", true)) {
config.preview = static_cast<uint8_t>(request->getParam("preview", true)->value().toInt());
}
if (request->hasParam("lock", true)) {
config.lock = static_cast<uint8_t>(request->getParam("lock", true)->value().toInt());
}
if (request->hasParam("sleeptime1", true)) {
config.sleepTime1 = static_cast<uint8_t>(request->getParam("sleeptime1", true)->value().toInt());
config.sleepTime2 = static_cast<uint8_t>(request->getParam("sleeptime2", true)->value().toInt());

View File

@@ -21,14 +21,11 @@
<div class="tab-container">
<div class="tablinks material-symbols-outlined" data-target="hometab" title="Dashboard">home</div>
<div class="tablinks material-symbols-outlined" data-target="tagtab" title="Tags">sell</div>
<div class="tablinks material-symbols-outlined" data-target="aptab" title="Access Points">cell_tower
</div>
<div class="tablinks material-symbols-outlined" data-target="aptab" title="Access Points">cell_tower</div>
<!--<div class="tablinks material-symbols-outlined" data-target="templatetab" title="Templates">browse
</div>-->
<div class="tablinks material-symbols-outlined" data-target="configtab" title="Settings">settings
</div>
<div class="tablinks material-symbols-outlined" data-target="logtab" title="Logging">text_snippet
</div>
<div class="tablinks material-symbols-outlined" data-target="configtab" title="Settings">settings</div>
<div class="tablinks material-symbols-outlined" data-target="logtab" title="Logging">text_snippet</div>
</div>
<!-- /tabs -->
<div><span id="runstate"></div>
@@ -116,68 +113,55 @@
<div id="tagtab" class="tabcontent">
<div class="tagheader">
<h3>Currently active tags</h3>
<div id="activefilter"></div><button class="material-symbols-outlined"
id="toggleFilters">filter_alt</button>
<div id="activefilter"></div><button class="material-symbols-outlined" id="toggleFilters">filter_alt</button>
</div>
<div id="filterOptions">
<div>
<div>group by</div>
<div>
<input type="radio" name="group" value="" id="rnone" checked><label
for="rnone">None</label>
<input type="radio" name="group" value="" id="rnone" checked><label for="rnone">None</label>
</div>
<div>
<input type="radio" name="group" value="model" id="rtagtype"><label for="rtagtype">Tag
model</label>
<input type="radio" name="group" value="model" id="rtagtype"><label for="rtagtype">Tag model</label>
</div>
<div>
<input type="radio" name="group" value="contentmode" id="rcontent"><label
for="rcontent">Content</label>
<input type="radio" name="group" value="contentmode" id="rcontent"><label for="rcontent">Content</label>
</div>
<div>
<input type="radio" name="group" value="data-channel" id="rchannel"><label
for="rchannel">Channel</label>
<input type="radio" name="group" value="data-channel" id="rchannel"><label for="rchannel">Channel</label>
</div>
</div>
<div>
<div>sort by</div>
<div>
<input type="radio" name="sort" value="alias" id="ralias" checked><label
for="ralias">Alias</label>
<input type="radio" name="sort" value="alias" id="ralias" checked><label for="ralias">Alias</label>
</div>
<div>
<input type="radio" name="sort" value="mac" id="rmac"><label for="rmac">Mac</label>
</div>
<div>
<input type="radio" name="sort" value="data-lastseen" id="rlastseen"><label
for="rlastseen">Last seen</label>
<input type="radio" name="sort" value="data-lastseen" id="rlastseen"><label for="rlastseen">Last seen</label>
</div>
<div>
<input type="radio" name="sort" value="data-nextupdate" id="rnext"><label
for="rnext">Next update</label>
<input type="radio" name="sort" value="data-nextupdate" id="rnext"><label for="rnext">Next update</label>
</div>
</div>
<div>
<div>filter</div>
<div>
<input type="checkbox" name="filter" value="local" id="rlocal"><label
for="rlocal">local</label>
<input type="checkbox" name="filter" value="local" id="rlocal"><label for="rlocal">local</label>
</div>
<div>
<input type="checkbox" name="filter" value="remote" id="rremote"><label
for="rremote">remote</label>
<input type="checkbox" name="filter" value="remote" id="rremote"><label for="rremote">remote</label>
</div>
<div>
<input type="checkbox" name="filter" value="inactive" id="rinactive"><label
for="rinactive">timed out</label>
<input type="checkbox" name="filter" value="inactive" id="rinactive"><label for="rinactive">timed out</label>
</div>
<div>
<input type="checkbox" name="filter" value="pending" id="rpending"><label
for="rpending">pending</label>
<input type="checkbox" name="filter" value="pending" id="rpending"><label for="rpending">pending</label>
</div>
<div>
<input type="checkbox" name="filter" value="lowbatt" id="rlowbatt"><label
for="rlowbatt">low battery</label>
<input type="checkbox" name="filter" value="lowbatt" id="rlowbatt"><label for="rlowbatt">low battery</label>
</div>
</div>
</div>
@@ -193,8 +177,7 @@
<div class="nextcheckin"></div>
<div class="nextupdate"></div>
<div class="corner">
<div class="pendingicon" title="A new message is waiting for the tag to pick up">
&circlearrowright;</div>
<div class="pendingicon" title="A new message is waiting for the tag to pick up">&circlearrowright;</div>
<div class="warningicon" title="This tag has not been seen for a long time">&#9888;
</div>
</div>
@@ -206,8 +189,7 @@
<div class="tabheader">
<div><img id="clearlog" src="data:image/gif;base64,R0lGODlhEAAQAPMAANXV1e3t7d/f39HR0dvb2/Hx8dTU1OLi4urq6mZmZpmZmf///wAAAAAAAAAAAAAAACH5BAEAAAwALAAAAAAQABAAAARBkMlJq71Yrp3ZXkr4WWCYnOZSgQVyEMYwJCq1nHhe20qgCAoA7QLyAYU7njE4JPV+zOSkCEUSFbmTVPPpbjvgTAQAOw==
"></div>
<div><input type="checkbox" id="showdebug" value="1"><label for="showdebug">Show all websocket
traffic</label></div>
<div><input type="checkbox" id="showdebug" value="1"><label for="showdebug">Show all websocket traffic</label></div>
</div>
<ul id="messages" class="messages">
</ul>
@@ -287,8 +269,12 @@
<option value="0" selected>EN English</option>
<option value="1">NL Nederlands</option>
<option value="2">DE Deutsch</option>
<option value="4">FR French</option>
<option value="3">NO Norwegian</option>
<option value="4">FR Français</option>
<option value="3">NO Norsk</option>
<option value="5">CZ Čeština</option>
<option value="6">SK Slovenčina</option>
<option value="7">PL Polski</option>
<option value="8">ES Español</option>
</select>
</p>
<p title="Depending on the content, a tag can sleep for
@@ -313,7 +299,8 @@
</select>
</p>
<p
title="Stops updates at night, and put the tags to sleep. During the configured night time, this overrides the maximum sleep time.">
title="Stops updates at night, and put the tags to sleep.
During the configured night time, this overrides the maximum sleep time.">
<label for="apcnight1">No updates between</label>
<select id="apcnight1"></select>
<span style="align-self:center;">and</span>
@@ -327,6 +314,17 @@
<option value="0">no</option>
</select>
</p>
<p
title="* Work in progress * When locking the tag inventory, the AP will only
show tags that are already in the database. For now, the AP will still keep answering
new tags, because that needs to be fixed in the radio firmware.
This will probably change in the future.">
<label for="apclock">Lock tag inventory</label>
<select id="apclock">
<option value="1">yes</option>
<option value="0" selected>no</option>
</select>
</p>
<p title="Wifi transmit power">
<label for="apcwifipower">Wifi power</label>
<select id="apcwifipower">
@@ -394,8 +392,7 @@
</p>
<h3>Manage</h3>
<p>
<button type="button" id="rebootbutton">Reboot AP</button> Saves the tagDB and instantly reboots the Access
Point
<button type="button" id="rebootbutton">Reboot AP</button> Saves the tagDB and instantly reboots the Access Point
</p>
<p>
<a href="/backup_db" id="downloadDBbutton">Download tagDB</a>
@@ -404,15 +401,12 @@
<button type="button" id="updatebutton" class="tablinks" data-target="updatetab" title="Update">Update</button> Manage firmware of the ESP32
</p>
<p>
<a href="/setup" target="setup" class="wifibutton">WiFi config</a> Opens a new window with WiFi
config options
<a href="/setup" target="setup" class="wifibutton">WiFi config</a> Opens a new window with WiFi config options
</p>
<p>
<br>
<a href="https://github.com/jjwbruijn/OpenEPaperLink" target="_new">Github
OpenEPaperLink</a><br>
<a href="https://github.com/jjwbruijn/OpenEPaperLink/wiki" target="_new">OpenEPaperLink
Wiki</a><br>
<a href="https://github.com/jjwbruijn/OpenEPaperLink" target="_new">Github OpenEPaperLink</a><br>
<a href="https://github.com/jjwbruijn/OpenEPaperLink/wiki" target="_new">OpenEPaperLink Wiki</a><br>
</p>
</div>
@@ -436,13 +430,16 @@
<h4>Other actions</h4>
<div>
<p>
<div id="rollbackOption" style="display:none"><button type="button" id="rollbackBtn">Rollback to previous
firmware</button></div>
<div id="rollbackOption" style="display:none">
<button type="button" id="rollbackBtn">Rollback to previous firmware</button>
</div>
</p>
<p>
<span id="c6Option">
<div id="updateC6Option"><button type="button" id="updateC6Btn">Update ESP32-C6</button> <input type="checkbox"
value="1" checked id="c6download"> download latest version</div>
<div id="updateC6Option">
<button type="button" id="updateC6Btn">Update ESP32-C6</button>
<input type="checkbox" value="1" checked id="c6download">download latest version
</div>
</span>
</p>
</div>
@@ -525,4 +522,4 @@
</body>
</html>
</html>

View File

@@ -590,6 +590,7 @@ document.addEventListener("loadTab", function (event) {
$("#apclatency").value = data.maxsleep;
$("#apcpreventsleep").value = data.stopsleep;
$("#apcpreview").value = data.preview;
$("#apclock").value = data.lock;
$("#apcwifipower").value = data.wifipower;
$("#apctimezone").value = data.timezone;
$("#apcnight1").value = data.sleeptime1;
@@ -614,6 +615,7 @@ $('#apcfgsave').onclick = function () {
formData.append('maxsleep', $('#apclatency').value);
formData.append('stopsleep', $('#apcpreventsleep').value);
formData.append('preview', $('#apcpreview').value);
formData.append('lock', $('#apclock').value);
formData.append('wifipower', $('#apcwifipower').value);
formData.append('timezone', $('#apctimezone').value);
formData.append('sleeptime1', $('#apcnight1').value);
@@ -858,7 +860,7 @@ function processQueue() {
return;
}
const { id, imageSrc } = imageQueue.shift();
const hwtype = $('#tag' + id).dataset.hwtype;
const hwtype = $('#tag' + id).dataset?.hwtype;
if (tagTypes[hwtype]?.busy) {
imageQueue.push({ id, imageSrc });
setTimeout(processQueue, 100);
@@ -1076,7 +1078,7 @@ async function getTagtype(hwtype) {
tagTypes[hwtype] = { busy: true };
const response = await fetch('/tagtypes/' + hwtype.toString(16).padStart(2, '0').toUpperCase() + '.json');
if (!response.ok) {
let data = { name: 'unknown id ' + hwtype, width: 0, height: 0, bpp: 0, rotatebuffer: 0, colortable: [], busy: false };
let data = { name: 'unknown id ' + hwtype.toString(16), width: 0, height: 0, bpp: 0, rotatebuffer: 0, colortable: [], busy: false };
tagTypes[hwtype] = data;
getTagtypeBusy = false;
return data;
@@ -1231,22 +1233,25 @@ $('#taglist').addEventListener('contextmenu', (e) => {
if (clickedGridItem) {
let mac = clickedGridItem.dataset.mac;
const hwtype = clickedGridItem.dataset.hwtype;
let contextMenuOptions = [
{ id: 'refresh', label: 'Force refresh' },
{ id: 'clear', label: 'Clear pending status' }
];
if (clickedGridItem.dataset.isexternal == "false") {
let contextMenuOptions = [];
if (tagTypes[hwtype]?.width > 0) {
contextMenuOptions.push(
{ id: 'scan', label: 'Scan channels' },
{ id: 'reboot', label: 'Reboot tag' },
);
};
if (tagTypes[hwtype].options?.includes("led")) {
contextMenuOptions.push(
{ id: 'ledflash', label: 'Flash the LED' },
{ id: 'ledflash_long', label: 'Flash the LED (long)' },
{ id: 'ledflash_stop', label: 'Stop flashing' }
{ id: 'refresh', label: 'Force refresh' },
{ id: 'clear', label: 'Clear pending status' }
);
if (clickedGridItem.dataset.isexternal == "false") {
contextMenuOptions.push(
{ id: 'scan', label: 'Scan channels' },
{ id: 'reboot', label: 'Reboot tag' },
);
};
if (tagTypes[hwtype]?.options?.includes("led")) {
contextMenuOptions.push(
{ id: 'ledflash', label: 'Flash the LED' },
{ id: 'ledflash_long', label: 'Flash the LED (long)' },
{ id: 'ledflash_stop', label: 'Stop flashing' }
);
}
}
contextMenuOptions.push(
{ id: 'del', label: 'Delete tag from list' }