T-Watch: fix path editor screen setting for repeater admin via custom paths.

Fix haptic power for watch s3
This commit is contained in:
Pelgraine
2026-07-12 22:48:07 +10:00
parent 8b196b2d7a
commit 2dd1b24fdf
7 changed files with 119 additions and 21 deletions
+12
View File
@@ -1307,8 +1307,20 @@ bool MyMesh::onContactPathRecv(ContactInfo& contact, uint8_t* in_path, uint8_t i
// auto-discovery overwrite it. Skip the base class call entirely — ACKs
// embedded in path responses will still be delivered via separate ACK packets.
if (contact.flags & CONTACT_FLAG_CUSTOM_PATH) {
#if defined(MECK_TWATCH)
// Watch: keep the manually set path (don't let discovery overwrite it), but
// STILL deliver an embedded login/response reply riding on this path packet
// -- otherwise logins to custom-path contacts silently time out. Gated to
// the watch so other builds' admin flow is unchanged.
MESH_DEBUG_PRINTLN("onContactPathRecv: custom-path %s, keeping path, delivering response (type=%d len=%d)", contact.name, extra_type, extra_len);
if (extra_type == PAYLOAD_TYPE_RESPONSE && extra_len > 0) {
onContactResponse(contact, extra, extra_len);
}
return false;
#else
MESH_DEBUG_PRINTLN("onContactPathRecv: skipping path update for custom-path contact %s", contact.name);
return false;
#endif
}
return BaseChatMesh::onContactPathRecv(contact, in_path, in_path_len, out_path, out_path_len, extra_type, extra, extra_len);
}
@@ -57,6 +57,9 @@ private:
bool _dirty; // Path has been modified
bool _wantExit; // Set by Save & Exit — caller should navigate back
#if defined(MECK_TWATCH)
bool _wantKeyboard = false; // Set by Add hop -- caller opens the path keyboard
#endif
bool _directLocked; // True = path is explicitly set to direct (0 hops, locked)
// --- helpers ---
@@ -189,6 +192,9 @@ public:
_repScroll = 0;
_dirty = false;
_wantExit = false;
#if defined(MECK_TWATCH)
_wantKeyboard = false;
#endif
_directLocked = false;
// Load contact info
@@ -489,6 +495,61 @@ public:
return 5000;
}
#if defined(MECK_TWATCH)
// Watch: replace the whole path from a typed string of comma-separated hex
// hops. Each hop is _bytesPerHop bytes (2*_bytesPerHop hex chars), matching
// the current mode toggle -- e.g. 1B "36,21,dd", 2B "3601,2198,dddd", 3B
// "360184,2198a7,dddddd". Returns NULL on success, or an error message (the
// existing path is left unchanged on any error). UITask is only forward
// declared here, so the caller shows the alert rather than this method.
const char* applyComposedPath(const char* str) {
if (str == nullptr) return "Empty path";
const int expectChars = _bytesPerHop * 2;
uint8_t tmp[MAX_PATH_SIZE];
int nbytes = 0, hops = 0;
const char* p = str;
while (*p) {
char tok[16];
int tlen = 0;
while (*p && *p != ',') {
if (*p != ' ') {
if (tlen >= (int)sizeof(tok) - 1) return "Hop too long";
tok[tlen++] = *p;
}
p++;
}
if (*p == ',') p++;
if (tlen == 0) continue; // tolerate stray/trailing commas
if (tlen != expectChars) return "Wrong hop length";
if (hops >= 8) return "Max 8 hops";
for (int b = 0; b < _bytesPerHop; b++) {
int hi = hexNibble(tok[b * 2]);
int lo = hexNibble(tok[b * 2 + 1]);
if (hi < 0 || lo < 0) return "Bad hex";
if (nbytes >= MAX_PATH_SIZE) return "Path too long";
tmp[nbytes++] = (uint8_t)((hi << 4) | lo);
}
hops++;
}
if (hops == 0) return "Empty path";
memset(_pathBuf, 0, sizeof(_pathBuf));
memcpy(_pathBuf, tmp, nbytes);
_hopCount = hops;
_pathLen = encodePath();
_directLocked = false;
_dirty = true;
_menuCount = buildMenuCount();
_menuSel = 0;
return nullptr;
}
static int hexNibble(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return -1;
}
#endif
bool handleInput(char c) override {
if (_state == STATE_PICK_HOP) {
return handlePickerInput(c);
@@ -551,12 +612,19 @@ public:
return true;
case MENU_ADD_HOP:
// Enter picker mode — adding a hop clears direct lock
_directLocked = false;
#if defined(MECK_TWATCH)
// Watch: request the on-screen keyboard to type the whole path as
// comma-separated hex hops. main loop polls wantsKeyboard() and opens
// it (UITask is only forward declared here, so we can't call it).
_wantKeyboard = true;
#else
// Enter picker mode -- adding a hop clears direct lock
buildRepeaterList();
_repSel = 0;
_repScroll = 0;
_state = STATE_PICK_HOP;
#endif
return true;
case MENU_SET_DIRECT:
@@ -706,6 +774,11 @@ public:
EditorState getState() const { return _state; }
bool isDirty() const { return _dirty; }
bool wantsExit() const { return _wantExit; }
#if defined(MECK_TWATCH)
bool wantsKeyboard() const { return _wantKeyboard; }
void clearWantKeyboard() { _wantKeyboard = false; }
int getContactIdx() const { return _contactIdx; }
#endif
private:
void switchMode(int newBytesPerHop) {
@@ -2592,6 +2592,13 @@ if (curr) curr->poll();
gotoHomeScreen();
}
showAlert(dmSuccess ? "DM sent!" : "DM failed!", 1500);
} else if (purpose == TWatchKeyboardScreen::TWKB_PATH) {
PathEditorScreen* pe = (PathEditorScreen*)getPathEditorScreen();
const char* perr = pe ? pe->applyComposedPath(sendText) : "No editor";
kb->clearOutBuf();
if (path_editor) setCurrScreen(path_editor);
else gotoHomeScreen();
if (perr) showAlert(perr, 1500);
} else { // TWKB_ADMIN_PASSWORD or TWKB_ADMIN_CLI
RepeaterAdminScreen* admin = (RepeaterAdminScreen*)getRepeaterAdminScreen();
if (admin) {
@@ -2604,6 +2611,13 @@ if (curr) curr->poll();
}
}
}
else if (curr == path_editor && path_editor != nullptr) {
PathEditorScreen* pe = (PathEditorScreen*)path_editor;
if (pe->wantsKeyboard()) {
pe->clearWantKeyboard();
openTWatchKeyboard(TWatchKeyboardScreen::TWKB_PATH, pe->getContactIdx());
}
}
#endif
if (_display != NULL && _display->isOn()) {