mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
feat: Add device info handling and improve UI for telemetry settings
This commit is contained in:
@@ -133,6 +133,29 @@ class ConnectionProvider with ChangeNotifier {
|
|||||||
onLoginFail?.call(publicKeyPrefix);
|
onLoginFail?.call(publicKeyPrefix);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_bleService.onDeviceInfoReceived = (deviceInfo) {
|
||||||
|
print('📥 [Provider] Received DeviceInfo:');
|
||||||
|
print(' Firmware Version: ${deviceInfo['firmwareVersion']}');
|
||||||
|
print(' Max Contacts: ${deviceInfo['maxContacts']}');
|
||||||
|
print(' Max Channels: ${deviceInfo['maxChannels']}');
|
||||||
|
print(' BLE PIN: ${deviceInfo['blePin']}');
|
||||||
|
print(' Build Date: ${deviceInfo['firmwareBuildDate']}');
|
||||||
|
print(' Model: ${deviceInfo['manufacturerModel']}');
|
||||||
|
print(' Version: ${deviceInfo['semanticVersion']}');
|
||||||
|
|
||||||
|
_deviceInfo = _deviceInfo.copyWith(
|
||||||
|
firmwareVersion: deviceInfo['firmwareVersion'] as int?,
|
||||||
|
maxContacts: deviceInfo['maxContacts'] as int?,
|
||||||
|
maxChannels: deviceInfo['maxChannels'] as int?,
|
||||||
|
blePin: deviceInfo['blePin'] as int?,
|
||||||
|
firmwareBuildDate: deviceInfo['firmwareBuildDate'] as String?,
|
||||||
|
manufacturerModel: deviceInfo['manufacturerModel'] as String?,
|
||||||
|
semanticVersion: deviceInfo['semanticVersion'] as String?,
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
print('✅ [Provider] Device info updated with DeviceInfo');
|
||||||
|
};
|
||||||
|
|
||||||
_bleService.onSelfInfoReceived = (selfInfo) {
|
_bleService.onSelfInfoReceived = (selfInfo) {
|
||||||
print('📥 [Provider] Received SelfInfo:');
|
print('📥 [Provider] Received SelfInfo:');
|
||||||
print(' TX Power: ${selfInfo['txPower']} / ${selfInfo['maxTxPower']} dBm');
|
print(' TX Power: ${selfInfo['txPower']} / ${selfInfo['maxTxPower']} dBm');
|
||||||
|
|||||||
@@ -294,10 +294,13 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
|||||||
_InfoRow('BLE Name', deviceInfo.deviceName ?? 'Unknown'),
|
_InfoRow('BLE Name', deviceInfo.deviceName ?? 'Unknown'),
|
||||||
_InfoRow('Mesh Name', deviceInfo.selfName ?? 'Not set'),
|
_InfoRow('Mesh Name', deviceInfo.selfName ?? 'Not set'),
|
||||||
_InfoRow('Type', _getDeviceTypeString(deviceInfo.deviceType)),
|
_InfoRow('Type', _getDeviceTypeString(deviceInfo.deviceType)),
|
||||||
_InfoRow('Firmware', deviceInfo.firmwareVersion?.toString() ?? 'Unknown'),
|
_InfoRow('Model', deviceInfo.manufacturerModel ?? 'Unknown'),
|
||||||
|
_InfoRow('Version', deviceInfo.semanticVersion ?? 'Unknown'),
|
||||||
|
_InfoRow('Build Date', deviceInfo.firmwareBuildDate ?? 'Unknown'),
|
||||||
|
_InfoRow('Firmware', 'v${deviceInfo.firmwareVersion?.toString() ?? "?"}'),
|
||||||
_InfoRow('Max Contacts', deviceInfo.maxContacts?.toString() ?? 'Unknown'),
|
_InfoRow('Max Contacts', deviceInfo.maxContacts?.toString() ?? 'Unknown'),
|
||||||
_InfoRow('Max Channels', deviceInfo.maxChannels?.toString() ?? 'Unknown'),
|
_InfoRow('Max Channels', deviceInfo.maxChannels?.toString() ?? 'Unknown'),
|
||||||
_InfoRow('Public Key', _getPublicKeyShort(deviceInfo.publicKey)),
|
_CopyableInfoRow('Public Key', _getPublicKeyHex(deviceInfo.publicKey)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -339,31 +342,40 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
|||||||
helperText: 'Name broadcast in mesh advertisements',
|
helperText: 'Name broadcast in mesh advertisements',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 8),
|
||||||
|
|
||||||
// Telemetry Toggle
|
// Telemetry Toggle - Compact version
|
||||||
SwitchListTile(
|
Row(
|
||||||
title: const Text('Enable Telemetry & Location Sharing'),
|
children: [
|
||||||
subtitle: const Text('Allow others to query your location and telemetry'),
|
Expanded(
|
||||||
value: _telemetryEnabled,
|
child: Text(
|
||||||
onChanged: (value) {
|
'Telemetry & Location Sharing',
|
||||||
setState(() {
|
style: theme.textTheme.bodyMedium,
|
||||||
_telemetryEnabled = value;
|
),
|
||||||
});
|
),
|
||||||
},
|
Switch(
|
||||||
|
value: _telemetryEnabled,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
_telemetryEnabled = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
|
||||||
|
|
||||||
// GPS Coordinates (only show if telemetry enabled)
|
// GPS Coordinates (only show if telemetry enabled)
|
||||||
if (_telemetryEnabled) ...[
|
if (_telemetryEnabled) ...[
|
||||||
|
const SizedBox(height: 12),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _latController,
|
controller: _latController,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Latitude',
|
labelText: 'Lat',
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
|
isDense: true,
|
||||||
),
|
),
|
||||||
keyboardType: const TextInputType.numberWithOptions(
|
keyboardType: const TextInputType.numberWithOptions(
|
||||||
decimal: true,
|
decimal: true,
|
||||||
@@ -371,13 +383,14 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 8),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _lonController,
|
controller: _lonController,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Longitude',
|
labelText: 'Lon',
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
|
isDense: true,
|
||||||
),
|
),
|
||||||
keyboardType: const TextInputType.numberWithOptions(
|
keyboardType: const TextInputType.numberWithOptions(
|
||||||
decimal: true,
|
decimal: true,
|
||||||
@@ -385,17 +398,14 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
IconButton.filled(
|
||||||
|
onPressed: _useCurrentLocation,
|
||||||
|
icon: const Icon(Icons.my_location, size: 20),
|
||||||
|
tooltip: 'Use current location',
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
|
||||||
ElevatedButton.icon(
|
|
||||||
onPressed: _useCurrentLocation,
|
|
||||||
icon: const Icon(Icons.my_location),
|
|
||||||
label: const Text('Use Current Location'),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
minimumSize: const Size(double.infinity, 48),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -542,13 +552,9 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _getPublicKeyShort(List<int>? publicKey) {
|
String _getPublicKeyHex(List<int>? publicKey) {
|
||||||
if (publicKey == null || publicKey.isEmpty) return 'N/A';
|
if (publicKey == null || publicKey.isEmpty) return 'N/A';
|
||||||
final hex = publicKey.map((b) => b.toRadixString(16).padLeft(2, '0')).join('');
|
return publicKey.map((b) => b.toRadixString(16).padLeft(2, '0')).join('');
|
||||||
if (hex.length >= 16) {
|
|
||||||
return '${hex.substring(0, 8)}...${hex.substring(hex.length - 8)}';
|
|
||||||
}
|
|
||||||
return hex;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,3 +594,62 @@ class _InfoRow extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _CopyableInfoRow extends StatelessWidget {
|
||||||
|
final String label;
|
||||||
|
final String value;
|
||||||
|
|
||||||
|
const _CopyableInfoRow(this.label, this.value);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 120,
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Clipboard.setData(ClipboardData(text: value));
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text('Copied $label to clipboard'),
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
value,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
const Icon(Icons.copy, size: 16, color: Colors.grey),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user