#pragma once #include #include "JsonCoordinateMapping.h" #include #include class Configuration { public: Configuration(const JsonObject& data); const char* get(const char* key, const char* defaultVal) const; int get(const char* key, int defaultVal) const; bool get(const char* key, bool defaultVal) const; private: const JsonObject& m_json; }; class ConfigTaskMixin : public virtual Loopable { public: void handleEvent(const InputEvent &evt) override; void loop() override {} virtual void handleConfigChange(const Configuration& config) {} }; struct HardwareConfig { uint8_t version = 3; uint8_t checksum = 0; struct Data { char loadedProfile[16] = {0}; uint8_t lastRed = 255; uint8_t lastGreen = 255; uint8_t lastBlue = 255; char lastScene[16] = {0}; }; Data data; static HardwareConfig load(); void save(); bool isValid() const; static constexpr uint16_t MAX_LED_NUM = 512; private: uint8_t getCRC() const; static constexpr uint8_t CRC7_POLY = 0x91; }; // A task that manages the EEPROM settings and coord mapping when modified via // Particle. This allows for multiple devices with wildly different displays to // run the same code struct ConfigService: public Task { ConfigService() : Task("Configuration") {state = Task::Running;} void onStart(); void loop() override; void handleEvent(const InputEvent &evt) override; const CoordinateMapping* coordMap() const { return &m_jsonMap; } const char* loadedProfile() const; void overrideProfile(const char* profileName); const char* getConfigValue(const char* key) const; const std::vector& commands() const override; struct filename_iterator: public std::iterator { Dir dir; String ret; bool valid; const char* suffix; explicit filename_iterator() : suffix(NULL), valid(false) {} explicit filename_iterator(const char* path, const char* suffix) : dir(LittleFS.openDir(path)), valid(true), suffix(suffix) { next(); } void next() { if (!valid) { return; } int extPos = -1; do { valid = dir.next(); Log.info("valid %F", valid); if (valid) { String fname = dir.fileName(); extPos = fname.lastIndexOf(suffix); Log.info("compare %s %d", fname.c_str(), extPos); if (extPos != -1) { ret = fname.substring(0, extPos); Log.info("found %s", ret.c_str()); } } } while (valid && extPos == -1); } filename_iterator& operator++() { next(); return *this; } filename_iterator& operator++(int) {filename_iterator ret = *this; ++(*this); return ret;} bool operator==(const filename_iterator &other) const { return valid == other.valid;} bool operator!=(const filename_iterator &other) const { return !(*this == other); } const char* operator*() const { if (!valid) { return NULL; } return ret.c_str(); } }; filename_iterator mapsBegin() const { return filename_iterator("/maps/", ".json"); } filename_iterator mapsEnd() const { return filename_iterator(); } filename_iterator profilesBegin() const { return filename_iterator("/profiles/", ".json"); } filename_iterator profilesEnd() const { return filename_iterator(); } private: HardwareConfig m_config; JsonCoordinateMapping m_jsonMap; const char* m_overrideProfile = nullptr; bool loadProfile(const char* name); bool loadMap(const String& mapName); };