Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

326 lines
8.9 KiB

7 years ago
6 years ago
  1. /*
  2. SETTINGS MODULE
  3. Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #include <vector>
  6. #include <ArduinoJson.h>
  7. #include "settings_internal.h"
  8. #include "settings.h"
  9. // -----------------------------------------------------------------------------
  10. // Reverse engineering EEPROM storage format
  11. // -----------------------------------------------------------------------------
  12. unsigned long settingsSize() {
  13. unsigned pos = SPI_FLASH_SEC_SIZE - 1;
  14. while (size_t len = EEPROMr.read(pos)) {
  15. if (0xFF == len) break;
  16. pos = pos - len - 2;
  17. }
  18. return SPI_FLASH_SEC_SIZE - pos + EEPROM_DATA_END;
  19. }
  20. // -----------------------------------------------------------------------------
  21. unsigned int settingsKeyCount() {
  22. unsigned count = 0;
  23. unsigned pos = SPI_FLASH_SEC_SIZE - 1;
  24. while (size_t len = EEPROMr.read(pos)) {
  25. if (0xFF == len) break;
  26. pos = pos - len - 2;
  27. len = EEPROMr.read(pos);
  28. pos = pos - len - 2;
  29. count ++;
  30. }
  31. return count;
  32. }
  33. String settingsKeyName(unsigned int index) {
  34. String s;
  35. unsigned count = 0;
  36. unsigned pos = SPI_FLASH_SEC_SIZE - 1;
  37. while (size_t len = EEPROMr.read(pos)) {
  38. if (0xFF == len) break;
  39. pos = pos - len - 2;
  40. if (count == index) {
  41. s.reserve(len);
  42. for (unsigned char i = 0 ; i < len; i++) {
  43. s += (char) EEPROMr.read(pos + i + 1);
  44. }
  45. break;
  46. }
  47. count++;
  48. len = EEPROMr.read(pos);
  49. pos = pos - len - 2;
  50. }
  51. return s;
  52. }
  53. std::vector<String> _settingsKeys() {
  54. // Get sorted list of keys
  55. std::vector<String> keys;
  56. //unsigned int size = settingsKeyCount();
  57. unsigned int size = settingsKeyCount();
  58. for (unsigned int i=0; i<size; i++) {
  59. //String key = settingsKeyName(i);
  60. String key = settingsKeyName(i);
  61. bool inserted = false;
  62. for (unsigned char j=0; j<keys.size(); j++) {
  63. // Check if we have to insert it before the current element
  64. if (keys[j].compareTo(key) > 0) {
  65. keys.insert(keys.begin() + j, key);
  66. inserted = true;
  67. break;
  68. }
  69. }
  70. // If we could not insert it, just push it at the end
  71. if (!inserted) keys.push_back(key);
  72. }
  73. return keys;
  74. }
  75. // -----------------------------------------------------------------------------
  76. // Key-value API
  77. // -----------------------------------------------------------------------------
  78. String settings_key_t::toString() const {
  79. if (index < 0) {
  80. return value;
  81. } else {
  82. return value + index;
  83. }
  84. }
  85. settings_move_key_t _moveKeys(const String& from, const String& to, unsigned char index) {
  86. return settings_move_key_t {{from, index}, {to, index}};
  87. }
  88. void moveSetting(const String& from, const String& to) {
  89. const auto value = getSetting(from);
  90. if (value.length() > 0) setSetting(to, value);
  91. delSetting(from);
  92. }
  93. void moveSetting(const String& from, const String& to, unsigned char index) {
  94. const auto keys = _moveKeys(from, to, index);
  95. const auto value = getSetting(keys.first);
  96. if (value.length() > 0) setSetting(keys.second, value);
  97. delSetting(keys.first);
  98. }
  99. void moveSettings(const String& from, const String& to) {
  100. unsigned char index = 0;
  101. while (index < 100) {
  102. const auto keys = _moveKeys(from, to, index);
  103. const auto value = getSetting(keys.first);
  104. if (value.length() == 0) break;
  105. setSetting(keys.second, value);
  106. delSetting(keys.first);
  107. ++index;
  108. }
  109. }
  110. template<typename R, settings::internal::convert_t<R> Rfunc = settings::internal::convert>
  111. R getSetting(const settings_key_t& key, R defaultValue) {
  112. String value;
  113. if (!Embedis::get(key.toString(), value)) {
  114. return defaultValue;
  115. }
  116. return Rfunc(value);
  117. }
  118. template<>
  119. String getSetting(const settings_key_t& key, String defaultValue) {
  120. String value;
  121. if (!Embedis::get(key.toString(), value)) {
  122. value = defaultValue;
  123. }
  124. return value;
  125. }
  126. String getSetting(const settings_key_t& key) {
  127. static const String defaultValue("");
  128. return getSetting(key, defaultValue);
  129. }
  130. String getSetting(const settings_key_t& key, const char* defaultValue) {
  131. return getSetting(key, String(defaultValue));
  132. }
  133. String getSetting(const settings_key_t& key, const __FlashStringHelper* defaultValue) {
  134. return getSetting(key, String(defaultValue));
  135. }
  136. template<typename T>
  137. bool setSetting(const settings_key_t& key, const T& value) {
  138. return Embedis::set(key.toString(), String(value));
  139. }
  140. bool delSetting(const settings_key_t& key) {
  141. return Embedis::del(key.toString());
  142. }
  143. bool hasSetting(const settings_key_t& key) {
  144. String value;
  145. return Embedis::get(key.toString(), value);
  146. }
  147. void saveSettings() {
  148. #if not SETTINGS_AUTOSAVE
  149. eepromCommit();
  150. #endif
  151. }
  152. void resetSettings() {
  153. for (unsigned int i = 0; i < EEPROM_SIZE; i++) {
  154. EEPROMr.write(i, 0xFF);
  155. }
  156. EEPROMr.commit();
  157. }
  158. // -----------------------------------------------------------------------------
  159. // Deprecated implementation
  160. // -----------------------------------------------------------------------------
  161. template<typename T>
  162. String getSetting(const String& key, unsigned char index, T defaultValue) {
  163. return getSetting({key, index}, defaultValue);
  164. }
  165. template<typename T>
  166. bool setSetting(const String& key, unsigned char index, T value) {
  167. return setSetting({key, index}, value);
  168. }
  169. template<typename T>
  170. bool hasSetting(const String& key, unsigned char index) {
  171. return hasSetting({key, index});
  172. }
  173. template<typename T>
  174. bool delSetting(const String& key, unsigned char index) {
  175. return delSetting({key, index});
  176. }
  177. // -----------------------------------------------------------------------------
  178. // API
  179. // -----------------------------------------------------------------------------
  180. size_t settingsMaxSize() {
  181. size_t size = EEPROM_SIZE;
  182. if (size > SPI_FLASH_SEC_SIZE) size = SPI_FLASH_SEC_SIZE;
  183. size = (size + 3) & (~3);
  184. return size;
  185. }
  186. bool settingsRestoreJson(JsonObject& data) {
  187. // Check this is an ESPurna configuration file (must have "app":"ESPURNA")
  188. const char* app = data["app"];
  189. if (!app || strcmp(app, APP_NAME) != 0) {
  190. DEBUG_MSG_P(PSTR("[SETTING] Wrong or missing 'app' key\n"));
  191. return false;
  192. }
  193. // Clear settings
  194. bool is_backup = data["backup"];
  195. if (is_backup) {
  196. for (unsigned int i = EEPROM_DATA_END; i < SPI_FLASH_SEC_SIZE; i++) {
  197. EEPROMr.write(i, 0xFF);
  198. }
  199. }
  200. // Dump settings to memory buffer
  201. for (auto element : data) {
  202. if (strcmp(element.key, "app") == 0) continue;
  203. if (strcmp(element.key, "version") == 0) continue;
  204. if (strcmp(element.key, "backup") == 0) continue;
  205. setSetting(element.key, element.value.as<char*>());
  206. }
  207. // Persist to EEPROM
  208. saveSettings();
  209. DEBUG_MSG_P(PSTR("[SETTINGS] Settings restored successfully\n"));
  210. return true;
  211. }
  212. bool settingsRestoreJson(char* json_string, size_t json_buffer_size = 1024) {
  213. // XXX: as of right now, arduinojson cannot trigger callbacks for each key individually
  214. // Manually separating kv pairs can allow to parse only a small chunk, since we know that there is only string type used (even with bools / ints). Can be problematic when parsing data that was not generated by us.
  215. // Current parsing method is limited only by keys (~sizeof(uintptr_t) bytes per key, data is not copied when string is non-const)
  216. DynamicJsonBuffer jsonBuffer(json_buffer_size);
  217. JsonObject& root = jsonBuffer.parseObject((char *) json_string);
  218. if (!root.success()) {
  219. DEBUG_MSG_P(PSTR("[SETTINGS] JSON parsing error\n"));
  220. return false;
  221. }
  222. return settingsRestoreJson(root);
  223. }
  224. void settingsGetJson(JsonObject& root) {
  225. // Get sorted list of keys
  226. std::vector<String> keys = _settingsKeys();
  227. // Add the key-values to the json object
  228. for (unsigned int i=0; i<keys.size(); i++) {
  229. String value = getSetting(keys[i]);
  230. root[keys[i]] = value;
  231. }
  232. }
  233. void settingsProcessConfig(const settings_cfg_list_t& config, settings_filter_t filter) {
  234. for (auto& entry : config) {
  235. String value = getSetting(entry.key, entry.default_value);
  236. if (filter) {
  237. value = filter(value);
  238. }
  239. if (value.equals(entry.setting)) continue;
  240. entry.setting = std::move(value);
  241. }
  242. }
  243. // -----------------------------------------------------------------------------
  244. // Initialization
  245. // -----------------------------------------------------------------------------
  246. void settingsSetup() {
  247. Embedis::dictionary( F("EEPROM"),
  248. SPI_FLASH_SEC_SIZE,
  249. [](size_t pos) -> char { return EEPROMr.read(pos); },
  250. [](size_t pos, char value) { EEPROMr.write(pos, value); },
  251. #if SETTINGS_AUTOSAVE
  252. []() { eepromCommit(); }
  253. #else
  254. []() {}
  255. #endif
  256. );
  257. }