@@ -235,22 +235,46 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
235235 }
236236 case meshtastic_AdminMessage_ota_request_tag: {
237237#if defined(ARCH_ESP32)
238+ LOG_INFO (" OTA Requested" );
239+
238240 if (r->ota_request .ota_hash .size != 32 ) {
239241 suppressRebootBanner = true ;
240- LOG_INFO ( " OTA Failed : Invalid `ota_hash` provided" );
242+ sendWarningAndLog ( " Cannot start OTA : Invalid `ota_hash` provided. " );
241243 break ;
242244 }
243245
244246 meshtastic_OTAMode mode = r->ota_request .reboot_ota_mode ;
247+ const char *mode_name = (mode == METHOD_OTA_BLE ? " BLE" : " WiFi" );
248+
249+ // Check that we have an OTA partition
250+ const esp_partition_t *part = MeshtasticOTA::getAppPartition ();
251+ if (part == NULL ) {
252+ suppressRebootBanner = true ;
253+ sendWarningAndLog (" Cannot start OTA: Cannot find OTA Loader partition." );
254+ break ;
255+ }
256+
257+ static esp_app_desc_t app_desc;
258+ if (!MeshtasticOTA::getAppDesc (part, &app_desc)) {
259+ suppressRebootBanner = true ;
260+ sendWarningAndLog (" Cannot start OTA: Device does have a valid OTA Loader." );
261+ break ;
262+ }
263+
264+ if (!MeshtasticOTA::checkOTACapability (&app_desc, mode)) {
265+ suppressRebootBanner = true ;
266+ sendWarningAndLog (" OTA Loader does not support %s" , mode_name);
267+ break ;
268+ }
269+
245270 if (MeshtasticOTA::trySwitchToOTA ()) {
246- LOG_INFO (" OTA Requested" );
247271 suppressRebootBanner = true ;
248272 if (screen)
249273 screen->startFirmwareUpdateScreen ();
250274 MeshtasticOTA::saveConfig (&config.network , mode, r->ota_request .ota_hash .bytes );
251- LOG_INFO (" Rebooting to WiFi OTA" );
275+ sendWarningAndLog (" Rebooting to %s OTA" , mode_name );
252276 } else {
253- LOG_INFO ( " WIFI OTA Failed " );
277+ sendWarningAndLog ( " Unable to switch to the OTA partition. " );
254278 }
255279#endif
256280 int s = 1 ; // Reboot in 1 second, hard coded
@@ -1472,15 +1496,43 @@ void AdminModule::handleSendInputEvent(const meshtastic_AdminMessage_InputEvent
14721496#endif
14731497}
14741498
1475- void AdminModule::sendWarning (const char *message )
1499+ void AdminModule::sendWarning (const char *format, ... )
14761500{
14771501 meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed ();
1502+ if (!cn)
1503+ return ;
1504+
14781505 cn->level = meshtastic_LogRecord_Level_WARNING;
14791506 cn->time = getValidTime (RTCQualityFromNet);
1480- strncpy (cn->message , message, sizeof (cn->message ));
1507+
1508+ va_list args;
1509+ va_start (args, format);
1510+ // Format the arguments directly into the notification object
1511+ vsnprintf (cn->message , sizeof (cn->message ), format, args);
1512+ va_end (args);
1513+
14811514 service->sendClientNotification (cn);
14821515}
14831516
1517+ void AdminModule::sendWarningAndLog (const char *format, ...)
1518+ {
1519+ // We need a temporary buffer to hold the formatted text so we can log it
1520+ // Using 250 bytes as a safe upper limit for typical text notifications
1521+ char buf[250 ];
1522+
1523+ va_list args;
1524+ va_start (args, format);
1525+ vsnprintf (buf, sizeof (buf), format, args);
1526+ va_end (args);
1527+
1528+ LOG_WARN (buf);
1529+ // 2. Call sendWarning
1530+ // SECURITY NOTE: We pass "%s", buf instead of just 'buf'.
1531+ // If 'buf' contained a % symbol (e.g. "Battery 50%"), passing it directly
1532+ // would crash sendWarning. "%s" treats it purely as text.
1533+ sendWarning (" %s" , buf);
1534+ }
1535+
14841536void disableBluetooth ()
14851537{
14861538#if HAS_BLUETOOTH
0 commit comments