#pragma once #include #include #include #include #include #include #ifdef PIN_BUZZER #include #endif #include "NodePrefs.h" enum class UIEventType { none, contactMessage, channelMessage, roomMessage, advertReceivedFlood, advertReceivedZeroHop, ack }; class AbstractUITask { protected: mesh::MainBoard* _board; BaseSerialInterface* _serial; bool _connected; AbstractUITask(mesh::MainBoard* board, BaseSerialInterface* serial) : _board(board), _serial(serial) { _connected = false; } public: void setHasConnection(bool connected) { bool prev = _connected; _connected = connected; if (prev && !connected) onBLEDisconnected(); } bool hasConnection() const { return _connected; } virtual void onBLEDisconnected() {} // An end-to-end ACK (CRC) arrived for one of our sent messages — drives the // DM delivery-status marker. Default no-op for UIs that don't track it. virtual void onMsgAck(uint32_t ack_crc) { (void)ack_crc; } // A repeater rebroadcast of one of our channel sends was heard (seq from // lastChannelRelaySeq()) — drives the channel "relayed into mesh" marker. virtual void onChannelRelayed(uint32_t seq) { (void)seq; } // Result of an on-device-UI-triggered MyMesh::sendRoomLogin() arrived. // pub_key is the contact's key prefix (>=4 bytes valid); permissions is the // room/repeater ACL byte (only meaningful when success is true). virtual void onRoomLoginResult(const uint8_t* pub_key, bool success, uint8_t permissions) { (void)pub_key; (void)success; (void)permissions; } // Text reply to an on-device-UI-triggered MyMesh::sendAdminCommand() arrived // (see AdminScreen). pub_key is the contact's key prefix (>=4 bytes valid). virtual void onAdminReply(const uint8_t* pub_key, const char* text) { (void)pub_key; (void)text; } // Bot action commands (!gps/!buzz, see MyMesh::botCommandReply) -- device // state changes triggered remotely, gated by the bot_actions_* prefs. // Default no-op so UI variants that don't wire these up just ignore them. virtual void botSetGPS(bool on) { (void)on; } virtual void botBuzz(int seconds) { (void)seconds; } // !gpio1..!gpio4 (idx 1-4). botSetGPIO returns false if the pin isn't // currently configured as an Output (or the board has none) -- lets the // bot reply distinguish "set" from "ignored". botGetGPIO returns false if // the pin is Off/unsupported; on true, fills is_output (current direction) // and value (live level). virtual bool botSetGPIO(int idx, bool on) { (void)idx; (void)on; return false; } virtual bool botGetGPIO(int idx, bool& is_output, bool& value) { (void)idx; (void)is_output; (void)value; return false; } // Analog read for pins that support it (GPIO1/GPIO2 on Wio Tracker L1 -- // the nRF52840's AIN0/AIN5). Returns false if the pin isn't in Analog mode // or doesn't support it; on true, fills millivolts with the reading. virtual bool botGetGPIOAnalog(int idx, int& millivolts) { (void)idx; (void)millivolts; return false; } // True only when a BLE central is actually bonded/connected. On a dual // (BLE+USB) interface hasConnection() is always true (USB counts), so use // this for BLE-specific UI like the pairing-PIN prompt. bool isBLEConnected() const { return _serial->isBLEConnected(); } // True when a companion app is connected over any transport (BLE bonded or an // open USB-CDC port). For app-connected behaviour like Auto buzzer mute. bool isClientConnected() const { return _serial->isClientConnected(); } uint16_t getBattMilliVolts() const { return _board->getBattMilliVolts(); } bool isSerialEnabled() const { return _serial->isEnabled(); } void enableSerial() { _serial->enable(); } void disableSerial() { _serial->disable(); } virtual void msgRead(int msgcount) = 0; virtual void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount, uint8_t contact_type = 0, const uint8_t* pub_key = nullptr) = 0; virtual void notify(UIEventType t = UIEventType::none) = 0; virtual void addChannelMsg(uint8_t channel_idx, const char* text, uint32_t timestamp = 0) {} virtual void addDMMsg(const uint8_t* pub_key, bool outgoing, const char* text, uint32_t sender_timestamp = 0) {} // A node shared its current position via a [LOC] message. pub_key is the // sender's key prefix for a verified DM share, or null for a channel share // (keyed by name, best-effort). Default no-op so UI variants opt in. virtual void onSharedLocation(const uint8_t* pub_key, const char* name, int32_t lat_1e6, int32_t lon_1e6, uint32_t ts, bool verified) {} // A contact is gone — removed explicitly (companion app / CLI command) or // silently auto-evicted to make room when the contact table is full. Lets // UI state that references contacts by pubkey (favourite slots, the // Locator/Live Share target) drop a reference that would otherwise dangle. // Default no-op. virtual void onContactRemoved(const uint8_t* pub_key) {} // A channel slot was cleared (companion app set it to an empty secret). // Drop any setting that referenced it by index — otherwise a new channel // added later at the same slot would silently inherit the old one's bot/ // share target or notification melody. Default no-op. virtual void onChannelRemoved(uint8_t channel_idx) {} virtual void loop() = 0; };