From 659c470eba542383987aad227b7f918100f131fa Mon Sep 17 00:00:00 2001
From: wangzhibo <wangzhibo@shsening.com>
Date: 星期五, 11 九月 2026 15:09:05 +0800
Subject: [PATCH] 小智终端 5种 场景模式 OK

---
 xiaozhi-esp32-2.2.6/main/boards/sening-xiaozhi-s3/mqtt.c |   97 ++++++++++++++++++++++++++++++++++--------------
 1 files changed, 68 insertions(+), 29 deletions(-)

diff --git a/xiaozhi-esp32-2.2.6/main/boards/sening-xiaozhi-s3/mqtt.c b/xiaozhi-esp32-2.2.6/main/boards/sening-xiaozhi-s3/mqtt.c
index 219255b..f0a4cb4 100644
--- a/xiaozhi-esp32-2.2.6/main/boards/sening-xiaozhi-s3/mqtt.c
+++ b/xiaozhi-esp32-2.2.6/main/boards/sening-xiaozhi-s3/mqtt.c
@@ -18,7 +18,7 @@
 #include "app_task.h"
 
 #define BOARD_MQTT_KEEPALIVE_SEC 120
-#define BOARD_MQTT_STATE_REPORT_INTERVAL_MS 5000
+#define BOARD_MQTT_STATE_REPORT_INTERVAL_MS 30000
 #define BOARD_MQTT_RECONNECT_DELAY_MS 30000
 #define BOARD_MQTT_NETWORK_TIMEOUT_MS 15000
 #define BOARD_MQTT_FINAL_REPORT_WAIT_MS 10000
@@ -80,7 +80,6 @@
 static void BoardMqtt_WaitConnectedForFinalReport(void);
 static bool BoardMqtt_BeginTask(board_mqtt_task_type_t type);
 static void BoardMqtt_FinishTask(board_mqtt_task_type_t type);
-static bool BoardMqtt_PlaybackSpeedIsSupported(float speed);
 static void BoardMqtt_StopPlaylistTask(void);
 static esp_err_t BoardMqtt_StartPlaylistTask(void);
 
@@ -145,6 +144,7 @@
     int volume;
     bool has_volume;
     float speed;
+    int repeat;
     int index;
     int count;
 } board_mqtt_playlist_item_t;
@@ -616,7 +616,8 @@
         strcmp(action, "SET_CLOCK") == 0 || strcmp(action, "PLAY_MEDIA") == 0 ||
         strcmp(action, "STOP_MEDIA") == 0 || strcmp(action, "DELETE_FILE") == 0 ||
         strcmp(action, "SET_ALARM") == 0 || strcmp(action, "UNSET_ALARM") == 0 ||
-        strcmp(action, "REBOOT") == 0;
+        strcmp(action, "REBOOT") == 0 || strcmp(action, "START_APP_TASK") == 0 ||
+        strcmp(action, "ENSURE_APP_DIRS") == 0;
 }
 
 static bool BoardMqtt_ActionIsTask(const char *action)
@@ -675,21 +676,6 @@
     esp_err_t err = DS2312_Set_Unix_Time_Saved((time_t)ts->valuedouble);
     return err == ESP_OK ? BoardMqtt_ResultOk("clock updated") :
         BoardMqtt_ResultFail(BOARD_MQTT_ERR_EXEC_FAILED, esp_err_to_name(err));
-}
-
-static bool BoardMqtt_PlaybackSpeedIsSupported(float speed)
-{
-    const float supported_speeds[] = {0.5f, 0.75f, 1.0f, 1.25f, 1.5f, 2.0f};
-    for (size_t i = 0; i < sizeof(supported_speeds) / sizeof(supported_speeds[0]); ++i) {
-        float diff = speed - supported_speeds[i];
-        if (diff < 0) {
-            diff = -diff;
-        }
-        if (diff < 0.001f) {
-            return true;
-        }
-    }
-    return false;
 }
 
 static char *BoardMqtt_ReadPlaylistFile(void)
@@ -850,13 +836,22 @@
                 cJSON_Delete(root);
                 return ESP_ERR_INVALID_ARG;
             }
-            speed = (float)speed_json->valuedouble;
+            speed = SDCard_Clamp_MP3_Speed((float)speed_json->valuedouble);
         }
-        if (!BoardMqtt_PlaybackSpeedIsSupported(speed)) {
-            cJSON_Delete(root);
-            return ESP_ERR_INVALID_ARG;
+        int repeat = 1;
+        cJSON *repeat_json = cJSON_GetObjectItem(item, "repeat");
+        if (!repeat_json) {
+            repeat_json = cJSON_GetObjectItem(item, "count");
         }
-
+        if (!repeat_json) {
+            repeat_json = cJSON_GetObjectItem(item, "play_count");
+        }
+        if (repeat_json) {
+            if (!BoardMqtt_NumberToInt(repeat_json, &repeat) || repeat < 1 || repeat > 99) {
+                cJSON_Delete(root);
+                return ESP_ERR_INVALID_ARG;
+            }
+        }
         cJSON *stored_item = cJSON_CreateObject();
         if (!stored_item) {
             cJSON_Delete(root);
@@ -868,6 +863,7 @@
             cJSON_AddNumberToObject(stored_item, "volume", volume_value);
         }
         cJSON_AddNumberToObject(stored_item, "speed", speed);
+        cJSON_AddNumberToObject(stored_item, "repeat", repeat);
         cJSON_AddBoolToObject(stored_item, "completed", false);
         cJSON_AddItemToArray(playlist_items, stored_item);
     }
@@ -901,6 +897,7 @@
             cJSON *sd_path = cJSON_GetObjectItem(item, "sd_path");
             cJSON *volume = cJSON_GetObjectItem(item, "volume");
             cJSON *speed_json = cJSON_GetObjectItem(item, "speed");
+            cJSON *repeat_json = cJSON_GetObjectItem(item, "repeat");
             if (!cJSON_IsString(path) || !cJSON_IsString(sd_path)) {
                 cJSON_Delete(root);
                 return false;
@@ -925,10 +922,14 @@
                 }
                 next_item->has_volume = true;
             }
-            next_item->speed = cJSON_IsNumber(speed_json) ? (float)speed_json->valuedouble : 1.0f;
-            if (!BoardMqtt_PlaybackSpeedIsSupported(next_item->speed)) {
-                cJSON_Delete(root);
-                return false;
+            next_item->speed = SDCard_Clamp_MP3_Speed(
+                cJSON_IsNumber(speed_json) ? (float)speed_json->valuedouble : 1.0f);
+            next_item->repeat = 1;
+            if (repeat_json) {
+                int repeat = 1;
+                if (BoardMqtt_NumberToInt(repeat_json, &repeat) && repeat >= 1) {
+                    next_item->repeat = repeat > 99 ? 99 : repeat;
+                }
             }
             next_item->index = index;
             next_item->count = count;
@@ -992,8 +993,10 @@
         BoardMqtt_ShowPlaylistAlert(alert_message, "music");
 
         SDCard_Show_Matching_JPG_For_MP3_Path(item.sd_path);
-        ESP_LOGI(TAG, "playlist play item %d/%d: %s speed=%.2fx", item.index + 1, item.count, item.sd_path, item.speed);
-        esp_err_t err = SDCard_Play_MP3_File_With_Speed(item.sd_path, item.speed);
+        ESP_LOGI(TAG, "playlist play item %d/%d: %s speed=%.2fx repeat=%d",
+                 item.index + 1, item.count, item.sd_path, item.speed, item.repeat);
+        esp_err_t err = SDCard_Play_MP3_File_With_Speed_Repeat(
+            item.sd_path, item.speed, item.repeat > 0 ? item.repeat : 1, false);
         if (err != ESP_OK) {
             ESP_LOGE(TAG, "playlist item playback failed: %s", esp_err_to_name(err));
             snprintf(alert_message, sizeof(alert_message), "鎾斁澶辫触 %d/%d: %s", item.index + 1, item.count, esp_err_to_name(err));
@@ -1894,6 +1897,38 @@
     return BoardMqtt_ResultOk("app task started");
 }
 
+static board_mqtt_action_result_t BoardMqtt_HandleEnsureAppDirs(cJSON *payload)
+{
+    if (!payload) {
+        return BoardMqtt_ResultFail(BOARD_MQTT_ERR_INVALID_PARAM, "missing payload");
+    }
+    cJSON *app_id = cJSON_GetObjectItem(payload, "app_id");
+    if (!cJSON_IsString(app_id) || !app_id->valuestring || app_id->valuestring[0] == '\0') {
+        return BoardMqtt_ResultFail(BOARD_MQTT_ERR_INVALID_PARAM, "missing app_id");
+    }
+    esp_err_t err = Board_EnsureAppLayout(app_id->valuestring, NULL);
+    if (err == ESP_ERR_INVALID_ARG || err == ESP_ERR_INVALID_SIZE) {
+        return BoardMqtt_ResultFail(BOARD_MQTT_ERR_INVALID_PARAM, "invalid app_id");
+    }
+    if (err != ESP_OK) {
+        return BoardMqtt_ResultFail(BOARD_MQTT_ERR_EXEC_FAILED, esp_err_to_name(err));
+    }
+    cJSON *tasks = cJSON_GetObjectItem(payload, "tasks");
+    if (cJSON_IsArray(tasks)) {
+        cJSON *task = NULL;
+        cJSON_ArrayForEach(task, tasks) {
+            if (!cJSON_IsString(task) || !task->valuestring) {
+                return BoardMqtt_ResultFail(BOARD_MQTT_ERR_INVALID_PARAM, "invalid tasks");
+            }
+            err = Board_EnsureAppLayout(app_id->valuestring, task->valuestring);
+            if (err != ESP_OK) {
+                return BoardMqtt_ResultFail(BOARD_MQTT_ERR_EXEC_FAILED, esp_err_to_name(err));
+            }
+        }
+    }
+    return BoardMqtt_ResultOk("app dirs ensured");
+}
+
 static board_mqtt_action_result_t BoardMqtt_DispatchAction(const char *req_id, const char *action, cJSON *payload)
 {
     if (strcmp(action, "SET_VOLUME") == 0) return BoardMqtt_HandleSetVolume(payload);
@@ -1909,6 +1944,7 @@
     if (strcmp(action, "DOWNLOAD") == 0) return BoardMqtt_HandleDownload(req_id, payload);
     if (strcmp(action, "OTA_UPDATE") == 0) return BoardMqtt_HandleOtaUpdate(req_id, payload);
     if (strcmp(action, "START_APP_TASK") == 0) return BoardMqtt_HandleStartAppTask(payload);
+    if (strcmp(action, "ENSURE_APP_DIRS") == 0) return BoardMqtt_HandleEnsureAppDirs(payload);
     return BoardMqtt_ResultFail(BOARD_MQTT_ERR_UNSUPPORTED, "unsupported action");
 }
 
@@ -2098,6 +2134,9 @@
     (void)arg;
     while (s_mqtt_client) {
         vTaskDelay(pdMS_TO_TICKS(BOARD_MQTT_STATE_REPORT_INTERVAL_MS));
+        if (Board_Is_Audio_Busy()) {
+            continue;
+        }
         BoardMqtt_PublishState();
     }
     s_state_task_handle = NULL;

--
Gitblit v1.9.1