mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-21 10:06:07 +01:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d08454fff | ||
|
|
f131b5ce84 | ||
|
|
33ba6a7aa7 | ||
|
|
75c6a6c0f9 | ||
|
|
f7e2025487 | ||
|
|
a91dd5c2a2 | ||
|
|
0ba287f734 | ||
|
|
ad52c64b94 | ||
|
|
8c06bb04f3 | ||
|
|
07807afe08 | ||
|
|
eb173e355f | ||
|
|
b792b71608 |
@@ -160,7 +160,7 @@ void watchdog_enable(int timeout) {
|
||||
|
||||
uint32_t sleepMsEntry = 0;
|
||||
uint32_t loops = 0;
|
||||
bool interruped = false;
|
||||
bool interrupted = false;
|
||||
|
||||
// uint8_t ledcfg[12] = {0b00100010,0x78,0b00100100,5,0x03,0b01000011,1,0xC2,0b1100001,10,10,0};
|
||||
// uint8_t ledcfg[12] = {0b00010010,0x7D,0,0,0x03,0xE8,0,0,0,0,0,0};
|
||||
@@ -185,14 +185,14 @@ void resettimer() {
|
||||
// tell the sleep function to net sleep again
|
||||
sleepMsEntry = sleepMsEntry - 999999999;
|
||||
loops = 0;
|
||||
interruped = true;
|
||||
interrupted = true;
|
||||
}
|
||||
|
||||
void flashled(uint8_t color, uint8_t brightnes) {
|
||||
void flashled(uint8_t color, uint8_t brightness) {
|
||||
uint8_t colorred = (color >> 5) & 0b00000111;
|
||||
uint8_t colorgreen = (color >> 2) & 0b00000111;
|
||||
uint8_t colorblue = color & 0b00000011;
|
||||
for (uint16_t i = 0; i < brightnes; i++) {
|
||||
for (uint16_t i = 0; i < brightness; i++) {
|
||||
digitalWrite(LED_RED, !(colorred >= 7));
|
||||
digitalWrite(LED_GREEN, !(colorgreen >= 7));
|
||||
digitalWrite(LED_BLUE, !(colorblue >= 3));
|
||||
@@ -248,11 +248,10 @@ void sleepwithinterrupts(uint32_t sleepinterval) {
|
||||
|
||||
void ledflashlogic(uint32_t ms) {
|
||||
watchdog_enable(ms + 1000);
|
||||
uint8_t brightnes = ledcfg[0] >> 4 & 0b00001111;
|
||||
uint8_t brightness = ledcfg[0] >> 4 & 0b00001111;
|
||||
uint8_t mode = ledcfg[0] & 0b00001111;
|
||||
// lets not blink for short delays
|
||||
if (ms < 2000) mode = 15;
|
||||
// if(mode == 0)sleepwithinterrupts(ms);
|
||||
if (mode == 1) {
|
||||
uint8_t color = ledcfg[1];
|
||||
uint32_t ledinerv = (ledcfg[2] << 24) + (ledcfg[3] << 16) + (ledcfg[4] << 8) + ledcfg[5];
|
||||
@@ -264,11 +263,12 @@ void ledflashlogic(uint32_t ms) {
|
||||
}
|
||||
if (sleepinterval > ms) sleepinterval = ms;
|
||||
for (uint32_t i = 0; i < loops; i++) {
|
||||
flashled(color, brightnes);
|
||||
flashled(color, brightness);
|
||||
sleepwithinterrupts(sleepinterval);
|
||||
}
|
||||
} else if (mode == 0) {
|
||||
interruped = false;
|
||||
}
|
||||
else if (mode == 0) {
|
||||
interrupted = false;
|
||||
uint8_t interloopdelayfactor = 100;
|
||||
u_int8_t loopdelayfactor = 100;
|
||||
uint8_t c1 = ledcfg[1];
|
||||
@@ -290,36 +290,40 @@ void ledflashlogic(uint32_t ms) {
|
||||
uint32_t looptimesum = fulllooptime1 + fulllooptime2 + fulllooptime3;
|
||||
int fittingrepeats = (int)ms / looptimesum;
|
||||
|
||||
grouprepeats = fittingrepeats;
|
||||
if (grouprepeats == 0) grouprepeats = 1;
|
||||
//catch edge case
|
||||
if (grouprepeats == 0) sleepwithinterrupts(ms);
|
||||
|
||||
for (int j = 0; j < grouprepeats; j++) {
|
||||
if (!interruped) {
|
||||
for (int j = 0; j < fittingrepeats; j++) {
|
||||
if(j > grouprepeats){
|
||||
brightness = 0;
|
||||
ledcfg[0] = 0xff;
|
||||
}
|
||||
if (!interrupted) {
|
||||
for (int i = 0; i < loopcnt1; i++) {
|
||||
flashled(c1, brightnes);
|
||||
flashled(c1, brightness);
|
||||
sleepwithinterrupts(loop1delay * loopdelayfactor);
|
||||
if (interruped) break;
|
||||
if (interrupted) break;
|
||||
}
|
||||
sleepwithinterrupts(ildelay1 * interloopdelayfactor);
|
||||
}
|
||||
if (!interruped) {
|
||||
if (!interrupted) {
|
||||
for (int i = 0; i < loopcnt2; i++) {
|
||||
flashled(c2, brightnes);
|
||||
flashled(c2, brightness);
|
||||
sleepwithinterrupts(loop2delay * loopdelayfactor);
|
||||
if (interruped) break;
|
||||
if (interrupted) break;
|
||||
}
|
||||
sleepwithinterrupts(ildelay2 * interloopdelayfactor);
|
||||
}
|
||||
|
||||
if (!interruped) {
|
||||
if (!interrupted) {
|
||||
for (int i = 0; i < loopcnt3; i++) {
|
||||
flashled(c3, brightnes);
|
||||
flashled(c3, brightness);
|
||||
sleepwithinterrupts(loop3delay * loopdelayfactor);
|
||||
if (interruped) break;
|
||||
if (interrupted) break;
|
||||
}
|
||||
sleepwithinterrupts(ildelay3 * interloopdelayfactor);
|
||||
}
|
||||
if (interruped) break;
|
||||
if (interrupted) break;
|
||||
}
|
||||
} else
|
||||
sleepwithinterrupts(ms);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -11,7 +11,7 @@ extern bool prepareDataAvail(String& filename, uint8_t dataType, uint8_t dataTyp
|
||||
extern void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP);
|
||||
extern void processXferComplete(struct espXferComplete* xfc, bool local);
|
||||
extern void processXferTimeout(struct espXferComplete* xfc, bool local);
|
||||
extern void processDataReq(struct espAvailDataReq* adr, bool local);
|
||||
extern void processDataReq(struct espAvailDataReq* adr, bool local, IPAddress remoteIP = IPAddress(0, 0, 0, 0));
|
||||
extern void processTagReturnData(struct espTagReturnData* trd, uint8_t len, bool local);
|
||||
|
||||
extern bool sendTagCommand(const uint8_t* dst, uint8_t cmd, bool local, const uint8_t* payload = nullptr);
|
||||
|
||||
@@ -33,6 +33,7 @@ class DynStorage {
|
||||
bool isInited;
|
||||
};
|
||||
|
||||
extern SemaphoreHandle_t fsMutex;
|
||||
extern DynStorage Storage;
|
||||
extern fs::FS *contentFS;
|
||||
extern void copyFile(File in, File out);
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
class tagRecord {
|
||||
public:
|
||||
tagRecord() : mac{0}, alias(""), lastseen(0), nextupdate(0), contentMode(0), pending(false), md5{0}, md5pending{0}, expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0), lastfullupdate(0), isExternal(false), pendingIdle(0), hasCustomLUT(false), rotate(0), lut(0), tagSoftwareVersion(0), currentChannel(0), dataType(0), filename(""), data(nullptr), len(0) {}
|
||||
tagRecord() : mac{0}, alias(""), lastseen(0), nextupdate(0), contentMode(0), pending(false), md5{0}, md5pending{0}, expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0), lastfullupdate(0), isExternal(false), apIp(IPAddress(0, 0, 0, 0)), pendingIdle(0), hasCustomLUT(false), rotate(0), lut(0), tagSoftwareVersion(0), currentChannel(0), dataType(0), filename(""), data(nullptr), len(0) {}
|
||||
|
||||
uint8_t mac[8];
|
||||
String alias;
|
||||
@@ -42,6 +42,7 @@ class tagRecord {
|
||||
uint8_t capabilities;
|
||||
uint32_t lastfullupdate;
|
||||
bool isExternal;
|
||||
IPAddress apIp;
|
||||
uint16_t pendingIdle;
|
||||
bool hasCustomLUT;
|
||||
uint8_t rotate;
|
||||
@@ -85,7 +86,6 @@ struct varStruct {
|
||||
bool changed;
|
||||
};
|
||||
|
||||
// extern SemaphoreHandle_t tagDBOwner;
|
||||
extern Config config;
|
||||
extern std::vector<tagRecord*> tagDB;
|
||||
extern std::unordered_map<int, HwType> hwtype;
|
||||
|
||||
@@ -123,5 +123,26 @@ static bool isSleeping(int sleeptime1, int sleeptime2) {
|
||||
}
|
||||
}
|
||||
|
||||
class Timer {
|
||||
public:
|
||||
Timer(unsigned long interval) : interval_(interval), previousMillis_(0) {}
|
||||
|
||||
void setInterval(unsigned long interval) {
|
||||
interval_ = interval;
|
||||
}
|
||||
|
||||
bool doRun() {
|
||||
unsigned long currentMillis = millis();
|
||||
if (currentMillis - previousMillis_ >= interval_) {
|
||||
previousMillis_ = currentMillis;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned long interval_;
|
||||
unsigned long previousMillis_;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
void init_web();
|
||||
void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
|
||||
void doJsonUpload(AsyncWebServerRequest *request);
|
||||
extern void networkProcess(void *parameter);
|
||||
void wsLog(String text);
|
||||
void wsErr(String text);
|
||||
void wsSendTaginfo(const uint8_t *mac, uint8_t syncMode);
|
||||
|
||||
@@ -148,9 +148,24 @@ void drawNew(const uint8_t mac[8], const bool buttonPressed, tagRecord *&taginfo
|
||||
memset(&wifimac[6], 0, 2);
|
||||
|
||||
const bool isAp = memcmp(mac, wifimac, 8) == 0;
|
||||
if ((taginfo->wakeupReason == WAKEUP_REASON_FIRSTBOOT || taginfo->wakeupReason == WAKEUP_REASON_WDT_RESET) && taginfo->contentMode == 0 && isAp) {
|
||||
taginfo->contentMode = 21;
|
||||
taginfo->nextupdate = 0;
|
||||
if ((taginfo->wakeupReason == WAKEUP_REASON_FIRSTBOOT || taginfo->wakeupReason == WAKEUP_REASON_WDT_RESET) && taginfo->contentMode == 0) {
|
||||
if (isAp) {
|
||||
taginfo->contentMode = 21;
|
||||
taginfo->nextupdate = 0;
|
||||
} else if (contentFS->exists("/tag_defaults.json")) {
|
||||
DynamicJsonDocument doc(1000);
|
||||
fs::File tagDefaults = contentFS->open("/tag_defaults.json", "r");
|
||||
DeserializationError err = deserializeJson(doc, tagDefaults);
|
||||
if (!err) {
|
||||
if (doc.containsKey("contentMode")) {
|
||||
taginfo->contentMode = doc["contentMode"];
|
||||
}
|
||||
if (doc.containsKey("modecfgjson")) {
|
||||
taginfo->modeConfigJson = doc["modecfgjson"].as<String>();
|
||||
}
|
||||
}
|
||||
tagDefaults.close();
|
||||
}
|
||||
}
|
||||
|
||||
char hexmac[17];
|
||||
@@ -393,7 +408,7 @@ void drawNew(const uint8_t mac[8], const bool buttonPressed, tagRecord *&taginfo
|
||||
if (!util::isEmptyOrNull(configFilename)) {
|
||||
String configUrl = cfgobj["url"].as<String>();
|
||||
if (!util::isEmptyOrNull(configUrl)) {
|
||||
StaticJsonDocument<1000> json;
|
||||
DynamicJsonDocument json(1000);
|
||||
Serial.println("Get json url + file");
|
||||
if (util::httpGetJson(configUrl, json, 1000)) {
|
||||
if (getJsonTemplateFileExtractVariables(filename, configFilename, json, taginfo, imageParams)) {
|
||||
@@ -676,7 +691,7 @@ void drawWeather(String &filename, JsonObject &cfgobj, const tagRecord *taginfo,
|
||||
units += "&temperature_unit=fahrenheit&windspeed_unit=mph";
|
||||
}
|
||||
|
||||
StaticJsonDocument<1000> doc;
|
||||
DynamicJsonDocument doc(1000);
|
||||
const bool success = util::httpGetJson("https://api.open-meteo.com/v1/forecast?latitude=" + lat + "&longitude=" + lon + "¤t_weather=true&windspeed_unit=ms&timezone=" + tz + units, doc, 5000);
|
||||
if (!success) {
|
||||
return;
|
||||
@@ -842,11 +857,15 @@ int getImgURL(String &filename, String URL, time_t fetched, imgParam &imageParam
|
||||
http.setTimeout(5000); // timeout in ms
|
||||
const int httpCode = http.GET();
|
||||
if (httpCode == 200) {
|
||||
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||
File f = contentFS->open("/temp/temp.jpg", "w");
|
||||
if (f) {
|
||||
http.writeToStream(&f);
|
||||
f.close();
|
||||
xSemaphoreGive(fsMutex);
|
||||
jpg2buffer("/temp/temp.jpg", filename, imageParams);
|
||||
} else {
|
||||
xSemaphoreGive(fsMutex);
|
||||
}
|
||||
} else {
|
||||
if (httpCode != 304) {
|
||||
@@ -1120,7 +1139,7 @@ void drawAPinfo(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgPa
|
||||
}
|
||||
|
||||
TFT_eSprite spr = TFT_eSprite(&tft);
|
||||
StaticJsonDocument<2048> loc;
|
||||
DynamicJsonDocument loc(2048);
|
||||
getTemplate(loc, 21, taginfo->hwType);
|
||||
|
||||
initSprite(spr, imageParams.width, imageParams.height, imageParams);
|
||||
@@ -1429,7 +1448,7 @@ void getLocation(JsonObject &cfgobj) {
|
||||
filter["results"][0]["latitude"] = true;
|
||||
filter["results"][0]["longitude"] = true;
|
||||
filter["results"][0]["timezone"] = true;
|
||||
StaticJsonDocument<1000> doc;
|
||||
DynamicJsonDocument doc(1000);
|
||||
if (util::httpGetJson("https://geocoding-api.open-meteo.com/v1/search?name=" + urlEncode(cfgobj["location"]) + "&count=1", doc, 5000, &filter)) {
|
||||
cfgobj["#lat"] = doc["results"][0]["latitude"].as<String>();
|
||||
cfgobj["#lon"] = doc["results"][0]["longitude"].as<String>();
|
||||
@@ -1493,7 +1512,7 @@ void prepareConfigFile(const uint8_t *dst, const JsonObject &config) {
|
||||
|
||||
void getTemplate(JsonDocument &json, const uint8_t id, const uint8_t hwtype) {
|
||||
StaticJsonDocument<80> filter;
|
||||
StaticJsonDocument<2048> doc;
|
||||
DynamicJsonDocument doc(2048);
|
||||
|
||||
const String idstr = String(id);
|
||||
constexpr const char *templateKey = "template";
|
||||
|
||||
@@ -125,6 +125,7 @@ bool downloadAndWriteBinary(String &filename, const char *url) {
|
||||
int binaryResponseCode = binaryHttp.GET();
|
||||
Serial.println(binaryResponseCode);
|
||||
if (binaryResponseCode == HTTP_CODE_OK) {
|
||||
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||
File file = contentFS->open(filename, "wb");
|
||||
if (file) {
|
||||
wsSerial("downloading " + String(filename));
|
||||
@@ -138,6 +139,7 @@ bool downloadAndWriteBinary(String &filename, const char *url) {
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
}
|
||||
file.close();
|
||||
xSemaphoreGive(fsMutex);
|
||||
binaryHttp.end();
|
||||
|
||||
file = contentFS->open(filename, "r");
|
||||
@@ -150,6 +152,7 @@ bool downloadAndWriteBinary(String &filename, const char *url) {
|
||||
file.close();
|
||||
}
|
||||
} else {
|
||||
xSemaphoreGive(fsMutex);
|
||||
wsSerial("file open error " + String(filename));
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -175,7 +175,7 @@ bool flasher::getInfoBlockType() {
|
||||
}
|
||||
|
||||
bool flasher::findTagByMD5() {
|
||||
StaticJsonDocument<3000> doc;
|
||||
DynamicJsonDocument doc(3000);
|
||||
DynamicJsonDocument APconfig(600);
|
||||
fs::File readfile = contentFS->open("/tag_md5_db.json", "r");
|
||||
DeserializationError err = deserializeJson(doc, readfile);
|
||||
@@ -205,7 +205,7 @@ bool flasher::findTagByMD5() {
|
||||
}
|
||||
|
||||
bool flasher::findTagByType(uint8_t type) {
|
||||
StaticJsonDocument<3000> doc;
|
||||
DynamicJsonDocument doc(3000);
|
||||
DynamicJsonDocument APconfig(600);
|
||||
fs::File readfile = contentFS->open("/tag_md5_db.json", "r");
|
||||
DeserializationError err = deserializeJson(doc, readfile);
|
||||
@@ -271,11 +271,13 @@ bool flasher::backupFlash() {
|
||||
getFirmwareMD5();
|
||||
if (!zbs->select_flash(0)) return false;
|
||||
md5char[16] = 0x00;
|
||||
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||
fs::File backup = contentFS->open("/" + (String)md5char + "_backup.bin", "w", true);
|
||||
for (uint32_t c = 0; c < 65535; c++) {
|
||||
backup.write(zbs->read_flash(c));
|
||||
}
|
||||
backup.close();
|
||||
xSemaphoreGive(fsMutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "storage.h"
|
||||
#include "system.h"
|
||||
#include "tag_db.h"
|
||||
#include "wifimanager.h"
|
||||
|
||||
#ifdef HAS_USB
|
||||
#include "usbflasher.h"
|
||||
@@ -21,6 +22,13 @@
|
||||
#include "util.h"
|
||||
#include "web.h"
|
||||
|
||||
util::Timer intervalSysinfo(3000);
|
||||
util::Timer intervalVars(10000);
|
||||
util::Timer intervalSaveDB(300000);
|
||||
util::Timer intervalContentRunner(1000);
|
||||
|
||||
SET_LOOP_TASK_STACK_SIZE(16 * 1024);
|
||||
|
||||
void pinTest();
|
||||
|
||||
void delayedStart(void* parameter) {
|
||||
@@ -33,32 +41,6 @@ void delayedStart(void* parameter) {
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void timeTask(void* parameter) {
|
||||
wsSendSysteminfo();
|
||||
util::printHeap();
|
||||
while (1) {
|
||||
unsigned long startMillis = millis();
|
||||
time_t now;
|
||||
time(&now);
|
||||
if (now % 5 == 0 || apInfo.state != AP_STATE_ONLINE || config.runStatus != RUNSTATUS_RUN) {
|
||||
wsSendSysteminfo();
|
||||
}
|
||||
if (now % 10 == 9 && config.runStatus != RUNSTATUS_STOP) {
|
||||
checkVars();
|
||||
}
|
||||
if (now % 300 == 7 && config.runStatus != RUNSTATUS_STOP) {
|
||||
saveDB("/current/tagDB.json");
|
||||
}
|
||||
if (apInfo.state == AP_STATE_ONLINE) {
|
||||
contentRunner();
|
||||
}
|
||||
|
||||
if (millis() - startMillis < 1000) {
|
||||
vTaskDelay((1000 - millis() + startMillis) / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.print(">\n");
|
||||
@@ -146,9 +128,7 @@ void setup() {
|
||||
#endif
|
||||
loadDB("/current/tagDB.json");
|
||||
cleanupCurrent();
|
||||
// tagDBOwner = xSemaphoreCreateMutex();
|
||||
xTaskCreate(APTask, "AP Process", 6000, NULL, 2, NULL);
|
||||
xTaskCreate(networkProcess, "Wifi", 6000, NULL, configMAX_PRIORITIES - 10, NULL);
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
|
||||
config.runStatus = RUNSTATUS_INIT;
|
||||
@@ -158,26 +138,39 @@ void setup() {
|
||||
config.runStatus = RUNSTATUS_PAUSE;
|
||||
}
|
||||
|
||||
xTaskCreate(timeTask, "timed tasks", 12000, NULL, 2, NULL);
|
||||
xTaskCreate(initTime, "init time", 5000, NULL, 2, NULL);
|
||||
xTaskCreate(delayedStart, "delaystart", 2000, NULL, 2, NULL);
|
||||
|
||||
wsSendSysteminfo();
|
||||
util::printHeap();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
vTaskDelay(10000 / portTICK_PERIOD_MS);
|
||||
// performDeviceFlash();
|
||||
while (1) {
|
||||
// pinTest();
|
||||
while (1) {
|
||||
ws.cleanupClients();
|
||||
wm.poll();
|
||||
|
||||
if (intervalSysinfo.doRun()) {
|
||||
wsSendSysteminfo();
|
||||
}
|
||||
if (intervalVars.doRun() && config.runStatus != RUNSTATUS_STOP) {
|
||||
checkVars();
|
||||
}
|
||||
if (intervalSaveDB.doRun() && config.runStatus != RUNSTATUS_STOP) {
|
||||
saveDB("/current/tagDB.json");
|
||||
}
|
||||
if (intervalContentRunner.doRun() && apInfo.state == AP_STATE_ONLINE) {
|
||||
contentRunner();
|
||||
}
|
||||
|
||||
#ifdef YELLOW_IPS_AP
|
||||
extern void yellow_ap_display_loop(void);
|
||||
yellow_ap_display_loop();
|
||||
#else
|
||||
vTaskDelay(10000 / portTICK_PERIOD_MS);
|
||||
// pinTest();
|
||||
extern void yellow_ap_display_loop(void);
|
||||
yellow_ap_display_loop();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef OPENEPAPERLINK_PCB
|
||||
time_t tagConnectTimer = 0;
|
||||
if (millis() - tagConnectTimer > 1000) {
|
||||
tagConnectTimer = millis();
|
||||
if (extTagConnected()) {
|
||||
flashCountDown(3);
|
||||
|
||||
@@ -190,7 +183,8 @@ void loop() {
|
||||
pinMode(FLASHER_EXT_TEST, INPUT);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
#endif
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
@@ -217,6 +217,7 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
|
||||
}
|
||||
#endif
|
||||
|
||||
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||
fs::File f_out = contentFS->open(fileout, "w");
|
||||
|
||||
switch (imageParams.bpp) {
|
||||
@@ -232,6 +233,8 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
|
||||
if (!buffer) {
|
||||
Serial.println("Failed to allocate buffer");
|
||||
util::printLargestFreeBlock();
|
||||
f_out.close();
|
||||
xSemaphoreGive(fsMutex);
|
||||
return;
|
||||
}
|
||||
spr2color(spr, imageParams, buffer, buffer_size, false);
|
||||
@@ -251,5 +254,6 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
|
||||
}
|
||||
|
||||
f_out.close();
|
||||
xSemaphoreGive(fsMutex);
|
||||
Serial.println("finished writing buffer " + String(millis() - t) + "ms");
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@ void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, const uint8
|
||||
} else {
|
||||
sendDataAvail(&pending);
|
||||
}
|
||||
|
||||
wsSendTaginfo(dst, SYNC_TAGSTATUS);
|
||||
}
|
||||
|
||||
@@ -240,18 +241,22 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
|
||||
http.begin(imageUrl);
|
||||
int httpCode = http.GET();
|
||||
if (httpCode == 200) {
|
||||
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||
File file = contentFS->open(filename, "w");
|
||||
http.writeToStream(&file);
|
||||
file.close();
|
||||
xSemaphoreGive(fsMutex);
|
||||
} else if (httpCode == 404) {
|
||||
imageUrl = "http://" + remoteIP.toString() + "/current/" + String(hexmac) + ".raw";
|
||||
http.end();
|
||||
http.begin(imageUrl);
|
||||
httpCode = http.GET();
|
||||
if (httpCode == 200) {
|
||||
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||
File file = contentFS->open(filename, "w");
|
||||
http.writeToStream(&file);
|
||||
file.close();
|
||||
xSemaphoreGive(fsMutex);
|
||||
}
|
||||
}
|
||||
http.end();
|
||||
@@ -430,7 +435,7 @@ void processXferTimeout(struct espXferComplete* xfc, bool local) {
|
||||
if (local) udpsync.netProcessXferTimeout(xfc);
|
||||
}
|
||||
|
||||
void processDataReq(struct espAvailDataReq* eadr, bool local) {
|
||||
void processDataReq(struct espAvailDataReq* eadr, bool local, IPAddress remoteIP) {
|
||||
if (config.runStatus == RUNSTATUS_STOP) return;
|
||||
char buffer[64];
|
||||
|
||||
@@ -450,13 +455,15 @@ void processDataReq(struct espAvailDataReq* eadr, bool local) {
|
||||
if (!local) {
|
||||
if (taginfo->isExternal == false) {
|
||||
wsLog("moved AP from local to external " + String(hexmac));
|
||||
taginfo->isExternal = true;
|
||||
}
|
||||
taginfo->isExternal = true;
|
||||
taginfo->apIp = remoteIP;
|
||||
} else {
|
||||
if (taginfo->isExternal == true) {
|
||||
wsLog("moved AP from external to local " + String(hexmac));
|
||||
taginfo->isExternal = false;
|
||||
}
|
||||
taginfo->isExternal = false;
|
||||
taginfo->apIp = IPAddress(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
if (taginfo->pendingIdle == 0) {
|
||||
@@ -508,9 +515,11 @@ void processDataReq(struct espAvailDataReq* eadr, bool local) {
|
||||
// sprintf(buffer, "<REMOTE ADR %02X%02X%02X%02X%02X%02X%02X%02X\n\0", eadr->src[7], eadr->src[6], eadr->src[5], eadr->src[4], eadr->src[3], eadr->src[2], eadr->src[1], eadr->src[0]);
|
||||
}
|
||||
|
||||
wsSendTaginfo(eadr->src, SYNC_TAGSTATUS);
|
||||
if (local) {
|
||||
wsSendTaginfo(eadr->src, SYNC_TAGSTATUS);
|
||||
udpsync.netProcessDataReq(eadr);
|
||||
} else {
|
||||
wsSendTaginfo(eadr->src, SYNC_NOSYNC);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -653,7 +662,7 @@ void updateTaginfoitem(struct TagInfo* taginfoitem, IPAddress remoteIP) {
|
||||
|
||||
char hexmac[17];
|
||||
mac2hex(taginfo->mac, hexmac);
|
||||
if (taginfo->contentMode != 12 && taginfoitem->contentMode != 12) {
|
||||
if (taginfo->contentMode != 12 && taginfoitem->contentMode != 12 && taginfoitem->contentMode != 0) {
|
||||
wsLog("Remote AP at " + remoteIP.toString() + " takes control over tag " + String(hexmac));
|
||||
taginfo->contentMode = 12;
|
||||
}
|
||||
@@ -712,11 +721,15 @@ bool checkMirror(struct tagRecord* taginfo, struct pendingData* pending) {
|
||||
} else {
|
||||
char dst_path[64];
|
||||
sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X.pending\0", taginfo2->mac[7], taginfo2->mac[6], taginfo2->mac[5], taginfo2->mac[4], taginfo2->mac[3], taginfo2->mac[2], taginfo2->mac[1], taginfo2->mac[0]);
|
||||
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||
File file = contentFS->open(dst_path, "w");
|
||||
if (file) {
|
||||
file.write(taginfo2->data, taginfo2->len);
|
||||
file.close();
|
||||
xSemaphoreGive(fsMutex);
|
||||
udpsync.netSendDataAvail(&pending2);
|
||||
} else {
|
||||
xSemaphoreGive(fsMutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,7 @@ void handleLittleFSUpload(AsyncWebServerRequest* request, String filename, size_
|
||||
} else {
|
||||
path = request->getParam("path", true)->value();
|
||||
Serial.println("update " + path);
|
||||
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||
request->_tempFile = contentFS->open(path, "w", true);
|
||||
}
|
||||
}
|
||||
@@ -105,6 +106,7 @@ void handleLittleFSUpload(AsyncWebServerRequest* request, String filename, size_
|
||||
}
|
||||
if (final) {
|
||||
request->_tempFile.close();
|
||||
xSemaphoreGive(fsMutex);
|
||||
if (error) {
|
||||
request->send(507, "text/plain", "Error. Disk full?");
|
||||
} else {
|
||||
@@ -155,7 +157,6 @@ void updateFirmware(const char* url, const char* expectedMd5, const size_t size)
|
||||
|
||||
config.runStatus = RUNSTATUS_STOP;
|
||||
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||
// xSemaphoreTake(tagDBOwner, portMAX_DELAY);
|
||||
saveDB("/current/tagDB.json");
|
||||
// destroyDB();
|
||||
|
||||
@@ -213,7 +214,6 @@ void updateFirmware(const char* url, const char* expectedMd5, const size_t size)
|
||||
httpClient.end();
|
||||
// loadDB("/current/tagDB.json");
|
||||
config.runStatus = RUNSTATUS_RUN;
|
||||
// xSemaphoreGive(tagDBOwner);
|
||||
}
|
||||
|
||||
void handleRollback(AsyncWebServerRequest* request) {
|
||||
@@ -303,7 +303,7 @@ void handleUpdateActions(AsyncWebServerRequest* request) {
|
||||
request->send(200, "No update actions needed");
|
||||
return;
|
||||
}
|
||||
StaticJsonDocument<1000> doc;
|
||||
DynamicJsonDocument doc(1000);
|
||||
DeserializationError error = deserializeJson(doc, file);
|
||||
const JsonArray deleteFiles = doc["deletefile"].as<JsonArray>();
|
||||
for (const auto& filePath : deleteFiles) {
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
DynStorage::DynStorage() : isInited(0) {}
|
||||
|
||||
SemaphoreHandle_t fsMutex;
|
||||
|
||||
static void initLittleFS() {
|
||||
LittleFS.begin();
|
||||
contentFS = &LittleFS;
|
||||
@@ -86,12 +88,15 @@ void copyBetweenFS(FS& sourceFS, const char* source_path, FS& targetFS) {
|
||||
|
||||
copyBetweenFS(sourceFS, file.path(), targetFS);
|
||||
} else {
|
||||
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||
File target = contentFS->open(file.path(), "w");
|
||||
if (target) {
|
||||
copyFile(file, target);
|
||||
target.close();
|
||||
file.close();
|
||||
xSemaphoreGive(fsMutex);
|
||||
} else {
|
||||
xSemaphoreGive(fsMutex);
|
||||
Serial.print("Couldn't create high target file");
|
||||
Serial.println(file.path());
|
||||
return;
|
||||
@@ -100,10 +105,14 @@ void copyBetweenFS(FS& sourceFS, const char* source_path, FS& targetFS) {
|
||||
file = root.openNextFile();
|
||||
}
|
||||
} else {
|
||||
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||
File target = contentFS->open(root.path(), "w");
|
||||
if (target) {
|
||||
copyFile(root, target);
|
||||
target.close();
|
||||
xSemaphoreGive(fsMutex);
|
||||
} else {
|
||||
xSemaphoreGive(fsMutex);
|
||||
Serial.print("Couldn't create target file ");
|
||||
Serial.println(root.path());
|
||||
return;
|
||||
@@ -120,6 +129,7 @@ void copyIfNeeded(const char* path) {
|
||||
#endif
|
||||
|
||||
void DynStorage::begin() {
|
||||
fsMutex = xSemaphoreCreateMutex();
|
||||
initLittleFS();
|
||||
|
||||
#ifdef HAS_SDCARD
|
||||
|
||||
@@ -19,7 +19,6 @@ std::unordered_map<int, HwType> hwdata = {
|
||||
{2, {400, 300, 0, 2}}};
|
||||
|
||||
Config config;
|
||||
// SemaphoreHandle_t tagDBOwner;
|
||||
|
||||
tagRecord* tagRecord::findByMAC(const uint8_t mac[8]) {
|
||||
for (tagRecord* tag : tagDB) {
|
||||
@@ -116,6 +115,7 @@ void fillNode(JsonObject& tag, const tagRecord* taginfo) {
|
||||
tag["capabilities"] = taginfo->capabilities;
|
||||
tag["modecfgjson"] = taginfo->modeConfigJson;
|
||||
tag["isexternal"] = taginfo->isExternal;
|
||||
tag["apip"] = taginfo->apIp.toString();
|
||||
tag["rotate"] = taginfo->rotate;
|
||||
tag["lut"] = taginfo->lut;
|
||||
tag["ch"] = taginfo->currentChannel;
|
||||
@@ -128,9 +128,11 @@ void saveDB(const String& filename) {
|
||||
const long t = millis();
|
||||
|
||||
Storage.begin();
|
||||
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||
fs::File file = contentFS->open(filename, "w");
|
||||
if (!file) {
|
||||
Serial.println("saveDB: Failed to open file");
|
||||
xSemaphoreGive(fsMutex);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -149,6 +151,7 @@ void saveDB(const String& filename) {
|
||||
file.write(']');
|
||||
|
||||
file.close();
|
||||
xSemaphoreGive(fsMutex);
|
||||
Serial.println("DB saved " + String(millis() - t) + "ms");
|
||||
}
|
||||
|
||||
@@ -168,7 +171,7 @@ void loadDB(const String& filename) {
|
||||
bool parsing = true;
|
||||
|
||||
if (readfile.find("[")) {
|
||||
StaticJsonDocument<1000> doc;
|
||||
DynamicJsonDocument doc(1000);
|
||||
while (parsing) {
|
||||
DeserializationError err = deserializeJson(doc, readfile);
|
||||
if (!err) {
|
||||
@@ -207,6 +210,7 @@ void loadDB(const String& filename) {
|
||||
taginfo->capabilities = tag["capabilities"];
|
||||
taginfo->modeConfigJson = tag["modecfgjson"].as<String>();
|
||||
taginfo->isExternal = tag["isexternal"].as<bool>();
|
||||
taginfo->apIp.fromString(tag["apip"].as<String>());
|
||||
taginfo->rotate = tag["rotate"] | 0;
|
||||
taginfo->lut = tag["lut"] | 0;
|
||||
taginfo->currentChannel = tag["ch"] | 0;
|
||||
@@ -313,6 +317,7 @@ void initAPconfig() {
|
||||
}
|
||||
|
||||
void saveAPconfig() {
|
||||
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||
fs::File configFile = contentFS->open("/current/apconfig.json", "w");
|
||||
DynamicJsonDocument APconfig(500);
|
||||
APconfig["channel"] = config.channel;
|
||||
@@ -328,6 +333,7 @@ void saveAPconfig() {
|
||||
APconfig["sleeptime2"] = config.sleepTime2;
|
||||
serializeJsonPretty(APconfig, configFile);
|
||||
configFile.close();
|
||||
xSemaphoreGive(fsMutex);
|
||||
}
|
||||
|
||||
HwType getHwType(const uint8_t id) {
|
||||
|
||||
@@ -50,7 +50,7 @@ void UDPcomm::processPacket(AsyncUDPPacket packet) {
|
||||
espAvailDataReq adr;
|
||||
memset(&adr, 0, sizeof(espAvailDataReq));
|
||||
memcpy(&adr, &packet.data()[1], std::min(packet.length() - 1, sizeof(espAvailDataReq)));
|
||||
processDataReq(&adr, false);
|
||||
processDataReq(&adr, false, senderIP);
|
||||
break;
|
||||
}
|
||||
case PKT_XFER_COMPLETE: {
|
||||
|
||||
@@ -35,15 +35,6 @@ WifiManager wm;
|
||||
SemaphoreHandle_t wsMutex;
|
||||
uint32_t lastssidscan = 0;
|
||||
|
||||
void networkProcess(void *parameter) {
|
||||
wsMutex = xSemaphoreCreateMutex();
|
||||
while (true) {
|
||||
ws.cleanupClients();
|
||||
wm.poll();
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void wsLog(String text) {
|
||||
StaticJsonDocument<250> doc;
|
||||
doc["logMsg"] = text;
|
||||
@@ -77,20 +68,22 @@ void wsSendSysteminfo() {
|
||||
time_t now;
|
||||
time(&now);
|
||||
static int freeSpaceLastRun = 0;
|
||||
static size_t tagDBsize = 0;
|
||||
static size_t freeSpace = Storage.freeSpace();
|
||||
sys["currtime"] = now;
|
||||
sys["heap"] = ESP.getFreeHeap();
|
||||
sys["recordcount"] = tagDB.size();
|
||||
sys["recordcount"] = tagDBsize;
|
||||
sys["dbsize"] = dbSize();
|
||||
if (millis() - freeSpaceLastRun > 30000) {
|
||||
if (millis() - freeSpaceLastRun > 30000 || freeSpaceLastRun == 0) {
|
||||
freeSpace = Storage.freeSpace();
|
||||
tagDBsize = tagDB.size();
|
||||
freeSpaceLastRun = millis();
|
||||
}
|
||||
sys["littlefsfree"] = freeSpace;
|
||||
sys["apstate"] = apInfo.state;
|
||||
sys["runstate"] = config.runStatus;
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32)
|
||||
sys["temp"] = temperatureRead();
|
||||
// sys["temp"] = temperatureRead();
|
||||
#endif
|
||||
sys["rssi"] = WiFi.RSSI();
|
||||
sys["wifistatus"] = WiFi.status();
|
||||
@@ -188,6 +181,7 @@ uint8_t wsClientCount() {
|
||||
}
|
||||
|
||||
void init_web() {
|
||||
wsMutex = xSemaphoreCreateMutex();
|
||||
Storage.begin();
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
@@ -322,6 +316,7 @@ void init_web() {
|
||||
}
|
||||
if (strcmp(cmdValue, "ledflash") == 0) {
|
||||
struct ledFlash flashData = {0};
|
||||
flashData.mode = 1;
|
||||
flashData.flashDuration = 8;
|
||||
flashData.color1 = 0x3C; // green
|
||||
flashData.color2 = 0xE4; // red
|
||||
@@ -338,6 +333,7 @@ void init_web() {
|
||||
}
|
||||
if (strcmp(cmdValue, "ledflash_long") == 0) {
|
||||
struct ledFlash flashData = {0};
|
||||
flashData.mode = 1;
|
||||
flashData.flashDuration = 15;
|
||||
flashData.color1 = 0xE4; // red
|
||||
flashData.flashCount1 = 5;
|
||||
@@ -347,6 +343,12 @@ void init_web() {
|
||||
const uint8_t *payload = reinterpret_cast<const uint8_t *>(&flashData);
|
||||
sendTagCommand(mac, CMD_DO_LEDFLASH, !taginfo->isExternal, payload);
|
||||
}
|
||||
if (strcmp(cmdValue, "ledflash_stop") == 0) {
|
||||
struct ledFlash flashData = {0};
|
||||
flashData.mode = 0;
|
||||
const uint8_t *payload = reinterpret_cast<const uint8_t *>(&flashData);
|
||||
sendTagCommand(mac, CMD_DO_LEDFLASH, !taginfo->isExternal, payload);
|
||||
}
|
||||
request->send(200, "text/plain", "Ok, done");
|
||||
} else {
|
||||
request->send(400, "text/plain", "Error: mac not found");
|
||||
@@ -360,6 +362,7 @@ void init_web() {
|
||||
server.on("/led_flash", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
// color picker: https://roger-random.github.io/RGB332_color_wheel_three.js/
|
||||
// http GET to /led_flash?mac=000000000000&pattern=3/0x1C,4,5/0xE0,3,1/0x4F,5,10/5
|
||||
// http://192.168.178.198/led_flash?mac=00007E1F250CB29C&pattern=1/0x1C,1,15/0xE0,1,15/0x4F,1,15/1
|
||||
// (flashDuration/color1,flashCount1,delay1/color2,flashCount2,delay2/color3,flashCount3,delay3/repeats)
|
||||
if (request->hasParam("mac")) {
|
||||
String dst = request->getParam("mac")->value();
|
||||
@@ -597,6 +600,9 @@ void init_web() {
|
||||
request->send(404);
|
||||
});
|
||||
|
||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
|
||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "content-type");
|
||||
|
||||
server.begin();
|
||||
}
|
||||
|
||||
@@ -611,6 +617,7 @@ void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index
|
||||
} else {
|
||||
filename = "unknown.jpg";
|
||||
}
|
||||
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||
request->_tempFile = contentFS->open("/" + filename, "w");
|
||||
}
|
||||
if (len) {
|
||||
@@ -618,6 +625,7 @@ void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index
|
||||
}
|
||||
if (final) {
|
||||
request->_tempFile.close();
|
||||
xSemaphoreGive(fsMutex);
|
||||
if (request->hasParam("mac", true)) {
|
||||
String dst = request->getParam("mac", true)->value();
|
||||
uint8_t mac[8];
|
||||
@@ -656,13 +664,16 @@ void doJsonUpload(AsyncWebServerRequest *request) {
|
||||
String dst = request->getParam("mac", true)->value();
|
||||
uint8_t mac[8];
|
||||
if (hex2mac(dst, mac)) {
|
||||
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||
File file = LittleFS.open("/current/" + dst + ".json", "w");
|
||||
if (!file) {
|
||||
request->send(400, "text/plain", "Failed to create file");
|
||||
xSemaphoreGive(fsMutex);
|
||||
return;
|
||||
}
|
||||
file.print(request->getParam("json", true)->value());
|
||||
file.close();
|
||||
xSemaphoreGive(fsMutex);
|
||||
tagRecord *taginfo = tagRecord::findByMAC(mac);
|
||||
if (taginfo != nullptr) {
|
||||
uint32_t ttl = 0;
|
||||
|
||||
@@ -215,7 +215,6 @@ void WifiManager::pollSerial() {
|
||||
}
|
||||
}
|
||||
|
||||
// temporary write some more debug info
|
||||
void WifiManager::WiFiEvent(WiFiEvent_t event) {
|
||||
Serial.printf("[WiFi-event %d] ", event);
|
||||
String eventname="";
|
||||
@@ -259,8 +258,8 @@ void WifiManager::WiFiEvent(WiFiEvent_t event) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
terminalLog(eventname);
|
||||
logLine("WiFi event [" + String(event) + "]: " + eventname);
|
||||
if (eventname) terminalLog(eventname);
|
||||
// logLine("WiFi event [" + String(event) + "]: " + eventname);
|
||||
}
|
||||
|
||||
// *** Improv
|
||||
|
||||
@@ -8,15 +8,419 @@
|
||||
<title>Open EPaper Link Access Point</title>
|
||||
<link rel="stylesheet" href="main.css" type="text/css" />
|
||||
<link rel="icon" type="image/vnd.icon" href="favicon.ico">
|
||||
<link rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<div class="logo">Open EPaper Link Access Point</div>
|
||||
<nav>
|
||||
<div>
|
||||
<!-- tabs -->
|
||||
<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="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>
|
||||
<!-- /tabs -->
|
||||
<div><span id="runstate"></div>
|
||||
<div><span id="apstatecolor">⬤</span> <span id="apstate">loading</span></div>
|
||||
<div><a href="/edit" target="littlefs" class="filebutton material-symbols-outlined">folder_open</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<div id="configbox">
|
||||
<div class="closebtn">✖</div>
|
||||
<form>
|
||||
<div class="container">
|
||||
|
||||
<div class="window">
|
||||
|
||||
<div id="hometab" class="tabcontent">
|
||||
<table>
|
||||
<tr onclick="setFilterAndShow('')">
|
||||
<td class="material-symbols-outlined" style="color:#239f26" id="dashboardStatusIcon">
|
||||
check_circle
|
||||
</td>
|
||||
<td id="dashboardStatus" style="color:#239f26" colspan="2">
|
||||
initialising...
|
||||
</td>
|
||||
</tr>
|
||||
<tr onclick="setFilterAndShow('')">
|
||||
<td class="material-symbols-outlined" style="color:#77239e">
|
||||
sell
|
||||
</td>
|
||||
<td>
|
||||
tags
|
||||
</td>
|
||||
<td id="dashboardTagCount" style="color:#77239e">
|
||||
0
|
||||
</td>
|
||||
</tr>
|
||||
<tr onclick="setFilterAndShow('pending')">
|
||||
<td class="material-symbols-outlined" style="color:#235f9e">
|
||||
hourglass_empty
|
||||
</td>
|
||||
<td>
|
||||
pending data
|
||||
</td>
|
||||
<td id="dashboardPending" style="color:#235f9e">
|
||||
0
|
||||
</td>
|
||||
</tr>
|
||||
<tr onclick="setFilterAndShow('lowbatt')">
|
||||
<td class="material-symbols-outlined" style="color:#9e9223">
|
||||
battery_low
|
||||
</td>
|
||||
<td>
|
||||
low battery
|
||||
</td>
|
||||
<td id="dashboardLowBatt" style="color:#9e9223">
|
||||
0
|
||||
</td>
|
||||
</tr>
|
||||
<tr onclick="setFilterAndShow('inactive')">
|
||||
<td class="material-symbols-outlined" style="color:#9e2323">
|
||||
signal_disconnected
|
||||
</td>
|
||||
<td>
|
||||
timeout
|
||||
</td>
|
||||
<td id="dashboardTimeout" style="color:#9e2323">
|
||||
0
|
||||
</td>
|
||||
</tr>
|
||||
<!--
|
||||
<tr onclick="$(`[data-target='aptab']`).click()">
|
||||
<td class="material-symbols-outlined" style="color:#239f26">
|
||||
cell_tower
|
||||
</td>
|
||||
<td>
|
||||
access points
|
||||
</td>
|
||||
<td id="dashboardApCount" style="color:#239f26">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
-->
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<div id="filterOptions">
|
||||
<div>
|
||||
<div>group by</div>
|
||||
<div>
|
||||
<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>
|
||||
</div>
|
||||
<div>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>sort by</div>
|
||||
<div>
|
||||
<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>
|
||||
</div>
|
||||
<div>
|
||||
<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>
|
||||
</div>
|
||||
<div>
|
||||
<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>
|
||||
</div>
|
||||
<div>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="taglist" class="taglist">
|
||||
<div class="tagcard" id="tagtemplate">
|
||||
<div class="currimg"><canvas class="tagimg"></div>
|
||||
<div class="mac"></div>
|
||||
<div class="alias"></div>
|
||||
<div class="model"></div>
|
||||
<div class="received"></div>
|
||||
<div class="contentmode"></div>
|
||||
<div class="lastseen"></div>
|
||||
<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">
|
||||
↻</div>
|
||||
<div class="warningicon" title="This tag has not been seen for a long time">⚠
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="logtab" class="tabcontent">
|
||||
<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>
|
||||
<ul id="messages" class="messages">
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="aptab" class="tabcontent">
|
||||
<h3>Active access points</h3>
|
||||
|
||||
<div id="aplist">
|
||||
<div id="apcard" class="apcard">
|
||||
<p class="apip">194.109.6.66</p>
|
||||
<p class="apalias">AP kitchen</p>
|
||||
<div>
|
||||
<span class="material-symbols-outlined">
|
||||
sell
|
||||
</span>
|
||||
<span class="aptagcount">13</span>
|
||||
<span class="material-symbols-outlined space">
|
||||
cell_tower
|
||||
</span>
|
||||
<span class="apchannel">25</span>
|
||||
</div>
|
||||
<p class="apswversion">
|
||||
fetching software version...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="templatetab" class="tabcontent">
|
||||
Work in progress...
|
||||
</div>
|
||||
|
||||
<div id="configtab" class="tabcontent">
|
||||
<h3>Access Point config</h3>
|
||||
<p>
|
||||
<label for="apcfgalias">Alias</label>
|
||||
<input id="apcfgalias" type="text">
|
||||
</p>
|
||||
<p>
|
||||
<label for="apcfgchid">Channel</label>
|
||||
<select id="apcfgchid">
|
||||
<option value="0" selected>auto</option>
|
||||
<option value="11">11</option>
|
||||
<option value="15">15</option>
|
||||
<option value="20">20</option>
|
||||
<option value="25">25</option>
|
||||
<option value="26">26</option>
|
||||
<option value="27">27</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<label for="apcfgledbrightness">LED brightness</label>
|
||||
<select id="apcfgledbrightness">
|
||||
<option value="-1">off</option>
|
||||
<option value="20">10%</option>
|
||||
<option value="64">25%</option>
|
||||
<option value="128" selected>50%</option>
|
||||
<option value="192">75%</option>
|
||||
<option value="255">100%</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<label for="apcfglanguage">Content language</label>
|
||||
<select id="apcfglanguage">
|
||||
<option value="0" selected>EN English</option>
|
||||
<option value="1">NL Nederlands</option>
|
||||
<option value="2">DE Deutsch</option>
|
||||
</select>
|
||||
</p>
|
||||
<p title="Depending on the content, a tag can sleep for
|
||||
longer periods when no updates are expected
|
||||
(like a date display). This setting specifies
|
||||
the maximum sleep time.">
|
||||
<label for="apclatency">Maximum sleep</label>
|
||||
<select id="apclatency">
|
||||
<option value="0" selected>shortest (40 sec)</option>
|
||||
<option value="5">5 minutes</option>
|
||||
<option value="10">10 minute</option>
|
||||
<option value="30">30 minutes</option>
|
||||
<option value="60">1 hour</option>
|
||||
</select>
|
||||
</p>
|
||||
<p title="If connected to the website, don't sleep extra.
|
||||
Latency will be around 40 seconds.">
|
||||
<label for="apcpreventsleep">Shorten latency during config</label>
|
||||
<select id="apcpreventsleep">
|
||||
<option value="0">no</option>
|
||||
<option value="1" selected>yes</option>
|
||||
</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.">
|
||||
<label for="apcnight1">No updates between</label>
|
||||
<select id="apcnight1"></select>
|
||||
<span style="align-self:center;">and</span>
|
||||
<select id="apcnight2"></select>
|
||||
</p>
|
||||
<p title="Turn off preview images on the webpage if you want to manage many tags,
|
||||
to save file system space">
|
||||
<label for="apcpreview">Preview images</label>
|
||||
<select id="apcpreview">
|
||||
<option value="1" selected>yes</option>
|
||||
<option value="0">no</option>
|
||||
</select>
|
||||
</p>
|
||||
<p title="Wifi transmit power">
|
||||
<label for="apcwifipower">Wifi power</label>
|
||||
<select id="apcwifipower">
|
||||
<option value="78">19.5 dBm</option>
|
||||
<option value="76">19.0 dBm</option>
|
||||
<option value="74">18.5 dBm</option>
|
||||
<option value="68">17.0 dBm</option>
|
||||
<option value="60">15.0 dBm</option>
|
||||
<option value="52">13.0 dBm</option>
|
||||
<option value="44">11.0 dBm</option>
|
||||
<option value="34" selected>8.5 dBm</option>
|
||||
<option value="28">7.0 dBm</option>
|
||||
<option value="20">5.0 dBm</option>
|
||||
<option value="8">2.0 dBm</option>
|
||||
</select>
|
||||
</p>
|
||||
<p title="Your local time zone">
|
||||
<label for="apctimezone">Local time zone</label>
|
||||
<select id="apctimezone">
|
||||
<optgroup label="Europe">
|
||||
<option value="CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00" selected>Central European
|
||||
Time</option>
|
||||
<option value="EET-2EEST-3,M3.5.0/03:00:00,M10.5.0/04:00:00">Athens, Greece</option>
|
||||
<option value="GMT+0IST-1,M3.5.0/01:00:00,M10.5.0/02:00:00">Dublin, Ireland</option>
|
||||
<option value="EET-2EEST-3,M3.5.0/03:00:00,M10.5.0/04:00:00">Helsinki, Finland</option>
|
||||
<option value="WET-0WEST-1,M3.5.0/01:00:00,M10.5.0/02:00:00">Lisbon, Portugal</option>
|
||||
<option value="GMT+0BST-1,M3.5.0/01:00:00,M10.5.0/02:00:00">London, Great Britain
|
||||
</option>
|
||||
<option value="EET-2EEST,M3.5.0/3,M10.5.0/4">Kyiv, Ukraine</option>
|
||||
</optgroup>
|
||||
<optgroup label="USA / Canada">
|
||||
<option value="HAW10">Hawaii Time</option>
|
||||
<option value="AKST9AKDT">Alaska Time</option>
|
||||
<option value="PST8PDT">Pacific Time</option>
|
||||
<option value="MST7MDT">Mountain Time</option>
|
||||
<option value="MST7">Arizona, no DST</option>
|
||||
<option value="CST6CDT">Central Time</option>
|
||||
<option value="EST5EDT">Eastern Time</option>
|
||||
</optgroup>
|
||||
<optgroup label="Australia / New Zealand">
|
||||
<option value="EST-10EDT-11,M10.5.0/02:00:00,M3.5.0/03:00:00">Melbourne, Sydney</option>
|
||||
<option value="WST-8">Perth</option>
|
||||
<option value="EST-10">Brisbane</option>
|
||||
<option value="CST-9:30CDT-10:30,M10.5.0/02:00:00,M3.5.0/03:00:00">Adelaide</option>
|
||||
<option value="CST-9:30">Darwin</option>
|
||||
<option value="EST-10EDT-11,M10.1.0/02:00:00,M3.5.0/03:00:00">Hobart</option>
|
||||
<option value="NZST-12NZDT-13,M9.4.0/02:00:00,M4.1.0/03:00:00">New Zealand</option>
|
||||
</optgroup>
|
||||
<optgroup label="Asia">
|
||||
<option value="JST-9">Tokyo</option>
|
||||
<option value="WIB-7">Jakarta</option>
|
||||
<option value="GMT+2">Jerusalem</option>
|
||||
<option value="SGT-8">Singapore</option>
|
||||
<option value="ULAT-8ULAST,M3.5.0/2,M9.5.0/2">Ulaanbaatar, Mongolia</option>
|
||||
</optgroup>
|
||||
<optgroup label="Central and South America">
|
||||
<option value="BRST+3BRDT+2,M10.3.0,M2.3.0">Brazil, Sao Paulo</option>
|
||||
<option value="UTC+3">Argentina</option>
|
||||
<option value="CST+6">Central America</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<input type="button" value="Save" id="apcfgsave"><span id="apcfgmsg"></span>
|
||||
</p>
|
||||
<h3>Manage</h3>
|
||||
<p>
|
||||
<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>
|
||||
</p>
|
||||
<p>
|
||||
<button id="updatebutton">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
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<footer class="logbox">
|
||||
<p>
|
||||
<span> </span>
|
||||
<span id="sysinfo"></span>
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
<!-- -->
|
||||
|
||||
<dialog id="configbox">
|
||||
<div class="closebtn2">✖</div>
|
||||
<h3 id="cfgmac">00000000</h3>
|
||||
<p>
|
||||
<label for="cfgalias">Alias</label>
|
||||
@@ -50,183 +454,33 @@
|
||||
<button id="cfgscan">scan</button>
|
||||
<button id="cfgdeepsleep">deep sleep</button>
|
||||
<button id="cfgreset">reset settings</button>
|
||||
<button id="cfgdelete" title="remove"><img src="data:image/gif;base64,R0lGODlhEAAQAPMAANXV1e3t7d/f39HR0dvb2/Hx8dTU1OLi4urq6mZmZpmZmf///wAAAAAAAAAAAAAAACH5BAEAAAwALAAAAAAQABAAAARBkMlJq71Yrp3ZXkr4WWCYnOZSgQVyEMYwJCq1nHhe20qgCAoA7QLyAYU7njE4JPV+zOSkCEUSFbmTVPPpbjvgTAQAOw== "></button>
|
||||
<button id="cfgdelete" title="remove"><img
|
||||
src="data:image/gif;base64,R0lGODlhEAAQAPMAANXV1e3t7d/f39HR0dvb2/Hx8dTU1OLi4urq6mZmZpmZmf///wAAAAAAAAAAAAAAACH5BAEAAAwALAAAAAAQABAAAARBkMlJq71Yrp3ZXkr4WWCYnOZSgQVyEMYwJCq1nHhe20qgCAoA7QLyAYU7njE4JPV+zOSkCEUSFbmTVPPpbjvgTAQAOw== "></button>
|
||||
</p>
|
||||
</div>
|
||||
<p id="savebar">
|
||||
<span><input type="button" value="Save" id="cfgsave"></span>
|
||||
<span id="cfgmore" title="advanced options">▼</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div id="apconfigbox">
|
||||
<div class="closebtn">✖</div>
|
||||
<h3>Access Point config</h3>
|
||||
<p>
|
||||
<label for="apcfgalias">Alias</label>
|
||||
<input id="apcfgalias" type="text">
|
||||
</p>
|
||||
<p>
|
||||
<label for="apcfgchid">Channel</label>
|
||||
<select id="apcfgchid">
|
||||
<option value="0" selected>auto</option>
|
||||
<option value="11">11</option>
|
||||
<option value="15">15</option>
|
||||
<option value="20">20</option>
|
||||
<option value="25">25</option>
|
||||
<option value="26">26</option>
|
||||
<option value="27">27</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<label for="apcfgledbrightness">LED brightness</label>
|
||||
<select id="apcfgledbrightness">
|
||||
<option value="-1">off</option>
|
||||
<option value="20">10%</option>
|
||||
<option value="64">25%</option>
|
||||
<option value="128" selected>50%</option>
|
||||
<option value="192">75%</option>
|
||||
<option value="255">100%</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<label for="apcfglanguage">Content language</label>
|
||||
<select id="apcfglanguage">
|
||||
<option value="0" selected>EN English</option>
|
||||
<option value="1">NL Nederlands</option>
|
||||
<option value="2">DE Deutsch</option>
|
||||
</select>
|
||||
</p>
|
||||
<p title="Depending on the content, a tag can sleep for
|
||||
longer periods when no updates are expected
|
||||
(like a date display). This setting specifies
|
||||
the maximum sleep time.">
|
||||
<label for="apclatency">Maximum sleep</label>
|
||||
<select id="apclatency">
|
||||
<option value="0" selected>shortest (40 sec)</option>
|
||||
<option value="5">5 minutes</option>
|
||||
<option value="10">10 minute</option>
|
||||
<option value="30">30 minutes</option>
|
||||
<option value="60">1 hour</option>
|
||||
</select>
|
||||
</p>
|
||||
<p title="If connected to the website, don't sleep extra.
|
||||
Latency will be around 40 seconds.">
|
||||
<label for="apcpreventsleep">Shorten latency during config</label>
|
||||
<select id="apcpreventsleep">
|
||||
<option value="0">no</option>
|
||||
<option value="1" selected>yes</option>
|
||||
</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.">
|
||||
<label for="apcnight1">No updates between</label>
|
||||
<select id="apcnight1"></select>
|
||||
<span style="align-self:center;">and</span>
|
||||
<select id="apcnight2"></select>
|
||||
</p>
|
||||
<p title="Turn off preview images on the webpage if you want to manage many tags,
|
||||
to save file system space">
|
||||
<label for="apcpreview">Preview images</label>
|
||||
<select id="apcpreview">
|
||||
<option value="1" selected>yes</option>
|
||||
<option value="0">no</option>
|
||||
</select>
|
||||
</p>
|
||||
<p title="Wifi transmit power">
|
||||
<label for="apcwifipower">Wifi power</label>
|
||||
<select id="apcwifipower">
|
||||
<option value="78">19.5 dBm</option>
|
||||
<option value="76">19.0 dBm</option>
|
||||
<option value="74">18.5 dBm</option>
|
||||
<option value="68">17.0 dBm</option>
|
||||
<option value="60">15.0 dBm</option>
|
||||
<option value="52">13.0 dBm</option>
|
||||
<option value="44">11.0 dBm</option>
|
||||
<option value="34" selected>8.5 dBm</option>
|
||||
<option value="28">7.0 dBm</option>
|
||||
<option value="20">5.0 dBm</option>
|
||||
<option value="8">2.0 dBm</option>
|
||||
</select>
|
||||
</p>
|
||||
<p title="Your local time zone">
|
||||
<label for="apctimezone">Local time zone</label>
|
||||
<select id="apctimezone">
|
||||
<optgroup label="Europe">
|
||||
<option value="CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00" selected>Central European Time</option>
|
||||
<option value="EET-2EEST-3,M3.5.0/03:00:00,M10.5.0/04:00:00">Athens, Greece</option>
|
||||
<option value="GMT+0IST-1,M3.5.0/01:00:00,M10.5.0/02:00:00">Dublin, Ireland</option>
|
||||
<option value="EET-2EEST-3,M3.5.0/03:00:00,M10.5.0/04:00:00">Helsinki, Finland</option>
|
||||
<option value="WET-0WEST-1,M3.5.0/01:00:00,M10.5.0/02:00:00">Lisbon, Portugal</option>
|
||||
<option value="GMT+0BST-1,M3.5.0/01:00:00,M10.5.0/02:00:00">London, Great Britain</option>
|
||||
<option value="EET-2EEST,M3.5.0/3,M10.5.0/4">Kyiv, Ukraine</option>
|
||||
</optgroup>
|
||||
<optgroup label="USA / Canada">
|
||||
<option value="HAW10">Hawaii Time</option>
|
||||
<option value="AKST9AKDT">Alaska Time</option>
|
||||
<option value="PST8PDT">Pacific Time</option>
|
||||
<option value="MST7MDT">Mountain Time</option>
|
||||
<option value="MST7">Arizona, no DST</option>
|
||||
<option value="CST6CDT">Central Time</option>
|
||||
<option value="EST5EDT">Eastern Time</option>
|
||||
</optgroup>
|
||||
<optgroup label="Australia / New Zealand">
|
||||
<option value="EST-10EDT-11,M10.5.0/02:00:00,M3.5.0/03:00:00">Melbourne, Sydney</option>
|
||||
<option value="WST-8">Perth</option>
|
||||
<option value="EST-10">Brisbane</option>
|
||||
<option value="CST-9:30CDT-10:30,M10.5.0/02:00:00,M3.5.0/03:00:00">Adelaide</option>
|
||||
<option value="CST-9:30">Darwin</option>
|
||||
<option value="EST-10EDT-11,M10.1.0/02:00:00,M3.5.0/03:00:00">Hobart</option>
|
||||
<option value="NZST-12NZDT-13,M9.4.0/02:00:00,M4.1.0/03:00:00">New Zealand</option>
|
||||
</optgroup>
|
||||
<optgroup label="Asia">
|
||||
<option value="JST-9">Tokyo</option>
|
||||
<option value="WIB-7">Jakarta</option>
|
||||
<option value="GMT+2">Jerusalem</option>
|
||||
<option value="SGT-8">Singapore</option>
|
||||
<option value="ULAT-8ULAST,M3.5.0/2,M9.5.0/2">Ulaanbaatar, Mongolia</option>
|
||||
</optgroup>
|
||||
<optgroup label="Central and South America">
|
||||
<option value="BRST+3BRDT+2,M10.3.0,M2.3.0">Brazil, Sao Paulo</option>
|
||||
<option value="UTC+3">Argentina</option>
|
||||
<option value="CST+6">Central America</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<input type="button" value="Save" id="apcfgsave">
|
||||
</p>
|
||||
<p>
|
||||
Active access points:<br>
|
||||
<table id="aptable">
|
||||
<tr>
|
||||
<th>ip</th>
|
||||
<th>alias</th>
|
||||
<th>tags</th>
|
||||
<th>ch</th>
|
||||
<th>AP ver</th>
|
||||
</tr>
|
||||
</table>
|
||||
</p>
|
||||
<p>
|
||||
<span id="rebootbutton">reboot AP</span>
|
||||
<a href="/backup_db" id="downloadDBbutton">download tagDB</a>
|
||||
<span id="updatebutton">update</span>
|
||||
<a href="/setup" target="setup" class="filebutton">WiFi config</a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://github.com/jjwbruijn/OpenEPaperLink" target="_new">Github OpenEPaperLink</a>
|
||||
</p>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<div id="apupdatebox">
|
||||
<div class="closebtn">✖</div>
|
||||
<h3>Update dashboard</h3>
|
||||
<div id="easyupdate"></div>
|
||||
<div id="advanceddiv">
|
||||
<!--<div>
|
||||
repo: <input type="text" name="repo"> <button id="switchRepo">Switch</button><br>
|
||||
environment: <span id="environment">environment</span>
|
||||
</div>-->
|
||||
<div id="releasetable"></div>
|
||||
<div>
|
||||
<div id="rollbackOption" style="display:none"><button id="rollbackBtn">Rollback to previous firmware</button></div>
|
||||
<span id="c6Option"><div id="updateC6Option"><button id="updateC6Btn">Update ESP32-C6</button> <input type="checkbox" value="1" checked id="c6download"> download latest version</div></span>
|
||||
<div id="rollbackOption" style="display:none"><button id="rollbackBtn">Rollback to previous
|
||||
firmware</button></div>
|
||||
<span id="c6Option">
|
||||
<div id="updateC6Option"><button id="updateC6Btn">Update ESP32-C6</button> <input type="checkbox"
|
||||
value="1" checked id="c6download"> download latest version</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -235,84 +489,8 @@ to save file system space">
|
||||
style="display: none; position: absolute; background: white; border: 1px solid gray; padding: 0; list-style: none;">
|
||||
</ul>
|
||||
|
||||
<form>
|
||||
<div class="container">
|
||||
|
||||
<div class="window">
|
||||
|
||||
<div class="actionbox">
|
||||
<div>
|
||||
<div>Currently active tags: <button class="filebutton" id="toggleFilters">arrange</button></div>
|
||||
<div><span id="temp"></div>
|
||||
<div><span id="runstate"></div>
|
||||
<div><span id="apstatecolor">⬤</span> <span id="apstate">loading</span></div>
|
||||
<div><span id="apconfigbutton">AP config</span></div>
|
||||
<div><a href="/edit" target="littlefs" class="filebutton">File System</a></div>
|
||||
</div>
|
||||
<div id="filterOptions">
|
||||
<div>
|
||||
<div>group by</div>
|
||||
<div><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></div>
|
||||
<div><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></div>
|
||||
</div>
|
||||
<div>
|
||||
<div>sort by</div>
|
||||
<div><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></div>
|
||||
<div><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">only local</label></div>
|
||||
<div><input type="checkbox" name="filter" value="remote" id="rremote"><label for="rremote">only remote</label></div>
|
||||
<div><input type="checkbox" name="filter" value="inactive" id="rinactive"><label for="rinactive">only inactive</label></div>
|
||||
<div><input type="checkbox" name="filter" value="pending" id="rpending"><label for="rpending">only pending</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="taglist" class="taglist">
|
||||
<div class="tagcard" id="tagtemplate">
|
||||
<div class="currimg"><canvas class="tagimg"></div>
|
||||
<div class="mac"></div>
|
||||
<div class="alias"></div>
|
||||
<div class="model"></div>
|
||||
|
||||
<div class="received"></div>
|
||||
|
||||
<div class="contentmode"></div>
|
||||
<div class="lastseen"></div>
|
||||
<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">↻</div>
|
||||
<div class="warningicon" title="This tag has not been seen for a long time">⚠</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="logbox">
|
||||
<p>
|
||||
<span>logging</span>
|
||||
<span><img id="clearlog" src="data:image/gif;base64,R0lGODlhEAAQAPMAANXV1e3t7d/f39HR0dvb2/Hx8dTU1OLi4urq6mZmZpmZmf///wAAAAAAAAAAAAAAACH5BAEAAAwALAAAAAAQABAAAARBkMlJq71Yrp3ZXkr4WWCYnOZSgQVyEMYwJCq1nHhe20qgCAoA7QLyAYU7njE4JPV+zOSkCEUSFbmTVPPpbjvgTAQAOw==
|
||||
"></span>
|
||||
<span id="sysinfo"></span>
|
||||
</p>
|
||||
<ul id="messages" class="messages">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script src="main.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,7 +1,7 @@
|
||||
* {
|
||||
margin:0;
|
||||
padding:0;
|
||||
border:0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
list-style-type: none;
|
||||
outline: none;
|
||||
font-weight: 400;
|
||||
@@ -12,7 +12,8 @@
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
html, body {
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -24,60 +25,263 @@ body {
|
||||
}
|
||||
|
||||
header {
|
||||
height: 50px;
|
||||
background-color: #646260;
|
||||
z-index: 999;
|
||||
position: sticky;
|
||||
top: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
label {
|
||||
width: 120px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
padding: 3px 0px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 0 auto;
|
||||
height: 50px;
|
||||
text-indent: 50px;
|
||||
overflow:hidden;
|
||||
font-size: 2.5em;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.window {
|
||||
margin: 0 auto;
|
||||
max-width: 94%;
|
||||
}
|
||||
|
||||
.actionbox {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.actionbox>div:first-child>div:first-child {
|
||||
nav>div:first-child>div:first-child {
|
||||
flex-grow: 2;
|
||||
}
|
||||
|
||||
.actionbox>div {
|
||||
display:flex;
|
||||
nav>div {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
padding: 10px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.actionbox label {
|
||||
nav label {
|
||||
padding: 0px 5px;
|
||||
vertical-align: text-bottom;
|
||||
width: auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#rebootbutton, #updatebutton, #downloadDBbutton, #apconfigbutton, .filebutton {
|
||||
padding: 2px 5px;
|
||||
background-color: #cccccc;
|
||||
footer {
|
||||
padding: 5px;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: white;
|
||||
|
||||
#sysinfo {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 0 auto;
|
||||
height: 50px;
|
||||
text-indent: 50px;
|
||||
overflow: hidden;
|
||||
font-size: 2.5em;
|
||||
color: white;
|
||||
}
|
||||
|
||||
h3 {
|
||||
padding-bottom: 10px;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
/* tabs */
|
||||
|
||||
.tab-container {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.tablinks {
|
||||
background-color: #f2f2f2;
|
||||
padding: 5px 10px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background-color: #ddd;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-container .active {
|
||||
background-color: #ccc;
|
||||
margin-top: 10px;
|
||||
margin-bottom: -10px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
.tabcontent {
|
||||
display: none;
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
|
||||
|
||||
label {
|
||||
width: 200px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
padding: 4px 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-bottom: 20px;
|
||||
;
|
||||
}
|
||||
|
||||
#hometab {
|
||||
font-size: 24px;
|
||||
|
||||
& table {
|
||||
margin: 20px;
|
||||
background: #fff;
|
||||
padding: 5px 20px;
|
||||
border-spacing: 0px;
|
||||
}
|
||||
|
||||
& td {
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
& td:nth-child(3) {
|
||||
text-align: right;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
& tr:hover {
|
||||
background-color: #ccc;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
& .material-symbols-outlined {
|
||||
font-size: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.tagheader {
|
||||
width: 100%;
|
||||
padding: 10px 10px;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
justify-content: space-between;
|
||||
|
||||
& #toggleFilters {
|
||||
padding: 0px 10px;
|
||||
}
|
||||
|
||||
& h3 {
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
& #activefilter {
|
||||
flex-grow: 2;
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
|
||||
.tabheader {
|
||||
background-color: white;
|
||||
padding: 5px 10px;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
|
||||
#filterOptions {
|
||||
background-color: white;
|
||||
max-height: 0;
|
||||
padding: 0px 10px;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease-in-out;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
|
||||
&.active {
|
||||
padding: 10px 10px;
|
||||
max-height: 100px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
& label {
|
||||
width: inherit;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
padding: 0px 5px;
|
||||
}
|
||||
}
|
||||
|
||||
#aptab,
|
||||
#configtab {
|
||||
padding: 10px;
|
||||
|
||||
& p {
|
||||
padding: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
#aplist {
|
||||
display: flex;
|
||||
gap: 1em;
|
||||
flex-flow: wrap;
|
||||
}
|
||||
|
||||
.apcard {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
width: 300px;
|
||||
|
||||
& .apalias {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
& .space {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
& div {
|
||||
display: flex;
|
||||
font-size: 2.0em;
|
||||
align-items: center;
|
||||
gap: 0.2em;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#apcard {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#apcfgsave {
|
||||
margin: 20px 205px;
|
||||
}
|
||||
|
||||
.filebutton {
|
||||
width: inherit;
|
||||
padding: 5px 10px;
|
||||
margin-bottom: 0px;
|
||||
margin-top: -1px;
|
||||
background-color: #ccc;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
|
||||
&:hover {
|
||||
background-color: #aaa;
|
||||
}
|
||||
}
|
||||
|
||||
#rebootbutton,
|
||||
#updatebutton,
|
||||
#downloadDBbutton,
|
||||
.wifibutton {
|
||||
padding: 4px 5px;
|
||||
background-color: #ccc;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
width: 120px;
|
||||
margin: 2px 5px 2px 20px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
|
||||
&:hover {
|
||||
background-color: #aaa;
|
||||
}
|
||||
}
|
||||
|
||||
.columns div {
|
||||
@@ -100,15 +304,17 @@ input {
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
input[type=button], button {
|
||||
input[type=button],
|
||||
button {
|
||||
border: 0px;
|
||||
padding: 4px 10px;
|
||||
cursor:pointer;
|
||||
cursor: pointer;
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
input[type=button]:hover,
|
||||
button:hover {
|
||||
background-color:#aaaaaa;
|
||||
background-color: #aaaaaa;
|
||||
}
|
||||
|
||||
select {
|
||||
@@ -117,7 +323,8 @@ select {
|
||||
border: solid 1px #cccccc;
|
||||
}
|
||||
|
||||
#configbox, #apconfigbox, #apupdatebox {
|
||||
#apconfigbox,
|
||||
#apupdatebox {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 65px;
|
||||
@@ -131,19 +338,35 @@ select {
|
||||
max-height: calc(100vh - 75px);
|
||||
}
|
||||
|
||||
#configbox p, #apconfigbox p, #apupdatebox p {
|
||||
#configbox {
|
||||
margin: auto;
|
||||
min-width: 380px;
|
||||
padding: 15px;
|
||||
background-color: #f0e6d3;
|
||||
z-index: 999;
|
||||
box-shadow: 7px 10px 52px -19px rgba(0, 0, 0, 0.63);
|
||||
overflow: auto;
|
||||
max-height: calc(100vh - 75px);
|
||||
}
|
||||
|
||||
#configbox p,
|
||||
#apconfigbox p,
|
||||
#apupdatebox p {
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
#configbox h3, #apconfigbox h3, #apupdatebox h3 {
|
||||
#configbox h3,
|
||||
#apconfigbox h3,
|
||||
#apupdatebox h3 {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#configbox input, #apconfigbox input {
|
||||
#configbox input,
|
||||
#apconfigbox input {
|
||||
border: solid 1px #cccccc;
|
||||
}
|
||||
|
||||
@@ -191,31 +414,6 @@ select {
|
||||
background-color: #e6f0d3;
|
||||
}
|
||||
|
||||
#aptable {
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
#aptable th {
|
||||
text-align: left;
|
||||
background-color: #00000020;
|
||||
padding: 0px 3px;
|
||||
}
|
||||
|
||||
#aptable th, #aptable td {
|
||||
border-right: 1px solid #000010;
|
||||
padding: 0px 3px;
|
||||
}
|
||||
|
||||
#aptable td:nth-child(1), #aptable th:nth-child(1) {
|
||||
border-left: 1px solid #000010;
|
||||
}
|
||||
|
||||
#aptable td:nth-child(3),
|
||||
#aptable td:nth-child(4) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#apupdatebox {
|
||||
background-color: #f0d0c8;
|
||||
width: 700px;
|
||||
@@ -228,7 +426,8 @@ select {
|
||||
padding: 2px 10px;
|
||||
}
|
||||
|
||||
.closebtn {
|
||||
.closebtn,
|
||||
.closebtn2 {
|
||||
border: 1px solid black;
|
||||
float: right;
|
||||
width: 19px;
|
||||
@@ -239,22 +438,21 @@ select {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.logbox {
|
||||
margin: 5px;
|
||||
}
|
||||
#logtab {
|
||||
& img {
|
||||
vertical-align: bottom;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.logbox p {
|
||||
background-color: #ffffff;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
& label {
|
||||
width: inherit;
|
||||
vertical-align: top;
|
||||
padding: 4px 10px;
|
||||
}
|
||||
|
||||
.logbox img {
|
||||
vertical-align: bottom;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.logbox #sysinfo {
|
||||
float: right;
|
||||
& input {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
}
|
||||
|
||||
.blink-red {
|
||||
@@ -265,10 +463,11 @@ select {
|
||||
.taglist {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0px 10px 30px 10px;
|
||||
}
|
||||
|
||||
#tagtemplate {
|
||||
display:none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tagcard {
|
||||
@@ -280,12 +479,12 @@ select {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #cccccc;
|
||||
transition: box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.tagcard:hover {
|
||||
cursor:pointer;
|
||||
box-shadow: 7px 10px 52px -19px rgba(0, 0, 0, 0.63);
|
||||
filter: brightness(1.02);
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
box-shadow: 7px 10px 52px -19px rgba(0, 0, 0, 0.63);
|
||||
filter: brightness(1.02);
|
||||
}
|
||||
}
|
||||
|
||||
.tagflash {
|
||||
@@ -305,30 +504,19 @@ select {
|
||||
margin: 0px 5px;
|
||||
}
|
||||
|
||||
#filterOptions {
|
||||
max-height: 0;
|
||||
padding: 0 10px;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease-in-out, padding 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
#filterOptions.active {
|
||||
max-height: 100px;
|
||||
padding: 10px 10px;
|
||||
}
|
||||
|
||||
.currimg {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.currimg img, .currimg canvas {
|
||||
.currimg img,
|
||||
.currimg canvas {
|
||||
max-width: 50px;
|
||||
border: 1px solid #c0c0c0;
|
||||
}
|
||||
|
||||
.mac {
|
||||
font-size: 0.9em;
|
||||
cursor:pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.alias {
|
||||
@@ -344,6 +532,7 @@ select {
|
||||
font-size: .85em;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.received div {
|
||||
display: inline-block;
|
||||
}
|
||||
@@ -354,16 +543,19 @@ select {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.lastseen, .nextcheckin, .nextupdate {
|
||||
.lastseen,
|
||||
.nextcheckin,
|
||||
.nextupdate {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.lastseen span,
|
||||
.nextcheckin span,
|
||||
.nextupdate span {
|
||||
width:105px;
|
||||
display:inline-block;
|
||||
width: 105px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.corner {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
@@ -383,7 +575,7 @@ select {
|
||||
}
|
||||
|
||||
.warningicon {
|
||||
display:none;
|
||||
display: none;
|
||||
font-size: 1.3em;
|
||||
background-color: yellow;
|
||||
color: black;
|
||||
@@ -412,12 +604,24 @@ ul.messages li.new {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-family: monospace;
|
||||
word-break: break-all;
|
||||
background-color: #666;
|
||||
color: #ccc;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.quote {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#paintbutton {
|
||||
padding: 1px 3px;
|
||||
border: 1px solid black;
|
||||
font-size: 1.3em;
|
||||
vertical-align: top;
|
||||
margin-left:12px;
|
||||
margin-left: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -472,6 +676,7 @@ ul.messages li.new {
|
||||
background-color: #dddddd;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
#buttonbar button {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
@@ -513,7 +718,7 @@ ul.messages li.new {
|
||||
|
||||
/* updatescreens */
|
||||
|
||||
#easyupdate{
|
||||
#easyupdate {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
@@ -578,7 +783,7 @@ ul.messages li.new {
|
||||
.console {
|
||||
width: 100%;
|
||||
background-color: black;
|
||||
font-family: 'lucida console','ui-monospace';
|
||||
font-family: 'lucida console', 'ui-monospace';
|
||||
color: white;
|
||||
padding: 5px 10px;
|
||||
margin: 20px 0px;
|
||||
@@ -587,6 +792,7 @@ ul.messages li.new {
|
||||
overflow-y: scroll;
|
||||
white-space: break-spaces;
|
||||
}
|
||||
|
||||
.console div {
|
||||
word-break: break-all;
|
||||
}
|
||||
@@ -594,36 +800,62 @@ ul.messages li.new {
|
||||
/* media */
|
||||
|
||||
@media(max-width: 460px) {
|
||||
.messages li div, ul.messages li div.date, ul.messages li div.message {
|
||||
display:block;
|
||||
position:relative;
|
||||
|
||||
.messages li div,
|
||||
ul.messages li div.date,
|
||||
ul.messages li div.message {
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
left: auto;
|
||||
}
|
||||
.messages li div.message, li.pending {
|
||||
|
||||
.messages li div.message,
|
||||
li.pending {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
ul.messages {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes new {
|
||||
0% { background-color: rgba(255, 255, 204, 1); }
|
||||
50% { background-color: rgba(255, 255, 204, .5); }
|
||||
100% { background-color: rgba(255, 255, 204, 0); }
|
||||
0% {
|
||||
background-color: rgba(255, 255, 204, 1);
|
||||
}
|
||||
|
||||
50% {
|
||||
background-color: rgba(255, 255, 204, .5);
|
||||
}
|
||||
|
||||
100% {
|
||||
background-color: rgba(255, 255, 204, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes tagflash {
|
||||
0% { opacity: 1; }
|
||||
50% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pending {
|
||||
0% { }
|
||||
50% { background-color: #d4d4f5;}
|
||||
100% { }
|
||||
0% {}
|
||||
|
||||
50% {
|
||||
background-color: #d4d4f5;
|
||||
}
|
||||
|
||||
100% {}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
@@ -638,7 +870,7 @@ ul.messages li.new {
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.logbox #sysinfo {
|
||||
footer #sysinfo {
|
||||
float: none;
|
||||
display: block;
|
||||
}
|
||||
@@ -682,4 +914,4 @@ ul.messages li.new {
|
||||
padding: 1px 1px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@ const WAKEUP_REASON_NETWORK_SCAN = 0xFD;
|
||||
const WAKEUP_REASON_WDT_RESET = 0xFE;
|
||||
|
||||
let tagTypes = {};
|
||||
let apConfig = {};
|
||||
let tagDB = {};
|
||||
|
||||
const apstate = [
|
||||
{ state: "offline", color: "red" },
|
||||
@@ -38,7 +40,22 @@ let socket;
|
||||
let finishedInitialLoading = false;
|
||||
let getTagtypeBusy = false;
|
||||
|
||||
const loadConfig = new Event("loadConfig");
|
||||
window.addEventListener("loadConfig", function () {
|
||||
fetch("/get_ap_config")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
apConfig = data;
|
||||
|
||||
if (data.alias) {
|
||||
$(".logo").innerHTML = data.alias;
|
||||
this.document.title = data.alias;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener("load", function () {
|
||||
initTabs();
|
||||
fetch('/content_cards.json')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
@@ -51,19 +68,38 @@ window.addEventListener("load", function () {
|
||||
console.error('Error:', error);
|
||||
alert("I can't load /www/content_cards.json.\r\nHave you upload it to the data partition?");
|
||||
});
|
||||
fetch("/get_ap_config")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.alias) {
|
||||
$(".logo").innerHTML = data.alias;
|
||||
this.document.title = data.alias;
|
||||
}
|
||||
});
|
||||
|
||||
window.dispatchEvent(loadConfig);
|
||||
|
||||
dropUpload();
|
||||
populateTimes($('#apcnight1'));
|
||||
populateTimes($('#apcnight2'));
|
||||
});
|
||||
|
||||
/* tabs */
|
||||
let activeTab = '';
|
||||
function initTabs() {
|
||||
const tabLinks = document.querySelectorAll(".tablinks");
|
||||
const tabContents = document.querySelectorAll(".tabcontent");
|
||||
|
||||
tabLinks.forEach(tabLink => {
|
||||
tabLink.addEventListener("click", function () {
|
||||
const targetId = this.getAttribute("data-target");
|
||||
const loadTabEvent = new CustomEvent('loadTab', { detail: targetId });
|
||||
document.dispatchEvent(loadTabEvent);
|
||||
tabContents.forEach(tabContent => {
|
||||
tabContent.style.display = "none";
|
||||
});
|
||||
tabLinks.forEach(link => {
|
||||
link.classList.remove("active");
|
||||
});
|
||||
document.getElementById(targetId).style.display = "block";
|
||||
this.classList.add("active");
|
||||
});
|
||||
});
|
||||
tabLinks[0].click();
|
||||
};
|
||||
|
||||
function loadTags(pos) {
|
||||
fetch("/get_db?pos=" + pos)
|
||||
.then(response => response.json())
|
||||
@@ -83,7 +119,10 @@ function connect() {
|
||||
});
|
||||
|
||||
socket.addEventListener("message", (event) => {
|
||||
// console.log(event.data)
|
||||
if ($('#showdebug').checked) {
|
||||
showMessage(event.data);
|
||||
console.log(event.data);
|
||||
}
|
||||
const msg = JSON.parse(event.data);
|
||||
if (msg.logMsg) {
|
||||
showMessage(msg.logMsg, false);
|
||||
@@ -104,17 +143,12 @@ function connect() {
|
||||
$("#apstatecolor").style.color = apstate[msg.sys.apstate].color;
|
||||
$("#apstate").innerHTML = apstate[msg.sys.apstate].state;
|
||||
$("#runstate").innerHTML = runstate[msg.sys.runstate].state;
|
||||
if (msg.sys.temp) $("#temp").innerHTML = msg.sys.temp.toFixed(1) + '°C';
|
||||
$('#dashboardStatus').innerHTML = apstate[msg.sys.apstate].state;
|
||||
}
|
||||
servertimediff = (Date.now() / 1000) - msg.sys.currtime;
|
||||
}
|
||||
if (msg.apitem) {
|
||||
let row = $("#aptable").insertRow();
|
||||
row.insertCell(0).innerHTML = "<a href=\"http://" + msg.apitem.ip + "\" target=\"_new\">" + msg.apitem.ip + "</a>";
|
||||
row.insertCell(1).innerHTML = msg.apitem.alias;
|
||||
row.insertCell(2).innerHTML = msg.apitem.count;
|
||||
row.insertCell(3).innerHTML = msg.apitem.channel;
|
||||
row.insertCell(4).innerHTML = msg.apitem.version;
|
||||
populateAPCard(msg.apitem);
|
||||
}
|
||||
if (msg.console) {
|
||||
if (otamodule && typeof (otamodule.print) === "function") {
|
||||
@@ -146,6 +180,7 @@ function convertSize(bytes) {
|
||||
function processTags(tagArray) {
|
||||
for (const element of tagArray) {
|
||||
const tagmac = element.mac;
|
||||
tagDB[tagmac] = element;
|
||||
|
||||
let div = $('#tag' + tagmac);
|
||||
if (div == null) {
|
||||
@@ -207,13 +242,18 @@ function processTags(tagArray) {
|
||||
$('#tag' + tagmac + ' .received').style.opacity = "0";
|
||||
}
|
||||
|
||||
if (element.contentMode == 20) {
|
||||
$('#tag' + tagmac + ' .tagimg').style.display = 'none';
|
||||
} else if (div.dataset.hash != element.hash && div.dataset.hwtype > -1 && (!element.isexternal || element.contentMode != 12)) {
|
||||
loadImage(tagmac, '/current/' + tagmac + '.raw?' + element.hash);
|
||||
if (!apConfig.preview || element.contentMode == 20) {
|
||||
$('#tag' + tagmac + ' .tagimg').style.display = 'none'
|
||||
} else if (div.dataset.hash != element.hash && div.dataset.hwtype > -1) {
|
||||
let cachetag = element.hash;
|
||||
if (element.hash == '00000000000000000000000000000000') cachetag = Math.random();
|
||||
if (element.isexternal && element.contentMode == 12) {
|
||||
loadImage(tagmac, 'http://' + tagDB[tagmac].apip + '/current/' + tagmac + '.raw?' + cachetag);
|
||||
} else {
|
||||
loadImage(tagmac, '/current/' + tagmac + '.raw?' + cachetag);
|
||||
}
|
||||
div.dataset.hash = element.hash;
|
||||
}
|
||||
if (element.isexternal && element.contentMode == 12) $('#tag' + tagmac + ' .tagimg').style.display = 'none';
|
||||
|
||||
if (element.nextupdate > 1672531200 && element.nextupdate != 3216153600) {
|
||||
const date = new Date(element.nextupdate * 1000);
|
||||
@@ -284,9 +324,16 @@ function processTags(tagArray) {
|
||||
|
||||
function updatecards() {
|
||||
if (servertimediff > 1000000000) servertimediff = 0;
|
||||
let tagcount = 0;
|
||||
let pendingcount = 0;
|
||||
let timeoutcount = 0;
|
||||
let lowbattcount = 0;
|
||||
|
||||
$('#taglist').querySelectorAll('[data-mac]').forEach(item => {
|
||||
let tagmac = item.dataset.mac;
|
||||
|
||||
tagcount++;
|
||||
if (tagDB[tagmac].pending) pendingcount++;
|
||||
if (tagDB[tagmac].batteryMv < 2400 && tagDB[tagmac].batteryMv != 0 && tagDB[tagmac].batteryMv != 1337) lowbattcount++;
|
||||
if (item.dataset.lastseen && item.dataset.lastseen > (Date.now() / 1000) - servertimediff - 30 * 24 * 3600 * 60) {
|
||||
let idletime = (Date.now() / 1000) - servertimediff - item.dataset.lastseen;
|
||||
$('#tag' + tagmac + ' .lastseen').innerHTML = "<span>last seen</span>" + displayTime(Math.floor(idletime)) + " ago";
|
||||
@@ -294,6 +341,7 @@ function updatecards() {
|
||||
$('#tag' + tagmac + ' .warningicon').style.display = 'inline-block';
|
||||
$('#tag' + tagmac).classList.remove("tagpending")
|
||||
$('#tag' + tagmac).style.background = '#e0e0a0';
|
||||
timeoutcount++;
|
||||
}
|
||||
if (idletime > 24 * 3600) {
|
||||
$('#tag' + tagmac).style.opacity = '.5';
|
||||
@@ -314,6 +362,11 @@ function updatecards() {
|
||||
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "";
|
||||
}
|
||||
})
|
||||
|
||||
$('#dashboardTagCount').innerHTML = tagcount;
|
||||
$('#dashboardPending').innerHTML = pendingcount;
|
||||
$('#dashboardLowBatt').innerHTML = lowbattcount;
|
||||
$('#dashboardTimeout').innerHTML = timeoutcount;
|
||||
}
|
||||
|
||||
$('#clearlog').onclick = function () {
|
||||
@@ -327,6 +380,13 @@ document.querySelectorAll('.closebtn').forEach(button => {
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.closebtn2').forEach(button => {
|
||||
button.addEventListener('click', (event) => {
|
||||
event.target.parentNode.close();
|
||||
$('#advancedoptions').style.height = '0px';
|
||||
});
|
||||
});
|
||||
|
||||
//clicking on a tag: load config dialog for tag
|
||||
$('#taglist').addEventListener("click", (event) => {
|
||||
let currentElement = event.target;
|
||||
@@ -365,7 +425,7 @@ function loadContentCard(mac) {
|
||||
$('#cfgrotate').value = tagdata.rotate;
|
||||
$('#cfglut').value = tagdata.lut;
|
||||
$('#cfgmore').innerHTML = '▼';
|
||||
$('#configbox').style.display = 'block';
|
||||
$('#configbox').showModal();
|
||||
})
|
||||
}
|
||||
|
||||
@@ -427,7 +487,7 @@ $('#cfgsave').onclick = function () {
|
||||
.catch(error => showMessage('Error: ' + error));
|
||||
|
||||
$('#advancedoptions').style.height = '0px';
|
||||
$('#configbox').style.display = 'none';
|
||||
$('#configbox').close();
|
||||
}
|
||||
|
||||
function sendCmd(mac, cmd) {
|
||||
@@ -446,7 +506,7 @@ function sendCmd(mac, cmd) {
|
||||
})
|
||||
.catch(error => showMessage('Error: ' + error));
|
||||
$('#advancedoptions').style.height = '0px';
|
||||
$('#configbox').style.display = 'none';
|
||||
$('#configbox').close();
|
||||
}
|
||||
|
||||
$('#cfgdelete').onclick = function () {
|
||||
@@ -477,7 +537,8 @@ $('#cfgreset').onclick = function () {
|
||||
sendCmd($('#cfgmac').dataset.mac, "reset");
|
||||
}
|
||||
|
||||
$('#rebootbutton').onclick = function () {
|
||||
$('#rebootbutton').onclick = function (event) {
|
||||
event.preventDefault();
|
||||
showMessage("rebooting AP....", true);
|
||||
fetch("/reboot", {
|
||||
method: "POST"
|
||||
@@ -485,29 +546,37 @@ $('#rebootbutton').onclick = function () {
|
||||
socket.close();
|
||||
}
|
||||
|
||||
$('#apconfigbutton').onclick = function () {
|
||||
let table = document.getElementById("aptable");
|
||||
const rowCount = table.rows.length;
|
||||
for (let i = rowCount - 1; i > 0; i--) {
|
||||
table.deleteRow(i);
|
||||
$('#configbox').addEventListener('click', (event) => {
|
||||
if (event.target.nodeName === 'DIALOG') {
|
||||
$('#configbox').close();
|
||||
}
|
||||
fetch("/get_ap_config")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
$('#apcfgalias').value = data.alias;
|
||||
$('#apcfgchid').value = data.channel;
|
||||
$("#apcfgledbrightness").value = data.led;
|
||||
$("#apcfglanguage").value = data.language;
|
||||
$("#apclatency").value = data.maxsleep;
|
||||
$("#apcpreventsleep").value = data.stopsleep;
|
||||
$("#apcpreview").value = data.preview;
|
||||
$("#apcwifipower").value = data.wifipower;
|
||||
$("#apctimezone").value = data.timezone;
|
||||
$("#apcnight1").value = data.sleeptime1;
|
||||
$("#apcnight2").value = data.sleeptime2;
|
||||
})
|
||||
$('#apconfigbox').style.display = 'block'
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("loadTab", function (event) {
|
||||
activeTab = event.detail;
|
||||
switch (event.detail) {
|
||||
case 'configtab':
|
||||
case 'aptab':
|
||||
fetch("/get_ap_config")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
apConfig = data;
|
||||
$('#apcfgalias').value = data.alias;
|
||||
$('#apcfgchid').value = data.channel;
|
||||
$("#apcfgledbrightness").value = data.led;
|
||||
$("#apcfglanguage").value = data.language;
|
||||
$("#apclatency").value = data.maxsleep;
|
||||
$("#apcpreventsleep").value = data.stopsleep;
|
||||
$("#apcpreview").value = data.preview;
|
||||
$("#apcwifipower").value = data.wifipower;
|
||||
$("#apctimezone").value = data.timezone;
|
||||
$("#apcnight1").value = data.sleeptime1;
|
||||
$("#apcnight2").value = data.sleeptime2;
|
||||
})
|
||||
$('#apcfgmsg').innerHTML = '';
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
$('#apcfgsave').onclick = function () {
|
||||
let formData = new FormData();
|
||||
@@ -522,19 +591,22 @@ $('#apcfgsave').onclick = function () {
|
||||
formData.append('timezone', $('#apctimezone').value);
|
||||
formData.append('sleeptime1', $('#apcnight1').value);
|
||||
formData.append('sleeptime2', $('#apcnight2').value);
|
||||
|
||||
fetch("/save_apcfg", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.text())
|
||||
.then(data => showMessage(data))
|
||||
.then(data => {
|
||||
showMessage(data);
|
||||
window.dispatchEvent(loadConfig);
|
||||
$('#apcfgmsg').innerHTML = 'OK, Saved';
|
||||
})
|
||||
.catch(error => showMessage('Error: ' + error));
|
||||
$(".logo").innerHTML = $('#apcfgalias').value;
|
||||
$('#apconfigbox').style.display = 'none';
|
||||
}
|
||||
|
||||
$('#updatebutton').onclick = function () {
|
||||
$('#apconfigbox').style.display = 'none';
|
||||
$('#updatebutton').onclick = function (event) {
|
||||
event.preventDefault();
|
||||
$('#apupdatebox').style.display = 'block';
|
||||
loadOTA();
|
||||
}
|
||||
@@ -698,7 +770,9 @@ function showMessage(message, iserr) {
|
||||
const messages = $('#messages');
|
||||
const date = new Date();
|
||||
const time = date.toLocaleTimeString('nl-NL', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
||||
if (iserr) {
|
||||
if (message.startsWith('{')) {
|
||||
messages.insertAdjacentHTML("afterbegin", '<li class="new">' + htmlEncode(time) + ' <span class="mono">' + message.replace(/"([^"]+)"/g, '"<span class="quote">$1</span>"') + '</span></li>');
|
||||
} else if (iserr) {
|
||||
messages.insertAdjacentHTML("afterbegin", '<li class="new error">' + htmlEncode(time + ' ' + message) + '</li>');
|
||||
} else {
|
||||
messages.insertAdjacentHTML("afterbegin", '<li class="new">' + htmlEncode(time + ' ' + message) + '</li>');
|
||||
@@ -865,12 +939,18 @@ function GroupSortFilter() {
|
||||
}
|
||||
|
||||
let show = true;
|
||||
let batteryMv = tagDB[item.dataset.mac].batteryMv;
|
||||
if ($('input[name="filter"][value="remote"]').checked && item.dataset.isexternal == "false") show = false;
|
||||
if ($('input[name="filter"][value="local"]').checked && item.dataset.isexternal == "true") show = false;
|
||||
if ($('input[name="filter"][value="inactive"]').checked && item.querySelector('.warningicon').style.display != 'inline-block') show = false;
|
||||
if ($('input[name="filter"][value="pending"]').checked && !item.classList.contains("tagpending")) show = false;
|
||||
if ($('input[name="filter"][value="lowbatt"]').checked && (batteryMv >= 2400 || batteryMv == 0 || batteryMv == 1337)) show = false;
|
||||
if (!show) item.style.display = 'none'; else item.style.display = 'block';
|
||||
item.style.order = order++;
|
||||
const checkedValues = Array.from(document.querySelectorAll('input[name="filter"]:checked'))
|
||||
.map(checkbox => checkbox.value)
|
||||
.join(', ');
|
||||
$('#activefilter').innerHTML = (checkedValues ? 'filtered by ' + checkedValues : '');
|
||||
});
|
||||
|
||||
headItems = Array.from($('#taglist').getElementsByClassName('taggroup'));
|
||||
@@ -879,7 +959,7 @@ function GroupSortFilter() {
|
||||
})
|
||||
}
|
||||
|
||||
$('#toggleFilters').addEventListener('click', () => {
|
||||
$('#toggleFilters').addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
const filterOptions = $('#filterOptions');
|
||||
filterOptions.classList.toggle('active');
|
||||
@@ -890,6 +970,13 @@ $('#toggleFilters').addEventListener('click', () => {
|
||||
}
|
||||
});
|
||||
|
||||
$('#activefilter').addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
const filterOptions = $('#filterOptions');
|
||||
filterOptions.classList.add('active');
|
||||
filterOptions.style.maxHeight = filterOptions.scrollHeight + 20 + 'px';
|
||||
});
|
||||
|
||||
async function getTagtype(hwtype) {
|
||||
if (tagTypes[hwtype]) {
|
||||
return tagTypes[hwtype];
|
||||
@@ -1094,7 +1181,8 @@ $('#taglist').addEventListener('contextmenu', (e) => {
|
||||
if (tagTypes[hwtype].options?.includes("led")) {
|
||||
contextMenuOptions.push(
|
||||
{ id: 'ledflash', label: 'Flash the LED' },
|
||||
{ id: 'ledflash_long', label: 'Flash the LED (long)' }
|
||||
{ id: 'ledflash_long', label: 'Flash the LED (long)' },
|
||||
{ id: 'ledflash_stop', label: 'Stop flashing' }
|
||||
);
|
||||
}
|
||||
contextMenuOptions.push(
|
||||
@@ -1133,4 +1221,83 @@ function populateTimes(element) {
|
||||
option.text = i.toString().padStart(2, "0") + ":00";
|
||||
element.appendChild(option);
|
||||
}
|
||||
}
|
||||
|
||||
function populateAPCard(msg) {
|
||||
let apip = msg.ip;
|
||||
let apid = apip.replace(/\./g, "-");
|
||||
if (!$('#ap' + apid)) {
|
||||
div = $('#apcard').cloneNode(true);
|
||||
div.setAttribute('id', 'ap' + apid);
|
||||
$('#aplist').appendChild(div);
|
||||
}
|
||||
let alias = msg.alias;
|
||||
if (!alias) alias = apip;
|
||||
$('#ap' + apid + ' .apip').innerHTML = "<a href=\"http://" + apip + "\" target=\"_new\">" + apip + "</a>";
|
||||
$('#ap' + apid + ' .apalias').innerHTML = alias;
|
||||
$('#ap' + apid + ' .aptagcount').innerHTML = msg.count;
|
||||
$('#ap' + apid + ' .apchannel').innerHTML = msg.channel;
|
||||
|
||||
const elements = document.querySelectorAll('.apchannel');
|
||||
Array.from(elements).forEach(element => {
|
||||
if (element.textContent === msg.channel && element.id !== 'ap' + apid) {
|
||||
$('#ap' + apid + ' .apchannel').style.color = 'red';
|
||||
$('#ap' + apid + ' .apchannel').innerHTML += ' conflict';
|
||||
}
|
||||
});
|
||||
|
||||
// $('#ap' + apid + ' .apversion').innerHTML = msg.version;
|
||||
if (activeTab == 'aptab') {
|
||||
populateAPInfo(apip);
|
||||
}
|
||||
}
|
||||
|
||||
function populateAPInfo(apip) {
|
||||
let apid = apip.replace(/\./g, "-");
|
||||
fetch('http://' + apip + '/sysinfo')
|
||||
.then(response => {
|
||||
if (response.status != 200) {
|
||||
$('#ap' + apid + ' .apswversion').innerHTML = "Error fetching sysinfo: " + response.status;
|
||||
return {};
|
||||
} else {
|
||||
return response.json();
|
||||
}
|
||||
})
|
||||
.then(data => {
|
||||
if (data.env) {
|
||||
let version = '';
|
||||
version += `env: ${data.env}<br>`;
|
||||
version += `build date: ${formatEpoch(data.buildtime)}<br>`;
|
||||
version += `esp32 version: ${data.buildversion}<br>`;
|
||||
version += `psram size: ${data.psramsize}<br>`;
|
||||
version += `flash size: ${data.flashsize}<br>`;
|
||||
$('#ap' + apid + ' .apswversion').innerHTML = version;
|
||||
// if (data.env == 'ESP32_S3_16_8_YELLOW_AP') $("#c6Option").style.display = 'block';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
$('#ap' + apid + ' .apswversion').innerHTML = "Error fetching sysinfo: " + error;
|
||||
});
|
||||
}
|
||||
|
||||
function formatEpoch(epochTime) {
|
||||
const date = new Date(epochTime * 1000); // Convert seconds to milliseconds
|
||||
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are zero-based
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
function setFilterAndShow(filter) {
|
||||
$('input[name="filter"][value="remote"]').checked = false;
|
||||
$('input[name="filter"][value="local"]').checked = false;
|
||||
$('input[name="filter"][value="inactive"]').checked = (filter == 'inactive');
|
||||
$('input[name="filter"][value="pending"]').checked = (filter == 'pending');
|
||||
$('input[name="filter"][value="lowbatt"]').checked = (filter == 'lowbatt');
|
||||
GroupSortFilter();
|
||||
$(`[data-target='tagtab']`).click();
|
||||
}
|
||||
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
BIN
Hardware/3D-Printed/4.2 mini stand v2 by Nic.stl
Normal file
BIN
Hardware/3D-Printed/4.2 mini stand v2 by Nic.stl
Normal file
Binary file not shown.
BIN
Hardware/3D-Printed/4.2_mini_stand_v2.jpeg
Normal file
BIN
Hardware/3D-Printed/4.2_mini_stand_v2.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 282 KiB |
@@ -6,4 +6,11 @@ There are holder and stands for every tag. 1.54", 2.9" and 4.2".
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
** 4.2 mini stand v2 by Nic.stl
|
||||

|
||||
4.2" mini stand by Nic. Note that these are designed with tight tolerances, to be printed on a resin printer. If you print it using your desktop 3D-printer, it might not fit.
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ Not all connections are required by all tags! If you want to solder fewer wires,
|
||||
|
||||
## Flashing the flasher
|
||||
Clone the [Tag_Flasher repo](https://github.com/jjwbruijn/OpenEPaperLink/tree/master/Tag_Flasher/ESP32_Flasher) and open into PlatformIO. Choose the correct COM-port and hit 'Upload'.
|
||||
Also, the precompiled binaries are part of any [release](https://github.com/jjwbruijn/OpenEPaperLink/releases), and to make it even easier, you can use the web flasher on https://install.openepaperlink.nl to even flash it without installing any extra software.
|
||||
|
||||
## OEPL-Flasher.py
|
||||
|
||||
|
||||
@@ -174,13 +174,16 @@ struct ledFlash {
|
||||
uint8_t flashDuration : 4;
|
||||
uint8_t color1;
|
||||
uint8_t flashCount1 : 4;
|
||||
uint8_t delay1 : 4;
|
||||
uint8_t flashSpeed1 : 4;
|
||||
uint8_t delay1;
|
||||
uint8_t color2;
|
||||
uint8_t flashCount2 : 4;
|
||||
uint8_t delay2 : 4;
|
||||
uint8_t flashSpeed2 : 4;
|
||||
uint8_t delay2;
|
||||
uint8_t color3;
|
||||
uint8_t flashCount3 : 4;
|
||||
uint8_t delay3 : 4;
|
||||
uint8_t flashSpeed3 : 4;
|
||||
uint8_t delay3;
|
||||
uint8_t repeats;
|
||||
uint8_t spare;
|
||||
} __packed;
|
||||
|
||||
Reference in New Issue
Block a user