50 favourite slots; favourite lists show only used slots
Save-to-slot preselects the first empty slot; the favourites cache lives in PSRAM.
This commit is contained in:
1 parent
89dfdf09a6
commit
1ea7b13bc7
4 files changed
+36
-20
No files matched your search
@@ -60,8 +60,8 @@ click a number to edit it (turn changes it, click confirms), hold to go back.
|
||||
Entries:
|
||||
|
||||
* **Save as favourite / Remove from favourites** — the camera that was playing;
|
||||
saving asks for one of the 24 slots (overwrites it).
|
||||
* **Favourites** — the 24 slots; click one to play it. A saved camera shows a
|
||||
saving asks for one of the 50 slots (it jumps to the first empty one; picking a used slot overwrites it).
|
||||
* **Favourites** — the saved cameras; click one to play it. A saved camera shows a
|
||||
yellow *FAV n* marker while it plays.
|
||||
* **Source** — *Random* draws from the sites, *Favourites* cycles the saved slots.
|
||||
* **Provider** — *insecam*, *opentopia* or *Mixed* (a random site per batch).
|
||||
@@ -120,7 +120,7 @@ it stays out of the way. The CC1101 board's BQ27220 gauge is not supported.
|
||||
elapses or the knob moves, skip dead cameras, keep history for "previous".
|
||||
* `ui.c` is LVGL 9 via `esp_lvgl_port`: a canvas for the frame, a translucent
|
||||
caption bar, a LIVE/PAUSED badge, a hairline progress bar and a six-row menu page.
|
||||
* `settings.c` keeps the settings blob and the 24 favourite slots in NVS
|
||||
* `settings.c` keeps the settings blob and the 50 favourite slots in NVS
|
||||
(namespace `widget`); `menu.c` is the knob-driven menu that edits them;
|
||||
`power.c` is the deep-sleep power-off with ext1 wake on the encoder pins.
|
||||
|
||||
|
||||
+27
-14
@@ -25,6 +25,7 @@ enum { ROW_FAV_TOGGLE, ROW_FAVS, ROW_SOURCE, ROW_PROVIDER, ROW_INTERVAL, ROW_LIS
|
||||
static ui_menu_row_t rows[TOP_ROWS > FAV_SLOTS + 1 ? TOP_ROWS : FAV_SLOTS + 1];
|
||||
static char values[TOP_ROWS][20];
|
||||
static char slot_labels[FAV_SLOTS][4];
|
||||
static int row_slot[FAV_SLOTS + 1]; // list row -> favourite slot (-1 = Back)
|
||||
|
||||
static const char *listing_name(uint8_t l)
|
||||
{
|
||||
@@ -65,16 +66,26 @@ static int build_top(const cam_info_t *current, int fav_slot)
|
||||
return TOP_ROWS;
|
||||
}
|
||||
|
||||
// Slot list: "1 .. 12" with the place name, then "Back".
|
||||
static int build_slots(void)
|
||||
// Slot list with the place names, then "Back". filled_only hides empty slots.
|
||||
static int build_slots(bool filled_only)
|
||||
{
|
||||
int n = 0;
|
||||
for (int i = 0; i < FAV_SLOTS; i++) {
|
||||
const cam_info_t *c = fav_get(i);
|
||||
if (filled_only && !c) continue;
|
||||
snprintf(slot_labels[i], sizeof slot_labels[i], "%d", i + 1);
|
||||
rows[i] = (ui_menu_row_t){slot_labels[i], c ? c->place : "empty", c == NULL};
|
||||
rows[n] = (ui_menu_row_t){slot_labels[i], c ? c->place : "empty", c == NULL};
|
||||
row_slot[n++] = i;
|
||||
}
|
||||
rows[FAV_SLOTS] = (ui_menu_row_t){"Back", NULL, false};
|
||||
return FAV_SLOTS + 1;
|
||||
rows[n] = (ui_menu_row_t){"Back", NULL, false};
|
||||
row_slot[n++] = -1;
|
||||
return n;
|
||||
}
|
||||
|
||||
static int first_empty_slot(void)
|
||||
{
|
||||
for (int i = 0; i < FAV_SLOTS; i++) if (!fav_get(i)) return i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clampi(int v, int lo, int hi) { return v < lo ? lo : v > hi ? hi : v; }
|
||||
@@ -92,7 +103,7 @@ menu_result_t menu_run(const cam_info_t *current, int *fav_slot_out)
|
||||
const char *title = "insecam widget";
|
||||
if (view == VIEW_TOP) count = build_top(current, fav_slot);
|
||||
else {
|
||||
count = build_slots();
|
||||
count = build_slots(view != VIEW_FAV_SAVE);
|
||||
title = view == VIEW_FAV_PLAY ? "Favourites" : view == VIEW_FAV_SAVE ? "Save to slot" : "Forget favourite";
|
||||
}
|
||||
sel = clampi(sel, 0, count - 1);
|
||||
@@ -134,26 +145,28 @@ menu_result_t menu_run(const cam_info_t *current, int *fav_slot_out)
|
||||
|
||||
// --- click
|
||||
if (view != VIEW_TOP) {
|
||||
if (sel == FAV_SLOTS) { view = VIEW_TOP; sel = top = 0; continue; }
|
||||
const cam_info_t *c = fav_get(sel);
|
||||
int slot = row_slot[sel];
|
||||
if (slot < 0) { view = VIEW_TOP; sel = top = 0; continue; }
|
||||
const cam_info_t *c = fav_get(slot);
|
||||
char msg[48];
|
||||
if (view == VIEW_FAV_SAVE) {
|
||||
if (current) {
|
||||
fav_set(sel, current);
|
||||
snprintf(msg, sizeof msg, "Saved to slot %d", sel + 1);
|
||||
fav_set(slot, current);
|
||||
snprintf(msg, sizeof msg, "Saved to slot %d", slot + 1);
|
||||
ui_toast(msg, 1500);
|
||||
}
|
||||
view = VIEW_TOP; sel = top = 0;
|
||||
} else if (view == VIEW_FAV_PLAY) {
|
||||
if (!c) { ui_toast("Empty slot", 1000); continue; }
|
||||
*fav_slot_out = sel;
|
||||
*fav_slot_out = slot;
|
||||
result = MENU_PLAY_FAV;
|
||||
goto out;
|
||||
} else { // VIEW_FAV_FORGET
|
||||
if (!c) { ui_toast("Empty slot", 1000); continue; }
|
||||
fav_clear(sel);
|
||||
snprintf(msg, sizeof msg, "Forgot slot %d", sel + 1);
|
||||
fav_clear(slot);
|
||||
snprintf(msg, sizeof msg, "Forgot slot %d", slot + 1);
|
||||
ui_toast(msg, 1500);
|
||||
if (fav_count() == 0) { view = VIEW_TOP; sel = top = 0; }
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -161,7 +174,7 @@ menu_result_t menu_run(const cam_info_t *current, int *fav_slot_out)
|
||||
case ROW_FAV_TOGGLE:
|
||||
if (!current) break;
|
||||
if (fav_slot >= 0) { fav_clear(fav_slot); ui_toast("Removed from favourites", 1500); }
|
||||
else { view = VIEW_FAV_SAVE; sel = top = 0; }
|
||||
else { view = VIEW_FAV_SAVE; sel = first_empty_slot(); top = 0; }
|
||||
break;
|
||||
case ROW_FAVS: view = VIEW_FAV_PLAY; sel = top = 0; break;
|
||||
case ROW_FORGET: view = VIEW_FAV_FORGET; sel = top = 0; break;
|
||||
|
||||
+5
-2
@@ -3,13 +3,15 @@
|
||||
#include "esp_log.h"
|
||||
#include "nvs.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char *TAG = "settings";
|
||||
static nvs_handle_t nvs;
|
||||
settings_t settings;
|
||||
static cam_info_t favs[FAV_SLOTS];
|
||||
static cam_info_t *favs; // FAV_SLOTS entries, in PSRAM
|
||||
static bool fav_used[FAV_SLOTS];
|
||||
|
||||
static void slot_key(int slot, char *key) { snprintf(key, 8, "fav%d", slot); }
|
||||
@@ -49,10 +51,11 @@ void settings_init(void)
|
||||
if (settings.source > SOURCE_FAVORITES) settings.source = SOURCE_RANDOM;
|
||||
if (settings.provider > PROVIDER_PREF_MIXED) settings.provider = PROVIDER_PREF_MIXED;
|
||||
}
|
||||
favs = heap_caps_calloc(FAV_SLOTS, sizeof *favs, MALLOC_CAP_SPIRAM);
|
||||
assert(favs);
|
||||
for (int i = 0; i < FAV_SLOTS; i++) {
|
||||
char key[8];
|
||||
slot_key(i, key);
|
||||
memset(&favs[i], 0, sizeof favs[i]);
|
||||
len = sizeof favs[i];
|
||||
fav_used[i] = nvs_get_blob(nvs, key, &favs[i], &len) == ESP_OK && len <= sizeof favs[i] && favs[i].url[0];
|
||||
if (fav_used[i] && favs[i].provider > PROVIDER_OPENTOPIA) favs[i].provider = PROVIDER_INSECAM;
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ void settings_save(void);
|
||||
void settings_request_portal(void);
|
||||
bool settings_take_portal_request(void);
|
||||
|
||||
#define FAV_SLOTS 24
|
||||
#define FAV_SLOTS 50
|
||||
const cam_info_t *fav_get(int slot); // NULL when empty
|
||||
void fav_set(int slot, const cam_info_t *cam);
|
||||
void fav_clear(int slot);
|
||||
|
||||
Reference in new issue
Block a user