Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions lib/models/api_queue_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ class ApiQueueItem extends HiveObject {
@HiveField(14)
final bool externalAntenna;

/// Radio power in watts (e.g., 0.3, 1.0, 2.0) — included in every API post
@HiveField(15)
final double? power;

ApiQueueItem({
required this.type,
required this.latitude,
Expand All @@ -63,6 +67,7 @@ class ApiQueueItem extends HiveObject {
this.retryCount = 0,
this.lastRetryAt,
this.noiseFloor,
this.power,
});

/// Create from TX ping
Expand All @@ -74,6 +79,7 @@ class ApiQueueItem extends HiveObject {
required int timestamp,
required bool externalAntenna,
int? noiseFloor,
double? power,
}) {
return ApiQueueItem(
type: 'TX',
Expand All @@ -84,6 +90,7 @@ class ApiQueueItem extends HiveObject {
canUploadAfter: DateTime.now().millisecondsSinceEpoch, // Immediate — flush timer controls upload timing
externalAntenna: externalAntenna,
noiseFloor: noiseFloor,
power: power,
);
}

Expand All @@ -96,6 +103,7 @@ class ApiQueueItem extends HiveObject {
required int timestamp,
required bool externalAntenna,
int? noiseFloor,
double? power,
}) {
return ApiQueueItem(
type: 'RX',
Expand All @@ -106,6 +114,7 @@ class ApiQueueItem extends HiveObject {
canUploadAfter: DateTime.now().millisecondsSinceEpoch, // Immediate
externalAntenna: externalAntenna,
noiseFloor: noiseFloor,
power: power,
);
}

Expand All @@ -123,6 +132,7 @@ class ApiQueueItem extends HiveObject {
required int timestamp,
required bool externalAntenna,
int? noiseFloor,
double? power,
}) {
// Format: "repeaterId:nodeType:localSnr:localRssi:remoteSnr:pubkeyFull"
final heardRepeats = '$repeaterId:$nodeType:${localSnr.toStringAsFixed(2)}:$localRssi:${remoteSnr.toStringAsFixed(2)}:$pubkeyFull';
Expand All @@ -135,6 +145,7 @@ class ApiQueueItem extends HiveObject {
canUploadAfter: DateTime.now().millisecondsSinceEpoch, // Immediate
externalAntenna: externalAntenna,
noiseFloor: noiseFloor,
power: power,
);
}

Expand All @@ -150,6 +161,7 @@ class ApiQueueItem extends HiveObject {
required int timestamp,
required bool externalAntenna,
int? noiseFloor,
double? power,
}) {
final heardRepeats = '$repeaterId:${localSnr.toStringAsFixed(2)}:$localRssi:${remoteSnr.toStringAsFixed(2)}';
return ApiQueueItem(
Expand All @@ -161,6 +173,7 @@ class ApiQueueItem extends HiveObject {
canUploadAfter: DateTime.now().millisecondsSinceEpoch, // Immediate
externalAntenna: externalAntenna,
noiseFloor: noiseFloor,
power: power,
);
}

Expand All @@ -171,6 +184,7 @@ class ApiQueueItem extends HiveObject {
required int timestamp,
required bool externalAntenna,
int? noiseFloor,
double? power,
}) {
return ApiQueueItem(
type: 'DISC',
Expand All @@ -181,6 +195,7 @@ class ApiQueueItem extends HiveObject {
canUploadAfter: DateTime.now().millisecondsSinceEpoch, // Immediate
externalAntenna: externalAntenna,
noiseFloor: noiseFloor,
power: power,
);
}

Expand All @@ -201,6 +216,7 @@ class ApiQueueItem extends HiveObject {
'remote_snr': parts.length > 3 ? double.tryParse(parts[3]) : null,
'timestamp': timestamp.millisecondsSinceEpoch ~/ 1000,
'external_antenna': externalAntenna,
'power': power != null ? '${power!.toStringAsFixed(1)}w' : null,
};
}

Expand All @@ -216,6 +232,7 @@ class ApiQueueItem extends HiveObject {
'repeater_id': 'None',
'timestamp': timestamp.millisecondsSinceEpoch ~/ 1000,
'external_antenna': externalAntenna,
'power': power != null ? '${power!.toStringAsFixed(1)}w' : null,
};
}

Expand All @@ -234,6 +251,7 @@ class ApiQueueItem extends HiveObject {
'public_key': parts.length > 5 ? parts[5] : '',
'timestamp': timestamp.millisecondsSinceEpoch ~/ 1000, // Unix timestamp in seconds
'external_antenna': externalAntenna,
'power': power != null ? '${power!.toStringAsFixed(1)}w' : null,
};
}

Expand All @@ -245,6 +263,7 @@ class ApiQueueItem extends HiveObject {
'heard_repeats': heardRepeats,
'timestamp': timestamp.millisecondsSinceEpoch ~/ 1000, // Unix timestamp in seconds
'external_antenna': externalAntenna,
'power': power != null ? '${power!.toStringAsFixed(1)}w' : null,
};
}

Expand Down
15 changes: 8 additions & 7 deletions lib/models/noise_floor_session.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import '../utils/ping_colors.dart';

part 'noise_floor_session.g.dart';

Expand Down Expand Up @@ -100,13 +101,13 @@ class PingEventMarker extends HiveObject {

/// Get the color for this event type
Color get color => switch (type) {
PingEventType.txSuccess => Colors.green,
PingEventType.txFail => Colors.red,
PingEventType.rx => Colors.blue,
PingEventType.discSuccess => Colors.purple,
PingEventType.discFail => Colors.grey,
PingEventType.traceSuccess => Colors.cyan,
PingEventType.traceFail => Colors.grey,
PingEventType.txSuccess => PingColors.txSuccess,
PingEventType.txFail => PingColors.txFail,
PingEventType.rx => PingColors.rx,
PingEventType.discSuccess => PingColors.discSuccess,
PingEventType.discFail => PingColors.discFail,
PingEventType.traceSuccess => PingColors.traceSuccess,
PingEventType.traceFail => PingColors.noResponse,
};

/// Get a display label for this event type
Expand Down
52 changes: 51 additions & 1 deletion lib/models/user_preferences.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,21 @@ class UserPreferences {
/// Show top 3 repeaters by SNR on the map during wardriving
final bool showTopRepeaters;

/// Coverage marker style on the map (dot, pin, diamond)
final String markerStyle;

/// GPS position marker style (arrow, car, bike, boat, walk)
final String gpsMarkerStyle;

/// Color vision type for accessibility (none, protanopia, deuteranopia, tritanopia, achromatopsia)
final String colorVisionType;

/// Download map tiles (base map + coverage overlay). When false, no tile network requests are made to save mobile data.
final bool mapTilesEnabled;

/// Disconnect alert: play audible alert when pinging stops unexpectedly (BLE disconnect, idle timeout, maintenance)
final bool disconnectAlertEnabled;

const UserPreferences({
this.powerLevel = 0.3,
this.txPower = 22,
Expand Down Expand Up @@ -114,6 +129,11 @@ class UserPreferences {
this.minPingDistanceMeters = 25,
this.autoStopAfterIdle = true,
this.showTopRepeaters = false,
this.markerStyle = 'dot',
this.gpsMarkerStyle = 'arrow',
this.colorVisionType = 'none',
this.mapTilesEnabled = true,
this.disconnectAlertEnabled = false,
});

/// Create from JSON (for persistence)
Expand Down Expand Up @@ -147,6 +167,11 @@ class UserPreferences {
minPingDistanceMeters: (json['minPingDistanceMeters'] as int?) ?? 25,
autoStopAfterIdle: (json['autoStopAfterIdle'] as bool?) ?? true,
showTopRepeaters: (json['showTopRepeaters'] as bool?) ?? false,
markerStyle: (json['markerStyle'] as String?) ?? 'dot',
gpsMarkerStyle: (json['gpsMarkerStyle'] as String?) ?? 'arrow',
colorVisionType: (json['colorVisionType'] as String?) ?? 'none',
mapTilesEnabled: (json['mapTilesEnabled'] as bool?) ?? true,
disconnectAlertEnabled: (json['disconnectAlertEnabled'] as bool?) ?? false,
);
}

Expand Down Expand Up @@ -181,6 +206,11 @@ class UserPreferences {
'minPingDistanceMeters': minPingDistanceMeters,
'autoStopAfterIdle': autoStopAfterIdle,
'showTopRepeaters': showTopRepeaters,
'markerStyle': markerStyle,
'gpsMarkerStyle': gpsMarkerStyle,
'colorVisionType': colorVisionType,
'mapTilesEnabled': mapTilesEnabled,
'disconnectAlertEnabled': disconnectAlertEnabled,
};
}

Expand Down Expand Up @@ -214,6 +244,11 @@ class UserPreferences {
int? minPingDistanceMeters,
bool? autoStopAfterIdle,
bool? showTopRepeaters,
String? markerStyle,
String? gpsMarkerStyle,
String? colorVisionType,
bool? mapTilesEnabled,
bool? disconnectAlertEnabled,
}) {
return UserPreferences(
powerLevel: powerLevel ?? this.powerLevel,
Expand Down Expand Up @@ -244,6 +279,11 @@ class UserPreferences {
minPingDistanceMeters: minPingDistanceMeters ?? this.minPingDistanceMeters,
autoStopAfterIdle: autoStopAfterIdle ?? this.autoStopAfterIdle,
showTopRepeaters: showTopRepeaters ?? this.showTopRepeaters,
markerStyle: markerStyle ?? this.markerStyle,
gpsMarkerStyle: gpsMarkerStyle ?? this.gpsMarkerStyle,
colorVisionType: colorVisionType ?? this.colorVisionType,
mapTilesEnabled: mapTilesEnabled ?? this.mapTilesEnabled,
disconnectAlertEnabled: disconnectAlertEnabled ?? this.disconnectAlertEnabled,
);
}

Expand Down Expand Up @@ -302,7 +342,12 @@ class UserPreferences {
other.deleteChannelOnDisconnect == deleteChannelOnDisconnect &&
other.minPingDistanceMeters == minPingDistanceMeters &&
other.autoStopAfterIdle == autoStopAfterIdle &&
other.showTopRepeaters == showTopRepeaters;
other.showTopRepeaters == showTopRepeaters &&
other.markerStyle == markerStyle &&
other.gpsMarkerStyle == gpsMarkerStyle &&
other.colorVisionType == colorVisionType &&
other.mapTilesEnabled == mapTilesEnabled &&
other.disconnectAlertEnabled == disconnectAlertEnabled;
}

@override
Expand Down Expand Up @@ -335,6 +380,11 @@ class UserPreferences {
minPingDistanceMeters,
autoStopAfterIdle,
showTopRepeaters,
markerStyle,
gpsMarkerStyle,
colorVisionType,
mapTilesEnabled,
disconnectAlertEnabled,
]);
}

Expand Down
Loading
Loading