mirror of
https://github.com/pelgraine/Meck.git
synced 2026-03-28 17:42:44 +01:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2163a4c56c | ||
|
|
a536196fd7 | ||
|
|
01a7ab80eb | ||
|
|
44fe5da876 | ||
|
|
652d853b0c |
@@ -1,4 +1,4 @@
|
||||
## SMS & Phone App (4G variant only) - Meck v0.9.3 (Alpha)
|
||||
## SMS & Phone App (4G variant only) - Meck v0.9.5
|
||||
|
||||
Press **T** from the home screen to open the SMS & Phone app.
|
||||
Requires a nano SIM card inserted in the T-Deck Pro V1.1 4G modem slot and an
|
||||
@@ -8,16 +8,31 @@ powered. The modem (and its red LED) can be switched off and on from the
|
||||
settings screen. After each modem startup, the system clock syncs from the
|
||||
cellular network, which takes roughly 15 seconds.
|
||||
|
||||
### App Menu
|
||||
|
||||
The SMS & Phone app opens to a landing screen with two options:
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| **Phone** | Open the phone dialer to call any number |
|
||||
| **SMS Inbox** | Open the SMS inbox for messaging and calling saved contacts |
|
||||
|
||||
Use **W / S** to select an option and **Enter** to confirm. Press **Q** to
|
||||
return to the home screen.
|
||||
|
||||
### Key Mapping
|
||||
|
||||
| Context | Key | Action |
|
||||
|---------|-----|--------|
|
||||
| Home screen | T | Open SMS & Phone app |
|
||||
| App menu | W / S | Select Phone or SMS Inbox |
|
||||
| App menu | Enter | Open selected option |
|
||||
| App menu | Q | Back to home screen |
|
||||
| Inbox | W / S | Scroll conversations |
|
||||
| Inbox | Enter | Open conversation |
|
||||
| Inbox | C | Compose new SMS (enter phone number) |
|
||||
| Inbox | D | Open contacts directory |
|
||||
| Inbox | Q | Back to home screen |
|
||||
| Inbox | Q | Back to app menu |
|
||||
| Conversation | W / S | Scroll messages |
|
||||
| Conversation | C | Reply to this conversation |
|
||||
| Conversation | F | Call this number |
|
||||
@@ -31,6 +46,10 @@ cellular network, which takes roughly 15 seconds.
|
||||
| Contacts | Q | Back to inbox |
|
||||
| Edit Contact | Enter | Save contact name |
|
||||
| Edit Contact | Shift+Del | Cancel without saving |
|
||||
| Phone Dialer | 0–9, *, +, # | Enter phone number (see input methods below) |
|
||||
| Phone Dialer | Enter | Place call |
|
||||
| Phone Dialer | Backspace | Delete last digit |
|
||||
| Phone Dialer | Q | Back to app menu |
|
||||
| Dialing | Enter or Q | Cancel / hang up |
|
||||
| Incoming Call | Enter | Answer call |
|
||||
| Incoming Call | Q | Reject call |
|
||||
@@ -55,23 +74,20 @@ shown in the footer while composing.
|
||||
|
||||
### Making a Phone Call
|
||||
|
||||
Press **F** to call from either the conversation view or the contacts
|
||||
directory. The display switches to a dialing screen showing the contact name
|
||||
(or phone number) and an animated progress indicator. Once the remote party
|
||||
answers, the screen transitions to the in-call view with a live call timer.
|
||||
There are three ways to start a call:
|
||||
|
||||
There are two ways to start a call:
|
||||
|
||||
1. **From a conversation** — open a conversation and press **F**. You can call
|
||||
1. **From the phone dialer** — select **Phone** from the app menu to open the
|
||||
dialer. Enter a phone number and press **Enter** to call. This is the
|
||||
easiest way to call a number you haven't messaged before.
|
||||
2. **From a conversation** — open a conversation and press **F**. You can call
|
||||
any number you have previously exchanged messages with, whether or not it is
|
||||
saved as a named contact.
|
||||
2. **From the contacts directory** — press **D** from the inbox, scroll to a
|
||||
3. **From the contacts directory** — press **D** from the inbox, scroll to a
|
||||
contact, and press **F**.
|
||||
|
||||
> **Note:** There is currently no way to dial an arbitrary phone number without
|
||||
> first creating a conversation. To call a new number, press **C** from the
|
||||
> inbox to compose a new SMS, enter the phone number, send a short message,
|
||||
> then open the resulting conversation and press **F** to call.
|
||||
The display switches to a dialing screen showing the contact name (or phone
|
||||
number) and an animated progress indicator. Once the remote party answers, the
|
||||
screen transitions to the in-call view with a live call timer.
|
||||
|
||||
During an active call, **W** and **S** adjust the speaker volume (0–5). The
|
||||
number keys **0–9**, **\***, and **#** send DTMF tones for navigating phone
|
||||
@@ -80,6 +96,26 @@ menus and voicemail systems. Press **Enter** or **Q** to hang up.
|
||||
Audio is routed through the A7682E modem's internal codec to the board speaker
|
||||
and microphone — no headphones or external audio hardware are required.
|
||||
|
||||
### Phone Dialer Input Methods
|
||||
|
||||
The phone dialer supports three ways to enter digits:
|
||||
|
||||
1. **Direct key press** — press the keyboard letter that corresponds to each
|
||||
number using the silk-screened labels on the T-Deck Pro keys:
|
||||
|
||||
| Key | Digit | | Key | Digit | | Key | Digit |
|
||||
|-----|-------|-|-----|-------|-|-----|-------|
|
||||
| W | 1 | | S | 4 | | Z | 7 |
|
||||
| E | 2 | | D | 5 | | X | 8 |
|
||||
| R | 3 | | F | 6 | | C | 9 |
|
||||
| A | * | | O | + | | Mic | 0 |
|
||||
|
||||
2. **Touchscreen tap** — tap the on-screen number buttons directly. Note: this
|
||||
currently requires fairly precise taps on the numbers themselves.
|
||||
|
||||
3. **Sym+key** — the standard symbol entry method (e.g. Sym+W for 1, Sym+S for
|
||||
4, etc.)
|
||||
|
||||
### Receiving a Phone Call
|
||||
|
||||
When an incoming call arrives, the app automatically switches to the incoming
|
||||
@@ -134,6 +170,12 @@ call screens. Bars are derived from the modem's CSQ (signal quality) reading,
|
||||
updated every 30 seconds. The modem state (REG, READY, OFF, etc.) is shown
|
||||
when not yet connected. During a call, the signal indicator remains visible.
|
||||
|
||||
### IMEI, Carrier & APN
|
||||
|
||||
The 4G modem's IMEI, current carrier name, and APN are displayed at the bottom
|
||||
of the settings screen (press **S** from the home screen), alongside your node
|
||||
ID and firmware version.
|
||||
|
||||
### SD Card Structure
|
||||
|
||||
```
|
||||
@@ -158,7 +200,6 @@ SD Card
|
||||
| SMS sends but no delivery | Check signal strength; below 5 bars is marginal. Move to better coverage |
|
||||
| Call drops immediately after dialing | Check signal strength and ensure the SIM plan supports voice calls |
|
||||
| No audio during call | The A7682E routes audio through its own codec; ensure the board speaker is not obstructed. Try adjusting volume with W/S |
|
||||
| Cannot dial a number | You must first have a conversation or saved contact for that number. Send a short SMS to create a conversation, then press F |
|
||||
|
||||
> **Note:** The SMS & Phone app is only available on the 4G modem variant of
|
||||
> the T-Deck Pro. It is not present on the audio or standalone BLE builds due
|
||||
|
||||
148
Web App Guide.md
148
Web App Guide.md
@@ -1,15 +1,97 @@
|
||||
# Web Reader - Integration Summary
|
||||
# Web Reader & IRC - Meck v0.9.5
|
||||
|
||||
### Conditional Compilation
|
||||
All web reader code is wrapped in `#ifdef MECK_WEB_READER` guards. The flag is set:
|
||||
- **meck_audio_ble**: Yes (`-D MECK_WEB_READER=1`) — WiFi available via BLE radio stack
|
||||
- **meck_4g_ble**: Yes (`-D MECK_WEB_READER=1`) — WiFi now, PPP via A7682E in future
|
||||
- **meck_audio_standalone**: No — excluded to preserve zero-radio-power design
|
||||
Press **B** from the home screen to open the web reader. The web reader is
|
||||
available on the BLE and 4G variants. It is excluded from the standalone audio
|
||||
variant to preserve zero-radio-power design.
|
||||
|
||||
### 4G Modem / PPP Support
|
||||
The web reader uses `isNetworkAvailable()` which checks both WiFi and (future) PPP connectivity. The `fetchPage()` method uses ESP32's standard `HTTPClient` which routes through whatever network interface is active — WiFi or PPP.
|
||||
The web reader home screen provides access to the **IRC client**, the **URL
|
||||
bar**, your **bookmarks**, and browsing **history**. Use **W / S** to navigate
|
||||
the list and **Enter** to select an item.
|
||||
|
||||
When PPP support is added to the 4G modem driver, the web reader will work over cellular automatically without code changes. The `isNetworkAvailable()` method has a `TODO` placeholder for the PPP status check.
|
||||
## Web Browser
|
||||
|
||||
A text-centric web browser ("reader mode") that fetches pages over WiFi,
|
||||
strips HTML to readable text, extracts links as numbered references, and
|
||||
paginates content for the e-ink display. Still very much in development, but
|
||||
already useful for text-heavy websites.
|
||||
|
||||
Includes basic web search via **DuckDuckGo Lite** — type a search query into
|
||||
the URL bar and it will be sent to DuckDuckGo.
|
||||
|
||||
### EPUB Downloads
|
||||
|
||||
If you follow a link to an `.epub` file, it will be saved directly to the
|
||||
`/books/` folder on your SD card. You can then read it in the e-book reader
|
||||
(press **E** from the home screen).
|
||||
|
||||
### Bookmarks
|
||||
|
||||
Press **K** while on a page to save a bookmark. Bookmarks appear on the web
|
||||
reader home screen below the URL bar. To delete a bookmark, open the browser
|
||||
home screen, scroll down to the bookmark, and press **Delete**.
|
||||
|
||||
### Cookies & History
|
||||
|
||||
Press **X** to clear cookies and browsing history.
|
||||
|
||||
---
|
||||
|
||||
## IRC Client
|
||||
|
||||
The IRC client lets you connect to IRC networks directly from the device. It
|
||||
is accessed from the web reader home screen — select **IRC Chat** (the first
|
||||
item) and press **Enter**.
|
||||
|
||||
If you are not currently connected, the IRC setup screen opens where you can
|
||||
configure the server, port, nickname, and channel. If you are already
|
||||
connected, you go straight to the chat view.
|
||||
|
||||
### IRC Setup
|
||||
|
||||
The setup screen has five fields. Use **W / S** to navigate between them and
|
||||
press **Enter** to edit a field (type the value, then **Enter** to confirm).
|
||||
|
||||
| Field | Description | Default |
|
||||
|-------|-------------|---------|
|
||||
| Host | IRC server hostname (e.g. `irc.libera.chat`) | — |
|
||||
| Port | Server port. Use `6697` for TLS or `6667` for plain | 6697 |
|
||||
| Nick | Your IRC nickname (max 16 characters) | — |
|
||||
| Channel | Channel to join, including the `#` (e.g. `#meshcore`) | — |
|
||||
| Connect | Select and press Enter to connect | — |
|
||||
|
||||
TLS is used automatically when the port is 6697. Other ports connect without
|
||||
encryption.
|
||||
|
||||
Configuration is saved to the SD card at `/web/irc.cfg` and restored on next
|
||||
launch, so you only need to enter server details once.
|
||||
|
||||
If WiFi is not connected when you press Connect, you'll be taken to the WiFi
|
||||
setup screen first.
|
||||
|
||||
### IRC Chat View
|
||||
|
||||
Once connected and joined to the channel, you'll see messages in a scrollable
|
||||
chat view. The channel name and connection status are shown at the top.
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| Enter | Start composing a message (type, then Enter to send) |
|
||||
| Backspace | Delete last character while composing; exit compose if empty |
|
||||
| W / S | Scroll up (older) / down (newer) through messages |
|
||||
| X | Disconnect from IRC and return to web reader home |
|
||||
| Q | Return to web reader home (connection stays alive in background) |
|
||||
|
||||
The IRC connection remains active when you press **Q** to go back to the web
|
||||
reader home screen. You'll see the connection status and channel name displayed
|
||||
on the IRC Chat line. Select it and press Enter to return to the chat. Press
|
||||
**X** from the chat view to disconnect.
|
||||
|
||||
The client automatically reconnects if the connection drops (10-second delay
|
||||
between attempts) and detects dead connections after 5 minutes of inactivity
|
||||
via ping timeout.
|
||||
|
||||
Messages are stored in a circular buffer of 64 messages. Older messages are
|
||||
discarded as new ones arrive.
|
||||
|
||||
---
|
||||
|
||||
@@ -23,8 +105,8 @@ When PPP support is added to the 4G modem driver, the web reader will work over
|
||||
### Web Reader - Home View
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `w` / `s` | Navigate up/down in bookmarks/history |
|
||||
| `Enter` | Select URL bar or bookmark/history item |
|
||||
| `w` / `s` | Navigate up/down in IRC / URL bar / bookmarks / history |
|
||||
| `Enter` | Select IRC Chat, activate URL bar, or open bookmark/history item |
|
||||
| Type | Enter URL (when URL bar is active) |
|
||||
| `q` | Exit to firmware home |
|
||||
|
||||
@@ -36,6 +118,7 @@ When PPP support is added to the 4G modem driver, the web reader will work over
|
||||
| `l` or `Enter` | Enter link selection (type link number) |
|
||||
| `g` | Go to new URL (return to web reader home) |
|
||||
| `k` | Bookmark current page |
|
||||
| `x` | Clear cookies and history |
|
||||
| `q` | Back to web reader home |
|
||||
|
||||
### Web Reader - WiFi Setup
|
||||
@@ -46,6 +129,37 @@ When PPP support is added to the 4G modem driver, the web reader will work over
|
||||
| Type | Enter WiFi password |
|
||||
| `q` | Back |
|
||||
|
||||
### IRC - Setup View
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `w` / `s` | Navigate fields (Host / Port / Nick / Channel / Connect) |
|
||||
| `Enter` | Edit selected field, or connect (when on Connect button) |
|
||||
| Type | Enter field value (when editing) |
|
||||
| `Backspace` | Delete last character (when editing) |
|
||||
| `q` | Back to web reader home |
|
||||
|
||||
### IRC - Chat View
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `Enter` | Start composing / send message |
|
||||
| `Backspace` | Delete character / exit compose if empty |
|
||||
| `w` / `s` | Scroll older / newer messages |
|
||||
| `x` | Disconnect and return to web reader home |
|
||||
| `q` | Back to web reader home (stays connected) |
|
||||
|
||||
---
|
||||
|
||||
## WiFi
|
||||
|
||||
The web reader and IRC client both use WiFi for network access. On first use,
|
||||
you'll be taken to the WiFi setup screen to scan for networks and enter a
|
||||
password. Credentials are saved to `/web/wifi.cfg` on the SD card and used for
|
||||
auto-reconnect on subsequent launches.
|
||||
|
||||
On the 4G variant, the web reader currently uses WiFi. A future update will add
|
||||
PPP support via the A7682E cellular modem, allowing the browser and IRC to work
|
||||
over cellular data without WiFi.
|
||||
|
||||
---
|
||||
|
||||
## SD Card Structure
|
||||
@@ -54,4 +168,14 @@ When PPP support is added to the 4G modem driver, the web reader will work over
|
||||
wifi.cfg - Saved WiFi credentials (auto-reconnect)
|
||||
bookmarks.txt - One URL per line
|
||||
history.txt - Recent URLs, newest first
|
||||
```
|
||||
irc.cfg - IRC server/port/nick/channel config
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Conditional Compilation
|
||||
All web reader code is wrapped in `#ifdef MECK_WEB_READER` guards. The flag is set:
|
||||
- **meck_audio_ble**: Yes (`-D MECK_WEB_READER=1`) — WiFi available via BLE radio stack
|
||||
- **meck_4g_ble**: Yes (`-D MECK_WEB_READER=1`) — WiFi now, PPP via A7682E in future
|
||||
- **meck_4g_standalone**: Yes (`-D MECK_WEB_READER=1`) — WiFi works better without BLE (no teardown needed, more free heap)
|
||||
- **meck_audio_standalone**: No — excluded to preserve zero-radio-power design
|
||||
@@ -48,7 +48,11 @@ public:
|
||||
virtual void forceRefresh() {}
|
||||
virtual void addSentChannelMessage(uint8_t channel_idx, const char* sender, const char* text) {}
|
||||
|
||||
// Mark a channel as read when BLE companion app syncs a message
|
||||
virtual void markChannelReadFromBLE(uint8_t channel_idx) {}
|
||||
|
||||
// Repeater admin callbacks (from MyMesh)
|
||||
virtual void onAdminLoginResult(bool success, uint8_t permissions, uint32_t server_time) {}
|
||||
virtual void onAdminCliResponse(const char* from_name, const char* text) {}
|
||||
virtual void onAdminTelemetryResult(const uint8_t* data, uint8_t len) {}
|
||||
};
|
||||
@@ -706,6 +706,29 @@ bool MyMesh::uiSendCliCommand(uint32_t contact_idx, const char* command) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MyMesh::uiSendTelemetryRequest(uint32_t contact_idx) {
|
||||
ContactInfo contact;
|
||||
if (!getContactByIdx(contact_idx, contact)) return false;
|
||||
|
||||
ContactInfo* recipient = lookupContactByPubKey(contact.id.pub_key, PUB_KEY_SIZE);
|
||||
if (!recipient) return false;
|
||||
|
||||
uint32_t tag, est_timeout;
|
||||
int result = sendRequest(*recipient, REQ_TYPE_GET_TELEMETRY_DATA, tag, est_timeout);
|
||||
if (result == MSG_SEND_FAILED) {
|
||||
MESH_DEBUG_PRINTLN("UI: Telemetry request send failed to %s", recipient->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
clearPendingReqs();
|
||||
pending_telemetry = tag;
|
||||
|
||||
MESH_DEBUG_PRINTLN("UI: Telemetry request sent to %s (%s), timeout=%dms",
|
||||
recipient->name, result == MSG_SEND_SENT_FLOOD ? "flood" : "direct",
|
||||
est_timeout);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t MyMesh::onContactRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data,
|
||||
uint8_t len, uint8_t *reply) {
|
||||
if (data[0] == REQ_TYPE_GET_TELEMETRY_DATA) {
|
||||
@@ -816,6 +839,7 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data,
|
||||
_serial->writeFrame(out_frame, i);
|
||||
} else if (len > 4 && tag == pending_telemetry) { // check for matching response tag
|
||||
pending_telemetry = 0;
|
||||
MESH_DEBUG_PRINTLN("Telemetry response received from %s, len=%d", contact.name, len);
|
||||
|
||||
int i = 0;
|
||||
out_frame[i++] = PUSH_CODE_TELEMETRY_RESPONSE;
|
||||
@@ -825,6 +849,11 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data,
|
||||
memcpy(&out_frame[i], &data[4], len - 4);
|
||||
i += (len - 4);
|
||||
_serial->writeFrame(out_frame, i);
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
// Route telemetry data to UI (LPP buffer after the 4-byte tag)
|
||||
if (_ui) _ui->onAdminTelemetryResult(&data[4], len - 4);
|
||||
#endif
|
||||
} else if (len > 4 && tag == pending_req) { // check for matching response tag
|
||||
pending_req = 0;
|
||||
|
||||
@@ -1381,7 +1410,19 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
if ((out_len = getFromOfflineQueue(out_frame)) > 0) {
|
||||
_serial->writeFrame(out_frame, out_len);
|
||||
#ifdef DISPLAY_CLASS
|
||||
if (_ui) _ui->msgRead(offline_queue_len);
|
||||
if (_ui) {
|
||||
_ui->msgRead(offline_queue_len);
|
||||
|
||||
// Mark channel as read when BLE companion app syncs the message.
|
||||
// Frame layout V3: [resp_code][snr][res1][res2][channel_idx][path_len]...
|
||||
// Frame layout V1: [resp_code][channel_idx][path_len]...
|
||||
bool is_v3_ch = (out_frame[0] == RESP_CODE_CHANNEL_MSG_RECV_V3);
|
||||
bool is_old_ch = (out_frame[0] == RESP_CODE_CHANNEL_MSG_RECV);
|
||||
if (is_v3_ch || is_old_ch) {
|
||||
uint8_t ch_idx = is_v3_ch ? out_frame[4] : out_frame[1];
|
||||
_ui->markChannelReadFromBLE(ch_idx);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
out_frame[0] = RESP_CODE_NO_MORE_MESSAGES;
|
||||
|
||||
@@ -111,6 +111,7 @@ public:
|
||||
// Repeater admin - UI-initiated operations
|
||||
bool uiLoginToRepeater(uint32_t contact_idx, const char* password);
|
||||
bool uiSendCliCommand(uint32_t contact_idx, const char* command);
|
||||
bool uiSendTelemetryRequest(uint32_t contact_idx);
|
||||
int getAdminContactIdx() const { return _admin_contact_idx; }
|
||||
|
||||
|
||||
|
||||
@@ -1156,7 +1156,7 @@ void handleKeyboardInput() {
|
||||
if (wasDM) {
|
||||
ui_task.gotoContactsScreen();
|
||||
} else {
|
||||
ui_task.gotoHomeScreen();
|
||||
ui_task.gotoChannelScreen();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1176,7 +1176,7 @@ void handleKeyboardInput() {
|
||||
if (wasDM) {
|
||||
ui_task.gotoContactsScreen();
|
||||
} else {
|
||||
ui_task.gotoHomeScreen();
|
||||
ui_task.gotoChannelScreen();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1484,8 +1484,8 @@ void handleKeyboardInput() {
|
||||
return;
|
||||
}
|
||||
|
||||
// In menu state: Shift+Del exits to contacts, C opens compose
|
||||
if (astate == RepeaterAdminScreen::STATE_MENU) {
|
||||
// In category menu (top level): Shift+Del exits to contacts, C opens compose
|
||||
if (astate == RepeaterAdminScreen::STATE_CATEGORY_MENU) {
|
||||
if (shiftDel) {
|
||||
Serial.println("Nav: Back to contacts from admin menu");
|
||||
ui_task.gotoContactsScreen();
|
||||
@@ -1507,8 +1507,9 @@ void handleKeyboardInput() {
|
||||
return;
|
||||
}
|
||||
|
||||
// In waiting/response/error states: convert Shift+Del to exit signal,
|
||||
// pass all other keys through
|
||||
// All other states (command menu, param entry, confirm, waiting,
|
||||
// response, error): convert Shift+Del to exit signal and let the
|
||||
// screen handle back-navigation internally
|
||||
if (shiftDel) {
|
||||
ui_task.injectKey(KEY_ADMIN_EXIT);
|
||||
} else {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1486,6 +1486,12 @@ void UITask::addSentChannelMessage(uint8_t channel_idx, const char* sender, cons
|
||||
((ChannelScreen *) channel_screen)->addMessage(channel_idx, 0, sender, formattedMsg);
|
||||
}
|
||||
|
||||
void UITask::markChannelReadFromBLE(uint8_t channel_idx) {
|
||||
((ChannelScreen *) channel_screen)->markChannelRead(channel_idx);
|
||||
// Trigger a refresh so the home screen unread count updates in real-time
|
||||
_next_refresh = millis() + 200;
|
||||
}
|
||||
|
||||
void UITask::gotoRepeaterAdmin(int contactIdx) {
|
||||
// Lazy-initialize on first use (same pattern as audiobook player)
|
||||
if (repeater_admin == nullptr) {
|
||||
@@ -1550,6 +1556,14 @@ void UITask::onAdminCliResponse(const char* from_name, const char* text) {
|
||||
}
|
||||
}
|
||||
|
||||
void UITask::onAdminTelemetryResult(const uint8_t* data, uint8_t len) {
|
||||
Serial.printf("[UITask] onAdminTelemetryResult: %d bytes, onAdmin=%d\n", len, isOnRepeaterAdmin());
|
||||
if (repeater_admin && isOnRepeaterAdmin()) {
|
||||
((RepeaterAdminScreen*)repeater_admin)->onTelemetryResult(data, len);
|
||||
_next_refresh = 100; // trigger re-render
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MECK_AUDIO_VARIANT
|
||||
bool UITask::isAudioPlayingInBackground() const {
|
||||
if (!audiobook_screen) return false;
|
||||
|
||||
@@ -155,9 +155,13 @@ public:
|
||||
// Add a sent message to the channel screen history
|
||||
void addSentChannelMessage(uint8_t channel_idx, const char* sender, const char* text) override;
|
||||
|
||||
// Mark channel as read when BLE companion app syncs messages
|
||||
void markChannelReadFromBLE(uint8_t channel_idx) override;
|
||||
|
||||
// Repeater admin callbacks
|
||||
void onAdminLoginResult(bool success, uint8_t permissions, uint32_t server_time) override;
|
||||
void onAdminCliResponse(const char* from_name, const char* text) override;
|
||||
void onAdminTelemetryResult(const uint8_t* data, uint8_t len) override;
|
||||
|
||||
// Get current screen for checking state
|
||||
UIScreen* getCurrentScreen() const { return curr; }
|
||||
|
||||
Reference in New Issue
Block a user