geocoding and other small improvements

- separated content types for 'not configured', 'static image', 'uploaded image' and 'home assistant' (which is all the same internally, but it confused the users to always see 'static image'
- changed channel reported by the AP to the ieee801.15.4 channel instead of the WiFi channel
- added geocoding to the 'location' fields (e.g. weather forecast). Now you can choose an ambiguous location from a dropdown.
- added 'firmware update rejected' wakeup reason
This commit is contained in:
Nic Limper
2024-01-07 19:32:56 +01:00
parent fcff10412d
commit 96e8fe8660
32 changed files with 220 additions and 79 deletions

View File

@@ -13,7 +13,7 @@
},
"shortlut": 2,
"options": ["button", "customlut"],
"contentids": [ 0, 1, 2, 3, 4, 5, 7, 10, 14, 15, 17, 18, 19, 20, 21],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 10, 14, 15, 17, 18, 19, 20, 21],
"template": {
"1": {
"weekday": [ 76, 9, "fonts/calibrib30" ],

View File

@@ -13,7 +13,7 @@
},
"shortlut": 2,
"options": ["button", "customlut"],
"contentids": [ 0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 15, 16, 17, 18, 19, 20, 21 ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 15, 16, 17, 18, 19, 20, 21 ],
"template": {
"1": {
"weekday": [ 148, -3, "Signika-SB.ttf", 60 ],

View File

@@ -13,7 +13,7 @@
},
"shortlut": 1,
"options": ["button"],
"contentids": [ 0, 1, 4, 5, 7, 8, 9, 10, 11, 17, 18, 19, 20],
"contentids": [ 22, 23, 1, 4, 5, 7, 8, 9, 10, 11, 17, 18, 19, 20],
"template": {
"1": {
"weekday": [ 200, 0, "Signika-SB.ttf", 70 ],

View File

@@ -13,7 +13,7 @@
},
"shortlut": 1,
"options": [],
"contentids": [ 0, 1, 4, 5, 7, 8, 9, 10, 11, 17, 18, 19, 20],
"contentids": [ 22, 23, 1, 4, 5, 7, 8, 9, 10, 11, 17, 18, 19, 20],
"template": {
"1": {
"weekday": [ 320, -5, "Signika-SB.ttf", 100 ],

View File

@@ -13,6 +13,6 @@
},
"shortlut": 0,
"options": ["button"],
"contentids": [ 0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, 20, 21],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, 20, 21],
"usetemplate": 1
}

View File

@@ -11,6 +11,6 @@
},
"shortlut": 0,
"options": ["button", "customlut"],
"contentids": [ 0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 15, 16, 17, 18, 19, 20, 21 ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 15, 16, 17, 18, 19, 20, 21 ],
"usetemplate": 1
}

View File

@@ -13,7 +13,7 @@
},
"shortlut": 0,
"options": ["button"],
"contentids": [ 0, 1, 5, 8, 9, 7, 19, 10, 11 ],
"contentids": [ 22, 23, 1, 5, 8, 9, 7, 19, 10, 11 ],
"usetemplate": 1,
"template": {
"1": {

View File

@@ -13,7 +13,7 @@
},
"shortlut": 0,
"options": ["button"],
"contentids": [ 0, 1, 2, 3, 4, 5, 7, 8, 9, 11, 17, 18, 19, 20],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 11, 17, 18, 19, 20],
"usetemplate": 1,
"template": {
"1": {

View File

@@ -13,6 +13,6 @@
},
"shortlut": 0,
"options": ["button"],
"contentids": [ 0, 1, 2, 3, 4, 8, 7, 19, 10, 21 ],
"contentids": [ 22, 23, 1, 2, 3, 4, 8, 7, 19, 10, 21 ],
"usetemplate": 0
}

View File

@@ -13,7 +13,7 @@
},
"shortlut": 0,
"options": ["button", "led"],
"contentids": [ 0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20],
"template": {
"1": {
"weekday": [148, 5, "Signika-SB.ttf", 60],

View File

@@ -13,7 +13,7 @@
},
"shortlut": 0,
"options": ["button", "led"],
"contentids": [ 0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20],
"usetemplate": 1,
"template": {
"1": {

View File

@@ -13,6 +13,6 @@
},
"shortlut": 1,
"options": ["led"],
"contentids": [ 0, 1, 4, 5, 7, 8, 9, 10, 11, 17, 18, 19, 20 ],
"contentids": [ 22, 23, 1, 4, 5, 7, 8, 9, 10, 11, 17, 18, 19, 20 ],
"usetemplate": 2
}

View File

@@ -11,7 +11,7 @@
"red": [ 255, 0, 0 ],
"gray": [ 150, 150, 150 ]
},
"contentids": [ 0, 1, 4, 5, 8, 9, 7, 19, 10, 11 ],
"contentids": [ 22, 23, 1, 4, 5, 8, 9, 7, 19, 10, 11 ],
"usetemplate": 1,
"template": {
"1": {

View File

@@ -13,6 +13,6 @@
},
"shortlut": 0,
"options": ["button", "led"],
"contentids": [ 0, 1, 4, 5, 7, 8, 9, 10, 11, 17, 18, 19, 20],
"contentids": [ 22, 23, 1, 4, 5, 7, 8, 9, 10, 11, 17, 18, 19, 20],
"usetemplate": 1
}

View File

@@ -13,6 +13,6 @@
},
"shortlut": 0,
"options": ["led"],
"contentids": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 17, 18, 19, 20 ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 17, 18, 19, 20 ],
"usetemplate": 1
}

View File

@@ -13,6 +13,6 @@
},
"shortlut": 0,
"options": ["led"],
"contentids": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 17, 18, 19, 20 ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 17, 18, 19, 20 ],
"usetemplate": 1
}

View File

@@ -13,6 +13,6 @@
},
"shortlut": 0,
"options": ["led"],
"contentids": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 17, 18, 19, 20 ],
"contentids": [ 22, 23, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 17, 18, 19, 20 ],
"usetemplate": 1
}

View File

@@ -13,7 +13,7 @@
},
"shortlut": 0,
"options": [],
"contentids": [ 0, 1, 2, 3, 4, 8, 7, 19, 10, 11, 21 ],
"contentids": [ 22, 1, 2, 3, 4, 8, 7, 19, 10, 11, 21 ],
"usetemplate": 1,
"template": {
"21": [

Binary file not shown.

View File

@@ -6,6 +6,7 @@
#define WAKEUP_REASON_NFC 3
#define WAKEUP_REASON_BUTTON1 4
#define WAKEUP_REASON_BUTTON2 5
#define WAKEUP_REASON_FAILED_OTA_FW 0xE0
#define WAKEUP_REASON_FIRSTBOOT 0xFC
#define WAKEUP_REASON_NETWORK_SCAN 0xFD
#define WAKEUP_REASON_WDT_RESET 0xFE

View File

@@ -216,7 +216,11 @@ void drawNew(const uint8_t mac[8], const bool buttonPressed, tagRecord *&taginfo
}
switch (taginfo->contentMode) {
case 0: // Image
case 0: // Not configured
case 22: // Static image
case 23: // Static image (advanced)
case 24: // External image
case 25: // Home Assistant
{
String configFilename = cfgobj["filename"].as<String>();
if (!util::isEmptyOrNull(configFilename)) {

View File

@@ -63,7 +63,7 @@ void sendAvail(uint8_t wakeupReason) {
WiFi.macAddress(mac);
memcpy(&eadr.src, mac, 6);
eadr.adr.lastPacketRSSI = WiFi.RSSI();
eadr.adr.currentChannel = WiFi.channel();
eadr.adr.currentChannel = config.channel;
eadr.adr.hwType = 0xE0;
eadr.adr.wakeupReason = wakeupReason;
eadr.adr.capabilities = 0;

View File

@@ -513,6 +513,8 @@ void processDataReq(struct espAvailDataReq* eadr, bool local, IPAddress remoteIP
reason = "Network scan";
else if (eadr->adr.wakeupReason == WAKEUP_REASON_WDT_RESET)
reason = "Watchdog reset";
else if (eadr->adr.wakeupReason == WAKEUP_REASON_FAILED_OTA_FW)
reason = "Firmware update rejected";
sprintf(buffer, "%02X%02X%02X%02X%02X%02X%02X%02X %s", eadr->src[7], eadr->src[6], eadr->src[5], eadr->src[4], eadr->src[3], eadr->src[2], eadr->src[1], eadr->src[0], reason);
logLine(buffer);
}

View File

@@ -734,7 +734,11 @@ void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index
}
}
taginfo->modeConfigJson = "{\"filename\":\"" + dst + ".jpg\",\"timetolive\":\"" + String(ttl) + "\",\"dither\":\"" + String(dither) + "\",\"delete\":\"1\", \"preload\":\"" + String(preload) + "\", \"preload_lut\":\"" + String(preloadlut) + "\", \"preload_type\":\"" + String(preloadtype) + "\"}";
taginfo->contentMode = 0;
if (request->hasParam("contentmode", true)) {
taginfo->contentMode = request->getParam("contentmode", true)->value().toInt();
} else {
taginfo->contentMode = 24;
}
taginfo->nextupdate = 0;
wsSendTaginfo(mac, SYNC_USERCFG);
request->send(200, "text/plain", "Ok, saved");

View File

@@ -1,8 +1,14 @@
[
{
"id": 0,
"name": "",
"desc": "Not configured",
"param": []
},
{
"id": 22,
"name": "Static image",
"desc": "Shows a static image, from file system, painter or external source. Make sure to resize the image to the correct resolution.",
"desc": "Shows a static image, from file system or painter. Make sure to resize the image to the correct resolution.",
"param": [
{
"key": "filename",
@@ -10,12 +16,6 @@
"desc": "Local filename on the littlefs drive",
"type": "jpgfile"
},
{
"key": "timetolive",
"name": "TimeToLive",
"desc": "Amount (minutes) that this image will stay valid. The tag might not respond meanwhile",
"type": "int"
},
{
"key": "dither",
"name": "Dithering",
@@ -25,50 +25,21 @@
"0": "off",
"1": "on"
}
},
{
"key": "preload",
"name": "Display or Preload",
"desc": "Display now or preload for later use",
"type": "select",
"options": {
"0": "Display",
"1": "Preload"
}
},
{
"key": "preload_lut",
"name": "Preload LUT",
"desc": "Triggered preload images will display with this LUT",
"type": "select",
"options": {
"0": "Default",
"1": "No Repeats",
"2": "Fast No Reds",
"3": "Fast"
}
},
{
"key": "preload_type",
"name": "Preload Image type",
"desc": "Preload type to send to tag, for later recall, or special use",
"type": "select",
"options": {
"0": "Normal",
"1": "UI: Splash Screen",
"2": "UI: Lost connection",
"3": "UI: AP Found",
"4": "UI: No AP Found",
"5": "UI: Long Term Sleep",
"15": "Slideshow image",
"16": "Wake: Button 1",
"17": "Wake: Button 2",
"29": "Wake: GPIO",
"30": "Wake: NFC"
}
}
]
},
{
"id": 24,
"name": "External image",
"desc": "Image from external source",
"param": [ ]
},
{
"id": 25,
"name": "Home Assistant",
"desc": "Image from Home Assistant",
"param": [ ]
},
{
"id": 1,
"name": "Current date",
@@ -116,13 +87,13 @@
{
"id": 4,
"name": "Current weather",
"desc": "Current weather. Weather data by Open-Meteo.com. Parameters Lat, Lon and Time Zone are filled automatically from the entered location. In case of an ambiguous location, you can alter those manually.",
"desc": "Current weather. Weather data by Open-Meteo.com. Parameters Lat, Lon and Time Zone are filled automatically from the entered location. In case of an ambiguous location, choose from the drop down list.",
"param": [
{
"key": "location",
"name": "Location",
"desc": "Name of the city. This is used to lookup the lat/long data, and to display as the title",
"type": "text"
"type": "geoselect"
},
{
"key": "units",
@@ -163,7 +134,7 @@
"key": "location",
"name": "Location",
"desc": "Name of the city. This is used to lookup the lat/long data, and to display as the title",
"type": "text"
"type": "geoselect"
},
{
"key": "units",
@@ -204,7 +175,7 @@
"key": "location",
"name": "Location",
"desc": "Name of the city. This is used to lookup the lat/long data, and to display as the title",
"type": "text"
"type": "geoselect"
},
{
"key": "#lat",
@@ -525,6 +496,76 @@
}
]
},
{
"id": 23,
"name": "Image preload",
"desc": "Preloads an image to be shown at different situations. Make sure to resize the image to the correct resolution.",
"param": [
{
"key": "filename",
"name": "Filename",
"desc": "Local filename on the littlefs drive",
"type": "jpgfile"
},
{
"key": "timetolive",
"name": "TimeToLive",
"desc": "Amount (minutes) that this image will stay valid. The tag might not respond meanwhile",
"type": "int"
},
{
"key": "dither",
"name": "Dithering",
"desc": "Turn halftone dithering on or off. Turn it on when displaying photos. For texts, you better leave if off",
"type": "select",
"options": {
"0": "off",
"1": "on"
}
},
{
"key": "preload",
"name": "Display or Preload",
"desc": "Display now or preload for later use",
"type": "select",
"options": {
"0": "Display",
"1": "Preload"
}
},
{
"key": "preload_lut",
"name": "Preload LUT",
"desc": "Triggered preload images will display with this LUT",
"type": "select",
"options": {
"0": "Default",
"1": "No Repeats",
"2": "Fast No Reds",
"3": "Fast"
}
},
{
"key": "preload_type",
"name": "Preload Image type",
"desc": "Preload type to send to tag, for later recall, or special use",
"type": "select",
"options": {
"0": "Normal",
"1": "UI: Splash Screen",
"2": "UI: Lost connection",
"3": "UI: AP Found",
"4": "UI: No AP Found",
"5": "UI: Long Term Sleep",
"15": "Slideshow image",
"16": "Wake: Button 1",
"17": "Wake: Button 2",
"29": "Wake: GPIO",
"30": "Wake: NFC"
}
}
]
},
{
"id": 21,
"name": "Access point info",

View File

@@ -675,6 +675,22 @@ ul.messages li.new {
color: white;
}
#georesults {
position: absolute;
background: white;
cursor: pointer;
width: 240px;
max-height: 115px;
overflow-y: scroll;
padding: 0px 2px 0px 2px;
& div {
display:flex;
}
& div:hover {
background-color: #e0e0e0;
}
}
#paintbutton {
padding: 1px 3px;
border: 1px solid black;

View File

@@ -6,6 +6,7 @@ const WAKEUP_REASON_GPIO = 2;
const WAKEUP_REASON_NFC = 3;
const WAKEUP_REASON_BUTTON1 = 4;
const WAKEUP_REASON_BUTTON2 = 5;
const WAKEUP_REASON_FAILED_OTA_FW = 0xE0;
const WAKEUP_REASON_FIRSTBOOT = 0xFC;
const WAKEUP_REASON_NETWORK_SCAN = 0xFD;
const WAKEUP_REASON_WDT_RESET = 0xFE;
@@ -326,6 +327,10 @@ function processTags(tagArray) {
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "Watchdog reset!"
$('#tag' + tagmac).style.background = "#d0a0a0";
break;
case WAKEUP_REASON_FAILED_OTA_FW:
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "Firmware update rejected!"
$('#tag' + tagmac).style.background = "#f0a0a0";
break;
}
$('#tag' + tagmac + ' .pendingicon').style.display = (element.pending ? 'inline-block' : 'none');
div.classList.add("tagflash");
@@ -482,8 +487,8 @@ $('#cfgsave').onclick = function() {
if (contentMode) {
extraoptions?.forEach(element => {
if ($('#opt' + element.key)) {
obj[element.key] = $('#opt' + element.key).value;
if (document.getElementById('opt' + element.key)) {
obj[element.key] = document.getElementById('opt' + element.key).value;
}
});
formData.append("contentmode", contentMode);
@@ -614,9 +619,6 @@ $('#cfgautoupdate').onclick = async function() {
else showMessage('Error: auto update failed');
formData.append("contentmode", 5);
formData.append("modecfgjson", JSON.stringify(obj));
formData.append("rotate", $('#cfgrotate').value);
formData.append("lut", $('#cfglut').value);
formData.append("invert", $('#cfginvert').value);
fetch("/save_cfg", {
method: "POST",
body: formData
@@ -786,7 +788,7 @@ function contentselected() {
if (contentDef) {
$('#customoptions').innerHTML = "<p>" + contentDef?.desc + "</p>"
}
$('#paintbutton').style.display = (contentMode == 0 ? 'inline-block' : 'none');
$('#paintbutton').style.display = (contentMode == 22 || contentMode == 23 ? 'inline-block' : 'none');
let extraoptions = contentDef?.param ?? null;
extraoptions?.forEach(element => {
let label = document.createElement("label");
@@ -849,6 +851,11 @@ function contentselected() {
input.appendChild(optionElement);
}
break;
case 'geoselect':
input.type = "text";
input.classList.add("geoselect");
input.setAttribute("autocomplete", "off");
break;
}
input.id = 'opt' + element.key;
input.title = element.desc;
@@ -856,6 +863,12 @@ function contentselected() {
let p = document.createElement("p");
p.appendChild(label);
p.appendChild(input);
if (element.type == 'geoselect') {
input.addEventListener('input', debounce(searchLocations, 300));
const resultsContainer = document.createElement('div');
resultsContainer.id = 'georesults';
p.appendChild(resultsContainer);
}
$('#customoptions').appendChild(p);
});
}
@@ -1469,3 +1482,62 @@ function setFilterAndShow(filter) {
GroupSortFilter();
$(`[data-target='tagtab']`).click();
}
// geocoding typeahead
async function searchLocations() {
const query = $(".geoselect").value.trim();
document.getElementById('opt#lat').value = '';
document.getElementById('opt#lon').value = '';
if (document.getElementById('opt#tz')) document.getElementById('opt#tz').value = '';
if (query.length === 0) {
$('#georesults').innerHTML = '';
return;
}
try {
const response = await fetch(`https://geocoding-api.open-meteo.com/v1/search?name=${query}&count=10&language=en&format=json`);
const data = await response.json();
displayResults(data.results);
} catch (error) {
console.error('Error fetching data:', error);
}
}
function displayResults(results) {
$('#georesults').innerHTML = '';
$('#georesults').style.top = $(".geoselect").offsetTop + $(".geoselect").offsetHeight + "px";
$('#georesults').style.left = $(".geoselect").offsetLeft + 'px';
// $('#georesults').style.width = $(".geoselect").offsetWidth + 'px';
if (results) {
results.forEach(result => {
const option = document.createElement('div');
option.textContent = result.name + ', ' + result.admin1 + ', ' + result.country ;
option.addEventListener('click', () => selectLocation(result));
$('#georesults').appendChild(option);
});
}
}
function selectLocation(location) {
$(".geoselect").value = location.name;
document.getElementById('opt#lat').value = location.latitude;
document.getElementById('opt#lon').value = location.longitude;
if (document.getElementById('opt#tz')) document.getElementById('opt#tz').value = location.timezone;
$('#georesults').innerHTML = '';
console.log('Selected location:', location);
}
function debounce(func, delay) {
let timeoutId;
return function () {
const context = this;
const args = arguments;
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(context, args);
}, delay);
};
}

View File

@@ -100,6 +100,7 @@ function startPainter(mac, width, height) {
const formData = new FormData();
formData.append('mac', mac);
formData.append('dither', '0');
formData.append('contentmode', '22');
formData.append('file', binaryImage, 'image.jpg');
const xhr = new XMLHttpRequest();
xhr.open('POST', '/imgupload');