Mirror of espurna firmware for wireless switches and more
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.

276 lines
7.6 KiB

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