mirror of
https://github.com/pelgraine/Meck.git
synced 2026-07-04 16:51:20 +02:00
add contacts settings in settings
This commit is contained in:
@@ -15,6 +15,24 @@ class UITask;
|
||||
class MyMesh;
|
||||
extern MyMesh the_mesh;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Auto-add config bitmask (mirrored from MyMesh.cpp for UI access)
|
||||
// ---------------------------------------------------------------------------
|
||||
#define AUTO_ADD_OVERWRITE_OLDEST (1 << 0) // 0x01 - overwrite oldest non-favourite when full
|
||||
#define AUTO_ADD_CHAT (1 << 1) // 0x02 - auto-add Chat (Companion) (ADV_TYPE_CHAT)
|
||||
#define AUTO_ADD_REPEATER (1 << 2) // 0x04 - auto-add Repeater (ADV_TYPE_REPEATER)
|
||||
#define AUTO_ADD_ROOM_SERVER (1 << 3) // 0x08 - auto-add Room Server (ADV_TYPE_ROOM)
|
||||
#define AUTO_ADD_SENSOR (1 << 4) // 0x10 - auto-add Sensor (ADV_TYPE_SENSOR)
|
||||
|
||||
// All type bits combined (excludes overwrite flag)
|
||||
#define AUTO_ADD_ALL_TYPES (AUTO_ADD_CHAT | AUTO_ADD_REPEATER | AUTO_ADD_ROOM_SERVER | AUTO_ADD_SENSOR)
|
||||
|
||||
// Contact mode indices for picker
|
||||
#define CONTACT_MODE_AUTO_ALL 0 // Add all contacts automatically
|
||||
#define CONTACT_MODE_CUSTOM 1 // Per-type toggles
|
||||
#define CONTACT_MODE_MANUAL 2 // No auto-add, companion app only
|
||||
#define CONTACT_MODE_COUNT 3
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Radio presets
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -51,28 +69,35 @@ static const RadioPreset RADIO_PRESETS[] = {
|
||||
// Settings row types
|
||||
// ---------------------------------------------------------------------------
|
||||
enum SettingsRowType : uint8_t {
|
||||
ROW_NAME, // Device name (text editor)
|
||||
ROW_RADIO_PRESET, // Radio preset picker
|
||||
ROW_FREQ, // Frequency (float)
|
||||
ROW_BW, // Bandwidth (float)
|
||||
ROW_SF, // Spreading factor (5-12)
|
||||
ROW_CR, // Coding rate (5-8)
|
||||
ROW_TX_POWER, // TX power (1-20 dBm)
|
||||
ROW_UTC_OFFSET, // UTC offset (-12 to +14)
|
||||
ROW_MSG_NOTIFY, // Keyboard flash on new msg toggle
|
||||
ROW_NAME, // Device name (text editor)
|
||||
ROW_RADIO_PRESET, // Radio preset picker
|
||||
ROW_FREQ, // Frequency (float)
|
||||
ROW_BW, // Bandwidth (float)
|
||||
ROW_SF, // Spreading factor (5-12)
|
||||
ROW_CR, // Coding rate (5-8)
|
||||
ROW_TX_POWER, // TX power (1-20 dBm)
|
||||
ROW_UTC_OFFSET, // UTC offset (-12 to +14)
|
||||
ROW_MSG_NOTIFY, // Keyboard flash on new msg toggle
|
||||
#ifdef HAS_4G_MODEM
|
||||
ROW_MODEM_TOGGLE, // 4G modem enable/disable toggle (4G builds only)
|
||||
ROW_MODEM_TOGGLE, // 4G modem enable/disable toggle (4G builds only)
|
||||
#endif
|
||||
ROW_CH_HEADER, // "--- Channels ---" separator
|
||||
ROW_CHANNEL, // A channel entry (dynamic, index stored separately)
|
||||
ROW_ADD_CHANNEL, // "+ Add Hashtag Channel"
|
||||
ROW_INFO_HEADER, // "--- Info ---" separator
|
||||
ROW_PUB_KEY, // Public key display
|
||||
ROW_FIRMWARE, // Firmware version
|
||||
ROW_CONTACT_HEADER, // "--- Contacts ---" separator
|
||||
ROW_CONTACT_MODE, // Contact auto-add mode picker (Auto All / Custom / Manual)
|
||||
ROW_AUTOADD_CHAT, // Toggle: auto-add Chat clients
|
||||
ROW_AUTOADD_REPEATER, // Toggle: auto-add Repeaters
|
||||
ROW_AUTOADD_ROOM, // Toggle: auto-add Room Servers
|
||||
ROW_AUTOADD_SENSOR, // Toggle: auto-add Sensors
|
||||
ROW_AUTOADD_OVERWRITE, // Toggle: overwrite oldest non-favourite when full
|
||||
ROW_CH_HEADER, // "--- Channels ---" separator
|
||||
ROW_CHANNEL, // A channel entry (dynamic, index stored separately)
|
||||
ROW_ADD_CHANNEL, // "+ Add Hashtag Channel"
|
||||
ROW_INFO_HEADER, // "--- Info ---" separator
|
||||
ROW_PUB_KEY, // Public key display
|
||||
ROW_FIRMWARE, // Firmware version
|
||||
#ifdef HAS_4G_MODEM
|
||||
ROW_IMEI, // IMEI display (read-only)
|
||||
ROW_OPERATOR_INFO, // Carrier/operator display (read-only)
|
||||
ROW_APN, // APN setting (editable)
|
||||
ROW_IMEI, // IMEI display (read-only)
|
||||
ROW_OPERATOR_INFO, // Carrier/operator display (read-only)
|
||||
ROW_APN, // APN setting (editable)
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -82,16 +107,16 @@ enum SettingsRowType : uint8_t {
|
||||
enum EditMode : uint8_t {
|
||||
EDIT_NONE, // Just browsing
|
||||
EDIT_TEXT, // Typing into a text buffer (name, channel name)
|
||||
EDIT_PICKER, // A/D cycles options (radio preset)
|
||||
EDIT_PICKER, // A/D cycles options (radio preset, contact mode)
|
||||
EDIT_NUMBER, // W/S adjusts value (freq, BW, SF, CR, TX, UTC)
|
||||
EDIT_CONFIRM, // Confirmation dialog (delete channel, apply radio)
|
||||
};
|
||||
|
||||
// Max rows in the settings list
|
||||
// Max rows in the settings list (increased for contact sub-toggles)
|
||||
#ifdef HAS_4G_MODEM
|
||||
#define SETTINGS_MAX_ROWS 46 // Extra rows for IMEI, Carrier, APN
|
||||
#define SETTINGS_MAX_ROWS 54 // Extra rows for IMEI, Carrier, APN + contacts
|
||||
#else
|
||||
#define SETTINGS_MAX_ROWS 40
|
||||
#define SETTINGS_MAX_ROWS 48
|
||||
#endif
|
||||
#define SETTINGS_TEXT_BUF 33 // 32 chars + null
|
||||
|
||||
@@ -101,7 +126,7 @@ private:
|
||||
mesh::RTCClock* _rtc;
|
||||
NodePrefs* _prefs;
|
||||
|
||||
// Row table  rebuilt whenever channels change
|
||||
// Row table — rebuilt whenever channels or contact mode change
|
||||
struct Row {
|
||||
SettingsRowType type;
|
||||
uint8_t param; // channel index for ROW_CHANNEL, preset index for ROW_RADIO_PRESET
|
||||
@@ -117,7 +142,7 @@ private:
|
||||
EditMode _editMode;
|
||||
char _editBuf[SETTINGS_TEXT_BUF];
|
||||
int _editPos;
|
||||
int _editPickerIdx; // for preset picker
|
||||
int _editPickerIdx; // for preset picker / contact mode picker
|
||||
float _editFloat; // for freq/BW editing
|
||||
int _editInt; // for SF/CR/TX/UTC editing
|
||||
int _confirmAction; // 0=none, 1=delete channel, 2=apply radio
|
||||
@@ -125,7 +150,7 @@ private:
|
||||
// Onboarding mode
|
||||
bool _onboarding;
|
||||
|
||||
// Dirty flag for radio params  prompt to apply
|
||||
// Dirty flag for radio params — prompt to apply
|
||||
bool _radioChanged;
|
||||
|
||||
// 4G modem state (runtime cache of config)
|
||||
@@ -133,6 +158,57 @@ private:
|
||||
bool _modemEnabled;
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Contact mode helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Determine current contact mode from prefs
|
||||
int getContactMode() const {
|
||||
if ((_prefs->manual_add_contacts & 1) == 0) {
|
||||
return CONTACT_MODE_AUTO_ALL;
|
||||
}
|
||||
// manual_add_contacts bit 0 is set — check if any type bits are enabled
|
||||
if ((_prefs->autoadd_config & AUTO_ADD_ALL_TYPES) != 0) {
|
||||
return CONTACT_MODE_CUSTOM;
|
||||
}
|
||||
return CONTACT_MODE_MANUAL;
|
||||
}
|
||||
|
||||
// Get display label for a contact mode
|
||||
static const char* contactModeLabel(int mode) {
|
||||
switch (mode) {
|
||||
case CONTACT_MODE_AUTO_ALL: return "Auto All";
|
||||
case CONTACT_MODE_CUSTOM: return "Custom";
|
||||
case CONTACT_MODE_MANUAL: return "Manual Only";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
|
||||
// Apply a contact mode selection from picker
|
||||
void applyContactMode(int mode) {
|
||||
switch (mode) {
|
||||
case CONTACT_MODE_AUTO_ALL:
|
||||
_prefs->manual_add_contacts &= ~1; // clear bit 0 → auto all
|
||||
break;
|
||||
case CONTACT_MODE_CUSTOM:
|
||||
_prefs->manual_add_contacts |= 1; // set bit 0 → selective
|
||||
// If no type bits are set, default to all types enabled
|
||||
if ((_prefs->autoadd_config & AUTO_ADD_ALL_TYPES) == 0) {
|
||||
_prefs->autoadd_config |= AUTO_ADD_ALL_TYPES;
|
||||
}
|
||||
break;
|
||||
case CONTACT_MODE_MANUAL:
|
||||
_prefs->manual_add_contacts |= 1; // set bit 0 → selective
|
||||
_prefs->autoadd_config &= ~AUTO_ADD_ALL_TYPES; // clear all type bits
|
||||
// Note: keeps AUTO_ADD_OVERWRITE_OLDEST bit unchanged
|
||||
break;
|
||||
}
|
||||
the_mesh.savePrefs();
|
||||
rebuildRows(); // show/hide sub-toggles
|
||||
Serial.printf("Settings: Contact mode = %s (manual=%d, autoadd=0x%02X)\n",
|
||||
contactModeLabel(mode), _prefs->manual_add_contacts, _prefs->autoadd_config);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Row table management
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -152,6 +228,21 @@ private:
|
||||
#ifdef HAS_4G_MODEM
|
||||
addRow(ROW_MODEM_TOGGLE);
|
||||
#endif
|
||||
|
||||
// --- Contacts section ---
|
||||
addRow(ROW_CONTACT_HEADER);
|
||||
addRow(ROW_CONTACT_MODE);
|
||||
|
||||
// Show per-type sub-toggles only in Custom mode
|
||||
if (getContactMode() == CONTACT_MODE_CUSTOM) {
|
||||
addRow(ROW_AUTOADD_CHAT);
|
||||
addRow(ROW_AUTOADD_REPEATER);
|
||||
addRow(ROW_AUTOADD_ROOM);
|
||||
addRow(ROW_AUTOADD_SENSOR);
|
||||
addRow(ROW_AUTOADD_OVERWRITE);
|
||||
}
|
||||
|
||||
// --- Channels section ---
|
||||
addRow(ROW_CH_HEADER);
|
||||
|
||||
// Enumerate current channels
|
||||
@@ -192,7 +283,7 @@ private:
|
||||
bool isSelectable(int idx) const {
|
||||
if (idx < 0 || idx >= _numRows) return false;
|
||||
SettingsRowType t = _rows[idx].type;
|
||||
return t != ROW_CH_HEADER && t != ROW_INFO_HEADER
|
||||
return t != ROW_CH_HEADER && t != ROW_INFO_HEADER && t != ROW_CONTACT_HEADER
|
||||
#ifdef HAS_4G_MODEM
|
||||
&& t != ROW_IMEI && t != ROW_OPERATOR_INFO
|
||||
#endif
|
||||
@@ -246,11 +337,11 @@ private:
|
||||
strncpy(newCh.name, chanName, sizeof(newCh.name));
|
||||
newCh.name[31] = '\0';
|
||||
|
||||
// SHA-256 the channel name → first 16 bytes become the secret
|
||||
// SHA-256 the channel name → first 16 bytes become the secret
|
||||
uint8_t hash[32];
|
||||
mesh::Utils::sha256(hash, 32, (const uint8_t*)chanName, strlen(chanName));
|
||||
memcpy(newCh.channel.secret, hash, 16);
|
||||
// Upper 16 bytes left as zero → setChannel uses 128-bit mode
|
||||
// Upper 16 bytes left as zero → setChannel uses 128-bit mode
|
||||
|
||||
// Find next empty slot
|
||||
for (uint8_t i = 0; i < MAX_GROUP_CHANNELS; i++) {
|
||||
@@ -518,6 +609,54 @@ public:
|
||||
break;
|
||||
#endif
|
||||
|
||||
// --- Contacts section ---
|
||||
case ROW_CONTACT_HEADER:
|
||||
display.setColor(DisplayDriver::YELLOW);
|
||||
display.print("--- Contacts ---");
|
||||
break;
|
||||
|
||||
case ROW_CONTACT_MODE:
|
||||
if (editing && _editMode == EDIT_PICKER) {
|
||||
snprintf(tmp, sizeof(tmp), "< Add Mode: %s >",
|
||||
contactModeLabel(_editPickerIdx));
|
||||
} else {
|
||||
snprintf(tmp, sizeof(tmp), "Add Mode: %s",
|
||||
contactModeLabel(getContactMode()));
|
||||
}
|
||||
display.print(tmp);
|
||||
break;
|
||||
|
||||
case ROW_AUTOADD_CHAT:
|
||||
snprintf(tmp, sizeof(tmp), " Chat: %s",
|
||||
(_prefs->autoadd_config & AUTO_ADD_CHAT) ? "ON" : "OFF");
|
||||
display.print(tmp);
|
||||
break;
|
||||
|
||||
case ROW_AUTOADD_REPEATER:
|
||||
snprintf(tmp, sizeof(tmp), " Repeater: %s",
|
||||
(_prefs->autoadd_config & AUTO_ADD_REPEATER) ? "ON" : "OFF");
|
||||
display.print(tmp);
|
||||
break;
|
||||
|
||||
case ROW_AUTOADD_ROOM:
|
||||
snprintf(tmp, sizeof(tmp), " Room Server: %s",
|
||||
(_prefs->autoadd_config & AUTO_ADD_ROOM_SERVER) ? "ON" : "OFF");
|
||||
display.print(tmp);
|
||||
break;
|
||||
|
||||
case ROW_AUTOADD_SENSOR:
|
||||
snprintf(tmp, sizeof(tmp), " Sensor: %s",
|
||||
(_prefs->autoadd_config & AUTO_ADD_SENSOR) ? "ON" : "OFF");
|
||||
display.print(tmp);
|
||||
break;
|
||||
|
||||
case ROW_AUTOADD_OVERWRITE:
|
||||
snprintf(tmp, sizeof(tmp), " Overwrite Oldest: %s",
|
||||
(_prefs->autoadd_config & AUTO_ADD_OVERWRITE_OLDEST) ? "ON" : "OFF");
|
||||
display.print(tmp);
|
||||
break;
|
||||
|
||||
// --- Channels section ---
|
||||
case ROW_CH_HEADER:
|
||||
display.setColor(DisplayDriver::YELLOW);
|
||||
display.print("--- Channels ---");
|
||||
@@ -609,7 +748,7 @@ public:
|
||||
char apnShort[29];
|
||||
strncpy(apnShort, apn, 28);
|
||||
apnShort[28] = '\0';
|
||||
// Abbreviate source: auto→A, network→N, user→U, none→?
|
||||
// Abbreviate source: auto->A, network->N, user->U, none->?
|
||||
char srcChar = '?';
|
||||
if (strcmp(src, "auto") == 0) srcChar = 'A';
|
||||
else if (strcmp(src, "network") == 0) srcChar = 'N';
|
||||
@@ -775,34 +914,53 @@ public:
|
||||
return true; // consume all keys in text edit
|
||||
}
|
||||
|
||||
// --- Picker mode (radio preset) ---
|
||||
// --- Picker mode (radio preset or contact mode) ---
|
||||
if (_editMode == EDIT_PICKER) {
|
||||
SettingsRowType type = _rows[_cursor].type;
|
||||
|
||||
if (c == 'a' || c == 'A') {
|
||||
_editPickerIdx--;
|
||||
if (_editPickerIdx < 0) _editPickerIdx = (int)NUM_RADIO_PRESETS - 1;
|
||||
if (type == ROW_CONTACT_MODE) {
|
||||
_editPickerIdx--;
|
||||
if (_editPickerIdx < 0) _editPickerIdx = CONTACT_MODE_COUNT - 1;
|
||||
} else {
|
||||
// Radio preset
|
||||
_editPickerIdx--;
|
||||
if (_editPickerIdx < 0) _editPickerIdx = (int)NUM_RADIO_PRESETS - 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (c == 'd' || c == 'D') {
|
||||
_editPickerIdx++;
|
||||
if (_editPickerIdx >= (int)NUM_RADIO_PRESETS) _editPickerIdx = 0;
|
||||
if (type == ROW_CONTACT_MODE) {
|
||||
_editPickerIdx++;
|
||||
if (_editPickerIdx >= CONTACT_MODE_COUNT) _editPickerIdx = 0;
|
||||
} else {
|
||||
// Radio preset
|
||||
_editPickerIdx++;
|
||||
if (_editPickerIdx >= (int)NUM_RADIO_PRESETS) _editPickerIdx = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (c == '\r' || c == 13) {
|
||||
// Apply preset
|
||||
if (_editPickerIdx >= 0 && _editPickerIdx < (int)NUM_RADIO_PRESETS) {
|
||||
const RadioPreset& p = RADIO_PRESETS[_editPickerIdx];
|
||||
_prefs->freq = p.freq;
|
||||
_prefs->bw = p.bw;
|
||||
_prefs->sf = p.sf;
|
||||
_prefs->cr = p.cr;
|
||||
_prefs->tx_power_dbm = p.tx_power;
|
||||
_radioChanged = true;
|
||||
}
|
||||
_editMode = EDIT_NONE;
|
||||
if (_onboarding) {
|
||||
// Apply and finish onboarding
|
||||
applyRadioParams();
|
||||
_onboarding = false;
|
||||
if (type == ROW_CONTACT_MODE) {
|
||||
applyContactMode(_editPickerIdx);
|
||||
_editMode = EDIT_NONE;
|
||||
} else {
|
||||
// Apply radio preset
|
||||
if (_editPickerIdx >= 0 && _editPickerIdx < (int)NUM_RADIO_PRESETS) {
|
||||
const RadioPreset& p = RADIO_PRESETS[_editPickerIdx];
|
||||
_prefs->freq = p.freq;
|
||||
_prefs->bw = p.bw;
|
||||
_prefs->sf = p.sf;
|
||||
_prefs->cr = p.cr;
|
||||
_prefs->tx_power_dbm = p.tx_power;
|
||||
_radioChanged = true;
|
||||
}
|
||||
_editMode = EDIT_NONE;
|
||||
if (_onboarding) {
|
||||
// Apply and finish onboarding
|
||||
applyRadioParams();
|
||||
_onboarding = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -963,6 +1121,44 @@ public:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
// --- Contact mode picker ---
|
||||
case ROW_CONTACT_MODE:
|
||||
startEditPicker(getContactMode());
|
||||
break;
|
||||
|
||||
// --- Contact sub-toggles (flip bit and save) ---
|
||||
case ROW_AUTOADD_CHAT:
|
||||
_prefs->autoadd_config ^= AUTO_ADD_CHAT;
|
||||
the_mesh.savePrefs();
|
||||
Serial.printf("Settings: Auto-add Chat = %s\n",
|
||||
(_prefs->autoadd_config & AUTO_ADD_CHAT) ? "ON" : "OFF");
|
||||
break;
|
||||
case ROW_AUTOADD_REPEATER:
|
||||
_prefs->autoadd_config ^= AUTO_ADD_REPEATER;
|
||||
the_mesh.savePrefs();
|
||||
Serial.printf("Settings: Auto-add Repeater = %s\n",
|
||||
(_prefs->autoadd_config & AUTO_ADD_REPEATER) ? "ON" : "OFF");
|
||||
break;
|
||||
case ROW_AUTOADD_ROOM:
|
||||
_prefs->autoadd_config ^= AUTO_ADD_ROOM_SERVER;
|
||||
the_mesh.savePrefs();
|
||||
Serial.printf("Settings: Auto-add Room = %s\n",
|
||||
(_prefs->autoadd_config & AUTO_ADD_ROOM_SERVER) ? "ON" : "OFF");
|
||||
break;
|
||||
case ROW_AUTOADD_SENSOR:
|
||||
_prefs->autoadd_config ^= AUTO_ADD_SENSOR;
|
||||
the_mesh.savePrefs();
|
||||
Serial.printf("Settings: Auto-add Sensor = %s\n",
|
||||
(_prefs->autoadd_config & AUTO_ADD_SENSOR) ? "ON" : "OFF");
|
||||
break;
|
||||
case ROW_AUTOADD_OVERWRITE:
|
||||
_prefs->autoadd_config ^= AUTO_ADD_OVERWRITE_OLDEST;
|
||||
the_mesh.savePrefs();
|
||||
Serial.printf("Settings: Overwrite oldest = %s\n",
|
||||
(_prefs->autoadd_config & AUTO_ADD_OVERWRITE_OLDEST) ? "ON" : "OFF");
|
||||
break;
|
||||
|
||||
case ROW_ADD_CHANNEL:
|
||||
startEditText("");
|
||||
break;
|
||||
@@ -986,7 +1182,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Q: back  if radio changed, prompt to apply first
|
||||
// Q: back — if radio changed, prompt to apply first
|
||||
if (c == 'q' || c == 'Q') {
|
||||
if (_radioChanged) {
|
||||
_editMode = EDIT_CONFIRM;
|
||||
|
||||
Reference in New Issue
Block a user