mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-21 08:06:46 +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 sleepMsEntry = 0;
|
||||||
uint32_t loops = 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] = {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};
|
// 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
|
// tell the sleep function to net sleep again
|
||||||
sleepMsEntry = sleepMsEntry - 999999999;
|
sleepMsEntry = sleepMsEntry - 999999999;
|
||||||
loops = 0;
|
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 colorred = (color >> 5) & 0b00000111;
|
||||||
uint8_t colorgreen = (color >> 2) & 0b00000111;
|
uint8_t colorgreen = (color >> 2) & 0b00000111;
|
||||||
uint8_t colorblue = color & 0b00000011;
|
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_RED, !(colorred >= 7));
|
||||||
digitalWrite(LED_GREEN, !(colorgreen >= 7));
|
digitalWrite(LED_GREEN, !(colorgreen >= 7));
|
||||||
digitalWrite(LED_BLUE, !(colorblue >= 3));
|
digitalWrite(LED_BLUE, !(colorblue >= 3));
|
||||||
@@ -248,11 +248,10 @@ void sleepwithinterrupts(uint32_t sleepinterval) {
|
|||||||
|
|
||||||
void ledflashlogic(uint32_t ms) {
|
void ledflashlogic(uint32_t ms) {
|
||||||
watchdog_enable(ms + 1000);
|
watchdog_enable(ms + 1000);
|
||||||
uint8_t brightnes = ledcfg[0] >> 4 & 0b00001111;
|
uint8_t brightness = ledcfg[0] >> 4 & 0b00001111;
|
||||||
uint8_t mode = ledcfg[0] & 0b00001111;
|
uint8_t mode = ledcfg[0] & 0b00001111;
|
||||||
// lets not blink for short delays
|
// lets not blink for short delays
|
||||||
if (ms < 2000) mode = 15;
|
if (ms < 2000) mode = 15;
|
||||||
// if(mode == 0)sleepwithinterrupts(ms);
|
|
||||||
if (mode == 1) {
|
if (mode == 1) {
|
||||||
uint8_t color = ledcfg[1];
|
uint8_t color = ledcfg[1];
|
||||||
uint32_t ledinerv = (ledcfg[2] << 24) + (ledcfg[3] << 16) + (ledcfg[4] << 8) + ledcfg[5];
|
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;
|
if (sleepinterval > ms) sleepinterval = ms;
|
||||||
for (uint32_t i = 0; i < loops; i++) {
|
for (uint32_t i = 0; i < loops; i++) {
|
||||||
flashled(color, brightnes);
|
flashled(color, brightness);
|
||||||
sleepwithinterrupts(sleepinterval);
|
sleepwithinterrupts(sleepinterval);
|
||||||
}
|
}
|
||||||
} else if (mode == 0) {
|
}
|
||||||
interruped = false;
|
else if (mode == 0) {
|
||||||
|
interrupted = false;
|
||||||
uint8_t interloopdelayfactor = 100;
|
uint8_t interloopdelayfactor = 100;
|
||||||
u_int8_t loopdelayfactor = 100;
|
u_int8_t loopdelayfactor = 100;
|
||||||
uint8_t c1 = ledcfg[1];
|
uint8_t c1 = ledcfg[1];
|
||||||
@@ -290,36 +290,40 @@ void ledflashlogic(uint32_t ms) {
|
|||||||
uint32_t looptimesum = fulllooptime1 + fulllooptime2 + fulllooptime3;
|
uint32_t looptimesum = fulllooptime1 + fulllooptime2 + fulllooptime3;
|
||||||
int fittingrepeats = (int)ms / looptimesum;
|
int fittingrepeats = (int)ms / looptimesum;
|
||||||
|
|
||||||
grouprepeats = fittingrepeats;
|
//catch edge case
|
||||||
if (grouprepeats == 0) grouprepeats = 1;
|
if (grouprepeats == 0) sleepwithinterrupts(ms);
|
||||||
|
|
||||||
for (int j = 0; j < grouprepeats; j++) {
|
for (int j = 0; j < fittingrepeats; j++) {
|
||||||
if (!interruped) {
|
if(j > grouprepeats){
|
||||||
|
brightness = 0;
|
||||||
|
ledcfg[0] = 0xff;
|
||||||
|
}
|
||||||
|
if (!interrupted) {
|
||||||
for (int i = 0; i < loopcnt1; i++) {
|
for (int i = 0; i < loopcnt1; i++) {
|
||||||
flashled(c1, brightnes);
|
flashled(c1, brightness);
|
||||||
sleepwithinterrupts(loop1delay * loopdelayfactor);
|
sleepwithinterrupts(loop1delay * loopdelayfactor);
|
||||||
if (interruped) break;
|
if (interrupted) break;
|
||||||
}
|
}
|
||||||
sleepwithinterrupts(ildelay1 * interloopdelayfactor);
|
sleepwithinterrupts(ildelay1 * interloopdelayfactor);
|
||||||
}
|
}
|
||||||
if (!interruped) {
|
if (!interrupted) {
|
||||||
for (int i = 0; i < loopcnt2; i++) {
|
for (int i = 0; i < loopcnt2; i++) {
|
||||||
flashled(c2, brightnes);
|
flashled(c2, brightness);
|
||||||
sleepwithinterrupts(loop2delay * loopdelayfactor);
|
sleepwithinterrupts(loop2delay * loopdelayfactor);
|
||||||
if (interruped) break;
|
if (interrupted) break;
|
||||||
}
|
}
|
||||||
sleepwithinterrupts(ildelay2 * interloopdelayfactor);
|
sleepwithinterrupts(ildelay2 * interloopdelayfactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!interruped) {
|
if (!interrupted) {
|
||||||
for (int i = 0; i < loopcnt3; i++) {
|
for (int i = 0; i < loopcnt3; i++) {
|
||||||
flashled(c3, brightnes);
|
flashled(c3, brightness);
|
||||||
sleepwithinterrupts(loop3delay * loopdelayfactor);
|
sleepwithinterrupts(loop3delay * loopdelayfactor);
|
||||||
if (interruped) break;
|
if (interrupted) break;
|
||||||
}
|
}
|
||||||
sleepwithinterrupts(ildelay3 * interloopdelayfactor);
|
sleepwithinterrupts(ildelay3 * interloopdelayfactor);
|
||||||
}
|
}
|
||||||
if (interruped) break;
|
if (interrupted) break;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
sleepwithinterrupts(ms);
|
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 prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP);
|
||||||
extern void processXferComplete(struct espXferComplete* xfc, bool local);
|
extern void processXferComplete(struct espXferComplete* xfc, bool local);
|
||||||
extern void processXferTimeout(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 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);
|
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;
|
bool isInited;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern SemaphoreHandle_t fsMutex;
|
||||||
extern DynStorage Storage;
|
extern DynStorage Storage;
|
||||||
extern fs::FS *contentFS;
|
extern fs::FS *contentFS;
|
||||||
extern void copyFile(File in, File out);
|
extern void copyFile(File in, File out);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
class tagRecord {
|
class tagRecord {
|
||||||
public:
|
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];
|
uint8_t mac[8];
|
||||||
String alias;
|
String alias;
|
||||||
@@ -42,6 +42,7 @@ class tagRecord {
|
|||||||
uint8_t capabilities;
|
uint8_t capabilities;
|
||||||
uint32_t lastfullupdate;
|
uint32_t lastfullupdate;
|
||||||
bool isExternal;
|
bool isExternal;
|
||||||
|
IPAddress apIp;
|
||||||
uint16_t pendingIdle;
|
uint16_t pendingIdle;
|
||||||
bool hasCustomLUT;
|
bool hasCustomLUT;
|
||||||
uint8_t rotate;
|
uint8_t rotate;
|
||||||
@@ -85,7 +86,6 @@ struct varStruct {
|
|||||||
bool changed;
|
bool changed;
|
||||||
};
|
};
|
||||||
|
|
||||||
// extern SemaphoreHandle_t tagDBOwner;
|
|
||||||
extern Config config;
|
extern Config config;
|
||||||
extern std::vector<tagRecord*> tagDB;
|
extern std::vector<tagRecord*> tagDB;
|
||||||
extern std::unordered_map<int, HwType> hwtype;
|
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
|
} // namespace util
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
void init_web();
|
void init_web();
|
||||||
void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
|
void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
|
||||||
void doJsonUpload(AsyncWebServerRequest *request);
|
void doJsonUpload(AsyncWebServerRequest *request);
|
||||||
extern void networkProcess(void *parameter);
|
|
||||||
void wsLog(String text);
|
void wsLog(String text);
|
||||||
void wsErr(String text);
|
void wsErr(String text);
|
||||||
void wsSendTaginfo(const uint8_t *mac, uint8_t syncMode);
|
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);
|
memset(&wifimac[6], 0, 2);
|
||||||
|
|
||||||
const bool isAp = memcmp(mac, wifimac, 8) == 0;
|
const bool isAp = memcmp(mac, wifimac, 8) == 0;
|
||||||
if ((taginfo->wakeupReason == WAKEUP_REASON_FIRSTBOOT || taginfo->wakeupReason == WAKEUP_REASON_WDT_RESET) && taginfo->contentMode == 0 && isAp) {
|
if ((taginfo->wakeupReason == WAKEUP_REASON_FIRSTBOOT || taginfo->wakeupReason == WAKEUP_REASON_WDT_RESET) && taginfo->contentMode == 0) {
|
||||||
taginfo->contentMode = 21;
|
if (isAp) {
|
||||||
taginfo->nextupdate = 0;
|
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];
|
char hexmac[17];
|
||||||
@@ -393,7 +408,7 @@ void drawNew(const uint8_t mac[8], const bool buttonPressed, tagRecord *&taginfo
|
|||||||
if (!util::isEmptyOrNull(configFilename)) {
|
if (!util::isEmptyOrNull(configFilename)) {
|
||||||
String configUrl = cfgobj["url"].as<String>();
|
String configUrl = cfgobj["url"].as<String>();
|
||||||
if (!util::isEmptyOrNull(configUrl)) {
|
if (!util::isEmptyOrNull(configUrl)) {
|
||||||
StaticJsonDocument<1000> json;
|
DynamicJsonDocument json(1000);
|
||||||
Serial.println("Get json url + file");
|
Serial.println("Get json url + file");
|
||||||
if (util::httpGetJson(configUrl, json, 1000)) {
|
if (util::httpGetJson(configUrl, json, 1000)) {
|
||||||
if (getJsonTemplateFileExtractVariables(filename, configFilename, json, taginfo, imageParams)) {
|
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";
|
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);
|
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) {
|
if (!success) {
|
||||||
return;
|
return;
|
||||||
@@ -842,11 +857,15 @@ int getImgURL(String &filename, String URL, time_t fetched, imgParam &imageParam
|
|||||||
http.setTimeout(5000); // timeout in ms
|
http.setTimeout(5000); // timeout in ms
|
||||||
const int httpCode = http.GET();
|
const int httpCode = http.GET();
|
||||||
if (httpCode == 200) {
|
if (httpCode == 200) {
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
File f = contentFS->open("/temp/temp.jpg", "w");
|
File f = contentFS->open("/temp/temp.jpg", "w");
|
||||||
if (f) {
|
if (f) {
|
||||||
http.writeToStream(&f);
|
http.writeToStream(&f);
|
||||||
f.close();
|
f.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
jpg2buffer("/temp/temp.jpg", filename, imageParams);
|
jpg2buffer("/temp/temp.jpg", filename, imageParams);
|
||||||
|
} else {
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (httpCode != 304) {
|
if (httpCode != 304) {
|
||||||
@@ -1120,7 +1139,7 @@ void drawAPinfo(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgPa
|
|||||||
}
|
}
|
||||||
|
|
||||||
TFT_eSprite spr = TFT_eSprite(&tft);
|
TFT_eSprite spr = TFT_eSprite(&tft);
|
||||||
StaticJsonDocument<2048> loc;
|
DynamicJsonDocument loc(2048);
|
||||||
getTemplate(loc, 21, taginfo->hwType);
|
getTemplate(loc, 21, taginfo->hwType);
|
||||||
|
|
||||||
initSprite(spr, imageParams.width, imageParams.height, imageParams);
|
initSprite(spr, imageParams.width, imageParams.height, imageParams);
|
||||||
@@ -1429,7 +1448,7 @@ void getLocation(JsonObject &cfgobj) {
|
|||||||
filter["results"][0]["latitude"] = true;
|
filter["results"][0]["latitude"] = true;
|
||||||
filter["results"][0]["longitude"] = true;
|
filter["results"][0]["longitude"] = true;
|
||||||
filter["results"][0]["timezone"] = 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)) {
|
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["#lat"] = doc["results"][0]["latitude"].as<String>();
|
||||||
cfgobj["#lon"] = doc["results"][0]["longitude"].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) {
|
void getTemplate(JsonDocument &json, const uint8_t id, const uint8_t hwtype) {
|
||||||
StaticJsonDocument<80> filter;
|
StaticJsonDocument<80> filter;
|
||||||
StaticJsonDocument<2048> doc;
|
DynamicJsonDocument doc(2048);
|
||||||
|
|
||||||
const String idstr = String(id);
|
const String idstr = String(id);
|
||||||
constexpr const char *templateKey = "template";
|
constexpr const char *templateKey = "template";
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ bool downloadAndWriteBinary(String &filename, const char *url) {
|
|||||||
int binaryResponseCode = binaryHttp.GET();
|
int binaryResponseCode = binaryHttp.GET();
|
||||||
Serial.println(binaryResponseCode);
|
Serial.println(binaryResponseCode);
|
||||||
if (binaryResponseCode == HTTP_CODE_OK) {
|
if (binaryResponseCode == HTTP_CODE_OK) {
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
File file = contentFS->open(filename, "wb");
|
File file = contentFS->open(filename, "wb");
|
||||||
if (file) {
|
if (file) {
|
||||||
wsSerial("downloading " + String(filename));
|
wsSerial("downloading " + String(filename));
|
||||||
@@ -138,6 +139,7 @@ bool downloadAndWriteBinary(String &filename, const char *url) {
|
|||||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
binaryHttp.end();
|
binaryHttp.end();
|
||||||
|
|
||||||
file = contentFS->open(filename, "r");
|
file = contentFS->open(filename, "r");
|
||||||
@@ -150,6 +152,7 @@ bool downloadAndWriteBinary(String &filename, const char *url) {
|
|||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
wsSerial("file open error " + String(filename));
|
wsSerial("file open error " + String(filename));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ bool flasher::getInfoBlockType() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool flasher::findTagByMD5() {
|
bool flasher::findTagByMD5() {
|
||||||
StaticJsonDocument<3000> doc;
|
DynamicJsonDocument doc(3000);
|
||||||
DynamicJsonDocument APconfig(600);
|
DynamicJsonDocument APconfig(600);
|
||||||
fs::File readfile = contentFS->open("/tag_md5_db.json", "r");
|
fs::File readfile = contentFS->open("/tag_md5_db.json", "r");
|
||||||
DeserializationError err = deserializeJson(doc, readfile);
|
DeserializationError err = deserializeJson(doc, readfile);
|
||||||
@@ -205,7 +205,7 @@ bool flasher::findTagByMD5() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool flasher::findTagByType(uint8_t type) {
|
bool flasher::findTagByType(uint8_t type) {
|
||||||
StaticJsonDocument<3000> doc;
|
DynamicJsonDocument doc(3000);
|
||||||
DynamicJsonDocument APconfig(600);
|
DynamicJsonDocument APconfig(600);
|
||||||
fs::File readfile = contentFS->open("/tag_md5_db.json", "r");
|
fs::File readfile = contentFS->open("/tag_md5_db.json", "r");
|
||||||
DeserializationError err = deserializeJson(doc, readfile);
|
DeserializationError err = deserializeJson(doc, readfile);
|
||||||
@@ -271,11 +271,13 @@ bool flasher::backupFlash() {
|
|||||||
getFirmwareMD5();
|
getFirmwareMD5();
|
||||||
if (!zbs->select_flash(0)) return false;
|
if (!zbs->select_flash(0)) return false;
|
||||||
md5char[16] = 0x00;
|
md5char[16] = 0x00;
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
fs::File backup = contentFS->open("/" + (String)md5char + "_backup.bin", "w", true);
|
fs::File backup = contentFS->open("/" + (String)md5char + "_backup.bin", "w", true);
|
||||||
for (uint32_t c = 0; c < 65535; c++) {
|
for (uint32_t c = 0; c < 65535; c++) {
|
||||||
backup.write(zbs->read_flash(c));
|
backup.write(zbs->read_flash(c));
|
||||||
}
|
}
|
||||||
backup.close();
|
backup.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "tag_db.h"
|
#include "tag_db.h"
|
||||||
|
#include "wifimanager.h"
|
||||||
|
|
||||||
#ifdef HAS_USB
|
#ifdef HAS_USB
|
||||||
#include "usbflasher.h"
|
#include "usbflasher.h"
|
||||||
@@ -21,6 +22,13 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "web.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 pinTest();
|
||||||
|
|
||||||
void delayedStart(void* parameter) {
|
void delayedStart(void* parameter) {
|
||||||
@@ -33,32 +41,6 @@ void delayedStart(void* parameter) {
|
|||||||
vTaskDelete(NULL);
|
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() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
Serial.print(">\n");
|
Serial.print(">\n");
|
||||||
@@ -146,9 +128,7 @@ void setup() {
|
|||||||
#endif
|
#endif
|
||||||
loadDB("/current/tagDB.json");
|
loadDB("/current/tagDB.json");
|
||||||
cleanupCurrent();
|
cleanupCurrent();
|
||||||
// tagDBOwner = xSemaphoreCreateMutex();
|
|
||||||
xTaskCreate(APTask, "AP Process", 6000, NULL, 2, NULL);
|
xTaskCreate(APTask, "AP Process", 6000, NULL, 2, NULL);
|
||||||
xTaskCreate(networkProcess, "Wifi", 6000, NULL, configMAX_PRIORITIES - 10, NULL);
|
|
||||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
config.runStatus = RUNSTATUS_INIT;
|
config.runStatus = RUNSTATUS_INIT;
|
||||||
@@ -158,26 +138,39 @@ void setup() {
|
|||||||
config.runStatus = RUNSTATUS_PAUSE;
|
config.runStatus = RUNSTATUS_PAUSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
xTaskCreate(timeTask, "timed tasks", 12000, NULL, 2, NULL);
|
|
||||||
xTaskCreate(initTime, "init time", 5000, NULL, 2, NULL);
|
xTaskCreate(initTime, "init time", 5000, NULL, 2, NULL);
|
||||||
xTaskCreate(delayedStart, "delaystart", 2000, NULL, 2, NULL);
|
xTaskCreate(delayedStart, "delaystart", 2000, NULL, 2, NULL);
|
||||||
|
|
||||||
|
wsSendSysteminfo();
|
||||||
|
util::printHeap();
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
vTaskDelay(10000 / portTICK_PERIOD_MS);
|
ws.cleanupClients();
|
||||||
// performDeviceFlash();
|
wm.poll();
|
||||||
while (1) {
|
|
||||||
// pinTest();
|
if (intervalSysinfo.doRun()) {
|
||||||
while (1) {
|
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
|
#ifdef YELLOW_IPS_AP
|
||||||
extern void yellow_ap_display_loop(void);
|
extern void yellow_ap_display_loop(void);
|
||||||
yellow_ap_display_loop();
|
yellow_ap_display_loop();
|
||||||
#else
|
|
||||||
vTaskDelay(10000 / portTICK_PERIOD_MS);
|
|
||||||
// pinTest();
|
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
#ifdef OPENEPAPERLINK_PCB
|
#ifdef OPENEPAPERLINK_PCB
|
||||||
|
time_t tagConnectTimer = 0;
|
||||||
|
if (millis() - tagConnectTimer > 1000) {
|
||||||
|
tagConnectTimer = millis();
|
||||||
if (extTagConnected()) {
|
if (extTagConnected()) {
|
||||||
flashCountDown(3);
|
flashCountDown(3);
|
||||||
|
|
||||||
@@ -190,7 +183,8 @@ void loop() {
|
|||||||
pinMode(FLASHER_EXT_TEST, INPUT);
|
pinMode(FLASHER_EXT_TEST, INPUT);
|
||||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
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
|
#endif
|
||||||
|
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
fs::File f_out = contentFS->open(fileout, "w");
|
fs::File f_out = contentFS->open(fileout, "w");
|
||||||
|
|
||||||
switch (imageParams.bpp) {
|
switch (imageParams.bpp) {
|
||||||
@@ -232,6 +233,8 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
|
|||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
Serial.println("Failed to allocate buffer");
|
Serial.println("Failed to allocate buffer");
|
||||||
util::printLargestFreeBlock();
|
util::printLargestFreeBlock();
|
||||||
|
f_out.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
spr2color(spr, imageParams, buffer, buffer_size, false);
|
spr2color(spr, imageParams, buffer, buffer_size, false);
|
||||||
@@ -251,5 +254,6 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
f_out.close();
|
f_out.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
Serial.println("finished writing buffer " + String(millis() - t) + "ms");
|
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 {
|
} else {
|
||||||
sendDataAvail(&pending);
|
sendDataAvail(&pending);
|
||||||
}
|
}
|
||||||
|
|
||||||
wsSendTaginfo(dst, SYNC_TAGSTATUS);
|
wsSendTaginfo(dst, SYNC_TAGSTATUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,18 +241,22 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
|
|||||||
http.begin(imageUrl);
|
http.begin(imageUrl);
|
||||||
int httpCode = http.GET();
|
int httpCode = http.GET();
|
||||||
if (httpCode == 200) {
|
if (httpCode == 200) {
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
File file = contentFS->open(filename, "w");
|
File file = contentFS->open(filename, "w");
|
||||||
http.writeToStream(&file);
|
http.writeToStream(&file);
|
||||||
file.close();
|
file.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
} else if (httpCode == 404) {
|
} else if (httpCode == 404) {
|
||||||
imageUrl = "http://" + remoteIP.toString() + "/current/" + String(hexmac) + ".raw";
|
imageUrl = "http://" + remoteIP.toString() + "/current/" + String(hexmac) + ".raw";
|
||||||
http.end();
|
http.end();
|
||||||
http.begin(imageUrl);
|
http.begin(imageUrl);
|
||||||
httpCode = http.GET();
|
httpCode = http.GET();
|
||||||
if (httpCode == 200) {
|
if (httpCode == 200) {
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
File file = contentFS->open(filename, "w");
|
File file = contentFS->open(filename, "w");
|
||||||
http.writeToStream(&file);
|
http.writeToStream(&file);
|
||||||
file.close();
|
file.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
http.end();
|
http.end();
|
||||||
@@ -430,7 +435,7 @@ void processXferTimeout(struct espXferComplete* xfc, bool local) {
|
|||||||
if (local) udpsync.netProcessXferTimeout(xfc);
|
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;
|
if (config.runStatus == RUNSTATUS_STOP) return;
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
|
|
||||||
@@ -450,13 +455,15 @@ void processDataReq(struct espAvailDataReq* eadr, bool local) {
|
|||||||
if (!local) {
|
if (!local) {
|
||||||
if (taginfo->isExternal == false) {
|
if (taginfo->isExternal == false) {
|
||||||
wsLog("moved AP from local to external " + String(hexmac));
|
wsLog("moved AP from local to external " + String(hexmac));
|
||||||
|
taginfo->isExternal = true;
|
||||||
}
|
}
|
||||||
taginfo->isExternal = true;
|
taginfo->apIp = remoteIP;
|
||||||
} else {
|
} else {
|
||||||
if (taginfo->isExternal == true) {
|
if (taginfo->isExternal == true) {
|
||||||
wsLog("moved AP from external to local " + String(hexmac));
|
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) {
|
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]);
|
// 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) {
|
if (local) {
|
||||||
|
wsSendTaginfo(eadr->src, SYNC_TAGSTATUS);
|
||||||
udpsync.netProcessDataReq(eadr);
|
udpsync.netProcessDataReq(eadr);
|
||||||
|
} else {
|
||||||
|
wsSendTaginfo(eadr->src, SYNC_NOSYNC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -653,7 +662,7 @@ void updateTaginfoitem(struct TagInfo* taginfoitem, IPAddress remoteIP) {
|
|||||||
|
|
||||||
char hexmac[17];
|
char hexmac[17];
|
||||||
mac2hex(taginfo->mac, hexmac);
|
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));
|
wsLog("Remote AP at " + remoteIP.toString() + " takes control over tag " + String(hexmac));
|
||||||
taginfo->contentMode = 12;
|
taginfo->contentMode = 12;
|
||||||
}
|
}
|
||||||
@@ -712,11 +721,15 @@ bool checkMirror(struct tagRecord* taginfo, struct pendingData* pending) {
|
|||||||
} else {
|
} else {
|
||||||
char dst_path[64];
|
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]);
|
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");
|
File file = contentFS->open(dst_path, "w");
|
||||||
if (file) {
|
if (file) {
|
||||||
file.write(taginfo2->data, taginfo2->len);
|
file.write(taginfo2->data, taginfo2->len);
|
||||||
file.close();
|
file.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
udpsync.netSendDataAvail(&pending2);
|
udpsync.netSendDataAvail(&pending2);
|
||||||
|
} else {
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ void handleLittleFSUpload(AsyncWebServerRequest* request, String filename, size_
|
|||||||
} else {
|
} else {
|
||||||
path = request->getParam("path", true)->value();
|
path = request->getParam("path", true)->value();
|
||||||
Serial.println("update " + path);
|
Serial.println("update " + path);
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
request->_tempFile = contentFS->open(path, "w", true);
|
request->_tempFile = contentFS->open(path, "w", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,6 +106,7 @@ void handleLittleFSUpload(AsyncWebServerRequest* request, String filename, size_
|
|||||||
}
|
}
|
||||||
if (final) {
|
if (final) {
|
||||||
request->_tempFile.close();
|
request->_tempFile.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
if (error) {
|
if (error) {
|
||||||
request->send(507, "text/plain", "Error. Disk full?");
|
request->send(507, "text/plain", "Error. Disk full?");
|
||||||
} else {
|
} else {
|
||||||
@@ -155,7 +157,6 @@ void updateFirmware(const char* url, const char* expectedMd5, const size_t size)
|
|||||||
|
|
||||||
config.runStatus = RUNSTATUS_STOP;
|
config.runStatus = RUNSTATUS_STOP;
|
||||||
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||||
// xSemaphoreTake(tagDBOwner, portMAX_DELAY);
|
|
||||||
saveDB("/current/tagDB.json");
|
saveDB("/current/tagDB.json");
|
||||||
// destroyDB();
|
// destroyDB();
|
||||||
|
|
||||||
@@ -213,7 +214,6 @@ void updateFirmware(const char* url, const char* expectedMd5, const size_t size)
|
|||||||
httpClient.end();
|
httpClient.end();
|
||||||
// loadDB("/current/tagDB.json");
|
// loadDB("/current/tagDB.json");
|
||||||
config.runStatus = RUNSTATUS_RUN;
|
config.runStatus = RUNSTATUS_RUN;
|
||||||
// xSemaphoreGive(tagDBOwner);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleRollback(AsyncWebServerRequest* request) {
|
void handleRollback(AsyncWebServerRequest* request) {
|
||||||
@@ -303,7 +303,7 @@ void handleUpdateActions(AsyncWebServerRequest* request) {
|
|||||||
request->send(200, "No update actions needed");
|
request->send(200, "No update actions needed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StaticJsonDocument<1000> doc;
|
DynamicJsonDocument doc(1000);
|
||||||
DeserializationError error = deserializeJson(doc, file);
|
DeserializationError error = deserializeJson(doc, file);
|
||||||
const JsonArray deleteFiles = doc["deletefile"].as<JsonArray>();
|
const JsonArray deleteFiles = doc["deletefile"].as<JsonArray>();
|
||||||
for (const auto& filePath : deleteFiles) {
|
for (const auto& filePath : deleteFiles) {
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
DynStorage::DynStorage() : isInited(0) {}
|
DynStorage::DynStorage() : isInited(0) {}
|
||||||
|
|
||||||
|
SemaphoreHandle_t fsMutex;
|
||||||
|
|
||||||
static void initLittleFS() {
|
static void initLittleFS() {
|
||||||
LittleFS.begin();
|
LittleFS.begin();
|
||||||
contentFS = &LittleFS;
|
contentFS = &LittleFS;
|
||||||
@@ -86,12 +88,15 @@ void copyBetweenFS(FS& sourceFS, const char* source_path, FS& targetFS) {
|
|||||||
|
|
||||||
copyBetweenFS(sourceFS, file.path(), targetFS);
|
copyBetweenFS(sourceFS, file.path(), targetFS);
|
||||||
} else {
|
} else {
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
File target = contentFS->open(file.path(), "w");
|
File target = contentFS->open(file.path(), "w");
|
||||||
if (target) {
|
if (target) {
|
||||||
copyFile(file, target);
|
copyFile(file, target);
|
||||||
target.close();
|
target.close();
|
||||||
file.close();
|
file.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
} else {
|
} else {
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
Serial.print("Couldn't create high target file");
|
Serial.print("Couldn't create high target file");
|
||||||
Serial.println(file.path());
|
Serial.println(file.path());
|
||||||
return;
|
return;
|
||||||
@@ -100,10 +105,14 @@ void copyBetweenFS(FS& sourceFS, const char* source_path, FS& targetFS) {
|
|||||||
file = root.openNextFile();
|
file = root.openNextFile();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
File target = contentFS->open(root.path(), "w");
|
File target = contentFS->open(root.path(), "w");
|
||||||
if (target) {
|
if (target) {
|
||||||
copyFile(root, target);
|
copyFile(root, target);
|
||||||
|
target.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
} else {
|
} else {
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
Serial.print("Couldn't create target file ");
|
Serial.print("Couldn't create target file ");
|
||||||
Serial.println(root.path());
|
Serial.println(root.path());
|
||||||
return;
|
return;
|
||||||
@@ -120,6 +129,7 @@ void copyIfNeeded(const char* path) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void DynStorage::begin() {
|
void DynStorage::begin() {
|
||||||
|
fsMutex = xSemaphoreCreateMutex();
|
||||||
initLittleFS();
|
initLittleFS();
|
||||||
|
|
||||||
#ifdef HAS_SDCARD
|
#ifdef HAS_SDCARD
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ std::unordered_map<int, HwType> hwdata = {
|
|||||||
{2, {400, 300, 0, 2}}};
|
{2, {400, 300, 0, 2}}};
|
||||||
|
|
||||||
Config config;
|
Config config;
|
||||||
// SemaphoreHandle_t tagDBOwner;
|
|
||||||
|
|
||||||
tagRecord* tagRecord::findByMAC(const uint8_t mac[8]) {
|
tagRecord* tagRecord::findByMAC(const uint8_t mac[8]) {
|
||||||
for (tagRecord* tag : tagDB) {
|
for (tagRecord* tag : tagDB) {
|
||||||
@@ -116,6 +115,7 @@ void fillNode(JsonObject& tag, const tagRecord* taginfo) {
|
|||||||
tag["capabilities"] = taginfo->capabilities;
|
tag["capabilities"] = taginfo->capabilities;
|
||||||
tag["modecfgjson"] = taginfo->modeConfigJson;
|
tag["modecfgjson"] = taginfo->modeConfigJson;
|
||||||
tag["isexternal"] = taginfo->isExternal;
|
tag["isexternal"] = taginfo->isExternal;
|
||||||
|
tag["apip"] = taginfo->apIp.toString();
|
||||||
tag["rotate"] = taginfo->rotate;
|
tag["rotate"] = taginfo->rotate;
|
||||||
tag["lut"] = taginfo->lut;
|
tag["lut"] = taginfo->lut;
|
||||||
tag["ch"] = taginfo->currentChannel;
|
tag["ch"] = taginfo->currentChannel;
|
||||||
@@ -128,9 +128,11 @@ void saveDB(const String& filename) {
|
|||||||
const long t = millis();
|
const long t = millis();
|
||||||
|
|
||||||
Storage.begin();
|
Storage.begin();
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
fs::File file = contentFS->open(filename, "w");
|
fs::File file = contentFS->open(filename, "w");
|
||||||
if (!file) {
|
if (!file) {
|
||||||
Serial.println("saveDB: Failed to open file");
|
Serial.println("saveDB: Failed to open file");
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,6 +151,7 @@ void saveDB(const String& filename) {
|
|||||||
file.write(']');
|
file.write(']');
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
Serial.println("DB saved " + String(millis() - t) + "ms");
|
Serial.println("DB saved " + String(millis() - t) + "ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +171,7 @@ void loadDB(const String& filename) {
|
|||||||
bool parsing = true;
|
bool parsing = true;
|
||||||
|
|
||||||
if (readfile.find("[")) {
|
if (readfile.find("[")) {
|
||||||
StaticJsonDocument<1000> doc;
|
DynamicJsonDocument doc(1000);
|
||||||
while (parsing) {
|
while (parsing) {
|
||||||
DeserializationError err = deserializeJson(doc, readfile);
|
DeserializationError err = deserializeJson(doc, readfile);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
@@ -207,6 +210,7 @@ void loadDB(const String& filename) {
|
|||||||
taginfo->capabilities = tag["capabilities"];
|
taginfo->capabilities = tag["capabilities"];
|
||||||
taginfo->modeConfigJson = tag["modecfgjson"].as<String>();
|
taginfo->modeConfigJson = tag["modecfgjson"].as<String>();
|
||||||
taginfo->isExternal = tag["isexternal"].as<bool>();
|
taginfo->isExternal = tag["isexternal"].as<bool>();
|
||||||
|
taginfo->apIp.fromString(tag["apip"].as<String>());
|
||||||
taginfo->rotate = tag["rotate"] | 0;
|
taginfo->rotate = tag["rotate"] | 0;
|
||||||
taginfo->lut = tag["lut"] | 0;
|
taginfo->lut = tag["lut"] | 0;
|
||||||
taginfo->currentChannel = tag["ch"] | 0;
|
taginfo->currentChannel = tag["ch"] | 0;
|
||||||
@@ -313,6 +317,7 @@ void initAPconfig() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void saveAPconfig() {
|
void saveAPconfig() {
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
fs::File configFile = contentFS->open("/current/apconfig.json", "w");
|
fs::File configFile = contentFS->open("/current/apconfig.json", "w");
|
||||||
DynamicJsonDocument APconfig(500);
|
DynamicJsonDocument APconfig(500);
|
||||||
APconfig["channel"] = config.channel;
|
APconfig["channel"] = config.channel;
|
||||||
@@ -328,6 +333,7 @@ void saveAPconfig() {
|
|||||||
APconfig["sleeptime2"] = config.sleepTime2;
|
APconfig["sleeptime2"] = config.sleepTime2;
|
||||||
serializeJsonPretty(APconfig, configFile);
|
serializeJsonPretty(APconfig, configFile);
|
||||||
configFile.close();
|
configFile.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
HwType getHwType(const uint8_t id) {
|
HwType getHwType(const uint8_t id) {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ void UDPcomm::processPacket(AsyncUDPPacket packet) {
|
|||||||
espAvailDataReq adr;
|
espAvailDataReq adr;
|
||||||
memset(&adr, 0, sizeof(espAvailDataReq));
|
memset(&adr, 0, sizeof(espAvailDataReq));
|
||||||
memcpy(&adr, &packet.data()[1], std::min(packet.length() - 1, sizeof(espAvailDataReq)));
|
memcpy(&adr, &packet.data()[1], std::min(packet.length() - 1, sizeof(espAvailDataReq)));
|
||||||
processDataReq(&adr, false);
|
processDataReq(&adr, false, senderIP);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PKT_XFER_COMPLETE: {
|
case PKT_XFER_COMPLETE: {
|
||||||
|
|||||||
@@ -35,15 +35,6 @@ WifiManager wm;
|
|||||||
SemaphoreHandle_t wsMutex;
|
SemaphoreHandle_t wsMutex;
|
||||||
uint32_t lastssidscan = 0;
|
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) {
|
void wsLog(String text) {
|
||||||
StaticJsonDocument<250> doc;
|
StaticJsonDocument<250> doc;
|
||||||
doc["logMsg"] = text;
|
doc["logMsg"] = text;
|
||||||
@@ -77,20 +68,22 @@ void wsSendSysteminfo() {
|
|||||||
time_t now;
|
time_t now;
|
||||||
time(&now);
|
time(&now);
|
||||||
static int freeSpaceLastRun = 0;
|
static int freeSpaceLastRun = 0;
|
||||||
|
static size_t tagDBsize = 0;
|
||||||
static size_t freeSpace = Storage.freeSpace();
|
static size_t freeSpace = Storage.freeSpace();
|
||||||
sys["currtime"] = now;
|
sys["currtime"] = now;
|
||||||
sys["heap"] = ESP.getFreeHeap();
|
sys["heap"] = ESP.getFreeHeap();
|
||||||
sys["recordcount"] = tagDB.size();
|
sys["recordcount"] = tagDBsize;
|
||||||
sys["dbsize"] = dbSize();
|
sys["dbsize"] = dbSize();
|
||||||
if (millis() - freeSpaceLastRun > 30000) {
|
if (millis() - freeSpaceLastRun > 30000 || freeSpaceLastRun == 0) {
|
||||||
freeSpace = Storage.freeSpace();
|
freeSpace = Storage.freeSpace();
|
||||||
|
tagDBsize = tagDB.size();
|
||||||
freeSpaceLastRun = millis();
|
freeSpaceLastRun = millis();
|
||||||
}
|
}
|
||||||
sys["littlefsfree"] = freeSpace;
|
sys["littlefsfree"] = freeSpace;
|
||||||
sys["apstate"] = apInfo.state;
|
sys["apstate"] = apInfo.state;
|
||||||
sys["runstate"] = config.runStatus;
|
sys["runstate"] = config.runStatus;
|
||||||
#if !defined(CONFIG_IDF_TARGET_ESP32)
|
#if !defined(CONFIG_IDF_TARGET_ESP32)
|
||||||
sys["temp"] = temperatureRead();
|
// sys["temp"] = temperatureRead();
|
||||||
#endif
|
#endif
|
||||||
sys["rssi"] = WiFi.RSSI();
|
sys["rssi"] = WiFi.RSSI();
|
||||||
sys["wifistatus"] = WiFi.status();
|
sys["wifistatus"] = WiFi.status();
|
||||||
@@ -188,6 +181,7 @@ uint8_t wsClientCount() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void init_web() {
|
void init_web() {
|
||||||
|
wsMutex = xSemaphoreCreateMutex();
|
||||||
Storage.begin();
|
Storage.begin();
|
||||||
WiFi.mode(WIFI_STA);
|
WiFi.mode(WIFI_STA);
|
||||||
|
|
||||||
@@ -322,6 +316,7 @@ void init_web() {
|
|||||||
}
|
}
|
||||||
if (strcmp(cmdValue, "ledflash") == 0) {
|
if (strcmp(cmdValue, "ledflash") == 0) {
|
||||||
struct ledFlash flashData = {0};
|
struct ledFlash flashData = {0};
|
||||||
|
flashData.mode = 1;
|
||||||
flashData.flashDuration = 8;
|
flashData.flashDuration = 8;
|
||||||
flashData.color1 = 0x3C; // green
|
flashData.color1 = 0x3C; // green
|
||||||
flashData.color2 = 0xE4; // red
|
flashData.color2 = 0xE4; // red
|
||||||
@@ -338,6 +333,7 @@ void init_web() {
|
|||||||
}
|
}
|
||||||
if (strcmp(cmdValue, "ledflash_long") == 0) {
|
if (strcmp(cmdValue, "ledflash_long") == 0) {
|
||||||
struct ledFlash flashData = {0};
|
struct ledFlash flashData = {0};
|
||||||
|
flashData.mode = 1;
|
||||||
flashData.flashDuration = 15;
|
flashData.flashDuration = 15;
|
||||||
flashData.color1 = 0xE4; // red
|
flashData.color1 = 0xE4; // red
|
||||||
flashData.flashCount1 = 5;
|
flashData.flashCount1 = 5;
|
||||||
@@ -347,6 +343,12 @@ void init_web() {
|
|||||||
const uint8_t *payload = reinterpret_cast<const uint8_t *>(&flashData);
|
const uint8_t *payload = reinterpret_cast<const uint8_t *>(&flashData);
|
||||||
sendTagCommand(mac, CMD_DO_LEDFLASH, !taginfo->isExternal, payload);
|
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");
|
request->send(200, "text/plain", "Ok, done");
|
||||||
} else {
|
} else {
|
||||||
request->send(400, "text/plain", "Error: mac not found");
|
request->send(400, "text/plain", "Error: mac not found");
|
||||||
@@ -360,6 +362,7 @@ void init_web() {
|
|||||||
server.on("/led_flash", HTTP_GET, [](AsyncWebServerRequest *request) {
|
server.on("/led_flash", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
// color picker: https://roger-random.github.io/RGB332_color_wheel_three.js/
|
// 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 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)
|
// (flashDuration/color1,flashCount1,delay1/color2,flashCount2,delay2/color3,flashCount3,delay3/repeats)
|
||||||
if (request->hasParam("mac")) {
|
if (request->hasParam("mac")) {
|
||||||
String dst = request->getParam("mac")->value();
|
String dst = request->getParam("mac")->value();
|
||||||
@@ -597,6 +600,9 @@ void init_web() {
|
|||||||
request->send(404);
|
request->send(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "content-type");
|
||||||
|
|
||||||
server.begin();
|
server.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,6 +617,7 @@ void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index
|
|||||||
} else {
|
} else {
|
||||||
filename = "unknown.jpg";
|
filename = "unknown.jpg";
|
||||||
}
|
}
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
request->_tempFile = contentFS->open("/" + filename, "w");
|
request->_tempFile = contentFS->open("/" + filename, "w");
|
||||||
}
|
}
|
||||||
if (len) {
|
if (len) {
|
||||||
@@ -618,6 +625,7 @@ void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index
|
|||||||
}
|
}
|
||||||
if (final) {
|
if (final) {
|
||||||
request->_tempFile.close();
|
request->_tempFile.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
if (request->hasParam("mac", true)) {
|
if (request->hasParam("mac", true)) {
|
||||||
String dst = request->getParam("mac", true)->value();
|
String dst = request->getParam("mac", true)->value();
|
||||||
uint8_t mac[8];
|
uint8_t mac[8];
|
||||||
@@ -656,13 +664,16 @@ void doJsonUpload(AsyncWebServerRequest *request) {
|
|||||||
String dst = request->getParam("mac", true)->value();
|
String dst = request->getParam("mac", true)->value();
|
||||||
uint8_t mac[8];
|
uint8_t mac[8];
|
||||||
if (hex2mac(dst, mac)) {
|
if (hex2mac(dst, mac)) {
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
File file = LittleFS.open("/current/" + dst + ".json", "w");
|
File file = LittleFS.open("/current/" + dst + ".json", "w");
|
||||||
if (!file) {
|
if (!file) {
|
||||||
request->send(400, "text/plain", "Failed to create file");
|
request->send(400, "text/plain", "Failed to create file");
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
file.print(request->getParam("json", true)->value());
|
file.print(request->getParam("json", true)->value());
|
||||||
file.close();
|
file.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
tagRecord *taginfo = tagRecord::findByMAC(mac);
|
tagRecord *taginfo = tagRecord::findByMAC(mac);
|
||||||
if (taginfo != nullptr) {
|
if (taginfo != nullptr) {
|
||||||
uint32_t ttl = 0;
|
uint32_t ttl = 0;
|
||||||
|
|||||||
@@ -215,7 +215,6 @@ void WifiManager::pollSerial() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// temporary write some more debug info
|
|
||||||
void WifiManager::WiFiEvent(WiFiEvent_t event) {
|
void WifiManager::WiFiEvent(WiFiEvent_t event) {
|
||||||
Serial.printf("[WiFi-event %d] ", event);
|
Serial.printf("[WiFi-event %d] ", event);
|
||||||
String eventname="";
|
String eventname="";
|
||||||
@@ -259,8 +258,8 @@ void WifiManager::WiFiEvent(WiFiEvent_t event) {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
terminalLog(eventname);
|
if (eventname) terminalLog(eventname);
|
||||||
logLine("WiFi event [" + String(event) + "]: " + eventname);
|
// logLine("WiFi event [" + String(event) + "]: " + eventname);
|
||||||
}
|
}
|
||||||
|
|
||||||
// *** Improv
|
// *** Improv
|
||||||
|
|||||||
@@ -8,15 +8,419 @@
|
|||||||
<title>Open EPaper Link Access Point</title>
|
<title>Open EPaper Link Access Point</title>
|
||||||
<link rel="stylesheet" href="main.css" type="text/css" />
|
<link rel="stylesheet" href="main.css" type="text/css" />
|
||||||
<link rel="icon" type="image/vnd.icon" href="favicon.ico">
|
<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>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<div class="logo">Open EPaper Link Access Point</div>
|
<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>
|
</header>
|
||||||
|
|
||||||
<div id="configbox">
|
<form>
|
||||||
<div class="closebtn">✖</div>
|
<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>
|
<h3 id="cfgmac">00000000</h3>
|
||||||
<p>
|
<p>
|
||||||
<label for="cfgalias">Alias</label>
|
<label for="cfgalias">Alias</label>
|
||||||
@@ -50,183 +454,33 @@
|
|||||||
<button id="cfgscan">scan</button>
|
<button id="cfgscan">scan</button>
|
||||||
<button id="cfgdeepsleep">deep sleep</button>
|
<button id="cfgdeepsleep">deep sleep</button>
|
||||||
<button id="cfgreset">reset settings</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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p id="savebar">
|
<p id="savebar">
|
||||||
<span><input type="button" value="Save" id="cfgsave"></span>
|
<span><input type="button" value="Save" id="cfgsave"></span>
|
||||||
<span id="cfgmore" title="advanced options">▼</span>
|
<span id="cfgmore" title="advanced options">▼</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</dialog>
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<div id="apupdatebox">
|
<div id="apupdatebox">
|
||||||
<div class="closebtn">✖</div>
|
<div class="closebtn">✖</div>
|
||||||
<h3>Update dashboard</h3>
|
<h3>Update dashboard</h3>
|
||||||
<div id="easyupdate"></div>
|
<div id="easyupdate"></div>
|
||||||
<div id="advanceddiv">
|
<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 id="releasetable"></div>
|
||||||
<div>
|
<div>
|
||||||
<div id="rollbackOption" style="display:none"><button id="rollbackBtn">Rollback to previous firmware</button></div>
|
<div id="rollbackOption" style="display:none"><button id="rollbackBtn">Rollback to previous
|
||||||
<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>
|
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>
|
</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;">
|
style="display: none; position: absolute; background: white; border: 1px solid gray; padding: 0; list-style: none;">
|
||||||
</ul>
|
</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>
|
<script src="main.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
* {
|
* {
|
||||||
margin:0;
|
margin: 0;
|
||||||
padding:0;
|
padding: 0;
|
||||||
border:0;
|
border: 0;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
@@ -12,7 +12,8 @@
|
|||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body {
|
html,
|
||||||
|
body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,60 +25,263 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
height: 50px;
|
|
||||||
background-color: #646260;
|
background-color: #646260;
|
||||||
|
z-index: 999;
|
||||||
|
position: sticky;
|
||||||
|
top: 0px;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
nav>div:first-child>div:first-child {
|
||||||
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 {
|
|
||||||
flex-grow: 2;
|
flex-grow: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actionbox>div {
|
nav>div {
|
||||||
display:flex;
|
display: flex;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actionbox label {
|
nav label {
|
||||||
padding: 0px 5px;
|
padding: 0px 5px;
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
width: auto;
|
width: auto;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#rebootbutton, #updatebutton, #downloadDBbutton, #apconfigbutton, .filebutton {
|
footer {
|
||||||
padding: 2px 5px;
|
padding: 5px;
|
||||||
background-color: #cccccc;
|
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;
|
text-decoration: none;
|
||||||
color: black;
|
color: black;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
white-space: nowrap;
|
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 {
|
.columns div {
|
||||||
@@ -100,15 +304,17 @@ input {
|
|||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=button], button {
|
input[type=button],
|
||||||
|
button {
|
||||||
border: 0px;
|
border: 0px;
|
||||||
padding: 4px 10px;
|
padding: 4px 10px;
|
||||||
cursor:pointer;
|
cursor: pointer;
|
||||||
|
background-color: #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=button]:hover,
|
input[type=button]:hover,
|
||||||
button:hover {
|
button:hover {
|
||||||
background-color:#aaaaaa;
|
background-color: #aaaaaa;
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
@@ -117,7 +323,8 @@ select {
|
|||||||
border: solid 1px #cccccc;
|
border: solid 1px #cccccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
#configbox, #apconfigbox, #apupdatebox {
|
#apconfigbox,
|
||||||
|
#apupdatebox {
|
||||||
display: none;
|
display: none;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 65px;
|
top: 65px;
|
||||||
@@ -131,19 +338,35 @@ select {
|
|||||||
max-height: calc(100vh - 75px);
|
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;
|
padding: 5px;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
#configbox h3, #apconfigbox h3, #apupdatebox h3 {
|
#configbox h3,
|
||||||
|
#apconfigbox h3,
|
||||||
|
#apupdatebox h3 {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
#configbox input, #apconfigbox input {
|
#configbox input,
|
||||||
|
#apconfigbox input {
|
||||||
border: solid 1px #cccccc;
|
border: solid 1px #cccccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,31 +414,6 @@ select {
|
|||||||
background-color: #e6f0d3;
|
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 {
|
#apupdatebox {
|
||||||
background-color: #f0d0c8;
|
background-color: #f0d0c8;
|
||||||
width: 700px;
|
width: 700px;
|
||||||
@@ -228,7 +426,8 @@ select {
|
|||||||
padding: 2px 10px;
|
padding: 2px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.closebtn {
|
.closebtn,
|
||||||
|
.closebtn2 {
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
float: right;
|
float: right;
|
||||||
width: 19px;
|
width: 19px;
|
||||||
@@ -239,22 +438,21 @@ select {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logbox {
|
#logtab {
|
||||||
margin: 5px;
|
& img {
|
||||||
}
|
vertical-align: bottom;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.logbox p {
|
& label {
|
||||||
background-color: #ffffff;
|
width: inherit;
|
||||||
padding: 5px 10px;
|
vertical-align: top;
|
||||||
}
|
padding: 4px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.logbox img {
|
& input {
|
||||||
vertical-align: bottom;
|
vertical-align: text-bottom;
|
||||||
cursor:pointer;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.logbox #sysinfo {
|
|
||||||
float: right;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.blink-red {
|
.blink-red {
|
||||||
@@ -265,10 +463,11 @@ select {
|
|||||||
.taglist {
|
.taglist {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
margin: 0px 10px 30px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tagtemplate {
|
#tagtemplate {
|
||||||
display:none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tagcard {
|
.tagcard {
|
||||||
@@ -280,12 +479,12 @@ select {
|
|||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border: 1px solid #cccccc;
|
border: 1px solid #cccccc;
|
||||||
transition: box-shadow 0.3s ease;
|
transition: box-shadow 0.3s ease;
|
||||||
}
|
|
||||||
|
|
||||||
.tagcard:hover {
|
&:hover {
|
||||||
cursor:pointer;
|
cursor: pointer;
|
||||||
box-shadow: 7px 10px 52px -19px rgba(0, 0, 0, 0.63);
|
box-shadow: 7px 10px 52px -19px rgba(0, 0, 0, 0.63);
|
||||||
filter: brightness(1.02);
|
filter: brightness(1.02);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tagflash {
|
.tagflash {
|
||||||
@@ -305,30 +504,19 @@ select {
|
|||||||
margin: 0px 5px;
|
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 {
|
.currimg {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.currimg img, .currimg canvas {
|
.currimg img,
|
||||||
|
.currimg canvas {
|
||||||
max-width: 50px;
|
max-width: 50px;
|
||||||
border: 1px solid #c0c0c0;
|
border: 1px solid #c0c0c0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mac {
|
.mac {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
cursor:pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alias {
|
.alias {
|
||||||
@@ -344,6 +532,7 @@ select {
|
|||||||
font-size: .85em;
|
font-size: .85em;
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.received div {
|
.received div {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
@@ -354,16 +543,19 @@ select {
|
|||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lastseen, .nextcheckin, .nextupdate {
|
.lastseen,
|
||||||
|
.nextcheckin,
|
||||||
|
.nextupdate {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lastseen span,
|
.lastseen span,
|
||||||
.nextcheckin span,
|
.nextcheckin span,
|
||||||
.nextupdate span {
|
.nextupdate span {
|
||||||
width:105px;
|
width: 105px;
|
||||||
display:inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.corner {
|
.corner {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
@@ -383,7 +575,7 @@ select {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.warningicon {
|
.warningicon {
|
||||||
display:none;
|
display: none;
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
background-color: yellow;
|
background-color: yellow;
|
||||||
color: black;
|
color: black;
|
||||||
@@ -412,12 +604,24 @@ ul.messages li.new {
|
|||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mono {
|
||||||
|
font-family: monospace;
|
||||||
|
word-break: break-all;
|
||||||
|
background-color: #666;
|
||||||
|
color: #ccc;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
#paintbutton {
|
#paintbutton {
|
||||||
padding: 1px 3px;
|
padding: 1px 3px;
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
margin-left:12px;
|
margin-left: 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,6 +676,7 @@ ul.messages li.new {
|
|||||||
background-color: #dddddd;
|
background-color: #dddddd;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#buttonbar button {
|
#buttonbar button {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -513,7 +718,7 @@ ul.messages li.new {
|
|||||||
|
|
||||||
/* updatescreens */
|
/* updatescreens */
|
||||||
|
|
||||||
#easyupdate{
|
#easyupdate {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -578,7 +783,7 @@ ul.messages li.new {
|
|||||||
.console {
|
.console {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
font-family: 'lucida console','ui-monospace';
|
font-family: 'lucida console', 'ui-monospace';
|
||||||
color: white;
|
color: white;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
margin: 20px 0px;
|
margin: 20px 0px;
|
||||||
@@ -587,6 +792,7 @@ ul.messages li.new {
|
|||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
white-space: break-spaces;
|
white-space: break-spaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
.console div {
|
.console div {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
@@ -594,36 +800,62 @@ ul.messages li.new {
|
|||||||
/* media */
|
/* media */
|
||||||
|
|
||||||
@media(max-width: 460px) {
|
@media(max-width: 460px) {
|
||||||
.messages li div, ul.messages li div.date, ul.messages li div.message {
|
|
||||||
display:block;
|
.messages li div,
|
||||||
position:relative;
|
ul.messages li div.date,
|
||||||
|
ul.messages li div.message {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
left: auto;
|
left: auto;
|
||||||
}
|
}
|
||||||
.messages li div.message, li.pending {
|
|
||||||
|
.messages li div.message,
|
||||||
|
li.pending {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul.messages {
|
ul.messages {
|
||||||
padding-bottom: 4px;
|
padding-bottom: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes new {
|
@keyframes new {
|
||||||
0% { background-color: rgba(255, 255, 204, 1); }
|
0% {
|
||||||
50% { background-color: rgba(255, 255, 204, .5); }
|
background-color: rgba(255, 255, 204, 1);
|
||||||
100% { background-color: rgba(255, 255, 204, 0); }
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
background-color: rgba(255, 255, 204, .5);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
background-color: rgba(255, 255, 204, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes tagflash {
|
@keyframes tagflash {
|
||||||
0% { opacity: 1; }
|
0% {
|
||||||
50% { opacity: 0; }
|
opacity: 1;
|
||||||
100% { opacity: 1; }
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pending {
|
@keyframes pending {
|
||||||
0% { }
|
0% {}
|
||||||
50% { background-color: #d4d4f5;}
|
|
||||||
100% { }
|
50% {
|
||||||
|
background-color: #d4d4f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 480px) {
|
@media screen and (max-width: 480px) {
|
||||||
@@ -638,7 +870,7 @@ ul.messages li.new {
|
|||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logbox #sysinfo {
|
footer #sysinfo {
|
||||||
float: none;
|
float: none;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
@@ -682,4 +914,4 @@ ul.messages li.new {
|
|||||||
padding: 1px 1px;
|
padding: 1px 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,8 @@ const WAKEUP_REASON_NETWORK_SCAN = 0xFD;
|
|||||||
const WAKEUP_REASON_WDT_RESET = 0xFE;
|
const WAKEUP_REASON_WDT_RESET = 0xFE;
|
||||||
|
|
||||||
let tagTypes = {};
|
let tagTypes = {};
|
||||||
|
let apConfig = {};
|
||||||
|
let tagDB = {};
|
||||||
|
|
||||||
const apstate = [
|
const apstate = [
|
||||||
{ state: "offline", color: "red" },
|
{ state: "offline", color: "red" },
|
||||||
@@ -38,7 +40,22 @@ let socket;
|
|||||||
let finishedInitialLoading = false;
|
let finishedInitialLoading = false;
|
||||||
let getTagtypeBusy = 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 () {
|
window.addEventListener("load", function () {
|
||||||
|
initTabs();
|
||||||
fetch('/content_cards.json')
|
fetch('/content_cards.json')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
@@ -51,19 +68,38 @@ window.addEventListener("load", function () {
|
|||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
alert("I can't load /www/content_cards.json.\r\nHave you upload it to the data partition?");
|
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())
|
window.dispatchEvent(loadConfig);
|
||||||
.then(data => {
|
|
||||||
if (data.alias) {
|
|
||||||
$(".logo").innerHTML = data.alias;
|
|
||||||
this.document.title = data.alias;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
dropUpload();
|
dropUpload();
|
||||||
populateTimes($('#apcnight1'));
|
populateTimes($('#apcnight1'));
|
||||||
populateTimes($('#apcnight2'));
|
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) {
|
function loadTags(pos) {
|
||||||
fetch("/get_db?pos=" + pos)
|
fetch("/get_db?pos=" + pos)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
@@ -83,7 +119,10 @@ function connect() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.addEventListener("message", (event) => {
|
socket.addEventListener("message", (event) => {
|
||||||
// console.log(event.data)
|
if ($('#showdebug').checked) {
|
||||||
|
showMessage(event.data);
|
||||||
|
console.log(event.data);
|
||||||
|
}
|
||||||
const msg = JSON.parse(event.data);
|
const msg = JSON.parse(event.data);
|
||||||
if (msg.logMsg) {
|
if (msg.logMsg) {
|
||||||
showMessage(msg.logMsg, false);
|
showMessage(msg.logMsg, false);
|
||||||
@@ -104,17 +143,12 @@ function connect() {
|
|||||||
$("#apstatecolor").style.color = apstate[msg.sys.apstate].color;
|
$("#apstatecolor").style.color = apstate[msg.sys.apstate].color;
|
||||||
$("#apstate").innerHTML = apstate[msg.sys.apstate].state;
|
$("#apstate").innerHTML = apstate[msg.sys.apstate].state;
|
||||||
$("#runstate").innerHTML = runstate[msg.sys.runstate].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;
|
servertimediff = (Date.now() / 1000) - msg.sys.currtime;
|
||||||
}
|
}
|
||||||
if (msg.apitem) {
|
if (msg.apitem) {
|
||||||
let row = $("#aptable").insertRow();
|
populateAPCard(msg.apitem);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
if (msg.console) {
|
if (msg.console) {
|
||||||
if (otamodule && typeof (otamodule.print) === "function") {
|
if (otamodule && typeof (otamodule.print) === "function") {
|
||||||
@@ -146,6 +180,7 @@ function convertSize(bytes) {
|
|||||||
function processTags(tagArray) {
|
function processTags(tagArray) {
|
||||||
for (const element of tagArray) {
|
for (const element of tagArray) {
|
||||||
const tagmac = element.mac;
|
const tagmac = element.mac;
|
||||||
|
tagDB[tagmac] = element;
|
||||||
|
|
||||||
let div = $('#tag' + tagmac);
|
let div = $('#tag' + tagmac);
|
||||||
if (div == null) {
|
if (div == null) {
|
||||||
@@ -207,13 +242,18 @@ function processTags(tagArray) {
|
|||||||
$('#tag' + tagmac + ' .received').style.opacity = "0";
|
$('#tag' + tagmac + ' .received').style.opacity = "0";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element.contentMode == 20) {
|
if (!apConfig.preview || element.contentMode == 20) {
|
||||||
$('#tag' + tagmac + ' .tagimg').style.display = 'none';
|
$('#tag' + tagmac + ' .tagimg').style.display = 'none'
|
||||||
} else if (div.dataset.hash != element.hash && div.dataset.hwtype > -1 && (!element.isexternal || element.contentMode != 12)) {
|
} else if (div.dataset.hash != element.hash && div.dataset.hwtype > -1) {
|
||||||
loadImage(tagmac, '/current/' + tagmac + '.raw?' + element.hash);
|
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;
|
div.dataset.hash = element.hash;
|
||||||
}
|
}
|
||||||
if (element.isexternal && element.contentMode == 12) $('#tag' + tagmac + ' .tagimg').style.display = 'none';
|
|
||||||
|
|
||||||
if (element.nextupdate > 1672531200 && element.nextupdate != 3216153600) {
|
if (element.nextupdate > 1672531200 && element.nextupdate != 3216153600) {
|
||||||
const date = new Date(element.nextupdate * 1000);
|
const date = new Date(element.nextupdate * 1000);
|
||||||
@@ -284,9 +324,16 @@ function processTags(tagArray) {
|
|||||||
|
|
||||||
function updatecards() {
|
function updatecards() {
|
||||||
if (servertimediff > 1000000000) servertimediff = 0;
|
if (servertimediff > 1000000000) servertimediff = 0;
|
||||||
|
let tagcount = 0;
|
||||||
|
let pendingcount = 0;
|
||||||
|
let timeoutcount = 0;
|
||||||
|
let lowbattcount = 0;
|
||||||
|
|
||||||
$('#taglist').querySelectorAll('[data-mac]').forEach(item => {
|
$('#taglist').querySelectorAll('[data-mac]').forEach(item => {
|
||||||
let tagmac = item.dataset.mac;
|
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) {
|
if (item.dataset.lastseen && item.dataset.lastseen > (Date.now() / 1000) - servertimediff - 30 * 24 * 3600 * 60) {
|
||||||
let idletime = (Date.now() / 1000) - servertimediff - item.dataset.lastseen;
|
let idletime = (Date.now() / 1000) - servertimediff - item.dataset.lastseen;
|
||||||
$('#tag' + tagmac + ' .lastseen').innerHTML = "<span>last seen</span>" + displayTime(Math.floor(idletime)) + " ago";
|
$('#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 + ' .warningicon').style.display = 'inline-block';
|
||||||
$('#tag' + tagmac).classList.remove("tagpending")
|
$('#tag' + tagmac).classList.remove("tagpending")
|
||||||
$('#tag' + tagmac).style.background = '#e0e0a0';
|
$('#tag' + tagmac).style.background = '#e0e0a0';
|
||||||
|
timeoutcount++;
|
||||||
}
|
}
|
||||||
if (idletime > 24 * 3600) {
|
if (idletime > 24 * 3600) {
|
||||||
$('#tag' + tagmac).style.opacity = '.5';
|
$('#tag' + tagmac).style.opacity = '.5';
|
||||||
@@ -314,6 +362,11 @@ function updatecards() {
|
|||||||
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "";
|
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "";
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$('#dashboardTagCount').innerHTML = tagcount;
|
||||||
|
$('#dashboardPending').innerHTML = pendingcount;
|
||||||
|
$('#dashboardLowBatt').innerHTML = lowbattcount;
|
||||||
|
$('#dashboardTimeout').innerHTML = timeoutcount;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#clearlog').onclick = function () {
|
$('#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
|
//clicking on a tag: load config dialog for tag
|
||||||
$('#taglist').addEventListener("click", (event) => {
|
$('#taglist').addEventListener("click", (event) => {
|
||||||
let currentElement = event.target;
|
let currentElement = event.target;
|
||||||
@@ -365,7 +425,7 @@ function loadContentCard(mac) {
|
|||||||
$('#cfgrotate').value = tagdata.rotate;
|
$('#cfgrotate').value = tagdata.rotate;
|
||||||
$('#cfglut').value = tagdata.lut;
|
$('#cfglut').value = tagdata.lut;
|
||||||
$('#cfgmore').innerHTML = '▼';
|
$('#cfgmore').innerHTML = '▼';
|
||||||
$('#configbox').style.display = 'block';
|
$('#configbox').showModal();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,7 +487,7 @@ $('#cfgsave').onclick = function () {
|
|||||||
.catch(error => showMessage('Error: ' + error));
|
.catch(error => showMessage('Error: ' + error));
|
||||||
|
|
||||||
$('#advancedoptions').style.height = '0px';
|
$('#advancedoptions').style.height = '0px';
|
||||||
$('#configbox').style.display = 'none';
|
$('#configbox').close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendCmd(mac, cmd) {
|
function sendCmd(mac, cmd) {
|
||||||
@@ -446,7 +506,7 @@ function sendCmd(mac, cmd) {
|
|||||||
})
|
})
|
||||||
.catch(error => showMessage('Error: ' + error));
|
.catch(error => showMessage('Error: ' + error));
|
||||||
$('#advancedoptions').style.height = '0px';
|
$('#advancedoptions').style.height = '0px';
|
||||||
$('#configbox').style.display = 'none';
|
$('#configbox').close();
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#cfgdelete').onclick = function () {
|
$('#cfgdelete').onclick = function () {
|
||||||
@@ -477,7 +537,8 @@ $('#cfgreset').onclick = function () {
|
|||||||
sendCmd($('#cfgmac').dataset.mac, "reset");
|
sendCmd($('#cfgmac').dataset.mac, "reset");
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#rebootbutton').onclick = function () {
|
$('#rebootbutton').onclick = function (event) {
|
||||||
|
event.preventDefault();
|
||||||
showMessage("rebooting AP....", true);
|
showMessage("rebooting AP....", true);
|
||||||
fetch("/reboot", {
|
fetch("/reboot", {
|
||||||
method: "POST"
|
method: "POST"
|
||||||
@@ -485,29 +546,37 @@ $('#rebootbutton').onclick = function () {
|
|||||||
socket.close();
|
socket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#apconfigbutton').onclick = function () {
|
$('#configbox').addEventListener('click', (event) => {
|
||||||
let table = document.getElementById("aptable");
|
if (event.target.nodeName === 'DIALOG') {
|
||||||
const rowCount = table.rows.length;
|
$('#configbox').close();
|
||||||
for (let i = rowCount - 1; i > 0; i--) {
|
|
||||||
table.deleteRow(i);
|
|
||||||
}
|
}
|
||||||
fetch("/get_ap_config")
|
});
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
document.addEventListener("loadTab", function (event) {
|
||||||
$('#apcfgalias').value = data.alias;
|
activeTab = event.detail;
|
||||||
$('#apcfgchid').value = data.channel;
|
switch (event.detail) {
|
||||||
$("#apcfgledbrightness").value = data.led;
|
case 'configtab':
|
||||||
$("#apcfglanguage").value = data.language;
|
case 'aptab':
|
||||||
$("#apclatency").value = data.maxsleep;
|
fetch("/get_ap_config")
|
||||||
$("#apcpreventsleep").value = data.stopsleep;
|
.then(response => response.json())
|
||||||
$("#apcpreview").value = data.preview;
|
.then(data => {
|
||||||
$("#apcwifipower").value = data.wifipower;
|
apConfig = data;
|
||||||
$("#apctimezone").value = data.timezone;
|
$('#apcfgalias').value = data.alias;
|
||||||
$("#apcnight1").value = data.sleeptime1;
|
$('#apcfgchid').value = data.channel;
|
||||||
$("#apcnight2").value = data.sleeptime2;
|
$("#apcfgledbrightness").value = data.led;
|
||||||
})
|
$("#apcfglanguage").value = data.language;
|
||||||
$('#apconfigbox').style.display = 'block'
|
$("#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 () {
|
$('#apcfgsave').onclick = function () {
|
||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
@@ -522,19 +591,22 @@ $('#apcfgsave').onclick = function () {
|
|||||||
formData.append('timezone', $('#apctimezone').value);
|
formData.append('timezone', $('#apctimezone').value);
|
||||||
formData.append('sleeptime1', $('#apcnight1').value);
|
formData.append('sleeptime1', $('#apcnight1').value);
|
||||||
formData.append('sleeptime2', $('#apcnight2').value);
|
formData.append('sleeptime2', $('#apcnight2').value);
|
||||||
|
|
||||||
fetch("/save_apcfg", {
|
fetch("/save_apcfg", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: formData
|
body: formData
|
||||||
})
|
})
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
.then(data => showMessage(data))
|
.then(data => {
|
||||||
|
showMessage(data);
|
||||||
|
window.dispatchEvent(loadConfig);
|
||||||
|
$('#apcfgmsg').innerHTML = 'OK, Saved';
|
||||||
|
})
|
||||||
.catch(error => showMessage('Error: ' + error));
|
.catch(error => showMessage('Error: ' + error));
|
||||||
$(".logo").innerHTML = $('#apcfgalias').value;
|
|
||||||
$('#apconfigbox').style.display = 'none';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#updatebutton').onclick = function () {
|
$('#updatebutton').onclick = function (event) {
|
||||||
$('#apconfigbox').style.display = 'none';
|
event.preventDefault();
|
||||||
$('#apupdatebox').style.display = 'block';
|
$('#apupdatebox').style.display = 'block';
|
||||||
loadOTA();
|
loadOTA();
|
||||||
}
|
}
|
||||||
@@ -698,7 +770,9 @@ function showMessage(message, iserr) {
|
|||||||
const messages = $('#messages');
|
const messages = $('#messages');
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
const time = date.toLocaleTimeString('nl-NL', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
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>');
|
messages.insertAdjacentHTML("afterbegin", '<li class="new error">' + htmlEncode(time + ' ' + message) + '</li>');
|
||||||
} else {
|
} else {
|
||||||
messages.insertAdjacentHTML("afterbegin", '<li class="new">' + htmlEncode(time + ' ' + message) + '</li>');
|
messages.insertAdjacentHTML("afterbegin", '<li class="new">' + htmlEncode(time + ' ' + message) + '</li>');
|
||||||
@@ -865,12 +939,18 @@ function GroupSortFilter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let show = true;
|
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="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="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="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="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';
|
if (!show) item.style.display = 'none'; else item.style.display = 'block';
|
||||||
item.style.order = order++;
|
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'));
|
headItems = Array.from($('#taglist').getElementsByClassName('taggroup'));
|
||||||
@@ -879,7 +959,7 @@ function GroupSortFilter() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#toggleFilters').addEventListener('click', () => {
|
$('#toggleFilters').addEventListener('click', (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const filterOptions = $('#filterOptions');
|
const filterOptions = $('#filterOptions');
|
||||||
filterOptions.classList.toggle('active');
|
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) {
|
async function getTagtype(hwtype) {
|
||||||
if (tagTypes[hwtype]) {
|
if (tagTypes[hwtype]) {
|
||||||
return tagTypes[hwtype];
|
return tagTypes[hwtype];
|
||||||
@@ -1094,7 +1181,8 @@ $('#taglist').addEventListener('contextmenu', (e) => {
|
|||||||
if (tagTypes[hwtype].options?.includes("led")) {
|
if (tagTypes[hwtype].options?.includes("led")) {
|
||||||
contextMenuOptions.push(
|
contextMenuOptions.push(
|
||||||
{ id: 'ledflash', label: 'Flash the LED' },
|
{ 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(
|
contextMenuOptions.push(
|
||||||
@@ -1133,4 +1221,83 @@ function populateTimes(element) {
|
|||||||
option.text = i.toString().padStart(2, "0") + ":00";
|
option.text = i.toString().padStart(2, "0") + ":00";
|
||||||
element.appendChild(option);
|
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
|
## 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'.
|
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
|
## OEPL-Flasher.py
|
||||||
|
|
||||||
|
|||||||
@@ -174,13 +174,16 @@ struct ledFlash {
|
|||||||
uint8_t flashDuration : 4;
|
uint8_t flashDuration : 4;
|
||||||
uint8_t color1;
|
uint8_t color1;
|
||||||
uint8_t flashCount1 : 4;
|
uint8_t flashCount1 : 4;
|
||||||
uint8_t delay1 : 4;
|
uint8_t flashSpeed1 : 4;
|
||||||
|
uint8_t delay1;
|
||||||
uint8_t color2;
|
uint8_t color2;
|
||||||
uint8_t flashCount2 : 4;
|
uint8_t flashCount2 : 4;
|
||||||
uint8_t delay2 : 4;
|
uint8_t flashSpeed2 : 4;
|
||||||
|
uint8_t delay2;
|
||||||
uint8_t color3;
|
uint8_t color3;
|
||||||
uint8_t flashCount3 : 4;
|
uint8_t flashCount3 : 4;
|
||||||
uint8_t delay3 : 4;
|
uint8_t flashSpeed3 : 4;
|
||||||
|
uint8_t delay3;
|
||||||
uint8_t repeats;
|
uint8_t repeats;
|
||||||
uint8_t spare;
|
uint8_t spare;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|||||||
Reference in New Issue
Block a user