Improvements and refactors after CR, add option to modify mdns name and simplify setup tool

This commit is contained in:
Lorow
2025-08-12 23:48:44 +02:00
parent 9326746e1d
commit 5a86ae042f
21 changed files with 668 additions and 574 deletions

View File

@@ -25,66 +25,70 @@ std::unordered_map<std::string, CommandType> commandTypeMap = {
{"get_device_mode", CommandType::GET_DEVICE_MODE},
};
std::function<CommandResult()> CommandManager::createCommand(const CommandType type, std::string_view json) const {
std::function<CommandResult()> CommandManager::createCommand(const CommandType type, std::string_view json) const
{
switch (type)
{
case CommandType::PING:
return { PingCommand };
return {PingCommand};
case CommandType::PAUSE:
return [json] {
PausePayload payload;
cJSON* root = cJSON_Parse(std::string(json).c_str());
if (root) {
cJSON* pauseItem = cJSON_GetObjectItem(root, "pause");
if (pauseItem && cJSON_IsBool(pauseItem)) {
payload.pause = cJSON_IsTrue(pauseItem);
} else {
payload.pause = true; // Default to pause if not specified
}
cJSON_Delete(root);
} else {
payload.pause = true; // Default to pause if parsing fails
}
return PauseCommand(payload);
};
return [json]
{ return PauseCommand(json); };
case CommandType::SET_STREAMING_MODE:
return [this, json] {return setDeviceModeCommand(this->registry, json); };
return [this, json]
{ return setDeviceModeCommand(this->registry, json); };
case CommandType::UPDATE_OTA_CREDENTIALS:
return [this, json] { return updateOTACredentialsCommand(this->registry, json); };
return [this, json]
{ return updateOTACredentialsCommand(this->registry, json); };
case CommandType::SET_WIFI:
return [this, json] { return setWiFiCommand(this->registry, json); };
return [this, json]
{ return setWiFiCommand(this->registry, json); };
case CommandType::UPDATE_WIFI:
return [this, json] { return updateWiFiCommand(this->registry, json); };
return [this, json]
{ return updateWiFiCommand(this->registry, json); };
case CommandType::UPDATE_AP_WIFI:
return [this, json] { return updateAPWiFiCommand(this->registry, json); };
return [this, json]
{ return updateAPWiFiCommand(this->registry, json); };
case CommandType::DELETE_NETWORK:
return [this, json] { return deleteWiFiCommand(this->registry, json); };
return [this, json]
{ return deleteWiFiCommand(this->registry, json); };
case CommandType::SET_MDNS:
return [this, json] { return setMDNSCommand(this->registry, json); };
return [this, json]
{ return setMDNSCommand(this->registry, json); };
case CommandType::UPDATE_CAMERA:
return [this, json] { return updateCameraCommand(this->registry, json); };
return [this, json]
{ return updateCameraCommand(this->registry, json); };
case CommandType::RESTART_CAMERA:
return [this, json] { return restartCameraCommand(this->registry, json); };
return [this, json]
{ return restartCameraCommand(this->registry, json); };
case CommandType::GET_CONFIG:
return [this] { return getConfigCommand(this->registry); };
return [this]
{ return getConfigCommand(this->registry); };
case CommandType::SAVE_CONFIG:
return [this] { return saveConfigCommand(this->registry); };
return [this]
{ return saveConfigCommand(this->registry); };
case CommandType::RESET_CONFIG:
return [this, json] { return resetConfigCommand(this->registry, json); };
return [this, json]
{ return resetConfigCommand(this->registry, json); };
case CommandType::RESTART_DEVICE:
return restartDeviceCommand;
case CommandType::SCAN_NETWORKS:
return [this] { return scanNetworksCommand(this->registry); };
return [this]
{ return scanNetworksCommand(this->registry); };
case CommandType::START_STREAMING:
return startStreamingCommand;
case CommandType::GET_WIFI_STATUS:
return [this] { return getWiFiStatusCommand(this->registry); };
return [this]
{ return getWiFiStatusCommand(this->registry); };
case CommandType::CONNECT_WIFI:
return [this] { return connectWiFiCommand(this->registry); };
return [this]
{ return connectWiFiCommand(this->registry); };
case CommandType::SWITCH_MODE:
return [this, json] { return switchModeCommand(this->registry, json); };
return [this, json]
{ return switchModeCommand(this->registry, json); };
case CommandType::GET_DEVICE_MODE:
return [this] { return getDeviceModeCommand(this->registry); };
return [this]
{ return getDeviceModeCommand(this->registry); };
default:
return nullptr;
}
@@ -135,7 +139,7 @@ CommandResult CommandManager::executeFromJson(const std::string_view json) const
cJSON_AddItemToArray(responses, response);
}
char* jsonString = cJSON_Print(responseDocument);
char *jsonString = cJSON_Print(responseDocument);
cJSON_Delete(responseDocument);
cJSON_Delete(parsedJson);

View File

@@ -22,30 +22,30 @@ public:
CommandResult(std::string message, const Status status)
{
this->status = status;
// Escape quotes and backslashes in the message for JSON
std::string escapedMessage = message;
size_t pos = 0;
// First escape backslashes
while ((pos = escapedMessage.find('\\', pos)) != std::string::npos) {
while ((pos = escapedMessage.find('\\', pos)) != std::string::npos)
{
escapedMessage.replace(pos, 1, "\\\\");
pos += 2;
}
// Then escape quotes
pos = 0;
while ((pos = escapedMessage.find('"', pos)) != std::string::npos) {
while ((pos = escapedMessage.find('"', pos)) != std::string::npos)
{
escapedMessage.replace(pos, 1, "\\\"");
pos += 2;
}
if (status == Status::SUCCESS)
{
// we gotta do it this way, because if we define it as { "result": " {} " } it crashes the compiler, lol
this->message = std::format("{}\"result\":\"{}\"{}", "{", escapedMessage, "}");
this->message = std::format("{{\"result\":\"{}\"}}", escapedMessage);
}
else
{
this->message = std::format("{}\"error\":\"{}\"{}", "{", escapedMessage, "}");
this->message = std::format("{{\"error\":\"{}\"}}", escapedMessage);
}
}
@@ -60,7 +60,7 @@ public:
{
return CommandResult(message, Status::FAILURE);
}
// Create a result that returns raw JSON without wrapper
static CommandResult getRawJsonResult(const std::string &jsonMessage)
{
@@ -70,7 +70,6 @@ public:
}
std::string getResult() const { return this->message; }
};
#endif

View File

@@ -1,25 +1,24 @@
#include "device_commands.hpp"
#include <cJSON.h>
#include <ProjectConfig.hpp>
#include "esp_timer.h"
#include <main_globals.hpp>
// Implementation inspired by SummerSigh work, initial PR opened in openiris repo, adapted to this rewrite
CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload) {
CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload)
{
const auto parsedJson = cJSON_Parse(jsonPayload.data());
if (parsedJson == nullptr) {
if (parsedJson == nullptr)
{
return CommandResult::getErrorResult("Invalid payload");
}
const auto modeObject = cJSON_GetObjectItem(parsedJson, "mode");
if (modeObject == nullptr) {
if (modeObject == nullptr)
{
return CommandResult::getErrorResult("Invalid payload - missing mode");
}
const auto mode = modeObject->valueint;
if (mode < 0 || mode > 2) {
if (mode < 0 || mode > 2)
{
return CommandResult::getErrorResult("Invalid payload - unsupported mode");
}
@@ -29,10 +28,12 @@ CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry,
return CommandResult::getSuccessResult("Device mode set");
}
CommandResult updateOTACredentialsCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload) {
CommandResult updateOTACredentialsCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload)
{
const auto parsedJson = cJSON_Parse(jsonPayload.data());
if (parsedJson == nullptr) {
if (parsedJson == nullptr)
{
return CommandResult::getErrorResult("Invalid payload");
}
@@ -42,18 +43,23 @@ CommandResult updateOTACredentialsCommand(std::shared_ptr<DependencyRegistry> re
auto OTAPassword = oldDeviceConfig.OTAPassword;
auto OTAPort = oldDeviceConfig.OTAPort;
if (const auto OTALoginObject = cJSON_GetObjectItem(parsedJson, "login"); OTALoginObject != nullptr) {
if (const auto newLogin = OTALoginObject->valuestring; strcmp(newLogin, "") != 0) {
if (const auto OTALoginObject = cJSON_GetObjectItem(parsedJson, "login"); OTALoginObject != nullptr)
{
if (const auto newLogin = OTALoginObject->valuestring; strcmp(newLogin, "") != 0)
{
OTALogin = newLogin;
}
}
if (const auto OTAPasswordObject = cJSON_GetObjectItem(parsedJson, "password"); OTAPasswordObject != nullptr) {
if (const auto OTAPasswordObject = cJSON_GetObjectItem(parsedJson, "password"); OTAPasswordObject != nullptr)
{
OTAPassword = OTAPasswordObject->valuestring;
}
if (const auto OTAPortObject = cJSON_GetObjectItem(parsedJson, "port"); OTAPortObject != nullptr) {
if (const auto newPort = OTAPortObject->valueint; newPort >= 82) {
if (const auto OTAPortObject = cJSON_GetObjectItem(parsedJson, "port"); OTAPortObject != nullptr)
{
if (const auto newPort = OTAPortObject->valueint; newPort >= 82)
{
OTAPort = newPort;
}
}
@@ -62,70 +68,82 @@ CommandResult updateOTACredentialsCommand(std::shared_ptr<DependencyRegistry> re
return CommandResult::getSuccessResult("OTA Config set");
}
CommandResult restartDeviceCommand() {
CommandResult restartDeviceCommand()
{
OpenIrisTasks::ScheduleRestart(2000);
return CommandResult::getSuccessResult("Device restarted");
}
CommandResult startStreamingCommand() {
CommandResult startStreamingCommand()
{
activateStreaming(false); // Don't disable setup interfaces by default
return CommandResult::getSuccessResult("Streaming started");
}
CommandResult switchModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload) {
CommandResult switchModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload)
{
const auto parsedJson = cJSON_Parse(jsonPayload.data());
if (parsedJson == nullptr) {
if (parsedJson == nullptr)
{
return CommandResult::getErrorResult("Invalid payload");
}
const auto modeObject = cJSON_GetObjectItem(parsedJson, "mode");
if (modeObject == nullptr) {
if (modeObject == nullptr)
{
return CommandResult::getErrorResult("Invalid payload - missing mode");
}
const char* modeStr = modeObject->valuestring;
const char *modeStr = modeObject->valuestring;
StreamingMode newMode;
ESP_LOGI("[DEVICE_COMMANDS]", "Switch mode command received with mode: %s", modeStr);
if (strcmp(modeStr, "uvc") == 0 || strcmp(modeStr, "UVC") == 0) {
if (strcmp(modeStr, "uvc") == 0)
{
newMode = StreamingMode::UVC;
} else if (strcmp(modeStr, "wifi") == 0 || strcmp(modeStr, "WiFi") == 0 || strcmp(modeStr, "WIFI") == 0) {
}
else if (strcmp(modeStr, "wifi") == 0)
{
newMode = StreamingMode::WIFI;
} else if (strcmp(modeStr, "auto") == 0 || strcmp(modeStr, "AUTO") == 0) {
}
else if (strcmp(modeStr, "auto") == 0)
{
newMode = StreamingMode::AUTO;
} else {
}
else
{
return CommandResult::getErrorResult("Invalid mode - use 'uvc', 'wifi', or 'auto'");
}
const auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
ESP_LOGI("[DEVICE_COMMANDS]", "Setting device mode to: %d", (int)newMode);
projectConfig->setDeviceMode(newMode);
cJSON_Delete(parsedJson);
return CommandResult::getSuccessResult("Device mode switched, restart to apply");
}
CommandResult getDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry) {
CommandResult getDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry)
{
const auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
StreamingMode currentMode = projectConfig->getDeviceMode();
const char* modeStr = "unknown";
switch (currentMode) {
case StreamingMode::UVC:
modeStr = "UVC";
break;
case StreamingMode::WIFI:
modeStr = "WiFi";
break;
case StreamingMode::AUTO:
modeStr = "Auto";
break;
const char *modeStr = "unknown";
switch (currentMode)
{
case StreamingMode::UVC:
modeStr = "UVC";
break;
case StreamingMode::WIFI:
modeStr = "WiFi";
break;
case StreamingMode::AUTO:
modeStr = "Auto";
break;
}
char result[100];
sprintf(result, "{\"mode\":\"%s\",\"value\":%d}", modeStr, (int)currentMode);
auto result = std::format("{{ \"mode\": \"{}\", \"value\": {} }}", modeStr, static_cast<int>(currentMode));
return CommandResult::getSuccessResult(result);
}

View File

@@ -1,6 +1,13 @@
#include "CommandResult.hpp"
#include "ProjectConfig.hpp"
#include "OpenIrisTasks.hpp"
#include "DependencyRegistry.hpp"
#include "esp_timer.h"
#include "cJSON.h"
#include "main_globals.hpp"
#include <format>
#include <string>
CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload);

View File

@@ -1,11 +1,10 @@
#include "scan_commands.hpp"
#include "cJSON.h"
#include "esp_log.h"
#include <string>
CommandResult scanNetworksCommand(std::shared_ptr<DependencyRegistry> registry) {
CommandResult scanNetworksCommand(std::shared_ptr<DependencyRegistry> registry)
{
auto wifiManager = registry->resolve<WiFiManager>(DependencyType::wifi_manager);
if (!wifiManager) {
if (!wifiManager)
{
return CommandResult::getErrorResult("WiFiManager not available");
}
@@ -15,7 +14,8 @@ CommandResult scanNetworksCommand(std::shared_ptr<DependencyRegistry> registry)
cJSON *networksArray = cJSON_CreateArray();
cJSON_AddItemToObject(root, "networks", networksArray);
for (const auto& network : networks) {
for (const auto &network : networks)
{
cJSON *networkObject = cJSON_CreateObject();
cJSON_AddStringToObject(networkObject, "ssid", network.ssid.c_str());
cJSON_AddNumberToObject(networkObject, "channel", network.channel);

View File

@@ -1,9 +1,12 @@
#ifndef SCAN_COMMANDS_HPP
#define SCAN_COMMANDS_HPP
#include "../CommandResult.hpp"
#include "../DependencyRegistry.hpp"
#include "CommandResult.hpp"
#include "DependencyRegistry.hpp"
#include "esp_log.h"
#include <cJSON.h>
#include <wifiManager.hpp>
#include <string>
CommandResult scanNetworksCommand(std::shared_ptr<DependencyRegistry> registry);

View File

@@ -1,24 +1,39 @@
#include "simple_commands.hpp"
#include "main_globals.hpp"
#include "esp_log.h"
static const char* TAG = "SimpleCommands";
static const char *TAG = "SimpleCommands";
CommandResult PingCommand()
{
return CommandResult::getSuccessResult("pong");
};
CommandResult PauseCommand(const PausePayload& payload)
CommandResult PauseCommand(std::string_view jsonPayload)
{
PausePayload payload;
// pause by default if this command gets executed, even if the payload was invalid
payload.pause = true;
cJSON *root = cJSON_Parse(std::string(jsonPayload).c_str());
if (root)
{
cJSON *pauseItem = cJSON_GetObjectItem(root, "pause");
if (pauseItem && cJSON_IsBool(pauseItem))
{
payload.pause = cJSON_IsTrue(pauseItem);
}
cJSON_Delete(root);
}
ESP_LOGI(TAG, "Pause command received: %s", payload.pause ? "true" : "false");
startupPaused = payload.pause;
if (payload.pause) {
setStartupPaused(payload.pause);
if (payload.pause)
{
ESP_LOGI(TAG, "Startup paused - device will remain in configuration mode");
return CommandResult::getSuccessResult("Startup paused");
} else {
}
else
{
ESP_LOGI(TAG, "Startup resumed");
return CommandResult::getSuccessResult("Startup resumed");
}

View File

@@ -4,8 +4,11 @@
#include <string>
#include "CommandResult.hpp"
#include "CommandSchema.hpp"
#include "main_globals.hpp"
#include "esp_log.h"
#include <cJSON.h>
CommandResult PingCommand();
CommandResult PauseCommand(const PausePayload& payload);
CommandResult PauseCommand(std::string_view jsonPayload);
#endif