Formal description
[esp-clock.git] / config.cpp
1 #include "Clock.h"
2 #include <Print.h>
3
4 #define MAX_PARAMS 64
5
6 Config cfg;
7
8 char* copystr(const char* s) {
9   int len = strlen(s);
10   char* res = new char[len+1];
11   strcpy(res, s);
12   return res;
13 }
14
15 char* copystr(const __FlashStringHelper* s) {
16   int len = strlen_P((PGM_P)s);
17   char* res = new char[len+1];
18   strcpy_P(res, (PGM_P)s);
19   return res;
20 }
21
22 ConfigParameter::ConfigParameter(const char *id, const char* value) {
23   init(id, 'S');
24   setValue(value);
25 };
26
27 ConfigParameter::ConfigParameter(const char *id, int value) {
28   init(id, 'I');
29   setValue(value);
30 };
31
32 ConfigParameter::ConfigParameter(const char *id, double value) {
33   init(id, 'F');
34   setValue(value);
35 };
36
37 ConfigParameter::ConfigParameter(const char *id, bool value) {
38   init(id, 'B');
39   setValue(value);
40 };
41
42 ConfigParameter::ConfigParameter(const __FlashStringHelper *id, const char* value) {
43   init(id, 'S');
44   setValue(value);
45 };
46
47 ConfigParameter::ConfigParameter(const __FlashStringHelper *id, int value) {
48   init(id, 'I');
49   setValue(value);
50 };
51
52 ConfigParameter::ConfigParameter(const __FlashStringHelper *id, double value) {
53   init(id, 'F');
54   setValue(value);
55 };
56
57 ConfigParameter::ConfigParameter(const __FlashStringHelper *id, bool value) {
58   init(id, 'B');
59   setValue(value);
60 };
61
62 ConfigParameter::~ConfigParameter() {
63   delete[] _id;
64   if (_type=='S' && _value.charValue) {
65     delete[] _value.charValue;
66   }
67 };
68
69 const char *ConfigParameter::getID() const {
70   return _id;
71 };
72
73 char  ConfigParameter::getType() const {
74   return _type;
75 };
76
77 const bool ConfigParameter::getBoolValue() const {
78   return (_type=='B')?_value.boolValue:false;
79 };
80
81 const int ConfigParameter::getIntValue() const {
82   return (_type=='I')?_value.intValue:0;
83 };
84
85 const double ConfigParameter::getFloatValue() const {
86   return (_type=='F')?_value.floatValue:0.0;
87 };
88
89 const char *ConfigParameter::getCharValue() const {
90   return (_type=='S')?_value.charValue:"";
91 };
92
93 void  ConfigParameter::setValue(const bool value) {
94   if (_type=='B') {
95     _value.boolValue = value;
96   }
97 };
98
99 void  ConfigParameter::setValue(const int value) {
100   if (_type=='I') {
101     _value.intValue = value;
102   }
103 };
104
105 void  ConfigParameter::setValue(const double value) {
106   if (_type=='F') {
107     _value.floatValue = value;
108   }
109 };
110
111 void  ConfigParameter::setValue(const char *value) {
112   if (_type=='S') {
113     int length = strlen(value);
114     if (!_value.charValue) {
115       _value.charValue  = new char[length + 1];
116     } else if (strlen(_value.charValue) != length) {
117       delete[] _value.charValue;
118       _value.charValue  = new char[length + 1];
119     }
120     strcpy(_value.charValue,value);
121   }
122 };
123
124 void ConfigParameter::init(const char *id, char type) {
125   _id = copystr(id);
126   _type = type;
127   _value.charValue = nullptr;
128 };
129
130 void ConfigParameter::init(const __FlashStringHelper *id, char type) {
131   _id = copystr(id);
132   _type = type;
133   _value.charValue = nullptr;
134 };
135
136 size_t ConfigParameter::printTo(Print& p) const {
137   size_t n = 0;
138   n += p.print(_id); n += p.print(":"); n += p.print(_type); n += p.print(":");
139   switch (_type) {
140     case 'B':
141       if (_value.boolValue) { n += p.print("true"); }
142       else { n += p.print("false"); }
143       break;
144     case 'I':
145       n += p.print(_value.intValue);
146       break;
147     case 'F':
148       n += p.print(_value.floatValue);
149       break;
150     case 'S':
151       n += p.print(_value.charValue);
152       break;
153   }
154   return n;
155 }
156
157 Config::Config() {
158   init();
159 }
160
161 void Config::init() {
162   _paramsCount = 0;
163   _max_params = MAX_PARAMS;
164   _params = NULL;
165   _timestamp = 0;
166 }
167
168 Config::~Config() {
169   if (_params != NULL) {
170     for (int i = 0; i < _paramsCount; i++) {
171       delete _params[i];
172       _params[i] = nullptr;
173     }
174     free(_params);
175     _params = NULL;
176   }
177 }
178
179 ConfigParameter* Config::getParameter(int i) {
180   return _params[i];
181 }
182
183 int Config::getParametersCount() const {
184   return _paramsCount;
185 }
186
187 ConfigParameter* Config::getParam(const char *id) const {
188   for (int j = 0; j < _paramsCount; j++) {
189     if (strcmp(id, _params[j]->getID()) == 0) {
190       return _params[j];
191     }
192   }
193   return nullptr;
194 }
195
196 ConfigParameter* Config::getParam(const __FlashStringHelper *id) const {
197   for (int j = 0; j < _paramsCount; j++) {
198     if (strcmp_P(_params[j]->getID(),(PGM_P)id) == 0) {
199       return _params[j];
200     }
201   }
202   return nullptr;
203 }
204
205 void Config::addParameter(ConfigParameter* p) {
206   if (_params == NULL) {
207     _params = (ConfigParameter**)malloc(_max_params * sizeof(ConfigParameter*));
208   }
209   if (_paramsCount == _max_params) {
210     _max_params += MAX_PARAMS;
211     ConfigParameter** new_params = (ConfigParameter**)realloc(_params, _max_params * sizeof(ConfigParameter*));
212     if (new_params != NULL) {
213       _params = new_params;
214     } else {
215       Serial.println(F("Не удалось расширить массив параметров"));
216       return;
217     }
218   }
219   _params[_paramsCount] = p;
220   _paramsCount++;
221 }
222
223
224 void Config::setValue(const char *id, const char *value) {
225   ConfigParameter* p = getParam(id);
226   if (p) {
227     p->setValue(value);
228   } else {
229     addParameter(new ConfigParameter(id,value));
230   }
231   _timestamp = now;
232 }
233
234 void Config::setValue(const __FlashStringHelper *id, const char *value) {
235   ConfigParameter* p = getParam(id);
236   if (p) {
237     p->setValue(value);
238   } else {
239     addParameter(new ConfigParameter(copystr(id),value));
240   }
241   _timestamp = now;
242 }
243
244 void Config::setValue(const char *id, int value) {
245   ConfigParameter* p = getParam(id);
246   if (p) {
247     p->setValue(value);
248   } else {
249     addParameter(new ConfigParameter(id,value));
250   }
251   _timestamp = now;
252 }
253
254 void Config::setValue(const __FlashStringHelper *id, int value) {
255   ConfigParameter* p = getParam(id);
256   if (p) {
257     p->setValue(value);
258   } else {
259     addParameter(new ConfigParameter(copystr(id),value));
260   }
261   _timestamp = now;
262 }
263
264 void Config::setValue(const char *id, const double value) {
265   ConfigParameter* p = getParam(id);
266   if (p) {
267     p->setValue(value);
268   } else {
269     addParameter(new ConfigParameter(id,value));
270   }
271   _timestamp = now;
272 }
273
274 void Config::setValue(const __FlashStringHelper *id, const double value) {
275   ConfigParameter* p = getParam(id);
276   if (p) {
277     p->setValue(value);
278   } else {
279     addParameter(new ConfigParameter(copystr(id),value));
280   }
281   _timestamp = now;
282 }
283
284 void Config::setValue(const char *id, const bool value) {
285   ConfigParameter* p = getParam(id);
286   if (p) {
287     p->setValue(value);
288   } else {
289     addParameter(new ConfigParameter(id,value));
290   }
291   _timestamp = now;
292 }
293
294 void Config::setValue(const __FlashStringHelper *id, const bool value) {
295   ConfigParameter* p = getParam(id);
296   if (p) {
297     p->setValue(value);
298   } else {
299     addParameter(new ConfigParameter(copystr(id),value));
300   }
301   _timestamp = now;
302 }
303
304 size_t Config::printTo(Print& p) const {
305   size_t n = 0;
306   for (int i=0; i<_paramsCount; i++) {
307     ConfigParameter* param = _params[i];
308     if (param) {
309       n += p.print(*param); n+= p.println();
310     }
311   }
312   return n;
313 }
314
315 int Config::getIntValue(const char *id) const {
316   ConfigParameter* p = getParam(id);
317   if (p) {
318     return p->getIntValue();
319   } else {
320     return 0;
321   }
322 }
323
324 int Config::getIntValue(const __FlashStringHelper *id) const {
325   ConfigParameter* p = getParam(id);
326   if (p) {
327     return p->getIntValue();
328   } else {
329     return 0;
330   }
331 }
332
333 double Config::getFloatValue(const char *id) const {
334   ConfigParameter* p = getParam(id);
335   if (p) {
336     return p->getFloatValue();
337   } else {
338     return 0.0;
339   }
340 };
341
342 double Config::getFloatValue(const __FlashStringHelper *id) const {
343   ConfigParameter* p = getParam(id);
344   if (p) {
345     return p->getFloatValue();
346   } else {
347     return 0.0;
348   }
349 };
350
351 bool Config::getBoolValue(const char *id) const {
352   ConfigParameter* p = getParam(id);
353   if (p) {
354     return p->getBoolValue();
355   } else {
356     return false;
357   }
358 };
359
360 bool Config::getBoolValue(const __FlashStringHelper *id) const {
361   ConfigParameter* p = getParam(id);
362   if (p) {
363     return p->getBoolValue();
364   } else {
365     return false;
366   }
367 };
368
369 const char* Config::getCharValue(const char *id) const {
370   ConfigParameter* p = getParam(id);
371   if (p) {
372     return p->getCharValue();
373   } else {
374     return "";
375   }
376 };
377
378 const char* Config::getCharValue(const __FlashStringHelper *id) const {
379   ConfigParameter* p = getParam(id);
380   if (p) {
381     return p->getCharValue();
382   } else {
383     return "";
384   }
385 };
386
387 void Config::readFrom(Stream& s) {
388   char buf[256]; // name + type + value + separators
389   while (s.available()) {
390     int x = s.readBytesUntil('\n',buf,255);
391     buf[x] = 0;
392     if (x>=3) {
393       char* origName = strtok(buf,":\r\n");
394       char* origType = strtok(NULL,":\r\n");
395       char* origValue = strtok(NULL,"\r\n");
396       if (origType && origName && origName[0]) {
397         if (origType[0]=='B') {
398           if (origValue) {
399             setValue(copystr(origName),(strcmp(origValue,"true")==0));
400           } else {
401             setValue(copystr(origName),false);
402           }
403         } else if (origType[0]=='I') {
404           if (origValue) {
405             setValue(copystr(origName),atoi(origValue));
406           } else {
407             setValue(copystr(origName),0);
408           }
409         } else if (origType[0]=='F') {
410           if (origValue) {
411             setValue(copystr(origName),atof(origValue));
412           } else {
413             setValue(copystr(origName),0.0);
414           }
415         } else if (origType[0]=='S') {
416           if (origValue) {
417             setValue(copystr(origName),origValue);
418           } else {
419             setValue(copystr(origName),"");
420           }
421         }
422       }
423     }
424   }
425 }
426
427 void Config::clear() {
428   if (_params != NULL) {
429     for (int i = 0; i < _paramsCount; i++) {
430       delete _params[i];
431       _params[i] = nullptr;
432     }
433     free(_params);
434   }
435   init();
436 }
437
438 void Config::dumpJson(Stream& s) const {
439   s.print("{");
440   for (int i=0; i<_paramsCount; i++) {
441     ConfigParameter* param = _params[i];
442     if (i) s.print(",");
443     s.print("\"");
444     s.print(param->getID());
445     s.print("\":");
446     switch (param->getType()) {
447       case 'B':
448         s.print(param->getBoolValue()?"true":"false");
449         break;
450       case 'I':
451         s.print(param->getIntValue());
452         break;
453       case 'F':
454         s.print(param->getFloatValue());
455         break;
456       case 'S':
457         s.print("\"");
458         s.print(param->getCharValue());
459         s.print("\"");
460         break;
461     }
462   }
463   s.print("}");
464 }
465
466 unsigned long Config::getTimestamp() {
467   return _timestamp;
468 }
469
470 void Config::resetTimestamp() {
471   _timestamp = 0;
472 }
473
474 void setupConfig() {
475   if (LittleFS.begin()) {
476     if (File f = LittleFS.open(F("/config.txt"),"r")) {
477       Serial.println('Открываю файл конфигурации');
478       cfg.readFrom(f);
479       f.close();
480       cfg.resetTimestamp();
481     }
482   } else {
483     Serial.println(F("Конфигурация не найдена"));
484   }
485 }
486
487 void saveConfig(bool force) {
488   if (cfg.getTimestamp() || force) {
489     if (File f = LittleFS.open(F("/config.txt"),"w")) {
490       f.print(cfg);
491       f.close();
492       Serial.print(cfg);
493       Serial.println(F("Конфигурация сохранена"));
494       cfg.resetTimestamp();
495     }
496   }
497 }
498
499 void reboot() {
500   saveConfig();
501   LittleFS.end();
502   messageModal(F("Перезагружаюсь"));
503   ESP.restart();
504 }
505
506 char* distConfig PROGMEM =
507 "sta_ssid:S:\n"
508 "sta_psk:S:\n"
509 "sta_wait:I:120\n"
510 "ap_ssid:S:WiFi_Clock\n"
511 "ap_psk:S:1234-5678\n"
512 "ntp_server:S:ru.pool.ntp.org\n"
513 "tz:S:MSK-3\n"
514 "pin_sda:I:4\n"
515 "pin_scl:I:5\n"
516 "i2c_speed:I:400000\n"
517 "enable_rtc:B:true\n"
518 "flash_dots:B:true\n"
519 "pin_din:I:12\n"
520 "pin_clk:I:15\n"
521 "pin_cs:I:13\n"
522 "led_modules:I:4\n"
523 "panel_font:I:1\n"
524 "panel_speed:I:15\n"
525 "panel_brightness_day:I:1\n"
526 "panel_brightness_night:I:0\n"
527 "panel_zero:B:true\n"
528 "panel_seconds:B:false\n"
529 "enable_button:B:true\n"
530 "button_pin:I:0\n"
531 "enable_buzzer:B:true\n"
532 "buzzer_pin:I:14\n"
533 "buzzer_passive:B:true\n"
534 "day_from:I:8\n"
535 "night_from:I:22\n"
536 "enable_hourly:B:true\n"
537 "hourly_night:B:false\n"
538 "hourly_count:I:2\n"
539 "hourly_tone:I:800\n"
540 "hourly_beep_ms:I:100\n"
541 "hourly_silent_ms:I:100\n"
542 "enable_alarm:B:false\n"
543 "alarm_hour:I:13\n"
544 "alarm_minute:I:12\n"
545 "alarm_days:S:1111111\n"
546 "alarm_tone:I:1500\n"
547 "alarm_length:I:60\n"
548 "alarm_beep_ms:I:300\n"
549 "alarm_silent_ms:I:200\n"
550 "enable_weather:B:false\n"
551 "weather_url:S:http://api.openweathermap.org/data/2.5/weather?lat=5{LATITUDE}&lon={LONGITUDE}&appid={API_KEY}&lang=ru&units=metric\n"
552 "weather_template:S:Сегодня %weather.[0].description%, температура %main.temp% (%main.feels_like%)°C, влажность %main.humidity%%%, ветер %wind.speed% м/с\n"
553 "weather_min:I:15\n"
554 "auth_user:S:\n"
555 "auth_pwd:S:\n";
556
557
558 void reset() {
559   messageModal(F("Сбрасываю настройки"));
560   if (File f = LittleFS.open(F("/config.txt"),"w")) {
561     f.print(distConfig);
562     f.close();
563   }
564   LittleFS.end();
565   ESP.restart();
566 }