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.

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