diff --git a/components/CommandManager/CommandManager/CommandManager.cpp b/components/CommandManager/CommandManager/CommandManager.cpp index f2bb174..61dc9bf 100644 --- a/components/CommandManager/CommandManager/CommandManager.cpp +++ b/components/CommandManager/CommandManager/CommandManager.cpp @@ -26,6 +26,7 @@ std::unordered_map commandTypeMap = { {"get_led_duty_cycle", CommandType::GET_LED_DUTY_CYCLE}, {"get_serial", CommandType::GET_SERIAL}, {"get_led_current", CommandType::GET_LED_CURRENT}, + {"get_battery_status", CommandType::GET_BATTERY_STATUS}, {"get_who_am_i", CommandType::GET_WHO_AM_I}, }; @@ -102,6 +103,9 @@ std::function CommandManager::createCommand(const CommandType t case CommandType::GET_LED_CURRENT: return [this] { return getLEDCurrentCommand(this->registry); }; + case CommandType::GET_BATTERY_STATUS: + return [this] + { return getBatteryStatusCommand(this->registry); }; case CommandType::GET_WHO_AM_I: return [this] { return getInfoCommand(this->registry); }; diff --git a/components/CommandManager/CommandManager/CommandManager.hpp b/components/CommandManager/CommandManager/CommandManager.hpp index 5563570..1ddc5ad 100644 --- a/components/CommandManager/CommandManager/CommandManager.hpp +++ b/components/CommandManager/CommandManager/CommandManager.hpp @@ -47,6 +47,7 @@ enum class CommandType GET_LED_DUTY_CYCLE, GET_SERIAL, GET_LED_CURRENT, + GET_BATTERY_STATUS, GET_WHO_AM_I, }; diff --git a/components/CommandManager/CommandManager/commands/device_commands.cpp b/components/CommandManager/CommandManager/commands/device_commands.cpp index d0dfed8..ccc80f4 100644 --- a/components/CommandManager/CommandManager/commands/device_commands.cpp +++ b/components/CommandManager/CommandManager/commands/device_commands.cpp @@ -1,4 +1,6 @@ #include "device_commands.hpp" +#include +#include #include "LEDManager.hpp" #include "MonitoringManager.hpp" #include "esp_mac.h" @@ -219,6 +221,69 @@ CommandResult getLEDCurrentCommand(std::shared_ptr registry) #endif } +CommandResult getBatteryStatusCommand(std::shared_ptr registry) +{ +#if CONFIG_MONITORING_BATTERY_ENABLE + auto mon = registry->resolve(DependencyType::monitoring_manager); + if (!mon) + { + return CommandResult::getErrorResult("MonitoringManager unavailable"); + } + + const float volts = mon->getBatteryVoltageMilliVolts(); + if (volts <= 0.0f) + { + return CommandResult::getErrorResult("Battery voltage unavailable"); + } + + struct VoltageSOC + { + float voltage_mv; + float soc; + }; + + constexpr std::array lookup = { + VoltageSOC{4200.0f, 100.0f}, VoltageSOC{4060.0f, 90.0f}, VoltageSOC{3980.0f, 80.0f}, VoltageSOC{3920.0f, 70.0f}, + VoltageSOC{3870.0f, 60.0f}, VoltageSOC{3820.0f, 50.0f}, VoltageSOC{3790.0f, 40.0f}, VoltageSOC{3770.0f, 30.0f}, + VoltageSOC{3740.0f, 20.0f}, VoltageSOC{3680.0f, 10.0f}, VoltageSOC{3450.0f, 5.0f}, VoltageSOC{3300.0f, 0.0f}, + }; + + float percent = 0.0f; + if (volts >= lookup.front().voltage_mv) + { + percent = lookup.front().soc; + } + else if (volts <= lookup.back().voltage_mv) + { + percent = lookup.back().soc; + } + else + { + for (size_t index = 0; index < lookup.size() - 1; ++index) + { + const auto high = lookup[index]; + const auto low = lookup[index + 1]; + if (volts <= high.voltage_mv && volts >= low.voltage_mv) + { + const float span = high.voltage_mv - low.voltage_mv; + const float ratio = (volts - low.voltage_mv) / (span > 0.0f ? span : 1.0f); + percent = low.soc + ratio * (high.soc - low.soc); + break; + } + } + } + percent = std::clamp(percent, 0.0f, 100.0f); + + const auto json = nlohmann::json{ + {"voltage_mv", std::format("{:.2f}", static_cast(volts))}, + {"percentage", std::format("{:.1f}", static_cast(percent))}, + }; + return CommandResult::getSuccessResult(json); +#else + return CommandResult::getErrorResult("Battery monitor disabled"); +#endif +} + CommandResult getInfoCommand(std::shared_ptr /*registry*/) { const char *who = CONFIG_GENERAL_BOARD; diff --git a/components/CommandManager/CommandManager/commands/device_commands.hpp b/components/CommandManager/CommandManager/commands/device_commands.hpp index 496aa8f..4a71a78 100644 --- a/components/CommandManager/CommandManager/commands/device_commands.hpp +++ b/components/CommandManager/CommandManager/commands/device_commands.hpp @@ -26,6 +26,7 @@ CommandResult getSerialNumberCommand(std::shared_ptr registr // Monitoring CommandResult getLEDCurrentCommand(std::shared_ptr registry); +CommandResult getBatteryStatusCommand(std::shared_ptr registry); // General info CommandResult getInfoCommand(std::shared_ptr registry); \ No newline at end of file diff --git a/tools/setup_openiris.py b/tools/setup_openiris.py index 3e40872..da22d54 100644 --- a/tools/setup_openiris.py +++ b/tools/setup_openiris.py @@ -232,6 +232,19 @@ def get_led_current(device: OpenIrisDevice) -> dict: } +def get_battery_status(device: OpenIrisDevice) -> dict: + response = device.send_command("get_battery_status") + if has_command_failed(response): + print(f"āŒ Failed to get battery status: {response}") + return {"voltage_mv": "unknown", "percentage": "unknown"} + + data = response["results"][0]["result"]["data"] + return { + "voltage_mv": data.get("voltage_mv", "unknown"), + "percentage": data.get("percentage", "unknown"), + } + + def configure_device_name(device: OpenIrisDevice, *args, **kwargs): current_name = get_mdns_name(device) print(f"\nšŸ“ Current device name: {current_name['name']} \n") @@ -340,6 +353,7 @@ def get_settings_summary(device: OpenIrisDevice, *args, **kwargs): ("Info", get_device_info), ("LED", get_led_duty_cycle), ("Current", get_led_current), + ("Battery", get_battery_status), ("Mode", get_device_mode), ("WiFi", get_wifi_status), ] @@ -357,6 +371,11 @@ def get_settings_summary(device: OpenIrisDevice, *args, **kwargs): led_current_ma = current_section.get("led_current_ma") print(f"šŸ”Œ LED Current: {led_current_ma} mA") + battery = summary.get("Battery", {}) + voltage_mv = battery.get("voltage_mv") + percentage = battery.get("percentage") + print(f"šŸ”‹ Battery: {voltage_mv} mV | {percentage} %") + advertised_name_data = summary.get("AdvertisedName", {}) advertised_name = advertised_name_data.get("name") print(f"šŸ“› Name: {advertised_name}")