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, &ltime);
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, &ltime);
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, &ltime);
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, &ltime);
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, &ltime);
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, &ltime);
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, &ltime);
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, &ltime);
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, &ltime);
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, &ltime);
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, &ltime);
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, &ltime);
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, &ltime);
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, &ltime);
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