1 #include "stdafx.h"
2 #include "SQLHelper.h"
3 #include <iostream> /* standard I/O functions */
4 #include <iomanip>
5 #include "RFXtrx.h"
6 #include "RFXNames.h"
7 #include "localtime_r.h"
8 #include "Logger.h"
9 #include "mainworker.h"
10 #include "../main/json_helper.h"
11 #include <sqlite3.h>
12 #include "../hardware/hardwaretypes.h"
13 #include "../smtpclient/SMTPClient.h"
14 #include "WebServerHelper.h"
15 #include "../webserver/Base64.h"
16 #include "clx_unzip.h"
17 #include "../notifications/NotificationHelper.h"
18 #include "IFTTT.h"
19 #ifdef ENABLE_PYTHON
20 #include "../hardware/plugins/Plugins.h"
21 #endif
22
23 #ifndef WIN32
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <pwd.h>
28 #else
29 #include "../msbuild/WindowsHelper.h"
30 #endif
31 #define __STDC_FORMAT_MACROS
32 #include <inttypes.h>
33
34 #define DB_VERSION 143
35
36 extern http::server::CWebServerHelper m_webservers;
37 extern std::string szWWWFolder;
38
39 const char* sqlCreateDeviceStatus =
40 "CREATE TABLE IF NOT EXISTS [DeviceStatus] ("
41 "[ID] INTEGER PRIMARY KEY, "
42 "[HardwareID] INTEGER NOT NULL, "
43 "[DeviceID] VARCHAR(25) NOT NULL, "
44 "[Unit] INTEGER DEFAULT 0, "
45 "[Name] VARCHAR(100) DEFAULT Unknown, "
46 "[Used] INTEGER DEFAULT 0, "
47 "[Type] INTEGER NOT NULL, "
48 "[SubType] INTEGER NOT NULL, "
49 "[SwitchType] INTEGER DEFAULT 0, "
50 "[Favorite] INTEGER DEFAULT 0, "
51 "[SignalLevel] INTEGER DEFAULT 0, "
52 "[BatteryLevel] INTEGER DEFAULT 0, "
53 "[nValue] INTEGER DEFAULT 0, "
54 "[sValue] VARCHAR(200) DEFAULT null, "
55 "[LastUpdate] DATETIME DEFAULT (datetime('now','localtime')),"
56 "[Order] INTEGER BIGINT(10) default 0, "
57 "[AddjValue] FLOAT DEFAULT 0, "
58 "[AddjMulti] FLOAT DEFAULT 1, "
59 "[AddjValue2] FLOAT DEFAULT 0, "
60 "[AddjMulti2] FLOAT DEFAULT 1, "
61 "[StrParam1] VARCHAR(200) DEFAULT '', "
62 "[StrParam2] VARCHAR(200) DEFAULT '', "
63 "[LastLevel] INTEGER DEFAULT 0, "
64 "[Protected] INTEGER DEFAULT 0, "
65 "[CustomImage] INTEGER DEFAULT 0, "
66 "[Description] VARCHAR(200) DEFAULT '', "
67 "[Options] TEXT DEFAULT null, "
68 "[Color] TEXT DEFAULT NULL);";
69
70 const char* sqlCreateDeviceStatusTrigger =
71 "CREATE TRIGGER IF NOT EXISTS devicestatusupdate AFTER INSERT ON DeviceStatus\n"
72 "BEGIN\n"
73 " UPDATE DeviceStatus SET [Order] = (SELECT MAX([Order]) FROM DeviceStatus)+1 WHERE DeviceStatus.ID = NEW.ID;\n"
74 "END;\n";
75
76 const char* sqlCreateLightingLog =
77 "CREATE TABLE IF NOT EXISTS [LightingLog] ("
78 "[DeviceRowID] BIGINT(10) NOT NULL, "
79 "[nValue] INTEGER DEFAULT 0, "
80 "[sValue] VARCHAR(200), "
81 "[User] VARCHAR(100) DEFAULT (''), "
82 "[Date] DATETIME DEFAULT (datetime('now','localtime')));";
83
84 const char* sqlCreateSceneLog =
85 "CREATE TABLE IF NOT EXISTS [SceneLog] ("
86 "[SceneRowID] BIGINT(10) NOT NULL, "
87 "[nValue] INTEGER DEFAULT 0, "
88 "[User] VARCHAR(100) DEFAULT (''), "
89 "[Date] DATETIME DEFAULT (datetime('now','localtime')));";
90
91 const char* sqlCreatePreferences =
92 "CREATE TABLE IF NOT EXISTS [Preferences] ("
93 "[Key] VARCHAR(50) NOT NULL, "
94 "[nValue] INTEGER DEFAULT 0, "
95 "[sValue] VARCHAR(200));";
96
97 const char* sqlCreateRain =
98 "CREATE TABLE IF NOT EXISTS [Rain] ("
99 "[DeviceRowID] BIGINT(10) NOT NULL, "
100 "[Total] FLOAT NOT NULL, "
101 "[Rate] INTEGER DEFAULT 0, "
102 "[Date] DATETIME DEFAULT (datetime('now','localtime')));";
103
104 const char* sqlCreateRain_Calendar =
105 "CREATE TABLE IF NOT EXISTS [Rain_Calendar] ("
106 "[DeviceRowID] BIGINT(10) NOT NULL, "
107 "[Total] FLOAT NOT NULL, "
108 "[Rate] INTEGER DEFAULT 0, "
109 "[Date] DATE NOT NULL);";
110
111 const char* sqlCreateTemperature =
112 "CREATE TABLE IF NOT EXISTS [Temperature] ("
113 "[DeviceRowID] BIGINT(10) NOT NULL, "
114 "[Temperature] FLOAT NOT NULL, "
115 "[Chill] FLOAT DEFAULT 0, "
116 "[Humidity] INTEGER DEFAULT 0, "
117 "[Barometer] INTEGER DEFAULT 0, "
118 "[DewPoint] FLOAT DEFAULT 0, "
119 "[SetPoint] FLOAT DEFAULT 0, "
120 "[Date] DATETIME DEFAULT (datetime('now','localtime')));";
121
122 const char* sqlCreateTemperature_Calendar =
123 "CREATE TABLE IF NOT EXISTS [Temperature_Calendar] ("
124 "[DeviceRowID] BIGINT(10) NOT NULL, "
125 "[Temp_Min] FLOAT NOT NULL, "
126 "[Temp_Max] FLOAT NOT NULL, "
127 "[Temp_Avg] FLOAT DEFAULT 0, "
128 "[Chill_Min] FLOAT DEFAULT 0, "
129 "[Chill_Max] FLOAT, "
130 "[Humidity] INTEGER DEFAULT 0, "
131 "[Barometer] INTEGER DEFAULT 0, "
132 "[DewPoint] FLOAT DEFAULT 0, "
133 "[SetPoint_Min] FLOAT DEFAULT 0, "
134 "[SetPoint_Max] FLOAT DEFAULT 0, "
135 "[SetPoint_Avg] FLOAT DEFAULT 0, "
136 "[Date] DATE NOT NULL);";
137
138 const char* sqlCreateTimers =
139 "CREATE TABLE IF NOT EXISTS [Timers] ("
140 "[ID] INTEGER PRIMARY KEY, "
141 "[Active] BOOLEAN DEFAULT true, "
142 "[DeviceRowID] BIGINT(10) NOT NULL, "
143 "[Date] DATE DEFAULT 0, "
144 "[Time] TIME NOT NULL, "
145 "[Type] INTEGER NOT NULL, "
146 "[Cmd] INTEGER NOT NULL, "
147 "[Level] INTEGER DEFAULT 15, "
148 "[Color] TEXT DEFAULT NULL, "
149 "[UseRandomness] INTEGER DEFAULT 0, "
150 "[TimerPlan] INTEGER DEFAULT 0, "
151 "[Days] INTEGER NOT NULL, "
152 "[Month] INTEGER DEFAULT 0, "
153 "[MDay] INTEGER DEFAULT 0, "
154 "[Occurence] INTEGER DEFAULT 0);";
155
156 const char* sqlCreateUV =
157 "CREATE TABLE IF NOT EXISTS [UV] ("
158 "[DeviceRowID] BIGINT(10) NOT NULL, "
159 "[Level] FLOAT NOT NULL, "
160 "[Date] DATETIME DEFAULT (datetime('now','localtime')));";
161
162 const char* sqlCreateUV_Calendar =
163 "CREATE TABLE IF NOT EXISTS [UV_Calendar] ("
164 "[DeviceRowID] BIGINT(10) NOT NULL, "
165 "[Level] FLOAT, "
166 "[Date] DATE NOT NULL);";
167
168 const char* sqlCreateWind =
169 "CREATE TABLE IF NOT EXISTS [Wind] ("
170 "[DeviceRowID] BIGINT(10) NOT NULL, "
171 "[Direction] FLOAT NOT NULL, "
172 "[Speed] INTEGER NOT NULL, "
173 "[Gust] INTEGER NOT NULL, "
174 "[Date] DATETIME DEFAULT (datetime('now','localtime')));";
175
176 const char* sqlCreateWind_Calendar =
177 "CREATE TABLE IF NOT EXISTS [Wind_Calendar] ("
178 "[DeviceRowID] BIGINT(10) NOT NULL, "
179 "[Direction] FLOAT NOT NULL, "
180 "[Speed_Min] INTEGER NOT NULL, "
181 "[Speed_Max] INTEGER NOT NULL, "
182 "[Gust_Min] INTEGER NOT NULL, "
183 "[Gust_Max] INTEGER NOT NULL, "
184 "[Date] DATE NOT NULL);";
185
186 const char* sqlCreateMultiMeter =
187 "CREATE TABLE IF NOT EXISTS [MultiMeter] ("
188 "[DeviceRowID] BIGINT(10) NOT NULL, "
189 "[Value1] BIGINT NOT NULL, "
190 "[Value2] BIGINT DEFAULT 0, "
191 "[Value3] BIGINT DEFAULT 0, "
192 "[Value4] BIGINT DEFAULT 0, "
193 "[Value5] BIGINT DEFAULT 0, "
194 "[Value6] BIGINT DEFAULT 0, "
195 "[Date] DATETIME DEFAULT (datetime('now','localtime')));";
196
197 const char* sqlCreateMultiMeter_Calendar =
198 "CREATE TABLE IF NOT EXISTS [MultiMeter_Calendar] ("
199 "[DeviceRowID] BIGINT(10) NOT NULL, "
200 "[Value1] BIGINT NOT NULL, "
201 "[Value2] BIGINT NOT NULL, "
202 "[Value3] BIGINT NOT NULL, "
203 "[Value4] BIGINT NOT NULL, "
204 "[Value5] BIGINT NOT NULL, "
205 "[Value6] BIGINT NOT NULL, "
206 "[Counter1] BIGINT DEFAULT 0, "
207 "[Counter2] BIGINT DEFAULT 0, "
208 "[Counter3] BIGINT DEFAULT 0, "
209 "[Counter4] BIGINT DEFAULT 0, "
210 "[Date] DATETIME DEFAULT (datetime('now','localtime')));";
211
212
213 const char* sqlCreateNotifications =
214 "CREATE TABLE IF NOT EXISTS [Notifications] ("
215 "[ID] INTEGER PRIMARY KEY, "
216 "[DeviceRowID] BIGINT(10) NOT NULL, "
217 "[Params] VARCHAR(100), "
218 "[CustomMessage] VARCHAR(300) DEFAULT (''), "
219 "[ActiveSystems] VARCHAR(200) DEFAULT (''), "
220 "[Priority] INTEGER default 0, "
221 "[SendAlways] INTEGER default 0, "
222 "[LastSend] DATETIME DEFAULT 0);";
223
224 const char* sqlCreateHardware =
225 "CREATE TABLE IF NOT EXISTS [Hardware] ("
226 "[ID] INTEGER PRIMARY KEY, "
227 "[Name] VARCHAR(200) NOT NULL, "
228 "[Enabled] INTEGER DEFAULT 1, "
229 "[Type] INTEGER NOT NULL, "
230 "[Address] VARCHAR(200), "
231 "[Port] INTEGER, "
232 "[SerialPort] TEXT DEFAULT (''), "
233 "[Username] VARCHAR(100), "
234 "[Password] VARCHAR(100), "
235 "[Extra] TEXT DEFAULT (''),"
236 "[Mode1] CHAR DEFAULT 0, "
237 "[Mode2] CHAR DEFAULT 0, "
238 "[Mode3] CHAR DEFAULT 0, "
239 "[Mode4] CHAR DEFAULT 0, "
240 "[Mode5] CHAR DEFAULT 0, "
241 "[Mode6] CHAR DEFAULT 0, "
242 "[DataTimeout] INTEGER DEFAULT 0, "
243 "[Configuration] TEXT DEFAULT (''));";
244
245 const char* sqlCreateUsers =
246 "CREATE TABLE IF NOT EXISTS [Users] ("
247 "[ID] INTEGER PRIMARY KEY, "
248 "[Active] INTEGER NOT NULL DEFAULT 0, "
249 "[Username] VARCHAR(200) NOT NULL, "
250 "[Password] VARCHAR(200) NOT NULL, "
251 "[Rights] INTEGER DEFAULT 255, "
252 "[TabsEnabled] INTEGER DEFAULT 255, "
253 "[RemoteSharing] INTEGER DEFAULT 0);";
254
255 const char* sqlCreateMeter =
256 "CREATE TABLE IF NOT EXISTS [Meter] ("
257 "[DeviceRowID] BIGINT NOT NULL, "
258 "[Value] BIGINT NOT NULL, "
259 "[Usage] INTEGER DEFAULT 0, "
260 "[Date] DATETIME DEFAULT (datetime('now','localtime')));";
261
262 const char* sqlCreateMeter_Calendar =
263 "CREATE TABLE IF NOT EXISTS [Meter_Calendar] ("
264 "[DeviceRowID] BIGINT NOT NULL, "
265 "[Value] BIGINT NOT NULL, "
266 "[Counter] BIGINT DEFAULT 0, "
267 "[Date] DATETIME DEFAULT (datetime('now','localtime')));";
268
269 const char* sqlCreateLightSubDevices =
270 "CREATE TABLE IF NOT EXISTS [LightSubDevices] ("
271 "[ID] INTEGER PRIMARY KEY, "
272 "[DeviceRowID] INTEGER NOT NULL, "
273 "[ParentID] INTEGER NOT NULL);";
274
275 const char* sqlCreateCameras =
276 "CREATE TABLE IF NOT EXISTS [Cameras] ("
277 "[ID] INTEGER PRIMARY KEY, "
278 "[Name] VARCHAR(200) NOT NULL, "
279 "[Enabled] INTEGER DEFAULT 1, "
280 "[Address] VARCHAR(200), "
281 "[Port] INTEGER, "
282 "[Protocol] INTEGER DEFAULT 0, "
283 "[Username] VARCHAR(100) DEFAULT (''), "
284 "[Password] VARCHAR(100) DEFAULT (''), "
285 "[ImageURL] VARCHAR(200) DEFAULT (''));";
286
287 const char* sqlCreateCamerasActiveDevices =
288 "CREATE TABLE IF NOT EXISTS [CamerasActiveDevices] ("
289 "[ID] INTEGER PRIMARY KEY, "
290 "[CameraRowID] INTEGER NOT NULL, "
291 "[DevSceneType] INTEGER NOT NULL, "
292 "[DevSceneRowID] INTEGER NOT NULL, "
293 "[DevSceneWhen] INTEGER NOT NULL, "
294 "[DevSceneDelay] INTEGER NOT NULL);";
295
296 const char* sqlCreatePlanMappings =
297 "CREATE TABLE IF NOT EXISTS [DeviceToPlansMap] ("
298 "[ID] INTEGER PRIMARY KEY, "
299 "[DeviceRowID] BIGINT NOT NULL, "
300 "[DevSceneType] INTEGER DEFAULT 0, "
301 "[PlanID] BIGINT NOT NULL, "
302 "[Order] INTEGER BIGINT(10) DEFAULT 0, "
303 "[XOffset] INTEGER default 0, "
304 "[YOffset] INTEGER default 0);";
305
306 const char* sqlCreateDevicesToPlanStatusTrigger =
307 "CREATE TRIGGER IF NOT EXISTS deviceplantatusupdate AFTER INSERT ON DeviceToPlansMap\n"
308 "BEGIN\n"
309 " UPDATE DeviceToPlansMap SET [Order] = (SELECT MAX([Order]) FROM DeviceToPlansMap)+1 WHERE DeviceToPlansMap.ID = NEW.ID;\n"
310 "END;\n";
311
312 const char* sqlCreatePlans =
313 "CREATE TABLE IF NOT EXISTS [Plans] ("
314 "[ID] INTEGER PRIMARY KEY, "
315 "[Order] INTEGER BIGINT(10) default 0, "
316 "[Name] VARCHAR(200) NOT NULL, "
317 "[FloorplanID] INTEGER default 0, "
318 "[Area] VARCHAR(200) DEFAULT '');";
319
320 const char* sqlCreatePlanOrderTrigger =
321 "CREATE TRIGGER IF NOT EXISTS planordertrigger AFTER INSERT ON Plans\n"
322 "BEGIN\n"
323 " UPDATE Plans SET [Order] = (SELECT MAX([Order]) FROM Plans)+1 WHERE Plans.ID = NEW.ID;\n"
324 "END;\n";
325
326 const char* sqlCreateScenes =
327 "CREATE TABLE IF NOT EXISTS [Scenes] (\n"
328 "[ID] INTEGER PRIMARY KEY, \n"
329 "[Name] VARCHAR(100) NOT NULL, \n"
330 "[Favorite] INTEGER DEFAULT 0, \n"
331 "[Order] INTEGER BIGINT(10) default 0, \n"
332 "[nValue] INTEGER DEFAULT 0, \n"
333 "[SceneType] INTEGER DEFAULT 0, \n"
334 "[Protected] INTEGER DEFAULT 0, \n"
335 "[OnAction] VARCHAR(200) DEFAULT '', "
336 "[OffAction] VARCHAR(200) DEFAULT '', "
337 "[Description] VARCHAR(200) DEFAULT '', "
338 "[Activators] VARCHAR(200) DEFAULT '', "
339 "[LastUpdate] DATETIME DEFAULT (datetime('now','localtime')));\n";
340
341 const char* sqlCreateScenesTrigger =
342 "CREATE TRIGGER IF NOT EXISTS scenesupdate AFTER INSERT ON Scenes\n"
343 "BEGIN\n"
344 " UPDATE Scenes SET [Order] = (SELECT MAX([Order]) FROM Scenes)+1 WHERE Scenes.ID = NEW.ID;\n"
345 "END;\n";
346
347 const char* sqlCreateSceneDevices =
348 "CREATE TABLE IF NOT EXISTS [SceneDevices] ("
349 "[ID] INTEGER PRIMARY KEY, "
350 "[Order] INTEGER BIGINT(10) default 0, "
351 "[SceneRowID] BIGINT NOT NULL, "
352 "[DeviceRowID] BIGINT NOT NULL, "
353 "[Cmd] INTEGER DEFAULT 1, "
354 "[Level] INTEGER DEFAULT 100, "
355 "[Color] TEXT DEFAULT NULL, "
356 "[OnDelay] INTEGER DEFAULT 0, "
357 "[OffDelay] INTEGER DEFAULT 0);";
358
359 const char* sqlCreateSceneDeviceTrigger =
360 "CREATE TRIGGER IF NOT EXISTS scenedevicesupdate AFTER INSERT ON SceneDevices\n"
361 "BEGIN\n"
362 " UPDATE SceneDevices SET [Order] = (SELECT MAX([Order]) FROM SceneDevices)+1 WHERE SceneDevices.ID = NEW.ID;\n"
363 "END;\n";
364
365 const char* sqlCreateTimerPlans =
366 "CREATE TABLE IF NOT EXISTS [TimerPlans] ("
367 "[ID] INTEGER PRIMARY KEY, "
368 "[Name] VARCHAR(200) NOT NULL);";
369
370 const char* sqlCreateSceneTimers =
371 "CREATE TABLE IF NOT EXISTS [SceneTimers] ("
372 "[ID] INTEGER PRIMARY KEY, "
373 "[Active] BOOLEAN DEFAULT true, "
374 "[SceneRowID] BIGINT(10) NOT NULL, "
375 "[Date] DATE DEFAULT 0, "
376 "[Time] TIME NOT NULL, "
377 "[Type] INTEGER NOT NULL, "
378 "[Cmd] INTEGER NOT NULL, "
379 "[Level] INTEGER DEFAULT 15, "
380 "[UseRandomness] INTEGER DEFAULT 0, "
381 "[TimerPlan] INTEGER DEFAULT 0, "
382 "[Days] INTEGER NOT NULL, "
383 "[Month] INTEGER DEFAULT 0, "
384 "[MDay] INTEGER DEFAULT 0, "
385 "[Occurence] INTEGER DEFAULT 0);";
386
387 const char* sqlCreateSetpointTimers =
388 "CREATE TABLE IF NOT EXISTS [SetpointTimers] ("
389 "[ID] INTEGER PRIMARY KEY, "
390 "[Active] BOOLEAN DEFAULT true, "
391 "[DeviceRowID] BIGINT(10) NOT NULL, "
392 "[Date] DATE DEFAULT 0, "
393 "[Time] TIME NOT NULL, "
394 "[Type] INTEGER NOT NULL, "
395 "[Temperature] FLOAT DEFAULT 0, "
396 "[TimerPlan] INTEGER DEFAULT 0, "
397 "[Days] INTEGER NOT NULL, "
398 "[Month] INTEGER DEFAULT 0, "
399 "[MDay] INTEGER DEFAULT 0, "
400 "[Occurence] INTEGER DEFAULT 0);";
401
402 const char* sqlCreateSharedDevices =
403 "CREATE TABLE IF NOT EXISTS [SharedDevices] ("
404 "[ID] INTEGER PRIMARY KEY, "
405 "[SharedUserID] BIGINT NOT NULL, "
406 "[DeviceRowID] BIGINT NOT NULL, "
407 "[Favorite] INTEGER DEFAULT 0);";
408
409 const char* sqlCreateEventMaster =
410 "CREATE TABLE IF NOT EXISTS [EventMaster] ("
411 "[ID] INTEGER PRIMARY KEY, "
412 "[Name] VARCHAR(200) NOT NULL, "
413 "[Interpreter] VARCHAR(10) DEFAULT 'Blockly', "
414 "[Type] VARCHAR(10) DEFAULT 'All', "
415 "[XMLStatement] TEXT NOT NULL, "
416 "[Status] INTEGER DEFAULT 0);";
417
418 const char* sqlCreateEventRules =
419 "CREATE TABLE IF NOT EXISTS [EventRules] ("
420 "[ID] INTEGER PRIMARY KEY, "
421 "[EMID] INTEGER, "
422 "[Conditions] TEXT NOT NULL, "
423 "[Actions] TEXT NOT NULL, "
424 "[SequenceNo] INTEGER NOT NULL, "
425 "FOREIGN KEY (EMID) REFERENCES EventMaster(ID));";
426
427 const char* sqlCreateZWaveNodes =
428 "CREATE TABLE IF NOT EXISTS [ZWaveNodes] ("
429 "[ID] INTEGER PRIMARY KEY, "
430 "[HardwareID] INTEGER NOT NULL, "
431 "[HomeID] INTEGER NOT NULL, "
432 "[NodeID] INTEGER NOT NULL, "
433 "[Name] VARCHAR(100) DEFAULT Unknown, "
434 "[ProductDescription] VARCHAR(100) DEFAULT Unknown, "
435 "[PollTime] INTEGER DEFAULT 0);";
436
437 const char* sqlCreateWOLNodes =
438 "CREATE TABLE IF NOT EXISTS [WOLNodes] ("
439 "[ID] INTEGER PRIMARY KEY, "
440 "[HardwareID] INTEGER NOT NULL, "
441 "[Name] VARCHAR(100) DEFAULT Unknown, "
442 "[MacAddress] VARCHAR(50) DEFAULT Unknown, "
443 "[Timeout] INTEGER DEFAULT 5);";
444
445 const char* sqlCreatePercentage =
446 "CREATE TABLE IF NOT EXISTS [Percentage] ("
447 "[DeviceRowID] BIGINT(10) NOT NULL, "
448 "[Percentage] FLOAT NOT NULL, "
449 "[Date] DATETIME DEFAULT (datetime('now','localtime')));";
450
451 const char* sqlCreatePercentage_Calendar =
452 "CREATE TABLE IF NOT EXISTS [Percentage_Calendar] ("
453 "[DeviceRowID] BIGINT(10) NOT NULL, "
454 "[Percentage_Min] FLOAT NOT NULL, "
455 "[Percentage_Max] FLOAT NOT NULL, "
456 "[Percentage_Avg] FLOAT DEFAULT 0, "
457 "[Date] DATE NOT NULL);";
458
459 const char* sqlCreateFan =
460 "CREATE TABLE IF NOT EXISTS [Fan] ("
461 "[DeviceRowID] BIGINT(10) NOT NULL, "
462 "[Speed] INTEGER NOT NULL, "
463 "[Date] DATETIME DEFAULT (datetime('now','localtime')));";
464
465 const char* sqlCreateFan_Calendar =
466 "CREATE TABLE IF NOT EXISTS [Fan_Calendar] ("
467 "[DeviceRowID] BIGINT(10) NOT NULL, "
468 "[Speed_Min] INTEGER NOT NULL, "
469 "[Speed_Max] INTEGER NOT NULL, "
470 "[Speed_Avg] INTEGER DEFAULT 0, "
471 "[Date] DATE NOT NULL);";
472
473 const char* sqlCreateBackupLog =
474 "CREATE TABLE IF NOT EXISTS [BackupLog] ("
475 "[Key] VARCHAR(50) NOT NULL, "
476 "[nValue] INTEGER DEFAULT 0); ";
477
478 const char* sqlCreateEnoceanSensors =
479 "CREATE TABLE IF NOT EXISTS [EnoceanSensors] ("
480 "[ID] INTEGER PRIMARY KEY, "
481 "[HardwareID] INTEGER NOT NULL, "
482 "[DeviceID] VARCHAR(25) NOT NULL, "
483 "[Manufacturer] INTEGER NOT NULL, "
484 "[Profile] INTEGER NOT NULL, "
485 "[Type] INTEGER NOT NULL);";
486
487 const char* sqlCreatePushLink =
488 "CREATE TABLE IF NOT EXISTS [PushLink] ("
489 "[ID] INTEGER PRIMARY KEY, "
490 "[PushType] INTEGER, "
491 "[DeviceRowID] BIGINT NOT NULL, "
492 "[DelimitedValue] INTEGER DEFAULT 0, "
493 "[TargetType] INTEGER DEFAULT 0, "
494 "[TargetVariable] VARCHAR(100), "
495 "[TargetDeviceID] INTEGER, "
496 "[TargetProperty] VARCHAR(100), "
497 "[Enabled] INTEGER DEFAULT 1, "
498 "[IncludeUnit] INTEGER default 0);";
499
500 const char* sqlCreateUserVariables =
501 "CREATE TABLE IF NOT EXISTS [UserVariables] ("
502 "[ID] INTEGER PRIMARY KEY, "
503 "[Name] VARCHAR(200), "
504 "[ValueType] INT NOT NULL, "
505 "[Value] VARCHAR(200), "
506 "[LastUpdate] DATETIME DEFAULT(datetime('now', 'localtime')));";
507
508 const char* sqlCreateFloorplans =
509 "CREATE TABLE IF NOT EXISTS [Floorplans] ("
510 "[ID] INTEGER PRIMARY KEY, "
511 "[Name] VARCHAR(200) NOT NULL, "
512 "[Image] BLOB, "
513 "[ScaleFactor] FLOAT DEFAULT 1.0, "
514 "[Order] INTEGER BIGINT(10) default 0);";
515
516 const char* sqlCreateFloorplanOrderTrigger =
517 "CREATE TRIGGER IF NOT EXISTS floorplanordertrigger AFTER INSERT ON Floorplans\n"
518 "BEGIN\n"
519 " UPDATE Floorplans SET [Order] = (SELECT MAX([Order]) FROM Floorplans)+1 WHERE Floorplans.ID = NEW.ID;\n"
520 "END;\n";
521
522 const char* sqlCreateCustomImages =
523 "CREATE TABLE IF NOT EXISTS [CustomImages]("
524 " [ID] INTEGER PRIMARY KEY, "
525 " [Base] VARCHAR(80) NOT NULL, "
526 " [Name] VARCHAR(80) NOT NULL, "
527 " [Description] VARCHAR(80) NOT NULL, "
528 " [IconSmall] BLOB, "
529 " [IconOn] BLOB, "
530 " [IconOff] BLOB);";
531
532 const char* sqlCreateMySensors =
533 "CREATE TABLE IF NOT EXISTS [MySensors]("
534 " [HardwareID] INTEGER NOT NULL,"
535 " [ID] INTEGER NOT NULL,"
536 " [Name] VARCHAR(100) DEFAULT Unknown,"
537 " [SketchName] VARCHAR(100) DEFAULT Unknown,"
538 " [SketchVersion] VARCHAR(40) DEFAULT(1.0));";
539
540 const char* sqlCreateMySensorsVariables =
541 "CREATE TABLE IF NOT EXISTS [MySensorsVars]("
542 " [HardwareID] INTEGER NOT NULL,"
543 " [NodeID] INTEGER NOT NULL,"
544 " [ChildID] INTEGER NOT NULL,"
545 " [VarID] INTEGER NOT NULL,"
546 " [Value] VARCHAR(100) NOT NULL);";
547
548 const char* sqlCreateMySensorsChilds =
549 "CREATE TABLE IF NOT EXISTS [MySensorsChilds]("
550 " [HardwareID] INTEGER NOT NULL,"
551 " [NodeID] INTEGER NOT NULL,"
552 " [ChildID] INTEGER NOT NULL,"
553 " [Name] VARCHAR(100) DEFAULT '',"
554 " [Type] INTEGER NOT NULL,"
555 " [UseAck] INTEGER DEFAULT 0,"
556 " [AckTimeout] INTEGER DEFAULT 1200);";
557
558 const char* sqlCreateToonDevices =
559 "CREATE TABLE IF NOT EXISTS [ToonDevices]("
560 " [HardwareID] INTEGER NOT NULL,"
561 " [UUID] VARCHAR(100) NOT NULL);";
562
563 const char* sqlCreateUserSessions =
564 "CREATE TABLE IF NOT EXISTS [UserSessions]("
565 " [SessionID] VARCHAR(100) NOT NULL,"
566 " [Username] VARCHAR(100) NOT NULL,"
567 " [AuthToken] VARCHAR(100) UNIQUE NOT NULL,"
568 " [ExpirationDate] DATETIME NOT NULL,"
569 " [RemoteHost] VARCHAR(50) NOT NULL,"
570 " [LastUpdate] DATETIME DEFAULT(datetime('now', 'localtime')),"
571 " PRIMARY KEY([SessionID]));";
572
573 const char* sqlCreateMobileDevices =
574 "CREATE TABLE IF NOT EXISTS [MobileDevices]("
575 "[ID] INTEGER PRIMARY KEY, "
576 "[Active] BOOLEAN DEFAULT false, "
577 "[Name] VARCHAR(100) DEFAULT '',"
578 "[DeviceType] VARCHAR(100) DEFAULT '',"
579 "[SenderID] TEXT NOT NULL,"
580 "[UUID] TEXT NOT NULL, "
581 "[LastUpdate] DATETIME DEFAULT(datetime('now', 'localtime'))"
582 ");";
583
584 extern std::string szUserDataFolder;
585
CSQLHelper(void)586 CSQLHelper::CSQLHelper(void)
587 {
588 m_LastSwitchRowID = 0;
589 m_dbase = NULL;
590 m_sensortimeoutcounter = 0;
591 m_bAcceptNewHardware = true;
592 m_bAllowWidgetOrdering = true;
593 m_ActiveTimerPlan = 0;
594 m_windunit = WINDUNIT_MS;
595 m_tempunit = TEMPUNIT_C;
596 m_weightunit = WEIGHTUNIT_KG;
597 SetUnitsAndScale();
598 m_bAcceptHardwareTimerActive = false;
599 m_iAcceptHardwareTimerCounter = 0;
600 m_bEnableEventSystem = true;
601 m_bEnableEventSystemFullURLLog = true;
602 m_bDisableDzVentsSystem = false;
603 m_ShortLogInterval = 5;
604 m_bPreviousAcceptNewHardware = false;
605 m_bLogEventScriptTrigger = false;
606
607 SetDatabaseName("domoticz.db");
608 }
609
~CSQLHelper(void)610 CSQLHelper::~CSQLHelper(void)
611 {
612 StopThread();
613 CloseDatabase();
614 }
615
OpenDatabase()616 bool CSQLHelper::OpenDatabase()
617 {
618 //Open Database
619 int rc = sqlite3_open(m_dbase_name.c_str(), &m_dbase);
620 if (rc)
621 {
622 _log.Log(LOG_ERROR, "Error opening SQLite3 database: %s", sqlite3_errmsg(m_dbase));
623 sqlite3_close(m_dbase);
624 return false;
625 }
626 #ifndef WIN32
627 //test, this could improve performance
628 sqlite3_exec(m_dbase, "PRAGMA synchronous = NORMAL", NULL, NULL, NULL);
629 sqlite3_exec(m_dbase, "PRAGMA journal_mode = WAL", NULL, NULL, NULL);
630 #else
631 sqlite3_exec(m_dbase, "PRAGMA journal_mode=DELETE", NULL, NULL, NULL);
632 #endif
633 sqlite3_exec(m_dbase, "PRAGMA foreign_keys = ON;", NULL, NULL, NULL);
634 std::vector<std::vector<std::string> > result = query("SELECT name FROM sqlite_master WHERE type='table' AND name='DeviceStatus'");
635 bool bNewInstall = (result.size() == 0);
636 int dbversion = 0;
637 if (!bNewInstall)
638 {
639 GetPreferencesVar("DB_Version", dbversion);
640 if (dbversion > DB_VERSION)
641 {
642 //User is using a newer database on a old Domoticz version
643 //This is very dangerous and should not be allowed
644 _log.Log(LOG_ERROR, "Database incompatible with this Domoticz version. (You cannot downgrade to an old Domoticz version!)");
645 sqlite3_close(m_dbase);
646 m_dbase = NULL;
647 return false;
648 }
649 //Pre-SQL Patches
650 }
651
652 //create database (if not exists)
653 sqlite3_exec(m_dbase, "BEGIN TRANSACTION;", NULL, NULL, NULL);
654 query(sqlCreateDeviceStatus);
655 query(sqlCreateDeviceStatusTrigger);
656 query(sqlCreateLightingLog);
657 query(sqlCreateSceneLog);
658 query(sqlCreatePreferences);
659 query(sqlCreateRain);
660 query(sqlCreateRain_Calendar);
661 query(sqlCreateTemperature);
662 query(sqlCreateTemperature_Calendar);
663 query(sqlCreateTimers);
664 query(sqlCreateSetpointTimers);
665 query(sqlCreateUV);
666 query(sqlCreateUV_Calendar);
667 query(sqlCreateWind);
668 query(sqlCreateWind_Calendar);
669 query(sqlCreateMeter);
670 query(sqlCreateMeter_Calendar);
671 query(sqlCreateMultiMeter);
672 query(sqlCreateMultiMeter_Calendar);
673 query(sqlCreateNotifications);
674 query(sqlCreateHardware);
675 query(sqlCreateUsers);
676 query(sqlCreateLightSubDevices);
677 query(sqlCreateCameras);
678 query(sqlCreateCamerasActiveDevices);
679 query(sqlCreatePlanMappings);
680 query(sqlCreateDevicesToPlanStatusTrigger);
681 query(sqlCreatePlans);
682 query(sqlCreatePlanOrderTrigger);
683 query(sqlCreateScenes);
684 query(sqlCreateScenesTrigger);
685 query(sqlCreateSceneDevices);
686 query(sqlCreateSceneDeviceTrigger);
687 query(sqlCreateTimerPlans);
688 query(sqlCreateSceneTimers);
689 query(sqlCreateSharedDevices);
690 query(sqlCreateEventMaster);
691 query(sqlCreateEventRules);
692 query(sqlCreateZWaveNodes);
693 query(sqlCreateWOLNodes);
694 query(sqlCreatePercentage);
695 query(sqlCreatePercentage_Calendar);
696 query(sqlCreateFan);
697 query(sqlCreateFan_Calendar);
698 query(sqlCreateBackupLog);
699 query(sqlCreateEnoceanSensors);
700 query(sqlCreatePushLink);
701 query(sqlCreateUserVariables);
702 query(sqlCreateFloorplans);
703 query(sqlCreateFloorplanOrderTrigger);
704 query(sqlCreateCustomImages);
705 query(sqlCreateMySensors);
706 query(sqlCreateMySensorsVariables);
707 query(sqlCreateMySensorsChilds);
708 query(sqlCreateToonDevices);
709 query(sqlCreateUserSessions);
710 query(sqlCreateMobileDevices);
711 //Add indexes to log tables
712 query("create index if not exists ds_hduts_idx on DeviceStatus(HardwareID, DeviceID, Unit, Type, SubType);");
713 query("create index if not exists f_id_idx on Fan(DeviceRowID);");
714 query("create index if not exists f_id_date_idx on Fan(DeviceRowID, Date);");
715 query("create index if not exists fc_id_idx on Fan_Calendar(DeviceRowID);");
716 query("create index if not exists fc_id_date_idx on Fan_Calendar(DeviceRowID, Date);");
717 query("create index if not exists ll_id_idx on LightingLog(DeviceRowID);");
718 query("create index if not exists ll_id_date_idx on LightingLog(DeviceRowID, Date);");
719 query("create index if not exists sl_id_idx on SceneLog(SceneRowID);");
720 query("create index if not exists sl_id_date_idx on SceneLog(SceneRowID, Date);");
721 query("create index if not exists m_id_idx on Meter(DeviceRowID);");
722 query("create index if not exists m_id_date_idx on Meter(DeviceRowID, Date);");
723 query("create index if not exists mc_id_idx on Meter_Calendar(DeviceRowID);");
724 query("create index if not exists mc_id_date_idx on Meter_Calendar(DeviceRowID, Date);");
725 query("create index if not exists mm_id_idx on MultiMeter(DeviceRowID);");
726 query("create index if not exists mm_id_date_idx on MultiMeter(DeviceRowID, Date);");
727 query("create index if not exists mmc_id_idx on MultiMeter_Calendar(DeviceRowID);");
728 query("create index if not exists mmc_id_date_idx on MultiMeter_Calendar(DeviceRowID, Date);");
729 query("create index if not exists p_id_idx on Percentage(DeviceRowID);");
730 query("create index if not exists p_id_date_idx on Percentage(DeviceRowID, Date);");
731 query("create index if not exists pc_id_idx on Percentage_Calendar(DeviceRowID);");
732 query("create index if not exists pc_id_date_idx on Percentage_Calendar(DeviceRowID, Date);");
733 query("create index if not exists r_id_idx on Rain(DeviceRowID);");
734 query("create index if not exists r_id_date_idx on Rain(DeviceRowID, Date);");
735 query("create index if not exists rc_id_idx on Rain_Calendar(DeviceRowID);");
736 query("create index if not exists rc_id_date_idx on Rain_Calendar(DeviceRowID, Date);");
737 query("create index if not exists t_id_idx on Temperature(DeviceRowID);");
738 query("create index if not exists t_id_date_idx on Temperature(DeviceRowID, Date);");
739 query("create index if not exists tc_id_idx on Temperature_Calendar(DeviceRowID);");
740 query("create index if not exists tc_id_date_idx on Temperature_Calendar(DeviceRowID, Date);");
741 query("create index if not exists u_id_idx on UV(DeviceRowID);");
742 query("create index if not exists u_id_date_idx on UV(DeviceRowID, Date);");
743 query("create index if not exists uv_id_idx on UV_Calendar(DeviceRowID);");
744 query("create index if not exists uv_id_date_idx on UV_Calendar(DeviceRowID, Date);");
745 query("create index if not exists w_id_idx on Wind(DeviceRowID);");
746 query("create index if not exists w_id_date_idx on Wind(DeviceRowID, Date);");
747 query("create index if not exists wc_id_idx on Wind_Calendar(DeviceRowID);");
748 query("create index if not exists wc_id_date_idx on Wind_Calendar(DeviceRowID, Date);");
749 sqlite3_exec(m_dbase, "END TRANSACTION;", NULL, NULL, NULL);
750
751 if ((!bNewInstall) && (dbversion < DB_VERSION))
752 {
753 //Post-SQL Patches
754 if (dbversion < 2)
755 {
756 query("ALTER TABLE DeviceStatus ADD COLUMN [Order] INTEGER BIGINT(10) default 0");
757 query(sqlCreateDeviceStatusTrigger);
758 CheckAndUpdateDeviceOrder();
759 }
760 if (dbversion < 3)
761 {
762 query("ALTER TABLE Hardware ADD COLUMN [Enabled] INTEGER default 1");
763 }
764 if (dbversion < 4)
765 {
766 query("ALTER TABLE DeviceStatus ADD COLUMN [AddjValue] FLOAT default 0");
767 query("ALTER TABLE DeviceStatus ADD COLUMN [AddjMulti] FLOAT default 1");
768 }
769 if (dbversion < 5)
770 {
771 query("ALTER TABLE SceneDevices ADD COLUMN [Cmd] INTEGER default 1");
772 query("ALTER TABLE SceneDevices ADD COLUMN [Level] INTEGER default 100");
773 }
774 if (dbversion < 6)
775 {
776 query("ALTER TABLE Cameras ADD COLUMN [ImageURL] VARCHAR(100)");
777 }
778 if (dbversion < 7)
779 {
780 query("ALTER TABLE DeviceStatus ADD COLUMN [AddjValue2] FLOAT default 0");
781 query("ALTER TABLE DeviceStatus ADD COLUMN [AddjMulti2] FLOAT default 1");
782 }
783 if (dbversion < 8)
784 {
785 query("DROP TABLE IF EXISTS [Cameras]");
786 query(sqlCreateCameras);
787 }
788 if (dbversion < 9) {
789 query("UPDATE Notifications SET Params = 'S' WHERE Params = ''");
790 }
791 if (dbversion < 10)
792 {
793 //P1 Smart meter power change, need to delete all short logs from today
794 char szDateStart[40];
795 time_t now = mytime(NULL);
796 struct tm tm1;
797 localtime_r(&now, &tm1);
798 struct tm ltime;
799 ltime.tm_isdst = tm1.tm_isdst;
800 ltime.tm_hour = 0;
801 ltime.tm_min = 0;
802 ltime.tm_sec = 0;
803 ltime.tm_year = tm1.tm_year;
804 ltime.tm_mon = tm1.tm_mon;
805 ltime.tm_mday = tm1.tm_mday;
806
807 sprintf(szDateStart, "%04d-%02d-%02d %02d:%02d:%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday, ltime.tm_hour, ltime.tm_min, ltime.tm_sec);
808
809 std::vector<std::vector<std::string> > result;
810 result = safe_query("SELECT ID FROM DeviceStatus WHERE (Type=%d)", pTypeP1Power);
811 if (!result.empty())
812 {
813 for (const auto& itt : result)
814 {
815 std::vector<std::string> sd = itt;
816 std::string idx = sd[0];
817 safe_query("DELETE FROM MultiMeter WHERE (DeviceRowID='%q') AND (Date>='%q')", idx.c_str(), szDateStart);
818 }
819 }
820 }
821 if (dbversion < 11)
822 {
823 std::vector<std::vector<std::string> > result;
824
825 result = safe_query("SELECT ID, Username, Password FROM Cameras ORDER BY ID");
826 if (!result.empty())
827 {
828 for (const auto& itt : result)
829 {
830 std::vector<std::string> sd = itt;
831 std::string camuser = base64_encode(sd[1]);
832 std::string campwd = base64_encode(sd[2]);
833 safe_query("UPDATE Cameras SET Username='%q', Password='%q' WHERE (ID=='%q')",
834 camuser.c_str(), campwd.c_str(), sd[0].c_str());
835 }
836 }
837 }
838 if (dbversion < 12)
839 {
840 std::vector<std::vector<std::string> > result;
841 result = query("SELECT t.RowID, u.RowID from MultiMeter_Calendar as t, MultiMeter_Calendar as u WHERE (t.[Date] == u.[Date]) AND (t.[DeviceRowID] == u.[DeviceRowID]) AND (t.[RowID] != u.[RowID])");
842 if (!result.empty())
843 {
844 std::vector<std::vector<std::string> >::const_iterator itt;
845 for (itt = result.begin(); itt != result.end(); ++itt)
846 {
847 ++itt;
848 std::vector<std::string> sd = *itt;
849 safe_query("DELETE FROM MultiMeter_Calendar WHERE (RowID=='%q')", sd[0].c_str());
850 }
851 }
852
853 }
854 if (dbversion < 13)
855 {
856 DeleteHardware("1001");
857 }
858 if (dbversion < 14)
859 {
860 query("ALTER TABLE Users ADD COLUMN [RemoteSharing] INTEGER default 0");
861 }
862 if (dbversion < 15)
863 {
864 query("DROP TABLE IF EXISTS [HardwareSharing]");
865 query("ALTER TABLE DeviceStatus ADD COLUMN [LastLevel] INTEGER default 0");
866 }
867 if (dbversion < 16)
868 {
869 query("ALTER TABLE Events RENAME TO tmp_Events;");
870 query("CREATE TABLE Events ([ID] INTEGER PRIMARY KEY, [Name] VARCHAR(200) NOT NULL, [XMLStatement] TEXT NOT NULL,[Conditions] TEXT, [Actions] TEXT);");
871 query("INSERT INTO Events(Name, XMLStatement) SELECT Name, XMLStatement FROM tmp_Events;");
872 query("DROP TABLE tmp_Events;");
873 }
874 if (dbversion < 17)
875 {
876 query("ALTER TABLE Events ADD COLUMN [Status] INTEGER default 0");
877 }
878 if (dbversion < 18)
879 {
880 query("ALTER TABLE Temperature ADD COLUMN [DewPoint] FLOAT default 0");
881 query("ALTER TABLE Temperature_Calendar ADD COLUMN [DewPoint] FLOAT default 0");
882 }
883 if (dbversion < 19)
884 {
885 query("ALTER TABLE Scenes ADD COLUMN [SceneType] INTEGER default 0");
886 }
887
888 if (dbversion < 20)
889 {
890 query("INSERT INTO EventMaster(Name, XMLStatement, Status) SELECT Name, XMLStatement, Status FROM Events;");
891 query("INSERT INTO EventRules(EMID, Conditions, Actions, SequenceNo) SELECT EventMaster.ID, Events.Conditions, Events.Actions, 1 FROM Events INNER JOIN EventMaster ON EventMaster.Name = Events.Name;");
892 query("DROP TABLE Events;");
893 }
894 if (dbversion < 21)
895 {
896 //increase Video/Image URL for camera's
897 //create a backup
898 query("ALTER TABLE Cameras RENAME TO tmp_Cameras");
899 //Create the new table
900 query(sqlCreateCameras);
901 //Copy values from tmp_Cameras back into our new table
902 query(
903 "INSERT INTO Cameras([ID],[Name],[Enabled],[Address],[Port],[Username],[Password],[ImageURL])"
904 "SELECT [ID],[Name],[Enabled],[Address],[Port],[Username],[Password],[ImageURL]"
905 "FROM tmp_Cameras");
906 //Drop the tmp_Cameras table
907 query("DROP TABLE tmp_Cameras");
908 }
909 if (dbversion < 22)
910 {
911 query("ALTER TABLE DeviceToPlansMap ADD COLUMN [Order] INTEGER BIGINT(10) default 0");
912 query(sqlCreateDevicesToPlanStatusTrigger);
913 }
914 if (dbversion < 23)
915 {
916 query("ALTER TABLE Temperature_Calendar ADD COLUMN [Temp_Avg] FLOAT default 0");
917
918 std::vector<std::vector<std::string> > result;
919 result = query("SELECT RowID, (Temp_Max+Temp_Min)/2 FROM Temperature_Calendar");
920 if (!result.empty())
921 {
922 sqlite3_exec(m_dbase, "BEGIN TRANSACTION;", NULL, NULL, NULL);
923 for (const auto& itt : result)
924 {
925 std::vector<std::string> sd = itt;
926 safe_query("UPDATE Temperature_Calendar SET Temp_Avg=%.1f WHERE RowID='%q'", atof(sd[1].c_str()), sd[0].c_str());
927 }
928 sqlite3_exec(m_dbase, "END TRANSACTION;", NULL, NULL, NULL);
929 }
930 }
931 if (dbversion < 24)
932 {
933 query("ALTER TABLE SceneDevices ADD COLUMN [Order] INTEGER BIGINT(10) default 0");
934 query(sqlCreateSceneDeviceTrigger);
935 CheckAndUpdateSceneDeviceOrder();
936 }
937 if (dbversion < 25)
938 {
939 query("DROP TABLE IF EXISTS [Plans]");
940 query(sqlCreatePlans);
941 query(sqlCreatePlanOrderTrigger);
942 }
943 if (dbversion < 26)
944 {
945 query("DROP TABLE IF EXISTS [DeviceToPlansMap]");
946 query(sqlCreatePlanMappings);
947 query(sqlCreateDevicesToPlanStatusTrigger);
948 }
949 if (dbversion < 27)
950 {
951 query("ALTER TABLE DeviceStatus ADD COLUMN [CustomImage] INTEGER default 0");
952 }
953 if (dbversion < 28)
954 {
955 query("ALTER TABLE Timers ADD COLUMN [UseRandomness] INTEGER default 0");
956 query("ALTER TABLE SceneTimers ADD COLUMN [UseRandomness] INTEGER default 0");
957 query("UPDATE Timers SET [Type]=2, [UseRandomness]=1 WHERE ([Type]=5)");
958 query("UPDATE SceneTimers SET [Type]=2, [UseRandomness]=1 WHERE ([Type]=5)");
959 //"[] INTEGER DEFAULT 0, "
960 }
961 if (dbversion < 30)
962 {
963 query("ALTER TABLE DeviceToPlansMap ADD COLUMN [DevSceneType] INTEGER default 0");
964 }
965 if (dbversion < 31)
966 {
967 query("ALTER TABLE Users ADD COLUMN [TabsEnabled] INTEGER default 255");
968 }
969 if (dbversion < 32)
970 {
971 query("ALTER TABLE SceneDevices ADD COLUMN [Hue] INTEGER default 0");
972 query("ALTER TABLE SceneTimers ADD COLUMN [Hue] INTEGER default 0");
973 query("ALTER TABLE Timers ADD COLUMN [Hue] INTEGER default 0");
974 }
975 if (dbversion < 33)
976 {
977 query("DROP TABLE IF EXISTS [Load]");
978 query("DROP TABLE IF EXISTS [Load_Calendar]");
979 query("DROP TABLE IF EXISTS [Fan]");
980 query("DROP TABLE IF EXISTS [Fan_Calendar]");
981 query(sqlCreatePercentage);
982 query(sqlCreatePercentage_Calendar);
983 query(sqlCreateFan);
984 query(sqlCreateFan_Calendar);
985
986 std::vector<std::vector<std::string> > result;
987 result = query("SELECT ID FROM DeviceStatus WHERE (DeviceID LIKE 'WMI%')");
988 if (!result.empty())
989 {
990 for (const auto& itt : result)
991 {
992 std::vector<std::string> sd = itt;
993 std::string idx = sd[0];
994 safe_query("DELETE FROM Temperature WHERE (DeviceRowID='%q')", idx.c_str());
995 safe_query("DELETE FROM Temperature_Calendar WHERE (DeviceRowID='%q')", idx.c_str());
996 }
997 }
998 query("DELETE FROM DeviceStatus WHERE (DeviceID LIKE 'WMI%')");
999 }
1000 if (dbversion < 34)
1001 {
1002 query("ALTER TABLE DeviceStatus ADD COLUMN [StrParam1] VARCHAR(200) DEFAULT ''");
1003 query("ALTER TABLE DeviceStatus ADD COLUMN [StrParam2] VARCHAR(200) DEFAULT ''");
1004 }
1005 if (dbversion < 35)
1006 {
1007 query("ALTER TABLE Notifications ADD COLUMN [Priority] INTEGER default 0");
1008 }
1009 if (dbversion < 36)
1010 {
1011 query("ALTER TABLE Meter ADD COLUMN [Usage] INTEGER default 0");
1012 }
1013 if (dbversion < 37)
1014 {
1015 //move all load data from tables into the new percentage one
1016 query(
1017 "INSERT INTO Percentage([DeviceRowID],[Percentage],[Date])"
1018 "SELECT [DeviceRowID],[Load],[Date] FROM Load");
1019 query(
1020 "INSERT INTO Percentage_Calendar([DeviceRowID],[Percentage_Min],[Percentage_Max],[Percentage_Avg],[Date])"
1021 "SELECT [DeviceRowID],[Load_Min],[Load_Max],[Load_Avg],[Date] FROM Load_Calendar");
1022 query("DROP TABLE IF EXISTS [Load]");
1023 query("DROP TABLE IF EXISTS [Load_Calendar]");
1024 }
1025 if (dbversion < 38)
1026 {
1027 query("ALTER TABLE DeviceStatus ADD COLUMN [Protected] INTEGER default 0");
1028 }
1029 if (dbversion < 39)
1030 {
1031 query("ALTER TABLE Scenes ADD COLUMN [Protected] INTEGER default 0");
1032 }
1033 if (dbversion < 40)
1034 {
1035 FixDaylightSaving();
1036 }
1037 if (dbversion < 41)
1038 {
1039 query("ALTER TABLE FibaroLink ADD COLUMN [IncludeUnit] INTEGER default 0");
1040 }
1041 if (dbversion < 42)
1042 {
1043 query("INSERT INTO Plans (Name) VALUES ('$Hidden Devices')");
1044 }
1045 if (dbversion < 43)
1046 {
1047 query("ALTER TABLE Scenes ADD COLUMN [OnAction] VARCHAR(200) DEFAULT ''");
1048 query("ALTER TABLE Scenes ADD COLUMN [OffAction] VARCHAR(200) DEFAULT ''");
1049 }
1050 if (dbversion < 44)
1051 {
1052 //Drop VideoURL
1053 //create a backup
1054 query("ALTER TABLE Cameras RENAME TO tmp_Cameras");
1055 //Create the new table
1056 query(sqlCreateCameras);
1057 //Copy values from tmp_Cameras back into our new table
1058 query(
1059 "INSERT INTO Cameras([ID],[Name],[Enabled],[Address],[Port],[Username],[Password],[ImageURL])"
1060 "SELECT [ID],[Name],[Enabled],[Address],[Port],[Username],[Password],[ImageURL]"
1061 "FROM tmp_Cameras");
1062 //Drop the tmp_Cameras table
1063 query("DROP TABLE tmp_Cameras");
1064 }
1065 if (dbversion < 45)
1066 {
1067 query("ALTER TABLE Timers ADD COLUMN [TimerPlan] INTEGER default 0");
1068 query("ALTER TABLE SceneTimers ADD COLUMN [TimerPlan] INTEGER default 0");
1069 }
1070 if (dbversion < 46)
1071 {
1072 query("ALTER TABLE Plans ADD COLUMN [FloorplanID] INTEGER default 0");
1073 query("ALTER TABLE Plans ADD COLUMN [Area] VARCHAR(200) DEFAULT ''");
1074 query("ALTER TABLE DeviceToPlansMap ADD COLUMN [XOffset] INTEGER default 0");
1075 query("ALTER TABLE DeviceToPlansMap ADD COLUMN [YOffset] INTEGER default 0");
1076 }
1077 if (dbversion < 47)
1078 {
1079 query("ALTER TABLE Hardware ADD COLUMN [DataTimeout] INTEGER default 0");
1080 }
1081 if (dbversion < 48)
1082 {
1083 result = safe_query("SELECT ID FROM DeviceStatus WHERE (Type=%d)", pTypeUsage);
1084 if (!result.empty())
1085 {
1086 for (const auto& itt : result)
1087 {
1088 std::vector<std::string> sd = itt;
1089 std::string idx = sd[0];
1090 safe_query("UPDATE Meter SET Value = Value * 10 WHERE (DeviceRowID='%q')", idx.c_str());
1091 safe_query("UPDATE Meter_Calendar SET Value = Value * 10 WHERE (DeviceRowID='%q')", idx.c_str());
1092 safe_query("UPDATE MultiMeter_Calendar SET Value1 = Value1 * 10, Value2 = Value2 * 10 WHERE (DeviceRowID='%q')", idx.c_str());
1093 }
1094 }
1095 query("ALTER TABLE Hardware ADD COLUMN [DataTimeout] INTEGER default 0");
1096 }
1097 if (dbversion < 49)
1098 {
1099 query("ALTER TABLE Plans ADD COLUMN [FloorplanID] INTEGER default 0");
1100 query("ALTER TABLE Plans ADD COLUMN [Area] VARCHAR(200) DEFAULT ''");
1101 query("ALTER TABLE DeviceToPlansMap ADD COLUMN [XOffset] INTEGER default 0");
1102 query("ALTER TABLE DeviceToPlansMap ADD COLUMN [YOffset] INTEGER default 0");
1103 }
1104 if (dbversion < 50)
1105 {
1106 query("ALTER TABLE Timers ADD COLUMN [Date] DATE default 0");
1107 query("ALTER TABLE SceneTimers ADD COLUMN [Date] DATE default 0");
1108 }
1109 if (dbversion < 51)
1110 {
1111 query("ALTER TABLE Meter_Calendar ADD COLUMN [Counter] BIGINT default 0");
1112 query("ALTER TABLE MultiMeter_Calendar ADD COLUMN [Counter1] BIGINT default 0");
1113 query("ALTER TABLE MultiMeter_Calendar ADD COLUMN [Counter2] BIGINT default 0");
1114 query("ALTER TABLE MultiMeter_Calendar ADD COLUMN [Counter3] BIGINT default 0");
1115 query("ALTER TABLE MultiMeter_Calendar ADD COLUMN [Counter4] BIGINT default 0");
1116 }
1117 if (dbversion < 52)
1118 {
1119 //Move onboard system sensor (temperature) to the motherboard hardware
1120 std::vector<std::vector<std::string> > result;
1121 result = safe_query("SELECT ID FROM Hardware WHERE (Type==%d) AND (Name=='Motherboard') LIMIT 1", HTYPE_System);
1122 if (!result.empty())
1123 {
1124 int hwId = atoi(result[0][0].c_str());
1125 safe_query("UPDATE DeviceStatus SET HardwareID=%d WHERE (HardwareID=1000)", hwId);
1126 }
1127 }
1128 if (dbversion < 53)
1129 {
1130 query("ALTER TABLE Floorplans ADD COLUMN [ScaleFactor] Float default 1.0");
1131 }
1132 if (dbversion < 54)
1133 {
1134 query("ALTER TABLE Temperature ADD COLUMN [SetPoint] FLOAT default 0");
1135 query("ALTER TABLE Temperature_Calendar ADD COLUMN [SetPoint_Min] FLOAT default 0");
1136 query("ALTER TABLE Temperature_Calendar ADD COLUMN [SetPoint_Max] FLOAT default 0");
1137 query("ALTER TABLE Temperature_Calendar ADD COLUMN [SetPoint_Avg] FLOAT default 0");
1138 }
1139 if (dbversion < 55)
1140 {
1141 query("DROP TABLE IF EXISTS [CustomImages]");
1142 query(sqlCreateCustomImages);
1143 }
1144 if (dbversion < 56)
1145 {
1146 std::stringstream szQuery2;
1147 std::vector<std::vector<std::string> > result2;
1148 std::string pHash;
1149 szQuery2 << "SELECT sValue FROM Preferences WHERE (Key='WebPassword')";
1150 result2 = query(szQuery2.str());
1151 if (!result2.empty())
1152 {
1153 std::string pwd = result2[0][0];
1154 if (pwd.size() != 32)
1155 {
1156 pHash = GenerateMD5Hash(base64_decode(pwd));
1157 szQuery2.clear();
1158 szQuery2.str("");
1159 szQuery2 << "UPDATE Preferences SET sValue='" << pHash << "' WHERE (Key='WebPassword')";
1160 query(szQuery2.str());
1161 }
1162 }
1163
1164 szQuery2.clear();
1165 szQuery2.str("");
1166 szQuery2 << "SELECT sValue FROM Preferences WHERE (Key='SecPassword')";
1167 result2 = query(szQuery2.str());
1168 if (!result2.empty())
1169 {
1170 std::string pwd = result2[0][0];
1171 if (pwd.size() != 32)
1172 {
1173 pHash = GenerateMD5Hash(base64_decode(pwd));
1174 szQuery2.clear();
1175 szQuery2.str("");
1176 szQuery2 << "UPDATE Preferences SET sValue='" << pHash << "' WHERE (Key='SecPassword')";
1177 query(szQuery2.str());
1178 }
1179 }
1180
1181 szQuery2.clear();
1182 szQuery2.str("");
1183 szQuery2 << "SELECT sValue FROM Preferences WHERE (Key='ProtectionPassword')";
1184 result2 = query(szQuery2.str());
1185 if (!result2.empty())
1186 {
1187 std::string pwd = result2[0][0];
1188 if (pwd.size() != 32)
1189 {
1190 pHash = GenerateMD5Hash(base64_decode(pwd));
1191 szQuery2.clear();
1192 szQuery2.str("");
1193 szQuery2 << "UPDATE Preferences SET sValue='" << pHash << "' WHERE (Key='ProtectionPassword')";
1194 query(szQuery2.str());
1195 }
1196 }
1197 szQuery2.clear();
1198 szQuery2.str("");
1199 szQuery2 << "SELECT ID, Password FROM Users";
1200 result2 = query(szQuery2.str());
1201 for (const auto& itt : result2)
1202 {
1203 std::vector<std::string> sd = itt;
1204 std::string pwd = sd[1];
1205 if (pwd.size() != 32)
1206 {
1207 pHash = GenerateMD5Hash(base64_decode(pwd));
1208 szQuery2.clear();
1209 szQuery2.str("");
1210 szQuery2 << "UPDATE Users SET Password='" << pHash << "' WHERE (ID=" << sd[0] << ")";
1211 query(szQuery2.str());
1212 }
1213 }
1214
1215 szQuery2.clear();
1216 szQuery2.str("");
1217 szQuery2 << "SELECT ID, Password FROM Hardware WHERE ([Type]==" << HTYPE_Domoticz << ")";
1218 result2 = query(szQuery2.str());
1219 for (const auto& itt : result2)
1220 {
1221 std::vector<std::string> sd = itt;
1222 std::string pwd = sd[1];
1223 if (pwd.size() != 32)
1224 {
1225 pHash = GenerateMD5Hash(pwd);
1226 szQuery2.clear();
1227 szQuery2.str("");
1228 szQuery2 << "UPDATE Hardware SET Password='" << pHash << "' WHERE (ID=" << sd[0] << ")";
1229 query(szQuery2.str());
1230 }
1231 }
1232 }
1233 if (dbversion < 57)
1234 {
1235 //S0 Meter patch
1236 std::stringstream szQuery2;
1237 std::vector<std::vector<std::string> > result;
1238 szQuery2 << "SELECT ID, Mode1, Mode2, Mode3, Mode4 FROM HARDWARE WHERE([Type]==" << HTYPE_S0SmartMeterUSB << ")";
1239 result = query(szQuery2.str());
1240 if (!result.empty())
1241 {
1242 for (const auto& itt : result)
1243 {
1244 std::vector<std::string> sd = itt;
1245 std::stringstream szAddress;
1246 szAddress
1247 << sd[1] << ";" << sd[2] << ";"
1248 << sd[3] << ";" << sd[4] << ";"
1249 << sd[1] << ";" << sd[2] << ";"
1250 << sd[1] << ";" << sd[2] << ";"
1251 << sd[1] << ";" << sd[2];
1252 szQuery2.clear();
1253 szQuery2.str("");
1254 szQuery2 << "UPDATE Hardware SET Address='" << szAddress.str() << "', Mode1=0, Mode2=0, Mode3=0, Mode4=0 WHERE (ID=" << sd[0] << ")";
1255 query(szQuery2.str());
1256 }
1257 }
1258 }
1259 if (dbversion < 58)
1260 {
1261 //Patch for new ZWave light sensor type
1262 std::stringstream szQuery2;
1263 std::vector<std::vector<std::string> > result;
1264 result = query("SELECT ID FROM Hardware WHERE ([Type] = 9) OR ([Type] = 21)");
1265 if (!result.empty())
1266 {
1267 for (const auto& itt : result)
1268 {
1269 std::vector<std::string> sd = itt;
1270
1271 szQuery2.clear();
1272 szQuery2.str("");
1273 //#define sTypeZWaveSwitch 0xA1
1274 szQuery2 << "UPDATE DeviceStatus SET SubType=" << 0xA1 << " WHERE ([Type]=" << pTypeLighting2 << ") AND (SubType=" << sTypeAC << ") AND (HardwareID=" << sd[0] << ")";
1275 query(szQuery2.str());
1276 }
1277 }
1278 }
1279 if (dbversion < 60)
1280 {
1281 query("ALTER TABLE SceneDevices ADD COLUMN [OnDelay] INTEGER default 0");
1282 query("ALTER TABLE SceneDevices ADD COLUMN [OffDelay] INTEGER default 0");
1283 }
1284 if (dbversion < 61)
1285 {
1286 query("ALTER TABLE DeviceStatus ADD COLUMN [Description] VARCHAR(200) DEFAULT ''");
1287 }
1288 if (dbversion < 62)
1289 {
1290 //Fix for Teleinfo hardware, where devices where created with Hardware_ID=0
1291 std::stringstream szQuery2;
1292 std::vector<std::vector<std::string> > result;
1293 result = query("SELECT ID FROM Hardware WHERE ([Type] = 19)");
1294 if (!result.empty())
1295 {
1296 int TeleInfoHWID = atoi(result[0][0].c_str());
1297 szQuery2 << "UPDATE DeviceStatus SET HardwareID=" << TeleInfoHWID << " WHERE ([HardwareID]=0)";
1298 query(szQuery2.str());
1299 }
1300 }
1301 if (dbversion < 63)
1302 {
1303 query("DROP TABLE IF EXISTS [TempVars]");
1304 }
1305 if (dbversion < 64)
1306 {
1307 FixDaylightSaving();
1308 }
1309 if (dbversion < 65)
1310 {
1311 //Patch for Toon, reverse counters
1312 std::stringstream szQuery;
1313 std::vector<std::vector<std::string> > result;
1314 std::vector<std::vector<std::string> > result2;
1315 std::vector<std::vector<std::string> > result3;
1316 szQuery << "SELECT ID FROM HARDWARE WHERE([Type]==" << HTYPE_TOONTHERMOSTAT << ")";
1317 result = query(szQuery.str());
1318 for (const auto& itt : result)
1319 {
1320 std::vector<std::string> sd = itt;
1321 int hwid = atoi(sd[0].c_str());
1322
1323 szQuery.clear();
1324 szQuery.str("");
1325 szQuery << "SELECT ID FROM DeviceStatus WHERE (Type=" << pTypeP1Power << ") AND (HardwareID=" << hwid << ")";
1326 result2 = query(szQuery.str());
1327 for (const auto& itt2 : result2)
1328 {
1329 std::vector<std::string> sd = itt2;
1330
1331 //First the shortlog
1332 szQuery.clear();
1333 szQuery.str("");
1334 szQuery << "SELECT ROWID, Value1, Value2, Value3, Value4, Value5, Value6 FROM MultiMeter WHERE (DeviceRowID==" << sd[0] << ")";
1335 result3 = query(szQuery.str());
1336 for (const auto& itt3 : result3)
1337 {
1338 std::vector<std::string> sd = itt3;
1339 //value1 = powerusage1;
1340 //value2 = powerdeliv1;
1341 //value5 = powerusage2;
1342 //value6 = powerdeliv2;
1343 //value3 = usagecurrent;
1344 //value4 = delivcurrent;
1345 szQuery.clear();
1346 szQuery.str("");
1347 szQuery << "UPDATE MultiMeter SET Value1=" << sd[5] << ", Value2=" << sd[6] << ", Value5=" << sd[1] << ", Value6=" << sd[2] << " WHERE (ROWID=" << sd[0] << ")";
1348 query(szQuery.str());
1349 }
1350 //Next for the calendar
1351 szQuery.clear();
1352 szQuery.str("");
1353 szQuery << "SELECT ROWID, Value1, Value2, Value3, Value4, Value5, Value6, Counter1, Counter2, Counter3, Counter4 FROM MultiMeter_Calendar WHERE (DeviceRowID==" << sd[0] << ")";
1354 result3 = query(szQuery.str());
1355 for (const auto& itt3 : result3)
1356 {
1357 std::vector<std::string> sd = itt3;
1358 szQuery.clear();
1359 szQuery.str("");
1360 szQuery << "UPDATE MultiMeter_Calendar SET Value1=" << sd[5] << ", Value2=" << sd[6] << ", Value5=" << sd[1] << ", Value6=" << sd[2] << ", Counter1=" << sd[9] << ", Counter2=" << sd[10] << ", Counter3=" << sd[7] << ", Counter4=" << sd[8] << " WHERE (ROWID=" << sd[0] << ")";
1361 query(szQuery.str());
1362 }
1363 }
1364 }
1365 }
1366 if (dbversion < 66)
1367 {
1368 query("ALTER TABLE Hardware ADD COLUMN [Mode6] CHAR default 0");
1369 }
1370 if (dbversion < 67)
1371 {
1372 //Enable all notification systems
1373 UpdatePreferencesVar("ProwlEnabled", 1);
1374 UpdatePreferencesVar("PushALotEnabled", 1);
1375 UpdatePreferencesVar("PushoverEnabled", 1);
1376 UpdatePreferencesVar("PushsaferEnabled", 1);
1377 UpdatePreferencesVar("ClickatellEnabled", 1);
1378 }
1379 if (dbversion < 68)
1380 {
1381 query("ALTER TABLE Notifications ADD COLUMN [CustomMessage] VARCHAR(300) DEFAULT ('')");
1382 query("ALTER TABLE Notifications ADD COLUMN [ActiveSystems] VARCHAR(300) DEFAULT ('')");
1383 }
1384 if (dbversion < 69)
1385 {
1386 //Serial Port patch (using complete port paths now)
1387 query("ALTER TABLE Hardware ADD COLUMN [SerialPort] VARCHAR(50) DEFAULT ('')");
1388
1389 //Convert all serial hardware to use the new column
1390 std::stringstream szQuery;
1391 std::vector<std::vector<std::string> > result;
1392 szQuery << "SELECT ID,[Type],Port FROM Hardware";
1393 result = query(szQuery.str());
1394 if (!result.empty())
1395 {
1396 for (const auto& itt : result)
1397 {
1398 std::vector<std::string> sd = itt;
1399 int hwId = atoi(sd[0].c_str());
1400 _eHardwareTypes hwtype = (_eHardwareTypes)atoi(sd[1].c_str());
1401 size_t Port = (size_t)atoi(sd[2].c_str());
1402
1403 if (IsSerialDevice(hwtype))
1404 {
1405 char szSerialPort[50];
1406 #if defined WIN32
1407 sprintf(szSerialPort, "COM%d", (int)Port);
1408 #else
1409 bool bUseDirectPath = false;
1410 std::vector<std::string> serialports = GetSerialPorts(bUseDirectPath);
1411 if (bUseDirectPath)
1412 {
1413 if (Port >= serialports.size())
1414 {
1415 _log.Log(LOG_ERROR, "Serial Port out of range!...");
1416 continue;
1417 }
1418 strcpy(szSerialPort, serialports[Port].c_str());
1419 }
1420 else
1421 {
1422 sprintf(szSerialPort, "/dev/ttyUSB%d", (int)Port);
1423 }
1424 #endif
1425 safe_query("UPDATE Hardware SET Port=0, SerialPort='%q' WHERE (ID=%d)",
1426 szSerialPort, hwId);
1427 }
1428 }
1429 }
1430 }
1431 if (dbversion < 70)
1432 {
1433 query("ALTER TABLE [WOLNodes] ADD COLUMN [Timeout] INTEGER DEFAULT 5");
1434 }
1435 if (dbversion < 71)
1436 {
1437 //Dropping debug cleanup triggers
1438 query("DROP TRIGGER IF EXISTS onTemperatureDelete");
1439 query("DROP TRIGGER IF EXISTS onRainDelete");
1440 query("DROP TRIGGER IF EXISTS onWindDelete");
1441 query("DROP TRIGGER IF EXISTS onUVDelete");
1442 query("DROP TRIGGER IF EXISTS onMeterDelete");
1443 query("DROP TRIGGER IF EXISTS onMultiMeterDelete");
1444 query("DROP TRIGGER IF EXISTS onPercentageDelete");
1445 query("DROP TRIGGER IF EXISTS onFanDelete");
1446 }
1447 if (dbversion < 72)
1448 {
1449 query("ALTER TABLE [Notifications] ADD COLUMN [SendAlways] INTEGER DEFAULT 0");
1450 }
1451 if (dbversion < 73)
1452 {
1453 if (!DoesColumnExistsInTable("Description", "DeviceStatus"))
1454 {
1455 query("ALTER TABLE DeviceStatus ADD COLUMN [Description] VARCHAR(200) DEFAULT ''");
1456 }
1457 query("ALTER TABLE Scenes ADD COLUMN [Description] VARCHAR(200) DEFAULT ''");
1458 }
1459 if (dbversion < 74)
1460 {
1461 if (!DoesColumnExistsInTable("Description", "DeviceStatus"))
1462 {
1463 query("ALTER TABLE DeviceStatus ADD COLUMN [Description] VARCHAR(200) DEFAULT ''");
1464 }
1465 }
1466 if (dbversion < 75)
1467 {
1468 safe_query("UPDATE Hardware SET Username='%q', Password='%q' WHERE ([Type]=%d)",
1469 "Change_user_pass", "", HTYPE_THERMOSMART);
1470 if (!DoesColumnExistsInTable("Description", "DeviceStatus"))
1471 {
1472 query("ALTER TABLE DeviceStatus ADD COLUMN [Description] VARCHAR(200) DEFAULT ''");
1473 }
1474 }
1475 if (dbversion < 76)
1476 {
1477 if (!DoesColumnExistsInTable("Name", "MySensorsChilds"))
1478 {
1479 query("ALTER TABLE MySensorsChilds ADD COLUMN [Name] VARCHAR(100) DEFAULT ''");
1480 }
1481 if (!DoesColumnExistsInTable("UseAck", "MySensorsChilds"))
1482 {
1483 query("ALTER TABLE MySensorsChilds ADD COLUMN [UseAck] INTEGER DEFAULT 0");
1484 }
1485 }
1486 if (dbversion < 77)
1487 {
1488 //Simplify Scenes table, and add support for multiple activators
1489 query("ALTER TABLE Scenes ADD COLUMN [Activators] VARCHAR(200) DEFAULT ''");
1490 std::vector<std::vector<std::string> > result, result2;
1491 result = safe_query("SELECT ID, HardwareID, DeviceID, Unit, [Type], SubType, SceneType, ListenCmd FROM Scenes");
1492 if (!result.empty())
1493 {
1494 for (const auto& itt : result)
1495 {
1496 std::vector<std::string> sd = itt;
1497 std::string Activator("");
1498 result2 = safe_query("SELECT ID FROM DeviceStatus WHERE (HardwareID==%q) AND (DeviceID=='%q') AND (Unit==%q) AND ([Type]==%q) AND (SubType==%q)",
1499 sd[1].c_str(), sd[2].c_str(), sd[3].c_str(), sd[4].c_str(), sd[5].c_str());
1500 if (!result2.empty())
1501 {
1502 Activator = result2[0][0];
1503 if (sd[6] == "0") { //Scene
1504 Activator += ":" + sd[7];
1505 }
1506 }
1507 safe_query("UPDATE Scenes SET Activators='%q' WHERE (ID==%q)", Activator.c_str(), sd[0].c_str());
1508 }
1509 }
1510 //create a backup
1511 query("ALTER TABLE Scenes RENAME TO tmp_Scenes");
1512 //Create the new table
1513 query(sqlCreateScenes);
1514 //Copy values from tmp_Scenes back into our new table
1515 query(
1516 "INSERT INTO Scenes ([ID],[Name],[Favorite],[Order],[nValue],[SceneType],[LastUpdate],[Protected],[OnAction],[OffAction],[Description],[Activators])"
1517 "SELECT [ID],[Name],[Favorite],[Order],[nValue],[SceneType],[LastUpdate],[Protected],[OnAction],[OffAction],[Description],[Activators] FROM tmp_Scenes");
1518 //Drop the tmp table
1519 query("DROP TABLE tmp_Scenes");
1520 }
1521 if (dbversion < 78)
1522 {
1523 //Patch for soil moisture to use large ID
1524 result = safe_query("SELECT ID, DeviceID FROM DeviceStatus WHERE (Type=%d) AND (SubType=%d)", pTypeGeneral, sTypeSoilMoisture);
1525 if (!result.empty())
1526 {
1527 for (const auto& itt : result)
1528 {
1529 std::vector<std::string> sd = itt;
1530 std::string idx = sd[0];
1531 int lid = atoi(sd[1].c_str());
1532 char szTmp[20];
1533 sprintf(szTmp, "%08X", lid);
1534 safe_query("UPDATE DeviceStatus SET DeviceID='%q' WHERE (ID='%q')", szTmp, idx.c_str());
1535 }
1536 }
1537 }
1538 if (dbversion < 79)
1539 {
1540 //MQTT filename for ca file
1541 query("ALTER TABLE Hardware ADD COLUMN [Extra] VARCHAR(200) DEFAULT ('')");
1542 }
1543 if (dbversion < 81)
1544 {
1545 // MQTT set default mode
1546 std::stringstream szQuery2;
1547 szQuery2 << "UPDATE Hardware SET Mode1=1 WHERE ([Type]==" << HTYPE_MQTT << " )";
1548 query(szQuery2.str());
1549 }
1550 if (dbversion < 82)
1551 {
1552 //pTypeEngery sensor to new kWh sensor
1553 std::vector<std::vector<std::string> > result2;
1554 result2 = safe_query("SELECT ID, DeviceID FROM DeviceStatus WHERE ([Type] = %d)", pTypeENERGY);
1555 for (const auto& itt2 : result2)
1556 {
1557 std::vector<std::string> sd2 = itt2;
1558
1559 //Change type to new sensor, and update ID
1560 int oldID = atoi(sd2[1].c_str());
1561 char szTmp[20];
1562 sprintf(szTmp, "%08X", oldID);
1563 safe_query("UPDATE DeviceStatus SET [DeviceID]='%s', [Type]=%d, [SubType]=%d, [Unit]=1 WHERE (ID==%s)", szTmp, pTypeGeneral, sTypeKwh, sd2[0].c_str());
1564
1565 //meter table
1566 safe_query("UPDATE Meter SET Value=Value/100, Usage=Usage*10 WHERE DeviceRowID=%s", sd2[0].c_str());
1567 //meter_calendar table
1568 safe_query("UPDATE Meter_Calendar SET Value=Value/100, Counter=Counter/100 WHERE (DeviceRowID==%s)", sd2[0].c_str());
1569 }
1570 }
1571 if (dbversion < 83)
1572 {
1573 //Add hardware monitor as normal hardware class (if not already added)
1574 std::vector<std::vector<std::string> > result;
1575 result = safe_query("SELECT ID FROM Hardware WHERE (Type==%d)", HTYPE_System);
1576 if (result.empty())
1577 {
1578 m_sql.safe_query("INSERT INTO Hardware (Name, Enabled, Type, Address, Port, Username, Password, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6) VALUES ('Motherboard',1, %d,'',1,'','',0,0,0,0,0,0)", HTYPE_System);
1579 }
1580 }
1581 if (dbversion < 85)
1582 {
1583 //MySensors, default use ACK for Childs
1584 safe_query("UPDATE MySensorsChilds SET[UseAck] = 1 WHERE(ChildID != 255)");
1585 }
1586 if (dbversion < 86)
1587 {
1588 //MySensors add Name field
1589 query("ALTER TABLE MySensors ADD COLUMN [Name] VARCHAR(100) DEFAULT ''");
1590 safe_query("UPDATE MySensors SET [Name] = [SketchName]");
1591 }
1592 if (dbversion < 87)
1593 {
1594 //MySensors change waterflow percentage sensor to a real waterflow sensor
1595 std::stringstream szQuery;
1596 std::vector<std::vector<std::string> > result;
1597 szQuery << "SELECT HardwareID,NodeID,ChildID FROM MySensorsChilds WHERE ([Type]==" << 21 << ")";
1598 result = query(szQuery.str());
1599 for (const auto& itt : result)
1600 {
1601 std::vector<std::string> sd = itt;
1602 int hwid = atoi(sd[0].c_str());
1603 int nodeid = atoi(sd[1].c_str());
1604 //int childid = atoi(sd[2].c_str());
1605
1606 szQuery.clear();
1607 szQuery.str("");
1608
1609 char szID[20];
1610 sprintf(szID, "%08X", nodeid);
1611
1612 szQuery << "UPDATE DeviceStatus SET SubType=" << sTypeWaterflow << " WHERE ([Type]=" << pTypeGeneral << ") AND (SubType=" << sTypePercentage << ") AND (HardwareID=" << hwid << ") AND (DeviceID='" << szID << "')";
1613 query(szQuery.str());
1614 }
1615 }
1616 if (dbversion < 88)
1617 {
1618 query("ALTER TABLE DeviceStatus ADD COLUMN [Options] VARCHAR(1024) DEFAULT null");
1619 }
1620 if (dbversion < 89)
1621 {
1622 std::stringstream szQuery;
1623 szQuery << "UPDATE DeviceStatus SET [DeviceID]='0' || DeviceID WHERE ([Type]=" << pTypeGeneralSwitch << ") AND (SubType=" << sSwitchTypeSelector << ") AND length(DeviceID) = 7";
1624 query(szQuery.str());
1625 }
1626 if (dbversion < 90)
1627 {
1628 if (!DoesColumnExistsInTable("Interpreter", "EventMaster"))
1629 {
1630 query("ALTER TABLE EventMaster ADD COLUMN [Interpreter] VARCHAR(10) DEFAULT 'Blockly'");
1631 }
1632 if (!DoesColumnExistsInTable("Type", "EventMaster"))
1633 {
1634 query("ALTER TABLE EventMaster ADD COLUMN [Type] VARCHAR(10) DEFAULT 'All'");
1635 }
1636 }
1637 if (dbversion < 91)
1638 {
1639 //Add DomoticzInternal as normal hardware class (if not already added)
1640 int hwdID = -1;
1641 std::string securityPanelDeviceID = "148702"; // 0x00148702
1642 std::vector<std::vector<std::string> > result;
1643 result = safe_query("SELECT ID FROM Hardware WHERE (Type==%d)", HTYPE_DomoticzInternal);
1644 if (result.empty())
1645 {
1646 m_sql.safe_query("INSERT INTO Hardware (Name, Enabled, Type, Address, Port, Username, Password, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6) VALUES ('Domoticz Internal',1, %d,'',1,'','',0,0,0,0,0,0)", HTYPE_DomoticzInternal);
1647 result = safe_query("SELECT ID FROM Hardware WHERE (Type==%d)", HTYPE_DomoticzInternal);
1648 }
1649 if (!result.empty())
1650 {
1651 hwdID = atoi(result[0][0].c_str());
1652 }
1653 if (hwdID > 0)
1654 {
1655 // Update HardwareID for Security Panel device
1656 int oldHwdID = 1000;
1657 result = safe_query("SELECT ID FROM DeviceStatus WHERE (HardwareID==%d) AND (DeviceID='%q') AND (Type=%d) AND (SubType=%d)", oldHwdID, securityPanelDeviceID.c_str(), pTypeSecurity1, sTypeDomoticzSecurity);
1658 if (!result.empty())
1659 {
1660 m_sql.safe_query("UPDATE DeviceStatus SET HardwareID=%d WHERE (HardwareID==%d) AND (DeviceID='%q') AND (Type=%d) AND (SubType=%d)", hwdID, oldHwdID, securityPanelDeviceID.c_str(), pTypeSecurity1, sTypeDomoticzSecurity);
1661 }
1662 // Update Name for Security Panel device
1663 result = safe_query("SELECT ID FROM DeviceStatus WHERE (HardwareID==%d) AND (DeviceID='%q') AND (Type=%d) AND (SubType=%d) AND Name='Unknown'", hwdID, securityPanelDeviceID.c_str(), pTypeSecurity1, sTypeDomoticzSecurity);
1664 if (!result.empty())
1665 {
1666 m_sql.safe_query("UPDATE DeviceStatus SET Name='Domoticz Security Panel' WHERE (HardwareID==%d) AND (DeviceID='%q') AND (Type=%d) AND (SubType=%d) AND Name='Unknown'", hwdID, securityPanelDeviceID.c_str(), pTypeSecurity1, sTypeDomoticzSecurity);
1667 }
1668 }
1669 }
1670 if (dbversion < 92)
1671 {
1672 // Change DeviceStatus.Options datatype from VARCHAR(1024) to TEXT
1673 std::string tableName = "DeviceStatus";
1674 std::string fieldList = "[ID],[HardwareID],[DeviceID],[Unit],[Name],[Used],[Type],[SubType],[SwitchType],[Favorite],[SignalLevel],[BatteryLevel],[nValue],[sValue],[LastUpdate],[Order],[AddjValue],[AddjMulti],[AddjValue2],[AddjMulti2],[StrParam1],[StrParam2],[LastLevel],[Protected],[CustomImage],[Description],[Options]";
1675 std::stringstream szQuery;
1676
1677 sqlite3_exec(m_dbase, "PRAGMA foreign_keys=off", NULL, NULL, NULL);
1678 sqlite3_exec(m_dbase, "BEGIN TRANSACTION", NULL, NULL, NULL);
1679
1680 // Drop indexes and trigger
1681 safe_query("DROP TRIGGER IF EXISTS devicestatusupdate");
1682 // Save all table rows
1683 szQuery.clear();
1684 szQuery.str("");
1685 szQuery << "ALTER TABLE " << tableName << " RENAME TO _" << tableName << "_old";
1686 safe_query(szQuery.str().c_str());
1687 // Create new table
1688 safe_query(sqlCreateDeviceStatus);
1689 // Restore all table rows
1690 szQuery.clear();
1691 szQuery.str("");
1692 szQuery << "INSERT INTO " << tableName << " (" << fieldList << ") SELECT " << fieldList << " FROM _" << tableName << "_old";
1693 safe_query(szQuery.str().c_str());
1694 // Restore indexes and triggers
1695 safe_query(sqlCreateDeviceStatusTrigger);
1696 // Delete old table
1697 szQuery.clear();
1698 szQuery.str("");
1699 szQuery << "DROP TABLE IF EXISTS _" << tableName << "_old";
1700 safe_query(szQuery.str().c_str());
1701
1702 sqlite3_exec(m_dbase, "END TRANSACTION", NULL, NULL, NULL);
1703 sqlite3_exec(m_dbase, "PRAGMA foreign_keys=on", NULL, NULL, NULL);
1704 }
1705 if (dbversion < 93)
1706 {
1707 if (!DoesColumnExistsInTable("Month", "Timers"))
1708 {
1709 query("ALTER TABLE Timers ADD COLUMN [Month] INTEGER DEFAULT 0");
1710 }
1711 if (!DoesColumnExistsInTable("MDay", "Timers"))
1712 {
1713 query("ALTER TABLE Timers ADD COLUMN [MDay] INTEGER DEFAULT 0");
1714 }
1715 if (!DoesColumnExistsInTable("Occurence", "Timers"))
1716 {
1717 query("ALTER TABLE Timers ADD COLUMN [Occurence] INTEGER DEFAULT 0");
1718 }
1719 if (!DoesColumnExistsInTable("Month", "SceneTimers"))
1720 {
1721 query("ALTER TABLE SceneTimers ADD COLUMN [Month] INTEGER DEFAULT 0");
1722 }
1723 if (!DoesColumnExistsInTable("MDay", "SceneTimers"))
1724 {
1725 query("ALTER TABLE SceneTimers ADD COLUMN [MDay] INTEGER DEFAULT 0");
1726 }
1727 if (!DoesColumnExistsInTable("Occurence", "SceneTimers"))
1728 {
1729 query("ALTER TABLE SceneTimers ADD COLUMN [Occurence] INTEGER DEFAULT 0");
1730 }
1731 }
1732 if (dbversion < 94)
1733 {
1734 std::stringstream szQuery;
1735 szQuery << "UPDATE Timers SET [Type]=[Type]+2 WHERE ([Type]>" << TTYPE_FIXEDDATETIME << ")";
1736 query(szQuery.str());
1737 szQuery.clear();
1738 szQuery.str("");
1739 szQuery << "UPDATE SceneTimers SET [Type]=[Type]+2 WHERE ([Type]>" << TTYPE_FIXEDDATETIME << ")";
1740 query(szQuery.str());
1741 }
1742 if (dbversion < 95)
1743 {
1744 if (!DoesColumnExistsInTable("Month", "SetpointTimers"))
1745 {
1746 query("ALTER TABLE SetpointTimers ADD COLUMN [Month] INTEGER DEFAULT 0");
1747 }
1748 if (!DoesColumnExistsInTable("MDay", "SetpointTimers"))
1749 {
1750 query("ALTER TABLE SetpointTimers ADD COLUMN [MDay] INTEGER DEFAULT 0");
1751 }
1752 if (!DoesColumnExistsInTable("Occurence", "SetpointTimers"))
1753 {
1754 query("ALTER TABLE SetpointTimers ADD COLUMN [Occurence] INTEGER DEFAULT 0");
1755 }
1756 }
1757 if (dbversion < 96)
1758 {
1759 if (!DoesColumnExistsInTable("Name", "MobileDevices"))
1760 {
1761 query("ALTER TABLE MobileDevices ADD COLUMN [Name] VARCHAR(100) DEFAULT ''");
1762 }
1763 }
1764 if (dbversion < 97)
1765 {
1766 //Patch for pTypeLighting2/sTypeZWaveSwitch to pTypeGeneralSwitch/sSwitchGeneralSwitch
1767 std::stringstream szQuery, szQuery2;
1768 std::vector<std::vector<std::string> > result, result2;
1769 std::vector<std::string> sd;
1770 result = query("SELECT ID FROM Hardware WHERE ([Type] = 9) OR ([Type] = 21)");
1771 if (!result.empty())
1772 {
1773 for (const auto& itt : result)
1774 {
1775 sd = itt;
1776 szQuery.clear();
1777 szQuery.str("");
1778 szQuery << "SELECT ID, DeviceID FROM DeviceStatus WHERE ([Type]=" << pTypeLighting2 << ") AND (SubType=" << 0xA1 << ") AND (HardwareID=" << sd[0] << ")";
1779 result2 = query(szQuery.str());
1780 if (!result2.empty())
1781 {
1782 for (const auto& itt2 : result2)
1783 {
1784 sd = itt2;
1785 std::string ndeviceid = "0" + sd[1];
1786 szQuery2.clear();
1787 szQuery2.str("");
1788 //#define sTypeZWaveSwitch 0xA1
1789 szQuery2 << "UPDATE DeviceStatus SET DeviceID='" << ndeviceid << "', [Type]=" << pTypeGeneralSwitch << ", SubType=" << sSwitchGeneralSwitch << " WHERE (ID=" << sd[0] << ")";
1790 query(szQuery2.str());
1791 }
1792 }
1793 }
1794 }
1795 }
1796 if (dbversion < 98)
1797 {
1798 // Shorthen cookies validity to 30 days
1799 query("UPDATE UserSessions SET ExpirationDate = datetime(ExpirationDate, '-335 days')");
1800 }
1801 if (dbversion < 99)
1802 {
1803 //Convert depricated CounterType 'Time' to type Counter with options ValueQuantity='Time' & ValueUnits='Min'
1804 //Add options ValueQuantity='Count' to existing CounterType 'Counter'
1805 const unsigned char charNTYPE_TODAYTIME = 'm';
1806 std::stringstream szQuery, szQuery1, szQuery2, szQuery3;
1807 std::vector<std::vector<std::string> > result, result1;
1808 std::vector<std::string> sd;
1809 szQuery.clear();
1810 szQuery.str("");
1811 szQuery << "SELECT ID FROM Hardware"
1812 " WHERE (([Type] = " << HTYPE_Dummy << ") OR ([Type] = " << HTYPE_YouLess << "))";
1813 result = query(szQuery.str());
1814 if (!result.empty())
1815 {
1816 for (const auto& itt : result)
1817 {
1818 sd = itt;
1819 szQuery1.clear();
1820 szQuery1.str("");
1821 szQuery1 << "SELECT ID, DeviceID, SwitchType FROM DeviceStatus"
1822 " WHERE ((([Type]=" << pTypeRFXMeter << ") AND (SubType=" << sTypeRFXMeterCount << "))"
1823 " OR (([Type]=" << pTypeGeneral << ") AND (SubType=" << sTypeCounterIncremental << "))"
1824 " OR ([Type]=" << pTypeYouLess << "))"
1825 " AND ((SwitchType=" << MTYPE_COUNTER << ") OR (SwitchType=" << MTYPE_TIME << "))"
1826 " AND (HardwareID=" << sd[0] << ")";
1827 result1 = query(szQuery1.str());
1828 if (!result1.empty())
1829 {
1830 for (const auto& itt2 : result1)
1831 {
1832 sd = itt2;
1833 uint64_t devidx = atoi(sd[0].c_str());
1834 _eMeterType switchType = (_eMeterType)atoi(sd[2].c_str());
1835
1836 if (switchType == MTYPE_COUNTER)
1837 {
1838 //Add options to existing SwitchType 'Counter'
1839 m_sql.SetDeviceOptions(devidx, m_sql.BuildDeviceOptions("ValueQuantity:Count;ValueUnits:", false));
1840 }
1841 else if (switchType == MTYPE_TIME)
1842 {
1843 //Set default options
1844 m_sql.SetDeviceOptions(devidx, m_sql.BuildDeviceOptions("ValueQuantity:Time;ValueUnits:Min", false));
1845
1846 //Convert to Counter
1847 szQuery2.clear();
1848 szQuery2.str("");
1849 szQuery2 << "UPDATE DeviceStatus"
1850 " SET SwitchType=" << MTYPE_COUNTER << " WHERE (ID=" << devidx << ")";
1851 query(szQuery2.str());
1852
1853 //Update notifications 'Time' -> 'Counter'
1854 szQuery3.clear();
1855 szQuery3.str("");
1856 szQuery3 << "UPDATE Notifications"
1857 " SET Params=REPLACE(Params, '" << charNTYPE_TODAYTIME << ";', '" << Notification_Type_Desc(NTYPE_TODAYCOUNTER, 1) << ";')"
1858 " WHERE (DeviceRowID=" << devidx << ")";
1859 query(szQuery3.str());
1860 }
1861 }
1862 }
1863 }
1864 }
1865 }
1866 if (dbversion < 100)
1867 {
1868 //Convert temperature sensor type sTypeTEMP10 to sTypeTEMP5 for specified hardware classes
1869 std::stringstream szQuery, szQuery2;
1870 std::vector<std::vector<std::string> > result;
1871 std::vector<std::string> sd;
1872 szQuery << "SELECT ID FROM Hardware WHERE ( "
1873 << "([Type] = " << HTYPE_OpenThermGateway << ") OR "
1874 << "([Type] = " << HTYPE_OpenThermGatewayTCP << ") OR "
1875 << "([Type] = " << HTYPE_DavisVantage << ") OR "
1876 << "([Type] = " << HTYPE_System << ") OR "
1877 << "([Type] = " << HTYPE_ICYTHERMOSTAT << ") OR "
1878 << "([Type] = " << HTYPE_Meteostick << ") OR "
1879 << "([Type] = " << HTYPE_PVOUTPUT_INPUT << ") OR "
1880 << "([Type] = " << HTYPE_SBFSpot << ") OR "
1881 << "([Type] = " << HTYPE_SolarEdgeTCP << ") OR "
1882 << "([Type] = " << HTYPE_TE923 << ") OR "
1883 << "([Type] = " << HTYPE_TOONTHERMOSTAT << ") OR "
1884 << "([Type] = " << HTYPE_Wunderground << ") OR "
1885 << "([Type] = " << HTYPE_DarkSky << ") OR "
1886 << "([Type] = " << HTYPE_AccuWeather << ") OR "
1887 << "([Type] = " << HTYPE_OpenZWave << ")"
1888 << ")";
1889 result = query(szQuery.str());
1890 if (!result.empty())
1891 {
1892 for (const auto& itt : result)
1893 {
1894 sd = itt;
1895 szQuery2.clear();
1896 szQuery2.str("");
1897 szQuery2 << "UPDATE DeviceStatus SET SubType=" << sTypeTEMP5 << " WHERE ([Type]==" << pTypeTEMP << ") AND (SubType==" << sTypeTEMP10 << ") AND (HardwareID=" << sd[0] << ")";
1898 query(szQuery2.str());
1899 }
1900 }
1901 }
1902 if (dbversion < 101)
1903 {
1904 //Convert TempHum sensor type sTypeTH2 to sTypeHUM1 for specified hardware classes
1905 std::stringstream szQuery, szQuery2;
1906 std::vector<std::vector<std::string> > result;
1907 std::vector<std::string> sd;
1908 szQuery << "SELECT ID FROM Hardware WHERE ( "
1909 << "([Type] = " << HTYPE_DavisVantage << ") OR "
1910 << "([Type] = " << HTYPE_TE923 << ") OR "
1911 << "([Type] = " << HTYPE_OpenZWave << ")"
1912 << ")";
1913 result = query(szQuery.str());
1914 if (!result.empty())
1915 {
1916 for (const auto& itt : result)
1917 {
1918 sd = itt;
1919 szQuery2.clear();
1920 szQuery2.str("");
1921 szQuery2 << "UPDATE DeviceStatus SET SubType=" << sTypeHUM1 << " WHERE ([Type]==" << pTypeHUM << ") AND (SubType==" << sTypeTH2 << ") AND (HardwareID=" << sd[0] << ")";
1922 query(szQuery2.str());
1923 }
1924 }
1925 }
1926 if (dbversion < 102)
1927 {
1928 // Remove old indexes
1929 query("drop index if exists f_idx;");
1930 query("drop index if exists fc_idx;");
1931 query("drop index if exists l_idx;");
1932 query("drop index if exists s_idx;");
1933 query("drop index if exists m_idx;");
1934 query("drop index if exists mc_idx;");
1935 query("drop index if exists mm_idx;");
1936 query("drop index if exists mmc_idx;");
1937 query("drop index if exists p_idx;");
1938 query("drop index if exists pc_idx;");
1939 query("drop index if exists r_idx;");
1940 query("drop index if exists rc_idx;");
1941 query("drop index if exists t_idx;");
1942 query("drop index if exists tc_idx;");
1943 query("drop index if exists u_idx;");
1944 query("drop index if exists uv_idx;");
1945 query("drop index if exists w_idx;");
1946 query("drop index if exists wc_idx;");
1947 // Add new indexes
1948 query("create index if not exists ds_hduts_idx on DeviceStatus(HardwareID, DeviceID, Unit, Type, SubType);");
1949 query("create index if not exists f_id_idx on Fan(DeviceRowID);");
1950 query("create index if not exists f_id_date_idx on Fan(DeviceRowID, Date);");
1951 query("create index if not exists fc_id_idx on Fan_Calendar(DeviceRowID);");
1952 query("create index if not exists fc_id_date_idx on Fan_Calendar(DeviceRowID, Date);");
1953 query("create index if not exists ll_id_idx on LightingLog(DeviceRowID);");
1954 query("create index if not exists ll_id_date_idx on LightingLog(DeviceRowID, Date);");
1955 query("create index if not exists sl_id_idx on SceneLog(SceneRowID);");
1956 query("create index if not exists sl_id_date_idx on SceneLog(SceneRowID, Date);");
1957 query("create index if not exists m_id_idx on Meter(DeviceRowID);");
1958 query("create index if not exists m_id_date_idx on Meter(DeviceRowID, Date);");
1959 query("create index if not exists mc_id_idx on Meter_Calendar(DeviceRowID);");
1960 query("create index if not exists mc_id_date_idx on Meter_Calendar(DeviceRowID, Date);");
1961 query("create index if not exists mm_id_idx on MultiMeter(DeviceRowID);");
1962 query("create index if not exists mm_id_date_idx on MultiMeter(DeviceRowID, Date);");
1963 query("create index if not exists mmc_id_idx on MultiMeter_Calendar(DeviceRowID);");
1964 query("create index if not exists mmc_id_date_idx on MultiMeter_Calendar(DeviceRowID, Date);");
1965 query("create index if not exists p_id_idx on Percentage(DeviceRowID);");
1966 query("create index if not exists p_id_date_idx on Percentage(DeviceRowID, Date);");
1967 query("create index if not exists pc_id_idx on Percentage_Calendar(DeviceRowID);");
1968 query("create index if not exists pc_id_date_idx on Percentage_Calendar(DeviceRowID, Date);");
1969 query("create index if not exists r_id_idx on Rain(DeviceRowID);");
1970 query("create index if not exists r_id_date_idx on Rain(DeviceRowID, Date);");
1971 query("create index if not exists rc_id_idx on Rain_Calendar(DeviceRowID);");
1972 query("create index if not exists rc_id_date_idx on Rain_Calendar(DeviceRowID, Date);");
1973 query("create index if not exists t_id_idx on Temperature(DeviceRowID);");
1974 query("create index if not exists t_id_date_idx on Temperature(DeviceRowID, Date);");
1975 query("create index if not exists tc_id_idx on Temperature_Calendar(DeviceRowID);");
1976 query("create index if not exists tc_id_date_idx on Temperature_Calendar(DeviceRowID, Date);");
1977 query("create index if not exists u_id_idx on UV(DeviceRowID);");
1978 query("create index if not exists u_id_date_idx on UV(DeviceRowID, Date);");
1979 query("create index if not exists uv_id_idx on UV_Calendar(DeviceRowID);");
1980 query("create index if not exists uv_id_date_idx on UV_Calendar(DeviceRowID, Date);");
1981 query("create index if not exists w_id_idx on Wind(DeviceRowID);");
1982 query("create index if not exists w_id_date_idx on Wind(DeviceRowID, Date);");
1983 query("create index if not exists wc_id_idx on Wind_Calendar(DeviceRowID);");
1984 query("create index if not exists wc_id_date_idx on Wind_Calendar(DeviceRowID, Date);");
1985 }
1986 if (dbversion < 103)
1987 {
1988 FixDaylightSaving();
1989 }
1990 if (dbversion < 104)
1991 {
1992 //S0 Meter patch
1993 std::stringstream szQuery2;
1994 std::vector<std::vector<std::string> > result;
1995 szQuery2 << "SELECT ID, Address FROM Hardware WHERE([Type]==" << HTYPE_S0SmartMeterUSB << ")";
1996 result = query(szQuery2.str());
1997 if (!result.empty())
1998 {
1999 for (const auto& itt : result)
2000 {
2001 std::vector<std::string> sd = itt;
2002 szQuery2.clear();
2003 szQuery2.str("");
2004 szQuery2 << "UPDATE Hardware SET Extra='" << sd[1] << "', Address='' WHERE (ID=" << sd[0] << ")";
2005 query(szQuery2.str());
2006 }
2007 }
2008 }
2009 if (dbversion < 105)
2010 {
2011 if (!DoesColumnExistsInTable("AckTimeout", "MySensorsChilds"))
2012 {
2013 query("ALTER TABLE MySensorsChilds ADD COLUMN [AckTimeout] INTEGER DEFAULT 1200");
2014 }
2015 }
2016 if (dbversion < 106)
2017 {
2018 //Adjust Limited device id's to uppercase HEX
2019 std::stringstream szQuery2;
2020 std::vector<std::vector<std::string> > result;
2021 szQuery2 << "SELECT ID, DeviceID FROM DeviceStatus WHERE([Type]==" << pTypeColorSwitch << ")";
2022 result = query(szQuery2.str());
2023 if (!result.empty())
2024 {
2025 for (const auto& itt : result)
2026 {
2027 std::vector<std::string> sd = itt;
2028 szQuery2.clear();
2029 szQuery2.str("");
2030 uint32_t lID;
2031 std::stringstream s_strid;
2032 s_strid << std::hex << sd[1];
2033 s_strid >> lID;
2034
2035 if (lID > 9)
2036 {
2037 char szTmp[10];
2038 sprintf(szTmp, "%08X", lID);
2039 szQuery2 << "UPDATE DeviceStatus SET DeviceID='" << szTmp << "' WHERE (ID=" << sd[0] << ")";
2040 query(szQuery2.str());
2041 }
2042 }
2043 }
2044 }
2045 if (dbversion < 107)
2046 {
2047 if (!DoesColumnExistsInTable("User", "LightingLog"))
2048 {
2049 query("ALTER TABLE LightingLog ADD COLUMN [User] VARCHAR(100) DEFAULT ('')");
2050 }
2051 }
2052 if (dbversion < 108)
2053 {
2054 //Fix possible HTTP notifier problem
2055 std::string sValue;
2056 GetPreferencesVar("HTTPPostContentType", sValue);
2057 if ((sValue.size() > 100) || (sValue.empty()))
2058 {
2059 sValue = "application/json";
2060 std::string sencoded = base64_encode(sValue);
2061 UpdatePreferencesVar("HTTPPostContentType", sencoded);
2062 }
2063 }
2064 if (dbversion < 109)
2065 {
2066 query("INSERT INTO TimerPlans (ID, Name) VALUES (0, 'default')");
2067 query("INSERT INTO TimerPlans (ID, Name) VALUES (1, 'Holiday')");
2068 }
2069 if (dbversion < 110)
2070 {
2071 query("ALTER TABLE Hardware RENAME TO tmp_Hardware;");
2072 query(sqlCreateHardware);
2073 query("INSERT INTO Hardware(ID, Name, Enabled, Type, Address, Port, SerialPort, Username, Password, Extra, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6, DataTimeout) SELECT ID, Name, Enabled, Type, Address, Port, SerialPort, Username, Password, Extra, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6, DataTimeout FROM tmp_Hardware;");
2074 query("DROP TABLE tmp_Hardware;");
2075
2076 result = safe_query("SELECT ID, Extra FROM Hardware WHERE Type=%d", HTYPE_HTTPPOLLER);
2077 if (!result.empty())
2078 {
2079 std::stringstream szQuery2;
2080 for (const auto& itt : result)
2081 {
2082 std::vector<std::string> sd = itt;
2083 std::string id = sd[0];
2084 std::string extra = sd[1];
2085 std::string extraBase64 = base64_encode(extra);
2086 szQuery2.clear();
2087 szQuery2.str("");
2088 szQuery2 << "UPDATE Hardware SET Mode1=0, Extra='%s' WHERE (ID=" << id << ")";
2089 safe_query(szQuery2.str().c_str(), extraBase64.c_str());
2090 }
2091 }
2092 }
2093 if (dbversion < 111)
2094 {
2095 //SolarEdge API, no need for Serial Number anymore
2096 std::stringstream szQuery2;
2097 std::vector<std::vector<std::string> > result;
2098 szQuery2 << "SELECT ID, Password FROM Hardware WHERE([Type]==" << HTYPE_SolarEdgeAPI << ")";
2099 result = query(szQuery2.str());
2100 if (!result.empty())
2101 {
2102 for (const auto& itt : result)
2103 {
2104 std::vector<std::string> sd = itt;
2105 safe_query("UPDATE Hardware SET Username='%q', Password='' WHERE (ID=%s)", sd[1].c_str(), sd[0].c_str());
2106 }
2107 }
2108 }
2109 if (dbversion < 112)
2110 {
2111 //Fix for MySensor general switch with wrong subtype
2112 std::stringstream szQuery2;
2113 std::vector<std::vector<std::string> > result;
2114 szQuery2 << "SELECT ID FROM Hardware WHERE ([Type]==" << HTYPE_MySensorsTCP << ") OR ([Type]==" << HTYPE_MySensorsUSB << ")";
2115 result = query(szQuery2.str());
2116 if (!result.empty())
2117 {
2118 for (const auto& itt : result)
2119 {
2120 std::vector<std::string> sd = itt;
2121 szQuery2.clear();
2122 szQuery2.str("");
2123 szQuery2 << "UPDATE DeviceStatus SET SubType=" << sSwitchTypeAC << " WHERE ([Type]==" << pTypeGeneralSwitch << ") AND (SubType==" << sTypeAC << ") AND (HardwareID=" << sd[0] << ")";
2124 query(szQuery2.str());
2125 }
2126 }
2127 }
2128 if (dbversion < 113)
2129 {
2130 //Patch for new 1-Wire subtypes
2131 std::stringstream szQuery2;
2132 std::vector<std::vector<std::string> > result;
2133 szQuery2 << "SELECT ID FROM Hardware WHERE([Type]==" << HTYPE_1WIRE << ")";
2134 result = query(szQuery2.str());
2135 if (!result.empty())
2136 {
2137 for (const auto& itt : result)
2138 {
2139 std::vector<std::string> sd = itt;
2140 safe_query("UPDATE DeviceStatus SET SubType='%d' WHERE ([Type]==%d) AND (SubType==%d) AND (HardwareID=%s)",
2141 sTypeTEMP5, pTypeTEMP, sTypeTEMP10, sd[0].c_str());
2142
2143 safe_query("UPDATE DeviceStatus SET SubType='%d' WHERE ([Type]==%d) AND (SubType==%d) AND (HardwareID=%s)",
2144 sTypeHUM1, pTypeHUM, sTypeHUM2, sd[0].c_str());
2145 }
2146 }
2147 }
2148 if (dbversion < 114)
2149 {
2150 //Set default values for new parameters in EcoDevices and Teleinfo EDF
2151 std::stringstream szQuery1, szQuery2;
2152 szQuery1 << "UPDATE Hardware SET Mode1 = 0, Mode2 = 60 WHERE Type =" << HTYPE_ECODEVICES;
2153 query(szQuery1.str());
2154 szQuery2 << "UPDATE Hardware SET Mode1 = 0, Mode2 = 0, Mode3 = 60 WHERE Type =" << HTYPE_TeleinfoMeter;
2155 query(szQuery2.str());
2156 }
2157 if (dbversion < 115)
2158 {
2159 //Patch for Evohome Web
2160 std::stringstream szQuery2;
2161 std::vector<std::vector<std::string> > result;
2162 szQuery2 << "SELECT ID, Name, Mode1, Mode2, Mode3, Mode4, Mode5 FROM Hardware WHERE([Type]==" << HTYPE_EVOHOME_WEB << ")";
2163 result = query(szQuery2.str());
2164 if (!result.empty())
2165 {
2166 for (const auto& itt : result)
2167 {
2168 std::vector<std::string> sd = itt;
2169 std::string lowerName = "fitbit";
2170 for (uint8_t i = 0; i < 6; i++)
2171 lowerName[i] = sd[1][i] | 0x20;
2172 if (lowerName == "fitbit")
2173 safe_query("DELETE FROM Hardware WHERE ID=%s", sd[0].c_str());
2174 else
2175 {
2176 int newParams = (sd[3] == "0") ? 0 : 1;
2177 if (sd[4] == "1")
2178 newParams += 2;
2179 if (sd[5] == "1")
2180 newParams += 4;
2181 m_sql.safe_query("UPDATE Hardware SET Mode2=%d, Mode3=%s, Mode4=0, Mode5=0 WHERE ID=%s", newParams, sd[6].c_str(), sd[0].c_str());
2182 m_sql.safe_query("UPDATE DeviceStatus SET StrParam1='' WHERE HardwareID=%s", sd[0].c_str());
2183 }
2184 }
2185 }
2186 }
2187 if (dbversion < 116)
2188 {
2189 //Patch for GCM/FCM
2190 safe_query("UPDATE MobileDevices SET Active=1");
2191 if (!DoesColumnExistsInTable("DeviceType", "MobileDevices"))
2192 {
2193 query("ALTER TABLE MobileDevices ADD COLUMN [DeviceType] VARCHAR(100) DEFAULT ('')");
2194 }
2195 }
2196 if (dbversion < 117)
2197 {
2198 //Add Protocol for Camera (HTTP/HTTPS/...)
2199 if (!DoesColumnExistsInTable("Protocol", "Cameras"))
2200 {
2201 query("ALTER TABLE Cameras ADD COLUMN [Protocol] INTEGER DEFAULT 0");
2202 }
2203 }
2204 if (dbversion < 118)
2205 {
2206 //Delete script that could crash domoticz (maybe the dev can have a look?)
2207 std::string sfile = szUserDataFolder + "scripts/python/script_device_PIRsmarter.py";
2208 std::remove(sfile.c_str());
2209 }
2210 if (dbversion < 119)
2211 {
2212 //change Disable Event System to Enable Event System
2213 int nValue = 0;
2214 if (GetPreferencesVar("DisableEventScriptSystem", nValue))
2215 {
2216 UpdatePreferencesVar("EnableEventScriptSystem", !nValue);
2217 }
2218 DeletePreferencesVar("DisableEventScriptSystem");
2219 }
2220 if (dbversion < 120)
2221 {
2222 // remove old dzVents dirs
2223 std::string dzv_Dir, dzv_scripts;
2224 #ifdef WIN32
2225 dzv_Dir = szUserDataFolder + "scripts\\dzVents\\generated_scripts\\";
2226 dzv_scripts = szUserDataFolder + "scripts\\dzVents\\";
2227 #else
2228 dzv_Dir = szUserDataFolder + "scripts/dzVents/generated_scripts/";
2229 dzv_scripts = szUserDataFolder + "scripts/dzVents/";
2230 #endif
2231 const std::string
2232 dzv_rm_Dir1 = dzv_scripts + "runtime",
2233 dzv_rm_Dir2 = dzv_scripts + "documentation";
2234
2235 if ((file_exist(dzv_rm_Dir1.c_str()) || file_exist(dzv_rm_Dir2.c_str())) &&
2236 !szUserDataFolder.empty())
2237 {
2238 std::string errorPath;
2239 if (int returncode = RemoveDir(dzv_rm_Dir1 + "|" + dzv_rm_Dir2, errorPath))
2240 _log.Log(LOG_ERROR, "EventSystem: (%d) Could not remove %s, please remove manually!", returncode, errorPath.c_str());
2241 }
2242 }
2243 if (dbversion < 121)
2244 {
2245 // incorrect call to hardware class from mainworker: move Evohome installation parameters from Mode5 to unused Mode3
2246 std::stringstream szQuery2;
2247 std::vector<std::vector<std::string> > result;
2248 szQuery2 << "SELECT ID, Mode5 FROM Hardware WHERE([Type]==" << HTYPE_EVOHOME_WEB << ")";
2249 result = query(szQuery2.str());
2250 if (!result.empty())
2251 {
2252 for (const auto& itt : result)
2253 {
2254 std::vector<std::string> sd = itt;
2255 safe_query("UPDATE Hardware SET Mode3='%q', Mode5='' WHERE (ID=%s)", sd[1].c_str(), sd[0].c_str());
2256 }
2257 }
2258 }
2259 if (dbversion < 122)
2260 {
2261 //Patch for Darksky ozone sensor
2262 std::stringstream szQuery2;
2263 std::vector<std::vector<std::string> > result;
2264 std::vector<std::vector<std::string> > result2;
2265 std::vector<std::vector<std::string> > result3;
2266 szQuery2 << "SELECT ID FROM Hardware WHERE([Type]==" << HTYPE_DarkSky << ")";
2267 result = query(szQuery2.str());
2268 if (!result.empty())
2269 {
2270 for (const auto& itt : result)
2271 {
2272 std::vector<std::string> sd = itt;
2273 szQuery2.clear();
2274 szQuery2.str("");
2275 szQuery2 << "SELECT ID FROM DeviceStatus WHERE ([Type]=" << (int)pTypeGeneral << ") AND (SubType=" << (int)sTypeSolarRadiation << ") AND (HardwareID=" << sd[0] << ")";
2276 result2 = query(szQuery2.str());
2277
2278 for (const auto& itt2 : result2)
2279 {
2280 sd = itt2;
2281
2282 //Change device type
2283 szQuery2.clear();
2284 szQuery2.str("");
2285 szQuery2 << "UPDATE DeviceStatus SET SubType=" << (int)sTypeCustom << ", DeviceID='00000100', Options='1;DU' WHERE (ID=" << sd[0] << ")";
2286 query(szQuery2.str());
2287
2288 //change log
2289 szQuery2.clear();
2290 szQuery2.str("");
2291 szQuery2 << "SELECT Value, Date FROM Meter WHERE ([DeviceRowID]=" << sd[0] << ")";
2292 result3 = query(szQuery2.str());
2293
2294 for (const auto& itt3 : result3)
2295 {
2296 std::vector<std::string> sd3 = itt3;
2297 szQuery2.clear();
2298 szQuery2.str("");
2299 szQuery2 << "INSERT INTO Percentage (DeviceRowID, Percentage, Date) VALUES (" << sd[0] << ", " << (float)atof(sd3[0].c_str()) / 10.0f << ", '" << sd3[1] << "')";
2300 query(szQuery2.str());
2301 }
2302 szQuery2.clear();
2303 szQuery2.str("");
2304 szQuery2 << "DELETE FROM Meter WHERE ([DeviceRowID]=" << sd[0] << ")";
2305 query(szQuery2.str());
2306
2307 szQuery2.clear();
2308 szQuery2.str("");
2309 szQuery2 << "SELECT Value1, Value2, Value3, Date FROM MultiMeter_Calendar WHERE ([DeviceRowID]=" << sd[0] << ")";
2310 result3 = query(szQuery2.str());
2311
2312 for (const auto& itt3 : result3)
2313 {
2314 std::vector<std::string> sd3 = itt3;
2315 szQuery2.clear();
2316 szQuery2.str("");
2317 float percentage_min = (float)atof(sd3[0].c_str()) / 10.0f;
2318 float percentage_max = (float)atof(sd3[1].c_str()) / 10.0f;
2319 float percentage_avg = (float)atof(sd3[2].c_str()) / 10.0f;
2320 szQuery2 << "INSERT INTO Percentage_Calendar (DeviceRowID, Percentage_Min, Percentage_Max, Percentage_Avg, Date) VALUES (" << sd[0] << ", " << percentage_min << ", " << percentage_max << ", " << percentage_avg << ", '" << sd3[3] << "')";
2321 query(szQuery2.str());
2322 }
2323 szQuery2.clear();
2324 szQuery2.str("");
2325 szQuery2 << "DELETE FROM Meter_Calendar WHERE ([DeviceRowID]=" << sd[0] << ")";
2326 query(szQuery2.str());
2327 }
2328 }
2329 }
2330 }
2331 if (dbversion < 124)
2332 {
2333 query("ALTER TABLE DeviceStatus ADD COLUMN [Color] TEXT DEFAULT NULL");
2334 query("ALTER TABLE Timers ADD COLUMN [Color] TEXT DEFAULT NULL");
2335 query("ALTER TABLE SceneDevices ADD COLUMN [Color] TEXT DEFAULT NULL");
2336
2337 std::stringstream szQuery2;
2338 std::vector<std::vector<std::string> > result;
2339 std::vector<std::vector<std::string> > result2;
2340
2341 //Convert stored Hue in Timers to color
2342 result = query("SELECT ID, Hue FROM Timers WHERE(Hue!=0)");
2343 if (!result.empty())
2344 {
2345 for (const auto& itt : result)
2346 {
2347 std::vector<std::string> sd = itt;
2348
2349 int r, g, b;
2350
2351 //convert hue to RGB
2352 float iHue = float(atof(sd[1].c_str()));
2353 hsb2rgb(iHue, 1.0f, 1.0f, r, g, b, 255);
2354
2355 _tColor color = _tColor(r, g, b, 0, 0, ColorModeRGB);
2356 szQuery2.clear();
2357 szQuery2.str("");
2358 szQuery2 << "UPDATE Timers SET Color='" << color.toJSONString() << "' WHERE (ID=" << sd[0] << ")";
2359 query(szQuery2.str());
2360 }
2361 }
2362
2363 //Convert stored Hue in SceneDevices to color
2364 result = query("SELECT ID, Hue FROM SceneDevices WHERE(Hue!=0)");
2365 if (!result.empty())
2366 {
2367 for (const auto& itt : result)
2368 {
2369 std::vector<std::string> sd = itt;
2370
2371 int r, g, b;
2372
2373 //convert hue to RGB
2374 float iHue = float(atof(sd[1].c_str()));
2375 hsb2rgb(iHue, 1.0f, 1.0f, r, g, b, 255);
2376
2377 _tColor color = _tColor(r, g, b, 0, 0, ColorModeRGB);
2378 szQuery2.clear();
2379 szQuery2.str("");
2380 szQuery2 << "UPDATE SceneDevices SET Color='" << color.toJSONString() << "' WHERE (ID=" << sd[0] << ")";
2381 query(szQuery2.str());
2382 }
2383 }
2384
2385 //Patch for ZWave, change device type from sTypeColor_RGB_W to sTypeColor_RGB_W_Z
2386 szQuery2.clear();
2387 szQuery2.str("");
2388 szQuery2 << "SELECT ID FROM Hardware WHERE([Type]==" << HTYPE_OpenZWave << ")";
2389 result = query(szQuery2.str());
2390 if (!result.empty())
2391 {
2392 for (const auto& itt : result)
2393 {
2394 std::vector<std::string> sd = itt;
2395 szQuery2.clear();
2396 szQuery2.str("");
2397 szQuery2 << "SELECT ID FROM DeviceStatus WHERE ([Type]=" << (int)pTypeColorSwitch << ") AND (SubType=" << (int)sTypeColor_RGB_W << ") AND (HardwareID=" << sd[0] << ")";
2398 result2 = query(szQuery2.str());
2399
2400 for (const auto& itt2 : result2)
2401 {
2402 sd = itt2;
2403
2404 //Change device type
2405 szQuery2.clear();
2406 szQuery2.str("");
2407 szQuery2 << "UPDATE DeviceStatus SET SubType=" << (int)sTypeColor_RGB_W_Z << " WHERE (ID=" << sd[0] << ")";
2408 query(szQuery2.str());
2409 }
2410 }
2411 }
2412 }
2413 if (dbversion < 125)
2414 {
2415 std::string sFile = szWWWFolder + "/js/domoticz.js.gz";
2416 std::remove(sFile.c_str());
2417 }
2418 if (dbversion < 126)
2419 {
2420 std::string sFile = szWWWFolder + "/js/domoticzdevices.js.gz";
2421 std::remove(sFile.c_str());
2422 }
2423 if (dbversion < 127)
2424 {
2425 safe_query("UPDATE Hardware SET Mode2 = 3 WHERE Type = %d", HTYPE_Philips_Hue);
2426 }
2427 if (dbversion < 128)
2428 {
2429 //Two more files to remove
2430 std::remove(std::string(szWWWFolder + "/js/domoticzblocks.js.gz").c_str());
2431 std::remove(std::string(szWWWFolder + "/js/domoticzblocks_messages_en.js.gz").c_str());
2432 }
2433 if (dbversion < 129)
2434 {
2435 //Put floorplan images into the database
2436 if (!DoesColumnExistsInTable("Image", "Floorplans"))
2437 {
2438 query("ALTER TABLE Floorplans ADD COLUMN [Image] BLOB");
2439 }
2440
2441 //Move image files into database
2442 //Get Dynamic Theme Files
2443 std::map<std::string, int> _FloorplanFiles;
2444 GetDirFilesRecursive(szWWWFolder + "/images/floorplans/", _FloorplanFiles);
2445
2446 for (const auto& itt : _FloorplanFiles)
2447 {
2448 std::string tname(itt.first);
2449 stdlower(tname);
2450
2451 if (
2452 (tname.find(".jpg") == std::string::npos)
2453 && (tname.find(".jpeg") == std::string::npos)
2454 && (tname.find(".png") == std::string::npos)
2455 && (tname.find(".bmp") == std::string::npos)
2456 && (tname.find(".gif") == std::string::npos)
2457 )
2458 continue; //not an image file
2459
2460 std::string sname = itt.first.substr(szWWWFolder.size() + 1);
2461 //Find the image file in our database
2462 std::vector<std::vector<std::string> > result;
2463 result = safe_query("SELECT ID FROM Floorplans WHERE (ImageFile == '%s') COLLATE NOCASE", sname.c_str());
2464 if (result.empty())
2465 {
2466 //could be our example sketch, or left over images, add it to the database
2467 std::string vname = sname.substr(strlen("images/floorplans/"));
2468 size_t tpos = vname.rfind('.');
2469 if (tpos != std::string::npos)
2470 {
2471 vname = vname.substr(0, tpos);
2472 }
2473 safe_query("INSERT INTO Floorplans ([Name],[ImageFile]) VALUES('%s','%s')", vname.c_str(), sname.c_str());
2474 result = safe_query("SELECT ID FROM Floorplans WHERE (ImageFile == '%s')", sname.c_str());
2475 }
2476 if (!result.empty())
2477 {
2478 std::string sID = result[0][0];
2479 std::ifstream is(itt.first.c_str(), std::ios::in | std::ios::binary);
2480 if (is)
2481 {
2482 std::string cfile;
2483 cfile.append((std::istreambuf_iterator<char>(is)),
2484 (std::istreambuf_iterator<char>()));
2485 is.close();
2486
2487 if (safe_UpdateBlobInTableWithID("Floorplans", "Image", sID, cfile))
2488 std::remove(itt.first.c_str());
2489 else
2490 _log.Log(LOG_ERROR, "SQL: Problem converting floorplan image into database! ");
2491 }
2492 }
2493 }
2494 //Remove ImageFile column
2495 query("ALTER TABLE Floorplans RENAME TO tmp_Floorplans;");
2496 query("CREATE TABLE[Floorplans]([ID] INTEGER PRIMARY KEY, [Name] VARCHAR(200) NOT NULL, [Image] BLOB, [Order] INTEGER BIGINT(10) default 0, [ScaleFactor] Float default 1.0);");
2497
2498 query(
2499 "INSERT INTO Floorplans ([ID],[Name],[Image],[Order],[ScaleFactor]) "
2500 "SELECT [ID],[Name],[Image],[Order],[ScaleFactor] "
2501 "FROM tmp_Floorplans");
2502
2503 query("DROP TABLE tmp_Floorplans;");
2504 }
2505 if (dbversion < 130)
2506 {
2507 //Patch for energymeters to store calculated or device energy usage in options map
2508 result = safe_query("SELECT ID, Options FROM DeviceStatus WHERE (Type=%d) AND (SubType=%d)", pTypeGeneral, sTypeKwh);
2509 if (!result.empty())
2510 {
2511 for (const auto& itt : result)
2512 {
2513 std::vector<std::string> sd = itt;
2514 std::string idx = sd[0];
2515 std::string EnergyMeterMode = sd[1];
2516 if (EnergyMeterMode == "1")
2517 {
2518 uint64_t ullidx = std::stoull(idx);
2519 m_sql.SetDeviceOptions(ullidx, m_sql.BuildDeviceOptions("EnergyMeterMode:" + EnergyMeterMode, false));
2520 }
2521 }
2522 }
2523 }
2524 if (dbversion < 131)
2525 {
2526 query("DROP TABLE IF EXISTS [EventActions]");
2527 }
2528 if (dbversion < 132)
2529 {
2530 //Patch for PhilipsHue pTypeLighting2/sTypeAC to pTypeGeneralSwitch/sSwitchGeneralSwitch
2531 std::stringstream szQuery;
2532 std::vector<std::vector<std::string> > result, result2;
2533 std::vector<std::string> sd;
2534 szQuery.clear();
2535 szQuery.str("");
2536 szQuery << "SELECT ID FROM Hardware WHERE([Type]==" << HTYPE_Philips_Hue << ")";
2537 result = query(szQuery.str());
2538 if (!result.empty())
2539 {
2540 for (const auto& itt : result)
2541 {
2542 sd = itt;
2543 szQuery.clear();
2544 szQuery.str("");
2545 szQuery << "SELECT ID, DeviceID FROM DeviceStatus WHERE ([Type]=" << pTypeLighting2 << ") AND (SubType=" << sTypeAC << ") AND (HardwareID=" << sd[0] << ")";
2546 result2 = query(szQuery.str());
2547 if (!result2.empty())
2548 {
2549 for (const auto& itt2 : result2)
2550 {
2551 sd = itt2;
2552 std::string ndeviceid = "0" + sd[1];
2553
2554 szQuery.clear();
2555 szQuery.str("");
2556 szQuery << "UPDATE DeviceStatus SET DeviceID='" << ndeviceid << "', [Type]=" << pTypeGeneralSwitch << ", SubType=" << sSwitchGeneralSwitch << " WHERE (ID=" << sd[0] << ")";
2557 query(szQuery.str());
2558 }
2559 }
2560 }
2561 }
2562 }
2563 if (dbversion < 134)
2564 {
2565 query("ALTER TABLE Hardware RENAME TO tmp_Hardware;");
2566 query(sqlCreateHardware);
2567 query("INSERT INTO Hardware(ID, Name, Enabled, Type, Address, Port, SerialPort, Username, Password, Extra, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6, DataTimeout) SELECT ID, Name, Enabled, Type, Address, Port, SerialPort, Username, Password, Extra, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6, DataTimeout FROM tmp_Hardware;");
2568 query("DROP TABLE tmp_Hardware;");
2569 }
2570 if (dbversion < 135)
2571 {
2572 //OpenZWave COMMAND_CLASS_METER new index, need to delete the cache!
2573 std::vector<std::string> root_files_;
2574 DirectoryListing(root_files_, szUserDataFolder + "Config", false, true);
2575 for (auto itt : root_files_)
2576 {
2577 if (itt.find("ozwcache_0x") != std::string::npos)
2578 {
2579 std::string dfile = szUserDataFolder + "Config/" + itt;
2580 std::remove(dfile.c_str());
2581 }
2582 }
2583 }
2584 if (dbversion < 136)
2585 {
2586 //SolarEdge WEB API Frequency sensor change from Percentage to Custom type
2587 std::stringstream szQuery;
2588 std::vector<std::vector<std::string> > hwResult, dsResult;
2589 std::vector<std::string> sd;
2590 szQuery << "SELECT ID FROM Hardware WHERE([Type]==" << HTYPE_SolarEdgeAPI << ")";
2591 hwResult = query(szQuery.str());
2592 if (!hwResult.empty())
2593 {
2594 for (const auto& itt : hwResult)
2595 {
2596 sd = itt;
2597 szQuery.clear();
2598 szQuery.str("");
2599 szQuery << "SELECT ID, DeviceID FROM DeviceStatus WHERE ([Type]=" << pTypeGeneral << ") AND (SubType=" << sTypePercentage << ") AND (HardwareID=" << sd[0] << ")";
2600 dsResult = query(szQuery.str());
2601 if (!dsResult.empty())
2602 {
2603 for (const auto& itt2 : dsResult)
2604 {
2605 sd = itt2;
2606 int id = atoi(sd[1].c_str());
2607 char szTmp[20];
2608 sprintf(szTmp, "%06X01", id);
2609
2610 szQuery.clear();
2611 szQuery.str("");
2612 szQuery << "UPDATE DeviceStatus SET DeviceID='" << szTmp << "', [Type]=" << pTypeGeneral << ", SubType=" << sTypeCustom << ", Options=\"1;Hz\" WHERE (ID=" << sd[0] << ")";
2613 query(szQuery.str());
2614 }
2615 }
2616 }
2617 }
2618 }
2619 if (dbversion < 137)
2620 {
2621 // Patch for OpenWebNetTCP: update unit and deviceID for Alert devices, update subtype for GeneralSwitch devices
2622 std::stringstream szQuery;
2623 std::vector<std::vector<std::string> > result, result2;
2624 std::vector<std::string> sd;
2625 szQuery << "SELECT ID FROM Hardware WHERE([Type]==" << HTYPE_OpenWebNetTCP << ")";
2626 result = query(szQuery.str());
2627 if (!result.empty())
2628 {
2629 for (const auto& itt : result)
2630 {
2631 sd = itt;
2632
2633 szQuery.clear();
2634 szQuery.str("");
2635 szQuery << "SELECT ID, DeviceID, SubType FROM DeviceStatus WHERE (HardwareID=" << sd[0] << ")";
2636 result2 = query(szQuery.str());
2637
2638 if (!result2.empty())
2639 {
2640 for (const auto& itt2 : result2)
2641 {
2642 sd = itt2;
2643
2644 int sub_type = atoi(sd[2].c_str());
2645 uint32_t NodeID;
2646 std::stringstream s_strid;
2647 s_strid << std::hex << sd[1];
2648 s_strid >> NodeID;
2649
2650 if (sub_type == sTypeAlert)
2651 {
2652 NodeID = NodeID & 0x000000ff;
2653 if (NodeID > 8) NodeID = 0xff - NodeID + 17; // update id BURGLAR status to allow the 'id bus interface'
2654
2655 char ndeviceid[10];
2656 sprintf(ndeviceid, "%u", NodeID);
2657
2658 _log.Log(LOG_STATUS, "COpenWebNetTCP: ID:%s, DeviceID change from %s to %s!", sd[0].c_str(), sd[1].c_str(), ndeviceid);
2659
2660 szQuery.clear();
2661 szQuery.str("");
2662 szQuery << "UPDATE DeviceStatus SET DeviceID='" << ndeviceid << "', Unit=1 WHERE (ID=" << sd[0] << ")";
2663 query(szQuery.str());
2664 }
2665 else if ((sub_type == sSwitchBlindsT1) || (sub_type == sSwitchLightT1) || (sub_type == sSwitchContactT1) || (sub_type == sSwitchAuxiliaryT1))
2666 {
2667 char ndeviceid[10];
2668 sprintf(ndeviceid, "%07X", NodeID);
2669
2670 _log.Log(LOG_STATUS, "COpenWebNetTCP: ID:%s, DeviceID change from %s to %s!", sd[0].c_str(), sd[1].c_str(), ndeviceid);
2671 szQuery.clear();
2672 szQuery.str("");
2673 szQuery << "UPDATE DeviceStatus SET DeviceID='" << ndeviceid << "', Type='" << pTypeLighting2 << "', SubType='" << sTypeAC << "' WHERE (ID=" << sd[0] << ")";
2674 query(szQuery.str());
2675 }
2676 }
2677 }
2678 }
2679 }
2680 }
2681 if (dbversion < 138)
2682 {
2683 query("ALTER TABLE SharedDevices ADD COLUMN [Favorite] INTEGER DEFAULT 0");
2684 query("UPDATE SharedDevices SET Favorite = 1 WHERE DeviceRowID IN (SELECT ID FROM DeviceStatus WHERE (Favorite=1))");
2685 }
2686 if (dbversion < 139)
2687 {
2688 if (!DoesColumnExistsInTable("User", "SceneLog"))
2689 {
2690 query("ALTER TABLE SceneLog ADD COLUMN [User] VARCHAR(100) DEFAULT ('')");
2691 }
2692 }
2693 if (dbversion < 140)
2694 {
2695 //Migrate all Pushers into one table
2696 safe_query("UPDATE PushLink SET PushType = %d", CBasePush::PushType::PUSHTYPE_INFLUXDB);
2697
2698 struct _tPushHelper
2699 {
2700 std::string DBName;
2701 CBasePush::PushType PushType;
2702 _tPushHelper(const std::string& dbname, const CBasePush::PushType pushtype)
2703 {
2704 DBName = dbname;
2705 PushType = pushtype;
2706 }
2707 };
2708 std::vector<_tPushHelper> dbToMigrate;
2709 dbToMigrate.push_back(_tPushHelper("HttpLink", CBasePush::PushType::PUSHTYPE_HTTP));
2710 dbToMigrate.push_back(_tPushHelper("GooglePubSubLink", CBasePush::PushType::PUSHTYPE_GOOGLE_PUB_SUB));
2711 dbToMigrate.push_back(_tPushHelper("FibaroLink", CBasePush::PushType::PUSHTYPE_FIBARO));
2712
2713 for (auto itt : dbToMigrate)
2714 {
2715 safe_query(
2716 "INSERT INTO PushLink (PushType, DeviceID, DelimitedValue, TargetType, TargetVariable, TargetDeviceID, TargetProperty, Enabled, IncludeUnit) "
2717 "SELECT %d, [DeviceID], [DelimitedValue], [TargetType], [TargetVariable], [TargetDeviceID], [TargetProperty], [Enabled], [IncludeUnit] FROM %s",
2718 itt.PushType,
2719 itt.DBName.c_str()
2720 );
2721 safe_query("DROP TABLE %s", itt.DBName.c_str());
2722 }
2723 //Change DeviceID to DeviceRowID
2724 query("ALTER TABLE PushLink RENAME TO tmp_PushLink");
2725 query(sqlCreatePushLink);
2726 //Copy values from tmp_PushLink back into our new table
2727 query(
2728 "INSERT INTO PushLink (PushType, DeviceRowID, DelimitedValue, TargetType, TargetVariable, TargetDeviceID, TargetProperty, Enabled, IncludeUnit) "
2729 "SELECT [PushType], [DeviceID], [DelimitedValue], [TargetType], [TargetVariable], [TargetDeviceID], [TargetProperty], [Enabled], [IncludeUnit] FROM tmp_PushLink");
2730 query("DROP TABLE tmp_PushLink");
2731 }
2732 if (dbversion < 141)
2733 {
2734 // Patch for OpenWebNetTCP: update unit and deviceID for Alert devices, update subtype for GeneralSwitch devices
2735 std::stringstream szQuery;
2736 std::vector<std::vector<std::string> > result;
2737 std::vector<std::string> sd;
2738 szQuery << "SELECT ID FROM Hardware WHERE([Type]==" << HTYPE_OpenWebNetTCP << ")";
2739 result = query(szQuery.str());
2740 if (!result.empty())
2741 {
2742 for (const auto& itt : result)
2743 {
2744 sd = itt;
2745 safe_query("UPDATE DeviceStatus SET DeviceID='0' || DeviceID, Type=%d, SubType=%d WHERE (HardwareID=%s AND Type=%d AND SubType=%d)", pTypeGeneralSwitch, sSwitchTypeAC, sd[0].c_str(), pTypeLighting2, sTypeAC);
2746 }
2747 }
2748 }
2749 if (dbversion < 142)
2750 {
2751 //(MySensors)MQTT, prevent loop by default
2752 safe_query("UPDATE Hardware SET Mode3=1 WHERE ([Type]==%d OR [Type]==%d)", HTYPE_MQTT, HTYPE_MySensorsMQTT);
2753 }
2754 if (dbversion < 143)
2755 {
2756 //Rename Google Cloud Messaging setting to Firebase Cloud Messaging
2757 int iEnabled = 0;
2758 if (!GetPreferencesVar("GCMEnabled", iEnabled))
2759 UpdatePreferencesVar("FCMEnabled", iEnabled);
2760 }
2761 }
2762 else if (bNewInstall)
2763 {
2764 //place here actions that needs to be performed on new databases
2765 query("INSERT INTO Plans (Name) VALUES ('$Hidden Devices')");
2766 // Add hardware for internal use
2767 m_sql.safe_query("INSERT INTO Hardware (Name, Enabled, Type, Address, Port, Username, Password, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6) VALUES ('Domoticz Internal',1, %d,'',1,'','',0,0,0,0,0,0)", HTYPE_DomoticzInternal);
2768 }
2769 UpdatePreferencesVar("DB_Version", DB_VERSION);
2770
2771 //Make sure we have some default preferences
2772 int nValue = 10;
2773 std::string sValue;
2774 if (!GetPreferencesVar("Title", sValue))
2775 {
2776 UpdatePreferencesVar("Title", "Domoticz");
2777 }
2778 if ((!GetPreferencesVar("LightHistoryDays", nValue)) || (nValue == 0))
2779 {
2780 UpdatePreferencesVar("LightHistoryDays", 30);
2781 }
2782 if ((!GetPreferencesVar("MeterDividerEnergy", nValue)) || (nValue == 0))
2783 {
2784 UpdatePreferencesVar("MeterDividerEnergy", 1000);
2785 }
2786 else if (nValue == 0)
2787 {
2788 //Sanity check!
2789 UpdatePreferencesVar("MeterDividerEnergy", 1000);
2790 }
2791 if ((!GetPreferencesVar("MeterDividerGas", nValue)) || (nValue == 0))
2792 {
2793 UpdatePreferencesVar("MeterDividerGas", 100);
2794 }
2795 else if (nValue == 0)
2796 {
2797 //Sanity check!
2798 UpdatePreferencesVar("MeterDividerGas", 100);
2799 }
2800 if ((!GetPreferencesVar("MeterDividerWater", nValue)) || (nValue == 0))
2801 {
2802 UpdatePreferencesVar("MeterDividerWater", 100);
2803 }
2804 else if (nValue == 0)
2805 {
2806 //Sanity check!
2807 UpdatePreferencesVar("MeterDividerWater", 100);
2808 }
2809 if ((!GetPreferencesVar("RandomTimerFrame", nValue)) || (nValue == 0))
2810 {
2811 UpdatePreferencesVar("RandomTimerFrame", 15);
2812 }
2813 if ((!GetPreferencesVar("ElectricVoltage", nValue)) || (nValue == 0))
2814 {
2815 UpdatePreferencesVar("ElectricVoltage", 230);
2816 }
2817 if (!GetPreferencesVar("CM113DisplayType", nValue))
2818 {
2819 UpdatePreferencesVar("CM113DisplayType", 0);
2820 }
2821 if ((!GetPreferencesVar("5MinuteHistoryDays", nValue)) || (nValue == 0))
2822 {
2823 UpdatePreferencesVar("5MinuteHistoryDays", 1);
2824 }
2825 if ((!GetPreferencesVar("SensorTimeout", nValue)) || (nValue == 0))
2826 {
2827 UpdatePreferencesVar("SensorTimeout", 60);
2828 }
2829 if (!GetPreferencesVar("SensorTimeoutNotification", nValue))
2830 {
2831 UpdatePreferencesVar("SensorTimeoutNotification", 0); //default disabled
2832 }
2833
2834 if (!GetPreferencesVar("UseAutoUpdate", nValue))
2835 {
2836 UpdatePreferencesVar("UseAutoUpdate", 1);
2837 }
2838
2839 if (!GetPreferencesVar("UseAutoBackup", nValue))
2840 {
2841 UpdatePreferencesVar("UseAutoBackup", 0);
2842 }
2843
2844 if (GetPreferencesVar("Rego6XXType", nValue))
2845 {
2846 // The setting is no longer here. It has moved to the hardware table (Mode1)
2847 // Copy the setting so no data is lost - if the rego is used. (zero is the default
2848 // so it's no point copying this value...)
2849 // THIS SETTING CAN BE REMOVED WHEN ALL CAN BE ASSUMED TO HAVE UPDATED (summer 2013?)
2850 if (nValue > 0)
2851 {
2852 std::stringstream szQuery;
2853
2854 szQuery.clear();
2855 szQuery.str("");
2856 szQuery << "SELECT ID,Mode1 FROM Hardware WHERE (Type=" << HTYPE_Rego6XX << ")";
2857 result = query(szQuery.str());
2858 if (!result.empty())
2859 {
2860 if (atoi(result[0][1].c_str()) != nValue)
2861 {
2862 UpdateRFXCOMHardwareDetails(atoi(result[0][0].c_str()), nValue, 0, 0, 0, 0, 0);
2863 }
2864 }
2865 UpdatePreferencesVar("Rego6XXType", 0); // Set to zero to avoid another copy
2866 }
2867 }
2868 //Costs for Energy/Gas and Water (See your provider, try to include tax and other stuff)
2869 if ((!GetPreferencesVar("CostEnergy", nValue)) || (nValue == 0))
2870 {
2871 UpdatePreferencesVar("CostEnergy", 2149);
2872 }
2873 if ((!GetPreferencesVar("CostEnergyT2", nValue)) || (nValue == 0))
2874 {
2875 GetPreferencesVar("CostEnergy", nValue);
2876 UpdatePreferencesVar("CostEnergyT2", nValue);
2877 }
2878 if ((!GetPreferencesVar("CostEnergyR1", nValue)) || (nValue == 0))
2879 {
2880 UpdatePreferencesVar("CostEnergyR1", 800);
2881 }
2882 if ((!GetPreferencesVar("CostEnergyR2", nValue)) || (nValue == 0))
2883 {
2884 GetPreferencesVar("CostEnergyR1", nValue);
2885 UpdatePreferencesVar("CostEnergyR2", nValue);
2886 }
2887
2888 if ((!GetPreferencesVar("CostGas", nValue)) || (nValue == 0))
2889 {
2890 UpdatePreferencesVar("CostGas", 6218);
2891 }
2892 if ((!GetPreferencesVar("CostWater", nValue)) || (nValue == 0))
2893 {
2894 UpdatePreferencesVar("CostWater", 16473);
2895 }
2896 if (!GetPreferencesVar("UseEmailInNotifications", nValue))
2897 {
2898 UpdatePreferencesVar("UseEmailInNotifications", 1);
2899 }
2900 if (!GetPreferencesVar("SendErrorNotifications", nValue))
2901 {
2902 UpdatePreferencesVar("SendErrorNotifications", 0);
2903 }
2904 if ((!GetPreferencesVar("EmailPort", nValue)) || (nValue == 0))
2905 {
2906 UpdatePreferencesVar("EmailPort", 25);
2907 }
2908 if (!GetPreferencesVar("EmailAsAttachment", nValue))
2909 {
2910 UpdatePreferencesVar("EmailAsAttachment", 0);
2911 }
2912 if (!GetPreferencesVar("DoorbellCommand", nValue))
2913 {
2914 UpdatePreferencesVar("DoorbellCommand", 0);
2915 }
2916 if (!GetPreferencesVar("SmartMeterType", nValue)) //0=meter has decimals, 1=meter does not have decimals, need this for the day graph
2917 {
2918 UpdatePreferencesVar("SmartMeterType", 0);
2919 }
2920 if (!GetPreferencesVar("EnableTabLights", nValue))
2921 {
2922 UpdatePreferencesVar("EnableTabLights", 1);
2923 }
2924 if (!GetPreferencesVar("EnableTabTemp", nValue))
2925 {
2926 UpdatePreferencesVar("EnableTabTemp", 1);
2927 }
2928 if (!GetPreferencesVar("EnableTabWeather", nValue))
2929 {
2930 UpdatePreferencesVar("EnableTabWeather", 1);
2931 }
2932 if (!GetPreferencesVar("EnableTabUtility", nValue))
2933 {
2934 UpdatePreferencesVar("EnableTabUtility", 1);
2935 }
2936 if (!GetPreferencesVar("EnableTabCustom", nValue))
2937 {
2938 UpdatePreferencesVar("EnableTabCustom", 1);
2939 }
2940 if (!GetPreferencesVar("EnableTabScenes", nValue))
2941 {
2942 UpdatePreferencesVar("EnableTabScenes", 1);
2943 }
2944 if (!GetPreferencesVar("EnableTabFloorplans", nValue))
2945 {
2946 UpdatePreferencesVar("EnableTabFloorplans", 0);
2947 }
2948 if (!GetPreferencesVar("NotificationSensorInterval", nValue))
2949 {
2950 UpdatePreferencesVar("NotificationSensorInterval", 12 * 60 * 60);
2951 }
2952 else
2953 {
2954 if (nValue < 60)
2955 UpdatePreferencesVar("NotificationSensorInterval", 60);
2956 }
2957 if (!GetPreferencesVar("NotificationSwitchInterval", nValue))
2958 {
2959 UpdatePreferencesVar("NotificationSwitchInterval", 0);
2960 }
2961 if ((!GetPreferencesVar("RemoteSharedPort", nValue)) || (nValue == 0))
2962 {
2963 UpdatePreferencesVar("RemoteSharedPort", 6144);
2964 }
2965 if ((!GetPreferencesVar("Language", sValue)) || (sValue.empty()))
2966 {
2967 UpdatePreferencesVar("Language", "en");
2968 }
2969 if (!GetPreferencesVar("DashboardType", nValue))
2970 {
2971 UpdatePreferencesVar("DashboardType", 0);
2972 }
2973 if (!GetPreferencesVar("MobileType", nValue))
2974 {
2975 UpdatePreferencesVar("MobileType", 0);
2976 }
2977 if (!GetPreferencesVar("WindUnit", nValue))
2978 {
2979 UpdatePreferencesVar("WindUnit", (int)WINDUNIT_MS);
2980 }
2981 else
2982 {
2983 m_windunit = (_eWindUnit)nValue;
2984 }
2985 if (!GetPreferencesVar("TempUnit", nValue))
2986 {
2987 UpdatePreferencesVar("TempUnit", (int)TEMPUNIT_C);
2988 }
2989 else
2990 {
2991 m_tempunit = (_eTempUnit)nValue;
2992
2993 }
2994 if (!GetPreferencesVar("WeightUnit", nValue))
2995 {
2996 UpdatePreferencesVar("WeightUnit", (int)WEIGHTUNIT_KG);
2997 }
2998 else
2999 {
3000 m_weightunit = (_eWeightUnit)nValue;
3001
3002 }
3003 SetUnitsAndScale();
3004
3005 if (!GetPreferencesVar("SecStatus", nValue))
3006 {
3007 UpdatePreferencesVar("SecStatus", 0);
3008 }
3009 if (!GetPreferencesVar("SecOnDelay", nValue))
3010 {
3011 UpdatePreferencesVar("SecOnDelay", 30);
3012 }
3013
3014 if (!GetPreferencesVar("AuthenticationMethod", nValue))
3015 {
3016 UpdatePreferencesVar("AuthenticationMethod", 0);//AUTH_LOGIN=0, AUTH_BASIC=1
3017 }
3018 if (!GetPreferencesVar("ReleaseChannel", nValue))
3019 {
3020 UpdatePreferencesVar("ReleaseChannel", 0);//Stable=0, Beta=1
3021 }
3022 if ((!GetPreferencesVar("RaspCamParams", sValue)) || (sValue.empty()))
3023 {
3024 UpdatePreferencesVar("RaspCamParams", "-w 800 -h 600 -t 1"); //width/height/time2wait
3025 }
3026 if ((!GetPreferencesVar("UVCParams", sValue)) || (sValue.empty()))
3027 {
3028 UpdatePreferencesVar("UVCParams", "-S80 -B128 -C128 -G80 -x800 -y600 -q100"); //width/height/time2wait
3029 }
3030 if (sValue == "- -S80 -B128 -C128 -G80 -x800 -y600 -q100")
3031 {
3032 UpdatePreferencesVar("UVCParams", "-S80 -B128 -C128 -G80 -x800 -y600 -q100"); //fix a bug
3033 }
3034
3035 nValue = 1;
3036 if (!GetPreferencesVar("AcceptNewHardware", nValue))
3037 {
3038 UpdatePreferencesVar("AcceptNewHardware", 1);
3039 nValue = 1;
3040 }
3041 m_bAcceptNewHardware = (nValue == 1);
3042 if ((!GetPreferencesVar("ZWavePollInterval", nValue)) || (nValue == 0))
3043 {
3044 UpdatePreferencesVar("ZWavePollInterval", 60);
3045 }
3046 if (!GetPreferencesVar("ZWaveEnableDebug", nValue))
3047 {
3048 UpdatePreferencesVar("ZWaveEnableDebug", 0);
3049 }
3050 if ((!GetPreferencesVar("ZWaveNetworkKey", sValue)) || (sValue.empty()))
3051 {
3052 sValue = "0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10";
3053 UpdatePreferencesVar("ZWaveNetworkKey", sValue);
3054 }
3055 //Double check network_key
3056 std::vector<std::string> splitresults;
3057 StringSplit(sValue, ",", splitresults);
3058 if (splitresults.size() != 16)
3059 {
3060 sValue = "0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10";
3061 UpdatePreferencesVar("ZWaveNetworkKey", sValue);
3062 }
3063
3064 if (!GetPreferencesVar("ZWaveEnableNightlyNetworkHeal", nValue))
3065 {
3066 UpdatePreferencesVar("ZWaveEnableNightlyNetworkHeal", 0);
3067 }
3068 if (!GetPreferencesVar("BatteryLowNotification", nValue))
3069 {
3070 UpdatePreferencesVar("BatteryLowNotification", 0); //default disabled
3071 }
3072 nValue = 1;
3073 if (!GetPreferencesVar("AllowWidgetOrdering", nValue))
3074 {
3075 UpdatePreferencesVar("AllowWidgetOrdering", 1); //default enabled
3076 nValue = 1;
3077 }
3078 m_bAllowWidgetOrdering = (nValue == 1);
3079 nValue = 0;
3080 if (!GetPreferencesVar("ActiveTimerPlan", nValue))
3081 {
3082 UpdatePreferencesVar("ActiveTimerPlan", 0); //default
3083 nValue = 0;
3084 }
3085 m_ActiveTimerPlan = nValue;
3086 if (!GetPreferencesVar("HideDisabledHardwareSensors", nValue))
3087 {
3088 UpdatePreferencesVar("HideDisabledHardwareSensors", 1);
3089 }
3090 nValue = 0;
3091 if (!GetPreferencesVar("EnableEventScriptSystem", nValue))
3092 {
3093 UpdatePreferencesVar("EnableEventScriptSystem", 1);
3094 nValue = 1;
3095 }
3096 m_bEnableEventSystem = (nValue == 1);
3097
3098 nValue = 0;
3099 if (!GetPreferencesVar("EventSystemLogFullURL", nValue))
3100 {
3101 UpdatePreferencesVar("EventSystemLogFullURL", 1);
3102 nValue = 1;
3103 }
3104 m_bEnableEventSystemFullURLLog = (nValue == 1);
3105
3106 nValue = 0;
3107 if (!GetPreferencesVar("DisableDzVentsSystem", nValue))
3108 {
3109 UpdatePreferencesVar("DisableDzVentsSystem", 0);
3110 nValue = 0;
3111 }
3112 m_bDisableDzVentsSystem = (nValue == 1);
3113
3114 if (!GetPreferencesVar("DzVentsLogLevel", nValue))
3115 {
3116 UpdatePreferencesVar("DzVentsLogLevel", 3);
3117 }
3118
3119 nValue = 1;
3120 if (!GetPreferencesVar("LogEventScriptTrigger", nValue))
3121 {
3122 UpdatePreferencesVar("LogEventScriptTrigger", 1);
3123 nValue = 1;
3124 }
3125 m_bLogEventScriptTrigger = (nValue != 0);
3126
3127 if ((!GetPreferencesVar("WebTheme", sValue)) || (sValue.empty()))
3128 {
3129 UpdatePreferencesVar("WebTheme", "default");
3130 }
3131
3132 if ((!GetPreferencesVar("FloorplanPopupDelay", nValue)) || (nValue == 0))
3133 {
3134 UpdatePreferencesVar("FloorplanPopupDelay", 750);
3135 }
3136 if (!GetPreferencesVar("FloorplanFullscreenMode", nValue))
3137 {
3138 UpdatePreferencesVar("FloorplanFullscreenMode", 0);
3139 }
3140 if (!GetPreferencesVar("FloorplanAnimateZoom", nValue))
3141 {
3142 UpdatePreferencesVar("FloorplanAnimateZoom", 1);
3143 }
3144 if (!GetPreferencesVar("FloorplanShowSensorValues", nValue))
3145 {
3146 UpdatePreferencesVar("FloorplanShowSensorValues", 1);
3147 }
3148 if (!GetPreferencesVar("FloorplanShowSwitchValues", nValue))
3149 {
3150 UpdatePreferencesVar("FloorplanShowSwitchValues", 0);
3151 }
3152 if (!GetPreferencesVar("FloorplanShowSceneNames", nValue))
3153 {
3154 UpdatePreferencesVar("FloorplanShowSceneNames", 1);
3155 }
3156 if ((!GetPreferencesVar("FloorplanRoomColour", sValue)) || (sValue.empty()))
3157 {
3158 UpdatePreferencesVar("FloorplanRoomColour", "Blue");
3159 }
3160 if (!GetPreferencesVar("FloorplanActiveOpacity", nValue))
3161 {
3162 UpdatePreferencesVar("FloorplanActiveOpacity", 25);
3163 }
3164 if (!GetPreferencesVar("FloorplanInactiveOpacity", nValue))
3165 {
3166 UpdatePreferencesVar("FloorplanInactiveOpacity", 5);
3167 }
3168 if ((!GetPreferencesVar("TempHome", sValue)) || (sValue.empty()))
3169 {
3170 UpdatePreferencesVar("TempHome", "20");
3171 }
3172 if ((!GetPreferencesVar("TempAway", sValue)) || (sValue.empty()))
3173 {
3174 UpdatePreferencesVar("TempAway", "15");
3175 }
3176 if ((!GetPreferencesVar("TempComfort", sValue)) || (sValue.empty()))
3177 {
3178 UpdatePreferencesVar("TempComfort", "22.0");
3179 }
3180 if ((!GetPreferencesVar("DegreeDaysBaseTemperature", sValue)) || (sValue.empty()))
3181 {
3182 UpdatePreferencesVar("DegreeDaysBaseTemperature", "18.0");
3183 }
3184 if ((!GetPreferencesVar("HTTPURL", sValue)) || (sValue.empty()))
3185 {
3186 sValue = "https://www.somegateway.com/pushurl.php?username=#FIELD1&password=#FIELD2&apikey=#FIELD3&from=#FIELD4&to=#TO&message=#MESSAGE";
3187 std::string sencoded = base64_encode(sValue);
3188 UpdatePreferencesVar("HTTPURL", sencoded);
3189 }
3190 if ((!GetPreferencesVar("HTTPPostContentType", sValue)) || (sValue.empty()))
3191 {
3192 sValue = "application/json";
3193 std::string sencoded = base64_encode(sValue);
3194 UpdatePreferencesVar("HTTPPostContentType", sencoded);
3195 }
3196 if (!GetPreferencesVar("ShowUpdateEffect", nValue))
3197 {
3198 UpdatePreferencesVar("ShowUpdateEffect", 0);
3199 }
3200 nValue = 5;
3201 if (!GetPreferencesVar("ShortLogInterval", nValue))
3202 {
3203 UpdatePreferencesVar("ShortLogInterval", nValue);
3204 }
3205 if (nValue < 1)
3206 nValue = 5;
3207 m_ShortLogInterval = nValue;
3208
3209 if (!GetPreferencesVar("SendErrorsAsNotification", nValue))
3210 {
3211 UpdatePreferencesVar("SendErrorsAsNotification", 0);
3212 nValue = 0;
3213 }
3214 _log.ForwardErrorsToNotificationSystem(nValue != 0);
3215
3216 if (!GetPreferencesVar("IFTTTEnabled", nValue))
3217 {
3218 UpdatePreferencesVar("IFTTTEnabled", 0);
3219 }
3220 if (!GetPreferencesVar("EmailEnabled", nValue))
3221 {
3222 UpdatePreferencesVar("EmailEnabled", 1);
3223 }
3224
3225 //Start background thread
3226 if (!StartThread())
3227 return false;
3228 return true;
3229 }
3230
CloseDatabase()3231 void CSQLHelper::CloseDatabase()
3232 {
3233 std::lock_guard<std::mutex> l(m_sqlQueryMutex);
3234 if (m_dbase != NULL)
3235 {
3236 OptimizeDatabase(m_dbase);
3237 sqlite3_close(m_dbase);
3238 m_dbase = NULL;
3239 }
3240 }
3241
StopThread()3242 void CSQLHelper::StopThread()
3243 {
3244 if (m_thread)
3245 {
3246 RequestStop();
3247 m_thread->join();
3248 m_thread.reset();
3249 }
3250 }
3251
StartThread()3252 bool CSQLHelper::StartThread()
3253 {
3254 RequestStart();
3255 m_thread = std::make_shared<std::thread>(&CSQLHelper::Do_Work, this);
3256 SetThreadName(m_thread->native_handle(), "SQLHelper");
3257 return (m_thread != NULL);
3258 }
3259
SwitchLightFromTasker(const std::string & idx,const std::string & switchcmd,const std::string & level,const std::string & color,const std::string & User)3260 bool CSQLHelper::SwitchLightFromTasker(const std::string& idx, const std::string& switchcmd, const std::string& level, const std::string& color, const std::string& User)
3261 {
3262 _tColor ocolor(color);
3263 return SwitchLightFromTasker(std::stoull(idx), switchcmd, atoi(level.c_str()), ocolor, User);
3264 }
3265
SwitchLightFromTasker(uint64_t idx,const std::string & switchcmd,int level,_tColor color,const std::string & User)3266 bool CSQLHelper::SwitchLightFromTasker(uint64_t idx, const std::string& switchcmd, int level, _tColor color, const std::string& User)
3267 {
3268 //Get Device details
3269 std::vector<std::vector<std::string> > result;
3270 result = safe_query("SELECT HardwareID, DeviceID,Unit,Type,SubType,SwitchType,AddjValue2,nValue,sValue,Name,Options FROM DeviceStatus WHERE (ID == %" PRIu64 ")", idx);
3271 if (result.empty())
3272 return false;
3273
3274 std::vector<std::string> sd = result[0];
3275 return m_mainworker.SwitchLightInt(sd, switchcmd, level, color, false, User);
3276 }
3277
Do_Work()3278 void CSQLHelper::Do_Work()
3279 {
3280 std::vector<_tTaskItem> _items2do;
3281
3282 while (!IsStopRequested(static_cast<const long>(1000.0f / timer_resolution_hz)))
3283 {
3284 if (m_bAcceptHardwareTimerActive)
3285 {
3286 m_iAcceptHardwareTimerCounter -= static_cast<float>(1. / timer_resolution_hz);
3287 if (m_iAcceptHardwareTimerCounter <= (1.0f / timer_resolution_hz / 2))
3288 {
3289 m_bAcceptHardwareTimerActive = false;
3290 m_bAcceptNewHardware = m_bPreviousAcceptNewHardware;
3291 UpdatePreferencesVar("AcceptNewHardware", (m_bAcceptNewHardware == true) ? 1 : 0);
3292 if (!m_bAcceptNewHardware)
3293 {
3294 _log.Log(LOG_STATUS, "Receiving of new sensors disabled!...");
3295 }
3296 }
3297 }
3298
3299 { // additional scope for lock (accessing size should be within lock too)
3300 std::lock_guard<std::mutex> l(m_background_task_mutex);
3301 if (m_background_task_queue.size() > 0)
3302 {
3303 _items2do.clear();
3304
3305 std::vector<_tTaskItem>::iterator itt = m_background_task_queue.begin();
3306 while (itt != m_background_task_queue.end())
3307 {
3308 if (itt->_DelayTime)
3309 {
3310 struct timeval tvDiff, DelayTimeEnd;
3311 getclock(&DelayTimeEnd);
3312 if (timeval_subtract(&tvDiff, &DelayTimeEnd, &itt->_DelayTimeBegin)) {
3313 tvDiff.tv_sec = 0;
3314 tvDiff.tv_usec = 0;
3315 }
3316 float diff = ((tvDiff.tv_usec / 1000000.0f) + tvDiff.tv_sec);
3317 if ((itt->_DelayTime) <= diff)
3318 {
3319 _items2do.push_back(*itt);
3320 itt = m_background_task_queue.erase(itt);
3321 }
3322 else
3323 ++itt;
3324 }
3325 else
3326 {
3327 _items2do.push_back(*itt);
3328 itt = m_background_task_queue.erase(itt);
3329 }
3330 }
3331 }
3332 }
3333
3334 if (_items2do.size() < 1) {
3335 continue;
3336 }
3337
3338 std::vector<_tTaskItem>::iterator itt = _items2do.begin();
3339 while (itt != _items2do.end())
3340 {
3341 _log.Debug(DEBUG_NORM, "SQLH: Do Task ItemType:%d Cmd:%s Value:%s ", itt->_ItemType, itt->_command.c_str(), itt->_sValue.c_str());
3342
3343 if (itt->_ItemType == TITEM_SWITCHCMD)
3344 {
3345 if (itt->_switchtype == STYPE_Motion)
3346 {
3347 std::string devname = "";
3348 switch (itt->_devType)
3349 {
3350 case pTypeLighting1:
3351 case pTypeLighting2:
3352 case pTypeLighting3:
3353 case pTypeLighting5:
3354 case pTypeLighting6:
3355 case pTypeColorSwitch:
3356 case pTypeGeneralSwitch:
3357 case pTypeHomeConfort:
3358 case pTypeFS20:
3359 SwitchLightFromTasker(itt->_idx, "Off", 0, NoColor, itt->_sUser);
3360 break;
3361 case pTypeSecurity1:
3362 switch (itt->_subType)
3363 {
3364 case sTypeSecX10M:
3365 SwitchLightFromTasker(itt->_idx, "No Motion", 0, NoColor, itt->_sUser);
3366 break;
3367 default:
3368 //just update internally
3369 UpdateValueInt(itt->_HardwareID, itt->_ID.c_str(), itt->_unit, itt->_devType, itt->_subType, itt->_signallevel, itt->_batterylevel, itt->_nValue, itt->_sValue.c_str(), devname, true);
3370 break;
3371 }
3372 break;
3373 case pTypeLighting4:
3374 //only update internally
3375 UpdateValueInt(itt->_HardwareID, itt->_ID.c_str(), itt->_unit, itt->_devType, itt->_subType, itt->_signallevel, itt->_batterylevel, itt->_nValue, itt->_sValue.c_str(), devname, true);
3376 break;
3377 default:
3378 //unknown hardware type, sensor will only be updated internally
3379 UpdateValueInt(itt->_HardwareID, itt->_ID.c_str(), itt->_unit, itt->_devType, itt->_subType, itt->_signallevel, itt->_batterylevel, itt->_nValue, itt->_sValue.c_str(), devname, true);
3380 break;
3381 }
3382 }
3383 else
3384 {
3385 if (itt->_devType == pTypeLighting4)
3386 {
3387 //only update internally
3388 std::string devname = "";
3389 UpdateValueInt(itt->_HardwareID, itt->_ID.c_str(), itt->_unit, itt->_devType, itt->_subType, itt->_signallevel, itt->_batterylevel, itt->_nValue, itt->_sValue.c_str(), devname, true);
3390 }
3391 else
3392 SwitchLightFromTasker(itt->_idx, "Off", 0, NoColor, itt->_sUser);
3393 }
3394 }
3395 else if (itt->_ItemType == TITEM_EXECUTE_SCRIPT)
3396 {
3397 //start script
3398 _log.Log(LOG_STATUS, "Executing script: %s", itt->_ID.c_str());
3399 #ifdef WIN32
3400 ShellExecute(NULL, "open", itt->_ID.c_str(), itt->_sValue.c_str(), NULL, SW_SHOWNORMAL);
3401 #else
3402 std::string lscript = itt->_ID + " " + itt->_sValue;
3403 int ret = system(lscript.c_str());
3404 if (ret != 0)
3405 {
3406 _log.Log(LOG_ERROR, "Error executing script command (%s). returned: %d", itt->_ID.c_str(), ret);
3407 }
3408 #endif
3409 }
3410 else if (itt->_ItemType == TITEM_EMAIL_CAMERA_SNAPSHOT)
3411 {
3412 m_mainworker.m_cameras.EmailCameraSnapshot(itt->_ID, itt->_sValue);
3413 }
3414 else if (itt->_ItemType == TITEM_GETURL)
3415 {
3416 std::string response;
3417 std::vector<std::string> headerData, extraHeaders;
3418 std::string postData = itt->_command;
3419 std::string callback = itt->_ID;
3420
3421 if (!itt->_relatedEvent.empty())
3422 StringSplit(itt->_relatedEvent, "!#", extraHeaders);
3423
3424 HTTPClient::_eHTTPmethod method = static_cast<HTTPClient::_eHTTPmethod>(itt->_switchtype);
3425
3426 bool ret = false;
3427 if (method == HTTPClient::HTTP_METHOD_GET)
3428 {
3429 ret = HTTPClient::GET(itt->_sValue, extraHeaders, response, headerData);
3430 }
3431 else if (method == HTTPClient::HTTP_METHOD_POST)
3432 {
3433 ret = HTTPClient::POST(itt->_sValue, postData, extraHeaders, response, headerData, true, true);
3434 }
3435 else if (method == HTTPClient::HTTP_METHOD_PUT)
3436 {
3437 ret = HTTPClient::PUT(itt->_sValue, postData, extraHeaders, response, headerData, true);
3438 }
3439 else if (method == HTTPClient::HTTP_METHOD_DELETE)
3440 {
3441 ret = HTTPClient::Delete(itt->_sValue, postData, extraHeaders, response, headerData, true);
3442 }
3443 else
3444 return; //unsupported method
3445
3446 if (m_bEnableEventSystem && !callback.empty())
3447 {
3448 m_mainworker.m_eventsystem.TriggerURL(response, headerData, callback);
3449 }
3450
3451 if (!ret)
3452 {
3453 _log.Log(LOG_ERROR, "Error opening url: %s", itt->_sValue.c_str());
3454 }
3455 }
3456 else if ((itt->_ItemType == TITEM_SEND_EMAIL) || (itt->_ItemType == TITEM_SEND_EMAIL_TO))
3457 {
3458 int nValue;
3459 if (GetPreferencesVar("EmailEnabled", nValue))
3460 {
3461 if (nValue)
3462 {
3463 std::string sValue;
3464 if (GetPreferencesVar("EmailServer", sValue))
3465 {
3466 if (sValue != "")
3467 {
3468 std::string EmailFrom;
3469 std::string EmailTo;
3470 std::string EmailServer = sValue;
3471 int EmailPort = 25;
3472 std::string EmailUsername;
3473 std::string EmailPassword;
3474 GetPreferencesVar("EmailFrom", EmailFrom);
3475 if (itt->_ItemType != TITEM_SEND_EMAIL_TO)
3476 {
3477 GetPreferencesVar("EmailTo", EmailTo);
3478 }
3479 else
3480 {
3481 EmailTo = itt->_command;
3482 }
3483 GetPreferencesVar("EmailUsername", EmailUsername);
3484 GetPreferencesVar("EmailPassword", EmailPassword);
3485
3486 GetPreferencesVar("EmailPort", EmailPort);
3487
3488 SMTPClient sclient;
3489 sclient.SetFrom(CURLEncode::URLDecode(EmailFrom.c_str()));
3490 sclient.SetTo(CURLEncode::URLDecode(EmailTo.c_str()));
3491 sclient.SetCredentials(base64_decode(EmailUsername), base64_decode(EmailPassword));
3492 sclient.SetServer(CURLEncode::URLDecode(EmailServer.c_str()), EmailPort);
3493 sclient.SetSubject(CURLEncode::URLDecode(itt->_ID));
3494 sclient.SetHTMLBody(itt->_sValue);
3495 bool bRet = sclient.SendEmail();
3496
3497 if (bRet)
3498 _log.Log(LOG_STATUS, "Notification sent (Email)");
3499 else
3500 _log.Log(LOG_ERROR, "Notification failed (Email)");
3501 }
3502 }
3503 }
3504 }
3505 }
3506 else if (itt->_ItemType == TITEM_SEND_SMS)
3507 {
3508 m_notifications.SendMessage(0, std::string(""), "clickatell", itt->_ID, itt->_ID, std::string(""), 1, std::string(""), false);
3509 }
3510 else if (itt->_ItemType == TITEM_SWITCHCMD_EVENT)
3511 {
3512 SwitchLightFromTasker(itt->_idx, itt->_command.c_str(), itt->_level, itt->_Color, itt->_sUser);
3513 }
3514
3515 else if (itt->_ItemType == TITEM_SWITCHCMD_SCENE)
3516 {
3517 m_mainworker.SwitchScene(itt->_idx, itt->_command.c_str(), itt->_sUser);
3518 }
3519 else if (itt->_ItemType == TITEM_SET_VARIABLE)
3520 {
3521 std::vector<std::vector<std::string> > result;
3522 std::stringstream s_str;
3523 result = safe_query("SELECT Name, ValueType FROM UserVariables WHERE (ID == %" PRIu64 ")", itt->_idx);
3524 if (!result.empty())
3525 {
3526 std::vector<std::string> sd = result[0];
3527 s_str.clear();
3528 s_str.str("");
3529 s_str << itt->_idx;
3530 std::string errorMessage;
3531 if (!UpdateUserVariable(s_str.str(), sd[0], (const _eUsrVariableType)atoi(sd[1].c_str()), itt->_sValue, (itt->_nValue == 0) ? false : true, errorMessage))
3532 {
3533 _log.Log(LOG_ERROR, "Error updating variable %s: %s", sd[0].c_str(), errorMessage.c_str());
3534 }
3535 else
3536 {
3537 _log.Log(LOG_STATUS, "Set UserVariable %s = %s", sd[0].c_str(), CURLEncode::URLDecode(itt->_sValue).c_str());
3538 }
3539 }
3540 else
3541 {
3542 _log.Log(LOG_ERROR, "Variable not found!");
3543 }
3544 }
3545 else if (itt->_ItemType == TITEM_SET_SETPOINT)
3546 {
3547 std::stringstream sstr;
3548 sstr << itt->_idx;
3549 std::string idx = sstr.str();
3550 float fValue = (float)atof(itt->_sValue.c_str());
3551 m_mainworker.SetSetPoint(idx, fValue, itt->_command, itt->_sUntil);
3552 }
3553 else if (itt->_ItemType == TITEM_SEND_NOTIFICATION)
3554 {
3555 std::vector<std::string> splitresults;
3556 StringSplit(itt->_command, "!#", splitresults);
3557 if (splitresults.size() >= 4) {
3558 std::string subsystem;
3559 if (splitresults.size() > 4)
3560 subsystem = splitresults[4];
3561 if (subsystem.empty()) {
3562 subsystem = NOTIFYALL;
3563 }
3564 if (subsystem == "gcm") {
3565 _log.Log(LOG_STATUS, "Deprecated Notification system specified (gcm), change this to 'fcm'!");
3566 subsystem = "fcm";
3567 }
3568 m_notifications.SendMessageEx(0, std::string(""), subsystem, splitresults[0], splitresults[1], splitresults[2], static_cast<int>(itt->_idx), splitresults[3], true);
3569 }
3570 }
3571 else if (itt->_ItemType == TITEM_SEND_IFTTT_TRIGGER)
3572 {
3573 std::vector<std::string> splitresults;
3574 StringSplit(itt->_command, "!#", splitresults);
3575 if (!splitresults.empty())
3576 {
3577 std::string sValue1, sValue2, sValue3;
3578 if (splitresults.size() > 0)
3579 sValue1 = splitresults[0];
3580 if (splitresults.size() > 1)
3581 sValue2 = splitresults[1];
3582 if (splitresults.size() > 2)
3583 sValue3 = splitresults[2];
3584 IFTTT::Send_IFTTT_Trigger(itt->_ID, sValue1, sValue2, sValue3);
3585 }
3586 }
3587 else if (itt->_ItemType == TITEM_UPDATEDEVICE)
3588 {
3589 m_mainworker.UpdateDevice(static_cast<int>(itt->_idx), itt->_nValue, itt->_sValue, 12, 255, (itt->_switchtype ? true : false));
3590 }
3591 else if (itt->_ItemType == TITEM_CUSTOM_COMMAND)
3592 {
3593 m_mainworker.m_eventsystem.CustomCommand(itt->_idx, itt->_command);
3594 }
3595 else if (itt->_ItemType == TITEM_CUSTOM_EVENT)
3596 {
3597 Json::Value eventInfo;
3598 eventInfo["name"] = itt->_ID;
3599 eventInfo["data"] = itt->_sValue;
3600 m_mainworker.m_notificationsystem.Notify(Notification::DZ_CUSTOM, Notification::STATUS_INFO, JSonToRawString(eventInfo));
3601 }
3602
3603 ++itt;
3604 }
3605 _items2do.clear();
3606 }
3607 }
3608
SetDatabaseName(const std::string & DBName)3609 void CSQLHelper::SetDatabaseName(const std::string& DBName)
3610 {
3611 m_dbase_name = DBName;
3612 }
3613
DoesColumnExistsInTable(const std::string & columnname,const std::string & tablename)3614 bool CSQLHelper::DoesColumnExistsInTable(const std::string& columnname, const std::string& tablename)
3615 {
3616 if (!m_dbase)
3617 {
3618 _log.Log(LOG_ERROR, "Database not open!!...Check your user rights!..");
3619 return false;
3620 }
3621 bool columnExists = false;
3622
3623 sqlite3_stmt* statement;
3624 std::string szQuery = "SELECT " + columnname + " FROM " + tablename;
3625 if (sqlite3_prepare_v2(m_dbase, szQuery.c_str(), -1, &statement, NULL) == SQLITE_OK)
3626 {
3627 columnExists = true;
3628 sqlite3_finalize(statement);
3629 }
3630 return columnExists;
3631 }
3632
safe_exec_no_return(const char * fmt,...)3633 void CSQLHelper::safe_exec_no_return(const char* fmt, ...)
3634 {
3635 if (!m_dbase)
3636 return;
3637
3638 va_list args;
3639 va_start(args, fmt);
3640 char* zQuery = sqlite3_vmprintf(fmt, args);
3641 va_end(args);
3642 if (!zQuery)
3643 return;
3644 sqlite3_exec(m_dbase, zQuery, NULL, NULL, NULL);
3645 sqlite3_free(zQuery);
3646 }
3647
safe_UpdateBlobInTableWithID(const std::string & Table,const std::string & Column,const std::string & sID,const std::string & BlobData)3648 bool CSQLHelper::safe_UpdateBlobInTableWithID(const std::string& Table, const std::string& Column, const std::string& sID, const std::string& BlobData)
3649 {
3650 if (!m_dbase)
3651 return false;
3652 sqlite3_stmt* stmt = NULL;
3653 char* zQuery = sqlite3_mprintf("UPDATE %q SET %q = ? WHERE ID=%q", Table.c_str(), Column.c_str(), sID.c_str());
3654 if (!zQuery)
3655 {
3656 _log.Log(LOG_ERROR, "SQL: Out of memory, or invalid printf!....");
3657 return false;
3658 }
3659 int rc = sqlite3_prepare_v2(m_dbase, zQuery, -1, &stmt, NULL);
3660 sqlite3_free(zQuery);
3661 if (rc != SQLITE_OK) {
3662 return false;
3663 }
3664 rc = sqlite3_bind_blob(stmt, 1, BlobData.c_str(), BlobData.size(), SQLITE_STATIC);
3665 if (rc != SQLITE_OK) {
3666 return false;
3667 }
3668 rc = sqlite3_step(stmt);
3669 if (rc != SQLITE_DONE)
3670 {
3671 return false;
3672 }
3673 sqlite3_finalize(stmt);
3674 return true;
3675 }
3676
safe_query(const char * fmt,...)3677 std::vector<std::vector<std::string> > CSQLHelper::safe_query(const char* fmt, ...)
3678 {
3679 va_list args;
3680 va_start(args, fmt);
3681 char* zQuery = sqlite3_vmprintf(fmt, args);
3682 va_end(args);
3683 if (!zQuery)
3684 {
3685 _log.Log(LOG_ERROR, "SQL: Out of memory, or invalid printf!....");
3686 std::vector<std::vector<std::string> > results;
3687 return results;
3688 }
3689 std::vector<std::vector<std::string> > results = query(zQuery);
3690 sqlite3_free(zQuery);
3691 return results;
3692 }
3693
query(const std::string & szQuery)3694 std::vector<std::vector<std::string> > CSQLHelper::query(const std::string& szQuery)
3695 {
3696 if (!m_dbase)
3697 {
3698 _log.Log(LOG_ERROR, "Database not open!!...Check your user rights!..");
3699 std::vector<std::vector<std::string> > results;
3700 return results;
3701 }
3702 std::lock_guard<std::mutex> l(m_sqlQueryMutex);
3703
3704 sqlite3_stmt* statement;
3705 std::vector<std::vector<std::string> > results;
3706
3707 if (sqlite3_prepare_v2(m_dbase, szQuery.c_str(), -1, &statement, 0) == SQLITE_OK)
3708 {
3709 int cols = sqlite3_column_count(statement);
3710 while (true)
3711 {
3712 int result = sqlite3_step(statement);
3713 if (result == SQLITE_ROW)
3714 {
3715 std::vector<std::string> values;
3716 for (int col = 0; col < cols; col++)
3717 {
3718 char* value = (char*)sqlite3_column_text(statement, col);
3719 if ((value == 0) && (col == 0))
3720 break;
3721 else if (value == 0)
3722 values.push_back(std::string("")); //insert empty string
3723 else
3724 values.push_back(value);
3725 }
3726 if (values.size() > 0)
3727 results.push_back(values);
3728 }
3729 else
3730 {
3731 break;
3732 }
3733 }
3734 sqlite3_finalize(statement);
3735 }
3736
3737 std::string error = sqlite3_errmsg(m_dbase);
3738 if (error != "not an error")
3739 _log.Log(LOG_ERROR, "SQL Query(\"%s\") : %s", szQuery.c_str(), error.c_str());
3740 return results;
3741 }
3742
safe_queryBlob(const char * fmt,...)3743 std::vector<std::vector<std::string> > CSQLHelper::safe_queryBlob(const char* fmt, ...)
3744 {
3745 va_list args;
3746 std::vector<std::vector<std::string> > results;
3747 va_start(args, fmt);
3748 char* zQuery = sqlite3_vmprintf(fmt, args);
3749 va_end(args);
3750 if (!zQuery)
3751 {
3752 _log.Log(LOG_ERROR, "SQL: Out of memory, or invalid printf!....");
3753 std::vector<std::vector<std::string> > results;
3754 return results;
3755 }
3756 results = queryBlob(zQuery);
3757 sqlite3_free(zQuery);
3758 return results;
3759 }
3760
queryBlob(const std::string & szQuery)3761 std::vector<std::vector<std::string> > CSQLHelper::queryBlob(const std::string& szQuery)
3762 {
3763 if (!m_dbase)
3764 {
3765 _log.Log(LOG_ERROR, "Database not open!!...Check your user rights!..");
3766 std::vector<std::vector<std::string> > results;
3767 return results;
3768 }
3769 std::lock_guard<std::mutex> l(m_sqlQueryMutex);
3770
3771 sqlite3_stmt* statement;
3772 std::vector<std::vector<std::string> > results;
3773
3774 if (sqlite3_prepare_v2(m_dbase, szQuery.c_str(), -1, &statement, 0) == SQLITE_OK)
3775 {
3776 int cols = sqlite3_column_count(statement);
3777 while (true)
3778 {
3779 int result = sqlite3_step(statement);
3780 if (result == SQLITE_ROW)
3781 {
3782 std::vector<std::string> values;
3783 for (int col = 0; col < cols; col++)
3784 {
3785 int blobSize = sqlite3_column_bytes(statement, col);
3786 char* value = (char*)sqlite3_column_blob(statement, col);
3787 if ((blobSize == 0) && (col == 0))
3788 break;
3789 else if (value == 0)
3790 values.push_back(std::string("")); //insert empty string
3791 else
3792 values.push_back(std::string(value, value + blobSize));
3793 }
3794 if (values.size() > 0)
3795 results.push_back(values);
3796 }
3797 else
3798 {
3799 break;
3800 }
3801 }
3802 sqlite3_finalize(statement);
3803 }
3804
3805 std::string error = sqlite3_errmsg(m_dbase);
3806 if (error != "not an error")
3807 _log.Log(LOG_ERROR, "SQL Query(\"%s\") : %s", szQuery.c_str(), error.c_str());
3808 return results;
3809 }
3810
CreateDevice(const int HardwareID,const int SensorType,const int SensorSubType,std::string & devname,const unsigned long nid,const std::string & soptions)3811 uint64_t CSQLHelper::CreateDevice(const int HardwareID, const int SensorType, const int SensorSubType, std::string& devname, const unsigned long nid, const std::string& soptions)
3812 {
3813 uint64_t DeviceRowIdx = (uint64_t)-1;
3814 char ID[20];
3815 sprintf(ID, "%lu", nid);
3816
3817 #ifdef ENABLE_PYTHON
3818 {
3819 std::vector<std::vector<std::string> > result;
3820 result = m_sql.safe_query("SELECT Type FROM Hardware WHERE (ID == %d)", HardwareID);
3821 if (!result.empty())
3822 {
3823 std::vector<std::string> sd = result[0];
3824 _eHardwareTypes Type = (_eHardwareTypes)atoi(sd[0].c_str());
3825 if (Type == HTYPE_PythonPlugin)
3826 {
3827 // Not allowed to add device to plugin HW (plugin framework does not use key column "ID" but instead uses column "unit" as key)
3828 _log.Log(LOG_ERROR, "CSQLHelper::CreateDevice: Not allowed to add device owned by plugin %u!", HardwareID);
3829 return DeviceRowIdx;
3830 }
3831 }
3832 }
3833 #endif
3834
3835 switch (SensorType)
3836 {
3837
3838 case pTypeTEMP:
3839 case pTypeWEIGHT:
3840 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 0, "0.0", devname);
3841 break;
3842 case pTypeUV:
3843 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 0, "0.0;0.0", devname);
3844 break;
3845 case pTypeRAIN:
3846 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 0, "0;0", devname);
3847 break;
3848 case pTypeTEMP_BARO:
3849 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 0, "0.0;1038.0;0;188.0", devname);
3850 break;
3851 case pTypeHUM:
3852 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 50, "1", devname);
3853 break;
3854 case pTypeTEMP_HUM:
3855 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 0, "0.0;50;1", devname);
3856 break;
3857 case pTypeTEMP_HUM_BARO:
3858 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 0, "0.0;50;1;1010;1", devname);
3859 break;
3860 case pTypeRFXMeter:
3861 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 10, 255, 0, "0", devname);
3862 break;
3863 case pTypeUsage:
3864 case pTypeLux:
3865 case pTypeP1Gas:
3866 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 0, "0", devname);
3867 break;
3868 case pTypeP1Power:
3869 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 0, "0;0;0;0;0;0", devname);
3870 break;
3871 case pTypeAirQuality:
3872 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 0, devname);
3873 break;
3874 case pTypeCURRENT:
3875 //Current/Ampere
3876 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 0, "0.0;0.0;0.0", devname);
3877 break;
3878 case pTypeThermostat: //Thermostat Setpoint
3879 {
3880 unsigned char ID1 = (unsigned char)((nid & 0xFF000000) >> 24);
3881 unsigned char ID2 = (unsigned char)((nid & 0x00FF0000) >> 16);
3882 unsigned char ID3 = (unsigned char)((nid & 0x0000FF00) >> 8);
3883 unsigned char ID4 = (unsigned char)((nid & 0x000000FF));
3884 sprintf(ID, "%X%02X%02X%02X", ID1, ID2, ID3, ID4);
3885
3886 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 0, "20.5", devname);
3887 break;
3888 }
3889
3890 case pTypeGeneral:
3891 {
3892 switch (SensorSubType)
3893 {
3894 case sTypePressure: //Pressure (Bar)
3895 case sTypePercentage: //Percentage
3896 case sTypeWaterflow: //Waterflow
3897 {
3898 std::string rID = std::string(ID);
3899 padLeft(rID, 8, '0');
3900 DeviceRowIdx = UpdateValue(HardwareID, rID.c_str(), 1, SensorType, SensorSubType, 12, 255, 0, "0.0", devname);
3901 }
3902 break;
3903 case sTypeCounterIncremental:
3904 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 0, "0", devname);
3905 break;
3906 case sTypeManagedCounter:
3907 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 0, "-1;0", devname);
3908 break;
3909 case sTypeVoltage: //Voltage
3910 {
3911 std::string rID = std::string(ID);
3912 padLeft(rID, 8, '0');
3913 DeviceRowIdx = UpdateValue(HardwareID, rID.c_str(), 1, SensorType, SensorSubType, 12, 255, 0, "0.000", devname);
3914 }
3915 break;
3916 case sTypeTextStatus: //Text
3917 {
3918 std::string rID = std::string(ID);
3919 padLeft(rID, 8, '0');
3920 DeviceRowIdx = UpdateValue(HardwareID, rID.c_str(), 1, SensorType, SensorSubType, 12, 255, 0, "Hello World", devname);
3921 }
3922 break;
3923 case sTypeAlert: //Alert
3924 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 0, "No Alert!", devname);
3925 break;
3926 case sTypeSoundLevel: //Sound Level
3927 {
3928 std::string rID = std::string(ID);
3929 padLeft(rID, 8, '0');
3930 DeviceRowIdx = UpdateValue(HardwareID, rID.c_str(), 1, SensorType, SensorSubType, 12, 255, 0, "65", devname);
3931 }
3932 break;
3933 case sTypeBaro: //Barometer (hPa)
3934 {
3935 std::string rID = std::string(ID);
3936 padLeft(rID, 8, '0');
3937 DeviceRowIdx = UpdateValue(HardwareID, rID.c_str(), 1, SensorType, SensorSubType, 12, 255, 0, "1021.34;0", devname);
3938 }
3939 break;
3940 case sTypeVisibility: //Visibility (km)
3941 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 0, "10.3", devname);
3942 break;
3943 case sTypeDistance: //Distance (cm)
3944 {
3945 std::string rID = std::string(ID);
3946 padLeft(rID, 8, '0');
3947 DeviceRowIdx = UpdateValue(HardwareID, rID.c_str(), 1, SensorType, SensorSubType, 12, 255, 0, "123.4", devname);
3948 }
3949 break;
3950 case sTypeSoilMoisture: //Soil Moisture
3951 {
3952 std::string rID = std::string(ID);
3953 padLeft(rID, 8, '0');
3954 DeviceRowIdx = UpdateValue(HardwareID, rID.c_str(), 1, SensorType, SensorSubType, 12, 255, 3, devname);
3955 }
3956 break;
3957 case sTypeLeafWetness: //Leaf Wetness
3958 {
3959 std::string rID = std::string(ID);
3960 padLeft(rID, 8, '0');
3961 DeviceRowIdx = UpdateValue(HardwareID, rID.c_str(), 1, SensorType, SensorSubType, 12, 255, 2, devname);
3962 }
3963 break;
3964 case sTypeKwh: //kWh
3965 {
3966 std::string rID = std::string(ID);
3967 padLeft(rID, 8, '0');
3968 DeviceRowIdx = UpdateValue(HardwareID, rID.c_str(), 1, SensorType, SensorSubType, 12, 255, 0, "0;0.0", devname);
3969 }
3970 break;
3971 case sTypeCurrent: //Current (Single)
3972 {
3973 std::string rID = std::string(ID);
3974 padLeft(rID, 8, '0');
3975 DeviceRowIdx = UpdateValue(HardwareID, rID.c_str(), 1, SensorType, SensorSubType, 12, 255, 0, "0.0", devname);
3976 }
3977 break;
3978 case sTypeSolarRadiation: //Solar Radiation
3979 {
3980 std::string rID = std::string(ID);
3981 padLeft(rID, 8, '0');
3982 DeviceRowIdx = UpdateValue(HardwareID, rID.c_str(), 1, SensorType, SensorSubType, 12, 255, 0, "1.0", devname);
3983 }
3984 break;
3985 case sTypeCustom: //Custom
3986 {
3987 if (!soptions.empty())
3988 {
3989 std::string rID = std::string(ID);
3990 padLeft(rID, 8, '0');
3991 DeviceRowIdx = UpdateValue(HardwareID, rID.c_str(), 1, SensorType, SensorSubType, 12, 255, 0, "0.0", devname);
3992 if (DeviceRowIdx != (uint64_t)-1)
3993 {
3994 //Set the Label
3995 m_sql.safe_query("UPDATE DeviceStatus SET Options='%q' WHERE (ID==%" PRIu64 ")", soptions.c_str(), DeviceRowIdx);
3996 }
3997 }
3998 break;
3999 }
4000 }
4001 break;
4002 }
4003
4004 case pTypeWIND:
4005 {
4006 switch (SensorSubType)
4007 {
4008 case sTypeWIND1: // sTypeWIND1
4009 case sTypeWIND4: //Wind + Temp + Chill
4010 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 0, "0;N;0;0;0;0", devname);
4011 break;
4012 }
4013 break;
4014 }
4015
4016 case pTypeGeneralSwitch:
4017 {
4018 switch (SensorSubType)
4019 {
4020 case sSwitchGeneralSwitch: //Switch
4021 {
4022 sprintf(ID, "%08lX", nid);
4023 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 0, "100", devname);
4024 }
4025 break;
4026 case sSwitchTypeSelector: //Selector Switch
4027 {
4028 unsigned char ID1 = (unsigned char)((nid & 0xFF000000) >> 24);
4029 unsigned char ID2 = (unsigned char)((nid & 0x00FF0000) >> 16);
4030 unsigned char ID3 = (unsigned char)((nid & 0x0000FF00) >> 8);
4031 unsigned char ID4 = (unsigned char)((nid & 0x000000FF));
4032 sprintf(ID, "%02X%02X%02X%02X", ID1, ID2, ID3, ID4);
4033 DeviceRowIdx = UpdateValue(HardwareID, ID, 1, SensorType, SensorSubType, 12, 255, 0, "0", devname);
4034 if (DeviceRowIdx != (uint64_t)-1)
4035 {
4036 //Set switch type to selector
4037 m_sql.safe_query("UPDATE DeviceStatus SET SwitchType=%d WHERE (ID==%" PRIu64 ")", STYPE_Selector, DeviceRowIdx);
4038 //Set default device options
4039 m_sql.SetDeviceOptions(DeviceRowIdx, BuildDeviceOptions("SelectorStyle:0;LevelNames:Off|Level1|Level2|Level3", false));
4040 }
4041 }
4042 break;
4043 }
4044 break;
4045 }
4046
4047 case pTypeColorSwitch:
4048 {
4049 switch (SensorSubType)
4050 {
4051 case sTypeColor_RGB: //RGB switch
4052 case sTypeColor_RGB_W: //RGBW switch
4053 case sTypeColor_RGB_CW_WW: //RGBWW switch
4054 case sTypeColor_RGB_W_Z: //RGBWZ switch
4055 case sTypeColor_RGB_CW_WW_Z: //RGBWWZ switch
4056 case sTypeColor_White: //Monochrome white switch
4057 case sTypeColor_CW_WW: //Adjustable color temperature white switch
4058 {
4059 std::string rID = std::string(ID);
4060 padLeft(rID, 8, '0');
4061 DeviceRowIdx = UpdateValue(HardwareID, rID.c_str(), 1, SensorType, SensorSubType, 12, 255, 1, devname);
4062 if (DeviceRowIdx != (uint64_t)-1)
4063 {
4064 //Set switch type to dimmer
4065 m_sql.safe_query("UPDATE DeviceStatus SET SwitchType=%d WHERE (ID==%" PRIu64 ")", STYPE_Dimmer, DeviceRowIdx);
4066 }
4067 }
4068 break;
4069 }
4070 break;
4071 }
4072 }
4073
4074 if (DeviceRowIdx != (uint64_t)-1)
4075 {
4076 m_sql.safe_query("UPDATE DeviceStatus SET Used=1 WHERE (ID==%" PRIu64 ")", DeviceRowIdx);
4077 m_mainworker.m_eventsystem.GetCurrentStates();
4078 }
4079
4080 return DeviceRowIdx;
4081 }
4082
4083
UpdateValue(const int HardwareID,const char * ID,const unsigned char unit,const unsigned char devType,const unsigned char subType,const unsigned char signallevel,const unsigned char batterylevel,const int nValue,std::string & devname,const bool bUseOnOffAction)4084 uint64_t CSQLHelper::UpdateValue(const int HardwareID, const char* ID, const unsigned char unit, const unsigned char devType, const unsigned char subType, const unsigned char signallevel, const unsigned char batterylevel, const int nValue, std::string& devname, const bool bUseOnOffAction)
4085 {
4086 return UpdateValue(HardwareID, ID, unit, devType, subType, signallevel, batterylevel, nValue, "", devname, bUseOnOffAction);
4087 }
4088
UpdateValue(const int HardwareID,const char * ID,const unsigned char unit,const unsigned char devType,const unsigned char subType,const unsigned char signallevel,const unsigned char batterylevel,const char * sValue,std::string & devname,const bool bUseOnOffAction)4089 uint64_t CSQLHelper::UpdateValue(const int HardwareID, const char* ID, const unsigned char unit, const unsigned char devType, const unsigned char subType, const unsigned char signallevel, const unsigned char batterylevel, const char* sValue, std::string& devname, const bool bUseOnOffAction)
4090 {
4091 return UpdateValue(HardwareID, ID, unit, devType, subType, signallevel, batterylevel, 0, sValue, devname, bUseOnOffAction);
4092 }
4093
UpdateValue(const int HardwareID,const char * ID,const unsigned char unit,const unsigned char devType,const unsigned char subType,const unsigned char signallevel,const unsigned char batterylevel,const int nValue,const char * sValue,std::string & devname,const bool bUseOnOffAction)4094 uint64_t CSQLHelper::UpdateValue(const int HardwareID, const char* ID, const unsigned char unit, const unsigned char devType, const unsigned char subType, const unsigned char signallevel, const unsigned char batterylevel, const int nValue, const char* sValue, std::string& devname, const bool bUseOnOffAction)
4095 {
4096 uint64_t devRowID = UpdateValueInt(HardwareID, ID, unit, devType, subType, signallevel, batterylevel, nValue, sValue, devname, bUseOnOffAction);
4097 if (devRowID == (uint64_t)-1)
4098 return -1;
4099
4100 if (!IsLightOrSwitch(devType, subType))
4101 {
4102 return devRowID;
4103 }
4104
4105 //Get the ID of this device
4106 std::vector<std::vector<std::string> > result, result2;
4107
4108 result = safe_query("SELECT ID FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)", HardwareID, ID, unit, devType, subType);
4109 if (result.empty())
4110 return devRowID; //should never happen, because it was previously inserted if non-existent
4111
4112 std::string idx = result[0][0];
4113
4114 time_t now = time(0);
4115 struct tm ltime;
4116 localtime_r(&now, <ime);
4117
4118 //Check if this switch was a Sub/Slave device for other devices, if so adjust the state of those other devices
4119 result = safe_query("SELECT A.ParentID, B.Name, B.HardwareID, B.[Type], B.[SubType], B.Unit FROM LightSubDevices as A, DeviceStatus as B WHERE (A.DeviceRowID=='%q') AND (A.DeviceRowID!=A.ParentID) AND (B.[ID] == A.ParentID)", idx.c_str());
4120 if (!result.empty())
4121 {
4122 //This is a sub/slave device for another main device
4123 //Set the Main Device state to the same state as this device
4124 for (const auto& itt : result)
4125 {
4126 std::vector<std::string> sd = itt;
4127 safe_query(
4128 "UPDATE DeviceStatus SET nValue=%d, sValue='%q', LastUpdate='%04d-%02d-%02d %02d:%02d:%02d' WHERE (ID == '%q')",
4129 nValue,
4130 sValue,
4131 ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday, ltime.tm_hour, ltime.tm_min, ltime.tm_sec,
4132 sd[0].c_str()
4133 );
4134
4135 //Call the EventSystem for the main switch
4136 uint64_t ParentID = (uint64_t)atoll(sd[0].c_str());
4137 std::string ParentName = sd[1];
4138 int ParentHardwareID = atoi(sd[2].c_str());
4139 unsigned char ParentType = (unsigned char)atoi(sd[3].c_str());
4140 unsigned char ParentSubType = (unsigned char)atoi(sd[4].c_str());
4141 unsigned char ParentUnit = (unsigned char)atoi(sd[5].c_str());
4142 m_mainworker.m_eventsystem.ProcessDevice(ParentHardwareID, ParentID, ParentUnit, ParentType, ParentSubType, signallevel, batterylevel, nValue, sValue, ParentName);
4143
4144 //Set the status of all slave devices from this device (except the one we just received) to off
4145 //Check if this switch was a Sub/Slave device for other devices, if so adjust the state of those other devices
4146 result2 = safe_query(
4147 "SELECT a.DeviceRowID, b.Type FROM LightSubDevices a, DeviceStatus b WHERE (a.ParentID=='%q') AND (a.DeviceRowID!='%q') AND (b.ID == a.DeviceRowID) AND (a.DeviceRowID!=a.ParentID)",
4148 sd[0].c_str(),
4149 idx.c_str()
4150 );
4151 if (!result2.empty())
4152 {
4153 for (const auto& itt2 : result2)
4154 {
4155 std::vector<std::string> sd = itt2;
4156 int oDevType = atoi(sd[1].c_str());
4157 int newnValue = 0;
4158 switch (oDevType)
4159 {
4160 case pTypeLighting1:
4161 newnValue = light1_sOff;
4162 break;
4163 case pTypeLighting2:
4164 newnValue = light2_sOff;
4165 break;
4166 case pTypeLighting3:
4167 newnValue = light3_sOff;
4168 break;
4169 case pTypeLighting4:
4170 newnValue = 0;//light4_sOff;
4171 break;
4172 case pTypeLighting5:
4173 newnValue = light5_sOff;
4174 break;
4175 case pTypeLighting6:
4176 newnValue = light6_sOff;
4177 break;
4178 case pTypeColorSwitch:
4179 newnValue = Color_LedOff;
4180 break;
4181 case pTypeSecurity1:
4182 newnValue = sStatusNormal;
4183 break;
4184 case pTypeSecurity2:
4185 newnValue = 0;// sStatusNormal;
4186 break;
4187 case pTypeCurtain:
4188 newnValue = curtain_sOpen;
4189 break;
4190 case pTypeBlinds:
4191 newnValue = blinds_sOpen;
4192 break;
4193 case pTypeRFY:
4194 newnValue = rfy_sUp;
4195 break;
4196 case pTypeThermostat2:
4197 newnValue = thermostat2_sOff;
4198 break;
4199 case pTypeThermostat3:
4200 newnValue = thermostat3_sOff;
4201 break;
4202 case pTypeThermostat4:
4203 newnValue = thermostat4_sOff;
4204 break;
4205 case pTypeRadiator1:
4206 newnValue = Radiator1_sNight;
4207 break;
4208 case pTypeGeneralSwitch:
4209 newnValue = gswitch_sOff;
4210 break;
4211 case pTypeHomeConfort:
4212 newnValue = HomeConfort_sOff;
4213 break;
4214 case pTypeFS20:
4215 newnValue = fs20_sOff;
4216 break;
4217 default:
4218 continue;
4219 }
4220 safe_query(
4221 "UPDATE DeviceStatus SET nValue=%d, sValue='%q', LastUpdate='%04d-%02d-%02d %02d:%02d:%02d' WHERE (ID == '%q')",
4222 newnValue,
4223 "",
4224 ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday, ltime.tm_hour, ltime.tm_min, ltime.tm_sec,
4225 sd[0].c_str()
4226 );
4227 }
4228 }
4229 // TODO: Should plugin be notified?
4230 }
4231 }
4232
4233 //If this is a 'Main' device, and it has Sub/Slave devices,
4234 //set the status of the Sub/Slave devices to Off, as we might be out of sync then
4235 result = safe_query(
4236 "SELECT a.DeviceRowID, b.Type FROM LightSubDevices a, DeviceStatus b WHERE (a.ParentID=='%q') AND (b.ID == a.DeviceRowID) AND (a.DeviceRowID!=a.ParentID)",
4237 idx.c_str()
4238 );
4239 if (!result.empty())
4240 {
4241 //set the status to off
4242 for (const auto& itt : result)
4243 {
4244 std::vector<std::string> sd = itt;
4245 int oDevType = atoi(sd[1].c_str());
4246 int newnValue = 0;
4247 switch (oDevType)
4248 {
4249 case pTypeLighting1:
4250 newnValue = light1_sOff;
4251 break;
4252 case pTypeLighting2:
4253 newnValue = light2_sOff;
4254 break;
4255 case pTypeLighting3:
4256 newnValue = light3_sOff;
4257 break;
4258 case pTypeLighting4:
4259 newnValue = 0;//light4_sOff;
4260 break;
4261 case pTypeLighting5:
4262 newnValue = light5_sOff;
4263 break;
4264 case pTypeLighting6:
4265 newnValue = light6_sOff;
4266 break;
4267 case pTypeColorSwitch:
4268 newnValue = Color_LedOff;
4269 break;
4270 case pTypeSecurity1:
4271 newnValue = sStatusNormal;
4272 break;
4273 case pTypeSecurity2:
4274 newnValue = 0;// sStatusNormal;
4275 break;
4276 case pTypeCurtain:
4277 newnValue = curtain_sOpen;
4278 break;
4279 case pTypeBlinds:
4280 newnValue = blinds_sOpen;
4281 break;
4282 case pTypeRFY:
4283 newnValue = rfy_sUp;
4284 break;
4285 case pTypeThermostat2:
4286 newnValue = thermostat2_sOff;
4287 break;
4288 case pTypeThermostat3:
4289 newnValue = thermostat3_sOff;
4290 break;
4291 case pTypeThermostat4:
4292 newnValue = thermostat4_sOff;
4293 break;
4294 case pTypeRadiator1:
4295 newnValue = Radiator1_sNight;
4296 break;
4297 case pTypeGeneralSwitch:
4298 newnValue = gswitch_sOff;
4299 break;
4300 case pTypeHomeConfort:
4301 newnValue = HomeConfort_sOff;
4302 break;
4303 case pTypeFS20:
4304 newnValue = fs20_sOff;
4305 break;
4306 default:
4307 continue;
4308 }
4309 safe_query(
4310 "UPDATE DeviceStatus SET nValue=%d, sValue='%q', LastUpdate='%04d-%02d-%02d %02d:%02d:%02d' WHERE (ID == '%q')",
4311 newnValue,
4312 "",
4313 ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday, ltime.tm_hour, ltime.tm_min, ltime.tm_sec,
4314 sd[0].c_str()
4315 );
4316 }
4317 // TODO: Should plugin be notified?
4318 }
4319 return devRowID;
4320 }
4321
InsertDevice(const int HardwareID,const char * ID,const unsigned char unit,const unsigned char devType,const unsigned char subType,const int switchType,const int nValue,const char * sValue,const std::string & devname,const unsigned char signallevel,const unsigned char batterylevel,const int used)4322 uint64_t CSQLHelper::InsertDevice(const int HardwareID, const char* ID, const unsigned char unit, const unsigned char devType, const unsigned char subType, const int switchType, const int nValue, const char* sValue, const std::string& devname, const unsigned char signallevel, const unsigned char batterylevel, const int used)
4323 {
4324 //TODO: 'unsigned char unit' only allows 256 devices / plugin
4325 //TODO: return -1 as error code does not make sense for a function returning an unsigned value
4326 std::vector<std::vector<std::string> > result;
4327 uint64_t ulID = 0;
4328 std::string name = devname;
4329
4330 if (!m_bAcceptNewHardware)
4331 {
4332 #ifdef _DEBUG
4333 _log.Log(LOG_STATUS, "Device creation failed, Domoticz settings prevent accepting new devices.");
4334 #endif
4335 return -1; //We do not allow new devices
4336 }
4337
4338 if (devname == "")
4339 {
4340 name = "Unknown";
4341 }
4342
4343 safe_query(
4344 "INSERT INTO DeviceStatus (HardwareID, DeviceID, Unit, Type, SubType, SwitchType, SignalLevel, BatteryLevel, nValue, sValue, Name) "
4345 "VALUES ('%d','%q','%d','%d','%d','%d','%d','%d','%d','%q','%q')",
4346 HardwareID, ID, unit,
4347 devType, subType, switchType,
4348 signallevel, batterylevel,
4349 nValue, sValue, name.c_str());
4350
4351 //Get new ID
4352 result = safe_query(
4353 "SELECT ID FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)",
4354 HardwareID, ID, unit, devType, subType);
4355 if (result.empty())
4356 {
4357 _log.Log(LOG_ERROR, "Serious database error, problem getting ID from DeviceStatus!");
4358 return -1;
4359 }
4360 ulID = std::stoull(result[0][0]);
4361
4362 return ulID;
4363 }
4364
DoesDeviceExist(const int HardwareID,const char * ID,const unsigned char unit,const unsigned char devType,const unsigned char subType)4365 bool CSQLHelper::DoesDeviceExist(const int HardwareID, const char* ID, const unsigned char unit, const unsigned char devType, const unsigned char subType) {
4366 std::vector<std::vector<std::string> > result;
4367 result = safe_query("SELECT ID,Name FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)", HardwareID, ID, unit, devType, subType);
4368 if (result.empty()) {
4369 return false;
4370 }
4371 else {
4372 return true;
4373 }
4374 }
4375
UpdateValueInt(const int HardwareID,const char * ID,const unsigned char unit,const unsigned char devType,const unsigned char subType,const unsigned char signallevel,const unsigned char batterylevel,const int nValue,const char * sValue,std::string & devname,const bool bUseOnOffAction)4376 uint64_t CSQLHelper::UpdateValueInt(const int HardwareID, const char* ID, const unsigned char unit, const unsigned char devType, const unsigned char subType, const unsigned char signallevel, const unsigned char batterylevel, const int nValue, const char* sValue, std::string& devname, const bool bUseOnOffAction)
4377 //TODO: 'unsigned char unit' only allows 256 devices / plugin
4378 //TODO: return -1 as error code does not make sense for a function returning an unsigned value
4379 {
4380 if (!m_dbase)
4381 return -1;
4382
4383 uint64_t ulID = 0;
4384 bool bDeviceUsed = false;
4385 bool bSameDeviceStatusValue = false;
4386 int old_nValue = -1;
4387 std::string old_sValue;
4388 _eSwitchType stype = STYPE_OnOff;
4389
4390 std::vector<std::vector<std::string> > result;
4391 result = safe_query("SELECT ID, Name, Used, SwitchType, nValue, sValue, LastUpdate, Options FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)", HardwareID, ID, unit, devType, subType);
4392 if (result.empty())
4393 {
4394 //Insert
4395 ulID = InsertDevice(HardwareID, ID, unit, devType, subType, 0, nValue, sValue, devname, signallevel, batterylevel);
4396
4397 if (ulID < 1)
4398 return -1;
4399
4400 #ifdef ENABLE_PYTHON
4401 //TODO: Plugins should perhaps be blocked from implicitly adding a device by update? It's most likely a bug due to updating a removed device..
4402 CDomoticzHardwareBase* pHardware = m_mainworker.GetHardware(HardwareID);
4403 if (pHardware != NULL && pHardware->HwdType == HTYPE_PythonPlugin)
4404 {
4405 _log.Debug(DEBUG_NORM, "CSQLHelper::UpdateValueInt: Notifying plugin %u about creation of device %u", HardwareID, unit);
4406 Plugins::CPlugin* pPlugin = (Plugins::CPlugin*)pHardware;
4407 pPlugin->DeviceAdded(unit);
4408 }
4409 #endif
4410 }
4411 else
4412 {
4413 //Update
4414 ulID = std::stoull(result[0][0]);
4415 std::string sOption = result[0][7];
4416 auto options = BuildDeviceOptions(sOption);
4417 devname = result[0][1];
4418 bDeviceUsed = atoi(result[0][2].c_str()) != 0;
4419 stype = (_eSwitchType)atoi(result[0][3].c_str());
4420 old_nValue = atoi(result[0][4].c_str());
4421 old_sValue = result[0][5];
4422 time_t now = time(0);
4423 struct tm ltime;
4424 localtime_r(&now, <ime);
4425 //Commit: If Option 1: energy is computed as usage*time
4426 //Default is option 0, read from device
4427 if (options["EnergyMeterMode"] == "1" && devType == pTypeGeneral && subType == sTypeKwh)
4428 {
4429 std::vector<std::string> parts;
4430 struct tm ntime;
4431 double interval;
4432 float nEnergy;
4433 char sCompValue[100];
4434 std::string sLastUpdate = result[0][6];
4435 time_t lutime;
4436 ParseSQLdatetime(lutime, ntime, sLastUpdate, ltime.tm_isdst);
4437
4438 interval = difftime(now, lutime);
4439 StringSplit(result[0][5].c_str(), ";", parts);
4440 nEnergy = static_cast<float>(strtof(parts[0].c_str(), NULL) * interval / 3600 + strtof(parts[1].c_str(), NULL)); //Rob: whats happening here... strtof ?
4441 StringSplit(sValue, ";", parts);
4442 sprintf(sCompValue, "%s;%.1f", parts[0].c_str(), nEnergy);
4443 sValue = sCompValue;
4444 }
4445 //~ use different update queries based on the device type
4446 if (devType == pTypeGeneral && subType == sTypeCounterIncremental)
4447 {
4448 result = safe_query(
4449 "UPDATE DeviceStatus SET SignalLevel=%d, BatteryLevel=%d, nValue= nValue + %d, sValue= sValue + '%q', LastUpdate='%04d-%02d-%02d %02d:%02d:%02d' "
4450 "WHERE (ID = %" PRIu64 ")",
4451 signallevel, batterylevel,
4452 nValue, sValue,
4453 ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday, ltime.tm_hour, ltime.tm_min, ltime.tm_sec,
4454 ulID);
4455 }
4456 else
4457 {
4458 if (
4459 (stype == STYPE_DoorContact) ||
4460 (stype == STYPE_DoorLock) ||
4461 (stype == STYPE_DoorLockInverted) ||
4462 (stype == STYPE_Contact)
4463 )
4464 {
4465 //Check if we received the same state as before, if yes, don't do anything (only update)
4466 //This is specially handy for devices that send a keep-alive status every xx minutes
4467 //like professional alarm system equipment
4468 //.. we should make this an option of course
4469 bSameDeviceStatusValue = (
4470 (nValue == old_nValue) &&
4471 (sValue == old_sValue)
4472 );
4473 }
4474
4475 if ((devType == pTypeGeneral) && (subType == sTypeManagedCounter) || (options["AddDBLogEntry"] == "true")) {
4476 std::vector<std::string> parts, parts2;
4477 StringSplit(sValue, ";", parts);
4478 if (parts.size() == 11) {
4479 // is last part date only, or date with hour with space?
4480 StringSplit(parts[10].c_str(), " ", parts2);
4481 bool shortLog = false;
4482 if (parts2.size() > 1) {
4483 shortLog = true;
4484 }
4485 // second part is date only, or date with hour with space
4486 // In DeviceStatus table, index 0 = Usage 1, 1 = Usage 2, 2 = Delivery 1, 3 = Delivery 2, 4 = Usage current, 5 = Delivery current
4487 // In MultiMeter table, index 0 = Usage 1, 1 = Delivery 1, 2 = Usage current, 3 = Delivery current, 4 = Usage 2, 5 = Delivery 2
4488 // In MultiMeter_Calendar table, same as Multimeter table + counter1, counter2, counter3 and counter4 when shortlog is False
4489 UpdateCalendarMeter(HardwareID, ID, unit, devType, subType,
4490 shortLog,
4491 true,
4492 parts[10].c_str(),
4493 std::stoll(parts[0]),
4494 std::stoll(parts[2]),
4495 std::stoll(parts[4]),
4496 std::stoll(parts[5]),
4497 std::stoll(parts[1]),
4498 std::stoll(parts[3]),
4499 std::stoll(parts[6]),
4500 std::stoll(parts[7]),
4501 std::stoll(parts[8]),
4502 std::stoll(parts[9])
4503 );
4504 return ulID;
4505 }
4506 else if (parts.size() == 7) {
4507 // is last part date only, or date with hour with space?
4508 StringSplit(parts[6].c_str(), " ", parts2);
4509 bool shortLog = false;
4510 if (parts2.size() > 1) {
4511 shortLog = true;
4512 }
4513 // In DeviceStatus table, index 0 = Usage 1, 1 = Usage 2, 2 = Delivery 1, 3 = Delivery 2, 4 = Usage current, 5 = Delivery current
4514 // In MultiMeter table, index 0 = Usage 1, 1 = Delivery 1, 2 = Usage current, 3 = Delivery current, 4 = Usage 2, 5 = Delivery 2
4515 // In MultiMeter_Calendar table, same as Multimeter table + counter1, counter2, counter3 and counter4 when shortlog is False
4516 UpdateCalendarMeter(HardwareID, ID, unit, devType, subType,
4517 shortLog,
4518 true,
4519 parts[6].c_str(),
4520 std::stoll(parts[0]),
4521 std::stoll(parts[2]),
4522 std::stoll(parts[4]),
4523 std::stoll(parts[5]),
4524 std::stoll(parts[1]),
4525 std::stoll(parts[3])
4526 );
4527 return ulID;
4528 }
4529 else if (parts.size() == 3) {
4530 StringSplit(parts[2].c_str(), " ", parts2);
4531 // second part is date only, or date with hour with space
4532 bool shortLog = false;
4533 if (parts2.size() > 1) {
4534 shortLog = true;
4535 }
4536 UpdateCalendarMeter(HardwareID, ID, unit, devType, subType,
4537 shortLog,
4538 false,
4539 parts[2].c_str(),
4540 std::stoll(parts[0]),
4541 std::stoll(parts[1])
4542 );
4543 return ulID;
4544 }
4545 }
4546
4547 result = safe_query(
4548 "UPDATE DeviceStatus SET SignalLevel=%d, BatteryLevel=%d, nValue=%d, sValue='%q', LastUpdate='%04d-%02d-%02d %02d:%02d:%02d' "
4549 "WHERE (ID = %" PRIu64 ")",
4550 signallevel, batterylevel,
4551 nValue, sValue,
4552 ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday, ltime.tm_hour, ltime.tm_min, ltime.tm_sec,
4553 ulID);
4554 }
4555 }
4556
4557 if (bSameDeviceStatusValue)
4558 return ulID; //status has not changed, no need to process further
4559
4560 switch (devType)
4561 {
4562 case pTypeRego6XXValue:
4563 if (subType != sTypeRego6XXStatus)
4564 {
4565 break;
4566 }
4567 case pTypeGeneral:
4568 if ((devType == pTypeGeneral) && (subType != sTypeTextStatus) && (subType != sTypeAlert))
4569 {
4570 break;
4571 }
4572 case pTypeLighting1:
4573 case pTypeLighting2:
4574 case pTypeLighting3:
4575 case pTypeLighting4:
4576 case pTypeLighting5:
4577 case pTypeLighting6:
4578 case pTypeColorSwitch:
4579 case pTypeSecurity1:
4580 case pTypeSecurity2:
4581 case pTypeEvohome:
4582 case pTypeEvohomeRelay:
4583 case pTypeCurtain:
4584 case pTypeBlinds:
4585 case pTypeFan:
4586 case pTypeRFY:
4587 case pTypeChime:
4588 case pTypeThermostat2:
4589 case pTypeThermostat3:
4590 case pTypeThermostat4:
4591 case pTypeRemote:
4592 case pTypeGeneralSwitch:
4593 case pTypeHomeConfort:
4594 case pTypeFS20:
4595 case pTypeRadiator1:
4596 case pTypeHunter:
4597 if ((devType == pTypeRadiator1) && (subType != sTypeSmartwaresSwitchRadiator))
4598 break;
4599 m_LastSwitchID = ID;
4600 m_LastSwitchRowID = ulID;
4601
4602 //Add Lighting log (Skip duplicates)
4603 if (
4604 (nValue != old_nValue)
4605 || (sValue != old_sValue)
4606 || (stype == STYPE_Doorbell)
4607 || (stype == STYPE_PushOn)
4608 || (stype == STYPE_PushOff)
4609 )
4610 {
4611 result = safe_query(
4612 "INSERT INTO LightingLog (DeviceRowID, nValue, sValue, User) "
4613 "VALUES ('%" PRIu64 "', '%d', '%q', '%q')",
4614 ulID,
4615 nValue, sValue,
4616 m_mainworker.m_szLastSwitchUser.c_str()
4617 );
4618 }
4619 if (!bDeviceUsed)
4620 return ulID; //don't process further as the device is not used
4621 std::string lstatus = "";
4622
4623 result = safe_query(
4624 "SELECT Name,SwitchType,AddjValue,StrParam1,StrParam2,Options,LastLevel FROM DeviceStatus WHERE (ID = %" PRIu64 ")",
4625 ulID);
4626 if (!result.empty())
4627 {
4628 bool bHaveGroupCmd = false;
4629 int maxDimLevel = 0;
4630 int llevel = 0;
4631 bool bHaveDimmer = false;
4632 std::vector<std::string> sd = result[0];
4633 std::string Name = sd[0];
4634 _eSwitchType switchtype = (_eSwitchType)atoi(sd[1].c_str());
4635 float AddjValue = static_cast<float>(atof(sd[2].c_str()));
4636 GetLightStatus(devType, subType, switchtype, nValue, sValue, lstatus, llevel, bHaveDimmer, maxDimLevel, bHaveGroupCmd);
4637
4638 bool bIsLightSwitchOn = IsLightSwitchOn(lstatus);
4639 std::string slevel = sd[6];
4640
4641 if (
4642 ((bIsLightSwitchOn) && (llevel != 0) && (llevel != 255))
4643 || (switchtype == STYPE_BlindsPercentage)
4644 || (switchtype == STYPE_BlindsPercentageInverted)
4645 )
4646 {
4647 if (switchtype == STYPE_BlindsPercentage || switchtype == STYPE_BlindsPercentageInverted)
4648 {
4649 if (nValue == light2_sOn)
4650 llevel = 100;
4651 else if (nValue == light2_sOff)
4652 llevel = 0;
4653 }
4654 //update level for device
4655 safe_query(
4656 "UPDATE DeviceStatus SET LastLevel='%d' WHERE (ID = %" PRIu64 ")",
4657 llevel,
4658 ulID);
4659 if (bUseOnOffAction)
4660 slevel = std::to_string(llevel);
4661 }
4662
4663 if (bUseOnOffAction)
4664 {
4665 //Perform any On/Off actions
4666 std::string OnAction = sd[3];
4667 std::string OffAction = sd[4];
4668 std::string Options = sd[5];
4669
4670 if (devType == pTypeEvohome)
4671 {
4672 bIsLightSwitchOn = true;//Force use of OnAction for all actions
4673
4674 }
4675 else if (switchtype == STYPE_Selector) {
4676 bIsLightSwitchOn = (llevel > 0) ? true : false;
4677 OnAction = GetSelectorSwitchLevelAction(BuildDeviceOptions(Options, true), llevel);
4678 OffAction = GetSelectorSwitchLevelAction(BuildDeviceOptions(Options, true), 0);
4679 }
4680 if (bIsLightSwitchOn) {
4681 stdreplace(OnAction, "{level}", slevel);
4682 stdreplace(OnAction, "{status}", lstatus);
4683 stdreplace(OnAction, "{deviceid}", ID);
4684 }
4685 else {
4686 stdreplace(OffAction, "{level}", slevel);
4687 stdreplace(OffAction, "{status}", lstatus);
4688 stdreplace(OffAction, "{deviceid}", ID);
4689 }
4690 HandleOnOffAction(bIsLightSwitchOn, OnAction, OffAction);
4691 }
4692
4693 //Check if we need to email a snapshot of a Camera
4694 std::string emailserver;
4695 int n2Value;
4696 if (GetPreferencesVar("EmailServer", n2Value, emailserver))
4697 {
4698 if (emailserver != "")
4699 {
4700 result = safe_query(
4701 "SELECT CameraRowID, DevSceneDelay FROM CamerasActiveDevices WHERE (DevSceneType==0) AND (DevSceneRowID==%" PRIu64 ") AND (DevSceneWhen==%d)",
4702 ulID,
4703 (bIsLightSwitchOn == true) ? 0 : 1
4704 );
4705 if (!result.empty())
4706 {
4707 for (const auto& ittCam : result)
4708 {
4709 std::vector<std::string> sd = ittCam;
4710 std::string camidx = sd[0];
4711 float delay = static_cast<float>(atof(sd[1].c_str()));
4712 std::string subject = Name + " Status: " + lstatus;
4713 AddTaskItem(_tTaskItem::EmailCameraSnapshot(delay + 1, camidx, subject));
4714 }
4715 }
4716 }
4717 }
4718
4719 if (m_bEnableEventSystem)
4720 {
4721 //Execute possible script
4722 std::string scriptname;
4723 #ifdef WIN32
4724 scriptname = szUserDataFolder + "scripts\\domoticz_main.bat";
4725 #else
4726 scriptname = szUserDataFolder + "scripts/domoticz_main";
4727 #endif
4728 if (file_exist(scriptname.c_str()))
4729 {
4730 //Add parameters
4731 std::stringstream s_scriptparams;
4732 std::string nszUserDataFolder = szUserDataFolder;
4733 if (nszUserDataFolder == "")
4734 nszUserDataFolder = ".";
4735 s_scriptparams << nszUserDataFolder << " " << HardwareID << " " << ulID << " " << (bIsLightSwitchOn ? "On" : "Off") << " \"" << lstatus << "\"" << " \"" << devname << "\"";
4736 //add script to background worker
4737 std::lock_guard<std::mutex> l(m_background_task_mutex);
4738 m_background_task_queue.push_back(_tTaskItem::ExecuteScript(1, scriptname, s_scriptparams.str()));
4739 }
4740 }
4741
4742 _eHardwareTypes HWtype = HTYPE_Domoticz; //just a value
4743 CDomoticzHardwareBase* pHardware = m_mainworker.GetHardware(HardwareID);
4744 if (pHardware != NULL)
4745 HWtype = pHardware->HwdType;
4746
4747 //Check for notifications
4748 if (HWtype != HTYPE_LogitechMediaServer) // Skip notifications for LMS here; is handled by the LMS plug-in
4749 {
4750 if (switchtype == STYPE_Selector)
4751 m_notifications.CheckAndHandleSwitchNotification(ulID, devname, (bIsLightSwitchOn) ? NTYPE_SWITCH_ON : NTYPE_SWITCH_OFF, llevel);
4752 else
4753 m_notifications.CheckAndHandleSwitchNotification(ulID, devname, (bIsLightSwitchOn) ? NTYPE_SWITCH_ON : NTYPE_SWITCH_OFF);
4754 }
4755 if (bIsLightSwitchOn)
4756 {
4757 if ((int)AddjValue != 0) //Off Delay
4758 {
4759 bool bAdd2DelayQueue = false;
4760 int cmd = 0;
4761 if (
4762 (switchtype == STYPE_OnOff) ||
4763 (switchtype == STYPE_Motion) ||
4764 (switchtype == STYPE_Dimmer) ||
4765 (switchtype == STYPE_PushOn) ||
4766 (switchtype == STYPE_DoorContact) ||
4767 (switchtype == STYPE_DoorLock) ||
4768 (switchtype == STYPE_DoorLockInverted) ||
4769 (switchtype == STYPE_Selector)
4770 )
4771 {
4772 switch (devType)
4773 {
4774 case pTypeLighting1:
4775 cmd = light1_sOff;
4776 bAdd2DelayQueue = true;
4777 break;
4778 case pTypeLighting2:
4779 cmd = light2_sOff;
4780 bAdd2DelayQueue = true;
4781 break;
4782 case pTypeLighting3:
4783 cmd = light3_sOff;
4784 bAdd2DelayQueue = true;
4785 break;
4786 case pTypeLighting4:
4787 cmd = light2_sOff;
4788 bAdd2DelayQueue = true;
4789 break;
4790 case pTypeLighting5:
4791 cmd = light5_sOff;
4792 bAdd2DelayQueue = true;
4793 break;
4794 case pTypeLighting6:
4795 cmd = light6_sOff;
4796 bAdd2DelayQueue = true;
4797 break;
4798 case pTypeRemote:
4799 cmd = light2_sOff;
4800 break;
4801 case pTypeColorSwitch:
4802 cmd = Color_LedOff;
4803 bAdd2DelayQueue = true;
4804 break;
4805 case pTypeRFY:
4806 cmd = rfy_sStop;
4807 bAdd2DelayQueue = true;
4808 break;
4809 case pTypeRadiator1:
4810 cmd = Radiator1_sNight;
4811 bAdd2DelayQueue = true;
4812 break;
4813 case pTypeGeneralSwitch:
4814 cmd = gswitch_sOff;
4815 bAdd2DelayQueue = true;
4816 break;
4817 case pTypeHomeConfort:
4818 cmd = HomeConfort_sOff;
4819 bAdd2DelayQueue = true;
4820 break;
4821 case pTypeFS20:
4822 cmd = fs20_sOff;
4823 bAdd2DelayQueue = true;
4824 break;
4825 }
4826 }
4827 /* Smoke detectors are manually reset!
4828 else if (
4829 (devType==pTypeSecurity1)&&
4830 ((subType==sTypeKD101)||(subType==sTypeSA30)||(subType==sTypeRM174RF))
4831 )
4832 {
4833 cmd=sStatusPanicOff;
4834 bAdd2DelayQueue=true;
4835 }
4836 */
4837 if (bAdd2DelayQueue == true)
4838 {
4839 std::lock_guard<std::mutex> l(m_background_task_mutex);
4840 _tTaskItem tItem = _tTaskItem::SwitchLight(AddjValue, ulID, HardwareID, ID, unit, devType, subType, switchtype, signallevel, batterylevel, cmd, sValue);
4841 //Remove all instances with this device from the queue first
4842 //otherwise command will be send twice, and first one will be to soon as it is currently counting
4843 std::vector<_tTaskItem>::iterator itt = m_background_task_queue.begin();
4844 while (itt != m_background_task_queue.end())
4845 {
4846 if (
4847 (itt->_ItemType == TITEM_SWITCHCMD) &&
4848 (itt->_idx == ulID) &&
4849 (itt->_HardwareID == HardwareID) &&
4850 (itt->_nValue == cmd)
4851 )
4852 {
4853 itt = m_background_task_queue.erase(itt);
4854 }
4855 else
4856 ++itt;
4857 }
4858 //finally add it to the queue
4859 m_background_task_queue.push_back(tItem);
4860 }
4861 }
4862 }
4863 }//end of check for notifications
4864
4865 //Check Scene Status
4866 CheckSceneStatusWithDevice(ulID);
4867 break;
4868 }
4869
4870 _log.Debug(DEBUG_NORM, "SQLH UpdateValueInt %s HwID:%d DevID:%s Type:%d sType:%d nValue:%d sValue:%s ", devname.c_str(), HardwareID, ID, devType, subType, nValue, sValue);
4871
4872 if (bDeviceUsed)
4873 m_mainworker.m_eventsystem.ProcessDevice(HardwareID, ulID, unit, devType, subType, signallevel, batterylevel, nValue, sValue, devname);
4874 return ulID;
4875 }
4876
GetLastValue(const int HardwareID,const char * DeviceID,const unsigned char unit,const unsigned char devType,const unsigned char subType,int & nValue,std::string & sValue,struct tm & LastUpdateTime)4877 bool CSQLHelper::GetLastValue(const int HardwareID, const char* DeviceID, const unsigned char unit, const unsigned char devType, const unsigned char subType, int& nValue, std::string& sValue, struct tm& LastUpdateTime)
4878 {
4879 bool result = false;
4880 std::vector<std::vector<std::string> > sqlresult;
4881 std::string sLastUpdate;
4882 //std::string sValue;
4883 //struct tm LastUpdateTime;
4884 sqlresult = safe_query(
4885 "SELECT nValue,sValue,LastUpdate FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d) order by LastUpdate desc limit 1",
4886 HardwareID, DeviceID, unit, devType, subType);
4887
4888 if (sqlresult.size() != 0)
4889 {
4890 nValue = (int)atoi(sqlresult[0][0].c_str());
4891 sValue = sqlresult[0][1];
4892 sLastUpdate = sqlresult[0][2];
4893
4894 time_t lutime;
4895 ParseSQLdatetime(lutime, LastUpdateTime, sLastUpdate);
4896
4897 result = true;
4898 }
4899
4900 return result;
4901 }
4902
4903
GetAddjustment(const int HardwareID,const char * ID,const unsigned char unit,const unsigned char devType,const unsigned char subType,float & AddjValue,float & AddjMulti)4904 void CSQLHelper::GetAddjustment(const int HardwareID, const char* ID, const unsigned char unit, const unsigned char devType, const unsigned char subType, float& AddjValue, float& AddjMulti)
4905 {
4906 AddjValue = 0.0f;
4907 AddjMulti = 1.0f;
4908 std::vector<std::vector<std::string> > result;
4909 result = safe_query(
4910 "SELECT AddjValue,AddjMulti FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)",
4911 HardwareID, ID, unit, devType, subType);
4912 if (!result.empty())
4913 {
4914 AddjValue = static_cast<float>(atof(result[0][0].c_str()));
4915 AddjMulti = static_cast<float>(atof(result[0][1].c_str()));
4916 }
4917 }
4918
GetMeterType(const int HardwareID,const char * ID,const unsigned char unit,const unsigned char devType,const unsigned char subType,int & meterType)4919 void CSQLHelper::GetMeterType(const int HardwareID, const char* ID, const unsigned char unit, const unsigned char devType, const unsigned char subType, int& meterType)
4920 {
4921 meterType = 0;
4922 std::vector<std::vector<std::string> > result;
4923 result = safe_query(
4924 "SELECT SwitchType FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)",
4925 HardwareID, ID, unit, devType, subType);
4926 if (!result.empty())
4927 {
4928 meterType = atoi(result[0][0].c_str());
4929 }
4930 }
4931
GetAddjustment2(const int HardwareID,const char * ID,const unsigned char unit,const unsigned char devType,const unsigned char subType,float & AddjValue,float & AddjMulti)4932 void CSQLHelper::GetAddjustment2(const int HardwareID, const char* ID, const unsigned char unit, const unsigned char devType, const unsigned char subType, float& AddjValue, float& AddjMulti)
4933 {
4934 AddjValue = 0.0f;
4935 AddjMulti = 1.0f;
4936 std::vector<std::vector<std::string> > result;
4937 result = safe_query(
4938 "SELECT AddjValue2,AddjMulti2 FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)",
4939 HardwareID, ID, unit, devType, subType);
4940 if (!result.empty())
4941 {
4942 AddjValue = static_cast<float>(atof(result[0][0].c_str()));
4943 AddjMulti = static_cast<float>(atof(result[0][1].c_str()));
4944 }
4945 }
4946
UpdatePreferencesVar(const std::string & Key,const std::string & sValue)4947 void CSQLHelper::UpdatePreferencesVar(const std::string& Key, const std::string& sValue)
4948 {
4949 UpdatePreferencesVar(Key, 0, sValue);
4950 }
UpdatePreferencesVar(const std::string & Key,const double Value)4951 void CSQLHelper::UpdatePreferencesVar(const std::string& Key, const double Value)
4952 {
4953 std::string sValue = boost::to_string(Value);
4954 UpdatePreferencesVar(Key, 0, sValue);
4955 }
4956
UpdatePreferencesVar(const std::string & Key,const int nValue)4957 void CSQLHelper::UpdatePreferencesVar(const std::string& Key, const int nValue)
4958 {
4959 UpdatePreferencesVar(Key, nValue, "");
4960 }
4961
UpdatePreferencesVar(const std::string & Key,const int nValue,const std::string & sValue)4962 void CSQLHelper::UpdatePreferencesVar(const std::string& Key, const int nValue, const std::string& sValue)
4963 {
4964 if (!m_dbase)
4965 return;
4966
4967 std::vector<std::vector<std::string> > result;
4968 result = safe_query("SELECT ROWID FROM Preferences WHERE (Key='%q')",
4969 Key.c_str());
4970 if (result.empty())
4971 {
4972 //Insert
4973 result = safe_query("INSERT INTO Preferences (Key, nValue, sValue) VALUES ('%q', %d,'%q')",
4974 Key.c_str(), nValue, sValue.c_str());
4975 }
4976 else
4977 {
4978 //Update
4979 result = safe_query("UPDATE Preferences SET Key='%q', nValue=%d, sValue='%q' WHERE (ROWID = '%q')",
4980 Key.c_str(), nValue, sValue.c_str(), result[0][0].c_str());
4981 }
4982 }
4983
GetPreferencesVar(const std::string & Key,std::string & sValue)4984 bool CSQLHelper::GetPreferencesVar(const std::string& Key, std::string& sValue)
4985 {
4986 if (!m_dbase)
4987 return false;
4988
4989
4990 std::vector<std::vector<std::string> > result;
4991 result = safe_query("SELECT sValue FROM Preferences WHERE (Key='%q')",
4992 Key.c_str());
4993 if (result.empty())
4994 return false;
4995 std::vector<std::string> sd = result[0];
4996 sValue = sd[0];
4997 return true;
4998 }
4999
GetPreferencesVar(const std::string & Key,double & Value)5000 bool CSQLHelper::GetPreferencesVar(const std::string& Key, double& Value)
5001 {
5002
5003 std::string sValue;
5004 int nValue;
5005 Value = 0;
5006 bool res = GetPreferencesVar(Key, nValue, sValue);
5007 if (!res)
5008 return false;
5009 Value = atof(sValue.c_str());
5010 return true;
5011 }
GetPreferencesVar(const std::string & Key,int & nValue,std::string & sValue)5012 bool CSQLHelper::GetPreferencesVar(const std::string& Key, int& nValue, std::string& sValue)
5013 {
5014 if (!m_dbase)
5015 return false;
5016
5017 std::vector<std::vector<std::string> > result;
5018 result = safe_query("SELECT nValue, sValue FROM Preferences WHERE (Key='%q')",
5019 Key.c_str());
5020 if (result.empty())
5021 return false;
5022 std::vector<std::string> sd = result[0];
5023 nValue = atoi(sd[0].c_str());
5024 sValue = sd[1];
5025 return true;
5026 }
5027
GetPreferencesVar(const std::string & Key,int & nValue)5028 bool CSQLHelper::GetPreferencesVar(const std::string& Key, int& nValue)
5029 {
5030 std::string sValue;
5031 return GetPreferencesVar(Key, nValue, sValue);
5032 }
DeletePreferencesVar(const std::string & Key)5033 void CSQLHelper::DeletePreferencesVar(const std::string& Key)
5034 {
5035 std::string sValue;
5036 if (!m_dbase)
5037 return;
5038
5039 //if found, delete
5040 if (GetPreferencesVar(Key, sValue) == true)
5041 {
5042 safe_query("DELETE FROM Preferences WHERE (Key='%q')", Key.c_str());
5043 }
5044 }
5045
5046
5047
GetLastBackupNo(const char * Key,int & nValue)5048 int CSQLHelper::GetLastBackupNo(const char* Key, int& nValue)
5049 {
5050 if (!m_dbase)
5051 return false;
5052
5053 std::vector<std::vector<std::string> > result;
5054 result = safe_query("SELECT nValue FROM BackupLog WHERE (Key='%q')", Key);
5055 if (result.empty())
5056 return -1;
5057 std::vector<std::string> sd = result[0];
5058 nValue = atoi(sd[0].c_str());
5059 return nValue;
5060 }
5061
SetLastBackupNo(const char * Key,const int nValue)5062 void CSQLHelper::SetLastBackupNo(const char* Key, const int nValue)
5063 {
5064 if (!m_dbase)
5065 return;
5066
5067 std::vector<std::vector<std::string> > result;
5068 result = safe_query("SELECT ROWID FROM BackupLog WHERE (Key='%q')", Key);
5069 if (result.empty())
5070 {
5071 //Insert
5072 safe_query(
5073 "INSERT INTO BackupLog (Key, nValue) "
5074 "VALUES ('%q','%d')",
5075 Key,
5076 nValue);
5077 }
5078 else
5079 {
5080 //Update
5081 uint64_t ID = std::stoull(result[0][0]);
5082
5083 safe_query(
5084 "UPDATE BackupLog SET Key='%q', nValue=%d "
5085 "WHERE (ROWID = %" PRIu64 ")",
5086 Key,
5087 nValue,
5088 ID);
5089 }
5090 }
5091
UpdateRFXCOMHardwareDetails(const int HardwareID,const int msg1,const int msg2,const int msg3,const int msg4,const int msg5,const int msg6)5092 void CSQLHelper::UpdateRFXCOMHardwareDetails(const int HardwareID, const int msg1, const int msg2, const int msg3, const int msg4, const int msg5, const int msg6)
5093 {
5094 safe_query("UPDATE Hardware SET Mode1=%d, Mode2=%d, Mode3=%d, Mode4=%d, Mode5=%d, Mode6=%d WHERE (ID == %d)",
5095 msg1, msg2, msg3, msg4, msg5, msg6, HardwareID);
5096 }
5097
HasTimers(const uint64_t Idx)5098 bool CSQLHelper::HasTimers(const uint64_t Idx)
5099 {
5100 if (!m_dbase)
5101 return false;
5102
5103 std::vector<std::vector<std::string> > result;
5104
5105 result = safe_query("SELECT COUNT(*) FROM Timers WHERE (DeviceRowID==%" PRIu64 ") AND (TimerPlan==%d)", Idx, m_ActiveTimerPlan);
5106 if (!result.empty())
5107 {
5108 std::vector<std::string> sd = result[0];
5109 int totaltimers = atoi(sd[0].c_str());
5110 if (totaltimers > 0)
5111 return true;
5112 }
5113 result = safe_query("SELECT COUNT(*) FROM SetpointTimers WHERE (DeviceRowID==%" PRIu64 ") AND (TimerPlan==%d)", Idx, m_ActiveTimerPlan);
5114 if (!result.empty())
5115 {
5116 std::vector<std::string> sd = result[0];
5117 int totaltimers = atoi(sd[0].c_str());
5118 return (totaltimers > 0);
5119 }
5120 return false;
5121 }
5122
HasTimers(const std::string & Idx)5123 bool CSQLHelper::HasTimers(const std::string& Idx)
5124 {
5125 uint64_t idxll = std::stoull(Idx);
5126 return HasTimers(idxll);
5127 }
5128
HasSceneTimers(const uint64_t Idx)5129 bool CSQLHelper::HasSceneTimers(const uint64_t Idx)
5130 {
5131 if (!m_dbase)
5132 return false;
5133
5134 std::vector<std::vector<std::string> > result;
5135
5136 result = safe_query("SELECT COUNT(*) FROM SceneTimers WHERE (SceneRowID==%" PRIu64 ") AND (TimerPlan==%d)", Idx, m_ActiveTimerPlan);
5137 if (result.empty())
5138 return false;
5139 std::vector<std::string> sd = result[0];
5140 int totaltimers = atoi(sd[0].c_str());
5141 return (totaltimers > 0);
5142 }
5143
HasSceneTimers(const std::string & Idx)5144 bool CSQLHelper::HasSceneTimers(const std::string& Idx)
5145 {
5146 uint64_t idxll = std::stoull(Idx);
5147 return HasSceneTimers(idxll);
5148 }
5149
ScheduleShortlog()5150 void CSQLHelper::ScheduleShortlog()
5151 {
5152 #ifdef _DEBUG
5153 //return;
5154 #endif
5155 if (!m_dbase)
5156 return;
5157
5158 try
5159 {
5160 //Force WAL flush
5161 sqlite3_wal_checkpoint(m_dbase, NULL);
5162
5163 UpdateTemperatureLog();
5164 UpdateRainLog();
5165 UpdateWindLog();
5166 UpdateUVLog();
5167 UpdateMeter();
5168 UpdateMultiMeter();
5169 UpdatePercentageLog();
5170 UpdateFanLog();
5171 //Removing the line below could cause a very large database,
5172 //and slow(large) data transfer (specially when working remote!!)
5173 CleanupShortLog();
5174 }
5175 catch (boost::exception& e)
5176 {
5177 _log.Log(LOG_ERROR, "Domoticz: Error running the shortlog schedule script!");
5178 #ifdef _DEBUG
5179 _log.Log(LOG_ERROR, "-----------------\n%s\n----------------", boost::diagnostic_information(e).c_str());
5180 #else
5181 (void)e;
5182 #endif
5183 return;
5184 }
5185 }
5186
ScheduleDay()5187 void CSQLHelper::ScheduleDay()
5188 {
5189 if (!m_dbase)
5190 return;
5191
5192 try
5193 {
5194 //Force WAL flush
5195 sqlite3_wal_checkpoint(m_dbase, NULL);
5196
5197 AddCalendarTemperature();
5198 AddCalendarUpdateRain();
5199 AddCalendarUpdateUV();
5200 AddCalendarUpdateWind();
5201 AddCalendarUpdateMeter();
5202 AddCalendarUpdateMultiMeter();
5203 AddCalendarUpdatePercentage();
5204 AddCalendarUpdateFan();
5205 CleanupLightSceneLog();
5206 }
5207 catch (boost::exception& e)
5208 {
5209 _log.Log(LOG_ERROR, "Domoticz: Error running the daily schedule script!");
5210 #ifdef _DEBUG
5211 _log.Log(LOG_ERROR, "-----------------\n%s\n----------------", boost::diagnostic_information(e).c_str());
5212 #else
5213 (void)e;
5214 #endif
5215 return;
5216 }
5217 }
5218
UpdateTemperatureLog()5219 void CSQLHelper::UpdateTemperatureLog()
5220 {
5221 time_t now = mytime(NULL);
5222 if (now == 0)
5223 return;
5224 struct tm tm1;
5225 localtime_r(&now, &tm1);
5226
5227 int SensorTimeOut = 60;
5228 GetPreferencesVar("SensorTimeout", SensorTimeOut);
5229
5230 std::vector<std::vector<std::string> > result;
5231 result = safe_query("SELECT ID,Type,SubType,nValue,sValue,LastUpdate FROM DeviceStatus WHERE (Type=%d OR Type=%d OR Type=%d OR Type=%d OR Type=%d OR Type=%d OR Type=%d OR Type=%d OR Type=%d OR Type=%d OR Type=%d OR Type=%d OR Type=%d OR (Type=%d AND SubType=%d) OR (Type=%d AND SubType=%d) OR (Type=%d AND SubType=%d))",
5232 pTypeTEMP,
5233 pTypeHUM,
5234 pTypeTEMP_HUM,
5235 pTypeTEMP_HUM_BARO,
5236 pTypeTEMP_BARO,
5237 pTypeUV,
5238 pTypeWIND,
5239 pTypeThermostat1,
5240 pTypeRFXSensor,
5241 pTypeRego6XXTemp,
5242 pTypeEvohomeZone,
5243 pTypeEvohomeWater,
5244 pTypeRadiator1,
5245 pTypeGeneral, sTypeSystemTemp,
5246 pTypeThermostat, sTypeThermSetpoint,
5247 pTypeGeneral, sTypeBaro
5248 );
5249 if (!result.empty())
5250 {
5251 for (const auto& itt : result)
5252 {
5253 std::vector<std::string> sd = itt;
5254
5255 uint64_t ID = std::stoull(sd[0]);
5256 unsigned char dType = atoi(sd[1].c_str());
5257 unsigned char dSubType = atoi(sd[2].c_str());
5258 int nValue = atoi(sd[3].c_str());
5259 std::string sValue = sd[4];
5260
5261 if (dType != pTypeRadiator1)
5262 {
5263 //do not include sensors that have no reading within an hour (except for devices that do not provide feedback, like the smartware radiator)
5264 std::string sLastUpdate = sd[5];
5265 struct tm ntime;
5266 time_t checktime;
5267 ParseSQLdatetime(checktime, ntime, sLastUpdate, tm1.tm_isdst);
5268
5269 if (difftime(now, checktime) >= SensorTimeOut * 60)
5270 continue;
5271 }
5272
5273 std::vector<std::string> splitresults;
5274 StringSplit(sValue, ";", splitresults);
5275 if (splitresults.size() < 1)
5276 continue; //impossible
5277
5278 float temp = 0;
5279 float chill = 0;
5280 unsigned char humidity = 0;
5281 int barometer = 0;
5282 float dewpoint = 0;
5283 float setpoint = 0;
5284
5285 switch (dType)
5286 {
5287 case pTypeRego6XXTemp:
5288 case pTypeTEMP:
5289 case pTypeThermostat:
5290 temp = static_cast<float>(atof(splitresults[0].c_str()));
5291 break;
5292 case pTypeThermostat1:
5293 temp = static_cast<float>(atof(splitresults[0].c_str()));
5294 break;
5295 case pTypeRadiator1:
5296 temp = static_cast<float>(atof(splitresults[0].c_str()));
5297 break;
5298 case pTypeEvohomeWater:
5299 if (splitresults.size() >= 2)
5300 {
5301 temp = static_cast<float>(atof(splitresults[0].c_str()));
5302 setpoint = static_cast<float>((splitresults[1] == "On") ? 60 : 0);
5303 //FIXME hack setpoint just on or off...may throw graph out so maybe pick sensible on off values?
5304 //(if the actual hw set point was retrievable should use that otherwise some config option)
5305 //actually if we plot the average it should give us an idea of how often hw has been switched on
5306 //more meaningful if it was plotted against the zone valve & boiler relay i guess (actual time hw heated)
5307 }
5308 break;
5309 case pTypeEvohomeZone:
5310 if (splitresults.size() >= 2)
5311 {
5312 temp = static_cast<float>(atof(splitresults[0].c_str()));
5313 setpoint = static_cast<float>(atof(splitresults[1].c_str()));
5314 }
5315 break;
5316 case pTypeHUM:
5317 humidity = nValue;
5318 break;
5319 case pTypeTEMP_HUM:
5320 if (splitresults.size() >= 2)
5321 {
5322 temp = static_cast<float>(atof(splitresults[0].c_str()));
5323 humidity = atoi(splitresults[1].c_str());
5324 dewpoint = (float)CalculateDewPoint(temp, humidity);
5325 }
5326 break;
5327 case pTypeTEMP_HUM_BARO:
5328 if (splitresults.size() == 5)
5329 {
5330 temp = static_cast<float>(atof(splitresults[0].c_str()));
5331 humidity = atoi(splitresults[1].c_str());
5332 if (dSubType == sTypeTHBFloat)
5333 barometer = int(atof(splitresults[3].c_str()) * 10.0f);
5334 else
5335 barometer = atoi(splitresults[3].c_str());
5336 dewpoint = (float)CalculateDewPoint(temp, humidity);
5337 }
5338 break;
5339 case pTypeTEMP_BARO:
5340 if (splitresults.size() >= 2)
5341 {
5342 temp = static_cast<float>(atof(splitresults[0].c_str()));
5343 barometer = int(atof(splitresults[1].c_str()) * 10.0f);
5344 }
5345 break;
5346 case pTypeUV:
5347 if (dSubType != sTypeUV3)
5348 continue;
5349 if (splitresults.size() >= 2)
5350 {
5351 temp = static_cast<float>(atof(splitresults[1].c_str()));
5352 }
5353 break;
5354 case pTypeWIND:
5355 if (dSubType == sTypeWINDNoTempNoChill)
5356 continue;
5357 if (splitresults.size() >= 6)
5358 {
5359 if (dSubType != sTypeWINDNoTemp)
5360 {
5361 temp = static_cast<float>(atof(splitresults[4].c_str()));
5362 }
5363 chill = static_cast<float>(atof(splitresults[5].c_str()));
5364 }
5365 break;
5366 case pTypeRFXSensor:
5367 if (dSubType != sTypeRFXSensorTemp)
5368 continue;
5369 temp = static_cast<float>(atof(splitresults[0].c_str()));
5370 break;
5371 case pTypeGeneral:
5372 if (dSubType == sTypeSystemTemp)
5373 {
5374 temp = static_cast<float>(atof(splitresults[0].c_str()));
5375 }
5376 else if (dSubType == sTypeBaro)
5377 {
5378 if (splitresults.size() != 2)
5379 continue;
5380 barometer = int(atof(splitresults[0].c_str()) * 10.0f);
5381 }
5382 break;
5383 }
5384 //insert record
5385 safe_query(
5386 "INSERT INTO Temperature (DeviceRowID, Temperature, Chill, Humidity, Barometer, DewPoint, SetPoint) "
5387 "VALUES ('%" PRIu64 "', '%.2f', '%.2f', '%d', '%d', '%.2f', '%.2f')",
5388 ID,
5389 temp,
5390 chill,
5391 humidity,
5392 barometer,
5393 dewpoint,
5394 setpoint
5395 );
5396 }
5397 }
5398 }
5399
UpdateRainLog()5400 void CSQLHelper::UpdateRainLog()
5401 {
5402 time_t now = mytime(NULL);
5403 if (now == 0)
5404 return;
5405 struct tm tm1;
5406 localtime_r(&now, &tm1);
5407
5408 int SensorTimeOut = 60;
5409 GetPreferencesVar("SensorTimeout", SensorTimeOut);
5410
5411 std::vector<std::vector<std::string> > result;
5412 result = safe_query("SELECT ID,Type,SubType,nValue,sValue,LastUpdate FROM DeviceStatus WHERE (Type=%d)", pTypeRAIN);
5413 if (!result.empty())
5414 {
5415 for (const auto& itt : result)
5416 {
5417 std::vector<std::string> sd = itt;
5418
5419 uint64_t ID = std::stoull(sd[0]);
5420 //unsigned char dType=atoi(sd[1].c_str());
5421 //unsigned char dSubType=atoi(sd[2].c_str());
5422 //int nValue=atoi(sd[3].c_str());
5423 std::string sValue = sd[4];
5424
5425 //do not include sensors that have no reading within an hour
5426 std::string sLastUpdate = sd[5];
5427 struct tm ntime;
5428 time_t checktime;
5429 ParseSQLdatetime(checktime, ntime, sLastUpdate, tm1.tm_isdst);
5430
5431 if (difftime(now, checktime) >= SensorTimeOut * 60)
5432 continue;
5433
5434 std::vector<std::string> splitresults;
5435 StringSplit(sValue, ";", splitresults);
5436 if (splitresults.size() < 2)
5437 continue; //impossible
5438
5439 int rate = atoi(splitresults[0].c_str());
5440 float total = static_cast<float>(atof(splitresults[1].c_str()));
5441
5442 //insert record
5443 safe_query(
5444 "INSERT INTO Rain (DeviceRowID, Total, Rate) "
5445 "VALUES ('%" PRIu64 "', '%.2f', '%d')",
5446 ID,
5447 total,
5448 rate
5449 );
5450 }
5451 }
5452 }
5453
UpdateWindLog()5454 void CSQLHelper::UpdateWindLog()
5455 {
5456 time_t now = mytime(NULL);
5457 if (now == 0)
5458 return;
5459 struct tm tm1;
5460 localtime_r(&now, &tm1);
5461
5462 int SensorTimeOut = 60;
5463 GetPreferencesVar("SensorTimeout", SensorTimeOut);
5464
5465 std::vector<std::vector<std::string> > result;
5466 result = safe_query("SELECT ID,DeviceID, Type,SubType,nValue,sValue,LastUpdate FROM DeviceStatus WHERE (Type=%d)", pTypeWIND);
5467 if (!result.empty())
5468 {
5469 for (const auto& itt : result)
5470 {
5471 std::vector<std::string> sd = itt;
5472
5473 uint64_t ID = std::stoull(sd[0]);
5474
5475 unsigned short DeviceID;
5476 std::stringstream s_str2(sd[1]);
5477 s_str2 >> DeviceID;
5478
5479 //unsigned char dType=atoi(sd[2].c_str());
5480 //unsigned char dSubType=atoi(sd[3].c_str());
5481 //int nValue=atoi(sd[4].c_str());
5482 std::string sValue = sd[5];
5483
5484 //do not include sensors that have no reading within an hour
5485 std::string sLastUpdate = sd[6];
5486 struct tm ntime;
5487 time_t checktime;
5488 ParseSQLdatetime(checktime, ntime, sLastUpdate, tm1.tm_isdst);
5489
5490 if (difftime(now, checktime) >= SensorTimeOut * 60)
5491 continue;
5492
5493 std::vector<std::string> splitresults;
5494 StringSplit(sValue, ";", splitresults);
5495 if (splitresults.size() < 4)
5496 continue; //impossible
5497
5498 float direction = static_cast<float>(atof(splitresults[0].c_str()));
5499
5500 int speed = atoi(splitresults[2].c_str());
5501 int gust = atoi(splitresults[3].c_str());
5502
5503 std::map<unsigned short, _tWindCalculator>::iterator ittWC = m_mainworker.m_wind_calculator.find(DeviceID);
5504 if (ittWC != m_mainworker.m_wind_calculator.end())
5505 {
5506 int speed_max, gust_max, speed_min, gust_min;
5507 ittWC->second.GetMMSpeedGust(speed_min, speed_max, gust_min, gust_max);
5508 if (speed_max != -1)
5509 speed = speed_max;
5510 if (gust_max != -1)
5511 gust = gust_max;
5512 }
5513
5514
5515 //insert record
5516 safe_query(
5517 "INSERT INTO Wind (DeviceRowID, Direction, Speed, Gust) "
5518 "VALUES ('%" PRIu64 "', '%.2f', '%d', '%d')",
5519 ID,
5520 direction,
5521 speed,
5522 gust
5523 );
5524 }
5525 }
5526 }
5527
UpdateUVLog()5528 void CSQLHelper::UpdateUVLog()
5529 {
5530 time_t now = mytime(NULL);
5531 if (now == 0)
5532 return;
5533 struct tm tm1;
5534 localtime_r(&now, &tm1);
5535
5536 int SensorTimeOut = 60;
5537 GetPreferencesVar("SensorTimeout", SensorTimeOut);
5538
5539 std::vector<std::vector<std::string> > result;
5540 result = safe_query("SELECT ID,Type,SubType,nValue,sValue,LastUpdate FROM DeviceStatus WHERE (Type=%d) OR (Type=%d AND SubType=%d)",
5541 pTypeUV,
5542 pTypeGeneral, sTypeUV
5543 );
5544 if (!result.empty())
5545 {
5546 for (const auto& itt : result)
5547 {
5548 std::vector<std::string> sd = itt;
5549
5550 uint64_t ID = std::stoull(sd[0]);
5551 //unsigned char dType=atoi(sd[1].c_str());
5552 //unsigned char dSubType=atoi(sd[2].c_str());
5553 //int nValue=atoi(sd[3].c_str());
5554 std::string sValue = sd[4];
5555
5556 //do not include sensors that have no reading within an hour
5557 std::string sLastUpdate = sd[5];
5558 struct tm ntime;
5559 time_t checktime;
5560 ParseSQLdatetime(checktime, ntime, sLastUpdate, tm1.tm_isdst);
5561
5562 if (difftime(now, checktime) >= SensorTimeOut * 60)
5563 continue;
5564
5565 std::vector<std::string> splitresults;
5566 StringSplit(sValue, ";", splitresults);
5567 if (splitresults.size() < 1)
5568 continue; //impossible
5569
5570 float level = static_cast<float>(atof(splitresults[0].c_str()));
5571
5572 //insert record
5573 safe_query(
5574 "INSERT INTO UV (DeviceRowID, Level) "
5575 "VALUES ('%" PRIu64 "', '%g')",
5576 ID,
5577 level
5578 );
5579 }
5580 }
5581 }
5582
UpdateCalendarMeter(const int HardwareID,const char * DeviceID,const unsigned char unit,const unsigned char devType,const unsigned char subType,const bool shortLog,const bool multiMeter,const char * date,const long long value1,const long long value2,const long long value3,const long long value4,const long long value5,const long long value6,const long long counter1,const long long counter2,const long long counter3,const long long counter4)5583 bool CSQLHelper::UpdateCalendarMeter(
5584 const int HardwareID,
5585 const char* DeviceID,
5586 const unsigned char unit,
5587 const unsigned char devType,
5588 const unsigned char subType,
5589 const bool shortLog,
5590 const bool multiMeter,
5591 const char* date,
5592 const long long value1,
5593 const long long value2,
5594 const long long value3,
5595 const long long value4,
5596 const long long value5,
5597 const long long value6,
5598 const long long counter1,
5599 const long long counter2,
5600 const long long counter3,
5601 const long long counter4)
5602 {
5603 std::vector<std::vector<std::string> > result;
5604
5605 result = safe_query("SELECT ID, Name, SwitchType FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)", HardwareID, DeviceID, unit, devType, subType);
5606 if (result.empty()) {
5607 return false;
5608 }
5609
5610 std::vector<std::string> sd = result[0];
5611 uint64_t DeviceRowID = std::stoull(sd[0]);
5612 //std::string devname = sd[1];
5613 //_eSwitchType switchtype = (_eSwitchType)atoi(sd[2].c_str());
5614
5615 if (shortLog)
5616 {
5617 if (!CheckDateTimeSQL(date)) {
5618 _log.Log(LOG_ERROR, "UpdateCalendarMeter(): incorrect date time format received, YYYY-MM-DD HH:mm:ss expected!");
5619 return false;
5620 }
5621
5622 //insert or replace record
5623 if (multiMeter) {
5624 result = safe_query(
5625 "SELECT DeviceRowID FROM MultiMeter "
5626 "WHERE ((DeviceRowID=='%" PRIu64 "') AND (Date=='%q'))",
5627 DeviceRowID, date
5628 );
5629 if (result.empty())
5630 {
5631 safe_query(
5632 "INSERT INTO MultiMeter (DeviceRowID, Value1, Value2, Value3, Value4, Value5, Value6, Date) "
5633 "VALUES ('%" PRIu64 "', '%lld', '%lld', '%lld', '%lld', '%lld', '%lld', '%q')",
5634 DeviceRowID,
5635 (value1 < 0) ? 0 : value1,
5636 (value2 < 0) ? 0 : value2,
5637 (value3 < 0) ? 0 : value3,
5638 (value4 < 0) ? 0 : value4,
5639 (value5 < 0) ? 0 : value5,
5640 (value6 < 0) ? 0 : value6,
5641 date
5642 );
5643 }
5644 else
5645 {
5646 safe_query(
5647 "UPDATE MultiMeter SET Value1='%lld', Value2='%lld', Value3='%lld', Value4='%lld', Value5='%lld', Value6='%lld' "
5648 "WHERE ((DeviceRowID=='%" PRIu64 "') AND (Date=='%q'))",
5649 (value1 < 0) ? 0 : value1,
5650 (value2 < 0) ? 0 : value2,
5651 (value3 < 0) ? 0 : value3,
5652 (value4 < 0) ? 0 : value4,
5653 (value5 < 0) ? 0 : value5,
5654 (value6 < 0) ? 0 : value6,
5655 DeviceRowID,
5656 date
5657 );
5658 }
5659 }
5660 else {
5661 result = safe_query(
5662 "SELECT DeviceRowID FROM Meter "
5663 "WHERE ((DeviceRowID=='%" PRIu64 "') AND (Date=='%q'))",
5664 DeviceRowID, date
5665 );
5666 if (result.empty())
5667 {
5668 safe_query(
5669 "INSERT INTO Meter (DeviceRowID, Value, Usage, Date) "
5670 "VALUES ('%" PRIu64 "','%lld','%lld','%q')",
5671 DeviceRowID, (value1 < 0) ? 0 : value1, (value2 < 0) ? 0 : value2, date
5672 );
5673 }
5674 else
5675 {
5676 safe_query(
5677 "UPDATE Meter SET DeviceRowID='%" PRIu64 "', Value='%lld', Usage='%lld', Date='%q' "
5678 "WHERE ((DeviceRowID=='%" PRIu64 "') AND (Date=='%q'))",
5679 DeviceRowID, (value1 < 0) ? 0 : value1, (value2 < 0) ? 0 : value2, date,
5680 DeviceRowID, date
5681 );
5682 }
5683 }
5684 }
5685 else
5686 {
5687 if (!CheckDateSQL(date)) {
5688 _log.Log(LOG_ERROR, "UpdateCalendarMeter(): incorrect date format received, YYYY-MM-DD expected!");
5689 return false;
5690 }
5691 if (multiMeter) {
5692 result = safe_query(
5693 "SELECT DeviceRowID FROM MultiMeter_Calendar "
5694 "WHERE (DeviceRowID=='%" PRIu64 "') AND (Date=='%q')",
5695 DeviceRowID, date
5696 );
5697 if (result.empty())
5698 {
5699 safe_query(
5700 "INSERT INTO MultiMeter_Calendar (DeviceRowID, Value1, Value2, Value3, Value4, Value5, Value6, Counter1, Counter2, Counter3, Counter4, Date) "
5701 "VALUES ('%" PRIu64 "', '%lld', '%lld', '%lld', '%lld', '%lld', '%lld', '%lld', '%lld', '%lld', '%lld', '%q')",
5702 DeviceRowID,
5703 (value1 < 0) ? 0 : value1,
5704 (value2 < 0) ? 0 : value2,
5705 (value3 < 0) ? 0 : value3,
5706 (value4 < 0) ? 0 : value4,
5707 (value5 < 0) ? 0 : value5,
5708 (value6 < 0) ? 0 : value6,
5709 (counter1 < 0) ? 0 : counter1,
5710 (counter2 < 0) ? 0 : counter2,
5711 (counter3 < 0) ? 0 : counter3,
5712 (counter4 < 0) ? 0 : counter4,
5713 date
5714 );
5715 }
5716 else
5717 {
5718 safe_query(
5719 "UPDATE MultiMeter_Calendar SET Value1='%lld', Value2='%lld', Value3='%lld', Value4='%lld', Value5='%lld', Value6='%lld' , Counter1='%lld' , Counter2='%lld' , Counter3='%lld' , Counter4='%lld' "
5720 "WHERE ((DeviceRowID=='%" PRIu64 "') AND (Date=='%q'))",
5721 (value1 < 0) ? 0 : value1,
5722 (value2 < 0) ? 0 : value2,
5723 (value3 < 0) ? 0 : value3,
5724 (value4 < 0) ? 0 : value4,
5725 (value5 < 0) ? 0 : value5,
5726 (value6 < 0) ? 0 : value6,
5727 (counter1 < 0) ? 0 : counter1,
5728 (counter2 < 0) ? 0 : counter2,
5729 (counter3 < 0) ? 0 : counter3,
5730 (counter4 < 0) ? 0 : counter4,
5731 DeviceRowID,
5732 date
5733 );
5734 }
5735 }
5736 else {
5737 result = safe_query(
5738 "SELECT DeviceRowID FROM Meter_Calendar "
5739 "WHERE (DeviceRowID=='%" PRIu64 "') AND (Date=='%q')",
5740 DeviceRowID, date
5741 );
5742 if (result.empty())
5743 {
5744 safe_query(
5745 "INSERT INTO Meter_Calendar (DeviceRowID, Counter, Value, Date) "
5746 "VALUES ('%" PRIu64 "', '%lld', '%lld', '%q')",
5747 DeviceRowID, (value1 < 0) ? 0 : value1, (value2 < 0) ? 0 : value2, date
5748 );
5749 }
5750 else
5751 {
5752 safe_query(
5753 "UPDATE Meter_Calendar SET DeviceRowID='%" PRIu64 "', Counter='%lld', Value='%lld', Date='%q' "
5754 "WHERE (DeviceRowID=='%" PRIu64 "') AND (Date=='%q')",
5755 DeviceRowID, (value1 < 0) ? 0 : value1, (value2 < 0) ? 0 : value2, date,
5756 DeviceRowID, date
5757 );
5758 }
5759 }
5760 }
5761 return true;
5762 }
5763
UpdateMeter()5764 void CSQLHelper::UpdateMeter()
5765 {
5766 time_t now = mytime(NULL);
5767 if (now == 0)
5768 return;
5769 struct tm tm1;
5770 localtime_r(&now, &tm1);
5771
5772 int SensorTimeOut = 60;
5773 GetPreferencesVar("SensorTimeout", SensorTimeOut);
5774
5775 std::vector<std::vector<std::string> > result;
5776 std::vector<std::vector<std::string> > result2;
5777
5778 result = safe_query(
5779 "SELECT ID,Name,HardwareID,DeviceID,Unit,Type,SubType,nValue,sValue,LastUpdate,Options FROM DeviceStatus WHERE ("
5780 "Type=%d OR " //pTypeRFXMeter
5781 "Type=%d OR " //pTypeP1Gas
5782 "Type=%d OR " //pTypeYouLess
5783 "Type=%d OR " //pTypeENERGY
5784 "Type=%d OR " //pTypePOWER
5785 "Type=%d OR " //pTypeAirQuality
5786 "Type=%d OR " //pTypeUsage
5787 "Type=%d OR " //pTypeLux
5788 "Type=%d OR " //pTypeWEIGHT
5789 "(Type=%d AND SubType=%d) OR " //pTypeRego6XXValue,sTypeRego6XXCounter
5790 "(Type=%d AND SubType=%d) OR " //pTypeGeneral,sTypeVisibility
5791 "(Type=%d AND SubType=%d) OR " //pTypeGeneral,sTypeSolarRadiation
5792 "(Type=%d AND SubType=%d) OR " //pTypeGeneral,sTypeSoilMoisture
5793 "(Type=%d AND SubType=%d) OR " //pTypeGeneral,sTypeLeafWetness
5794 "(Type=%d AND SubType=%d) OR " //pTypeRFXSensor,sTypeRFXSensorAD
5795 "(Type=%d AND SubType=%d) OR" //pTypeRFXSensor,sTypeRFXSensorVolt
5796 "(Type=%d AND SubType=%d) OR" //pTypeGeneral,sTypeVoltage
5797 "(Type=%d AND SubType=%d) OR" //pTypeGeneral,sTypeCurrent
5798 "(Type=%d AND SubType=%d) OR" //pTypeGeneral,sTypeSoundLevel
5799 "(Type=%d AND SubType=%d) OR " //pTypeGeneral,sTypeDistance
5800 "(Type=%d AND SubType=%d) OR " //pTypeGeneral,sTypePressure
5801 "(Type=%d AND SubType=%d) OR " //pTypeGeneral,sTypeCounterIncremental
5802 "(Type=%d AND SubType=%d)" //pTypeGeneral,sTypeKwh
5803 ")",
5804 pTypeRFXMeter,
5805 pTypeP1Gas,
5806 pTypeYouLess,
5807 pTypeENERGY,
5808 pTypePOWER,
5809 pTypeAirQuality,
5810 pTypeUsage,
5811 pTypeLux,
5812 pTypeWEIGHT,
5813 pTypeRego6XXValue, sTypeRego6XXCounter,
5814 pTypeGeneral, sTypeVisibility,
5815 pTypeGeneral, sTypeSolarRadiation,
5816 pTypeGeneral, sTypeSoilMoisture,
5817 pTypeGeneral, sTypeLeafWetness,
5818 pTypeRFXSensor, sTypeRFXSensorAD,
5819 pTypeRFXSensor, sTypeRFXSensorVolt,
5820 pTypeGeneral, sTypeVoltage,
5821 pTypeGeneral, sTypeCurrent,
5822 pTypeGeneral, sTypeSoundLevel,
5823 pTypeGeneral, sTypeDistance,
5824 pTypeGeneral, sTypePressure,
5825 pTypeGeneral, sTypeCounterIncremental,
5826 pTypeGeneral, sTypeKwh
5827 );
5828 if (!result.empty())
5829 {
5830 for (const auto& itt : result)
5831 {
5832 char szTmp[200];
5833 std::vector<std::string> sd = itt;
5834
5835 std::string sOptions = sd[10];
5836 std::map<std::string, std::string> options = BuildDeviceOptions(sOptions);
5837 // We don't want to update meter if externally managed
5838 if (options["DisableLogAutoUpdate"] == "true")
5839 {
5840 continue;
5841 }
5842
5843 uint64_t ID = std::stoull(sd[0]);
5844 std::string devname = sd[1];
5845 int hardwareID = atoi(sd[2].c_str());
5846 std::string DeviceID = sd[3];
5847 unsigned char Unit = atoi(sd[4].c_str());
5848
5849 unsigned char dType = atoi(sd[5].c_str());
5850 unsigned char dSubType = atoi(sd[6].c_str());
5851 int nValue = atoi(sd[7].c_str());
5852 std::string sValue = sd[8];
5853 std::string sLastUpdate = sd[9];
5854
5855 std::string sUsage = "0";
5856
5857 //do not include sensors that have no reading within an hour
5858 struct tm ntime;
5859 time_t checktime;
5860 ParseSQLdatetime(checktime, ntime, sLastUpdate, tm1.tm_isdst);
5861
5862
5863 //Check for timeout, if timeout then dont add value
5864 if (dType != pTypeP1Gas)
5865 {
5866 if (difftime(now, checktime) >= SensorTimeOut * 60)
5867 continue;
5868 }
5869 else
5870 {
5871 //P1 Gas meter transmits results every 1 a 2 hours
5872 if (difftime(now, checktime) >= 3 * 3600)
5873 continue;
5874 }
5875
5876 if (dType == pTypeYouLess)
5877 {
5878 std::vector<std::string> splitresults;
5879 StringSplit(sValue, ";", splitresults);
5880 if (splitresults.size() < 2)
5881 continue;
5882 sValue = splitresults[0];
5883 sUsage = splitresults[1];
5884 }
5885 else if (dType == pTypeENERGY)
5886 {
5887 std::vector<std::string> splitresults;
5888 StringSplit(sValue, ";", splitresults);
5889 if (splitresults.size() < 2)
5890 continue;
5891 sUsage = splitresults[0];
5892 double fValue = atof(splitresults[1].c_str()) * 100;
5893 sprintf(szTmp, "%.0f", fValue);
5894 sValue = szTmp;
5895 }
5896 else if (dType == pTypePOWER)
5897 {
5898 std::vector<std::string> splitresults;
5899 StringSplit(sValue, ";", splitresults);
5900 if (splitresults.size() < 2)
5901 continue;
5902 sUsage = splitresults[0];
5903 double fValue = atof(splitresults[1].c_str()) * 100;
5904 sprintf(szTmp, "%.0f", fValue);
5905 sValue = szTmp;
5906 }
5907 else if (dType == pTypeAirQuality)
5908 {
5909 sprintf(szTmp, "%d", nValue);
5910 sValue = szTmp;
5911 m_notifications.CheckAndHandleNotification(ID, hardwareID, DeviceID, devname, Unit, dType, dSubType, (int)nValue);
5912 }
5913 else if ((dType == pTypeGeneral) && ((dSubType == sTypeSoilMoisture) || (dSubType == sTypeLeafWetness)))
5914 {
5915 sprintf(szTmp, "%d", nValue);
5916 sValue = szTmp;
5917 }
5918 else if ((dType == pTypeGeneral) && (dSubType == sTypeVisibility))
5919 {
5920 double fValue = atof(sValue.c_str()) * 10.0f;
5921 sprintf(szTmp, "%.0f", fValue);
5922 sValue = szTmp;
5923 }
5924 else if ((dType == pTypeGeneral) && (dSubType == sTypeDistance))
5925 {
5926 double fValue = atof(sValue.c_str()) * 10.0f;
5927 sprintf(szTmp, "%.0f", fValue);
5928 sValue = szTmp;
5929 }
5930 else if ((dType == pTypeGeneral) && (dSubType == sTypeSolarRadiation))
5931 {
5932 double fValue = atof(sValue.c_str()) * 10.0f;
5933 sprintf(szTmp, "%.0f", fValue);
5934 sValue = szTmp;
5935 }
5936 else if ((dType == pTypeGeneral) && (dSubType == sTypeSoundLevel))
5937 {
5938 double fValue = atof(sValue.c_str()) * 10.0f;
5939 sprintf(szTmp, "%.0f", fValue);
5940 sValue = szTmp;
5941 }
5942 else if ((dType == pTypeGeneral) && (dSubType == sTypeKwh))
5943 {
5944 std::vector<std::string> splitresults;
5945 StringSplit(sValue, ";", splitresults);
5946 if (splitresults.size() < 2)
5947 continue;
5948
5949 double fValue = atof(splitresults[0].c_str()) * 10.0f;
5950 sprintf(szTmp, "%.0f", fValue);
5951 sUsage = szTmp;
5952
5953 fValue = atof(splitresults[1].c_str());
5954 sprintf(szTmp, "%.0f", fValue);
5955 sValue = szTmp;
5956 }
5957 else if (dType == pTypeLux)
5958 {
5959 double fValue = atof(sValue.c_str());
5960 sprintf(szTmp, "%.0f", fValue);
5961 sValue = szTmp;
5962 }
5963 else if (dType == pTypeWEIGHT)
5964 {
5965 double fValue = atof(sValue.c_str()) * 10.0f;
5966 sprintf(szTmp, "%.0f", fValue);
5967 sValue = szTmp;
5968 }
5969 else if (dType == pTypeRFXSensor)
5970 {
5971 double fValue = atof(sValue.c_str());
5972 sprintf(szTmp, "%.0f", fValue);
5973 sValue = szTmp;
5974 }
5975 else if ((dType == pTypeGeneral) && (dSubType == sTypeCounterIncremental))
5976 {
5977 double fValue = atof(sValue.c_str());
5978 sprintf(szTmp, "%.0f", fValue);
5979 sValue = szTmp;
5980 }
5981 else if ((dType == pTypeGeneral) && (dSubType == sTypeVoltage))
5982 {
5983 double fValue = atof(sValue.c_str()) * 1000.0f;
5984 sprintf(szTmp, "%.0f", fValue);
5985 sValue = szTmp;
5986 }
5987 else if ((dType == pTypeGeneral) && (dSubType == sTypeCurrent))
5988 {
5989 double fValue = atof(sValue.c_str()) * 1000.0f;
5990 sprintf(szTmp, "%.0f", fValue);
5991 sValue = szTmp;
5992 }
5993 else if ((dType == pTypeGeneral) && (dSubType == sTypePressure))
5994 {
5995 double fValue = atof(sValue.c_str()) * 10.0f;
5996 sprintf(szTmp, "%.0f", fValue);
5997 sValue = szTmp;
5998 }
5999 else if (dType == pTypeUsage)
6000 {
6001 double fValue = atof(sValue.c_str()) * 10.0f;
6002 sprintf(szTmp, "%.0f", fValue);
6003 sValue = szTmp;
6004 }
6005
6006 long long MeterValue = 0;
6007 long long MeterUsage = 0;
6008
6009 try
6010 {
6011 MeterUsage = std::stoll(sUsage);
6012 MeterValue = std::stoll(sValue);
6013 }
6014 catch (const std::exception&)
6015 {
6016 _log.Log(LOG_ERROR, "UpdateMeter: Error converting sValue/sUsage! (IDX: %s, sValue: '%s', sUsage: '%s', dType: %d, sType: %d)", sd[0].c_str(), sValue.c_str(), sUsage.c_str(), dType, dSubType);
6017 }
6018
6019 //insert record
6020 safe_query(
6021 "INSERT INTO Meter (DeviceRowID, Value, [Usage]) "
6022 "VALUES ('%" PRIu64 "', '%lld', '%lld')",
6023 ID,
6024 MeterValue,
6025 MeterUsage
6026 );
6027 }
6028 }
6029 }
6030
UpdateMultiMeter()6031 void CSQLHelper::UpdateMultiMeter()
6032 {
6033 time_t now = mytime(NULL);
6034 if (now == 0)
6035 return;
6036 struct tm tm1;
6037 localtime_r(&now, &tm1);
6038
6039 int SensorTimeOut = 60;
6040 GetPreferencesVar("SensorTimeout", SensorTimeOut);
6041
6042 std::vector<std::vector<std::string> > result;
6043 result = safe_query("SELECT ID,Type,SubType,nValue,sValue,LastUpdate,Options FROM DeviceStatus WHERE (Type=%d OR Type=%d OR Type=%d)",
6044 pTypeP1Power,
6045 pTypeCURRENT,
6046 pTypeCURRENTENERGY
6047 );
6048 if (!result.empty())
6049 {
6050 for (const auto& itt : result)
6051 {
6052 std::vector<std::string> sd = itt;
6053
6054 std::string sOptions = sd[6];
6055 std::map<std::string, std::string> options = BuildDeviceOptions(sOptions);
6056 // We don't want to update meter if externally managed
6057 if (options["DisableLogAutoUpdate"] == "true")
6058 {
6059 continue;
6060 }
6061
6062 uint64_t ID = std::stoull(sd[0]);
6063 unsigned char dType = atoi(sd[1].c_str());
6064 unsigned char dSubType = atoi(sd[2].c_str());
6065 //int nValue=atoi(sd[3].c_str());
6066 std::string sValue = sd[4];
6067
6068 //do not include sensors that have no reading within an hour
6069 std::string sLastUpdate = sd[5];
6070 struct tm ntime;
6071 time_t checktime;
6072 ParseSQLdatetime(checktime, ntime, sLastUpdate, tm1.tm_isdst);
6073
6074 if (difftime(now, checktime) >= SensorTimeOut * 60)
6075 continue;
6076 std::vector<std::string> splitresults;
6077 StringSplit(sValue, ";", splitresults);
6078
6079 unsigned long long value1 = 0;
6080 unsigned long long value2 = 0;
6081 unsigned long long value3 = 0;
6082 unsigned long long value4 = 0;
6083 unsigned long long value5 = 0;
6084 unsigned long long value6 = 0;
6085
6086 if (dType == pTypeP1Power)
6087 {
6088 if (splitresults.size() != 6)
6089 continue; //impossible
6090 unsigned long long powerusage1 = std::stoull(splitresults[0]);
6091 unsigned long long powerusage2 = std::stoull(splitresults[1]);
6092 unsigned long long powerdeliv1 = std::stoull(splitresults[2]);
6093 unsigned long long powerdeliv2 = std::stoull(splitresults[3]);
6094 unsigned long long usagecurrent = std::stoull(splitresults[4]);
6095 unsigned long long delivcurrent = std::stoull(splitresults[5]);
6096
6097 value1 = powerusage1;
6098 value2 = powerdeliv1;
6099 value5 = powerusage2;
6100 value6 = powerdeliv2;
6101 value3 = usagecurrent;
6102 value4 = delivcurrent;
6103 }
6104 else if ((dType == pTypeCURRENT) && (dSubType == sTypeELEC1))
6105 {
6106 if (splitresults.size() != 3)
6107 continue; //impossible
6108
6109 value1 = (unsigned long)(atof(splitresults[0].c_str()) * 10.0f);
6110 value2 = (unsigned long)(atof(splitresults[1].c_str()) * 10.0f);
6111 value3 = (unsigned long)(atof(splitresults[2].c_str()) * 10.0f);
6112 }
6113 else if ((dType == pTypeCURRENTENERGY) && (dSubType == sTypeELEC4))
6114 {
6115 if (splitresults.size() != 4)
6116 continue; //impossible
6117
6118 value1 = (unsigned long)(atof(splitresults[0].c_str()) * 10.0f);
6119 value2 = (unsigned long)(atof(splitresults[1].c_str()) * 10.0f);
6120 value3 = (unsigned long)(atof(splitresults[2].c_str()) * 10.0f);
6121 value4 = (unsigned long long)(atof(splitresults[3].c_str()) * 1000.0f);
6122 }
6123 else
6124 continue;//don't know you (yet)
6125
6126 //insert record
6127 safe_query(
6128 "INSERT INTO MultiMeter (DeviceRowID, Value1, Value2, Value3, Value4, Value5, Value6) "
6129 "VALUES ('%" PRIu64 "', '%llu', '%llu', '%llu', '%llu', '%llu', '%llu')",
6130 ID,
6131 value1,
6132 value2,
6133 value3,
6134 value4,
6135 value5,
6136 value6
6137 );
6138 }
6139 }
6140 }
6141
UpdatePercentageLog()6142 void CSQLHelper::UpdatePercentageLog()
6143 {
6144 time_t now = mytime(NULL);
6145 if (now == 0)
6146 return;
6147 struct tm tm1;
6148 localtime_r(&now, &tm1);
6149
6150 int SensorTimeOut = 60;
6151 GetPreferencesVar("SensorTimeout", SensorTimeOut);
6152
6153 std::vector<std::vector<std::string> > result;
6154 result = safe_query("SELECT ID,Type,SubType,nValue,sValue,LastUpdate FROM DeviceStatus WHERE (Type=%d AND SubType=%d) OR (Type=%d AND SubType=%d) OR (Type=%d AND SubType=%d)",
6155 pTypeGeneral, sTypePercentage,
6156 pTypeGeneral, sTypeWaterflow,
6157 pTypeGeneral, sTypeCustom
6158 );
6159 if (!result.empty())
6160 {
6161 for (const auto& itt : result)
6162 {
6163 std::vector<std::string> sd = itt;
6164
6165 uint64_t ID = std::stoull(sd[0]);
6166
6167 //unsigned char dType=atoi(sd[1].c_str());
6168 //unsigned char dSubType=atoi(sd[2].c_str());
6169 //int nValue=atoi(sd[3].c_str());
6170 std::string sValue = sd[4];
6171
6172 //do not include sensors that have no reading within an hour
6173 std::string sLastUpdate = sd[5];
6174 struct tm ntime;
6175 time_t checktime;
6176 ParseSQLdatetime(checktime, ntime, sLastUpdate, tm1.tm_isdst);
6177
6178 if (difftime(now, checktime) >= SensorTimeOut * 60)
6179 continue;
6180
6181 std::vector<std::string> splitresults;
6182 StringSplit(sValue, ";", splitresults);
6183 if (splitresults.size() < 1)
6184 continue; //impossible
6185
6186 float percentage = static_cast<float>(atof(sValue.c_str()));
6187
6188 //insert record
6189 safe_query(
6190 "INSERT INTO Percentage (DeviceRowID, Percentage) "
6191 "VALUES ('%" PRIu64 "', '%g')",
6192 ID,
6193 percentage
6194 );
6195 }
6196 }
6197 }
6198
UpdateFanLog()6199 void CSQLHelper::UpdateFanLog()
6200 {
6201 time_t now = mytime(NULL);
6202 if (now == 0)
6203 return;
6204 struct tm tm1;
6205 localtime_r(&now, &tm1);
6206
6207 int SensorTimeOut = 60;
6208 GetPreferencesVar("SensorTimeout", SensorTimeOut);
6209
6210 std::vector<std::vector<std::string> > result;
6211 result = safe_query("SELECT ID,Type,SubType,nValue,sValue,LastUpdate FROM DeviceStatus WHERE (Type=%d AND SubType=%d)",
6212 pTypeGeneral, sTypeFan
6213 );
6214 if (!result.empty())
6215 {
6216 for (const auto& itt : result)
6217 {
6218 std::vector<std::string> sd = itt;
6219
6220 uint64_t ID = std::stoull(sd[0]);
6221
6222 //unsigned char dType=atoi(sd[1].c_str());
6223 //unsigned char dSubType=atoi(sd[2].c_str());
6224 //int nValue=atoi(sd[3].c_str());
6225 std::string sValue = sd[4];
6226
6227 //do not include sensors that have no reading within an hour
6228 std::string sLastUpdate = sd[5];
6229 struct tm ntime;
6230 time_t checktime;
6231 ParseSQLdatetime(checktime, ntime, sLastUpdate, tm1.tm_isdst);
6232
6233 if (difftime(now, checktime) >= SensorTimeOut * 60)
6234 continue;
6235
6236 std::vector<std::string> splitresults;
6237 StringSplit(sValue, ";", splitresults);
6238 if (splitresults.size() < 1)
6239 continue; //impossible
6240
6241 int speed = (int)atoi(sValue.c_str());
6242
6243 //insert record
6244 safe_query(
6245 "INSERT INTO Fan (DeviceRowID, Speed) "
6246 "VALUES ('%" PRIu64 "', '%d')",
6247 ID,
6248 speed
6249 );
6250 }
6251 }
6252 }
6253
6254
AddCalendarTemperature()6255 void CSQLHelper::AddCalendarTemperature()
6256 {
6257 //Get All temperature devices in the Temperature Table
6258 std::vector<std::vector<std::string> > resultdevices;
6259 resultdevices = safe_query("SELECT DISTINCT(DeviceRowID) FROM Temperature ORDER BY DeviceRowID");
6260 if (resultdevices.size() < 1)
6261 return; //nothing to do
6262
6263 char szDateStart[40];
6264 char szDateEnd[40];
6265
6266 time_t now = mytime(NULL);
6267 struct tm ltime;
6268 localtime_r(&now, <ime);
6269 sprintf(szDateEnd, "%04d-%02d-%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
6270
6271 time_t yesterday;
6272 struct tm tm2;
6273 getNoon(yesterday, tm2, ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday - 1); // we only want the date
6274 sprintf(szDateStart, "%04d-%02d-%02d", tm2.tm_year + 1900, tm2.tm_mon + 1, tm2.tm_mday);
6275
6276 std::vector<std::vector<std::string> > result;
6277
6278 for (const auto& itt : resultdevices)
6279 {
6280 std::vector<std::string> sddev = itt;
6281 uint64_t ID = std::stoull(sddev[0]);
6282
6283 result = safe_query("SELECT MIN(Temperature), MAX(Temperature), AVG(Temperature), MIN(Chill), MAX(Chill), AVG(Humidity), AVG(Barometer), MIN(DewPoint), MIN(SetPoint), MAX(SetPoint), AVG(SetPoint) FROM Temperature WHERE (DeviceRowID='%" PRIu64 "' AND Date>='%q' AND Date<='%q 00:00:00')",
6284 ID,
6285 szDateStart,
6286 szDateEnd
6287 );
6288 if (!result.empty())
6289 {
6290 std::vector<std::string> sd = result[0];
6291
6292 float temp_min = static_cast<float>(atof(sd[0].c_str()));
6293 float temp_max = static_cast<float>(atof(sd[1].c_str()));
6294 float temp_avg = static_cast<float>(atof(sd[2].c_str()));
6295 float chill_min = static_cast<float>(atof(sd[3].c_str()));
6296 float chill_max = static_cast<float>(atof(sd[4].c_str()));
6297 int humidity = atoi(sd[5].c_str());
6298 int barometer = atoi(sd[6].c_str());
6299 float dewpoint = static_cast<float>(atof(sd[7].c_str()));
6300 float setpoint_min = static_cast<float>(atof(sd[8].c_str()));
6301 float setpoint_max = static_cast<float>(atof(sd[9].c_str()));
6302 float setpoint_avg = static_cast<float>(atof(sd[10].c_str()));
6303 result = safe_query(
6304 "INSERT INTO Temperature_Calendar (DeviceRowID, Temp_Min, Temp_Max, Temp_Avg, Chill_Min, Chill_Max, Humidity, Barometer, DewPoint, SetPoint_Min, SetPoint_Max, SetPoint_Avg, Date) "
6305 "VALUES ('%" PRIu64 "', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%d', '%d', '%.2f', '%.2f', '%.2f', '%.2f', '%q')",
6306 ID,
6307 temp_min,
6308 temp_max,
6309 temp_avg,
6310 chill_min,
6311 chill_max,
6312 humidity,
6313 barometer,
6314 dewpoint,
6315 setpoint_min,
6316 setpoint_max,
6317 setpoint_avg,
6318 szDateStart
6319 );
6320 }
6321 }
6322 }
6323
AddCalendarUpdateRain()6324 void CSQLHelper::AddCalendarUpdateRain()
6325 {
6326 //Get All UV devices
6327 std::vector<std::vector<std::string> > resultdevices;
6328 resultdevices = safe_query("SELECT DISTINCT(DeviceRowID) FROM Rain ORDER BY DeviceRowID");
6329 if (resultdevices.size() < 1)
6330 return; //nothing to do
6331
6332 char szDateStart[40];
6333 char szDateEnd[40];
6334
6335 time_t now = mytime(NULL);
6336 struct tm ltime;
6337 localtime_r(&now, <ime);
6338 sprintf(szDateEnd, "%04d-%02d-%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
6339
6340 time_t yesterday;
6341 struct tm tm2;
6342 getNoon(yesterday, tm2, ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday - 1); // we only want the date
6343 sprintf(szDateStart, "%04d-%02d-%02d", tm2.tm_year + 1900, tm2.tm_mon + 1, tm2.tm_mday);
6344
6345 std::vector<std::vector<std::string> > result;
6346
6347 for (const auto& itt : resultdevices)
6348 {
6349 std::vector<std::string> sddev = itt;
6350 uint64_t ID = std::stoull(sddev[0]);
6351
6352 //Get Device Information
6353 result = safe_query("SELECT SubType FROM DeviceStatus WHERE (ID='%" PRIu64 "')", ID);
6354 if (result.empty())
6355 continue;
6356 std::vector<std::string> sd = result[0];
6357
6358 unsigned char subType = atoi(sd[0].c_str());
6359
6360 if (subType == sTypeRAINWU || subType == sTypeRAINByRate)
6361 {
6362 result = safe_query("SELECT Total, Total, Rate FROM Rain WHERE (DeviceRowID='%" PRIu64 "' AND Date>='%q' AND Date<='%q 00:00:00') ORDER BY ROWID DESC LIMIT 1",
6363 ID,
6364 szDateStart,
6365 szDateEnd
6366 );
6367 }
6368 else
6369 {
6370 result = safe_query("SELECT MIN(Total), MAX(Total), MAX(Rate) FROM Rain WHERE (DeviceRowID='%" PRIu64 "' AND Date>='%q' AND Date<='%q 00:00:00')",
6371 ID,
6372 szDateStart,
6373 szDateEnd
6374 );
6375 }
6376
6377 if (!result.empty())
6378 {
6379 std::vector<std::string> sd = result[0];
6380
6381 float total_min = static_cast<float>(atof(sd[0].c_str()));
6382 float total_max = static_cast<float>(atof(sd[1].c_str()));
6383 int rate = atoi(sd[2].c_str());
6384
6385 float total_real = 0;
6386 if (subType == sTypeRAINWU || subType == sTypeRAINByRate)
6387 {
6388 total_real = total_max;
6389 }
6390 else
6391 {
6392 total_real = total_max - total_min;
6393 }
6394
6395
6396 if (total_real < 1000)
6397 {
6398 result = safe_query(
6399 "INSERT INTO Rain_Calendar (DeviceRowID, Total, Rate, Date) "
6400 "VALUES ('%" PRIu64 "', '%.2f', '%d', '%q')",
6401 ID,
6402 total_real,
6403 rate,
6404 szDateStart
6405 );
6406 }
6407 }
6408 }
6409 }
6410
AddCalendarUpdateMeter()6411 void CSQLHelper::AddCalendarUpdateMeter()
6412 {
6413 float EnergyDivider = 1000.0f;
6414 float GasDivider = 100.0f;
6415 float WaterDivider = 100.0f;
6416 float musage = 0;
6417 int tValue;
6418 if (GetPreferencesVar("MeterDividerEnergy", tValue))
6419 {
6420 EnergyDivider = float(tValue);
6421 }
6422 if (GetPreferencesVar("MeterDividerGas", tValue))
6423 {
6424 GasDivider = float(tValue);
6425 }
6426 if (GetPreferencesVar("MeterDividerWater", tValue))
6427 {
6428 WaterDivider = float(tValue);
6429 }
6430
6431 //Get All Meter devices
6432 std::vector<std::vector<std::string> > resultdevices;
6433 resultdevices = safe_query("SELECT DISTINCT(DeviceRowID) FROM Meter ORDER BY DeviceRowID");
6434 if (resultdevices.size() < 1)
6435 return; //nothing to do
6436
6437 char szDateStart[40];
6438 char szDateEnd[40];
6439
6440 time_t now = mytime(NULL);
6441 struct tm ltime;
6442 localtime_r(&now, <ime);
6443 sprintf(szDateEnd, "%04d-%02d-%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
6444
6445 time_t yesterday;
6446 struct tm tm2;
6447 getNoon(yesterday, tm2, ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday - 1); // we only want the date
6448 sprintf(szDateStart, "%04d-%02d-%02d", tm2.tm_year + 1900, tm2.tm_mon + 1, tm2.tm_mday);
6449
6450 std::vector<std::vector<std::string> > result;
6451
6452 for (const auto& itt : resultdevices)
6453 {
6454 std::vector<std::string> sddev = itt;
6455 uint64_t ID = std::stoull(sddev[0]);
6456
6457 //Get Device Information
6458 result = safe_query("SELECT Name, HardwareID, DeviceID, Unit, Type, SubType, SwitchType, Options FROM DeviceStatus WHERE (ID='%" PRIu64 "')", ID);
6459 if (result.empty())
6460 continue;
6461 std::vector<std::string> sd = result[0];
6462
6463 std::string sOptions = sd[7];
6464 std::map<std::string, std::string> options = BuildDeviceOptions(sOptions);
6465 // We don't want to update meter if externally managed
6466 if (options["DisableLogAutoUpdate"] == "true")
6467 {
6468 continue;
6469 }
6470 std::string devname = sd[0];
6471 //int hardwareID= atoi(sd[1].c_str());
6472 //std::string DeviceID=sd[2];
6473 //unsigned char Unit = atoi(sd[3].c_str());
6474 unsigned char devType = atoi(sd[4].c_str());
6475 unsigned char subType = atoi(sd[5].c_str());
6476 _eSwitchType switchtype = (_eSwitchType)atoi(sd[6].c_str());
6477 _eMeterType metertype = (_eMeterType)switchtype;
6478
6479 float tGasDivider = GasDivider;
6480
6481 if (devType == pTypeP1Power)
6482 {
6483 metertype = MTYPE_ENERGY;
6484 }
6485 else if (devType == pTypeP1Gas)
6486 {
6487 metertype = MTYPE_GAS;
6488 tGasDivider = 1000.0f;
6489 }
6490 else if ((devType == pTypeRego6XXValue) && (subType == sTypeRego6XXCounter))
6491 {
6492 metertype = MTYPE_COUNTER;
6493 }
6494
6495
6496 result = safe_query("SELECT MIN(Value), MAX(Value), AVG(Value) FROM Meter WHERE (DeviceRowID='%" PRIu64 "' AND Date>='%q' AND Date<='%q 00:00:00')",
6497 ID,
6498 szDateStart,
6499 szDateEnd
6500 );
6501 if (!result.empty())
6502 {
6503 std::vector<std::string> sd = result[0];
6504
6505 double total_min = (double)atof(sd[0].c_str());
6506 double total_max = (double)atof(sd[1].c_str());
6507 double avg_value = (double)atof(sd[2].c_str());
6508
6509 if (
6510 (devType != pTypeAirQuality) &&
6511 (devType != pTypeRFXSensor) &&
6512 (!((devType == pTypeGeneral) && (subType == sTypeVisibility))) &&
6513 (!((devType == pTypeGeneral) && (subType == sTypeDistance))) &&
6514 (!((devType == pTypeGeneral) && (subType == sTypeSolarRadiation))) &&
6515 (!((devType == pTypeGeneral) && (subType == sTypeSoilMoisture))) &&
6516 (!((devType == pTypeGeneral) && (subType == sTypeLeafWetness))) &&
6517 (!((devType == pTypeGeneral) && (subType == sTypeVoltage))) &&
6518 (!((devType == pTypeGeneral) && (subType == sTypeCurrent))) &&
6519 (!((devType == pTypeGeneral) && (subType == sTypePressure))) &&
6520 (!((devType == pTypeGeneral) && (subType == sTypeSoundLevel))) &&
6521 (devType != pTypeLux) &&
6522 (devType != pTypeWEIGHT) &&
6523 (devType != pTypeUsage)
6524 )
6525 {
6526 double total_real = total_max - total_min;
6527 double counter = total_max;
6528
6529 result = safe_query(
6530 "INSERT INTO Meter_Calendar (DeviceRowID, Value, Counter, Date) "
6531 "VALUES ('%" PRIu64 "', '%.2f', '%.2f', '%q')",
6532 ID,
6533 total_real,
6534 counter,
6535 szDateStart
6536 );
6537
6538 //Check for Notification
6539 musage = 0;
6540 switch (metertype)
6541 {
6542 case MTYPE_ENERGY:
6543 case MTYPE_ENERGY_GENERATED:
6544 musage = float(total_real) / EnergyDivider;
6545 if (musage != 0)
6546 m_notifications.CheckAndHandleNotification(ID, devname, devType, subType, NTYPE_TODAYENERGY, musage);
6547 break;
6548 case MTYPE_GAS:
6549 musage = float(total_real) / tGasDivider;
6550 if (musage != 0)
6551 m_notifications.CheckAndHandleNotification(ID, devname, devType, subType, NTYPE_TODAYGAS, musage);
6552 break;
6553 case MTYPE_WATER:
6554 musage = float(total_real) / WaterDivider;
6555 if (musage != 0)
6556 m_notifications.CheckAndHandleNotification(ID, devname, devType, subType, NTYPE_TODAYGAS, musage);
6557 break;
6558 case MTYPE_COUNTER:
6559 musage = float(total_real);
6560 if (musage != 0)
6561 m_notifications.CheckAndHandleNotification(ID, devname, devType, subType, NTYPE_TODAYCOUNTER, musage);
6562 break;
6563 default:
6564 //Unhandled
6565 musage = 0;
6566 break;
6567 }
6568 }
6569 else
6570 {
6571 //AirQuality/Usage Meter/Moisture/RFXSensor/Voltage/Lux/SoundLevel insert into MultiMeter_Calendar table
6572 result = safe_query(
6573 "INSERT INTO MultiMeter_Calendar (DeviceRowID, Value1,Value2,Value3,Value4,Value5,Value6, Date) "
6574 "VALUES ('%" PRIu64 "', '%.2f','%.2f','%.2f','%.2f','%.2f','%.2f', '%q')",
6575 ID,
6576 total_min, total_max, avg_value, 0.0f, 0.0f, 0.0f,
6577 szDateStart
6578 );
6579 }
6580 if (
6581 (devType != pTypeAirQuality) &&
6582 (devType != pTypeRFXSensor) &&
6583 ((devType != pTypeGeneral) && (subType != sTypeVisibility)) &&
6584 ((devType != pTypeGeneral) && (subType != sTypeDistance)) &&
6585 ((devType != pTypeGeneral) && (subType != sTypeSolarRadiation)) &&
6586 ((devType != pTypeGeneral) && (subType != sTypeVoltage)) &&
6587 ((devType != pTypeGeneral) && (subType != sTypeCurrent)) &&
6588 ((devType != pTypeGeneral) && (subType != sTypePressure)) &&
6589 ((devType != pTypeGeneral) && (subType != sTypeSoilMoisture)) &&
6590 ((devType != pTypeGeneral) && (subType != sTypeLeafWetness)) &&
6591 ((devType != pTypeGeneral) && (subType != sTypeSoundLevel)) &&
6592 (devType != pTypeLux) &&
6593 (devType != pTypeWEIGHT)
6594 )
6595 {
6596 result = safe_query("SELECT Value FROM Meter WHERE (DeviceRowID='%" PRIu64 "') ORDER BY ROWID DESC LIMIT 1", ID);
6597 if (!result.empty())
6598 {
6599 std::vector<std::string> sd = result[0];
6600 //Insert the last (max) counter value into the meter table to get the "today" value correct.
6601 result = safe_query(
6602 "INSERT INTO Meter (DeviceRowID, Value, Date) "
6603 "VALUES ('%" PRIu64 "', '%q', '%q')",
6604 ID,
6605 sd[0].c_str(),
6606 szDateEnd
6607 );
6608 }
6609 }
6610 }
6611 else
6612 {
6613 //no new meter result received in last day
6614 result = safe_query(
6615 "INSERT INTO Meter_Calendar (DeviceRowID, Value, Date) "
6616 "VALUES ('%" PRIu64 "', '%.2f', '%q')",
6617 ID,
6618 0.0f,
6619 szDateStart
6620 );
6621 }
6622 }
6623 }
6624
AddCalendarUpdateMultiMeter()6625 void CSQLHelper::AddCalendarUpdateMultiMeter()
6626 {
6627 float EnergyDivider = 1000.0f;
6628 int tValue;
6629 if (GetPreferencesVar("MeterDividerEnergy", tValue))
6630 {
6631 EnergyDivider = float(tValue);
6632 }
6633
6634 //Get All meter devices
6635 std::vector<std::vector<std::string> > resultdevices;
6636 resultdevices = safe_query("SELECT DISTINCT(DeviceRowID) FROM MultiMeter ORDER BY DeviceRowID");
6637 if (resultdevices.size() < 1)
6638 return; //nothing to do
6639
6640 char szDateStart[40];
6641 char szDateEnd[40];
6642
6643 time_t now = mytime(NULL);
6644 struct tm ltime;
6645 localtime_r(&now, <ime);
6646 sprintf(szDateEnd, "%04d-%02d-%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
6647
6648 time_t yesterday;
6649 struct tm tm2;
6650 getNoon(yesterday, tm2, ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday - 1); // we only want the date
6651 sprintf(szDateStart, "%04d-%02d-%02d", tm2.tm_year + 1900, tm2.tm_mon + 1, tm2.tm_mday);
6652
6653 std::vector<std::vector<std::string> > result;
6654
6655 for (const auto& itt : resultdevices)
6656 {
6657 std::vector<std::string> sddev = itt;
6658 uint64_t ID = std::stoull(sddev[0]);
6659
6660 //Get Device Information
6661 result = safe_query("SELECT Name, HardwareID, DeviceID, Unit, Type, SubType, SwitchType, Options FROM DeviceStatus WHERE (ID='%" PRIu64 "')", ID);
6662 if (result.empty())
6663 continue;
6664 std::vector<std::string> sd = result[0];
6665
6666 std::string sOptions = sd[7];
6667 std::map<std::string, std::string> options = BuildDeviceOptions(sOptions);
6668 // We don't want to update meter if externally managed
6669 if (options["DisableLogAutoUpdate"] == "true")
6670 {
6671 continue;
6672 }
6673
6674 std::string devname = sd[0];
6675 //int hardwareID= atoi(sd[1].c_str());
6676 //std::string DeviceID=sd[2];
6677 //unsigned char Unit = atoi(sd[3].c_str());
6678 unsigned char devType = atoi(sd[4].c_str());
6679 unsigned char subType = atoi(sd[5].c_str());
6680 //_eSwitchType switchtype=(_eSwitchType) atoi(sd[6].c_str());
6681 //_eMeterType metertype=(_eMeterType)switchtype;
6682
6683 result = safe_query(
6684 "SELECT MIN(Value1), MAX(Value1), MIN(Value2), MAX(Value2), MIN(Value3), MAX(Value3), MIN(Value4), MAX(Value4), MIN(Value5), MAX(Value5), MIN(Value6), MAX(Value6) FROM MultiMeter WHERE (DeviceRowID='%" PRIu64 "' AND Date>='%q' AND Date<='%q 00:00:00')",
6685 ID,
6686 szDateStart,
6687 szDateEnd
6688 );
6689 if (!result.empty())
6690 {
6691 std::vector<std::string> sd = result[0];
6692
6693 float total_real[6];
6694 float counter1 = 0;
6695 float counter2 = 0;
6696 float counter3 = 0;
6697 float counter4 = 0;
6698
6699 if (devType == pTypeP1Power)
6700 {
6701 for (int ii = 0; ii < 6; ii++)
6702 {
6703 float total_min = static_cast<float>(atof(sd[(ii * 2) + 0].c_str()));
6704 float total_max = static_cast<float>(atof(sd[(ii * 2) + 1].c_str()));
6705 total_real[ii] = total_max - total_min;
6706 }
6707 counter1 = static_cast<float>(atof(sd[1].c_str()));
6708 counter2 = static_cast<float>(atof(sd[3].c_str()));
6709 counter3 = static_cast<float>(atof(sd[9].c_str()));
6710 counter4 = static_cast<float>(atof(sd[11].c_str()));
6711 }
6712 else
6713 {
6714 for (int ii = 0; ii < 6; ii++)
6715 {
6716 float fvalue = static_cast<float>(atof(sd[ii].c_str()));
6717 total_real[ii] = fvalue;
6718 }
6719 }
6720
6721 result = safe_query(
6722 "INSERT INTO MultiMeter_Calendar (DeviceRowID, Value1, Value2, Value3, Value4, Value5, Value6, Counter1, Counter2, Counter3, Counter4, Date) "
6723 "VALUES ('%" PRIu64 "', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%q')",
6724 ID,
6725 total_real[0],
6726 total_real[1],
6727 total_real[2],
6728 total_real[3],
6729 total_real[4],
6730 total_real[5],
6731 counter1,
6732 counter2,
6733 counter3,
6734 counter4,
6735 szDateStart
6736 );
6737
6738 //Check for Notification
6739 if (devType == pTypeP1Power)
6740 {
6741 float musage = (total_real[0] + total_real[4]) / EnergyDivider;
6742 m_notifications.CheckAndHandleNotification(ID, devname, devType, subType, NTYPE_TODAYENERGY, musage);
6743 }
6744 /*
6745 //Insert the last (max) counter values into the table to get the "today" value correct.
6746 sprintf(szTmp,
6747 "INSERT INTO MultiMeter (DeviceRowID, Value1, Value2, Value3, Value4, Value5, Value6, Date) "
6748 "VALUES (%" PRIu64 ", %s, %s, %s, %s, %s, %s, '%s')",
6749 ID,
6750 sd[0].c_str(),
6751 sd[1].c_str(),
6752 sd[2].c_str(),
6753 sd[3].c_str(),
6754 sd[4].c_str(),
6755 sd[5].c_str(),
6756 szDateEnd
6757 );
6758 result=query(szTmp);
6759 */
6760 }
6761 }
6762 }
6763
AddCalendarUpdateWind()6764 void CSQLHelper::AddCalendarUpdateWind()
6765 {
6766 //Get All Wind devices
6767 std::vector<std::vector<std::string> > resultdevices;
6768 resultdevices = safe_query("SELECT DISTINCT(DeviceRowID) FROM Wind ORDER BY DeviceRowID");
6769 if (resultdevices.size() < 1)
6770 return; //nothing to do
6771
6772 char szDateStart[40];
6773 char szDateEnd[40];
6774
6775 time_t now = mytime(NULL);
6776 struct tm ltime;
6777 localtime_r(&now, <ime);
6778 sprintf(szDateEnd, "%04d-%02d-%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
6779
6780 time_t yesterday;
6781 struct tm tm2;
6782 getNoon(yesterday, tm2, ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday - 1); // we only want the date
6783 sprintf(szDateStart, "%04d-%02d-%02d", tm2.tm_year + 1900, tm2.tm_mon + 1, tm2.tm_mday);
6784
6785 std::vector<std::vector<std::string> > result;
6786
6787 for (const auto& itt : resultdevices)
6788 {
6789 std::vector<std::string> sddev = itt;
6790 uint64_t ID = std::stoull(sddev[0]);
6791
6792 result = safe_query("SELECT AVG(Direction), MIN(Speed), MAX(Speed), MIN(Gust), MAX(Gust) FROM Wind WHERE (DeviceRowID='%" PRIu64 "' AND Date>='%q' AND Date<='%q 00:00:00')",
6793 ID,
6794 szDateStart,
6795 szDateEnd
6796 );
6797 if (!result.empty())
6798 {
6799 std::vector<std::string> sd = result[0];
6800
6801 float Direction = static_cast<float>(atof(sd[0].c_str()));
6802 int speed_min = atoi(sd[1].c_str());
6803 int speed_max = atoi(sd[2].c_str());
6804 int gust_min = atoi(sd[3].c_str());
6805 int gust_max = atoi(sd[4].c_str());
6806
6807 result = safe_query(
6808 "INSERT INTO Wind_Calendar (DeviceRowID, Direction, Speed_Min, Speed_Max, Gust_Min, Gust_Max, Date) "
6809 "VALUES ('%" PRIu64 "', '%.2f', '%d', '%d', '%d', '%d', '%q')",
6810 ID,
6811 Direction,
6812 speed_min,
6813 speed_max,
6814 gust_min,
6815 gust_max,
6816 szDateStart
6817 );
6818 }
6819 }
6820 }
6821
AddCalendarUpdateUV()6822 void CSQLHelper::AddCalendarUpdateUV()
6823 {
6824 //Get All UV devices
6825 std::vector<std::vector<std::string> > resultdevices;
6826 resultdevices = safe_query("SELECT DISTINCT(DeviceRowID) FROM UV ORDER BY DeviceRowID");
6827 if (resultdevices.size() < 1)
6828 return; //nothing to do
6829
6830 char szDateStart[40];
6831 char szDateEnd[40];
6832
6833 time_t now = mytime(NULL);
6834 struct tm ltime;
6835 localtime_r(&now, <ime);
6836 sprintf(szDateEnd, "%04d-%02d-%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
6837
6838 time_t yesterday;
6839 struct tm tm2;
6840 getNoon(yesterday, tm2, ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday - 1); // we only want the date
6841 sprintf(szDateStart, "%04d-%02d-%02d", tm2.tm_year + 1900, tm2.tm_mon + 1, tm2.tm_mday);
6842
6843 std::vector<std::vector<std::string> > result;
6844
6845 for (const auto& itt : resultdevices)
6846 {
6847 std::vector<std::string> sddev = itt;
6848 uint64_t ID = std::stoull(sddev[0]);
6849
6850 result = safe_query("SELECT MAX(Level) FROM UV WHERE (DeviceRowID='%" PRIu64 "' AND Date>='%q' AND Date<='%q 00:00:00')",
6851 ID,
6852 szDateStart,
6853 szDateEnd
6854 );
6855 if (!result.empty())
6856 {
6857 std::vector<std::string> sd = result[0];
6858
6859 float level = static_cast<float>(atof(sd[0].c_str()));
6860
6861 result = safe_query(
6862 "INSERT INTO UV_Calendar (DeviceRowID, Level, Date) "
6863 "VALUES ('%" PRIu64 "', '%g', '%q')",
6864 ID,
6865 level,
6866 szDateStart
6867 );
6868 }
6869 }
6870 }
6871
AddCalendarUpdatePercentage()6872 void CSQLHelper::AddCalendarUpdatePercentage()
6873 {
6874 //Get All Percentage devices in the Percentage Table
6875 std::vector<std::vector<std::string> > resultdevices;
6876 resultdevices = safe_query("SELECT DISTINCT(DeviceRowID) FROM Percentage ORDER BY DeviceRowID");
6877 if (resultdevices.size() < 1)
6878 return; //nothing to do
6879
6880 char szDateStart[40];
6881 char szDateEnd[40];
6882
6883 time_t now = mytime(NULL);
6884 struct tm ltime;
6885 localtime_r(&now, <ime);
6886 sprintf(szDateEnd, "%04d-%02d-%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
6887
6888 time_t yesterday;
6889 struct tm tm2;
6890 getNoon(yesterday, tm2, ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday - 1); // we only want the date
6891 sprintf(szDateStart, "%04d-%02d-%02d", tm2.tm_year + 1900, tm2.tm_mon + 1, tm2.tm_mday);
6892
6893 std::vector<std::vector<std::string> > result;
6894
6895 for (const auto& itt : resultdevices)
6896 {
6897 std::vector<std::string> sddev = itt;
6898 uint64_t ID = std::stoull(sddev[0]);
6899
6900 result = safe_query("SELECT MIN(Percentage), MAX(Percentage), AVG(Percentage) FROM Percentage WHERE (DeviceRowID='%" PRIu64 "' AND Date>='%q' AND Date<='%q 00:00:00')",
6901 ID,
6902 szDateStart,
6903 szDateEnd
6904 );
6905 if (!result.empty())
6906 {
6907 std::vector<std::string> sd = result[0];
6908
6909 float percentage_min = static_cast<float>(atof(sd[0].c_str()));
6910 float percentage_max = static_cast<float>(atof(sd[1].c_str()));
6911 float percentage_avg = static_cast<float>(atof(sd[2].c_str()));
6912 result = safe_query(
6913 "INSERT INTO Percentage_Calendar (DeviceRowID, Percentage_Min, Percentage_Max, Percentage_Avg, Date) "
6914 "VALUES ('%" PRIu64 "', '%g', '%g', '%g','%q')",
6915 ID,
6916 percentage_min,
6917 percentage_max,
6918 percentage_avg,
6919 szDateStart
6920 );
6921 }
6922 }
6923 }
6924
6925
AddCalendarUpdateFan()6926 void CSQLHelper::AddCalendarUpdateFan()
6927 {
6928 //Get All FAN devices in the Fan Table
6929 std::vector<std::vector<std::string> > resultdevices;
6930 resultdevices = safe_query("SELECT DISTINCT(DeviceRowID) FROM Fan ORDER BY DeviceRowID");
6931 if (resultdevices.size() < 1)
6932 return; //nothing to do
6933
6934 char szDateStart[40];
6935 char szDateEnd[40];
6936
6937 time_t now = mytime(NULL);
6938 struct tm ltime;
6939 localtime_r(&now, <ime);
6940 sprintf(szDateEnd, "%04d-%02d-%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
6941
6942 time_t yesterday;
6943 struct tm tm2;
6944 getNoon(yesterday, tm2, ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday - 1); // we only want the date
6945 sprintf(szDateStart, "%04d-%02d-%02d", tm2.tm_year + 1900, tm2.tm_mon + 1, tm2.tm_mday);
6946
6947 std::vector<std::vector<std::string> > result;
6948
6949 for (const auto& itt : resultdevices)
6950 {
6951 std::vector<std::string> sddev = itt;
6952 uint64_t ID = std::stoull(sddev[0]);
6953
6954 result = safe_query("SELECT MIN(Speed), MAX(Speed), AVG(Speed) FROM Fan WHERE (DeviceRowID='%" PRIu64 "' AND Date>='%q' AND Date<='%q 00:00:00')",
6955 ID,
6956 szDateStart,
6957 szDateEnd
6958 );
6959 if (!result.empty())
6960 {
6961 std::vector<std::string> sd = result[0];
6962
6963 int speed_min = (int)atoi(sd[0].c_str());
6964 int speed_max = (int)atoi(sd[1].c_str());
6965 int speed_avg = (int)atoi(sd[2].c_str());
6966 result = safe_query(
6967 "INSERT INTO Fan_Calendar (DeviceRowID, Speed_Min, Speed_Max, Speed_Avg, Date) "
6968 "VALUES ('%" PRIu64 "', '%d', '%d', '%d','%q')",
6969 ID,
6970 speed_min,
6971 speed_max,
6972 speed_avg,
6973 szDateStart
6974 );
6975 }
6976 }
6977 }
6978
CleanupShortLog()6979 void CSQLHelper::CleanupShortLog()
6980 {
6981 int n5MinuteHistoryDays = 1;
6982 if (GetPreferencesVar("5MinuteHistoryDays", n5MinuteHistoryDays))
6983 {
6984 // If the history days is zero then all data in the short logs is deleted!
6985 if (n5MinuteHistoryDays == 0)
6986 {
6987 _log.Log(LOG_ERROR, "CleanupShortLog(): MinuteHistoryDays is zero!");
6988 return;
6989 }
6990 #if 0
6991 char szDateStr[40];
6992 time_t clear_time = mytime(NULL) - (n5MinuteHistoryDays * 24 * 3600);
6993 struct tm ltime;
6994 localtime_r(&clear_time, <ime);
6995 sprintf(szDateStr, "%04d-%02d-%02d %02d:%02d:%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday, ltime.tm_hour, ltime.tm_min, ltime.tm_sec);
6996 _log.Log(LOG_STATUS, "Cleaning up shortlog older than %s", szDateStr);
6997 #endif
6998
6999 char szQuery[250];
7000 std::string szQueryFilter = "strftime('%s',datetime('now','localtime')) - strftime('%s',Date) > (SELECT p.nValue * 86400 From Preferences AS p WHERE p.Key='5MinuteHistoryDays')";
7001
7002 sprintf(szQuery, "DELETE FROM Temperature WHERE %s", szQueryFilter.c_str());
7003 query(szQuery);
7004
7005 sprintf(szQuery, "DELETE FROM Rain WHERE %s", szQueryFilter.c_str());
7006 query(szQuery);
7007
7008 sprintf(szQuery, "DELETE FROM Wind WHERE %s", szQueryFilter.c_str());
7009 query(szQuery);
7010
7011 sprintf(szQuery, "DELETE FROM UV WHERE %s", szQueryFilter.c_str());
7012 query(szQuery);
7013
7014 sprintf(szQuery, "DELETE FROM Meter WHERE %s", szQueryFilter.c_str());
7015 query(szQuery);
7016
7017 sprintf(szQuery, "DELETE FROM MultiMeter WHERE %s", szQueryFilter.c_str());
7018 query(szQuery);
7019
7020 sprintf(szQuery, "DELETE FROM Percentage WHERE %s", szQueryFilter.c_str());
7021 query(szQuery);
7022
7023 sprintf(szQuery, "DELETE FROM Fan WHERE %s", szQueryFilter.c_str());
7024 query(szQuery);
7025 }
7026 }
7027
ClearShortLog()7028 void CSQLHelper::ClearShortLog()
7029 {
7030 query("DELETE FROM Temperature");
7031 query("DELETE FROM Rain");
7032 query("DELETE FROM Wind");
7033 query("DELETE FROM UV");
7034 query("DELETE FROM Meter");
7035 query("DELETE FROM MultiMeter");
7036 query("DELETE FROM Percentage");
7037 query("DELETE FROM Fan");
7038 VacuumDatabase();
7039 }
7040
VacuumDatabase()7041 void CSQLHelper::VacuumDatabase()
7042 {
7043 query("VACUUM");
7044 }
7045
OptimizeDatabase(sqlite3 * dbase)7046 void CSQLHelper::OptimizeDatabase(sqlite3* dbase)
7047 {
7048 if (dbase == NULL)
7049 return;
7050 sqlite3_exec(dbase, "PRAGMA optimize;", NULL, NULL, NULL);
7051 }
7052
DeleteHardware(const std::string & idx)7053 void CSQLHelper::DeleteHardware(const std::string& idx)
7054 {
7055 safe_query("DELETE FROM Hardware WHERE (ID == '%q')", idx.c_str());
7056
7057 //and now delete all records in the DeviceStatus table itself
7058 std::vector<std::vector<std::string> > result;
7059 result = safe_query("SELECT ID FROM DeviceStatus WHERE (HardwareID == '%q')", idx.c_str());
7060 if (!result.empty())
7061 {
7062 std::string devs2delete = "";
7063 for (const auto& itt : result)
7064 {
7065 std::vector<std::string> sd = itt;
7066 if (!devs2delete.empty())
7067 devs2delete += ";";
7068 devs2delete += sd[0];
7069 }
7070 DeleteDevices(devs2delete);
7071 }
7072 //also delete all records in other tables
7073 safe_query("DELETE FROM ZWaveNodes WHERE (HardwareID== '%q')", idx.c_str());
7074 safe_query("DELETE FROM EnoceanSensors WHERE (HardwareID== '%q')", idx.c_str());
7075 safe_query("DELETE FROM MySensors WHERE (HardwareID== '%q')", idx.c_str());
7076 safe_query("DELETE FROM WOLNodes WHERE (HardwareID == '%q')", idx.c_str());
7077 }
7078
DeleteCamera(const std::string & idx)7079 void CSQLHelper::DeleteCamera(const std::string& idx)
7080 {
7081 safe_query("DELETE FROM Cameras WHERE (ID == '%q')", idx.c_str());
7082 safe_query("DELETE FROM CamerasActiveDevices WHERE (CameraRowID == '%q')", idx.c_str());
7083 }
7084
DeletePlan(const std::string & idx)7085 void CSQLHelper::DeletePlan(const std::string& idx)
7086 {
7087 safe_query("DELETE FROM Plans WHERE (ID == '%q')", idx.c_str());
7088 }
7089
DeleteEvent(const std::string & idx)7090 void CSQLHelper::DeleteEvent(const std::string& idx)
7091 {
7092 safe_query("DELETE FROM EventRules WHERE (EMID == '%q')", idx.c_str());
7093 safe_query("DELETE FROM EventMaster WHERE (ID == '%q')", idx.c_str());
7094 }
7095
7096 //Argument, one or multiple devices separated by a semicolumn (;)
DeleteDevices(const std::string & idx)7097 void CSQLHelper::DeleteDevices(const std::string& idx)
7098 {
7099 std::vector<std::string> _idx;
7100 StringSplit(idx, ";", _idx);
7101 if (_idx.empty())
7102 return;
7103 std::set<std::pair<std::string, std::string> > removeddevices;
7104 #ifdef ENABLE_PYTHON
7105 for (const auto& itt : _idx)
7106 {
7107 _log.Debug(DEBUG_NORM, "CSQLHelper::DeleteDevices: ID: %s", itt.c_str());
7108 std::vector<std::vector<std::string> > result;
7109 result = safe_query("SELECT HardwareID, Unit FROM DeviceStatus WHERE (ID == '%q')", itt.c_str());
7110 if (!result.empty())
7111 {
7112 std::vector<std::string> sd = result[0];
7113 std::string HwID = sd[0];
7114 std::string Unit = sd[1];
7115 CDomoticzHardwareBase* pHardware = m_mainworker.GetHardwareByIDType(HwID, HTYPE_PythonPlugin);
7116 if (pHardware != NULL)
7117 {
7118 removeddevices.insert(std::make_pair(HwID, Unit));
7119 }
7120 }
7121 }
7122 #endif
7123 {
7124 //Avoid mutex deadlock here
7125 std::lock_guard<std::mutex> l(m_sqlQueryMutex);
7126
7127 char* errorMessage;
7128 sqlite3_exec(m_dbase, "BEGIN TRANSACTION", NULL, NULL, &errorMessage);
7129
7130 for (const auto& itt : _idx)
7131 {
7132 safe_exec_no_return("DELETE FROM LightingLog WHERE (DeviceRowID == '%q')", itt.c_str());
7133 safe_exec_no_return("DELETE FROM LightSubDevices WHERE (ParentID == '%q')", itt.c_str());
7134 safe_exec_no_return("DELETE FROM LightSubDevices WHERE (DeviceRowID == '%q')", itt.c_str());
7135 safe_exec_no_return("DELETE FROM Notifications WHERE (DeviceRowID == '%q')", itt.c_str());
7136 safe_exec_no_return("DELETE FROM Rain WHERE (DeviceRowID == '%q')", itt.c_str());
7137 safe_exec_no_return("DELETE FROM Rain_Calendar WHERE (DeviceRowID == '%q')", itt.c_str());
7138 safe_exec_no_return("DELETE FROM Temperature WHERE (DeviceRowID == '%q')", itt.c_str());
7139 safe_exec_no_return("DELETE FROM Temperature_Calendar WHERE (DeviceRowID == '%q')", itt.c_str());
7140 safe_exec_no_return("DELETE FROM Timers WHERE (DeviceRowID == '%q')", itt.c_str());
7141 safe_exec_no_return("DELETE FROM SetpointTimers WHERE (DeviceRowID == '%q')", itt.c_str());
7142 safe_exec_no_return("DELETE FROM UV WHERE (DeviceRowID == '%q')", itt.c_str());
7143 safe_exec_no_return("DELETE FROM UV_Calendar WHERE (DeviceRowID == '%q')", itt.c_str());
7144 safe_exec_no_return("DELETE FROM Wind WHERE (DeviceRowID == '%q')", itt.c_str());
7145 safe_exec_no_return("DELETE FROM Wind_Calendar WHERE (DeviceRowID == '%q')", itt.c_str());
7146 safe_exec_no_return("DELETE FROM Meter WHERE (DeviceRowID == '%q')", itt.c_str());
7147 safe_exec_no_return("DELETE FROM Meter_Calendar WHERE (DeviceRowID == '%q')", itt.c_str());
7148 safe_exec_no_return("DELETE FROM MultiMeter WHERE (DeviceRowID == '%q')", itt.c_str());
7149 safe_exec_no_return("DELETE FROM MultiMeter_Calendar WHERE (DeviceRowID == '%q')", itt.c_str());
7150 safe_exec_no_return("DELETE FROM Percentage WHERE (DeviceRowID == '%q')", itt.c_str());
7151 safe_exec_no_return("DELETE FROM Percentage_Calendar WHERE (DeviceRowID == '%q')", itt.c_str());
7152 safe_exec_no_return("DELETE FROM Fan WHERE (DeviceRowID == '%q')", itt.c_str());
7153 safe_exec_no_return("DELETE FROM Fan_Calendar WHERE (DeviceRowID == '%q')", itt.c_str());
7154 safe_exec_no_return("DELETE FROM SceneDevices WHERE (DeviceRowID == '%q')", itt.c_str());
7155 safe_exec_no_return("DELETE FROM DeviceToPlansMap WHERE (DeviceRowID == '%q')", itt.c_str());
7156 safe_exec_no_return("DELETE FROM CamerasActiveDevices WHERE (DevSceneType==0) AND (DevSceneRowID == '%q')", itt.c_str());
7157 safe_exec_no_return("DELETE FROM SharedDevices WHERE (DeviceRowID== '%q')", itt.c_str());
7158 safe_exec_no_return("DELETE FROM PushLink WHERE (DeviceRowID== '%q')", itt.c_str());
7159 //notify eventsystem device is no longer present
7160 uint64_t ullidx = std::stoull(itt);
7161 m_mainworker.m_eventsystem.RemoveSingleState(ullidx, m_mainworker.m_eventsystem.REASON_DEVICE);
7162 //and now delete all records in the DeviceStatus table itself
7163 safe_exec_no_return("DELETE FROM DeviceStatus WHERE (ID == '%q')", itt.c_str());
7164 }
7165 sqlite3_exec(m_dbase, "COMMIT TRANSACTION", NULL, NULL, &errorMessage);
7166 }
7167 #ifdef ENABLE_PYTHON
7168 for (const auto& it : removeddevices)
7169 {
7170 int HwID = atoi(it.first.c_str());
7171 int Unit = atoi(it.second.c_str());
7172 // Notify plugin to sync plugins' device list
7173 CDomoticzHardwareBase* pHardware = m_mainworker.GetHardware(HwID);
7174 if (pHardware != NULL && pHardware->HwdType == HTYPE_PythonPlugin)
7175 {
7176 _log.Debug(DEBUG_NORM, "CSQLHelper::DeleteDevices: Notifying plugin %u about deletion of device %u", HwID, Unit);
7177 Plugins::CPlugin* pPlugin = (Plugins::CPlugin*)pHardware;
7178 pPlugin->DeviceRemoved(Unit);
7179 }
7180 }
7181 #endif
7182
7183 m_notifications.ReloadNotifications();
7184 }
7185
7186 //Argument, one or multiple devices separated by a semicolumn (;)
DeleteScenes(const std::string & idx)7187 void CSQLHelper::DeleteScenes(const std::string& idx)
7188 {
7189 std::vector<std::string> _idx;
7190 StringSplit(idx, ";", _idx);
7191 if (_idx.empty())
7192 return;
7193 {
7194 //Avoid mutex deadlock here
7195 std::lock_guard<std::mutex> l(m_sqlQueryMutex);
7196
7197 char* errorMessage;
7198 sqlite3_exec(m_dbase, "BEGIN TRANSACTION", NULL, NULL, &errorMessage);
7199
7200 for (const auto& itt : _idx)
7201 {
7202 safe_exec_no_return("DELETE FROM Scenes WHERE (ID == '%q')", itt.c_str());
7203 safe_exec_no_return("DELETE FROM SceneDevices WHERE (SceneRowID == '%q')", itt.c_str());
7204 safe_exec_no_return("DELETE FROM SceneTimers WHERE (SceneRowID == '%q')", itt.c_str());
7205 safe_exec_no_return("DELETE FROM SceneLog WHERE (SceneRowID=='%q')", itt.c_str());
7206 uint64_t ullidx = std::stoull(itt);
7207 m_mainworker.m_eventsystem.RemoveSingleState(ullidx, m_mainworker.m_eventsystem.REASON_SCENEGROUP);
7208 }
7209
7210 sqlite3_exec(m_dbase, "COMMIT TRANSACTION", NULL, NULL, &errorMessage);
7211 }
7212
7213 m_notifications.ReloadNotifications();
7214 }
7215
TransferDevice(const std::string & idx,const std::string & newidx)7216 void CSQLHelper::TransferDevice(const std::string& idx, const std::string& newidx)
7217 {
7218 std::vector<std::vector<std::string> > result;
7219
7220 safe_query("UPDATE LightingLog SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7221 safe_query("UPDATE LightSubDevices SET ParentID='%q' WHERE (ParentID == '%q')", newidx.c_str(), idx.c_str());
7222 safe_query("UPDATE LightSubDevices SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7223 safe_query("UPDATE Notifications SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7224 safe_query("UPDATE DeviceToPlansMap SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7225 safe_query("UPDATE SharedDevices SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7226 safe_query("UPDATE Timers SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7227
7228 //Rain
7229 result = safe_query("SELECT Date FROM Rain WHERE (DeviceRowID == '%q') ORDER BY Date ASC LIMIT 1", newidx.c_str());
7230 if (!result.empty())
7231 safe_query("UPDATE Rain SET DeviceRowID='%q' WHERE (DeviceRowID == '%q') AND (Date<'%q')", newidx.c_str(), idx.c_str(), result[0][0].c_str());
7232 else
7233 safe_query("UPDATE Rain SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7234
7235 result = safe_query("SELECT Date FROM Rain_Calendar WHERE (DeviceRowID == '%q') ORDER BY Date ASC LIMIT 1", newidx.c_str());
7236 if (!result.empty())
7237 safe_query("UPDATE Rain_Calendar SET DeviceRowID='%q' WHERE (DeviceRowID == '%q') AND (Date<'%q')", newidx.c_str(), idx.c_str(), result[0][0].c_str());
7238 else
7239 safe_query("UPDATE Rain_Calendar SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7240
7241 //Temperature
7242 result = safe_query("SELECT Date FROM Temperature WHERE (DeviceRowID == '%q') ORDER BY Date ASC LIMIT 1", newidx.c_str());
7243 if (!result.empty())
7244 safe_query("UPDATE Temperature SET DeviceRowID='%q' WHERE (DeviceRowID == '%q') AND (Date<'%q')", newidx.c_str(), idx.c_str(), result[0][0].c_str());
7245 else
7246 safe_query("UPDATE Temperature SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7247
7248 result = safe_query("SELECT Date FROM Temperature_Calendar WHERE (DeviceRowID == '%q') ORDER BY Date ASC LIMIT 1", newidx.c_str());
7249 if (!result.empty())
7250 safe_query("UPDATE Temperature_Calendar SET DeviceRowID='%q' WHERE (DeviceRowID == '%q') AND (Date<'%q')", newidx.c_str(), idx.c_str(), result[0][0].c_str());
7251 else
7252 safe_query("UPDATE Temperature_Calendar SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7253
7254 //UV
7255 result = safe_query("SELECT Date FROM UV WHERE (DeviceRowID == '%q') ORDER BY Date ASC LIMIT 1", newidx.c_str());
7256 if (!result.empty())
7257 safe_query("UPDATE UV SET DeviceRowID='%q' WHERE (DeviceRowID == '%q') AND (Date<'%q')", newidx.c_str(), idx.c_str(), result[0][0].c_str());
7258 else
7259 safe_query("UPDATE UV SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7260
7261 result = safe_query("SELECT Date FROM UV_Calendar WHERE (DeviceRowID == '%q') ORDER BY Date ASC LIMIT 1", newidx.c_str());
7262 if (!result.empty())
7263 safe_query("UPDATE UV_Calendar SET DeviceRowID='%q' WHERE (DeviceRowID == '%q') AND (Date<'%q')", newidx.c_str(), idx.c_str(), result[0][0].c_str());
7264 else
7265 safe_query("UPDATE UV_Calendar SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7266
7267 //Wind
7268 result = safe_query("SELECT Date FROM Wind WHERE (DeviceRowID == '%q') ORDER BY Date ASC LIMIT 1", newidx.c_str());
7269 if (!result.empty())
7270 safe_query("UPDATE Wind SET DeviceRowID='%q' WHERE (DeviceRowID == '%q') AND (Date<'%q')", newidx.c_str(), idx.c_str(), result[0][0].c_str());
7271 else
7272 safe_query("UPDATE Wind SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7273
7274 result = safe_query("SELECT Date FROM Wind_Calendar WHERE (DeviceRowID == '%q') ORDER BY Date ASC LIMIT 1", newidx.c_str());
7275 if (!result.empty())
7276 safe_query("UPDATE Wind_Calendar SET DeviceRowID='%q' WHERE (DeviceRowID == '%q') AND (Date<'%q')", newidx.c_str(), idx.c_str(), result[0][0].c_str());
7277 else
7278 safe_query("UPDATE Wind_Calendar SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7279
7280 //Meter
7281 result = safe_query("SELECT Date FROM Meter WHERE (DeviceRowID == '%q') ORDER BY Date ASC LIMIT 1", newidx.c_str());
7282 if (!result.empty())
7283 safe_query("UPDATE Meter SET DeviceRowID='%q' WHERE (DeviceRowID == '%q') AND (Date<'%q')", newidx.c_str(), idx.c_str(), result[0][0].c_str());
7284 else
7285 safe_query("UPDATE Meter SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7286
7287 result = safe_query("SELECT Date FROM Meter_Calendar WHERE (DeviceRowID == '%q') ORDER BY Date ASC LIMIT 1", newidx.c_str());
7288 if (!result.empty())
7289 safe_query("UPDATE Meter_Calendar SET DeviceRowID='%q' WHERE (DeviceRowID == '%q') AND (Date<'%q')", newidx.c_str(), idx.c_str(), result[0][0].c_str());
7290 else
7291 safe_query("UPDATE Meter_Calendar SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7292
7293 //Multimeter
7294 result = safe_query("SELECT Date FROM MultiMeter WHERE (DeviceRowID == '%q') ORDER BY Date ASC LIMIT 1", newidx.c_str());
7295 if (!result.empty())
7296 safe_query("UPDATE MultiMeter SET DeviceRowID='%q' WHERE (DeviceRowID == '%q') AND (Date<'%q')", newidx.c_str(), idx.c_str(), result[0][0].c_str());
7297 else
7298 safe_query("UPDATE MultiMeter SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7299
7300 result = safe_query("SELECT Date FROM MultiMeter_Calendar WHERE (DeviceRowID == '%q') ORDER BY Date ASC LIMIT 1", newidx.c_str());
7301 if (!result.empty())
7302 safe_query("UPDATE MultiMeter_Calendar SET DeviceRowID='%q' WHERE (DeviceRowID == '%q') AND (Date<'%q')", newidx.c_str(), idx.c_str(), result[0][0].c_str());
7303 else
7304 safe_query("UPDATE MultiMeter_Calendar SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7305
7306 //Fan
7307 result = safe_query("SELECT Date FROM Fan WHERE (DeviceRowID == '%q') ORDER BY Date ASC LIMIT 1", newidx.c_str());
7308 if (!result.empty())
7309 safe_query("UPDATE Fan SET DeviceRowID='%q' WHERE (DeviceRowID == '%q') AND (Date<'%q')", newidx.c_str(), idx.c_str(), result[0][0].c_str());
7310 else
7311 safe_query("UPDATE Fan SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7312
7313 result = safe_query("SELECT Date FROM Fan_Calendar WHERE (DeviceRowID == '%q') ORDER BY Date ASC LIMIT 1", newidx.c_str());
7314 if (!result.empty())
7315 safe_query("UPDATE Fan_Calendar SET DeviceRowID='%q' WHERE (DeviceRowID == '%q') AND (Date<'%q')", newidx.c_str(), idx.c_str(), result[0][0].c_str());
7316 else
7317 safe_query("UPDATE Fan_Calendar SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7318
7319
7320 //Percentage
7321 result = safe_query("SELECT Date FROM Percentage WHERE (DeviceRowID == '%q') ORDER BY Date ASC LIMIT 1", newidx.c_str());
7322 if (!result.empty())
7323 safe_query("UPDATE Percentage SET DeviceRowID='%q' WHERE (DeviceRowID == '%q') AND (Date<'%q')", newidx.c_str(), idx.c_str(), result[0][0].c_str());
7324 else
7325 safe_query("UPDATE Percentage SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7326
7327 result = safe_query("SELECT Date FROM Percentage_Calendar WHERE (DeviceRowID == '%q') ORDER BY Date ASC LIMIT 1", newidx.c_str());
7328 if (!result.empty())
7329 safe_query("UPDATE Percentage_Calendar SET DeviceRowID='%q' WHERE (DeviceRowID == '%q') AND (Date<'%q')", newidx.c_str(), idx.c_str(), result[0][0].c_str());
7330 else
7331 safe_query("UPDATE Percentage_Calendar SET DeviceRowID='%q' WHERE (DeviceRowID == '%q')", newidx.c_str(), idx.c_str());
7332 }
7333
CheckAndUpdateDeviceOrder()7334 void CSQLHelper::CheckAndUpdateDeviceOrder()
7335 {
7336 std::vector<std::vector<std::string> > result;
7337
7338 //Get All ID's where Order=0
7339 result = safe_query("SELECT ROWID FROM DeviceStatus WHERE ([Order]==0)");
7340 if (!result.empty())
7341 {
7342 for (const auto& itt : result)
7343 {
7344 std::vector<std::string> sd = itt;
7345 safe_query("UPDATE DeviceStatus SET [Order] = (SELECT MAX([Order]) FROM DeviceStatus)+1 WHERE (ROWID == '%q')", sd[0].c_str());
7346 }
7347 }
7348 }
7349
CheckAndUpdateSceneDeviceOrder()7350 void CSQLHelper::CheckAndUpdateSceneDeviceOrder()
7351 {
7352 std::vector<std::vector<std::string> > result;
7353
7354 //Get All ID's where Order=0
7355 result = safe_query("SELECT ROWID FROM SceneDevices WHERE ([Order]==0)");
7356 if (!result.empty())
7357 {
7358 for (const auto& itt : result)
7359 {
7360 std::vector<std::string> sd = itt;
7361 safe_query("UPDATE SceneDevices SET [Order] = (SELECT MAX([Order]) FROM SceneDevices)+1 WHERE (ROWID == '%q')", sd[0].c_str());
7362 }
7363 }
7364 }
7365
CleanupLightSceneLog()7366 void CSQLHelper::CleanupLightSceneLog()
7367 {
7368 //cleanup the lighting log
7369 int nMaxDays = 30;
7370 GetPreferencesVar("LightHistoryDays", nMaxDays);
7371
7372 char szDateEnd[40];
7373 time_t now = mytime(NULL);
7374 struct tm tm1;
7375 localtime_r(&now, &tm1);
7376
7377 time_t daybefore;
7378 struct tm tm2;
7379 constructTime(daybefore, tm2, tm1.tm_year + 1900, tm1.tm_mon + 1, tm1.tm_mday - nMaxDays, tm1.tm_hour, tm1.tm_min, 0, tm1.tm_isdst);
7380 sprintf(szDateEnd, "%04d-%02d-%02d %02d:%02d:00", tm2.tm_year + 1900, tm2.tm_mon + 1, tm2.tm_mday, tm2.tm_hour, tm2.tm_min);
7381
7382
7383 safe_query("DELETE FROM LightingLog WHERE (Date<'%q')", szDateEnd);
7384 safe_query("DELETE FROM SceneLog WHERE (Date<'%q')", szDateEnd);
7385 }
7386
DoesSceneByNameExits(const std::string & SceneName)7387 bool CSQLHelper::DoesSceneByNameExits(const std::string& SceneName)
7388 {
7389 std::vector<std::vector<std::string> > result;
7390
7391 //Get All ID's where Order=0
7392 result = safe_query("SELECT ID FROM Scenes WHERE (Name=='%q')", SceneName.c_str());
7393 return (result.size() > 0);
7394 }
7395
CheckSceneStatusWithDevice(const std::string & DevIdx)7396 void CSQLHelper::CheckSceneStatusWithDevice(const std::string& DevIdx)
7397 {
7398 std::stringstream s_str(DevIdx);
7399 uint64_t idxll;
7400 s_str >> idxll;
7401 return CheckSceneStatusWithDevice(idxll);
7402 }
7403
CheckSceneStatusWithDevice(const uint64_t DevIdx)7404 void CSQLHelper::CheckSceneStatusWithDevice(const uint64_t DevIdx)
7405 {
7406 std::vector<std::vector<std::string> > result;
7407
7408 result = safe_query("SELECT SceneRowID FROM SceneDevices WHERE (DeviceRowID == %" PRIu64 ")", DevIdx);
7409 for (const auto& itt : result)
7410 {
7411 std::vector<std::string> sd = itt;
7412 CheckSceneStatus(sd[0]);
7413 }
7414 }
7415
CheckSceneStatus(const std::string & Idx)7416 void CSQLHelper::CheckSceneStatus(const std::string& Idx)
7417 {
7418 uint64_t idxll = std::stoull(Idx);
7419 return CheckSceneStatus(idxll);
7420 }
7421
CheckSceneStatus(const uint64_t Idx)7422 void CSQLHelper::CheckSceneStatus(const uint64_t Idx)
7423 {
7424 std::vector<std::vector<std::string> > result;
7425
7426 result = safe_query("SELECT nValue FROM Scenes WHERE (ID == %" PRIu64 ")", Idx);
7427 if (result.empty())
7428 return; //not found
7429
7430 unsigned char orgValue = (unsigned char)atoi(result[0][0].c_str());
7431 unsigned char newValue = orgValue;
7432
7433 result = safe_query("SELECT a.ID, a.DeviceID, a.Unit, a.Type, a.SubType, a.SwitchType, a.nValue, a.sValue FROM DeviceStatus AS a, SceneDevices as b WHERE (a.ID == b.DeviceRowID) AND (b.SceneRowID == %" PRIu64 ")",
7434 Idx);
7435 if (result.empty())
7436 return; //no devices in scene
7437
7438 std::vector<bool> _DeviceStatusResults;
7439
7440 for (const auto& itt : result)
7441 {
7442 std::vector<std::string> sd = itt;
7443 int nValue = atoi(sd[6].c_str());
7444 std::string sValue = sd[7];
7445 //unsigned char Unit=atoi(sd[2].c_str());
7446 unsigned char dType = atoi(sd[3].c_str());
7447 unsigned char dSubType = atoi(sd[4].c_str());
7448 _eSwitchType switchtype = (_eSwitchType)atoi(sd[5].c_str());
7449
7450 std::string lstatus = "";
7451 int llevel = 0;
7452 bool bHaveDimmer = false;
7453 bool bHaveGroupCmd = false;
7454 int maxDimLevel = 0;
7455
7456 GetLightStatus(dType, dSubType, switchtype, nValue, sValue, lstatus, llevel, bHaveDimmer, maxDimLevel, bHaveGroupCmd);
7457 _DeviceStatusResults.push_back(IsLightSwitchOn(lstatus));
7458 }
7459
7460 //Check if all on/off
7461 size_t totOn = 0;
7462 size_t totOff = 0;
7463
7464 for (const auto& itt2 : _DeviceStatusResults)
7465 {
7466 if (itt2 == true)
7467 totOn++;
7468 else
7469 totOff++;
7470 }
7471 if (totOn == _DeviceStatusResults.size())
7472 {
7473 //All are on
7474 newValue = 1;
7475 }
7476 else if (totOff == _DeviceStatusResults.size())
7477 {
7478 //All are Off
7479 newValue = 0;
7480 }
7481 else
7482 {
7483 //Some are on, some are off
7484 newValue = 2;
7485 }
7486 if (newValue != orgValue)
7487 {
7488 //Set new Scene status
7489 safe_query("UPDATE Scenes SET nValue=%d WHERE (ID == %" PRIu64 ")",
7490 int(newValue), Idx);
7491 if (m_sql.m_bEnableEventSystem) // Only when eventSystem is active
7492 m_mainworker.m_eventsystem.GetCurrentScenesGroups();
7493 }
7494 }
7495
DeleteDataPoint(const char * ID,const std::string & Date)7496 void CSQLHelper::DeleteDataPoint(const char* ID, const std::string& Date)
7497 {
7498 std::vector<std::vector<std::string> > result;
7499 result = safe_query("SELECT Type,SubType FROM DeviceStatus WHERE (ID==%q)", ID);
7500 if (result.empty())
7501 return;
7502
7503 if (Date.find(':') != std::string::npos)
7504 {
7505 char szDateEnd[100];
7506
7507 time_t now = mytime(NULL);
7508 struct tm tLastUpdate;
7509 localtime_r(&now, &tLastUpdate);
7510
7511 time_t cEndTime;
7512 ParseSQLdatetime(cEndTime, tLastUpdate, Date, tLastUpdate.tm_isdst);
7513 tLastUpdate.tm_min += 2;
7514 sprintf(szDateEnd, "%04d-%02d-%02d %02d:%02d:%02d", tLastUpdate.tm_year + 1900, tLastUpdate.tm_mon + 1, tLastUpdate.tm_mday, tLastUpdate.tm_hour, tLastUpdate.tm_min, tLastUpdate.tm_sec);
7515
7516 //Short log
7517 safe_query("DELETE FROM Rain WHERE (DeviceRowID=='%q') AND (Date>='%q') AND (Date<='%q')", ID, Date.c_str(), szDateEnd);
7518 safe_query("DELETE FROM Wind WHERE (DeviceRowID=='%q') AND (Date>='%q') AND (Date<='%q')", ID, Date.c_str(), szDateEnd);
7519 safe_query("DELETE FROM UV WHERE (DeviceRowID=='%q') AND (Date>='%q') AND (Date<='%q')", ID, Date.c_str(), szDateEnd);
7520 safe_query("DELETE FROM Temperature WHERE (DeviceRowID=='%q') AND (Date>='%q') AND (Date<='%q')", ID, Date.c_str(), szDateEnd);
7521 safe_query("DELETE FROM Meter WHERE (DeviceRowID=='%q') AND (Date>='%q') AND (Date<='%q')", ID, Date.c_str(), szDateEnd);
7522 safe_query("DELETE FROM MultiMeter WHERE (DeviceRowID=='%q') AND (Date>='%q') AND (Date<='%q')", ID, Date.c_str(), szDateEnd);
7523 safe_query("DELETE FROM Percentage WHERE (DeviceRowID=='%q') AND (Date>='%q') AND (Date<='%q')", ID, Date.c_str(), szDateEnd);
7524 safe_query("DELETE FROM Fan WHERE (DeviceRowID=='%q') AND (Date>='%q') AND (Date<='%q')", ID, Date.c_str(), szDateEnd);
7525 }
7526 else
7527 {
7528 //Day/Month/Year
7529 safe_query("DELETE FROM Rain_Calendar WHERE (DeviceRowID=='%q') AND (Date=='%q')", ID, Date.c_str());
7530 safe_query("DELETE FROM Wind_Calendar WHERE (DeviceRowID=='%q') AND (Date=='%q')", ID, Date.c_str());
7531 safe_query("DELETE FROM UV_Calendar WHERE (DeviceRowID=='%q') AND (Date=='%q')", ID, Date.c_str());
7532 safe_query("DELETE FROM Temperature_Calendar WHERE (DeviceRowID=='%q') AND (Date=='%q')", ID, Date.c_str());
7533 safe_query("DELETE FROM Meter_Calendar WHERE (DeviceRowID=='%q') AND (Date=='%q')", ID, Date.c_str());
7534 safe_query("DELETE FROM MultiMeter_Calendar WHERE (DeviceRowID=='%q') AND (Date=='%q')", ID, Date.c_str());
7535 safe_query("DELETE FROM Percentage_Calendar WHERE (DeviceRowID=='%q') AND (Date=='%q')", ID, Date.c_str());
7536 safe_query("DELETE FROM Fan_Calendar WHERE (DeviceRowID=='%q') AND (Date=='%q')", ID, Date.c_str());
7537 }
7538 }
7539
AddTaskItem(const _tTaskItem & tItem,const bool cancelItem)7540 void CSQLHelper::AddTaskItem(const _tTaskItem& tItem, const bool cancelItem)
7541 {
7542 std::lock_guard<std::mutex> l(m_background_task_mutex);
7543
7544 // Check if an event for the same device is already in queue, and if so, replace it
7545 _log.Debug(DEBUG_NORM, "SQLH AddTask: Request to add task: idx=%" PRIu64 ", DelayTime=%f, Command='%s', Level=%d, Color='%s', RelatedEvent='%s'", tItem._idx, tItem._DelayTime, tItem._command.c_str(), tItem._level, tItem._Color.toString().c_str(), tItem._relatedEvent.c_str());
7546 // Remove any previous task linked to the same device
7547
7548 if (
7549 (tItem._ItemType == TITEM_SWITCHCMD_EVENT) ||
7550 (tItem._ItemType == TITEM_SWITCHCMD_SCENE) ||
7551 (tItem._ItemType == TITEM_UPDATEDEVICE) ||
7552 (tItem._ItemType == TITEM_SET_VARIABLE)
7553 )
7554 {
7555 std::vector<_tTaskItem>::iterator itt = m_background_task_queue.begin();
7556 while (itt != m_background_task_queue.end())
7557 {
7558 _log.Debug(DEBUG_NORM, "SQLH AddTask: Comparing with item in queue: idx=%" PRId64 ", DelayTime=%f, Command='%s', Level=%d, Color='%s', RelatedEvent='%s'", itt->_idx, itt->_DelayTime, itt->_command.c_str(), itt->_level, itt->_Color.toString().c_str(), itt->_relatedEvent.c_str());
7559 if (itt->_idx == tItem._idx && itt->_ItemType == tItem._ItemType)
7560 {
7561 float iDelayDiff = tItem._DelayTime - itt->_DelayTime;
7562 if (iDelayDiff < (1. / timer_resolution_hz / 2))
7563 {
7564 _log.Debug(DEBUG_NORM, "SQLH AddTask: => Already present. Cancelling previous task item");
7565 itt = m_background_task_queue.erase(itt);
7566 }
7567 else
7568 ++itt;
7569 }
7570 else
7571 ++itt;
7572 }
7573 }
7574 // _log.Log(LOG_NORM, "=> Adding new task item");
7575 if (!cancelItem)
7576 m_background_task_queue.push_back(tItem);
7577 }
7578
EventsGetTaskItems(std::vector<_tTaskItem> & currentTasks)7579 void CSQLHelper::EventsGetTaskItems(std::vector<_tTaskItem>& currentTasks)
7580 {
7581 std::lock_guard<std::mutex> l(m_background_task_mutex);
7582
7583 currentTasks.clear();
7584
7585 for (const auto& it : m_background_task_queue)
7586 currentTasks.push_back(it);
7587 }
7588
RestoreDatabase(const std::string & dbase)7589 bool CSQLHelper::RestoreDatabase(const std::string& dbase)
7590 {
7591 _log.Log(LOG_STATUS, "Restore Database: Starting...");
7592 //write file to disk
7593 std::string fpath;
7594 #ifdef WIN32
7595 size_t bpos = m_dbase_name.rfind('\\');
7596 #else
7597 size_t bpos = m_dbase_name.rfind('/');
7598 #endif
7599 if (bpos != std::string::npos)
7600 fpath = m_dbase_name.substr(0, bpos + 1);
7601 #ifdef WIN32
7602 std::string outputfile = fpath + "restore.db";
7603 #else
7604 std::string outputfile = "/tmp/restore.db";
7605 #endif
7606 std::ofstream outfile;
7607 outfile.open(outputfile.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
7608 if (!outfile.is_open())
7609 {
7610 _log.Log(LOG_ERROR, "Restore Database: Could not open backup file for writing!");
7611 return false;
7612 }
7613 outfile << dbase;
7614 outfile.flush();
7615 outfile.close();
7616 //check if we can open the database (check if valid)
7617 sqlite3* dbase_restore = NULL;
7618 int rc = sqlite3_open(outputfile.c_str(), &dbase_restore);
7619 if (rc)
7620 {
7621 _log.Log(LOG_ERROR, "Restore Database: Could not open SQLite3 database: %s", sqlite3_errmsg(dbase_restore));
7622 sqlite3_close(dbase_restore);
7623 return false;
7624 }
7625 if (dbase_restore == NULL)
7626 return false;
7627 //could still be not valid
7628 std::stringstream ss;
7629 ss << "SELECT sValue FROM Preferences WHERE (Key='DB_Version')";
7630 sqlite3_stmt* statement;
7631 if (sqlite3_prepare_v2(dbase_restore, ss.str().c_str(), -1, &statement, 0) != SQLITE_OK)
7632 {
7633 _log.Log(LOG_ERROR, "Restore Database: Seems this is not our database, or it is corrupted!");
7634 sqlite3_close(dbase_restore);
7635 return false;
7636 }
7637 OptimizeDatabase(dbase_restore);
7638 sqlite3_close(dbase_restore);
7639 //we have a valid database!
7640 std::remove(outputfile.c_str());
7641
7642 StopThread();
7643
7644 //stop database
7645 sqlite3_close(m_dbase);
7646 m_dbase = NULL;
7647 std::ofstream outfile2;
7648 outfile2.open(m_dbase_name.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
7649 if (!outfile2.is_open())
7650 {
7651 _log.Log(LOG_ERROR, "Restore Database: Could not open backup file for writing!");
7652 return false;
7653 }
7654 outfile2 << dbase;
7655 outfile2.flush();
7656 outfile2.close();
7657 //change ownership
7658 #ifndef WIN32
7659 struct stat info;
7660 if (stat(m_dbase_name.c_str(), &info) == 0)
7661 {
7662 struct passwd* pw = getpwuid(info.st_uid);
7663 int ret = chown(m_dbase_name.c_str(), pw->pw_uid, pw->pw_gid);
7664 if (ret != 0)
7665 {
7666 _log.Log(LOG_ERROR, "Restore Database: Could not set database ownership (chown returned an error!)");
7667 }
7668 }
7669 #endif
7670 if (!OpenDatabase())
7671 {
7672 _log.Log(LOG_ERROR, "Restore Database: Error opening new database!");
7673 return false;
7674 }
7675 //Cleanup the database
7676 VacuumDatabase();
7677 _log.Log(LOG_STATUS, "Restore Database: Succeeded!");
7678 return true;
7679 }
7680
BackupDatabase(const std::string & OutputFile)7681 bool CSQLHelper::BackupDatabase(const std::string& OutputFile)
7682 {
7683 if (!m_dbase)
7684 return false; //database not open!
7685
7686 //First cleanup the database
7687 OptimizeDatabase(m_dbase);
7688 VacuumDatabase();
7689
7690 std::lock_guard<std::mutex> l(m_sqlQueryMutex);
7691
7692 int rc; // Function return code
7693 sqlite3* pFile; // Database connection opened on zFilename
7694 sqlite3_backup* pBackup; // Backup handle used to copy data
7695
7696 // Open the database file identified by zFilename.
7697 rc = sqlite3_open(OutputFile.c_str(), &pFile);
7698 if (rc != SQLITE_OK)
7699 return false;
7700
7701 // Open the sqlite3_backup object used to accomplish the transfer
7702 pBackup = sqlite3_backup_init(pFile, "main", m_dbase, "main");
7703 if (pBackup)
7704 {
7705 // Each iteration of this loop copies 5 database pages from database
7706 // pDb to the backup database.
7707 do {
7708 rc = sqlite3_backup_step(pBackup, 5);
7709 //xProgress( sqlite3_backup_remaining(pBackup), sqlite3_backup_pagecount(pBackup) );
7710 //if( rc==SQLITE_OK || rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){
7711 //sqlite3_sleep(250);
7712 //}
7713 } while (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED);
7714
7715 /* Release resources allocated by backup_init(). */
7716 sqlite3_backup_finish(pBackup);
7717 }
7718 rc = sqlite3_errcode(pFile);
7719 // Close the database connection opened on database file zFilename
7720 // and return the result of this function.
7721 sqlite3_close(pFile);
7722
7723 return (rc == SQLITE_OK);
7724 }
7725
UpdateValueLighting2GroupCmd(const int HardwareID,const char * ID,const unsigned char unit,const unsigned char devType,const unsigned char subType,const unsigned char signallevel,const unsigned char batterylevel,const int nValue,const char * sValue,std::string & devname,const bool bUseOnOffAction)7726 uint64_t CSQLHelper::UpdateValueLighting2GroupCmd(const int HardwareID, const char* ID, const unsigned char unit,
7727 const unsigned char devType, const unsigned char subType,
7728 const unsigned char signallevel, const unsigned char batterylevel,
7729 const int nValue, const char* sValue,
7730 std::string& devname,
7731 const bool bUseOnOffAction)
7732 {
7733 // We only have to update all others units within the ID group. If the current unit does not have the same value,
7734 // it will be updated too. The reason we choose the UpdateValue is the propagation of the change to all units involved, including LogUpdate.
7735
7736 uint64_t devRowIndex = -1;
7737 typedef std::vector<std::vector<std::string> > VectorVectorString;
7738
7739 VectorVectorString result = safe_query("SELECT Unit FROM DeviceStatus WHERE ((DeviceID=='%q') AND (Type==%d) AND (SubType==%d) AND (nValue!=%d))",
7740 ID,
7741 pTypeLighting2,
7742 subType,
7743 nValue);
7744
7745 for (VectorVectorString::const_iterator itt = result.begin(); itt != result.end(); ++itt)
7746 {
7747 unsigned char theUnit = atoi((*itt)[0].c_str()); // get the unit value
7748 devRowIndex = UpdateValue(HardwareID, ID, theUnit, devType, subType, signallevel, batterylevel, nValue, sValue, devname, bUseOnOffAction);
7749 }
7750 return devRowIndex;
7751 }
7752
Lighting2GroupCmd(const std::string & ID,const unsigned char subType,const unsigned char GroupCmd)7753 void CSQLHelper::Lighting2GroupCmd(const std::string& ID, const unsigned char subType, const unsigned char GroupCmd)
7754 {
7755 time_t now = mytime(NULL);
7756 struct tm ltime;
7757 localtime_r(&now, <ime);
7758
7759 safe_query("UPDATE DeviceStatus SET nValue='%d', sValue='%q', LastUpdate='%04d-%02d-%02d %02d:%02d:%02d' WHERE (DeviceID=='%q') And (Type==%d) And (SubType==%d) And (nValue!=%d)",
7760 GroupCmd,
7761 "OFF",
7762 ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday, ltime.tm_hour, ltime.tm_min, ltime.tm_sec,
7763 ID.c_str(),
7764 pTypeLighting2,
7765 subType,
7766 GroupCmd);
7767 }
7768
UpdateValueHomeConfortGroupCmd(const int HardwareID,const char * ID,const unsigned char unit,const unsigned char devType,const unsigned char subType,const unsigned char signallevel,const unsigned char batterylevel,const int nValue,const char * sValue,std::string & devname,const bool bUseOnOffAction)7769 uint64_t CSQLHelper::UpdateValueHomeConfortGroupCmd(const int HardwareID, const char* ID, const unsigned char unit,
7770 const unsigned char devType, const unsigned char subType,
7771 const unsigned char signallevel, const unsigned char batterylevel,
7772 const int nValue, const char* sValue,
7773 std::string& devname,
7774 const bool bUseOnOffAction)
7775 {
7776 // We only have to update all others units within the ID group. If the current unit does not have the same value,
7777 // it will be updated too. The reason we choose the UpdateValue is the propagation of the change to all units involved, including LogUpdate.
7778
7779 uint64_t devRowIndex = -1;
7780 typedef std::vector<std::vector<std::string> > VectorVectorString;
7781
7782 VectorVectorString result = safe_query("SELECT Unit FROM DeviceStatus WHERE ((DeviceID=='%q') AND (Type==%d) AND (SubType==%d) AND (nValue!=%d))",
7783 ID,
7784 pTypeHomeConfort,
7785 subType,
7786 nValue);
7787
7788 for (VectorVectorString::const_iterator itt = result.begin(); itt != result.end(); ++itt)
7789 {
7790 unsigned char theUnit = atoi((*itt)[0].c_str()); // get the unit value
7791 devRowIndex = UpdateValue(HardwareID, ID, theUnit, devType, subType, signallevel, batterylevel, nValue, sValue, devname, bUseOnOffAction);
7792 }
7793 return devRowIndex;
7794 }
7795
HomeConfortGroupCmd(const std::string & ID,const unsigned char subType,const unsigned char GroupCmd)7796 void CSQLHelper::HomeConfortGroupCmd(const std::string& ID, const unsigned char subType, const unsigned char GroupCmd)
7797 {
7798 time_t now = mytime(NULL);
7799 struct tm ltime;
7800 localtime_r(&now, <ime);
7801
7802 safe_query("UPDATE DeviceStatus SET nValue='%s', sValue='%q', LastUpdate='%04d-%02d-%02d %02d:%02d:%02d' WHERE (DeviceID=='%q') And (Type==%d) And (SubType==%d) And (nValue!=%d)",
7803 GroupCmd,
7804 "OFF",
7805 ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday, ltime.tm_hour, ltime.tm_min, ltime.tm_sec,
7806 ID.c_str(),
7807 pTypeHomeConfort,
7808 subType,
7809 GroupCmd);
7810 }
7811
GeneralSwitchGroupCmd(const std::string & ID,const unsigned char subType,const unsigned char GroupCmd)7812 void CSQLHelper::GeneralSwitchGroupCmd(const std::string& ID, const unsigned char subType, const unsigned char GroupCmd)
7813 {
7814 safe_query("UPDATE DeviceStatus SET nValue = %d WHERE (DeviceID=='%q') And (Type==%d) And (SubType==%d)", GroupCmd, ID.c_str(), pTypeGeneralSwitch, subType);
7815 }
7816
SetUnitsAndScale()7817 void CSQLHelper::SetUnitsAndScale()
7818 {
7819 //Wind
7820 if (m_windunit == WINDUNIT_MS)
7821 {
7822 m_windsign = "m/s";
7823 m_windscale = 0.1f;
7824 }
7825 else if (m_windunit == WINDUNIT_KMH)
7826 {
7827 m_windsign = "km/h";
7828 m_windscale = 0.36f;
7829 }
7830 else if (m_windunit == WINDUNIT_MPH)
7831 {
7832 m_windsign = "mph";
7833 m_windscale = 0.223693629205f;
7834 }
7835 else if (m_windunit == WINDUNIT_Knots)
7836 {
7837 m_windsign = "kn";
7838 m_windscale = 0.1943844492457398f;
7839 }
7840 else if (m_windunit == WINDUNIT_Beaufort)
7841 {
7842 m_windsign = "bf";
7843 m_windscale = 1;
7844 }
7845
7846 //Temp
7847 if (m_tempunit == TEMPUNIT_C)
7848 {
7849 m_tempsign = "C";
7850 m_tempscale = 1.0f;
7851 }
7852 else if (m_tempunit == TEMPUNIT_F)
7853 {
7854 m_tempsign = "F";
7855 m_tempscale = 1.0f; // *1.8 + 32
7856 }
7857
7858 if (m_weightunit == WEIGHTUNIT_KG)
7859 {
7860 m_weightsign = "kg";
7861 m_weightscale = 1.0f;
7862 }
7863 else if (m_weightunit == WEIGHTUNIT_LB)
7864 {
7865 m_weightsign = "lb";
7866 m_weightscale = 2.20462f;
7867 }
7868 }
7869
HandleOnOffAction(const bool bIsOn,const std::string & OnAction,const std::string & OffAction)7870 bool CSQLHelper::HandleOnOffAction(const bool bIsOn, const std::string& OnAction, const std::string& OffAction)
7871 {
7872 if (bIsOn)
7873 _log.Debug(DEBUG_NORM, "SQLH HandleOnOffAction: OnAction:%s", OnAction.c_str());
7874 else
7875 _log.Debug(DEBUG_NORM, "SQLH HandleOnOffAction: OffAction:%s", OffAction.c_str());
7876
7877 if (bIsOn)
7878 {
7879 if (OnAction.empty())
7880 return true;
7881
7882 if ((OnAction.find("http://") == 0) || (OnAction.find("https://") == 0))
7883 {
7884 AddTaskItem(_tTaskItem::GetHTTPPage(0.2f, OnAction, "SwitchActionOn"));
7885 }
7886 else if (OnAction.find("script://") == 0)
7887 {
7888 //Execute possible script
7889 if (OnAction.find("../") != std::string::npos)
7890 {
7891 _log.Log(LOG_ERROR, "SQLHelper: Invalid script location! '%s'", OnAction.c_str());
7892 return false;
7893 }
7894
7895 std::string scriptname = OnAction.substr(9);
7896 #if !defined WIN32
7897 if (scriptname.find("/") != 0)
7898 scriptname = szUserDataFolder + "scripts/" + scriptname;
7899 #endif
7900 std::string scriptparams = "";
7901 //Add parameters
7902 size_t pindex = scriptname.find(' ');
7903 if (pindex != std::string::npos)
7904 {
7905 scriptparams = scriptname.substr(pindex + 1);
7906 scriptname = scriptname.substr(0, pindex);
7907 }
7908 if (file_exist(scriptname.c_str()))
7909 {
7910 AddTaskItem(_tTaskItem::ExecuteScript(0.2f, scriptname, scriptparams));
7911 }
7912 else
7913 _log.Log(LOG_ERROR, "SQLHelper: Error script not found '%s'", scriptname.c_str());
7914 }
7915 return true;
7916 }
7917
7918 //Off action
7919 if (OffAction.empty())
7920 return true;
7921
7922 if ((OffAction.find("http://") == 0) || (OffAction.find("https://") == 0))
7923 {
7924 AddTaskItem(_tTaskItem::GetHTTPPage(0.2f, OffAction, "SwitchActionOff"));
7925 }
7926 else if (OffAction.find("script://") == 0)
7927 {
7928 //Execute possible script
7929 if (OffAction.find("../") != std::string::npos)
7930 {
7931 _log.Log(LOG_ERROR, "SQLHelper: Invalid script location! '%s'", OffAction.c_str());
7932 return false;
7933 }
7934
7935 std::string scriptname = OffAction.substr(9);
7936 #if !defined WIN32
7937 if (scriptname.find("/") != 0)
7938 scriptname = szUserDataFolder + "scripts/" + scriptname;
7939 #endif
7940 std::string scriptparams = "";
7941 size_t pindex = scriptname.find(' ');
7942 if (pindex != std::string::npos)
7943 {
7944 scriptparams = scriptname.substr(pindex + 1);
7945 scriptname = scriptname.substr(0, pindex);
7946 }
7947 if (file_exist(scriptname.c_str()))
7948 {
7949 AddTaskItem(_tTaskItem::ExecuteScript(0.2f, scriptname, scriptparams));
7950 }
7951 }
7952 return true;
7953 }
7954
7955 //Executed every hour
CheckBatteryLow()7956 void CSQLHelper::CheckBatteryLow()
7957 {
7958 int iBatteryLowLevel = 0;
7959 GetPreferencesVar("BatteryLowNotification", iBatteryLowLevel);
7960 if (iBatteryLowLevel == 0)
7961 return;//disabled
7962
7963 std::vector<std::vector<std::string> > result;
7964 result = safe_query("SELECT ID,Name, BatteryLevel FROM DeviceStatus WHERE (Used!=0 AND BatteryLevel<%d AND BatteryLevel!=255)", iBatteryLowLevel);
7965 if (result.empty())
7966 return;
7967
7968 time_t now = mytime(NULL);
7969 struct tm stoday;
7970 localtime_r(&now, &stoday);
7971
7972 //check if last batterylow_notification is not sent today and if true, send notification
7973 for (const auto& itt : result)
7974 {
7975 std::vector<std::string> sd = itt;
7976 uint64_t ulID = std::stoull(sd[0]);
7977 bool bDoSend = true;
7978 std::map<uint64_t, int>::const_iterator sitt;
7979 sitt = m_batterylowlastsend.find(ulID);
7980 if (sitt != m_batterylowlastsend.end())
7981 {
7982 bDoSend = (stoday.tm_mday != sitt->second);
7983 }
7984 if (bDoSend)
7985 {
7986 char szTmp[300];
7987 int batlevel = atoi(sd[2].c_str());
7988 if (batlevel == 0)
7989 sprintf(szTmp, "Battery Low: %s (Level: Low)", sd[1].c_str());
7990 else
7991 sprintf(szTmp, "Battery Low: %s (Level: %d %%)", sd[1].c_str(), batlevel);
7992 m_notifications.SendMessageEx(0, std::string(""), NOTIFYALL, szTmp, szTmp, std::string(""), 1, std::string(""), true);
7993 m_batterylowlastsend[ulID] = stoday.tm_mday;
7994 }
7995 }
7996 }
7997
7998 //Executed every hour
CheckDeviceTimeout()7999 void CSQLHelper::CheckDeviceTimeout()
8000 {
8001 int TimeoutCheckInterval = 1;
8002 GetPreferencesVar("SensorTimeoutNotification", TimeoutCheckInterval);
8003
8004 if (TimeoutCheckInterval == 0)
8005 return;
8006 m_sensortimeoutcounter += 1;
8007 if (m_sensortimeoutcounter < TimeoutCheckInterval)
8008 return;
8009 m_sensortimeoutcounter = 0;
8010
8011 int SensorTimeOut = 60;
8012 GetPreferencesVar("SensorTimeout", SensorTimeOut);
8013 time_t now = mytime(NULL);
8014 struct tm stoday;
8015 localtime_r(&now, &stoday);
8016 now -= (SensorTimeOut * 60);
8017 struct tm ltime;
8018 localtime_r(&now, <ime);
8019
8020 std::vector<std::vector<std::string> > result;
8021 result = safe_query(
8022 "SELECT ID, Name, LastUpdate FROM DeviceStatus WHERE (Used!=0 AND LastUpdate<='%04d-%02d-%02d %02d:%02d:%02d' "
8023 "AND Type!=%d AND Type!=%d AND Type!=%d AND Type!=%d AND Type!=%d AND Type!=%d AND Type!=%d AND Type!=%d AND Type!=%d "
8024 "AND Type!=%d AND Type!=%d AND Type!=%d AND Type!=%d AND Type!=%d AND Type!=%d AND Type!=%d AND Type!=%d AND Type!=%d AND Type!=%d AND Type!=%d AND Type!=%d AND Type!=%d) "
8025 "ORDER BY Name",
8026 ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday, ltime.tm_hour, ltime.tm_min, ltime.tm_sec,
8027 pTypeLighting1,
8028 pTypeLighting2,
8029 pTypeLighting3,
8030 pTypeLighting4,
8031 pTypeLighting5,
8032 pTypeLighting6,
8033 pTypeFan,
8034 pTypeRadiator1,
8035 pTypeColorSwitch,
8036 pTypeSecurity1,
8037 pTypeCurtain,
8038 pTypeBlinds,
8039 pTypeRFY,
8040 pTypeChime,
8041 pTypeThermostat2,
8042 pTypeThermostat3,
8043 pTypeThermostat4,
8044 pTypeRemote,
8045 pTypeGeneralSwitch,
8046 pTypeHomeConfort,
8047 pTypeFS20,
8048 pTypeHunter
8049 );
8050 if (result.empty())
8051 return;
8052
8053 //check if last timeout_notification is not sent today and if true, send notification
8054 for (const auto& itt : result)
8055 {
8056 std::vector<std::string> sd = itt;
8057 uint64_t ulID = std::stoull(sd[0]);
8058 bool bDoSend = true;
8059 std::map<uint64_t, int>::const_iterator sitt;
8060 sitt = m_timeoutlastsend.find(ulID);
8061 if (sitt != m_timeoutlastsend.end())
8062 {
8063 bDoSend = (stoday.tm_mday != sitt->second);
8064 }
8065 if (bDoSend)
8066 {
8067 char szTmp[300];
8068 sprintf(szTmp, "Sensor Timeout: %s, Last Received: %s", sd[1].c_str(), sd[2].c_str());
8069 m_notifications.SendMessageEx(0, std::string(""), NOTIFYALL, szTmp, szTmp, std::string(""), 1, std::string(""), true);
8070 m_timeoutlastsend[ulID] = stoday.tm_mday;
8071 }
8072 }
8073 }
8074
FixDaylightSavingTableSimple(const std::string & TableName)8075 void CSQLHelper::FixDaylightSavingTableSimple(const std::string& TableName)
8076 {
8077 std::vector<std::vector<std::string> > result;
8078
8079 result = safe_query("SELECT t.RowID, u.RowID, t.Date FROM %s as t, %s as u WHERE (t.[Date] == u.[Date]) AND (t.[DeviceRowID] == u.[DeviceRowID]) AND (t.[RowID] != u.[RowID]) ORDER BY t.[RowID]",
8080 TableName.c_str(),
8081 TableName.c_str());
8082 if (!result.empty())
8083 {
8084 std::stringstream sstr;
8085 unsigned long ID1;
8086 unsigned long ID2;
8087 for (const auto& itt : result)
8088 {
8089 std::vector<std::string> sd = itt;
8090 sstr.clear();
8091 sstr.str("");
8092 sstr << sd[0];
8093 sstr >> ID1;
8094 sstr.clear();
8095 sstr.str("");
8096 sstr << sd[1];
8097 sstr >> ID2;
8098 if (ID2 > ID1)
8099 {
8100 std::string szDate = sd[2];
8101 std::vector<std::vector<std::string> > result2;
8102 result2 = safe_query("SELECT date('%q','+1 day')",
8103 szDate.c_str());
8104
8105 std::string szDateNew = result2[0][0];
8106
8107 //Check if Date+1 exists, if yes, remove current double value
8108 result2 = safe_query("SELECT RowID FROM %s WHERE (Date='%q') AND (RowID=='%q')",
8109 TableName.c_str(), szDateNew.c_str(), sd[1].c_str());
8110 if (!result2.empty())
8111 {
8112 //Delete row
8113 safe_query("DELETE FROM %s WHERE (RowID=='%q')", TableName.c_str(), sd[1].c_str());
8114 }
8115 else
8116 {
8117 //Update date
8118 safe_query("UPDATE %s SET Date='%q' WHERE (RowID=='%q')", TableName.c_str(), szDateNew.c_str(), sd[1].c_str());
8119 }
8120 }
8121 }
8122 }
8123 }
8124
FixDaylightSaving()8125 void CSQLHelper::FixDaylightSaving()
8126 {
8127 //First the easy tables
8128 FixDaylightSavingTableSimple("Fan_Calendar");
8129 FixDaylightSavingTableSimple("Percentage_Calendar");
8130 FixDaylightSavingTableSimple("Rain_Calendar");
8131 FixDaylightSavingTableSimple("Temperature_Calendar");
8132 FixDaylightSavingTableSimple("UV_Calendar");
8133 FixDaylightSavingTableSimple("Wind_Calendar");
8134
8135 //Meter_Calendar
8136 std::vector<std::vector<std::string> > result;
8137
8138 result = safe_query("SELECT t.RowID, u.RowID, t.Value, u.Value, t.Date from Meter_Calendar as t, Meter_Calendar as u WHERE (t.[Date] == u.[Date]) AND (t.[DeviceRowID] == u.[DeviceRowID]) AND (t.[RowID] != u.[RowID]) ORDER BY t.[RowID]");
8139 if (!result.empty())
8140 {
8141 std::stringstream sstr;
8142 unsigned long ID1;
8143 unsigned long ID2;
8144 unsigned long long Value1;
8145 unsigned long long Value2;
8146 unsigned long long ValueDest;
8147 for (const auto& itt : result)
8148 {
8149 std::vector<std::string> sd1 = itt;
8150
8151 sstr.clear();
8152 sstr.str("");
8153 sstr << sd1[0];
8154 sstr >> ID1;
8155 sstr.clear();
8156 sstr.str("");
8157 sstr << sd1[1];
8158 sstr >> ID2;
8159 sstr.clear();
8160 sstr.str("");
8161 sstr << sd1[2];
8162 sstr >> Value1;
8163 sstr.clear();
8164 sstr.str("");
8165 sstr << sd1[3];
8166 sstr >> Value2;
8167 if (ID2 > ID1)
8168 {
8169 if (Value2 > Value1)
8170 ValueDest = Value2 - Value1;
8171 else
8172 ValueDest = Value2;
8173
8174 std::string szDate = sd1[4];
8175 std::vector<std::vector<std::string> > result2;
8176 result2 = safe_query("SELECT date('%q','+1 day')", szDate.c_str());
8177
8178 std::string szDateNew = result2[0][0];
8179
8180 //Check if Date+1 exists, if yes, remove current double value
8181 result2 = safe_query("SELECT RowID FROM Meter_Calendar WHERE (Date='%q') AND (RowID=='%q')", szDateNew.c_str(), sd1[1].c_str());
8182 if (!result2.empty())
8183 {
8184 //Delete Row
8185 safe_query("DELETE FROM Meter_Calendar WHERE (RowID=='%q')", sd1[1].c_str());
8186 }
8187 else
8188 {
8189 //Update row with new Date
8190 safe_query("UPDATE Meter_Calendar SET Date='%q', Value=%llu WHERE (RowID=='%q')", szDateNew.c_str(), ValueDest, sd1[1].c_str());
8191 }
8192 }
8193 }
8194 }
8195
8196 //Last (but not least) MultiMeter_Calendar
8197 result = safe_query("SELECT t.RowID, u.RowID, t.Value1, t.Value2, t.Value3, t.Value4, t.Value5, t.Value6, u.Value1, u.Value2, u.Value3, u.Value4, u.Value5, u.Value6, t.Date from MultiMeter_Calendar as t, MultiMeter_Calendar as u WHERE (t.[Date] == u.[Date]) AND (t.[DeviceRowID] == u.[DeviceRowID]) AND (t.[RowID] != u.[RowID]) ORDER BY t.[RowID]");
8198 if (!result.empty())
8199 {
8200 std::stringstream sstr;
8201 unsigned long ID1;
8202 unsigned long ID2;
8203 unsigned long long tValue1;
8204 unsigned long long tValue2;
8205 unsigned long long tValue3;
8206 unsigned long long tValue4;
8207 unsigned long long tValue5;
8208 unsigned long long tValue6;
8209
8210 unsigned long long uValue1;
8211 unsigned long long uValue2;
8212 unsigned long long uValue3;
8213 unsigned long long uValue4;
8214 unsigned long long uValue5;
8215 unsigned long long uValue6;
8216
8217 unsigned long long ValueDest1;
8218 unsigned long long ValueDest2;
8219 unsigned long long ValueDest3;
8220 unsigned long long ValueDest4;
8221 unsigned long long ValueDest5;
8222 unsigned long long ValueDest6;
8223 for (const auto& itt : result)
8224 {
8225 std::vector<std::string> sd1 = itt;
8226
8227 sstr.clear();
8228 sstr.str("");
8229 sstr << sd1[0];
8230 sstr >> ID1;
8231 sstr.clear();
8232 sstr.str("");
8233 sstr << sd1[1];
8234 sstr >> ID2;
8235
8236 sstr.clear();
8237 sstr.str("");
8238 sstr << sd1[2];
8239 sstr >> tValue1;
8240 sstr.clear();
8241 sstr.str("");
8242 sstr << sd1[3];
8243 sstr >> tValue2;
8244 sstr.clear();
8245 sstr.str("");
8246 sstr << sd1[4];
8247 sstr >> tValue3;
8248 sstr.clear();
8249 sstr.str("");
8250 sstr << sd1[5];
8251 sstr >> tValue4;
8252 sstr.clear();
8253 sstr.str("");
8254 sstr << sd1[6];
8255 sstr >> tValue5;
8256 sstr.clear();
8257 sstr.str("");
8258 sstr << sd1[7];
8259 sstr >> tValue6;
8260
8261 sstr.clear();
8262 sstr.str("");
8263 sstr << sd1[8];
8264 sstr >> uValue1;
8265 sstr.clear();
8266 sstr.str("");
8267 sstr << sd1[9];
8268 sstr >> uValue2;
8269 sstr.clear();
8270 sstr.str("");
8271 sstr << sd1[10];
8272 sstr >> uValue3;
8273 sstr.clear();
8274 sstr.str("");
8275 sstr << sd1[11];
8276 sstr >> uValue4;
8277 sstr.clear();
8278 sstr.str("");
8279 sstr << sd1[12];
8280 sstr >> uValue5;
8281 sstr.clear();
8282 sstr.str("");
8283 sstr << sd1[13];
8284 sstr >> uValue6;
8285
8286 if (ID2 > ID1)
8287 {
8288 if (uValue1 > tValue1)
8289 ValueDest1 = uValue1 - tValue1;
8290 else
8291 ValueDest1 = uValue1;
8292 if (uValue2 > tValue2)
8293 ValueDest2 = uValue2 - tValue2;
8294 else
8295 ValueDest2 = uValue2;
8296 if (uValue3 > tValue3)
8297 ValueDest3 = uValue3 - tValue3;
8298 else
8299 ValueDest3 = uValue3;
8300 if (uValue4 > tValue4)
8301 ValueDest4 = uValue4 - tValue4;
8302 else
8303 ValueDest4 = uValue4;
8304 if (uValue5 > tValue5)
8305 ValueDest5 = uValue5 - tValue5;
8306 else
8307 ValueDest5 = uValue5;
8308 if (uValue6 > tValue6)
8309 ValueDest6 = uValue6 - tValue6;
8310 else
8311 ValueDest6 = uValue6;
8312
8313 std::string szDate = sd1[14];
8314 std::vector<std::vector<std::string> > result2;
8315 result2 = safe_query("SELECT date('%q','+1 day')", szDate.c_str());
8316
8317 std::string szDateNew = result2[0][0];
8318
8319 //Check if Date+1 exists, if yes, remove current double value
8320 result2 = safe_query("SELECT RowID FROM MultiMeter_Calendar WHERE (Date='%q') AND (RowID=='%q')", szDateNew.c_str(), sd1[1].c_str());
8321 if (!result2.empty())
8322 {
8323 //Delete Row
8324 safe_query("DELETE FROM MultiMeter_Calendar WHERE (RowID=='%q')", sd1[1].c_str());
8325 }
8326 else
8327 {
8328 //Update row with new Date
8329 safe_query("UPDATE MultiMeter_Calendar SET Date='%q', Value1=%llu, Value2=%llu, Value3=%llu, Value4=%llu, Value5=%llu, Value6=%llu WHERE (RowID=='%q')",
8330 szDateNew.c_str(), ValueDest1, ValueDest2, ValueDest3, ValueDest4, ValueDest5, ValueDest6, sd1[1].c_str());
8331 }
8332 }
8333 }
8334 }
8335
8336 }
8337
DeleteUserVariable(const std::string & idx)8338 void CSQLHelper::DeleteUserVariable(const std::string& idx)
8339 {
8340 safe_query("DELETE FROM UserVariables WHERE (ID=='%q')", idx.c_str());
8341 if (m_bEnableEventSystem)
8342 {
8343 m_mainworker.m_eventsystem.GetCurrentUserVariables();
8344 }
8345 }
8346
AddUserVariable(const std::string & varname,const _eUsrVariableType eVartype,const std::string & varvalue,std::string & errorMessage)8347 bool CSQLHelper::AddUserVariable(const std::string& varname, const _eUsrVariableType eVartype, const std::string& varvalue, std::string& errorMessage)
8348 {
8349 std::vector<std::vector<std::string> > result;
8350 result = safe_query("SELECT Name FROM UserVariables WHERE (Name=='%q')", varname.c_str());
8351 if (!result.empty())
8352 {
8353 errorMessage = "Variable with the same Name already exists!";
8354 return false;
8355 }
8356
8357 if (!CheckUserVariable(eVartype, varvalue, errorMessage))
8358 return false;
8359
8360 std::string szVarValue = CURLEncode::URLDecode(varvalue.c_str());
8361 safe_query("INSERT INTO UserVariables (Name, ValueType, Value) VALUES ('%q','%d','%q')", varname.c_str(), eVartype, szVarValue.c_str());
8362
8363 if (m_bEnableEventSystem)
8364 m_mainworker.m_eventsystem.GetCurrentUserVariables();
8365
8366 return true;
8367 }
8368
UpdateUserVariable(const std::string & idx,const std::string & varname,const _eUsrVariableType eVartype,const std::string & varvalue,const bool eventtrigger,std::string & errorMessage)8369 bool CSQLHelper::UpdateUserVariable(const std::string& idx, const std::string& varname, const _eUsrVariableType eVartype, const std::string& varvalue, const bool eventtrigger, std::string& errorMessage)
8370 {
8371 if (!CheckUserVariable(eVartype, varvalue, errorMessage))
8372 return false;
8373
8374 std::string szLastUpdate = TimeToString(NULL, TF_DateTime);
8375 std::string szVarValue = CURLEncode::URLDecode(varvalue.c_str());
8376 safe_query(
8377 "UPDATE UserVariables SET Name='%q', ValueType='%d', Value='%q', LastUpdate='%q' WHERE (ID == '%q')",
8378 varname.c_str(),
8379 eVartype,
8380 szVarValue.c_str(),
8381 szLastUpdate.c_str(),
8382 idx.c_str()
8383 );
8384 if (m_bEnableEventSystem)
8385 {
8386 uint64_t vId = std::stoull(idx);
8387 if (eventtrigger)
8388 m_mainworker.m_eventsystem.SetEventTrigger(vId, m_mainworker.m_eventsystem.REASON_USERVARIABLE, 0);
8389 m_mainworker.m_eventsystem.UpdateUserVariable(vId, szVarValue, szLastUpdate);
8390 }
8391 return true;
8392 }
8393
CheckUserVariable(const _eUsrVariableType eVartype,const std::string & varvalue,std::string & errorMessage)8394 bool CSQLHelper::CheckUserVariable(const _eUsrVariableType eVartype, const std::string& varvalue, std::string& errorMessage)
8395 {
8396
8397 if (varvalue.size() > 200) {
8398 errorMessage = "String exceeds maximum size";
8399 return false;
8400 }
8401 if (eVartype == USERVARTYPE_INTEGER) {
8402 //integer (0)
8403 std::istringstream iss(varvalue);
8404 int i;
8405 iss >> std::noskipws >> i;
8406 if (!(iss.eof() && !iss.fail()))
8407 {
8408 errorMessage = "Not a valid integer";
8409 return false;
8410 }
8411 return true;
8412 }
8413 else if (eVartype == USERVARTYPE_FLOAT) {
8414 //float (1)
8415 std::istringstream iss(varvalue);
8416 float f;
8417 iss >> std::noskipws >> f;
8418 if (!(iss.eof() && !iss.fail()))
8419 {
8420 errorMessage = "Not a valid float";
8421 return false;
8422 }
8423 return true;
8424 }
8425 else if (eVartype == USERVARTYPE_STRING) {
8426 //string (2)
8427 return true;
8428 }
8429 else if (eVartype == USERVARTYPE_DATE) {
8430 //date (3)
8431 int d, m, y;
8432 if (!CheckDate(varvalue, d, m, y))
8433 {
8434 errorMessage = "Not a valid date notation (DD/MM/YYYY)";
8435 return false;
8436 }
8437 return true;
8438 }
8439 else if (eVartype == USERVARTYPE_TIME) {
8440 //time (4)
8441 if (!CheckTime(varvalue))
8442 {
8443 errorMessage = "Not a valid time notation (HH:MM)";
8444 return false;
8445 }
8446 return true;
8447 }
8448
8449 errorMessage = "Unknown variable type!";
8450 return false;
8451 }
8452
CheckDate(const std::string & sDate,int & d,int & m,int & y)8453 bool CSQLHelper::CheckDate(const std::string& sDate, int& d, int& m, int& y)
8454 {
8455 std::istringstream is(sDate);
8456 char delimiter;
8457 if (is >> d >> delimiter >> m >> delimiter >> y) {
8458 struct tm t = { 0 };
8459 t.tm_mday = d;
8460 t.tm_mon = m - 1;
8461 t.tm_year = y - 1900;
8462 t.tm_isdst = -1;
8463
8464 time_t when = mktime(&t);
8465 struct tm norm;
8466 localtime_r(&when, &norm);
8467
8468 return (norm.tm_mday == d &&
8469 norm.tm_mon == m - 1 &&
8470 norm.tm_year == y - 1900);
8471 }
8472 return false;
8473 }
8474
CheckDateSQL(const std::string & sDate)8475 bool CSQLHelper::CheckDateSQL(const std::string& sDate)
8476 {
8477 if (sDate.size() != 10) {
8478 return false;
8479 }
8480
8481 std::istringstream is(sDate);
8482 int d, m, y;
8483 char delimiter1, delimiter2;
8484
8485 if (is >> y >> delimiter1 >> m >> delimiter2 >> d) {
8486 if (
8487 (delimiter1 != '-')
8488 || (delimiter2 != '-')
8489 ) {
8490 return false;
8491 }
8492 struct tm t = { 0 };
8493 t.tm_mday = d;
8494 t.tm_mon = m - 1;
8495 t.tm_year = y - 1900;
8496 t.tm_isdst = -1;
8497
8498 time_t when = mktime(&t);
8499 struct tm norm;
8500 localtime_r(&when, &norm);
8501
8502 return (norm.tm_mday == d &&
8503 norm.tm_mon == m - 1 &&
8504 norm.tm_year == y - 1900);
8505 }
8506 return false;
8507 }
8508
CheckDateTimeSQL(const std::string & sDateTime)8509 bool CSQLHelper::CheckDateTimeSQL(const std::string& sDateTime)
8510 {
8511 if (sDateTime.size() != 19) {
8512 return false;
8513 }
8514
8515 struct tm t;
8516 time_t when;
8517 bool result = ParseSQLdatetime(when, t, sDateTime);
8518
8519 if (result) {
8520 struct tm norm;
8521 localtime_r(&when, &norm);
8522
8523 return (
8524 norm.tm_mday == t.tm_mday
8525 && norm.tm_mon == t.tm_mon
8526 && norm.tm_year == t.tm_year
8527 && norm.tm_hour == t.tm_hour
8528 && norm.tm_min == t.tm_min
8529 && norm.tm_mday == t.tm_mday
8530 && norm.tm_sec == t.tm_sec
8531 );
8532 }
8533 return false;
8534 }
8535
CheckTime(const std::string & sTime)8536 bool CSQLHelper::CheckTime(const std::string& sTime)
8537 {
8538 size_t iSemiColon = sTime.find(':');
8539 if ((iSemiColon == std::string::npos) || (iSemiColon < 1) || (iSemiColon > 2) || (iSemiColon == sTime.length() - 1)) return false;
8540 if ((sTime.length() < 3) || (sTime.length() > 5)) return false;
8541 if (atoi(sTime.substr(0, iSemiColon).c_str()) >= 24) return false;
8542 if (atoi(sTime.substr(iSemiColon + 1).c_str()) >= 60) return false;
8543 return true;
8544 }
8545
AllowNewHardwareTimer(const int iTotMinutes)8546 void CSQLHelper::AllowNewHardwareTimer(const int iTotMinutes)
8547 {
8548 m_iAcceptHardwareTimerCounter = iTotMinutes * 60.0f;
8549 if (m_bAcceptHardwareTimerActive == false)
8550 {
8551 m_bPreviousAcceptNewHardware = m_bAcceptNewHardware;
8552 }
8553 m_bAcceptNewHardware = true;
8554 m_bAcceptHardwareTimerActive = true;
8555 _log.Log(LOG_STATUS, "New sensors allowed for %d minutes...", iTotMinutes);
8556 }
8557
8558 /*
8559 std::string CSQLHelper::GetDeviceValue(const char * FieldName, const char *Idx)
8560 {
8561 TSqlQueryResult result = safe_query("SELECT %s from DeviceStatus WHERE (ID == %s )", FieldName, Idx);
8562 if (!result.empty())
8563 return result[0][0];
8564 else
8565 return "";
8566 }
8567 */
8568
UpdateDeviceValue(const char * FieldName,const std::string & Value,const std::string & Idx)8569 void CSQLHelper::UpdateDeviceValue(const char* FieldName, const std::string& Value, const std::string& Idx)
8570 {
8571 safe_query("UPDATE DeviceStatus SET %s='%s' , LastUpdate='%s' WHERE (ID == %s )", FieldName, Value.c_str(), TimeToString(nullptr, TF_DateTime).c_str(), Idx.c_str());
8572 }
UpdateDeviceValue(const char * FieldName,const int Value,const std::string & Idx)8573 void CSQLHelper::UpdateDeviceValue(const char* FieldName, const int Value, const std::string& Idx)
8574 {
8575 safe_query("UPDATE DeviceStatus SET %s=%d , LastUpdate='%s' WHERE (ID == %s )", FieldName, Value, TimeToString(nullptr, TF_DateTime).c_str(), Idx.c_str());
8576 }
UpdateDeviceValue(const char * FieldName,const float Value,const std::string & Idx)8577 void CSQLHelper::UpdateDeviceValue(const char* FieldName, const float Value, const std::string& Idx)
8578 {
8579 safe_query("UPDATE DeviceStatus SET %s=%4.2f , LastUpdate='%s' WHERE (ID == %s )", FieldName, Value, TimeToString(nullptr, TF_DateTime).c_str(), Idx.c_str());
8580 }
8581
UpdateDeviceName(const std::string & Idx,const std::string & Name)8582 void CSQLHelper::UpdateDeviceName(const std::string& Idx, const std::string& Name)
8583 {
8584 safe_query("UPDATE DeviceStatus SET Name='%q', LastUpdate='%s' WHERE (ID == %s )", Name.c_str(), TimeToString(nullptr, TF_DateTime).c_str(), Idx.c_str());
8585 }
8586
InsertCustomIconFromZip(const std::string & szZip,std::string & ErrorMessage)8587 bool CSQLHelper::InsertCustomIconFromZip(const std::string& szZip, std::string& ErrorMessage)
8588 {
8589 //write file to disk
8590 #ifdef WIN32
8591 std::string outputfile = "custom_icons.zip";
8592 #else
8593 std::string outputfile = "/tmp/custom_icons.zip";
8594 #endif
8595 std::ofstream outfile;
8596 outfile.open(outputfile.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
8597 if (!outfile.is_open())
8598 {
8599 ErrorMessage = "Error writing zip to disk";
8600 return false;
8601 }
8602 outfile << szZip;
8603 outfile.flush();
8604 outfile.close();
8605
8606 return InsertCustomIconFromZipFile(outputfile, ErrorMessage);
8607 }
8608
InsertCustomIconFromZipFile(const std::string & szZipFile,std::string & ErrorMessage)8609 bool CSQLHelper::InsertCustomIconFromZipFile(const std::string& szZipFile, std::string& ErrorMessage)
8610 {
8611 clx::basic_unzip<char> in(szZipFile);
8612 if (!in.is_open())
8613 {
8614 ErrorMessage = "Error opening zip file";
8615 return false;
8616 }
8617
8618 int iTotalAdded = 0;
8619
8620 for (clx::unzip::iterator pos = in.begin(); pos != in.end(); ++pos) {
8621 //_log.Log(LOG_STATUS, "unzip: %s", pos->path().c_str());
8622 std::string fpath = pos->path();
8623
8624 //Skip strange folders
8625 if (fpath.find("__MACOSX") != std::string::npos)
8626 continue;
8627
8628 size_t ipos = fpath.find("icons.txt");
8629 if (ipos != std::string::npos)
8630 {
8631 std::string rpath;
8632 if (ipos > 0)
8633 rpath = fpath.substr(0, ipos);
8634
8635 uLong fsize;
8636 unsigned char* pFBuf = (unsigned char*)(pos).Extract(fsize, 1);
8637 if (pFBuf == NULL)
8638 {
8639 ErrorMessage = "Could not extract icons.txt";
8640 return false;
8641 }
8642 pFBuf[fsize] = 0; //null terminate
8643
8644 std::string _defFile = std::string(pFBuf, pFBuf + fsize);
8645 free(pFBuf);
8646
8647 _defFile.erase(std::remove(_defFile.begin(), _defFile.end(), '\r'), _defFile.end());
8648
8649 std::vector<std::string> _Lines;
8650 StringSplit(_defFile, "\n", _Lines);
8651 for (const auto& itt : _Lines)
8652 {
8653 std::string sLine = itt;
8654 std::vector<std::string> splitresult;
8655 StringSplit(sLine, ";", splitresult);
8656 if (splitresult.size() == 3)
8657 {
8658 std::string IconBase = splitresult[0];
8659 std::string IconName = splitresult[1];
8660 std::string IconDesc = splitresult[2];
8661
8662 //Check if this Icon(Name) does not exist in the database already
8663 std::vector<std::vector<std::string> > result;
8664 result = safe_query("SELECT ID FROM CustomImages WHERE Base='%q'", IconBase.c_str());
8665 bool bIsDuplicate = (result.size() > 0);
8666 int RowID = 0;
8667 if (bIsDuplicate)
8668 {
8669 RowID = atoi(result[0][0].c_str());
8670 }
8671
8672 //Locate the files in the zip, if not present back out
8673 std::string IconFile16 = IconBase + ".png";
8674 std::string IconFile48On = IconBase + "48_On.png";
8675 std::string IconFile48Off = IconBase + "48_Off.png";
8676
8677 std::map<std::string, std::string> _dbImageFiles;
8678 _dbImageFiles["IconSmall"] = IconFile16;
8679 _dbImageFiles["IconOn"] = IconFile48On;
8680 _dbImageFiles["IconOff"] = IconFile48Off;
8681
8682 //Check if all icons are there
8683 for (const auto& iItt : _dbImageFiles)
8684 {
8685 //std::string TableField = iItt.first;
8686 std::string IconFile = rpath + iItt.second;
8687 if (in.find(IconFile) == in.end())
8688 {
8689 ErrorMessage = "Icon File: " + IconFile + " is not present";
8690 if (iTotalAdded > 0)
8691 {
8692 m_webservers.ReloadCustomSwitchIcons();
8693 }
8694 return false;
8695 }
8696 }
8697
8698 //All good, now lets add it to the database
8699 if (!bIsDuplicate)
8700 {
8701 safe_query("INSERT INTO CustomImages (Base,Name, Description) VALUES ('%q', '%q', '%q')",
8702 IconBase.c_str(), IconName.c_str(), IconDesc.c_str());
8703
8704 //Get our Database ROWID
8705 result = safe_query("SELECT ID FROM CustomImages WHERE Base='%q'", IconBase.c_str());
8706 if (result.empty())
8707 {
8708 ErrorMessage = "Error adding new row to database!";
8709 if (iTotalAdded > 0)
8710 {
8711 m_webservers.ReloadCustomSwitchIcons();
8712 }
8713 return false;
8714 }
8715 RowID = atoi(result[0][0].c_str());
8716 }
8717 else
8718 {
8719 //Update
8720 safe_query("UPDATE CustomImages SET Name='%q', Description='%q' WHERE ID=%d",
8721 IconName.c_str(), IconDesc.c_str(), RowID);
8722
8723 //Delete from disk, so it will be updated when we exit this function
8724 std::string IconFile16 = szWWWFolder + "/images/" + IconBase + ".png";
8725 std::string IconFile48On = szWWWFolder + "/images/" + IconBase + "48_On.png";
8726 std::string IconFile48Off = szWWWFolder + "/images/" + IconBase + "48_Off.png";
8727 std::remove(IconFile16.c_str());
8728 std::remove(IconFile48On.c_str());
8729 std::remove(IconFile48Off.c_str());
8730 }
8731
8732 //Insert the Icons
8733
8734 for (const auto& iItt : _dbImageFiles)
8735 {
8736 std::string TableField = iItt.first;
8737 std::string IconFile = rpath + iItt.second;
8738
8739 sqlite3_stmt* stmt = NULL;
8740 char* zQuery = sqlite3_mprintf("UPDATE CustomImages SET %s = ? WHERE ID=%d", TableField.c_str(), RowID);
8741 if (!zQuery)
8742 {
8743 _log.Log(LOG_ERROR, "SQL: Out of memory, or invalid printf!....");
8744 return false;
8745 }
8746 int rc = sqlite3_prepare_v2(m_dbase, zQuery, -1, &stmt, NULL);
8747 sqlite3_free(zQuery);
8748 if (rc != SQLITE_OK) {
8749 ErrorMessage = "Problem inserting icon into database! " + std::string(sqlite3_errmsg(m_dbase));
8750 if (iTotalAdded > 0)
8751 {
8752 m_webservers.ReloadCustomSwitchIcons();
8753 }
8754 return false;
8755 }
8756 // SQLITE_STATIC because the statement is finalized
8757 // before the buffer is freed:
8758 pFBuf = (unsigned char*)in.find(IconFile).Extract(fsize);
8759 if (pFBuf == NULL)
8760 {
8761 ErrorMessage = "Could not extract File: " + IconFile16;
8762 if (iTotalAdded > 0)
8763 {
8764 m_webservers.ReloadCustomSwitchIcons();
8765 }
8766 return false;
8767 }
8768 rc = sqlite3_bind_blob(stmt, 1, pFBuf, fsize, SQLITE_STATIC);
8769 if (rc != SQLITE_OK) {
8770 ErrorMessage = "Problem inserting icon into database! " + std::string(sqlite3_errmsg(m_dbase));
8771 free(pFBuf);
8772 if (iTotalAdded > 0)
8773 {
8774 m_webservers.ReloadCustomSwitchIcons();
8775 }
8776 return false;
8777 }
8778 else {
8779 rc = sqlite3_step(stmt);
8780 if (rc != SQLITE_DONE)
8781 {
8782 free(pFBuf);
8783 ErrorMessage = "Problem inserting icon into database! " + std::string(sqlite3_errmsg(m_dbase));
8784 if (iTotalAdded > 0)
8785 {
8786 m_webservers.ReloadCustomSwitchIcons();
8787 }
8788 return false;
8789 }
8790 }
8791 sqlite3_finalize(stmt);
8792 free(pFBuf);
8793 iTotalAdded++;
8794 }
8795 }
8796 }
8797
8798 }
8799 }
8800
8801 if (iTotalAdded == 0)
8802 {
8803 //definition file not found
8804 ErrorMessage = "No Icon definition file not found";
8805 return false;
8806 }
8807
8808 m_webservers.ReloadCustomSwitchIcons();
8809 return true;
8810 }
8811
BuildDeviceOptions(const std::string & options,const bool decode)8812 std::map<std::string, std::string> CSQLHelper::BuildDeviceOptions(const std::string& options, const bool decode)
8813 {
8814 std::map<std::string, std::string> optionsMap;
8815 if (!options.empty()) {
8816 //_log.Log(LOG_STATUS, "DEBUG : Build device options from '%s'...", options.c_str());
8817 std::vector<std::string> optionsArray;
8818 StringSplit(options, ";", optionsArray);
8819 for (const auto& itt : optionsArray)
8820 {
8821 std::string oValue = itt;
8822 if (oValue.empty())
8823 continue;
8824 size_t tpos = oValue.find_first_of(':');
8825 if ((tpos != std::string::npos) && (oValue.size() > tpos + 1))
8826 {
8827 std::string optionName = oValue.substr(0, tpos);
8828 oValue = oValue.substr(tpos + 1);
8829 std::string optionValue = decode ? base64_decode(oValue) : oValue;
8830 //_log.Log(LOG_STATUS, "DEBUG : Build device option ['%s': '%s'] => ['%s': '%s']", optionArray[0].c_str(), optionArray[1].c_str(), optionName.c_str(), optionValue.c_str());
8831 optionsMap.insert(std::pair<std::string, std::string>(optionName, optionValue));
8832 }
8833 }
8834 }
8835 //_log.Log(LOG_STATUS, "DEBUG : Build %d device(s) option(s)", optionsMap.size());
8836 return optionsMap;
8837 }
8838
GetDeviceOptions(const std::string & idx)8839 std::map<std::string, std::string> CSQLHelper::GetDeviceOptions(const std::string& idx)
8840 {
8841 std::map<std::string, std::string> optionsMap;
8842
8843 if (idx.empty()) {
8844 _log.Log(LOG_ERROR, "Cannot set options on device %s", idx.c_str());
8845 return optionsMap;
8846 }
8847
8848 uint64_t ulID = std::stoull(idx);
8849 std::vector<std::vector<std::string> > result;
8850 result = safe_query("SELECT Options FROM DeviceStatus WHERE (ID==%" PRIu64 ")", ulID);
8851 if (!result.empty()) {
8852 std::vector<std::string> sd = result[0];
8853 optionsMap = BuildDeviceOptions(sd[0].c_str());
8854 }
8855 return optionsMap;
8856 }
8857
FormatDeviceOptions(const std::map<std::string,std::string> & optionsMap)8858 std::string CSQLHelper::FormatDeviceOptions(const std::map<std::string, std::string>& optionsMap)
8859 {
8860 std::string options;
8861 int count = optionsMap.size();
8862 if (count > 0) {
8863 int i = 0;
8864 std::stringstream ssoptions;
8865 for (const auto& itt : optionsMap)
8866 {
8867 i++;
8868 //_log.Log(LOG_STATUS, "DEBUG : Reading device option ['%s', '%s']", itt->first.c_str(), itt->second.c_str());
8869 std::string optionName = itt.first.c_str();
8870 std::string optionValue = base64_encode(itt.second);
8871 ssoptions << optionName << ":" << optionValue;
8872 if (i < count) {
8873 ssoptions << ";";
8874 }
8875 }
8876 options.assign(ssoptions.str());
8877 }
8878
8879 return options;
8880 }
8881
SetDeviceOptions(const uint64_t idx,const std::map<std::string,std::string> & optionsMap)8882 bool CSQLHelper::SetDeviceOptions(const uint64_t idx, const std::map<std::string, std::string>& optionsMap)
8883 {
8884 if (idx < 1) {
8885 _log.Log(LOG_ERROR, "Cannot set options on device %" PRIu64 "", idx);
8886 return false;
8887 }
8888
8889 if (optionsMap.empty()) {
8890 //_log.Log(LOG_STATUS, "DEBUG : removing options on device %" PRIu64 "", idx);
8891 safe_query("UPDATE DeviceStatus SET Options = null WHERE (ID==%" PRIu64 ")", idx);
8892 }
8893 else {
8894 std::string options = FormatDeviceOptions(optionsMap);
8895 if (options.empty()) {
8896 _log.Log(LOG_ERROR, "Cannot parse options for device %" PRIu64 "", idx);
8897 return false;
8898 }
8899 //_log.Log(LOG_STATUS, "DEBUG : setting options '%s' on device %" PRIu64 "", options.c_str(), idx);
8900 safe_query("UPDATE DeviceStatus SET Options = '%q' WHERE (ID==%" PRIu64 ")", options.c_str(), idx);
8901 }
8902 return true;
8903 }
8904
GetCounterDivider(const int metertype,const int dType,const float DefaultValue)8905 float CSQLHelper::GetCounterDivider(const int metertype, const int dType, const float DefaultValue)
8906 {
8907 float divider = float(DefaultValue);
8908 if (divider == 0)
8909 {
8910 int tValue;
8911 switch (metertype)
8912 {
8913 case MTYPE_ENERGY:
8914 case MTYPE_ENERGY_GENERATED:
8915 if (GetPreferencesVar("MeterDividerEnergy", tValue))
8916 {
8917 divider = float(tValue);
8918 }
8919 break;
8920 case MTYPE_GAS:
8921 if (GetPreferencesVar("MeterDividerGas", tValue))
8922 {
8923 divider = float(tValue);
8924 }
8925 break;
8926 case MTYPE_WATER:
8927 if (GetPreferencesVar("MeterDividerWater", tValue))
8928 {
8929 divider = float(tValue);
8930 }
8931 break;
8932 }
8933 if (dType == pTypeP1Gas)
8934 divider = 1000;
8935 else if ((dType == pTypeENERGY) || (dType == pTypePOWER))
8936 divider *= 100.0f;
8937
8938 if (divider == 0) divider = 1.0f;
8939 }
8940 return divider;
8941 }
8942