11#include <ArduinoJson.h>
15#ifndef HOST_TEST_BUILD
18#include "../test/fakes/arduino_shim.h"
21using namespace ArduinoJson;
33const char* ConfigLoader::DEFAULT_CONFIG =
37 " \"name\": \"Navigation\",\n"
38 " \"description\": \"Profile for controlling media playback\",\n"
40 " \"A\": {\"type\": \"SendStringAction\", \"name\": \"Space\", \"value\": \" \"},\n"
41 " \"B\": {\"type\": \"SendMediaKeyAction\", \"name\": \"Stop\", \"value\": "
43 " \"C\": {\"type\": \"SendCharAction\", \"name\": \"Rewind\", \"value\": "
45 " \"D\": {\"type\": \"SendCharAction\", \"name\": \"Forward\", \"value\": "
86 const std::string& configPath)
89 if (! fileSystem_->
readFile(configPath.c_str(), content))
91 logger_->
log(
"Failed to read config file");
110 const std::string& jsonConfig)
112 DynamicJsonDocument doc(8192);
113 DeserializationError error = deserializeJson(doc, jsonConfig);
117 logger_->
log(
"JSON parsing failed:", error.c_str());
126 JsonArray profiles = doc[
"profiles"];
129 JsonObject profileJson = profiles[i];
130 const char* profileName = profileJson[
"name"] |
"";
131 const char* profileDescription = profileJson[
"description"] |
"";
133 auto newProfile = std::unique_ptr<Profile>(
new Profile(profileName));
134 newProfile->setDescription(profileDescription);
135 populateProfileFromJson(*newProfile, profileJson[
"buttons"], keyboard);
154 if (buttonName[0] >=
'A' && buttonName[0] <= (
'A' + Btn::MAX - 1) && buttonName[1] ==
'\0')
156 return static_cast<uint8_t
>(buttonName[0] -
'A');
171void ConfigLoader::populateProfileFromJson(
Profile& profile,
179 if (! buttons.containsKey(buttonName))
184 JsonObject actionJson = buttons[buttonName];
186 std::unique_ptr<Action> action = createActionFromJson(actionJson, keyboard);
189 const char* actionName = actionJson[
"name"] |
"";
190 if (actionName[0] !=
'\0')
192 action->setName(actionName);
202std::unique_ptr<Action> ConfigLoader::createSendCharActionFromJson(
const JsonObject& actionJson,
205 const char* value = actionJson[
"value"] |
"";
209 return std::unique_ptr<Action>(
new SendCharAction(keyboard,
static_cast<char>(code)));
212 if (value[0] !=
'\0' && value[1] ==
'\0')
214 return std::unique_ptr<Action>(
new SendCharAction(keyboard, value[0]));
216 logger_->
log(
"SendChar: unknown key value: ", value);
231std::unique_ptr<Action> ConfigLoader::createActionFromJson(
const JsonObject& actionJson,
234 const char* typeName = actionJson[
"type"] |
"";
241 const char* value = actionJson[
"value"] |
"";
245 return createSendCharActionFromJson(actionJson, keyboard);
248 uint8_t code =
lookupKey(actionJson[
"value"] |
"");
251 return std::unique_ptr<Action>(
new SendKeyAction(keyboard, code));
266 const char* value = actionJson[
"value"] |
"";
271 uint32_t delayMs = actionJson[
"delayMs"] | 0U;
272 JsonObject nestedJson = actionJson[
"action"];
273 std::unique_ptr<Action> inner = createActionFromJson(nestedJson, keyboard);
276 return std::unique_ptr<Action>(
new DelayedAction(std::move(inner), delayMs));
static uint8_t getButtonIndex(const char *buttonName)
Converts button name to button index.
ConfigLoader()
Default constructor: uses production singletons (firmware path)
bool loadFromFile(ProfileManager &profileManager, IBleKeyboard *keyboard, const std::string &configPath)
Loads configuration from a file.
bool loadFromString(ProfileManager &profileManager, IBleKeyboard *keyboard, const std::string &jsonConfig)
Loads configuration from a JSON string.
Wraps an action and defers its execution by a configurable delay.
Interface for Bluetooth LE keyboard functionality.
Abstract interface for file system operations.
virtual bool readFile(const char *path, std::string &content)=0
Read entire file content.
Interface for logging functionality.
virtual void log(const char *message)=0
Logs a single message.
Manages up to MAX_PROFILES profiles with LED feedback.
void resetToFirstProfile()
Reset to the first populated slot (or slot 0 if all empty)
void addProfile(uint8_t profileIndex, std::unique_ptr< Profile > profile)
Represents a single button configuration profile.
void addAction(uint8_t button, std::unique_ptr< Action > action)
Adds an action to a specific button in this profile.
Sends a single character via BLE keyboard.
Sends a USB HID key code via BLE keyboard.
Sends a text string via BLE keyboard.
Action that outputs text to serial console.
const HardwareConfig hardwareConfig
Global hardware configuration instance.
IFileSystem * createFileSystem()
ILogger * createLogger()
Creates and returns a SerialLogger instance.
const uint8_t * lookupMediaKey(const char *name)
Looks up a media key report by name.
uint8_t lookupKey(const char *name)
Looks up a key code by name.
Action::Type lookupActionType(const char *name)
Looks up an action type by name.
ProfileManager * profileManager
void name(uint8_t index, char *buf)
Write the letter name for a button index into buf.
uint8_t numButtons
Action buttons wired (1..26, A–Z)
uint8_t numProfiles
Number of active profiles (1..MAX_PROFILES)