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.

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