fix: init tabs advert quick add

ref:
This commit is contained in:
Janez T
2026-02-28 11:23:11 +01:00
parent baf49d27d8
commit 6bd7517180
32 changed files with 850 additions and 11558 deletions

View File

@@ -98,6 +98,8 @@ class BleResponseHandler {
OnChannelInfoCallback? onChannelInfoReceived;
OnMessageEchoDetectedCallback? onMessageEchoDetected;
VoidCallback? onRxActivity;
void Function(Uint8List publicKey)? onContactDeleted;
VoidCallback? onContactsFull;
// Track the last command that was sent, so we can retry if it fails with ERR_CODE_NOT_FOUND
Uint8List? _lastContactPublicKey;
@@ -183,6 +185,14 @@ class BleResponseHandler {
debugPrint(' → Handling ChannelMessage');
_handleChannelMessage(reader);
break;
case MeshCoreConstants.respContactMsgRecvV3:
debugPrint(' → Handling ContactMessage V3');
_handleContactMessageV3(reader);
break;
case MeshCoreConstants.respChannelMsgRecvV3:
debugPrint(' → Handling ChannelMessage V3');
_handleChannelMessageV3(reader);
break;
case MeshCoreConstants.pushTelemetryResponse:
debugPrint(' → Handling TelemetryResponse');
_handleTelemetryResponse(reader);
@@ -251,6 +261,20 @@ class BleResponseHandler {
debugPrint(' → Response: No More Messages');
onNoMoreMessages?.call();
break;
case MeshCoreConstants.pushPathDiscoveryResponse:
debugPrint(' → Path discovery response (not yet handled)');
break;
case MeshCoreConstants.pushControlData:
debugPrint(' → Control data push (not yet handled)');
break;
case MeshCoreConstants.pushContactDeleted:
debugPrint(' → Handling ContactDeleted push');
_handleContactDeleted(reader);
break;
case MeshCoreConstants.pushContactsFull:
debugPrint(' → Contacts storage full');
onContactsFull?.call();
break;
case MeshCoreConstants.respOk:
debugPrint(' → Response: OK');
// Complete any pending ACK command
@@ -349,6 +373,43 @@ class BleResponseHandler {
}
}
/// Handle ContactMessage V3 response (firmware ver >= 3, has SNR header)
void _handleContactMessageV3(BufferReader reader) {
try {
final message = FrameParser.parseContactMessageV3(reader);
debugPrint(' ✅ [ContactMessage V3] Parsed successfully');
onMessageReceived?.call(message);
} catch (e) {
debugPrint(' ❌ [ContactMessage V3] Parsing error: $e');
onError?.call('Contact message V3 parsing error: $e');
}
}
/// Handle ChannelMessage V3 response (firmware ver >= 3, has SNR header)
void _handleChannelMessageV3(BufferReader reader) {
try {
final message = FrameParser.parseChannelMessageV3(reader);
debugPrint(' ✅ [ChannelMessage V3] Parsed successfully');
onMessageReceived?.call(message);
} catch (e) {
debugPrint(' ❌ [ChannelMessage V3] Parsing error: $e');
onError?.call('Channel message V3 parsing error: $e');
}
}
/// Handle ContactDeleted push (0x8F) — contact overwritten due to contacts full
void _handleContactDeleted(BufferReader reader) {
try {
if (reader.remainingBytesCount >= 32) {
final publicKey = reader.readBytes(32);
debugPrint(' ✅ [ContactDeleted] Contact removed by firmware');
onContactDeleted?.call(Uint8List.fromList(publicKey));
}
} catch (e) {
debugPrint(' ❌ [ContactDeleted] Parsing error: $e');
}
}
/// Handle TelemetryResponse push
void _handleTelemetryResponse(BufferReader reader) {
try {

View File

@@ -91,6 +91,8 @@ class MeshCoreBleService {
OnErrorCallback? onError;
OnContactNotFoundCallback? onContactNotFound;
OnChannelInfoCallback? onChannelInfoReceived;
void Function(Uint8List publicKey)? onContactDeleted;
VoidCallback? onContactsFull;
// Activity callbacks (for blinking indicators)
VoidCallback? onRxActivity;
@@ -213,6 +215,12 @@ class MeshCoreBleService {
_responseHandler.onChannelInfoReceived = (int channelIdx, String channelName, Uint8List secret, int? flags) {
onChannelInfoReceived?.call(channelIdx, channelName, secret, flags);
};
_responseHandler.onContactDeleted = (publicKey) {
onContactDeleted?.call(publicKey);
};
_responseHandler.onContactsFull = () {
onContactsFull?.call();
};
_responseHandler.onRxActivity = () {
onRxActivity?.call();
};

View File

@@ -1,7 +1,7 @@
/// MeshCore BLE and Protocol Constants
class MeshCoreConstants {
// Supported protocol version
static const int supportedCompanionProtocolVersion = 1;
// Supported protocol version (firmware uses this to decide V1 vs V3 message frames)
static const int supportedCompanionProtocolVersion = 3;
// BLE Service and Characteristic UUIDs
static const String bleServiceUuid =
@@ -39,6 +39,8 @@ class MeshCoreConstants {
static const int cmdSendRawData = 25;
static const int cmdSendLogin = 26;
static const int cmdSendStatusReq = 27;
static const int cmdHasConnection = 28;
static const int cmdLogout = 29;
static const int cmdGetContactByKey = 30;
static const int cmdGetChannel = 31;
static const int cmdSetChannel = 32;
@@ -46,9 +48,23 @@ class MeshCoreConstants {
static const int cmdSignData = 34;
static const int cmdSignFinish = 35;
static const int cmdSendTracePath = 36;
static const int cmdSetDevicePin = 37;
static const int cmdSetOtherParams = 38;
static const int cmdSendTelemetryReq = 39;
static const int cmdGetCustomVars = 40;
static const int cmdSetCustomVar = 41;
static const int cmdGetAdvertPath = 42;
static const int cmdGetTuningParams = 43;
static const int cmdSendBinaryReq = 50;
static const int cmdFactoryReset = 51;
static const int cmdSendPathDiscoveryReq = 52;
static const int cmdSetFloodScope = 54; // v8+
static const int cmdSendControlData = 55; // v8+
static const int cmdGetStats = 56; // v8+
static const int cmdSendAnonReq = 57;
static const int cmdSetAutoaddConfig = 58;
static const int cmdGetAutoaddConfig = 59;
static const int cmdGetAllowedRepeatFreq = 60;
// Response Codes (Device -> App)
static const int respOk = 0;
@@ -58,8 +74,8 @@ class MeshCoreConstants {
static const int respEndOfContacts = 4;
static const int respSelfInfo = 5;
static const int respSent = 6;
static const int respContactMsgRecv = 7;
static const int respChannelMsgRecv = 8;
static const int respContactMsgRecv = 7; // firmware ver < 3
static const int respChannelMsgRecv = 8; // firmware ver < 3
static const int respCurrTime = 9;
static const int respNoMoreMessages = 10;
static const int respExportContact = 11;
@@ -67,12 +83,17 @@ class MeshCoreConstants {
static const int respDeviceInfo = 13;
static const int respPrivateKey = 14;
static const int respDisabled = 15;
static const int respContactMsgRecvV3 = 16; // firmware ver >= 3 (adds SNR header)
static const int respChannelMsgRecvV3 = 17; // firmware ver >= 3 (adds SNR header)
static const int respChannelInfo = 18;
static const int respSignStart = 19;
static const int respSignature = 20;
static const int respCustomVars = 21;
static const int respAdvertPath = 22;
static const int respTuningParams = 21; // Same as respCustomVars per protocol
static const int respTuningParams = 23;
static const int respStats = 24; // v8+
static const int respAutoaddConfig = 25;
static const int respAllowedRepeatFreq = 26;
// Push Codes (Device -> App, unsolicited)
static const int pushAdvert = 0x80;
@@ -88,6 +109,15 @@ class MeshCoreConstants {
static const int pushNewAdvert = 0x8A;
static const int pushTelemetryResponse = 0x8B;
static const int pushBinaryResponse = 0x8C;
static const int pushPathDiscoveryResponse = 0x8D;
static const int pushControlData = 0x8E; // v8+
static const int pushContactDeleted = 0x8F; // contact overwritten when contacts full
static const int pushContactsFull = 0x90; // contacts storage is full
// Stats sub-types for cmdGetStats
static const int statsTypeCore = 0;
static const int statsTypeRadio = 1;
static const int statsTypePackets = 2;
// Error Codes
static const int errUnsupportedCmd = 1;

View File

@@ -59,6 +59,10 @@ class MeshCoreOpcodeNames {
return 'SEND_LOGIN';
case MeshCoreConstants.cmdSendStatusReq:
return 'SEND_STATUS_REQ';
case MeshCoreConstants.cmdHasConnection:
return 'HAS_CONNECTION';
case MeshCoreConstants.cmdLogout:
return 'LOGOUT';
case MeshCoreConstants.cmdGetContactByKey:
return 'GET_CONTACT_BY_KEY';
case MeshCoreConstants.cmdGetChannel:
@@ -73,12 +77,40 @@ class MeshCoreOpcodeNames {
return 'SIGN_FINISH';
case MeshCoreConstants.cmdSendTracePath:
return 'SEND_TRACE_PATH';
case MeshCoreConstants.cmdSetDevicePin:
return 'SET_DEVICE_PIN';
case MeshCoreConstants.cmdSetOtherParams:
return 'SET_OTHER_PARAMS';
case MeshCoreConstants.cmdSendTelemetryReq:
return 'SEND_TELEMETRY_REQ';
case MeshCoreConstants.cmdGetCustomVars:
return 'GET_CUSTOM_VARS';
case MeshCoreConstants.cmdSetCustomVar:
return 'SET_CUSTOM_VAR';
case MeshCoreConstants.cmdGetAdvertPath:
return 'GET_ADVERT_PATH';
case MeshCoreConstants.cmdGetTuningParams:
return 'GET_TUNING_PARAMS';
case MeshCoreConstants.cmdSendBinaryReq:
return 'SEND_BINARY_REQ';
case MeshCoreConstants.cmdFactoryReset:
return 'FACTORY_RESET';
case MeshCoreConstants.cmdSendPathDiscoveryReq:
return 'SEND_PATH_DISCOVERY_REQ';
case MeshCoreConstants.cmdSetFloodScope:
return 'SET_FLOOD_SCOPE';
case MeshCoreConstants.cmdSendControlData:
return 'SEND_CONTROL_DATA';
case MeshCoreConstants.cmdGetStats:
return 'GET_STATS';
case MeshCoreConstants.cmdSendAnonReq:
return 'SEND_ANON_REQ';
case MeshCoreConstants.cmdSetAutoaddConfig:
return 'SET_AUTOADD_CONFIG';
case MeshCoreConstants.cmdGetAutoaddConfig:
return 'GET_AUTOADD_CONFIG';
case MeshCoreConstants.cmdGetAllowedRepeatFreq:
return 'GET_ALLOWED_REPEAT_FREQ';
default:
return 'CMD_UNKNOWN';
}
@@ -119,12 +151,28 @@ class MeshCoreOpcodeNames {
return 'PRIVATE_KEY';
case MeshCoreConstants.respDisabled:
return 'DISABLED';
case MeshCoreConstants.respContactMsgRecvV3:
return 'CONTACT_MSG_RECV_V3';
case MeshCoreConstants.respChannelMsgRecvV3:
return 'CHANNEL_MSG_RECV_V3';
case MeshCoreConstants.respChannelInfo:
return 'CHANNEL_INFO';
case MeshCoreConstants.respSignStart:
return 'SIGN_START';
case MeshCoreConstants.respSignature:
return 'SIGNATURE';
case MeshCoreConstants.respCustomVars:
return 'CUSTOM_VARS';
case MeshCoreConstants.respAdvertPath:
return 'ADVERT_PATH';
case MeshCoreConstants.respTuningParams:
return 'TUNING_PARAMS';
case MeshCoreConstants.respStats:
return 'STATS';
case MeshCoreConstants.respAutoaddConfig:
return 'AUTOADD_CONFIG';
case MeshCoreConstants.respAllowedRepeatFreq:
return 'ALLOWED_REPEAT_FREQ';
default:
return 'RESP_UNKNOWN';
}
@@ -159,6 +207,14 @@ class MeshCoreOpcodeNames {
return 'TELEMETRY_RESPONSE';
case MeshCoreConstants.pushBinaryResponse:
return 'BINARY_RESPONSE';
case MeshCoreConstants.pushPathDiscoveryResponse:
return 'PATH_DISCOVERY_RESPONSE';
case MeshCoreConstants.pushControlData:
return 'CONTROL_DATA';
case MeshCoreConstants.pushContactDeleted:
return 'CONTACT_DELETED';
case MeshCoreConstants.pushContactsFull:
return 'CONTACTS_FULL';
default:
return 'PUSH_UNKNOWN';
}

View File

@@ -59,6 +59,23 @@ class FrameParser {
return {};
}
/// Parse ContactMessage V3 response (firmware ver >= 3)
/// V3 prepends 3 bytes: [snr_scaled(int8)][reserved][reserved]
/// snr_dB = snr_scaled / 4.0
static Message parseContactMessageV3(BufferReader reader) {
reader.readInt8(); // snr scaled by 4 (ignored for now)
reader.readBytes(2); // reserved
return parseContactMessage(reader);
}
/// Parse ChannelMessage V3 response (firmware ver >= 3)
/// V3 prepends 3 bytes: [snr_scaled(int8)][reserved][reserved]
static Message parseChannelMessageV3(BufferReader reader) {
reader.readInt8(); // snr scaled by 4 (ignored for now)
reader.readBytes(2); // reserved
return parseChannelMessage(reader);
}
/// Parse ContactMessage response
static Message parseContactMessage(BufferReader reader) {
final pubKeyPrefix = reader.readBytes(6);