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.

475 lines
12 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 "settings.h"
  6. #include <ArduinoJson.h>
  7. #include <vector>
  8. #include <cstdlib>
  9. // -----------------------------------------------------------------------------
  10. // (HACK) Embedis storage format, reverse engineered
  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. namespace settings {
  22. namespace internal {
  23. uint32_t u32fromString(const String& string, int base) {
  24. const char *ptr = string.c_str();
  25. char *value_endptr = nullptr;
  26. // invalidate the whole string when invalid chars are detected
  27. const auto value = strtoul(ptr, &value_endptr, base);
  28. if (value_endptr == ptr || value_endptr[0] != '\0') {
  29. return 0;
  30. }
  31. return value;
  32. }
  33. // --------------------------------------------------------------------------
  34. template <>
  35. float convert(const String& value) {
  36. return atof(value.c_str());
  37. }
  38. template <>
  39. double convert(const String& value) {
  40. return atof(value.c_str());
  41. }
  42. template <>
  43. int convert(const String& value) {
  44. return value.toInt();
  45. }
  46. template <>
  47. long convert(const String& value) {
  48. return value.toInt();
  49. }
  50. template <>
  51. bool convert(const String& value) {
  52. return convert<int>(value) == 1;
  53. }
  54. template <>
  55. unsigned long convert(const String& value) {
  56. if (!value.length()) {
  57. return 0;
  58. }
  59. int base = 10;
  60. if (value.length() > 2) {
  61. if (value.startsWith("0b")) {
  62. base = 2;
  63. } else if (value.startsWith("0o")) {
  64. base = 8;
  65. } else if (value.startsWith("0x")) {
  66. base = 16;
  67. }
  68. }
  69. return u32fromString((base == 10) ? value : value.substring(2), base);
  70. }
  71. template <>
  72. unsigned int convert(const String& value) {
  73. return convert<unsigned long>(value);
  74. }
  75. template <>
  76. unsigned short convert(const String& value) {
  77. return convert<unsigned long>(value);
  78. }
  79. template <>
  80. unsigned char convert(const String& value) {
  81. return convert<unsigned long>(value);
  82. }
  83. } // namespace settings::internal
  84. } // namespace settings
  85. // -----------------------------------------------------------------------------
  86. size_t settingsKeyCount() {
  87. unsigned count = 0;
  88. unsigned pos = SPI_FLASH_SEC_SIZE - 1;
  89. while (size_t len = EEPROMr.read(pos)) {
  90. if (0xFF == len) break;
  91. pos = pos - len - 2;
  92. len = EEPROMr.read(pos);
  93. pos = pos - len - 2;
  94. count ++;
  95. }
  96. return count;
  97. }
  98. String settingsKeyName(unsigned int index) {
  99. String s;
  100. unsigned count = 0;
  101. unsigned pos = SPI_FLASH_SEC_SIZE - 1;
  102. while (size_t len = EEPROMr.read(pos)) {
  103. if (0xFF == len) break;
  104. pos = pos - len - 2;
  105. if (count == index) {
  106. s.reserve(len);
  107. for (unsigned char i = 0 ; i < len; i++) {
  108. s += (char) EEPROMr.read(pos + i + 1);
  109. }
  110. break;
  111. }
  112. count++;
  113. len = EEPROMr.read(pos);
  114. pos = pos - len - 2;
  115. }
  116. return s;
  117. }
  118. /*
  119. struct SettingsKeys {
  120. struct iterator {
  121. iterator(size_t total) :
  122. total(total)
  123. {}
  124. iterator& operator++() {
  125. if (total && (current_index < (total - 1))) {
  126. ++current_index
  127. current_value = settingsKeyName(current_index);
  128. return *this;
  129. }
  130. return end();
  131. }
  132. iterator operator++(int) {
  133. iterator val = *this;
  134. ++(*this);
  135. return val;
  136. }
  137. operator String() {
  138. return (current_index < total) ? current_value : empty_value;
  139. }
  140. bool operator ==(iterator& const other) const {
  141. return (total == other.total) && (current_index == other.current_index);
  142. }
  143. bool operator !=(iterator& const other) const {
  144. return !(*this == other);
  145. }
  146. using difference_type = size_t;
  147. using value_type = size_t;
  148. using pointer = const size_t*;
  149. using reference = const size_t&;
  150. using iterator_category = std::forward_iterator_tag;
  151. const size_t total;
  152. String empty_value;
  153. String current_value;
  154. size_t current_index = 0;
  155. };
  156. iterator begin() {
  157. return iterator {total};
  158. }
  159. iterator end() {
  160. return iterator {0};
  161. }
  162. };
  163. */
  164. std::vector<String> settingsKeys() {
  165. // Get sorted list of keys
  166. std::vector<String> keys;
  167. //unsigned int size = settingsKeyCount();
  168. auto size = settingsKeyCount();
  169. for (unsigned int i=0; i<size; i++) {
  170. //String key = settingsKeyName(i);
  171. String key = settingsKeyName(i);
  172. bool inserted = false;
  173. for (unsigned char j=0; j<keys.size(); j++) {
  174. // Check if we have to insert it before the current element
  175. if (keys[j].compareTo(key) > 0) {
  176. keys.insert(keys.begin() + j, key);
  177. inserted = true;
  178. break;
  179. }
  180. }
  181. // If we could not insert it, just push it at the end
  182. if (!inserted) keys.push_back(key);
  183. }
  184. return keys;
  185. }
  186. // -----------------------------------------------------------------------------
  187. // Key-value API
  188. // -----------------------------------------------------------------------------
  189. String settings_key_t::toString() const {
  190. if (index < 0) {
  191. return value;
  192. } else {
  193. return value + index;
  194. }
  195. }
  196. settings_move_key_t _moveKeys(const String& from, const String& to, unsigned char index) {
  197. return settings_move_key_t {{from, index}, {to, index}};
  198. }
  199. void moveSetting(const String& from, const String& to) {
  200. const auto value = getSetting(from);
  201. if (value.length() > 0) setSetting(to, value);
  202. delSetting(from);
  203. }
  204. void moveSetting(const String& from, const String& to, unsigned char index) {
  205. const auto keys = _moveKeys(from, to, index);
  206. const auto value = getSetting(keys.first);
  207. if (value.length() > 0) setSetting(keys.second, value);
  208. delSetting(keys.first);
  209. }
  210. void moveSettings(const String& from, const String& to) {
  211. unsigned char index = 0;
  212. while (index < 100) {
  213. const auto keys = _moveKeys(from, to, index);
  214. const auto value = getSetting(keys.first);
  215. if (value.length() == 0) break;
  216. setSetting(keys.second, value);
  217. delSetting(keys.first);
  218. ++index;
  219. }
  220. }
  221. #if 0
  222. template<typename R, settings::internal::convert_t<R> Rfunc = settings::internal::convert>
  223. R getSetting(const settings_key_t& key, R defaultValue) {
  224. String value;
  225. if (!Embedis::get(key.toString(), value)) {
  226. return defaultValue;
  227. }
  228. return Rfunc(value);
  229. }
  230. #endif
  231. template<>
  232. String getSetting(const settings_key_t& key, String defaultValue) {
  233. String value;
  234. if (!Embedis::get(key.toString(), value)) {
  235. value = defaultValue;
  236. }
  237. return value;
  238. }
  239. template
  240. bool getSetting(const settings_key_t& key, bool defaultValue);
  241. template
  242. int getSetting(const settings_key_t& key, int defaultValue);
  243. template
  244. long getSetting(const settings_key_t& key, long defaultValue);
  245. template
  246. unsigned char getSetting(const settings_key_t& key, unsigned char defaultValue);
  247. template
  248. unsigned short getSetting(const settings_key_t& key, unsigned short defaultValue);
  249. template
  250. unsigned int getSetting(const settings_key_t& key, unsigned int defaultValue);
  251. template
  252. unsigned long getSetting(const settings_key_t& key, unsigned long defaultValue);
  253. template
  254. float getSetting(const settings_key_t& key, float defaultValue);
  255. template
  256. double getSetting(const settings_key_t& key, double defaultValue);
  257. String getSetting(const settings_key_t& key) {
  258. static const String defaultValue("");
  259. return getSetting(key, defaultValue);
  260. }
  261. String getSetting(const settings_key_t& key, const char* defaultValue) {
  262. return getSetting(key, String(defaultValue));
  263. }
  264. String getSetting(const settings_key_t& key, const __FlashStringHelper* defaultValue) {
  265. return getSetting(key, String(defaultValue));
  266. }
  267. template<>
  268. bool setSetting(const settings_key_t& key, const String& value) {
  269. return Embedis::set(key.toString(), value);
  270. }
  271. bool delSetting(const settings_key_t& key) {
  272. return Embedis::del(key.toString());
  273. }
  274. bool hasSetting(const settings_key_t& key) {
  275. String value;
  276. return Embedis::get(key.toString(), value);
  277. }
  278. void saveSettings() {
  279. #if not SETTINGS_AUTOSAVE
  280. eepromCommit();
  281. #endif
  282. }
  283. void resetSettings() {
  284. for (unsigned int i = 0; i < EEPROM_SIZE; i++) {
  285. EEPROMr.write(i, 0xFF);
  286. }
  287. EEPROMr.commit();
  288. }
  289. // -----------------------------------------------------------------------------
  290. // API
  291. // -----------------------------------------------------------------------------
  292. size_t settingsMaxSize() {
  293. size_t size = EEPROM_SIZE;
  294. if (size > SPI_FLASH_SEC_SIZE) size = SPI_FLASH_SEC_SIZE;
  295. size = (size + 3) & (~3);
  296. return size;
  297. }
  298. bool settingsRestoreJson(JsonObject& data) {
  299. // Check this is an ESPurna configuration file (must have "app":"ESPURNA")
  300. const char* app = data["app"];
  301. if (!app || strcmp(app, APP_NAME) != 0) {
  302. DEBUG_MSG_P(PSTR("[SETTING] Wrong or missing 'app' key\n"));
  303. return false;
  304. }
  305. // Clear settings
  306. bool is_backup = data["backup"];
  307. if (is_backup) {
  308. for (unsigned int i = EEPROM_DATA_END; i < SPI_FLASH_SEC_SIZE; i++) {
  309. EEPROMr.write(i, 0xFF);
  310. }
  311. }
  312. // Dump settings to memory buffer
  313. for (auto element : data) {
  314. if (strcmp(element.key, "app") == 0) continue;
  315. if (strcmp(element.key, "version") == 0) continue;
  316. if (strcmp(element.key, "backup") == 0) continue;
  317. setSetting(element.key, element.value.as<char*>());
  318. }
  319. // Persist to EEPROM
  320. saveSettings();
  321. DEBUG_MSG_P(PSTR("[SETTINGS] Settings restored successfully\n"));
  322. return true;
  323. }
  324. bool settingsRestoreJson(char* json_string, size_t json_buffer_size) {
  325. // XXX: as of right now, arduinojson cannot trigger callbacks for each key individually
  326. // 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.
  327. // Current parsing method is limited only by keys (~sizeof(uintptr_t) bytes per key, data is not copied when string is non-const)
  328. DynamicJsonBuffer jsonBuffer(json_buffer_size);
  329. JsonObject& root = jsonBuffer.parseObject((char *) json_string);
  330. if (!root.success()) {
  331. DEBUG_MSG_P(PSTR("[SETTINGS] JSON parsing error\n"));
  332. return false;
  333. }
  334. return settingsRestoreJson(root);
  335. }
  336. void settingsGetJson(JsonObject& root) {
  337. // Get sorted list of keys
  338. auto keys = settingsKeys();
  339. // Add the key-values to the json object
  340. for (unsigned int i=0; i<keys.size(); i++) {
  341. String value = getSetting(keys[i]);
  342. root[keys[i]] = value;
  343. }
  344. }
  345. void settingsProcessConfig(const settings_cfg_list_t& config, settings_filter_t filter) {
  346. for (auto& entry : config) {
  347. String value = getSetting(entry.key, entry.default_value);
  348. if (filter) {
  349. value = filter(value);
  350. }
  351. if (value.equals(entry.setting)) continue;
  352. entry.setting = std::move(value);
  353. }
  354. }
  355. // -----------------------------------------------------------------------------
  356. // Initialization
  357. // -----------------------------------------------------------------------------
  358. void settingsSetup() {
  359. Embedis::dictionary( F("EEPROM"),
  360. SPI_FLASH_SEC_SIZE,
  361. [](size_t pos) -> char { return EEPROMr.read(pos); },
  362. [](size_t pos, char value) { EEPROMr.write(pos, value); },
  363. #if SETTINGS_AUTOSAVE
  364. []() { eepromCommit(); }
  365. #else
  366. []() {}
  367. #endif
  368. );
  369. }