add contacts settings in settings

This commit is contained in:
pelgraine
2026-03-01 14:11:39 +11:00
parent 1dfab7d9a6
commit 513715e472
+247 -51
View File
@@ -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: autoA, networkN, userU, 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;