@@ -35,6 +35,9 @@ class PingControls extends StatelessWidget {
3535 // TX is blocked when offline mode is active and connected
3636 final txBlockedByOffline = appState.offlineMode && appState.isConnected;
3737
38+ // TX not allowed when API says zone is at TX capacity
39+ final txNotAllowed = appState.isConnected && ! appState.txAllowed;
40+
3841 // Calculate distance feedback for TX Ping button
3942 final distanceFromLastPing = appState.distanceFromLastPing;
4043 final needToMove = validation == PingValidation .tooCloseToLastPing && distanceFromLastPing != null ;
@@ -95,25 +98,27 @@ class PingControls extends StatelessWidget {
9598 icon: Icons .cell_tower,
9699 label: txBlockedByOffline
97100 ? 'TX Disabled'
98- : isActiveModeRunning
99- ? 'Send Ping' // Just disabled when Active Mode is running
100- : isPingSending
101- ? 'Sending...'
102- : rxWindowActive
103- ? 'Listening ${rxWindowRemaining }s' // Manual ping listening (works during Passive Mode too)
104- : discoveryWindowActive
105- ? 'Cooldown ${discoveryWindowRemaining }s' // Cooldown during Passive Mode listening
106- : cooldownActive
107- ? 'Cooldown ${cooldownRemaining }s' // After Active Mode disabled
108- : 'Send Ping' ,
101+ : txNotAllowed
102+ ? 'Zone Full'
103+ : isActiveModeRunning
104+ ? 'Send Ping' // Just disabled when Active Mode is running
105+ : isPingSending
106+ ? 'Sending...'
107+ : rxWindowActive
108+ ? 'Listening ${rxWindowRemaining }s' // Manual ping listening (works during Passive Mode too)
109+ : discoveryWindowActive
110+ ? 'Cooldown ${discoveryWindowRemaining }s' // Cooldown during Passive Mode listening
111+ : cooldownActive
112+ ? 'Cooldown ${cooldownRemaining }s' // After Active Mode disabled
113+ : 'Send Ping' ,
109114 color: const Color (0xFF0EA5E9 ), // sky-500
110- enabled: canPing && ! isActiveModeRunning && ! cooldownActive && ! txBlockedByOffline &&
115+ enabled: canPing && ! isActiveModeRunning && ! cooldownActive && ! txBlockedByOffline && ! txNotAllowed &&
111116 ! rxWindowActive && ! isPingSending && ! discoveryWindowActive && ! isPendingDisable,
112117 isActive: (isPingSending || rxWindowActive) && ! isActiveModeRunning, // Only active during manual ping flow
113118 onPressed: () => _sendPing (context, appState),
114119 showCooldown: false , // No longer needed - countdown shown in label
115- subtitle: txBlockedByOffline ? 'Offline Mode' : ((isPingSending || rxWindowActive || cooldownActive || discoveryWindowActive) ? null : moveSubtitle),
116- subtitleColor: txBlockedByOffline ? Colors .orange : Colors .orange.shade600,
120+ subtitle: txBlockedByOffline ? 'Offline Mode' : txNotAllowed ? 'Passive Only' : ((isPingSending || rxWindowActive || cooldownActive || discoveryWindowActive) ? null : moveSubtitle),
121+ subtitleColor: txBlockedByOffline ? Colors .orange : txNotAllowed ? Colors .red : Colors .orange.shade600,
117122 ),
118123 ),
119124 const SizedBox (width: 10 ),
@@ -127,35 +132,37 @@ class PingControls extends StatelessWidget {
127132 icon: Icons .sensors,
128133 label: txBlockedByOffline
129134 ? 'TX Disabled'
130- : isPendingDisable
131- ? (rxWindowActive
132- ? 'Stopping ${rxWindowRemaining }s' // Show remaining time until disable completes
133- : 'Stopping...' ) // Brief transition state
134- : isActiveModeRunning
135- ? (isPingInProgress && ! rxWindowActive
136- ? 'Sending...' // Brief moment while ping is being sent
135+ : txNotAllowed
136+ ? 'Zone Full'
137+ : isPendingDisable
138+ ? (rxWindowActive
139+ ? 'Stopping ${rxWindowRemaining }s' // Show remaining time until disable completes
140+ : 'Stopping...' ) // Brief transition state
141+ : isActiveModeRunning
142+ ? (isPingInProgress && ! rxWindowActive
143+ ? 'Sending...' // Brief moment while ping is being sent
144+ : rxWindowActive
145+ ? 'Listening ${rxWindowRemaining }s' // During RX window
146+ : autoPingWaiting
147+ ? 'Next ping ${autoPingRemaining }s' // Waiting for next auto ping
148+ : 'Active Mode' ) // Initial state before first ping
137149 : rxWindowActive
138- ? 'Listening ${rxWindowRemaining }s' // During RX window
139- : autoPingWaiting
140- ? 'Next ping ${autoPingRemaining }s' // Waiting for next auto ping
141- : 'Active Mode' ) // Initial state before first ping
142- : rxWindowActive
143- ? 'Cooldown ${rxWindowRemaining }s' // During manual ping
144- : cooldownActive
145- ? 'Cooldown ${cooldownRemaining }s' // After Active Mode disabled
146- : 'Active Mode' ,
150+ ? 'Cooldown ${rxWindowRemaining }s' // During manual ping
151+ : cooldownActive
152+ ? 'Cooldown ${cooldownRemaining }s' // After Active Mode disabled
153+ : 'Active Mode' ,
147154 color: isPendingDisable
148155 ? Colors .orange // Orange when stopping
149156 : isActiveModeRunning
150157 ? const Color (0xFF22C55E ) // green-500
151158 : const Color (0xFF6366F1 ), // indigo-500
152159 // When pending disable, button is disabled but still shows stopping state
153- enabled: ! isPendingDisable && ((isActiveModeRunning || (canStartAuto && ! isPassiveModeRunning && ! cooldownActive && ! isPingSending && ! rxWindowActive)) && ! txBlockedByOffline),
160+ enabled: ! isPendingDisable && ((isActiveModeRunning || (canStartAuto && ! isPassiveModeRunning && ! cooldownActive && ! isPingSending && ! rxWindowActive)) && ! txBlockedByOffline && ! txNotAllowed ),
154161 isActive: isPendingDisable || (isActiveModeRunning && (isPingInProgress || rxWindowActive || autoPingWaiting)), // Active during stopping or sending/listening/waiting phases
155162 onPressed: () => _toggleTxRxAuto (context, appState),
156163 showCooldown: false , // No longer needed - countdown shown in label
157- subtitle: txBlockedByOffline ? 'Offline Mode' : (isPendingDisable ? 'Stopping' : null ),
158- subtitleColor: Colors .orange,
164+ subtitle: txBlockedByOffline ? 'Offline Mode' : txNotAllowed ? 'Passive Only' : (isPendingDisable ? 'Stopping' : null ),
165+ subtitleColor: txBlockedByOffline ? Colors .orange : txNotAllowed ? Colors .red : Colors .orange,
159166 ),
160167 ),
161168 const SizedBox (width: 10 ),
@@ -764,6 +771,9 @@ class _CompactPingControlsState extends State<CompactPingControls> {
764771 // TX is blocked when offline mode is active and connected
765772 final txBlockedByOffline = appState.offlineMode && appState.isConnected;
766773
774+ // TX not allowed when API says zone is at TX capacity
775+ final txNotAllowed = appState.isConnected && ! appState.txAllowed;
776+
767777 final prefs = appState.preferences;
768778 final isPowerSet = prefs.autoPowerSet || prefs.powerLevelSet || appState.deviceModel != null ;
769779
@@ -795,12 +805,12 @@ class _CompactPingControlsState extends State<CompactPingControls> {
795805 (cooldownActive && _lastActiveButton == _LastActiveButton .passiveMode);
796806
797807 // Determine which buttons are colored (enabled or active)
798- final sendPingEnabled = canPing && ! isActiveModeRunning && ! cooldownActive && ! txBlockedByOffline &&
808+ final sendPingEnabled = canPing && ! isActiveModeRunning && ! cooldownActive && ! txBlockedByOffline && ! txNotAllowed &&
799809 ! rxWindowActive && ! isPingSending && ! discoveryWindowActive && ! isPendingDisable;
800810 final sendPingActive = (isPingSending || rxWindowActive) && ! isActiveModeRunning && ! cooldownActive;
801811 final sendPingShowColor = sendPingEnabled || sendPingActive;
802812
803- final activeModeEnabled = ! isPendingDisable && ((isActiveModeRunning || (canStartAuto && ! isPassiveModeRunning && ! cooldownActive && ! isPingSending && ! rxWindowActive)) && ! txBlockedByOffline);
813+ final activeModeEnabled = ! isPendingDisable && ((isActiveModeRunning || (canStartAuto && ! isPassiveModeRunning && ! cooldownActive && ! isPingSending && ! rxWindowActive)) && ! txBlockedByOffline && ! txNotAllowed );
804814 final activeModeActive = isPendingDisable || (isActiveModeRunning && (isPingInProgress || rxWindowActive || autoPingWaiting));
805815 final activeModeShowColor = activeModeEnabled || activeModeActive;
806816
@@ -1063,6 +1073,9 @@ class LandscapePingControls extends StatelessWidget {
10631073 // TX is blocked when offline mode is active and connected
10641074 final txBlockedByOffline = appState.offlineMode && appState.isConnected;
10651075
1076+ // TX not allowed when API says zone is at TX capacity
1077+ final txNotAllowed = appState.isConnected && ! appState.txAllowed;
1078+
10661079 final prefs = appState.preferences;
10671080 final isPowerSet = prefs.autoPowerSet || prefs.powerLevelSet || appState.deviceModel != null ;
10681081
@@ -1091,9 +1104,9 @@ class LandscapePingControls extends StatelessWidget {
10911104 Expanded (
10921105 child: _LandscapeIconButton (
10931106 icon: Icons .cell_tower,
1094- tooltip: 'Send Ping' ,
1107+ tooltip: txNotAllowed ? 'Zone Full (Passive Only)' : 'Send Ping' ,
10951108 color: const Color (0xFF0EA5E9 ), // sky-500
1096- enabled: canPing && ! isActiveModeRunning && ! cooldownActive && ! txBlockedByOffline &&
1109+ enabled: canPing && ! isActiveModeRunning && ! cooldownActive && ! txBlockedByOffline && ! txNotAllowed &&
10971110 ! rxWindowActive && ! isPingSending && ! discoveryWindowActive && ! isPendingDisable,
10981111 isActive: (isPingSending || rxWindowActive) && ! isActiveModeRunning,
10991112 countdown: isPingSending
@@ -1114,13 +1127,13 @@ class LandscapePingControls extends StatelessWidget {
11141127 Expanded (
11151128 child: _LandscapeIconButton (
11161129 icon: Icons .sensors,
1117- tooltip: 'Active Mode' ,
1130+ tooltip: txNotAllowed ? 'Zone Full (Passive Only)' : 'Active Mode' ,
11181131 color: isPendingDisable
11191132 ? Colors .orange
11201133 : isActiveModeRunning
11211134 ? const Color (0xFF22C55E ) // green-500
11221135 : const Color (0xFF6366F1 ), // indigo-500
1123- enabled: ! isPendingDisable && ((isActiveModeRunning || (canStartAuto && ! isPassiveModeRunning && ! cooldownActive && ! isPingSending && ! rxWindowActive)) && ! txBlockedByOffline),
1136+ enabled: ! isPendingDisable && ((isActiveModeRunning || (canStartAuto && ! isPassiveModeRunning && ! cooldownActive && ! isPingSending && ! rxWindowActive)) && ! txBlockedByOffline && ! txNotAllowed ),
11241137 isActive: isPendingDisable || (isActiveModeRunning && (isPingInProgress || rxWindowActive || autoPingWaiting)),
11251138 countdown: isActiveModeRunning
11261139 ? (rxWindowActive
0 commit comments