1 #include "stdafx.h"
2 #include "WebServer.h"
3 #include "WebServerHelper.h"
4 #include <boost/bind.hpp>
5 #include <iostream>
6 #include <fstream>
7 #include "mainworker.h"
8 #include "Helper.h"
9 #include "localtime_r.h"
10 #include "EventSystem.h"
11 #include "HTMLSanitizer.h"
12 #include "dzVents.h"
13 #include "../httpclient/HTTPClient.h"
14 #include "../hardware/hardwaretypes.h"
15 #include "../hardware/1Wire.h"
16 #include "../hardware/OTGWBase.h"
17 #ifdef WITH_OPENZWAVE
18 #include "../hardware/OpenZWave.h"
19 #endif
20 #include "../hardware/EnOceanESP2.h"
21 #include "../hardware/EnOceanESP3.h"
22 #include "../hardware/Wunderground.h"
23 #include "../hardware/DarkSky.h"
24 #include "../hardware/AccuWeather.h"
25 #include "../hardware/OpenWeatherMap.h"
26 #include "../hardware/Buienradar.h"
27 #include "../hardware/Kodi.h"
28 #include "../hardware/Limitless.h"
29 #include "../hardware/LogitechMediaServer.h"
30 #include "../hardware/MySensorsBase.h"
31 #include "../hardware/RFXBase.h"
32 #include "../hardware/RFLinkBase.h"
33 #include "../hardware/SysfsGpio.h"
34 #include "../hardware/HEOS.h"
35 #include "../hardware/eHouseTCP.h"
36 #include "../hardware/USBtin.h"
37 #include "../hardware/USBtin_MultiblocV8.h"
38 #ifdef WITH_GPIO
39 #include "../hardware/Gpio.h"
40 #include "../hardware/GpioPin.h"
41 #endif // WITH_GPIO
42 #include "../hardware/Tellstick.h"
43 #include "../webserver/Base64.h"
44 #include "../smtpclient/SMTPClient.h"
45 #include <json/json.h>
46 #include "../main/json_helper.h"
47 #include "Logger.h"
48 #include "SQLHelper.h"
49 #include "../push/BasePush.h"
50 #include <algorithm>
51 #ifdef ENABLE_PYTHON
52 #include "../hardware/plugins/Plugins.h"
53 #endif
54 
55 #ifndef WIN32
56 #include <sys/utsname.h>
57 #include <dirent.h>
58 #else
59 #include "../msbuild/WindowsHelper.h"
60 #include "dirent_windows.h"
61 #endif
62 #include "../notifications/NotificationHelper.h"
63 #include "../main/LuaHandler.h"
64 
65 #define __STDC_FORMAT_MACROS
66 #include <inttypes.h>
67 
68 #define round(a) ( int ) ( a + .5 )
69 
70 extern std::string szStartupFolder;
71 extern std::string szUserDataFolder;
72 extern std::string szWWWFolder;
73 
74 extern std::string szAppVersion;
75 extern std::string szAppHash;
76 extern std::string szAppDate;
77 extern std::string szPyVersion;
78 
79 extern bool g_bUseUpdater;
80 
81 extern time_t m_StartTime;
82 
83 extern bool g_bDontCacheWWW;
84 
85 struct _tGuiLanguage {
86 	const char* szShort;
87 	const char* szLong;
88 };
89 
90 static const _tGuiLanguage guiLanguage[] =
91 {
92 	{ "en", "English" },
93 	{ "sq", "Albanian" },
94 	{ "ar", "Arabic" },
95 	{ "bs", "Bosnian" },
96 	{ "bg", "Bulgarian" },
97 	{ "ca", "Catalan" },
98 	{ "zh", "Chinese" },
99 	{ "cs", "Czech" },
100 	{ "da", "Danish" },
101 	{ "nl", "Dutch" },
102 	{ "et", "Estonian" },
103 	{ "de", "German" },
104 	{ "el", "Greek" },
105 	{ "fr", "French" },
106 	{ "fi", "Finnish" },
107 	{ "he", "Hebrew" },
108 	{ "hu", "Hungarian" },
109 	{ "is", "Icelandic" },
110 	{ "it", "Italian" },
111 	{ "lt", "Lithuanian" },
112 	{ "lv", "Latvian" },
113 	{ "mk", "Macedonian" },
114 	{ "no", "Norwegian" },
115 	{ "fa", "Persian" },
116 	{ "pl", "Polish" },
117 	{ "pt", "Portuguese" },
118 	{ "ro", "Romanian" },
119 	{ "ru", "Russian" },
120 	{ "sr", "Serbian" },
121 	{ "sk", "Slovak" },
122 	{ "sl", "Slovenian" },
123 	{ "es", "Spanish" },
124 	{ "sv", "Swedish" },
125 	{ "zh_TW", "Taiwanese" },
126 	{ "tr", "Turkish" },
127 	{ "uk", "Ukrainian" },
128 	{ NULL, NULL }
129 };
130 
131 extern http::server::CWebServerHelper m_webservers;
132 
133 namespace http {
134 	namespace server {
135 
CWebServer(void)136 		CWebServer::CWebServer(void) : session_store()
137 		{
138 			m_pWebEm = NULL;
139 			m_bDoStop = false;
140 #ifdef WITH_OPENZWAVE
141 			m_ZW_Hwidx = -1;
142 #endif
143 		}
144 
145 
~CWebServer(void)146 		CWebServer::~CWebServer(void)
147 		{
148 			// RK, we call StopServer() instead of just deleting m_pWebEm. The Do_Work thread might still be accessing that object
149 			StopServer();
150 		}
151 
Do_Work()152 		void CWebServer::Do_Work()
153 		{
154 			bool exception_thrown = false;
155 			while (!m_bDoStop)
156 			{
157 				exception_thrown = false;
158 				try {
159 					if (m_pWebEm) {
160 						m_pWebEm->Run();
161 					}
162 				}
163 				catch (std::exception& e) {
164 					_log.Log(LOG_ERROR, "WebServer(%s) exception occurred : '%s'", m_server_alias.c_str(), e.what());
165 					exception_thrown = true;
166 				}
167 				catch (...) {
168 					_log.Log(LOG_ERROR, "WebServer(%s) unknown exception occurred", m_server_alias.c_str());
169 					exception_thrown = true;
170 				}
171 				if (exception_thrown) {
172 					_log.Log(LOG_STATUS, "WebServer(%s) restart server in 5 seconds", m_server_alias.c_str());
173 					sleep_milliseconds(5000); // prevents from an exception flood
174 					continue;
175 				}
176 				break;
177 			}
178 			_log.Log(LOG_STATUS, "WebServer(%s) stopped", m_server_alias.c_str());
179 		}
180 
ReloadCustomSwitchIcons()181 		void CWebServer::ReloadCustomSwitchIcons()
182 		{
183 			m_custom_light_icons.clear();
184 			m_custom_light_icons_lookup.clear();
185 			std::string sLine = "";
186 
187 			//First get them from the switch_icons.txt file
188 			std::ifstream infile;
189 			std::string switchlightsfile = szWWWFolder + "/switch_icons.txt";
190 			infile.open(switchlightsfile.c_str());
191 			if (infile.is_open())
192 			{
193 				int index = 0;
194 				while (!infile.eof())
195 				{
196 					getline(infile, sLine);
197 					if (sLine.size() != 0)
198 					{
199 						std::vector<std::string> results;
200 						StringSplit(sLine, ";", results);
201 						if (results.size() == 3)
202 						{
203 							_tCustomIcon cImage;
204 							cImage.idx = index++;
205 							cImage.RootFile = results[0];
206 							cImage.Title = results[1];
207 							cImage.Description = results[2];
208 							m_custom_light_icons.push_back(cImage);
209 							m_custom_light_icons_lookup[cImage.idx] = m_custom_light_icons.size() - 1;
210 						}
211 					}
212 				}
213 				infile.close();
214 			}
215 			//Now get them from the database (idx 100+)
216 			std::vector<std::vector<std::string> > result;
217 			result = m_sql.safe_query("SELECT ID,Base,Name,Description FROM CustomImages");
218 			if (!result.empty())
219 			{
220 				int ii = 0;
221 				for (const auto & itt : result)
222 				{
223 					std::vector<std::string> sd = itt;
224 
225 					int ID = atoi(sd[0].c_str());
226 
227 					_tCustomIcon cImage;
228 					cImage.idx = 100 + ID;
229 					cImage.RootFile = sd[1];
230 					cImage.Title = sd[2];
231 					cImage.Description = sd[3];
232 
233 					std::string IconFile16 = cImage.RootFile + ".png";
234 					std::string IconFile48On = cImage.RootFile + "48_On.png";
235 					std::string IconFile48Off = cImage.RootFile + "48_Off.png";
236 
237 					std::map<std::string, std::string> _dbImageFiles;
238 					_dbImageFiles["IconSmall"] = szWWWFolder + "/images/" + IconFile16;
239 					_dbImageFiles["IconOn"] = szWWWFolder + "/images/" + IconFile48On;
240 					_dbImageFiles["IconOff"] = szWWWFolder + "/images/" + IconFile48Off;
241 
242 					//Check if files are on disk, else add them
243 					for (const auto & iItt : _dbImageFiles)
244 					{
245 						std::string TableField = iItt.first;
246 						std::string IconFile = iItt.second;
247 
248 						if (!file_exist(IconFile.c_str()))
249 						{
250 							//Does not exists, extract it from the database and add it
251 							std::vector<std::vector<std::string> > result2;
252 							result2 = m_sql.safe_queryBlob("SELECT %s FROM CustomImages WHERE ID=%d", TableField.c_str(), ID);
253 							if (!result2.empty())
254 							{
255 								std::ofstream file;
256 								file.open(IconFile.c_str(), std::ios::out | std::ios::binary);
257 								if (!file.is_open())
258 									return;
259 
260 								file << result2[0][0];
261 								file.close();
262 							}
263 						}
264 					}
265 
266 					m_custom_light_icons.push_back(cImage);
267 					m_custom_light_icons_lookup[cImage.idx] = m_custom_light_icons.size() - 1;
268 					ii++;
269 				}
270 			}
271 		}
272 
StartServer(server_settings & settings,const std::string & serverpath,const bool bIgnoreUsernamePassword)273 		bool CWebServer::StartServer(server_settings & settings, const std::string & serverpath, const bool bIgnoreUsernamePassword)
274 		{
275 			m_server_alias = (settings.is_secure() == true) ? "SSL" : "HTTP";
276 
277 			if (!settings.is_enabled())
278 				return true;
279 
280 			ReloadCustomSwitchIcons();
281 
282 			int tries = 0;
283 			bool exception = false;
284 
285 			//_log.Log(LOG_STATUS, "CWebServer::StartServer() : settings : %s", settings.to_string().c_str());
286 			do {
287 				try {
288 					exception = false;
289 					m_pWebEm = new http::server::cWebem(settings, serverpath.c_str());
290 				}
291 				catch (std::exception& e) {
292 					exception = true;
293 					switch (tries) {
294 					case 0:
295 						settings.listening_address = "::";
296 						break;
297 					case 1:
298 						settings.listening_address = "0.0.0.0";
299 						break;
300 					case 2:
301 						_log.Log(LOG_ERROR, "WebServer(%s) startup failed on address %s with port: %s: %s", m_server_alias.c_str(), settings.listening_address.c_str(), settings.listening_port.c_str(), e.what());
302 						if (atoi(settings.listening_port.c_str()) < 1024)
303 							_log.Log(LOG_ERROR, "WebServer(%s) check privileges for opening ports below 1024", m_server_alias.c_str());
304 						else
305 							_log.Log(LOG_ERROR, "WebServer(%s) check if no other application is using port: %s", m_server_alias.c_str(), settings.listening_port.c_str());
306 						return false;
307 					}
308 					tries++;
309 				}
310 			} while (exception);
311 
312 			_log.Log(LOG_STATUS, "WebServer(%s) started on address: %s with port %s", m_server_alias.c_str(), settings.listening_address.c_str(), settings.listening_port.c_str());
313 
314 			m_pWebEm->SetDigistRealm("Domoticz.com");
315 			m_pWebEm->SetSessionStore(this);
316 
317 			if (!bIgnoreUsernamePassword)
318 			{
319 				LoadUsers();
320 				std::string WebLocalNetworks;
321 				int nValue;
322 				if (m_sql.GetPreferencesVar("WebLocalNetworks", nValue, WebLocalNetworks))
323 				{
324 					std::vector<std::string> strarray;
325 					StringSplit(WebLocalNetworks, ";", strarray);
326 					for (const auto & itt : strarray)
327 						m_pWebEm->AddLocalNetworks(itt);
328 					//add local hostname
329 					m_pWebEm->AddLocalNetworks("");
330 				}
331 			}
332 
333 			std::string WebRemoteProxyIPs;
334 			int nValue;
335 			if (m_sql.GetPreferencesVar("WebRemoteProxyIPs", nValue, WebRemoteProxyIPs))
336 			{
337 				std::vector<std::string> strarray;
338 				StringSplit(WebRemoteProxyIPs, ";", strarray);
339 				for (const auto & itt : strarray)
340 					m_pWebEm->AddRemoteProxyIPs(itt);
341 			}
342 
343 			//register callbacks
344 			m_pWebEm->RegisterIncludeCode("switchtypes", boost::bind(&CWebServer::DisplaySwitchTypesCombo, this, _1));
345 			m_pWebEm->RegisterIncludeCode("metertypes", boost::bind(&CWebServer::DisplayMeterTypesCombo, this, _1));
346 			m_pWebEm->RegisterIncludeCode("timertypes", boost::bind(&CWebServer::DisplayTimerTypesCombo, this, _1));
347 			m_pWebEm->RegisterIncludeCode("combolanguage", boost::bind(&CWebServer::DisplayLanguageCombo, this, _1));
348 
349 			m_pWebEm->RegisterPageCode("/json.htm", boost::bind(&CWebServer::GetJSonPage, this, _1, _2, _3));
350 			m_pWebEm->RegisterPageCode("/uploadcustomicon", boost::bind(&CWebServer::Post_UploadCustomIcon, this, _1, _2, _3));
351 			m_pWebEm->RegisterPageCode("/html5.appcache", boost::bind(&CWebServer::GetAppCache, this, _1, _2, _3));
352 			m_pWebEm->RegisterPageCode("/camsnapshot.jpg", boost::bind(&CWebServer::GetCameraSnapshot, this, _1, _2, _3));
353 			m_pWebEm->RegisterPageCode("/backupdatabase.php", boost::bind(&CWebServer::GetDatabaseBackup, this, _1, _2, _3));
354 			m_pWebEm->RegisterPageCode("/raspberry.cgi", boost::bind(&CWebServer::GetInternalCameraSnapshot, this, _1, _2, _3));
355 			m_pWebEm->RegisterPageCode("/uvccapture.cgi", boost::bind(&CWebServer::GetInternalCameraSnapshot, this, _1, _2, _3));
356 			m_pWebEm->RegisterPageCode("/images/floorplans/plan", boost::bind(&CWebServer::GetFloorplanImage, this, _1, _2, _3));
357 
358 			m_pWebEm->RegisterPageCode("/storesettings", boost::bind(&CWebServer::PostSettings, this, _1, _2, _3));
359 			m_pWebEm->RegisterActionCode("setrfxcommode", boost::bind(&CWebServer::SetRFXCOMMode, this, _1, _2, _3));
360 			m_pWebEm->RegisterActionCode("rfxupgradefirmware", boost::bind(&CWebServer::RFXComUpgradeFirmware, this, _1, _2, _3));
361 			RegisterCommandCode("rfxfirmwaregetpercentage", boost::bind(&CWebServer::Cmd_RFXComGetFirmwarePercentage, this, _1, _2, _3), true);
362 			m_pWebEm->RegisterActionCode("setrego6xxtype", boost::bind(&CWebServer::SetRego6XXType, this, _1, _2, _3));
363 			m_pWebEm->RegisterActionCode("sets0metertype", boost::bind(&CWebServer::SetS0MeterType, this, _1, _2, _3));
364 			m_pWebEm->RegisterActionCode("setlimitlesstype", boost::bind(&CWebServer::SetLimitlessType, this, _1, _2, _3));
365 
366 			m_pWebEm->RegisterActionCode("uploadfloorplanimage", boost::bind(&CWebServer::UploadFloorplanImage, this, _1, _2, _3));
367 
368 
369 			m_pWebEm->RegisterActionCode("setopenthermsettings", boost::bind(&CWebServer::SetOpenThermSettings, this, _1, _2, _3));
370 			RegisterCommandCode("sendopenthermcommand", boost::bind(&CWebServer::Cmd_SendOpenThermCommand, this, _1, _2, _3), true);
371 
372 			m_pWebEm->RegisterActionCode("reloadpiface", boost::bind(&CWebServer::ReloadPiFace, this, _1, _2, _3));
373 			m_pWebEm->RegisterActionCode("setcurrentcostmetertype", boost::bind(&CWebServer::SetCurrentCostUSBType, this, _1, _2, _3));
374 			m_pWebEm->RegisterActionCode("restoredatabase", boost::bind(&CWebServer::RestoreDatabase, this, _1, _2, _3));
375 			m_pWebEm->RegisterActionCode("sbfspotimportolddata", boost::bind(&CWebServer::SBFSpotImportOldData, this, _1, _2, _3));
376 
377 			m_pWebEm->RegisterActionCode("event_create", boost::bind(&CWebServer::EventCreate, this, _1, _2, _3));
378 
379 			RegisterCommandCode("getlanguage", boost::bind(&CWebServer::Cmd_GetLanguage, this, _1, _2, _3), true);
380 			RegisterCommandCode("getthemes", boost::bind(&CWebServer::Cmd_GetThemes, this, _1, _2, _3), true);
381 			RegisterCommandCode("gettitle", boost::bind(&CWebServer::Cmd_GetTitle, this, _1, _2, _3), true);
382 
383 			RegisterCommandCode("logincheck", boost::bind(&CWebServer::Cmd_LoginCheck, this, _1, _2, _3), true);
384 			m_pWebEm->RegisterPageCode("/logincheck", boost::bind(&CWebServer::PostLoginCheck, this, _1, _2, _3), true);
385 
386 			RegisterCommandCode("getversion", boost::bind(&CWebServer::Cmd_GetVersion, this, _1, _2, _3), true);
387 			RegisterCommandCode("getlog", boost::bind(&CWebServer::Cmd_GetLog, this, _1, _2, _3));
388 			RegisterCommandCode("clearlog", boost::bind(&CWebServer::Cmd_ClearLog, this, _1, _2, _3));
389 			RegisterCommandCode("getauth", boost::bind(&CWebServer::Cmd_GetAuth, this, _1, _2, _3), true);
390 			RegisterCommandCode("getuptime", boost::bind(&CWebServer::Cmd_GetUptime, this, _1, _2, _3), true);
391 
392 
393 			RegisterCommandCode("gethardwaretypes", boost::bind(&CWebServer::Cmd_GetHardwareTypes, this, _1, _2, _3));
394 			RegisterCommandCode("addhardware", boost::bind(&CWebServer::Cmd_AddHardware, this, _1, _2, _3));
395 			RegisterCommandCode("updatehardware", boost::bind(&CWebServer::Cmd_UpdateHardware, this, _1, _2, _3));
396 			RegisterCommandCode("deletehardware", boost::bind(&CWebServer::Cmd_DeleteHardware, this, _1, _2, _3));
397 
398 			RegisterCommandCode("addcamera", boost::bind(&CWebServer::Cmd_AddCamera, this, _1, _2, _3));
399 			RegisterCommandCode("updatecamera", boost::bind(&CWebServer::Cmd_UpdateCamera, this, _1, _2, _3));
400 			RegisterCommandCode("deletecamera", boost::bind(&CWebServer::Cmd_DeleteCamera, this, _1, _2, _3));
401 
402 			RegisterCommandCode("wolgetnodes", boost::bind(&CWebServer::Cmd_WOLGetNodes, this, _1, _2, _3));
403 			RegisterCommandCode("woladdnode", boost::bind(&CWebServer::Cmd_WOLAddNode, this, _1, _2, _3));
404 			RegisterCommandCode("wolupdatenode", boost::bind(&CWebServer::Cmd_WOLUpdateNode, this, _1, _2, _3));
405 			RegisterCommandCode("wolremovenode", boost::bind(&CWebServer::Cmd_WOLRemoveNode, this, _1, _2, _3));
406 			RegisterCommandCode("wolclearnodes", boost::bind(&CWebServer::Cmd_WOLClearNodes, this, _1, _2, _3));
407 
408 			RegisterCommandCode("mysensorsgetnodes", boost::bind(&CWebServer::Cmd_MySensorsGetNodes, this, _1, _2, _3));
409 			RegisterCommandCode("mysensorsgetchilds", boost::bind(&CWebServer::Cmd_MySensorsGetChilds, this, _1, _2, _3));
410 			RegisterCommandCode("mysensorsupdatenode", boost::bind(&CWebServer::Cmd_MySensorsUpdateNode, this, _1, _2, _3));
411 			RegisterCommandCode("mysensorsremovenode", boost::bind(&CWebServer::Cmd_MySensorsRemoveNode, this, _1, _2, _3));
412 			RegisterCommandCode("mysensorsremovechild", boost::bind(&CWebServer::Cmd_MySensorsRemoveChild, this, _1, _2, _3));
413 			RegisterCommandCode("mysensorsupdatechild", boost::bind(&CWebServer::Cmd_MySensorsUpdateChild, this, _1, _2, _3));
414 
415 			RegisterCommandCode("pingersetmode", boost::bind(&CWebServer::Cmd_PingerSetMode, this, _1, _2, _3));
416 			RegisterCommandCode("pingergetnodes", boost::bind(&CWebServer::Cmd_PingerGetNodes, this, _1, _2, _3));
417 			RegisterCommandCode("pingeraddnode", boost::bind(&CWebServer::Cmd_PingerAddNode, this, _1, _2, _3));
418 			RegisterCommandCode("pingerupdatenode", boost::bind(&CWebServer::Cmd_PingerUpdateNode, this, _1, _2, _3));
419 			RegisterCommandCode("pingerremovenode", boost::bind(&CWebServer::Cmd_PingerRemoveNode, this, _1, _2, _3));
420 			RegisterCommandCode("pingerclearnodes", boost::bind(&CWebServer::Cmd_PingerClearNodes, this, _1, _2, _3));
421 
422 			RegisterCommandCode("kodisetmode", boost::bind(&CWebServer::Cmd_KodiSetMode, this, _1, _2, _3));
423 			RegisterCommandCode("kodigetnodes", boost::bind(&CWebServer::Cmd_KodiGetNodes, this, _1, _2, _3));
424 			RegisterCommandCode("kodiaddnode", boost::bind(&CWebServer::Cmd_KodiAddNode, this, _1, _2, _3));
425 			RegisterCommandCode("kodiupdatenode", boost::bind(&CWebServer::Cmd_KodiUpdateNode, this, _1, _2, _3));
426 			RegisterCommandCode("kodiremovenode", boost::bind(&CWebServer::Cmd_KodiRemoveNode, this, _1, _2, _3));
427 			RegisterCommandCode("kodiclearnodes", boost::bind(&CWebServer::Cmd_KodiClearNodes, this, _1, _2, _3));
428 			RegisterCommandCode("kodimediacommand", boost::bind(&CWebServer::Cmd_KodiMediaCommand, this, _1, _2, _3));
429 
430 			RegisterCommandCode("panasonicsetmode", boost::bind(&CWebServer::Cmd_PanasonicSetMode, this, _1, _2, _3));
431 			RegisterCommandCode("panasonicgetnodes", boost::bind(&CWebServer::Cmd_PanasonicGetNodes, this, _1, _2, _3));
432 			RegisterCommandCode("panasonicaddnode", boost::bind(&CWebServer::Cmd_PanasonicAddNode, this, _1, _2, _3));
433 			RegisterCommandCode("panasonicupdatenode", boost::bind(&CWebServer::Cmd_PanasonicUpdateNode, this, _1, _2, _3));
434 			RegisterCommandCode("panasonicremovenode", boost::bind(&CWebServer::Cmd_PanasonicRemoveNode, this, _1, _2, _3));
435 			RegisterCommandCode("panasonicclearnodes", boost::bind(&CWebServer::Cmd_PanasonicClearNodes, this, _1, _2, _3));
436 			RegisterCommandCode("panasonicmediacommand", boost::bind(&CWebServer::Cmd_PanasonicMediaCommand, this, _1, _2, _3));
437 
438 			RegisterCommandCode("heossetmode", boost::bind(&CWebServer::Cmd_HEOSSetMode, this, _1, _2, _3));
439 			RegisterCommandCode("heosmediacommand", boost::bind(&CWebServer::Cmd_HEOSMediaCommand, this, _1, _2, _3));
440 
441 			RegisterCommandCode("onkyoeiscpcommand", boost::bind(&CWebServer::Cmd_OnkyoEiscpCommand, this, _1, _2, _3));
442 
443 			RegisterCommandCode("bleboxsetmode", boost::bind(&CWebServer::Cmd_BleBoxSetMode, this, _1, _2, _3));
444 			RegisterCommandCode("bleboxgetnodes", boost::bind(&CWebServer::Cmd_BleBoxGetNodes, this, _1, _2, _3));
445 			RegisterCommandCode("bleboxaddnode", boost::bind(&CWebServer::Cmd_BleBoxAddNode, this, _1, _2, _3));
446 			RegisterCommandCode("bleboxremovenode", boost::bind(&CWebServer::Cmd_BleBoxRemoveNode, this, _1, _2, _3));
447 			RegisterCommandCode("bleboxclearnodes", boost::bind(&CWebServer::Cmd_BleBoxClearNodes, this, _1, _2, _3));
448 			RegisterCommandCode("bleboxautosearchingnodes", boost::bind(&CWebServer::Cmd_BleBoxAutoSearchingNodes, this, _1, _2, _3));
449 			RegisterCommandCode("bleboxupdatefirmware", boost::bind(&CWebServer::Cmd_BleBoxUpdateFirmware, this, _1, _2, _3));
450 
451 			RegisterCommandCode("lmssetmode", boost::bind(&CWebServer::Cmd_LMSSetMode, this, _1, _2, _3));
452 			RegisterCommandCode("lmsgetnodes", boost::bind(&CWebServer::Cmd_LMSGetNodes, this, _1, _2, _3));
453 			RegisterCommandCode("lmsgetplaylists", boost::bind(&CWebServer::Cmd_LMSGetPlaylists, this, _1, _2, _3));
454 			RegisterCommandCode("lmsmediacommand", boost::bind(&CWebServer::Cmd_LMSMediaCommand, this, _1, _2, _3));
455 			RegisterCommandCode("lmsdeleteunuseddevices", boost::bind(&CWebServer::Cmd_LMSDeleteUnusedDevices, this, _1, _2, _3));
456 
457 			RegisterCommandCode("savefibarolinkconfig", boost::bind(&CWebServer::Cmd_SaveFibaroLinkConfig, this, _1, _2, _3));
458 			RegisterCommandCode("getfibarolinkconfig", boost::bind(&CWebServer::Cmd_GetFibaroLinkConfig, this, _1, _2, _3));
459 			RegisterCommandCode("getfibarolinks", boost::bind(&CWebServer::Cmd_GetFibaroLinks, this, _1, _2, _3));
460 			RegisterCommandCode("savefibarolink", boost::bind(&CWebServer::Cmd_SaveFibaroLink, this, _1, _2, _3));
461 			RegisterCommandCode("deletefibarolink", boost::bind(&CWebServer::Cmd_DeleteFibaroLink, this, _1, _2, _3));
462 
463 			RegisterCommandCode("saveinfluxlinkconfig", boost::bind(&CWebServer::Cmd_SaveInfluxLinkConfig, this, _1, _2, _3));
464 			RegisterCommandCode("getinfluxlinkconfig", boost::bind(&CWebServer::Cmd_GetInfluxLinkConfig, this, _1, _2, _3));
465 			RegisterCommandCode("getinfluxlinks", boost::bind(&CWebServer::Cmd_GetInfluxLinks, this, _1, _2, _3));
466 			RegisterCommandCode("saveinfluxlink", boost::bind(&CWebServer::Cmd_SaveInfluxLink, this, _1, _2, _3));
467 			RegisterCommandCode("deleteinfluxlink", boost::bind(&CWebServer::Cmd_DeleteInfluxLink, this, _1, _2, _3));
468 
469 			RegisterCommandCode("savehttplinkconfig", boost::bind(&CWebServer::Cmd_SaveHttpLinkConfig, this, _1, _2, _3));
470 			RegisterCommandCode("gethttplinkconfig", boost::bind(&CWebServer::Cmd_GetHttpLinkConfig, this, _1, _2, _3));
471 			RegisterCommandCode("gethttplinks", boost::bind(&CWebServer::Cmd_GetHttpLinks, this, _1, _2, _3));
472 			RegisterCommandCode("savehttplink", boost::bind(&CWebServer::Cmd_SaveHttpLink, this, _1, _2, _3));
473 			RegisterCommandCode("deletehttplink", boost::bind(&CWebServer::Cmd_DeleteHttpLink, this, _1, _2, _3));
474 
475 			RegisterCommandCode("savegooglepubsublinkconfig", boost::bind(&CWebServer::Cmd_SaveGooglePubSubLinkConfig, this, _1, _2, _3));
476 			RegisterCommandCode("getgooglepubsublinkconfig", boost::bind(&CWebServer::Cmd_GetGooglePubSubLinkConfig, this, _1, _2, _3));
477 			RegisterCommandCode("getgooglepubsublinks", boost::bind(&CWebServer::Cmd_GetGooglePubSubLinks, this, _1, _2, _3));
478 			RegisterCommandCode("savegooglepubsublink", boost::bind(&CWebServer::Cmd_SaveGooglePubSubLink, this, _1, _2, _3));
479 			RegisterCommandCode("deletegooglepubsublink", boost::bind(&CWebServer::Cmd_DeleteGooglePubSubLink, this, _1, _2, _3));
480 
481 			RegisterCommandCode("getdevicevalueoptions", boost::bind(&CWebServer::Cmd_GetDeviceValueOptions, this, _1, _2, _3));
482 			RegisterCommandCode("getdevicevalueoptionwording", boost::bind(&CWebServer::Cmd_GetDeviceValueOptionWording, this, _1, _2, _3));
483 
484 			RegisterCommandCode("adduservariable", boost::bind(&CWebServer::Cmd_AddUserVariable, this, _1, _2, _3));
485 			RegisterCommandCode("updateuservariable", boost::bind(&CWebServer::Cmd_UpdateUserVariable, this, _1, _2, _3));
486 			RegisterCommandCode("deleteuservariable", boost::bind(&CWebServer::Cmd_DeleteUserVariable, this, _1, _2, _3));
487 			RegisterCommandCode("getuservariables", boost::bind(&CWebServer::Cmd_GetUserVariables, this, _1, _2, _3));
488 			RegisterCommandCode("getuservariable", boost::bind(&CWebServer::Cmd_GetUserVariable, this, _1, _2, _3));
489 
490 			RegisterCommandCode("allownewhardware", boost::bind(&CWebServer::Cmd_AllowNewHardware, this, _1, _2, _3));
491 
492 			RegisterCommandCode("addplan", boost::bind(&CWebServer::Cmd_AddPlan, this, _1, _2, _3));
493 			RegisterCommandCode("updateplan", boost::bind(&CWebServer::Cmd_UpdatePlan, this, _1, _2, _3));
494 			RegisterCommandCode("deleteplan", boost::bind(&CWebServer::Cmd_DeletePlan, this, _1, _2, _3));
495 			RegisterCommandCode("getunusedplandevices", boost::bind(&CWebServer::Cmd_GetUnusedPlanDevices, this, _1, _2, _3));
496 			RegisterCommandCode("addplanactivedevice", boost::bind(&CWebServer::Cmd_AddPlanActiveDevice, this, _1, _2, _3));
497 			RegisterCommandCode("getplandevices", boost::bind(&CWebServer::Cmd_GetPlanDevices, this, _1, _2, _3));
498 			RegisterCommandCode("deleteplandevice", boost::bind(&CWebServer::Cmd_DeletePlanDevice, this, _1, _2, _3));
499 			RegisterCommandCode("setplandevicecoords", boost::bind(&CWebServer::Cmd_SetPlanDeviceCoords, this, _1, _2, _3));
500 			RegisterCommandCode("deleteallplandevices", boost::bind(&CWebServer::Cmd_DeleteAllPlanDevices, this, _1, _2, _3));
501 			RegisterCommandCode("changeplanorder", boost::bind(&CWebServer::Cmd_ChangePlanOrder, this, _1, _2, _3));
502 			RegisterCommandCode("changeplandeviceorder", boost::bind(&CWebServer::Cmd_ChangePlanDeviceOrder, this, _1, _2, _3));
503 
504 			RegisterCommandCode("gettimerplans", boost::bind(&CWebServer::Cmd_GetTimerPlans, this, _1, _2, _3));
505 			RegisterCommandCode("addtimerplan", boost::bind(&CWebServer::Cmd_AddTimerPlan, this, _1, _2, _3));
506 			RegisterCommandCode("updatetimerplan", boost::bind(&CWebServer::Cmd_UpdateTimerPlan, this, _1, _2, _3));
507 			RegisterCommandCode("deletetimerplan", boost::bind(&CWebServer::Cmd_DeleteTimerPlan, this, _1, _2, _3));
508 			RegisterCommandCode("duplicatetimerplan", boost::bind(&CWebServer::Cmd_DuplicateTimerPlan, this, _1, _2, _3));
509 
510 			RegisterCommandCode("getactualhistory", boost::bind(&CWebServer::Cmd_GetActualHistory, this, _1, _2, _3));
511 			RegisterCommandCode("getnewhistory", boost::bind(&CWebServer::Cmd_GetNewHistory, this, _1, _2, _3));
512 
513 			RegisterCommandCode("getconfig", boost::bind(&CWebServer::Cmd_GetConfig, this, _1, _2, _3), true);
514 			RegisterCommandCode("getlocation", boost::bind(&CWebServer::Cmd_GetLocation, this, _1, _2, _3));
515 			RegisterCommandCode("sendnotification", boost::bind(&CWebServer::Cmd_SendNotification, this, _1, _2, _3));
516 			RegisterCommandCode("emailcamerasnapshot", boost::bind(&CWebServer::Cmd_EmailCameraSnapshot, this, _1, _2, _3));
517 			RegisterCommandCode("udevice", boost::bind(&CWebServer::Cmd_UpdateDevice, this, _1, _2, _3));
518 			RegisterCommandCode("udevices", boost::bind(&CWebServer::Cmd_UpdateDevices, this, _1, _2, _3));
519 			RegisterCommandCode("thermostatstate", boost::bind(&CWebServer::Cmd_SetThermostatState, this, _1, _2, _3));
520 			RegisterCommandCode("system_shutdown", boost::bind(&CWebServer::Cmd_SystemShutdown, this, _1, _2, _3));
521 			RegisterCommandCode("system_reboot", boost::bind(&CWebServer::Cmd_SystemReboot, this, _1, _2, _3));
522 			RegisterCommandCode("execute_script", boost::bind(&CWebServer::Cmd_ExcecuteScript, this, _1, _2, _3));
523 			RegisterCommandCode("getcosts", boost::bind(&CWebServer::Cmd_GetCosts, this, _1, _2, _3));
524 			RegisterCommandCode("checkforupdate", boost::bind(&CWebServer::Cmd_CheckForUpdate, this, _1, _2, _3));
525 			RegisterCommandCode("downloadupdate", boost::bind(&CWebServer::Cmd_DownloadUpdate, this, _1, _2, _3));
526 			RegisterCommandCode("downloadready", boost::bind(&CWebServer::Cmd_DownloadReady, this, _1, _2, _3));
527 			RegisterCommandCode("update_application", boost::bind(&CWebServer::Cmd_UpdateApplication, this, _1, _2, _3));
528 			RegisterCommandCode("deletedatapoint", boost::bind(&CWebServer::Cmd_DeleteDatePoint, this, _1, _2, _3));
529 			RegisterCommandCode("customevent", boost::bind(&CWebServer::Cmd_CustomEvent, this, _1, _2, _3));
530 
531 			RegisterCommandCode("setactivetimerplan", boost::bind(&CWebServer::Cmd_SetActiveTimerPlan, this, _1, _2, _3));
532 			RegisterCommandCode("addtimer", boost::bind(&CWebServer::Cmd_AddTimer, this, _1, _2, _3));
533 			RegisterCommandCode("updatetimer", boost::bind(&CWebServer::Cmd_UpdateTimer, this, _1, _2, _3));
534 			RegisterCommandCode("deletetimer", boost::bind(&CWebServer::Cmd_DeleteTimer, this, _1, _2, _3));
535 			RegisterCommandCode("enabletimer", boost::bind(&CWebServer::Cmd_EnableTimer, this, _1, _2, _3));
536 			RegisterCommandCode("disabletimer", boost::bind(&CWebServer::Cmd_DisableTimer, this, _1, _2, _3));
537 			RegisterCommandCode("cleartimers", boost::bind(&CWebServer::Cmd_ClearTimers, this, _1, _2, _3));
538 
539 			RegisterCommandCode("addscenetimer", boost::bind(&CWebServer::Cmd_AddSceneTimer, this, _1, _2, _3));
540 			RegisterCommandCode("updatescenetimer", boost::bind(&CWebServer::Cmd_UpdateSceneTimer, this, _1, _2, _3));
541 			RegisterCommandCode("deletescenetimer", boost::bind(&CWebServer::Cmd_DeleteSceneTimer, this, _1, _2, _3));
542 			RegisterCommandCode("enablescenetimer", boost::bind(&CWebServer::Cmd_EnableSceneTimer, this, _1, _2, _3));
543 			RegisterCommandCode("disablescenetimer", boost::bind(&CWebServer::Cmd_DisableSceneTimer, this, _1, _2, _3));
544 			RegisterCommandCode("clearscenetimers", boost::bind(&CWebServer::Cmd_ClearSceneTimers, this, _1, _2, _3));
545 			RegisterCommandCode("getsceneactivations", boost::bind(&CWebServer::Cmd_GetSceneActivations, this, _1, _2, _3));
546 			RegisterCommandCode("addscenecode", boost::bind(&CWebServer::Cmd_AddSceneCode, this, _1, _2, _3));
547 			RegisterCommandCode("removescenecode", boost::bind(&CWebServer::Cmd_RemoveSceneCode, this, _1, _2, _3));
548 			RegisterCommandCode("clearscenecodes", boost::bind(&CWebServer::Cmd_ClearSceneCodes, this, _1, _2, _3));
549 			RegisterCommandCode("renamescene", boost::bind(&CWebServer::Cmd_RenameScene, this, _1, _2, _3));
550 
551 			RegisterCommandCode("setsetpoint", boost::bind(&CWebServer::Cmd_SetSetpoint, this, _1, _2, _3));
552 			RegisterCommandCode("addsetpointtimer", boost::bind(&CWebServer::Cmd_AddSetpointTimer, this, _1, _2, _3));
553 			RegisterCommandCode("updatesetpointtimer", boost::bind(&CWebServer::Cmd_UpdateSetpointTimer, this, _1, _2, _3));
554 			RegisterCommandCode("deletesetpointtimer", boost::bind(&CWebServer::Cmd_DeleteSetpointTimer, this, _1, _2, _3));
555 			RegisterCommandCode("enablesetpointtimer", boost::bind(&CWebServer::Cmd_EnableSetpointTimer, this, _1, _2, _3));
556 			RegisterCommandCode("disablesetpointtimer", boost::bind(&CWebServer::Cmd_DisableSetpointTimer, this, _1, _2, _3));
557 			RegisterCommandCode("clearsetpointtimers", boost::bind(&CWebServer::Cmd_ClearSetpointTimers, this, _1, _2, _3));
558 
559 			RegisterCommandCode("serial_devices", boost::bind(&CWebServer::Cmd_GetSerialDevices, this, _1, _2, _3));
560 			RegisterCommandCode("devices_list", boost::bind(&CWebServer::Cmd_GetDevicesList, this, _1, _2, _3));
561 			RegisterCommandCode("devices_list_onoff", boost::bind(&CWebServer::Cmd_GetDevicesListOnOff, this, _1, _2, _3));
562 
563 			RegisterCommandCode("registerhue", boost::bind(&CWebServer::Cmd_PhilipsHueRegister, this, _1, _2, _3));
564 
565 			RegisterCommandCode("getcustomiconset", boost::bind(&CWebServer::Cmd_GetCustomIconSet, this, _1, _2, _3));
566 			RegisterCommandCode("deletecustomicon", boost::bind(&CWebServer::Cmd_DeleteCustomIcon, this, _1, _2, _3));
567 			RegisterCommandCode("updatecustomicon", boost::bind(&CWebServer::Cmd_UpdateCustomIcon, this, _1, _2, _3));
568 
569 			RegisterCommandCode("renamedevice", boost::bind(&CWebServer::Cmd_RenameDevice, this, _1, _2, _3));
570 			RegisterCommandCode("setunused", boost::bind(&CWebServer::Cmd_SetUnused, this, _1, _2, _3));
571 
572 			RegisterCommandCode("addlogmessage", boost::bind(&CWebServer::Cmd_AddLogMessage, this, _1, _2, _3));
573 			RegisterCommandCode("clearshortlog", boost::bind(&CWebServer::Cmd_ClearShortLog, this, _1, _2, _3));
574 			RegisterCommandCode("vacuumdatabase", boost::bind(&CWebServer::Cmd_VacuumDatabase, this, _1, _2, _3));
575 
576 			RegisterCommandCode("addmobiledevice", boost::bind(&CWebServer::Cmd_AddMobileDevice, this, _1, _2, _3));
577 			RegisterCommandCode("updatemobiledevice", boost::bind(&CWebServer::Cmd_UpdateMobileDevice, this, _1, _2, _3));
578 			RegisterCommandCode("deletemobiledevice", boost::bind(&CWebServer::Cmd_DeleteMobileDevice, this, _1, _2, _3));
579 
580 			RegisterCommandCode("addyeelight", boost::bind(&CWebServer::Cmd_AddYeeLight, this, _1, _2, _3));
581 
582 			RegisterCommandCode("addArilux", boost::bind(&CWebServer::Cmd_AddArilux, this, _1, _2, _3));
583 
584 			RegisterRType("graph", boost::bind(&CWebServer::RType_HandleGraph, this, _1, _2, _3));
585 			RegisterRType("lightlog", boost::bind(&CWebServer::RType_LightLog, this, _1, _2, _3));
586 			RegisterRType("textlog", boost::bind(&CWebServer::RType_TextLog, this, _1, _2, _3));
587 			RegisterRType("scenelog", boost::bind(&CWebServer::RType_SceneLog, this, _1, _2, _3));
588 			RegisterRType("settings", boost::bind(&CWebServer::RType_Settings, this, _1, _2, _3));
589 			RegisterRType("events", boost::bind(&CWebServer::RType_Events, this, _1, _2, _3));
590 
591 			RegisterRType("hardware", boost::bind(&CWebServer::RType_Hardware, this, _1, _2, _3));
592 			RegisterRType("devices", boost::bind(&CWebServer::RType_Devices, this, _1, _2, _3));
593 			RegisterRType("deletedevice", boost::bind(&CWebServer::RType_DeleteDevice, this, _1, _2, _3));
594 			RegisterRType("cameras", boost::bind(&CWebServer::RType_Cameras, this, _1, _2, _3));
595 			RegisterRType("cameras_user", boost::bind(&CWebServer::RType_CamerasUser, this, _1, _2, _3));
596 			RegisterRType("users", boost::bind(&CWebServer::RType_Users, this, _1, _2, _3));
597 			RegisterRType("mobiles", boost::bind(&CWebServer::RType_Mobiles, this, _1, _2, _3));
598 
599 			RegisterRType("timers", boost::bind(&CWebServer::RType_Timers, this, _1, _2, _3));
600 			RegisterRType("scenetimers", boost::bind(&CWebServer::RType_SceneTimers, this, _1, _2, _3));
601 			RegisterRType("setpointtimers", boost::bind(&CWebServer::RType_SetpointTimers, this, _1, _2, _3));
602 
603 			RegisterRType("gettransfers", boost::bind(&CWebServer::RType_GetTransfers, this, _1, _2, _3));
604 			RegisterRType("transferdevice", boost::bind(&CWebServer::RType_TransferDevice, this, _1, _2, _3));
605 			RegisterRType("notifications", boost::bind(&CWebServer::RType_Notifications, this, _1, _2, _3));
606 			RegisterRType("schedules", boost::bind(&CWebServer::RType_Schedules, this, _1, _2, _3));
607 			RegisterRType("getshareduserdevices", boost::bind(&CWebServer::RType_GetSharedUserDevices, this, _1, _2, _3));
608 			RegisterRType("setshareduserdevices", boost::bind(&CWebServer::RType_SetSharedUserDevices, this, _1, _2, _3));
609 			RegisterRType("setused", boost::bind(&CWebServer::RType_SetUsed, this, _1, _2, _3));
610 			RegisterRType("scenes", boost::bind(&CWebServer::RType_Scenes, this, _1, _2, _3));
611 			RegisterRType("addscene", boost::bind(&CWebServer::RType_AddScene, this, _1, _2, _3));
612 			RegisterRType("deletescene", boost::bind(&CWebServer::RType_DeleteScene, this, _1, _2, _3));
613 			RegisterRType("updatescene", boost::bind(&CWebServer::RType_UpdateScene, this, _1, _2, _3));
614 			RegisterRType("createvirtualsensor", boost::bind(&CWebServer::RType_CreateMappedSensor, this, _1, _2, _3));
615 			RegisterRType("createdevice", boost::bind(&CWebServer::RType_CreateDevice, this, _1, _2, _3));
616 
617 			RegisterRType("createevohomesensor", boost::bind(&CWebServer::RType_CreateEvohomeSensor, this, _1, _2, _3));
618 			RegisterRType("bindevohome", boost::bind(&CWebServer::RType_BindEvohome, this, _1, _2, _3));
619 			RegisterRType("createrflinkdevice", boost::bind(&CWebServer::RType_CreateRFLinkDevice, this, _1, _2, _3));
620 
621 			RegisterRType("custom_light_icons", boost::bind(&CWebServer::RType_CustomLightIcons, this, _1, _2, _3));
622 			RegisterRType("plans", boost::bind(&CWebServer::RType_Plans, this, _1, _2, _3));
623 			RegisterRType("floorplans", boost::bind(&CWebServer::RType_FloorPlans, this, _1, _2, _3));
624 #ifdef WITH_OPENZWAVE
625 			//ZWave
626 			RegisterCommandCode("updatezwavenode", boost::bind(&CWebServer::Cmd_ZWaveUpdateNode, this, _1, _2, _3));
627 			RegisterCommandCode("deletezwavenode", boost::bind(&CWebServer::Cmd_ZWaveDeleteNode, this, _1, _2, _3));
628 			RegisterCommandCode("zwaveinclude", boost::bind(&CWebServer::Cmd_ZWaveInclude, this, _1, _2, _3));
629 			RegisterCommandCode("zwaveexclude", boost::bind(&CWebServer::Cmd_ZWaveExclude, this, _1, _2, _3));
630 
631 			RegisterCommandCode("zwaveisnodeincluded", boost::bind(&CWebServer::Cmd_ZWaveIsNodeIncluded, this, _1, _2, _3));
632 			RegisterCommandCode("zwaveisnodeexcluded", boost::bind(&CWebServer::Cmd_ZWaveIsNodeExcluded, this, _1, _2, _3));
633 
634 			RegisterCommandCode("zwavesoftreset", boost::bind(&CWebServer::Cmd_ZWaveSoftReset, this, _1, _2, _3));
635 			RegisterCommandCode("zwavehardreset", boost::bind(&CWebServer::Cmd_ZWaveHardReset, this, _1, _2, _3));
636 			RegisterCommandCode("zwavenetworkheal", boost::bind(&CWebServer::Cmd_ZWaveNetworkHeal, this, _1, _2, _3));
637 			RegisterCommandCode("zwavenodeheal", boost::bind(&CWebServer::Cmd_ZWaveNodeHeal, this, _1, _2, _3));
638 			RegisterCommandCode("zwavenetworkinfo", boost::bind(&CWebServer::Cmd_ZWaveNetworkInfo, this, _1, _2, _3));
639 			RegisterCommandCode("zwaveremovegroupnode", boost::bind(&CWebServer::Cmd_ZWaveRemoveGroupNode, this, _1, _2, _3));
640 			RegisterCommandCode("zwaveaddgroupnode", boost::bind(&CWebServer::Cmd_ZWaveAddGroupNode, this, _1, _2, _3));
641 			RegisterCommandCode("zwavegroupinfo", boost::bind(&CWebServer::Cmd_ZWaveGroupInfo, this, _1, _2, _3));
642 			RegisterCommandCode("zwavecancel", boost::bind(&CWebServer::Cmd_ZWaveCancel, this, _1, _2, _3));
643 			RegisterCommandCode("applyzwavenodeconfig", boost::bind(&CWebServer::Cmd_ApplyZWaveNodeConfig, this, _1, _2, _3));
644 			RegisterCommandCode("requestzwavenodeconfig", boost::bind(&CWebServer::Cmd_ZWaveRequestNodeConfig, this, _1, _2, _3));
645 			RegisterCommandCode("requestzwavenodeinfo", boost::bind(&CWebServer::Cmd_ZWaveRequestNodeInfo, this, _1, _2, _3));
646 			RegisterCommandCode("zwavestatecheck", boost::bind(&CWebServer::Cmd_ZWaveStateCheck, this, _1, _2, _3));
647 			RegisterCommandCode("zwavereceiveconfigurationfromothercontroller", boost::bind(&CWebServer::Cmd_ZWaveReceiveConfigurationFromOtherController, this, _1, _2, _3));
648 			RegisterCommandCode("zwavesendconfigurationtosecondcontroller", boost::bind(&CWebServer::Cmd_ZWaveSendConfigurationToSecondaryController, this, _1, _2, _3));
649 			RegisterCommandCode("zwavetransferprimaryrole", boost::bind(&CWebServer::Cmd_ZWaveTransferPrimaryRole, this, _1, _2, _3));
650 			RegisterCommandCode("zwavestartusercodeenrollmentmode", boost::bind(&CWebServer::Cmd_ZWaveSetUserCodeEnrollmentMode, this, _1, _2, _3));
651 			RegisterCommandCode("zwavegetusercodes", boost::bind(&CWebServer::Cmd_ZWaveGetNodeUserCodes, this, _1, _2, _3));
652 			RegisterCommandCode("zwaveremoveusercode", boost::bind(&CWebServer::Cmd_ZWaveRemoveUserCode, this, _1, _2, _3));
653 			RegisterCommandCode("zwavegetbatterylevels", boost::bind(&CWebServer::Cmd_ZWaveGetBatteryLevels, this, _1, _2, _3));
654 
655 			m_pWebEm->RegisterPageCode("/zwavegetconfig.php", boost::bind(&CWebServer::ZWaveGetConfigFile, this, _1, _2, _3));
656 
657 			m_pWebEm->RegisterPageCode("/ozwcp/poll.xml", boost::bind(&CWebServer::ZWaveCPPollXml, this, _1, _2, _3));
658 			m_pWebEm->RegisterPageCode("/ozwcp/cp.html", boost::bind(&CWebServer::ZWaveCPIndex, this, _1, _2, _3));
659 			m_pWebEm->RegisterPageCode("/ozwcp/confparmpost.html", boost::bind(&CWebServer::ZWaveCPNodeGetConf, this, _1, _2, _3));
660 			m_pWebEm->RegisterPageCode("/ozwcp/refreshpost.html", boost::bind(&CWebServer::ZWaveCPNodeGetValues, this, _1, _2, _3));
661 			m_pWebEm->RegisterPageCode("/ozwcp/valuepost.html", boost::bind(&CWebServer::ZWaveCPNodeSetValue, this, _1, _2, _3));
662 			m_pWebEm->RegisterPageCode("/ozwcp/buttonpost.html", boost::bind(&CWebServer::ZWaveCPNodeSetButton, this, _1, _2, _3));
663 			m_pWebEm->RegisterPageCode("/ozwcp/admpost.html", boost::bind(&CWebServer::ZWaveCPAdminCommand, this, _1, _2, _3));
664 			m_pWebEm->RegisterPageCode("/ozwcp/nodepost.html", boost::bind(&CWebServer::ZWaveCPNodeChange, this, _1, _2, _3));
665 			m_pWebEm->RegisterPageCode("/ozwcp/thpost.html", boost::bind(&CWebServer::ZWaveCPTestHeal, this, _1, _2, _3));
666 			m_pWebEm->RegisterPageCode("/ozwcp/topopost.html", boost::bind(&CWebServer::ZWaveCPGetTopo, this, _1, _2, _3));
667 			m_pWebEm->RegisterPageCode("/ozwcp/statpost.html", boost::bind(&CWebServer::ZWaveCPGetStats, this, _1, _2, _3));
668 			m_pWebEm->RegisterPageCode("/ozwcp/grouppost.html", boost::bind(&CWebServer::ZWaveCPSetGroup, this, _1, _2, _3));
669 			//
670 			//pollpost.html
671 			RegisterRType("openzwavenodes", boost::bind(&CWebServer::RType_OpenZWaveNodes, this, _1, _2, _3));
672 #endif
673 			RegisterCommandCode("tellstickApplySettings", boost::bind(&CWebServer::Cmd_TellstickApplySettings, this, _1, _2, _3));
674 
675 			m_pWebEm->RegisterWhitelistURLString("/html5.appcache");
676 			m_pWebEm->RegisterWhitelistURLString("/images/floorplans/plan");
677 
678 			//Start normal worker thread
679 			m_bDoStop = false;
680 			m_thread = std::make_shared<std::thread>(&CWebServer::Do_Work, this);
681 			std::string server_name = "WebServer_" + settings.listening_port;
682 			SetThreadName(m_thread->native_handle(), server_name.c_str());
683 			return (m_thread != nullptr);
684 		}
685 
StopServer()686 		void CWebServer::StopServer()
687 		{
688 			m_bDoStop = true;
689 			try
690 			{
691 				if (m_pWebEm == NULL)
692 					return;
693 				m_pWebEm->Stop();
694 				if (m_thread) {
695 					m_thread->join();
696 					m_thread.reset();
697 				}
698 				delete m_pWebEm;
699 				m_pWebEm = NULL;
700 			}
701 			catch (...)
702 			{
703 
704 			}
705 		}
706 
SetWebCompressionMode(const _eWebCompressionMode gzmode)707 		void CWebServer::SetWebCompressionMode(const _eWebCompressionMode gzmode)
708 		{
709 			if (m_pWebEm == NULL)
710 				return;
711 			m_pWebEm->SetWebCompressionMode(gzmode);
712 		}
713 
SetAuthenticationMethod(const _eAuthenticationMethod amethod)714 		void CWebServer::SetAuthenticationMethod(const _eAuthenticationMethod amethod)
715 		{
716 			if (m_pWebEm == NULL)
717 				return;
718 			m_pWebEm->SetAuthenticationMethod(amethod);
719 		}
720 
SetWebTheme(const std::string & themename)721 		void CWebServer::SetWebTheme(const std::string &themename)
722 		{
723 			if (m_pWebEm == NULL)
724 				return;
725 			m_pWebEm->SetWebTheme(themename);
726 		}
727 
SetWebRoot(const std::string & webRoot)728 		void CWebServer::SetWebRoot(const std::string &webRoot)
729 		{
730 			if (m_pWebEm == NULL)
731 				return;
732 			m_pWebEm->SetWebRoot(webRoot);
733 		}
734 
RegisterCommandCode(const char * idname,webserver_response_function ResponseFunction,bool bypassAuthentication)735 		void CWebServer::RegisterCommandCode(const char* idname, webserver_response_function ResponseFunction, bool bypassAuthentication)
736 		{
737 			m_webcommands.insert(std::pair<std::string, webserver_response_function >(std::string(idname), ResponseFunction));
738 			if (bypassAuthentication)
739 			{
740 				m_pWebEm->RegisterWhitelistCommandsString(idname);
741 			}
742 		}
743 
RegisterRType(const char * idname,webserver_response_function ResponseFunction)744 		void CWebServer::RegisterRType(const char* idname, webserver_response_function ResponseFunction)
745 		{
746 			m_webrtypes.insert(std::pair<std::string, webserver_response_function >(std::string(idname), ResponseFunction));
747 		}
748 
HandleRType(const std::string & rtype,WebEmSession & session,const request & req,Json::Value & root)749 		void CWebServer::HandleRType(const std::string &rtype, WebEmSession & session, const request& req, Json::Value &root)
750 		{
751 			std::map < std::string, webserver_response_function >::iterator pf = m_webrtypes.find(rtype);
752 			if (pf != m_webrtypes.end())
753 			{
754 				pf->second(session, req, root);
755 			}
756 		}
757 
GetAppCache(WebEmSession & session,const request & req,reply & rep)758 		void CWebServer::GetAppCache(WebEmSession & session, const request& req, reply & rep)
759 		{
760 			std::string response = "";
761 			if (g_bDontCacheWWW)
762 			{
763 				return;
764 			}
765 			//Return the appcache file (dynamically generated)
766 			std::string sLine;
767 			std::string filename = szWWWFolder + "/html5.appcache";
768 
769 
770 			std::string sWebTheme = "default";
771 			m_sql.GetPreferencesVar("WebTheme", sWebTheme);
772 
773 			//Get Dynamic Theme Files
774 			std::map<std::string, int> _ThemeFiles;
775 			GetDirFilesRecursive(szWWWFolder + "/styles/" + sWebTheme + "/", _ThemeFiles);
776 
777 			//Get Dynamic Floorplan Images from database
778 			std::map<std::string, int> _FloorplanFiles;
779 			std::vector<std::vector<std::string> > result;
780 			result = m_sql.safe_query("SELECT ID FROM Floorplans ORDER BY [Order]");
781 			if (!result.empty())
782 			{
783 				for (const auto & itt : result)
784 				{
785 					std::vector<std::string> sd = itt;
786 					std::string ImageURL = "images/floorplans/plan?idx=" + sd[0];
787 					_FloorplanFiles[ImageURL] = 1;
788 				}
789 			}
790 
791 			std::ifstream is(filename.c_str());
792 			if (is)
793 			{
794 				while (!is.eof())
795 				{
796 					getline(is, sLine);
797 					if (!sLine.empty())
798 					{
799 						if (sLine.find("#BuildHash") != std::string::npos)
800 						{
801 							stdreplace(sLine, "#BuildHash", szAppHash);
802 						}
803 						else if (sLine.find("#ThemeFiles") != std::string::npos)
804 						{
805 							response += "#Theme=" + sWebTheme + '\n';
806 							//Add all theme files
807 							for (const auto & itt : _ThemeFiles)
808 							{
809 								std::string tfname = itt.first.substr(szWWWFolder.size() + 1);
810 								stdreplace(tfname, "styles/" + sWebTheme, "acttheme");
811 								response += tfname + '\n';
812 							}
813 							continue;
814 						}
815 						else if (sLine.find("#Floorplans") != std::string::npos)
816 						{
817 							//Add all floorplans
818 							for (const auto & itt : _FloorplanFiles)
819 							{
820 								std::string tfname = itt.first;
821 								response += tfname + '\n';
822 							}
823 							continue;
824 						}
825 						else if (sLine.find("#SwitchIcons") != std::string::npos)
826 						{
827 							//Add database switch icons
828 							for (const auto & itt : m_custom_light_icons)
829 							{
830 								if (itt.idx >= 100)
831 								{
832 									std::string IconFile16 = itt.RootFile + ".png";
833 									std::string IconFile48On = itt.RootFile + "48_On.png";
834 									std::string IconFile48Off = itt.RootFile + "48_Off.png";
835 
836 									response += "images/" + CURLEncode::URLEncode(IconFile16) + '\n';
837 									response += "images/" + CURLEncode::URLEncode(IconFile48On) + '\n';
838 									response += "images/" + CURLEncode::URLEncode(IconFile48Off) + '\n';
839 								}
840 							}
841 						}
842 					}
843 					response += sLine + '\n';
844 				}
845 			}
846 			reply::set_content(&rep, response);
847 		}
848 
GetJSonPage(WebEmSession & session,const request & req,reply & rep)849 		void CWebServer::GetJSonPage(WebEmSession & session, const request& req, reply & rep)
850 		{
851 			Json::Value root;
852 			root["status"] = "ERR";
853 
854 			std::string rtype = request::findValue(&req, "type");
855 			if (rtype == "command")
856 			{
857 				std::string cparam = request::findValue(&req, "param");
858 				if (cparam.empty())
859 				{
860 					cparam = request::findValue(&req, "dparam");
861 					if (cparam.empty())
862 					{
863 						goto exitjson;
864 					}
865 				}
866 				if (cparam == "dologout")
867 				{
868 					session.forcelogin = true;
869 					root["status"] = "OK";
870 					root["title"] = "Logout";
871 					goto exitjson;
872 
873 				}
874 				_log.Debug(DEBUG_WEBSERVER, "WEBS GetJSon :%s :%s ", cparam.c_str(), req.uri.c_str());
875 				HandleCommand(cparam, session, req, root);
876 			} //(rtype=="command")
877 			else {
878 				HandleRType(rtype, session, req, root);
879 			}
880 		exitjson:
881 			std::string jcallback = request::findValue(&req, "jsoncallback");
882 			if (jcallback.size() == 0) {
883 				reply::set_content(&rep, root.toStyledString());
884 				return;
885 			}
886 			reply::set_content(&rep, "var data=" + root.toStyledString() + '\n' + jcallback + "(data);");
887 		}
888 
Cmd_GetLanguage(WebEmSession & session,const request & req,Json::Value & root)889 		void CWebServer::Cmd_GetLanguage(WebEmSession & session, const request& req, Json::Value &root)
890 		{
891 			std::string sValue;
892 			if (m_sql.GetPreferencesVar("Language", sValue))
893 			{
894 				root["status"] = "OK";
895 				root["title"] = "GetLanguage";
896 				root["language"] = sValue;
897 			}
898 		}
899 
Cmd_GetThemes(WebEmSession & session,const request & req,Json::Value & root)900 		void CWebServer::Cmd_GetThemes(WebEmSession & session, const request& req, Json::Value &root)
901 		{
902 			root["status"] = "OK";
903 			root["title"] = "GetThemes";
904 			m_mainworker.GetAvailableWebThemes();
905 			int ii = 0;
906 			for (const auto & itt : m_mainworker.m_webthemes)
907 			{
908 				root["result"][ii]["theme"] = itt;
909 				ii++;
910 			}
911 		}
912 
Cmd_GetTitle(WebEmSession & session,const request & req,Json::Value & root)913 		void CWebServer::Cmd_GetTitle(WebEmSession & session, const request& req, Json::Value &root)
914 		{
915 			std::string sValue;
916 			root["status"] = "OK";
917 			root["title"] = "GetTitle";
918 			if (m_sql.GetPreferencesVar("Title", sValue))
919 				root["Title"] = sValue;
920 			else
921 				root["Title"] = "Domoticz";
922 		}
923 
924 		//PostSettings
PostLoginCheck(WebEmSession & session,const request & req,reply & rep)925 		void CWebServer::PostLoginCheck(WebEmSession& session, const request& req, reply& rep)
926 		{
927 			Json::Value root;
928 			Cmd_LoginCheck(session, req, root);
929 
930 			std::string jcallback = request::findValue(&req, "jsoncallback");
931 			if (jcallback.size() == 0) {
932 				reply::set_content(&rep, root.toStyledString());
933 				return;
934 			}
935 			reply::set_content(&rep, "var data=" + root.toStyledString() + '\n' + jcallback + "(data);");
936 		}
937 
Cmd_LoginCheck(WebEmSession & session,const request & req,Json::Value & root)938 		void CWebServer::Cmd_LoginCheck(WebEmSession & session, const request& req, Json::Value &root)
939 		{
940 			std::string tmpusrname = request::findValue(&req, "username");
941 			std::string tmpusrpass = request::findValue(&req, "password");
942 			if (
943 				(tmpusrname.empty()) ||
944 				(tmpusrpass.empty())
945 				)
946 				return;
947 
948 			std::string rememberme = request::findValue(&req, "rememberme");
949 
950 			std::string usrname;
951 			std::string usrpass;
952 			if (request_handler::url_decode(tmpusrname, usrname))
953 			{
954 				if (request_handler::url_decode(tmpusrpass, usrpass))
955 				{
956 					usrname = base64_decode(usrname);
957 					int iUser = FindUser(usrname.c_str());
958 					if (iUser == -1) {
959 						// log brute force attack
960 						_log.Log(LOG_ERROR, "Failed login attempt from %s for user '%s' !", session.remote_host.c_str(), usrname.c_str());
961 						return;
962 					}
963 					if (m_users[iUser].Password != usrpass) {
964 						// log brute force attack
965 						_log.Log(LOG_ERROR, "Failed login attempt from %s for '%s' !", session.remote_host.c_str(), m_users[iUser].Username.c_str());
966 						return;
967 					}
968 					_log.Log(LOG_STATUS, "Login successful from %s for user '%s'", session.remote_host.c_str(), m_users[iUser].Username.c_str());
969 					root["status"] = "OK";
970 					root["version"] = szAppVersion;
971 					root["title"] = "logincheck";
972 					session.isnew = true;
973 					session.username = m_users[iUser].Username;
974 					session.rights = m_users[iUser].userrights;
975 					session.rememberme = (rememberme == "true");
976 					root["user"] = session.username;
977 					root["rights"] = session.rights;
978 				}
979 			}
980 		}
981 
Cmd_GetHardwareTypes(WebEmSession & session,const request & req,Json::Value & root)982 		void CWebServer::Cmd_GetHardwareTypes(WebEmSession & session, const request& req, Json::Value &root)
983 		{
984 			if (session.rights != 2)
985 			{
986 				session.reply_status = reply::forbidden;
987 				return; //Only admin user allowed
988 			}
989 
990 			root["status"] = "OK";
991 			root["title"] = "GetHardwareTypes";
992 			std::map<std::string, int> _htypes;
993 			for (int ii = 0; ii < HTYPE_END; ii++)
994 			{
995 				bool bDoAdd = true;
996 #ifndef _DEBUG
997 #ifdef WIN32
998 				if (
999 					(ii == HTYPE_RaspberryBMP085) ||
1000 					(ii == HTYPE_RaspberryHTU21D) ||
1001 					(ii == HTYPE_RaspberryTSL2561) ||
1002 					(ii == HTYPE_RaspberryPCF8574) ||
1003 					(ii == HTYPE_RaspberryBME280) ||
1004 					(ii == HTYPE_RaspberryMCP23017)
1005 					)
1006 				{
1007 					bDoAdd = false;
1008 				}
1009 				else
1010 				{
1011 #ifndef WITH_LIBUSB
1012 					if (
1013 						(ii == HTYPE_VOLCRAFTCO20) ||
1014 						(ii == HTYPE_TE923)
1015 						)
1016 					{
1017 						bDoAdd = false;
1018 					}
1019 #endif
1020 
1021 		}
1022 #endif
1023 #endif
1024 #ifndef WITH_OPENZWAVE
1025 				if (ii == HTYPE_OpenZWave)
1026 					bDoAdd = false;
1027 #endif
1028 #ifndef WITH_GPIO
1029 				if (ii == HTYPE_RaspberryGPIO)
1030 				{
1031 					bDoAdd = false;
1032 				}
1033 
1034 				if (ii == HTYPE_SysfsGpio)
1035 				{
1036 					bDoAdd = false;
1037 				}
1038 #endif
1039 				if (ii == HTYPE_PythonPlugin)
1040 					bDoAdd = false;
1041 				if (bDoAdd)
1042 					_htypes[Hardware_Type_Desc(ii)] = ii;
1043 	}
1044 			//return a sorted hardware list
1045 			int ii = 0;
1046 			for (const auto & itt : _htypes)
1047 			{
1048 				root["result"][ii]["idx"] = itt.second;
1049 				root["result"][ii]["name"] = itt.first;
1050 				ii++;
1051 			}
1052 
1053 #ifdef ENABLE_PYTHON
1054 			// Append Plugin list as well
1055 			PluginList(root["result"]);
1056 #endif
1057 }
1058 
Cmd_AddHardware(WebEmSession & session,const request & req,Json::Value & root)1059 		void CWebServer::Cmd_AddHardware(WebEmSession & session, const request& req, Json::Value &root)
1060 		{
1061 			if (session.rights != 2)
1062 			{
1063 				session.reply_status = reply::forbidden;
1064 				return; //Only admin user allowed
1065 			}
1066 
1067 			std::string name = HTMLSanitizer::Sanitize(CURLEncode::URLDecode(request::findValue(&req, "name")));
1068 			std::string senabled = request::findValue(&req, "enabled");
1069 			std::string shtype = request::findValue(&req, "htype");
1070 			std::string address = HTMLSanitizer::Sanitize(request::findValue(&req, "address"));
1071 			std::string sport = request::findValue(&req, "port");
1072 			std::string username = HTMLSanitizer::Sanitize(CURLEncode::URLDecode(request::findValue(&req, "username")));
1073 			std::string password = HTMLSanitizer::Sanitize(CURLEncode::URLDecode(request::findValue(&req, "password")));
1074 			std::string extra = CURLEncode::URLDecode(request::findValue(&req, "extra"));
1075 			std::string sdatatimeout = request::findValue(&req, "datatimeout");
1076 			if (
1077 				(name.empty()) ||
1078 				(senabled.empty()) ||
1079 				(shtype.empty())
1080 				)
1081 				return;
1082 			_eHardwareTypes htype = (_eHardwareTypes)atoi(shtype.c_str());
1083 
1084 			int iDataTimeout = atoi(sdatatimeout.c_str());
1085 			int mode1 = 0;
1086 			int mode2 = 0;
1087 			int mode3 = 0;
1088 			int mode4 = 0;
1089 			int mode5 = 0;
1090 			int mode6 = 0;
1091 			int port = atoi(sport.c_str());
1092 			std::string mode1Str = request::findValue(&req, "Mode1");
1093 			if (!mode1Str.empty()) {
1094 				mode1 = atoi(mode1Str.c_str());
1095 			}
1096 			std::string mode2Str = request::findValue(&req, "Mode2");
1097 			if (!mode2Str.empty()) {
1098 				mode2 = atoi(mode2Str.c_str());
1099 			}
1100 			std::string mode3Str = request::findValue(&req, "Mode3");
1101 			if (!mode3Str.empty()) {
1102 				mode3 = atoi(mode3Str.c_str());
1103 			}
1104 			std::string mode4Str = request::findValue(&req, "Mode4");
1105 			if (!mode4Str.empty()) {
1106 				mode4 = atoi(mode4Str.c_str());
1107 			}
1108 			std::string mode5Str = request::findValue(&req, "Mode5");
1109 			if (!mode5Str.empty()) {
1110 				mode5 = atoi(mode5Str.c_str());
1111 			}
1112 			std::string mode6Str = request::findValue(&req, "Mode6");
1113 			if (!mode6Str.empty()) {
1114 				mode6 = atoi(mode6Str.c_str());
1115 			}
1116 
1117 			if (IsSerialDevice(htype))
1118 			{
1119 				//USB/System
1120 				if (sport.empty())
1121 					return; //need to have a serial port
1122 
1123 				if (htype == HTYPE_TeleinfoMeter) {
1124 					// Teleinfo always has decimals. Chances to have a P1 and a Teleinfo device on the same
1125 					// Domoticz instance are very low as both are national standards (NL and FR)
1126 					m_sql.UpdatePreferencesVar("SmartMeterType", 0);
1127 				}
1128 			}
1129 			else if (IsNetworkDevice(htype))
1130 			{
1131 				//Lan
1132 				if (address.empty() || port == 0)
1133 					return;
1134 
1135 				if (htype == HTYPE_MySensorsMQTT || htype == HTYPE_MQTT) {
1136 					std::string modeqStr = request::findValue(&req, "mode1");
1137 					if (!modeqStr.empty()) {
1138 						mode1 = atoi(modeqStr.c_str());
1139 					}
1140 				}
1141 
1142 				if (htype == HTYPE_ECODEVICES) {
1143 					// EcoDevices always have decimals. Chances to have a P1 and a EcoDevice/Teleinfo device on the same
1144 					// Domoticz instance are very low as both are national standards (NL and FR)
1145 					m_sql.UpdatePreferencesVar("SmartMeterType", 0);
1146 				}
1147 			}
1148 			else if (htype == HTYPE_DomoticzInternal) {
1149 				// DomoticzInternal cannot be added manually
1150 				return;
1151 			}
1152 			else if (htype == HTYPE_Domoticz) {
1153 				//Remote Domoticz
1154 				if (address.empty() || port == 0)
1155 					return;
1156 			}
1157 			else if (htype == HTYPE_TE923) {
1158 				//all fine here!
1159 			}
1160 			else if (htype == HTYPE_VOLCRAFTCO20) {
1161 				//all fine here!
1162 			}
1163 			else if (htype == HTYPE_System) {
1164 				//There should be only one
1165 				std::vector<std::vector<std::string> > result;
1166 				result = m_sql.safe_query("SELECT ID FROM Hardware WHERE (Type==%d)", HTYPE_System);
1167 				if (!result.empty())
1168 					return;
1169 			}
1170 			else if (htype == HTYPE_1WIRE) {
1171 				//all fine here!
1172 			}
1173 			else if (htype == HTYPE_Rtl433) {
1174 				//all fine here!
1175 			}
1176 			else if (htype == HTYPE_Pinger) {
1177 				//all fine here!
1178 			}
1179 			else if (htype == HTYPE_Kodi) {
1180 				//all fine here!
1181 			}
1182 			else if (htype == HTYPE_PanasonicTV) {
1183 				// all fine here!
1184 			}
1185 			else if (htype == HTYPE_LogitechMediaServer) {
1186 				//all fine here!
1187 			}
1188 			else if (htype == HTYPE_RaspberryBMP085) {
1189 				//all fine here!
1190 			}
1191 			else if (htype == HTYPE_RaspberryHTU21D) {
1192 				//all fine here!
1193 			}
1194 			else if (htype == HTYPE_RaspberryTSL2561) {
1195 				//all fine here!
1196 			}
1197 			else if (htype == HTYPE_RaspberryBME280) {
1198 				//all fine here!
1199 			}
1200 			else if (htype == HTYPE_RaspberryMCP23017) {
1201 				//all fine here!
1202 			}
1203 			else if (htype == HTYPE_Dummy) {
1204 				//all fine here!
1205 			}
1206 			else if (htype == HTYPE_Tellstick) {
1207 				//all fine here!
1208 			}
1209 			else if (htype == HTYPE_EVOHOME_SCRIPT || htype == HTYPE_EVOHOME_SERIAL || htype == HTYPE_EVOHOME_WEB || htype == HTYPE_EVOHOME_TCP) {
1210 				//all fine here!
1211 			}
1212 			else if (htype == HTYPE_PiFace) {
1213 				//all fine here!
1214 			}
1215 			else if (htype == HTYPE_HTTPPOLLER) {
1216 				//all fine here!
1217 			}
1218 			else if (htype == HTYPE_BleBox) {
1219 				//all fine here!
1220 			}
1221 			else if (htype == HTYPE_HEOS) {
1222 				//all fine here!
1223 			}
1224 			else if (htype == HTYPE_Yeelight) {
1225 				//all fine here!
1226 			}
1227 			else if (htype == HTYPE_XiaomiGateway) {
1228 				//all fine here!
1229 			}
1230 			else if (htype == HTYPE_Arilux) {
1231 				//all fine here!
1232 			}
1233 			else if (htype == HTYPE_USBtinGateway) {
1234 				//All fine here
1235 			}
1236 			else if (htype == HTYPE_BuienRadar) {
1237 				//All fine here
1238 			}
1239 			else if (
1240 				(htype == HTYPE_Wunderground) ||
1241 				(htype == HTYPE_DarkSky) ||
1242 				(htype == HTYPE_AccuWeather) ||
1243 				(htype == HTYPE_OpenWeatherMap) ||
1244 				(htype == HTYPE_ICYTHERMOSTAT) ||
1245 				(htype == HTYPE_TOONTHERMOSTAT) ||
1246 				(htype == HTYPE_AtagOne) ||
1247 				(htype == HTYPE_PVOUTPUT_INPUT) ||
1248 				(htype == HTYPE_NEST) ||
1249 				(htype == HTYPE_ANNATHERMOSTAT) ||
1250 				(htype == HTYPE_THERMOSMART) ||
1251 				(htype == HTYPE_Tado) ||
1252 				(htype == HTYPE_Tesla) ||
1253 				(htype == HTYPE_Netatmo)
1254 				)
1255 			{
1256 				if (
1257 					(username.empty()) ||
1258 					(password.empty())
1259 					)
1260 					return;
1261 			}
1262 			else if (htype == HTYPE_SolarEdgeAPI)
1263 			{
1264 				if (
1265 					(username.empty())
1266 					)
1267 					return;
1268 			}
1269 			else if (htype == HTYPE_Nest_OAuthAPI) {
1270 				if (
1271 					(username == "") &&
1272 					(extra == "||")
1273 					)
1274 					return;
1275 			}
1276 			else if (htype == HTYPE_SBFSpot) {
1277 				if (username.empty())
1278 					return;
1279 			}
1280 			else if (htype == HTYPE_HARMONY_HUB) {
1281 				if (
1282 					(address.empty() || port == 0)
1283 					)
1284 					return;
1285 			}
1286 			else if (htype == HTYPE_Philips_Hue) {
1287 				if (
1288 					(username.empty()) ||
1289 					(address.empty() || port == 0)
1290 					)
1291 					return;
1292 				if (port == 0)
1293 					port = 80;
1294 			}
1295 			else if (htype == HTYPE_WINDDELEN) {
1296 				std::string mill_id = request::findValue(&req, "Mode1");
1297 				if (
1298 					(mill_id.empty()) ||
1299 					(sport.empty())
1300 					)
1301 
1302 					return;
1303 				mode1 = atoi(mill_id.c_str());
1304 			}
1305 			else if (htype == HTYPE_Honeywell) {
1306 				//all fine here!
1307 			}
1308 			else if (htype == HTYPE_RaspberryGPIO) {
1309 				//all fine here!
1310 			}
1311 			else if (htype == HTYPE_SysfsGpio) {
1312 				//all fine here!
1313 			}
1314 			else if (htype == HTYPE_OpenWebNetTCP) {
1315 				//All fine here
1316 			}
1317 			else if (htype == HTYPE_Daikin) {
1318 				//All fine here
1319 			}
1320 			else if (htype == HTYPE_GoodweAPI) {
1321 				if (username.empty())
1322 					return;
1323 			}
1324 			else if (htype == HTYPE_PythonPlugin) {
1325 				//All fine here
1326 			}
1327 			else if (htype == HTYPE_RaspberryPCF8574) {
1328 				//All fine here
1329 			}
1330 			else if (htype == HTYPE_OpenWebNetUSB) {
1331 				//All fine here
1332 			}
1333 			else if (htype == HTYPE_IntergasInComfortLAN2RF) {
1334 				//All fine here
1335 			}
1336 			else if (htype == HTYPE_EnphaseAPI) {
1337 				//All fine here
1338 			}
1339 			else if (htype == HTYPE_EcoCompteur) {
1340 				//all fine here!
1341 			}
1342 			else
1343 				return;
1344 
1345 			root["status"] = "OK";
1346 			root["title"] = "AddHardware";
1347 
1348 			std::vector<std::vector<std::string> > result;
1349 
1350 			if (htype == HTYPE_Domoticz)
1351 			{
1352 				if (password.size() != 32)
1353 				{
1354 					password = GenerateMD5Hash(password);
1355 				}
1356 			}
1357 			else if ((htype == HTYPE_S0SmartMeterUSB) || (htype == HTYPE_S0SmartMeterTCP))
1358 			{
1359 				extra = "0;1000;0;1000;0;1000;0;1000;0;1000";
1360 			}
1361 			else if (htype == HTYPE_Pinger)
1362 			{
1363 				mode1 = 30;
1364 				mode2 = 1000;
1365 			}
1366 			else if (htype == HTYPE_Kodi)
1367 			{
1368 				mode1 = 30;
1369 				mode2 = 1000;
1370 			}
1371 			else if (htype == HTYPE_PanasonicTV)
1372 			{
1373 				mode1 = 30;
1374 				mode2 = 1000;
1375 			}
1376 			else if (htype == HTYPE_LogitechMediaServer)
1377 			{
1378 				mode1 = 30;
1379 				mode2 = 1000;
1380 			}
1381 			else if (htype == HTYPE_HEOS)
1382 			{
1383 				mode1 = 30;
1384 				mode2 = 1000;
1385 			}
1386 			else if (htype == HTYPE_Tellstick)
1387 			{
1388 				mode1 = 4;
1389 				mode2 = 500;
1390 			}
1391 
1392 			if (htype == HTYPE_HTTPPOLLER) {
1393 				m_sql.safe_query(
1394 					"INSERT INTO Hardware (Name, Enabled, Type, Address, Port, SerialPort, Username, Password, Extra, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6, DataTimeout) VALUES ('%q',%d, %d,'%q',%d,'%q','%q','%q','%q','%q','%q', '%q', '%q', '%q', '%q', %d)",
1395 					name.c_str(),
1396 					(senabled == "true") ? 1 : 0,
1397 					htype,
1398 					address.c_str(),
1399 					port,
1400 					sport.c_str(),
1401 					username.c_str(),
1402 					password.c_str(),
1403 					extra.c_str(),
1404 					mode1Str.c_str(), mode2Str.c_str(), mode3Str.c_str(), mode4Str.c_str(), mode5Str.c_str(), mode6Str.c_str(),
1405 					iDataTimeout
1406 				);
1407 			}
1408 			else if (htype == HTYPE_PythonPlugin) {
1409 				sport = request::findValue(&req, "serialport");
1410 				m_sql.safe_query(
1411 					"INSERT INTO Hardware (Name, Enabled, Type, Address, Port, SerialPort, Username, Password, Extra, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6, DataTimeout) VALUES ('%q',%d, %d,'%q',%d,'%q','%q','%q','%q','%q','%q', '%q', '%q', '%q', '%q', %d)",
1412 					name.c_str(),
1413 					(senabled == "true") ? 1 : 0,
1414 					htype,
1415 					address.c_str(),
1416 					port,
1417 					sport.c_str(),
1418 					username.c_str(),
1419 					password.c_str(),
1420 					extra.c_str(),
1421 					mode1Str.c_str(), mode2Str.c_str(), mode3Str.c_str(), mode4Str.c_str(), mode5Str.c_str(), mode6Str.c_str(),
1422 					iDataTimeout
1423 				);
1424 			}
1425 			else if (
1426 				(htype == HTYPE_RFXtrx433)||
1427 				(htype == HTYPE_RFXtrx868)
1428 				)
1429 			{
1430 				//No Extra field here, handled in CWebServer::SetRFXCOMMode
1431 				m_sql.safe_query(
1432 					"INSERT INTO Hardware (Name, Enabled, Type, Address, Port, SerialPort, Username, Password, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6, DataTimeout) VALUES ('%q',%d, %d,'%q',%d,'%q','%q','%q',%d,%d,%d,%d,%d,%d,%d)",
1433 					name.c_str(),
1434 					(senabled == "true") ? 1 : 0,
1435 					htype,
1436 					address.c_str(),
1437 					port,
1438 					sport.c_str(),
1439 					username.c_str(),
1440 					password.c_str(),
1441 					mode1, mode2, mode3, mode4, mode5, mode6,
1442 					iDataTimeout
1443 				);
1444 				extra = "0";
1445 			}
1446 			else {
1447 				m_sql.safe_query(
1448 					"INSERT INTO Hardware (Name, Enabled, Type, Address, Port, SerialPort, Username, Password, Extra, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6, DataTimeout) VALUES ('%q',%d, %d,'%q',%d,'%q','%q','%q','%q',%d,%d,%d,%d,%d,%d,%d)",
1449 					name.c_str(),
1450 					(senabled == "true") ? 1 : 0,
1451 					htype,
1452 					address.c_str(),
1453 					port,
1454 					sport.c_str(),
1455 					username.c_str(),
1456 					password.c_str(),
1457 					extra.c_str(),
1458 					mode1, mode2, mode3, mode4, mode5, mode6,
1459 					iDataTimeout
1460 				);
1461 			}
1462 
1463 			//add the device for real in our system
1464 			result = m_sql.safe_query("SELECT MAX(ID) FROM Hardware");
1465 			if (!result.empty())
1466 			{
1467 				std::vector<std::string> sd = result[0];
1468 				int ID = atoi(sd[0].c_str());
1469 
1470 				root["idx"] = sd[0].c_str(); // OTO output the created ID for easier management on the caller side (if automated)
1471 
1472 				m_mainworker.AddHardwareFromParams(ID, name, (senabled == "true") ? true : false, htype, address, port, sport, username, password, extra, mode1, mode2, mode3, mode4, mode5, mode6, iDataTimeout, true);
1473 			}
1474 		}
1475 
Cmd_UpdateHardware(WebEmSession & session,const request & req,Json::Value & root)1476 		void CWebServer::Cmd_UpdateHardware(WebEmSession & session, const request& req, Json::Value &root)
1477 		{
1478 			if (session.rights != 2)
1479 			{
1480 				session.reply_status = reply::forbidden;
1481 				return; //Only admin user allowed
1482 			}
1483 
1484 			std::string idx = request::findValue(&req, "idx");
1485 			if (idx.empty())
1486 				return;
1487 			std::string name = HTMLSanitizer::Sanitize(CURLEncode::URLDecode(request::findValue(&req, "name")));
1488 			std::string senabled = request::findValue(&req, "enabled");
1489 			std::string shtype = request::findValue(&req, "htype");
1490 			std::string address = HTMLSanitizer::Sanitize(request::findValue(&req, "address"));
1491 			std::string sport = request::findValue(&req, "port");
1492 			std::string username = HTMLSanitizer::Sanitize(CURLEncode::URLDecode(request::findValue(&req, "username")));
1493 			std::string password = CURLEncode::URLDecode(request::findValue(&req, "password"));
1494 			std::string extra = HTMLSanitizer::Sanitize(CURLEncode::URLDecode(request::findValue(&req, "extra")));
1495 			std::string sdatatimeout = request::findValue(&req, "datatimeout");
1496 
1497 			if (
1498 				(name.empty()) ||
1499 				(senabled.empty()) ||
1500 				(shtype.empty())
1501 				)
1502 				return;
1503 
1504 			int mode1 = atoi(request::findValue(&req, "Mode1").c_str());
1505 			int mode2 = atoi(request::findValue(&req, "Mode2").c_str());
1506 			int mode3 = atoi(request::findValue(&req, "Mode3").c_str());
1507 			int mode4 = atoi(request::findValue(&req, "Mode4").c_str());
1508 			int mode5 = atoi(request::findValue(&req, "Mode5").c_str());
1509 			int mode6 = atoi(request::findValue(&req, "Mode6").c_str());
1510 
1511 			bool bEnabled = (senabled == "true") ? true : false;
1512 
1513 			_eHardwareTypes htype = (_eHardwareTypes)atoi(shtype.c_str());
1514 			int iDataTimeout = atoi(sdatatimeout.c_str());
1515 
1516 			int port = atoi(sport.c_str());
1517 
1518 			bool bIsSerial = false;
1519 
1520 			if (IsSerialDevice(htype))
1521 			{
1522 				//USB/System
1523 				bIsSerial = true;
1524 				if (bEnabled)
1525 				{
1526 					if (sport.empty())
1527 						return; //need to have a serial port
1528 				}
1529 			}
1530 			else if (
1531 				(htype == HTYPE_RFXLAN) || (htype == HTYPE_P1SmartMeterLAN)
1532 				|| (htype == HTYPE_YouLess) || (htype == HTYPE_OpenThermGatewayTCP) || (htype == HTYPE_LimitlessLights)
1533 				|| (htype == HTYPE_SolarEdgeTCP) || (htype == HTYPE_WOL) || (htype == HTYPE_S0SmartMeterTCP) || (htype == HTYPE_ECODEVICES) || (htype == HTYPE_Mochad)
1534 				|| (htype == HTYPE_MySensorsTCP) || (htype == HTYPE_MySensorsMQTT) || (htype == HTYPE_MQTT) || (htype == HTYPE_TTN_MQTT) || (htype == HTYPE_FRITZBOX) || (htype == HTYPE_ETH8020) || (htype == HTYPE_Sterbox)
1535 				|| (htype == HTYPE_KMTronicTCP) || (htype == HTYPE_KMTronicUDP) || (htype == HTYPE_SOLARMAXTCP) || (htype == HTYPE_RelayNet) || (htype == HTYPE_SatelIntegra) || (htype == HTYPE_eHouseTCP) || (htype == HTYPE_RFLINKTCP)
1536 				|| (htype == HTYPE_Comm5TCP || (htype == HTYPE_Comm5SMTCP) || (htype == HTYPE_CurrentCostMeterLAN))
1537 				|| (htype == HTYPE_NefitEastLAN) || (htype == HTYPE_DenkoviHTTPDevices) || (htype == HTYPE_DenkoviTCPDevices) || (htype == HTYPE_Ec3kMeterTCP) || (htype == HTYPE_MultiFun) || (htype == HTYPE_ZIBLUETCP) || (htype == HTYPE_OnkyoAVTCP)
1538 				|| (htype == HTYPE_OctoPrint)
1539 				) {
1540 				//Lan
1541 				if (address.empty())
1542 					return;
1543 			}
1544 			else if (htype == HTYPE_DomoticzInternal) {
1545 				// DomoticzInternal cannot be updated
1546 				return;
1547 			}
1548 			else if (htype == HTYPE_Domoticz) {
1549 				//Remote Domoticz
1550 				if (address.empty())
1551 					return;
1552 			}
1553 			else if (htype == HTYPE_System) {
1554 				//There should be only one, and with this ID
1555 				std::vector<std::vector<std::string> > result;
1556 				result = m_sql.safe_query("SELECT ID FROM Hardware WHERE (Type==%d)", HTYPE_System);
1557 				if (!result.empty())
1558 				{
1559 					int hID = atoi(result[0][0].c_str());
1560 					int aID = atoi(idx.c_str());
1561 					if (hID != aID)
1562 						return;
1563 				}
1564 			}
1565 			else if (htype == HTYPE_TE923) {
1566 				//All fine here
1567 			}
1568 			else if (htype == HTYPE_VOLCRAFTCO20) {
1569 				//All fine here
1570 			}
1571 			else if (htype == HTYPE_1WIRE) {
1572 				//All fine here
1573 			}
1574 			else if (htype == HTYPE_Pinger) {
1575 				//All fine here
1576 			}
1577 			else if (htype == HTYPE_Kodi) {
1578 				//All fine here
1579 			}
1580 			else if (htype == HTYPE_PanasonicTV) {
1581 				//All fine here
1582 			}
1583 			else if (htype == HTYPE_LogitechMediaServer) {
1584 				//All fine here
1585 			}
1586 			else if (htype == HTYPE_RaspberryBMP085) {
1587 				//All fine here
1588 			}
1589 			else if (htype == HTYPE_RaspberryHTU21D) {
1590 				//All fine here
1591 			}
1592 			else if (htype == HTYPE_RaspberryTSL2561) {
1593 				//All fine here
1594 			}
1595 			else if (htype == HTYPE_RaspberryBME280) {
1596 				//All fine here
1597 			}
1598 			else if (htype == HTYPE_RaspberryMCP23017) {
1599 				//all fine here!
1600 			}
1601 			else if (htype == HTYPE_Dummy) {
1602 				//All fine here
1603 			}
1604 			else if (htype == HTYPE_EVOHOME_SCRIPT || htype == HTYPE_EVOHOME_SERIAL || htype == HTYPE_EVOHOME_WEB || htype == HTYPE_EVOHOME_TCP) {
1605 				//All fine here
1606 			}
1607 			else if (htype == HTYPE_PiFace) {
1608 				//All fine here
1609 			}
1610 			else if (htype == HTYPE_HTTPPOLLER) {
1611 				//all fine here!
1612 			}
1613 			else if (htype == HTYPE_BleBox) {
1614 				//All fine here
1615 			}
1616 			else if (htype == HTYPE_HEOS) {
1617 				//All fine here
1618 			}
1619 			else if (htype == HTYPE_Yeelight) {
1620 				//All fine here
1621 			}
1622 			else if (htype == HTYPE_XiaomiGateway) {
1623 				//All fine here
1624 			}
1625 			else if (htype == HTYPE_Arilux) {
1626 				//All fine here
1627 			}
1628 			else if (htype == HTYPE_USBtinGateway) {
1629 				//All fine here
1630 			}
1631 			else if (htype == HTYPE_BuienRadar) {
1632 				//All fine here
1633 			}
1634 			else if (
1635 				(htype == HTYPE_Wunderground) ||
1636 				(htype == HTYPE_DarkSky) ||
1637 				(htype == HTYPE_AccuWeather) ||
1638 				(htype == HTYPE_OpenWeatherMap) ||
1639 				(htype == HTYPE_ICYTHERMOSTAT) ||
1640 				(htype == HTYPE_TOONTHERMOSTAT) ||
1641 				(htype == HTYPE_AtagOne) ||
1642 				(htype == HTYPE_PVOUTPUT_INPUT) ||
1643 				(htype == HTYPE_NEST) ||
1644 				(htype == HTYPE_ANNATHERMOSTAT) ||
1645 				(htype == HTYPE_THERMOSMART) ||
1646 				(htype == HTYPE_Tado) ||
1647 				(htype == HTYPE_Tesla) ||
1648 				(htype == HTYPE_Netatmo)
1649 				)
1650 			{
1651 				if (
1652 					(username.empty()) ||
1653 					(password.empty())
1654 					)
1655 					return;
1656 			}
1657 			else if (htype == HTYPE_SolarEdgeAPI)
1658 			{
1659 				if (
1660 					(username.empty())
1661 					)
1662 					return;
1663 			}
1664 			else if (htype == HTYPE_Nest_OAuthAPI) {
1665 				if (
1666 					(username == "") &&
1667 					(extra == "||")
1668 					)
1669 					return;
1670 			}
1671 			else if (htype == HTYPE_HARMONY_HUB) {
1672 				if (
1673 					(address.empty())
1674 					)
1675 					return;
1676 			}
1677 			else if (htype == HTYPE_Philips_Hue) {
1678 				if (
1679 					(username.empty()) ||
1680 					(address.empty())
1681 					)
1682 					return;
1683 				if (port == 0)
1684 					port = 80;
1685 			}
1686 			else if (htype == HTYPE_RaspberryGPIO) {
1687 				//all fine here!
1688 			}
1689 			else if (htype == HTYPE_SysfsGpio) {
1690 				//all fine here!
1691 			}
1692 			else if (htype == HTYPE_Rtl433) {
1693 				//all fine here!
1694 			}
1695 			else if (htype == HTYPE_Daikin) {
1696 				//all fine here!
1697 			}
1698 			else if (htype == HTYPE_SBFSpot) {
1699 				if (username.empty())
1700 					return;
1701 			}
1702 			else if (htype == HTYPE_WINDDELEN) {
1703 				std::string mill_id = request::findValue(&req, "Mode1");
1704 				if (
1705 					(mill_id.empty()) ||
1706 					(sport.empty())
1707 					)
1708 					return;
1709 			}
1710 			else if (htype == HTYPE_Honeywell) {
1711 				//All fine here
1712 			}
1713 			else if (htype == HTYPE_OpenWebNetTCP) {
1714 				//All fine here
1715 			}
1716 			else if (htype == HTYPE_PythonPlugin) {
1717 				//All fine here
1718 			}
1719 			else if (htype == HTYPE_GoodweAPI) {
1720 				if (username.empty()) {
1721 					return;
1722 				}
1723 			}
1724 			else if (htype == HTYPE_RaspberryPCF8574) {
1725 				//All fine here
1726 			}
1727 			else if (htype == HTYPE_OpenWebNetUSB) {
1728 				//All fine here
1729 			}
1730 			else if (htype == HTYPE_IntergasInComfortLAN2RF) {
1731 				//All fine here
1732 			}
1733 			else if (htype == HTYPE_EnphaseAPI) {
1734 				//all fine here!
1735 			}
1736 			else
1737 				return;
1738 
1739 			std::string mode1Str;
1740 			std::string mode2Str;
1741 			std::string mode3Str;
1742 			std::string mode4Str;
1743 			std::string mode5Str;
1744 			std::string mode6Str;
1745 
1746 			root["status"] = "OK";
1747 			root["title"] = "UpdateHardware";
1748 
1749 			if (htype == HTYPE_Domoticz)
1750 			{
1751 				if (password.size() != 32)
1752 				{
1753 					password = GenerateMD5Hash(password);
1754 				}
1755 			}
1756 
1757 			if ((bIsSerial) && (!bEnabled) && (sport.empty()))
1758 			{
1759 				//just disable the device
1760 				m_sql.safe_query(
1761 					"UPDATE Hardware SET Enabled=%d WHERE (ID == '%q')",
1762 					(bEnabled == true) ? 1 : 0,
1763 					idx.c_str()
1764 				);
1765 			}
1766 			else
1767 			{
1768 				if (htype == HTYPE_HTTPPOLLER) {
1769 					m_sql.safe_query(
1770 						"UPDATE Hardware SET Name='%q', Enabled=%d, Type=%d, Address='%q', Port=%d, SerialPort='%q', Username='%q', Password='%q', Extra='%q', DataTimeout=%d WHERE (ID == '%q')",
1771 						name.c_str(),
1772 						(senabled == "true") ? 1 : 0,
1773 						htype,
1774 						address.c_str(),
1775 						port,
1776 						sport.c_str(),
1777 						username.c_str(),
1778 						password.c_str(),
1779 						extra.c_str(),
1780 						iDataTimeout,
1781 						idx.c_str()
1782 					);
1783 				}
1784 				else if (htype == HTYPE_PythonPlugin) {
1785 					mode1Str = request::findValue(&req, "Mode1");
1786 					mode2Str = request::findValue(&req, "Mode2");
1787 					mode3Str = request::findValue(&req, "Mode3");
1788 					mode4Str = request::findValue(&req, "Mode4");
1789 					mode5Str = request::findValue(&req, "Mode5");
1790 					mode6Str = request::findValue(&req, "Mode6");
1791 					sport = request::findValue(&req, "serialport");
1792 					m_sql.safe_query(
1793 						"UPDATE Hardware SET Name='%q', Enabled=%d, Type=%d, Address='%q', Port=%d, SerialPort='%q', Username='%q', Password='%q', Extra='%q', Mode1='%q', Mode2='%q', Mode3='%q', Mode4='%q', Mode5='%q', Mode6='%q', DataTimeout=%d WHERE (ID == '%q')",
1794 						name.c_str(),
1795 						(senabled == "true") ? 1 : 0,
1796 						htype,
1797 						address.c_str(),
1798 						port,
1799 						sport.c_str(),
1800 						username.c_str(),
1801 						password.c_str(),
1802 						extra.c_str(),
1803 						mode1Str.c_str(), mode2Str.c_str(), mode3Str.c_str(), mode4Str.c_str(), mode5Str.c_str(), mode6Str.c_str(),
1804 						iDataTimeout,
1805 						idx.c_str()
1806 					);
1807 				}
1808 				else if (
1809 					(htype == HTYPE_RFXtrx433) ||
1810 					(htype == HTYPE_RFXtrx868)
1811 					)
1812 				{
1813 					//No Extra field here, handled in CWebServer::SetRFXCOMMode
1814 					m_sql.safe_query(
1815 						"UPDATE Hardware SET Name='%q', Enabled=%d, Type=%d, Address='%q', Port=%d, SerialPort='%q', Username='%q', Password='%q', Mode1=%d, Mode2=%d, Mode3=%d, Mode4=%d, Mode5=%d, Mode6=%d, DataTimeout=%d WHERE (ID == '%q')",
1816 						name.c_str(),
1817 						(bEnabled == true) ? 1 : 0,
1818 						htype,
1819 						address.c_str(),
1820 						port,
1821 						sport.c_str(),
1822 						username.c_str(),
1823 						password.c_str(),
1824 						mode1, mode2, mode3, mode4, mode5, mode6,
1825 						iDataTimeout,
1826 						idx.c_str()
1827 					);
1828 					std::vector<std::vector<std::string> > result;
1829 					result = m_sql.safe_query("SELECT Extra FROM Hardware WHERE ID=%q", idx.c_str());
1830 					if (!result.empty())
1831 						extra = result[0][0];
1832 				}
1833 				else {
1834 					m_sql.safe_query(
1835 						"UPDATE Hardware SET Name='%q', Enabled=%d, Type=%d, Address='%q', Port=%d, SerialPort='%q', Username='%q', Password='%q', Extra='%q', Mode1=%d, Mode2=%d, Mode3=%d, Mode4=%d, Mode5=%d, Mode6=%d, DataTimeout=%d WHERE (ID == '%q')",
1836 						name.c_str(),
1837 						(bEnabled == true) ? 1 : 0,
1838 						htype,
1839 						address.c_str(),
1840 						port,
1841 						sport.c_str(),
1842 						username.c_str(),
1843 						password.c_str(),
1844 						extra.c_str(),
1845 						mode1, mode2, mode3, mode4, mode5, mode6,
1846 						iDataTimeout,
1847 						idx.c_str()
1848 					);
1849 				}
1850 			}
1851 
1852 			//re-add the device in our system
1853 			int ID = atoi(idx.c_str());
1854 			m_mainworker.AddHardwareFromParams(ID, name, bEnabled, htype, address, port, sport, username, password, extra, mode1, mode2, mode3, mode4, mode5, mode6, iDataTimeout, true);
1855 		}
1856 
Cmd_GetDeviceValueOptions(WebEmSession & session,const request & req,Json::Value & root)1857 		void CWebServer::Cmd_GetDeviceValueOptions(WebEmSession & session, const request& req, Json::Value &root)
1858 		{
1859 			if (session.rights != 2)
1860 			{
1861 				session.reply_status = reply::forbidden;
1862 				return; //Only admin user allowed
1863 			}
1864 			std::string idx = request::findValue(&req, "idx");
1865 			if (idx.empty())
1866 				return;
1867 			std::vector<std::string> result;
1868 			result = CBasePush::DropdownOptions(atoi(idx.c_str()));
1869 			if ((result.size() == 1) && result[0] == "Status") {
1870 				root["result"][0]["Value"] = 0;
1871 				root["result"][0]["Wording"] = result[0];
1872 			}
1873 			else {
1874 				int ii = 0;
1875 				for (const auto & itt : result)
1876 				{
1877 					std::string ddOption = itt;
1878 					root["result"][ii]["Value"] = ii + 1;
1879 					root["result"][ii]["Wording"] = ddOption.c_str();
1880 					ii++;
1881 				}
1882 
1883 			}
1884 			root["status"] = "OK";
1885 			root["title"] = "GetDeviceValueOptions";
1886 		}
1887 
Cmd_GetDeviceValueOptionWording(WebEmSession & session,const request & req,Json::Value & root)1888 		void CWebServer::Cmd_GetDeviceValueOptionWording(WebEmSession & session, const request& req, Json::Value &root)
1889 		{
1890 			if (session.rights != 2)
1891 			{
1892 				session.reply_status = reply::forbidden;
1893 				return; //Only admin user allowed
1894 			}
1895 			std::string idx = request::findValue(&req, "idx");
1896 			std::string pos = request::findValue(&req, "pos");
1897 			if ((idx.empty()) || (pos.empty()))
1898 				return;
1899 			std::string wording;
1900 			wording = CBasePush::DropdownOptionsValue(atoi(idx.c_str()), atoi(pos.c_str()));
1901 			root["wording"] = wording;
1902 			root["status"] = "OK";
1903 			root["title"] = "GetDeviceValueOptions";
1904 		}
1905 
Cmd_AddUserVariable(WebEmSession & session,const request & req,Json::Value & root)1906 		void CWebServer::Cmd_AddUserVariable(WebEmSession & session, const request& req, Json::Value &root)
1907 		{
1908 			if (session.rights != 2)
1909 			{
1910 				session.reply_status = reply::forbidden;
1911 				_log.Log(LOG_ERROR, "User: %s tried to add a uservariable!", session.username.c_str());
1912 				return; //Only admin user allowed
1913 			}
1914 			std::string variablename = HTMLSanitizer::Sanitize(request::findValue(&req, "vname"));
1915 			std::string variablevalue = request::findValue(&req, "vvalue");
1916 			std::string variabletype = request::findValue(&req, "vtype");
1917 
1918 			root["title"] = "AddUserVariable";
1919 			root["status"] = "ERR";
1920 
1921 			if (!std::isdigit(variabletype[0]))
1922 			{
1923 				stdlower(variabletype);
1924 				if (variabletype == "integer")
1925 					variabletype = "0";
1926 				else if (variabletype == "float")
1927 					variabletype = "1";
1928 				else if (variabletype == "string")
1929 					variabletype = "2";
1930 				else if (variabletype == "date")
1931 					variabletype = "3";
1932 				else if (variabletype == "time")
1933 					variabletype = "4";
1934 				else
1935 				{
1936 					root["message"] = "Invalid variabletype " + variabletype;
1937 					return;
1938 				}
1939 			}
1940 
1941 			if (
1942 				(variablename.empty()) ||
1943 				(variabletype.empty()) ||
1944 				((variabletype != "0") && (variabletype != "1") && (variabletype != "2") && (variabletype != "3") && (variabletype != "4")) ||
1945 				((variablevalue.empty()) && (variabletype != "2"))
1946 				)
1947 			{
1948 				root["message"] = "Invalid variabletype " + variabletype;
1949 				return;
1950 			}
1951 
1952 			std::string errorMessage;
1953 			if (!m_sql.AddUserVariable(variablename, (const _eUsrVariableType)atoi(variabletype.c_str()), variablevalue, errorMessage))
1954 			{
1955 				root["message"] = errorMessage;
1956 			}
1957 			else
1958 			{
1959 				root["status"] = "OK";
1960 			}
1961 		}
1962 
Cmd_DeleteUserVariable(WebEmSession & session,const request & req,Json::Value & root)1963 		void CWebServer::Cmd_DeleteUserVariable(WebEmSession & session, const request& req, Json::Value &root)
1964 		{
1965 			if (session.rights != 2)
1966 			{
1967 				_log.Log(LOG_ERROR, "User: %s tried to delete a uservariable!", session.username.c_str());
1968 				session.reply_status = reply::forbidden;
1969 				return; //Only admin user allowed
1970 			}
1971 			std::string idx = request::findValue(&req, "idx");
1972 			if (idx.empty())
1973 				return;
1974 
1975 			m_sql.DeleteUserVariable(idx);
1976 			root["status"] = "OK";
1977 			root["title"] = "DeleteUserVariable";
1978 		}
1979 
Cmd_UpdateUserVariable(WebEmSession & session,const request & req,Json::Value & root)1980 		void CWebServer::Cmd_UpdateUserVariable(WebEmSession & session, const request& req, Json::Value &root)
1981 		{
1982 			if (session.rights != 2)
1983 			{
1984 				_log.Log(LOG_ERROR, "User: %s tried to update a uservariable!", session.username.c_str());
1985 				session.reply_status = reply::forbidden;
1986 				return; //Only admin user allowed
1987 			}
1988 
1989 			std::string idx = request::findValue(&req, "idx");
1990 			std::string variablename = HTMLSanitizer::Sanitize(request::findValue(&req, "vname"));
1991 			std::string variablevalue = request::findValue(&req, "vvalue");
1992 			std::string variabletype = request::findValue(&req, "vtype");
1993 
1994 			root["title"] = "UpdateUserVariable";
1995 			root["status"] = "ERR";
1996 
1997 			if (!std::isdigit(variabletype[0]))
1998 			{
1999 				stdlower(variabletype);
2000 				if (variabletype == "integer")
2001 					variabletype = "0";
2002 				else if (variabletype == "float")
2003 					variabletype = "1";
2004 				else if (variabletype == "string")
2005 					variabletype = "2";
2006 				else if (variabletype == "date")
2007 					variabletype = "3";
2008 				else if (variabletype == "time")
2009 					variabletype = "4";
2010 				else
2011 				{
2012 					root["message"] = "Invalid variabletype " + variabletype;
2013 					return;
2014 				}
2015 			}
2016 
2017 			if (
2018 				(variablename.empty()) ||
2019 				(variabletype.empty()) ||
2020 				((variabletype != "0") && (variabletype != "1") && (variabletype != "2") && (variabletype != "3") && (variabletype != "4")) ||
2021 				((variablevalue.empty()) && (variabletype != "2"))
2022 				)
2023 			{
2024 				root["message"] = "Invalid variabletype " + variabletype;
2025 				return;
2026 			}
2027 
2028 			std::vector<std::vector<std::string> > result;
2029 			if (idx.empty())
2030 			{
2031 				result = m_sql.safe_query("SELECT ID FROM UserVariables WHERE Name='%q'", variablename.c_str());
2032 				if (result.empty())
2033 				{
2034 					root["message"] = "Uservariable " + variablename + " does not exist";
2035 					return;
2036 				}
2037 				idx = result[0][0];
2038 			}
2039 
2040 			result = m_sql.safe_query("SELECT Name, ValueType FROM UserVariables WHERE ID='%q'", idx.c_str());
2041 			if (result.empty())
2042 			{
2043 				root["message"] = "Uservariable " + variablename + " does not exist";
2044 				return;
2045 			}
2046 
2047 			bool bTypeNameChanged = false;
2048 			if (variablename != result[0][0])
2049 				bTypeNameChanged = true; //new name
2050 			else if (variabletype != result[0][1])
2051 				bTypeNameChanged = true; //new type
2052 
2053 			std::string errorMessage;
2054 			if (!m_sql.UpdateUserVariable(idx, variablename, (const _eUsrVariableType)atoi(variabletype.c_str()), variablevalue, !bTypeNameChanged, errorMessage))
2055 			{
2056 				root["message"] = errorMessage;
2057 			}
2058 			else {
2059 				root["status"] = "OK";
2060 				if (bTypeNameChanged)
2061 				{
2062 					if (m_sql.m_bEnableEventSystem)
2063 						m_mainworker.m_eventsystem.GetCurrentUserVariables();
2064 				}
2065 			}
2066 		}
2067 
2068 
Cmd_GetUserVariables(WebEmSession & session,const request & req,Json::Value & root)2069 		void CWebServer::Cmd_GetUserVariables(WebEmSession & session, const request& req, Json::Value &root)
2070 		{
2071 			std::vector<std::vector<std::string> > result;
2072 			result = m_sql.safe_query("SELECT ID, Name, ValueType, Value, LastUpdate FROM UserVariables");
2073 			int ii = 0;
2074 			for (const auto & itt : result)
2075 			{
2076 				std::vector<std::string> sd = itt;
2077 				root["result"][ii]["idx"] = sd[0];
2078 				root["result"][ii]["Name"] = sd[1];
2079 				root["result"][ii]["Type"] = sd[2];
2080 				root["result"][ii]["Value"] = sd[3];
2081 				root["result"][ii]["LastUpdate"] = sd[4];
2082 				ii++;
2083 			}
2084 			root["status"] = "OK";
2085 			root["title"] = "GetUserVariables";
2086 		}
2087 
Cmd_GetUserVariable(WebEmSession & session,const request & req,Json::Value & root)2088 		void CWebServer::Cmd_GetUserVariable(WebEmSession & session, const request& req, Json::Value &root)
2089 		{
2090 			std::string idx = request::findValue(&req, "idx");
2091 			if (idx.empty())
2092 				return;
2093 
2094 			int iVarID = atoi(idx.c_str());
2095 
2096 			std::vector<std::vector<std::string> > result;
2097 			result = m_sql.safe_query("SELECT ID, Name, ValueType, Value, LastUpdate FROM UserVariables WHERE (ID==%d)", iVarID);
2098 			int ii = 0;
2099 			for (const auto & itt : result)
2100 			{
2101 				std::vector<std::string> sd = itt;
2102 				root["result"][ii]["idx"] = sd[0];
2103 				root["result"][ii]["Name"] = sd[1];
2104 				root["result"][ii]["Type"] = sd[2];
2105 				root["result"][ii]["Value"] = sd[3];
2106 				root["result"][ii]["LastUpdate"] = sd[4];
2107 				ii++;
2108 			}
2109 			root["status"] = "OK";
2110 			root["title"] = "GetUserVariable";
2111 		}
2112 
2113 
Cmd_AllowNewHardware(WebEmSession & session,const request & req,Json::Value & root)2114 		void CWebServer::Cmd_AllowNewHardware(WebEmSession & session, const request& req, Json::Value &root)
2115 		{
2116 			if (session.rights != 2)
2117 			{
2118 				session.reply_status = reply::forbidden;
2119 				return; //Only admin user allowed
2120 			}
2121 			std::string sTimeout = request::findValue(&req, "timeout");
2122 			if (sTimeout.empty())
2123 				return;
2124 			root["status"] = "OK";
2125 			root["title"] = "AllowNewHardware";
2126 
2127 			m_sql.AllowNewHardwareTimer(atoi(sTimeout.c_str()));
2128 		}
2129 
2130 
Cmd_DeleteHardware(WebEmSession & session,const request & req,Json::Value & root)2131 		void CWebServer::Cmd_DeleteHardware(WebEmSession & session, const request& req, Json::Value &root)
2132 		{
2133 			if (session.rights != 2)
2134 			{
2135 				session.reply_status = reply::forbidden;
2136 				return; //Only admin user allowed
2137 			}
2138 
2139 			std::string idx = request::findValue(&req, "idx");
2140 			if (idx.empty())
2141 				return;
2142 			int hwID = atoi(idx.c_str());
2143 
2144 			CDomoticzHardwareBase *pBaseHardware = m_mainworker.GetHardware(hwID);
2145 			if ((pBaseHardware != NULL) && (pBaseHardware->HwdType == HTYPE_DomoticzInternal)) {
2146 				// DomoticzInternal cannot be removed
2147 				return;
2148 			}
2149 
2150 			root["status"] = "OK";
2151 			root["title"] = "DeleteHardware";
2152 
2153 			m_mainworker.RemoveDomoticzHardware(hwID);
2154 			m_sql.DeleteHardware(idx);
2155 		}
2156 
Cmd_GetLog(WebEmSession & session,const request & req,Json::Value & root)2157 		void CWebServer::Cmd_GetLog(WebEmSession & session, const request& req, Json::Value &root)
2158 		{
2159 			root["status"] = "OK";
2160 			root["title"] = "GetLog";
2161 
2162 			time_t lastlogtime = 0;
2163 			std::string slastlogtime = request::findValue(&req, "lastlogtime");
2164 			if (slastlogtime != "")
2165 			{
2166 				std::stringstream s_str(slastlogtime);
2167 				s_str >> lastlogtime;
2168 			}
2169 
2170 			_eLogLevel lLevel = LOG_NORM;
2171 			std::string sloglevel = request::findValue(&req, "loglevel");
2172 			if (!sloglevel.empty())
2173 			{
2174 				lLevel = (_eLogLevel)atoi(sloglevel.c_str());
2175 			}
2176 
2177 			std::list<CLogger::_tLogLineStruct> logmessages = _log.GetLog(lLevel);
2178 			int ii = 0;
2179 			for (const auto & itt : logmessages)
2180 			{
2181 				if (itt.logtime > lastlogtime)
2182 				{
2183 					std::stringstream szLogTime;
2184 					szLogTime << itt.logtime;
2185 					root["LastLogTime"] = szLogTime.str();
2186 					root["result"][ii]["level"] = static_cast<int>(itt.level);
2187 					root["result"][ii]["message"] = itt.logmessage;
2188 					ii++;
2189 				}
2190 			}
2191 		}
2192 
Cmd_ClearLog(WebEmSession & session,const request & req,Json::Value & root)2193 		void CWebServer::Cmd_ClearLog(WebEmSession & session, const request& req, Json::Value &root)
2194 		{
2195 			root["status"] = "OK";
2196 			root["title"] = "ClearLog";
2197 			_log.ClearLog();
2198 		}
2199 
2200 		//Plan Functions
Cmd_AddPlan(WebEmSession & session,const request & req,Json::Value & root)2201 		void CWebServer::Cmd_AddPlan(WebEmSession & session, const request& req, Json::Value &root)
2202 		{
2203 			if (session.rights != 2)
2204 			{
2205 				session.reply_status = reply::forbidden;
2206 				return; //Only admin user allowed
2207 			}
2208 
2209 			std::string name = HTMLSanitizer::Sanitize(request::findValue(&req, "name"));
2210 			if (name.empty())
2211 			{
2212 				session.reply_status = reply::bad_request;
2213 			}
2214 
2215 			root["status"] = "OK";
2216 			root["title"] = "AddPlan";
2217 			m_sql.safe_query(
2218 				"INSERT INTO Plans (Name) VALUES ('%q')",
2219 				name.c_str()
2220 			);
2221 			std::vector<std::vector<std::string> > result;
2222 			result = m_sql.safe_query("SELECT MAX(ID) FROM Plans");
2223 			if (!result.empty())
2224 			{
2225 				std::vector<std::string> sd = result[0];
2226 				int ID = atoi(sd[0].c_str());
2227 
2228 				root["idx"] = ID; // OTO output the created ID for easier management on the caller side (if automated)
2229 			}
2230 		}
2231 
Cmd_UpdatePlan(WebEmSession & session,const request & req,Json::Value & root)2232 		void CWebServer::Cmd_UpdatePlan(WebEmSession & session, const request& req, Json::Value &root)
2233 		{
2234 			if (session.rights != 2)
2235 			{
2236 				session.reply_status = reply::forbidden;
2237 				return; //Only admin user allowed
2238 			}
2239 
2240 			std::string idx = request::findValue(&req, "idx");
2241 			if (idx.empty())
2242 				return;
2243 			std::string name = HTMLSanitizer::Sanitize(request::findValue(&req, "name"));
2244 			if (name.empty())
2245 			{
2246 				session.reply_status = reply::bad_request;
2247 				return;
2248 			}
2249 
2250 			root["status"] = "OK";
2251 			root["title"] = "UpdatePlan";
2252 
2253 			m_sql.safe_query(
2254 				"UPDATE Plans SET Name='%q' WHERE (ID == '%q')",
2255 				name.c_str(),
2256 				idx.c_str()
2257 			);
2258 		}
2259 
Cmd_DeletePlan(WebEmSession & session,const request & req,Json::Value & root)2260 		void CWebServer::Cmd_DeletePlan(WebEmSession & session, const request& req, Json::Value &root)
2261 		{
2262 			if (session.rights != 2)
2263 			{
2264 				session.reply_status = reply::forbidden;
2265 				return; //Only admin user allowed
2266 			}
2267 
2268 			std::string idx = request::findValue(&req, "idx");
2269 			if (idx.empty())
2270 				return;
2271 			root["status"] = "OK";
2272 			root["title"] = "DeletePlan";
2273 			m_sql.safe_query(
2274 				"DELETE FROM DeviceToPlansMap WHERE (PlanID == '%q')",
2275 				idx.c_str()
2276 			);
2277 			m_sql.safe_query(
2278 				"DELETE FROM Plans WHERE (ID == '%q')",
2279 				idx.c_str()
2280 			);
2281 		}
2282 
Cmd_GetUnusedPlanDevices(WebEmSession & session,const request & req,Json::Value & root)2283 		void CWebServer::Cmd_GetUnusedPlanDevices(WebEmSession & session, const request& req, Json::Value &root)
2284 		{
2285 			root["status"] = "OK";
2286 			root["title"] = "GetUnusedPlanDevices";
2287 			std::string sunique = request::findValue(&req, "unique");
2288 			if (sunique.empty())
2289 				return;
2290 			int iUnique = (sunique == "true") ? 1 : 0;
2291 			int ii = 0;
2292 
2293 			std::vector<std::vector<std::string> > result;
2294 			std::vector<std::vector<std::string> > result2;
2295 			result = m_sql.safe_query("SELECT T1.[ID], T1.[Name], T1.[Type], T1.[SubType], T2.[Name] AS HardwareName FROM DeviceStatus as T1, Hardware as T2 WHERE (T1.[Used]==1) AND (T2.[ID]==T1.[HardwareID]) ORDER BY T2.[Name], T1.[Name]");
2296 			if (!result.empty())
2297 			{
2298 				for (const auto & itt : result)
2299 				{
2300 					std::vector<std::string> sd = itt;
2301 
2302 					bool bDoAdd = true;
2303 					if (iUnique)
2304 					{
2305 						result2 = m_sql.safe_query("SELECT ID FROM DeviceToPlansMap WHERE (DeviceRowID=='%q') AND (DevSceneType==0)",
2306 							sd[0].c_str());
2307 						bDoAdd = (result2.size() == 0);
2308 					}
2309 					if (bDoAdd)
2310 					{
2311 						int _dtype = atoi(sd[2].c_str());
2312 						std::string Name = "[" + sd[4] + "] " + sd[1] + " (" + RFX_Type_Desc(_dtype, 1) + "/" + RFX_Type_SubType_Desc(_dtype, atoi(sd[3].c_str())) + ")";
2313 						root["result"][ii]["type"] = 0;
2314 						root["result"][ii]["idx"] = sd[0];
2315 						root["result"][ii]["Name"] = Name;
2316 						ii++;
2317 					}
2318 				}
2319 			}
2320 			//Add Scenes
2321 			result = m_sql.safe_query("SELECT ID, Name FROM Scenes ORDER BY Name");
2322 			if (!result.empty())
2323 			{
2324 				for (const auto & itt : result)
2325 				{
2326 					std::vector<std::string> sd = itt;
2327 
2328 					bool bDoAdd = true;
2329 					if (iUnique)
2330 					{
2331 						result2 = m_sql.safe_query("SELECT ID FROM DeviceToPlansMap WHERE (DeviceRowID=='%q') AND (DevSceneType==1)",
2332 							sd[0].c_str());
2333 						bDoAdd = (result2.size() == 0);
2334 					}
2335 					if (bDoAdd)
2336 					{
2337 						root["result"][ii]["type"] = 1;
2338 						root["result"][ii]["idx"] = sd[0];
2339 						std::string sname = "[Scene] " + sd[1];
2340 						root["result"][ii]["Name"] = sname;
2341 						ii++;
2342 					}
2343 				}
2344 			}
2345 		}
2346 
Cmd_AddPlanActiveDevice(WebEmSession & session,const request & req,Json::Value & root)2347 		void CWebServer::Cmd_AddPlanActiveDevice(WebEmSession & session, const request& req, Json::Value &root)
2348 		{
2349 			if (session.rights != 2)
2350 			{
2351 				session.reply_status = reply::forbidden;
2352 				return; //Only admin user allowed
2353 			}
2354 
2355 			std::string idx = request::findValue(&req, "idx");
2356 			std::string sactivetype = request::findValue(&req, "activetype");
2357 			std::string activeidx = request::findValue(&req, "activeidx");
2358 			if (
2359 				(idx.empty()) ||
2360 				(sactivetype.empty()) ||
2361 				(activeidx.empty())
2362 				)
2363 				return;
2364 			root["status"] = "OK";
2365 			root["title"] = "AddPlanActiveDevice";
2366 
2367 			int activetype = atoi(sactivetype.c_str());
2368 
2369 			//check if it is not already there
2370 			std::vector<std::vector<std::string> > result;
2371 			result = m_sql.safe_query("SELECT ID FROM DeviceToPlansMap WHERE (DeviceRowID=='%q') AND (DevSceneType==%d) AND (PlanID=='%q')",
2372 				activeidx.c_str(), activetype, idx.c_str());
2373 			if (result.empty())
2374 			{
2375 				m_sql.safe_query(
2376 					"INSERT INTO DeviceToPlansMap (DevSceneType,DeviceRowID, PlanID) VALUES (%d,'%q','%q')",
2377 					activetype,
2378 					activeidx.c_str(),
2379 					idx.c_str()
2380 				);
2381 			}
2382 		}
2383 
Cmd_GetPlanDevices(WebEmSession & session,const request & req,Json::Value & root)2384 		void CWebServer::Cmd_GetPlanDevices(WebEmSession & session, const request& req, Json::Value &root)
2385 		{
2386 			std::string idx = request::findValue(&req, "idx");
2387 			if (idx.empty())
2388 				return;
2389 			root["status"] = "OK";
2390 			root["title"] = "GetPlanDevices";
2391 
2392 			std::vector<std::vector<std::string> > result;
2393 			result = m_sql.safe_query("SELECT ID, DevSceneType, DeviceRowID, [Order] FROM DeviceToPlansMap WHERE (PlanID=='%q') ORDER BY [Order]",
2394 				idx.c_str());
2395 			if (!result.empty())
2396 			{
2397 				int ii = 0;
2398 				for (const auto & itt : result)
2399 				{
2400 					std::vector<std::string> sd = itt;
2401 
2402 					std::string ID = sd[0];
2403 					int DevSceneType = atoi(sd[1].c_str());
2404 					std::string DevSceneRowID = sd[2];
2405 
2406 					std::string Name = "";
2407 					if (DevSceneType == 0)
2408 					{
2409 						std::vector<std::vector<std::string> > result2;
2410 						result2 = m_sql.safe_query("SELECT Name FROM DeviceStatus WHERE (ID=='%q')",
2411 							DevSceneRowID.c_str());
2412 						if (!result2.empty())
2413 						{
2414 							Name = result2[0][0];
2415 						}
2416 					}
2417 					else
2418 					{
2419 						std::vector<std::vector<std::string> > result2;
2420 						result2 = m_sql.safe_query("SELECT Name FROM Scenes WHERE (ID=='%q')",
2421 							DevSceneRowID.c_str());
2422 						if (!result2.empty())
2423 						{
2424 							Name = "[Scene] " + result2[0][0];
2425 						}
2426 					}
2427 					if (Name != "")
2428 					{
2429 						root["result"][ii]["idx"] = ID;
2430 						root["result"][ii]["devidx"] = DevSceneRowID;
2431 						root["result"][ii]["type"] = DevSceneType;
2432 						root["result"][ii]["DevSceneRowID"] = DevSceneRowID;
2433 						root["result"][ii]["order"] = sd[3];
2434 						root["result"][ii]["Name"] = Name;
2435 						ii++;
2436 					}
2437 				}
2438 			}
2439 		}
2440 
Cmd_DeletePlanDevice(WebEmSession & session,const request & req,Json::Value & root)2441 		void CWebServer::Cmd_DeletePlanDevice(WebEmSession & session, const request& req, Json::Value &root)
2442 		{
2443 			if (session.rights != 2)
2444 			{
2445 				session.reply_status = reply::forbidden;
2446 				return; //Only admin user allowed
2447 			}
2448 			std::string idx = request::findValue(&req, "idx");
2449 			if (idx.empty())
2450 				return;
2451 			root["status"] = "OK";
2452 			root["title"] = "DeletePlanDevice";
2453 			m_sql.safe_query("DELETE FROM DeviceToPlansMap WHERE (ID == '%q')", idx.c_str());
2454 		}
2455 
Cmd_SetPlanDeviceCoords(WebEmSession & session,const request & req,Json::Value & root)2456 		void CWebServer::Cmd_SetPlanDeviceCoords(WebEmSession & session, const request& req, Json::Value &root)
2457 		{
2458 			std::string idx = request::findValue(&req, "idx");
2459 			std::string planidx = request::findValue(&req, "planidx");
2460 			std::string xoffset = request::findValue(&req, "xoffset");
2461 			std::string yoffset = request::findValue(&req, "yoffset");
2462 			std::string type = request::findValue(&req, "DevSceneType");
2463 			if ((idx.empty()) || (planidx.empty()) || (xoffset.empty()) || (yoffset.empty()))
2464 				return;
2465 			if (type != "1") type = "0";  // 0 = Device, 1 = Scene/Group
2466 			root["status"] = "OK";
2467 			root["title"] = "SetPlanDeviceCoords";
2468 			m_sql.safe_query("UPDATE DeviceToPlansMap SET [XOffset] = '%q', [YOffset] = '%q' WHERE (DeviceRowID='%q') and (PlanID='%q') and (DevSceneType='%q')",
2469 				xoffset.c_str(), yoffset.c_str(), idx.c_str(), planidx.c_str(), type.c_str());
2470 			_log.Log(LOG_STATUS, "(Floorplan) Device '%s' coordinates set to '%s,%s' in plan '%s'.", idx.c_str(), xoffset.c_str(), yoffset.c_str(), planidx.c_str());
2471 		}
2472 
Cmd_DeleteAllPlanDevices(WebEmSession & session,const request & req,Json::Value & root)2473 		void CWebServer::Cmd_DeleteAllPlanDevices(WebEmSession & session, const request& req, Json::Value &root)
2474 		{
2475 			if (session.rights != 2)
2476 			{
2477 				session.reply_status = reply::forbidden;
2478 				return; //Only admin user allowed
2479 			}
2480 			std::string idx = request::findValue(&req, "idx");
2481 			if (idx.empty())
2482 				return;
2483 			root["status"] = "OK";
2484 			root["title"] = "DeleteAllPlanDevices";
2485 			m_sql.safe_query("DELETE FROM DeviceToPlansMap WHERE (PlanID == '%q')", idx.c_str());
2486 		}
2487 
Cmd_ChangePlanOrder(WebEmSession & session,const request & req,Json::Value & root)2488 		void CWebServer::Cmd_ChangePlanOrder(WebEmSession & session, const request& req, Json::Value &root)
2489 		{
2490 			std::string idx = request::findValue(&req, "idx");
2491 			if (idx.empty())
2492 				return;
2493 			std::string sway = request::findValue(&req, "way");
2494 			if (sway.empty())
2495 				return;
2496 			bool bGoUp = (sway == "0");
2497 
2498 			std::string aOrder, oID, oOrder;
2499 
2500 			std::vector<std::vector<std::string> > result;
2501 			result = m_sql.safe_query("SELECT [Order] FROM Plans WHERE (ID=='%q')",
2502 				idx.c_str());
2503 			if (result.empty())
2504 				return;
2505 			aOrder = result[0][0];
2506 
2507 			if (!bGoUp)
2508 			{
2509 				//Get next device order
2510 				result = m_sql.safe_query("SELECT ID, [Order] FROM Plans WHERE ([Order]>'%q') ORDER BY [Order] ASC",
2511 					aOrder.c_str());
2512 				if (result.empty())
2513 					return;
2514 				oID = result[0][0];
2515 				oOrder = result[0][1];
2516 			}
2517 			else
2518 			{
2519 				//Get previous device order
2520 				result = m_sql.safe_query("SELECT ID, [Order] FROM Plans WHERE ([Order]<'%q') ORDER BY [Order] DESC",
2521 					aOrder.c_str());
2522 				if (result.empty())
2523 					return;
2524 				oID = result[0][0];
2525 				oOrder = result[0][1];
2526 			}
2527 			//Swap them
2528 			root["status"] = "OK";
2529 			root["title"] = "ChangePlanOrder";
2530 
2531 			m_sql.safe_query("UPDATE Plans SET [Order] = '%q' WHERE (ID='%q')",
2532 				oOrder.c_str(), idx.c_str());
2533 			m_sql.safe_query("UPDATE Plans SET [Order] = '%q' WHERE (ID='%q')",
2534 				aOrder.c_str(), oID.c_str());
2535 		}
2536 
Cmd_ChangePlanDeviceOrder(WebEmSession & session,const request & req,Json::Value & root)2537 		void CWebServer::Cmd_ChangePlanDeviceOrder(WebEmSession & session, const request& req, Json::Value &root)
2538 		{
2539 			std::string planid = request::findValue(&req, "planid");
2540 			std::string idx = request::findValue(&req, "idx");
2541 			std::string sway = request::findValue(&req, "way");
2542 			if (
2543 				(planid.empty()) ||
2544 				(idx.empty()) ||
2545 				(sway.empty())
2546 				)
2547 				return;
2548 			bool bGoUp = (sway == "0");
2549 
2550 			std::string aOrder, oID, oOrder;
2551 
2552 			std::vector<std::vector<std::string> > result;
2553 			result = m_sql.safe_query("SELECT [Order] FROM DeviceToPlansMap WHERE ((ID=='%q') AND (PlanID=='%q'))",
2554 				idx.c_str(), planid.c_str());
2555 			if (result.empty())
2556 				return;
2557 			aOrder = result[0][0];
2558 
2559 			if (!bGoUp)
2560 			{
2561 				//Get next device order
2562 				result = m_sql.safe_query("SELECT ID, [Order] FROM DeviceToPlansMap WHERE (([Order]>'%q') AND (PlanID=='%q')) ORDER BY [Order] ASC",
2563 					aOrder.c_str(), planid.c_str());
2564 				if (result.empty())
2565 					return;
2566 				oID = result[0][0];
2567 				oOrder = result[0][1];
2568 			}
2569 			else
2570 			{
2571 				//Get previous device order
2572 				result = m_sql.safe_query("SELECT ID, [Order] FROM DeviceToPlansMap WHERE (([Order]<'%q') AND (PlanID=='%q')) ORDER BY [Order] DESC",
2573 					aOrder.c_str(), planid.c_str());
2574 				if (result.empty())
2575 					return;
2576 				oID = result[0][0];
2577 				oOrder = result[0][1];
2578 			}
2579 			//Swap them
2580 			root["status"] = "OK";
2581 			root["title"] = "ChangePlanOrder";
2582 
2583 			m_sql.safe_query("UPDATE DeviceToPlansMap SET [Order] = '%q' WHERE (ID='%q')",
2584 				oOrder.c_str(), idx.c_str());
2585 			m_sql.safe_query("UPDATE DeviceToPlansMap SET [Order] = '%q' WHERE (ID='%q')",
2586 				aOrder.c_str(), oID.c_str());
2587 		}
2588 
Cmd_GetVersion(WebEmSession & session,const request & req,Json::Value & root)2589 		void CWebServer::Cmd_GetVersion(WebEmSession & session, const request& req, Json::Value &root)
2590 		{
2591 			root["status"] = "OK";
2592 			root["title"] = "GetVersion";
2593 			root["version"] = szAppVersion;
2594 			root["hash"] = szAppHash;
2595 			root["build_time"] = szAppDate;
2596 			CdzVents* dzvents = CdzVents::GetInstance();
2597 			root["dzvents_version"] = dzvents->GetVersion();
2598 			root["python_version"] = szPyVersion;
2599 
2600 			if (session.rights != 2)
2601 			{
2602 				//only admin users will receive the update notification
2603 				root["UseUpdate"] = false;
2604 				root["HaveUpdate"] = false;
2605 			}
2606 			else
2607 			{
2608 				root["UseUpdate"] = g_bUseUpdater;
2609 				root["HaveUpdate"] = m_mainworker.IsUpdateAvailable(false);
2610 				root["DomoticzUpdateURL"] = m_mainworker.m_szDomoticzUpdateURL;
2611 				root["SystemName"] = m_mainworker.m_szSystemName;
2612 				root["Revision"] = m_mainworker.m_iRevision;
2613 			}
2614 		}
2615 
Cmd_GetAuth(WebEmSession & session,const request & req,Json::Value & root)2616 		void CWebServer::Cmd_GetAuth(WebEmSession & session, const request& req, Json::Value &root)
2617 		{
2618 			root["status"] = "OK";
2619 			root["title"] = "GetAuth";
2620 			if (session.rights != -1)
2621 			{
2622 				root["version"] = szAppVersion;
2623 			}
2624 			root["user"] = session.username;
2625 			root["rights"] = session.rights;
2626 		}
2627 
Cmd_GetUptime(WebEmSession & session,const request & req,Json::Value & root)2628 		void CWebServer::Cmd_GetUptime(WebEmSession & session, const request& req, Json::Value &root)
2629 		{
2630 			//this is used in the about page, we are going to round the seconds a bit to display nicer
2631 			time_t atime = mytime(NULL);
2632 			time_t tuptime = atime - m_StartTime;
2633 			//round to 5 seconds (nicer in about page)
2634 			tuptime = ((tuptime / 5) * 5) + 5;
2635 			int days, hours, minutes, seconds;
2636 			days = (int)(tuptime / 86400);
2637 			tuptime -= (days * 86400);
2638 			hours = (int)(tuptime / 3600);
2639 			tuptime -= (hours * 3600);
2640 			minutes = (int)(tuptime / 60);
2641 			tuptime -= (minutes * 60);
2642 			seconds = (int)tuptime;
2643 			root["status"] = "OK";
2644 			root["title"] = "GetUptime";
2645 			root["days"] = days;
2646 			root["hours"] = hours;
2647 			root["minutes"] = minutes;
2648 			root["seconds"] = seconds;
2649 		}
2650 
Cmd_GetActualHistory(WebEmSession & session,const request & req,Json::Value & root)2651 		void CWebServer::Cmd_GetActualHistory(WebEmSession & session, const request& req, Json::Value &root)
2652 		{
2653 			root["status"] = "OK";
2654 			root["title"] = "GetActualHistory";
2655 
2656 			std::string historyfile = szUserDataFolder + "History.txt";
2657 
2658 			std::ifstream infile;
2659 			int ii = 0;
2660 			infile.open(historyfile.c_str());
2661 			std::string sLine;
2662 			if (infile.is_open())
2663 			{
2664 				while (!infile.eof())
2665 				{
2666 					getline(infile, sLine);
2667 					root["LastLogTime"] = "";
2668 					if (sLine.find("Version ") == 0)
2669 						root["result"][ii]["level"] = 1;
2670 					else
2671 						root["result"][ii]["level"] = 0;
2672 					root["result"][ii]["message"] = sLine;
2673 					ii++;
2674 				}
2675 			}
2676 		}
2677 
Cmd_GetNewHistory(WebEmSession & session,const request & req,Json::Value & root)2678 		void CWebServer::Cmd_GetNewHistory(WebEmSession & session, const request& req, Json::Value &root)
2679 		{
2680 			root["status"] = "OK";
2681 			root["title"] = "GetNewHistory";
2682 
2683 			std::string historyfile;
2684 			int nValue;
2685 			m_sql.GetPreferencesVar("ReleaseChannel", nValue);
2686 			bool bIsBetaChannel = (nValue != 0);
2687 
2688 			std::string szHistoryURL = "https://www.domoticz.com/download.php?channel=stable&type=history";
2689 			if (bIsBetaChannel)
2690 			{
2691 				utsname my_uname;
2692 				if (uname(&my_uname) < 0)
2693 					return;
2694 
2695 				std::string systemname = my_uname.sysname;
2696 				std::string machine = my_uname.machine;
2697 				std::transform(systemname.begin(), systemname.end(), systemname.begin(), ::tolower);
2698 
2699 				if (machine == "armv6l")
2700 				{
2701 					//Seems like old arm systems can also use the new arm build
2702 					machine = "armv7l";
2703 				}
2704 
2705 				if (((machine != "armv6l") && (machine != "armv7l") && (systemname != "windows") && (machine != "x86_64") && (machine != "aarch64")) || (strstr(my_uname.release, "ARCH+") != NULL))
2706 					szHistoryURL = "https://www.domoticz.com/download.php?channel=beta&type=history";
2707 				else
2708 					szHistoryURL = "https://www.domoticz.com/download.php?channel=beta&type=history&system=" + systemname + "&machine=" + machine;
2709 			}
2710 			if (!HTTPClient::GET(szHistoryURL, historyfile))
2711 			{
2712 				historyfile = "Unable to get Online History document !!";
2713 			}
2714 
2715 			std::istringstream stream(historyfile);
2716 			std::string sLine;
2717 			int ii = 0;
2718 			while (std::getline(stream, sLine))
2719 			{
2720 				root["LastLogTime"] = "";
2721 				if (sLine.find("Version ") == 0)
2722 					root["result"][ii]["level"] = 1;
2723 				else
2724 					root["result"][ii]["level"] = 0;
2725 				root["result"][ii]["message"] = sLine;
2726 				ii++;
2727 			}
2728 		}
2729 
Cmd_GetConfig(WebEmSession & session,const request & req,Json::Value & root)2730 		void CWebServer::Cmd_GetConfig(WebEmSession & session, const request& req, Json::Value &root)
2731 		{
2732 			root["status"] = "OK";
2733 			root["title"] = "GetConfig";
2734 
2735 			bool bHaveUser = (session.username != "");
2736 			//int urights = 3;
2737 			unsigned long UserID = 0;
2738 			if (bHaveUser)
2739 			{
2740 				int iUser = FindUser(session.username.c_str());
2741 				if (iUser != -1)
2742 				{
2743 					//urights = static_cast<int>(m_users[iUser].userrights);
2744 					UserID = m_users[iUser].ID;
2745 				}
2746 			}
2747 
2748 			int nValue;
2749 			std::string sValue;
2750 
2751 			if (m_sql.GetPreferencesVar("Language", sValue))
2752 			{
2753 				root["language"] = sValue;
2754 			}
2755 			if (m_sql.GetPreferencesVar("DegreeDaysBaseTemperature", sValue))
2756 			{
2757 				root["DegreeDaysBaseTemperature"] = atof(sValue.c_str());
2758 			}
2759 
2760 			nValue = 0;
2761 			int iDashboardType = 0;
2762 			m_sql.GetPreferencesVar("DashboardType", iDashboardType);
2763 			root["DashboardType"] = iDashboardType;
2764 			m_sql.GetPreferencesVar("MobileType", nValue);
2765 			root["MobileType"] = nValue;
2766 
2767 			nValue = 1;
2768 			m_sql.GetPreferencesVar("5MinuteHistoryDays", nValue);
2769 			root["FiveMinuteHistoryDays"] = nValue;
2770 
2771 			nValue = 1;
2772 			m_sql.GetPreferencesVar("ShowUpdateEffect", nValue);
2773 			root["result"]["ShowUpdatedEffect"] = (nValue == 1);
2774 
2775 			root["AllowWidgetOrdering"] = m_sql.m_bAllowWidgetOrdering;
2776 
2777 			root["WindScale"] = m_sql.m_windscale*10.0f;
2778 			root["WindSign"] = m_sql.m_windsign;
2779 			root["TempScale"] = m_sql.m_tempscale;
2780 			root["TempSign"] = m_sql.m_tempsign;
2781 
2782 #ifndef NOCLOUD
2783 			bool bEnableTabProxy = request::get_req_header(&req, "X-From-MyDomoticz") != NULL;
2784 #else
2785 			bool bEnableTabProxy = false;
2786 #endif
2787 			int bEnableTabDashboard = 1;
2788 			int bEnableTabFloorplans = 1;
2789 			int bEnableTabLight = 1;
2790 			int bEnableTabScenes = 1;
2791 			int bEnableTabTemp = 1;
2792 			int bEnableTabWeather = 1;
2793 			int bEnableTabUtility = 1;
2794 			int bEnableTabCustom = 1;
2795 
2796 			std::vector<std::vector<std::string> > result;
2797 
2798 			if ((UserID != 0) && (UserID != 10000))
2799 			{
2800 				result = m_sql.safe_query("SELECT TabsEnabled FROM Users WHERE (ID==%lu)",
2801 					UserID);
2802 				if (!result.empty())
2803 				{
2804 					int TabsEnabled = atoi(result[0][0].c_str());
2805 					bEnableTabLight = (TabsEnabled&(1 << 0));
2806 					bEnableTabScenes = (TabsEnabled&(1 << 1));
2807 					bEnableTabTemp = (TabsEnabled&(1 << 2));
2808 					bEnableTabWeather = (TabsEnabled&(1 << 3));
2809 					bEnableTabUtility = (TabsEnabled&(1 << 4));
2810 					bEnableTabCustom = (TabsEnabled&(1 << 5));
2811 					bEnableTabFloorplans = (TabsEnabled&(1 << 6));
2812 				}
2813 			}
2814 			else
2815 			{
2816 				m_sql.GetPreferencesVar("EnableTabFloorplans", bEnableTabFloorplans);
2817 				m_sql.GetPreferencesVar("EnableTabLights", bEnableTabLight);
2818 				m_sql.GetPreferencesVar("EnableTabScenes", bEnableTabScenes);
2819 				m_sql.GetPreferencesVar("EnableTabTemp", bEnableTabTemp);
2820 				m_sql.GetPreferencesVar("EnableTabWeather", bEnableTabWeather);
2821 				m_sql.GetPreferencesVar("EnableTabUtility", bEnableTabUtility);
2822 				m_sql.GetPreferencesVar("EnableTabCustom", bEnableTabCustom);
2823 			}
2824 			if (iDashboardType == 3)
2825 			{
2826 				//Floorplan , no need to show a tab floorplan
2827 				bEnableTabFloorplans = 0;
2828 			}
2829 			root["result"]["EnableTabProxy"] = bEnableTabProxy;
2830 			root["result"]["EnableTabDashboard"] = bEnableTabDashboard != 0;
2831 			root["result"]["EnableTabFloorplans"] = bEnableTabFloorplans != 0;
2832 			root["result"]["EnableTabLights"] = bEnableTabLight != 0;
2833 			root["result"]["EnableTabScenes"] = bEnableTabScenes != 0;
2834 			root["result"]["EnableTabTemp"] = bEnableTabTemp != 0;
2835 			root["result"]["EnableTabWeather"] = bEnableTabWeather != 0;
2836 			root["result"]["EnableTabUtility"] = bEnableTabUtility != 0;
2837 			root["result"]["EnableTabCustom"] = bEnableTabCustom != 0;
2838 
2839 			if (bEnableTabCustom)
2840 			{
2841 				//Add custom templates
2842 				DIR *lDir;
2843 				struct dirent *ent;
2844 				std::string templatesFolder = szWWWFolder + "/templates";
2845 				int iFile = 0;
2846 				if ((lDir = opendir(templatesFolder.c_str())) != NULL)
2847 				{
2848 					while ((ent = readdir(lDir)) != NULL)
2849 					{
2850 						std::string filename = ent->d_name;
2851 						size_t pos = filename.find(".htm");
2852 						if (pos != std::string::npos)
2853 						{
2854 							std::string shortfile = filename.substr(0, pos);
2855 							root["result"]["templates"][iFile++] = shortfile;
2856 						}
2857 					}
2858 					closedir(lDir);
2859 				}
2860 			}
2861 		}
2862 
Cmd_GetLocation(WebEmSession & session,const request & req,Json::Value & root)2863 		void CWebServer::Cmd_GetLocation(WebEmSession& session, const request& req, Json::Value& root)
2864 		{
2865 			if (session.rights == -1)
2866 			{
2867 				session.reply_status = reply::forbidden;
2868 				return;//Only auth user allowed
2869 			}
2870 			std::string Latitude = "1";
2871 			std::string Longitude = "1";
2872 			std::string sValue;
2873 			if (m_sql.GetPreferencesVar("Location", sValue))
2874 			{
2875 				std::vector<std::string> strarray;
2876 				StringSplit(sValue, ";", strarray);
2877 
2878 				if (strarray.size() == 2)
2879 				{
2880 					Latitude = strarray[0];
2881 					Longitude = strarray[1];
2882 				}
2883 			}
2884 			root["Latitude"] = Latitude;
2885 			root["Longitude"] = Longitude;
2886 		}
2887 
Cmd_SendNotification(WebEmSession & session,const request & req,Json::Value & root)2888 		void CWebServer::Cmd_SendNotification(WebEmSession & session, const request& req, Json::Value &root)
2889 		{
2890 			std::string subject = request::findValue(&req, "subject");
2891 			std::string body = request::findValue(&req, "body");
2892 			std::string subsystem = request::findValue(&req, "subsystem");
2893 			if (
2894 				(subject.empty()) ||
2895 				(body.empty())
2896 				)
2897 				return;
2898 			if (subsystem.empty()) subsystem = NOTIFYALL;
2899 			//Add to queue
2900 			if (m_notifications.SendMessage(0, std::string(""), subsystem, subject, body, std::string(""), 1, std::string(""), false)) {
2901 				root["status"] = "OK";
2902 			}
2903 			root["title"] = "SendNotification";
2904 		}
2905 
Cmd_EmailCameraSnapshot(WebEmSession & session,const request & req,Json::Value & root)2906 		void CWebServer::Cmd_EmailCameraSnapshot(WebEmSession & session, const request& req, Json::Value &root)
2907 		{
2908 			std::string camidx = request::findValue(&req, "camidx");
2909 			std::string subject = request::findValue(&req, "subject");
2910 			if (
2911 				(camidx.empty()) ||
2912 				(subject.empty())
2913 				)
2914 				return;
2915 			//Add to queue
2916 			m_sql.AddTaskItem(_tTaskItem::EmailCameraSnapshot(1, camidx, subject));
2917 			root["status"] = "OK";
2918 			root["title"] = "Email Camera Snapshot";
2919 		}
2920 
Cmd_UpdateDevice(WebEmSession & session,const request & req,Json::Value & root)2921 		void CWebServer::Cmd_UpdateDevice(WebEmSession & session, const request& req, Json::Value &root)
2922 		{
2923 			if (session.rights < 1)
2924 			{
2925 				session.reply_status = reply::forbidden;
2926 				return; //only user or higher allowed
2927 			}
2928 
2929 			std::string idx = request::findValue(&req, "idx");
2930 
2931 			if (!IsIdxForUser(&session, atoi(idx.c_str())))
2932 			{
2933 				_log.Log(LOG_ERROR, "User: %s tried to update an Unauthorized device!", session.username.c_str());
2934 				session.reply_status = reply::forbidden;
2935 				return;
2936 			}
2937 
2938 			std::string hid = request::findValue(&req, "hid");
2939 			std::string did = request::findValue(&req, "did");
2940 			std::string dunit = request::findValue(&req, "dunit");
2941 			std::string dtype = request::findValue(&req, "dtype");
2942 			std::string dsubtype = request::findValue(&req, "dsubtype");
2943 
2944 			std::string nvalue = request::findValue(&req, "nvalue");
2945 			std::string svalue = request::findValue(&req, "svalue");
2946 			std::string ptrigger = request::findValue(&req, "parsetrigger");
2947 
2948 			bool parseTrigger = (ptrigger != "false");
2949 
2950 			if ((nvalue.empty() && svalue.empty()))
2951 			{
2952 				return;
2953 			}
2954 
2955 			int signallevel = 12;
2956 			int batterylevel = 255;
2957 
2958 			if (idx.empty())
2959 			{
2960 				//No index supplied, check if raw parameters where supplied
2961 				if (
2962 					(hid.empty()) ||
2963 					(did.empty()) ||
2964 					(dunit.empty()) ||
2965 					(dtype.empty()) ||
2966 					(dsubtype.empty())
2967 					)
2968 					return;
2969 			}
2970 			else
2971 			{
2972 				//Get the raw device parameters
2973 				std::vector<std::vector<std::string> > result;
2974 				result = m_sql.safe_query("SELECT HardwareID, DeviceID, Unit, Type, SubType FROM DeviceStatus WHERE (ID=='%q')",
2975 					idx.c_str());
2976 				if (result.empty())
2977 					return;
2978 				hid = result[0][0];
2979 				did = result[0][1];
2980 				dunit = result[0][2];
2981 				dtype = result[0][3];
2982 				dsubtype = result[0][4];
2983 			}
2984 
2985 			int HardwareID = atoi(hid.c_str());
2986 			std::string DeviceID = did;
2987 			int unit = atoi(dunit.c_str());
2988 			int devType = atoi(dtype.c_str());
2989 			int subType = atoi(dsubtype.c_str());
2990 
2991 			//uint64_t ulIdx = std::strtoull(idx.c_str(), nullptr, 10);
2992 
2993 			int invalue = atoi(nvalue.c_str());
2994 
2995 			std::string sSignalLevel = request::findValue(&req, "rssi");
2996 			if (sSignalLevel != "")
2997 			{
2998 				signallevel = atoi(sSignalLevel.c_str());
2999 			}
3000 			std::string sBatteryLevel = request::findValue(&req, "battery");
3001 			if (sBatteryLevel != "")
3002 			{
3003 				batterylevel = atoi(sBatteryLevel.c_str());
3004 			}
3005 			if (m_mainworker.UpdateDevice(HardwareID, DeviceID, unit, devType, subType, invalue, svalue, signallevel, batterylevel, parseTrigger))
3006 			{
3007 				root["status"] = "OK";
3008 				root["title"] = "Update Device";
3009 			}
3010 		}
3011 
Cmd_UpdateDevices(WebEmSession & session,const request & req,Json::Value & root)3012 		void CWebServer::Cmd_UpdateDevices(WebEmSession & session, const request& req, Json::Value &root)
3013 		{
3014 			std::string script = request::findValue(&req, "script");
3015 			if (script.empty())
3016 			{
3017 				return;
3018 			}
3019 			std::string content = req.content;
3020 
3021 			std::vector<std::string> allParameters;
3022 
3023 			// Keep the url content on the right of the '?'
3024 			std::vector<std::string> allParts;
3025 			StringSplit(req.uri, "?", allParts);
3026 			if (!allParts.empty())
3027 			{
3028 				// Split all url parts separated by a '&'
3029 				StringSplit(allParts[1], "&", allParameters);
3030 			}
3031 
3032 			CLuaHandler luaScript;
3033 			bool ret = luaScript.executeLuaScript(script, content, allParameters);
3034 			if (ret)
3035 			{
3036 				root["status"] = "OK";
3037 				root["title"] = "Update Device";
3038 			}
3039 		}
3040 
Cmd_CustomEvent(WebEmSession & session,const request & req,Json::Value & root)3041 		void CWebServer::Cmd_CustomEvent(WebEmSession& session, const request& req, Json::Value& root)
3042 		{
3043 			if (session.rights < 1)
3044 			{
3045 				session.reply_status = reply::forbidden;
3046 				return; //only user or higher allowed
3047 			}
3048 			Json::Value eventInfo;
3049 			eventInfo["name"] = request::findValue(&req, "event");
3050 			eventInfo["data"] = request::findValue(&req, "data");
3051 
3052 			if (eventInfo["name"].empty())
3053 			{
3054 				return;
3055 			}
3056 
3057 			m_mainworker.m_notificationsystem.Notify(Notification::DZ_CUSTOM, Notification::STATUS_INFO, JSonToRawString(eventInfo));
3058 
3059 			root["status"] = "OK";
3060 			root["title"] = "Custom Event";
3061 		}
3062 
Cmd_SetThermostatState(WebEmSession & session,const request & req,Json::Value & root)3063 		void CWebServer::Cmd_SetThermostatState(WebEmSession & session, const request& req, Json::Value &root)
3064 		{
3065 			std::string sstate = request::findValue(&req, "state");
3066 			std::string idx = request::findValue(&req, "idx");
3067 			std::string name = HTMLSanitizer::Sanitize(request::findValue(&req, "name"));
3068 
3069 			if (
3070 				(idx.empty()) ||
3071 				(sstate.empty())
3072 				)
3073 				return;
3074 			int iState = atoi(sstate.c_str());
3075 
3076 			int urights = 3;
3077 			bool bHaveUser = (session.username != "");
3078 			if (bHaveUser)
3079 			{
3080 				int iUser = FindUser(session.username.c_str());
3081 				if (iUser != -1)
3082 				{
3083 					urights = static_cast<int>(m_users[iUser].userrights);
3084 					_log.Log(LOG_STATUS, "User: %s initiated a Thermostat State change command", m_users[iUser].Username.c_str());
3085 				}
3086 			}
3087 			if (urights < 1)
3088 				return;
3089 
3090 			root["status"] = "OK";
3091 			root["title"] = "Set Thermostat State";
3092 			_log.Log(LOG_NORM, "Setting Thermostat State....");
3093 			m_mainworker.SetThermostatState(idx, iState);
3094 		}
3095 
Cmd_SystemShutdown(WebEmSession & session,const request & req,Json::Value & root)3096 		void CWebServer::Cmd_SystemShutdown(WebEmSession & session, const request& req, Json::Value &root)
3097 		{
3098 			if (session.rights != 2)
3099 			{
3100 				session.reply_status = reply::forbidden;
3101 				return; //Only admin user allowed
3102 			}
3103 #ifdef WIN32
3104 			int ret = system("shutdown -s -f -t 1 -d up:125:1");
3105 #else
3106 			int ret = system("sudo shutdown -h now");
3107 #endif
3108 			if (ret != 0)
3109 			{
3110 				_log.Log(LOG_ERROR, "Error executing shutdown command. returned: %d", ret);
3111 				return;
3112 			}
3113 			root["title"] = "SystemShutdown";
3114 			root["status"] = "OK";
3115 		}
3116 
Cmd_SystemReboot(WebEmSession & session,const request & req,Json::Value & root)3117 		void CWebServer::Cmd_SystemReboot(WebEmSession & session, const request& req, Json::Value &root)
3118 		{
3119 			if (session.rights != 2)
3120 			{
3121 				session.reply_status = reply::forbidden;
3122 				return; //Only admin user allowed
3123 			}
3124 #ifdef WIN32
3125 			int ret = system("shutdown -r -f -t 1 -d up:125:1");
3126 #else
3127 			int ret = system("sudo shutdown -r now");
3128 #endif
3129 			if (ret != 0)
3130 			{
3131 				_log.Log(LOG_ERROR, "Error executing reboot command. returned: %d", ret);
3132 				return;
3133 			}
3134 			root["title"] = "SystemReboot";
3135 			root["status"] = "OK";
3136 		}
3137 
Cmd_ExcecuteScript(WebEmSession & session,const request & req,Json::Value & root)3138 		void CWebServer::Cmd_ExcecuteScript(WebEmSession & session, const request& req, Json::Value &root)
3139 		{
3140 			if (session.rights != 2)
3141 			{
3142 				session.reply_status = reply::forbidden;
3143 				return; //Only admin user allowed
3144 			}
3145 			std::string scriptname = request::findValue(&req, "scriptname");
3146 			if (scriptname.empty())
3147 				return;
3148 			if (scriptname.find("..") != std::string::npos)
3149 				return;
3150 #ifdef WIN32
3151 			scriptname = szUserDataFolder + "scripts\\" + scriptname;
3152 #else
3153 			scriptname = szUserDataFolder + "scripts/" + scriptname;
3154 #endif
3155 			if (!file_exist(scriptname.c_str()))
3156 				return;
3157 			std::string script_params = request::findValue(&req, "scriptparams");
3158 			std::string strparm = szUserDataFolder;
3159 			if (!script_params.empty())
3160 			{
3161 				if (strparm.size() > 0)
3162 					strparm += " " + script_params;
3163 				else
3164 					strparm = script_params;
3165 			}
3166 			std::string sdirect = request::findValue(&req, "direct");
3167 			if (sdirect == "true")
3168 			{
3169 				_log.Log(LOG_STATUS, "Executing script: %s", scriptname.c_str());
3170 #ifdef WIN32
3171 				ShellExecute(NULL, "open", scriptname.c_str(), strparm.c_str(), NULL, SW_SHOWNORMAL);
3172 #else
3173 				std::string lscript = scriptname + " " + strparm;
3174 				int ret = system(lscript.c_str());
3175 				if (ret != 0)
3176 				{
3177 					_log.Log(LOG_ERROR, "Error executing script command (%s). returned: %d", lscript.c_str(), ret);
3178 					return;
3179 			}
3180 #endif
3181 		}
3182 			else
3183 			{
3184 				//add script to background worker
3185 				m_sql.AddTaskItem(_tTaskItem::ExecuteScript(0.2f, scriptname, strparm));
3186 			}
3187 			root["title"] = "ExecuteScript";
3188 			root["status"] = "OK";
3189 		}
3190 
3191 		//Only for Unix systems
Cmd_UpdateApplication(WebEmSession & session,const request & req,Json::Value & root)3192 		void CWebServer::Cmd_UpdateApplication(WebEmSession& session, const request& req, Json::Value& root)
3193 		{
3194 			if (session.rights != 2)
3195 			{
3196 				session.reply_status = reply::forbidden;
3197 				return; //Only admin user allowed
3198 			}
3199 #ifdef WIN32
3200 #ifndef _DEBUG
3201 			return;
3202 #endif
3203 #endif
3204 			int nValue;
3205 			m_sql.GetPreferencesVar("ReleaseChannel", nValue);
3206 			bool bIsBetaChannel = (nValue != 0);
3207 
3208 			std::string scriptname(szStartupFolder);
3209 			scriptname += (bIsBetaChannel) ? "updatebeta" : "updaterelease";
3210 			//run script in background
3211 			std::string lscript = scriptname + " &";
3212 			int ret = system(lscript.c_str());
3213 			root["title"] = "UpdateApplication";
3214 			root["status"] = "OK";
3215 		}
3216 
Cmd_GetCosts(WebEmSession & session,const request & req,Json::Value & root)3217 		void CWebServer::Cmd_GetCosts(WebEmSession & session, const request& req, Json::Value &root)
3218 		{
3219 			std::string idx = request::findValue(&req, "idx");
3220 			if (idx.empty())
3221 				return;
3222 			char szTmp[100];
3223 			std::vector<std::vector<std::string> > result;
3224 			result = m_sql.safe_query("SELECT Type, SubType, nValue, sValue FROM DeviceStatus WHERE (ID=='%q')",
3225 				idx.c_str());
3226 			if (!result.empty())
3227 			{
3228 				std::vector<std::string> sd = result[0];
3229 
3230 				int nValue = 0;
3231 				root["status"] = "OK";
3232 				root["title"] = "GetElectraCosts";
3233 				m_sql.GetPreferencesVar("CostEnergy", nValue);
3234 				root["CostEnergy"] = nValue;
3235 				m_sql.GetPreferencesVar("CostEnergyT2", nValue);
3236 				root["CostEnergyT2"] = nValue;
3237 				m_sql.GetPreferencesVar("CostEnergyR1", nValue);
3238 				root["CostEnergyR1"] = nValue;
3239 				m_sql.GetPreferencesVar("CostEnergyR2", nValue);
3240 				root["CostEnergyR2"] = nValue;
3241 				m_sql.GetPreferencesVar("CostGas", nValue);
3242 				root["CostGas"] = nValue;
3243 				m_sql.GetPreferencesVar("CostWater", nValue);
3244 				root["CostWater"] = nValue;
3245 
3246 				int tValue = 1000;
3247 				if (m_sql.GetPreferencesVar("MeterDividerWater", tValue))
3248 				{
3249 					root["DividerWater"] = float(tValue);
3250 				}
3251 
3252 				unsigned char dType = atoi(sd[0].c_str());
3253 				//unsigned char subType = atoi(sd[1].c_str());
3254 				//nValue = (unsigned char)atoi(sd[2].c_str());
3255 				std::string sValue = sd[3];
3256 
3257 				if (dType == pTypeP1Power)
3258 				{
3259 					//also provide the counter values
3260 
3261 					std::vector<std::string> splitresults;
3262 					StringSplit(sValue, ";", splitresults);
3263 					if (splitresults.size() != 6)
3264 						return;
3265 
3266 					float EnergyDivider = 1000.0f;
3267 					if (m_sql.GetPreferencesVar("MeterDividerEnergy", tValue))
3268 					{
3269 						EnergyDivider = float(tValue);
3270 					}
3271 
3272 					unsigned long long powerusage1 = std::strtoull(splitresults[0].c_str(), nullptr, 10);
3273 					unsigned long long powerusage2 = std::strtoull(splitresults[1].c_str(), nullptr, 10);
3274 					unsigned long long powerdeliv1 = std::strtoull(splitresults[2].c_str(), nullptr, 10);
3275 					unsigned long long powerdeliv2 = std::strtoull(splitresults[3].c_str(), nullptr, 10);
3276 					//unsigned long long usagecurrent = std::strtoull(splitresults[4].c_str(), nullptr, 10);
3277 					//unsigned long long delivcurrent = std::strtoull(splitresults[5].c_str(), nullptr, 10);
3278 
3279 					powerdeliv1 = (powerdeliv1 < 10) ? 0 : powerdeliv1;
3280 					powerdeliv2 = (powerdeliv2 < 10) ? 0 : powerdeliv2;
3281 
3282 					sprintf(szTmp, "%.03f", float(powerusage1) / EnergyDivider);
3283 					root["CounterT1"] = szTmp;
3284 					sprintf(szTmp, "%.03f", float(powerusage2) / EnergyDivider);
3285 					root["CounterT2"] = szTmp;
3286 					sprintf(szTmp, "%.03f", float(powerdeliv1) / EnergyDivider);
3287 					root["CounterR1"] = szTmp;
3288 					sprintf(szTmp, "%.03f", float(powerdeliv2) / EnergyDivider);
3289 					root["CounterR2"] = szTmp;
3290 				}
3291 			}
3292 		}
3293 
Cmd_CheckForUpdate(WebEmSession & session,const request & req,Json::Value & root)3294 		void CWebServer::Cmd_CheckForUpdate(WebEmSession & session, const request& req, Json::Value &root)
3295 		{
3296 			bool bHaveUser = (session.username != "");
3297 			int urights = 3;
3298 			if (bHaveUser)
3299 			{
3300 				int iUser = FindUser(session.username.c_str());
3301 				if (iUser != -1)
3302 					urights = static_cast<int>(m_users[iUser].userrights);
3303 			}
3304 			root["statuscode"] = urights;
3305 
3306 			root["status"] = "OK";
3307 			root["title"] = "CheckForUpdate";
3308 			root["HaveUpdate"] = false;
3309 			root["Revision"] = m_mainworker.m_iRevision;
3310 
3311 			if (session.rights != 2)
3312 			{
3313 				session.reply_status = reply::forbidden;
3314 				return; //Only admin users may update
3315 			}
3316 
3317 			bool bIsForced = (request::findValue(&req, "forced") == "true");
3318 
3319 			if (!bIsForced)
3320 			{
3321 				int nValue = 0;
3322 				m_sql.GetPreferencesVar("UseAutoUpdate", nValue);
3323 				if (nValue != 1)
3324 				{
3325 					return;
3326 				}
3327 			}
3328 
3329 			root["HaveUpdate"] = m_mainworker.IsUpdateAvailable(bIsForced);
3330 			root["DomoticzUpdateURL"] = m_mainworker.m_szDomoticzUpdateURL;
3331 			root["SystemName"] = m_mainworker.m_szSystemName;
3332 			root["Revision"] = m_mainworker.m_iRevision;
3333 		}
3334 
Cmd_DownloadUpdate(WebEmSession & session,const request & req,Json::Value & root)3335 		void CWebServer::Cmd_DownloadUpdate(WebEmSession & session, const request& req, Json::Value &root)
3336 		{
3337 			if (!m_mainworker.StartDownloadUpdate())
3338 				return;
3339 			root["status"] = "OK";
3340 			root["title"] = "DownloadUpdate";
3341 		}
3342 
Cmd_DownloadReady(WebEmSession & session,const request & req,Json::Value & root)3343 		void CWebServer::Cmd_DownloadReady(WebEmSession & session, const request& req, Json::Value &root)
3344 		{
3345 			if (!m_mainworker.m_bHaveDownloadedDomoticzUpdate)
3346 				return;
3347 			root["status"] = "OK";
3348 			root["title"] = "DownloadReady";
3349 			root["downloadok"] = (m_mainworker.m_bHaveDownloadedDomoticzUpdateSuccessFull) ? true : false;
3350 		}
3351 
Cmd_DeleteDatePoint(WebEmSession & session,const request & req,Json::Value & root)3352 		void CWebServer::Cmd_DeleteDatePoint(WebEmSession & session, const request& req, Json::Value &root)
3353 		{
3354 			const std::string idx = request::findValue(&req, "idx");
3355 			const std::string Date = request::findValue(&req, "date");
3356 			if (
3357 				(idx.empty()) ||
3358 				(Date.empty())
3359 				)
3360 				return;
3361 			root["status"] = "OK";
3362 			root["title"] = "deletedatapoint";
3363 			m_sql.DeleteDataPoint(idx.c_str(), Date);
3364 		}
3365 
IsIdxForUser(const WebEmSession * pSession,const int Idx)3366 		bool CWebServer::IsIdxForUser(const WebEmSession *pSession, const int Idx)
3367 		{
3368 			if (pSession->rights == 2)
3369 				return true;
3370 			if (pSession->rights == 0)
3371 				return false; //viewer
3372 			//User
3373 			int iUser = FindUser(pSession->username.c_str());
3374 			if ((iUser < 0) || (iUser >= (int)m_users.size()))
3375 				return false;
3376 
3377 			if (m_users[iUser].TotSensors == 0)
3378 				return true; // all sensors
3379 
3380 			std::vector<std::vector<std::string> > result = m_sql.safe_query("SELECT DeviceRowID FROM SharedDevices WHERE (SharedUserID == '%d') AND (DeviceRowID == '%d')", m_users[iUser].ID, Idx);
3381 			return (!result.empty());
3382 		}
3383 
3384 
HandleCommand(const std::string & cparam,WebEmSession & session,const request & req,Json::Value & root)3385 		void CWebServer::HandleCommand(const std::string &cparam, WebEmSession & session, const request& req, Json::Value &root)
3386 		{
3387 			std::map < std::string, webserver_response_function >::iterator pf = m_webcommands.find(cparam);
3388 			if (pf != m_webcommands.end())
3389 			{
3390 				pf->second(session, req, root);
3391 				return;
3392 			}
3393 
3394 			std::vector<std::vector<std::string> > result;
3395 			char szTmp[300];
3396 
3397 			bool bHaveUser = (session.username != "");
3398 /*
3399 			int iUser = -1;
3400 			if (bHaveUser)
3401 			{
3402 				iUser = FindUser(session.username.c_str());
3403 			}
3404 */
3405 			if (cparam == "deleteallsubdevices")
3406 			{
3407 				if (session.rights < 2)
3408 				{
3409 					session.reply_status = reply::forbidden;
3410 					return; //Only admin user allowed
3411 				}
3412 
3413 				std::string idx = request::findValue(&req, "idx");
3414 				if (idx.empty())
3415 					return;
3416 				root["status"] = "OK";
3417 				root["title"] = "DeleteAllSubDevices";
3418 				result = m_sql.safe_query("DELETE FROM LightSubDevices WHERE (ParentID == '%q')", idx.c_str());
3419 			}
3420 			else if (cparam == "deletesubdevice")
3421 			{
3422 				if (session.rights < 2)
3423 				{
3424 					session.reply_status = reply::forbidden;
3425 					return; //Only admin user allowed
3426 				}
3427 
3428 				std::string idx = request::findValue(&req, "idx");
3429 				if (idx.empty())
3430 					return;
3431 				root["status"] = "OK";
3432 				root["title"] = "DeleteSubDevice";
3433 				result = m_sql.safe_query("DELETE FROM LightSubDevices WHERE (ID == '%q')", idx.c_str());
3434 			}
3435 			else if (cparam == "addsubdevice")
3436 			{
3437 				if (session.rights < 2)
3438 				{
3439 					session.reply_status = reply::forbidden;
3440 					return; //Only admin user allowed
3441 				}
3442 
3443 				std::string idx = request::findValue(&req, "idx");
3444 				std::string subidx = request::findValue(&req, "subidx");
3445 				if ((idx.empty()) || (subidx.empty()))
3446 					return;
3447 				if (idx == subidx)
3448 					return;
3449 
3450 				//first check if it is not already a sub device
3451 				result = m_sql.safe_query("SELECT ID FROM LightSubDevices WHERE (DeviceRowID=='%q') AND (ParentID =='%q')",
3452 					subidx.c_str(), idx.c_str());
3453 				if (result.empty())
3454 				{
3455 					root["status"] = "OK";
3456 					root["title"] = "AddSubDevice";
3457 					//no it is not, add it
3458 					result = m_sql.safe_query(
3459 						"INSERT INTO LightSubDevices (DeviceRowID, ParentID) VALUES ('%q','%q')",
3460 						subidx.c_str(),
3461 						idx.c_str()
3462 					);
3463 				}
3464 			}
3465 			else if (cparam == "addscenedevice")
3466 			{
3467 				if (session.rights < 2)
3468 				{
3469 					session.reply_status = reply::forbidden;
3470 					return; //Only admin user allowed
3471 				}
3472 
3473 				std::string idx = request::findValue(&req, "idx");
3474 				std::string devidx = request::findValue(&req, "devidx");
3475 				std::string isscene = request::findValue(&req, "isscene");
3476 				std::string scommand = request::findValue(&req, "command");
3477 				int ondelay = atoi(request::findValue(&req, "ondelay").c_str());
3478 				int offdelay = atoi(request::findValue(&req, "offdelay").c_str());
3479 
3480 				if (
3481 					(idx.empty()) ||
3482 					(devidx.empty()) ||
3483 					(isscene.empty())
3484 					)
3485 					return;
3486 				int level = -1;
3487 				if (request::hasValue(&req, "level"))
3488 					level = atoi(request::findValue(&req, "level").c_str());
3489 				std::string color = _tColor(request::findValue(&req, "color")).toJSONString(); //Parse the color to detect incorrectly formatted color data
3490 
3491 				unsigned char command = 0;
3492 				result = m_sql.safe_query("SELECT HardwareID, DeviceID, Unit, Type, SubType, SwitchType, Options FROM DeviceStatus WHERE (ID=='%q')",
3493 					devidx.c_str());
3494 				if (!result.empty())
3495 				{
3496 					int dType = atoi(result[0][3].c_str());
3497 					int sType = atoi(result[0][4].c_str());
3498 					_eSwitchType switchtype = (_eSwitchType)atoi(result[0][5].c_str());
3499 					std::map<std::string, std::string> options = m_sql.BuildDeviceOptions(result[0][6].c_str());
3500 					GetLightCommand(dType, sType, switchtype, scommand, command, options);
3501 				}
3502 
3503 				//first check if this device is not the scene code!
3504 				result = m_sql.safe_query("SELECT Activators, SceneType FROM Scenes WHERE (ID=='%q')", idx.c_str());
3505 				if (!result.empty())
3506 				{
3507 					//int SceneType = atoi(result[0][1].c_str());
3508 
3509 					std::vector<std::string> arrayActivators;
3510 					StringSplit(result[0][0], ";", arrayActivators);
3511 					for (const auto & ittAct : arrayActivators)
3512 					{
3513 						std::string sCodeCmd = ittAct;
3514 
3515 						std::vector<std::string> arrayCode;
3516 						StringSplit(sCodeCmd, ":", arrayCode);
3517 
3518 						std::string sID = arrayCode[0];
3519 						std::string sCode = "";
3520 						if (arrayCode.size() == 2)
3521 						{
3522 							sCode = arrayCode[1];
3523 						}
3524 
3525 						if (sID == devidx)
3526 						{
3527 							return; //Group does not work with separate codes, so already there
3528 						}
3529 					}
3530 				}
3531 				//first check if it is not already a part of this scene/group (with the same OnDelay)
3532 				if (isscene == "true") {
3533 					result = m_sql.safe_query("SELECT ID FROM SceneDevices WHERE (DeviceRowID=='%q') AND (SceneRowID =='%q') AND (OnDelay == %d) AND (OffDelay == %d) AND (Cmd == %d)",
3534 						devidx.c_str(), idx.c_str(), ondelay, offdelay, command);
3535 				}
3536 				else {
3537 					result = m_sql.safe_query("SELECT ID FROM SceneDevices WHERE (DeviceRowID=='%q') AND (SceneRowID =='%q') AND (OnDelay == %d)",
3538 						devidx.c_str(), idx.c_str(), ondelay);
3539 				}
3540 				if (result.empty())
3541 				{
3542 					root["status"] = "OK";
3543 					root["title"] = "AddSceneDevice";
3544 					//no it is not, add it
3545 					if (isscene == "true")
3546 					{
3547 						m_sql.safe_query(
3548 							"INSERT INTO SceneDevices (DeviceRowID, SceneRowID, Cmd, Level, Color, OnDelay, OffDelay) VALUES ('%q','%q',%d,%d,'%q',%d,%d)",
3549 							devidx.c_str(),
3550 							idx.c_str(),
3551 							command,
3552 							level,
3553 							color.c_str(),
3554 							ondelay,
3555 							offdelay
3556 						);
3557 					}
3558 					else
3559 					{
3560 						m_sql.safe_query(
3561 							"INSERT INTO SceneDevices (DeviceRowID, SceneRowID, Level, Color, OnDelay, OffDelay) VALUES ('%q','%q',%d,'%q',%d,%d)",
3562 							devidx.c_str(),
3563 							idx.c_str(),
3564 							level,
3565 							color.c_str(),
3566 							ondelay,
3567 							offdelay
3568 						);
3569 					}
3570 					if (m_sql.m_bEnableEventSystem)
3571 						m_mainworker.m_eventsystem.GetCurrentScenesGroups();
3572 				}
3573 			}
3574 			else if (cparam == "updatescenedevice")
3575 			{
3576 				if (session.rights < 2)
3577 				{
3578 					session.reply_status = reply::forbidden;
3579 					return; //Only admin user allowed
3580 				}
3581 
3582 				std::string idx = request::findValue(&req, "idx");
3583 				std::string devidx = request::findValue(&req, "devidx");
3584 				std::string scommand = request::findValue(&req, "command");
3585 				int ondelay = atoi(request::findValue(&req, "ondelay").c_str());
3586 				int offdelay = atoi(request::findValue(&req, "offdelay").c_str());
3587 
3588 				if (
3589 					(idx.empty()) ||
3590 					(devidx.empty())
3591 					)
3592 					return;
3593 
3594 				unsigned char command = 0;
3595 
3596 				result = m_sql.safe_query("SELECT HardwareID, DeviceID, Unit, Type, SubType, SwitchType, Options FROM DeviceStatus WHERE (ID=='%q')",
3597 					devidx.c_str());
3598 				if (!result.empty())
3599 				{
3600 					int dType = atoi(result[0][3].c_str());
3601 					int sType = atoi(result[0][4].c_str());
3602 					_eSwitchType switchtype = (_eSwitchType)atoi(result[0][5].c_str());
3603 					std::map<std::string, std::string> options = m_sql.BuildDeviceOptions(result[0][6].c_str());
3604 					GetLightCommand(dType, sType, switchtype, scommand, command, options);
3605 				}
3606 				int level = -1;
3607 				if (request::hasValue(&req, "level"))
3608 					level = atoi(request::findValue(&req, "level").c_str());
3609 				std::string color = _tColor(request::findValue(&req, "color")).toJSONString(); //Parse the color to detect incorrectly formatted color data
3610 				root["status"] = "OK";
3611 				root["title"] = "UpdateSceneDevice";
3612 				result = m_sql.safe_query(
3613 					"UPDATE SceneDevices SET Cmd=%d, Level=%d, Color='%q', OnDelay=%d, OffDelay=%d  WHERE (ID == '%q')",
3614 					command, level, color.c_str(), ondelay, offdelay, idx.c_str());
3615 			}
3616 			else if (cparam == "deletescenedevice")
3617 			{
3618 				if (session.rights < 2)
3619 				{
3620 					session.reply_status = reply::forbidden;
3621 					return; //Only admin user allowed
3622 				}
3623 
3624 				std::string idx = request::findValue(&req, "idx");
3625 				if (idx.empty())
3626 					return;
3627 				root["status"] = "OK";
3628 				root["title"] = "DeleteSceneDevice";
3629 				m_sql.safe_query("DELETE FROM SceneDevices WHERE (ID == '%q')", idx.c_str());
3630 				m_sql.safe_query("DELETE FROM CamerasActiveDevices WHERE (DevSceneType==1) AND (DevSceneRowID == '%q')", idx.c_str());
3631 				if (m_sql.m_bEnableEventSystem)
3632 					m_mainworker.m_eventsystem.GetCurrentScenesGroups();
3633 			}
3634 			else if (cparam == "getsubdevices")
3635 			{
3636 				std::string idx = request::findValue(&req, "idx");
3637 				if (idx.empty())
3638 					return;
3639 
3640 				root["status"] = "OK";
3641 				root["title"] = "GetSubDevices";
3642 				result = m_sql.safe_query("SELECT a.ID, b.Name FROM LightSubDevices a, DeviceStatus b WHERE (a.ParentID=='%q') AND (b.ID == a.DeviceRowID)",
3643 					idx.c_str());
3644 				if (!result.empty())
3645 				{
3646 					int ii = 0;
3647 					for (const auto & itt : result)
3648 					{
3649 						std::vector<std::string> sd = itt;
3650 
3651 						root["result"][ii]["ID"] = sd[0];
3652 						root["result"][ii]["Name"] = sd[1];
3653 						ii++;
3654 					}
3655 				}
3656 			}
3657 			else if (cparam == "getscenedevices")
3658 			{
3659 				std::string idx = request::findValue(&req, "idx");
3660 				std::string isscene = request::findValue(&req, "isscene");
3661 
3662 				if (
3663 					(idx.empty()) ||
3664 					(isscene.empty())
3665 					)
3666 					return;
3667 
3668 				root["status"] = "OK";
3669 				root["title"] = "GetSceneDevices";
3670 
3671 				result = m_sql.safe_query("SELECT a.ID, b.Name, a.DeviceRowID, b.Type, b.SubType, b.nValue, b.sValue, a.Cmd, a.Level, b.ID, a.[Order], a.Color, a.OnDelay, a.OffDelay, b.SwitchType FROM SceneDevices a, DeviceStatus b WHERE (a.SceneRowID=='%q') AND (b.ID == a.DeviceRowID) ORDER BY a.[Order]",
3672 					idx.c_str());
3673 				if (!result.empty())
3674 				{
3675 					int ii = 0;
3676 					for (const auto & itt : result)
3677 					{
3678 						std::vector<std::string> sd = itt;
3679 
3680 						root["result"][ii]["ID"] = sd[0];
3681 						root["result"][ii]["Name"] = sd[1];
3682 						root["result"][ii]["DevID"] = sd[2];
3683 						root["result"][ii]["DevRealIdx"] = sd[9];
3684 						root["result"][ii]["Order"] = atoi(sd[10].c_str());
3685 						root["result"][ii]["OnDelay"] = atoi(sd[12].c_str());
3686 						root["result"][ii]["OffDelay"] = atoi(sd[13].c_str());
3687 
3688 						_eSwitchType switchtype = (_eSwitchType)atoi(sd[14].c_str());
3689 
3690 						unsigned char devType = atoi(sd[3].c_str());
3691 
3692 						//switchtype seemed not to be used down with the GetLightStatus command,
3693 						//causing RFY to go wrong, fixing here
3694 						if (devType != pTypeRFY)
3695 							switchtype = STYPE_OnOff;
3696 
3697 						unsigned char subType = atoi(sd[4].c_str());
3698 						//unsigned char nValue = (unsigned char)atoi(sd[5].c_str());
3699 						std::string sValue = sd[6];
3700 						int command = atoi(sd[7].c_str());
3701 						int level = atoi(sd[8].c_str());
3702 
3703 						std::string lstatus = "";
3704 						int llevel = 0;
3705 						bool bHaveDimmer = false;
3706 						bool bHaveGroupCmd = false;
3707 						int maxDimLevel = 0;
3708 						GetLightStatus(devType, subType, switchtype, command, sValue, lstatus, llevel, bHaveDimmer, maxDimLevel, bHaveGroupCmd);
3709 						root["result"][ii]["Command"] = lstatus;
3710 						root["result"][ii]["Level"] = level;
3711 						root["result"][ii]["Color"] = _tColor(sd[11]).toJSONString();
3712 						root["result"][ii]["Type"] = RFX_Type_Desc(devType, 1);
3713 						root["result"][ii]["SubType"] = RFX_Type_SubType_Desc(devType, subType);
3714 						ii++;
3715 					}
3716 				}
3717 			}
3718 			else if (cparam == "changescenedeviceorder")
3719 			{
3720 				if (session.rights < 2)
3721 				{
3722 					session.reply_status = reply::forbidden;
3723 					return; //Only admin user allowed
3724 				}
3725 
3726 				std::string idx = request::findValue(&req, "idx");
3727 				if (idx.empty())
3728 					return;
3729 				std::string sway = request::findValue(&req, "way");
3730 				if (sway.empty())
3731 					return;
3732 				bool bGoUp = (sway == "0");
3733 
3734 				std::string aScene, aOrder, oID, oOrder;
3735 
3736 				//Get actual device order
3737 				result = m_sql.safe_query("SELECT SceneRowID, [Order] FROM SceneDevices WHERE (ID=='%q')",
3738 					idx.c_str());
3739 				if (result.empty())
3740 					return;
3741 				aScene = result[0][0];
3742 				aOrder = result[0][1];
3743 
3744 				if (!bGoUp)
3745 				{
3746 					//Get next device order
3747 					result = m_sql.safe_query("SELECT ID, [Order] FROM SceneDevices WHERE (SceneRowID=='%q' AND [Order]>'%q') ORDER BY [Order] ASC",
3748 						aScene.c_str(), aOrder.c_str());
3749 					if (result.empty())
3750 						return;
3751 					oID = result[0][0];
3752 					oOrder = result[0][1];
3753 				}
3754 				else
3755 				{
3756 					//Get previous device order
3757 					result = m_sql.safe_query("SELECT ID, [Order] FROM SceneDevices WHERE (SceneRowID=='%q' AND [Order]<'%q') ORDER BY [Order] DESC",
3758 						aScene.c_str(), aOrder.c_str());
3759 					if (result.empty())
3760 						return;
3761 					oID = result[0][0];
3762 					oOrder = result[0][1];
3763 				}
3764 				//Swap them
3765 				root["status"] = "OK";
3766 				root["title"] = "ChangeSceneDeviceOrder";
3767 
3768 				result = m_sql.safe_query("UPDATE SceneDevices SET [Order] = '%q' WHERE (ID='%q')",
3769 					oOrder.c_str(), idx.c_str());
3770 				result = m_sql.safe_query("UPDATE SceneDevices SET [Order] = '%q' WHERE (ID='%q')",
3771 					aOrder.c_str(), oID.c_str());
3772 			}
3773 			else if (cparam == "deleteallscenedevices")
3774 			{
3775 				if (session.rights < 2)
3776 				{
3777 					session.reply_status = reply::forbidden;
3778 					return; //Only admin user allowed
3779 				}
3780 
3781 				std::string idx = request::findValue(&req, "idx");
3782 				if (idx.empty())
3783 					return;
3784 				root["status"] = "OK";
3785 				root["title"] = "DeleteAllSceneDevices";
3786 				result = m_sql.safe_query("DELETE FROM SceneDevices WHERE (SceneRowID == %q)", idx.c_str());
3787 			}
3788 			else if (cparam == "getmanualhardware")
3789 			{
3790 				//used by Add Manual Light/Switch dialog
3791 				root["status"] = "OK";
3792 				root["title"] = "GetHardware";
3793 				result = m_sql.safe_query("SELECT ID, Name, Type FROM Hardware ORDER BY ID ASC");
3794 				if (!result.empty())
3795 				{
3796 					int ii = 0;
3797 					for (const auto & itt : result)
3798 					{
3799 						std::vector<std::string> sd = itt;
3800 
3801 						int ID = atoi(sd[0].c_str());
3802 						std::string Name = sd[1];
3803 						_eHardwareTypes Type = (_eHardwareTypes)atoi(sd[2].c_str());
3804 
3805 						if (
3806 							(Type == HTYPE_RFXLAN) ||
3807 							(Type == HTYPE_RFXtrx315) ||
3808 							(Type == HTYPE_RFXtrx433) ||
3809 							(Type == HTYPE_RFXtrx868) ||
3810 							(Type == HTYPE_EnOceanESP2) ||
3811 							(Type == HTYPE_EnOceanESP3) ||
3812 							(Type == HTYPE_Dummy) ||
3813 							(Type == HTYPE_Tellstick) ||
3814 							(Type == HTYPE_EVOHOME_SCRIPT) ||
3815 							(Type == HTYPE_EVOHOME_SERIAL) ||
3816 							(Type == HTYPE_EVOHOME_WEB) ||
3817 							(Type == HTYPE_EVOHOME_TCP) ||
3818 							(Type == HTYPE_RaspberryGPIO) ||
3819 							(Type == HTYPE_RFLINKUSB) ||
3820 							(Type == HTYPE_RFLINKTCP) ||
3821 							(Type == HTYPE_ZIBLUEUSB) ||
3822 							(Type == HTYPE_ZIBLUETCP) ||
3823 							(Type == HTYPE_OpenWebNetTCP) ||
3824 							(Type == HTYPE_OpenWebNetUSB) ||
3825 							(Type == HTYPE_SysfsGpio) ||
3826 							(Type == HTYPE_USBtinGateway)
3827 							)
3828 						{
3829 							root["result"][ii]["idx"] = ID;
3830 							root["result"][ii]["Name"] = Name;
3831 							ii++;
3832 						}
3833 					}
3834 				}
3835 			}
3836 			else if (cparam == "getgpio")
3837 			{
3838 				//used by Add Manual Light/Switch dialog
3839 				root["title"] = "GetGpio";
3840 #ifdef WITH_GPIO
3841 				std::vector<CGpioPin> pins = CGpio::GetPinList();
3842 				if (pins.size() == 0) {
3843 					root["status"] = "ERROR";
3844 					root["result"][0]["idx"] = 0;
3845 					root["result"][0]["Name"] = "GPIO INIT ERROR";
3846 				}
3847 				else {
3848 					int ii = 0;
3849 					for (const auto & it : pins)
3850 					{
3851 						CGpioPin pin = it;
3852 						root["status"] = "OK";
3853 						root["result"][ii]["idx"] = pin.GetPin();
3854 						root["result"][ii]["Name"] = pin.ToString();
3855 						ii++;
3856 					}
3857 				}
3858 #else
3859 				root["status"] = "OK";
3860 				root["result"][0]["idx"] = 0;
3861 				root["result"][0]["Name"] = "N/A";
3862 #endif
3863 			}
3864 			else if (cparam == "getsysfsgpio")
3865 			{
3866 				//used by Add Manual Light/Switch dialog
3867 				root["title"] = "GetSysfsGpio";
3868 #ifdef WITH_GPIO
3869 				std::vector<int> gpio_ids = CSysfsGpio::GetGpioIds();
3870 				std::vector<std::string> gpio_names = CSysfsGpio::GetGpioNames();
3871 
3872 				if (gpio_ids.size() == 0) {
3873 					root["status"] = "ERROR";
3874 					root["result"][0]["idx"] = 0;
3875 					root["result"][0]["Name"] = "No sysfs-gpio exports";
3876 				}
3877 				else {
3878 					for (int ii = 0; ii < gpio_ids.size(); ii++)
3879 					{
3880 						root["status"] = "OK";
3881 						root["result"][ii]["idx"] = gpio_ids[ii];
3882 						root["result"][ii]["Name"] = gpio_names[ii];
3883 			}
3884 				}
3885 #else
3886 				root["status"] = "OK";
3887 				root["result"][0]["idx"] = 0;
3888 				root["result"][0]["Name"] = "N/A";
3889 #endif
3890 			}
3891 			else if (cparam == "getlightswitches")
3892 			{
3893 				root["status"] = "OK";
3894 				root["title"] = "GetLightSwitches";
3895 				result = m_sql.safe_query("SELECT ID, Name, Type, SubType, Used, SwitchType, Options FROM DeviceStatus ORDER BY Name");
3896 				if (!result.empty())
3897 				{
3898 					int ii = 0;
3899 					for (const auto & itt : result)
3900 					{
3901 						std::vector<std::string> sd = itt;
3902 
3903 						std::string ID = sd[0];
3904 						std::string Name = sd[1];
3905 						int Type = atoi(sd[2].c_str());
3906 						int SubType = atoi(sd[3].c_str());
3907 						int used = atoi(sd[4].c_str());
3908 						_eSwitchType switchtype = (_eSwitchType)atoi(sd[5].c_str());
3909 						std::map<std::string, std::string> options = m_sql.BuildDeviceOptions(sd[6]);
3910 						bool bdoAdd = false;
3911 						switch (Type)
3912 						{
3913 						case pTypeLighting1:
3914 						case pTypeLighting2:
3915 						case pTypeLighting3:
3916 						case pTypeLighting4:
3917 						case pTypeLighting5:
3918 						case pTypeLighting6:
3919 						case pTypeFan:
3920 						case pTypeColorSwitch:
3921 						case pTypeSecurity1:
3922 						case pTypeSecurity2:
3923 						case pTypeEvohome:
3924 						case pTypeEvohomeRelay:
3925 						case pTypeCurtain:
3926 						case pTypeBlinds:
3927 						case pTypeRFY:
3928 						case pTypeChime:
3929 						case pTypeThermostat2:
3930 						case pTypeThermostat3:
3931 						case pTypeThermostat4:
3932 						case pTypeRemote:
3933 						case pTypeRadiator1:
3934 						case pTypeGeneralSwitch:
3935 						case pTypeHomeConfort:
3936 						case pTypeFS20:
3937 						case pTypeHunter:
3938 							bdoAdd = true;
3939 							if (!used)
3940 							{
3941 								bdoAdd = false;
3942 								//bool bIsSubDevice = false;
3943 								std::vector<std::vector<std::string> > resultSD;
3944 								resultSD = m_sql.safe_query("SELECT ID FROM LightSubDevices WHERE (DeviceRowID=='%q')",
3945 									sd[0].c_str());
3946 								if (resultSD.size() > 0)
3947 									bdoAdd = true;
3948 							}
3949 							if ((Type == pTypeRadiator1) && (SubType != sTypeSmartwaresSwitchRadiator))
3950 								bdoAdd = false;
3951 							if (bdoAdd)
3952 							{
3953 								int idx = atoi(ID.c_str());
3954 								if (!IsIdxForUser(&session, idx))
3955 									continue;
3956 								root["result"][ii]["idx"] = ID;
3957 								root["result"][ii]["Name"] = Name;
3958 								root["result"][ii]["Type"] = RFX_Type_Desc(Type, 1);
3959 								root["result"][ii]["SubType"] = RFX_Type_SubType_Desc(Type, SubType);
3960 								bool bIsDimmer = (
3961 									(switchtype == STYPE_Dimmer) ||
3962 									(switchtype == STYPE_BlindsPercentage) ||
3963 									(switchtype == STYPE_BlindsPercentageInverted) ||
3964 									(switchtype == STYPE_Selector)
3965 									);
3966 								root["result"][ii]["IsDimmer"] = bIsDimmer;
3967 
3968 								std::string dimmerLevels = "none";
3969 
3970 								if (bIsDimmer)
3971 								{
3972 									std::stringstream ss;
3973 
3974 									if (switchtype == STYPE_Selector) {
3975 										std::map<std::string, std::string> selectorStatuses;
3976 										GetSelectorSwitchStatuses(options, selectorStatuses);
3977 										bool levelOffHidden = (options["LevelOffHidden"] == "true");
3978 										for (int i = 0; i < (int)selectorStatuses.size(); i++) {
3979 											if (levelOffHidden && (i == 0)) {
3980 												continue;
3981 											}
3982 											if ((levelOffHidden && (i > 1)) || (i > 0)) {
3983 												ss << ",";
3984 											}
3985 											ss << i * 10;
3986 										}
3987 									}
3988 									else
3989 									{
3990 										int nValue = 0;
3991 										std::string sValue = "";
3992 										std::string lstatus = "";
3993 										int llevel = 0;
3994 										bool bHaveDimmer = false;
3995 										int maxDimLevel = 0;
3996 										bool bHaveGroupCmd = false;
3997 
3998 										GetLightStatus(Type, SubType, switchtype, nValue, sValue, lstatus, llevel, bHaveDimmer, maxDimLevel, bHaveGroupCmd);
3999 
4000 										for (int i = 0; i <= maxDimLevel; i++)
4001 										{
4002 											if (i != 0)
4003 											{
4004 												ss << ",";
4005 											}
4006 											ss << (int)float((100.0f / float(maxDimLevel))*i);
4007 										}
4008 									}
4009 									dimmerLevels = ss.str();
4010 								}
4011 								root["result"][ii]["DimmerLevels"] = dimmerLevels;
4012 								ii++;
4013 							}
4014 							break;
4015 						}
4016 					}
4017 				}
4018 			}
4019 			else if (cparam == "getlightswitchesscenes")
4020 			{
4021 				root["status"] = "OK";
4022 				root["title"] = "GetLightSwitchesScenes";
4023 				int ii = 0;
4024 
4025 				//First List/Switch Devices
4026 				result = m_sql.safe_query("SELECT ID, Name, Type, SubType, Used FROM DeviceStatus ORDER BY Name");
4027 				if (!result.empty())
4028 				{
4029 					for (const auto & itt : result)
4030 					{
4031 						std::vector<std::string> sd = itt;
4032 
4033 						std::string ID = sd[0];
4034 						std::string Name = sd[1];
4035 						int Type = atoi(sd[2].c_str());
4036 						int SubType = atoi(sd[3].c_str());
4037 						int used = atoi(sd[4].c_str());
4038 						if (used)
4039 						{
4040 							switch (Type)
4041 							{
4042 							case pTypeLighting1:
4043 							case pTypeLighting2:
4044 							case pTypeLighting3:
4045 							case pTypeLighting4:
4046 							case pTypeLighting5:
4047 							case pTypeLighting6:
4048 							case pTypeFan:
4049 							case pTypeColorSwitch:
4050 							case pTypeSecurity1:
4051 							case pTypeSecurity2:
4052 							case pTypeEvohome:
4053 							case pTypeEvohomeRelay:
4054 							case pTypeCurtain:
4055 							case pTypeBlinds:
4056 							case pTypeRFY:
4057 							case pTypeChime:
4058 							case pTypeThermostat2:
4059 							case pTypeThermostat3:
4060 							case pTypeThermostat4:
4061 							case pTypeRemote:
4062 							case pTypeGeneralSwitch:
4063 							case pTypeHomeConfort:
4064 							case pTypeFS20:
4065 							case pTypeHunter:
4066 								root["result"][ii]["type"] = 0;
4067 								root["result"][ii]["idx"] = ID;
4068 								root["result"][ii]["Name"] = "[Light/Switch] " + Name;
4069 								ii++;
4070 								break;
4071 							case pTypeRadiator1:
4072 								if (SubType == sTypeSmartwaresSwitchRadiator)
4073 								{
4074 									root["result"][ii]["type"] = 0;
4075 									root["result"][ii]["idx"] = ID;
4076 									root["result"][ii]["Name"] = "[Light/Switch] " + Name;
4077 									ii++;
4078 								}
4079 								break;
4080 							}
4081 						}
4082 					}
4083 				}//end light/switches
4084 
4085 				//Add Scenes
4086 				result = m_sql.safe_query("SELECT ID, Name FROM Scenes ORDER BY Name");
4087 				if (!result.empty())
4088 				{
4089 					for (const auto & itt : result)
4090 					{
4091 						std::vector<std::string> sd = itt;
4092 
4093 						std::string ID = sd[0];
4094 						std::string Name = sd[1];
4095 
4096 						root["result"][ii]["type"] = 1;
4097 						root["result"][ii]["idx"] = ID;
4098 						root["result"][ii]["Name"] = "[Scene] " + Name;
4099 						ii++;
4100 					}
4101 				}//end light/switches
4102 			}
4103 			else if (cparam == "getcamactivedevices")
4104 			{
4105 				std::string idx = request::findValue(&req, "idx");
4106 				if (idx.empty())
4107 					return;
4108 				root["status"] = "OK";
4109 				root["title"] = "GetCameraActiveDevices";
4110 				//First List/Switch Devices
4111 				result = m_sql.safe_query("SELECT ID, DevSceneType, DevSceneRowID, DevSceneWhen, DevSceneDelay FROM CamerasActiveDevices WHERE (CameraRowID=='%q') ORDER BY ID",
4112 					idx.c_str());
4113 				if (!result.empty())
4114 				{
4115 					int ii = 0;
4116 					for (const auto & itt : result)
4117 					{
4118 						std::vector<std::string> sd = itt;
4119 
4120 						std::string ID = sd[0];
4121 						int DevSceneType = atoi(sd[1].c_str());
4122 						std::string DevSceneRowID = sd[2];
4123 						int DevSceneWhen = atoi(sd[3].c_str());
4124 						int DevSceneDelay = atoi(sd[4].c_str());
4125 
4126 						std::string Name = "";
4127 						if (DevSceneType == 0)
4128 						{
4129 							std::vector<std::vector<std::string> > result2;
4130 							result2 = m_sql.safe_query("SELECT Name FROM DeviceStatus WHERE (ID=='%q')",
4131 								DevSceneRowID.c_str());
4132 							if (!result2.empty())
4133 							{
4134 								Name = "[Light/Switches] " + result2[0][0];
4135 							}
4136 						}
4137 						else
4138 						{
4139 							std::vector<std::vector<std::string> > result2;
4140 							result2 = m_sql.safe_query("SELECT Name FROM Scenes WHERE (ID=='%q')",
4141 								DevSceneRowID.c_str());
4142 							if (!result2.empty())
4143 							{
4144 								Name = "[Scene] " + result2[0][0];
4145 							}
4146 						}
4147 						if (Name != "")
4148 						{
4149 							root["result"][ii]["idx"] = ID;
4150 							root["result"][ii]["type"] = DevSceneType;
4151 							root["result"][ii]["DevSceneRowID"] = DevSceneRowID;
4152 							root["result"][ii]["when"] = DevSceneWhen;
4153 							root["result"][ii]["delay"] = DevSceneDelay;
4154 							root["result"][ii]["Name"] = Name;
4155 							ii++;
4156 						}
4157 					}
4158 				}
4159 			}
4160 			else if (cparam == "addcamactivedevice")
4161 			{
4162 				if (session.rights < 2)
4163 				{
4164 					session.reply_status = reply::forbidden;
4165 					return; //Only admin user allowed
4166 				}
4167 
4168 				std::string idx = request::findValue(&req, "idx");
4169 				std::string activeidx = request::findValue(&req, "activeidx");
4170 				std::string sactivetype = request::findValue(&req, "activetype");
4171 				std::string sactivewhen = request::findValue(&req, "activewhen");
4172 				std::string sactivedelay = request::findValue(&req, "activedelay");
4173 
4174 				if (
4175 					(idx.empty()) ||
4176 					(activeidx.empty()) ||
4177 					(sactivetype.empty()) ||
4178 					(sactivewhen.empty()) ||
4179 					(sactivedelay.empty())
4180 					)
4181 				{
4182 					return;
4183 				}
4184 
4185 				int activetype = atoi(sactivetype.c_str());
4186 				int activewhen = atoi(sactivewhen.c_str());
4187 				int activedelay = atoi(sactivedelay.c_str());
4188 
4189 				//first check if it is not already a Active Device
4190 				result = m_sql.safe_query(
4191 					"SELECT ID FROM CamerasActiveDevices WHERE (CameraRowID=='%q')"
4192 					" AND (DevSceneType==%d) AND (DevSceneRowID=='%q')"
4193 					" AND (DevSceneWhen==%d)",
4194 					idx.c_str(), activetype, activeidx.c_str(), activewhen);
4195 				if (result.empty())
4196 				{
4197 					root["status"] = "OK";
4198 					root["title"] = "AddCameraActiveDevice";
4199 					//no it is not, add it
4200 					result = m_sql.safe_query(
4201 						"INSERT INTO CamerasActiveDevices (CameraRowID, DevSceneType, DevSceneRowID, DevSceneWhen, DevSceneDelay) VALUES ('%q',%d,'%q',%d,%d)",
4202 						idx.c_str(),
4203 						activetype,
4204 						activeidx.c_str(),
4205 						activewhen,
4206 						activedelay
4207 					);
4208 					m_mainworker.m_cameras.ReloadCameras();
4209 				}
4210 			}
4211 			else if (cparam == "deleteamactivedevice")
4212 			{
4213 				if (session.rights < 2)
4214 				{
4215 					session.reply_status = reply::forbidden;
4216 					return; //Only admin user allowed
4217 				}
4218 
4219 				std::string idx = request::findValue(&req, "idx");
4220 				if (idx.empty())
4221 					return;
4222 				root["status"] = "OK";
4223 				root["title"] = "DeleteCameraActiveDevice";
4224 				result = m_sql.safe_query("DELETE FROM CamerasActiveDevices WHERE (ID == '%q')", idx.c_str());
4225 				m_mainworker.m_cameras.ReloadCameras();
4226 			}
4227 			else if (cparam == "deleteallactivecamdevices")
4228 			{
4229 				if (session.rights < 2)
4230 				{
4231 					session.reply_status = reply::forbidden;
4232 					return; //Only admin user allowed
4233 				}
4234 
4235 				std::string idx = request::findValue(&req, "idx");
4236 				if (idx.empty())
4237 					return;
4238 				root["status"] = "OK";
4239 				root["title"] = "DeleteAllCameraActiveDevices";
4240 				result = m_sql.safe_query("DELETE FROM CamerasActiveDevices WHERE (CameraRowID == '%q')", idx.c_str());
4241 				m_mainworker.m_cameras.ReloadCameras();
4242 			}
4243 			else if (cparam == "testnotification")
4244 			{
4245 				if (session.rights < 2)
4246 				{
4247 					session.reply_status = reply::forbidden;
4248 					return; //Only admin user allowed
4249 				}
4250 
4251 				std::string notification_Title = "Domoticz test";
4252 				std::string notification_Message = "Domoticz test message!";
4253 				std::string subsystem = request::findValue(&req, "subsystem");
4254 
4255 				std::string extraData = request::findValue(&req, "extradata");
4256 
4257 				m_notifications.ConfigFromGetvars(req, false);
4258 				if (m_notifications.SendMessage(0, std::string(""), subsystem, notification_Title, notification_Message, extraData, 1, std::string(""), false)) {
4259 					root["status"] = "OK";
4260 				}
4261 				/* we need to reload the config, because the values that were set were only for testing */
4262 				m_notifications.LoadConfig();
4263 			}
4264 			else if (cparam == "testswitch")
4265 			{
4266 				std::string Username = "Admin";
4267 				if (!session.username.empty())
4268 					Username = session.username;
4269 				if (session.rights < 2)
4270 				{
4271 					session.reply_status = reply::forbidden;
4272 					return; //Only admin user allowed
4273 				}
4274 
4275 				std::string hwdid = request::findValue(&req, "hwdid");
4276 				std::string sswitchtype = request::findValue(&req, "switchtype");
4277 				std::string slighttype = request::findValue(&req, "lighttype");
4278 
4279 				if (
4280 					(hwdid.empty()) ||
4281 					(sswitchtype.empty()) ||
4282 					(slighttype.empty())
4283 					)
4284 					return;
4285 				_eSwitchType switchtype = (_eSwitchType)atoi(sswitchtype.c_str());
4286 				int lighttype = atoi(slighttype.c_str());
4287 				int dtype;
4288 				int subtype = 0;
4289 				std::string sunitcode;
4290 				std::string devid;
4291 
4292 				if (lighttype == 70)
4293 				{
4294 					//EnOcean (Lighting2 with Base_ID offset)
4295 					dtype = pTypeLighting2;
4296 					subtype = sTypeAC;
4297 					std::string sgroupcode = request::findValue(&req, "groupcode");
4298 					sunitcode = request::findValue(&req, "unitcode");
4299 					int iUnitTest = atoi(sunitcode.c_str());	//only First Rocker_ID at the moment, gives us 128 devices we can control, should be enough!
4300 					if (
4301 						(sunitcode.empty()) ||
4302 						(sgroupcode.empty()) ||
4303 						((iUnitTest < 1) || (iUnitTest > 128))
4304 						)
4305 						return;
4306 					sunitcode = sgroupcode;//Button A or B
4307 					CDomoticzHardwareBase *pBaseHardware = reinterpret_cast<CDomoticzHardwareBase*>(m_mainworker.GetHardware(atoi(hwdid.c_str())));
4308 					if (pBaseHardware == NULL)
4309 						return;
4310 					if ((pBaseHardware->HwdType != HTYPE_EnOceanESP2) && (pBaseHardware->HwdType != HTYPE_EnOceanESP3)
4311 						&& (pBaseHardware->HwdType != HTYPE_USBtinGateway) )
4312 						return;
4313 					unsigned long rID = 0;
4314 					if (pBaseHardware->HwdType == HTYPE_EnOceanESP2)
4315 					{
4316 						CEnOceanESP2 *pEnoceanHardware = reinterpret_cast<CEnOceanESP2 *>(pBaseHardware);
4317 						rID = pEnoceanHardware->m_id_base + iUnitTest;
4318 					}
4319 					else if (pBaseHardware->HwdType == HTYPE_EnOceanESP3)
4320 					{
4321 						CEnOceanESP3 *pEnoceanHardware = reinterpret_cast<CEnOceanESP3 *>(pBaseHardware);
4322 						rID = pEnoceanHardware->m_id_base + iUnitTest;
4323 					}
4324 					else if (pBaseHardware->HwdType == HTYPE_USBtinGateway) //Like EnOcean (Lighting2 with Base_ID offset)
4325 					{
4326 						USBtin *pUSBtinHardware = reinterpret_cast<USBtin *>(pBaseHardware);
4327 						//base ID calculate in the USBtinharwade dependant of the CAN Layer !
4328 						//for exemple see MultiblocV8 layer...
4329 						rID = pUSBtinHardware->switch_id_base;
4330 						std::stringstream ssunitcode;
4331 						ssunitcode << iUnitTest;
4332 						sunitcode = ssunitcode.str();
4333 					}
4334 					//convert to hex, and we have our ID
4335 					std::stringstream s_strid;
4336 					s_strid << std::hex << std::uppercase << rID;
4337 					devid = s_strid.str();
4338 				}
4339 				else if (lighttype == 68)
4340 				{
4341 #ifdef WITH_GPIO
4342 					dtype = pTypeLighting1;
4343 					subtype = sTypeIMPULS;
4344 					devid = "0";
4345 					sunitcode = request::findValue(&req, "unitcode"); //Unit code = GPIO number
4346 
4347 					if (sunitcode.empty()) {
4348 						root["status"] = "ERROR";
4349 						root["message"] = "No GPIO number given";
4350 						return;
4351 					}
4352 					CGpio *pGpio = reinterpret_cast<CGpio *>(m_mainworker.GetHardware(atoi(hwdid.c_str())));
4353 					if (pGpio == NULL) {
4354 						root["status"] = "ERROR";
4355 						root["message"] = "Could not retrieve GPIO hardware pointer";
4356 						return;
4357 					}
4358 					if (pGpio->HwdType != HTYPE_RaspberryGPIO) {
4359 						root["status"] = "ERROR";
4360 						root["message"] = "Given hardware is not GPIO";
4361 						return;
4362 					}
4363 					CGpioPin *pPin = CGpio::GetPPinById(atoi(sunitcode.c_str()));
4364 					if (pPin == NULL) {
4365 						root["status"] = "ERROR";
4366 						root["message"] = "Given pin does not exist on this GPIO hardware";
4367 						return;
4368 					}
4369 					if (pPin->GetIsInput()) {
4370 						root["status"] = "ERROR";
4371 						root["message"] = "Given pin is not configured for output";
4372 						return;
4373 			}
4374 #else
4375 					root["status"] = "ERROR";
4376 					root["message"] = "GPIO support is disabled";
4377 					return;
4378 #endif
4379 				}
4380 				else if (lighttype == 69)
4381 				{
4382 #ifdef WITH_GPIO
4383 
4384 					sunitcode = request::findValue(&req, "unitcode"); // sysfs-gpio number
4385 					int unitcode = atoi(sunitcode.c_str());
4386 					dtype = pTypeLighting2;
4387 					subtype = sTypeAC;
4388 					std::string sswitchtype = request::findValue(&req, "switchtype");
4389 					_eSwitchType switchtype = (_eSwitchType)atoi(sswitchtype.c_str());
4390 
4391 					std::string id = request::findValue(&req, "id");
4392 					if ((id.empty()) || (sunitcode.empty()))
4393 					{
4394 						return;
4395 					}
4396 					devid = id;
4397 
4398 					if (sunitcode.empty())
4399 					{
4400 						root["status"] = "ERROR";
4401 						root["message"] = "No GPIO number given";
4402 						return;
4403 					}
4404 
4405 					CSysfsGpio *pSysfsGpio = reinterpret_cast<CSysfsGpio *>(m_mainworker.GetHardware(atoi(hwdid.c_str())));
4406 
4407 					if (pSysfsGpio == NULL) {
4408 						root["status"] = "ERROR";
4409 						root["message"] = "Could not retrieve SysfsGpio hardware pointer";
4410 						return;
4411 					}
4412 
4413 					if (pSysfsGpio->HwdType != HTYPE_SysfsGpio) {
4414 						root["status"] = "ERROR";
4415 						root["message"] = "Given hardware is not SysfsGpio";
4416 						return;
4417 					}
4418 #else
4419 					root["status"] = "ERROR";
4420 					root["message"] = "GPIO support is disabled";
4421 					return;
4422 #endif
4423 				}
4424 				else if (lighttype < 20)
4425 				{
4426 					dtype = pTypeLighting1;
4427 					subtype = lighttype;
4428 					std::string shousecode = request::findValue(&req, "housecode");
4429 					sunitcode = request::findValue(&req, "unitcode");
4430 					if (
4431 						(shousecode.empty()) ||
4432 						(sunitcode.empty())
4433 						)
4434 						return;
4435 					devid = shousecode;
4436 				}
4437 				else if (lighttype < 30)
4438 				{
4439 					dtype = pTypeLighting2;
4440 					subtype = lighttype - 20;
4441 					std::string id = request::findValue(&req, "id");
4442 					sunitcode = request::findValue(&req, "unitcode");
4443 					if (
4444 						(id.empty()) ||
4445 						(sunitcode.empty())
4446 						)
4447 						return;
4448 					devid = id;
4449 				}
4450 				else if (lighttype < 70)
4451 				{
4452 					dtype = pTypeLighting5;
4453 					subtype = lighttype - 50;
4454 					if (lighttype == 59)
4455 						subtype = sTypeIT;
4456 					std::string id = request::findValue(&req, "id");
4457 					sunitcode = request::findValue(&req, "unitcode");
4458 					if (
4459 						(id.empty()) ||
4460 						(sunitcode.empty())
4461 						)
4462 						return;
4463 					if ((subtype != sTypeEMW100) && (subtype != sTypeLivolo) && (subtype != sTypeLivolo1to10) && (subtype != sTypeRGB432W) && (subtype != sTypeIT))
4464 						devid = "00" + id;
4465 					else
4466 						devid = id;
4467 				}
4468 				else
4469 				{
4470 					if (lighttype == 100)
4471 					{
4472 						//Chime/ByronSX
4473 						dtype = pTypeChime;
4474 						subtype = sTypeByronSX;
4475 						std::string id = request::findValue(&req, "id");
4476 						sunitcode = request::findValue(&req, "unitcode");
4477 						if (
4478 							(id.empty()) ||
4479 							(sunitcode.empty())
4480 							)
4481 							return;
4482 						int iUnitCode = atoi(sunitcode.c_str()) - 1;
4483 						switch (iUnitCode)
4484 						{
4485 						case 0:
4486 							iUnitCode = chime_sound0;
4487 							break;
4488 						case 1:
4489 							iUnitCode = chime_sound1;
4490 							break;
4491 						case 2:
4492 							iUnitCode = chime_sound2;
4493 							break;
4494 						case 3:
4495 							iUnitCode = chime_sound3;
4496 							break;
4497 						case 4:
4498 							iUnitCode = chime_sound4;
4499 							break;
4500 						case 5:
4501 							iUnitCode = chime_sound5;
4502 							break;
4503 						case 6:
4504 							iUnitCode = chime_sound6;
4505 							break;
4506 						case 7:
4507 							iUnitCode = chime_sound7;
4508 							break;
4509 						}
4510 						sprintf(szTmp, "%d", iUnitCode);
4511 						sunitcode = szTmp;
4512 						devid = id;
4513 					}
4514 					else if (lighttype == 101)
4515 					{
4516 						//Curtain Harrison
4517 						dtype = pTypeCurtain;
4518 						subtype = sTypeHarrison;
4519 						std::string shousecode = request::findValue(&req, "housecode");
4520 						sunitcode = request::findValue(&req, "unitcode");
4521 						if (
4522 							(shousecode.empty()) ||
4523 							(sunitcode.empty())
4524 							)
4525 							return;
4526 						devid = shousecode;
4527 					}
4528 					else if (lighttype == 102)
4529 					{
4530 						//RFY
4531 						dtype = pTypeRFY;
4532 						subtype = sTypeRFY;
4533 						std::string id = request::findValue(&req, "id");
4534 						sunitcode = request::findValue(&req, "unitcode");
4535 						if (
4536 							(id.empty()) ||
4537 							(sunitcode.empty())
4538 							)
4539 							return;
4540 						devid = id;
4541 					}
4542 					else if (lighttype == 103)
4543 					{
4544 						//Meiantech
4545 						dtype = pTypeSecurity1;
4546 						subtype = sTypeMeiantech;
4547 						std::string id = request::findValue(&req, "id");
4548 						if (
4549 							(id.empty())
4550 							)
4551 							return;
4552 						devid = id;
4553 						sunitcode = "0";
4554 					}
4555 					else if (lighttype == 104)
4556 					{
4557 						//HE105
4558 						dtype = pTypeThermostat2;
4559 						subtype = sTypeHE105;
4560 						sunitcode = request::findValue(&req, "unitcode");
4561 						if (sunitcode.empty())
4562 							return;
4563 						//convert to hex, and we have our Unit Code
4564 						std::stringstream s_strid;
4565 						s_strid << std::hex << std::uppercase << sunitcode;
4566 						int iUnitCode;
4567 						s_strid >> iUnitCode;
4568 						sprintf(szTmp, "%d", iUnitCode);
4569 						sunitcode = szTmp;
4570 						devid = "1";
4571 					}
4572 					else if (lighttype == 105)
4573 					{
4574 						//ASA
4575 						dtype = pTypeRFY;
4576 						subtype = sTypeASA;
4577 						std::string id = request::findValue(&req, "id");
4578 						sunitcode = request::findValue(&req, "unitcode");
4579 						if (
4580 							(id.empty()) ||
4581 							(sunitcode.empty())
4582 							)
4583 							return;
4584 						devid = id;
4585 					}
4586 					else if (lighttype == 106)
4587 					{
4588 						//Blyss
4589 						dtype = pTypeLighting6;
4590 						subtype = sTypeBlyss;
4591 						std::string sgroupcode = request::findValue(&req, "groupcode");
4592 						sunitcode = request::findValue(&req, "unitcode");
4593 						std::string id = request::findValue(&req, "id");
4594 						if (
4595 							(sgroupcode.empty()) ||
4596 							(sunitcode.empty()) ||
4597 							(id.empty())
4598 							)
4599 							return;
4600 						devid = id + sgroupcode;
4601 					}
4602 					else if (lighttype == 107)
4603 					{
4604 						//RFY2
4605 						dtype = pTypeRFY;
4606 						subtype = sTypeRFY2;
4607 						std::string id = request::findValue(&req, "id");
4608 						sunitcode = request::findValue(&req, "unitcode");
4609 						if (
4610 							(id.empty()) ||
4611 							(sunitcode.empty())
4612 							)
4613 							return;
4614 						devid = id;
4615 					}
4616 					else if ((lighttype >= 200) && (lighttype < 300))
4617 					{
4618 						dtype = pTypeBlinds;
4619 						subtype = lighttype - 200;
4620 						std::string id = request::findValue(&req, "id");
4621 						sunitcode = request::findValue(&req, "unitcode");
4622 						if (
4623 							(id.empty()) ||
4624 							(sunitcode.empty())
4625 							)
4626 							return;
4627 						int iUnitCode = atoi(sunitcode.c_str());
4628 						sprintf(szTmp, "%d", iUnitCode);
4629 						sunitcode = szTmp;
4630 						devid = id;
4631 					}
4632 					else if (lighttype == 301)
4633 					{
4634 						//Smartwares Radiator
4635 						dtype = pTypeRadiator1;
4636 						subtype = sTypeSmartwaresSwitchRadiator;
4637 						std::string id = request::findValue(&req, "id");
4638 						sunitcode = request::findValue(&req, "unitcode");
4639 						if (
4640 							(id.empty()) ||
4641 							(sunitcode.empty())
4642 							)
4643 							return;
4644 						devid = id;
4645 					}
4646 					else if (lighttype == 302)
4647 					{
4648 						//Home Confort
4649 						dtype = pTypeHomeConfort;
4650 						subtype = sTypeHomeConfortTEL010;
4651 						std::string id = request::findValue(&req, "id");
4652 
4653 						std::string shousecode = request::findValue(&req, "housecode");
4654 						sunitcode = request::findValue(&req, "unitcode");
4655 						if (
4656 							(id.empty()) ||
4657 							(shousecode.empty()) ||
4658 							(sunitcode.empty())
4659 							)
4660 							return;
4661 
4662 						int iUnitCode = atoi(sunitcode.c_str());
4663 						sprintf(szTmp, "%d", iUnitCode);
4664 						sunitcode = szTmp;
4665 						sprintf(szTmp, "%02X", atoi(shousecode.c_str()));
4666 						shousecode = szTmp;
4667 						devid = id + shousecode;
4668 					}
4669 					else if (lighttype == 303)
4670 					{
4671 						//Selector Switch
4672 						dtype = pTypeGeneralSwitch;
4673 						subtype = sSwitchTypeSelector;
4674 						std::string id = request::findValue(&req, "id");
4675 						sunitcode = request::findValue(&req, "unitcode");
4676 						if (
4677 							(id.empty()) ||
4678 							(sunitcode.empty())
4679 							)
4680 							return;
4681 						devid = id;
4682 					}
4683 					else if (lighttype == 304)
4684 					{
4685 						//Itho CVE RFT
4686 						dtype = pTypeFan;
4687 						subtype = sTypeItho;
4688 						std::string id = request::findValue(&req, "id");
4689 						if (id.empty())
4690 							return;
4691 						devid = id;
4692 						sunitcode = "0";
4693 					}
4694 					else if (lighttype == 305)
4695 					{
4696 						//Lucci Air/DC
4697 						dtype = pTypeFan;
4698 						subtype = sTypeLucciAir;
4699 						std::string id = request::findValue(&req, "id");
4700 						if (id.empty())
4701 							return;
4702 						devid = id;
4703 						sunitcode = "0";
4704 					}
4705 					else if (lighttype == 306)
4706 					{
4707 						//Lucci Air DC
4708 						dtype = pTypeFan;
4709 						subtype = sTypeLucciAirDC;
4710 						std::string id = request::findValue(&req, "id");
4711 						if (id.empty())
4712 							return;
4713 						devid = id;
4714 						sunitcode = "0";
4715 					}
4716 					else if (lighttype == 307)
4717 					{
4718 						//Westinghouse
4719 						dtype = pTypeFan;
4720 						subtype = sTypeWestinghouse;
4721 						std::string id = request::findValue(&req, "id");
4722 						if (id.empty())
4723 							return;
4724 						devid = id;
4725 						sunitcode = "0";
4726 					}
4727 					else if (lighttype == 400) {
4728 						//Openwebnet Bus Blinds
4729 						dtype = pTypeGeneralSwitch;
4730 						subtype = sSwitchTypeAC;
4731 						devid = request::findValue(&req, "id");
4732 						sunitcode = request::findValue(&req, "unitcode");
4733 						if (
4734 							(devid.empty()) ||
4735 							(sunitcode.empty())
4736 							)
4737 							return;
4738 					}
4739 					else if (lighttype == 401) {
4740 						//Openwebnet Bus Lights
4741 						dtype = pTypeGeneralSwitch;
4742 						subtype = sSwitchTypeAC;
4743 						devid = request::findValue(&req, "id");
4744 						sunitcode = request::findValue(&req, "unitcode");
4745 						if (
4746 							(devid.empty()) ||
4747 							(sunitcode.empty())
4748 							)
4749 							return;
4750 					}
4751 					else if (lighttype == 402)
4752 					{
4753 						//Openwebnet Bus Auxiliary
4754 						dtype = pTypeGeneralSwitch;
4755 						subtype = sSwitchTypeAC;
4756 						devid = request::findValue(&req, "id");
4757 						sunitcode = request::findValue(&req, "unitcode");
4758 						if (
4759 							(devid.empty()) ||
4760 							(sunitcode.empty())
4761 							)
4762 							return;
4763 					}
4764 					else if (lighttype == 403) {
4765 						//Openwebnet Zigbee Blinds
4766 						dtype = pTypeGeneralSwitch;
4767 						subtype = sSwitchBlindsT2;
4768 						devid = request::findValue(&req, "id");
4769 						sunitcode = request::findValue(&req, "unitcode");
4770 						if (
4771 							(devid.empty()) ||
4772 							(sunitcode.empty())
4773 							)
4774 							return;
4775 					}
4776 					else if (lighttype == 404) {
4777 						//Light Openwebnet Zigbee
4778 						dtype = pTypeGeneralSwitch;
4779 						subtype = sSwitchLightT2;
4780 						devid = request::findValue(&req, "id");
4781 						sunitcode = request::findValue(&req, "unitcode");
4782 						if (
4783 							(devid.empty()) ||
4784 							(sunitcode.empty())
4785 							)
4786 							return;
4787 					}
4788 					else if ((lighttype == 405) || (lighttype == 406)) {
4789 						// Openwebnet Dry Contact / IR Detection
4790 						dtype = pTypeGeneralSwitch;
4791 						subtype = sSwitchContactT1;
4792 						devid = request::findValue(&req, "id");
4793 						sunitcode = request::findValue(&req, "unitcode");
4794 						if (
4795 							(devid.empty()) ||
4796 							(sunitcode.empty())
4797 							)
4798 							return;
4799 					}
4800 					else if (lighttype == 407) {
4801 						//Openwebnet Bus Custom
4802 						dtype = pTypeGeneralSwitch;
4803 						subtype = sSwitchTypeAC;
4804 						devid = request::findValue(&req, "id");
4805 						sunitcode = request::findValue(&req, "unitcode");
4806 						std::string StrParam1 = request::findValue(&req, "StrParam1");
4807 						if (
4808 							(devid.empty()) ||
4809 							(sunitcode.empty()) ||
4810 							(StrParam1.empty())
4811 							)
4812 						{
4813 							root["message"] = "Some field empty or not valid.";
4814 							return;
4815 						}
4816 					}
4817 				}
4818 				// ----------- If needed convert to GeneralSwitch type (for o.a. RFlink) -----------
4819 				CDomoticzHardwareBase *pBaseHardware = reinterpret_cast<CDomoticzHardwareBase*>(m_mainworker.GetHardware(atoi(hwdid.c_str())));
4820 				if (pBaseHardware != NULL)
4821 				{
4822 					if ((pBaseHardware->HwdType == HTYPE_RFLINKUSB) || (pBaseHardware->HwdType == HTYPE_RFLINKTCP)) {
4823 						ConvertToGeneralSwitchType(devid, dtype, subtype);
4824 					}
4825 				}
4826 				// -----------------------------------------------
4827 
4828 				root["status"] = "OK";
4829 				root["message"] = "OK";
4830 				root["title"] = "TestSwitch";
4831 				std::vector<std::string> sd;
4832 
4833 				sd.push_back(hwdid);
4834 				sd.push_back(devid);
4835 				sd.push_back(sunitcode);
4836 				sprintf(szTmp, "%d", dtype);
4837 				sd.push_back(szTmp);
4838 				sprintf(szTmp, "%d", subtype);
4839 				sd.push_back(szTmp);
4840 				sprintf(szTmp, "%d", switchtype);
4841 				sd.push_back(szTmp);
4842 				sd.push_back(""); //AddjValue2
4843 				sd.push_back(""); //nValue
4844 				sd.push_back(""); //sValue
4845 				sd.push_back(""); //Name
4846 				sd.push_back(""); //Options
4847 
4848 				std::string switchcmd = "On";
4849 				int level = 0;
4850 				if (lighttype == 70)
4851 				{
4852 					//Special EnOcean case, if it is a dimmer, set a dim value
4853 					if (switchtype == STYPE_Dimmer)
4854 						level = 5;
4855 				}
4856 				m_mainworker.SwitchLightInt(sd, switchcmd, level, NoColor, true, Username);
4857 			}
4858 			else if (cparam == "addswitch")
4859 			{
4860 				if (session.rights < 2)
4861 				{
4862 					session.reply_status = reply::forbidden;
4863 					return; //Only admin user allowed
4864 				}
4865 
4866 				std::string hwdid = request::findValue(&req, "hwdid");
4867 				std::string name = HTMLSanitizer::Sanitize(request::findValue(&req, "name"));
4868 				std::string sswitchtype = request::findValue(&req, "switchtype");
4869 				std::string slighttype = request::findValue(&req, "lighttype");
4870 				std::string maindeviceidx = request::findValue(&req, "maindeviceidx");
4871 				std::string deviceoptions;
4872 
4873 				if (
4874 					(hwdid.empty()) ||
4875 					(sswitchtype.empty()) ||
4876 					(slighttype.empty()) ||
4877 					(name.empty())
4878 					)
4879 					return;
4880 				_eSwitchType switchtype = (_eSwitchType)atoi(sswitchtype.c_str());
4881 				int lighttype = atoi(slighttype.c_str());
4882 				int dtype = 0;
4883 				int subtype = 0;
4884 				std::string sunitcode;
4885 				std::string devid;
4886 				std::string StrParam1;
4887 
4888 #ifdef ENABLE_PYTHON
4889 				//check if HW is plugin
4890 				{
4891 					result = m_sql.safe_query("SELECT Type FROM Hardware WHERE (ID == '%q')", hwdid.c_str());
4892 					if (!result.empty())
4893 					{
4894 						std::vector<std::string> sd = result[0];
4895 						_eHardwareTypes Type = (_eHardwareTypes)atoi(sd[0].c_str());
4896 						if (Type == HTYPE_PythonPlugin)
4897 						{
4898 							// Not allowed to add device to plugin HW (plugin framework does not use key column "ID" but instead uses column "unit" as key)
4899 							_log.Log(LOG_ERROR, "CWebServer::HandleCommand addswitch: Not allowed to add device owned by plugin %u!", atoi(hwdid.c_str()));
4900 							root["message"] = "Not allowed to add switch to plugin HW!";
4901 							return;
4902 						}
4903 					}
4904 				}
4905 #endif
4906 
4907 				if (lighttype == 70)
4908 				{
4909 					//EnOcean (Lighting2 with Base_ID offset)
4910 					dtype = pTypeLighting2;
4911 					subtype = sTypeAC;
4912 					sunitcode = request::findValue(&req, "unitcode");
4913 					std::string sgroupcode = request::findValue(&req, "groupcode");
4914 					int iUnitTest = atoi(sunitcode.c_str());	//gives us 128 devices we can control, should be enough!
4915 					if (
4916 						(sunitcode.empty()) ||
4917 						(sgroupcode.empty()) ||
4918 						((iUnitTest < 1) || (iUnitTest > 128))
4919 						)
4920 						return;
4921 					sunitcode = sgroupcode;//Button A/B
4922 					CDomoticzHardwareBase *pBaseHardware = reinterpret_cast<CDomoticzHardwareBase*>(m_mainworker.GetHardware(atoi(hwdid.c_str())));
4923 					if (pBaseHardware == NULL)
4924 						return;
4925 					if ((pBaseHardware->HwdType != HTYPE_EnOceanESP2) && (pBaseHardware->HwdType != HTYPE_EnOceanESP3)
4926 						&& (pBaseHardware->HwdType != HTYPE_USBtinGateway) )
4927 						return;
4928 					unsigned long rID = 0;
4929 					if (pBaseHardware->HwdType == HTYPE_EnOceanESP2)
4930 					{
4931 						CEnOceanESP2 *pEnoceanHardware = reinterpret_cast<CEnOceanESP2*>(pBaseHardware);
4932 						if (pEnoceanHardware->m_id_base == 0)
4933 						{
4934 							root["message"] = "BaseID not found, is the hardware running?";
4935 							return;
4936 						}
4937 						rID = pEnoceanHardware->m_id_base + iUnitTest;
4938 					}
4939 					else if (pBaseHardware->HwdType == HTYPE_EnOceanESP3)
4940 					{
4941 						CEnOceanESP3 *pEnoceanHardware = reinterpret_cast<CEnOceanESP3*>(pBaseHardware);
4942 						if (pEnoceanHardware->m_id_base == 0)
4943 						{
4944 							root["message"] = "BaseID not found, is the hardware running?";
4945 							return;
4946 						}
4947 						rID = pEnoceanHardware->m_id_base + iUnitTest;
4948 					}
4949 					else if (pBaseHardware->HwdType == HTYPE_USBtinGateway)
4950 					{
4951 						USBtin *pUSBtinHardware = reinterpret_cast<USBtin *>(pBaseHardware);
4952 						rID = pUSBtinHardware->switch_id_base;
4953 						std::stringstream ssunitcode;
4954 						ssunitcode << iUnitTest;
4955 						sunitcode = ssunitcode.str();
4956 					}
4957 					//convert to hex, and we have our ID
4958 					std::stringstream s_strid;
4959 					s_strid << std::hex << std::uppercase << rID;
4960 					devid = s_strid.str();
4961 				}
4962 				else if (lighttype == 68)
4963 				{
4964 #ifdef WITH_GPIO
4965 					dtype = pTypeLighting1;
4966 					subtype = sTypeIMPULS;
4967 					devid = "0";
4968 					sunitcode = request::findValue(&req, "unitcode"); //Unit code = GPIO number
4969 
4970 					if (sunitcode.empty()) {
4971 						return;
4972 					}
4973 					CGpio *pGpio = reinterpret_cast<CGpio *>(m_mainworker.GetHardware(atoi(hwdid.c_str())));
4974 					if (pGpio == NULL) {
4975 						return;
4976 					}
4977 					if (pGpio->HwdType != HTYPE_RaspberryGPIO) {
4978 						return;
4979 					}
4980 					CGpioPin *pPin = CGpio::GetPPinById(atoi(sunitcode.c_str()));
4981 					if (pPin == NULL) {
4982 						return;
4983 			}
4984 #else
4985 					return;
4986 #endif
4987 				}
4988 				else if (lighttype == 69)
4989 				{
4990 #ifdef WITH_GPIO
4991 					dtype = pTypeLighting2;
4992 					subtype = sTypeAC;
4993 					devid = "0";
4994 					sunitcode = request::findValue(&req, "unitcode"); // sysfs-gpio number
4995 					int unitcode = atoi(sunitcode.c_str());
4996 					std::string sswitchtype = request::findValue(&req, "switchtype");
4997 					_eSwitchType switchtype = (_eSwitchType)atoi(sswitchtype.c_str());
4998 					std::string id = request::findValue(&req, "id");
4999 					CSysfsGpio::RequestDbUpdate(unitcode);
5000 
5001 					if ((id.empty()) || (sunitcode.empty()))
5002 					{
5003 						return;
5004 					}
5005 					devid = id;
5006 
5007 					CSysfsGpio *pSysfsGpio = reinterpret_cast<CSysfsGpio *>(m_mainworker.GetHardware(atoi(hwdid.c_str())));
5008 
5009 					if ((pSysfsGpio == NULL) || (pSysfsGpio->HwdType != HTYPE_SysfsGpio))
5010 					{
5011 						return;
5012 					}
5013 #else
5014 					return;
5015 #endif
5016 				}
5017 				else if (lighttype < 20)
5018 				{
5019 					dtype = pTypeLighting1;
5020 					subtype = lighttype;
5021 					std::string shousecode = request::findValue(&req, "housecode");
5022 					sunitcode = request::findValue(&req, "unitcode");
5023 					if (
5024 						(shousecode.empty()) ||
5025 						(sunitcode.empty())
5026 						)
5027 						return;
5028 					devid = shousecode;
5029 				}
5030 				else if (lighttype < 30)
5031 				{
5032 					dtype = pTypeLighting2;
5033 					subtype = lighttype - 20;
5034 					std::string id = request::findValue(&req, "id");
5035 					sunitcode = request::findValue(&req, "unitcode");
5036 					if (
5037 						(id.empty()) ||
5038 						(sunitcode.empty())
5039 						)
5040 						return;
5041 					devid = id;
5042 				}
5043 				else if (lighttype < 70)
5044 				{
5045 					dtype = pTypeLighting5;
5046 					subtype = lighttype - 50;
5047 					if (lighttype == 59)
5048 						subtype = sTypeIT;
5049 					std::string id = request::findValue(&req, "id");
5050 					sunitcode = request::findValue(&req, "unitcode");
5051 					if (
5052 						(id.empty()) ||
5053 						(sunitcode.empty())
5054 						)
5055 						return;
5056 					if ((subtype != sTypeEMW100) && (subtype != sTypeLivolo) && (subtype != sTypeLivolo1to10) && (subtype != sTypeRGB432W) && (subtype != sTypeLightwaveRF) && (subtype != sTypeIT))
5057 						devid = "00" + id;
5058 					else
5059 						devid = id;
5060 				}
5061 				else if (lighttype == 101)
5062 				{
5063 					//Curtain Harrison
5064 					dtype = pTypeCurtain;
5065 					subtype = sTypeHarrison;
5066 					std::string shousecode = request::findValue(&req, "housecode");
5067 					sunitcode = request::findValue(&req, "unitcode");
5068 					if (
5069 						(shousecode.empty()) ||
5070 						(sunitcode.empty())
5071 						)
5072 						return;
5073 					devid = shousecode;
5074 				}
5075 				else if (lighttype == 102)
5076 				{
5077 					//RFY
5078 					dtype = pTypeRFY;
5079 					subtype = sTypeRFY;
5080 					std::string id = request::findValue(&req, "id");
5081 					sunitcode = request::findValue(&req, "unitcode");
5082 					if (
5083 						(id.empty()) ||
5084 						(sunitcode.empty())
5085 						)
5086 						return;
5087 					devid = id;
5088 				}
5089 				else if (lighttype == 103)
5090 				{
5091 					//Meiantech
5092 					dtype = pTypeSecurity1;
5093 					subtype = sTypeMeiantech;
5094 					std::string id = request::findValue(&req, "id");
5095 					if (
5096 						(id.empty())
5097 						)
5098 						return;
5099 					devid = id;
5100 					sunitcode = "0";
5101 				}
5102 				else if (lighttype == 104)
5103 				{
5104 					//HE105
5105 					dtype = pTypeThermostat2;
5106 					subtype = sTypeHE105;
5107 					sunitcode = request::findValue(&req, "unitcode");
5108 					if (sunitcode.empty())
5109 						return;
5110 					//convert to hex, and we have our Unit Code
5111 					std::stringstream s_strid;
5112 					s_strid << std::hex << std::uppercase << sunitcode;
5113 					int iUnitCode;
5114 					s_strid >> iUnitCode;
5115 					sprintf(szTmp, "%d", iUnitCode);
5116 					sunitcode = szTmp;
5117 					devid = "1";
5118 				}
5119 				else if (lighttype == 105)
5120 				{
5121 					//ASA
5122 					dtype = pTypeRFY;
5123 					subtype = sTypeASA;
5124 					std::string id = request::findValue(&req, "id");
5125 					sunitcode = request::findValue(&req, "unitcode");
5126 					if (
5127 						(id.empty()) ||
5128 						(sunitcode.empty())
5129 						)
5130 						return;
5131 					devid = id;
5132 				}
5133 				else if (lighttype == 106)
5134 				{
5135 					//Blyss
5136 					dtype = pTypeLighting6;
5137 					subtype = sTypeBlyss;
5138 					std::string sgroupcode = request::findValue(&req, "groupcode");
5139 					sunitcode = request::findValue(&req, "unitcode");
5140 					std::string id = request::findValue(&req, "id");
5141 					if (
5142 						(sgroupcode.empty()) ||
5143 						(sunitcode.empty()) ||
5144 						(id.empty())
5145 						)
5146 						return;
5147 					devid = id + sgroupcode;
5148 				}
5149 				else if (lighttype == 107)
5150 				{
5151 					//RFY2
5152 					dtype = pTypeRFY;
5153 					subtype = sTypeRFY2;
5154 					std::string id = request::findValue(&req, "id");
5155 					sunitcode = request::findValue(&req, "unitcode");
5156 					if (
5157 						(id.empty()) ||
5158 						(sunitcode.empty())
5159 						)
5160 						return;
5161 					devid = id;
5162 				}
5163 				else
5164 				{
5165 					if (lighttype == 100)
5166 					{
5167 						//Chime/ByronSX
5168 						dtype = pTypeChime;
5169 						subtype = sTypeByronSX;
5170 						std::string id = request::findValue(&req, "id");
5171 						sunitcode = request::findValue(&req, "unitcode");
5172 						if (
5173 							(id.empty()) ||
5174 							(sunitcode.empty())
5175 							)
5176 							return;
5177 						int iUnitCode = atoi(sunitcode.c_str()) - 1;
5178 						switch (iUnitCode)
5179 						{
5180 						case 0:
5181 							iUnitCode = chime_sound0;
5182 							break;
5183 						case 1:
5184 							iUnitCode = chime_sound1;
5185 							break;
5186 						case 2:
5187 							iUnitCode = chime_sound2;
5188 							break;
5189 						case 3:
5190 							iUnitCode = chime_sound3;
5191 							break;
5192 						case 4:
5193 							iUnitCode = chime_sound4;
5194 							break;
5195 						case 5:
5196 							iUnitCode = chime_sound5;
5197 							break;
5198 						case 6:
5199 							iUnitCode = chime_sound6;
5200 							break;
5201 						case 7:
5202 							iUnitCode = chime_sound7;
5203 							break;
5204 						}
5205 						sprintf(szTmp, "%d", iUnitCode);
5206 						sunitcode = szTmp;
5207 						devid = id;
5208 					}
5209 					else if ((lighttype >= 200) && (lighttype < 300))
5210 					{
5211 						dtype = pTypeBlinds;
5212 						subtype = lighttype - 200;
5213 						std::string id = request::findValue(&req, "id");
5214 						sunitcode = request::findValue(&req, "unitcode");
5215 						if (
5216 							(id.empty()) ||
5217 							(sunitcode.empty())
5218 							)
5219 							return;
5220 						int iUnitCode = atoi(sunitcode.c_str());
5221 						sprintf(szTmp, "%d", iUnitCode);
5222 						sunitcode = szTmp;
5223 						devid = id;
5224 					}
5225 					else if (lighttype == 301)
5226 					{
5227 						//Smartwares Radiator
5228 						std::string id = request::findValue(&req, "id");
5229 						sunitcode = request::findValue(&req, "unitcode");
5230 						if (
5231 							(id.empty()) ||
5232 							(sunitcode.empty())
5233 							)
5234 							return;
5235 						devid = id;
5236 
5237 						//For this device, we will also need to add a Radiator type, do that first
5238 						dtype = pTypeRadiator1;
5239 						subtype = sTypeSmartwares;
5240 
5241 						//check if switch is unique
5242 						result = m_sql.safe_query(
5243 							"SELECT Name FROM DeviceStatus WHERE (HardwareID=='%q' AND DeviceID=='%q' AND Unit=='%q' AND Type==%d AND SubType==%d)",
5244 							hwdid.c_str(), devid.c_str(), sunitcode.c_str(), dtype, subtype);
5245 						if (!result.empty())
5246 						{
5247 							root["message"] = "Switch already exists!";
5248 							return;
5249 						}
5250 						bool bActEnabledState = m_sql.m_bAcceptNewHardware;
5251 						m_sql.m_bAcceptNewHardware = true;
5252 						std::string devname;
5253 						m_sql.UpdateValue(atoi(hwdid.c_str()), devid.c_str(), atoi(sunitcode.c_str()), dtype, subtype, 0, -1, 0, "20.5", devname);
5254 						m_sql.m_bAcceptNewHardware = bActEnabledState;
5255 
5256 						//set name and switchtype
5257 						result = m_sql.safe_query(
5258 							"SELECT ID FROM DeviceStatus WHERE (HardwareID=='%q' AND DeviceID=='%q' AND Unit=='%q' AND Type==%d AND SubType==%d)",
5259 							hwdid.c_str(), devid.c_str(), sunitcode.c_str(), dtype, subtype);
5260 						if (result.empty())
5261 						{
5262 							root["message"] = "Error finding switch in Database!?!?";
5263 							return;
5264 						}
5265 						std::string ID = result[0][0];
5266 
5267 						m_sql.safe_query(
5268 							"UPDATE DeviceStatus SET Used=1, Name='%q', SwitchType=%d WHERE (ID == '%q')",
5269 							name.c_str(), switchtype, ID.c_str());
5270 
5271 						//Now continue to insert the switch
5272 						dtype = pTypeRadiator1;
5273 						subtype = sTypeSmartwaresSwitchRadiator;
5274 					}
5275 					else if (lighttype == 302)
5276 					{
5277 						//Home Confort
5278 						dtype = pTypeHomeConfort;
5279 						subtype = sTypeHomeConfortTEL010;
5280 						std::string id = request::findValue(&req, "id");
5281 
5282 						std::string shousecode = request::findValue(&req, "housecode");
5283 						sunitcode = request::findValue(&req, "unitcode");
5284 						if (
5285 							(id.empty()) ||
5286 							(shousecode.empty()) ||
5287 							(sunitcode.empty())
5288 							)
5289 							return;
5290 
5291 						int iUnitCode = atoi(sunitcode.c_str());
5292 						sprintf(szTmp, "%d", iUnitCode);
5293 						sunitcode = szTmp;
5294 						sprintf(szTmp, "%02X", atoi(shousecode.c_str()));
5295 						shousecode = szTmp;
5296 						devid = id + shousecode;
5297 					}
5298 					else if (lighttype == 303)
5299 					{
5300 						//Selector Switch
5301 						dtype = pTypeGeneralSwitch;
5302 						subtype = sSwitchTypeSelector;
5303 						std::string id = request::findValue(&req, "id");
5304 						sunitcode = request::findValue(&req, "unitcode");
5305 						if (
5306 							(id.empty()) ||
5307 							(sunitcode.empty())
5308 							)
5309 							return;
5310 						devid = "0" + id;
5311 						switchtype = STYPE_Selector;
5312 						if (!deviceoptions.empty()) {
5313 							deviceoptions.append(";");
5314 						}
5315 						deviceoptions.append("SelectorStyle:0;LevelNames:Off|Level1|Level2|Level3");
5316 					}
5317 					else if (lighttype == 304)
5318 					{
5319 						//Itho CVE RFT
5320 						dtype = pTypeFan;
5321 						subtype = sTypeItho;
5322 						std::string id = request::findValue(&req, "id");
5323 						if (id.empty())
5324 							return;
5325 						devid = id;
5326 						sunitcode = "0";
5327 					}
5328 					else if (lighttype == 305)
5329 					{
5330 						//Lucci Air
5331 						dtype = pTypeFan;
5332 						subtype = sTypeLucciAir;
5333 						std::string id = request::findValue(&req, "id");
5334 						if (id.empty())
5335 							return;
5336 						devid = id;
5337 						sunitcode = "0";
5338 					}
5339 					else if (lighttype == 306)
5340 					{
5341 						//Lucci Air DC
5342 						dtype = pTypeFan;
5343 						subtype = sTypeLucciAirDC;
5344 						std::string id = request::findValue(&req, "id");
5345 						if (id.empty())
5346 							return;
5347 						devid = id;
5348 						sunitcode = "0";
5349 					}
5350 					else if (lighttype == 307)
5351 					{
5352 						//Westinghouse
5353 						dtype = pTypeFan;
5354 						subtype = sTypeWestinghouse;
5355 						std::string id = request::findValue(&req, "id");
5356 						if (id.empty())
5357 							return;
5358 						devid = id;
5359 						sunitcode = "0";
5360 					}
5361 					else if (lighttype == 400)
5362 					{
5363 						//Openwebnet Bus Blinds
5364 						dtype = pTypeGeneralSwitch;
5365 						subtype = sSwitchTypeAC;
5366 						devid = request::findValue(&req, "id");
5367 						sunitcode = request::findValue(&req, "unitcode");
5368 						if (
5369 							(devid.empty()) ||
5370 							(sunitcode.empty())
5371 							)
5372 							return;
5373 					}
5374 					else if (lighttype == 401)
5375 					{
5376 						//Openwebnet Bus Lights
5377 						dtype = pTypeGeneralSwitch;
5378 						subtype = sSwitchTypeAC;
5379 						devid = request::findValue(&req, "id");
5380 						sunitcode = request::findValue(&req, "unitcode");
5381 						if (
5382 							(devid.empty()) ||
5383 							(sunitcode.empty())
5384 							)
5385 							return;
5386 					}
5387 					else if (lighttype == 402)
5388 					{
5389 						//Openwebnet Bus Auxiliary
5390 						dtype = pTypeGeneralSwitch;
5391 						subtype = sSwitchTypeAC;
5392 						devid = request::findValue(&req, "id");
5393 						sunitcode = request::findValue(&req, "unitcode");
5394 						if (
5395 							(devid.empty()) ||
5396 							(sunitcode.empty())
5397 							)
5398 							return;
5399 					}
5400 					else if (lighttype == 403)
5401 					{
5402 						//Openwebnet Zigbee Blinds
5403 						dtype = pTypeGeneralSwitch;
5404 						subtype = sSwitchBlindsT2;
5405 						devid = request::findValue(&req, "id");
5406 						sunitcode = request::findValue(&req, "unitcode");
5407 						if (
5408 							(devid.empty()) ||
5409 							(sunitcode.empty())
5410 							)
5411 							return;
5412 					}
5413 					else if (lighttype == 404)
5414 					{
5415 						//Openwebnet Zigbee Lights
5416 						dtype = pTypeGeneralSwitch;
5417 						subtype = sSwitchLightT2;
5418 						devid = request::findValue(&req, "id");
5419 						sunitcode = request::findValue(&req, "unitcode");
5420 						if (
5421 							(devid.empty()) ||
5422 							(sunitcode.empty())
5423 							)
5424 							return;
5425 					}
5426 					else if ((lighttype == 405) || (lighttype == 406))
5427 					{
5428 						//Openwebnet Dry Contact / IR Detection
5429 						dtype = pTypeGeneralSwitch;
5430 						subtype = sSwitchContactT1;
5431 						devid = request::findValue(&req, "id");
5432 						sunitcode = request::findValue(&req, "unitcode");
5433 						if (
5434 							(devid.empty()) ||
5435 							(sunitcode.empty())
5436 							)
5437 							return;
5438 					}
5439 					else if (lighttype == 407) {
5440 						//Openwebnet Bus Custom
5441 						dtype = pTypeGeneralSwitch;
5442 						subtype = sSwitchTypeAC;
5443 						devid = request::findValue(&req, "id");
5444 						sunitcode = request::findValue(&req, "unitcode");
5445 						StrParam1 = request::findValue(&req, "StrParam1");
5446 						_log.Log(LOG_STATUS, "COpenWebNetTCP: custom command: '%s'", StrParam1.c_str());
5447 						if (
5448 							(devid.empty()) ||
5449 							(sunitcode.empty()) ||
5450 							(StrParam1.empty())
5451 							)
5452 						{
5453 							root["message"] = "Some field empty or not valid.";
5454 							return;
5455 						}
5456 					}
5457 				}
5458 
5459 				//check if switch is unique
5460 				result = m_sql.safe_query(
5461 					"SELECT Name FROM DeviceStatus WHERE (HardwareID=='%q' AND DeviceID=='%q' AND Unit=='%q' AND Type==%d AND SubType==%d)",
5462 					hwdid.c_str(), devid.c_str(), sunitcode.c_str(), dtype, subtype);
5463 				if (!result.empty())
5464 				{
5465 					root["message"] = "Switch already exists!";
5466 					return;
5467 				}
5468 
5469 				// ----------- If needed convert to GeneralSwitch type (for o.a. RFlink) -----------
5470 				CDomoticzHardwareBase *pBaseHardware = m_mainworker.GetHardware(atoi(hwdid.c_str()));
5471 				if (pBaseHardware != NULL)
5472 				{
5473 					if ((pBaseHardware->HwdType == HTYPE_RFLINKUSB) || (pBaseHardware->HwdType == HTYPE_RFLINKTCP)) {
5474 						ConvertToGeneralSwitchType(devid, dtype, subtype);
5475 					}
5476 				}
5477 				// -----------------------------------------------
5478 
5479 				bool bActEnabledState = m_sql.m_bAcceptNewHardware;
5480 				m_sql.m_bAcceptNewHardware = true;
5481 				std::string devname;
5482 				m_sql.UpdateValue(atoi(hwdid.c_str()), devid.c_str(), atoi(sunitcode.c_str()), dtype, subtype, 0, -1, 0, devname);
5483 				m_sql.m_bAcceptNewHardware = bActEnabledState;
5484 
5485 				//set name and switchtype
5486 				result = m_sql.safe_query(
5487 					"SELECT ID FROM DeviceStatus WHERE (HardwareID=='%q' AND DeviceID=='%q' AND Unit=='%q' AND Type==%d AND SubType==%d)",
5488 					hwdid.c_str(), devid.c_str(), sunitcode.c_str(), dtype, subtype);
5489 				if (result.empty())
5490 				{
5491 					root["message"] = "Error finding switch in Database!?!?";
5492 					return;
5493 				}
5494 				std::string ID = result[0][0];
5495 
5496 				m_sql.safe_query(
5497 					"UPDATE DeviceStatus SET Used=1, Name='%q', SwitchType=%d WHERE (ID == '%q')",
5498 					name.c_str(), switchtype, ID.c_str());
5499 
5500 				if (lighttype == 407) {
5501 					//Openwebnet Bus Custom
5502 					m_sql.safe_query(
5503 						"UPDATE DeviceStatus SET StrParam1='%s' WHERE (ID == '%q')",
5504 						StrParam1.c_str(), ID.c_str());
5505 				}
5506 
5507 				m_mainworker.m_eventsystem.GetCurrentStates();
5508 
5509 				//Set device options
5510 				m_sql.SetDeviceOptions(atoi(ID.c_str()), m_sql.BuildDeviceOptions(deviceoptions, false));
5511 
5512 				if (maindeviceidx != "")
5513 				{
5514 					if (maindeviceidx != ID)
5515 					{
5516 						//this is a sub device for another light/switch
5517 						//first check if it is not already a sub device
5518 						result = m_sql.safe_query(
5519 							"SELECT ID FROM LightSubDevices WHERE (DeviceRowID=='%q') AND (ParentID =='%q')",
5520 							ID.c_str(), maindeviceidx.c_str());
5521 						if (result.empty())
5522 						{
5523 							//no it is not, add it
5524 							result = m_sql.safe_query(
5525 								"INSERT INTO LightSubDevices (DeviceRowID, ParentID) VALUES ('%q','%q')",
5526 								ID.c_str(),
5527 								maindeviceidx.c_str()
5528 							);
5529 						}
5530 					}
5531 				}
5532 
5533 				root["status"] = "OK";
5534 				root["title"] = "AddSwitch";
5535 			}
5536 			else if (cparam == "getnotificationtypes")
5537 			{
5538 				if (session.rights < 2)
5539 				{
5540 					session.reply_status = reply::forbidden;
5541 					return; //Only admin user allowed
5542 				}
5543 
5544 				std::string idx = request::findValue(&req, "idx");
5545 				if (idx.empty())
5546 					return;
5547 				//First get Device Type/SubType
5548 				result = m_sql.safe_query("SELECT Type, SubType, SwitchType FROM DeviceStatus WHERE (ID == '%q')",
5549 					idx.c_str());
5550 				if (result.empty())
5551 					return;
5552 
5553 				root["status"] = "OK";
5554 				root["title"] = "GetNotificationTypes";
5555 				unsigned char dType = atoi(result[0][0].c_str());
5556 				unsigned char dSubType = atoi(result[0][1].c_str());
5557 				unsigned char switchtype = atoi(result[0][2].c_str());
5558 
5559 				int ii = 0;
5560 				if (
5561 					(dType == pTypeLighting1) ||
5562 					(dType == pTypeLighting2) ||
5563 					(dType == pTypeLighting3) ||
5564 					(dType == pTypeLighting4) ||
5565 					(dType == pTypeLighting5) ||
5566 					(dType == pTypeLighting6) ||
5567 					(dType == pTypeColorSwitch) ||
5568 					(dType == pTypeSecurity1) ||
5569 					(dType == pTypeSecurity2) ||
5570 					(dType == pTypeEvohome) ||
5571 					(dType == pTypeEvohomeRelay) ||
5572 					(dType == pTypeCurtain) ||
5573 					(dType == pTypeBlinds) ||
5574 					(dType == pTypeRFY) ||
5575 					(dType == pTypeChime) ||
5576 					(dType == pTypeThermostat2) ||
5577 					(dType == pTypeThermostat3) ||
5578 					(dType == pTypeThermostat4) ||
5579 					(dType == pTypeRemote) ||
5580 					(dType == pTypeGeneralSwitch) ||
5581 					(dType == pTypeHomeConfort) ||
5582 					(dType == pTypeFS20) ||
5583 					((dType == pTypeRadiator1) && (dSubType == sTypeSmartwaresSwitchRadiator))
5584 					)
5585 				{
5586 					if (switchtype != STYPE_PushOff)
5587 					{
5588 						root["result"][ii]["val"] = NTYPE_SWITCH_ON;
5589 						root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_SWITCH_ON, 0);
5590 						root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_SWITCH_ON, 1);
5591 						ii++;
5592 					}
5593 					if (switchtype != STYPE_PushOn)
5594 					{
5595 						root["result"][ii]["val"] = NTYPE_SWITCH_OFF;
5596 						root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_SWITCH_OFF, 0);
5597 						root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_SWITCH_OFF, 1);
5598 						ii++;
5599 					}
5600 					if (switchtype == STYPE_Media)
5601 					{
5602 						std::string idx = request::findValue(&req, "idx");
5603 
5604 						result = m_sql.safe_query("SELECT HardwareID FROM DeviceStatus WHERE (ID=='%q')", idx.c_str());
5605 						if (!result.empty())
5606 						{
5607 							std::string hdwid = result[0][0];
5608 							CDomoticzHardwareBase *pBaseHardware = reinterpret_cast<CDomoticzHardwareBase*>(m_mainworker.GetHardware(atoi(hdwid.c_str())));
5609 							if (pBaseHardware != NULL) {
5610 								_eHardwareTypes type = pBaseHardware->HwdType;
5611 								root["result"][ii]["val"] = NTYPE_PAUSED;
5612 								root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_PAUSED, 0);
5613 								root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_PAUSED, 1);
5614 								ii++;
5615 								if (type == HTYPE_Kodi) {
5616 									root["result"][ii]["val"] = NTYPE_AUDIO;
5617 									root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_AUDIO, 0);
5618 									root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_AUDIO, 1);
5619 									ii++;
5620 									root["result"][ii]["val"] = NTYPE_VIDEO;
5621 									root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_VIDEO, 0);
5622 									root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_VIDEO, 1);
5623 									ii++;
5624 									root["result"][ii]["val"] = NTYPE_PHOTO;
5625 									root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_PHOTO, 0);
5626 									root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_PHOTO, 1);
5627 									ii++;
5628 								}
5629 								if (type == HTYPE_LogitechMediaServer) {
5630 									root["result"][ii]["val"] = NTYPE_PLAYING;
5631 									root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_PLAYING, 0);
5632 									root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_PLAYING, 1);
5633 									ii++;
5634 									root["result"][ii]["val"] = NTYPE_STOPPED;
5635 									root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_STOPPED, 0);
5636 									root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_STOPPED, 1);
5637 									ii++;
5638 								}
5639 								if (type == HTYPE_HEOS) {
5640 									root["result"][ii]["val"] = NTYPE_PLAYING;
5641 									root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_PLAYING, 0);
5642 									root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_PLAYING, 1);
5643 									ii++;
5644 									root["result"][ii]["val"] = NTYPE_STOPPED;
5645 									root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_STOPPED, 0);
5646 									root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_STOPPED, 1);
5647 									ii++;
5648 								}
5649 							}
5650 						}
5651 					}
5652 				}
5653 				if (
5654 					(
5655 					(dType == pTypeTEMP) ||
5656 						(dType == pTypeTEMP_HUM) ||
5657 						(dType == pTypeTEMP_HUM_BARO) ||
5658 						(dType == pTypeTEMP_BARO) ||
5659 						(dType == pTypeEvohomeZone) ||
5660 						(dType == pTypeEvohomeWater) ||
5661 						(dType == pTypeThermostat1) ||
5662 						(dType == pTypeRego6XXTemp) ||
5663 						((dType == pTypeRFXSensor) && (dSubType == sTypeRFXSensorTemp))
5664 						) ||
5665 						((dType == pTypeUV) && (dSubType == sTypeUV3)) ||
5666 					((dType == pTypeWIND) && (dSubType == sTypeWIND4)) ||
5667 					((dType == pTypeWIND) && (dSubType == sTypeWINDNoTemp)) ||
5668 					((dType == pTypeGeneral) && (dSubType == sTypeSystemTemp))
5669 					)
5670 				{
5671 					root["result"][ii]["val"] = NTYPE_TEMPERATURE;
5672 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_TEMPERATURE, 0);
5673 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_TEMPERATURE, 1);
5674 					ii++;
5675 				}
5676 				if (
5677 					(dType == pTypeHUM) ||
5678 					(dType == pTypeTEMP_HUM) ||
5679 					(dType == pTypeTEMP_HUM_BARO)
5680 					)
5681 				{
5682 					root["result"][ii]["val"] = NTYPE_HUMIDITY;
5683 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_HUMIDITY, 0);
5684 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_HUMIDITY, 1);
5685 					ii++;
5686 				}
5687 				if (
5688 					(dType == pTypeTEMP_HUM) ||
5689 					(dType == pTypeTEMP_HUM_BARO)
5690 					)
5691 				{
5692 					root["result"][ii]["val"] = NTYPE_DEWPOINT;
5693 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_DEWPOINT, 0);
5694 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_DEWPOINT, 1);
5695 					ii++;
5696 				}
5697 				if (dType == pTypeRAIN)
5698 				{
5699 					root["result"][ii]["val"] = NTYPE_RAIN;
5700 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_RAIN, 0);
5701 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_RAIN, 1);
5702 					ii++;
5703 				}
5704 				if (dType == pTypeWIND)
5705 				{
5706 					root["result"][ii]["val"] = NTYPE_WIND;
5707 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_WIND, 0);
5708 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_WIND, 1);
5709 					ii++;
5710 				}
5711 				if (dType == pTypeUV)
5712 				{
5713 					root["result"][ii]["val"] = NTYPE_UV;
5714 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_UV, 0);
5715 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_UV, 1);
5716 					ii++;
5717 				}
5718 				if (
5719 					(dType == pTypeTEMP_HUM_BARO) ||
5720 					(dType == pTypeBARO) ||
5721 					(dType == pTypeTEMP_BARO)
5722 					)
5723 				{
5724 					root["result"][ii]["val"] = NTYPE_BARO;
5725 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_BARO, 0);
5726 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_BARO, 1);
5727 					ii++;
5728 				}
5729 				if (
5730 					((dType == pTypeRFXMeter) && (dSubType == sTypeRFXMeterCount)) ||
5731 					((dType == pTypeGeneral) && (dSubType == sTypeCounterIncremental)) ||
5732 					(dType == pTypeYouLess) ||
5733 					((dType == pTypeRego6XXValue) && (dSubType == sTypeRego6XXCounter))
5734 					)
5735 				{
5736 					if ((switchtype == MTYPE_ENERGY) || (switchtype == MTYPE_ENERGY_GENERATED))
5737 					{
5738 						root["result"][ii]["val"] = NTYPE_TODAYENERGY;
5739 						root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_TODAYENERGY, 0);
5740 						root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_TODAYENERGY, 1);
5741 					}
5742 					else if (switchtype == MTYPE_GAS)
5743 					{
5744 						root["result"][ii]["val"] = NTYPE_TODAYGAS;
5745 						root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_TODAYGAS, 0);
5746 						root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_TODAYGAS, 1);
5747 					}
5748 					else if (switchtype == MTYPE_COUNTER)
5749 					{
5750 						root["result"][ii]["val"] = NTYPE_TODAYCOUNTER;
5751 						root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_TODAYCOUNTER, 0);
5752 						root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_TODAYCOUNTER, 1);
5753 					}
5754 					else
5755 					{
5756 						//water (same as gas)
5757 						root["result"][ii]["val"] = NTYPE_TODAYGAS;
5758 						root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_TODAYGAS, 0);
5759 						root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_TODAYGAS, 1);
5760 					}
5761 					ii++;
5762 				}
5763 				if (dType == pTypeYouLess)
5764 				{
5765 					root["result"][ii]["val"] = NTYPE_USAGE;
5766 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5767 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5768 					ii++;
5769 				}
5770 				if (dType == pTypeAirQuality)
5771 				{
5772 					root["result"][ii]["val"] = NTYPE_USAGE;
5773 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5774 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5775 					ii++;
5776 				}
5777 				else if ((dType == pTypeGeneral) && ((dSubType == sTypeSoilMoisture) || (dSubType == sTypeLeafWetness)))
5778 				{
5779 					root["result"][ii]["val"] = NTYPE_USAGE;
5780 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5781 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5782 					ii++;
5783 				}
5784 				if ((dType == pTypeGeneral) && (dSubType == sTypeVisibility))
5785 				{
5786 					root["result"][ii]["val"] = NTYPE_USAGE;
5787 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5788 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5789 					ii++;
5790 				}
5791 				if ((dType == pTypeGeneral) && (dSubType == sTypeDistance))
5792 				{
5793 					root["result"][ii]["val"] = NTYPE_USAGE;
5794 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5795 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5796 					ii++;
5797 				}
5798 				if ((dType == pTypeGeneral) && (dSubType == sTypeSolarRadiation))
5799 				{
5800 					root["result"][ii]["val"] = NTYPE_USAGE;
5801 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5802 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5803 					ii++;
5804 				}
5805 				if ((dType == pTypeGeneral) && (dSubType == sTypeVoltage))
5806 				{
5807 					root["result"][ii]["val"] = NTYPE_USAGE;
5808 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5809 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5810 					ii++;
5811 				}
5812 				if ((dType == pTypeGeneral) && (dSubType == sTypeCurrent))
5813 				{
5814 					root["result"][ii]["val"] = NTYPE_USAGE;
5815 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5816 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5817 					ii++;
5818 				}
5819 				if ((dType == pTypeGeneral) && (dSubType == sTypePressure))
5820 				{
5821 					root["result"][ii]["val"] = NTYPE_USAGE;
5822 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5823 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5824 					ii++;
5825 				}
5826 				if ((dType == pTypeGeneral) && (dSubType == sTypeBaro))
5827 				{
5828 					root["result"][ii]["val"] = NTYPE_USAGE;
5829 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5830 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5831 					ii++;
5832 				}
5833 				if (dType == pTypeLux)
5834 				{
5835 					root["result"][ii]["val"] = NTYPE_USAGE;
5836 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5837 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5838 					ii++;
5839 				}
5840 				if ((dType == pTypeGeneral) && (dSubType == sTypeSoundLevel))
5841 				{
5842 					root["result"][ii]["val"] = NTYPE_USAGE;
5843 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5844 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5845 					ii++;
5846 				}
5847 				if (dType == pTypeWEIGHT)
5848 				{
5849 					root["result"][ii]["val"] = NTYPE_USAGE;
5850 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5851 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5852 					ii++;
5853 				}
5854 				if (dType == pTypeUsage)
5855 				{
5856 					root["result"][ii]["val"] = NTYPE_USAGE;
5857 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5858 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5859 					ii++;
5860 				}
5861 				if (
5862 					(dType == pTypeENERGY) ||
5863 					((dType == pTypeGeneral) && (dSubType == sTypeKwh))
5864 					)
5865 				{
5866 					root["result"][ii]["val"] = NTYPE_USAGE;
5867 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5868 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5869 					ii++;
5870 				}
5871 				if (dType == pTypePOWER)
5872 				{
5873 					root["result"][ii]["val"] = NTYPE_USAGE;
5874 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5875 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5876 					ii++;
5877 				}
5878 				if ((dType == pTypeCURRENT) && (dSubType == sTypeELEC1))
5879 				{
5880 					root["result"][ii]["val"] = NTYPE_AMPERE1;
5881 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_AMPERE1, 0);
5882 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_AMPERE1, 1);
5883 					ii++;
5884 					root["result"][ii]["val"] = NTYPE_AMPERE2;
5885 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_AMPERE2, 0);
5886 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_AMPERE2, 1);
5887 					ii++;
5888 					root["result"][ii]["val"] = NTYPE_AMPERE3;
5889 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_AMPERE3, 0);
5890 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_AMPERE3, 1);
5891 					ii++;
5892 				}
5893 				if ((dType == pTypeCURRENTENERGY) && (dSubType == sTypeELEC4))
5894 				{
5895 					root["result"][ii]["val"] = NTYPE_AMPERE1;
5896 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_AMPERE1, 0);
5897 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_AMPERE1, 1);
5898 					ii++;
5899 					root["result"][ii]["val"] = NTYPE_AMPERE2;
5900 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_AMPERE2, 0);
5901 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_AMPERE2, 1);
5902 					ii++;
5903 					root["result"][ii]["val"] = NTYPE_AMPERE3;
5904 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_AMPERE3, 0);
5905 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_AMPERE3, 1);
5906 					ii++;
5907 				}
5908 				if (dType == pTypeP1Power)
5909 				{
5910 					root["result"][ii]["val"] = NTYPE_USAGE;
5911 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5912 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5913 					ii++;
5914 					root["result"][ii]["val"] = NTYPE_TODAYENERGY;
5915 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_TODAYENERGY, 0);
5916 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_TODAYENERGY, 1);
5917 					ii++;
5918 				}
5919 				if (dType == pTypeP1Gas)
5920 				{
5921 					root["result"][ii]["val"] = NTYPE_TODAYGAS;
5922 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_TODAYGAS, 0);
5923 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_TODAYGAS, 1);
5924 					ii++;
5925 				}
5926 				if ((dType == pTypeThermostat) && (dSubType == sTypeThermSetpoint))
5927 				{
5928 					root["result"][ii]["val"] = NTYPE_TEMPERATURE;
5929 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_TEMPERATURE, 0);
5930 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_TEMPERATURE, 1);
5931 					ii++;
5932 				}
5933 				if (dType == pTypeEvohomeZone)
5934 				{
5935 					root["result"][ii]["val"] = NTYPE_TEMPERATURE;
5936 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_SETPOINT, 0); //FIXME NTYPE_SETPOINT implementation?
5937 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_SETPOINT, 1);
5938 					ii++;
5939 				}
5940 				if ((dType == pTypeRFXSensor) && ((dSubType == sTypeRFXSensorAD) || (dSubType == sTypeRFXSensorVolt)))
5941 				{
5942 					root["result"][ii]["val"] = NTYPE_USAGE;
5943 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5944 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5945 					ii++;
5946 				}
5947 				if ((dType == pTypeGeneral) && (dSubType == sTypePercentage))
5948 				{
5949 					root["result"][ii]["val"] = NTYPE_PERCENTAGE;
5950 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_PERCENTAGE, 0);
5951 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_PERCENTAGE, 1);
5952 					ii++;
5953 				}
5954 				if ((dType == pTypeGeneral) && (dSubType == sTypeWaterflow))
5955 				{
5956 					root["result"][ii]["val"] = NTYPE_USAGE;
5957 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5958 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5959 					ii++;
5960 				}
5961 				if ((dType == pTypeGeneral) && (dSubType == sTypeCustom))
5962 				{
5963 					root["result"][ii]["val"] = NTYPE_USAGE;
5964 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5965 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5966 					ii++;
5967 				}
5968 				if ((dType == pTypeGeneral) && (dSubType == sTypeFan))
5969 				{
5970 					root["result"][ii]["val"] = NTYPE_RPM;
5971 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_RPM, 0);
5972 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_RPM, 1);
5973 					ii++;
5974 				}
5975 				if ((dType == pTypeGeneral) && (dSubType == sTypeAlert))
5976 				{
5977 					root["result"][ii]["val"] = NTYPE_USAGE;
5978 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_USAGE, 0);
5979 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_USAGE, 1);
5980 					ii++;
5981 				}
5982 				if ((dType == pTypeGeneral) && (dSubType == sTypeZWaveAlarm))
5983 				{
5984 					root["result"][ii]["val"] = NTYPE_VALUE;
5985 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_VALUE, 0);
5986 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_VALUE, 1);
5987 					ii++;
5988 				}
5989 				if ((dType == pTypeRego6XXValue) && (dSubType == sTypeRego6XXStatus))
5990 				{
5991 					root["result"][ii]["val"] = NTYPE_SWITCH_ON;
5992 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_SWITCH_ON, 0);
5993 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_SWITCH_ON, 1);
5994 					ii++;
5995 					root["result"][ii]["val"] = NTYPE_SWITCH_OFF;
5996 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_SWITCH_OFF, 0);
5997 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_SWITCH_OFF, 1);
5998 					ii++;
5999 				}
6000 				if (!IsLightOrSwitch(dType, dSubType))
6001 				{
6002 					root["result"][ii]["val"] = NTYPE_LASTUPDATE;
6003 					root["result"][ii]["text"] = Notification_Type_Desc(NTYPE_LASTUPDATE, 0);
6004 					root["result"][ii]["ptag"] = Notification_Type_Desc(NTYPE_LASTUPDATE, 1);
6005 					ii++;
6006 				}
6007 			}
6008 			else if (cparam == "addnotification")
6009 			{
6010 				if (session.rights < 2)
6011 				{
6012 					session.reply_status = reply::forbidden;
6013 					return; //Only admin user allowed
6014 				}
6015 
6016 				std::string idx = request::findValue(&req, "idx");
6017 				if (idx.empty())
6018 					return;
6019 
6020 				std::string stype = request::findValue(&req, "ttype");
6021 				std::string swhen = request::findValue(&req, "twhen");
6022 				std::string svalue = request::findValue(&req, "tvalue");
6023 				std::string scustommessage = request::findValue(&req, "tmsg");
6024 				std::string sactivesystems = request::findValue(&req, "tsystems");
6025 				std::string spriority = request::findValue(&req, "tpriority");
6026 				std::string ssendalways = request::findValue(&req, "tsendalways");
6027 				std::string srecovery = (request::findValue(&req, "trecovery") == "true") ? "1" : "0";
6028 
6029 				if ((stype.empty()) || (swhen.empty()) || (svalue.empty()) || (spriority.empty()) || (ssendalways.empty()) || (srecovery.empty()))
6030 					return;
6031 
6032 				_eNotificationTypes ntype = (_eNotificationTypes)atoi(stype.c_str());
6033 				std::string ttype = Notification_Type_Desc(ntype, 1);
6034 				if (
6035 					(ntype == NTYPE_SWITCH_ON) ||
6036 					(ntype == NTYPE_SWITCH_OFF) ||
6037 					(ntype == NTYPE_DEWPOINT)
6038 					)
6039 				{
6040 					if ((ntype == NTYPE_SWITCH_ON) && (swhen == "2")) { // '='
6041 						unsigned char twhen = '=';
6042 						sprintf(szTmp, "%s;%c;%s", ttype.c_str(), twhen, svalue.c_str());
6043 					}
6044 					else
6045 						strcpy(szTmp, ttype.c_str());
6046 				}
6047 				else
6048 				{
6049 					std::string twhen;
6050 					if (swhen == "0")
6051 						twhen = ">";
6052 					else if (swhen == "1")
6053 						twhen = ">=";
6054 					else if (swhen == "2")
6055 						twhen = "=";
6056 					else if (swhen == "3")
6057 						twhen = "!=";
6058 					else if (swhen == "4")
6059 						twhen = "<=";
6060 					else
6061 						twhen = "<";
6062 					sprintf(szTmp, "%s;%s;%s;%s", ttype.c_str(), twhen.c_str(), svalue.c_str(), srecovery.c_str());
6063 				}
6064 				int priority = atoi(spriority.c_str());
6065 				bool bOK = m_notifications.AddNotification(idx, szTmp, scustommessage, sactivesystems, priority, (ssendalways == "true") ? true : false);
6066 				if (bOK) {
6067 					root["status"] = "OK";
6068 					root["title"] = "AddNotification";
6069 				}
6070 			}
6071 			else if (cparam == "updatenotification")
6072 			{
6073 				if (session.rights < 2)
6074 				{
6075 					session.reply_status = reply::forbidden;
6076 					return; //Only admin user allowed
6077 				}
6078 
6079 				std::string idx = request::findValue(&req, "idx");
6080 				std::string devidx = request::findValue(&req, "devidx");
6081 				if ((idx.empty()) || (devidx.empty()))
6082 					return;
6083 
6084 				std::string stype = request::findValue(&req, "ttype");
6085 				std::string swhen = request::findValue(&req, "twhen");
6086 				std::string svalue = request::findValue(&req, "tvalue");
6087 				std::string scustommessage = request::findValue(&req, "tmsg");
6088 				std::string sactivesystems = request::findValue(&req, "tsystems");
6089 				std::string spriority = request::findValue(&req, "tpriority");
6090 				std::string ssendalways = request::findValue(&req, "tsendalways");
6091 				std::string srecovery = (request::findValue(&req, "trecovery") == "true") ? "1" : "0";
6092 
6093 				if ((stype.empty()) || (swhen.empty()) || (svalue.empty()) || (spriority.empty()) || (ssendalways.empty()) || srecovery.empty())
6094 					return;
6095 				root["status"] = "OK";
6096 				root["title"] = "UpdateNotification";
6097 
6098 				std::string recoverymsg;
6099 				if ((srecovery == "1") && (m_notifications.CustomRecoveryMessage(strtoull(idx.c_str(), NULL, 0), recoverymsg, true)))
6100 				{
6101 					scustommessage.append(";;");
6102 					scustommessage.append(recoverymsg);
6103 				}
6104 				//delete old record
6105 				m_notifications.RemoveNotification(idx);
6106 
6107 				_eNotificationTypes ntype = (_eNotificationTypes)atoi(stype.c_str());
6108 				std::string ttype = Notification_Type_Desc(ntype, 1);
6109 				if (
6110 					(ntype == NTYPE_SWITCH_ON) ||
6111 					(ntype == NTYPE_SWITCH_OFF) ||
6112 					(ntype == NTYPE_DEWPOINT)
6113 					)
6114 				{
6115 					if ((ntype == NTYPE_SWITCH_ON) && (swhen == "2")) { // '='
6116 						unsigned char twhen = '=';
6117 						sprintf(szTmp, "%s;%c;%s", ttype.c_str(), twhen, svalue.c_str());
6118 					}
6119 					else
6120 						strcpy(szTmp, ttype.c_str());
6121 				}
6122 				else
6123 				{
6124 					std::string twhen;
6125 					if (swhen == "0")
6126 						twhen = ">";
6127 					else if (swhen == "1")
6128 						twhen = ">=";
6129 					else if (swhen == "2")
6130 						twhen = "=";
6131 					else if (swhen == "3")
6132 						twhen = "!=";
6133 					else if (swhen == "4")
6134 						twhen = "<=";
6135 					else
6136 						twhen = "<";
6137 					sprintf(szTmp, "%s;%s;%s;%s", ttype.c_str(), twhen.c_str(), svalue.c_str(), srecovery.c_str());
6138 				}
6139 				int priority = atoi(spriority.c_str());
6140 				m_notifications.AddNotification(devidx, szTmp, scustommessage, sactivesystems, priority, (ssendalways == "true") ? true : false);
6141 			}
6142 			else if (cparam == "deletenotification")
6143 			{
6144 				if (session.rights < 2)
6145 				{
6146 					session.reply_status = reply::forbidden;
6147 					return; //Only admin user allowed
6148 				}
6149 
6150 				std::string idx = request::findValue(&req, "idx");
6151 				if (idx.empty())
6152 					return;
6153 
6154 				root["status"] = "OK";
6155 				root["title"] = "DeleteNotification";
6156 
6157 				m_notifications.RemoveNotification(idx);
6158 			}
6159 			else if (cparam == "switchdeviceorder")
6160 			{
6161 				if (session.rights < 2)
6162 				{
6163 					session.reply_status = reply::forbidden;
6164 					return; //Only admin user allowed
6165 				}
6166 
6167 				std::string idx1 = request::findValue(&req, "idx1");
6168 				std::string idx2 = request::findValue(&req, "idx2");
6169 				if ((idx1.empty()) || (idx2.empty()))
6170 					return;
6171 				std::string sroomid = request::findValue(&req, "roomid");
6172 				int roomid = atoi(sroomid.c_str());
6173 
6174 				std::string Order1, Order2;
6175 				if (roomid == 0)
6176 				{
6177 					//get device order 1
6178 					result = m_sql.safe_query("SELECT [Order] FROM DeviceStatus WHERE (ID == '%q')",
6179 						idx1.c_str());
6180 					if (result.empty())
6181 						return;
6182 					Order1 = result[0][0];
6183 
6184 					//get device order 2
6185 					result = m_sql.safe_query("SELECT [Order] FROM DeviceStatus WHERE (ID == '%q')",
6186 						idx2.c_str());
6187 					if (result.empty())
6188 						return;
6189 					Order2 = result[0][0];
6190 
6191 					root["status"] = "OK";
6192 					root["title"] = "SwitchDeviceOrder";
6193 
6194 					if (atoi(Order1.c_str()) < atoi(Order2.c_str()))
6195 					{
6196 						m_sql.safe_query(
6197 							"UPDATE DeviceStatus SET [Order] = [Order]+1 WHERE ([Order] >= '%q' AND [Order] < '%q')",
6198 							Order1.c_str(), Order2.c_str());
6199 					}
6200 					else
6201 					{
6202 						m_sql.safe_query(
6203 							"UPDATE DeviceStatus SET [Order] = [Order]-1 WHERE ([Order] > '%q' AND [Order] <= '%q')",
6204 							Order2.c_str(), Order1.c_str());
6205 					}
6206 
6207 					m_sql.safe_query("UPDATE DeviceStatus SET [Order] = '%q' WHERE (ID == '%q')",
6208 						Order1.c_str(), idx2.c_str());
6209 				}
6210 				else
6211 				{
6212 					//change order in a room
6213 					//get device order 1
6214 					result = m_sql.safe_query("SELECT [Order] FROM DeviceToPlansMap WHERE (DeviceRowID == '%q') AND (PlanID==%d)",
6215 						idx1.c_str(), roomid);
6216 					if (result.empty())
6217 						return;
6218 					Order1 = result[0][0];
6219 
6220 					//get device order 2
6221 					result = m_sql.safe_query("SELECT [Order] FROM DeviceToPlansMap WHERE (DeviceRowID == '%q') AND (PlanID==%d)",
6222 						idx2.c_str(), roomid);
6223 					if (result.empty())
6224 						return;
6225 					Order2 = result[0][0];
6226 
6227 					root["status"] = "OK";
6228 					root["title"] = "SwitchDeviceOrder";
6229 
6230 					if (atoi(Order1.c_str()) < atoi(Order2.c_str()))
6231 					{
6232 						m_sql.safe_query(
6233 							"UPDATE DeviceToPlansMap SET [Order] = [Order]+1 WHERE ([Order] >= '%q' AND [Order] < '%q') AND (PlanID==%d)",
6234 							Order1.c_str(), Order2.c_str(), roomid);
6235 					}
6236 					else
6237 					{
6238 						m_sql.safe_query(
6239 							"UPDATE DeviceToPlansMap SET [Order] = [Order]-1 WHERE ([Order] > '%q' AND [Order] <= '%q') AND (PlanID==%d)",
6240 							Order2.c_str(), Order1.c_str(), roomid);
6241 					}
6242 
6243 					m_sql.safe_query("UPDATE DeviceToPlansMap SET [Order] = '%q' WHERE (DeviceRowID == '%q') AND (PlanID==%d)",
6244 						Order1.c_str(), idx2.c_str(), roomid);
6245 				}
6246 			}
6247 			else if (cparam == "switchsceneorder")
6248 			{
6249 				if (session.rights < 2)
6250 				{
6251 					session.reply_status = reply::forbidden;
6252 					return; //Only admin user allowed
6253 				}
6254 
6255 				std::string idx1 = request::findValue(&req, "idx1");
6256 				std::string idx2 = request::findValue(&req, "idx2");
6257 				if ((idx1.empty()) || (idx2.empty()))
6258 					return;
6259 
6260 				std::string Order1, Order2;
6261 				//get device order 1
6262 				result = m_sql.safe_query("SELECT [Order] FROM Scenes WHERE (ID == '%q')",
6263 					idx1.c_str());
6264 				if (result.empty())
6265 					return;
6266 				Order1 = result[0][0];
6267 
6268 				//get device order 2
6269 				result = m_sql.safe_query("SELECT [Order] FROM Scenes WHERE (ID == '%q')",
6270 					idx2.c_str());
6271 				if (result.empty())
6272 					return;
6273 				Order2 = result[0][0];
6274 
6275 				root["status"] = "OK";
6276 				root["title"] = "SwitchSceneOrder";
6277 
6278 				if (atoi(Order1.c_str()) < atoi(Order2.c_str()))
6279 				{
6280 					m_sql.safe_query(
6281 						"UPDATE Scenes SET [Order] = [Order]+1 WHERE ([Order] >= '%q' AND [Order] < '%q')",
6282 						Order1.c_str(), Order2.c_str());
6283 				}
6284 				else
6285 				{
6286 					m_sql.safe_query(
6287 						"UPDATE Scenes SET [Order] = [Order]-1 WHERE ([Order] > '%q' AND [Order] <= '%q')",
6288 						Order2.c_str(), Order1.c_str());
6289 				}
6290 
6291 				m_sql.safe_query("UPDATE Scenes SET [Order] = '%q' WHERE (ID == '%q')",
6292 					Order1.c_str(), idx2.c_str());
6293 			}
6294 			else if (cparam == "clearnotifications")
6295 			{
6296 				if (session.rights < 2)
6297 				{
6298 					session.reply_status = reply::forbidden;
6299 					return; //Only admin user allowed
6300 				}
6301 
6302 				std::string idx = request::findValue(&req, "idx");
6303 				if (idx.empty())
6304 					return;
6305 
6306 				root["status"] = "OK";
6307 				root["title"] = "ClearNotification";
6308 
6309 				m_notifications.RemoveDeviceNotifications(idx);
6310 			}
6311 			else if (cparam == "adduser")
6312 			{
6313 				if (session.rights < 2)
6314 				{
6315 					session.reply_status = reply::forbidden;
6316 					return; //Only admin user allowed
6317 				}
6318 
6319 				std::string senabled = request::findValue(&req, "enabled");
6320 				std::string username = request::findValue(&req, "username");
6321 				std::string password = request::findValue(&req, "password");
6322 				std::string srights = request::findValue(&req, "rights");
6323 				std::string sRemoteSharing = request::findValue(&req, "RemoteSharing");
6324 				std::string sTabsEnabled = request::findValue(&req, "TabsEnabled");
6325 				if (
6326 					(senabled.empty()) ||
6327 					(username.empty()) ||
6328 					(password.empty()) ||
6329 					(srights.empty()) ||
6330 					(sRemoteSharing.empty()) ||
6331 					(sTabsEnabled.empty())
6332 					)
6333 					return;
6334 				int rights = atoi(srights.c_str());
6335 				if (rights != 2)
6336 				{
6337 					if (!FindAdminUser())
6338 					{
6339 						root["message"] = "Add a Admin user first! (Or enable Settings/Website Protection)";
6340 						return;
6341 					}
6342 				}
6343 				//Check for duplicate user name
6344 				result = m_sql.safe_query("SELECT ID FROM Users WHERE (Username == '%q')", base64_encode(username).c_str());
6345 				if (!result.empty())
6346 				{
6347 					root["message"] = "Duplicate Username!";
6348 					return;
6349 				}
6350 				root["status"] = "OK";
6351 				root["title"] = "AddUser";
6352 				m_sql.safe_query(
6353 					"INSERT INTO Users (Active, Username, Password, Rights, RemoteSharing, TabsEnabled) VALUES (%d,'%q','%q','%d','%d','%d')",
6354 					(senabled == "true") ? 1 : 0,
6355 					base64_encode(username).c_str(),
6356 					password.c_str(),
6357 					rights,
6358 					(sRemoteSharing == "true") ? 1 : 0,
6359 					atoi(sTabsEnabled.c_str())
6360 				);
6361 				LoadUsers();
6362 			}
6363 			else if (cparam == "updateuser")
6364 			{
6365 				if (session.rights < 2)
6366 				{
6367 					session.reply_status = reply::forbidden;
6368 					return; //Only admin user allowed
6369 				}
6370 
6371 				std::string idx = request::findValue(&req, "idx");
6372 				if (idx.empty())
6373 					return;
6374 				std::string senabled = request::findValue(&req, "enabled");
6375 				std::string username = request::findValue(&req, "username");
6376 				std::string password = request::findValue(&req, "password");
6377 				std::string srights = request::findValue(&req, "rights");
6378 				std::string sRemoteSharing = request::findValue(&req, "RemoteSharing");
6379 				std::string sTabsEnabled = request::findValue(&req, "TabsEnabled");
6380 				if (
6381 					(senabled.empty()) ||
6382 					(username.empty()) ||
6383 					(password.empty()) ||
6384 					(srights.empty()) ||
6385 					(sRemoteSharing.empty()) ||
6386 					(sTabsEnabled.empty())
6387 					)
6388 					return;
6389 				int rights = atoi(srights.c_str());
6390 				if (rights != 2)
6391 				{
6392 					if (!FindAdminUser())
6393 					{
6394 						root["message"] = "Add a Admin user first! (Or enable Settings/Website Protection)";
6395 						return;
6396 					}
6397 				}
6398 				std::string sHashedUsername = base64_encode(username);
6399 
6400 				//Check for duplicate user name
6401 				result = m_sql.safe_query("SELECT ID FROM Users WHERE (Username == '%q')", base64_encode(username).c_str());
6402 				if (!result.empty())
6403 				{
6404 					std::string oidx = result[0][0];
6405 					if (oidx != idx)
6406 					{
6407 						root["message"] = "Duplicate Username!";
6408 						return;
6409 					}
6410 				}
6411 
6412 				// Invalid user's sessions if username or password has changed
6413 				std::string sOldUsername;
6414 				std::string sOldPassword;
6415 				result = m_sql.safe_query("SELECT Username, Password FROM Users WHERE (ID == '%q')", idx.c_str());
6416 				if (result.size() == 1)
6417 				{
6418 					sOldUsername = result[0][0];
6419 					sOldPassword = result[0][1];
6420 				}
6421 				if ((sHashedUsername != sOldUsername) || (password != sOldPassword))
6422 					RemoveUsersSessions(sOldUsername, session);
6423 
6424 				root["status"] = "OK";
6425 				root["title"] = "UpdateUser";
6426 				m_sql.safe_query(
6427 					"UPDATE Users SET Active=%d, Username='%q', Password='%q', Rights=%d, RemoteSharing=%d, TabsEnabled=%d WHERE (ID == '%q')",
6428 					(senabled == "true") ? 1 : 0,
6429 					sHashedUsername.c_str(),
6430 					password.c_str(),
6431 					rights,
6432 					(sRemoteSharing == "true") ? 1 : 0,
6433 					atoi(sTabsEnabled.c_str()),
6434 					idx.c_str()
6435 				);
6436 				LoadUsers();
6437 
6438 
6439 			}
6440 			else if (cparam == "deleteuser")
6441 			{
6442 				if (session.rights < 2)
6443 				{
6444 					session.reply_status = reply::forbidden;
6445 					return; //Only admin user allowed
6446 				}
6447 
6448 				std::string idx = request::findValue(&req, "idx");
6449 				if (idx.empty())
6450 					return;
6451 
6452 				root["status"] = "OK";
6453 				root["title"] = "DeleteUser";
6454 
6455 				// Remove user's sessions
6456 				result = m_sql.safe_query("SELECT Username FROM Users WHERE (ID == '%q')", idx.c_str());
6457 				if (result.size() == 1)
6458 				{
6459 					RemoveUsersSessions(result[0][0], session);
6460 				}
6461 
6462 				m_sql.safe_query("DELETE FROM Users WHERE (ID == '%q')", idx.c_str());
6463 
6464 				m_sql.safe_query("DELETE FROM SharedDevices WHERE (SharedUserID == '%q')", idx.c_str());
6465 
6466 				LoadUsers();
6467 			}
6468 			else if (cparam == "clearlightlog")
6469 			{
6470 				if (session.rights < 2)
6471 				{
6472 					session.reply_status = reply::forbidden;
6473 					return; //Only admin user allowed
6474 				}
6475 
6476 				std::string idx = request::findValue(&req, "idx");
6477 				if (idx.empty())
6478 					return;
6479 				//First get Device Type/SubType
6480 				result = m_sql.safe_query("SELECT Type, SubType FROM DeviceStatus WHERE (ID == '%q')",
6481 					idx.c_str());
6482 				if (result.empty())
6483 					return;
6484 
6485 				unsigned char dType = atoi(result[0][0].c_str());
6486 				unsigned char dSubType = atoi(result[0][1].c_str());
6487 
6488 				if (
6489 					(dType != pTypeLighting1) &&
6490 					(dType != pTypeLighting2) &&
6491 					(dType != pTypeLighting3) &&
6492 					(dType != pTypeLighting4) &&
6493 					(dType != pTypeLighting5) &&
6494 					(dType != pTypeLighting6) &&
6495 					(dType != pTypeFan) &&
6496 					(dType != pTypeColorSwitch) &&
6497 					(dType != pTypeSecurity1) &&
6498 					(dType != pTypeSecurity2) &&
6499 					(dType != pTypeEvohome) &&
6500 					(dType != pTypeEvohomeRelay) &&
6501 					(dType != pTypeCurtain) &&
6502 					(dType != pTypeBlinds) &&
6503 					(dType != pTypeRFY) &&
6504 					(dType != pTypeChime) &&
6505 					(dType != pTypeThermostat2) &&
6506 					(dType != pTypeThermostat3) &&
6507 					(dType != pTypeThermostat4) &&
6508 					(dType != pTypeRemote) &&
6509 					(dType != pTypeGeneralSwitch) &&
6510 					(dType != pTypeHomeConfort) &&
6511 					(dType != pTypeFS20) &&
6512 					(!((dType == pTypeRadiator1) && (dSubType == sTypeSmartwaresSwitchRadiator))) &&
6513 					(!((dType == pTypeGeneral) && (dSubType == sTypeTextStatus))) &&
6514 					(!((dType == pTypeGeneral) && (dSubType == sTypeAlert))) &&
6515 					(dType != pTypeHunter)
6516 					)
6517 					return; //no light device! we should not be here!
6518 
6519 				root["status"] = "OK";
6520 				root["title"] = "ClearLightLog";
6521 
6522 				result = m_sql.safe_query("DELETE FROM LightingLog WHERE (DeviceRowID=='%q')", idx.c_str());
6523 			}
6524 			else if (cparam == "clearscenelog")
6525 			{
6526 				if (session.rights < 2)
6527 				{
6528 					session.reply_status = reply::forbidden;
6529 					return; //Only admin user allowed
6530 				}
6531 
6532 				std::string idx = request::findValue(&req, "idx");
6533 				if (idx.empty())
6534 					return;
6535 				root["status"] = "OK";
6536 				root["title"] = "ClearSceneLog";
6537 
6538 				result = m_sql.safe_query("DELETE FROM SceneLog WHERE (SceneRowID=='%q')", idx.c_str());
6539 			}
6540 			else if (cparam == "learnsw")
6541 			{
6542 				if (session.rights < 2)
6543 				{
6544 					session.reply_status = reply::forbidden;
6545 					return; //Only admin user allowed
6546 				}
6547 
6548 				m_sql.AllowNewHardwareTimer(5);
6549 				m_sql.m_LastSwitchID = "";
6550 				bool bReceivedSwitch = false;
6551 				unsigned char cntr = 0;
6552 				while ((!bReceivedSwitch) && (cntr < 50))	//wait for max. 5 seconds
6553 				{
6554 					if (m_sql.m_LastSwitchID != "")
6555 					{
6556 						bReceivedSwitch = true;
6557 						break;
6558 					}
6559 					else
6560 					{
6561 						//sleep 100ms
6562 						sleep_milliseconds(100);
6563 						cntr++;
6564 					}
6565 				}
6566 				if (bReceivedSwitch)
6567 				{
6568 					//check if used
6569 					result = m_sql.safe_query("SELECT Name, Used, nValue FROM DeviceStatus WHERE (ID==%" PRIu64 ")",
6570 						m_sql.m_LastSwitchRowID);
6571 					if (!result.empty())
6572 					{
6573 						root["status"] = "OK";
6574 						root["title"] = "LearnSW";
6575 						root["ID"] = m_sql.m_LastSwitchID;
6576 						root["idx"] = m_sql.m_LastSwitchRowID;
6577 						root["Name"] = result[0][0];
6578 						root["Used"] = atoi(result[0][1].c_str());
6579 						root["Cmd"] = atoi(result[0][2].c_str());
6580 					}
6581 				}
6582 			} //learnsw
6583 			else if (cparam == "makefavorite")
6584 			{
6585 				if (session.rights < 1)
6586 				{
6587 					session.reply_status = reply::forbidden;
6588 					return; //Only admin user allowed
6589 				}
6590 				std::string idx = request::findValue(&req, "idx");
6591 				std::string sisfavorite = request::findValue(&req, "isfavorite");
6592 				if ((idx.empty()) || (sisfavorite.empty()))
6593 					return;
6594 				int isfavorite = atoi(sisfavorite.c_str());
6595 
6596 				root["status"] = "OK";
6597 				root["title"] = "MakeFavorite";
6598 
6599 				const int iUser = FindUser(session.username.c_str());
6600 				if (iUser != -1)
6601 				{
6602 					const _eUserRights urights = m_users[iUser].userrights;
6603 					if ((urights != URIGHTS_ADMIN) && (m_users[iUser].ID != 0xFFFF))
6604 					{
6605 						m_sql.safe_query("UPDATE SharedDevices SET Favorite=%d WHERE (DeviceRowID == '%q') AND (SharedUserID == %d)", isfavorite, idx.c_str(), m_users[iUser].ID);
6606 						return;
6607 					}
6608 				}
6609 				m_sql.safe_query("UPDATE DeviceStatus SET Favorite=%d WHERE (ID == '%q')", isfavorite, idx.c_str());
6610 			} //makefavorite
6611 			else if (cparam == "makescenefavorite")
6612 			{
6613 				if (session.rights < 2)
6614 				{
6615 					session.reply_status = reply::forbidden;
6616 					return; //Only admin user allowed
6617 				}
6618 
6619 				std::string idx = request::findValue(&req, "idx");
6620 				std::string sisfavorite = request::findValue(&req, "isfavorite");
6621 				if ((idx.empty()) || (sisfavorite.empty()))
6622 					return;
6623 				int isfavorite = atoi(sisfavorite.c_str());
6624 				m_sql.safe_query("UPDATE Scenes SET Favorite=%d WHERE (ID == '%q')",
6625 					isfavorite, idx.c_str());
6626 				root["status"] = "OK";
6627 				root["title"] = "MakeSceneFavorite";
6628 			} //makescenefavorite
6629 			else if (cparam == "resetsecuritystatus")
6630 			{
6631 				std::string idx = request::findValue(&req, "idx");
6632 				std::string switchcmd = request::findValue(&req, "switchcmd");
6633 
6634 				if ((idx.empty()) || (switchcmd.empty()))
6635 					return;
6636 
6637 				root["status"] = "OK";
6638 				root["title"] = "ResetSecurityStatus";
6639 
6640 				int nValue = -1;
6641 
6642 				// Change to generic *Security_Status_Desc lookup...
6643 
6644 				if (switchcmd == "Panic End") {
6645 					nValue = 7;
6646 				}
6647 				else if (switchcmd == "Normal") {
6648 					nValue = 0;
6649 				}
6650 
6651 				if (nValue >= 0)
6652 				{
6653 					m_sql.safe_query("UPDATE DeviceStatus SET nValue=%d WHERE (ID == '%q')",
6654 						nValue, idx.c_str());
6655 					root["status"] = "OK";
6656 					root["title"] = "SwitchLight";
6657 				}
6658 			}
6659 			else if (cparam == "verifypasscode")
6660 			{
6661 				std::string passcode = request::findValue(&req, "passcode");
6662 				if (passcode.empty())
6663 					return;
6664 				//Check if passcode is correct
6665 				passcode = GenerateMD5Hash(passcode);
6666 				std::string rpassword;
6667 				int nValue = 1;
6668 				m_sql.GetPreferencesVar("ProtectionPassword", nValue, rpassword);
6669 				if (passcode == rpassword)
6670 				{
6671 					root["title"] = "VerifyPasscode";
6672 					root["status"] = "OK";
6673 					return;
6674 				}
6675 			}
6676 			else if (cparam == "switchmodal")
6677 			{
6678 				int urights = 3;
6679 				if (bHaveUser)
6680 				{
6681 					int iUser = -1;
6682 					iUser = FindUser(session.username.c_str());
6683 					if (iUser != -1)
6684 					{
6685 						urights = (int)m_users[iUser].userrights;
6686 						_log.Log(LOG_STATUS, "User: %s initiated a modal command", m_users[iUser].Username.c_str());
6687 					}
6688 				}
6689 				if (urights < 1)
6690 					return;
6691 
6692 				std::string idx = request::findValue(&req, "idx");
6693 				std::string switchcmd = request::findValue(&req, "status");
6694 				std::string until = request::findValue(&req, "until");//optional until date / time as applicable
6695 				std::string action = request::findValue(&req, "action");//Run action or not (update status only)
6696 				std::string onlyonchange = request::findValue(&req, "ooc");//No update unless the value changed (check if updated)
6697 				//The on action is used to call a script to update the real device so we only want to use it when altering the status in the Domoticz Web Client
6698 				//If we're posting the status from the real device to domoticz we don't want to run the on action script ("action"!=1) to avoid loops and contention
6699 				//""... we only want to log a change (and trigger an event) when the status has actually changed ("ooc"==1) i.e. suppress non transient updates
6700 				if ((idx.empty()) || (switchcmd.empty()))
6701 					return;
6702 
6703 				std::string passcode = request::findValue(&req, "passcode");
6704 				if (passcode.size() > 0)
6705 				{
6706 					//Check if passcode is correct
6707 					passcode = GenerateMD5Hash(passcode);
6708 					std::string rpassword;
6709 					int nValue = 1;
6710 					m_sql.GetPreferencesVar("ProtectionPassword", nValue, rpassword);
6711 					if (passcode != rpassword)
6712 					{
6713 						root["title"] = "Modal";
6714 						root["status"] = "ERROR";
6715 						root["message"] = "WRONG CODE";
6716 						return;
6717 					}
6718 				}
6719 
6720 				if (m_mainworker.SwitchModal(idx, switchcmd, action, onlyonchange, until) == true)//FIXME we need to return a status of already set / no update if ooc=="1" and no status update was performed
6721 				{
6722 					root["status"] = "OK";
6723 					root["title"] = "Modal";
6724 				}
6725 			}
6726 			else if (cparam == "switchlight")
6727 			{
6728 				if (session.rights < 1)
6729 				{
6730 					session.reply_status = reply::forbidden;
6731 					return; //Only user/admin allowed
6732 				}
6733 				std::string Username = "Admin";
6734 				if (!session.username.empty())
6735 					Username = session.username;
6736 
6737 				std::string idx = request::findValue(&req, "idx");
6738 
6739 				std::string switchcmd = request::findValue(&req, "switchcmd");
6740 				std::string level = "-1";
6741 				if (switchcmd == "Set Level")
6742 					level = request::findValue(&req, "level");
6743 				std::string onlyonchange = request::findValue(&req, "ooc");//No update unless the value changed (check if updated)
6744 				_log.Debug(DEBUG_WEBSERVER, "WEBS switchlight idx:%s switchcmd:%s level:%s", idx.c_str(), switchcmd.c_str(), level.c_str());
6745 				std::string passcode = request::findValue(&req, "passcode");
6746 				if ((idx.empty()) || (switchcmd.empty()) || ((switchcmd == "Set Level") && (level.empty())) )
6747 					return;
6748 
6749 				result = m_sql.safe_query(
6750 					"SELECT [Protected],[Name] FROM DeviceStatus WHERE (ID = '%q')", idx.c_str());
6751 				if (result.empty())
6752 				{
6753 					//Switch not found!
6754 					_log.Log(LOG_ERROR, "User: %s, switch not found (idx=%s)!", Username.c_str(), idx.c_str());
6755 					return;
6756 				}
6757 				bool bIsProtected = atoi(result[0][0].c_str()) != 0;
6758 				std::string sSwitchName = result[0][1];
6759 				if (session.rights == 1)
6760 				{
6761 					if (!IsIdxForUser(&session, atoi(idx.c_str())))
6762 					{
6763 						_log.Log(LOG_ERROR, "User: %s initiated a Unauthorized switch command!", Username.c_str());
6764 						session.reply_status = reply::forbidden;
6765 						return;
6766 					}
6767 				}
6768 
6769 				if (bIsProtected)
6770 				{
6771 					if (passcode.empty())
6772 					{
6773 						//Switch is protected, but no passcode has been
6774 						root["title"] = "SwitchLight";
6775 						root["status"] = "ERROR";
6776 						root["message"] = "WRONG CODE";
6777 						return;
6778 					}
6779 					//Check if passcode is correct
6780 					passcode = GenerateMD5Hash(passcode);
6781 					std::string rpassword;
6782 					int nValue = 1;
6783 					m_sql.GetPreferencesVar("ProtectionPassword", nValue, rpassword);
6784 					if (passcode != rpassword)
6785 					{
6786 						_log.Log(LOG_ERROR, "User: %s initiated a switch command (Wrong code!)", Username.c_str());
6787 						root["title"] = "SwitchLight";
6788 						root["status"] = "ERROR";
6789 						root["message"] = "WRONG CODE";
6790 						return;
6791 					}
6792 				}
6793 
6794 				_log.Log(LOG_STATUS, "User: %s initiated a switch command (%s/%s/%s)", Username.c_str(), idx.c_str(), sSwitchName.c_str(), switchcmd.c_str());
6795 
6796 				root["title"] = "SwitchLight";
6797 				if (m_mainworker.SwitchLight(idx, switchcmd, level, "-1", onlyonchange, 0, Username) == true)
6798 				{
6799 					root["status"] = "OK";
6800 				}
6801 				else
6802 				{
6803 					root["status"] = "ERROR";
6804 					root["message"] = "Error sending switch command, check device/hardware (idx=" + idx + ") !";
6805 				}
6806 			} //(rtype=="switchlight")
6807 			else if (cparam == "switchscene")
6808 			{
6809 				if (session.rights < 1)
6810 				{
6811 					session.reply_status = reply::forbidden;
6812 					return; //Only user/admin allowed
6813 				}
6814 				std::string Username = "Admin";
6815 				if (!session.username.empty())
6816 					Username = session.username;
6817 
6818 				std::string idx = request::findValue(&req, "idx");
6819 				std::string switchcmd = request::findValue(&req, "switchcmd");
6820 				std::string passcode = request::findValue(&req, "passcode");
6821 				if ((idx.empty()) || (switchcmd.empty()))
6822 					return;
6823 
6824 				result = m_sql.safe_query(
6825 					"SELECT [Protected] FROM Scenes WHERE (ID = '%q')", idx.c_str());
6826 				if (result.empty())
6827 				{
6828 					//Scene/Group not found!
6829 					_log.Log(LOG_ERROR, "User: %s, scene not found (idx=%s)!", Username.c_str(), idx.c_str());
6830 					return;
6831 				}
6832 				bool bIsProtected = atoi(result[0][0].c_str()) != 0;
6833 				if (bIsProtected)
6834 				{
6835 					if (passcode.empty())
6836 					{
6837 						root["title"] = "SwitchScene";
6838 						root["status"] = "ERROR";
6839 						root["message"] = "WRONG CODE";
6840 						return;
6841 					}
6842 					//Check if passcode is correct
6843 					passcode = GenerateMD5Hash(passcode);
6844 					std::string rpassword;
6845 					int nValue = 1;
6846 					m_sql.GetPreferencesVar("ProtectionPassword", nValue, rpassword);
6847 					if (passcode != rpassword)
6848 					{
6849 						root["title"] = "SwitchScene";
6850 						root["status"] = "ERROR";
6851 						root["message"] = "WRONG CODE";
6852 						_log.Log(LOG_ERROR, "User: %s initiated a scene/group command (Wrong code!)", Username.c_str());
6853 						return;
6854 					}
6855 				}
6856 				_log.Log(LOG_STATUS, "User: %s initiated a scene/group command", Username.c_str());
6857 
6858 				if (m_mainworker.SwitchScene(idx, switchcmd, Username) == true)
6859 				{
6860 					root["status"] = "OK";
6861 					root["title"] = "SwitchScene";
6862 				}
6863 			} //(rtype=="switchscene")
6864 			else if (cparam == "getSunRiseSet") {
6865 				if (!m_mainworker.m_LastSunriseSet.empty())
6866 				{
6867 					std::vector<std::string> strarray;
6868 					StringSplit(m_mainworker.m_LastSunriseSet, ";", strarray);
6869 					if (strarray.size() == 10)
6870 					{
6871 						struct tm loctime;
6872 						time_t now = mytime(NULL);
6873 
6874 						localtime_r(&now, &loctime);
6875 						//strftime(szTmp, 80, "%b %d %Y %X", &loctime);
6876 						strftime(szTmp, 80, "%Y-%m-%d %X", &loctime);
6877 
6878 						root["status"] = "OK";
6879 						root["title"] = "getSunRiseSet";
6880 						root["ServerTime"] = szTmp;
6881 						root["Sunrise"] = strarray[0];
6882 						root["Sunset"] = strarray[1];
6883 						root["SunAtSouth"] = strarray[2];
6884 						root["CivTwilightStart"] = strarray[3];
6885 						root["CivTwilightEnd"] = strarray[4];
6886 						root["NautTwilightStart"] = strarray[5];
6887 						root["NautTwilightEnd"] = strarray[6];
6888 						root["AstrTwilightStart"] = strarray[7];
6889 						root["AstrTwilightEnd"] = strarray[8];
6890 						root["DayLength"] = strarray[9];
6891 					}
6892 				}
6893 			}
6894 			else if (cparam == "getServerTime") {
6895 
6896 				struct tm loctime;
6897 				time_t now = mytime(NULL);
6898 
6899 				localtime_r(&now, &loctime);
6900 				//strftime(szTmp, 80, "%b %d %Y %X", &loctime);
6901 				strftime(szTmp, 80, "%Y-%m-%d %X", &loctime);
6902 
6903 				root["status"] = "OK";
6904 				root["title"] = "getServerTime";
6905 				root["ServerTime"] = szTmp;
6906 			}
6907 			else if (cparam == "getsecstatus")
6908 			{
6909 				root["status"] = "OK";
6910 				root["title"] = "GetSecStatus";
6911 
6912 				int secstatus = 0;
6913 				m_sql.GetPreferencesVar("SecStatus", secstatus);
6914 				root["secstatus"] = secstatus;
6915 
6916 				int secondelay = 30;
6917 				m_sql.GetPreferencesVar("SecOnDelay", secondelay);
6918 				root["secondelay"] = secondelay;
6919 			}
6920 			else if (cparam == "setsecstatus")
6921 			{
6922 				std::string ssecstatus = request::findValue(&req, "secstatus");
6923 				std::string seccode = request::findValue(&req, "seccode");
6924 				if ((ssecstatus.empty()) || (seccode.empty()))
6925 				{
6926 					root["message"] = "WRONG CODE";
6927 					return;
6928 				}
6929 				root["title"] = "SetSecStatus";
6930 				std::string rpassword;
6931 				int nValue = 1;
6932 				m_sql.GetPreferencesVar("SecPassword", nValue, rpassword);
6933 				if (seccode != rpassword)
6934 				{
6935 					root["status"] = "ERROR";
6936 					root["message"] = "WRONG CODE";
6937 					return;
6938 				}
6939 				root["status"] = "OK";
6940 				int iSecStatus = atoi(ssecstatus.c_str());
6941 				m_mainworker.UpdateDomoticzSecurityStatus(iSecStatus);
6942 			}
6943 			else if (cparam == "setcolbrightnessvalue")
6944 			{
6945 				if (session.rights < 1)
6946 				{
6947 					session.reply_status = reply::forbidden;
6948 					return; //Only user/admin allowed
6949 				}
6950 
6951 				std::string Username = "Admin";
6952 				if (!session.username.empty())
6953 					Username = session.username;
6954 
6955 				std::string idx = request::findValue(&req, "idx");
6956 
6957 				if (idx.empty())
6958 				{
6959 					return;
6960 				}
6961 				uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
6962 				_tColor color;
6963 
6964 				std::string json = request::findValue(&req, "color");
6965 				std::string hex = request::findValue(&req, "hex");
6966 				std::string hue = request::findValue(&req, "hue");
6967 				std::string sat = request::findValue(&req, "sat");
6968 				std::string brightness = request::findValue(&req, "brightness");
6969 				std::string iswhite = request::findValue(&req, "iswhite");
6970 
6971 				int ival = 100;
6972 				float brightnessAdj = 1.0f;
6973 
6974 				if (!json.empty())
6975 				{
6976 					color = _tColor(json);
6977 					if (color.mode == ColorModeRGB)
6978 					{
6979 						// Normalize RGB to full brightness
6980 						float hsb[3];
6981 						int r, g, b;
6982 						rgb2hsb(color.r, color.g, color.b, hsb);
6983 						hsb2rgb(hsb[0]*360.0f, hsb[1], 1.0f, r, g, b, 255);
6984 						color.r = r;
6985 						color.g = g;
6986 						color.b = b;
6987 						brightnessAdj = hsb[2];
6988 					}
6989 
6990 					//_log.Debug(DEBUG_WEBSERVER, "setcolbrightnessvalue: json: %s, color: '%s', bri: '%s'", json.c_str(), color.toString().c_str(), brightness.c_str());
6991 				}
6992 				else if (!hex.empty())
6993 				{
6994 					uint64_t ihex = hexstrtoui64(hex);
6995 					//_log.Debug(DEBUG_WEBSERVER, "setcolbrightnessvalue: hex: '%s', ihex: %" PRIx64 ", bri: '%s', iswhite: '%s'", hex.c_str(), ihex, brightness.c_str(), iswhite.c_str());
6996 					uint8_t r = 0;
6997 					uint8_t g = 0;
6998 					uint8_t b = 0;
6999 					uint8_t cw = 0;
7000 					uint8_t ww = 0;
7001 					switch (hex.length())
7002 					{
7003 						case 6: //RGB
7004 							r = (uint8_t)((ihex & 0x0000FF0000) >> 16);
7005 							g = (uint8_t)((ihex & 0x000000FF00) >> 8);
7006 							b = (uint8_t)ihex & 0xFF;
7007 							float hsb[3];
7008 							int tr, tg, tb; // tmp of 'int' type so can be passed as references to hsb2rgb
7009 							rgb2hsb(r, g, b, hsb);
7010 							// Normalize RGB to full brightness
7011 							hsb2rgb(hsb[0]*360.0f, hsb[1], 1.0f, tr, tg, tb, 255);
7012 							r = tr;
7013 							g = tg;
7014 							b = tb;
7015 							brightnessAdj = hsb[2];
7016 							// Backwards compatibility: set iswhite for unsaturated colors
7017 							iswhite = (hsb[1] < (20.0 / 255.0)) ? "true" : "false";
7018 							color = _tColor(r, g, b, cw, ww, ColorModeRGB);
7019 							break;
7020 						case 8: //RGB_WW
7021 							r = (uint8_t)((ihex & 0x00FF000000) >> 24);
7022 							g = (uint8_t)((ihex & 0x0000FF0000) >> 16);
7023 							b = (uint8_t)((ihex & 0x000000FF00) >> 8);
7024 							ww = (uint8_t)ihex & 0xFF;
7025 							color = _tColor(r, g, b, cw, ww, ColorModeCustom);
7026 							break;
7027 						case 10: //RGB_CW_WW
7028 							r = (uint8_t)((ihex & 0xFF00000000) >> 32);
7029 							g = (uint8_t)((ihex & 0x00FF000000) >> 24);
7030 							b = (uint8_t)((ihex & 0x0000FF0000) >> 16);
7031 							cw = (uint8_t)((ihex & 0x000000FF00) >> 8);
7032 							ww = (uint8_t)ihex & 0xFF;
7033 							color = _tColor(r, g, b, cw, ww, ColorModeCustom);
7034 							break;
7035 					}
7036 					if (iswhite == "true") color.mode = ColorModeWhite;
7037 					//_log.Debug(DEBUG_WEBSERVER, "setcolbrightnessvalue: rgbww: %02x%02x%02x%02x%02x, color: '%s'", r, g, b, cw, ww, color.toString().c_str());
7038 				}
7039 				else if (!hue.empty())
7040 				{
7041 					int r, g, b;
7042 
7043 					//convert hue to RGB
7044 					float iHue = float(atof(hue.c_str()));
7045 					float iSat = 100.0f;
7046 					if (!sat.empty()) iSat = float(atof(sat.c_str()));
7047 					hsb2rgb(iHue, iSat/100.0f, 1.0f, r, g, b, 255);
7048 
7049 					color = _tColor(r, g, b, 0, 0, ColorModeRGB);
7050 					if (iswhite == "true") color.mode = ColorModeWhite;
7051 					//_log.Debug(DEBUG_WEBSERVER, "setcolbrightnessvalue2: hue: %f, rgb: %02x%02x%02x, color: '%s'", iHue, r, g, b, color.toString().c_str());
7052 				}
7053 
7054 				if (color.mode == ColorModeNone)
7055 				{
7056 					return;
7057 				}
7058 
7059 				if (!brightness.empty())
7060 					ival = atoi(brightness.c_str());
7061 				ival = int(ival * brightnessAdj);
7062 				ival = std::max(ival, 0);
7063 				ival = std::min(ival, 100);
7064 
7065 				_log.Log(LOG_STATUS, "setcolbrightnessvalue: ID: %" PRIx64 ", bri: %d, color: '%s'", ID, ival, color.toString().c_str());
7066 				m_mainworker.SwitchLight(ID, "Set Color", (unsigned char)ival, color, false, 0, Username);
7067 
7068 				root["status"] = "OK";
7069 				root["title"] = "SetColBrightnessValue";
7070 			}
7071 			else if (cparam.find("setkelvinlevel") == 0)
7072 			{
7073 				if (session.rights < 1)
7074 				{
7075 					session.reply_status = reply::forbidden;
7076 					return; //Only user/admin allowed
7077 				}
7078 
7079 				std::string Username = "Admin";
7080 				if (!session.username.empty())
7081 					Username = session.username;
7082 
7083 				root["status"] = "OK";
7084 				root["title"] = "Set Kelvin Level";
7085 
7086 				std::string idx = request::findValue(&req, "idx");
7087 
7088 				if (idx.empty())
7089 				{
7090 					return;
7091 				}
7092 
7093 				uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
7094 
7095 				std::string kelvin = request::findValue(&req, "kelvin");
7096 				double ival = atof(kelvin.c_str());
7097 				ival = std::max(ival, 0.0);
7098 				ival = std::min(ival, 100.0);
7099 				_tColor color = _tColor(round(ival*255.0f/100.0f), ColorModeTemp);
7100 				_log.Log(LOG_STATUS, "setkelvinlevel: t: %f, color: '%s'", ival, color.toString().c_str());
7101 
7102 				m_mainworker.SwitchLight(ID, "Set Color", -1, color, false, 0, Username);
7103 			}
7104 			else if (cparam == "brightnessup")
7105 			{
7106 				if (session.rights < 1)
7107 				{
7108 					session.reply_status = reply::forbidden;
7109 					return; //Only user/admin allowed
7110 				}
7111 
7112 				std::string Username = "Admin";
7113 				if (!session.username.empty())
7114 					Username = session.username;
7115 
7116 				root["status"] = "OK";
7117 				root["title"] = "Set brightness up!";
7118 
7119 				std::string idx = request::findValue(&req, "idx");
7120 
7121 				if (idx.empty())
7122 				{
7123 					return;
7124 				}
7125 
7126 				uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
7127 				m_mainworker.SwitchLight(ID, "Bright Up", 0, NoColor, false, 0, Username);
7128 			}
7129 			else if (cparam == "brightnessdown")
7130 			{
7131 				if (session.rights < 1)
7132 				{
7133 					session.reply_status = reply::forbidden;
7134 					return; //Only user/admin allowed
7135 				}
7136 
7137 				std::string Username = "Admin";
7138 				if (!session.username.empty())
7139 					Username = session.username;
7140 
7141 				root["status"] = "OK";
7142 				root["title"] = "Set brightness down!";
7143 
7144 				std::string idx = request::findValue(&req, "idx");
7145 
7146 				if (idx.empty())
7147 				{
7148 					return;
7149 				}
7150 
7151 				uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
7152 				m_mainworker.SwitchLight(ID, "Bright Down", 0, NoColor, false, 0, Username);
7153 			}
7154 			else if (cparam == "discomode")
7155 			{
7156 				if (session.rights < 1)
7157 				{
7158 					session.reply_status = reply::forbidden;
7159 					return; //Only user/admin allowed
7160 				}
7161 
7162 				std::string Username = "Admin";
7163 				if (!session.username.empty())
7164 					Username = session.username;
7165 
7166 				root["status"] = "OK";
7167 				root["title"] = "Set to last known disco mode!";
7168 
7169 				std::string idx = request::findValue(&req, "idx");
7170 
7171 				if (idx.empty())
7172 				{
7173 					return;
7174 				}
7175 
7176 				uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
7177 				m_mainworker.SwitchLight(ID, "Disco Mode", 0, NoColor, false, 0, Username);
7178 			}
7179 			else if (cparam.find("discomodenum") == 0 && cparam != "discomode" && cparam.size() == 13)
7180 			{
7181 				if (session.rights < 1)
7182 				{
7183 					session.reply_status = reply::forbidden;
7184 					return; //Only user/admin allowed
7185 				}
7186 
7187 				std::string Username = "Admin";
7188 				if (!session.username.empty())
7189 					Username = session.username;
7190 
7191 				root["status"] = "OK";
7192 				root["title"] = "Set to disco mode!";
7193 
7194 				std::string idx = request::findValue(&req, "idx");
7195 
7196 				if (idx.empty())
7197 				{
7198 					return;
7199 				}
7200 
7201 				uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
7202 				char szTmp[40];
7203 				sprintf(szTmp, "Disco Mode %s", cparam.substr(12).c_str());
7204 				m_mainworker.SwitchLight(ID, szTmp, 0, NoColor, false, 0, Username);
7205 			}
7206 			else if (cparam == "discoup")
7207 			{
7208 				if (session.rights < 1)
7209 				{
7210 					session.reply_status = reply::forbidden;
7211 					return; //Only user/admin allowed
7212 				}
7213 
7214 				std::string Username = "Admin";
7215 				if (!session.username.empty())
7216 					Username = session.username;
7217 
7218 				root["status"] = "OK";
7219 				root["title"] = "Set to next disco mode!";
7220 
7221 				std::string idx = request::findValue(&req, "idx");
7222 
7223 				if (idx.empty())
7224 				{
7225 					return;
7226 				}
7227 
7228 				uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
7229 				m_mainworker.SwitchLight(ID, "Disco Up", 0, NoColor, false, 0, Username);
7230 			}
7231 			else if (cparam == "discodown")
7232 			{
7233 				if (session.rights < 1)
7234 				{
7235 					session.reply_status = reply::forbidden;
7236 					return; //Only user/admin allowed
7237 				}
7238 
7239 				std::string Username = "Admin";
7240 				if (!session.username.empty())
7241 					Username = session.username;
7242 
7243 				root["status"] = "OK";
7244 				root["title"] = "Set to previous disco mode!";
7245 
7246 				std::string idx = request::findValue(&req, "idx");
7247 
7248 				if (idx.empty())
7249 				{
7250 					return;
7251 				}
7252 
7253 				uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
7254 				m_mainworker.SwitchLight(ID, "Disco Down", 0, NoColor, false, 0, Username);
7255 			}
7256 			else if (cparam == "speedup")
7257 			{
7258 				if (session.rights < 1)
7259 				{
7260 					session.reply_status = reply::forbidden;
7261 					return; //Only user/admin allowed
7262 				}
7263 
7264 				std::string Username = "Admin";
7265 				if (!session.username.empty())
7266 					Username = session.username;
7267 
7268 				root["status"] = "OK";
7269 				root["title"] = "Set disco speed up!";
7270 
7271 				std::string idx = request::findValue(&req, "idx");
7272 
7273 				if (idx.empty())
7274 				{
7275 					return;
7276 				}
7277 
7278 				uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
7279 				m_mainworker.SwitchLight(ID, "Speed Up", 0, NoColor, false, 0, Username);
7280 			}
7281 			else if (cparam == "speeduplong")
7282 			{
7283 				if (session.rights < 1)
7284 				{
7285 					session.reply_status = reply::forbidden;
7286 					return; //Only user/admin allowed
7287 				}
7288 
7289 				std::string Username = "Admin";
7290 				if (!session.username.empty())
7291 					Username = session.username;
7292 
7293 				root["status"] = "OK";
7294 				root["title"] = "Set speed long!";
7295 
7296 				std::string idx = request::findValue(&req, "idx");
7297 
7298 				if (idx.empty())
7299 				{
7300 					return;
7301 				}
7302 
7303 				uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
7304 				m_mainworker.SwitchLight(ID, "Speed Up Long", 0, NoColor, false, 0, Username);
7305 			}
7306 			else if (cparam == "speeddown")
7307 			{
7308 				if (session.rights < 1)
7309 				{
7310 					session.reply_status = reply::forbidden;
7311 					return; //Only user/admin allowed
7312 				}
7313 
7314 				std::string Username = "Admin";
7315 				if (!session.username.empty())
7316 					Username = session.username;
7317 
7318 				root["status"] = "OK";
7319 				root["title"] = "Set disco speed down!";
7320 
7321 				std::string idx = request::findValue(&req, "idx");
7322 
7323 				if (idx.empty())
7324 				{
7325 					return;
7326 				}
7327 
7328 				uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
7329 				m_mainworker.SwitchLight(ID, "Speed Down", 0, NoColor, false, 0, Username);
7330 			}
7331 			else if (cparam == "speedmin")
7332 			{
7333 				if (session.rights < 1)
7334 				{
7335 					session.reply_status = reply::forbidden;
7336 					return; //Only user/admin allowed
7337 				}
7338 
7339 				std::string Username = "Admin";
7340 				if (!session.username.empty())
7341 					Username = session.username;
7342 
7343 				root["status"] = "OK";
7344 				root["title"] = "Set disco speed minimal!";
7345 
7346 				std::string idx = request::findValue(&req, "idx");
7347 
7348 				if (idx.empty())
7349 				{
7350 					return;
7351 				}
7352 
7353 				uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
7354 				m_mainworker.SwitchLight(ID, "Speed Minimal", 0, NoColor, false, 0, Username);
7355 			}
7356 			else if (cparam == "speedmax")
7357 			{
7358 				if (session.rights < 1)
7359 				{
7360 					session.reply_status = reply::forbidden;
7361 					return; //Only user/admin allowed
7362 				}
7363 
7364 				std::string Username = "Admin";
7365 				if (!session.username.empty())
7366 					Username = session.username;
7367 
7368 				root["status"] = "OK";
7369 				root["title"] = "Set disco speed maximal!";
7370 
7371 				std::string idx = request::findValue(&req, "idx");
7372 
7373 				if (idx.empty())
7374 				{
7375 					return;
7376 				}
7377 
7378 				uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
7379 				m_mainworker.SwitchLight(ID, "Speed Maximal", 0, NoColor, false, 0, Username);
7380 			}
7381 			else if (cparam == "warmer")
7382 			{
7383 				if (session.rights < 1)
7384 				{
7385 					session.reply_status = reply::forbidden;
7386 					return; //Only user/admin allowed
7387 				}
7388 
7389 				std::string Username = "Admin";
7390 				if (!session.username.empty())
7391 					Username = session.username;
7392 
7393 				root["status"] = "OK";
7394 				root["title"] = "Set Kelvin up!";
7395 
7396 				std::string idx = request::findValue(&req, "idx");
7397 
7398 				if (idx.empty())
7399 				{
7400 					return;
7401 				}
7402 
7403 				uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
7404 				m_mainworker.SwitchLight(ID, "Warmer", 0, NoColor, false, 0, Username);
7405 			}
7406 			else if (cparam == "cooler")
7407 			{
7408 				if (session.rights < 1)
7409 				{
7410 					session.reply_status = reply::forbidden;
7411 					return; //Only user/admin allowed
7412 				}
7413 
7414 				std::string Username = "Admin";
7415 				if (!session.username.empty())
7416 					Username = session.username;
7417 
7418 				root["status"] = "OK";
7419 				root["title"] = "Set Kelvin down!";
7420 
7421 				std::string idx = request::findValue(&req, "idx");
7422 
7423 				if (idx.empty())
7424 				{
7425 					return;
7426 				}
7427 
7428 				uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
7429 				m_mainworker.SwitchLight(ID, "Cooler", 0, NoColor, false, 0, Username);
7430 			}
7431 			else if (cparam == "fulllight")
7432 			{
7433 				if (session.rights < 1)
7434 				{
7435 					session.reply_status = reply::forbidden;
7436 					return; //Only user/admin allowed
7437 				}
7438 
7439 				std::string Username = "Admin";
7440 				if (!session.username.empty())
7441 					Username = session.username;
7442 
7443 				root["status"] = "OK";
7444 				root["title"] = "Set Full!";
7445 
7446 				std::string idx = request::findValue(&req, "idx");
7447 
7448 				if (idx.empty())
7449 				{
7450 					return;
7451 				}
7452 
7453 				uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
7454 				m_mainworker.SwitchLight(ID, "Set Full", 0, NoColor, false, 0, Username);
7455 			}
7456 			else if (cparam == "nightlight")
7457 			{
7458 				if (session.rights < 1)
7459 				{
7460 					session.reply_status = reply::forbidden;
7461 					return; //Only user/admin allowed
7462 				}
7463 
7464 				std::string Username = "Admin";
7465 				if (!session.username.empty())
7466 					Username = session.username;
7467 
7468 				root["status"] = "OK";
7469 				root["title"] = "Set to nightlight!";
7470 
7471 				std::string idx = request::findValue(&req, "idx");
7472 
7473 				if (idx.empty())
7474 				{
7475 					return;
7476 				}
7477 
7478 				uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
7479 				m_mainworker.SwitchLight(ID, "Set Night", 0, NoColor, false, 0, Username);
7480 			}
7481 			else if (cparam == "whitelight")
7482 			{
7483 				if (session.rights < 1)
7484 				{
7485 					session.reply_status = reply::forbidden;
7486 					return; //Only user/admin allowed
7487 				}
7488 
7489 				std::string Username = "Admin";
7490 				if (!session.username.empty())
7491 					Username = session.username;
7492 
7493 				root["status"] = "OK";
7494 				root["title"] = "Set to clear white!";
7495 
7496 				std::string idx = request::findValue(&req, "idx");
7497 
7498 				if (idx.empty())
7499 				{
7500 					return;
7501 				}
7502 
7503 				uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
7504 				//TODO: Change to color with mode=ColorModeWhite and level=100?
7505 				m_mainworker.SwitchLight(ID, "Set White", 0, NoColor, false, 0, Username);
7506 			}
7507 			else if (cparam == "getfloorplanimages")
7508 			{
7509 				root["status"] = "OK";
7510 				root["title"] = "GetFloorplanImages";
7511 
7512 				bool bReturnUnused = atoi(request::findValue(&req, "unused").c_str()) != 0;
7513 
7514 				if (!bReturnUnused)
7515 					result = m_sql.safe_query("SELECT ID, Name, ScaleFactor FROM Floorplans ORDER BY [Name]");
7516 				else
7517 					result = m_sql.safe_query("SELECT ID, Name, ScaleFactor FROM Floorplans WHERE ID NOT IN(SELECT FloorplanID FROM Plans)");
7518 				if (!result.empty())
7519 				{
7520 					int ii = 0;
7521 					for (const auto & itt : result)
7522 					{
7523 						std::vector<std::string> sd = itt;
7524 
7525 						root["result"][ii]["idx"] = sd[0];
7526 						root["result"][ii]["name"] = sd[1];
7527 						root["result"][ii]["scalefactor"] = sd[2];
7528 						ii++;
7529 					}
7530 				}
7531 			}
7532 			else if (cparam == "updatefloorplan")
7533 			{
7534 				if (session.rights < 2)
7535 				{
7536 					session.reply_status = reply::forbidden;
7537 					return; //Only admin user allowed
7538 				}
7539 
7540 				std::string idx = request::findValue(&req, "idx");
7541 				if (idx.empty())
7542 					return;
7543 				std::string name = HTMLSanitizer::Sanitize(request::findValue(&req, "name"));
7544 				std::string scalefactor = request::findValue(&req, "scalefactor");
7545 				if (
7546 					(name.empty())
7547 					||(scalefactor.empty())
7548 					)
7549 					return;
7550 
7551 				root["status"] = "OK";
7552 				root["title"] = "UpdateFloorplan";
7553 
7554 				m_sql.safe_query(
7555 					"UPDATE Floorplans SET Name='%q',ScaleFactor='%q' WHERE (ID == '%q')",
7556 					name.c_str(),
7557 					scalefactor.c_str(),
7558 					idx.c_str()
7559 				);
7560 			}
7561 			else if (cparam == "deletefloorplan")
7562 			{
7563 				if (session.rights < 2)
7564 				{
7565 					session.reply_status = reply::forbidden;
7566 					return; //Only admin user allowed
7567 				}
7568 
7569 				std::string idx = request::findValue(&req, "idx");
7570 				if (idx.empty())
7571 					return;
7572 				root["status"] = "OK";
7573 				root["title"] = "DeleteFloorplan";
7574 				m_sql.safe_query("UPDATE DeviceToPlansMap SET XOffset=0,YOffset=0 WHERE (PlanID IN (SELECT ID from Plans WHERE (FloorplanID == '%q')))", idx.c_str());
7575 				m_sql.safe_query("UPDATE Plans SET FloorplanID=0,Area='' WHERE (FloorplanID == '%q')", idx.c_str());
7576 				m_sql.safe_query("DELETE FROM Floorplans WHERE (ID == '%q')", idx.c_str());
7577 			}
7578 			else if (cparam == "changefloorplanorder")
7579 			{
7580 				if (session.rights < 2)
7581 				{
7582 					session.reply_status = reply::forbidden;
7583 					return; //Only admin user allowed
7584 				}
7585 
7586 				std::string idx = request::findValue(&req, "idx");
7587 				if (idx.empty())
7588 					return;
7589 				std::string sway = request::findValue(&req, "way");
7590 				if (sway.empty())
7591 					return;
7592 				bool bGoUp = (sway == "0");
7593 
7594 				std::string aOrder, oID, oOrder;
7595 
7596 				result = m_sql.safe_query("SELECT [Order] FROM Floorplans WHERE (ID=='%q')",
7597 					idx.c_str());
7598 				if (result.empty())
7599 					return;
7600 				aOrder = result[0][0];
7601 
7602 				if (!bGoUp)
7603 				{
7604 					//Get next device order
7605 					result = m_sql.safe_query("SELECT ID, [Order] FROM Floorplans WHERE ([Order]>'%q') ORDER BY [Order] ASC",
7606 						aOrder.c_str());
7607 					if (result.empty())
7608 						return;
7609 					oID = result[0][0];
7610 					oOrder = result[0][1];
7611 				}
7612 				else
7613 				{
7614 					//Get previous device order
7615 					result = m_sql.safe_query("SELECT ID, [Order] FROM Floorplans WHERE ([Order]<'%q') ORDER BY [Order] DESC",
7616 						aOrder.c_str());
7617 					if (result.empty())
7618 						return;
7619 					oID = result[0][0];
7620 					oOrder = result[0][1];
7621 				}
7622 				//Swap them
7623 				root["status"] = "OK";
7624 				root["title"] = "ChangeFloorPlanOrder";
7625 
7626 				m_sql.safe_query("UPDATE Floorplans SET [Order] = '%q' WHERE (ID='%q')",
7627 					oOrder.c_str(), idx.c_str());
7628 				m_sql.safe_query("UPDATE Floorplans SET [Order] = '%q' WHERE (ID='%q')",
7629 					aOrder.c_str(), oID.c_str());
7630 			}
7631 			else if (cparam == "getunusedfloorplanplans")
7632 			{
7633 				if (session.rights < 2)
7634 				{
7635 					session.reply_status = reply::forbidden;
7636 					return; //Only admin user allowed
7637 				}
7638 
7639 				root["status"] = "OK";
7640 				root["title"] = "GetUnusedFloorplanPlans";
7641 				int ii = 0;
7642 
7643 				result = m_sql.safe_query("SELECT ID, Name FROM Plans WHERE (FloorplanID==0) ORDER BY Name");
7644 				if (!result.empty())
7645 				{
7646 					for (const auto & itt : result)
7647 					{
7648 						std::vector<std::string> sd = itt;
7649 
7650 						root["result"][ii]["type"] = 0;
7651 						root["result"][ii]["idx"] = sd[0];
7652 						root["result"][ii]["Name"] = sd[1];
7653 						ii++;
7654 					}
7655 				}
7656 			}
7657 			else if (cparam == "getfloorplanplans")
7658 			{
7659 				std::string idx = request::findValue(&req, "idx");
7660 				if (idx.empty())
7661 					return;
7662 				root["status"] = "OK";
7663 				root["title"] = "GetFloorplanPlans";
7664 				int ii = 0;
7665 				result = m_sql.safe_query("SELECT ID, Name, Area FROM Plans WHERE (FloorplanID=='%q') ORDER BY Name",
7666 					idx.c_str());
7667 				if (!result.empty())
7668 				{
7669 					for (const auto & itt : result)
7670 					{
7671 						std::vector<std::string> sd = itt;
7672 
7673 						root["result"][ii]["idx"] = sd[0];
7674 						root["result"][ii]["Name"] = sd[1];
7675 						root["result"][ii]["Area"] = sd[2];
7676 						ii++;
7677 					}
7678 				}
7679 			}
7680 			else if (cparam == "addfloorplanplan")
7681 			{
7682 				if (session.rights < 2)
7683 				{
7684 					session.reply_status = reply::forbidden;
7685 					return; //Only admin user allowed
7686 				}
7687 
7688 				std::string idx = request::findValue(&req, "idx");
7689 				std::string planidx = request::findValue(&req, "planidx");
7690 				if (
7691 					(idx.empty()) ||
7692 					(planidx.empty())
7693 					)
7694 					return;
7695 				root["status"] = "OK";
7696 				root["title"] = "AddFloorplanPlan";
7697 
7698 				m_sql.safe_query(
7699 					"UPDATE Plans SET FloorplanID='%q' WHERE (ID == '%q')",
7700 					idx.c_str(),
7701 					planidx.c_str()
7702 				);
7703 				_log.Log(LOG_STATUS, "(Floorplan) Plan '%s' added to floorplan '%s'.", planidx.c_str(), idx.c_str());
7704 			}
7705 			else if (cparam == "updatefloorplanplan")
7706 			{
7707 				if (session.rights < 2)
7708 				{
7709 					session.reply_status = reply::forbidden;
7710 					return; //Only admin user allowed
7711 				}
7712 
7713 				std::string planidx = request::findValue(&req, "planidx");
7714 				std::string planarea = request::findValue(&req, "area");
7715 				if (planidx.empty())
7716 					return;
7717 				root["status"] = "OK";
7718 				root["title"] = "UpdateFloorplanPlan";
7719 
7720 				m_sql.safe_query(
7721 					"UPDATE Plans SET Area='%q' WHERE (ID == '%q')",
7722 					planarea.c_str(),
7723 					planidx.c_str()
7724 				);
7725 				_log.Log(LOG_STATUS, "(Floorplan) Plan '%s' floor area updated to '%s'.", planidx.c_str(), planarea.c_str());
7726 			}
7727 			else if (cparam == "deletefloorplanplan")
7728 			{
7729 				if (session.rights < 2)
7730 				{
7731 					session.reply_status = reply::forbidden;
7732 					return; //Only admin user allowed
7733 				}
7734 
7735 				std::string idx = request::findValue(&req, "idx");
7736 				if (idx.empty())
7737 					return;
7738 				root["status"] = "OK";
7739 				root["title"] = "DeleteFloorplanPlan";
7740 				m_sql.safe_query(
7741 					"UPDATE DeviceToPlansMap SET XOffset=0,YOffset=0 WHERE (PlanID == '%q')",
7742 					idx.c_str()
7743 				);
7744 				_log.Log(LOG_STATUS, "(Floorplan) Device coordinates reset for plan '%s'.", idx.c_str());
7745 				m_sql.safe_query(
7746 					"UPDATE Plans SET FloorplanID=0,Area='' WHERE (ID == '%q')",
7747 					idx.c_str()
7748 				);
7749 				_log.Log(LOG_STATUS, "(Floorplan) Plan '%s' floorplan data reset.", idx.c_str());
7750 			}
7751 		}
7752 
DisplaySwitchTypesCombo(std::string & content_part)7753 		void CWebServer::DisplaySwitchTypesCombo(std::string & content_part)
7754 		{
7755 			char szTmp[200];
7756 
7757 			std::map<std::string, int> _switchtypes;
7758 
7759 			for (int ii = 0; ii < STYPE_END; ii++)
7760 			{
7761 				_switchtypes[Switch_Type_Desc((_eSwitchType)ii)] = ii;
7762 			}
7763 			//return a sorted list
7764 			for (const auto & itt : _switchtypes)
7765 			{
7766 				sprintf(szTmp, "<option value=\"%d\">%s</option>\n", itt.second, itt.first.c_str());
7767 				content_part += szTmp;
7768 			}
7769 		}
7770 
DisplayMeterTypesCombo(std::string & content_part)7771 		void CWebServer::DisplayMeterTypesCombo(std::string & content_part)
7772 		{
7773 			char szTmp[200];
7774 			for (int ii = 0; ii < MTYPE_END; ii++)
7775 			{
7776 				sprintf(szTmp, "<option value=\"%d\">%s</option>\n", ii, Meter_Type_Desc((_eMeterType)ii));
7777 				content_part += szTmp;
7778 			}
7779 		}
7780 
DisplayLanguageCombo(std::string & content_part)7781 		void CWebServer::DisplayLanguageCombo(std::string & content_part)
7782 		{
7783 			//return a sorted list
7784 			std::map<std::string, std::string> _ltypes;
7785 			char szTmp[200];
7786 			int ii = 0;
7787 			while (guiLanguage[ii].szShort != NULL)
7788 			{
7789 				_ltypes[guiLanguage[ii].szLong] = guiLanguage[ii].szShort;
7790 				ii++;
7791 			}
7792 			for (const auto & itt : _ltypes)
7793 			{
7794 				sprintf(szTmp, "<option value=\"%s\">%s</option>\n", itt.second.c_str(), itt.first.c_str());
7795 				content_part += szTmp;
7796 			}
7797 		}
7798 
DisplayTimerTypesCombo(std::string & content_part)7799 		void CWebServer::DisplayTimerTypesCombo(std::string & content_part)
7800 		{
7801 			char szTmp[200];
7802 			for (int ii = 0; ii < TTYPE_END; ii++)
7803 			{
7804 				sprintf(szTmp, "<option data-i18n=\"%s\" value=\"%d\">%s</option>\n", Timer_Type_Desc(ii), ii, Timer_Type_Desc(ii));
7805 				content_part += szTmp;
7806 			}
7807 		}
7808 
LoadUsers()7809 		void CWebServer::LoadUsers()
7810 		{
7811 			ClearUserPasswords();
7812 			std::string WebUserName, WebPassword;
7813 			int nValue = 0;
7814 			if (m_sql.GetPreferencesVar("WebUserName", nValue, WebUserName))
7815 			{
7816 				if (m_sql.GetPreferencesVar("WebPassword", nValue, WebPassword))
7817 				{
7818 					if ((WebUserName != "") && (WebPassword != ""))
7819 					{
7820 						WebUserName = base64_decode(WebUserName);
7821 						//WebPassword = WebPassword;
7822 						AddUser(10000, WebUserName, WebPassword, URIGHTS_ADMIN, 0xFFFF);
7823 
7824 						std::vector<std::vector<std::string> > result;
7825 						result = m_sql.safe_query("SELECT ID, Active, Username, Password, Rights, TabsEnabled FROM Users");
7826 						if (!result.empty())
7827 						{
7828 							for (const auto & itt : result)
7829 							{
7830 								std::vector<std::string> sd = itt;
7831 
7832 								int bIsActive = static_cast<int>(atoi(sd[1].c_str()));
7833 								if (bIsActive)
7834 								{
7835 									unsigned long ID = (unsigned long)atol(sd[0].c_str());
7836 
7837 									std::string username = base64_decode(sd[2]);
7838 									std::string password = sd[3];
7839 
7840 									_eUserRights rights = (_eUserRights)atoi(sd[4].c_str());
7841 									int activetabs = atoi(sd[5].c_str());
7842 
7843 									AddUser(ID, username, password, rights, activetabs);
7844 								}
7845 							}
7846 						}
7847 					}
7848 				}
7849 			}
7850 			m_mainworker.LoadSharedUsers();
7851 		}
7852 
AddUser(const unsigned long ID,const std::string & username,const std::string & password,const int userrights,const int activetabs)7853 		void CWebServer::AddUser(const unsigned long ID, const std::string &username, const std::string &password, const int userrights, const int activetabs)
7854 		{
7855 			std::vector<std::vector<std::string> > result = m_sql.safe_query("SELECT COUNT(*) FROM SharedDevices WHERE (SharedUserID == '%d')", ID);
7856 			if (result.empty())
7857 				return;
7858 
7859 			_tWebUserPassword wtmp;
7860 			wtmp.ID = ID;
7861 			wtmp.Username = username;
7862 			wtmp.Password = password;
7863 			wtmp.userrights = (_eUserRights)userrights;
7864 			wtmp.ActiveTabs = activetabs;
7865 			wtmp.TotSensors = atoi(result[0][0].c_str());
7866 			m_users.push_back(wtmp);
7867 
7868 			m_pWebEm->AddUserPassword(ID, username, password, (_eUserRights)userrights, activetabs);
7869 		}
7870 
ClearUserPasswords()7871 		void CWebServer::ClearUserPasswords()
7872 		{
7873 			m_users.clear();
7874 			m_pWebEm->ClearUserPasswords();
7875 		}
7876 
FindUser(const char * szUserName)7877 		int CWebServer::FindUser(const char* szUserName)
7878 		{
7879 			int iUser = 0;
7880 			for (const auto & itt : m_users)
7881 			{
7882 				if (itt.Username == szUserName)
7883 					return iUser;
7884 				iUser++;
7885 			}
7886 			return -1;
7887 		}
7888 
FindAdminUser()7889 		bool CWebServer::FindAdminUser()
7890 		{
7891 			for (const auto & itt : m_users)
7892 			{
7893 				if (itt.userrights == URIGHTS_ADMIN)
7894 					return true;
7895 			}
7896 			return false;
7897 		}
7898 
PostSettings(WebEmSession & session,const request & req,reply & rep)7899 		void CWebServer::PostSettings(WebEmSession& session, const request& req, reply& rep)
7900 		{
7901 			if (session.rights != 2)
7902 			{
7903 				session.reply_status = reply::forbidden;
7904 				return; //Only admin user allowed
7905 			}
7906 
7907 			std::string Latitude = request::findValue(&req, "Latitude");
7908 			std::string Longitude = request::findValue(&req, "Longitude");
7909 			if ((Latitude != "") && (Longitude != ""))
7910 			{
7911 				std::string LatLong = Latitude + ";" + Longitude;
7912 				m_sql.UpdatePreferencesVar("Location", LatLong.c_str());
7913 				m_mainworker.GetSunSettings();
7914 			}
7915 			m_notifications.ConfigFromGetvars(req, true);
7916 			std::string DashboardType = request::findValue(&req, "DashboardType");
7917 			m_sql.UpdatePreferencesVar("DashboardType", atoi(DashboardType.c_str()));
7918 			std::string MobileType = request::findValue(&req, "MobileType");
7919 			m_sql.UpdatePreferencesVar("MobileType", atoi(MobileType.c_str()));
7920 
7921 			int nUnit = atoi(request::findValue(&req, "WindUnit").c_str());
7922 			m_sql.UpdatePreferencesVar("WindUnit", nUnit);
7923 			m_sql.m_windunit = (_eWindUnit)nUnit;
7924 
7925 			nUnit = atoi(request::findValue(&req, "TempUnit").c_str());
7926 			m_sql.UpdatePreferencesVar("TempUnit", nUnit);
7927 			m_sql.m_tempunit = (_eTempUnit)nUnit;
7928 
7929 			nUnit = atoi(request::findValue(&req, "WeightUnit").c_str());
7930 			m_sql.UpdatePreferencesVar("WeightUnit", nUnit);
7931 			m_sql.m_weightunit = (_eWeightUnit)nUnit;
7932 
7933 
7934 			m_sql.SetUnitsAndScale();
7935 
7936 			std::string AuthenticationMethod = request::findValue(&req, "AuthenticationMethod");
7937 			_eAuthenticationMethod amethod = (_eAuthenticationMethod)atoi(AuthenticationMethod.c_str());
7938 			m_sql.UpdatePreferencesVar("AuthenticationMethod", static_cast<int>(amethod));
7939 			m_pWebEm->SetAuthenticationMethod(amethod);
7940 
7941 			std::string ReleaseChannel = request::findValue(&req, "ReleaseChannel");
7942 			m_sql.UpdatePreferencesVar("ReleaseChannel", atoi(ReleaseChannel.c_str()));
7943 
7944 			std::string LightHistoryDays = request::findValue(&req, "LightHistoryDays");
7945 			m_sql.UpdatePreferencesVar("LightHistoryDays", atoi(LightHistoryDays.c_str()));
7946 
7947 			std::string s5MinuteHistoryDays = request::findValue(&req, "ShortLogDays");
7948 			m_sql.UpdatePreferencesVar("5MinuteHistoryDays", atoi(s5MinuteHistoryDays.c_str()));
7949 
7950 			int iShortLogInterval = atoi(request::findValue(&req, "ShortLogInterval").c_str());
7951 			if (iShortLogInterval < 1)
7952 				iShortLogInterval = 5;
7953 			m_sql.UpdatePreferencesVar("ShortLogInterval", iShortLogInterval);
7954 			m_sql.m_ShortLogInterval = iShortLogInterval;
7955 
7956 			std::string sElectricVoltage = request::findValue(&req, "ElectricVoltage");
7957 			m_sql.UpdatePreferencesVar("ElectricVoltage", atoi(sElectricVoltage.c_str()));
7958 
7959 			std::string sCM113DisplayType = request::findValue(&req, "CM113DisplayType");
7960 			m_sql.UpdatePreferencesVar("CM113DisplayType", atoi(sCM113DisplayType.c_str()));
7961 
7962 			std::string WebUserName = base64_encode(CURLEncode::URLDecode(request::findValue(&req, "WebUserName")));
7963 			std::string WebPassword = CURLEncode::URLDecode(request::findValue(&req, "WebPassword"));
7964 
7965 			//Get old username/password
7966 			std::string sOldWebLogin;
7967 			std::string sOldWebPassword;
7968 			m_sql.GetPreferencesVar("WebUserName", sOldWebLogin);
7969 			m_sql.GetPreferencesVar("WebPassword", sOldWebPassword);
7970 
7971 			bool bHaveAdminUserPasswordChange = false;
7972 
7973 			if ((WebUserName == sOldWebLogin) && (WebPassword.empty()))
7974 			{
7975 				//All is OK, no changes
7976 			}
7977 			else if (WebUserName.empty() || WebPassword.empty())
7978 			{
7979 				//If no Admin User/Password is specified, we clear them
7980 				if ((!sOldWebLogin.empty()) || (!sOldWebPassword.empty()))
7981 					bHaveAdminUserPasswordChange = true;
7982 				WebUserName = "";
7983 				WebPassword = "";
7984 			}
7985 			else {
7986 				if ((WebUserName != sOldWebLogin) || (WebPassword != sOldWebPassword))
7987 				{
7988 					bHaveAdminUserPasswordChange = true;
7989 				}
7990 			}
7991 
7992 			// Invalid sessions of WebUser when the username or password has been changed
7993 			if (bHaveAdminUserPasswordChange)
7994 			{
7995 				RemoveUsersSessions(sOldWebLogin, session);
7996 				m_sql.UpdatePreferencesVar("WebUserName", WebUserName.c_str());
7997 				m_sql.UpdatePreferencesVar("WebPassword", WebPassword.c_str());
7998 			}
7999 
8000 			std::string WebLocalNetworks = CURLEncode::URLDecode(request::findValue(&req, "WebLocalNetworks"));
8001 			std::string WebRemoteProxyIPs = CURLEncode::URLDecode(request::findValue(&req, "WebRemoteProxyIPs"));
8002 			m_sql.UpdatePreferencesVar("WebLocalNetworks", WebLocalNetworks.c_str());
8003 			m_sql.UpdatePreferencesVar("WebRemoteProxyIPs", WebRemoteProxyIPs.c_str());
8004 
8005 			LoadUsers();
8006 			m_pWebEm->ClearLocalNetworks();
8007 			std::vector<std::string> strarray;
8008 			StringSplit(WebLocalNetworks, ";", strarray);
8009 			for (const auto & itt : strarray)
8010 				m_pWebEm->AddLocalNetworks(itt);
8011 			//add local hostname
8012 			m_pWebEm->AddLocalNetworks("");
8013 
8014 			m_pWebEm->ClearRemoteProxyIPs();
8015 			strarray.clear();
8016 			StringSplit(WebRemoteProxyIPs, ";", strarray);
8017 			for (const auto & itt : strarray)
8018 				m_pWebEm->AddRemoteProxyIPs(itt);
8019 
8020 			if (session.username.empty())
8021 			{
8022 				//Local network could be changed so lets for a check here
8023 				session.rights = -1;
8024 			}
8025 
8026 			std::string SecPassword = request::findValue(&req, "SecPassword");
8027 			SecPassword = CURLEncode::URLDecode(SecPassword);
8028 			if (SecPassword.size() != 32)
8029 			{
8030 				SecPassword = GenerateMD5Hash(SecPassword);
8031 			}
8032 			m_sql.UpdatePreferencesVar("SecPassword", SecPassword.c_str());
8033 
8034 			std::string ProtectionPassword = request::findValue(&req, "ProtectionPassword");
8035 			ProtectionPassword = CURLEncode::URLDecode(ProtectionPassword);
8036 			if (ProtectionPassword.size() != 32)
8037 			{
8038 				ProtectionPassword = GenerateMD5Hash(ProtectionPassword);
8039 			}
8040 			m_sql.UpdatePreferencesVar("ProtectionPassword", ProtectionPassword.c_str());
8041 
8042 			int EnergyDivider = atoi(request::findValue(&req, "EnergyDivider").c_str());
8043 			int GasDivider = atoi(request::findValue(&req, "GasDivider").c_str());
8044 			int WaterDivider = atoi(request::findValue(&req, "WaterDivider").c_str());
8045 			if (EnergyDivider < 1)
8046 				EnergyDivider = 1000;
8047 			if (GasDivider < 1)
8048 				GasDivider = 100;
8049 			if (WaterDivider < 1)
8050 				WaterDivider = 100;
8051 			m_sql.UpdatePreferencesVar("MeterDividerEnergy", EnergyDivider);
8052 			m_sql.UpdatePreferencesVar("MeterDividerGas", GasDivider);
8053 			m_sql.UpdatePreferencesVar("MeterDividerWater", WaterDivider);
8054 
8055 			std::string scheckforupdates = request::findValue(&req, "checkforupdates");
8056 			m_sql.UpdatePreferencesVar("UseAutoUpdate", (scheckforupdates == "on" ? 1 : 0));
8057 
8058 			std::string senableautobackup = request::findValue(&req, "enableautobackup");
8059 			m_sql.UpdatePreferencesVar("UseAutoBackup", (senableautobackup == "on" ? 1 : 0));
8060 
8061 			float CostEnergy = static_cast<float>(atof(request::findValue(&req, "CostEnergy").c_str()));
8062 			float CostEnergyT2 = static_cast<float>(atof(request::findValue(&req, "CostEnergyT2").c_str()));
8063 			float CostEnergyR1 = static_cast<float>(atof(request::findValue(&req, "CostEnergyR1").c_str()));
8064 			float CostEnergyR2 = static_cast<float>(atof(request::findValue(&req, "CostEnergyR2").c_str()));
8065 			float CostGas = static_cast<float>(atof(request::findValue(&req, "CostGas").c_str()));
8066 			float CostWater = static_cast<float>(atof(request::findValue(&req, "CostWater").c_str()));
8067 			m_sql.UpdatePreferencesVar("CostEnergy", int(CostEnergy*10000.0f));
8068 			m_sql.UpdatePreferencesVar("CostEnergyT2", int(CostEnergyT2*10000.0f));
8069 			m_sql.UpdatePreferencesVar("CostEnergyR1", int(CostEnergyR1*10000.0f));
8070 			m_sql.UpdatePreferencesVar("CostEnergyR2", int(CostEnergyR2*10000.0f));
8071 			m_sql.UpdatePreferencesVar("CostGas", int(CostGas*10000.0f));
8072 			m_sql.UpdatePreferencesVar("CostWater", int(CostWater*10000.0f));
8073 
8074 			int rnOldvalue = 0;
8075 			int rnvalue = 0;
8076 
8077 			m_sql.GetPreferencesVar("ActiveTimerPlan", rnOldvalue);
8078 			rnvalue = atoi(request::findValue(&req, "ActiveTimerPlan").c_str());
8079 			if (rnOldvalue != rnvalue)
8080 			{
8081 				m_sql.UpdatePreferencesVar("ActiveTimerPlan", rnvalue);
8082 				m_sql.m_ActiveTimerPlan = rnvalue;
8083 				m_mainworker.m_scheduler.ReloadSchedules();
8084 			}
8085 			m_sql.UpdatePreferencesVar("DoorbellCommand", atoi(request::findValue(&req, "DoorbellCommand").c_str()));
8086 			m_sql.UpdatePreferencesVar("SmartMeterType", atoi(request::findValue(&req, "SmartMeterType").c_str()));
8087 
8088 			std::string EnableTabFloorplans = request::findValue(&req, "EnableTabFloorplans");
8089 			m_sql.UpdatePreferencesVar("EnableTabFloorplans", (EnableTabFloorplans == "on" ? 1 : 0));
8090 			std::string EnableTabLights = request::findValue(&req, "EnableTabLights");
8091 			m_sql.UpdatePreferencesVar("EnableTabLights", (EnableTabLights == "on" ? 1 : 0));
8092 			std::string EnableTabTemp = request::findValue(&req, "EnableTabTemp");
8093 			m_sql.UpdatePreferencesVar("EnableTabTemp", (EnableTabTemp == "on" ? 1 : 0));
8094 			std::string EnableTabWeather = request::findValue(&req, "EnableTabWeather");
8095 			m_sql.UpdatePreferencesVar("EnableTabWeather", (EnableTabWeather == "on" ? 1 : 0));
8096 			std::string EnableTabUtility = request::findValue(&req, "EnableTabUtility");
8097 			m_sql.UpdatePreferencesVar("EnableTabUtility", (EnableTabUtility == "on" ? 1 : 0));
8098 			std::string EnableTabScenes = request::findValue(&req, "EnableTabScenes");
8099 			m_sql.UpdatePreferencesVar("EnableTabScenes", (EnableTabScenes == "on" ? 1 : 0));
8100 			std::string EnableTabCustom = request::findValue(&req, "EnableTabCustom");
8101 			m_sql.UpdatePreferencesVar("EnableTabCustom", (EnableTabCustom == "on" ? 1 : 0));
8102 
8103 			m_sql.GetPreferencesVar("NotificationSensorInterval", rnOldvalue);
8104 			rnvalue = atoi(request::findValue(&req, "NotificationSensorInterval").c_str());
8105 			if (rnOldvalue != rnvalue)
8106 			{
8107 				m_sql.UpdatePreferencesVar("NotificationSensorInterval", rnvalue);
8108 				m_notifications.ReloadNotifications();
8109 			}
8110 			m_sql.GetPreferencesVar("NotificationSwitchInterval", rnOldvalue);
8111 			rnvalue = atoi(request::findValue(&req, "NotificationSwitchInterval").c_str());
8112 			if (rnOldvalue != rnvalue)
8113 			{
8114 				m_sql.UpdatePreferencesVar("NotificationSwitchInterval", rnvalue);
8115 				m_notifications.ReloadNotifications();
8116 			}
8117 			std::string RaspCamParams = request::findValue(&req, "RaspCamParams");
8118 			if (RaspCamParams != "")
8119 			{
8120 				if (IsArgumentSecure(RaspCamParams))
8121 					m_sql.UpdatePreferencesVar("RaspCamParams", RaspCamParams.c_str());
8122 			}
8123 
8124 			std::string UVCParams = request::findValue(&req, "UVCParams");
8125 			if (UVCParams != "")
8126 			{
8127 				if (IsArgumentSecure(UVCParams))
8128 					m_sql.UpdatePreferencesVar("UVCParams", UVCParams.c_str());
8129 			}
8130 
8131 			std::string EnableNewHardware = request::findValue(&req, "AcceptNewHardware");
8132 			int iEnableNewHardware = (EnableNewHardware == "on" ? 1 : 0);
8133 			m_sql.UpdatePreferencesVar("AcceptNewHardware", iEnableNewHardware);
8134 			m_sql.m_bAcceptNewHardware = (iEnableNewHardware == 1);
8135 
8136 			std::string HideDisabledHardwareSensors = request::findValue(&req, "HideDisabledHardwareSensors");
8137 			int iHideDisabledHardwareSensors = (HideDisabledHardwareSensors == "on" ? 1 : 0);
8138 			m_sql.UpdatePreferencesVar("HideDisabledHardwareSensors", iHideDisabledHardwareSensors);
8139 
8140 			std::string ShowUpdateEffect = request::findValue(&req, "ShowUpdateEffect");
8141 			int iShowUpdateEffect = (ShowUpdateEffect == "on" ? 1 : 0);
8142 			m_sql.UpdatePreferencesVar("ShowUpdateEffect", iShowUpdateEffect);
8143 
8144 			std::string SendErrorsAsNotification = request::findValue(&req, "SendErrorsAsNotification");
8145 			int iSendErrorsAsNotification = (SendErrorsAsNotification == "on" ? 1 : 0);
8146 			m_sql.UpdatePreferencesVar("SendErrorsAsNotification", iSendErrorsAsNotification);
8147 			_log.ForwardErrorsToNotificationSystem(iSendErrorsAsNotification != 0);
8148 
8149 			std::string DegreeDaysBaseTemperature = request::findValue(&req, "DegreeDaysBaseTemperature");
8150 			m_sql.UpdatePreferencesVar("DegreeDaysBaseTemperature", DegreeDaysBaseTemperature);
8151 
8152 			rnOldvalue = 0;
8153 			m_sql.GetPreferencesVar("EnableEventScriptSystem", rnOldvalue);
8154 			std::string EnableEventScriptSystem = request::findValue(&req, "EnableEventScriptSystem");
8155 			int iEnableEventScriptSystem = (EnableEventScriptSystem == "on" ? 1 : 0);
8156 			m_sql.UpdatePreferencesVar("EnableEventScriptSystem", iEnableEventScriptSystem);
8157 			m_sql.m_bEnableEventSystem = (iEnableEventScriptSystem == 1);
8158 			if (iEnableEventScriptSystem != rnOldvalue)
8159 			{
8160 				m_mainworker.m_eventsystem.SetEnabled(m_sql.m_bEnableEventSystem);
8161 				m_mainworker.m_eventsystem.StartEventSystem();
8162 			}
8163 			std::string EnableEventSystemFullURLLog = request::findValue(&req, "EventSystemLogFullURL");
8164 			m_sql.m_bEnableEventSystemFullURLLog = EnableEventSystemFullURLLog == "on" ? true : false;
8165 			m_sql.UpdatePreferencesVar("EventSystemLogFullURL", (int)m_sql.m_bEnableEventSystemFullURLLog);
8166 
8167 			rnOldvalue = 0;
8168 			m_sql.GetPreferencesVar("DisableDzVentsSystem", rnOldvalue);
8169 			std::string DisableDzVentsSystem = request::findValue(&req, "DisableDzVentsSystem");
8170 			int iDisableDzVentsSystem = (DisableDzVentsSystem == "on" ? 0 : 1);
8171 			m_sql.UpdatePreferencesVar("DisableDzVentsSystem", iDisableDzVentsSystem);
8172 			m_sql.m_bDisableDzVentsSystem = (iDisableDzVentsSystem == 1);
8173 			if (m_sql.m_bEnableEventSystem && !iDisableDzVentsSystem && iDisableDzVentsSystem != rnOldvalue)
8174 			{
8175 				m_mainworker.m_eventsystem.LoadEvents();
8176 				m_mainworker.m_eventsystem.GetCurrentStates();
8177 			}
8178 			m_sql.UpdatePreferencesVar("DzVentsLogLevel", atoi(request::findValue(&req, "DzVentsLogLevel").c_str()));
8179 
8180 			std::string LogEventScriptTrigger = request::findValue(&req, "LogEventScriptTrigger");
8181 			m_sql.m_bLogEventScriptTrigger = (LogEventScriptTrigger == "on" ? 1 : 0);
8182 			m_sql.UpdatePreferencesVar("LogEventScriptTrigger", m_sql.m_bLogEventScriptTrigger);
8183 
8184 			std::string EnableWidgetOrdering = request::findValue(&req, "AllowWidgetOrdering");
8185 			int iEnableAllowWidgetOrdering = (EnableWidgetOrdering == "on" ? 1 : 0);
8186 			m_sql.UpdatePreferencesVar("AllowWidgetOrdering", iEnableAllowWidgetOrdering);
8187 			m_sql.m_bAllowWidgetOrdering = (iEnableAllowWidgetOrdering == 1);
8188 
8189 			rnOldvalue = 0;
8190 			m_sql.GetPreferencesVar("RemoteSharedPort", rnOldvalue);
8191 
8192 			m_sql.UpdatePreferencesVar("RemoteSharedPort", atoi(request::findValue(&req, "RemoteSharedPort").c_str()));
8193 
8194 			rnvalue = 0;
8195 			m_sql.GetPreferencesVar("RemoteSharedPort", rnvalue);
8196 
8197 			if (rnvalue != rnOldvalue)
8198 			{
8199 				m_mainworker.m_sharedserver.StopServer();
8200 				if (rnvalue != 0)
8201 				{
8202 					char szPort[100];
8203 					sprintf(szPort, "%d", rnvalue);
8204 					m_mainworker.m_sharedserver.StartServer("::", szPort);
8205 					m_mainworker.LoadSharedUsers();
8206 				}
8207 			}
8208 
8209 			m_sql.UpdatePreferencesVar("Language", request::findValue(&req, "Language").c_str());
8210 			std::string SelectedTheme = request::findValue(&req, "Themes");
8211 			m_sql.UpdatePreferencesVar("WebTheme", SelectedTheme.c_str());
8212 			m_pWebEm->SetWebTheme(SelectedTheme);
8213 			std::string Title = request::findValue(&req, "Title").c_str();
8214 			m_sql.UpdatePreferencesVar("Title", (Title.empty()) ? "Domoticz" : Title);
8215 
8216 			m_sql.GetPreferencesVar("RandomTimerFrame", rnOldvalue);
8217 			rnvalue = atoi(request::findValue(&req, "RandomSpread").c_str());
8218 			if (rnOldvalue != rnvalue)
8219 			{
8220 				m_sql.UpdatePreferencesVar("RandomTimerFrame", rnvalue);
8221 				m_mainworker.m_scheduler.ReloadSchedules();
8222 			}
8223 
8224 			m_sql.UpdatePreferencesVar("SecOnDelay", atoi(request::findValue(&req, "SecOnDelay").c_str()));
8225 
8226 			int sensortimeout = atoi(request::findValue(&req, "SensorTimeout").c_str());
8227 			if (sensortimeout < 10)
8228 				sensortimeout = 10;
8229 			m_sql.UpdatePreferencesVar("SensorTimeout", sensortimeout);
8230 
8231 			int batterylowlevel = atoi(request::findValue(&req, "BatterLowLevel").c_str());
8232 			if (batterylowlevel > 100)
8233 				batterylowlevel = 100;
8234 			m_sql.GetPreferencesVar("BatteryLowNotification", rnOldvalue);
8235 			m_sql.UpdatePreferencesVar("BatteryLowNotification", batterylowlevel);
8236 			if ((rnOldvalue != batterylowlevel) && (batterylowlevel != 0))
8237 				m_sql.CheckBatteryLow();
8238 
8239 			int nValue = 0;
8240 			nValue = atoi(request::findValue(&req, "FloorplanPopupDelay").c_str());
8241 			m_sql.UpdatePreferencesVar("FloorplanPopupDelay", nValue);
8242 			std::string FloorplanFullscreenMode = request::findValue(&req, "FloorplanFullscreenMode");
8243 			m_sql.UpdatePreferencesVar("FloorplanFullscreenMode", (FloorplanFullscreenMode == "on" ? 1 : 0));
8244 			std::string FloorplanAnimateZoom = request::findValue(&req, "FloorplanAnimateZoom");
8245 			m_sql.UpdatePreferencesVar("FloorplanAnimateZoom", (FloorplanAnimateZoom == "on" ? 1 : 0));
8246 			std::string FloorplanShowSensorValues = request::findValue(&req, "FloorplanShowSensorValues");
8247 			m_sql.UpdatePreferencesVar("FloorplanShowSensorValues", (FloorplanShowSensorValues == "on" ? 1 : 0));
8248 			std::string FloorplanShowSwitchValues = request::findValue(&req, "FloorplanShowSwitchValues");
8249 			m_sql.UpdatePreferencesVar("FloorplanShowSwitchValues", (FloorplanShowSwitchValues == "on" ? 1 : 0));
8250 			std::string FloorplanShowSceneNames = request::findValue(&req, "FloorplanShowSceneNames");
8251 			m_sql.UpdatePreferencesVar("FloorplanShowSceneNames", (FloorplanShowSceneNames == "on" ? 1 : 0));
8252 			m_sql.UpdatePreferencesVar("FloorplanRoomColour", CURLEncode::URLDecode(request::findValue(&req, "FloorplanRoomColour").c_str()).c_str());
8253 			m_sql.UpdatePreferencesVar("FloorplanActiveOpacity", atoi(request::findValue(&req, "FloorplanActiveOpacity").c_str()));
8254 			m_sql.UpdatePreferencesVar("FloorplanInactiveOpacity", atoi(request::findValue(&req, "FloorplanInactiveOpacity").c_str()));
8255 
8256 #ifndef NOCLOUD
8257 			std::string md_userid, md_password, pf_userid, pf_password;
8258 			int md_subsystems, pf_subsystems;
8259 			m_sql.GetPreferencesVar("MyDomoticzUserId", pf_userid);
8260 			m_sql.GetPreferencesVar("MyDomoticzPassword", pf_password);
8261 			m_sql.GetPreferencesVar("MyDomoticzSubsystems", pf_subsystems);
8262 			md_userid = CURLEncode::URLDecode(request::findValue(&req, "MyDomoticzUserId"));
8263 			md_password = CURLEncode::URLDecode(request::findValue(&req, "MyDomoticzPassword"));
8264 			md_subsystems = (request::findValue(&req, "SubsystemHttp").empty() ? 0 : 1) + (request::findValue(&req, "SubsystemShared").empty() ? 0 : 2) + (request::findValue(&req, "SubsystemApps").empty() ? 0 : 4);
8265 			if (md_userid != pf_userid || md_password != pf_password || md_subsystems != pf_subsystems) {
8266 				m_sql.UpdatePreferencesVar("MyDomoticzUserId", md_userid);
8267 				if (md_password != pf_password) {
8268 					md_password = base64_encode(md_password);
8269 					m_sql.UpdatePreferencesVar("MyDomoticzPassword", md_password);
8270 				}
8271 				m_sql.UpdatePreferencesVar("MyDomoticzSubsystems", md_subsystems);
8272 				m_webservers.RestartProxy();
8273 			}
8274 #endif
8275 
8276 			m_sql.UpdatePreferencesVar("OneWireSensorPollPeriod", atoi(request::findValue(&req, "OneWireSensorPollPeriod").c_str()));
8277 			m_sql.UpdatePreferencesVar("OneWireSwitchPollPeriod", atoi(request::findValue(&req, "OneWireSwitchPollPeriod").c_str()));
8278 
8279 			std::string IFTTTEnabled = request::findValue(&req, "IFTTTEnabled");
8280 			int iIFTTTEnabled = (IFTTTEnabled == "on" ? 1 : 0);
8281 			m_sql.UpdatePreferencesVar("IFTTTEnabled", iIFTTTEnabled);
8282 			std::string szKey = request::findValue(&req, "IFTTTAPI");
8283 			m_sql.UpdatePreferencesVar("IFTTTAPI", base64_encode(szKey));
8284 
8285 			m_notifications.LoadConfig();
8286 #ifdef ENABLE_PYTHON
8287 			//Signal plugins to update Settings dictionary
8288 			PluginLoadConfig();
8289 #endif
8290 
8291 			Json::Value root;
8292 			root["status"] = "OK";
8293 			root["title"] = "StoreSettings";
8294 
8295 			std::string jcallback = request::findValue(&req, "jsoncallback");
8296 			if (jcallback.size() == 0) {
8297 				reply::set_content(&rep, root.toStyledString());
8298 				return;
8299 			}
8300 			reply::set_content(&rep, "var data=" + root.toStyledString() + '\n' + jcallback + "(data);");
8301 
8302 		}
8303 
RestoreDatabase(WebEmSession & session,const request & req,std::string & redirect_uri)8304 		void CWebServer::RestoreDatabase(WebEmSession & session, const request& req, std::string & redirect_uri)
8305 		{
8306 			redirect_uri = "/index.html";
8307 			if (session.rights != 2)
8308 			{
8309 				session.reply_status = reply::forbidden;
8310 				return; //Only admin user allowed
8311 			}
8312 
8313 			std::string dbasefile = request::findValue(&req, "dbasefile");
8314 			if (dbasefile.empty()) {
8315 				return;
8316 			}
8317 
8318 			m_mainworker.StopDomoticzHardware();
8319 
8320 			m_sql.RestoreDatabase(dbasefile);
8321 			m_mainworker.AddAllDomoticzHardware();
8322 		}
8323 
8324 		struct _tHardwareListInt {
8325 			std::string Name;
8326 			int HardwareTypeVal;
8327 			std::string HardwareType;
8328 			bool Enabled;
8329 			std::string Mode1; // Used to flag DimmerType as relative for some old LimitLessLight type bulbs
8330 			std::string Mode2; // Used to flag DimmerType as relative for some old LimitLessLight type bulbs
8331 		} tHardwareList;
8332 
GetJSonDevices(Json::Value & root,const std::string & rused,const std::string & rfilter,const std::string & order,const std::string & rowid,const std::string & planID,const std::string & floorID,const bool bDisplayHidden,const bool bDisplayDisabled,const bool bFetchFavorites,const time_t LastUpdate,const std::string & username,const std::string & hardwareid)8333 		void CWebServer::GetJSonDevices(
8334 			Json::Value &root,
8335 			const std::string &rused,
8336 			const std::string &rfilter,
8337 			const std::string &order,
8338 			const std::string &rowid,
8339 			const std::string &planID,
8340 			const std::string &floorID,
8341 			const bool bDisplayHidden,
8342 			const bool bDisplayDisabled,
8343 			const bool bFetchFavorites,
8344 			const time_t LastUpdate,
8345 			const std::string &username,
8346 			const std::string &hardwareid)
8347 		{
8348 			std::vector<std::vector<std::string> > result;
8349 
8350 			time_t now = mytime(NULL);
8351 			struct tm tm1;
8352 			localtime_r(&now, &tm1);
8353 			struct tm tLastUpdate;
8354 			localtime_r(&now, &tLastUpdate);
8355 
8356 			const time_t iLastUpdate = LastUpdate - 1;
8357 
8358 			int SensorTimeOut = 60;
8359 			m_sql.GetPreferencesVar("SensorTimeout", SensorTimeOut);
8360 
8361 			//Get All Hardware ID's/Names, need them later
8362 			std::map<int, _tHardwareListInt> _hardwareNames;
8363 			result = m_sql.safe_query("SELECT ID, Name, Enabled, Type, Mode1, Mode2 FROM Hardware");
8364 			if (!result.empty())
8365 			{
8366 				for (const auto & itt : result)
8367 				{
8368 					std::vector<std::string> sd = itt;
8369 					_tHardwareListInt tlist;
8370 					int ID = atoi(sd[0].c_str());
8371 					tlist.Name = sd[1];
8372 					tlist.Enabled = (atoi(sd[2].c_str()) != 0);
8373 					tlist.HardwareTypeVal = atoi(sd[3].c_str());
8374 #ifndef ENABLE_PYTHON
8375 					tlist.HardwareType = Hardware_Type_Desc(tlist.HardwareTypeVal);
8376 #else
8377 					if (tlist.HardwareTypeVal != HTYPE_PythonPlugin)
8378 					{
8379 						tlist.HardwareType = Hardware_Type_Desc(tlist.HardwareTypeVal);
8380 					}
8381 					else
8382 					{
8383 						tlist.HardwareType = PluginHardwareDesc(ID);
8384 					}
8385 #endif
8386 					tlist.Mode1 = sd[4];
8387 					tlist.Mode2 = sd[5];
8388 					_hardwareNames[ID] = tlist;
8389 				}
8390 			}
8391 
8392 			root["ActTime"] = static_cast<int>(now);
8393 
8394 			char szTmp[300];
8395 
8396 			if (!m_mainworker.m_LastSunriseSet.empty())
8397 			{
8398 				std::vector<std::string> strarray;
8399 				StringSplit(m_mainworker.m_LastSunriseSet, ";", strarray);
8400 				if (strarray.size() == 10)
8401 				{
8402 					//strftime(szTmp, 80, "%b %d %Y %X", &tm1);
8403 					strftime(szTmp, 80, "%Y-%m-%d %X", &tm1);
8404 					root["ServerTime"] = szTmp;
8405 					root["Sunrise"] = strarray[0];
8406 					root["Sunset"] = strarray[1];
8407 					root["SunAtSouth"] = strarray[2];
8408 					root["CivTwilightStart"] = strarray[3];
8409 					root["CivTwilightEnd"] = strarray[4];
8410 					root["NautTwilightStart"] = strarray[5];
8411 					root["NautTwilightEnd"] = strarray[6];
8412 					root["AstrTwilightStart"] = strarray[7];
8413 					root["AstrTwilightEnd"] = strarray[8];
8414 					root["DayLength"] = strarray[9];
8415 				}
8416 			}
8417 
8418 			char szOrderBy[50];
8419 			std::string szQuery;
8420 			bool isAlpha = true;
8421 			const std::string orderBy = order.c_str();
8422 			for (size_t i = 0; i < orderBy.size(); i++) {
8423 				if (!isalpha(orderBy[i])) {
8424 					isAlpha = false;
8425 				}
8426 			}
8427 			if (order.empty() || (!isAlpha)) {
8428 				strcpy(szOrderBy, "A.[Order],A.LastUpdate DESC");
8429 			} else {
8430 				sprintf(szOrderBy, "A.[Order],A.%%s ASC");
8431 			}
8432 
8433 			unsigned char tempsign = m_sql.m_tempsign[0];
8434 
8435 			bool bHaveUser = false;
8436 			int iUser = -1;
8437 			unsigned int totUserDevices = 0;
8438 			bool bShowScenes = true;
8439 			bHaveUser = (username != "");
8440 			if (bHaveUser)
8441 			{
8442 				iUser = FindUser(username.c_str());
8443 				if (iUser != -1)
8444 				{
8445 					_eUserRights urights = m_users[iUser].userrights;
8446 					if (urights != URIGHTS_ADMIN)
8447 					{
8448 						result = m_sql.safe_query("SELECT COUNT(*) FROM SharedDevices WHERE (SharedUserID == %lu)", m_users[iUser].ID);
8449 						if (!result.empty())
8450 						{
8451 							totUserDevices = (unsigned int)std::stoi(result[0][0]);
8452 						}
8453 						bShowScenes = (m_users[iUser].ActiveTabs&(1 << 1)) != 0;
8454 					}
8455 				}
8456 			}
8457 
8458 			std::set<std::string> _HiddenDevices;
8459 			bool bAllowDeviceToBeHidden = false;
8460 
8461 			int ii = 0;
8462 			if (rfilter == "all")
8463 			{
8464 				if (
8465 					(bShowScenes) &&
8466 					((rused == "all") || (rused == "true"))
8467 					)
8468 				{
8469 					//add scenes
8470 					if (rowid != "")
8471 						result = m_sql.safe_query(
8472 							"SELECT A.ID, A.Name, A.nValue, A.LastUpdate, A.Favorite, A.SceneType,"
8473 							" A.Protected, B.XOffset, B.YOffset, B.PlanID, A.Description"
8474 							" FROM Scenes as A"
8475 							" LEFT OUTER JOIN DeviceToPlansMap as B ON (B.DeviceRowID==a.ID) AND (B.DevSceneType==1)"
8476 							" WHERE (A.ID=='%q')",
8477 							rowid.c_str());
8478 					else if ((planID != "") && (planID != "0"))
8479 						result = m_sql.safe_query(
8480 							"SELECT A.ID, A.Name, A.nValue, A.LastUpdate, A.Favorite, A.SceneType,"
8481 							" A.Protected, B.XOffset, B.YOffset, B.PlanID, A.Description"
8482 							" FROM Scenes as A, DeviceToPlansMap as B WHERE (B.PlanID=='%q')"
8483 							" AND (B.DeviceRowID==a.ID) AND (B.DevSceneType==1) ORDER BY B.[Order]",
8484 							planID.c_str());
8485 					else if ((floorID != "") && (floorID != "0"))
8486 						result = m_sql.safe_query(
8487 							"SELECT A.ID, A.Name, A.nValue, A.LastUpdate, A.Favorite, A.SceneType,"
8488 							" A.Protected, B.XOffset, B.YOffset, B.PlanID, A.Description"
8489 							" FROM Scenes as A, DeviceToPlansMap as B, Plans as C"
8490 							" WHERE (C.FloorplanID=='%q') AND (C.ID==B.PlanID) AND (B.DeviceRowID==a.ID)"
8491 							" AND (B.DevSceneType==1) ORDER BY B.[Order]",
8492 							floorID.c_str());
8493 					else {
8494 						szQuery = (
8495 							"SELECT A.ID, A.Name, A.nValue, A.LastUpdate, A.Favorite, A.SceneType,"
8496 							" A.Protected, B.XOffset, B.YOffset, B.PlanID, A.Description"
8497 							" FROM Scenes as A"
8498 							" LEFT OUTER JOIN DeviceToPlansMap as B ON (B.DeviceRowID==a.ID) AND (B.DevSceneType==1)"
8499 							" ORDER BY ");
8500 						szQuery += szOrderBy;
8501                                                 result = m_sql.safe_query(szQuery.c_str(), order.c_str());
8502 					}
8503 
8504 					if (!result.empty())
8505 					{
8506 						for (const auto & itt : result)
8507 						{
8508 							std::vector<std::string> sd = itt;
8509 
8510 							unsigned char favorite = atoi(sd[4].c_str());
8511 							//Check if we only want favorite devices
8512 							if ((bFetchFavorites) && (!favorite))
8513 								continue;
8514 
8515 							std::string sLastUpdate = sd[3];
8516 
8517 							if (iLastUpdate != 0)
8518 							{
8519 								time_t cLastUpdate;
8520 								ParseSQLdatetime(cLastUpdate, tLastUpdate, sLastUpdate, tm1.tm_isdst);
8521 								if (cLastUpdate <= iLastUpdate)
8522 									continue;
8523 							}
8524 
8525 							int nValue = atoi(sd[2].c_str());
8526 
8527 							unsigned char scenetype = atoi(sd[5].c_str());
8528 							int iProtected = atoi(sd[6].c_str());
8529 
8530 							std::string sSceneName = sd[1];
8531 							if (!bDisplayHidden && sSceneName[0] == '$')
8532 							{
8533 								continue;
8534 							}
8535 
8536 							if (scenetype == 0)
8537 							{
8538 								root["result"][ii]["Type"] = "Scene";
8539 								root["result"][ii]["TypeImg"] = "scene";
8540 								root["result"][ii]["Image"] = "Push";
8541 							}
8542 							else
8543 							{
8544 								root["result"][ii]["Type"] = "Group";
8545 								root["result"][ii]["TypeImg"] = "group";
8546 							}
8547 
8548 							// has this scene/group already been seen, now with different plan?
8549 							// assume results are ordered such that same device is adjacent
8550 							// if the idx and the Type are equal (type to prevent matching against Scene with same idx)
8551 							std::string thisIdx = sd[0];
8552 
8553 							if ((ii > 0) && thisIdx == root["result"][ii - 1]["idx"].asString()) {
8554 								std::string typeOfThisOne = root["result"][ii]["Type"].asString();
8555 								if (typeOfThisOne == root["result"][ii - 1]["Type"].asString()) {
8556 									root["result"][ii - 1]["PlanIDs"].append(atoi(sd[9].c_str()));
8557 									continue;
8558 								}
8559 							}
8560 
8561 							root["result"][ii]["idx"] = sd[0];
8562 							root["result"][ii]["Name"] = sSceneName;
8563 							root["result"][ii]["Description"] = sd[10];
8564 							root["result"][ii]["Favorite"] = favorite;
8565 							root["result"][ii]["Protected"] = (iProtected != 0);
8566 							root["result"][ii]["LastUpdate"] = sLastUpdate;
8567 							root["result"][ii]["PlanID"] = sd[9].c_str();
8568 							Json::Value jsonArray;
8569 							jsonArray.append(atoi(sd[9].c_str()));
8570 							root["result"][ii]["PlanIDs"] = jsonArray;
8571 
8572 							if (nValue == 0)
8573 								root["result"][ii]["Status"] = "Off";
8574 							else if (nValue == 1)
8575 								root["result"][ii]["Status"] = "On";
8576 							else
8577 								root["result"][ii]["Status"] = "Mixed";
8578 							root["result"][ii]["Data"] = root["result"][ii]["Status"];
8579 							uint64_t camIDX = m_mainworker.m_cameras.IsDevSceneInCamera(1, sd[0]);
8580 							root["result"][ii]["UsedByCamera"] = (camIDX != 0) ? true : false;
8581 							if (camIDX != 0) {
8582 								std::stringstream scidx;
8583 								scidx << camIDX;
8584 								root["result"][ii]["CameraIdx"] = scidx.str();
8585 							}
8586 							root["result"][ii]["XOffset"] = atoi(sd[7].c_str());
8587 							root["result"][ii]["YOffset"] = atoi(sd[8].c_str());
8588 							ii++;
8589 						}
8590 					}
8591 				}
8592 			}
8593 
8594 			char szData[320];
8595 			if (totUserDevices == 0)
8596 			{
8597 				//All
8598 				if (rowid != "")
8599 				{
8600 					//_log.Log(LOG_STATUS, "Getting device with id: %s", rowid.c_str());
8601 					result = m_sql.safe_query(
8602 						"SELECT A.ID, A.DeviceID, A.Unit, A.Name, A.Used, A.Type, A.SubType,"
8603 						" A.SignalLevel, A.BatteryLevel, A.nValue, A.sValue,"
8604 						" A.LastUpdate, A.Favorite, A.SwitchType, A.HardwareID,"
8605 						" A.AddjValue, A.AddjMulti, A.AddjValue2, A.AddjMulti2,"
8606 						" A.LastLevel, A.CustomImage, A.StrParam1, A.StrParam2,"
8607 						" A.Protected, IFNULL(B.XOffset,0), IFNULL(B.YOffset,0), IFNULL(B.PlanID,0), A.Description,"
8608 						" A.Options, A.Color "
8609 						"FROM DeviceStatus A LEFT OUTER JOIN DeviceToPlansMap as B ON (B.DeviceRowID==a.ID) "
8610 						"WHERE (A.ID=='%q')",
8611 						rowid.c_str());
8612 				}
8613 				else if ((planID != "") && (planID != "0"))
8614 					result = m_sql.safe_query(
8615 						"SELECT A.ID, A.DeviceID, A.Unit, A.Name, A.Used,"
8616 						" A.Type, A.SubType, A.SignalLevel, A.BatteryLevel,"
8617 						" A.nValue, A.sValue, A.LastUpdate, A.Favorite,"
8618 						" A.SwitchType, A.HardwareID, A.AddjValue,"
8619 						" A.AddjMulti, A.AddjValue2, A.AddjMulti2,"
8620 						" A.LastLevel, A.CustomImage, A.StrParam1,"
8621 						" A.StrParam2, A.Protected, B.XOffset, B.YOffset,"
8622 						" B.PlanID, A.Description,"
8623 						" A.Options, A.Color "
8624 						"FROM DeviceStatus as A, DeviceToPlansMap as B "
8625 						"WHERE (B.PlanID=='%q') AND (B.DeviceRowID==a.ID)"
8626 						" AND (B.DevSceneType==0) ORDER BY B.[Order]",
8627 						planID.c_str());
8628 				else if ((floorID != "") && (floorID != "0"))
8629 					result = m_sql.safe_query(
8630 						"SELECT A.ID, A.DeviceID, A.Unit, A.Name, A.Used,"
8631 						" A.Type, A.SubType, A.SignalLevel, A.BatteryLevel,"
8632 						" A.nValue, A.sValue, A.LastUpdate, A.Favorite,"
8633 						" A.SwitchType, A.HardwareID, A.AddjValue,"
8634 						" A.AddjMulti, A.AddjValue2, A.AddjMulti2,"
8635 						" A.LastLevel, A.CustomImage, A.StrParam1,"
8636 						" A.StrParam2, A.Protected, B.XOffset, B.YOffset,"
8637 						" B.PlanID, A.Description,"
8638 						" A.Options, A.Color "
8639 						"FROM DeviceStatus as A, DeviceToPlansMap as B,"
8640 						" Plans as C "
8641 						"WHERE (C.FloorplanID=='%q') AND (C.ID==B.PlanID)"
8642 						" AND (B.DeviceRowID==a.ID) AND (B.DevSceneType==0) "
8643 						"ORDER BY B.[Order]",
8644 						floorID.c_str());
8645 				else {
8646 					if (!bDisplayHidden)
8647 					{
8648 						//Build a list of Hidden Devices
8649 						result = m_sql.safe_query("SELECT ID FROM Plans WHERE (Name=='$Hidden Devices')");
8650 						if (!result.empty())
8651 						{
8652 							std::string pID = result[0][0];
8653 							result = m_sql.safe_query("SELECT DeviceRowID FROM DeviceToPlansMap WHERE (PlanID=='%q') AND (DevSceneType==0)",
8654 								pID.c_str());
8655 							if (!result.empty())
8656 							{
8657 								std::vector<std::vector<std::string> >::const_iterator ittP;
8658 								for (ittP = result.begin(); ittP != result.end(); ++ittP)
8659 								{
8660 									_HiddenDevices.insert(ittP[0][0]);
8661 								}
8662 							}
8663 						}
8664 						bAllowDeviceToBeHidden = true;
8665 					}
8666 
8667 					if (order.empty() || (!isAlpha))
8668 						strcpy(szOrderBy, "A.[Order],A.LastUpdate DESC");
8669 					else
8670 					{
8671 						sprintf(szOrderBy, "A.[Order],A.%%s ASC");
8672 					}
8673 					//_log.Log(LOG_STATUS, "Getting all devices: order by %s ", szOrderBy);
8674 					if (hardwareid != "") {
8675 						szQuery = (
8676 							"SELECT A.ID, A.DeviceID, A.Unit, A.Name, A.Used,A.Type, A.SubType,"
8677 							" A.SignalLevel, A.BatteryLevel, A.nValue, A.sValue,"
8678 							" A.LastUpdate, A.Favorite, A.SwitchType, A.HardwareID,"
8679 							" A.AddjValue, A.AddjMulti, A.AddjValue2, A.AddjMulti2,"
8680 							" A.LastLevel, A.CustomImage, A.StrParam1, A.StrParam2,"
8681 							" A.Protected, IFNULL(B.XOffset,0), IFNULL(B.YOffset,0), IFNULL(B.PlanID,0), A.Description,"
8682 							" A.Options, A.Color "
8683 							"FROM DeviceStatus as A LEFT OUTER JOIN DeviceToPlansMap as B "
8684 							"ON (B.DeviceRowID==a.ID) AND (B.DevSceneType==0) "
8685 							"WHERE (A.HardwareID == %q) "
8686 							"ORDER BY ");
8687 						szQuery += szOrderBy;
8688 						result = m_sql.safe_query(szQuery.c_str(), hardwareid.c_str(), order.c_str());
8689 					}
8690 					else {
8691 						szQuery = (
8692 							"SELECT A.ID, A.DeviceID, A.Unit, A.Name, A.Used,A.Type, A.SubType,"
8693 							" A.SignalLevel, A.BatteryLevel, A.nValue, A.sValue,"
8694 							" A.LastUpdate, A.Favorite, A.SwitchType, A.HardwareID,"
8695 							" A.AddjValue, A.AddjMulti, A.AddjValue2, A.AddjMulti2,"
8696 							" A.LastLevel, A.CustomImage, A.StrParam1, A.StrParam2,"
8697 							" A.Protected, IFNULL(B.XOffset,0), IFNULL(B.YOffset,0), IFNULL(B.PlanID,0), A.Description,"
8698 							" A.Options, A.Color "
8699 							"FROM DeviceStatus as A LEFT OUTER JOIN DeviceToPlansMap as B "
8700 							"ON (B.DeviceRowID==a.ID) AND (B.DevSceneType==0) "
8701 							"ORDER BY ");
8702 						szQuery += szOrderBy;
8703 						result = m_sql.safe_query(szQuery.c_str(), order.c_str());
8704 					}
8705 				}
8706 			}
8707 			else
8708 			{
8709 				if (iUser == -1) {
8710 					return;
8711 				}
8712 				//Specific devices
8713 				if (rowid != "")
8714 				{
8715 					//_log.Log(LOG_STATUS, "Getting device with id: %s for user %lu", rowid.c_str(), m_users[iUser].ID);
8716 					result = m_sql.safe_query(
8717 						"SELECT A.ID, A.DeviceID, A.Unit, A.Name, A.Used,"
8718 						" A.Type, A.SubType, A.SignalLevel, A.BatteryLevel,"
8719 						" A.nValue, A.sValue, A.LastUpdate, B.Favorite,"
8720 						" A.SwitchType, A.HardwareID, A.AddjValue,"
8721 						" A.AddjMulti, A.AddjValue2, A.AddjMulti2,"
8722 						" A.LastLevel, A.CustomImage, A.StrParam1,"
8723 						" A.StrParam2, A.Protected, 0 as XOffset,"
8724 						" 0 as YOffset, 0 as PlanID, A.Description,"
8725 						" A.Options, A.Color "
8726 						"FROM DeviceStatus as A, SharedDevices as B "
8727 						"WHERE (B.DeviceRowID==a.ID)"
8728 						" AND (B.SharedUserID==%lu) AND (A.ID=='%q')",
8729 						m_users[iUser].ID, rowid.c_str());
8730 				}
8731 				else if ((planID != "") && (planID != "0"))
8732 					result = m_sql.safe_query(
8733 						"SELECT A.ID, A.DeviceID, A.Unit, A.Name, A.Used,"
8734 						" A.Type, A.SubType, A.SignalLevel, A.BatteryLevel,"
8735 						" A.nValue, A.sValue, A.LastUpdate, B.Favorite,"
8736 						" A.SwitchType, A.HardwareID, A.AddjValue,"
8737 						" A.AddjMulti, A.AddjValue2, A.AddjMulti2,"
8738 						" A.LastLevel, A.CustomImage, A.StrParam1,"
8739 						" A.StrParam2, A.Protected, C.XOffset,"
8740 						" C.YOffset, C.PlanID, A.Description,"
8741 						" A.Options, A.Color "
8742 						"FROM DeviceStatus as A, SharedDevices as B,"
8743 						" DeviceToPlansMap as C "
8744 						"WHERE (C.PlanID=='%q') AND (C.DeviceRowID==a.ID)"
8745 						" AND (B.DeviceRowID==a.ID) "
8746 						"AND (B.SharedUserID==%lu) ORDER BY C.[Order]",
8747 						planID.c_str(), m_users[iUser].ID);
8748 				else if ((floorID != "") && (floorID != "0"))
8749 					result = m_sql.safe_query(
8750 						"SELECT A.ID, A.DeviceID, A.Unit, A.Name, A.Used,"
8751 						" A.Type, A.SubType, A.SignalLevel, A.BatteryLevel,"
8752 						" A.nValue, A.sValue, A.LastUpdate, B.Favorite,"
8753 						" A.SwitchType, A.HardwareID, A.AddjValue,"
8754 						" A.AddjMulti, A.AddjValue2, A.AddjMulti2,"
8755 						" A.LastLevel, A.CustomImage, A.StrParam1,"
8756 						" A.StrParam2, A.Protected, C.XOffset, C.YOffset,"
8757 						" C.PlanID, A.Description,"
8758 						" A.Options, A.Color "
8759 						"FROM DeviceStatus as A, SharedDevices as B,"
8760 						" DeviceToPlansMap as C, Plans as D "
8761 						"WHERE (D.FloorplanID=='%q') AND (D.ID==C.PlanID)"
8762 						" AND (C.DeviceRowID==a.ID) AND (B.DeviceRowID==a.ID)"
8763 						" AND (B.SharedUserID==%lu) ORDER BY C.[Order]",
8764 						floorID.c_str(), m_users[iUser].ID);
8765 				else {
8766 					if (!bDisplayHidden)
8767 					{
8768 						//Build a list of Hidden Devices
8769 						result = m_sql.safe_query("SELECT ID FROM Plans WHERE (Name=='$Hidden Devices')");
8770 						if (!result.empty())
8771 						{
8772 							std::string pID = result[0][0];
8773 							result = m_sql.safe_query("SELECT DeviceRowID FROM DeviceToPlansMap WHERE (PlanID=='%q')  AND (DevSceneType==0)",
8774 								pID.c_str());
8775 							if (!result.empty())
8776 							{
8777 								std::vector<std::vector<std::string> >::const_iterator ittP;
8778 								for (ittP = result.begin(); ittP != result.end(); ++ittP)
8779 								{
8780 									_HiddenDevices.insert(ittP[0][0]);
8781 								}
8782 							}
8783 						}
8784 						bAllowDeviceToBeHidden = true;
8785 					}
8786 
8787 					if (order.empty() || (!isAlpha))
8788 					{
8789 						strcpy(szOrderBy, "A.[Order],A.LastUpdate DESC");
8790 					}
8791 					else
8792 					{
8793 						sprintf(szOrderBy, "A.[Order],A.%%s ASC");
8794 					}
8795 					// _log.Log(LOG_STATUS, "Getting all devices for user %lu", m_users[iUser].ID);
8796 					szQuery = (
8797 						"SELECT A.ID, A.DeviceID, A.Unit, A.Name, A.Used,"
8798 						" A.Type, A.SubType, A.SignalLevel, A.BatteryLevel,"
8799 						" A.nValue, A.sValue, A.LastUpdate, B.Favorite,"
8800 						" A.SwitchType, A.HardwareID, A.AddjValue,"
8801 						" A.AddjMulti, A.AddjValue2, A.AddjMulti2,"
8802 						" A.LastLevel, A.CustomImage, A.StrParam1,"
8803 						" A.StrParam2, A.Protected, IFNULL(C.XOffset,0),"
8804 						" IFNULL(C.YOffset,0), IFNULL(C.PlanID,0), A.Description,"
8805 						" A.Options, A.Color "
8806 						"FROM DeviceStatus as A, SharedDevices as B "
8807 						"LEFT OUTER JOIN DeviceToPlansMap as C  ON (C.DeviceRowID==A.ID)"
8808 						"WHERE (B.DeviceRowID==A.ID)"
8809 						" AND (B.SharedUserID==%lu) ORDER BY ");
8810 					szQuery += szOrderBy;
8811 					result = m_sql.safe_query(szQuery.c_str(), m_users[iUser].ID, order.c_str());
8812 				}
8813 			}
8814 
8815 			if (!result.empty())
8816 			{
8817 				for (const auto & itt : result)
8818 				{
8819 					std::vector<std::string> sd = itt;
8820 
8821 					unsigned char favorite = atoi(sd[12].c_str());
8822 					if ((planID != "") && (planID != "0"))
8823 						favorite = 1;
8824 
8825 					//Check if we only want favorite devices
8826 					if ((bFetchFavorites) && (!favorite))
8827 						continue;
8828 
8829 					std::string sDeviceName = sd[3];
8830 
8831 					if (!bDisplayHidden)
8832 					{
8833 						if (_HiddenDevices.find(sd[0]) != _HiddenDevices.end())
8834 							continue;
8835 						if (sDeviceName[0] == '$')
8836 						{
8837 							if (bAllowDeviceToBeHidden)
8838 								continue;
8839 							if (planID.size() > 0)
8840 								sDeviceName = sDeviceName.substr(1);
8841 						}
8842 					}
8843 					int hardwareID = atoi(sd[14].c_str());
8844 					std::map<int, _tHardwareListInt>::iterator hItt = _hardwareNames.find(hardwareID);
8845 					if (hItt != _hardwareNames.end())
8846 					{
8847 						//ignore sensors where the hardware is disabled
8848 						if ((!bDisplayDisabled) && (!(*hItt).second.Enabled))
8849 							continue;
8850 					}
8851 
8852 					unsigned int dType = atoi(sd[5].c_str());
8853 					unsigned int dSubType = atoi(sd[6].c_str());
8854 					unsigned int used = atoi(sd[4].c_str());
8855 					int nValue = atoi(sd[9].c_str());
8856 					std::string sValue = sd[10];
8857 					std::string sLastUpdate = sd[11];
8858 					if (sLastUpdate.size() > 19)
8859 						sLastUpdate = sLastUpdate.substr(0, 19);
8860 
8861 					if (iLastUpdate != 0)
8862 					{
8863 						time_t cLastUpdate;
8864 						ParseSQLdatetime(cLastUpdate, tLastUpdate, sLastUpdate, tm1.tm_isdst);
8865 						if (cLastUpdate <= iLastUpdate)
8866 							continue;
8867 					}
8868 
8869 					_eSwitchType switchtype = (_eSwitchType)atoi(sd[13].c_str());
8870 					_eMeterType metertype = (_eMeterType)switchtype;
8871 					double AddjValue = atof(sd[15].c_str());
8872 					double AddjMulti = atof(sd[16].c_str());
8873 					double AddjValue2 = atof(sd[17].c_str());
8874 					double AddjMulti2 = atof(sd[18].c_str());
8875 					int LastLevel = atoi(sd[19].c_str());
8876 					int CustomImage = atoi(sd[20].c_str());
8877 					std::string strParam1 = base64_encode(sd[21]);
8878 					std::string strParam2 = base64_encode(sd[22]);
8879 					int iProtected = atoi(sd[23].c_str());
8880 
8881 					std::string Description = sd[27];
8882 					std::string sOptions = sd[28];
8883 					std::string sColor = sd[29];
8884 					std::map<std::string, std::string> options = m_sql.BuildDeviceOptions(sOptions);
8885 
8886 					struct tm ntime;
8887 					time_t checktime;
8888 					ParseSQLdatetime(checktime, ntime, sLastUpdate, tm1.tm_isdst);
8889 					bool bHaveTimeout = (now - checktime >= SensorTimeOut * 60);
8890 
8891 					if (dType == pTypeTEMP_RAIN)
8892 						continue; //dont want you for now
8893 
8894 					if ((rused == "true") && (!used))
8895 						continue;
8896 
8897 					if (
8898 						(rused == "false") &&
8899 						(used)
8900 						)
8901 						continue;
8902 					if (rfilter != "")
8903 					{
8904 						if (rfilter == "light")
8905 						{
8906 							if (
8907 								(dType != pTypeLighting1) &&
8908 								(dType != pTypeLighting2) &&
8909 								(dType != pTypeLighting3) &&
8910 								(dType != pTypeLighting4) &&
8911 								(dType != pTypeLighting5) &&
8912 								(dType != pTypeLighting6) &&
8913 								(dType != pTypeFan) &&
8914 								(dType != pTypeColorSwitch) &&
8915 								(dType != pTypeSecurity1) &&
8916 								(dType != pTypeSecurity2) &&
8917 								(dType != pTypeEvohome) &&
8918 								(dType != pTypeEvohomeRelay) &&
8919 								(dType != pTypeCurtain) &&
8920 								(dType != pTypeBlinds) &&
8921 								(dType != pTypeRFY) &&
8922 								(dType != pTypeChime) &&
8923 								(dType != pTypeThermostat2) &&
8924 								(dType != pTypeThermostat3) &&
8925 								(dType != pTypeThermostat4) &&
8926 								(dType != pTypeRemote) &&
8927 								(dType != pTypeGeneralSwitch) &&
8928 								(dType != pTypeHomeConfort) &&
8929 								(dType != pTypeChime) &&
8930 								(dType != pTypeFS20) &&
8931 								(!((dType == pTypeRego6XXValue) && (dSubType == sTypeRego6XXStatus))) &&
8932 								(!((dType == pTypeRadiator1) && (dSubType == sTypeSmartwaresSwitchRadiator)))&&
8933 								(dType != pTypeHunter)
8934 								)
8935 								continue;
8936 						}
8937 						else if (rfilter == "temp")
8938 						{
8939 							if (
8940 								(dType != pTypeTEMP) &&
8941 								(dType != pTypeHUM) &&
8942 								(dType != pTypeTEMP_HUM) &&
8943 								(dType != pTypeTEMP_HUM_BARO) &&
8944 								(dType != pTypeTEMP_BARO) &&
8945 								(dType != pTypeEvohomeZone) &&
8946 								(dType != pTypeEvohomeWater) &&
8947 								(!((dType == pTypeWIND) && (dSubType == sTypeWIND4))) &&
8948 								(!((dType == pTypeUV) && (dSubType == sTypeUV3))) &&
8949 								(!((dType == pTypeGeneral) && (dSubType == sTypeSystemTemp))) &&
8950 								(dType != pTypeThermostat1) &&
8951 								(!((dType == pTypeRFXSensor) && (dSubType == sTypeRFXSensorTemp))) &&
8952 								(dType != pTypeRego6XXTemp)
8953 								)
8954 								continue;
8955 						}
8956 						else if (rfilter == "weather")
8957 						{
8958 							if (
8959 								(dType != pTypeWIND) &&
8960 								(dType != pTypeRAIN) &&
8961 								(dType != pTypeTEMP_HUM_BARO) &&
8962 								(dType != pTypeTEMP_BARO) &&
8963 								(dType != pTypeUV) &&
8964 								(!((dType == pTypeGeneral) && (dSubType == sTypeVisibility))) &&
8965 								(!((dType == pTypeGeneral) && (dSubType == sTypeBaro))) &&
8966 								(!((dType == pTypeGeneral) && (dSubType == sTypeSolarRadiation)))
8967 								)
8968 								continue;
8969 						}
8970 						else if (rfilter == "utility")
8971 						{
8972 							if (
8973 								(dType != pTypeRFXMeter) &&
8974 								(!((dType == pTypeRFXSensor) && (dSubType == sTypeRFXSensorAD))) &&
8975 								(!((dType == pTypeRFXSensor) && (dSubType == sTypeRFXSensorVolt))) &&
8976 								(!((dType == pTypeGeneral) && (dSubType == sTypeVoltage))) &&
8977 								(!((dType == pTypeGeneral) && (dSubType == sTypeCurrent))) &&
8978 								(!((dType == pTypeGeneral) && (dSubType == sTypeTextStatus))) &&
8979 								(!((dType == pTypeGeneral) && (dSubType == sTypeAlert))) &&
8980 								(!((dType == pTypeGeneral) && (dSubType == sTypePressure))) &&
8981 								(!((dType == pTypeGeneral) && (dSubType == sTypeSoilMoisture))) &&
8982 								(!((dType == pTypeGeneral) && (dSubType == sTypeLeafWetness))) &&
8983 								(!((dType == pTypeGeneral) && (dSubType == sTypePercentage))) &&
8984 								(!((dType == pTypeGeneral) && (dSubType == sTypeWaterflow))) &&
8985 								(!((dType == pTypeGeneral) && (dSubType == sTypeCustom))) &&
8986 								(!((dType == pTypeGeneral) && (dSubType == sTypeFan))) &&
8987 								(!((dType == pTypeGeneral) && (dSubType == sTypeSoundLevel))) &&
8988 								(!((dType == pTypeGeneral) && (dSubType == sTypeZWaveClock))) &&
8989 								(!((dType == pTypeGeneral) && (dSubType == sTypeZWaveThermostatMode))) &&
8990 								(!((dType == pTypeGeneral) && (dSubType == sTypeZWaveThermostatFanMode))) &&
8991 								(!((dType == pTypeGeneral) && (dSubType == sTypeZWaveThermostatOperatingState))) &&
8992 								(!((dType == pTypeGeneral) && (dSubType == sTypeDistance))) &&
8993 								(!((dType == pTypeGeneral) && (dSubType == sTypeCounterIncremental))) &&
8994 								(!((dType == pTypeGeneral) && (dSubType == sTypeManagedCounter))) &&
8995 								(!((dType == pTypeGeneral) && (dSubType == sTypeKwh))) &&
8996 								(dType != pTypeCURRENT) &&
8997 								(dType != pTypeCURRENTENERGY) &&
8998 								(dType != pTypeENERGY) &&
8999 								(dType != pTypePOWER) &&
9000 								(dType != pTypeP1Power) &&
9001 								(dType != pTypeP1Gas) &&
9002 								(dType != pTypeYouLess) &&
9003 								(dType != pTypeAirQuality) &&
9004 								(dType != pTypeLux) &&
9005 								(dType != pTypeUsage) &&
9006 								(!((dType == pTypeRego6XXValue) && (dSubType == sTypeRego6XXCounter))) &&
9007 								(!((dType == pTypeThermostat) && (dSubType == sTypeThermSetpoint))) &&
9008 								(dType != pTypeWEIGHT) &&
9009 								(!((dType == pTypeRadiator1) && (dSubType == sTypeSmartwares)))
9010 								)
9011 								continue;
9012 						}
9013 						else if (rfilter == "wind")
9014 						{
9015 							if (
9016 								(dType != pTypeWIND)
9017 								)
9018 								continue;
9019 						}
9020 						else if (rfilter == "rain")
9021 						{
9022 							if (
9023 								(dType != pTypeRAIN)
9024 								)
9025 								continue;
9026 						}
9027 						else if (rfilter == "uv")
9028 						{
9029 							if (
9030 								(dType != pTypeUV)
9031 								)
9032 								continue;
9033 						}
9034 						else if (rfilter == "baro")
9035 						{
9036 							if (
9037 								(dType != pTypeTEMP_HUM_BARO) &&
9038 								(dType != pTypeTEMP_BARO)
9039 								)
9040 								continue;
9041 						}
9042 						else if (rfilter == "zwavealarms")
9043 						{
9044 							if (!((dType == pTypeGeneral) && (dSubType == sTypeZWaveAlarm)))
9045 								continue;
9046 						}
9047 					}
9048 
9049 					// has this device already been seen, now with different plan?
9050 					// assume results are ordered such that same device is adjacent
9051 					// if the idx and the Type are equal (type to prevent matching against Scene with same idx)
9052 					std::string thisIdx = sd[0];
9053 					int devIdx = atoi(thisIdx.c_str());
9054 
9055 					if ((ii > 0) && thisIdx == root["result"][ii - 1]["idx"].asString()) {
9056 						std::string typeOfThisOne = RFX_Type_Desc(dType, 1);
9057 						if (typeOfThisOne == root["result"][ii - 1]["Type"].asString()) {
9058 							root["result"][ii - 1]["PlanIDs"].append(atoi(sd[26].c_str()));
9059 							continue;
9060 						}
9061 					}
9062 
9063 					root["result"][ii]["HardwareID"] = hardwareID;
9064 					if (_hardwareNames.find(hardwareID) == _hardwareNames.end())
9065 					{
9066 						root["result"][ii]["HardwareName"] = "Unknown?";
9067 						root["result"][ii]["HardwareTypeVal"] = 0;
9068 						root["result"][ii]["HardwareType"] = "Unknown?";
9069 					}
9070 					else
9071 					{
9072 						root["result"][ii]["HardwareName"] = _hardwareNames[hardwareID].Name;
9073 						root["result"][ii]["HardwareTypeVal"] = _hardwareNames[hardwareID].HardwareTypeVal;
9074 						root["result"][ii]["HardwareType"] = _hardwareNames[hardwareID].HardwareType;
9075 					}
9076 					root["result"][ii]["idx"] = sd[0];
9077 					root["result"][ii]["Protected"] = (iProtected != 0);
9078 
9079 					CDomoticzHardwareBase *pHardware = m_mainworker.GetHardware(hardwareID);
9080 					if (pHardware != NULL)
9081 					{
9082 						if (pHardware->HwdType == HTYPE_SolarEdgeAPI)
9083 						{
9084 							int seSensorTimeOut = 60 * 24 * 60;
9085 							bHaveTimeout = (now - checktime >= seSensorTimeOut * 60);
9086 						}
9087 						else if (pHardware->HwdType == HTYPE_Wunderground)
9088 						{
9089 							CWunderground *pWHardware = reinterpret_cast<CWunderground *>(pHardware);
9090 							std::string forecast_url = pWHardware->GetForecastURL();
9091 							if (forecast_url != "")
9092 							{
9093 								root["result"][ii]["forecast_url"] = base64_encode(forecast_url);
9094 							}
9095 						}
9096 						else if (pHardware->HwdType == HTYPE_DarkSky)
9097 						{
9098 							CDarkSky *pWHardware = reinterpret_cast<CDarkSky*>(pHardware);
9099 							std::string forecast_url = pWHardware->GetForecastURL();
9100 							if (forecast_url != "")
9101 							{
9102 								root["result"][ii]["forecast_url"] = base64_encode(forecast_url);
9103 							}
9104 						}
9105 						else if (pHardware->HwdType == HTYPE_AccuWeather)
9106 						{
9107 							CAccuWeather *pWHardware = reinterpret_cast<CAccuWeather*>(pHardware);
9108 							std::string forecast_url = pWHardware->GetForecastURL();
9109 							if (forecast_url != "")
9110 							{
9111 								root["result"][ii]["forecast_url"] = base64_encode(forecast_url);
9112 							}
9113 						}
9114 						else if (pHardware->HwdType == HTYPE_OpenWeatherMap)
9115 						{
9116 							COpenWeatherMap *pWHardware = reinterpret_cast<COpenWeatherMap*>(pHardware);
9117 							std::string forecast_url = pWHardware->GetForecastURL();
9118 							if (forecast_url != "")
9119 							{
9120 								root["result"][ii]["forecast_url"] = base64_encode(forecast_url);
9121 							}
9122 						}
9123 						else if (pHardware->HwdType == HTYPE_BuienRadar)
9124 						{
9125 							CBuienRadar* pWHardware = reinterpret_cast<CBuienRadar*>(pHardware);
9126 							std::string forecast_url = pWHardware->GetForecastURL();
9127 							if (forecast_url != "")
9128 							{
9129 								root["result"][ii]["forecast_url"] = base64_encode(forecast_url);
9130 							}
9131 						}
9132 					}
9133 
9134 					if ((pHardware != NULL) && (pHardware->HwdType == HTYPE_PythonPlugin))
9135 					{
9136 						// Device ID special formatting should not be applied to Python plugins
9137 						root["result"][ii]["ID"] = sd[1];
9138 					}
9139 					else
9140 					{
9141 						sprintf(szData, "%04X", (unsigned int)atoi(sd[1].c_str()));
9142 						if (
9143 							(dType == pTypeTEMP) ||
9144 							(dType == pTypeTEMP_BARO) ||
9145 							(dType == pTypeTEMP_HUM) ||
9146 							(dType == pTypeTEMP_HUM_BARO) ||
9147 							(dType == pTypeBARO) ||
9148 							(dType == pTypeHUM) ||
9149 							(dType == pTypeWIND) ||
9150 							(dType == pTypeRAIN) ||
9151 							(dType == pTypeUV) ||
9152 							(dType == pTypeCURRENT) ||
9153 							(dType == pTypeCURRENTENERGY) ||
9154 							(dType == pTypeENERGY) ||
9155 							(dType == pTypeRFXMeter) ||
9156 							(dType == pTypeAirQuality) ||
9157 							(dType == pTypeRFXSensor) ||
9158 							(dType == pTypeP1Power) ||
9159 							(dType == pTypeP1Gas)
9160 							)
9161 						{
9162 							root["result"][ii]["ID"] = szData;
9163 						}
9164 						else
9165 						{
9166 							root["result"][ii]["ID"] = sd[1];
9167 						}
9168 					}
9169 					root["result"][ii]["Unit"] = atoi(sd[2].c_str());
9170 					root["result"][ii]["Type"] = RFX_Type_Desc(dType, 1);
9171 					root["result"][ii]["SubType"] = RFX_Type_SubType_Desc(dType, dSubType);
9172 					root["result"][ii]["TypeImg"] = RFX_Type_Desc(dType, 2);
9173 					root["result"][ii]["Name"] = sDeviceName;
9174 					root["result"][ii]["Description"] = Description;
9175 					root["result"][ii]["Used"] = used;
9176 					root["result"][ii]["Favorite"] = favorite;
9177 
9178 					int iSignalLevel = atoi(sd[7].c_str());
9179 					if (iSignalLevel < 12)
9180 						root["result"][ii]["SignalLevel"] = iSignalLevel;
9181 					else
9182 						root["result"][ii]["SignalLevel"] = "-";
9183 					root["result"][ii]["BatteryLevel"] = atoi(sd[8].c_str());
9184 					root["result"][ii]["LastUpdate"] = sLastUpdate;
9185 					root["result"][ii]["CustomImage"] = CustomImage;
9186 					root["result"][ii]["XOffset"] = sd[24].c_str();
9187 					root["result"][ii]["YOffset"] = sd[25].c_str();
9188 					root["result"][ii]["PlanID"] = sd[26].c_str();
9189 					Json::Value jsonArray;
9190 					jsonArray.append(atoi(sd[26].c_str()));
9191 					root["result"][ii]["PlanIDs"] = jsonArray;
9192 					root["result"][ii]["AddjValue"] = AddjValue;
9193 					root["result"][ii]["AddjMulti"] = AddjMulti;
9194 					root["result"][ii]["AddjValue2"] = AddjValue2;
9195 					root["result"][ii]["AddjMulti2"] = AddjMulti2;
9196 
9197 					std::stringstream s_data;
9198 					s_data << int(nValue) << ", " << sValue;
9199 					root["result"][ii]["Data"] = s_data.str();
9200 
9201 					root["result"][ii]["Notifications"] = (m_notifications.HasNotifications(sd[0]) == true) ? "true" : "false";
9202 					root["result"][ii]["ShowNotifications"] = true;
9203 
9204 					bool bHasTimers = false;
9205 
9206 					if (
9207 						(dType == pTypeLighting1) ||
9208 						(dType == pTypeLighting2) ||
9209 						(dType == pTypeLighting3) ||
9210 						(dType == pTypeLighting4) ||
9211 						(dType == pTypeLighting5) ||
9212 						(dType == pTypeLighting6) ||
9213 						(dType == pTypeFan) ||
9214 						(dType == pTypeColorSwitch) ||
9215 						(dType == pTypeCurtain) ||
9216 						(dType == pTypeBlinds) ||
9217 						(dType == pTypeRFY) ||
9218 						(dType == pTypeChime) ||
9219 						(dType == pTypeThermostat2) ||
9220 						(dType == pTypeThermostat3) ||
9221 						(dType == pTypeThermostat4) ||
9222 						(dType == pTypeRemote) ||
9223 						(dType == pTypeGeneralSwitch) ||
9224 						(dType == pTypeHomeConfort) ||
9225 						(dType == pTypeFS20) ||
9226 						((dType == pTypeRadiator1) && (dSubType == sTypeSmartwaresSwitchRadiator)) ||
9227 						((dType == pTypeRego6XXValue) && (dSubType == sTypeRego6XXStatus)) ||
9228 						(dType == pTypeHunter)
9229 						)
9230 					{
9231 						//add light details
9232 						bHasTimers = m_sql.HasTimers(sd[0]);
9233 
9234 						bHaveTimeout = false;
9235 #ifdef WITH_OPENZWAVE
9236 						if (pHardware != NULL)
9237 						{
9238 							if (pHardware->HwdType == HTYPE_OpenZWave)
9239 							{
9240 								COpenZWave *pZWave = reinterpret_cast<COpenZWave*>(pHardware);
9241 								unsigned long ID;
9242 								std::stringstream s_strid;
9243 								s_strid << std::hex << sd[1];
9244 								s_strid >> ID;
9245 								int nodeID = (ID & 0x0000FF00) >> 8;
9246 								bHaveTimeout = pZWave->HasNodeFailed(nodeID);
9247 							}
9248 						}
9249 #endif
9250 						root["result"][ii]["HaveTimeout"] = bHaveTimeout;
9251 
9252 						std::string lstatus = "";
9253 						int llevel = 0;
9254 						bool bHaveDimmer = false;
9255 						bool bHaveGroupCmd = false;
9256 						int maxDimLevel = 0;
9257 
9258 						GetLightStatus(dType, dSubType, switchtype, nValue, sValue, lstatus, llevel, bHaveDimmer, maxDimLevel, bHaveGroupCmd);
9259 
9260 						root["result"][ii]["Status"] = lstatus;
9261 						root["result"][ii]["StrParam1"] = strParam1;
9262 						root["result"][ii]["StrParam2"] = strParam2;
9263 
9264 						std::string IconFile = "Light";
9265 						std::map<int, int>::const_iterator ittIcon = m_custom_light_icons_lookup.find(CustomImage);
9266 						if (ittIcon != m_custom_light_icons_lookup.end())
9267 						{
9268 							IconFile = m_custom_light_icons[ittIcon->second].RootFile;
9269 						}
9270 						root["result"][ii]["Image"] = IconFile;
9271 
9272 						if (switchtype == STYPE_Dimmer)
9273 						{
9274 							root["result"][ii]["Level"] = LastLevel;
9275 							int iLevel = round((float(maxDimLevel) / 100.0f)*LastLevel);
9276 							root["result"][ii]["LevelInt"] = iLevel;
9277 							if ((dType == pTypeColorSwitch) ||
9278 							    (dType == pTypeLighting5 && dSubType == sTypeTRC02) ||
9279 							    (dType == pTypeLighting5 && dSubType == sTypeTRC02_2) ||
9280 							    (dType == pTypeGeneralSwitch && dSubType == sSwitchTypeTRC02) ||
9281 							    (dType == pTypeGeneralSwitch && dSubType == sSwitchTypeTRC02_2))
9282 							{
9283 								_tColor color(sColor);
9284 								std::string jsonColor = color.toJSONString();
9285 								root["result"][ii]["Color"] = jsonColor;
9286 								llevel = LastLevel;
9287 								if (lstatus == "Set Level" || lstatus == "Set Color")
9288 								{
9289 									sprintf(szTmp, "Set Level: %d %%", LastLevel);
9290 									root["result"][ii]["Status"] = szTmp;
9291 								}
9292 							}
9293 						}
9294 						else
9295 						{
9296 							root["result"][ii]["Level"] = llevel;
9297 							root["result"][ii]["LevelInt"] = atoi(sValue.c_str());
9298 						}
9299 						root["result"][ii]["HaveDimmer"] = bHaveDimmer;
9300 						std::string DimmerType = "none";
9301 						if (switchtype == STYPE_Dimmer)
9302 						{
9303 							DimmerType = "abs";
9304 							if (_hardwareNames.find(hardwareID) != _hardwareNames.end())
9305 							{
9306 								// Milight V4/V5 bridges do not support absolute dimming for RGB or CW_WW lights
9307 								if (_hardwareNames[hardwareID].HardwareTypeVal == HTYPE_LimitlessLights &&
9308 								    atoi(_hardwareNames[hardwareID].Mode2.c_str()) != CLimitLess::LBTYPE_V6 &&
9309 									(atoi(_hardwareNames[hardwareID].Mode1.c_str()) == sTypeColor_RGB ||
9310 									 atoi(_hardwareNames[hardwareID].Mode1.c_str()) == sTypeColor_White ||
9311 									 atoi(_hardwareNames[hardwareID].Mode1.c_str()) == sTypeColor_CW_WW))
9312 								{
9313 									DimmerType = "rel";
9314 								}
9315 							}
9316 						}
9317 						root["result"][ii]["DimmerType"] = DimmerType;
9318 						root["result"][ii]["MaxDimLevel"] = maxDimLevel;
9319 						root["result"][ii]["HaveGroupCmd"] = bHaveGroupCmd;
9320 						root["result"][ii]["SwitchType"] = Switch_Type_Desc(switchtype);
9321 						root["result"][ii]["SwitchTypeVal"] = switchtype;
9322 						uint64_t camIDX = m_mainworker.m_cameras.IsDevSceneInCamera(0, sd[0]);
9323 						root["result"][ii]["UsedByCamera"] = (camIDX != 0) ? true : false;
9324 						if (camIDX != 0) {
9325 							std::stringstream scidx;
9326 							scidx << camIDX;
9327 							root["result"][ii]["CameraIdx"] = scidx.str();
9328 						}
9329 
9330 						bool bIsSubDevice = false;
9331 						std::vector<std::vector<std::string> > resultSD;
9332 						resultSD = m_sql.safe_query("SELECT ID FROM LightSubDevices WHERE (DeviceRowID=='%q')",
9333 							sd[0].c_str());
9334 						bIsSubDevice = (resultSD.size() > 0);
9335 
9336 						root["result"][ii]["IsSubDevice"] = bIsSubDevice;
9337 
9338 						if (switchtype == STYPE_Doorbell)
9339 						{
9340 							root["result"][ii]["TypeImg"] = "doorbell";
9341 							root["result"][ii]["Status"] = "";//"Pressed";
9342 						}
9343 						else if (switchtype == STYPE_DoorContact)
9344 						{
9345 							if (CustomImage == 0)
9346 							{
9347 								root["result"][ii]["Image"] = "Door";
9348 							}
9349 							root["result"][ii]["TypeImg"] = "door";
9350 							bool bIsOn = IsLightSwitchOn(lstatus);
9351 							root["result"][ii]["InternalState"] = (bIsOn == true) ? "Open" : "Closed";
9352 							if (bIsOn) {
9353 								lstatus = "Open";
9354 							}
9355 							else {
9356 								lstatus = "Closed";
9357 							}
9358 							root["result"][ii]["Status"] = lstatus;
9359 						}
9360 						else if (switchtype == STYPE_DoorLock)
9361 						{
9362 							if (CustomImage == 0)
9363 							{
9364 								root["result"][ii]["Image"] = "Door";
9365 							}
9366 							root["result"][ii]["TypeImg"] = "door";
9367 							bool bIsOn = IsLightSwitchOn(lstatus);
9368 							root["result"][ii]["InternalState"] = (bIsOn == true) ? "Locked" : "Unlocked";
9369 							if (bIsOn) {
9370 								lstatus = "Locked";
9371 							}
9372 							else {
9373 								lstatus = "Unlocked";
9374 							}
9375 							root["result"][ii]["Status"] = lstatus;
9376 						}
9377 						else if (switchtype == STYPE_DoorLockInverted)
9378 						{
9379 							if (CustomImage == 0)
9380 							{
9381 								root["result"][ii]["Image"] = "Door";
9382 							}
9383 							root["result"][ii]["TypeImg"] = "door";
9384 							bool bIsOn = IsLightSwitchOn(lstatus);
9385 							root["result"][ii]["InternalState"] = (bIsOn == true) ? "Unlocked" : "Locked";
9386 							if (bIsOn) {
9387 								lstatus = "Unlocked";
9388 							}
9389 							else {
9390 								lstatus = "Locked";
9391 							}
9392 							root["result"][ii]["Status"] = lstatus;
9393 						}
9394 						else if (switchtype == STYPE_PushOn)
9395 						{
9396 							if (CustomImage == 0)
9397 							{
9398 								root["result"][ii]["Image"] = "Push";
9399 							}
9400 							root["result"][ii]["TypeImg"] = "push";
9401 							root["result"][ii]["Status"] = "";
9402 							root["result"][ii]["InternalState"] = (IsLightSwitchOn(lstatus) == true) ? "On" : "Off";
9403 						}
9404 						else if (switchtype == STYPE_PushOff)
9405 						{
9406 							if (CustomImage == 0)
9407 							{
9408 								root["result"][ii]["Image"] = "Push";
9409 							}
9410 							root["result"][ii]["TypeImg"] = "push";
9411 							root["result"][ii]["Status"] = "";
9412 							root["result"][ii]["TypeImg"] = "pushoff";
9413 						}
9414 						else if (switchtype == STYPE_X10Siren)
9415 							root["result"][ii]["TypeImg"] = "siren";
9416 						else if (switchtype == STYPE_SMOKEDETECTOR)
9417 						{
9418 							root["result"][ii]["TypeImg"] = "smoke";
9419 							root["result"][ii]["SwitchTypeVal"] = STYPE_SMOKEDETECTOR;
9420 							root["result"][ii]["SwitchType"] = Switch_Type_Desc(STYPE_SMOKEDETECTOR);
9421 						}
9422 						else if (switchtype == STYPE_Contact)
9423 						{
9424 							if (CustomImage == 0)
9425 							{
9426 								root["result"][ii]["Image"] = "Contact";
9427 							}
9428 							root["result"][ii]["TypeImg"] = "contact";
9429 							bool bIsOn = IsLightSwitchOn(lstatus);
9430 							if (bIsOn) {
9431 								lstatus = "Open";
9432 							}
9433 							else {
9434 								lstatus = "Closed";
9435 							}
9436 							root["result"][ii]["Status"] = lstatus;
9437 						}
9438 						else if (switchtype == STYPE_Media)
9439 						{
9440 							if ((pHardware != NULL) && (pHardware->HwdType == HTYPE_LogitechMediaServer))
9441 								root["result"][ii]["TypeImg"] = "LogitechMediaServer";
9442 							else
9443 								root["result"][ii]["TypeImg"] = "Media";
9444 							root["result"][ii]["Status"] = Media_Player_States((_eMediaStatus)nValue);
9445 							lstatus = sValue;
9446 						}
9447 						else if (
9448 							(switchtype == STYPE_Blinds) ||
9449 							(switchtype == STYPE_VenetianBlindsUS) ||
9450 							(switchtype == STYPE_VenetianBlindsEU)
9451 							)
9452 						{
9453 							root["result"][ii]["TypeImg"] = "blinds";
9454 							if ((lstatus == "On") || (lstatus == "Close inline relay")) {
9455 								lstatus = "Closed";
9456 							}
9457 							else if ((lstatus == "Stop") || (lstatus == "Stop inline relay")) {
9458 								lstatus = "Stopped";
9459 							}
9460 							else {
9461 								lstatus = "Open";
9462 							}
9463 							root["result"][ii]["Status"] = lstatus;
9464 						}
9465 						else if (switchtype == STYPE_BlindsInverted)
9466 						{
9467 							root["result"][ii]["TypeImg"] = "blinds";
9468 							if (lstatus == "On") {
9469 								lstatus = "Open";
9470 							}
9471 							else {
9472 								lstatus = "Closed";
9473 							}
9474 							root["result"][ii]["Status"] = lstatus;
9475 						}
9476 						else if ((switchtype == STYPE_BlindsPercentage) || (switchtype == STYPE_BlindsPercentageInverted))
9477 						{
9478 							root["result"][ii]["TypeImg"] = "blinds";
9479 							root["result"][ii]["Level"] = LastLevel;
9480 							int iLevel = round((float(maxDimLevel) / 100.0f)*LastLevel);
9481 							root["result"][ii]["LevelInt"] = iLevel;
9482 							if (lstatus == "On") {
9483 								lstatus = (switchtype == STYPE_BlindsPercentage) ? "Closed" : "Open";
9484 							}
9485 							else if (lstatus == "Off") {
9486 								lstatus = (switchtype == STYPE_BlindsPercentage) ? "Open" : "Closed";
9487 							}
9488 
9489 							root["result"][ii]["Status"] = lstatus;
9490 						}
9491 						else if (switchtype == STYPE_Dimmer)
9492 						{
9493 							root["result"][ii]["TypeImg"] = "dimmer";
9494 						}
9495 						else if (switchtype == STYPE_Motion)
9496 						{
9497 							root["result"][ii]["TypeImg"] = "motion";
9498 						}
9499 						else if (switchtype == STYPE_Selector)
9500 						{
9501 							std::string selectorStyle = options["SelectorStyle"];
9502 							std::string levelOffHidden = options["LevelOffHidden"];
9503 							std::string levelNames = options["LevelNames"];
9504 							std::string levelActions = options["LevelActions"];
9505 							if (selectorStyle.empty()) {
9506 								selectorStyle.assign("0"); // default is 'button set'
9507 							}
9508 							if (levelOffHidden.empty()) {
9509 								levelOffHidden.assign("false"); // default is 'not hidden'
9510 							}
9511 							if (levelNames.empty()) {
9512 								levelNames.assign("Off"); // default is Off only
9513 							}
9514 							root["result"][ii]["TypeImg"] = "Light";
9515 							root["result"][ii]["SelectorStyle"] = atoi(selectorStyle.c_str());
9516 							root["result"][ii]["LevelOffHidden"] = (levelOffHidden == "true");
9517 							root["result"][ii]["LevelNames"] = base64_encode(levelNames);
9518 							root["result"][ii]["LevelActions"] = base64_encode(levelActions);
9519 						}
9520 						sprintf(szData, "%s", lstatus.c_str());
9521 						root["result"][ii]["Data"] = szData;
9522 					}
9523 					else if (dType == pTypeSecurity1)
9524 					{
9525 						std::string lstatus = "";
9526 						int llevel = 0;
9527 						bool bHaveDimmer = false;
9528 						bool bHaveGroupCmd = false;
9529 						int maxDimLevel = 0;
9530 
9531 						GetLightStatus(dType, dSubType, switchtype, nValue, sValue, lstatus, llevel, bHaveDimmer, maxDimLevel, bHaveGroupCmd);
9532 
9533 						root["result"][ii]["Status"] = lstatus;
9534 						root["result"][ii]["HaveDimmer"] = bHaveDimmer;
9535 						root["result"][ii]["MaxDimLevel"] = maxDimLevel;
9536 						root["result"][ii]["HaveGroupCmd"] = bHaveGroupCmd;
9537 						root["result"][ii]["SwitchType"] = "Security";
9538 						root["result"][ii]["SwitchTypeVal"] = switchtype; //was 0?;
9539 						root["result"][ii]["TypeImg"] = "security";
9540 						root["result"][ii]["StrParam1"] = strParam1;
9541 						root["result"][ii]["StrParam2"] = strParam2;
9542 						root["result"][ii]["Protected"] = (iProtected != 0);
9543 
9544 						if (
9545 							(dSubType == sTypeKD101)
9546 							|| (dSubType == sTypeSA30)
9547 							|| (dSubType == sTypeRM174RF)
9548 							|| (switchtype == STYPE_SMOKEDETECTOR)
9549 							)
9550 						{
9551 							root["result"][ii]["SwitchTypeVal"] = STYPE_SMOKEDETECTOR;
9552 							root["result"][ii]["TypeImg"] = "smoke";
9553 							root["result"][ii]["SwitchType"] = Switch_Type_Desc(STYPE_SMOKEDETECTOR);
9554 						}
9555 						sprintf(szData, "%s", lstatus.c_str());
9556 						root["result"][ii]["Data"] = szData;
9557 						root["result"][ii]["HaveTimeout"] = false;
9558 					}
9559 					else if (dType == pTypeSecurity2)
9560 					{
9561 						std::string lstatus = "";
9562 						int llevel = 0;
9563 						bool bHaveDimmer = false;
9564 						bool bHaveGroupCmd = false;
9565 						int maxDimLevel = 0;
9566 
9567 						GetLightStatus(dType, dSubType, switchtype, nValue, sValue, lstatus, llevel, bHaveDimmer, maxDimLevel, bHaveGroupCmd);
9568 
9569 						root["result"][ii]["Status"] = lstatus;
9570 						root["result"][ii]["HaveDimmer"] = bHaveDimmer;
9571 						root["result"][ii]["MaxDimLevel"] = maxDimLevel;
9572 						root["result"][ii]["HaveGroupCmd"] = bHaveGroupCmd;
9573 						root["result"][ii]["SwitchType"] = "Security";
9574 						root["result"][ii]["SwitchTypeVal"] = switchtype; //was 0?;
9575 						root["result"][ii]["TypeImg"] = "security";
9576 						root["result"][ii]["StrParam1"] = strParam1;
9577 						root["result"][ii]["StrParam2"] = strParam2;
9578 						root["result"][ii]["Protected"] = (iProtected != 0);
9579 						sprintf(szData, "%s", lstatus.c_str());
9580 						root["result"][ii]["Data"] = szData;
9581 						root["result"][ii]["HaveTimeout"] = false;
9582 					}
9583 					else if (dType == pTypeEvohome || dType == pTypeEvohomeRelay)
9584 					{
9585 						std::string lstatus = "";
9586 						int llevel = 0;
9587 						bool bHaveDimmer = false;
9588 						bool bHaveGroupCmd = false;
9589 						int maxDimLevel = 0;
9590 
9591 						GetLightStatus(dType, dSubType, switchtype, nValue, sValue, lstatus, llevel, bHaveDimmer, maxDimLevel, bHaveGroupCmd);
9592 
9593 						root["result"][ii]["Status"] = lstatus;
9594 						root["result"][ii]["HaveDimmer"] = bHaveDimmer;
9595 						root["result"][ii]["MaxDimLevel"] = maxDimLevel;
9596 						root["result"][ii]["HaveGroupCmd"] = bHaveGroupCmd;
9597 						root["result"][ii]["SwitchType"] = "evohome";
9598 						root["result"][ii]["SwitchTypeVal"] = switchtype; //was 0?;
9599 						root["result"][ii]["TypeImg"] = "override_mini";
9600 						root["result"][ii]["StrParam1"] = strParam1;
9601 						root["result"][ii]["StrParam2"] = strParam2;
9602 						root["result"][ii]["Protected"] = (iProtected != 0);
9603 
9604 						sprintf(szData, "%s", lstatus.c_str());
9605 						root["result"][ii]["Data"] = szData;
9606 						root["result"][ii]["HaveTimeout"] = false;
9607 
9608 						if (dType == pTypeEvohomeRelay)
9609 						{
9610 							root["result"][ii]["SwitchType"] = "TPI";
9611 							root["result"][ii]["Level"] = llevel;
9612 							root["result"][ii]["LevelInt"] = atoi(sValue.c_str());
9613 							if (root["result"][ii]["Unit"].asInt() > 100)
9614 								root["result"][ii]["Protected"] = true;
9615 
9616 							sprintf(szData, "%s: %d", lstatus.c_str(), atoi(sValue.c_str()));
9617 							root["result"][ii]["Data"] = szData;
9618 						}
9619 					}
9620 					else if ((dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater))
9621 					{
9622 						root["result"][ii]["HaveTimeout"] = bHaveTimeout;
9623 						root["result"][ii]["TypeImg"] = "override_mini";
9624 
9625 						std::vector<std::string> strarray;
9626 						StringSplit(sValue, ";", strarray);
9627 						if (strarray.size() >= 3)
9628 						{
9629 							int i = 0;
9630 							double tempCelcius = atof(strarray[i++].c_str());
9631 							double temp = ConvertTemperature(tempCelcius, tempsign);
9632 							double tempSetPoint;
9633 							root["result"][ii]["Temp"] = temp;
9634 							if (dType == pTypeEvohomeZone)
9635 							{
9636 								tempCelcius = atof(strarray[i++].c_str());
9637 								tempSetPoint = ConvertTemperature(tempCelcius, tempsign);
9638 								root["result"][ii]["SetPoint"] = tempSetPoint;
9639 							}
9640 							else
9641 								root["result"][ii]["State"] = strarray[i++];
9642 
9643 							std::string strstatus = strarray[i++];
9644 							root["result"][ii]["Status"] = strstatus;
9645 
9646 							if ((dType == pTypeEvohomeZone || dType == pTypeEvohomeWater) && strarray.size() >= 4)
9647 							{
9648 								root["result"][ii]["Until"] = strarray[i++];
9649 							}
9650 							if (dType == pTypeEvohomeZone)
9651 							{
9652 								if (tempCelcius == 325.1)
9653 									sprintf(szTmp, "Off");
9654 								else
9655 									sprintf(szTmp, "%.1f %c", tempSetPoint, tempsign);
9656 								if (strarray.size() >= 4)
9657 									sprintf(szData, "%.1f %c, (%s), %s until %s", temp, tempsign, szTmp, strstatus.c_str(), strarray[3].c_str());
9658 								else
9659 									sprintf(szData, "%.1f %c, (%s), %s", temp, tempsign, szTmp, strstatus.c_str());
9660 							}
9661 							else
9662 								if (strarray.size() >= 4)
9663 									sprintf(szData, "%.1f %c, %s, %s until %s", temp, tempsign, strarray[1].c_str(), strstatus.c_str(), strarray[3].c_str());
9664 								else
9665 									sprintf(szData, "%.1f %c, %s, %s", temp, tempsign, strarray[1].c_str(), strstatus.c_str());
9666 							root["result"][ii]["Data"] = szData;
9667 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
9668 						}
9669 					}
9670 					else if ((dType == pTypeTEMP) || (dType == pTypeRego6XXTemp))
9671 					{
9672 						double tvalue = ConvertTemperature(atof(sValue.c_str()), tempsign);
9673 						root["result"][ii]["Temp"] = tvalue;
9674 						sprintf(szData, "%.1f %c", tvalue, tempsign);
9675 						root["result"][ii]["Data"] = szData;
9676 						root["result"][ii]["HaveTimeout"] = bHaveTimeout;
9677 
9678 						_tTrendCalculator::_eTendencyType tstate = _tTrendCalculator::_eTendencyType::TENDENCY_UNKNOWN;
9679 						uint64_t tID = ((uint64_t)(hardwareID & 0x7FFFFFFF) << 32) | (devIdx & 0x7FFFFFFF);
9680 						if (m_mainworker.m_trend_calculator.find(tID) != m_mainworker.m_trend_calculator.end())
9681 						{
9682 							tstate = m_mainworker.m_trend_calculator[tID].m_state;
9683 						}
9684 						root["result"][ii]["trend"] = (int)tstate;
9685 					}
9686 					else if (dType == pTypeThermostat1)
9687 					{
9688 						std::vector<std::string> strarray;
9689 						StringSplit(sValue, ";", strarray);
9690 						if (strarray.size() == 4)
9691 						{
9692 							double tvalue = ConvertTemperature(atof(strarray[0].c_str()), tempsign);
9693 							root["result"][ii]["Temp"] = tvalue;
9694 							sprintf(szData, "%.1f %c", tvalue, tempsign);
9695 							root["result"][ii]["Data"] = szData;
9696 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
9697 						}
9698 					}
9699 					else if ((dType == pTypeRFXSensor) && (dSubType == sTypeRFXSensorTemp))
9700 					{
9701 						double tvalue = ConvertTemperature(atof(sValue.c_str()), tempsign);
9702 						root["result"][ii]["Temp"] = tvalue;
9703 						sprintf(szData, "%.1f %c", tvalue, tempsign);
9704 						root["result"][ii]["Data"] = szData;
9705 						root["result"][ii]["TypeImg"] = "temperature";
9706 						root["result"][ii]["HaveTimeout"] = bHaveTimeout;
9707 						_tTrendCalculator::_eTendencyType tstate = _tTrendCalculator::_eTendencyType::TENDENCY_UNKNOWN;
9708 						uint64_t tID = ((uint64_t)(hardwareID & 0x7FFFFFFF) << 32) | (devIdx & 0x7FFFFFFF);
9709 						if (m_mainworker.m_trend_calculator.find(tID) != m_mainworker.m_trend_calculator.end())
9710 						{
9711 							tstate = m_mainworker.m_trend_calculator[tID].m_state;
9712 						}
9713 						root["result"][ii]["trend"] = (int)tstate;
9714 					}
9715 					else if (dType == pTypeHUM)
9716 					{
9717 						root["result"][ii]["Humidity"] = nValue;
9718 						root["result"][ii]["HumidityStatus"] = RFX_Humidity_Status_Desc(atoi(sValue.c_str()));
9719 						sprintf(szData, "Humidity %d %%", nValue);
9720 						root["result"][ii]["Data"] = szData;
9721 						root["result"][ii]["HaveTimeout"] = bHaveTimeout;
9722 					}
9723 					else if (dType == pTypeTEMP_HUM)
9724 					{
9725 						std::vector<std::string> strarray;
9726 						StringSplit(sValue, ";", strarray);
9727 						if (strarray.size() == 3)
9728 						{
9729 							double tempCelcius = atof(strarray[0].c_str());
9730 							double temp = ConvertTemperature(tempCelcius, tempsign);
9731 							int humidity = atoi(strarray[1].c_str());
9732 
9733 							root["result"][ii]["Temp"] = temp;
9734 							root["result"][ii]["Humidity"] = humidity;
9735 							root["result"][ii]["HumidityStatus"] = RFX_Humidity_Status_Desc(atoi(strarray[2].c_str()));
9736 							sprintf(szData, "%.1f %c, %d %%", temp, tempsign, atoi(strarray[1].c_str()));
9737 							root["result"][ii]["Data"] = szData;
9738 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
9739 
9740 							//Calculate dew point
9741 
9742 							sprintf(szTmp, "%.2f", ConvertTemperature(CalculateDewPoint(tempCelcius, humidity), tempsign));
9743 							root["result"][ii]["DewPoint"] = szTmp;
9744 
9745 							_tTrendCalculator::_eTendencyType tstate = _tTrendCalculator::_eTendencyType::TENDENCY_UNKNOWN;
9746 							uint64_t tID = ((uint64_t)(hardwareID & 0x7FFFFFFF) << 32) | (devIdx & 0x7FFFFFFF);
9747 							if (m_mainworker.m_trend_calculator.find(tID) != m_mainworker.m_trend_calculator.end())
9748 							{
9749 								tstate = m_mainworker.m_trend_calculator[tID].m_state;
9750 							}
9751 							root["result"][ii]["trend"] = (int)tstate;
9752 						}
9753 					}
9754 					else if (dType == pTypeTEMP_HUM_BARO)
9755 					{
9756 						std::vector<std::string> strarray;
9757 						StringSplit(sValue, ";", strarray);
9758 						if (strarray.size() == 5)
9759 						{
9760 							double tempCelcius = atof(strarray[0].c_str());
9761 							double temp = ConvertTemperature(tempCelcius, tempsign);
9762 							int humidity = atoi(strarray[1].c_str());
9763 
9764 							root["result"][ii]["Temp"] = temp;
9765 							root["result"][ii]["Humidity"] = humidity;
9766 							root["result"][ii]["HumidityStatus"] = RFX_Humidity_Status_Desc(atoi(strarray[2].c_str()));
9767 							root["result"][ii]["Forecast"] = atoi(strarray[4].c_str());
9768 
9769 							sprintf(szTmp, "%.2f", ConvertTemperature(CalculateDewPoint(tempCelcius, humidity), tempsign));
9770 							root["result"][ii]["DewPoint"] = szTmp;
9771 
9772 							if (dSubType == sTypeTHBFloat)
9773 							{
9774 								root["result"][ii]["Barometer"] = atof(strarray[3].c_str());
9775 								root["result"][ii]["ForecastStr"] = RFX_WSForecast_Desc(atoi(strarray[4].c_str()));
9776 							}
9777 							else
9778 							{
9779 								root["result"][ii]["Barometer"] = atoi(strarray[3].c_str());
9780 								root["result"][ii]["ForecastStr"] = RFX_Forecast_Desc(atoi(strarray[4].c_str()));
9781 							}
9782 							if (dSubType == sTypeTHBFloat)
9783 							{
9784 								sprintf(szData, "%.1f %c, %d %%, %.1f hPa",
9785 									temp,
9786 									tempsign,
9787 									atoi(strarray[1].c_str()),
9788 									atof(strarray[3].c_str())
9789 								);
9790 							}
9791 							else
9792 							{
9793 								sprintf(szData, "%.1f %c, %d %%, %d hPa",
9794 									temp,
9795 									tempsign,
9796 									atoi(strarray[1].c_str()),
9797 									atoi(strarray[3].c_str())
9798 								);
9799 							}
9800 							root["result"][ii]["Data"] = szData;
9801 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
9802 
9803 							_tTrendCalculator::_eTendencyType tstate = _tTrendCalculator::_eTendencyType::TENDENCY_UNKNOWN;
9804 							uint64_t tID = ((uint64_t)(hardwareID & 0x7FFFFFFF) << 32) | (devIdx & 0x7FFFFFFF);
9805 							if (m_mainworker.m_trend_calculator.find(tID) != m_mainworker.m_trend_calculator.end())
9806 							{
9807 								tstate = m_mainworker.m_trend_calculator[tID].m_state;
9808 							}
9809 							root["result"][ii]["trend"] = (int)tstate;
9810 						}
9811 					}
9812 					else if (dType == pTypeTEMP_BARO)
9813 					{
9814 						std::vector<std::string> strarray;
9815 						StringSplit(sValue, ";", strarray);
9816 						if (strarray.size() >= 3)
9817 						{
9818 							double tvalue = ConvertTemperature(atof(strarray[0].c_str()), tempsign);
9819 							root["result"][ii]["Temp"] = tvalue;
9820 							int forecast = atoi(strarray[2].c_str());
9821 							root["result"][ii]["Forecast"] = forecast;
9822 							root["result"][ii]["ForecastStr"] = BMP_Forecast_Desc(forecast);
9823 							root["result"][ii]["Barometer"] = atof(strarray[1].c_str());
9824 
9825 							sprintf(szData, "%.1f %c, %.1f hPa",
9826 								tvalue,
9827 								tempsign,
9828 								atof(strarray[1].c_str())
9829 							);
9830 							root["result"][ii]["Data"] = szData;
9831 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
9832 
9833 							_tTrendCalculator::_eTendencyType tstate = _tTrendCalculator::_eTendencyType::TENDENCY_UNKNOWN;
9834 							uint64_t tID = ((uint64_t)(hardwareID & 0x7FFFFFFF) << 32) | (devIdx & 0x7FFFFFFF);
9835 							if (m_mainworker.m_trend_calculator.find(tID) != m_mainworker.m_trend_calculator.end())
9836 							{
9837 								tstate = m_mainworker.m_trend_calculator[tID].m_state;
9838 							}
9839 							root["result"][ii]["trend"] = (int)tstate;
9840 						}
9841 					}
9842 					else if (dType == pTypeUV)
9843 					{
9844 						std::vector<std::string> strarray;
9845 						StringSplit(sValue, ";", strarray);
9846 						if (strarray.size() == 2)
9847 						{
9848 							float UVI = static_cast<float>(atof(strarray[0].c_str()));
9849 							root["result"][ii]["UVI"] = strarray[0];
9850 							if (dSubType == sTypeUV3)
9851 							{
9852 								double tvalue = ConvertTemperature(atof(strarray[1].c_str()), tempsign);
9853 
9854 								root["result"][ii]["Temp"] = tvalue;
9855 								sprintf(szData, "%.1f UVI, %.1f&deg; %c", UVI, tvalue, tempsign);
9856 
9857 								_tTrendCalculator::_eTendencyType tstate = _tTrendCalculator::_eTendencyType::TENDENCY_UNKNOWN;
9858 								uint64_t tID = ((uint64_t)(hardwareID & 0x7FFFFFFF) << 32) | (devIdx & 0x7FFFFFFF);
9859 								if (m_mainworker.m_trend_calculator.find(tID) != m_mainworker.m_trend_calculator.end())
9860 								{
9861 									tstate = m_mainworker.m_trend_calculator[tID].m_state;
9862 								}
9863 								root["result"][ii]["trend"] = (int)tstate;
9864 							}
9865 							else
9866 							{
9867 								sprintf(szData, "%.1f UVI", UVI);
9868 							}
9869 							root["result"][ii]["Data"] = szData;
9870 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
9871 						}
9872 					}
9873 					else if (dType == pTypeWIND)
9874 					{
9875 						std::vector<std::string> strarray;
9876 						StringSplit(sValue, ";", strarray);
9877 						if (strarray.size() == 6)
9878 						{
9879 							root["result"][ii]["Direction"] = atof(strarray[0].c_str());
9880 							root["result"][ii]["DirectionStr"] = strarray[1];
9881 
9882 							if (dSubType != sTypeWIND5)
9883 							{
9884 								int intSpeed = atoi(strarray[2].c_str());
9885 								if (m_sql.m_windunit != WINDUNIT_Beaufort)
9886 								{
9887 									sprintf(szTmp, "%.1f", float(intSpeed) * m_sql.m_windscale);
9888 								}
9889 								else
9890 								{
9891 									float windms = float(intSpeed) * 0.1f;
9892 									sprintf(szTmp, "%d", MStoBeaufort(windms));
9893 								}
9894 								root["result"][ii]["Speed"] = szTmp;
9895 							}
9896 
9897 							//if (dSubType!=sTypeWIND6) //problem in RFXCOM firmware? gust=speed?
9898 							{
9899 								int intGust = atoi(strarray[3].c_str());
9900 								if (m_sql.m_windunit != WINDUNIT_Beaufort)
9901 								{
9902 									sprintf(szTmp, "%.1f", float(intGust) *m_sql.m_windscale);
9903 								}
9904 								else
9905 								{
9906 									float gustms = float(intGust) * 0.1f;
9907 									sprintf(szTmp, "%d", MStoBeaufort(gustms));
9908 								}
9909 								root["result"][ii]["Gust"] = szTmp;
9910 							}
9911 							if ((dSubType == sTypeWIND4) || (dSubType == sTypeWINDNoTemp))
9912 							{
9913 								if (dSubType == sTypeWIND4)
9914 								{
9915 									double tvalue = ConvertTemperature(atof(strarray[4].c_str()), tempsign);
9916 									root["result"][ii]["Temp"] = tvalue;
9917 								}
9918 								double tvalue = ConvertTemperature(atof(strarray[5].c_str()), tempsign);
9919 								root["result"][ii]["Chill"] = tvalue;
9920 
9921 								_tTrendCalculator::_eTendencyType tstate = _tTrendCalculator::_eTendencyType::TENDENCY_UNKNOWN;
9922 								uint64_t tID = ((uint64_t)(hardwareID & 0x7FFFFFFF) << 32) | (devIdx & 0x7FFFFFFF);
9923 								if (m_mainworker.m_trend_calculator.find(tID) != m_mainworker.m_trend_calculator.end())
9924 								{
9925 									tstate = m_mainworker.m_trend_calculator[tID].m_state;
9926 								}
9927 								root["result"][ii]["trend"] = (int)tstate;
9928 							}
9929 							root["result"][ii]["Data"] = sValue;
9930 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
9931 						}
9932 					}
9933 					else if (dType == pTypeRAIN)
9934 					{
9935 						std::vector<std::string> strarray;
9936 						StringSplit(sValue, ";", strarray);
9937 						if (strarray.size() == 2)
9938 						{
9939 							//get lowest value of today, and max rate
9940 							time_t now = mytime(NULL);
9941 							struct tm ltime;
9942 							localtime_r(&now, &ltime);
9943 							char szDate[40];
9944 							sprintf(szDate, "%04d-%02d-%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
9945 
9946 							std::vector<std::vector<std::string> > result2;
9947 
9948 							if (dSubType == sTypeRAINWU || dSubType == sTypeRAINByRate)
9949 							{
9950 								result2 = m_sql.safe_query(
9951 									"SELECT Total, Rate FROM Rain WHERE (DeviceRowID='%q' AND Date>='%q') ORDER BY ROWID DESC LIMIT 1", sd[0].c_str(), szDate);
9952 							}
9953 							else
9954 							{
9955 								result2 = m_sql.safe_query(
9956 									"SELECT MIN(Total), MAX(Total) FROM Rain WHERE (DeviceRowID='%q' AND Date>='%q')", sd[0].c_str(), szDate);
9957 							}
9958 
9959 							if (!result2.empty())
9960 							{
9961 								double total_real = 0;
9962 								float rate = 0;
9963 								std::vector<std::string> sd2 = result2[0];
9964 
9965 								if (dSubType == sTypeRAINWU || dSubType == sTypeRAINByRate)
9966 								{
9967 									total_real = atof(sd2[0].c_str());
9968 								}
9969 								else
9970 								{
9971 									double total_min = atof(sd2[0].c_str());
9972 									double total_max = atof(strarray[1].c_str());
9973 									total_real = total_max - total_min;
9974 								}
9975 
9976 								total_real *= AddjMulti;
9977 								if (dSubType == sTypeRAINByRate)
9978 								{
9979 									rate = static_cast<float>(atof(sd2[1].c_str()) / 10000.0f);
9980 								}
9981 								else
9982 								{
9983 									rate = (static_cast<float>(atof(strarray[0].c_str())) / 100.0f)*float(AddjMulti);
9984 								}
9985 
9986 								sprintf(szTmp, "%.1f", total_real);
9987 								root["result"][ii]["Rain"] = szTmp;
9988 								sprintf(szTmp, "%g", rate);
9989 								root["result"][ii]["RainRate"] = szTmp;
9990 								root["result"][ii]["Data"] = sValue;
9991 								root["result"][ii]["HaveTimeout"] = bHaveTimeout;
9992 							}
9993 							else
9994 							{
9995 								root["result"][ii]["Rain"] = "0";
9996 								root["result"][ii]["RainRate"] = "0";
9997 								root["result"][ii]["Data"] = "0";
9998 								root["result"][ii]["HaveTimeout"] = bHaveTimeout;
9999 							}
10000 						}
10001 					}
10002 					else if (dType == pTypeRFXMeter)
10003 					{
10004 						std::string ValueQuantity = options["ValueQuantity"];
10005 						std::string ValueUnits = options["ValueUnits"];
10006 
10007 						if (ValueQuantity.empty()) {
10008 							ValueQuantity.assign("Count");
10009 						}
10010 						if (ValueUnits.empty()) {
10011 							ValueUnits.assign("");
10012 						}
10013 
10014 						//get value of today
10015 						time_t now = mytime(NULL);
10016 						struct tm ltime;
10017 						localtime_r(&now, &ltime);
10018 						char szDate[40];
10019 						sprintf(szDate, "%04d-%02d-%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
10020 
10021 						std::vector<std::vector<std::string> > result2;
10022 						strcpy(szTmp, "0");
10023 						result2 = m_sql.safe_query("SELECT MIN(Value) FROM Meter WHERE (DeviceRowID='%q' AND Date>='%q')", sd[0].c_str(), szDate);
10024 						if (!result2.empty())
10025 						{
10026 							std::vector<std::string> sd2 = result2[0];
10027 
10028 							uint64_t total_min = std::stoull(sd2[0]);
10029 							uint64_t total_max = std::stoull(sValue);
10030 							uint64_t total_real = total_max - total_min;
10031 							sprintf(szTmp, "%" PRIu64, total_real);
10032 
10033 							float divider = m_sql.GetCounterDivider(int(metertype), int(dType), float(AddjValue2));
10034 							float musage = 0.0f;
10035 							switch (metertype)
10036 							{
10037 							case MTYPE_ENERGY:
10038 							case MTYPE_ENERGY_GENERATED:
10039 								musage = float(total_real) / divider;
10040 								sprintf(szTmp, "%.3f kWh", musage);
10041 								break;
10042 							case MTYPE_GAS:
10043 								musage = float(total_real) / divider;
10044 								sprintf(szTmp, "%.3f m3", musage);
10045 								break;
10046 							case MTYPE_WATER:
10047 								musage = float(total_real) / (divider / 1000.0f);
10048 								sprintf(szTmp, "%d Liter", round(musage));
10049 								break;
10050 							case MTYPE_COUNTER:
10051 								sprintf(szTmp, "%" PRIu64, total_real);
10052 								if (!ValueUnits.empty())
10053 								{
10054 									strcat(szTmp, " ");
10055 									strcat(szTmp, ValueUnits.c_str());
10056 								}
10057 								break;
10058 							default:
10059 								strcpy(szTmp, "?");
10060 								break;
10061 							}
10062 						}
10063 						root["result"][ii]["CounterToday"] = szTmp;
10064 
10065 						root["result"][ii]["SwitchTypeVal"] = metertype;
10066 						root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10067 						root["result"][ii]["ValueQuantity"] = "";
10068 						root["result"][ii]["ValueUnits"] = "";
10069 
10070 						double meteroffset = AddjValue;
10071 						float divider = m_sql.GetCounterDivider(int(metertype), int(dType), float(AddjValue2));
10072 
10073 						double dvalue = static_cast<double>(atof(sValue.c_str()));
10074 
10075 						switch (metertype)
10076 						{
10077 						case MTYPE_ENERGY:
10078 						case MTYPE_ENERGY_GENERATED:
10079 							sprintf(szTmp, "%.3f kWh", meteroffset + (dvalue / divider));
10080 							root["result"][ii]["Data"] = szTmp;
10081 							root["result"][ii]["Counter"] = szTmp;
10082 							break;
10083 						case MTYPE_GAS:
10084 							sprintf(szTmp, "%.3f m3", meteroffset + (dvalue / divider));
10085 							root["result"][ii]["Data"] = szTmp;
10086 							root["result"][ii]["Counter"] = szTmp;
10087 							break;
10088 						case MTYPE_WATER:
10089 							sprintf(szTmp, "%.3f m3", meteroffset + (dvalue / divider));
10090 							root["result"][ii]["Data"] = szTmp;
10091 							root["result"][ii]["Counter"] = szTmp;
10092 							break;
10093 						case MTYPE_COUNTER:
10094 							sprintf(szTmp, "%g %s", meteroffset + dvalue, ValueUnits.c_str());
10095 							root["result"][ii]["Data"] = szTmp;
10096 							root["result"][ii]["Counter"] = szTmp;
10097 							root["result"][ii]["ValueQuantity"] = ValueQuantity;
10098 							root["result"][ii]["ValueUnits"] = ValueUnits;
10099 							break;
10100 						default:
10101 							root["result"][ii]["Data"] = "?";
10102 							root["result"][ii]["Counter"] = "?";
10103 							root["result"][ii]["ValueQuantity"] = ValueQuantity;
10104 							root["result"][ii]["ValueUnits"] = ValueUnits;
10105 							break;
10106 						}
10107 					}
10108 					else if ((dType == pTypeGeneral) && (dSubType == sTypeCounterIncremental))
10109 					{
10110 						std::string ValueQuantity = options["ValueQuantity"];
10111 						std::string ValueUnits = options["ValueUnits"];
10112 						if (ValueQuantity.empty()) {
10113 							ValueQuantity.assign("Count");
10114 						}
10115 						if (ValueUnits.empty()) {
10116 							ValueUnits.assign("");
10117 						}
10118 						float divider = m_sql.GetCounterDivider(int(metertype), int(dType), float(AddjValue2));
10119 
10120 						//get value of today
10121 						time_t now = mytime(NULL);
10122 						struct tm ltime;
10123 						localtime_r(&now, &ltime);
10124 						char szDate[40];
10125 						sprintf(szDate, "%04d-%02d-%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
10126 
10127 						std::vector<std::vector<std::string> > result2;
10128 						strcpy(szTmp, "0");
10129 						result2 = m_sql.safe_query("SELECT MIN(Value) FROM Meter WHERE (DeviceRowID='%q' AND Date>='%q')", sd[0].c_str(), szDate);
10130 						if (!result2.empty())
10131 						{
10132 							std::vector<std::string> sd2 = result2[0];
10133 
10134 							uint64_t total_min = std::stoull(sd2[0]);
10135 							uint64_t total_max = std::stoull(sValue);
10136 							uint64_t total_real = total_max - total_min;
10137 							sprintf(szTmp, "%" PRIu64, total_real);
10138 
10139 							float musage = 0;
10140 							switch (metertype)
10141 							{
10142 							case MTYPE_ENERGY:
10143 							case MTYPE_ENERGY_GENERATED:
10144 								musage = float(total_real) / divider;
10145 								sprintf(szTmp, "%.3f kWh", musage);
10146 								break;
10147 							case MTYPE_GAS:
10148 								musage = float(total_real) / divider;
10149 								sprintf(szTmp, "%.3f m3", musage);
10150 								break;
10151 							case MTYPE_WATER:
10152 								musage = float(total_real) / divider;
10153 								sprintf(szTmp, "%.3f m3", musage);
10154 								break;
10155 							case MTYPE_COUNTER:
10156 								sprintf(szTmp, "%" PRIu64, total_real);
10157 								if (!ValueUnits.empty())
10158 								{
10159 									strcat(szTmp, " ");
10160 									strcat(szTmp, ValueUnits.c_str());
10161 								}
10162 								break;
10163 							default:
10164 								strcpy(szTmp, "0");
10165 								break;
10166 							}
10167 						}
10168 						root["result"][ii]["Counter"] = sValue;
10169 						root["result"][ii]["CounterToday"] = szTmp;
10170 						root["result"][ii]["SwitchTypeVal"] = metertype;
10171 						root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10172 						root["result"][ii]["TypeImg"] = "counter";
10173 						root["result"][ii]["ValueQuantity"] = "";
10174 						root["result"][ii]["ValueUnits"] = "";
10175 						double dvalue = static_cast<double>(atof(sValue.c_str()));
10176 						double meteroffset = AddjValue;
10177 
10178 						switch (metertype)
10179 						{
10180 						case MTYPE_ENERGY:
10181 						case MTYPE_ENERGY_GENERATED:
10182 							sprintf(szTmp, "%.3f kWh", meteroffset + (dvalue / divider));
10183 							root["result"][ii]["Data"] = szTmp;
10184 							root["result"][ii]["Counter"] = szTmp;
10185 							break;
10186 						case MTYPE_GAS:
10187 							sprintf(szTmp, "%.3f m3", meteroffset + (dvalue / divider));
10188 							root["result"][ii]["Data"] = szTmp;
10189 							root["result"][ii]["Counter"] = szTmp;
10190 							break;
10191 						case MTYPE_WATER:
10192 							sprintf(szTmp, "%.3f m3", meteroffset + (dvalue / divider));
10193 							root["result"][ii]["Data"] = szTmp;
10194 							root["result"][ii]["Counter"] = szTmp;
10195 							break;
10196 						case MTYPE_COUNTER:
10197 							sprintf(szTmp, "%" PRIu64 " %s", static_cast<uint64_t>(meteroffset + dvalue), ValueUnits.c_str());
10198 							root["result"][ii]["Data"] = szTmp;
10199 							root["result"][ii]["Counter"] = szTmp;
10200 							root["result"][ii]["ValueQuantity"] = ValueQuantity;
10201 							root["result"][ii]["ValueUnits"] = ValueUnits;
10202 							break;
10203 						default:
10204 							root["result"][ii]["Data"] = "?";
10205 							root["result"][ii]["Counter"] = "?";
10206 							root["result"][ii]["ValueQuantity"] = ValueQuantity;
10207 							root["result"][ii]["ValueUnits"] = ValueUnits;
10208 							break;
10209 						}
10210 					}
10211 					else if ((dType == pTypeGeneral) && (dSubType == sTypeManagedCounter))
10212 					{
10213 						std::string ValueQuantity = options["ValueQuantity"];
10214 						std::string ValueUnits = options["ValueUnits"];
10215 						if (ValueQuantity.empty()) {
10216 							ValueQuantity.assign("Count");
10217 						}
10218 						if (ValueUnits.empty()) {
10219 							ValueUnits.assign("");
10220 						}
10221 						float divider = m_sql.GetCounterDivider(int(metertype), int(dType), float(AddjValue2));
10222 
10223 						std::vector<std::string> splitresults;
10224 						StringSplit(sValue, ";", splitresults);
10225 						double dvalue;
10226 						if (splitresults.size() < 2) {
10227 							dvalue = static_cast<double>(atof(sValue.c_str()));
10228 						}
10229 						else {
10230 							dvalue = static_cast<double>(atof(splitresults[1].c_str()));
10231 							if (dvalue < 0.0) {
10232 								dvalue = static_cast<double>(atof(splitresults[0].c_str()));
10233 							}
10234 						}
10235 						root["result"][ii]["Data"] = root["result"][ii]["Counter"];
10236 
10237 						root["result"][ii]["SwitchTypeVal"] = metertype;
10238 						root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10239 						root["result"][ii]["TypeImg"] = "counter";
10240 						root["result"][ii]["ValueQuantity"] = "";
10241 						root["result"][ii]["ValueUnits"] = "";
10242 						root["result"][ii]["ShowNotifications"] = false;
10243 						double meteroffset = AddjValue;
10244 
10245 						switch (metertype)
10246 						{
10247 						case MTYPE_ENERGY:
10248 						case MTYPE_ENERGY_GENERATED:
10249 							sprintf(szTmp, "%.3f kWh", meteroffset + (dvalue / divider));
10250 							root["result"][ii]["Data"] = szTmp;
10251 							root["result"][ii]["Counter"] = szTmp;
10252 							break;
10253 						case MTYPE_GAS:
10254 							sprintf(szTmp, "%.3f m3", meteroffset + (dvalue / divider));
10255 							root["result"][ii]["Data"] = szTmp;
10256 							root["result"][ii]["Counter"] = szTmp;
10257 							break;
10258 						case MTYPE_WATER:
10259 							sprintf(szTmp, "%.3f m3", meteroffset + (dvalue / divider));
10260 							root["result"][ii]["Data"] = szTmp;
10261 							root["result"][ii]["Counter"] = szTmp;
10262 							break;
10263 						case MTYPE_COUNTER:
10264 							sprintf(szTmp, "%g %s", meteroffset + dvalue, ValueUnits.c_str());
10265 							root["result"][ii]["Data"] = szTmp;
10266 							root["result"][ii]["Counter"] = szTmp;
10267 							root["result"][ii]["ValueQuantity"] = ValueQuantity;
10268 							root["result"][ii]["ValueUnits"] = ValueUnits;
10269 							break;
10270 						default:
10271 							root["result"][ii]["Data"] = "?";
10272 							root["result"][ii]["Counter"] = "?";
10273 							root["result"][ii]["ValueQuantity"] = ValueQuantity;
10274 							root["result"][ii]["ValueUnits"] = ValueUnits;
10275 							break;
10276 						}
10277 					}
10278 					else if (dType == pTypeYouLess)
10279 					{
10280 						std::string ValueQuantity = options["ValueQuantity"];
10281 						std::string ValueUnits = options["ValueUnits"];
10282 						float musage = 0;
10283 						if (ValueQuantity.empty()) {
10284 							ValueQuantity.assign("Count");
10285 						}
10286 						if (ValueUnits.empty()) {
10287 							ValueUnits.assign("");
10288 						}
10289 						float divider = m_sql.GetCounterDivider(int(metertype), int(dType), float(AddjValue2));
10290 
10291 						//get value of today
10292 						time_t now = mytime(NULL);
10293 						struct tm ltime;
10294 						localtime_r(&now, &ltime);
10295 						char szDate[40];
10296 						sprintf(szDate, "%04d-%02d-%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
10297 
10298 						std::vector<std::vector<std::string> > result2;
10299 						strcpy(szTmp, "0");
10300 						result2 = m_sql.safe_query("SELECT MIN(Value), MAX(Value) FROM Meter WHERE (DeviceRowID='%q' AND Date>='%q')", sd[0].c_str(), szDate);
10301 						if (!result2.empty())
10302 						{
10303 							std::vector<std::string> sd2 = result2[0];
10304 
10305 							unsigned long long total_min = std::strtoull(sd2[0].c_str(), nullptr, 10);
10306 							unsigned long long total_max = std::strtoull(sd2[1].c_str(), nullptr, 10);
10307 							unsigned long long total_real;
10308 
10309 							total_real = total_max - total_min;
10310 							sprintf(szTmp, "%llu", total_real);
10311 
10312 							musage = 0;
10313 							switch (metertype)
10314 							{
10315 							case MTYPE_ENERGY:
10316 							case MTYPE_ENERGY_GENERATED:
10317 								musage = float(total_real) / divider;
10318 								sprintf(szTmp, "%.3f kWh", musage);
10319 								break;
10320 							case MTYPE_GAS:
10321 								musage = float(total_real) / divider;
10322 								sprintf(szTmp, "%.3f m3", musage);
10323 								break;
10324 							case MTYPE_WATER:
10325 								musage = float(total_real) / divider;
10326 								sprintf(szTmp, "%.3f m3", musage);
10327 								break;
10328 							case MTYPE_COUNTER:
10329 								sprintf(szTmp, "%llu %s", total_real, ValueUnits.c_str());
10330 								break;
10331 							default:
10332 								strcpy(szTmp, "0");
10333 								break;
10334 							}
10335 						}
10336 						root["result"][ii]["CounterToday"] = szTmp;
10337 
10338 
10339 						std::vector<std::string> splitresults;
10340 						StringSplit(sValue, ";", splitresults);
10341 						if (splitresults.size() < 2)
10342 							continue;
10343 
10344 						unsigned long long total_actual = std::strtoull(splitresults[0].c_str(), nullptr, 10);
10345 						musage = 0;
10346 						switch (metertype)
10347 						{
10348 						case MTYPE_ENERGY:
10349 						case MTYPE_ENERGY_GENERATED:
10350 							musage = float(total_actual) / divider;
10351 							sprintf(szTmp, "%.03f", musage);
10352 							break;
10353 						case MTYPE_GAS:
10354 						case MTYPE_WATER:
10355 							musage = float(total_actual) / divider;
10356 							sprintf(szTmp, "%.03f", musage);
10357 							break;
10358 						case MTYPE_COUNTER:
10359 							sprintf(szTmp, "%llu", total_actual);
10360 							break;
10361 						default:
10362 							strcpy(szTmp, "0");
10363 							break;
10364 						}
10365 						root["result"][ii]["Counter"] = szTmp;
10366 
10367 						root["result"][ii]["SwitchTypeVal"] = metertype;
10368 
10369 						unsigned long long acounter = std::strtoull(sValue.c_str(), nullptr, 10);
10370 						musage = 0;
10371 						switch (metertype)
10372 						{
10373 						case MTYPE_ENERGY:
10374 						case MTYPE_ENERGY_GENERATED:
10375 							musage = float(acounter) / divider;
10376 							sprintf(szTmp, "%.3f kWh %s Watt", musage, splitresults[1].c_str());
10377 							break;
10378 						case MTYPE_GAS:
10379 							musage = float(acounter) / divider;
10380 							sprintf(szTmp, "%.3f m3", musage);
10381 							break;
10382 						case MTYPE_WATER:
10383 							musage = float(acounter) / divider;
10384 							sprintf(szTmp, "%.3f m3", musage);
10385 							break;
10386 						case MTYPE_COUNTER:
10387 							sprintf(szTmp, "%llu %s", acounter, ValueUnits.c_str());
10388 							break;
10389 						default:
10390 							strcpy(szTmp, "0");
10391 							break;
10392 						}
10393 						root["result"][ii]["Data"] = szTmp;
10394 						root["result"][ii]["ValueQuantity"] = "";
10395 						root["result"][ii]["ValueUnits"] = "";
10396 						switch (metertype)
10397 						{
10398 						case MTYPE_ENERGY:
10399 						case MTYPE_ENERGY_GENERATED:
10400 							sprintf(szTmp, "%s Watt", splitresults[1].c_str());
10401 							break;
10402 						case MTYPE_GAS:
10403 							sprintf(szTmp, "%s m3", splitresults[1].c_str());
10404 							break;
10405 						case MTYPE_WATER:
10406 							sprintf(szTmp, "%s m3", splitresults[1].c_str());
10407 							break;
10408 						case MTYPE_COUNTER:
10409 							sprintf(szTmp, "%s", splitresults[1].c_str());
10410 							root["result"][ii]["ValueQuantity"] = ValueQuantity;
10411 							root["result"][ii]["ValueUnits"] = ValueUnits;
10412 							break;
10413 						default:
10414 							strcpy(szTmp, "0");
10415 							break;
10416 						}
10417 
10418 						root["result"][ii]["Usage"] = szTmp;
10419 						root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10420 					}
10421 					else if (dType == pTypeP1Power)
10422 					{
10423 						std::vector<std::string> splitresults;
10424 						StringSplit(sValue, ";", splitresults);
10425 						if (splitresults.size() != 6)
10426 						{
10427 							root["result"][ii]["SwitchTypeVal"] = MTYPE_ENERGY;
10428 							root["result"][ii]["Counter"] = "0";
10429 							root["result"][ii]["CounterDeliv"] = "0";
10430 							root["result"][ii]["Usage"] = "Invalid";
10431 							root["result"][ii]["UsageDeliv"] = "Invalid";
10432 							root["result"][ii]["Data"] = "Invalid!: " + sValue;
10433 							root["result"][ii]["HaveTimeout"] = true;
10434 							root["result"][ii]["CounterToday"] = "Invalid";
10435 							root["result"][ii]["CounterDelivToday"] = "Invalid";
10436 						}
10437 						else
10438 						{
10439 							float EnergyDivider = 1000.0f;
10440 							int tValue;
10441 							if (m_sql.GetPreferencesVar("MeterDividerEnergy", tValue))
10442 							{
10443 								EnergyDivider = float(tValue);
10444 							}
10445 
10446 							unsigned long long powerusage1 = std::strtoull(splitresults[0].c_str(), nullptr, 10);
10447 							unsigned long long powerusage2 = std::strtoull(splitresults[1].c_str(), nullptr, 10);
10448 							unsigned long long powerdeliv1 = std::strtoull(splitresults[2].c_str(), nullptr, 10);
10449 							unsigned long long powerdeliv2 = std::strtoull(splitresults[3].c_str(), nullptr, 10);
10450 							unsigned long long usagecurrent = std::strtoull(splitresults[4].c_str(), nullptr, 10);
10451 							unsigned long long delivcurrent = std::strtoull(splitresults[5].c_str(), nullptr, 10);
10452 
10453 							powerdeliv1 = (powerdeliv1 < 10) ? 0 : powerdeliv1;
10454 							powerdeliv2 = (powerdeliv2 < 10) ? 0 : powerdeliv2;
10455 
10456 							unsigned long long powerusage = powerusage1 + powerusage2;
10457 							unsigned long long powerdeliv = powerdeliv1 + powerdeliv2;
10458 							if (powerdeliv < 2)
10459 								powerdeliv = 0;
10460 
10461 							double musage = 0;
10462 
10463 							root["result"][ii]["SwitchTypeVal"] = MTYPE_ENERGY;
10464 							musage = double(powerusage) / EnergyDivider;
10465 							sprintf(szTmp, "%.03f", musage);
10466 							root["result"][ii]["Counter"] = szTmp;
10467 							musage = double(powerdeliv) / EnergyDivider;
10468 							sprintf(szTmp, "%.03f", musage);
10469 							root["result"][ii]["CounterDeliv"] = szTmp;
10470 
10471 							if (bHaveTimeout)
10472 							{
10473 								usagecurrent = 0;
10474 								delivcurrent = 0;
10475 							}
10476 							sprintf(szTmp, "%llu Watt", usagecurrent);
10477 							root["result"][ii]["Usage"] = szTmp;
10478 							sprintf(szTmp, "%llu Watt", delivcurrent);
10479 							root["result"][ii]["UsageDeliv"] = szTmp;
10480 							root["result"][ii]["Data"] = sValue;
10481 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10482 
10483 							//get value of today
10484 							time_t now = mytime(NULL);
10485 							struct tm ltime;
10486 							localtime_r(&now, &ltime);
10487 							char szDate[40];
10488 							sprintf(szDate, "%04d-%02d-%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
10489 
10490 							std::vector<std::vector<std::string> > result2;
10491 							strcpy(szTmp, "0");
10492 							result2 = m_sql.safe_query("SELECT MIN(Value1), MIN(Value2), MIN(Value5), MIN(Value6) FROM MultiMeter WHERE (DeviceRowID='%q' AND Date>='%q')",
10493 								sd[0].c_str(), szDate);
10494 							if (!result2.empty())
10495 							{
10496 								std::vector<std::string> sd2 = result2[0];
10497 
10498 								unsigned long long total_min_usage_1 = std::strtoull(sd2[0].c_str(), nullptr, 10);
10499 								unsigned long long total_min_deliv_1 = std::strtoull(sd2[1].c_str(), nullptr, 10);
10500 								unsigned long long total_min_usage_2 = std::strtoull(sd2[2].c_str(), nullptr, 10);
10501 								unsigned long long total_min_deliv_2 = std::strtoull(sd2[3].c_str(), nullptr, 10);
10502 								unsigned long long total_real_usage, total_real_deliv;
10503 
10504 								total_real_usage = powerusage - (total_min_usage_1 + total_min_usage_2);
10505 								total_real_deliv = powerdeliv - (total_min_deliv_1 + total_min_deliv_2);
10506 
10507 								musage = double(total_real_usage) / EnergyDivider;
10508 								sprintf(szTmp, "%.3f kWh", musage);
10509 								root["result"][ii]["CounterToday"] = szTmp;
10510 								musage = double(total_real_deliv) / EnergyDivider;
10511 								sprintf(szTmp, "%.3f kWh", musage);
10512 								root["result"][ii]["CounterDelivToday"] = szTmp;
10513 							}
10514 							else
10515 							{
10516 								sprintf(szTmp, "%.3f kWh", 0.0f);
10517 								root["result"][ii]["CounterToday"] = szTmp;
10518 								root["result"][ii]["CounterDelivToday"] = szTmp;
10519 							}
10520 						}
10521 					}
10522 					else if (dType == pTypeP1Gas)
10523 					{
10524 						root["result"][ii]["SwitchTypeVal"] = MTYPE_GAS;
10525 
10526 						//get lowest value of today
10527 						time_t now = mytime(NULL);
10528 						struct tm ltime;
10529 						localtime_r(&now, &ltime);
10530 						char szDate[40];
10531 						sprintf(szDate, "%04d-%02d-%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
10532 
10533 						std::vector<std::vector<std::string> > result2;
10534 
10535 						float divider = m_sql.GetCounterDivider(int(metertype), int(dType), float(AddjValue2));
10536 
10537 						strcpy(szTmp, "0");
10538 						result2 = m_sql.safe_query("SELECT MIN(Value) FROM Meter WHERE (DeviceRowID='%q' AND Date>='%q')",
10539 							sd[0].c_str(), szDate);
10540 						if (!result2.empty())
10541 						{
10542 							std::vector<std::string> sd2 = result2[0];
10543 
10544 							uint64_t total_min_gas = std::stoull(sd2[0]);
10545 							uint64_t gasactual = std::stoull(sValue);
10546 							uint64_t total_real_gas = gasactual - total_min_gas;
10547 
10548 							double musage = double(gasactual) / divider;
10549 							sprintf(szTmp, "%.03f", musage);
10550 							root["result"][ii]["Counter"] = szTmp;
10551 							musage = double(total_real_gas) / divider;
10552 							sprintf(szTmp, "%.03f m3", musage);
10553 							root["result"][ii]["CounterToday"] = szTmp;
10554 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10555 							sprintf(szTmp, "%.03f", atof(sValue.c_str()) / divider);
10556 							root["result"][ii]["Data"] = szTmp;
10557 						}
10558 						else
10559 						{
10560 							sprintf(szTmp, "%.03f", 0.0f);
10561 							root["result"][ii]["Counter"] = szTmp;
10562 							sprintf(szTmp, "%.03f m3", 0.0f);
10563 							root["result"][ii]["CounterToday"] = szTmp;
10564 							sprintf(szTmp, "%.03f", atof(sValue.c_str()) / divider);
10565 							root["result"][ii]["Data"] = szTmp;
10566 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10567 						}
10568 					}
10569 					else if (dType == pTypeCURRENT)
10570 					{
10571 						std::vector<std::string> strarray;
10572 						StringSplit(sValue, ";", strarray);
10573 						if (strarray.size() == 3)
10574 						{
10575 							//CM113
10576 							int displaytype = 0;
10577 							int voltage = 230;
10578 							m_sql.GetPreferencesVar("CM113DisplayType", displaytype);
10579 							m_sql.GetPreferencesVar("ElectricVoltage", voltage);
10580 
10581 							double val1 = atof(strarray[0].c_str());
10582 							double val2 = atof(strarray[1].c_str());
10583 							double val3 = atof(strarray[2].c_str());
10584 
10585 							if (displaytype == 0)
10586 							{
10587 								if ((val2 == 0) && (val3 == 0))
10588 									sprintf(szData, "%.1f A", val1);
10589 								else
10590 									sprintf(szData, "%.1f A, %.1f A, %.1f A", val1, val2, val3);
10591 							}
10592 							else
10593 							{
10594 								if ((val2 == 0) && (val3 == 0))
10595 									sprintf(szData, "%d Watt", int(val1*voltage));
10596 								else
10597 									sprintf(szData, "%d Watt, %d Watt, %d Watt", int(val1*voltage), int(val2*voltage), int(val3*voltage));
10598 							}
10599 							root["result"][ii]["Data"] = szData;
10600 							root["result"][ii]["displaytype"] = displaytype;
10601 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10602 						}
10603 					}
10604 					else if (dType == pTypeCURRENTENERGY)
10605 					{
10606 						std::vector<std::string> strarray;
10607 						StringSplit(sValue, ";", strarray);
10608 						if (strarray.size() == 4)
10609 						{
10610 							//CM180i
10611 							int displaytype = 0;
10612 							int voltage = 230;
10613 							m_sql.GetPreferencesVar("CM113DisplayType", displaytype);
10614 							m_sql.GetPreferencesVar("ElectricVoltage", voltage);
10615 
10616 							double total = atof(strarray[3].c_str());
10617 							if (displaytype == 0)
10618 							{
10619 								sprintf(szData, "%.1f A, %.1f A, %.1f A", atof(strarray[0].c_str()), atof(strarray[1].c_str()), atof(strarray[2].c_str()));
10620 							}
10621 							else
10622 							{
10623 								sprintf(szData, "%d Watt, %d Watt, %d Watt", int(atof(strarray[0].c_str())*voltage), int(atof(strarray[1].c_str())*voltage), int(atof(strarray[2].c_str())*voltage));
10624 							}
10625 							if (total > 0)
10626 							{
10627 								sprintf(szTmp, ", Total: %.3f kWh", total / 1000.0f);
10628 								strcat(szData, szTmp);
10629 							}
10630 							root["result"][ii]["Data"] = szData;
10631 							root["result"][ii]["displaytype"] = displaytype;
10632 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10633 						}
10634 					}
10635 					else if (
10636 						((dType == pTypeENERGY) || (dType == pTypePOWER)) ||
10637 						((dType == pTypeGeneral) && (dSubType == sTypeKwh))
10638 						)
10639 					{
10640 						std::vector<std::string> strarray;
10641 						StringSplit(sValue, ";", strarray);
10642 						if (strarray.size() == 2)
10643 						{
10644 							double total = atof(strarray[1].c_str()) / 1000;
10645 
10646 							time_t now = mytime(NULL);
10647 							struct tm ltime;
10648 							localtime_r(&now, &ltime);
10649 							char szDate[40];
10650 							sprintf(szDate, "%04d-%02d-%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
10651 
10652 							std::vector<std::vector<std::string> > result2;
10653 							strcpy(szTmp, "0");
10654 							result2 = m_sql.safe_query("SELECT MIN(Value) FROM Meter WHERE (DeviceRowID='%q' AND Date>='%q')",
10655 								sd[0].c_str(), szDate);
10656 							if (!result2.empty())
10657 							{
10658 								float divider = m_sql.GetCounterDivider(int(metertype), int(dType), float(AddjValue2));
10659 
10660 								std::vector<std::string> sd2 = result2[0];
10661 								double minimum = atof(sd2[0].c_str()) / divider;
10662 
10663 								sprintf(szData, "%.3f kWh", total);
10664 								root["result"][ii]["Data"] = szData;
10665 								if ((dType == pTypeENERGY) || (dType == pTypePOWER))
10666 								{
10667 									sprintf(szData, "%ld Watt", atol(strarray[0].c_str()));
10668 								}
10669 								else
10670 								{
10671 									sprintf(szData, "%g Watt", atof(strarray[0].c_str()));
10672 								}
10673 								root["result"][ii]["Usage"] = szData;
10674 								root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10675 								sprintf(szTmp, "%.3f kWh", total - minimum);
10676 								root["result"][ii]["CounterToday"] = szTmp;
10677 							}
10678 							else
10679 							{
10680 								sprintf(szData, "%.3f kWh", total);
10681 								root["result"][ii]["Data"] = szData;
10682 								if ((dType == pTypeENERGY) || (dType == pTypePOWER))
10683 								{
10684 									sprintf(szData, "%ld Watt", atol(strarray[0].c_str()));
10685 								}
10686 								else
10687 								{
10688 									sprintf(szData, "%g Watt", atof(strarray[0].c_str()));
10689 								}
10690 								root["result"][ii]["Usage"] = szData;
10691 								root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10692 								sprintf(szTmp, "%d kWh", 0);
10693 								root["result"][ii]["CounterToday"] = szTmp;
10694 							}
10695 							root["result"][ii]["TypeImg"] = "current";
10696 							root["result"][ii]["SwitchTypeVal"] = switchtype; //MTYPE_ENERGY
10697 							root["result"][ii]["EnergyMeterMode"] = options["EnergyMeterMode"];  //for alternate Energy Reading
10698 						}
10699 					}
10700 					else if (dType == pTypeAirQuality)
10701 					{
10702 						if (bHaveTimeout)
10703 							nValue = 0;
10704 						sprintf(szTmp, "%d ppm", nValue);
10705 						root["result"][ii]["Data"] = szTmp;
10706 						root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10707 						int airquality = nValue;
10708 						if (airquality < 700)
10709 							root["result"][ii]["Quality"] = "Excellent";
10710 						else if (airquality < 900)
10711 							root["result"][ii]["Quality"] = "Good";
10712 						else if (airquality < 1100)
10713 							root["result"][ii]["Quality"] = "Fair";
10714 						else if (airquality < 1600)
10715 							root["result"][ii]["Quality"] = "Mediocre";
10716 						else
10717 							root["result"][ii]["Quality"] = "Bad";
10718 					}
10719 					else if (dType == pTypeThermostat)
10720 					{
10721 						if (dSubType == sTypeThermSetpoint)
10722 						{
10723 							bHasTimers = m_sql.HasTimers(sd[0]);
10724 
10725 							double tempCelcius = atof(sValue.c_str());
10726 							double temp = ConvertTemperature(tempCelcius, tempsign);
10727 
10728 							sprintf(szTmp, "%.1f", temp);
10729 							root["result"][ii]["Data"] = szTmp;
10730 							root["result"][ii]["SetPoint"] = szTmp;
10731 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10732 							root["result"][ii]["TypeImg"] = "override_mini";
10733 						}
10734 					}
10735 					else if (dType == pTypeRadiator1)
10736 					{
10737 						if (dSubType == sTypeSmartwares)
10738 						{
10739 							bHasTimers = m_sql.HasTimers(sd[0]);
10740 
10741 							double tempCelcius = atof(sValue.c_str());
10742 							double temp = ConvertTemperature(tempCelcius, tempsign);
10743 
10744 							sprintf(szTmp, "%.1f", temp);
10745 							root["result"][ii]["Data"] = szTmp;
10746 							root["result"][ii]["SetPoint"] = szTmp;
10747 							root["result"][ii]["HaveTimeout"] = false; //this device does not provide feedback, so no timeout!
10748 							root["result"][ii]["TypeImg"] = "override_mini";
10749 						}
10750 					}
10751 					else if (dType == pTypeGeneral)
10752 					{
10753 						if (dSubType == sTypeVisibility)
10754 						{
10755 							float vis = static_cast<float>(atof(sValue.c_str()));
10756 							if (metertype == 0)
10757 							{
10758 								//km
10759 								sprintf(szTmp, "%.1f km", vis);
10760 							}
10761 							else
10762 							{
10763 								//miles
10764 								sprintf(szTmp, "%.1f mi", vis*0.6214f);
10765 							}
10766 							root["result"][ii]["Data"] = szTmp;
10767 							root["result"][ii]["Visibility"] = atof(sValue.c_str());
10768 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10769 							root["result"][ii]["TypeImg"] = "visibility";
10770 							root["result"][ii]["SwitchTypeVal"] = metertype;
10771 						}
10772 						else if (dSubType == sTypeDistance)
10773 						{
10774 							float vis = static_cast<float>(atof(sValue.c_str()));
10775 							if (metertype == 0)
10776 							{
10777 								//Metric
10778 								sprintf(szTmp, "%.1f cm", vis);
10779 							}
10780 							else
10781 							{
10782 								//Imperial
10783 								sprintf(szTmp, "%.1f in", vis*0.6214f);
10784 							}
10785 							root["result"][ii]["Data"] = szTmp;
10786 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10787 							root["result"][ii]["TypeImg"] = "visibility";
10788 							root["result"][ii]["SwitchTypeVal"] = metertype;
10789 						}
10790 						else if (dSubType == sTypeSolarRadiation)
10791 						{
10792 							float radiation = static_cast<float>(atof(sValue.c_str()));
10793 							sprintf(szTmp, "%.1f Watt/m2", radiation);
10794 							root["result"][ii]["Data"] = szTmp;
10795 							root["result"][ii]["Radiation"] = atof(sValue.c_str());
10796 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10797 							root["result"][ii]["TypeImg"] = "radiation";
10798 							root["result"][ii]["SwitchTypeVal"] = metertype;
10799 						}
10800 						else if (dSubType == sTypeSoilMoisture)
10801 						{
10802 							sprintf(szTmp, "%d cb", nValue);
10803 							root["result"][ii]["Data"] = szTmp;
10804 							root["result"][ii]["Desc"] = Get_Moisture_Desc(nValue);
10805 							root["result"][ii]["TypeImg"] = "moisture";
10806 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10807 							root["result"][ii]["SwitchTypeVal"] = metertype;
10808 						}
10809 						else if (dSubType == sTypeLeafWetness)
10810 						{
10811 							sprintf(szTmp, "%d", nValue);
10812 							root["result"][ii]["Data"] = szTmp;
10813 							root["result"][ii]["TypeImg"] = "leaf";
10814 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10815 							root["result"][ii]["SwitchTypeVal"] = metertype;
10816 						}
10817 						else if (dSubType == sTypeSystemTemp)
10818 						{
10819 							double tvalue = ConvertTemperature(atof(sValue.c_str()), tempsign);
10820 							root["result"][ii]["Temp"] = tvalue;
10821 							sprintf(szData, "%.1f %c", tvalue, tempsign);
10822 							root["result"][ii]["Data"] = szData;
10823 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10824 							root["result"][ii]["Image"] = "Computer";
10825 							root["result"][ii]["TypeImg"] = "temperature";
10826 							root["result"][ii]["Type"] = "temperature";
10827 							_tTrendCalculator::_eTendencyType tstate = _tTrendCalculator::_eTendencyType::TENDENCY_UNKNOWN;
10828 							uint64_t tID = ((uint64_t)(hardwareID & 0x7FFFFFFF) << 32) | (devIdx & 0x7FFFFFFF);
10829 							if (m_mainworker.m_trend_calculator.find(tID) != m_mainworker.m_trend_calculator.end())
10830 							{
10831 								tstate = m_mainworker.m_trend_calculator[tID].m_state;
10832 							}
10833 							root["result"][ii]["trend"] = (int)tstate;
10834 						}
10835 						else if (dSubType == sTypePercentage)
10836 						{
10837 							sprintf(szData, "%g%%", atof(sValue.c_str()));
10838 							root["result"][ii]["Data"] = szData;
10839 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10840 							root["result"][ii]["Image"] = "Computer";
10841 							root["result"][ii]["TypeImg"] = "hardware";
10842 						}
10843 						else if (dSubType == sTypeWaterflow)
10844 						{
10845 							sprintf(szData, "%g l/min", atof(sValue.c_str()));
10846 							root["result"][ii]["Data"] = szData;
10847 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10848 							root["result"][ii]["Image"] = "Moisture";
10849 							root["result"][ii]["TypeImg"] = "moisture";
10850 						}
10851 						else if (dSubType == sTypeCustom)
10852 						{
10853 							std::string szAxesLabel = "";
10854 							int SensorType = 1;
10855 							std::vector<std::string> sResults;
10856 							StringSplit(sOptions, ";", sResults);
10857 
10858 							if (sResults.size() == 2)
10859 							{
10860 								SensorType = atoi(sResults[0].c_str());
10861 								szAxesLabel = sResults[1];
10862 							}
10863 							sprintf(szData, "%g %s", atof(sValue.c_str()), szAxesLabel.c_str());
10864 							root["result"][ii]["Data"] = szData;
10865 							root["result"][ii]["SensorType"] = SensorType;
10866 							root["result"][ii]["SensorUnit"] = szAxesLabel;
10867 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10868 
10869 							std::string IconFile = "Custom";
10870 							if (CustomImage != 0)
10871 							{
10872 								std::map<int, int>::const_iterator ittIcon = m_custom_light_icons_lookup.find(CustomImage);
10873 								if (ittIcon != m_custom_light_icons_lookup.end())
10874 								{
10875 									IconFile = m_custom_light_icons[ittIcon->second].RootFile;
10876 								}
10877 							}
10878 							root["result"][ii]["Image"] = IconFile;
10879 							root["result"][ii]["TypeImg"] = IconFile;
10880 						}
10881 						else if (dSubType == sTypeFan)
10882 						{
10883 							sprintf(szData, "%d RPM", atoi(sValue.c_str()));
10884 							root["result"][ii]["Data"] = szData;
10885 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10886 							root["result"][ii]["Image"] = "Fan";
10887 							root["result"][ii]["TypeImg"] = "Fan";
10888 						}
10889 						else if (dSubType == sTypeSoundLevel)
10890 						{
10891 							sprintf(szData, "%d dB", atoi(sValue.c_str()));
10892 							root["result"][ii]["Data"] = szData;
10893 							root["result"][ii]["TypeImg"] = "Speaker";
10894 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10895 						}
10896 						else if (dSubType == sTypeVoltage)
10897 						{
10898 							sprintf(szData, "%g V", atof(sValue.c_str()));
10899 							root["result"][ii]["Data"] = szData;
10900 							root["result"][ii]["TypeImg"] = "current";
10901 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10902 							root["result"][ii]["Voltage"] = atof(sValue.c_str());
10903 						}
10904 						else if (dSubType == sTypeCurrent)
10905 						{
10906 							sprintf(szData, "%g A", atof(sValue.c_str()));
10907 							root["result"][ii]["Data"] = szData;
10908 							root["result"][ii]["TypeImg"] = "current";
10909 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10910 							root["result"][ii]["Current"] = atof(sValue.c_str());
10911 						}
10912 						else if (dSubType == sTypeTextStatus)
10913 						{
10914 							root["result"][ii]["Data"] = sValue;
10915 							root["result"][ii]["TypeImg"] = "text";
10916 							root["result"][ii]["HaveTimeout"] = false;
10917 							root["result"][ii]["ShowNotifications"] = false;
10918 						}
10919 						else if (dSubType == sTypeAlert)
10920 						{
10921 							if (nValue > 4)
10922 								nValue = 4;
10923 							sprintf(szData, "Level: %d", nValue);
10924 							root["result"][ii]["Data"] = szData;
10925 							if (!sValue.empty())
10926 								root["result"][ii]["Data"] = sValue;
10927 							else
10928 								root["result"][ii]["Data"] = Get_Alert_Desc(nValue);
10929 							root["result"][ii]["TypeImg"] = "Alert";
10930 							root["result"][ii]["Level"] = nValue;
10931 							root["result"][ii]["HaveTimeout"] = false;
10932 						}
10933 						else if (dSubType == sTypePressure)
10934 						{
10935 							sprintf(szData, "%.1f Bar", atof(sValue.c_str()));
10936 							root["result"][ii]["Data"] = szData;
10937 							root["result"][ii]["TypeImg"] = "gauge";
10938 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10939 							root["result"][ii]["Pressure"] = atof(sValue.c_str());
10940 						}
10941 						else if (dSubType == sTypeBaro)
10942 						{
10943 							std::vector<std::string> tstrarray;
10944 							StringSplit(sValue, ";", tstrarray);
10945 							if (tstrarray.empty())
10946 								continue;
10947 							sprintf(szData, "%g hPa", atof(tstrarray[0].c_str()));
10948 							root["result"][ii]["Data"] = szData;
10949 							root["result"][ii]["TypeImg"] = "gauge";
10950 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10951 							if (tstrarray.size() > 1)
10952 							{
10953 								root["result"][ii]["Barometer"] = atof(tstrarray[0].c_str());
10954 								int forecast = atoi(tstrarray[1].c_str());
10955 								root["result"][ii]["Forecast"] = forecast;
10956 								root["result"][ii]["ForecastStr"] = BMP_Forecast_Desc(forecast);
10957 							}
10958 						}
10959 						else if (dSubType == sTypeZWaveClock)
10960 						{
10961 							std::vector<std::string> tstrarray;
10962 							StringSplit(sValue, ";", tstrarray);
10963 							int day = 0;
10964 							int hour = 0;
10965 							int minute = 0;
10966 							if (tstrarray.size() == 3)
10967 							{
10968 								day = atoi(tstrarray[0].c_str());
10969 								hour = atoi(tstrarray[1].c_str());
10970 								minute = atoi(tstrarray[2].c_str());
10971 							}
10972 							sprintf(szData, "%s %02d:%02d", ZWave_Clock_Days(day), hour, minute);
10973 							root["result"][ii]["DayTime"] = sValue;
10974 							root["result"][ii]["Data"] = szData;
10975 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10976 							root["result"][ii]["TypeImg"] = "clock";
10977 						}
10978 						else if (dSubType == sTypeZWaveThermostatMode)
10979 						{
10980 							strcpy(szData, "");
10981 							root["result"][ii]["Mode"] = nValue;
10982 							root["result"][ii]["TypeImg"] = "mode";
10983 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
10984 							std::string modes = "";
10985 							//Add supported modes
10986 #ifdef WITH_OPENZWAVE
10987 							if (pHardware)
10988 							{
10989 								if (pHardware->HwdType == HTYPE_OpenZWave)
10990 								{
10991 									COpenZWave *pZWave = reinterpret_cast<COpenZWave*>(pHardware);
10992 									unsigned long ID;
10993 									std::stringstream s_strid;
10994 									s_strid << std::hex << sd[1];
10995 									s_strid >> ID;
10996 									std::vector<std::string> vmodes = pZWave->GetSupportedThermostatModes(ID);
10997 									int smode = 0;
10998 									char szTmp[200];
10999 									for (const auto & itt : vmodes)
11000 									{
11001 										//Value supported
11002 										sprintf(szTmp, "%d;%s;", smode, itt.c_str());
11003 										modes += szTmp;
11004 										smode++;
11005 									}
11006 
11007 									if (!vmodes.empty())
11008 									{
11009 										if (nValue < (int)vmodes.size())
11010 										{
11011 											sprintf(szData, "%s", vmodes[nValue].c_str());
11012 										}
11013 									}
11014 								}
11015 							}
11016 #endif
11017 							root["result"][ii]["Data"] = szData;
11018 							root["result"][ii]["Modes"] = modes;
11019 						}
11020 						else if (dSubType == sTypeZWaveThermostatFanMode)
11021 						{
11022 							sprintf(szData, "%s", ZWave_Thermostat_Fan_Modes[nValue]);
11023 							root["result"][ii]["Data"] = szData;
11024 							root["result"][ii]["Mode"] = nValue;
11025 							root["result"][ii]["TypeImg"] = "mode";
11026 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
11027 							//Add supported modes (add all for now)
11028 							bool bAddedSupportedModes = false;
11029 							std::string modes = "";
11030 							//Add supported modes
11031 #ifdef WITH_OPENZWAVE
11032 							if (pHardware)
11033 							{
11034 								if (pHardware->HwdType == HTYPE_OpenZWave)
11035 								{
11036 									COpenZWave *pZWave = reinterpret_cast<COpenZWave*>(pHardware);
11037 									unsigned long ID;
11038 									std::stringstream s_strid;
11039 									s_strid << std::hex << sd[1];
11040 									s_strid >> ID;
11041 									modes = pZWave->GetSupportedThermostatFanModes(ID);
11042 									bAddedSupportedModes = !modes.empty();
11043 								}
11044 							}
11045 #endif
11046 							if (!bAddedSupportedModes)
11047 							{
11048 								int smode = 0;
11049 								while (ZWave_Thermostat_Fan_Modes[smode] != NULL)
11050 								{
11051 									sprintf(szTmp, "%d;%s;", smode, ZWave_Thermostat_Fan_Modes[smode]);
11052 									modes += szTmp;
11053 									smode++;
11054 								}
11055 							}
11056 							root["result"][ii]["Modes"] = modes;
11057 						}
11058 						else if (dSubType == sTypeZWaveThermostatOperatingState)
11059 						{
11060 							strcpy(szData, "");
11061 							root["result"][ii]["State"] = nValue;
11062 							root["result"][ii]["TypeImg"] = "Fan";
11063 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
11064 							if (nValue == 1)
11065 							{
11066 								sprintf(szData, "%s", "Cooling");
11067 							}
11068 							else if (nValue == 2)
11069 							{
11070 								sprintf(szData, "%s", "Heating");
11071 							}
11072 							else
11073 							{
11074 								sprintf(szData, "%s", "Idle");
11075 							}
11076 							root["result"][ii]["Data"] = szData;
11077 						}
11078 						else if (dSubType == sTypeZWaveAlarm)
11079 						{
11080 							sprintf(szData, "Event: 0x%02X (%d)", nValue, nValue);
11081 							root["result"][ii]["Data"] = szData;
11082 							root["result"][ii]["TypeImg"] = "Alert";
11083 							root["result"][ii]["Level"] = nValue;
11084 							root["result"][ii]["HaveTimeout"] = false;
11085 						}
11086 					}
11087 					else if (dType == pTypeLux)
11088 					{
11089 						sprintf(szTmp, "%.0f Lux", atof(sValue.c_str()));
11090 						root["result"][ii]["Data"] = szTmp;
11091 						root["result"][ii]["HaveTimeout"] = bHaveTimeout;
11092 					}
11093 					else if (dType == pTypeWEIGHT)
11094 					{
11095 						sprintf(szTmp, "%g %s", m_sql.m_weightscale * atof(sValue.c_str()), m_sql.m_weightsign.c_str());
11096 						root["result"][ii]["Data"] = szTmp;
11097 						root["result"][ii]["HaveTimeout"] = false;
11098 						root["result"][ii]["SwitchTypeVal"] = (m_sql.m_weightsign=="kg") ? 0 : 1;
11099 					}
11100 					else if (dType == pTypeUsage)
11101 					{
11102 						if (dSubType == sTypeElectric)
11103 						{
11104 							sprintf(szData, "%g Watt", atof(sValue.c_str()));
11105 							root["result"][ii]["Data"] = szData;
11106 						}
11107 						else
11108 						{
11109 							root["result"][ii]["Data"] = sValue;
11110 						}
11111 						root["result"][ii]["HaveTimeout"] = bHaveTimeout;
11112 					}
11113 					else if (dType == pTypeRFXSensor)
11114 					{
11115 						switch (dSubType)
11116 						{
11117 						case sTypeRFXSensorAD:
11118 							sprintf(szData, "%d mV", atoi(sValue.c_str()));
11119 							root["result"][ii]["TypeImg"] = "current";
11120 							break;
11121 						case sTypeRFXSensorVolt:
11122 							sprintf(szData, "%d mV", atoi(sValue.c_str()));
11123 							root["result"][ii]["TypeImg"] = "current";
11124 							break;
11125 						}
11126 						root["result"][ii]["Data"] = szData;
11127 						root["result"][ii]["HaveTimeout"] = bHaveTimeout;
11128 					}
11129 					else if (dType == pTypeRego6XXValue)
11130 					{
11131 						switch (dSubType)
11132 						{
11133 						case sTypeRego6XXStatus:
11134 						{
11135 							std::string lstatus = "On";
11136 
11137 							if (atoi(sValue.c_str()) == 0)
11138 							{
11139 								lstatus = "Off";
11140 							}
11141 							root["result"][ii]["Status"] = lstatus;
11142 							root["result"][ii]["HaveDimmer"] = false;
11143 							root["result"][ii]["MaxDimLevel"] = 0;
11144 							root["result"][ii]["HaveGroupCmd"] = false;
11145 							root["result"][ii]["TypeImg"] = "utility";
11146 							root["result"][ii]["SwitchTypeVal"] = STYPE_OnOff;
11147 							root["result"][ii]["SwitchType"] = Switch_Type_Desc(STYPE_OnOff);
11148 							sprintf(szData, "%d", atoi(sValue.c_str()));
11149 							root["result"][ii]["Data"] = szData;
11150 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
11151 							root["result"][ii]["StrParam1"] = strParam1;
11152 							root["result"][ii]["StrParam2"] = strParam2;
11153 							root["result"][ii]["Protected"] = (iProtected != 0);
11154 
11155 							if (CustomImage < static_cast<int>(m_custom_light_icons.size()))
11156 								root["result"][ii]["Image"] = m_custom_light_icons[CustomImage].RootFile;
11157 							else
11158 								root["result"][ii]["Image"] = "Light";
11159 
11160 							uint64_t camIDX = m_mainworker.m_cameras.IsDevSceneInCamera(0, sd[0]);
11161 							root["result"][ii]["UsedByCamera"] = (camIDX != 0) ? true : false;
11162 							if (camIDX != 0) {
11163 								std::stringstream scidx;
11164 								scidx << camIDX;
11165 								root["result"][ii]["CameraIdx"] = scidx.str();
11166 							}
11167 
11168 							root["result"][ii]["Level"] = 0;
11169 							root["result"][ii]["LevelInt"] = atoi(sValue.c_str());
11170 						}
11171 						break;
11172 						case sTypeRego6XXCounter:
11173 						{
11174 							//get value of today
11175 							time_t now = mytime(NULL);
11176 							struct tm ltime;
11177 							localtime_r(&now, &ltime);
11178 							char szDate[40];
11179 							sprintf(szDate, "%04d-%02d-%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
11180 
11181 							std::vector<std::vector<std::string> > result2;
11182 							strcpy(szTmp, "0");
11183 							result2 = m_sql.safe_query("SELECT MIN(Value), MAX(Value) FROM Meter WHERE (DeviceRowID='%q' AND Date>='%q')",
11184 								sd[0].c_str(), szDate);
11185 							if (!result2.empty())
11186 							{
11187 								std::vector<std::string> sd2 = result2[0];
11188 
11189 								unsigned long long total_min = std::strtoull(sd2[0].c_str(), nullptr, 10);
11190 								unsigned long long total_max = std::strtoull(sd2[1].c_str(), nullptr, 10);
11191 								unsigned long long total_real;
11192 
11193 								total_real = total_max - total_min;
11194 								sprintf(szTmp, "%llu", total_real);
11195 							}
11196 							root["result"][ii]["SwitchTypeVal"] = MTYPE_COUNTER;
11197 							root["result"][ii]["Counter"] = sValue;
11198 							root["result"][ii]["CounterToday"] = szTmp;
11199 							root["result"][ii]["Data"] = sValue;
11200 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
11201 						}
11202 						break;
11203 						}
11204 					}
11205 #ifdef ENABLE_PYTHON
11206 					if (pHardware != NULL)
11207 					{
11208 						if (pHardware->HwdType == HTYPE_PythonPlugin)
11209 						{
11210 							Plugins::CPlugin *pPlugin = (Plugins::CPlugin*)pHardware;
11211 							bHaveTimeout = pPlugin->HasNodeFailed(atoi(sd[2].c_str()));
11212 							root["result"][ii]["HaveTimeout"] = bHaveTimeout;
11213 						}
11214 					}
11215 #endif
11216 					root["result"][ii]["Timers"] = (bHasTimers == true) ? "true" : "false";
11217 					ii++;
11218 				}
11219 			}
11220 		}
11221 
UploadFloorplanImage(WebEmSession & session,const request & req,std::string & redirect_uri)11222 		void CWebServer::UploadFloorplanImage(WebEmSession & session, const request& req, std::string & redirect_uri)
11223 		{
11224 			redirect_uri = "/index.html";
11225 			if (session.rights != 2)
11226 			{
11227 				session.reply_status = reply::forbidden;
11228 				return; //Only admin user allowed
11229 			}
11230 
11231 			std::string planname = request::findValue(&req, "planname");
11232 			std::string scalefactor = request::findValue(&req, "scalefactor");
11233 			std::string imagefile = request::findValue(&req, "imagefile");
11234 
11235 			std::vector<std::vector<std::string> > result;
11236 			m_sql.safe_query("INSERT INTO Floorplans ([Name],[ScaleFactor]) VALUES('%s','%s')", planname.c_str(),scalefactor.c_str());
11237 			result = m_sql.safe_query("SELECT MAX(ID) FROM Floorplans");
11238 			if (!result.empty())
11239 			{
11240 				if (!m_sql.safe_UpdateBlobInTableWithID("Floorplans", "Image", result[0][0], imagefile))
11241 					_log.Log(LOG_ERROR, "SQL: Problem inserting floorplan image into database! ");
11242 			}
11243 		}
11244 
GetFloorplanImage(WebEmSession & session,const request & req,reply & rep)11245 		void CWebServer::GetFloorplanImage(WebEmSession & session, const request& req, reply & rep)
11246 		{
11247 			std::string idx = request::findValue(&req, "idx");
11248 			if (idx == "") {
11249 				return;
11250 			}
11251 			std::vector<std::vector<std::string> > result;
11252 			result = m_sql.safe_queryBlob("SELECT Image FROM Floorplans WHERE ID=%d", atol(idx.c_str()));
11253 			if (result.empty())
11254 				return;
11255 			reply::set_content(&rep, result[0][0].begin(), result[0][0].end());
11256 			std::string oname = "floorplan";
11257 			if (result[0][0].size() > 10)
11258 			{
11259 				if (result[0][0][0] == 'P')
11260 					oname += ".png";
11261 				else if (result[0][0][0] == -1)
11262 					oname += ".jpg";
11263 				else if (result[0][0][0] == 'B')
11264 					oname += ".bmp";
11265 				else if (result[0][0][0] == 'G')
11266 					oname += ".gif";
11267 				else if ((result[0][0][0] == '<') && (result[0][0][1] == 's') && (result[0][0][2] == 'v') && (result[0][0][3] == 'g'))
11268 					oname += ".svg";
11269 				else if (result[0][0].find("<svg") != std::string::npos) //some SVG's start with <xml
11270 					oname += ".svg";
11271 			}
11272 			reply::add_header_attachment(&rep, oname);
11273 		}
11274 
GetDatabaseBackup(WebEmSession & session,const request & req,reply & rep)11275 		void CWebServer::GetDatabaseBackup(WebEmSession & session, const request& req, reply & rep)
11276 		{
11277 			if (session.rights != 2)
11278 			{
11279 				session.reply_status = reply::forbidden;
11280 				return; //Only admin user allowed
11281 			}
11282 			time_t now = mytime(NULL);
11283 			Json::Value backupInfo;
11284 
11285 			backupInfo["type"] = "Web";
11286 #ifdef WIN32
11287 			backupInfo["location"] = szUserDataFolder + "backup.db";
11288 #else
11289 			backupInfo["location"] = "/tmp/backup.db";
11290 #endif
11291 			if (m_sql.BackupDatabase(backupInfo["location"].asString()))
11292 			{
11293 				std::string szAttachmentName = "domoticz.db";
11294 				std::string szVar;
11295 				if (m_sql.GetPreferencesVar("Title", szVar))
11296 				{
11297 					stdreplace(szVar, " ", "_");
11298 					stdreplace(szVar, "/", "_");
11299 					stdreplace(szVar, "\\", "_");
11300 					if (!szVar.empty()) {
11301 						szAttachmentName = szVar + ".db";
11302 					}
11303 				}
11304 				reply::set_download_file(&rep, backupInfo["location"].asString(), szAttachmentName);
11305 				backupInfo["duration"] = difftime(mytime(NULL),now);
11306 				m_mainworker.m_notificationsystem.Notify(Notification::DZ_BACKUP_DONE, Notification::STATUS_INFO, JSonToRawString(backupInfo));
11307 			}
11308 		}
11309 
RType_DeleteDevice(WebEmSession & session,const request & req,Json::Value & root)11310 		void CWebServer::RType_DeleteDevice(WebEmSession & session, const request& req, Json::Value &root)
11311 		{
11312 			if (session.rights != 2)
11313 			{
11314 				session.reply_status = reply::forbidden;
11315 				return; //Only admin user allowed
11316 			}
11317 
11318 			std::string idx = request::findValue(&req, "idx");
11319 			if (idx.empty())
11320 				return;
11321 
11322 			root["status"] = "OK";
11323 			root["title"] = "DeleteDevice";
11324 			m_sql.DeleteDevices(idx);
11325 			m_mainworker.m_scheduler.ReloadSchedules();
11326 		}
11327 
RType_AddScene(WebEmSession & session,const request & req,Json::Value & root)11328 		void CWebServer::RType_AddScene(WebEmSession & session, const request& req, Json::Value &root)
11329 		{
11330 			if (session.rights != 2)
11331 			{
11332 				session.reply_status = reply::forbidden;
11333 				return; //Only admin user allowed
11334 			}
11335 
11336 			std::string name = HTMLSanitizer::Sanitize(request::findValue(&req, "name"));
11337 			name = HTMLSanitizer::Sanitize(name);
11338 			if (name.empty())
11339 			{
11340 				root["status"] = "ERR";
11341 				root["message"] = "No Scene Name specified!";
11342 				return;
11343 			}
11344 			std::string stype = request::findValue(&req, "scenetype");
11345 			if (stype.empty())
11346 			{
11347 				root["status"] = "ERR";
11348 				root["message"] = "No Scene Type specified!";
11349 				return;
11350 			}
11351 			if (m_sql.DoesSceneByNameExits(name) == true)
11352 			{
11353 				root["status"] = "ERR";
11354 				root["message"] = "A Scene with this Name already Exits!";
11355 				return;
11356 			}
11357 			root["status"] = "OK";
11358 			root["title"] = "AddScene";
11359 			m_sql.safe_query(
11360 				"INSERT INTO Scenes (Name,SceneType) VALUES ('%q',%d)",
11361 				name.c_str(),
11362 				atoi(stype.c_str())
11363 			);
11364 			if (m_sql.m_bEnableEventSystem)
11365 			{
11366 				m_mainworker.m_eventsystem.GetCurrentScenesGroups();
11367 			}
11368 		}
11369 
RType_DeleteScene(WebEmSession & session,const request & req,Json::Value & root)11370 		void CWebServer::RType_DeleteScene(WebEmSession & session, const request& req, Json::Value &root)
11371 		{
11372 			if (session.rights != 2)
11373 			{
11374 				session.reply_status = reply::forbidden;
11375 				return; //Only admin user allowed
11376 			}
11377 
11378 			std::string idx = request::findValue(&req, "idx");
11379 			if (idx.empty())
11380 				return;
11381 			root["status"] = "OK";
11382 			root["title"] = "DeleteScene";
11383 			m_sql.DeleteScenes(idx);
11384 		}
11385 
RType_UpdateScene(WebEmSession & session,const request & req,Json::Value & root)11386 		void CWebServer::RType_UpdateScene(WebEmSession & session, const request& req, Json::Value &root)
11387 		{
11388 			if (session.rights != 2)
11389 			{
11390 				session.reply_status = reply::forbidden;
11391 				return; //Only admin user allowed
11392 			}
11393 
11394 			std::string idx = request::findValue(&req, "idx");
11395 			std::string name = HTMLSanitizer::Sanitize(request::findValue(&req, "name"));
11396 			std::string description = HTMLSanitizer::Sanitize(request::findValue(&req, "description"));
11397 
11398 			name = HTMLSanitizer::Sanitize(name);
11399 			description = HTMLSanitizer::Sanitize(description);
11400 
11401 			if ((idx.empty()) || (name.empty()))
11402 				return;
11403 			std::string stype = request::findValue(&req, "scenetype");
11404 			if (stype.empty())
11405 			{
11406 				root["status"] = "ERR";
11407 				root["message"] = "No Scene Type specified!";
11408 				return;
11409 			}
11410 			std::string tmpstr = request::findValue(&req, "protected");
11411 			int iProtected = (tmpstr == "true") ? 1 : 0;
11412 
11413 			std::string onaction = base64_decode(request::findValue(&req, "onaction"));
11414 			std::string offaction = base64_decode(request::findValue(&req, "offaction"));
11415 
11416 
11417 			root["status"] = "OK";
11418 			root["title"] = "UpdateScene";
11419 			m_sql.safe_query("UPDATE Scenes SET Name='%q', Description='%q', SceneType=%d, Protected=%d, OnAction='%q', OffAction='%q' WHERE (ID == '%q')",
11420 				name.c_str(),
11421 				description.c_str(),
11422 				atoi(stype.c_str()),
11423 				iProtected,
11424 				onaction.c_str(),
11425 				offaction.c_str(),
11426 				idx.c_str()
11427 			);
11428 			uint64_t ullidx = std::strtoull(idx.c_str(), nullptr, 10);
11429 			m_mainworker.m_eventsystem.WWWUpdateSingleState(ullidx, name, m_mainworker.m_eventsystem.REASON_SCENEGROUP);
11430 		}
11431 
compareIconsByName(const http::server::CWebServer::_tCustomIcon & a,const http::server::CWebServer::_tCustomIcon & b)11432 		bool compareIconsByName(const http::server::CWebServer::_tCustomIcon &a, const http::server::CWebServer::_tCustomIcon &b)
11433 		{
11434 			return a.Title < b.Title;
11435 		}
11436 
RType_CustomLightIcons(WebEmSession & session,const request & req,Json::Value & root)11437 		void CWebServer::RType_CustomLightIcons(WebEmSession & session, const request& req, Json::Value &root)
11438 		{
11439 			int ii = 0;
11440 
11441 			std::vector<_tCustomIcon> temp_custom_light_icons = m_custom_light_icons;
11442 			//Sort by name
11443 			std::sort(temp_custom_light_icons.begin(), temp_custom_light_icons.end(), compareIconsByName);
11444 
11445 			for (const auto & itt : temp_custom_light_icons)
11446 			{
11447 				root["result"][ii]["idx"] = itt.idx;
11448 				root["result"][ii]["imageSrc"] = itt.RootFile;
11449 				root["result"][ii]["text"] = itt.Title;
11450 				root["result"][ii]["description"] = itt.Description;
11451 				ii++;
11452 			}
11453 			root["status"] = "OK";
11454 		}
11455 
RType_Plans(WebEmSession & session,const request & req,Json::Value & root)11456 		void CWebServer::RType_Plans(WebEmSession & session, const request& req, Json::Value &root)
11457 		{
11458 			root["status"] = "OK";
11459 			root["title"] = "Plans";
11460 
11461 			std::string sDisplayHidden = request::findValue(&req, "displayhidden");
11462 			bool bDisplayHidden = (sDisplayHidden == "1");
11463 
11464 			std::vector<std::vector<std::string> > result, result2;
11465 			result = m_sql.safe_query("SELECT ID, Name, [Order] FROM Plans ORDER BY [Order]");
11466 			if (!result.empty())
11467 			{
11468 				int ii = 0;
11469 				for (const auto & itt : result)
11470 				{
11471 					std::vector<std::string> sd = itt;
11472 
11473 					std::string Name = sd[1];
11474 					bool bIsHidden = (Name[0] == '$');
11475 
11476 					if ((bDisplayHidden) || (!bIsHidden))
11477 					{
11478 						root["result"][ii]["idx"] = sd[0];
11479 						root["result"][ii]["Name"] = Name;
11480 						root["result"][ii]["Order"] = sd[2];
11481 
11482 						unsigned int totDevices = 0;
11483 
11484 						result2 = m_sql.safe_query("SELECT COUNT(*) FROM DeviceToPlansMap WHERE (PlanID=='%q')",
11485 							sd[0].c_str());
11486 						if (!result2.empty())
11487 						{
11488 							totDevices = (unsigned int)atoi(result2[0][0].c_str());
11489 						}
11490 						root["result"][ii]["Devices"] = totDevices;
11491 
11492 						ii++;
11493 					}
11494 				}
11495 			}
11496 		}
11497 
RType_FloorPlans(WebEmSession & session,const request & req,Json::Value & root)11498 		void CWebServer::RType_FloorPlans(WebEmSession & session, const request& req, Json::Value &root)
11499 		{
11500 			root["status"] = "OK";
11501 			root["title"] = "Floorplans";
11502 
11503 			std::vector<std::vector<std::string> > result, result2, result3;
11504 
11505 			result = m_sql.safe_query("SELECT Key, nValue, sValue FROM Preferences WHERE Key LIKE 'Floorplan%%'");
11506 			if (result.empty())
11507 				return;
11508 
11509 			for (const auto & itt : result)
11510 			{
11511 				std::vector<std::string> sd = itt;
11512 				std::string Key = sd[0];
11513 				int nValue = atoi(sd[1].c_str());
11514 				std::string sValue = sd[2];
11515 
11516 				if (Key == "FloorplanPopupDelay")
11517 				{
11518 					root["PopupDelay"] = nValue;
11519 				}
11520 				if (Key == "FloorplanFullscreenMode")
11521 				{
11522 					root["FullscreenMode"] = nValue;
11523 				}
11524 				if (Key == "FloorplanAnimateZoom")
11525 				{
11526 					root["AnimateZoom"] = nValue;
11527 				}
11528 				if (Key == "FloorplanShowSensorValues")
11529 				{
11530 					root["ShowSensorValues"] = nValue;
11531 				}
11532 				if (Key == "FloorplanShowSwitchValues")
11533 				{
11534 					root["ShowSwitchValues"] = nValue;
11535 				}
11536 				if (Key == "FloorplanShowSceneNames")
11537 				{
11538 					root["ShowSceneNames"] = nValue;
11539 				}
11540 				if (Key == "FloorplanRoomColour")
11541 				{
11542 					root["RoomColour"] = sValue;
11543 				}
11544 				if (Key == "FloorplanActiveOpacity")
11545 				{
11546 					root["ActiveRoomOpacity"] = nValue;
11547 				}
11548 				if (Key == "FloorplanInactiveOpacity")
11549 				{
11550 					root["InactiveRoomOpacity"] = nValue;
11551 				}
11552 			}
11553 
11554 			result2 = m_sql.safe_query("SELECT ID, Name, ScaleFactor, [Order] FROM Floorplans ORDER BY [Order]");
11555 			if (!result2.empty())
11556 			{
11557 				int ii = 0;
11558 				for (const auto & itt : result2)
11559 				{
11560 					std::vector<std::string> sd = itt;
11561 
11562 					root["result"][ii]["idx"] = sd[0];
11563 					root["result"][ii]["Name"] = sd[1];
11564 					std::string ImageURL = "images/floorplans/plan?idx=" + sd[0];
11565 					root["result"][ii]["Image"] = ImageURL;
11566 					root["result"][ii]["ScaleFactor"] = sd[2];
11567 					root["result"][ii]["Order"] = sd[3];
11568 
11569 					unsigned int totPlans = 0;
11570 
11571 					result3 = m_sql.safe_query("SELECT COUNT(*) FROM Plans WHERE (FloorplanID=='%q')", sd[0].c_str());
11572 					if (!result3.empty())
11573 					{
11574 						totPlans = (unsigned int)atoi(result3[0][0].c_str());
11575 					}
11576 					root["result"][ii]["Plans"] = totPlans;
11577 
11578 					ii++;
11579 				}
11580 			}
11581 		}
11582 
RType_Scenes(WebEmSession & session,const request & req,Json::Value & root)11583 		void CWebServer::RType_Scenes(WebEmSession & session, const request& req, Json::Value &root)
11584 		{
11585 			root["status"] = "OK";
11586 			root["title"] = "Scenes";
11587 			root["AllowWidgetOrdering"] = m_sql.m_bAllowWidgetOrdering;
11588 
11589 			std::string sDisplayHidden = request::findValue(&req, "displayhidden");
11590 			bool bDisplayHidden = (sDisplayHidden == "1");
11591 
11592 			std::string sLastUpdate = request::findValue(&req, "lastupdate");
11593 
11594 			std::string rid = request::findValue(&req, "rid");
11595 
11596 			time_t LastUpdate = 0;
11597 			if (sLastUpdate != "")
11598 			{
11599 				std::stringstream sstr;
11600 				sstr << sLastUpdate;
11601 				sstr >> LastUpdate;
11602 			}
11603 
11604 			time_t now = mytime(NULL);
11605 			struct tm tm1;
11606 			localtime_r(&now, &tm1);
11607 			struct tm tLastUpdate;
11608 			localtime_r(&now, &tLastUpdate);
11609 
11610 			root["ActTime"] = static_cast<int>(now);
11611 
11612 			std::vector<std::vector<std::string> > result, result2;
11613 			std::string szQuery = "SELECT ID, Name, Activators, Favorite, nValue, SceneType, LastUpdate, Protected, OnAction, OffAction, Description FROM Scenes";
11614 			if (!rid.empty())
11615 				szQuery += " WHERE (ID == " + rid + ")";
11616 			szQuery += " ORDER BY [Order]";
11617 			result = m_sql.safe_query(szQuery.c_str());
11618 			if (!result.empty())
11619 			{
11620 				int ii = 0;
11621 				for (const auto & itt : result)
11622 				{
11623 					std::vector<std::string> sd = itt;
11624 
11625 					std::string sName = sd[1];
11626 					if ((bDisplayHidden == false) && (sName[0] == '$'))
11627 						continue;
11628 
11629 					std::string sLastUpdate = sd[6].c_str();
11630 					if (LastUpdate != 0)
11631 					{
11632 						time_t cLastUpdate;
11633 						ParseSQLdatetime(cLastUpdate, tLastUpdate, sLastUpdate, tm1.tm_isdst);
11634 						if (cLastUpdate <= LastUpdate)
11635 							continue;
11636 					}
11637 
11638 					unsigned char nValue = atoi(sd[4].c_str());
11639 					unsigned char scenetype = atoi(sd[5].c_str());
11640 					int iProtected = atoi(sd[7].c_str());
11641 
11642 					std::string onaction = base64_encode(sd[8]);
11643 					std::string offaction = base64_encode(sd[9]);
11644 
11645 					root["result"][ii]["idx"] = sd[0];
11646 					root["result"][ii]["Name"] = sName;
11647 					root["result"][ii]["Description"] = sd[10];
11648 					root["result"][ii]["Favorite"] = atoi(sd[3].c_str());
11649 					root["result"][ii]["Protected"] = (iProtected != 0);
11650 					root["result"][ii]["OnAction"] = onaction;
11651 					root["result"][ii]["OffAction"] = offaction;
11652 
11653 					if (scenetype == 0)
11654 					{
11655 						root["result"][ii]["Type"] = "Scene";
11656 					}
11657 					else
11658 					{
11659 						root["result"][ii]["Type"] = "Group";
11660 					}
11661 
11662 					root["result"][ii]["LastUpdate"] = sLastUpdate;
11663 
11664 					if (nValue == 0)
11665 						root["result"][ii]["Status"] = "Off";
11666 					else if (nValue == 1)
11667 						root["result"][ii]["Status"] = "On";
11668 					else
11669 						root["result"][ii]["Status"] = "Mixed";
11670 					root["result"][ii]["Timers"] = (m_sql.HasSceneTimers(sd[0]) == true) ? "true" : "false";
11671 					uint64_t camIDX = m_mainworker.m_cameras.IsDevSceneInCamera(1, sd[0]);
11672 					root["result"][ii]["UsedByCamera"] = (camIDX != 0) ? true : false;
11673 					if (camIDX != 0) {
11674 						std::stringstream scidx;
11675 						scidx << camIDX;
11676 						root["result"][ii]["CameraIdx"] = scidx.str();
11677 					}
11678 					ii++;
11679 				}
11680 			}
11681 			if (!m_mainworker.m_LastSunriseSet.empty())
11682 			{
11683 				std::vector<std::string> strarray;
11684 				StringSplit(m_mainworker.m_LastSunriseSet, ";", strarray);
11685 				if (strarray.size() == 10)
11686 				{
11687 					char szTmp[100];
11688 					//strftime(szTmp, 80, "%b %d %Y %X", &tm1);
11689 					strftime(szTmp, 80, "%Y-%m-%d %X", &tm1);
11690 					root["ServerTime"] = szTmp;
11691 					root["Sunrise"] = strarray[0];
11692 					root["Sunset"] = strarray[1];
11693 					root["SunAtSouth"] = strarray[2];
11694 					root["CivTwilightStart"] = strarray[3];
11695 					root["CivTwilightEnd"] = strarray[4];
11696 					root["NautTwilightStart"] = strarray[5];
11697 					root["NautTwilightEnd"] = strarray[6];
11698 					root["AstrTwilightStart"] = strarray[7];
11699 					root["AstrTwilightEnd"] = strarray[8];
11700 					root["DayLength"] = strarray[9];
11701 				}
11702 			}
11703 		}
11704 
RType_Hardware(WebEmSession & session,const request & req,Json::Value & root)11705 		void CWebServer::RType_Hardware(WebEmSession & session, const request& req, Json::Value &root)
11706 		{
11707 			root["status"] = "OK";
11708 			root["title"] = "Hardware";
11709 
11710 #ifdef WITH_OPENZWAVE
11711 			m_ZW_Hwidx = -1;
11712 #endif
11713 
11714 			std::vector<std::vector<std::string> > result;
11715 			result = m_sql.safe_query("SELECT ID, Name, Enabled, Type, Address, Port, SerialPort, Username, Password, Extra, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6, DataTimeout FROM Hardware ORDER BY ID ASC");
11716 			if (!result.empty())
11717 			{
11718 				int ii = 0;
11719 				for (const auto & itt : result)
11720 				{
11721 					std::vector<std::string> sd = itt;
11722 
11723 					_eHardwareTypes hType = (_eHardwareTypes)atoi(sd[3].c_str());
11724 					if (hType == HTYPE_DomoticzInternal)
11725 						continue;
11726 					root["result"][ii]["idx"] = sd[0];
11727 					root["result"][ii]["Name"] = sd[1];
11728 					root["result"][ii]["Enabled"] = (sd[2] == "1") ? "true" : "false";
11729 					root["result"][ii]["Type"] = hType;
11730 					root["result"][ii]["Address"] = sd[4];
11731 					root["result"][ii]["Port"] = atoi(sd[5].c_str());
11732 					root["result"][ii]["SerialPort"] = sd[6];
11733 					root["result"][ii]["Username"] = sd[7];
11734 					root["result"][ii]["Password"] = sd[8];
11735 					root["result"][ii]["Extra"] = sd[9];
11736 
11737 					if (hType == HTYPE_PythonPlugin) {
11738 						root["result"][ii]["Mode1"] = sd[10];  // Plugins can have non-numeric values in the Mode fields
11739 						root["result"][ii]["Mode2"] = sd[11];
11740 						root["result"][ii]["Mode3"] = sd[12];
11741 						root["result"][ii]["Mode4"] = sd[13];
11742 						root["result"][ii]["Mode5"] = sd[14];
11743 						root["result"][ii]["Mode6"] = sd[15];
11744 					}
11745 					else {
11746 						root["result"][ii]["Mode1"] = atoi(sd[10].c_str());
11747 						root["result"][ii]["Mode2"] = atoi(sd[11].c_str());
11748 						root["result"][ii]["Mode3"] = atoi(sd[12].c_str());
11749 						root["result"][ii]["Mode4"] = atoi(sd[13].c_str());
11750 						root["result"][ii]["Mode5"] = atoi(sd[14].c_str());
11751 						root["result"][ii]["Mode6"] = atoi(sd[15].c_str());
11752 					}
11753 					root["result"][ii]["DataTimeout"] = atoi(sd[16].c_str());
11754 
11755 					//Special case for openzwave (status for nodes queried)
11756 					CDomoticzHardwareBase *pHardware = m_mainworker.GetHardware(atoi(sd[0].c_str()));
11757 					if (pHardware != NULL)
11758 					{
11759 						if (
11760 							(pHardware->HwdType == HTYPE_RFXtrx315) ||
11761 							(pHardware->HwdType == HTYPE_RFXtrx433) ||
11762 							(pHardware->HwdType == HTYPE_RFXtrx868) ||
11763 							(pHardware->HwdType == HTYPE_RFXLAN)
11764 							)
11765 						{
11766 							CRFXBase *pMyHardware = reinterpret_cast<CRFXBase*>(pHardware);
11767 							if (!pMyHardware->m_Version.empty())
11768 								root["result"][ii]["version"] = pMyHardware->m_Version;
11769 							else
11770 								root["result"][ii]["version"] = sd[11];
11771 							root["result"][ii]["noiselvl"] = pMyHardware->m_NoiseLevel;
11772 						}
11773 						else if ((pHardware->HwdType == HTYPE_MySensorsUSB) || (pHardware->HwdType == HTYPE_MySensorsTCP) || (pHardware->HwdType == HTYPE_MySensorsMQTT))
11774 						{
11775 							MySensorsBase *pMyHardware = reinterpret_cast<MySensorsBase*>(pHardware);
11776 							root["result"][ii]["version"] = pMyHardware->GetGatewayVersion();
11777 						}
11778 						else if ((pHardware->HwdType == HTYPE_OpenThermGateway) || (pHardware->HwdType == HTYPE_OpenThermGatewayTCP))
11779 						{
11780 							OTGWBase *pMyHardware = reinterpret_cast<OTGWBase*>(pHardware);
11781 							root["result"][ii]["version"] = pMyHardware->m_Version;
11782 						}
11783 						else if ((pHardware->HwdType == HTYPE_RFLINKUSB) || (pHardware->HwdType == HTYPE_RFLINKTCP))
11784 						{
11785 							CRFLinkBase *pMyHardware = reinterpret_cast<CRFLinkBase*>(pHardware);
11786 							root["result"][ii]["version"] = pMyHardware->m_Version;
11787 						}
11788 						else
11789 						{
11790 #ifdef WITH_OPENZWAVE
11791 							if (pHardware->HwdType == HTYPE_OpenZWave)
11792 							{
11793 								COpenZWave *pOZWHardware = reinterpret_cast<COpenZWave*>(pHardware);
11794 								root["result"][ii]["version"] = pOZWHardware->GetVersionLong();
11795 								root["result"][ii]["NodesQueried"] = (pOZWHardware->m_awakeNodesQueried || pOZWHardware->m_allNodesQueried);
11796 							}
11797 #endif
11798 						}
11799 					}
11800 					ii++;
11801 				}
11802 			}
11803 		}
11804 
RType_Devices(WebEmSession & session,const request & req,Json::Value & root)11805 		void CWebServer::RType_Devices(WebEmSession & session, const request& req, Json::Value &root)
11806 		{
11807 			std::string rfilter = request::findValue(&req, "filter");
11808 			std::string order = request::findValue(&req, "order");
11809 			std::string rused = request::findValue(&req, "used");
11810 			std::string rid = request::findValue(&req, "rid");
11811 			std::string planid = request::findValue(&req, "plan");
11812 			std::string floorid = request::findValue(&req, "floor");
11813 			std::string sDisplayHidden = request::findValue(&req, "displayhidden");
11814 			std::string sFetchFavorites = request::findValue(&req, "favorite");
11815 			std::string sDisplayDisabled = request::findValue(&req, "displaydisabled");
11816 			bool bDisplayHidden = (sDisplayHidden == "1");
11817 			bool bFetchFavorites = (sFetchFavorites == "1");
11818 
11819 			int HideDisabledHardwareSensors = 0;
11820 			m_sql.GetPreferencesVar("HideDisabledHardwareSensors", HideDisabledHardwareSensors);
11821 			bool bDisabledDisabled = (HideDisabledHardwareSensors == 0);
11822 			if (sDisplayDisabled == "1")
11823 				bDisabledDisabled = true;
11824 
11825 			std::string sLastUpdate = request::findValue(&req, "lastupdate");
11826 			std::string hwidx = request::findValue(&req, "hwidx"); // OTO
11827 
11828 			time_t LastUpdate = 0;
11829 			if (sLastUpdate != "")
11830 			{
11831 				std::stringstream sstr;
11832 				sstr << sLastUpdate;
11833 				sstr >> LastUpdate;
11834 			}
11835 
11836 			root["status"] = "OK";
11837 			root["title"] = "Devices";
11838 			root["app_version"] = szAppVersion;
11839 			GetJSonDevices(root, rused, rfilter, order, rid, planid, floorid, bDisplayHidden, bDisabledDisabled, bFetchFavorites, LastUpdate, session.username, hwidx);
11840 		}
11841 
RType_Users(WebEmSession & session,const request & req,Json::Value & root)11842 		void CWebServer::RType_Users(WebEmSession & session, const request& req, Json::Value &root)
11843 		{
11844 			bool bHaveUser = (session.username != "");
11845 			int urights = 3;
11846 			if (bHaveUser)
11847 			{
11848 				int iUser = FindUser(session.username.c_str());
11849 				if (iUser != -1)
11850 					urights = static_cast<int>(m_users[iUser].userrights);
11851 			}
11852 			if (urights < 2)
11853 				return;
11854 
11855 			root["status"] = "OK";
11856 			root["title"] = "Users";
11857 
11858 			std::vector<std::vector<std::string> > result;
11859 			result = m_sql.safe_query("SELECT ID, Active, Username, Password, Rights, RemoteSharing, TabsEnabled FROM USERS ORDER BY ID ASC");
11860 			if (!result.empty())
11861 			{
11862 				int ii = 0;
11863 				for (const auto & itt : result)
11864 				{
11865 					std::vector<std::string> sd = itt;
11866 
11867 					root["result"][ii]["idx"] = sd[0];
11868 					root["result"][ii]["Enabled"] = (sd[1] == "1") ? "true" : "false";
11869 					root["result"][ii]["Username"] = base64_decode(sd[2]);
11870 					root["result"][ii]["Password"] = sd[3];
11871 					root["result"][ii]["Rights"] = atoi(sd[4].c_str());
11872 					root["result"][ii]["RemoteSharing"] = atoi(sd[5].c_str());
11873 					root["result"][ii]["TabsEnabled"] = atoi(sd[6].c_str());
11874 					ii++;
11875 				}
11876 			}
11877 		}
11878 
RType_Mobiles(WebEmSession & session,const request & req,Json::Value & root)11879 		void CWebServer::RType_Mobiles(WebEmSession & session, const request& req, Json::Value &root)
11880 		{
11881 			bool bHaveUser = (session.username != "");
11882 			int urights = 3;
11883 			if (bHaveUser)
11884 			{
11885 				int iUser = FindUser(session.username.c_str());
11886 				if (iUser != -1)
11887 					urights = static_cast<int>(m_users[iUser].userrights);
11888 			}
11889 			if (urights < 2)
11890 				return;
11891 
11892 			root["status"] = "OK";
11893 			root["title"] = "Mobiles";
11894 
11895 			std::vector<std::vector<std::string> > result;
11896 			result = m_sql.safe_query("SELECT ID, Active, Name, UUID, LastUpdate, DeviceType FROM MobileDevices ORDER BY Name ASC");
11897 			if (!result.empty())
11898 			{
11899 				int ii = 0;
11900 				for (const auto & itt : result)
11901 				{
11902 					std::vector<std::string> sd = itt;
11903 
11904 					root["result"][ii]["idx"] = sd[0];
11905 					root["result"][ii]["Enabled"] = (sd[1] == "1") ? "true" : "false";
11906 					root["result"][ii]["Name"] = sd[2];
11907 					root["result"][ii]["UUID"] = sd[3];
11908 					root["result"][ii]["LastUpdate"] = sd[4];
11909 					root["result"][ii]["DeviceType"] = sd[5];
11910 					ii++;
11911 				}
11912 			}
11913 		}
11914 
Cmd_SetSetpoint(WebEmSession & session,const request & req,Json::Value & root)11915 		void CWebServer::Cmd_SetSetpoint(WebEmSession & session, const request& req, Json::Value &root)
11916 		{
11917 			bool bHaveUser = (session.username != "");
11918 			int iUser = -1;
11919 			int urights = 3;
11920 			if (bHaveUser)
11921 			{
11922 				iUser = FindUser(session.username.c_str());
11923 				if (iUser != -1)
11924 				{
11925 					urights = static_cast<int>(m_users[iUser].userrights);
11926 				}
11927 			}
11928 			if (urights < 1)
11929 				return;
11930 
11931 			std::string idx = request::findValue(&req, "idx");
11932 			std::string setpoint = request::findValue(&req, "setpoint");
11933 			if (
11934 				(idx.empty()) ||
11935 				(setpoint.empty())
11936 				)
11937 				return;
11938 			root["status"] = "OK";
11939 			root["title"] = "SetSetpoint";
11940 			if (iUser != -1)
11941 			{
11942 				_log.Log(LOG_STATUS, "User: %s initiated a SetPoint command", m_users[iUser].Username.c_str());
11943 			}
11944 			m_mainworker.SetSetPoint(idx, static_cast<float>(atof(setpoint.c_str())));
11945 		}
11946 
Cmd_GetSceneActivations(WebEmSession & session,const request & req,Json::Value & root)11947 		void CWebServer::Cmd_GetSceneActivations(WebEmSession & session, const request& req, Json::Value &root)
11948 		{
11949 			if (session.rights != 2)
11950 			{
11951 				session.reply_status = reply::forbidden;
11952 				return; //Only admin user allowed
11953 			}
11954 
11955 			std::string idx = request::findValue(&req, "idx");
11956 			if (idx.empty())
11957 				return;
11958 
11959 			root["status"] = "OK";
11960 			root["title"] = "GetSceneActivations";
11961 
11962 			std::vector<std::vector<std::string> > result, result2;
11963 			result = m_sql.safe_query("SELECT Activators, SceneType FROM Scenes WHERE (ID==%q)", idx.c_str());
11964 			if (result.empty())
11965 				return;
11966 			int ii = 0;
11967 			std::string Activators = result[0][0];
11968 			int SceneType = atoi(result[0][1].c_str());
11969 			if (!Activators.empty())
11970 			{
11971 				//Get Activator device names
11972 				std::vector<std::string> arrayActivators;
11973 				StringSplit(Activators, ";", arrayActivators);
11974 				for (const auto & ittAct : arrayActivators)
11975 				{
11976 					std::string sCodeCmd = ittAct;
11977 
11978 					std::vector<std::string> arrayCode;
11979 					StringSplit(sCodeCmd, ":", arrayCode);
11980 
11981 					std::string sID = arrayCode[0];
11982 					int sCode = 0;
11983 					if (arrayCode.size() == 2)
11984 					{
11985 						sCode = atoi(arrayCode[1].c_str());
11986 					}
11987 
11988 
11989 					result2 = m_sql.safe_query("SELECT Name, [Type], SubType, SwitchType FROM DeviceStatus WHERE (ID==%q)", sID.c_str());
11990 					if (!result2.empty())
11991 					{
11992 						std::vector<std::string> sd = result2[0];
11993 						std::string lstatus = "-";
11994 						if ((SceneType == 0) && (arrayCode.size() == 2))
11995 						{
11996 							unsigned char devType = (unsigned char)atoi(sd[1].c_str());
11997 							unsigned char subType = (unsigned char)atoi(sd[2].c_str());
11998 							_eSwitchType switchtype = (_eSwitchType)atoi(sd[3].c_str());
11999 							int nValue = sCode;
12000 							std::string sValue = "";
12001 							int llevel = 0;
12002 							bool bHaveDimmer = false;
12003 							bool bHaveGroupCmd = false;
12004 							int maxDimLevel = 0;
12005 							GetLightStatus(devType, subType, switchtype, nValue, sValue, lstatus, llevel, bHaveDimmer, maxDimLevel, bHaveGroupCmd);
12006 						}
12007 						uint64_t dID = std::strtoull(sID.c_str(), nullptr, 10);
12008 						root["result"][ii]["idx"] = dID;
12009 						root["result"][ii]["name"] = sd[0];
12010 						root["result"][ii]["code"] = sCode;
12011 						root["result"][ii]["codestr"] = lstatus;
12012 						ii++;
12013 					}
12014 				}
12015 			}
12016 		}
12017 
Cmd_AddSceneCode(WebEmSession & session,const request & req,Json::Value & root)12018 		void CWebServer::Cmd_AddSceneCode(WebEmSession & session, const request& req, Json::Value &root)
12019 		{
12020 			if (session.rights != 2)
12021 			{
12022 				session.reply_status = reply::forbidden;
12023 				return; //Only admin user allowed
12024 			}
12025 
12026 			std::string sceneidx = request::findValue(&req, "sceneidx");
12027 			std::string idx = request::findValue(&req, "idx");
12028 			std::string cmnd = request::findValue(&req, "cmnd");
12029 			if (
12030 				(sceneidx.empty()) ||
12031 				(idx.empty()) ||
12032 				(cmnd.empty())
12033 				)
12034 				return;
12035 			root["status"] = "OK";
12036 			root["title"] = "AddSceneCode";
12037 
12038 			//First check if we do not already have this device as activation code
12039 			std::vector<std::vector<std::string> > result;
12040 			result = m_sql.safe_query("SELECT Activators, SceneType FROM Scenes WHERE (ID==%q)", sceneidx.c_str());
12041 			if (result.empty())
12042 				return;
12043 			std::string Activators = result[0][0];
12044 			unsigned char scenetype = atoi(result[0][1].c_str());
12045 
12046 			if (!Activators.empty())
12047 			{
12048 				//Get Activator device names
12049 				std::vector<std::string> arrayActivators;
12050 				StringSplit(Activators, ";", arrayActivators);
12051 				for (const auto & ittAct : arrayActivators)
12052 				{
12053 					std::string sCodeCmd = ittAct;
12054 
12055 					std::vector<std::string> arrayCode;
12056 					StringSplit(sCodeCmd, ":", arrayCode);
12057 
12058 					std::string sID = arrayCode[0];
12059 					std::string sCode = "";
12060 					if (arrayCode.size() == 2)
12061 					{
12062 						sCode = arrayCode[1];
12063 					}
12064 
12065 					if (sID == idx)
12066 					{
12067 						if (scenetype == 1)
12068 							return; //Group does not work with separate codes, so already there
12069 						if (sCode == cmnd)
12070 							return; //same code, already there!
12071 					}
12072 				}
12073 			}
12074 			if (!Activators.empty())
12075 				Activators += ";";
12076 			Activators += idx;
12077 			if (scenetype == 0)
12078 			{
12079 				Activators += ":" + cmnd;
12080 			}
12081 			m_sql.safe_query("UPDATE Scenes SET Activators='%q' WHERE (ID==%q)", Activators.c_str(), sceneidx.c_str());
12082 		}
12083 
Cmd_RemoveSceneCode(WebEmSession & session,const request & req,Json::Value & root)12084 		void CWebServer::Cmd_RemoveSceneCode(WebEmSession & session, const request& req, Json::Value &root)
12085 		{
12086 			if (session.rights != 2)
12087 			{
12088 				session.reply_status = reply::forbidden;
12089 				return; //Only admin user allowed
12090 			}
12091 
12092 			std::string sceneidx = request::findValue(&req, "sceneidx");
12093 			std::string idx = request::findValue(&req, "idx");
12094 			std::string code = request::findValue(&req, "code");
12095 			if (
12096 				(idx.empty()) ||
12097 				(sceneidx.empty()) ||
12098 				(code.empty())
12099 				)
12100 				return;
12101 			root["status"] = "OK";
12102 			root["title"] = "RemoveSceneCode";
12103 
12104 			std::vector<std::vector<std::string> > result;
12105 			result = m_sql.safe_query("SELECT Activators, SceneType FROM Scenes WHERE (ID==%q)", sceneidx.c_str());
12106 			if (result.empty())
12107 				return;
12108 			std::string Activators = result[0][0];
12109 			int SceneType = atoi(result[0][1].c_str());
12110 			if (!Activators.empty())
12111 			{
12112 				//Get Activator device names
12113 				std::vector<std::string> arrayActivators;
12114 				StringSplit(Activators, ";", arrayActivators);
12115 				std::string newActivation = "";
12116 				for (const auto & ittAct : arrayActivators)
12117 				{
12118 					std::string sCodeCmd = ittAct;
12119 
12120 					std::vector<std::string> arrayCode;
12121 					StringSplit(sCodeCmd, ":", arrayCode);
12122 
12123 					std::string sID = arrayCode[0];
12124 					std::string sCode = "";
12125 					if (arrayCode.size() == 2)
12126 					{
12127 						sCode = arrayCode[1];
12128 					}
12129 					bool bFound = false;
12130 					if (sID == idx)
12131 					{
12132 						if ((SceneType == 1) || (sCode.empty()))
12133 						{
12134 							bFound = true;
12135 						}
12136 						else
12137 						{
12138 							//Also check the code
12139 							bFound = (sCode == code);
12140 						}
12141 					}
12142 					if (!bFound)
12143 					{
12144 						if (!newActivation.empty())
12145 							newActivation += ";";
12146 						newActivation += sID;
12147 						if ((SceneType == 0) && (!sCode.empty()))
12148 						{
12149 							newActivation += ":" + sCode;
12150 						}
12151 					}
12152 				}
12153 				if (Activators != newActivation)
12154 				{
12155 					m_sql.safe_query("UPDATE Scenes SET Activators='%q' WHERE (ID==%q)", newActivation.c_str(), sceneidx.c_str());
12156 				}
12157 			}
12158 		}
12159 
Cmd_ClearSceneCodes(WebEmSession & session,const request & req,Json::Value & root)12160 		void CWebServer::Cmd_ClearSceneCodes(WebEmSession & session, const request& req, Json::Value &root)
12161 		{
12162 			if (session.rights != 2)
12163 			{
12164 				session.reply_status = reply::forbidden;
12165 				return; //Only admin user allowed
12166 			}
12167 
12168 			std::string sceneidx = request::findValue(&req, "sceneidx");
12169 			if (sceneidx.empty())
12170 				return;
12171 			root["status"] = "OK";
12172 			root["title"] = "ClearSceneCode";
12173 
12174 			m_sql.safe_query("UPDATE Scenes SET Activators='' WHERE (ID==%q)", sceneidx.c_str());
12175 		}
12176 
Cmd_GetSerialDevices(WebEmSession & session,const request & req,Json::Value & root)12177 		void CWebServer::Cmd_GetSerialDevices(WebEmSession & session, const request& req, Json::Value &root)
12178 		{
12179 			root["status"] = "OK";
12180 			root["title"] = "GetSerialDevices";
12181 
12182 			bool bUseDirectPath = false;
12183 			std::vector<std::string> serialports = GetSerialPorts(bUseDirectPath);
12184 			int ii = 0;
12185 			for (const auto & itt : serialports)
12186 			{
12187 				root["result"][ii]["name"] = itt;
12188 				root["result"][ii]["value"] = ii;
12189 				ii++;
12190 			}
12191 		}
12192 
Cmd_GetDevicesList(WebEmSession & session,const request & req,Json::Value & root)12193 		void CWebServer::Cmd_GetDevicesList(WebEmSession & session, const request& req, Json::Value &root)
12194 		{
12195 			root["status"] = "OK";
12196 			root["title"] = "GetDevicesList";
12197 			int ii = 0;
12198 			std::vector<std::vector<std::string> > result;
12199 			result = m_sql.safe_query("SELECT ID, Name FROM DeviceStatus WHERE (Used == 1) ORDER BY Name");
12200 			if (!result.empty())
12201 			{
12202 				for (const auto & itt : result)
12203 				{
12204 					std::vector<std::string> sd = itt;
12205 					root["result"][ii]["name"] = sd[1];
12206 					root["result"][ii]["value"] = sd[0];
12207 					ii++;
12208 				}
12209 			}
12210 		}
12211 
Post_UploadCustomIcon(WebEmSession & session,const request & req,reply & rep)12212 		void CWebServer::Post_UploadCustomIcon(WebEmSession & session, const request& req, reply & rep)
12213 		{
12214 			Json::Value root;
12215 			root["title"] = "UploadCustomIcon";
12216 			root["status"] = "ERROR";
12217 			root["error"] = "Invalid";
12218 			//Only admin user allowed
12219 			if (session.rights != 2)
12220 			{
12221 				session.reply_status = reply::forbidden;
12222 				return; //Only admin user allowed
12223 			}
12224 			std::string zipfile = request::findValue(&req, "file");
12225 			if (zipfile != "")
12226 			{
12227 				std::string ErrorMessage;
12228 				bool bOK = m_sql.InsertCustomIconFromZip(zipfile, ErrorMessage);
12229 				if (bOK)
12230 				{
12231 					root["status"] = "OK";
12232 				}
12233 				else
12234 				{
12235 					root["status"] = "ERROR";
12236 					root["error"] = ErrorMessage;
12237 				}
12238 			}
12239 			std::string jcallback = request::findValue(&req, "jsoncallback");
12240 			if (jcallback.size() == 0) {
12241 				reply::set_content(&rep, root.toStyledString());
12242 				return;
12243 			}
12244 			reply::set_content(&rep, "var data=" + root.toStyledString() + '\n' + jcallback + "(data);");
12245 		}
12246 
Cmd_GetCustomIconSet(WebEmSession & session,const request & req,Json::Value & root)12247 		void CWebServer::Cmd_GetCustomIconSet(WebEmSession & session, const request& req, Json::Value &root)
12248 		{
12249 			root["status"] = "OK";
12250 			root["title"] = "GetCustomIconSet";
12251 			int ii = 0;
12252 			for (const auto & itt : m_custom_light_icons)
12253 			{
12254 				if (itt.idx >= 100)
12255 				{
12256 					std::string IconFile16 = "images/" + itt.RootFile + ".png";
12257 					std::string IconFile48On = "images/" + itt.RootFile + "48_On.png";
12258 					std::string IconFile48Off = "images/" + itt.RootFile + "48_Off.png";
12259 
12260 					root["result"][ii]["idx"] = itt.idx - 100;
12261 					root["result"][ii]["Title"] = itt.Title;
12262 					root["result"][ii]["Description"] = itt.Description;
12263 					root["result"][ii]["IconFile16"] = IconFile16;
12264 					root["result"][ii]["IconFile48On"] = IconFile48On;
12265 					root["result"][ii]["IconFile48Off"] = IconFile48Off;
12266 					ii++;
12267 				}
12268 			}
12269 		}
12270 
Cmd_DeleteCustomIcon(WebEmSession & session,const request & req,Json::Value & root)12271 		void CWebServer::Cmd_DeleteCustomIcon(WebEmSession & session, const request& req, Json::Value &root)
12272 		{
12273 			if (session.rights != 2)
12274 			{
12275 				session.reply_status = reply::forbidden;
12276 				return; //Only admin user allowed
12277 			}
12278 
12279 			std::string sidx = request::findValue(&req, "idx");
12280 			if (sidx.empty())
12281 				return;
12282 			int idx = atoi(sidx.c_str());
12283 			root["status"] = "OK";
12284 			root["title"] = "DeleteCustomIcon";
12285 
12286 			m_sql.safe_query("DELETE FROM CustomImages WHERE (ID == %d)", idx);
12287 
12288 			//Delete icons file from disk
12289 			for (const auto & itt : m_custom_light_icons)
12290 			{
12291 				if (itt.idx == idx + 100)
12292 				{
12293 					std::string IconFile16 = szWWWFolder + "/images/" + itt.RootFile + ".png";
12294 					std::string IconFile48On = szWWWFolder + "/images/" + itt.RootFile + "48_On.png";
12295 					std::string IconFile48Off = szWWWFolder + "/images/" + itt.RootFile + "48_Off.png";
12296 					std::remove(IconFile16.c_str());
12297 					std::remove(IconFile48On.c_str());
12298 					std::remove(IconFile48Off.c_str());
12299 					break;
12300 				}
12301 			}
12302 			ReloadCustomSwitchIcons();
12303 		}
12304 
Cmd_UpdateCustomIcon(WebEmSession & session,const request & req,Json::Value & root)12305 		void CWebServer::Cmd_UpdateCustomIcon(WebEmSession & session, const request& req, Json::Value &root)
12306 		{
12307 			if (session.rights != 2)
12308 			{
12309 				session.reply_status = reply::forbidden;
12310 				return; //Only admin user allowed
12311 			}
12312 
12313 			std::string sidx = request::findValue(&req, "idx");
12314 			std::string sname = HTMLSanitizer::Sanitize(request::findValue(&req, "name"));
12315 			std::string sdescription = HTMLSanitizer::Sanitize(request::findValue(&req, "description"));
12316 			if (
12317 				(sidx.empty()) ||
12318 				(sname.empty()) ||
12319 				(sdescription.empty())
12320 				)
12321 				return;
12322 
12323 			int idx = atoi(sidx.c_str());
12324 			root["status"] = "OK";
12325 			root["title"] = "UpdateCustomIcon";
12326 
12327 			m_sql.safe_query("UPDATE CustomImages SET Name='%q', Description='%q' WHERE (ID == %d)", sname.c_str(), sdescription.c_str(), idx);
12328 			ReloadCustomSwitchIcons();
12329 		}
12330 
Cmd_RenameDevice(WebEmSession & session,const request & req,Json::Value & root)12331 		void CWebServer::Cmd_RenameDevice(WebEmSession & session, const request& req, Json::Value &root)
12332 		{
12333 			if (session.rights != 2)
12334 			{
12335 				session.reply_status = reply::forbidden;
12336 				return; //Only admin user allowed
12337 			}
12338 
12339 			std::string sidx = request::findValue(&req, "idx");
12340 			std::string sname = HTMLSanitizer::Sanitize(request::findValue(&req, "name"));
12341 			if (
12342 				(sidx.empty()) ||
12343 				(sname.empty())
12344 				)
12345 				return;
12346 			int idx = atoi(sidx.c_str());
12347 			root["status"] = "OK";
12348 			root["title"] = "RenameDevice";
12349 
12350 			m_sql.safe_query("UPDATE DeviceStatus SET Name='%q' WHERE (ID == %d)", sname.c_str(), idx);
12351 			uint64_t ullidx = std::strtoull(sidx.c_str(), nullptr, 10);
12352 			m_mainworker.m_eventsystem.WWWUpdateSingleState(ullidx, sname, m_mainworker.m_eventsystem.REASON_DEVICE);
12353 
12354 #ifdef ENABLE_PYTHON
12355 			// Notify plugin framework about the change
12356 			m_mainworker.m_pluginsystem.DeviceModified(idx);
12357 #endif
12358 		}
12359 
Cmd_RenameScene(WebEmSession & session,const request & req,Json::Value & root)12360 		void CWebServer::Cmd_RenameScene(WebEmSession & session, const request& req, Json::Value &root)
12361 		{
12362 			if (session.rights != 2)
12363 			{
12364 				session.reply_status = reply::forbidden;
12365 				return; //Only admin user allowed
12366 			}
12367 
12368 			std::string sidx = request::findValue(&req, "idx");
12369 			std::string sname = HTMLSanitizer::Sanitize(request::findValue(&req, "name"));
12370 			if (
12371 				(sidx.empty()) ||
12372 				(sname.empty())
12373 				)
12374 				return;
12375 			int idx = atoi(sidx.c_str());
12376 			root["status"] = "OK";
12377 			root["title"] = "RenameScene";
12378 
12379 			m_sql.safe_query("UPDATE Scenes SET Name='%q' WHERE (ID == %d)", sname.c_str(), idx);
12380 			uint64_t ullidx = std::strtoull(sidx.c_str(), nullptr, 10);
12381 			m_mainworker.m_eventsystem.WWWUpdateSingleState(ullidx, sname, m_mainworker.m_eventsystem.REASON_SCENEGROUP);
12382 		}
12383 
Cmd_SetUnused(WebEmSession & session,const request & req,Json::Value & root)12384 		void CWebServer::Cmd_SetUnused(WebEmSession & session, const request& req, Json::Value &root)
12385 		{
12386 			if (session.rights != 2)
12387 			{
12388 				session.reply_status = reply::forbidden;
12389 				return; //Only admin user allowed
12390 			}
12391 
12392 			std::string sidx = request::findValue(&req, "idx");
12393 			if (sidx.empty())
12394 				return;
12395 			int idx = atoi(sidx.c_str());
12396 			root["status"] = "OK";
12397 			root["title"] = "SetUnused";
12398 			m_sql.safe_query("UPDATE DeviceStatus SET Used=0 WHERE (ID == %d)", idx);
12399 			if (m_sql.m_bEnableEventSystem)
12400 				m_mainworker.m_eventsystem.RemoveSingleState(idx, m_mainworker.m_eventsystem.REASON_DEVICE);
12401 
12402 #ifdef ENABLE_PYTHON
12403 			// Notify plugin framework about the change
12404 			m_mainworker.m_pluginsystem.DeviceModified(idx);
12405 #endif
12406 		}
12407 
Cmd_AddLogMessage(WebEmSession & session,const request & req,Json::Value & root)12408 		void CWebServer::Cmd_AddLogMessage(WebEmSession & session, const request& req, Json::Value &root)
12409 		{
12410 			std::string smessage = request::findValue(&req, "message");
12411 			if (smessage.empty())
12412 				return;
12413 			root["status"] = "OK";
12414 			root["title"] = "AddLogMessage";
12415 
12416 			_log.Log(LOG_STATUS, "%s", smessage.c_str());
12417 		}
12418 
Cmd_ClearShortLog(WebEmSession & session,const request & req,Json::Value & root)12419 		void CWebServer::Cmd_ClearShortLog(WebEmSession & session, const request& req, Json::Value &root)
12420 		{
12421 			if (session.rights != 2)
12422 			{
12423 				session.reply_status = reply::forbidden;
12424 				return; //Only admin user allowed
12425 			}
12426 			root["status"] = "OK";
12427 			root["title"] = "ClearShortLog";
12428 
12429 			_log.Log(LOG_STATUS, "Clearing Short Log...");
12430 
12431 			m_sql.ClearShortLog();
12432 
12433 			_log.Log(LOG_STATUS, "Short Log Cleared!");
12434 		}
12435 
Cmd_VacuumDatabase(WebEmSession & session,const request & req,Json::Value & root)12436 		void CWebServer::Cmd_VacuumDatabase(WebEmSession & session, const request& req, Json::Value &root)
12437 		{
12438 			if (session.rights != 2)
12439 			{
12440 				session.reply_status = reply::forbidden;
12441 				return; //Only admin user allowed
12442 			}
12443 			root["status"] = "OK";
12444 			root["title"] = "VacuumDatabase";
12445 
12446 			m_sql.VacuumDatabase();
12447 		}
12448 
Cmd_AddMobileDevice(WebEmSession & session,const request & req,Json::Value & root)12449 		void CWebServer::Cmd_AddMobileDevice(WebEmSession & session, const request& req, Json::Value &root)
12450 		{
12451 			std::string suuid = HTMLSanitizer::Sanitize(request::findValue(&req, "uuid"));
12452 			std::string ssenderid = HTMLSanitizer::Sanitize(request::findValue(&req, "senderid"));
12453 			std::string sname = HTMLSanitizer::Sanitize(request::findValue(&req, "name"));
12454 			std::string sdevtype = HTMLSanitizer::Sanitize(request::findValue(&req, "devicetype"));
12455 			std::string sactive = request::findValue(&req, "active");
12456 			if (
12457 				(suuid.empty()) ||
12458 				(ssenderid.empty())
12459 				)
12460 				return;
12461 			root["status"] = "OK";
12462 			root["title"] = "AddMobileDevice";
12463 
12464 			if (sactive.empty())
12465 				sactive = "1";
12466 			int iActive = (sactive == "1") ? 1 : 0;
12467 
12468 			std::vector<std::vector<std::string> > result;
12469 			result = m_sql.safe_query("SELECT ID, Name, DeviceType FROM MobileDevices WHERE (UUID=='%q')", suuid.c_str());
12470 			if (result.empty())
12471 			{
12472 				//New
12473 				m_sql.safe_query("INSERT INTO MobileDevices (Active,UUID,SenderID,Name,DeviceType) VALUES (%d,'%q','%q','%q','%q')",
12474 					iActive,
12475 					suuid.c_str(),
12476 					ssenderid.c_str(),
12477 					sname.c_str(),
12478 					sdevtype.c_str());
12479 			}
12480 			else
12481 			{
12482 				//Update
12483 				time_t now = mytime(NULL);
12484 				struct tm ltime;
12485 				localtime_r(&now, &ltime);
12486 				m_sql.safe_query("UPDATE MobileDevices SET Active=%d, SenderID='%q', LastUpdate='%04d-%02d-%02d %02d:%02d:%02d' WHERE (UUID == '%q')",
12487 					iActive,
12488 					ssenderid.c_str(),
12489 					ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday, ltime.tm_hour, ltime.tm_min, ltime.tm_sec,
12490 					suuid.c_str()
12491 				);
12492 
12493 				std::string dname = result[0][1];
12494 				std::string ddevtype = result[0][2];
12495 				if (dname.empty() || ddevtype.empty())
12496 				{
12497 					m_sql.safe_query("UPDATE MobileDevices SET Name='%q', DeviceType='%q' WHERE (UUID == '%q')",
12498 						sname.c_str(), sdevtype.c_str(),
12499 						suuid.c_str()
12500 					);
12501 				}
12502 			}
12503 		}
12504 
Cmd_UpdateMobileDevice(WebEmSession & session,const request & req,Json::Value & root)12505 		void CWebServer::Cmd_UpdateMobileDevice(WebEmSession & session, const request& req, Json::Value &root)
12506 		{
12507 			if (session.rights != 2)
12508 			{
12509 				session.reply_status = reply::forbidden;
12510 				return; //Only admin user allowed
12511 			}
12512 			std::string sidx = request::findValue(&req, "idx");
12513 			std::string enabled = request::findValue(&req, "enabled");
12514 			std::string name = HTMLSanitizer::Sanitize(request::findValue(&req, "name"));
12515 
12516 			if (
12517 				(sidx.empty()) ||
12518 				(enabled.empty()) ||
12519 				(name.empty())
12520 				)
12521 				return;
12522 			uint64_t idx = std::strtoull(sidx.c_str(), nullptr, 10);
12523 
12524 			m_sql.safe_query("UPDATE MobileDevices SET Name='%q', Active=%d WHERE (ID==%" PRIu64 ")",
12525 				name.c_str(), (enabled == "true") ? 1 : 0, idx);
12526 
12527 			root["status"] = "OK";
12528 			root["title"] = "UpdateMobile";
12529 		}
12530 
Cmd_DeleteMobileDevice(WebEmSession & session,const request & req,Json::Value & root)12531 		void CWebServer::Cmd_DeleteMobileDevice(WebEmSession & session, const request& req, Json::Value &root)
12532 		{
12533 			if (session.rights != 2)
12534 			{
12535 				session.reply_status = reply::forbidden;
12536 				return; //Only admin user allowed
12537 			}
12538 			std::string suuid = request::findValue(&req, "uuid");
12539 			if (suuid.empty())
12540 				return;
12541 			std::vector<std::vector<std::string> > result;
12542 			result = m_sql.safe_query("SELECT ID FROM MobileDevices WHERE (UUID=='%q')", suuid.c_str());
12543 			if (result.empty())
12544 				return;
12545 			m_sql.safe_query("DELETE FROM MobileDevices WHERE (UUID == '%q')", suuid.c_str());
12546 			root["status"] = "OK";
12547 			root["title"] = "DeleteMobileDevice";
12548 		}
12549 
12550 
RType_GetTransfers(WebEmSession & session,const request & req,Json::Value & root)12551 		void CWebServer::RType_GetTransfers(WebEmSession & session, const request& req, Json::Value &root)
12552 		{
12553 			root["status"] = "OK";
12554 			root["title"] = "GetTransfers";
12555 
12556 			uint64_t idx = 0;
12557 			if (request::findValue(&req, "idx") != "")
12558 			{
12559 				idx = std::strtoull(request::findValue(&req, "idx").c_str(), nullptr, 10);
12560 			}
12561 
12562 			std::vector<std::vector<std::string> > result;
12563 			result = m_sql.safe_query("SELECT Type, SubType FROM DeviceStatus WHERE (ID==%" PRIu64 ")",
12564 				idx);
12565 			if (!result.empty())
12566 			{
12567 				int dType = atoi(result[0][0].c_str());
12568 				if (
12569 					(dType == pTypeTEMP)
12570 					|| (dType == pTypeTEMP_HUM)
12571 					|| (dType == pTypeTEMP_HUM_BARO)
12572 					)
12573 				{
12574 					result = m_sql.safe_query(
12575 						"SELECT ID, Name FROM DeviceStatus WHERE (Type=='%q') AND (ID!=%" PRIu64 ")",
12576 						result[0][0].c_str(), idx);
12577 				}
12578 				else
12579 				{
12580 					result = m_sql.safe_query(
12581 						"SELECT ID, Name FROM DeviceStatus WHERE (Type=='%q') AND (SubType=='%q') AND (ID!=%" PRIu64 ")",
12582 						result[0][0].c_str(), result[0][1].c_str(), idx);
12583 				}
12584 
12585 				std::sort(std::begin(result), std::end(result), [](std::vector<std::string> a, std::vector<std::string> b) {return a[1] < b[1]; });
12586 
12587 				int ii = 0;
12588 				for (const auto & itt : result)
12589 				{
12590 					std::vector<std::string> sd = itt;
12591 
12592 					root["result"][ii]["idx"] = sd[0];
12593 					root["result"][ii]["Name"] = sd[1];
12594 					ii++;
12595 				}
12596 			}
12597 		}
12598 
12599 		//Will transfer Newest sensor log to OLD sensor,
12600 		//then set the HardwareID/DeviceID/Unit/Name/Type/Subtype/Unit for the OLD sensor to the NEW sensor ID/Type/Subtype/Unit
12601 		//then delete the NEW sensor
RType_TransferDevice(WebEmSession & session,const request & req,Json::Value & root)12602 		void CWebServer::RType_TransferDevice(WebEmSession & session, const request& req, Json::Value &root)
12603 		{
12604 			std::string sidx = request::findValue(&req, "idx");
12605 			if (sidx.empty())
12606 				return;
12607 
12608 			std::string newidx = request::findValue(&req, "newidx");
12609 			if (newidx.empty())
12610 				return;
12611 
12612 			std::vector<std::vector<std::string> > result;
12613 
12614 			//Check which device is newer
12615 
12616 			time_t now = mytime(NULL);
12617 			struct tm tm1;
12618 			localtime_r(&now, &tm1);
12619 			struct tm LastUpdateTime_A;
12620 			struct tm LastUpdateTime_B;
12621 
12622 			result = m_sql.safe_query(
12623 				"SELECT A.LastUpdate, B.LastUpdate FROM DeviceStatus as A, DeviceStatus as B WHERE (A.ID == '%q') AND (B.ID == '%q')",
12624 				sidx.c_str(), newidx.c_str());
12625 			if (result.empty())
12626 				return;
12627 
12628 			std::string sLastUpdate_A = result[0][0];
12629 			std::string sLastUpdate_B = result[0][1];
12630 
12631 			time_t timeA, timeB;
12632 			ParseSQLdatetime(timeA, LastUpdateTime_A, sLastUpdate_A, tm1.tm_isdst);
12633 			ParseSQLdatetime(timeB, LastUpdateTime_B, sLastUpdate_B, tm1.tm_isdst);
12634 
12635 			if (timeA < timeB)
12636 			{
12637 				//Swap idx with newidx
12638 				sidx.swap(newidx);
12639 			}
12640 
12641 			result = m_sql.safe_query(
12642 				"SELECT HardwareID, DeviceID, Unit, Name, Type, SubType, SignalLevel, BatteryLevel, nValue, sValue FROM DeviceStatus WHERE (ID == '%q')",
12643 				newidx.c_str());
12644 			if (result.empty())
12645 				return;
12646 
12647 			root["status"] = "OK";
12648 			root["title"] = "TransferDevice";
12649 
12650 			//transfer device logs (new to old)
12651 			m_sql.TransferDevice(newidx, sidx);
12652 
12653 			//now delete the NEW device
12654 			m_sql.DeleteDevices(newidx);
12655 
12656 			m_mainworker.m_scheduler.ReloadSchedules();
12657 		}
12658 
RType_Notifications(WebEmSession & session,const request & req,Json::Value & root)12659 		void CWebServer::RType_Notifications(WebEmSession & session, const request& req, Json::Value &root)
12660 		{
12661 			root["status"] = "OK";
12662 			root["title"] = "Notifications";
12663 
12664 			int ii = 0;
12665 
12666 			//Add known notification systems
12667 			for (const auto & ittNotifiers : m_notifications.m_notifiers)
12668 			{
12669 				root["notifiers"][ii]["name"] = ittNotifiers.first;
12670 				root["notifiers"][ii]["description"] = ittNotifiers.first;
12671 				ii++;
12672 			}
12673 
12674 			uint64_t idx = 0;
12675 			if (request::findValue(&req, "idx") != "")
12676 			{
12677 				idx = std::strtoull(request::findValue(&req, "idx").c_str(), nullptr, 10);
12678 			}
12679 			std::vector<_tNotification> notifications = m_notifications.GetNotifications(idx);
12680 			if (notifications.size() > 0)
12681 			{
12682 				ii = 0;
12683 				for (const auto & itt : notifications)
12684 				{
12685 					root["result"][ii]["idx"] = itt.ID;
12686 					std::string sParams = itt.Params;
12687 					if (sParams.empty()) {
12688 						sParams = "S";
12689 					}
12690 					root["result"][ii]["Params"] = sParams;
12691 					root["result"][ii]["Priority"] = itt.Priority;
12692 					root["result"][ii]["SendAlways"] = itt.SendAlways;
12693 					root["result"][ii]["CustomMessage"] = itt.CustomMessage;
12694 					root["result"][ii]["ActiveSystems"] = itt.ActiveSystems;
12695 					ii++;
12696 				}
12697 			}
12698 		}
12699 
RType_GetSharedUserDevices(WebEmSession & session,const request & req,Json::Value & root)12700 		void CWebServer::RType_GetSharedUserDevices(WebEmSession & session, const request& req, Json::Value &root)
12701 		{
12702 			std::string idx = request::findValue(&req, "idx");
12703 			if (idx.empty())
12704 				return;
12705 			root["status"] = "OK";
12706 			root["title"] = "GetSharedUserDevices";
12707 
12708 			std::vector<std::vector<std::string> > result;
12709 			result = m_sql.safe_query("SELECT DeviceRowID FROM SharedDevices WHERE (SharedUserID == '%q')", idx.c_str());
12710 			if (!result.empty())
12711 			{
12712 				int ii = 0;
12713 				for (const auto & itt : result)
12714 				{
12715 					std::vector<std::string> sd = itt;
12716 					root["result"][ii]["DeviceRowIdx"] = sd[0];
12717 					ii++;
12718 				}
12719 			}
12720 		}
12721 
RType_SetSharedUserDevices(WebEmSession & session,const request & req,Json::Value & root)12722 		void CWebServer::RType_SetSharedUserDevices(WebEmSession & session, const request& req, Json::Value &root)
12723 		{
12724 			std::string idx = request::findValue(&req, "idx");
12725 			std::string userdevices = request::findValue(&req, "devices");
12726 			if (idx.empty())
12727 				return;
12728 			root["status"] = "OK";
12729 			root["title"] = "SetSharedUserDevices";
12730 			std::vector<std::string> strarray;
12731 			StringSplit(userdevices, ";", strarray);
12732 
12733 			//First delete all devices for this user, then add the (new) onces
12734 			m_sql.safe_query("DELETE FROM SharedDevices WHERE (SharedUserID == '%q')", idx.c_str());
12735 
12736 			int nDevices = static_cast<int>(strarray.size());
12737 			for (int ii = 0; ii < nDevices; ii++)
12738 			{
12739 				m_sql.safe_query("INSERT INTO SharedDevices (SharedUserID,DeviceRowID) VALUES ('%q','%q')", idx.c_str(), strarray[ii].c_str());
12740 			}
12741 			LoadUsers();
12742 		}
12743 
RType_SetUsed(WebEmSession & session,const request & req,Json::Value & root)12744 		void CWebServer::RType_SetUsed(WebEmSession & session, const request& req, Json::Value &root)
12745 		{
12746 			if (session.rights != 2)
12747 			{
12748 				session.reply_status = reply::forbidden;
12749 				return; //Only admin user allowed
12750 			}
12751 
12752 			std::string idx = request::findValue(&req, "idx");
12753 			std::string deviceid = request::findValue(&req, "deviceid");
12754 			std::string name = HTMLSanitizer::Sanitize(request::findValue(&req, "name"));
12755 			std::string description = HTMLSanitizer::Sanitize(request::findValue(&req, "description"));
12756 			std::string sused = request::findValue(&req, "used");
12757 			std::string sswitchtype = request::findValue(&req, "switchtype");
12758 			std::string maindeviceidx = request::findValue(&req, "maindeviceidx");
12759 			std::string addjvalue = request::findValue(&req, "addjvalue");
12760 			std::string addjmulti = request::findValue(&req, "addjmulti");
12761 			std::string addjvalue2 = request::findValue(&req, "addjvalue2");
12762 			std::string addjmulti2 = request::findValue(&req, "addjmulti2");
12763 			std::string setPoint = request::findValue(&req, "setpoint");
12764 			std::string state = request::findValue(&req, "state");
12765 			std::string mode = request::findValue(&req, "mode");
12766 			std::string until = request::findValue(&req, "until");
12767 			std::string clock = request::findValue(&req, "clock");
12768 			std::string tmode = request::findValue(&req, "tmode");
12769 			std::string fmode = request::findValue(&req, "fmode");
12770 			std::string sCustomImage = request::findValue(&req, "customimage");
12771 
12772 			std::string strunit = request::findValue(&req, "unit");
12773 			std::string strParam1 = HTMLSanitizer::Sanitize(base64_decode(request::findValue(&req, "strparam1")));
12774 			std::string strParam2 = HTMLSanitizer::Sanitize(base64_decode(request::findValue(&req, "strparam2")));
12775 			std::string tmpstr = request::findValue(&req, "protected");
12776 			bool bHasstrParam1 = request::hasValue(&req, "strparam1");
12777 			int iProtected = (tmpstr == "true") ? 1 : 0;
12778 
12779 			std::string sOptions = HTMLSanitizer::Sanitize(base64_decode(request::findValue(&req, "options")));
12780 			std::string devoptions = HTMLSanitizer::Sanitize(CURLEncode::URLDecode(request::findValue(&req, "devoptions")));
12781 			std::string EnergyMeterMode = CURLEncode::URLDecode(request::findValue(&req, "EnergyMeterMode"));
12782 
12783 			char szTmp[200];
12784 
12785 			bool bHaveUser = (session.username != "");
12786 			//int iUser = -1;
12787 			if (bHaveUser)
12788 			{
12789 				//iUser = FindUser(session.username.c_str());
12790 			}
12791 
12792 			int switchtype = -1;
12793 			if (sswitchtype != "")
12794 				switchtype = atoi(sswitchtype.c_str());
12795 
12796 			if ((idx.empty()) || (sused.empty()))
12797 				return;
12798 			int used = (sused == "true") ? 1 : 0;
12799 			if (maindeviceidx != "")
12800 				used = 0;
12801 
12802 			int CustomImage = 0;
12803 			if (sCustomImage != "")
12804 				CustomImage = atoi(sCustomImage.c_str());
12805 
12806 			//Strip trailing spaces in 'name'
12807 			name = stdstring_trim(name);
12808 
12809 			//Strip trailing spaces in 'description'
12810 			description = stdstring_trim(description);
12811 
12812 			std::vector<std::vector<std::string> > result;
12813 
12814 			result = m_sql.safe_query("SELECT Type,SubType,HardwareID FROM DeviceStatus WHERE (ID == '%q')", idx.c_str());
12815 			if (result.empty())
12816 				return;
12817 			std::vector<std::string> sd = result[0];
12818 
12819 			unsigned char dType = atoi(sd[0].c_str());
12820 			//unsigned char dSubType=atoi(sd[1].c_str());
12821 			int HwdID = atoi(sd[2].c_str());
12822 			std::string sHwdID = sd[2];
12823 
12824 			if (setPoint != "" || state != "")
12825 			{
12826 				double tempcelcius = atof(setPoint.c_str());
12827 				if (m_sql.m_tempunit == TEMPUNIT_F)
12828 				{
12829 					//Convert back to Celsius
12830 					tempcelcius = ConvertToCelsius(tempcelcius);
12831 				}
12832 				sprintf(szTmp, "%.2f", tempcelcius);
12833 
12834 				if (dType != pTypeEvohomeZone && dType != pTypeEvohomeWater)//sql update now done in setsetpoint for evohome devices
12835 				{
12836 					m_sql.safe_query("UPDATE DeviceStatus SET Used=%d, sValue='%q' WHERE (ID == '%q')",
12837 						used, szTmp, idx.c_str());
12838 				}
12839 			}
12840 			if (name.empty())
12841 			{
12842 				m_sql.safe_query("UPDATE DeviceStatus SET Used=%d WHERE (ID == '%q')",
12843 					used, idx.c_str());
12844 			}
12845 			else
12846 			{
12847 				if (switchtype == -1)
12848 				{
12849 					m_sql.safe_query("UPDATE DeviceStatus SET Used=%d, Name='%q', Description='%q' WHERE (ID == '%q')",
12850 						used, name.c_str(), description.c_str(), idx.c_str());
12851 				}
12852 				else
12853 				{
12854 					m_sql.safe_query(
12855 						"UPDATE DeviceStatus SET Used=%d, Name='%q', Description='%q', SwitchType=%d, CustomImage=%d WHERE (ID == '%q')",
12856 						used, name.c_str(), description.c_str(), switchtype, CustomImage, idx.c_str());
12857 				}
12858 			}
12859 
12860 			if (bHasstrParam1)
12861 			{
12862 				m_sql.safe_query("UPDATE DeviceStatus SET StrParam1='%q', StrParam2='%q' WHERE (ID == '%q')",
12863 					strParam1.c_str(), strParam2.c_str(), idx.c_str());
12864 			}
12865 
12866 			m_sql.safe_query("UPDATE DeviceStatus SET Protected=%d WHERE (ID == '%q')", iProtected, idx.c_str());
12867 
12868 			if (!setPoint.empty() || !state.empty())
12869 			{
12870 				int urights = 3;
12871 				if (bHaveUser)
12872 				{
12873 					int iUser = FindUser(session.username.c_str());
12874 					if (iUser != -1)
12875 					{
12876 						urights = static_cast<int>(m_users[iUser].userrights);
12877 						_log.Log(LOG_STATUS, "User: %s initiated a SetPoint command", m_users[iUser].Username.c_str());
12878 					}
12879 				}
12880 				if (urights < 1)
12881 					return;
12882 				if (dType == pTypeEvohomeWater)
12883 					m_mainworker.SetSetPoint(idx, (state == "On") ? 1.0f : 0.0f, mode, until);//FIXME float not guaranteed precise?
12884 				else if (dType == pTypeEvohomeZone)
12885 					m_mainworker.SetSetPoint(idx, static_cast<float>(atof(setPoint.c_str())), mode, until);
12886 				else
12887 					m_mainworker.SetSetPoint(idx, static_cast<float>(atof(setPoint.c_str())));
12888 			}
12889 			else if (!clock.empty())
12890 			{
12891 				int urights = 3;
12892 				if (bHaveUser)
12893 				{
12894 					int iUser = FindUser(session.username.c_str());
12895 					if (iUser != -1)
12896 					{
12897 						urights = static_cast<int>(m_users[iUser].userrights);
12898 						_log.Log(LOG_STATUS, "User: %s initiated a SetClock command", m_users[iUser].Username.c_str());
12899 					}
12900 				}
12901 				if (urights < 1)
12902 					return;
12903 				m_mainworker.SetClock(idx, clock);
12904 			}
12905 			else if (!tmode.empty())
12906 			{
12907 				int urights = 3;
12908 				if (bHaveUser)
12909 				{
12910 					int iUser = FindUser(session.username.c_str());
12911 					if (iUser != -1)
12912 					{
12913 						urights = static_cast<int>(m_users[iUser].userrights);
12914 						_log.Log(LOG_STATUS, "User: %s initiated a Thermostat Mode command", m_users[iUser].Username.c_str());
12915 					}
12916 				}
12917 				if (urights < 1)
12918 					return;
12919 				m_mainworker.SetZWaveThermostatMode(idx, atoi(tmode.c_str()));
12920 			}
12921 			else if (!fmode.empty())
12922 			{
12923 				int urights = 3;
12924 				if (bHaveUser)
12925 				{
12926 					int iUser = FindUser(session.username.c_str());
12927 					if (iUser != -1)
12928 					{
12929 						urights = static_cast<int>(m_users[iUser].userrights);
12930 						_log.Log(LOG_STATUS, "User: %s initiated a Thermostat Fan Mode command", m_users[iUser].Username.c_str());
12931 					}
12932 				}
12933 				if (urights < 1)
12934 					return;
12935 				m_mainworker.SetZWaveThermostatFanMode(idx, atoi(fmode.c_str()));
12936 			}
12937 
12938 			if (!strunit.empty())
12939 			{
12940 				bool bUpdateUnit = true;
12941 #ifdef ENABLE_PYTHON
12942 				//check if HW is plugin
12943 				std::vector<std::vector<std::string> > result;
12944 				result = m_sql.safe_query("SELECT Type FROM Hardware WHERE (ID == %d)", HwdID);
12945 				if (!result.empty())
12946 				{
12947 					std::vector<std::string> sd = result[0];
12948 					_eHardwareTypes Type = (_eHardwareTypes)atoi(sd[0].c_str());
12949 					if (Type == HTYPE_PythonPlugin)
12950 					{
12951 						bUpdateUnit = false;
12952 						_log.Log(LOG_ERROR, "CWebServer::RType_SetUsed: Not allowed to change unit of device owned by plugin %u!", HwdID);
12953 					}
12954 				}
12955 #endif
12956 				if (bUpdateUnit)
12957 				{
12958 					m_sql.safe_query("UPDATE DeviceStatus SET Unit='%q' WHERE (ID == '%q')",
12959 						strunit.c_str(), idx.c_str());
12960 				}
12961 			}
12962 			//FIXME evohome ...we need the zone id to update the correct zone...but this should be ok as a generic call?
12963 			if (!deviceid.empty())
12964 			{
12965 				m_sql.safe_query("UPDATE DeviceStatus SET DeviceID='%q' WHERE (ID == '%q')",
12966 					deviceid.c_str(), idx.c_str());
12967 			}
12968 			if (!addjvalue.empty())
12969 			{
12970 				double faddjvalue = atof(addjvalue.c_str());
12971 				m_sql.safe_query("UPDATE DeviceStatus SET AddjValue=%f WHERE (ID == '%q')",
12972 					faddjvalue, idx.c_str());
12973 			}
12974 			if (!addjmulti.empty())
12975 			{
12976 				double faddjmulti = atof(addjmulti.c_str());
12977 				if (faddjmulti == 0)
12978 					faddjmulti = 1;
12979 				m_sql.safe_query("UPDATE DeviceStatus SET AddjMulti=%f WHERE (ID == '%q')",
12980 					faddjmulti, idx.c_str());
12981 			}
12982 			if (!addjvalue2.empty())
12983 			{
12984 				double faddjvalue2 = atof(addjvalue2.c_str());
12985 				m_sql.safe_query("UPDATE DeviceStatus SET AddjValue2=%f WHERE (ID == '%q')",
12986 					faddjvalue2, idx.c_str());
12987 			}
12988 			if (!addjmulti2.empty())
12989 			{
12990 				double faddjmulti2 = atof(addjmulti2.c_str());
12991 				if (faddjmulti2 == 0)
12992 					faddjmulti2 = 1;
12993 				m_sql.safe_query("UPDATE DeviceStatus SET AddjMulti2=%f WHERE (ID == '%q')",
12994 					faddjmulti2, idx.c_str());
12995 			}
12996 			if (!EnergyMeterMode.empty())
12997 			{
12998 				auto options = m_sql.GetDeviceOptions(idx);
12999 				options["EnergyMeterMode"] = EnergyMeterMode;
13000 				uint64_t ullidx = std::strtoull(idx.c_str(), nullptr, 10);
13001 				m_sql.SetDeviceOptions(ullidx, options);
13002 			}
13003 
13004 			if (!devoptions.empty())
13005 			{
13006 				m_sql.safe_query("UPDATE DeviceStatus SET Options='%q' WHERE (ID == '%q')", devoptions.c_str(), idx.c_str());
13007 			}
13008 
13009 			if (used == 0)
13010 			{
13011 				bool bRemoveSubDevices = (request::findValue(&req, "RemoveSubDevices") == "true");
13012 
13013 				if (bRemoveSubDevices)
13014 				{
13015 					//if this device was a slave device, remove it
13016 					m_sql.safe_query("DELETE FROM LightSubDevices WHERE (DeviceRowID == '%q')", idx.c_str());
13017 				}
13018 				m_sql.safe_query("DELETE FROM LightSubDevices WHERE (ParentID == '%q')", idx.c_str());
13019 
13020 				m_sql.safe_query("DELETE FROM Timers WHERE (DeviceRowID == '%q')", idx.c_str());
13021 			}
13022 
13023 			// Save device options
13024 			if (!sOptions.empty())
13025 			{
13026 				uint64_t ullidx = std::strtoull(idx.c_str(), nullptr, 10);
13027 				m_sql.SetDeviceOptions(ullidx, m_sql.BuildDeviceOptions(sOptions, false));
13028 			}
13029 
13030 			if (maindeviceidx != "")
13031 			{
13032 				if (maindeviceidx != idx)
13033 				{
13034 					//this is a sub device for another light/switch
13035 					//first check if it is not already a sub device
13036 					result = m_sql.safe_query("SELECT ID FROM LightSubDevices WHERE (DeviceRowID=='%q') AND (ParentID =='%q')",
13037 						idx.c_str(), maindeviceidx.c_str());
13038 					if (result.empty())
13039 					{
13040 						//no it is not, add it
13041 						m_sql.safe_query(
13042 							"INSERT INTO LightSubDevices (DeviceRowID, ParentID) VALUES ('%q','%q')",
13043 							idx.c_str(),
13044 							maindeviceidx.c_str()
13045 						);
13046 					}
13047 				}
13048 			}
13049 			if ((used == 0) && (maindeviceidx.empty()))
13050 			{
13051 				//really remove it, including log etc
13052 				m_sql.DeleteDevices(idx);
13053 			}
13054 			else
13055 			{
13056 #ifdef ENABLE_PYTHON
13057 				// Notify plugin framework about the change
13058 				m_mainworker.m_pluginsystem.DeviceModified(atoi(idx.c_str()));
13059 #endif
13060 			}
13061 			if (!result.empty())
13062 			{
13063 				root["status"] = "OK";
13064 				root["title"] = "SetUsed";
13065 			}
13066 			if (m_sql.m_bEnableEventSystem)
13067 				m_mainworker.m_eventsystem.GetCurrentStates();
13068 		}
13069 
RType_Settings(WebEmSession & session,const request & req,Json::Value & root)13070 		void CWebServer::RType_Settings(WebEmSession & session, const request& req, Json::Value &root)
13071 		{
13072 			std::vector<std::vector<std::string> > result;
13073 			char szTmp[100];
13074 
13075 			result = m_sql.safe_query("SELECT Key, nValue, sValue FROM Preferences");
13076 			if (result.empty())
13077 				return;
13078 			root["status"] = "OK";
13079 			root["title"] = "settings";
13080 #ifndef NOCLOUD
13081 			root["cloudenabled"] = true;
13082 #else
13083 			root["cloudenabled"] = false;
13084 #endif
13085 
13086 			for (const auto & itt : result)
13087 			{
13088 				std::vector<std::string> sd = itt;
13089 				std::string Key = sd[0];
13090 				int nValue = atoi(sd[1].c_str());
13091 				std::string sValue = sd[2];
13092 
13093 				if (Key == "Location")
13094 				{
13095 					std::vector<std::string> strarray;
13096 					StringSplit(sValue, ";", strarray);
13097 
13098 					if (strarray.size() == 2)
13099 					{
13100 						root["Location"]["Latitude"] = strarray[0];
13101 						root["Location"]["Longitude"] = strarray[1];
13102 					}
13103 				}
13104 				/* RK: notification settings */
13105 				if (m_notifications.IsInConfig(Key)) {
13106 					if (sValue.empty() && nValue > 0) {
13107 						root[Key] = nValue;
13108 					}
13109 					else {
13110 						root[Key] = sValue;
13111 					}
13112 				}
13113 				else if (Key == "DashboardType")
13114 				{
13115 					root["DashboardType"] = nValue;
13116 				}
13117 				else if (Key == "MobileType")
13118 				{
13119 					root["MobileType"] = nValue;
13120 				}
13121 				else if (Key == "LightHistoryDays")
13122 				{
13123 					root["LightHistoryDays"] = nValue;
13124 				}
13125 				else if (Key == "5MinuteHistoryDays")
13126 				{
13127 					root["ShortLogDays"] = nValue;
13128 				}
13129 				else if (Key == "ShortLogInterval")
13130 				{
13131 					root["ShortLogInterval"] = nValue;
13132 				}
13133 				else if (Key == "WebUserName")
13134 				{
13135 					root["WebUserName"] = base64_decode(sValue);
13136 				}
13137 				//else if (Key == "WebPassword")
13138 				//{
13139 				//	root["WebPassword"] = sValue;
13140 				//}
13141 				else if (Key == "SecPassword")
13142 				{
13143 					root["SecPassword"] = sValue;
13144 				}
13145 				else if (Key == "ProtectionPassword")
13146 				{
13147 					root["ProtectionPassword"] = sValue;
13148 				}
13149 				else if (Key == "WebLocalNetworks")
13150 				{
13151 					root["WebLocalNetworks"] = sValue;
13152 				}
13153 				else if (Key == "WebRemoteProxyIPs")
13154 				{
13155 					root["WebRemoteProxyIPs"] = sValue;
13156 				}
13157 				else if (Key == "RandomTimerFrame")
13158 				{
13159 					root["RandomTimerFrame"] = nValue;
13160 				}
13161 				else if (Key == "MeterDividerEnergy")
13162 				{
13163 					root["EnergyDivider"] = nValue;
13164 				}
13165 				else if (Key == "MeterDividerGas")
13166 				{
13167 					root["GasDivider"] = nValue;
13168 				}
13169 				else if (Key == "MeterDividerWater")
13170 				{
13171 					root["WaterDivider"] = nValue;
13172 				}
13173 				else if (Key == "ElectricVoltage")
13174 				{
13175 					root["ElectricVoltage"] = nValue;
13176 				}
13177 				else if (Key == "CM113DisplayType")
13178 				{
13179 					root["CM113DisplayType"] = nValue;
13180 				}
13181 				else if (Key == "UseAutoUpdate")
13182 				{
13183 					root["UseAutoUpdate"] = nValue;
13184 				}
13185 				else if (Key == "UseAutoBackup")
13186 				{
13187 					root["UseAutoBackup"] = nValue;
13188 				}
13189 				else if (Key == "Rego6XXType")
13190 				{
13191 					root["Rego6XXType"] = nValue;
13192 				}
13193 				else if (Key == "CostEnergy")
13194 				{
13195 					sprintf(szTmp, "%.4f", (float)(nValue) / 10000.0f);
13196 					root["CostEnergy"] = szTmp;
13197 				}
13198 				else if (Key == "CostEnergyT2")
13199 				{
13200 					sprintf(szTmp, "%.4f", (float)(nValue) / 10000.0f);
13201 					root["CostEnergyT2"] = szTmp;
13202 				}
13203 				else if (Key == "CostEnergyR1")
13204 				{
13205 					sprintf(szTmp, "%.4f", (float)(nValue) / 10000.0f);
13206 					root["CostEnergyR1"] = szTmp;
13207 				}
13208 				else if (Key == "CostEnergyR2")
13209 				{
13210 					sprintf(szTmp, "%.4f", (float)(nValue) / 10000.0f);
13211 					root["CostEnergyR2"] = szTmp;
13212 				}
13213 				else if (Key == "CostGas")
13214 				{
13215 					sprintf(szTmp, "%.4f", (float)(nValue) / 10000.0f);
13216 					root["CostGas"] = szTmp;
13217 				}
13218 				else if (Key == "CostWater")
13219 				{
13220 					sprintf(szTmp, "%.4f", (float)(nValue) / 10000.0f);
13221 					root["CostWater"] = szTmp;
13222 				}
13223 				else if (Key == "ActiveTimerPlan")
13224 				{
13225 					root["ActiveTimerPlan"] = nValue;
13226 				}
13227 				else if (Key == "DoorbellCommand")
13228 				{
13229 					root["DoorbellCommand"] = nValue;
13230 				}
13231 				else if (Key == "SmartMeterType")
13232 				{
13233 					root["SmartMeterType"] = nValue;
13234 				}
13235 				else if (Key == "EnableTabFloorplans")
13236 				{
13237 					root["EnableTabFloorplans"] = nValue;
13238 				}
13239 				else if (Key == "EnableTabLights")
13240 				{
13241 					root["EnableTabLights"] = nValue;
13242 				}
13243 				else if (Key == "EnableTabTemp")
13244 				{
13245 					root["EnableTabTemp"] = nValue;
13246 				}
13247 				else if (Key == "EnableTabWeather")
13248 				{
13249 					root["EnableTabWeather"] = nValue;
13250 				}
13251 				else if (Key == "EnableTabUtility")
13252 				{
13253 					root["EnableTabUtility"] = nValue;
13254 				}
13255 				else if (Key == "EnableTabScenes")
13256 				{
13257 					root["EnableTabScenes"] = nValue;
13258 				}
13259 				else if (Key == "EnableTabCustom")
13260 				{
13261 					root["EnableTabCustom"] = nValue;
13262 				}
13263 				else if (Key == "NotificationSensorInterval")
13264 				{
13265 					root["NotificationSensorInterval"] = nValue;
13266 				}
13267 				else if (Key == "NotificationSwitchInterval")
13268 				{
13269 					root["NotificationSwitchInterval"] = nValue;
13270 				}
13271 				else if (Key == "RemoteSharedPort")
13272 				{
13273 					root["RemoteSharedPort"] = nValue;
13274 				}
13275 				else if (Key == "Language")
13276 				{
13277 					root["Language"] = sValue;
13278 				}
13279 				else if (Key == "Title")
13280 				{
13281 					root["Title"] = sValue;
13282 				}
13283 				else if (Key == "WindUnit")
13284 				{
13285 					root["WindUnit"] = nValue;
13286 				}
13287 				else if (Key == "TempUnit")
13288 				{
13289 					root["TempUnit"] = nValue;
13290 				}
13291 				else if (Key == "WeightUnit")
13292 				{
13293 					root["WeightUnit"] = nValue;
13294 				}
13295 				else if (Key == "AuthenticationMethod")
13296 				{
13297 					root["AuthenticationMethod"] = nValue;
13298 				}
13299 				else if (Key == "ReleaseChannel")
13300 				{
13301 					root["ReleaseChannel"] = nValue;
13302 				}
13303 				else if (Key == "RaspCamParams")
13304 				{
13305 					root["RaspCamParams"] = sValue;
13306 				}
13307 				else if (Key == "UVCParams")
13308 				{
13309 					root["UVCParams"] = sValue;
13310 				}
13311 				else if (Key == "AcceptNewHardware")
13312 				{
13313 					root["AcceptNewHardware"] = nValue;
13314 				}
13315 				else if (Key == "HideDisabledHardwareSensors")
13316 				{
13317 					root["HideDisabledHardwareSensors"] = nValue;
13318 				}
13319 				else if (Key == "ShowUpdateEffect")
13320 				{
13321 					root["ShowUpdateEffect"] = nValue;
13322 				}
13323 				else if (Key == "DegreeDaysBaseTemperature")
13324 				{
13325 					root["DegreeDaysBaseTemperature"] = sValue;
13326 				}
13327 				else if (Key == "EnableEventScriptSystem")
13328 				{
13329 					root["EnableEventScriptSystem"] = nValue;
13330 				}
13331 				else if (Key == "EventSystemLogFullURL")
13332 				{
13333 					root["EventSystemLogFullURL"] = nValue;
13334 				}
13335 				else if (Key == "DisableDzVentsSystem")
13336 				{
13337 					root["DisableDzVentsSystem"] = nValue;
13338 				}
13339 				else if (Key == "DzVentsLogLevel")
13340 				{
13341 					root["DzVentsLogLevel"] = nValue;
13342 				}
13343 				else if (Key == "LogEventScriptTrigger")
13344 				{
13345 					root["LogEventScriptTrigger"] = nValue;
13346 				}
13347 				else if (Key == "(1WireSensorPollPeriod")
13348 				{
13349 					root["1WireSensorPollPeriod"] = nValue;
13350 				}
13351 				else if (Key == "(1WireSwitchPollPeriod")
13352 				{
13353 					root["1WireSwitchPollPeriod"] = nValue;
13354 				}
13355 				else if (Key == "SecOnDelay")
13356 				{
13357 					root["SecOnDelay"] = nValue;
13358 				}
13359 				else if (Key == "AllowWidgetOrdering")
13360 				{
13361 					root["AllowWidgetOrdering"] = nValue;
13362 				}
13363 				else if (Key == "FloorplanPopupDelay")
13364 				{
13365 					root["FloorplanPopupDelay"] = nValue;
13366 				}
13367 				else if (Key == "FloorplanFullscreenMode")
13368 				{
13369 					root["FloorplanFullscreenMode"] = nValue;
13370 				}
13371 				else if (Key == "FloorplanAnimateZoom")
13372 				{
13373 					root["FloorplanAnimateZoom"] = nValue;
13374 				}
13375 				else if (Key == "FloorplanShowSensorValues")
13376 				{
13377 					root["FloorplanShowSensorValues"] = nValue;
13378 				}
13379 				else if (Key == "FloorplanShowSwitchValues")
13380 				{
13381 					root["FloorplanShowSwitchValues"] = nValue;
13382 				}
13383 				else if (Key == "FloorplanShowSceneNames")
13384 				{
13385 					root["FloorplanShowSceneNames"] = nValue;
13386 				}
13387 				else if (Key == "FloorplanRoomColour")
13388 				{
13389 					root["FloorplanRoomColour"] = sValue;
13390 				}
13391 				else if (Key == "FloorplanActiveOpacity")
13392 				{
13393 					root["FloorplanActiveOpacity"] = nValue;
13394 				}
13395 				else if (Key == "FloorplanInactiveOpacity")
13396 				{
13397 					root["FloorplanInactiveOpacity"] = nValue;
13398 				}
13399 				else if (Key == "SensorTimeout")
13400 				{
13401 					root["SensorTimeout"] = nValue;
13402 				}
13403 				else if (Key == "BatteryLowNotification")
13404 				{
13405 					root["BatterLowLevel"] = nValue;
13406 				}
13407 				else if (Key == "WebTheme")
13408 				{
13409 					root["WebTheme"] = sValue;
13410 				}
13411 #ifndef NOCLOUD
13412 				else if (Key == "MyDomoticzInstanceId") {
13413 					root["MyDomoticzInstanceId"] = sValue;
13414 				}
13415 				else if (Key == "MyDomoticzUserId") {
13416 					root["MyDomoticzUserId"] = sValue;
13417 				}
13418 				else if (Key == "MyDomoticzPassword") {
13419 					root["MyDomoticzPassword"] = sValue;
13420 				}
13421 				else if (Key == "MyDomoticzSubsystems") {
13422 					root["MyDomoticzSubsystems"] = nValue;
13423 				}
13424 #endif
13425 				else if (Key == "MyDomoticzSubsystems") {
13426 					root["MyDomoticzSubsystems"] = nValue;
13427 				}
13428 				else if (Key == "SendErrorsAsNotification") {
13429 					root["SendErrorsAsNotification"] = nValue;
13430 				}
13431 				else if (Key == "DeltaTemperatureLog") {
13432 					root[Key] = sValue;
13433 				}
13434 				else if (Key == "IFTTTEnabled") {
13435 					root["IFTTTEnabled"] = nValue;
13436 				}
13437 				else if (Key == "IFTTTAPI") {
13438 					root["IFTTTAPI"] = sValue;
13439 				}
13440 			}
13441 		}
13442 
RType_LightLog(WebEmSession & session,const request & req,Json::Value & root)13443 		void CWebServer::RType_LightLog(WebEmSession & session, const request& req, Json::Value &root)
13444 		{
13445 			uint64_t idx = 0;
13446 			if (request::findValue(&req, "idx") != "")
13447 			{
13448 				idx = std::strtoull(request::findValue(&req, "idx").c_str(), nullptr, 10);
13449 			}
13450 			std::vector<std::vector<std::string> > result;
13451 			//First get Device Type/SubType
13452 			result = m_sql.safe_query("SELECT Type, SubType, SwitchType, Options FROM DeviceStatus WHERE (ID == %" PRIu64 ")",
13453 				idx);
13454 			if (result.empty())
13455 				return;
13456 
13457 			unsigned char dType = atoi(result[0][0].c_str());
13458 			unsigned char dSubType = atoi(result[0][1].c_str());
13459 			_eSwitchType switchtype = (_eSwitchType)atoi(result[0][2].c_str());
13460 			std::map<std::string, std::string> options = m_sql.BuildDeviceOptions(result[0][3].c_str());
13461 
13462 			if (
13463 				(dType != pTypeLighting1) &&
13464 				(dType != pTypeLighting2) &&
13465 				(dType != pTypeLighting3) &&
13466 				(dType != pTypeLighting4) &&
13467 				(dType != pTypeLighting5) &&
13468 				(dType != pTypeLighting6) &&
13469 				(dType != pTypeFan) &&
13470 				(dType != pTypeColorSwitch) &&
13471 				(dType != pTypeSecurity1) &&
13472 				(dType != pTypeSecurity2) &&
13473 				(dType != pTypeEvohome) &&
13474 				(dType != pTypeEvohomeRelay) &&
13475 				(dType != pTypeCurtain) &&
13476 				(dType != pTypeBlinds) &&
13477 				(dType != pTypeRFY) &&
13478 				(dType != pTypeRego6XXValue) &&
13479 				(dType != pTypeChime) &&
13480 				(dType != pTypeThermostat2) &&
13481 				(dType != pTypeThermostat3) &&
13482 				(dType != pTypeThermostat4) &&
13483 				(dType != pTypeRemote) &&
13484 				(dType != pTypeGeneralSwitch) &&
13485 				(dType != pTypeHomeConfort) &&
13486 				(dType != pTypeFS20) &&
13487 				(!((dType == pTypeRadiator1) && (dSubType == sTypeSmartwaresSwitchRadiator))) &&
13488 				(dType != pTypeHunter)
13489 				)
13490 				return; //no light device! we should not be here!
13491 
13492 			root["status"] = "OK";
13493 			root["title"] = "LightLog";
13494 
13495 			result = m_sql.safe_query("SELECT ROWID, nValue, sValue, User, Date FROM LightingLog WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date DESC", idx);
13496 			if (!result.empty())
13497 			{
13498 				std::map<std::string, std::string> selectorStatuses;
13499 				if (switchtype == STYPE_Selector) {
13500 					GetSelectorSwitchStatuses(options, selectorStatuses);
13501 				}
13502 
13503 				int ii = 0;
13504 				for (const auto & itt : result)
13505 				{
13506 					std::vector<std::string> sd = itt;
13507 
13508 					std::string lidx = sd.at(0);
13509 					int nValue = atoi(sd.at(1).c_str());
13510 					std::string sValue = sd.at(2);
13511 					std::string sUser = sd.at(3);
13512 					std::string ldate = sd.at(4);
13513 
13514 					//add light details
13515 					std::string lstatus;
13516 					std::string ldata;
13517 					int llevel = 0;
13518 					bool bHaveDimmer = false;
13519 					bool bHaveSelector = false;
13520 					bool bHaveGroupCmd = false;
13521 					int maxDimLevel = 0;
13522 
13523 					if (switchtype == STYPE_Media) {
13524 						if (sValue == "0") continue; //skip 0-values in log for MediaPlayers
13525 							lstatus = sValue;
13526 						ldata = lstatus;
13527 					}
13528 					else if (switchtype == STYPE_Selector)
13529 					{
13530 						if (ii == 0) {
13531 							bHaveSelector = true;
13532 							maxDimLevel = selectorStatuses.size();
13533 						}
13534 						if (!selectorStatuses.empty()) {
13535 
13536 							std::string sLevel = selectorStatuses[sValue];
13537 							ldata = sLevel;
13538 							lstatus = "Set Level: " + sLevel;
13539 							llevel = atoi(sValue.c_str());
13540 						}
13541 					}
13542 					else {
13543 						GetLightStatus(dType, dSubType, switchtype, nValue, sValue, lstatus, llevel, bHaveDimmer, maxDimLevel, bHaveGroupCmd);
13544 						ldata = lstatus;
13545 					}
13546 
13547 					if (ii == 0)
13548 					{
13549 						//Log these parameters once
13550 						root["HaveDimmer"] = bHaveDimmer;
13551 						root["result"][ii]["MaxDimLevel"] = maxDimLevel;
13552 						root["HaveGroupCmd"] = bHaveGroupCmd;
13553 						root["HaveSelector"] = bHaveSelector;
13554 					}
13555 
13556 					//Corrent names for certain switch types
13557 					switch (switchtype)
13558 					{
13559 					case STYPE_Contact:
13560 						ldata = (ldata == "On") ? "Open" : "Closed";
13561 						break;
13562 					case STYPE_DoorContact:
13563 						ldata = (ldata == "On") ? "Open" : "Closed";
13564 						break;
13565 					case STYPE_DoorLock:
13566 						ldata = (ldata == "On") ? "Locked" : "Unlocked";
13567 						break;
13568 					case STYPE_DoorLockInverted:
13569 						ldata = (ldata == "On") ? "Unlocked" : "Locked";
13570 						break;
13571 					case STYPE_Blinds:
13572 					case STYPE_VenetianBlindsEU:
13573 					case STYPE_VenetianBlindsUS:
13574 						ldata = (ldata == "On") ? "Closed" : "Open";
13575 						break;
13576 					case STYPE_BlindsInverted:
13577 						ldata = (ldata == "On") ? "Open" : "Closed";
13578 						break;
13579 					case STYPE_BlindsPercentage:
13580 						if ((ldata == "On") || (ldata == "Off"))
13581 						{
13582 							ldata = (ldata == "On") ? "Closed" : "Open";
13583 						}
13584 						break;
13585 					case STYPE_BlindsPercentageInverted:
13586 						if ((ldata == "On") || (ldata == "Off"))
13587 						{
13588 							ldata = (ldata == "On") ? "Open" : "Closed";
13589 						}
13590 						break;
13591 					}
13592 
13593 					root["result"][ii]["idx"] = lidx;
13594 					root["result"][ii]["Date"] = ldate;
13595 					root["result"][ii]["Data"] = ldata;
13596 					root["result"][ii]["Status"] = lstatus;
13597 					root["result"][ii]["Level"] = llevel;
13598 					root["result"][ii]["User"] = sUser;
13599 					ii++;
13600 				}
13601 			}
13602 		}
13603 
RType_TextLog(WebEmSession & session,const request & req,Json::Value & root)13604 		void CWebServer::RType_TextLog(WebEmSession & session, const request& req, Json::Value &root)
13605 		{
13606 			uint64_t idx = 0;
13607 			if (request::findValue(&req, "idx") != "")
13608 			{
13609 				idx = std::strtoull(request::findValue(&req, "idx").c_str(), nullptr, 10);
13610 			}
13611 			std::vector<std::vector<std::string> > result;
13612 
13613 			root["status"] = "OK";
13614 			root["title"] = "TextLog";
13615 
13616 			result = m_sql.safe_query("SELECT ROWID, sValue, User, Date FROM LightingLog WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date DESC",
13617 				idx);
13618 			if (!result.empty())
13619 			{
13620 				int ii = 0;
13621 				for (const auto & itt : result)
13622 				{
13623 					std::vector<std::string> sd = itt;
13624 
13625 					root["result"][ii]["idx"] = sd[0];
13626 					root["result"][ii]["Data"] = sd[1];
13627 					root["result"][ii]["User"] = sd[2];
13628 					root["result"][ii]["Date"] = sd[3];
13629 					ii++;
13630 				}
13631 			}
13632 		}
13633 
RType_SceneLog(WebEmSession & session,const request & req,Json::Value & root)13634 		void CWebServer::RType_SceneLog(WebEmSession & session, const request& req, Json::Value &root)
13635 		{
13636 			uint64_t idx = 0;
13637 			if (request::findValue(&req, "idx") != "")
13638 			{
13639 				idx = std::strtoull(request::findValue(&req, "idx").c_str(), nullptr, 10);
13640 			}
13641 			std::vector<std::vector<std::string> > result;
13642 
13643 			root["status"] = "OK";
13644 			root["title"] = "SceneLog";
13645 
13646 			result = m_sql.safe_query("SELECT ROWID, nValue, User, Date FROM SceneLog WHERE (SceneRowID==%" PRIu64 ") ORDER BY Date DESC", idx);
13647 			if (!result.empty())
13648 			{
13649 				int ii = 0;
13650 				for (const auto & itt : result)
13651 				{
13652 					std::vector<std::string> sd = itt;
13653 
13654 					root["result"][ii]["idx"] = sd[0];
13655 					int nValue = atoi(sd[1].c_str());
13656 					root["result"][ii]["Data"] = (nValue == 0) ? "Off" : "On";
13657 					root["result"][ii]["User"] = sd[2];
13658 					root["result"][ii]["Date"] = sd[3];
13659 					ii++;
13660 				}
13661 			}
13662 		}
13663 
RType_HandleGraph(WebEmSession & session,const request & req,Json::Value & root)13664 		void CWebServer::RType_HandleGraph(WebEmSession & session, const request& req, Json::Value &root)
13665 		{
13666 			uint64_t idx = 0;
13667 			if (request::findValue(&req, "idx") != "")
13668 			{
13669 				idx = std::strtoull(request::findValue(&req, "idx").c_str(), nullptr, 10);
13670 			}
13671 
13672 			std::vector<std::vector<std::string> > result;
13673 			char szTmp[300];
13674 
13675 			std::string sensor = request::findValue(&req, "sensor");
13676 			if (sensor == "")
13677 				return;
13678 			std::string srange = request::findValue(&req, "range");
13679 			if (srange == "")
13680 				return;
13681 
13682 			time_t now = mytime(NULL);
13683 			struct tm tm1;
13684 			localtime_r(&now, &tm1);
13685 
13686 			result = m_sql.safe_query("SELECT Type, SubType, SwitchType, AddjValue, AddjMulti, AddjValue2, Options FROM DeviceStatus WHERE (ID == %" PRIu64 ")",
13687 				idx);
13688 			if (result.empty())
13689 				return;
13690 
13691 			unsigned char dType = atoi(result[0][0].c_str());
13692 			unsigned char dSubType = atoi(result[0][1].c_str());
13693 			_eMeterType metertype = (_eMeterType)atoi(result[0][2].c_str());
13694 			if (
13695 				(dType == pTypeP1Power) ||
13696 				(dType == pTypeENERGY) ||
13697 				(dType == pTypePOWER) ||
13698 				(dType == pTypeCURRENTENERGY) ||
13699 				((dType == pTypeGeneral) && (dSubType == sTypeKwh))
13700 				)
13701 			{
13702 				metertype = MTYPE_ENERGY;
13703 			}
13704 			else if (dType == pTypeP1Gas)
13705 				metertype = MTYPE_GAS;
13706 			else if ((dType == pTypeRego6XXValue) && (dSubType == sTypeRego6XXCounter))
13707 				metertype = MTYPE_COUNTER;
13708 
13709 			// Special case of managed counter: Usage instead of Value in Meter table, and we don't want to calculate last value
13710 			bool bIsManagedCounter = (dType == pTypeGeneral) && (dSubType == sTypeManagedCounter);
13711 
13712 			double AddjValue = atof(result[0][3].c_str());
13713 			double AddjMulti = atof(result[0][4].c_str());
13714 			double AddjValue2 = atof(result[0][5].c_str());
13715 			std::string sOptions = result[0][6].c_str();
13716 			std::map<std::string, std::string> options = m_sql.BuildDeviceOptions(sOptions);
13717 
13718 			float divider = m_sql.GetCounterDivider(int(metertype), int(dType), float(AddjValue2));
13719 
13720 			std::string dbasetable = "";
13721 			if (srange == "day") {
13722 				if (sensor == "temp")
13723 					dbasetable = "Temperature";
13724 				else if (sensor == "rain")
13725 					dbasetable = "Rain";
13726 				else if (sensor == "Percentage")
13727 					dbasetable = "Percentage";
13728 				else if (sensor == "fan")
13729 					dbasetable = "Fan";
13730 				else if (sensor == "counter")
13731 				{
13732 					if ((dType == pTypeP1Power) || (dType == pTypeCURRENT) || (dType == pTypeCURRENTENERGY))
13733 					{
13734 						dbasetable = "MultiMeter";
13735 					}
13736 					else
13737 					{
13738 						dbasetable = "Meter";
13739 					}
13740 				}
13741 				else if ((sensor == "wind") || (sensor == "winddir"))
13742 					dbasetable = "Wind";
13743 				else if (sensor == "uv")
13744 					dbasetable = "UV";
13745 				else
13746 					return;
13747 			}
13748 			else
13749 			{
13750 				//week,year,month
13751 				if (sensor == "temp")
13752 					dbasetable = "Temperature_Calendar";
13753 				else if (sensor == "rain")
13754 					dbasetable = "Rain_Calendar";
13755 				else if (sensor == "Percentage")
13756 					dbasetable = "Percentage_Calendar";
13757 				else if (sensor == "fan")
13758 					dbasetable = "Fan_Calendar";
13759 				else if (sensor == "counter")
13760 				{
13761 					if (
13762 						(dType == pTypeP1Power) ||
13763 						(dType == pTypeCURRENT) ||
13764 						(dType == pTypeCURRENTENERGY) ||
13765 						(dType == pTypeAirQuality) ||
13766 						((dType == pTypeGeneral) && (dSubType == sTypeVisibility)) ||
13767 						((dType == pTypeGeneral) && (dSubType == sTypeDistance)) ||
13768 						((dType == pTypeGeneral) && (dSubType == sTypeSolarRadiation)) ||
13769 						((dType == pTypeGeneral) && (dSubType == sTypeSoilMoisture)) ||
13770 						((dType == pTypeGeneral) && (dSubType == sTypeLeafWetness)) ||
13771 						((dType == pTypeRFXSensor) && (dSubType == sTypeRFXSensorAD)) ||
13772 						((dType == pTypeRFXSensor) && (dSubType == sTypeRFXSensorVolt)) ||
13773 						((dType == pTypeGeneral) && (dSubType == sTypeVoltage)) ||
13774 						((dType == pTypeGeneral) && (dSubType == sTypeCurrent)) ||
13775 						((dType == pTypeGeneral) && (dSubType == sTypePressure)) ||
13776 						((dType == pTypeGeneral) && (dSubType == sTypeSoundLevel)) ||
13777 						(dType == pTypeLux) ||
13778 						(dType == pTypeWEIGHT) ||
13779 						(dType == pTypeUsage)
13780 						)
13781 						dbasetable = "MultiMeter_Calendar";
13782 					else
13783 						dbasetable = "Meter_Calendar";
13784 				}
13785 				else if ((sensor == "wind") || (sensor == "winddir"))
13786 					dbasetable = "Wind_Calendar";
13787 				else if (sensor == "uv")
13788 					dbasetable = "UV_Calendar";
13789 				else
13790 					return;
13791 			}
13792 			unsigned char tempsign = m_sql.m_tempsign[0];
13793 			int iPrev;
13794 
13795 			if (srange == "day")
13796 			{
13797 				if (sensor == "temp") {
13798 					root["status"] = "OK";
13799 					root["title"] = "Graph " + sensor + " " + srange;
13800 
13801 					result = m_sql.safe_query("SELECT Temperature, Chill, Humidity, Barometer, Date, SetPoint FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
13802 					if (!result.empty())
13803 					{
13804 						int ii = 0;
13805 						for (const auto & itt : result)
13806 						{
13807 							std::vector<std::string> sd = itt;
13808 
13809 							root["result"][ii]["d"] = sd[4].substr(0, 16);
13810 							if (
13811 								(dType == pTypeRego6XXTemp) ||
13812 								(dType == pTypeTEMP) ||
13813 								(dType == pTypeTEMP_HUM) ||
13814 								(dType == pTypeTEMP_HUM_BARO) ||
13815 								(dType == pTypeTEMP_BARO) ||
13816 								((dType == pTypeWIND) && (dSubType == sTypeWIND4)) ||
13817 								((dType == pTypeUV) && (dSubType == sTypeUV3)) ||
13818 								(dType == pTypeThermostat1) ||
13819 								(dType == pTypeRadiator1) ||
13820 								((dType == pTypeRFXSensor) && (dSubType == sTypeRFXSensorTemp)) ||
13821 								((dType == pTypeGeneral) && (dSubType == sTypeSystemTemp)) ||
13822 								((dType == pTypeGeneral) && (dSubType == sTypeBaro)) ||
13823 								((dType == pTypeThermostat) && (dSubType == sTypeThermSetpoint)) ||
13824 								(dType == pTypeEvohomeZone) ||
13825 								(dType == pTypeEvohomeWater)
13826 								)
13827 							{
13828 								double tvalue = ConvertTemperature(atof(sd[0].c_str()), tempsign);
13829 								root["result"][ii]["te"] = tvalue;
13830 							}
13831 							if (
13832 								((dType == pTypeWIND) && (dSubType == sTypeWIND4)) ||
13833 								((dType == pTypeWIND) && (dSubType == sTypeWINDNoTemp))
13834 								)
13835 							{
13836 								double tvalue = ConvertTemperature(atof(sd[1].c_str()), tempsign);
13837 								root["result"][ii]["ch"] = tvalue;
13838 							}
13839 							if ((dType == pTypeHUM) || (dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO))
13840 							{
13841 								root["result"][ii]["hu"] = sd[2];
13842 							}
13843 							if (
13844 								(dType == pTypeTEMP_HUM_BARO) ||
13845 								(dType == pTypeTEMP_BARO) ||
13846 								((dType == pTypeGeneral) && (dSubType == sTypeBaro))
13847 								)
13848 							{
13849 								if (dType == pTypeTEMP_HUM_BARO)
13850 								{
13851 									if (dSubType == sTypeTHBFloat)
13852 									{
13853 										sprintf(szTmp, "%.1f", atof(sd[3].c_str()) / 10.0f);
13854 										root["result"][ii]["ba"] = szTmp;
13855 									}
13856 									else
13857 										root["result"][ii]["ba"] = sd[3];
13858 								}
13859 								else if (dType == pTypeTEMP_BARO)
13860 								{
13861 									sprintf(szTmp, "%.1f", atof(sd[3].c_str()) / 10.0f);
13862 									root["result"][ii]["ba"] = szTmp;
13863 								}
13864 								else if ((dType == pTypeGeneral) && (dSubType == sTypeBaro))
13865 								{
13866 									sprintf(szTmp, "%.1f", atof(sd[3].c_str()) / 10.0f);
13867 									root["result"][ii]["ba"] = szTmp;
13868 								}
13869 							}
13870 							if ((dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater))
13871 							{
13872 								double se = ConvertTemperature(atof(sd[5].c_str()), tempsign);
13873 								root["result"][ii]["se"] = se;
13874 							}
13875 
13876 							ii++;
13877 						}
13878 					}
13879 				}
13880 				else if (sensor == "Percentage") {
13881 					root["status"] = "OK";
13882 					root["title"] = "Graph " + sensor + " " + srange;
13883 
13884 					result = m_sql.safe_query("SELECT Percentage, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
13885 					if (!result.empty())
13886 					{
13887 						int ii = 0;
13888 						for (const auto & itt : result)
13889 						{
13890 							std::vector<std::string> sd = itt;
13891 
13892 							root["result"][ii]["d"] = sd[1].substr(0, 16);
13893 							root["result"][ii]["v"] = sd[0];
13894 							ii++;
13895 						}
13896 					}
13897 				}
13898 				else if (sensor == "fan") {
13899 					root["status"] = "OK";
13900 					root["title"] = "Graph " + sensor + " " + srange;
13901 
13902 					result = m_sql.safe_query("SELECT Speed, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
13903 					if (!result.empty())
13904 					{
13905 						int ii = 0;
13906 						for (const auto & itt : result)
13907 						{
13908 							std::vector<std::string> sd = itt;
13909 
13910 							root["result"][ii]["d"] = sd[1].substr(0, 16);
13911 							root["result"][ii]["v"] = sd[0];
13912 							ii++;
13913 						}
13914 					}
13915 				}
13916 
13917 				else if (sensor == "counter")
13918 				{
13919 					if (dType == pTypeP1Power)
13920 					{
13921 						root["status"] = "OK";
13922 						root["title"] = "Graph " + sensor + " " + srange;
13923 
13924 						result = m_sql.safe_query("SELECT Value1, Value2, Value3, Value4, Value5, Value6, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
13925 						if (!result.empty())
13926 						{
13927 							int ii = 0;
13928 							bool bHaveDeliverd = false;
13929 							bool bHaveFirstValue = false;
13930 							long long lastUsage1, lastUsage2, lastDeliv1, lastDeliv2;
13931 							time_t lastTime = 0;
13932 
13933 							long long firstUsage1 = 0;
13934 							long long firstUsage2 = 0;
13935 							long long firstDeliv1 = 0;
13936 							long long firstDeliv2 = 0;
13937 
13938 							int nMeterType = 0;
13939 							m_sql.GetPreferencesVar("SmartMeterType", nMeterType);
13940 
13941 							int lastDay = 0;
13942 
13943 							for (const auto & itt : result)
13944 							{
13945 								std::vector<std::string> sd = itt;
13946 
13947 								if (nMeterType == 0)
13948 								{
13949 									long long actUsage1 = std::strtoll(sd[0].c_str(), nullptr, 10);
13950 									long long actUsage2 = std::strtoll(sd[4].c_str(), nullptr, 10);
13951 									long long actDeliv1 = std::strtoll(sd[1].c_str(), nullptr, 10);
13952 									long long actDeliv2 = std::strtoll(sd[5].c_str(), nullptr, 10);
13953 									actDeliv1 = (actDeliv1 < 10) ? 0 : actDeliv1;
13954 									actDeliv2 = (actDeliv2 < 10) ? 0 : actDeliv2;
13955 
13956 									std::string stime = sd[6];
13957 									struct tm ntime;
13958 									time_t atime;
13959 									ParseSQLdatetime(atime, ntime, stime, -1);
13960 									if (lastDay != ntime.tm_mday)
13961 									{
13962 										lastDay = ntime.tm_mday;
13963 										firstUsage1 = actUsage1;
13964 										firstUsage2 = actUsage2;
13965 										firstDeliv1 = actDeliv1;
13966 										firstDeliv2 = actDeliv2;
13967 									}
13968 
13969 									if (bHaveFirstValue)
13970 									{
13971 										long curUsage1 = (long)(actUsage1 - lastUsage1);
13972 										long curUsage2 = (long)(actUsage2 - lastUsage2);
13973 										long curDeliv1 = (long)(actDeliv1 - lastDeliv1);
13974 										long curDeliv2 = (long)(actDeliv2 - lastDeliv2);
13975 
13976 										if ((curUsage1 < 0) || (curUsage1 > 100000))
13977 											curUsage1 = 0;
13978 										if ((curUsage2 < 0) || (curUsage2 > 100000))
13979 											curUsage2 = 0;
13980 										if ((curDeliv1 < 0) || (curDeliv1 > 100000))
13981 											curDeliv1 = 0;
13982 										if ((curDeliv2 < 0) || (curDeliv2 > 100000))
13983 											curDeliv2 = 0;
13984 
13985 										float tdiff = static_cast<float>(difftime(atime, lastTime));
13986 										if (tdiff == 0)
13987 											tdiff = 1;
13988 										float tlaps = 3600.0f / tdiff;
13989 										curUsage1 *= int(tlaps);
13990 										curUsage2 *= int(tlaps);
13991 										curDeliv1 *= int(tlaps);
13992 										curDeliv2 *= int(tlaps);
13993 
13994 										root["result"][ii]["d"] = sd[6].substr(0, 16);
13995 
13996 										if ((curDeliv1 != 0) || (curDeliv2 != 0))
13997 											bHaveDeliverd = true;
13998 
13999 										sprintf(szTmp, "%ld", curUsage1);
14000 										root["result"][ii]["v"] = szTmp;
14001 										sprintf(szTmp, "%ld", curUsage2);
14002 										root["result"][ii]["v2"] = szTmp;
14003 										sprintf(szTmp, "%ld", curDeliv1);
14004 										root["result"][ii]["r1"] = szTmp;
14005 										sprintf(szTmp, "%ld", curDeliv2);
14006 										root["result"][ii]["r2"] = szTmp;
14007 
14008 										long pUsage1 = (long)(actUsage1 - firstUsage1);
14009 										long pUsage2 = (long)(actUsage2 - firstUsage2);
14010 
14011 										sprintf(szTmp, "%ld", pUsage1 + pUsage2);
14012 										root["result"][ii]["eu"] = szTmp;
14013 										if (bHaveDeliverd)
14014 										{
14015 											long pDeliv1 = (long)(actDeliv1 - firstDeliv1);
14016 											long pDeliv2 = (long)(actDeliv2 - firstDeliv2);
14017 											sprintf(szTmp, "%ld", pDeliv1 + pDeliv2);
14018 											root["result"][ii]["eg"] = szTmp;
14019 										}
14020 
14021 										ii++;
14022 									}
14023 									else
14024 									{
14025 										bHaveFirstValue = true;
14026 										if ((ntime.tm_hour != 0) && (ntime.tm_min != 0))
14027 										{
14028 											struct tm ltime;
14029 											localtime_r(&atime, &tm1);
14030 											getNoon(atime, ltime, ntime.tm_year + 1900, ntime.tm_mon + 1, ntime.tm_mday - 1); // We're only interested in finding the date
14031 											int year = ltime.tm_year + 1900;
14032 											int mon = ltime.tm_mon + 1;
14033 											int day = ltime.tm_mday;
14034 											sprintf(szTmp, "%04d-%02d-%02d", year, mon, day);
14035 											std::vector<std::vector<std::string> > result2;
14036 											result2 = m_sql.safe_query(
14037 												"SELECT Counter1, Counter2, Counter3, Counter4 FROM Multimeter_Calendar WHERE (DeviceRowID==%" PRIu64 ") AND (Date=='%q')",
14038 												idx, szTmp);
14039 											if (!result2.empty())
14040 											{
14041 												std::vector<std::string> sd = result2[0];
14042 												firstUsage1 = std::strtoll(sd[0].c_str(), nullptr, 10);
14043 												firstDeliv1 = std::strtoll(sd[1].c_str(), nullptr, 10);
14044 												firstUsage2 = std::strtoll(sd[2].c_str(), nullptr, 10);
14045 												firstDeliv2 = std::strtoll(sd[3].c_str(), nullptr, 10);
14046 												lastDay = ntime.tm_mday;
14047 											}
14048 										}
14049 
14050 									}
14051 									lastUsage1 = actUsage1;
14052 									lastUsage2 = actUsage2;
14053 									lastDeliv1 = actDeliv1;
14054 									lastDeliv2 = actDeliv2;
14055 									lastTime = atime;
14056 								}
14057 								else
14058 								{
14059 									//this meter has no decimals, so return the use peaks
14060 									root["result"][ii]["d"] = sd[6].substr(0, 16);
14061 
14062 									if (sd[3] != "0")
14063 										bHaveDeliverd = true;
14064 									root["result"][ii]["v"] = sd[2];
14065 									root["result"][ii]["r1"] = sd[3];
14066 									ii++;
14067 
14068 								}
14069 							}
14070 							if (bHaveDeliverd)
14071 							{
14072 								root["delivered"] = true;
14073 							}
14074 						}
14075 					}
14076 					else if (dType == pTypeAirQuality)
14077 					{//day
14078 						root["status"] = "OK";
14079 						root["title"] = "Graph " + sensor + " " + srange;
14080 
14081 						result = m_sql.safe_query("SELECT Value, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
14082 						if (!result.empty())
14083 						{
14084 							int ii = 0;
14085 							for (const auto & itt : result)
14086 							{
14087 								std::vector<std::string> sd = itt;
14088 
14089 								root["result"][ii]["d"] = sd[1].substr(0, 16);
14090 								root["result"][ii]["co2"] = sd[0];
14091 								ii++;
14092 							}
14093 						}
14094 					}
14095 					else if ((dType == pTypeGeneral) && ((dSubType == sTypeSoilMoisture) || (dSubType == sTypeLeafWetness)))
14096 					{//day
14097 						root["status"] = "OK";
14098 						root["title"] = "Graph " + sensor + " " + srange;
14099 
14100 						result = m_sql.safe_query("SELECT Value, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
14101 						if (!result.empty())
14102 						{
14103 							int ii = 0;
14104 							for (const auto & itt : result)
14105 							{
14106 								std::vector<std::string> sd = itt;
14107 
14108 								root["result"][ii]["d"] = sd[1].substr(0, 16);
14109 								root["result"][ii]["v"] = sd[0];
14110 								ii++;
14111 							}
14112 						}
14113 					}
14114 					else if (
14115 						((dType == pTypeGeneral) && (dSubType == sTypeVisibility)) ||
14116 						((dType == pTypeGeneral) && (dSubType == sTypeDistance)) ||
14117 						((dType == pTypeGeneral) && (dSubType == sTypeSolarRadiation)) ||
14118 						((dType == pTypeGeneral) && (dSubType == sTypeVoltage)) ||
14119 						((dType == pTypeGeneral) && (dSubType == sTypeCurrent)) ||
14120 						((dType == pTypeGeneral) && (dSubType == sTypePressure)) ||
14121 						((dType == pTypeGeneral) && (dSubType == sTypeSoundLevel))
14122 						)
14123 					{//day
14124 						root["status"] = "OK";
14125 						root["title"] = "Graph " + sensor + " " + srange;
14126 						float vdiv = 10.0f;
14127 						if (
14128 							((dType == pTypeGeneral) && (dSubType == sTypeVoltage)) ||
14129 							((dType == pTypeGeneral) && (dSubType == sTypeCurrent))
14130 							)
14131 						{
14132 							vdiv = 1000.0f;
14133 						}
14134 						result = m_sql.safe_query("SELECT Value, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
14135 						if (!result.empty())
14136 						{
14137 							int ii = 0;
14138 							for (const auto & itt : result)
14139 							{
14140 								std::vector<std::string> sd = itt;
14141 
14142 								root["result"][ii]["d"] = sd[1].substr(0, 16);
14143 								float fValue = float(atof(sd[0].c_str())) / vdiv;
14144 								if (metertype == 1)
14145 									fValue *= 0.6214f;
14146 								if ((dType == pTypeGeneral) && (dSubType == sTypeVoltage))
14147 									sprintf(szTmp, "%.3f", fValue);
14148 								else if ((dType == pTypeGeneral) && (dSubType == sTypeCurrent))
14149 									sprintf(szTmp, "%.3f", fValue);
14150 								else
14151 									sprintf(szTmp, "%.1f", fValue);
14152 								root["result"][ii]["v"] = szTmp;
14153 								ii++;
14154 							}
14155 						}
14156 					}
14157 					else if ((dType == pTypeRFXSensor) && ((dSubType == sTypeRFXSensorAD) || (dSubType == sTypeRFXSensorVolt)))
14158 					{//day
14159 						root["status"] = "OK";
14160 						root["title"] = "Graph " + sensor + " " + srange;
14161 
14162 						result = m_sql.safe_query("SELECT Value, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
14163 						if (!result.empty())
14164 						{
14165 							int ii = 0;
14166 							for (const auto & itt : result)
14167 							{
14168 								std::vector<std::string> sd = itt;
14169 
14170 								root["result"][ii]["d"] = sd[1].substr(0, 16);
14171 								root["result"][ii]["v"] = sd[0];
14172 								ii++;
14173 							}
14174 						}
14175 					}
14176 					else if (dType == pTypeLux)
14177 					{//day
14178 						root["status"] = "OK";
14179 						root["title"] = "Graph " + sensor + " " + srange;
14180 
14181 						result = m_sql.safe_query("SELECT Value, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
14182 						if (!result.empty())
14183 						{
14184 							int ii = 0;
14185 							for (const auto & itt : result)
14186 							{
14187 								std::vector<std::string> sd = itt;
14188 
14189 								root["result"][ii]["d"] = sd[1].substr(0, 16);
14190 								root["result"][ii]["lux"] = sd[0];
14191 								ii++;
14192 							}
14193 						}
14194 					}
14195 					else if (dType == pTypeWEIGHT)
14196 					{//day
14197 						root["status"] = "OK";
14198 						root["title"] = "Graph " + sensor + " " + srange;
14199 
14200 						result = m_sql.safe_query("SELECT Value, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
14201 						if (!result.empty())
14202 						{
14203 							int ii = 0;
14204 							for (const auto & itt : result)
14205 							{
14206 								std::vector<std::string> sd = itt;
14207 
14208 								root["result"][ii]["d"] = sd[1].substr(0, 16);
14209 								sprintf(szTmp, "%.1f", m_sql.m_weightscale * atof(sd[0].c_str()) / 10.0f);
14210 								root["result"][ii]["v"] = szTmp;
14211 								ii++;
14212 							}
14213 						}
14214 					}
14215 					else if (dType == pTypeUsage)
14216 					{//day
14217 						root["status"] = "OK";
14218 						root["title"] = "Graph " + sensor + " " + srange;
14219 
14220 						result = m_sql.safe_query("SELECT Value, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
14221 						if (!result.empty())
14222 						{
14223 							int ii = 0;
14224 							for (const auto & itt : result)
14225 							{
14226 								std::vector<std::string> sd = itt;
14227 
14228 								root["result"][ii]["d"] = sd[1].substr(0, 16);
14229 								root["result"][ii]["u"] = atof(sd[0].c_str()) / 10.0f;
14230 								ii++;
14231 							}
14232 						}
14233 					}
14234 					else if (dType == pTypeCURRENT)
14235 					{
14236 						root["status"] = "OK";
14237 						root["title"] = "Graph " + sensor + " " + srange;
14238 
14239 						//CM113
14240 						int displaytype = 0;
14241 						int voltage = 230;
14242 						m_sql.GetPreferencesVar("CM113DisplayType", displaytype);
14243 						m_sql.GetPreferencesVar("ElectricVoltage", voltage);
14244 
14245 						root["displaytype"] = displaytype;
14246 
14247 						result = m_sql.safe_query("SELECT Value1, Value2, Value3, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
14248 						if (!result.empty())
14249 						{
14250 							int ii = 0;
14251 							bool bHaveL1 = false;
14252 							bool bHaveL2 = false;
14253 							bool bHaveL3 = false;
14254 							for (const auto & itt : result)
14255 							{
14256 								std::vector<std::string> sd = itt;
14257 
14258 								root["result"][ii]["d"] = sd[3].substr(0, 16);
14259 
14260 								float fval1 = static_cast<float>(atof(sd[0].c_str()) / 10.0f);
14261 								float fval2 = static_cast<float>(atof(sd[1].c_str()) / 10.0f);
14262 								float fval3 = static_cast<float>(atof(sd[2].c_str()) / 10.0f);
14263 
14264 								if (fval1 != 0)
14265 									bHaveL1 = true;
14266 								if (fval2 != 0)
14267 									bHaveL2 = true;
14268 								if (fval3 != 0)
14269 									bHaveL3 = true;
14270 
14271 								if (displaytype == 0)
14272 								{
14273 									sprintf(szTmp, "%.1f", fval1);
14274 									root["result"][ii]["v1"] = szTmp;
14275 									sprintf(szTmp, "%.1f", fval2);
14276 									root["result"][ii]["v2"] = szTmp;
14277 									sprintf(szTmp, "%.1f", fval3);
14278 									root["result"][ii]["v3"] = szTmp;
14279 								}
14280 								else
14281 								{
14282 									sprintf(szTmp, "%d", int(fval1*voltage));
14283 									root["result"][ii]["v1"] = szTmp;
14284 									sprintf(szTmp, "%d", int(fval2*voltage));
14285 									root["result"][ii]["v2"] = szTmp;
14286 									sprintf(szTmp, "%d", int(fval3*voltage));
14287 									root["result"][ii]["v3"] = szTmp;
14288 								}
14289 								ii++;
14290 							}
14291 							if (
14292 								(!bHaveL1) &&
14293 								(!bHaveL2) &&
14294 								(!bHaveL3)
14295 								) {
14296 								root["haveL1"] = true; //show at least something
14297 							}
14298 							else {
14299 								if (bHaveL1)
14300 									root["haveL1"] = true;
14301 								if (bHaveL2)
14302 									root["haveL2"] = true;
14303 								if (bHaveL3)
14304 									root["haveL3"] = true;
14305 							}
14306 						}
14307 					}
14308 					else if (dType == pTypeCURRENTENERGY)
14309 					{
14310 						root["status"] = "OK";
14311 						root["title"] = "Graph " + sensor + " " + srange;
14312 
14313 						//CM113
14314 						int displaytype = 0;
14315 						int voltage = 230;
14316 						m_sql.GetPreferencesVar("CM113DisplayType", displaytype);
14317 						m_sql.GetPreferencesVar("ElectricVoltage", voltage);
14318 
14319 						root["displaytype"] = displaytype;
14320 
14321 						result = m_sql.safe_query("SELECT Value1, Value2, Value3, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
14322 						if (!result.empty())
14323 						{
14324 							int ii = 0;
14325 							bool bHaveL1 = false;
14326 							bool bHaveL2 = false;
14327 							bool bHaveL3 = false;
14328 							for (const auto & itt : result)
14329 							{
14330 								std::vector<std::string> sd = itt;
14331 
14332 								root["result"][ii]["d"] = sd[3].substr(0, 16);
14333 
14334 								float fval1 = static_cast<float>(atof(sd[0].c_str()) / 10.0f);
14335 								float fval2 = static_cast<float>(atof(sd[1].c_str()) / 10.0f);
14336 								float fval3 = static_cast<float>(atof(sd[2].c_str()) / 10.0f);
14337 
14338 								if (fval1 != 0)
14339 									bHaveL1 = true;
14340 								if (fval2 != 0)
14341 									bHaveL2 = true;
14342 								if (fval3 != 0)
14343 									bHaveL3 = true;
14344 
14345 								if (displaytype == 0)
14346 								{
14347 									sprintf(szTmp, "%.1f", fval1);
14348 									root["result"][ii]["v1"] = szTmp;
14349 									sprintf(szTmp, "%.1f", fval2);
14350 									root["result"][ii]["v2"] = szTmp;
14351 									sprintf(szTmp, "%.1f", fval3);
14352 									root["result"][ii]["v3"] = szTmp;
14353 								}
14354 								else
14355 								{
14356 									sprintf(szTmp, "%d", int(fval1*voltage));
14357 									root["result"][ii]["v1"] = szTmp;
14358 									sprintf(szTmp, "%d", int(fval2*voltage));
14359 									root["result"][ii]["v2"] = szTmp;
14360 									sprintf(szTmp, "%d", int(fval3*voltage));
14361 									root["result"][ii]["v3"] = szTmp;
14362 								}
14363 								ii++;
14364 							}
14365 							if (
14366 								(!bHaveL1) &&
14367 								(!bHaveL2) &&
14368 								(!bHaveL3)
14369 								) {
14370 								root["haveL1"] = true; //show at least something
14371 							}
14372 							else {
14373 								if (bHaveL1)
14374 									root["haveL1"] = true;
14375 								if (bHaveL2)
14376 									root["haveL2"] = true;
14377 								if (bHaveL3)
14378 									root["haveL3"] = true;
14379 							}
14380 						}
14381 					}
14382 					else if ((dType == pTypeENERGY) || (dType == pTypePOWER) || (dType == pTypeYouLess) || ((dType == pTypeGeneral) && (dSubType == sTypeKwh)))
14383 					{
14384 						root["status"] = "OK";
14385 						root["title"] = "Graph " + sensor + " " + srange;
14386 						root["ValueQuantity"] = options["ValueQuantity"];
14387 						root["ValueUnits"] = options["ValueUnits"];
14388 
14389 						//First check if we had any usage in the short log, if not, its probably a meter without usage
14390 						bool bHaveUsage = true;
14391 						result = m_sql.safe_query("SELECT MIN([Usage]), MAX([Usage]) FROM %s WHERE (DeviceRowID==%" PRIu64 ")", dbasetable.c_str(), idx);
14392 						if (!result.empty())
14393 						{
14394 							long long minValue = std::strtoll(result[0][0].c_str(), nullptr, 10);
14395 							long long maxValue = std::strtoll(result[0][1].c_str(), nullptr, 10);
14396 							if ((minValue == 0) && (maxValue == 0))
14397 							{
14398 								bHaveUsage = false;
14399 							}
14400 						}
14401 
14402 						int ii = 0;
14403 						result = m_sql.safe_query("SELECT Value,[Usage], Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
14404 
14405 						int method = 0;
14406 						std::string sMethod = request::findValue(&req, "method");
14407 						if (sMethod.size() > 0)
14408 							method = atoi(sMethod.c_str());
14409 						if (bHaveUsage == false)
14410 							method = 0;
14411 
14412 						if ((dType == pTypeYouLess) && ((metertype == MTYPE_ENERGY) || (metertype == MTYPE_ENERGY_GENERATED)))
14413 							method = 1;
14414 
14415 						if (method != 0)
14416 						{
14417 							//realtime graph
14418 							if ((dType == pTypeENERGY) || (dType == pTypePOWER))
14419 								divider /= 100.0f;
14420 						}
14421 						root["method"] = method;
14422 						bool bHaveFirstValue = false;
14423 						bool bHaveFirstRealValue = false;
14424 						long long ulFirstRealValue = 0;
14425 						long long ulFirstValue = 0;
14426 						long long ulLastValue = 0;
14427 						std::string LastDateTime = "";
14428 
14429 						if (!result.empty())
14430 						{
14431 							std::vector<std::vector<std::string> >::const_iterator itt;
14432 							for (itt = result.begin(); itt!=result.end(); ++itt)
14433 							{
14434 								std::vector<std::string> sd = *itt;
14435 
14436 								//If method == 1, provide BOTH hourly and instant usage for combined graph
14437 								{
14438 									//bars / hour
14439 									std::string actDateTimeHour = sd[2].substr(0, 13);
14440 									long long actValue = std::strtoll(sd[0].c_str(), nullptr, 10);
14441 
14442 									if (actValue >= ulLastValue)
14443 										ulLastValue = actValue;
14444 
14445 									if (actDateTimeHour != LastDateTime || ((method == 1) && (itt + 1 == result.end())))
14446 									{
14447 										if (bHaveFirstValue)
14448 										{
14449 											//root["result"][ii]["d"] = LastDateTime + (method == 1 ? ":30" : ":00");
14450 											//^^ not necessarily bad, but is currently inconsistent with all other day graphs
14451 											root["result"][ii]["d"] = LastDateTime + ":00";
14452 
14453 											long long ulTotalValue = ulLastValue - ulFirstValue;
14454 											if (ulTotalValue == 0)
14455 											{
14456 												//Could be the P1 Gas Meter, only transmits one every 1 a 2 hours
14457 												ulTotalValue = ulLastValue - ulFirstRealValue;
14458 											}
14459 											ulFirstRealValue = ulLastValue;
14460 											float TotalValue = float(ulTotalValue);
14461 											switch (metertype)
14462 											{
14463 											case MTYPE_ENERGY:
14464 											case MTYPE_ENERGY_GENERATED:
14465 												sprintf(szTmp, "%.3f", (TotalValue / divider)*1000.0f);	//from kWh -> Watt
14466 												break;
14467 											case MTYPE_GAS:
14468 												sprintf(szTmp, "%.3f", TotalValue / divider);
14469 												break;
14470 											case MTYPE_WATER:
14471 												sprintf(szTmp, "%.3f", TotalValue / divider);
14472 												break;
14473 											case MTYPE_COUNTER:
14474 												sprintf(szTmp, "%.1f", TotalValue);
14475 												break;
14476 											default:
14477 												strcpy(szTmp, "0");
14478 												break;
14479 											}
14480 											root["result"][ii][method == 1 ? "eu" : "v"] = szTmp;
14481 											ii++;
14482 										}
14483 										LastDateTime = actDateTimeHour;
14484 										bHaveFirstValue = false;
14485 									}
14486 									if (!bHaveFirstValue)
14487 									{
14488 										ulFirstValue = ulLastValue;
14489 										bHaveFirstValue = true;
14490 									}
14491 									if (!bHaveFirstRealValue)
14492 									{
14493 										bHaveFirstRealValue = true;
14494 										ulFirstRealValue = ulLastValue;
14495 									}
14496 								}
14497 
14498 								if (method == 1)
14499 								{
14500 									long long actValue = std::strtoll(sd[1].c_str(), nullptr, 10);
14501 
14502 									root["result"][ii]["d"] = sd[2].substr(0, 16);
14503 
14504 									float TotalValue = float(actValue);
14505 									if ((dType == pTypeGeneral) && (dSubType == sTypeKwh))
14506 										TotalValue /= 10.0f;
14507 									switch (metertype)
14508 									{
14509 									case MTYPE_ENERGY:
14510 									case MTYPE_ENERGY_GENERATED:
14511 										sprintf(szTmp, "%.3f", (TotalValue / divider)*1000.0f);	//from kWh -> Watt
14512 										break;
14513 									case MTYPE_GAS:
14514 										sprintf(szTmp, "%.2f", TotalValue / divider);
14515 										break;
14516 									case MTYPE_WATER:
14517 										sprintf(szTmp, "%.3f", TotalValue / divider);
14518 										break;
14519 									case MTYPE_COUNTER:
14520 										sprintf(szTmp, "%.1f", TotalValue);
14521 										break;
14522 									default:
14523 										strcpy(szTmp, "0");
14524 										break;
14525 									}
14526 									root["result"][ii]["v"] = szTmp;
14527 									ii++;
14528 								}
14529 							}
14530 						}
14531 					}
14532 					else
14533 					{
14534 						root["status"] = "OK";
14535 						root["title"] = "Graph " + sensor + " " + srange;
14536 						root["ValueQuantity"] = options["ValueQuantity"];
14537 						root["ValueUnits"] = options["ValueUnits"];
14538 
14539 						int ii = 0;
14540 
14541 						bool bHaveFirstValue = false;
14542 						bool bHaveFirstRealValue = false;
14543 						unsigned long long ulFirstValue = 0;
14544 						unsigned long long ulLastValue = 0;
14545 
14546 						std::string LastDateTime = "";
14547 						time_t lastTime = 0;
14548 
14549 						if (bIsManagedCounter) {
14550 							result = m_sql.safe_query("SELECT Usage, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
14551 							bHaveFirstValue = true;
14552 							bHaveFirstRealValue = true;
14553 						}
14554 						else {
14555 							result = m_sql.safe_query("SELECT Value, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
14556 						}
14557 
14558 						int method = 0;
14559 						std::string sMethod = request::findValue(&req, "method");
14560 						if (sMethod.size() > 0)
14561 							method = atoi(sMethod.c_str());
14562 
14563 						if (!result.empty())
14564 						{
14565 							for (const auto & itt : result)
14566 							{
14567 								std::vector<std::string> sd = itt;
14568 
14569 								if (method == 0)
14570 								{
14571 									//bars / hour
14572 
14573 									unsigned long long actValue = std::strtoull(sd[0].c_str(), nullptr, 10);
14574 
14575 									std::string actDateTimeHour = sd[1].substr(0, 13);
14576 									if (actDateTimeHour != LastDateTime)
14577 									{
14578 										if (bHaveFirstValue)
14579 										{
14580 											struct tm ntime;
14581 											time_t atime;
14582 											if (actDateTimeHour.size() == 10)
14583 												actDateTimeHour += " 00";
14584 											constructTime(atime, ntime,
14585 												atoi(actDateTimeHour.substr(0, 4).c_str()),
14586 												atoi(actDateTimeHour.substr(5, 2).c_str()),
14587 												atoi(actDateTimeHour.substr(8, 2).c_str()),
14588 												atoi(actDateTimeHour.substr(11, 2).c_str()) - 1,
14589 												0, 0, -1);
14590 
14591 											char szTime[50];
14592 											sprintf(szTime, "%04d-%02d-%02d %02d:00", ntime.tm_year + 1900, ntime.tm_mon + 1, ntime.tm_mday, ntime.tm_hour);
14593 											root["result"][ii]["d"] = szTime;
14594 
14595 											//float TotalValue = float(actValue - ulFirstValue);
14596 
14597 											//prevents graph from going crazy if the meter counter resets
14598 											float TotalValue = (actValue >= ulFirstValue) ? float(actValue - ulFirstValue) : actValue;
14599 
14600 											//if (TotalValue != 0)
14601 											{
14602 												switch (metertype)
14603 												{
14604 												case MTYPE_ENERGY:
14605 												case MTYPE_ENERGY_GENERATED:
14606 													sprintf(szTmp, "%.3f", (TotalValue / divider)*1000.0f);	//from kWh -> Watt
14607 													break;
14608 												case MTYPE_GAS:
14609 													sprintf(szTmp, "%.3f", TotalValue / divider);
14610 													break;
14611 												case MTYPE_WATER:
14612 													sprintf(szTmp, "%.3f", TotalValue / divider);
14613 													break;
14614 												case MTYPE_COUNTER:
14615 													sprintf(szTmp, "%.1f", TotalValue);
14616 													break;
14617 												default:
14618 													strcpy(szTmp, "0");
14619 													break;
14620 												}
14621 												root["result"][ii]["v"] = szTmp;
14622 												ii++;
14623 											}
14624 										}
14625 										if (!bIsManagedCounter) {
14626 											ulFirstValue = actValue;
14627 										}
14628 										LastDateTime = actDateTimeHour;
14629 									}
14630 
14631 									if (!bHaveFirstValue)
14632 									{
14633 										ulFirstValue = actValue;
14634 										bHaveFirstValue = true;
14635 									}
14636 									ulLastValue = actValue;
14637 								}
14638 								else
14639 								{
14640 									//realtime graph
14641 									unsigned long long actValue = std::strtoull(sd[0].c_str(), nullptr, 10);
14642 
14643 									std::string stime = sd[1];
14644 									struct tm ntime;
14645 									time_t atime;
14646 									ParseSQLdatetime(atime, ntime, stime, -1);
14647 									if (bHaveFirstRealValue)
14648 									{
14649 										long long curValue = actValue - ulLastValue;
14650 
14651 										float tdiff = static_cast<float>(difftime(atime, lastTime));
14652 										if (tdiff == 0)
14653 											tdiff = 1;
14654 										float tlaps = 3600.0f / tdiff;
14655 										curValue *= int(tlaps);
14656 
14657 										root["result"][ii]["d"] = sd[1].substr(0, 16);
14658 
14659 										float TotalValue = float(curValue);
14660 										//if (TotalValue != 0)
14661 										{
14662 											switch (metertype)
14663 											{
14664 											case MTYPE_ENERGY:
14665 											case MTYPE_ENERGY_GENERATED:
14666 												sprintf(szTmp, "%.3f", (TotalValue / divider)*1000.0f);	//from kWh -> Watt
14667 												break;
14668 											case MTYPE_GAS:
14669 												sprintf(szTmp, "%.2f", TotalValue / divider);
14670 												break;
14671 											case MTYPE_WATER:
14672 												sprintf(szTmp, "%.3f", TotalValue / divider);
14673 												break;
14674 											case MTYPE_COUNTER:
14675 												sprintf(szTmp, "%.1f", TotalValue);
14676 												break;
14677 											default:
14678 												strcpy(szTmp, "0");
14679 												break;
14680 											}
14681 											root["result"][ii]["v"] = szTmp;
14682 											ii++;
14683 										}
14684 
14685 									}
14686 									else
14687 										bHaveFirstRealValue = true;
14688 									if (!bIsManagedCounter) {
14689 										ulLastValue = actValue;
14690 									}
14691 									lastTime = atime;
14692 								}
14693 							}
14694 						}
14695 						if ((!bIsManagedCounter) && (bHaveFirstValue) && (method == 0))
14696 						{
14697 							//add last value
14698 							root["result"][ii]["d"] = LastDateTime + ":00";
14699 
14700 							unsigned long long ulTotalValue = ulLastValue - ulFirstValue;
14701 
14702 							float TotalValue = float(ulTotalValue);
14703 
14704 							//if (TotalValue != 0)
14705 							{
14706 								switch (metertype)
14707 								{
14708 								case MTYPE_ENERGY:
14709 								case MTYPE_ENERGY_GENERATED:
14710 									sprintf(szTmp, "%.3f", (TotalValue / divider)*1000.0f);	//from kWh -> Watt
14711 									break;
14712 								case MTYPE_GAS:
14713 									sprintf(szTmp, "%.3f", TotalValue / divider);
14714 									break;
14715 								case MTYPE_WATER:
14716 									sprintf(szTmp, "%.3f", TotalValue / divider);
14717 									break;
14718 								case MTYPE_COUNTER:
14719 									sprintf(szTmp, "%.1f", TotalValue);
14720 									break;
14721 								default:
14722 									strcpy(szTmp, "0");
14723 									break;
14724 								}
14725 								root["result"][ii]["v"] = szTmp;
14726 								ii++;
14727 							}
14728 						}
14729 					}
14730 				}
14731 				else if (sensor == "uv") {
14732 					root["status"] = "OK";
14733 					root["title"] = "Graph " + sensor + " " + srange;
14734 
14735 					result = m_sql.safe_query("SELECT Level, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
14736 					if (!result.empty())
14737 					{
14738 						int ii = 0;
14739 						for (const auto & itt : result)
14740 						{
14741 							std::vector<std::string> sd = itt;
14742 
14743 							root["result"][ii]["d"] = sd[1].substr(0, 16);
14744 							root["result"][ii]["uvi"] = sd[0];
14745 							ii++;
14746 						}
14747 					}
14748 				}
14749 				else if (sensor == "rain") {
14750 					root["status"] = "OK";
14751 					root["title"] = "Graph " + sensor + " " + srange;
14752 
14753 					int LastHour = -1;
14754 					float LastTotalPreviousHour = -1;
14755 
14756 					float LastValue = -1;
14757 					std::string LastDate = "";
14758 
14759 					result = m_sql.safe_query("SELECT Total, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
14760 					if (!result.empty())
14761 					{
14762 						int ii = 0;
14763 						for (const auto & itt : result)
14764 						{
14765 							std::vector<std::string> sd = itt;
14766 							float ActTotal = static_cast<float>(atof(sd[0].c_str()));
14767 							int Hour = atoi(sd[1].substr(11, 2).c_str());
14768 							if (Hour != LastHour)
14769 							{
14770 								if (LastHour != -1)
14771 								{
14772 									int NextCalculatedHour = (LastHour + 1) % 24;
14773 									if (Hour != NextCalculatedHour)
14774 									{
14775 										//Looks like we have a GAP somewhere, finish the last hour
14776 										root["result"][ii]["d"] = LastDate;
14777 										double mmval = ActTotal - LastValue;
14778 										mmval *= AddjMulti;
14779 										sprintf(szTmp, "%.1f", mmval);
14780 										root["result"][ii]["mm"] = szTmp;
14781 										ii++;
14782 									}
14783 									else
14784 									{
14785 										root["result"][ii]["d"] = sd[1].substr(0, 16);
14786 										double mmval = ActTotal - LastTotalPreviousHour;
14787 										mmval *= AddjMulti;
14788 										sprintf(szTmp, "%.1f", mmval);
14789 										root["result"][ii]["mm"] = szTmp;
14790 										ii++;
14791 									}
14792 								}
14793 								LastHour = Hour;
14794 								LastTotalPreviousHour = ActTotal;
14795 							}
14796 							LastValue = ActTotal;
14797 							LastDate = sd[1];
14798 						}
14799 					}
14800 				}
14801 				else if (sensor == "wind") {
14802 					root["status"] = "OK";
14803 					root["title"] = "Graph " + sensor + " " + srange;
14804 
14805 					result = m_sql.safe_query("SELECT Direction, Speed, Gust, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
14806 					if (!result.empty())
14807 					{
14808 						int ii = 0;
14809 						for (const auto & itt : result)
14810 						{
14811 							std::vector<std::string> sd = itt;
14812 
14813 							root["result"][ii]["d"] = sd[3].substr(0, 16);
14814 							root["result"][ii]["di"] = sd[0];
14815 
14816 							int intSpeed = atoi(sd[1].c_str());
14817 							int intGust = atoi(sd[2].c_str());
14818 							if (m_sql.m_windunit != WINDUNIT_Beaufort)
14819 							{
14820 								sprintf(szTmp, "%.1f", float(intSpeed) * m_sql.m_windscale);
14821 								root["result"][ii]["sp"] = szTmp;
14822 								sprintf(szTmp, "%.1f", float(intGust) * m_sql.m_windscale);
14823 								root["result"][ii]["gu"] = szTmp;
14824 							}
14825 							else
14826 							{
14827 								float windspeedms = float(intSpeed)*0.1f;
14828 								float windgustms = float(intGust)*0.1f;
14829 								sprintf(szTmp, "%d", MStoBeaufort(windspeedms));
14830 								root["result"][ii]["sp"] = szTmp;
14831 								sprintf(szTmp, "%d", MStoBeaufort(windgustms));
14832 								root["result"][ii]["gu"] = szTmp;
14833 							}
14834 							ii++;
14835 						}
14836 					}
14837 				}
14838 				else if (sensor == "winddir") {
14839 					root["status"] = "OK";
14840 					root["title"] = "Graph " + sensor + " " + srange;
14841 
14842 					result = m_sql.safe_query("SELECT Direction, Speed, Gust FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
14843 					if (!result.empty())
14844 					{
14845 						std::map<int, int> _directions;
14846 						int wdirtabletemp[17][8];
14847 						std::string szLegendLabels[7];
14848 						int ii = 0;
14849 
14850 						int totalvalues = 0;
14851 						//init dir list
14852 						int idir;
14853 						for (idir = 0; idir < 360 + 1; idir++)
14854 							_directions[idir] = 0;
14855 						for (ii = 0; ii < 17; ii++)
14856 						{
14857 							for (int jj = 0; jj < 8; jj++)
14858 							{
14859 								wdirtabletemp[ii][jj] = 0;
14860 							}
14861 						}
14862 
14863 						if (m_sql.m_windunit == WINDUNIT_MS)
14864 						{
14865 							szLegendLabels[0] = "&lt; 0.5 " + m_sql.m_windsign;
14866 							szLegendLabels[1] = "0.5-2 " + m_sql.m_windsign;
14867 							szLegendLabels[2] = "2-4 " + m_sql.m_windsign;
14868 							szLegendLabels[3] = "4-6 " + m_sql.m_windsign;
14869 							szLegendLabels[4] = "6-8 " + m_sql.m_windsign;
14870 							szLegendLabels[5] = "8-10 " + m_sql.m_windsign;
14871 							szLegendLabels[6] = "&gt; 10" + m_sql.m_windsign;
14872 						}
14873 						else if (m_sql.m_windunit == WINDUNIT_KMH)
14874 						{
14875 							szLegendLabels[0] = "&lt; 2 " + m_sql.m_windsign;
14876 							szLegendLabels[1] = "2-4 " + m_sql.m_windsign;
14877 							szLegendLabels[2] = "4-6 " + m_sql.m_windsign;
14878 							szLegendLabels[3] = "6-10 " + m_sql.m_windsign;
14879 							szLegendLabels[4] = "10-20 " + m_sql.m_windsign;
14880 							szLegendLabels[5] = "20-36 " + m_sql.m_windsign;
14881 							szLegendLabels[6] = "&gt; 36" + m_sql.m_windsign;
14882 						}
14883 						else if (m_sql.m_windunit == WINDUNIT_MPH)
14884 						{
14885 							szLegendLabels[0] = "&lt; 3 " + m_sql.m_windsign;
14886 							szLegendLabels[1] = "3-7 " + m_sql.m_windsign;
14887 							szLegendLabels[2] = "7-12 " + m_sql.m_windsign;
14888 							szLegendLabels[3] = "12-18 " + m_sql.m_windsign;
14889 							szLegendLabels[4] = "18-24 " + m_sql.m_windsign;
14890 							szLegendLabels[5] = "24-46 " + m_sql.m_windsign;
14891 							szLegendLabels[6] = "&gt; 46" + m_sql.m_windsign;
14892 						}
14893 						else if (m_sql.m_windunit == WINDUNIT_Knots)
14894 						{
14895 							szLegendLabels[0] = "&lt; 3 " + m_sql.m_windsign;
14896 							szLegendLabels[1] = "3-7 " + m_sql.m_windsign;
14897 							szLegendLabels[2] = "7-17 " + m_sql.m_windsign;
14898 							szLegendLabels[3] = "17-27 " + m_sql.m_windsign;
14899 							szLegendLabels[4] = "27-34 " + m_sql.m_windsign;
14900 							szLegendLabels[5] = "34-41 " + m_sql.m_windsign;
14901 							szLegendLabels[6] = "&gt; 41" + m_sql.m_windsign;
14902 						}
14903 						else if (m_sql.m_windunit == WINDUNIT_Beaufort)
14904 						{
14905 							szLegendLabels[0] = "&lt; 2 " + m_sql.m_windsign;
14906 							szLegendLabels[1] = "2-4 " + m_sql.m_windsign;
14907 							szLegendLabels[2] = "4-6 " + m_sql.m_windsign;
14908 							szLegendLabels[3] = "6-8 " + m_sql.m_windsign;
14909 							szLegendLabels[4] = "8-10 " + m_sql.m_windsign;
14910 							szLegendLabels[5] = "10-12 " + m_sql.m_windsign;
14911 							szLegendLabels[6] = "&gt; 12" + m_sql.m_windsign;
14912 						}
14913 						else {
14914 							//Todo !
14915 							szLegendLabels[0] = "&lt; 0.5 " + m_sql.m_windsign;
14916 							szLegendLabels[1] = "0.5-2 " + m_sql.m_windsign;
14917 							szLegendLabels[2] = "2-4 " + m_sql.m_windsign;
14918 							szLegendLabels[3] = "4-6 " + m_sql.m_windsign;
14919 							szLegendLabels[4] = "6-8 " + m_sql.m_windsign;
14920 							szLegendLabels[5] = "8-10 " + m_sql.m_windsign;
14921 							szLegendLabels[6] = "&gt; 10" + m_sql.m_windsign;
14922 						}
14923 
14924 
14925 						for (const auto & itt : result)
14926 						{
14927 							std::vector<std::string> sd = itt;
14928 							float fdirection = static_cast<float>(atof(sd[0].c_str()));
14929 							if (fdirection >= 360)
14930 								fdirection = 0;
14931 							int direction = int(fdirection);
14932 							float speedOrg = static_cast<float>(atof(sd[1].c_str()));
14933 							float gustOrg = static_cast<float>(atof(sd[2].c_str()));
14934 							if ((gustOrg == 0) && (speedOrg != 0))
14935 								gustOrg = speedOrg;
14936 							if (gustOrg == 0)
14937 								continue; //no direction if wind is still
14938 							//float speed = speedOrg * m_sql.m_windscale;
14939 							float gust = gustOrg * m_sql.m_windscale;
14940 							int bucket = int(fdirection / 22.5f);
14941 
14942 							int speedpos = 0;
14943 
14944 							if (m_sql.m_windunit == WINDUNIT_MS)
14945 							{
14946 								if (gust < 0.5f) speedpos = 0;
14947 								else if (gust < 2.0f) speedpos = 1;
14948 								else if (gust < 4.0f) speedpos = 2;
14949 								else if (gust < 6.0f) speedpos = 3;
14950 								else if (gust < 8.0f) speedpos = 4;
14951 								else if (gust < 10.0f) speedpos = 5;
14952 								else speedpos = 6;
14953 							}
14954 							else if (m_sql.m_windunit == WINDUNIT_KMH)
14955 							{
14956 								if (gust < 2.0f) speedpos = 0;
14957 								else if (gust < 4.0f) speedpos = 1;
14958 								else if (gust < 6.0f) speedpos = 2;
14959 								else if (gust < 10.0f) speedpos = 3;
14960 								else if (gust < 20.0f) speedpos = 4;
14961 								else if (gust < 36.0f) speedpos = 5;
14962 								else speedpos = 6;
14963 							}
14964 							else if (m_sql.m_windunit == WINDUNIT_MPH)
14965 							{
14966 								if (gust < 3.0f) speedpos = 0;
14967 								else if (gust < 7.0f) speedpos = 1;
14968 								else if (gust < 12.0f) speedpos = 2;
14969 								else if (gust < 18.0f) speedpos = 3;
14970 								else if (gust < 24.0f) speedpos = 4;
14971 								else if (gust < 46.0f) speedpos = 5;
14972 								else speedpos = 6;
14973 							}
14974 							else if (m_sql.m_windunit == WINDUNIT_Knots)
14975 							{
14976 								if (gust < 3.0f) speedpos = 0;
14977 								else if (gust < 7.0f) speedpos = 1;
14978 								else if (gust < 17.0f) speedpos = 2;
14979 								else if (gust < 27.0f) speedpos = 3;
14980 								else if (gust < 34.0f) speedpos = 4;
14981 								else if (gust < 41.0f) speedpos = 5;
14982 								else speedpos = 6;
14983 							}
14984 							else if (m_sql.m_windunit == WINDUNIT_Beaufort)
14985 							{
14986 								float gustms = gustOrg * 0.1f;
14987 								int iBeaufort = MStoBeaufort(gustms);
14988 								if (iBeaufort < 2) speedpos = 0;
14989 								else if (iBeaufort < 4) speedpos = 1;
14990 								else if (iBeaufort < 6) speedpos = 2;
14991 								else if (iBeaufort < 8) speedpos = 3;
14992 								else if (iBeaufort < 10) speedpos = 4;
14993 								else if (iBeaufort < 12) speedpos = 5;
14994 								else speedpos = 6;
14995 							}
14996 							else
14997 							{
14998 								//Still todo !
14999 								if (gust < 0.5f) speedpos = 0;
15000 								else if (gust < 2.0f) speedpos = 1;
15001 								else if (gust < 4.0f) speedpos = 2;
15002 								else if (gust < 6.0f) speedpos = 3;
15003 								else if (gust < 8.0f) speedpos = 4;
15004 								else if (gust < 10.0f) speedpos = 5;
15005 								else speedpos = 6;
15006 							}
15007 							wdirtabletemp[bucket][speedpos]++;
15008 							_directions[direction]++;
15009 							totalvalues++;
15010 						}
15011 
15012 						for (int jj = 0; jj < 7; jj++)
15013 						{
15014 							root["result_speed"][jj]["label"] = szLegendLabels[jj];
15015 
15016 							for (ii = 0; ii < 16; ii++)
15017 							{
15018 								float svalue = 0;
15019 								if (totalvalues > 0)
15020 								{
15021 									svalue = (100.0f / totalvalues)*wdirtabletemp[ii][jj];
15022 								}
15023 								sprintf(szTmp, "%.2f", svalue);
15024 								root["result_speed"][jj]["sp"][ii] = szTmp;
15025 							}
15026 						}
15027 						ii = 0;
15028 						for (idir = 0; idir < 360 + 1; idir++)
15029 						{
15030 							if (_directions[idir] != 0)
15031 							{
15032 								root["result"][ii]["dig"] = idir;
15033 								float percentage = 0;
15034 								if (totalvalues > 0)
15035 								{
15036 									percentage = (float(100.0 / float(totalvalues))*float(_directions[idir]));
15037 								}
15038 								sprintf(szTmp, "%.2f", percentage);
15039 								root["result"][ii]["div"] = szTmp;
15040 								ii++;
15041 							}
15042 						}
15043 					}
15044 				}
15045 
15046 			}//day
15047 			else if (srange == "week")
15048 			{
15049 				if (sensor == "rain") {
15050 					root["status"] = "OK";
15051 					root["title"] = "Graph " + sensor + " " + srange;
15052 
15053 					char szDateStart[40];
15054 					char szDateEnd[40];
15055 					sprintf(szDateEnd, "%04d-%02d-%02d", tm1.tm_year + 1900, tm1.tm_mon + 1, tm1.tm_mday);
15056 
15057 					//Subtract one week
15058 					time_t weekbefore;
15059 					struct tm tm2;
15060 					getNoon(weekbefore, tm2, tm1.tm_year + 1900, tm1.tm_mon + 1, tm1.tm_mday - 7); // We only want the date
15061 					sprintf(szDateStart, "%04d-%02d-%02d", tm2.tm_year + 1900, tm2.tm_mon + 1, tm2.tm_mday);
15062 
15063 					result = m_sql.safe_query("SELECT Total, Rate, Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStart, szDateEnd);
15064 					int ii = 0;
15065 					if (!result.empty())
15066 					{
15067 						for (const auto & itt : result)
15068 						{
15069 							std::vector<std::string> sd = itt;
15070 
15071 							root["result"][ii]["d"] = sd[2].substr(0, 16);
15072 							double mmval = atof(sd[0].c_str());
15073 							mmval *= AddjMulti;
15074 							sprintf(szTmp, "%.1f", mmval);
15075 							root["result"][ii]["mm"] = szTmp;
15076 							ii++;
15077 						}
15078 					}
15079 					//add today (have to calculate it)
15080 					if (dSubType == sTypeRAINWU || dSubType == sTypeRAINByRate)
15081 					{
15082 						result = m_sql.safe_query(
15083 							"SELECT Total, Total, Rate FROM Rain WHERE (DeviceRowID=%" PRIu64 " AND Date>='%q') ORDER BY ROWID DESC LIMIT 1",
15084 							idx, szDateEnd);
15085 					}
15086 					else
15087 					{
15088 						result = m_sql.safe_query(
15089 							"SELECT MIN(Total), MAX(Total), MAX(Rate) FROM Rain WHERE (DeviceRowID=%" PRIu64 " AND Date>='%q')",
15090 							idx, szDateEnd);
15091 					}
15092 					if (!result.empty())
15093 					{
15094 						std::vector<std::string> sd = result[0];
15095 
15096 						float total_min = static_cast<float>(atof(sd[0].c_str()));
15097 						float total_max = static_cast<float>(atof(sd[1].c_str()));
15098 						//int rate = atoi(sd[2].c_str());
15099 
15100 						double total_real = 0;
15101 						if (dSubType == sTypeRAINWU || dSubType == sTypeRAINByRate)
15102 						{
15103 							total_real = total_max;
15104 						}
15105 						else
15106 						{
15107 							total_real = total_max - total_min;
15108 						}
15109 						total_real *= AddjMulti;
15110 						sprintf(szTmp, "%.1f", total_real);
15111 						root["result"][ii]["d"] = szDateEnd;
15112 						root["result"][ii]["mm"] = szTmp;
15113 						ii++;
15114 					}
15115 				}
15116 				else if (sensor == "counter")
15117 				{
15118 					root["status"] = "OK";
15119 					root["title"] = "Graph " + sensor + " " + srange;
15120 					root["ValueQuantity"] = options["ValueQuantity"];
15121 					root["ValueUnits"] = options["ValueUnits"];
15122 
15123 					char szDateStart[40];
15124 					char szDateEnd[40];
15125 					sprintf(szDateEnd, "%04d-%02d-%02d", tm1.tm_year + 1900, tm1.tm_mon + 1, tm1.tm_mday);
15126 
15127 					//Subtract one week
15128 					time_t weekbefore;
15129 					struct tm tm2;
15130 					getNoon(weekbefore, tm2, tm1.tm_year + 1900, tm1.tm_mon + 1, tm1.tm_mday - 7); // We only want the date
15131 					sprintf(szDateStart, "%04d-%02d-%02d", tm2.tm_year + 1900, tm2.tm_mon + 1, tm2.tm_mday);
15132 
15133 					int ii = 0;
15134 					if (dType == pTypeP1Power)
15135 					{
15136 						result = m_sql.safe_query("SELECT Value1,Value2,Value5,Value6,Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStart, szDateEnd);
15137 						if (!result.empty())
15138 						{
15139 							bool bHaveDeliverd = false;
15140 							for (const auto & itt : result)
15141 							{
15142 								std::vector<std::string> sd = itt;
15143 								root["result"][ii]["d"] = sd[4].substr(0, 16);
15144 								std::string szValueUsage1 = sd[0];
15145 								std::string szValueDeliv1 = sd[1];
15146 								std::string szValueUsage2 = sd[2];
15147 								std::string szValueDeliv2 = sd[3];
15148 
15149 								float fUsage1 = (float)(atof(szValueUsage1.c_str()));
15150 								float fUsage2 = (float)(atof(szValueUsage2.c_str()));
15151 								float fDeliv1 = (float)(atof(szValueDeliv1.c_str()));
15152 								float fDeliv2 = (float)(atof(szValueDeliv2.c_str()));
15153 
15154 								fDeliv1 = (fDeliv1 < 10) ? 0 : fDeliv1;
15155 								fDeliv2 = (fDeliv2 < 10) ? 0 : fDeliv2;
15156 
15157 								if ((fDeliv1 != 0) || (fDeliv2 != 0))
15158 									bHaveDeliverd = true;
15159 								sprintf(szTmp, "%.3f", fUsage1 / divider);
15160 								root["result"][ii]["v"] = szTmp;
15161 								sprintf(szTmp, "%.3f", fUsage2 / divider);
15162 								root["result"][ii]["v2"] = szTmp;
15163 								sprintf(szTmp, "%.3f", fDeliv1 / divider);
15164 								root["result"][ii]["r1"] = szTmp;
15165 								sprintf(szTmp, "%.3f", fDeliv2 / divider);
15166 								root["result"][ii]["r2"] = szTmp;
15167 								ii++;
15168 							}
15169 							if (bHaveDeliverd)
15170 							{
15171 								root["delivered"] = true;
15172 							}
15173 						}
15174 					}
15175 					else
15176 					{
15177 						result = m_sql.safe_query("SELECT Value, Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStart, szDateEnd);
15178 						if (!result.empty())
15179 						{
15180 							for (const auto & itt : result)
15181 							{
15182 								std::vector<std::string> sd = itt;
15183 
15184 								root["result"][ii]["d"] = sd[1].substr(0, 16);
15185 								std::string szValue = sd[0];
15186 								switch (metertype)
15187 								{
15188 								case MTYPE_ENERGY:
15189 								case MTYPE_ENERGY_GENERATED:
15190 									sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
15191 									szValue = szTmp;
15192 									break;
15193 								case MTYPE_GAS:
15194 									sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
15195 									szValue = szTmp;
15196 									break;
15197 								case MTYPE_WATER:
15198 									sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
15199 									szValue = szTmp;
15200 									break;
15201 								case MTYPE_COUNTER:
15202 									//value already set above!
15203 									break;
15204 								default:
15205 									szValue = "0";
15206 									break;
15207 								}
15208 								root["result"][ii]["v"] = szValue;
15209 								ii++;
15210 							}
15211 						}
15212 					}
15213 					//add today (have to calculate it)
15214 					if (dType == pTypeP1Power)
15215 					{
15216 						result = m_sql.safe_query(
15217 							"SELECT MIN(Value1), MAX(Value1), MIN(Value2), MAX(Value2),MIN(Value5), MAX(Value5), MIN(Value6), MAX(Value6) FROM MultiMeter WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
15218 							idx, szDateEnd);
15219 						if (!result.empty())
15220 						{
15221 							std::vector<std::string> sd = result[0];
15222 
15223 							unsigned long long total_min_usage_1 = std::strtoull(sd[0].c_str(), nullptr, 10);
15224 							unsigned long long total_max_usage_1 = std::strtoull(sd[1].c_str(), nullptr, 10);
15225 							unsigned long long total_min_usage_2 = std::strtoull(sd[4].c_str(), nullptr, 10);
15226 							unsigned long long total_max_usage_2 = std::strtoull(sd[5].c_str(), nullptr, 10);
15227 							unsigned long long total_real_usage_1, total_real_usage_2;
15228 							unsigned long long total_min_deliv_1 = std::strtoull(sd[2].c_str(), nullptr, 10);
15229 							unsigned long long total_max_deliv_1 = std::strtoull(sd[3].c_str(), nullptr, 10);
15230 							unsigned long long total_min_deliv_2 = std::strtoull(sd[6].c_str(), nullptr, 10);
15231 							unsigned long long total_max_deliv_2 = std::strtoull(sd[7].c_str(), nullptr, 10);
15232 							unsigned long long total_real_deliv_1, total_real_deliv_2;
15233 
15234 							bool bHaveDeliverd = false;
15235 
15236 							total_real_usage_1 = total_max_usage_1 - total_min_usage_1;
15237 							total_real_usage_2 = total_max_usage_2 - total_min_usage_2;
15238 
15239 							total_real_deliv_1 = total_max_deliv_1 - total_min_deliv_1;
15240 							total_real_deliv_2 = total_max_deliv_2 - total_min_deliv_2;
15241 							if ((total_real_deliv_1 != 0) || (total_real_deliv_2 != 0))
15242 								bHaveDeliverd = true;
15243 
15244 							root["result"][ii]["d"] = szDateEnd;
15245 
15246 							sprintf(szTmp, "%llu", total_real_usage_1);
15247 							std::string szValue = szTmp;
15248 							sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
15249 							root["result"][ii]["v"] = szTmp;
15250 							sprintf(szTmp, "%llu", total_real_usage_2);
15251 							szValue = szTmp;
15252 							sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
15253 							root["result"][ii]["v2"] = szTmp;
15254 
15255 							sprintf(szTmp, "%llu", total_real_deliv_1);
15256 							szValue = szTmp;
15257 							sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
15258 							root["result"][ii]["r1"] = szTmp;
15259 							sprintf(szTmp, "%llu", total_real_deliv_2);
15260 							szValue = szTmp;
15261 							sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
15262 							root["result"][ii]["r2"] = szTmp;
15263 
15264 							ii++;
15265 							if (bHaveDeliverd)
15266 							{
15267 								root["delivered"] = true;
15268 							}
15269 						}
15270 					}
15271 					else if (!bIsManagedCounter)
15272 					{
15273 						result = m_sql.safe_query("SELECT MIN(Value), MAX(Value) FROM Meter WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
15274 							idx, szDateEnd);
15275 						if (!result.empty())
15276 						{
15277 							std::vector<std::string> sd = result[0];
15278 
15279 							unsigned long long total_min = std::strtoull(sd[0].c_str(), nullptr, 10);
15280 							unsigned long long total_max = std::strtoull(sd[1].c_str(), nullptr, 10);
15281 							unsigned long long total_real;
15282 
15283 							total_real = total_max - total_min;
15284 							sprintf(szTmp, "%llu", total_real);
15285 							std::string szValue = szTmp;
15286 							switch (metertype)
15287 							{
15288 							case MTYPE_ENERGY:
15289 							case MTYPE_ENERGY_GENERATED:
15290 								sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
15291 								szValue = szTmp;
15292 								break;
15293 							case MTYPE_GAS:
15294 								sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
15295 								szValue = szTmp;
15296 								break;
15297 							case MTYPE_WATER:
15298 								sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
15299 								szValue = szTmp;
15300 								break;
15301 							case MTYPE_COUNTER:
15302 								//value already set above!
15303 								break;
15304 							default:
15305 								szValue = "0";
15306 								break;
15307 							}
15308 
15309 							root["result"][ii]["d"] = szDateEnd;
15310 							root["result"][ii]["v"] = szValue;
15311 							ii++;
15312 						}
15313 					}
15314 				}
15315 			}//week
15316 			else if ((srange == "month") || (srange == "year"))
15317 			{
15318 				char szDateStart[40];
15319 				char szDateEnd[40];
15320 				char szDateStartPrev[40];
15321 				char szDateEndPrev[40];
15322 
15323 				std::string sactmonth = request::findValue(&req, "actmonth");
15324 				std::string sactyear = request::findValue(&req, "actyear");
15325 
15326 				int actMonth = atoi(sactmonth.c_str());
15327 				int actYear = atoi(sactyear.c_str());
15328 
15329 				if ((sactmonth != "") && (sactyear != ""))
15330 				{
15331 					sprintf(szDateStart, "%04d-%02d-%02d", actYear, actMonth, 1);
15332 					sprintf(szDateStartPrev, "%04d-%02d-%02d", actYear - 1, actMonth, 1);
15333 					actMonth++;
15334 					if (actMonth == 13)
15335 					{
15336 						actMonth = 1;
15337 						actYear++;
15338 					}
15339 					sprintf(szDateEnd, "%04d-%02d-%02d", actYear, actMonth, 1);
15340 					sprintf(szDateEndPrev, "%04d-%02d-%02d", actYear - 1, actMonth, 1);
15341 				}
15342 				else if (sactyear != "")
15343 				{
15344 					sprintf(szDateStart, "%04d-%02d-%02d", actYear, 1, 1);
15345 					sprintf(szDateStartPrev, "%04d-%02d-%02d", actYear - 1, 1, 1);
15346 					actYear++;
15347 					sprintf(szDateEnd, "%04d-%02d-%02d", actYear, 1, 1);
15348 					sprintf(szDateEndPrev, "%04d-%02d-%02d", actYear - 1, 1, 1);
15349 				}
15350 				else
15351 				{
15352 					sprintf(szDateEnd, "%04d-%02d-%02d", tm1.tm_year + 1900, tm1.tm_mon + 1, tm1.tm_mday);
15353 					sprintf(szDateEndPrev, "%04d-%02d-%02d", tm1.tm_year + 1900 - 1, tm1.tm_mon + 1, tm1.tm_mday);
15354 
15355 					struct tm tm2;
15356 					if (srange == "month")
15357 					{
15358 						//Subtract one month
15359 						time_t monthbefore;
15360 						getNoon(monthbefore, tm2, tm1.tm_year + 1900, tm1.tm_mon, tm1.tm_mday);
15361 					}
15362 					else
15363 					{
15364 						//Subtract one year
15365 						time_t yearbefore;
15366 						getNoon(yearbefore, tm2, tm1.tm_year + 1900 - 1, tm1.tm_mon + 1, tm1.tm_mday);
15367 					}
15368 
15369 					sprintf(szDateStart, "%04d-%02d-%02d", tm2.tm_year + 1900, tm2.tm_mon + 1, tm2.tm_mday);
15370 					sprintf(szDateStartPrev, "%04d-%02d-%02d", tm2.tm_year + 1900 - 1, tm2.tm_mon + 1, tm2.tm_mday);
15371 				}
15372 
15373 				if (sensor == "temp") {
15374 					root["status"] = "OK";
15375 					root["title"] = "Graph " + sensor + " " + srange;
15376 
15377 					//Actual Year
15378 					result = m_sql.safe_query(
15379 						"SELECT Temp_Min, Temp_Max, Chill_Min, Chill_Max,"
15380 						" Humidity, Barometer, Temp_Avg, Date, SetPoint_Min,"
15381 						" SetPoint_Max, SetPoint_Avg "
15382 						"FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q'"
15383 						" AND Date<='%q') ORDER BY Date ASC",
15384 						dbasetable.c_str(), idx, szDateStart, szDateEnd);
15385 					int ii = 0;
15386 					if (!result.empty())
15387 					{
15388 						for (const auto & itt : result)
15389 						{
15390 							std::vector<std::string> sd = itt;
15391 
15392 							root["result"][ii]["d"] = sd[7].substr(0, 16);
15393 
15394 							if (
15395 								(dType == pTypeRego6XXTemp) || (dType == pTypeTEMP) || (dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO) || (dType == pTypeTEMP_BARO) || (dType == pTypeWIND) || (dType == pTypeThermostat1) || (dType == pTypeRadiator1) ||
15396 								((dType == pTypeRFXSensor) && (dSubType == sTypeRFXSensorTemp)) ||
15397 								((dType == pTypeUV) && (dSubType == sTypeUV3)) ||
15398 								((dType == pTypeGeneral) && (dSubType == sTypeSystemTemp)) ||
15399 								((dType == pTypeThermostat) && (dSubType == sTypeThermSetpoint)) ||
15400 								(dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater) ||
15401 								((dType == pTypeGeneral) && (dSubType == sTypeBaro))
15402 								)
15403 							{
15404 								bool bOK = true;
15405 								if (dType == pTypeWIND)
15406 								{
15407 									bOK = ((dSubType != sTypeWINDNoTemp) && (dSubType != sTypeWINDNoTempNoChill));
15408 								}
15409 								if (bOK)
15410 								{
15411 									double te = ConvertTemperature(atof(sd[1].c_str()), tempsign);
15412 									double tm = ConvertTemperature(atof(sd[0].c_str()), tempsign);
15413 									double ta = ConvertTemperature(atof(sd[6].c_str()), tempsign);
15414 									root["result"][ii]["te"] = te;
15415 									root["result"][ii]["tm"] = tm;
15416 									root["result"][ii]["ta"] = ta;
15417 								}
15418 							}
15419 							if (
15420 								((dType == pTypeWIND) && (dSubType == sTypeWIND4)) ||
15421 								((dType == pTypeWIND) && (dSubType == sTypeWINDNoTemp))
15422 								)
15423 							{
15424 								double ch = ConvertTemperature(atof(sd[3].c_str()), tempsign);
15425 								double cm = ConvertTemperature(atof(sd[2].c_str()), tempsign);
15426 								root["result"][ii]["ch"] = ch;
15427 								root["result"][ii]["cm"] = cm;
15428 							}
15429 							if ((dType == pTypeHUM) || (dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO))
15430 							{
15431 								root["result"][ii]["hu"] = sd[4];
15432 							}
15433 							if (
15434 								(dType == pTypeTEMP_HUM_BARO) ||
15435 								(dType == pTypeTEMP_BARO) ||
15436 								((dType == pTypeGeneral) && (dSubType == sTypeBaro))
15437 								)
15438 							{
15439 								if (dType == pTypeTEMP_HUM_BARO)
15440 								{
15441 									if (dSubType == sTypeTHBFloat)
15442 									{
15443 										sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
15444 										root["result"][ii]["ba"] = szTmp;
15445 									}
15446 									else
15447 										root["result"][ii]["ba"] = sd[5];
15448 								}
15449 								else if (dType == pTypeTEMP_BARO)
15450 								{
15451 									sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
15452 									root["result"][ii]["ba"] = szTmp;
15453 								}
15454 								else if ((dType == pTypeGeneral) && (dSubType == sTypeBaro))
15455 								{
15456 									sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
15457 									root["result"][ii]["ba"] = szTmp;
15458 								}
15459 							}
15460 							if ((dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater))
15461 							{
15462 								double sm = ConvertTemperature(atof(sd[8].c_str()), tempsign);
15463 								double sx = ConvertTemperature(atof(sd[9].c_str()), tempsign);
15464 								double se = ConvertTemperature(atof(sd[10].c_str()), tempsign);
15465 								root["result"][ii]["sm"] = sm;
15466 								root["result"][ii]["se"] = se;
15467 								root["result"][ii]["sx"] = sx;
15468 							}
15469 							ii++;
15470 						}
15471 					}
15472 					//add today (have to calculate it)
15473 					result = m_sql.safe_query(
15474 						"SELECT MIN(Temperature), MAX(Temperature),"
15475 						" MIN(Chill), MAX(Chill), AVG(Humidity),"
15476 						" AVG(Barometer), AVG(Temperature), MIN(SetPoint),"
15477 						" MAX(SetPoint), AVG(SetPoint) "
15478 						"FROM Temperature WHERE (DeviceRowID==%" PRIu64 ""
15479 						" AND Date>='%q')",
15480 						idx, szDateEnd);
15481 					if (!result.empty())
15482 					{
15483 						std::vector<std::string> sd = result[0];
15484 
15485 						root["result"][ii]["d"] = szDateEnd;
15486 						if (
15487 							((dType == pTypeRego6XXTemp) || (dType == pTypeTEMP) || (dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO) || (dType == pTypeTEMP_BARO) || (dType == pTypeWIND) || (dType == pTypeThermostat1) || (dType == pTypeRadiator1)) ||
15488 							((dType == pTypeUV) && (dSubType == sTypeUV3)) ||
15489 							((dType == pTypeWIND) && (dSubType == sTypeWIND4)) ||
15490 							(dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater)
15491 							)
15492 						{
15493 							double te = ConvertTemperature(atof(sd[1].c_str()), tempsign);
15494 							double tm = ConvertTemperature(atof(sd[0].c_str()), tempsign);
15495 							double ta = ConvertTemperature(atof(sd[6].c_str()), tempsign);
15496 
15497 							root["result"][ii]["te"] = te;
15498 							root["result"][ii]["tm"] = tm;
15499 							root["result"][ii]["ta"] = ta;
15500 						}
15501 						if (
15502 							((dType == pTypeWIND) && (dSubType == sTypeWIND4)) ||
15503 							((dType == pTypeWIND) && (dSubType == sTypeWINDNoTemp))
15504 							)
15505 						{
15506 							double ch = ConvertTemperature(atof(sd[3].c_str()), tempsign);
15507 							double cm = ConvertTemperature(atof(sd[2].c_str()), tempsign);
15508 							root["result"][ii]["ch"] = ch;
15509 							root["result"][ii]["cm"] = cm;
15510 						}
15511 						if ((dType == pTypeHUM) || (dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO))
15512 						{
15513 							root["result"][ii]["hu"] = sd[4];
15514 						}
15515 						if (
15516 							(dType == pTypeTEMP_HUM_BARO) ||
15517 							(dType == pTypeTEMP_BARO) ||
15518 							((dType == pTypeGeneral) && (dSubType == sTypeBaro))
15519 							)
15520 						{
15521 							if (dType == pTypeTEMP_HUM_BARO)
15522 							{
15523 								if (dSubType == sTypeTHBFloat)
15524 								{
15525 									sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
15526 									root["result"][ii]["ba"] = szTmp;
15527 								}
15528 								else
15529 									root["result"][ii]["ba"] = sd[5];
15530 							}
15531 							else if (dType == pTypeTEMP_BARO)
15532 							{
15533 								sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
15534 								root["result"][ii]["ba"] = szTmp;
15535 							}
15536 							else if ((dType == pTypeGeneral) && (dSubType == sTypeBaro))
15537 							{
15538 								sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
15539 								root["result"][ii]["ba"] = szTmp;
15540 							}
15541 						}
15542 						if ((dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater))
15543 						{
15544 							double sx = ConvertTemperature(atof(sd[8].c_str()), tempsign);
15545 							double sm = ConvertTemperature(atof(sd[7].c_str()), tempsign);
15546 							double se = ConvertTemperature(atof(sd[9].c_str()), tempsign);
15547 							root["result"][ii]["se"] = se;
15548 							root["result"][ii]["sm"] = sm;
15549 							root["result"][ii]["sx"] = sx;
15550 						}
15551 						ii++;
15552 					}
15553 					//Previous Year
15554 					result = m_sql.safe_query(
15555 						"SELECT Temp_Min, Temp_Max, Chill_Min, Chill_Max,"
15556 						" Humidity, Barometer, Temp_Avg, Date, SetPoint_Min,"
15557 						" SetPoint_Max, SetPoint_Avg "
15558 						"FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q'"
15559 						" AND Date<='%q') ORDER BY Date ASC",
15560 						dbasetable.c_str(), idx, szDateStartPrev, szDateEndPrev);
15561 					if (!result.empty())
15562 					{
15563 						iPrev = 0;
15564 						for (const auto & itt : result)
15565 						{
15566 							std::vector<std::string> sd = itt;
15567 
15568 							root["resultprev"][iPrev]["d"] = sd[7].substr(0, 16);
15569 
15570 							if (
15571 								(dType == pTypeRego6XXTemp) || (dType == pTypeTEMP) || (dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO) || (dType == pTypeTEMP_BARO) || (dType == pTypeWIND) || (dType == pTypeThermostat1) || (dType == pTypeRadiator1) ||
15572 								((dType == pTypeRFXSensor) && (dSubType == sTypeRFXSensorTemp)) ||
15573 								((dType == pTypeUV) && (dSubType == sTypeUV3)) ||
15574 								((dType == pTypeGeneral) && (dSubType == sTypeSystemTemp)) ||
15575 								((dType == pTypeThermostat) && (dSubType == sTypeThermSetpoint)) ||
15576 								(dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater)
15577 								)
15578 							{
15579 								bool bOK = true;
15580 								if (dType == pTypeWIND)
15581 								{
15582 									bOK = ((dSubType == sTypeWIND4) || (dSubType == sTypeWINDNoTemp));
15583 								}
15584 								if (bOK)
15585 								{
15586 									double te = ConvertTemperature(atof(sd[1].c_str()), tempsign);
15587 									double tm = ConvertTemperature(atof(sd[0].c_str()), tempsign);
15588 									double ta = ConvertTemperature(atof(sd[6].c_str()), tempsign);
15589 									root["resultprev"][iPrev]["te"] = te;
15590 									root["resultprev"][iPrev]["tm"] = tm;
15591 									root["resultprev"][iPrev]["ta"] = ta;
15592 								}
15593 							}
15594 							if (
15595 								((dType == pTypeWIND) && (dSubType == sTypeWIND4)) ||
15596 								((dType == pTypeWIND) && (dSubType == sTypeWINDNoTemp))
15597 								)
15598 							{
15599 								double ch = ConvertTemperature(atof(sd[3].c_str()), tempsign);
15600 								double cm = ConvertTemperature(atof(sd[2].c_str()), tempsign);
15601 								root["resultprev"][iPrev]["ch"] = ch;
15602 								root["resultprev"][iPrev]["cm"] = cm;
15603 							}
15604 							if ((dType == pTypeHUM) || (dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO))
15605 							{
15606 								root["resultprev"][iPrev]["hu"] = sd[4];
15607 							}
15608 							if (
15609 								(dType == pTypeTEMP_HUM_BARO) ||
15610 								(dType == pTypeTEMP_BARO) ||
15611 								((dType == pTypeGeneral) && (dSubType == sTypeBaro))
15612 								)
15613 							{
15614 								if (dType == pTypeTEMP_HUM_BARO)
15615 								{
15616 									if (dSubType == sTypeTHBFloat)
15617 									{
15618 										sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
15619 										root["resultprev"][iPrev]["ba"] = szTmp;
15620 									}
15621 									else
15622 										root["resultprev"][iPrev]["ba"] = sd[5];
15623 								}
15624 								else if (dType == pTypeTEMP_BARO)
15625 								{
15626 									sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
15627 									root["resultprev"][iPrev]["ba"] = szTmp;
15628 								}
15629 								else if ((dType == pTypeGeneral) && (dSubType == sTypeBaro))
15630 								{
15631 									sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
15632 									root["resultprev"][iPrev]["ba"] = szTmp;
15633 								}
15634 							}
15635 							if ((dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater))
15636 							{
15637 								double sx = ConvertTemperature(atof(sd[8].c_str()), tempsign);
15638 								double sm = ConvertTemperature(atof(sd[7].c_str()), tempsign);
15639 								double se = ConvertTemperature(atof(sd[9].c_str()), tempsign);
15640 								root["resultprev"][iPrev]["se"] = se;
15641 								root["resultprev"][iPrev]["sm"] = sm;
15642 								root["resultprev"][iPrev]["sx"] = sx;
15643 							}
15644 							iPrev++;
15645 						}
15646 					}
15647 				}
15648 				else if (sensor == "Percentage") {
15649 					root["status"] = "OK";
15650 					root["title"] = "Graph " + sensor + " " + srange;
15651 
15652 					result = m_sql.safe_query("SELECT Percentage_Min, Percentage_Max, Percentage_Avg, Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStart, szDateEnd);
15653 					int ii = 0;
15654 					if (!result.empty())
15655 					{
15656 						for (const auto & itt : result)
15657 						{
15658 							std::vector<std::string> sd = itt;
15659 
15660 							root["result"][ii]["d"] = sd[3].substr(0, 16);
15661 							root["result"][ii]["v_min"] = sd[0];
15662 							root["result"][ii]["v_max"] = sd[1];
15663 							root["result"][ii]["v_avg"] = sd[2];
15664 							ii++;
15665 						}
15666 					}
15667 					//add today (have to calculate it)
15668 					result = m_sql.safe_query(
15669 						"SELECT MIN(Percentage), MAX(Percentage), AVG(Percentage) FROM Percentage WHERE (DeviceRowID=%" PRIu64 " AND Date>='%q')",
15670 						idx, szDateEnd);
15671 					if (!result.empty())
15672 					{
15673 						std::vector<std::string> sd = result[0];
15674 						root["result"][ii]["d"] = szDateEnd;
15675 						root["result"][ii]["v_min"] = sd[0];
15676 						root["result"][ii]["v_max"] = sd[1];
15677 						root["result"][ii]["v_avg"] = sd[2];
15678 						ii++;
15679 					}
15680 
15681 				}
15682 				else if (sensor == "fan") {
15683 					root["status"] = "OK";
15684 					root["title"] = "Graph " + sensor + " " + srange;
15685 
15686 					result = m_sql.safe_query("SELECT Speed_Min, Speed_Max, Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStart, szDateEnd);
15687 					int ii = 0;
15688 					if (!result.empty())
15689 					{
15690 						for (const auto & itt : result)
15691 						{
15692 							std::vector<std::string> sd = itt;
15693 
15694 							root["result"][ii]["d"] = sd[2].substr(0, 16);
15695 							root["result"][ii]["v_max"] = sd[1];
15696 							root["result"][ii]["v_min"] = sd[0];
15697 							ii++;
15698 						}
15699 					}
15700 					//add today (have to calculate it)
15701 					result = m_sql.safe_query("SELECT MIN(Speed), MAX(Speed) FROM Fan WHERE (DeviceRowID=%" PRIu64 " AND Date>='%q')",
15702 						idx, szDateEnd);
15703 					if (!result.empty())
15704 					{
15705 						std::vector<std::string> sd = result[0];
15706 						root["result"][ii]["d"] = szDateEnd;
15707 						root["result"][ii]["v_max"] = sd[1];
15708 						root["result"][ii]["v_min"] = sd[0];
15709 						ii++;
15710 					}
15711 
15712 				}
15713 				else if (sensor == "uv") {
15714 					root["status"] = "OK";
15715 					root["title"] = "Graph " + sensor + " " + srange;
15716 
15717 					result = m_sql.safe_query("SELECT Level, Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStart, szDateEnd);
15718 					int ii = 0;
15719 					if (!result.empty())
15720 					{
15721 						for (const auto & itt : result)
15722 						{
15723 							std::vector<std::string> sd = itt;
15724 
15725 							root["result"][ii]["d"] = sd[1].substr(0, 16);
15726 							root["result"][ii]["uvi"] = sd[0];
15727 							ii++;
15728 						}
15729 					}
15730 					//add today (have to calculate it)
15731 					result = m_sql.safe_query(
15732 						"SELECT MAX(Level) FROM UV WHERE (DeviceRowID=%" PRIu64 " AND Date>='%q')",
15733 						idx, szDateEnd);
15734 					if (!result.empty())
15735 					{
15736 						std::vector<std::string> sd = result[0];
15737 
15738 						root["result"][ii]["d"] = szDateEnd;
15739 						root["result"][ii]["uvi"] = sd[0];
15740 						ii++;
15741 					}
15742 					//Previous Year
15743 					result = m_sql.safe_query("SELECT Level, Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStartPrev, szDateEndPrev);
15744 					if (!result.empty())
15745 					{
15746 						iPrev = 0;
15747 						for (const auto & itt : result)
15748 						{
15749 							std::vector<std::string> sd = itt;
15750 
15751 							root["resultprev"][iPrev]["d"] = sd[1].substr(0, 16);
15752 							root["resultprev"][iPrev]["uvi"] = sd[0];
15753 							iPrev++;
15754 						}
15755 					}
15756 				}
15757 				else if (sensor == "rain") {
15758 					root["status"] = "OK";
15759 					root["title"] = "Graph " + sensor + " " + srange;
15760 
15761 					result = m_sql.safe_query("SELECT Total, Rate, Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStart, szDateEnd);
15762 					int ii = 0;
15763 					if (!result.empty())
15764 					{
15765 						for (const auto & itt : result)
15766 						{
15767 							std::vector<std::string> sd = itt;
15768 
15769 							root["result"][ii]["d"] = sd[2].substr(0, 16);
15770 							double mmval = atof(sd[0].c_str());
15771 							mmval *= AddjMulti;
15772 							sprintf(szTmp, "%.1f", mmval);
15773 							root["result"][ii]["mm"] = szTmp;
15774 							ii++;
15775 						}
15776 					}
15777 					//add today (have to calculate it)
15778 					if (dSubType == sTypeRAINWU || dSubType == sTypeRAINByRate)
15779 					{
15780 						result = m_sql.safe_query(
15781 							"SELECT Total, Total, Rate FROM Rain WHERE (DeviceRowID=%" PRIu64 " AND Date>='%q') ORDER BY ROWID DESC LIMIT 1",
15782 							idx, szDateEnd);
15783 					}
15784 					else
15785 					{
15786 						result = m_sql.safe_query(
15787 							"SELECT MIN(Total), MAX(Total), MAX(Rate) FROM Rain WHERE (DeviceRowID=%" PRIu64 " AND Date>='%q')",
15788 							idx, szDateEnd);
15789 					}
15790 					if (!result.empty())
15791 					{
15792 						std::vector<std::string> sd = result[0];
15793 
15794 						float total_min = static_cast<float>(atof(sd[0].c_str()));
15795 						float total_max = static_cast<float>(atof(sd[1].c_str()));
15796 						//int rate = atoi(sd[2].c_str());
15797 
15798 						double total_real = 0;
15799 						if (dSubType == sTypeRAINWU || dSubType == sTypeRAINByRate)
15800 						{
15801 							total_real = total_max;
15802 						}
15803 						else
15804 						{
15805 							total_real = total_max - total_min;
15806 						}
15807 						total_real *= AddjMulti;
15808 						sprintf(szTmp, "%.1f", total_real);
15809 						root["result"][ii]["d"] = szDateEnd;
15810 						root["result"][ii]["mm"] = szTmp;
15811 						ii++;
15812 					}
15813 					//Previous Year
15814 					result = m_sql.safe_query(
15815 						"SELECT Total, Rate, Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStartPrev, szDateEndPrev);
15816 					if (!result.empty())
15817 					{
15818 						iPrev = 0;
15819 						for (const auto & itt : result)
15820 						{
15821 							std::vector<std::string> sd = itt;
15822 
15823 							root["resultprev"][iPrev]["d"] = sd[2].substr(0, 16);
15824 							double mmval = atof(sd[0].c_str());
15825 							mmval *= AddjMulti;
15826 							sprintf(szTmp, "%.1f", mmval);
15827 							root["resultprev"][iPrev]["mm"] = szTmp;
15828 							iPrev++;
15829 						}
15830 					}
15831 				}
15832 				else if (sensor == "counter") {
15833 					root["status"] = "OK";
15834 					root["title"] = "Graph " + sensor + " " + srange;
15835 					root["ValueQuantity"] = options["ValueQuantity"];
15836 					root["ValueUnits"] = options["ValueUnits"];
15837 
15838 					//int nValue = 0;
15839 					std::string sValue = "";
15840 
15841 					result = m_sql.safe_query("SELECT nValue, sValue FROM DeviceStatus WHERE (ID==%" PRIu64 ")",
15842 						idx);
15843 					if (!result.empty())
15844 					{
15845 						std::vector<std::string> sd = result[0];
15846 						//nValue = atoi(sd[0].c_str());
15847 						sValue = sd[1];
15848 					}
15849 
15850 					int ii = 0;
15851 					iPrev = 0;
15852 					if (dType == pTypeP1Power)
15853 					{
15854 						//Actual Year
15855 						result = m_sql.safe_query(
15856 							"SELECT Value1,Value2,Value5,Value6, Date,"
15857 							" Counter1, Counter2, Counter3, Counter4 "
15858 							"FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q'"
15859 							" AND Date<='%q') ORDER BY Date ASC",
15860 							dbasetable.c_str(), idx, szDateStart, szDateEnd);
15861 						if (!result.empty())
15862 						{
15863 							bool bHaveDeliverd = false;
15864 							for (const auto & itt : result)
15865 							{
15866 								std::vector<std::string> sd = itt;
15867 
15868 								root["result"][ii]["d"] = sd[4].substr(0, 16);
15869 
15870 								double counter_1 = std::stod(sd[5]);
15871 								double counter_2 = std::stod(sd[6]);
15872 								double counter_3 = std::stod(sd[7]);
15873 								double counter_4 = std::stod(sd[8]);
15874 
15875 								float fUsage_1 = std::stof(sd[0]);
15876 								float fUsage_2 = std::stof(sd[2]);
15877 								float fDeliv_1 = std::stof(sd[1]);
15878 								float fDeliv_2 = std::stof(sd[3]);
15879 
15880 								fDeliv_1 = (fDeliv_1 < 10) ? 0 : fDeliv_1;
15881 								fDeliv_2 = (fDeliv_2 < 10) ? 0 : fDeliv_2;
15882 
15883 								if ((fDeliv_1 != 0) || (fDeliv_2 != 0))
15884 									bHaveDeliverd = true;
15885 								sprintf(szTmp, "%.3f", fUsage_1 / divider);
15886 								root["result"][ii]["v"] = szTmp;
15887 								sprintf(szTmp, "%.3f", fUsage_2 / divider);
15888 								root["result"][ii]["v2"] = szTmp;
15889 								sprintf(szTmp, "%.3f", fDeliv_1 / divider);
15890 								root["result"][ii]["r1"] = szTmp;
15891 								sprintf(szTmp, "%.3f", fDeliv_2 / divider);
15892 								root["result"][ii]["r2"] = szTmp;
15893 
15894 								if (counter_1 != 0)
15895 								{
15896 									sprintf(szTmp, "%.3f", (counter_1 - fUsage_1) / divider);
15897 								}
15898 								else
15899 								{
15900 									strcpy(szTmp, "0");
15901 								}
15902 								root["result"][ii]["c1"] = szTmp;
15903 
15904 								if (counter_2 != 0)
15905 								{
15906 									sprintf(szTmp, "%.3f", (counter_2 - fDeliv_1) / divider);
15907 								}
15908 								else
15909 								{
15910 									strcpy(szTmp, "0");
15911 								}
15912 								root["result"][ii]["c2"] = szTmp;
15913 
15914 								if (counter_3 != 0)
15915 								{
15916 									sprintf(szTmp, "%.3f", (counter_3 - fUsage_2) / divider);
15917 								}
15918 								else
15919 								{
15920 									strcpy(szTmp, "0");
15921 								}
15922 								root["result"][ii]["c3"] = szTmp;
15923 
15924 								if (counter_4 != 0)
15925 								{
15926 									sprintf(szTmp, "%.3f", (counter_4 - fDeliv_2) / divider);
15927 								}
15928 								else
15929 								{
15930 									strcpy(szTmp, "0");
15931 								}
15932 								root["result"][ii]["c4"] = szTmp;
15933 
15934 								ii++;
15935 							}
15936 							if (bHaveDeliverd)
15937 							{
15938 								root["delivered"] = true;
15939 							}
15940 						}
15941 						//Previous Year
15942 						result = m_sql.safe_query(
15943 							"SELECT Value1,Value2,Value5,Value6, Date "
15944 							"FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC",
15945 							dbasetable.c_str(), idx, szDateStartPrev, szDateEndPrev);
15946 						if (!result.empty())
15947 						{
15948 							bool bHaveDeliverd = false;
15949 							iPrev = 0;
15950 							for (const auto & itt : result)
15951 							{
15952 								std::vector<std::string> sd = itt;
15953 
15954 								root["resultprev"][iPrev]["d"] = sd[4].substr(0, 16);
15955 
15956 								float fUsage_1 = std::stof(sd[0]);
15957 								float fUsage_2 = std::stof(sd[2]);
15958 								float fDeliv_1 = std::stof(sd[1]);
15959 								float fDeliv_2 = std::stof(sd[3]);
15960 
15961 								if ((fDeliv_1 != 0) || (fDeliv_2 != 0))
15962 									bHaveDeliverd = true;
15963 								sprintf(szTmp, "%.3f", fUsage_1 / divider);
15964 								root["resultprev"][iPrev]["v"] = szTmp;
15965 								sprintf(szTmp, "%.3f", fUsage_2 / divider);
15966 								root["resultprev"][iPrev]["v2"] = szTmp;
15967 								sprintf(szTmp, "%.3f", fDeliv_1 / divider);
15968 								root["resultprev"][iPrev]["r1"] = szTmp;
15969 								sprintf(szTmp, "%.3f", fDeliv_2 / divider);
15970 								root["resultprev"][iPrev]["r2"] = szTmp;
15971 								iPrev++;
15972 							}
15973 							if (bHaveDeliverd)
15974 							{
15975 								root["delivered"] = true;
15976 							}
15977 						}
15978 					}
15979 					else if (dType == pTypeAirQuality)
15980 					{//month/year
15981 						root["status"] = "OK";
15982 						root["title"] = "Graph " + sensor + " " + srange;
15983 
15984 						result = m_sql.safe_query("SELECT Value1,Value2,Value3,Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStart, szDateEnd);
15985 						if (!result.empty())
15986 						{
15987 							for (const auto & itt : result)
15988 							{
15989 								std::vector<std::string> sd = itt;
15990 
15991 								root["result"][ii]["d"] = sd[3].substr(0, 16);
15992 								root["result"][ii]["co2_min"] = sd[0];
15993 								root["result"][ii]["co2_max"] = sd[1];
15994 								root["result"][ii]["co2_avg"] = sd[2];
15995 								ii++;
15996 							}
15997 						}
15998 						result = m_sql.safe_query("SELECT Value2,Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStartPrev, szDateEndPrev);
15999 						if (!result.empty())
16000 						{
16001 							iPrev = 0;
16002 							for (const auto & itt : result)
16003 							{
16004 								std::vector<std::string> sd = itt;
16005 
16006 								root["resultprev"][iPrev]["d"] = sd[1].substr(0, 16);
16007 								root["resultprev"][iPrev]["co2_max"] = sd[0];
16008 								iPrev++;
16009 							}
16010 						}
16011 					}
16012 					else if (
16013 						((dType == pTypeGeneral) && ((dSubType == sTypeSoilMoisture) || (dSubType == sTypeLeafWetness))) ||
16014 						((dType == pTypeRFXSensor) && ((dSubType == sTypeRFXSensorAD) || (dSubType == sTypeRFXSensorVolt)))
16015 						)
16016 					{//month/year
16017 						root["status"] = "OK";
16018 						root["title"] = "Graph " + sensor + " " + srange;
16019 
16020 						result = m_sql.safe_query("SELECT Value1,Value2, Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStart, szDateEnd);
16021 						if (!result.empty())
16022 						{
16023 							for (const auto & itt : result)
16024 							{
16025 								std::vector<std::string> sd = itt;
16026 
16027 								root["result"][ii]["d"] = sd[2].substr(0, 16);
16028 								root["result"][ii]["v_min"] = sd[0];
16029 								root["result"][ii]["v_max"] = sd[1];
16030 								ii++;
16031 							}
16032 						}
16033 					}
16034 					else if (
16035 						((dType == pTypeGeneral) && (dSubType == sTypeVisibility)) ||
16036 						((dType == pTypeGeneral) && (dSubType == sTypeDistance)) ||
16037 						((dType == pTypeGeneral) && (dSubType == sTypeSolarRadiation)) ||
16038 						((dType == pTypeGeneral) && (dSubType == sTypeVoltage)) ||
16039 						((dType == pTypeGeneral) && (dSubType == sTypeCurrent)) ||
16040 						((dType == pTypeGeneral) && (dSubType == sTypePressure)) ||
16041 						((dType == pTypeGeneral) && (dSubType == sTypeSoundLevel))
16042 						)
16043 					{//month/year
16044 						root["status"] = "OK";
16045 						root["title"] = "Graph " + sensor + " " + srange;
16046 
16047 						float vdiv = 10.0f;
16048 						if (
16049 							((dType == pTypeGeneral) && (dSubType == sTypeVoltage)) ||
16050 							((dType == pTypeGeneral) && (dSubType == sTypeCurrent))
16051 							)
16052 						{
16053 							vdiv = 1000.0f;
16054 						}
16055 
16056 						result = m_sql.safe_query("SELECT Value1,Value2,Value3,Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStart, szDateEnd);
16057 						if (!result.empty())
16058 						{
16059 							for (const auto & itt : result)
16060 							{
16061 								std::vector<std::string> sd = itt;
16062 
16063 								float fValue1 = float(atof(sd[0].c_str())) / vdiv;
16064 								float fValue2 = float(atof(sd[1].c_str())) / vdiv;
16065 								float fValue3 = float(atof(sd[2].c_str())) / vdiv;
16066 								root["result"][ii]["d"] = sd[3].substr(0, 16);
16067 
16068 								if (metertype == 1)
16069 								{
16070 									fValue1 *= 0.6214f;
16071 									fValue2 *= 0.6214f;
16072 								}
16073 								if (
16074 									((dType == pTypeGeneral) && (dSubType == sTypeVoltage)) ||
16075 									((dType == pTypeGeneral) && (dSubType == sTypeCurrent))
16076 									)
16077 								{
16078 									sprintf(szTmp, "%.3f", fValue1);
16079 									root["result"][ii]["v_min"] = szTmp;
16080 									sprintf(szTmp, "%.3f", fValue2);
16081 									root["result"][ii]["v_max"] = szTmp;
16082 									if (fValue3 != 0)
16083 									{
16084 										sprintf(szTmp, "%.3f", fValue3);
16085 										root["result"][ii]["v_avg"] = szTmp;
16086 									}
16087 								}
16088 								else
16089 								{
16090 									sprintf(szTmp, "%.1f", fValue1);
16091 									root["result"][ii]["v_min"] = szTmp;
16092 									sprintf(szTmp, "%.1f", fValue2);
16093 									root["result"][ii]["v_max"] = szTmp;
16094 									if (fValue3 != 0)
16095 									{
16096 										sprintf(szTmp, "%.1f", fValue3);
16097 										root["result"][ii]["v_avg"] = szTmp;
16098 									}
16099 								}
16100 								ii++;
16101 							}
16102 						}
16103 					}
16104 					else if (dType == pTypeLux)
16105 					{//month/year
16106 						root["status"] = "OK";
16107 						root["title"] = "Graph " + sensor + " " + srange;
16108 
16109 						result = m_sql.safe_query("SELECT Value1,Value2,Value3, Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStart, szDateEnd);
16110 						if (!result.empty())
16111 						{
16112 							for (const auto & itt : result)
16113 							{
16114 								std::vector<std::string> sd = itt;
16115 
16116 								root["result"][ii]["d"] = sd[3].substr(0, 16);
16117 								root["result"][ii]["lux_min"] = sd[0];
16118 								root["result"][ii]["lux_max"] = sd[1];
16119 								root["result"][ii]["lux_avg"] = sd[2];
16120 								ii++;
16121 							}
16122 						}
16123 					}
16124 					else if (dType == pTypeWEIGHT)
16125 					{//month/year
16126 						root["status"] = "OK";
16127 						root["title"] = "Graph " + sensor + " " + srange;
16128 
16129 						result = m_sql.safe_query(
16130 							"SELECT Value1,Value2, Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStart, szDateEnd);
16131 						if (!result.empty())
16132 						{
16133 							for (const auto & itt : result)
16134 							{
16135 								std::vector<std::string> sd = itt;
16136 
16137 								root["result"][ii]["d"] = sd[2].substr(0, 16);
16138 								sprintf(szTmp, "%.1f", m_sql.m_weightscale * atof(sd[0].c_str()) / 10.0f);
16139 								root["result"][ii]["v_min"] = szTmp;
16140 								sprintf(szTmp, "%.1f", m_sql.m_weightscale * atof(sd[1].c_str()) / 10.0f);
16141 								root["result"][ii]["v_max"] = szTmp;
16142 								ii++;
16143 							}
16144 						}
16145 					}
16146 					else if (dType == pTypeUsage)
16147 					{//month/year
16148 						root["status"] = "OK";
16149 						root["title"] = "Graph " + sensor + " " + srange;
16150 
16151 						result = m_sql.safe_query(
16152 							"SELECT Value1,Value2, Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStart, szDateEnd);
16153 						if (!result.empty())
16154 						{
16155 							for (const auto & itt : result)
16156 							{
16157 								std::vector<std::string> sd = itt;
16158 
16159 								root["result"][ii]["d"] = sd[2].substr(0, 16);
16160 								root["result"][ii]["u_min"] = atof(sd[0].c_str()) / 10.0f;
16161 								root["result"][ii]["u_max"] = atof(sd[1].c_str()) / 10.0f;
16162 								ii++;
16163 							}
16164 						}
16165 					}
16166 					else if (dType == pTypeCURRENT)
16167 					{
16168 						result = m_sql.safe_query("SELECT Value1,Value2,Value3,Value4,Value5,Value6, Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStart, szDateEnd);
16169 						if (!result.empty())
16170 						{
16171 							//CM113
16172 							int displaytype = 0;
16173 							int voltage = 230;
16174 							m_sql.GetPreferencesVar("CM113DisplayType", displaytype);
16175 							m_sql.GetPreferencesVar("ElectricVoltage", voltage);
16176 
16177 							root["displaytype"] = displaytype;
16178 
16179 							bool bHaveL1 = false;
16180 							bool bHaveL2 = false;
16181 							bool bHaveL3 = false;
16182 							for (const auto & itt : result)
16183 							{
16184 								std::vector<std::string> sd = itt;
16185 
16186 								root["result"][ii]["d"] = sd[6].substr(0, 16);
16187 
16188 								float fval1 = static_cast<float>(atof(sd[0].c_str()) / 10.0f);
16189 								float fval2 = static_cast<float>(atof(sd[1].c_str()) / 10.0f);
16190 								float fval3 = static_cast<float>(atof(sd[2].c_str()) / 10.0f);
16191 								float fval4 = static_cast<float>(atof(sd[3].c_str()) / 10.0f);
16192 								float fval5 = static_cast<float>(atof(sd[4].c_str()) / 10.0f);
16193 								float fval6 = static_cast<float>(atof(sd[5].c_str()) / 10.0f);
16194 
16195 								if ((fval1 != 0) || (fval2 != 0))
16196 									bHaveL1 = true;
16197 								if ((fval3 != 0) || (fval4 != 0))
16198 									bHaveL2 = true;
16199 								if ((fval5 != 0) || (fval6 != 0))
16200 									bHaveL3 = true;
16201 
16202 								if (displaytype == 0)
16203 								{
16204 									sprintf(szTmp, "%.1f", fval1);
16205 									root["result"][ii]["v1"] = szTmp;
16206 									sprintf(szTmp, "%.1f", fval2);
16207 									root["result"][ii]["v2"] = szTmp;
16208 									sprintf(szTmp, "%.1f", fval3);
16209 									root["result"][ii]["v3"] = szTmp;
16210 									sprintf(szTmp, "%.1f", fval4);
16211 									root["result"][ii]["v4"] = szTmp;
16212 									sprintf(szTmp, "%.1f", fval5);
16213 									root["result"][ii]["v5"] = szTmp;
16214 									sprintf(szTmp, "%.1f", fval6);
16215 									root["result"][ii]["v6"] = szTmp;
16216 								}
16217 								else
16218 								{
16219 									sprintf(szTmp, "%d", int(fval1*voltage));
16220 									root["result"][ii]["v1"] = szTmp;
16221 									sprintf(szTmp, "%d", int(fval2*voltage));
16222 									root["result"][ii]["v2"] = szTmp;
16223 									sprintf(szTmp, "%d", int(fval3*voltage));
16224 									root["result"][ii]["v3"] = szTmp;
16225 									sprintf(szTmp, "%d", int(fval4*voltage));
16226 									root["result"][ii]["v4"] = szTmp;
16227 									sprintf(szTmp, "%d", int(fval5*voltage));
16228 									root["result"][ii]["v5"] = szTmp;
16229 									sprintf(szTmp, "%d", int(fval6*voltage));
16230 									root["result"][ii]["v6"] = szTmp;
16231 								}
16232 
16233 								ii++;
16234 							}
16235 							if (
16236 								(!bHaveL1) &&
16237 								(!bHaveL2) &&
16238 								(!bHaveL3)
16239 								) {
16240 								root["haveL1"] = true; //show at least something
16241 							}
16242 							else {
16243 								if (bHaveL1)
16244 									root["haveL1"] = true;
16245 								if (bHaveL2)
16246 									root["haveL2"] = true;
16247 								if (bHaveL3)
16248 									root["haveL3"] = true;
16249 							}
16250 						}
16251 					}
16252 					else if (dType == pTypeCURRENTENERGY)
16253 					{
16254 						result = m_sql.safe_query("SELECT Value1,Value2,Value3,Value4,Value5,Value6, Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStart, szDateEnd);
16255 						if (!result.empty())
16256 						{
16257 							//CM180i
16258 							int displaytype = 0;
16259 							int voltage = 230;
16260 							m_sql.GetPreferencesVar("CM113DisplayType", displaytype);
16261 							m_sql.GetPreferencesVar("ElectricVoltage", voltage);
16262 
16263 							root["displaytype"] = displaytype;
16264 
16265 							bool bHaveL1 = false;
16266 							bool bHaveL2 = false;
16267 							bool bHaveL3 = false;
16268 							for (const auto & itt : result)
16269 							{
16270 								std::vector<std::string> sd = itt;
16271 
16272 								root["result"][ii]["d"] = sd[6].substr(0, 16);
16273 
16274 								float fval1 = static_cast<float>(atof(sd[0].c_str()) / 10.0f);
16275 								float fval2 = static_cast<float>(atof(sd[1].c_str()) / 10.0f);
16276 								float fval3 = static_cast<float>(atof(sd[2].c_str()) / 10.0f);
16277 								float fval4 = static_cast<float>(atof(sd[3].c_str()) / 10.0f);
16278 								float fval5 = static_cast<float>(atof(sd[4].c_str()) / 10.0f);
16279 								float fval6 = static_cast<float>(atof(sd[5].c_str()) / 10.0f);
16280 
16281 								if ((fval1 != 0) || (fval2 != 0))
16282 									bHaveL1 = true;
16283 								if ((fval3 != 0) || (fval4 != 0))
16284 									bHaveL2 = true;
16285 								if ((fval5 != 0) || (fval6 != 0))
16286 									bHaveL3 = true;
16287 
16288 								if (displaytype == 0)
16289 								{
16290 									sprintf(szTmp, "%.1f", fval1);
16291 									root["result"][ii]["v1"] = szTmp;
16292 									sprintf(szTmp, "%.1f", fval2);
16293 									root["result"][ii]["v2"] = szTmp;
16294 									sprintf(szTmp, "%.1f", fval3);
16295 									root["result"][ii]["v3"] = szTmp;
16296 									sprintf(szTmp, "%.1f", fval4);
16297 									root["result"][ii]["v4"] = szTmp;
16298 									sprintf(szTmp, "%.1f", fval5);
16299 									root["result"][ii]["v5"] = szTmp;
16300 									sprintf(szTmp, "%.1f", fval6);
16301 									root["result"][ii]["v6"] = szTmp;
16302 								}
16303 								else
16304 								{
16305 									sprintf(szTmp, "%d", int(fval1*voltage));
16306 									root["result"][ii]["v1"] = szTmp;
16307 									sprintf(szTmp, "%d", int(fval2*voltage));
16308 									root["result"][ii]["v2"] = szTmp;
16309 									sprintf(szTmp, "%d", int(fval3*voltage));
16310 									root["result"][ii]["v3"] = szTmp;
16311 									sprintf(szTmp, "%d", int(fval4*voltage));
16312 									root["result"][ii]["v4"] = szTmp;
16313 									sprintf(szTmp, "%d", int(fval5*voltage));
16314 									root["result"][ii]["v5"] = szTmp;
16315 									sprintf(szTmp, "%d", int(fval6*voltage));
16316 									root["result"][ii]["v6"] = szTmp;
16317 								}
16318 
16319 								ii++;
16320 							}
16321 							if (
16322 								(!bHaveL1) &&
16323 								(!bHaveL2) &&
16324 								(!bHaveL3)
16325 								) {
16326 								root["haveL1"] = true; //show at least something
16327 							}
16328 							else {
16329 								if (bHaveL1)
16330 									root["haveL1"] = true;
16331 								if (bHaveL2)
16332 									root["haveL2"] = true;
16333 								if (bHaveL3)
16334 									root["haveL3"] = true;
16335 							}
16336 						}
16337 					}
16338 					else
16339 					{
16340 						if (dType == pTypeP1Gas)
16341 						{
16342 							//Add last counter value
16343 							sprintf(szTmp, "%.3f", atof(sValue.c_str()) / 1000.0);
16344 							root["counter"] = szTmp;
16345 						}
16346 						else if (dType == pTypeENERGY)
16347 						{
16348 							size_t spos = sValue.find(";");
16349 							if (spos != std::string::npos)
16350 							{
16351 								float fvalue = static_cast<float>(atof(sValue.substr(spos + 1).c_str()));
16352 								sprintf(szTmp, "%.3f", fvalue / (divider / 100.0f));
16353 								root["counter"] = szTmp;
16354 							}
16355 						}
16356 						else if ((dType == pTypeGeneral) && (dSubType == sTypeKwh))
16357 						{
16358 							size_t spos = sValue.find(";");
16359 							if (spos != std::string::npos)
16360 							{
16361 								float fvalue = static_cast<float>(atof(sValue.substr(spos + 1).c_str()));
16362 								sprintf(szTmp, "%.3f", fvalue / divider);
16363 								root["counter"] = szTmp;
16364 							}
16365 						}
16366 						else if (dType == pTypeRFXMeter)
16367 						{
16368 							//Add last counter value
16369 							float fvalue = static_cast<float>(atof(sValue.c_str()));
16370 							switch (metertype)
16371 							{
16372 							case MTYPE_ENERGY:
16373 							case MTYPE_ENERGY_GENERATED:
16374 								sprintf(szTmp, "%.3f", AddjValue + (fvalue / divider));
16375 								break;
16376 							case MTYPE_GAS:
16377 								sprintf(szTmp, "%.2f", AddjValue + (fvalue / divider));
16378 								break;
16379 							case MTYPE_WATER:
16380 								sprintf(szTmp, "%.3f", AddjValue + (fvalue / divider));
16381 								break;
16382 							default:
16383 								strcpy(szTmp, "");
16384 								break;
16385 							}
16386 							root["counter"] = szTmp;
16387 						}
16388 						else if (dType == pTypeYouLess)
16389 						{
16390 							std::vector<std::string> results;
16391 							StringSplit(sValue, ";", results);
16392 							if (results.size() == 2)
16393 							{
16394 								//Add last counter value
16395 								float fvalue = static_cast<float>(atof(results[0].c_str()));
16396 								switch (metertype)
16397 								{
16398 								case MTYPE_ENERGY:
16399 								case MTYPE_ENERGY_GENERATED:
16400 									sprintf(szTmp, "%.3f", fvalue / divider);
16401 									break;
16402 								case MTYPE_GAS:
16403 									sprintf(szTmp, "%.2f", fvalue / divider);
16404 									break;
16405 								case MTYPE_WATER:
16406 									sprintf(szTmp, "%.3f", fvalue / divider);
16407 									break;
16408 								default:
16409 									strcpy(szTmp, "");
16410 									break;
16411 								}
16412 								root["counter"] = szTmp;
16413 							}
16414 						}
16415 						else if (!bIsManagedCounter)
16416 						{
16417 							//Add last counter value
16418 							sprintf(szTmp, "%d", atoi(sValue.c_str()));
16419 							root["counter"] = szTmp;
16420 						}
16421 						else
16422 						{
16423 							root["counter"] = "0";
16424 						}
16425 						//Actual Year
16426 						result = m_sql.safe_query("SELECT Value, Date, Counter FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStart, szDateEnd);
16427 						if (!result.empty())
16428 						{
16429 							for (const auto & itt : result)
16430 							{
16431 								std::vector<std::string> sd = itt;
16432 
16433 								root["result"][ii]["d"] = sd[1].substr(0, 16);
16434 
16435 								std::string szValue = sd[0];
16436 
16437 								double fcounter = atof(sd[2].c_str());
16438 
16439 								switch (metertype)
16440 								{
16441 								case MTYPE_ENERGY:
16442 								case MTYPE_ENERGY_GENERATED:
16443 									sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
16444 									root["result"][ii]["v"] = szTmp;
16445 									if (fcounter != 0)
16446 										sprintf(szTmp, "%.3f", AddjValue + ((fcounter - atof(szValue.c_str())) / divider));
16447 									else
16448 										strcpy(szTmp, "0");
16449 									root["result"][ii]["c"] = szTmp;
16450 									break;
16451 								case MTYPE_GAS:
16452 									sprintf(szTmp, "%.2f", atof(szValue.c_str()) / divider);
16453 									root["result"][ii]["v"] = szTmp;
16454 									if (fcounter != 0)
16455 										sprintf(szTmp, "%.2f", AddjValue + ((fcounter - atof(szValue.c_str())) / divider));
16456 									else
16457 										strcpy(szTmp, "0");
16458 									root["result"][ii]["c"] = szTmp;
16459 									break;
16460 								case MTYPE_WATER:
16461 									sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
16462 									root["result"][ii]["v"] = szTmp;
16463 									if (fcounter != 0)
16464 										sprintf(szTmp, "%.3f", AddjValue + ((fcounter - atof(szValue.c_str())) / divider));
16465 									else
16466 										strcpy(szTmp, "0");
16467 									root["result"][ii]["c"] = szTmp;
16468 									break;
16469 								case MTYPE_COUNTER:
16470 									sprintf(szTmp, "%.0f", atof(szValue.c_str()));
16471 									root["result"][ii]["v"] = szTmp;
16472 									if (fcounter != 0)
16473 										sprintf(szTmp, "%.0f", AddjValue + ((fcounter - atof(szValue.c_str()))));
16474 									else
16475 										strcpy(szTmp, "0");
16476 									root["result"][ii]["c"] = szTmp;
16477 									break;
16478 								}
16479 								ii++;
16480 							}
16481 						}
16482 						//Past Year
16483 						result = m_sql.safe_query("SELECT Value, Date, Counter FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStartPrev, szDateEndPrev);
16484 						if (!result.empty())
16485 						{
16486 							iPrev = 0;
16487 							for (const auto & itt : result)
16488 							{
16489 								std::vector<std::string> sd = itt;
16490 
16491 								root["resultprev"][iPrev]["d"] = sd[1].substr(0, 16);
16492 
16493 								std::string szValue = sd[0];
16494 								switch (metertype)
16495 								{
16496 								case MTYPE_ENERGY:
16497 								case MTYPE_ENERGY_GENERATED:
16498 									sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
16499 									root["resultprev"][iPrev]["v"] = szTmp;
16500 									break;
16501 								case MTYPE_GAS:
16502 									sprintf(szTmp, "%.2f", atof(szValue.c_str()) / divider);
16503 									root["resultprev"][iPrev]["v"] = szTmp;
16504 									break;
16505 								case MTYPE_WATER:
16506 									sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
16507 									root["resultprev"][iPrev]["v"] = szTmp;
16508 									break;
16509 								case MTYPE_COUNTER:
16510 									sprintf(szTmp, "%.0f", atof(szValue.c_str()));
16511 									root["resultprev"][iPrev]["v"] = szTmp;
16512 									break;
16513 								}
16514 								iPrev++;
16515 							}
16516 						}
16517 					}
16518 					//add today (have to calculate it)
16519 
16520 					if ((sactmonth != "") || (sactyear != ""))
16521 					{
16522 						struct tm loctime;
16523 						time_t now = mytime(NULL);
16524 						localtime_r(&now, &loctime);
16525 						if ((sactmonth != "") && (sactyear != ""))
16526 						{
16527 							bool bIsThisMonth = (atoi(sactyear.c_str()) == loctime.tm_year + 1900) && (atoi(sactmonth.c_str()) == loctime.tm_mon + 1);
16528 							if (bIsThisMonth)
16529 							{
16530 								sprintf(szDateEnd, "%04d-%02d-%02d", loctime.tm_year + 1900, loctime.tm_mon + 1, loctime.tm_mday);
16531 							}
16532 						}
16533 						else if (sactyear != "")
16534 						{
16535 							bool bIsThisYear = (atoi(sactyear.c_str()) == loctime.tm_year + 1900);
16536 							if (bIsThisYear)
16537 							{
16538 								sprintf(szDateEnd, "%04d-%02d-%02d", loctime.tm_year + 1900, loctime.tm_mon + 1, loctime.tm_mday);
16539 							}
16540 
16541 						}
16542 					}
16543 
16544 					if (dType == pTypeP1Power)
16545 					{
16546 						result = m_sql.safe_query(
16547 							"SELECT MIN(Value1), MAX(Value1), MIN(Value2),"
16548 							" MAX(Value2), MIN(Value5), MAX(Value5),"
16549 							" MIN(Value6), MAX(Value6) "
16550 							"FROM MultiMeter WHERE (DeviceRowID=%" PRIu64 ""
16551 							" AND Date>='%q')",
16552 							idx, szDateEnd);
16553 						bool bHaveDeliverd = false;
16554 						if (!result.empty())
16555 						{
16556 							std::vector<std::string> sd = result[0];
16557 							unsigned long long total_min_usage_1 = std::strtoull(sd[0].c_str(), nullptr, 10);
16558 							unsigned long long total_max_usage_1 = std::strtoull(sd[1].c_str(), nullptr, 10);
16559 							unsigned long long total_min_usage_2 = std::strtoull(sd[4].c_str(), nullptr, 10);
16560 							unsigned long long total_max_usage_2 = std::strtoull(sd[5].c_str(), nullptr, 10);
16561 							unsigned long long total_real_usage_1, total_real_usage_2;
16562 							unsigned long long total_min_deliv_1 = std::strtoull(sd[2].c_str(), nullptr, 10);
16563 							unsigned long long total_max_deliv_1 = std::strtoull(sd[3].c_str(), nullptr, 10);
16564 							unsigned long long total_min_deliv_2 = std::strtoull(sd[6].c_str(), nullptr, 10);
16565 							unsigned long long total_max_deliv_2 = std::strtoull(sd[7].c_str(), nullptr, 10);
16566 							unsigned long long total_real_deliv_1, total_real_deliv_2;
16567 
16568 							total_real_usage_1 = total_max_usage_1 - total_min_usage_1;
16569 							total_real_usage_2 = total_max_usage_2 - total_min_usage_2;
16570 
16571 							total_real_deliv_1 = total_max_deliv_1 - total_min_deliv_1;
16572 							total_real_deliv_2 = total_max_deliv_2 - total_min_deliv_2;
16573 
16574 							if ((total_real_deliv_1 != 0) || (total_real_deliv_2 != 0))
16575 								bHaveDeliverd = true;
16576 
16577 							root["result"][ii]["d"] = szDateEnd;
16578 
16579 							sprintf(szTmp, "%.3f", (float)(total_real_usage_1 / divider));
16580 							root["result"][ii]["v"] = szTmp;
16581 							sprintf(szTmp, "%.3f", (float)(total_real_usage_2 / divider));
16582 							root["result"][ii]["v2"] = szTmp;
16583 
16584 							sprintf(szTmp, "%.3f", (float)(total_real_deliv_1 / divider));
16585 							root["result"][ii]["r1"] = szTmp;
16586 							sprintf(szTmp, "%.3f", (float)(total_real_deliv_2 / divider));
16587 							root["result"][ii]["r2"] = szTmp;
16588 
16589 							sprintf(szTmp, "%.3f", (float)(total_min_usage_1 / divider));
16590 							root["result"][ii]["c1"] = szTmp;
16591 							sprintf(szTmp, "%.3f", (float)(total_min_usage_2 / divider));
16592 							root["result"][ii]["c3"] = szTmp;
16593 
16594 							if (total_max_deliv_2 != 0)
16595 							{
16596 								sprintf(szTmp, "%.3f", (float)(total_min_deliv_1 / divider));
16597 								root["result"][ii]["c2"] = szTmp;
16598 								sprintf(szTmp, "%.3f", (float)(total_min_deliv_2 / divider));
16599 								root["result"][ii]["c4"] = szTmp;
16600 							}
16601 							else
16602 							{
16603 								strcpy(szTmp, "0");
16604 								root["result"][ii]["c2"] = szTmp;
16605 								root["result"][ii]["c4"] = szTmp;
16606 							}
16607 
16608 							ii++;
16609 						}
16610 						if (bHaveDeliverd)
16611 						{
16612 							root["delivered"] = true;
16613 						}
16614 					}
16615 					else if (dType == pTypeAirQuality)
16616 					{
16617 						result = m_sql.safe_query(
16618 							"SELECT MIN(Value), MAX(Value), AVG(Value) FROM Meter WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
16619 							idx, szDateEnd);
16620 						if (!result.empty())
16621 						{
16622 							root["result"][ii]["d"] = szDateEnd;
16623 							root["result"][ii]["co2_min"] = result[0][0];
16624 							root["result"][ii]["co2_max"] = result[0][1];
16625 							root["result"][ii]["co2_avg"] = result[0][2];
16626 							ii++;
16627 						}
16628 					}
16629 					else if (
16630 						((dType == pTypeGeneral) && ((dSubType == sTypeSoilMoisture) || (dSubType == sTypeLeafWetness))) ||
16631 						((dType == pTypeRFXSensor) && ((dSubType == sTypeRFXSensorAD) || (dSubType == sTypeRFXSensorVolt)))
16632 						)
16633 					{
16634 						result = m_sql.safe_query(
16635 							"SELECT MIN(Value), MAX(Value) FROM Meter WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
16636 							idx, szDateEnd);
16637 						if (!result.empty())
16638 						{
16639 							root["result"][ii]["d"] = szDateEnd;
16640 							root["result"][ii]["v_min"] = result[0][0];
16641 							root["result"][ii]["v_max"] = result[0][1];
16642 							ii++;
16643 						}
16644 					}
16645 					else if (
16646 						((dType == pTypeGeneral) && (dSubType == sTypeVisibility)) ||
16647 						((dType == pTypeGeneral) && (dSubType == sTypeDistance)) ||
16648 						((dType == pTypeGeneral) && (dSubType == sTypeSolarRadiation)) ||
16649 						((dType == pTypeGeneral) && (dSubType == sTypeVoltage)) ||
16650 						((dType == pTypeGeneral) && (dSubType == sTypeCurrent)) ||
16651 						((dType == pTypeGeneral) && (dSubType == sTypePressure)) ||
16652 						((dType == pTypeGeneral) && (dSubType == sTypeSoundLevel))
16653 						)
16654 					{
16655 						float vdiv = 10.0f;
16656 						if (
16657 							((dType == pTypeGeneral) && (dSubType == sTypeVoltage)) ||
16658 							((dType == pTypeGeneral) && (dSubType == sTypeCurrent))
16659 							)
16660 						{
16661 							vdiv = 1000.0f;
16662 						}
16663 
16664 						result = m_sql.safe_query(
16665 							"SELECT MIN(Value), MAX(Value) FROM Meter WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
16666 							idx, szDateEnd);
16667 						if (!result.empty())
16668 						{
16669 							root["result"][ii]["d"] = szDateEnd;
16670 							float fValue1 = float(atof(result[0][0].c_str())) / vdiv;
16671 							float fValue2 = float(atof(result[0][1].c_str())) / vdiv;
16672 							if (metertype == 1)
16673 							{
16674 								fValue1 *= 0.6214f;
16675 								fValue2 *= 0.6214f;
16676 							}
16677 
16678 							if ((dType == pTypeGeneral) && (dSubType == sTypeVoltage))
16679 								sprintf(szTmp, "%.3f", fValue1);
16680 							else if ((dType == pTypeGeneral) && (dSubType == sTypeCurrent))
16681 								sprintf(szTmp, "%.3f", fValue1);
16682 							else
16683 								sprintf(szTmp, "%.1f", fValue1);
16684 							root["result"][ii]["v_min"] = szTmp;
16685 							if ((dType == pTypeGeneral) && (dSubType == sTypeVoltage))
16686 								sprintf(szTmp, "%.3f", fValue2);
16687 							else if ((dType == pTypeGeneral) && (dSubType == sTypeCurrent))
16688 								sprintf(szTmp, "%.3f", fValue2);
16689 							else
16690 								sprintf(szTmp, "%.1f", fValue2);
16691 							root["result"][ii]["v_max"] = szTmp;
16692 							ii++;
16693 						}
16694 					}
16695 					else if (dType == pTypeLux)
16696 					{
16697 						result = m_sql.safe_query(
16698 							"SELECT MIN(Value), MAX(Value), AVG(Value) FROM Meter WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
16699 							idx, szDateEnd);
16700 						if (!result.empty())
16701 						{
16702 							root["result"][ii]["d"] = szDateEnd;
16703 							root["result"][ii]["lux_min"] = result[0][0];
16704 							root["result"][ii]["lux_max"] = result[0][1];
16705 							root["result"][ii]["lux_avg"] = result[0][2];
16706 							ii++;
16707 						}
16708 					}
16709 					else if (dType == pTypeWEIGHT)
16710 					{
16711 						result = m_sql.safe_query(
16712 							"SELECT MIN(Value), MAX(Value) FROM Meter WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
16713 							idx, szDateEnd);
16714 						if (!result.empty())
16715 						{
16716 							root["result"][ii]["d"] = szDateEnd;
16717 							sprintf(szTmp, "%.1f", m_sql.m_weightscale* atof(result[0][0].c_str()) / 10.0f);
16718 							root["result"][ii]["v_min"] = szTmp;
16719 							sprintf(szTmp, "%.1f", m_sql.m_weightscale * atof(result[0][1].c_str()) / 10.0f);
16720 							root["result"][ii]["v_max"] = szTmp;
16721 							ii++;
16722 						}
16723 					}
16724 					else if (dType == pTypeUsage)
16725 					{
16726 						result = m_sql.safe_query(
16727 							"SELECT MIN(Value), MAX(Value) FROM Meter WHERE (DeviceRowID=%" PRIu64 " AND Date>='%q')",
16728 							idx, szDateEnd);
16729 						if (!result.empty())
16730 						{
16731 							root["result"][ii]["d"] = szDateEnd;
16732 							root["result"][ii]["u_min"] = atof(result[0][0].c_str()) / 10.0f;
16733 							root["result"][ii]["u_max"] = atof(result[0][1].c_str()) / 10.0f;
16734 							ii++;
16735 						}
16736 					}
16737 					else if (!bIsManagedCounter)
16738 					{
16739 						result = m_sql.safe_query(
16740 							"SELECT MIN(Value), MAX(Value) FROM Meter WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
16741 							idx, szDateEnd);
16742 						if (!result.empty())
16743 						{
16744 							std::vector<std::string> sd = result[0];
16745 							unsigned long long total_min = std::strtoull(sd[0].c_str(), nullptr, 10);
16746 							unsigned long long total_max = std::strtoull(sd[1].c_str(), nullptr, 10);
16747 							unsigned long long total_real;
16748 
16749 							total_real = total_max - total_min;
16750 							sprintf(szTmp, "%llu", total_real);
16751 
16752 							root["result"][ii]["d"] = szDateEnd;
16753 
16754 							std::string szValue = szTmp;
16755 							switch (metertype)
16756 							{
16757 							case MTYPE_ENERGY:
16758 							case MTYPE_ENERGY_GENERATED:
16759 							{
16760 								sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
16761 								root["result"][ii]["v"] = szTmp;
16762 
16763 								std::vector<std::string> mresults;
16764 								StringSplit(sValue, ";", mresults);
16765 								if (mresults.size() == 2)
16766 								{
16767 									sValue = mresults[1];
16768 								}
16769 								if (dType == pTypeENERGY)
16770 									sprintf(szTmp, "%.3f", AddjValue + (((atof(sValue.c_str())*100.0f) - atof(szValue.c_str())) / divider));
16771 								else
16772 									sprintf(szTmp, "%.3f", AddjValue + ((atof(sValue.c_str()) - atof(szValue.c_str())) / divider));
16773 								root["result"][ii]["c"] = szTmp;
16774 							}
16775 							break;
16776 							case MTYPE_GAS:
16777 								sprintf(szTmp, "%.2f", atof(szValue.c_str()) / divider);
16778 								root["result"][ii]["v"] = szTmp;
16779 								sprintf(szTmp, "%.2f", AddjValue + ((atof(sValue.c_str()) - atof(szValue.c_str())) / divider));
16780 								root["result"][ii]["c"] = szTmp;
16781 								break;
16782 							case MTYPE_WATER:
16783 								sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
16784 								root["result"][ii]["v"] = szTmp;
16785 								sprintf(szTmp, "%.3f", AddjValue + ((atof(sValue.c_str()) - atof(szValue.c_str())) / divider));
16786 								root["result"][ii]["c"] = szTmp;
16787 								break;
16788 							case MTYPE_COUNTER:
16789 								sprintf(szTmp, "%.0f", atof(szValue.c_str()));
16790 								root["result"][ii]["v"] = szTmp;
16791 								sprintf(szTmp, "%.0f", AddjValue + ((atof(sValue.c_str()) - atof(szValue.c_str()))));
16792 								root["result"][ii]["c"] = szTmp;
16793 								break;
16794 							}
16795 							ii++;
16796 						}
16797 					}
16798 				}
16799 				else if (sensor == "wind") {
16800 					root["status"] = "OK";
16801 					root["title"] = "Graph " + sensor + " " + srange;
16802 
16803 					int ii = 0;
16804 
16805 					result = m_sql.safe_query(
16806 						"SELECT Direction, Speed_Min, Speed_Max, Gust_Min,"
16807 						" Gust_Max, Date "
16808 						"FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q'"
16809 						" AND Date<='%q') ORDER BY Date ASC",
16810 						dbasetable.c_str(), idx, szDateStart, szDateEnd);
16811 					if (!result.empty())
16812 					{
16813 						for (const auto & itt : result)
16814 						{
16815 							std::vector<std::string> sd = itt;
16816 
16817 							root["result"][ii]["d"] = sd[5].substr(0, 16);
16818 							root["result"][ii]["di"] = sd[0];
16819 
16820 							int intSpeed = atoi(sd[2].c_str());
16821 							int intGust = atoi(sd[4].c_str());
16822 							if (m_sql.m_windunit != WINDUNIT_Beaufort)
16823 							{
16824 								sprintf(szTmp, "%.1f", float(intSpeed) * m_sql.m_windscale);
16825 								root["result"][ii]["sp"] = szTmp;
16826 								sprintf(szTmp, "%.1f", float(intGust) * m_sql.m_windscale);
16827 								root["result"][ii]["gu"] = szTmp;
16828 							}
16829 							else
16830 							{
16831 								float windspeedms = float(intSpeed)*0.1f;
16832 								float windgustms = float(intGust)*0.1f;
16833 								sprintf(szTmp, "%d", MStoBeaufort(windspeedms));
16834 								root["result"][ii]["sp"] = szTmp;
16835 								sprintf(szTmp, "%d", MStoBeaufort(windgustms));
16836 								root["result"][ii]["gu"] = szTmp;
16837 							}
16838 							ii++;
16839 						}
16840 					}
16841 					//add today (have to calculate it)
16842 					result = m_sql.safe_query(
16843 						"SELECT AVG(Direction), MIN(Speed), MAX(Speed),"
16844 						" MIN(Gust), MAX(Gust) "
16845 						"FROM Wind WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q') ORDER BY Date ASC",
16846 						idx, szDateEnd);
16847 					if (!result.empty())
16848 					{
16849 						std::vector<std::string> sd = result[0];
16850 
16851 						root["result"][ii]["d"] = szDateEnd;
16852 						root["result"][ii]["di"] = sd[0];
16853 
16854 						int intSpeed = atoi(sd[2].c_str());
16855 						int intGust = atoi(sd[4].c_str());
16856 						if (m_sql.m_windunit != WINDUNIT_Beaufort)
16857 						{
16858 							sprintf(szTmp, "%.1f", float(intSpeed) * m_sql.m_windscale);
16859 							root["result"][ii]["sp"] = szTmp;
16860 							sprintf(szTmp, "%.1f", float(intGust) * m_sql.m_windscale);
16861 							root["result"][ii]["gu"] = szTmp;
16862 						}
16863 						else
16864 						{
16865 							float windspeedms = float(intSpeed)*0.1f;
16866 							float windgustms = float(intGust)*0.1f;
16867 							sprintf(szTmp, "%d", MStoBeaufort(windspeedms));
16868 							root["result"][ii]["sp"] = szTmp;
16869 							sprintf(szTmp, "%d", MStoBeaufort(windgustms));
16870 							root["result"][ii]["gu"] = szTmp;
16871 						}
16872 						ii++;
16873 					}
16874 					//Previous Year
16875 					result = m_sql.safe_query(
16876 						"SELECT Direction, Speed_Min, Speed_Max, Gust_Min,"
16877 						" Gust_Max, Date "
16878 						"FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q'"
16879 						" AND Date<='%q') ORDER BY Date ASC",
16880 						dbasetable.c_str(), idx, szDateStartPrev, szDateEndPrev);
16881 					if (!result.empty())
16882 					{
16883 						iPrev = 0;
16884 						for (const auto & itt : result)
16885 						{
16886 							std::vector<std::string> sd = itt;
16887 
16888 							root["resultprev"][iPrev]["d"] = sd[5].substr(0, 16);
16889 							root["resultprev"][iPrev]["di"] = sd[0];
16890 
16891 							int intSpeed = atoi(sd[2].c_str());
16892 							int intGust = atoi(sd[4].c_str());
16893 							if (m_sql.m_windunit != WINDUNIT_Beaufort)
16894 							{
16895 								sprintf(szTmp, "%.1f", float(intSpeed) * m_sql.m_windscale);
16896 								root["resultprev"][iPrev]["sp"] = szTmp;
16897 								sprintf(szTmp, "%.1f", float(intGust) * m_sql.m_windscale);
16898 								root["resultprev"][iPrev]["gu"] = szTmp;
16899 							}
16900 							else
16901 							{
16902 								float windspeedms = float(intSpeed)*0.1f;
16903 								float windgustms = float(intGust)*0.1f;
16904 								sprintf(szTmp, "%d", MStoBeaufort(windspeedms));
16905 								root["resultprev"][iPrev]["sp"] = szTmp;
16906 								sprintf(szTmp, "%d", MStoBeaufort(windgustms));
16907 								root["resultprev"][iPrev]["gu"] = szTmp;
16908 							}
16909 							iPrev++;
16910 						}
16911 					}
16912 				}
16913 			}//month or year
16914 			else if ((srange.substr(0, 1) == "2") && (srange.substr(10, 1) == "T") && (srange.substr(11, 1) == "2")) // custom range 2013-01-01T2013-12-31
16915 			{
16916 				std::string szDateStart = srange.substr(0, 10);
16917 				std::string szDateEnd = srange.substr(11, 10);
16918 				std::string sgraphtype = request::findValue(&req, "graphtype");
16919 				std::string sgraphTemp = request::findValue(&req, "graphTemp");
16920 				std::string sgraphChill = request::findValue(&req, "graphChill");
16921 				std::string sgraphHum = request::findValue(&req, "graphHum");
16922 				std::string sgraphBaro = request::findValue(&req, "graphBaro");
16923 				std::string sgraphDew = request::findValue(&req, "graphDew");
16924 				std::string sgraphSet = request::findValue(&req, "graphSet");
16925 
16926 				if (sensor == "temp") {
16927 					root["status"] = "OK";
16928 					root["title"] = "Graph " + sensor + " " + srange;
16929 
16930 					bool sendTemp = false;
16931 					bool sendChill = false;
16932 					bool sendHum = false;
16933 					bool sendBaro = false;
16934 					bool sendDew = false;
16935 					bool sendSet = false;
16936 
16937 					if ((sgraphTemp == "true") &&
16938 						((dType == pTypeRego6XXTemp) || (dType == pTypeTEMP) || (dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO) || (dType == pTypeTEMP_BARO) || (dType == pTypeWIND) || (dType == pTypeThermostat1) || (dType == pTypeRadiator1) ||
16939 						((dType == pTypeUV) && (dSubType == sTypeUV3)) ||
16940 							((dType == pTypeWIND) && (dSubType == sTypeWIND4)) ||
16941 							((dType == pTypeRFXSensor) && (dSubType == sTypeRFXSensorTemp)) ||
16942 							((dType == pTypeThermostat) && (dSubType == sTypeThermSetpoint)) ||
16943 							(dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater)
16944 							)
16945 						)
16946 					{
16947 						sendTemp = true;
16948 					}
16949 					if ((sgraphSet == "true") &&
16950 						((dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater))) //FIXME cheat for water setpoint is just on or off
16951 					{
16952 						sendSet = true;
16953 					}
16954 					if ((sgraphChill == "true") &&
16955 						(((dType == pTypeWIND) && (dSubType == sTypeWIND4)) ||
16956 						((dType == pTypeWIND) && (dSubType == sTypeWINDNoTemp)))
16957 						)
16958 					{
16959 						sendChill = true;
16960 					}
16961 					if ((sgraphHum == "true") &&
16962 						((dType == pTypeHUM) || (dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO))
16963 						)
16964 					{
16965 						sendHum = true;
16966 					}
16967 					if ((sgraphBaro == "true") && (
16968 						(dType == pTypeTEMP_HUM_BARO) ||
16969 						(dType == pTypeTEMP_BARO) ||
16970 						((dType == pTypeGeneral) && (dSubType == sTypeBaro))
16971 						))
16972 					{
16973 						sendBaro = true;
16974 					}
16975 					if ((sgraphDew == "true") && ((dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO)))
16976 					{
16977 						sendDew = true;
16978 					}
16979 
16980 					if (sgraphtype == "1")
16981 					{
16982 						// Need to get all values of the end date so 23:59:59 is appended to the date string
16983 						result = m_sql.safe_query(
16984 							"SELECT Temperature, Chill, Humidity, Barometer,"
16985 							" Date, DewPoint, SetPoint "
16986 							"FROM Temperature WHERE (DeviceRowID==%" PRIu64 ""
16987 							" AND Date>='%q' AND Date<='%q 23:59:59') ORDER BY Date ASC",
16988 							idx, szDateStart.c_str(), szDateEnd.c_str());
16989 						int ii = 0;
16990 						if (!result.empty())
16991 						{
16992 							for (const auto & itt : result)
16993 							{
16994 								std::vector<std::string> sd = itt;
16995 
16996 								root["result"][ii]["d"] = sd[4];//.substr(0,16);
16997 								if (sendTemp)
16998 								{
16999 									double te = ConvertTemperature(atof(sd[0].c_str()), tempsign);
17000 									double tm = ConvertTemperature(atof(sd[0].c_str()), tempsign);
17001 									root["result"][ii]["te"] = te;
17002 									root["result"][ii]["tm"] = tm;
17003 								}
17004 								if (sendChill)
17005 								{
17006 									double ch = ConvertTemperature(atof(sd[1].c_str()), tempsign);
17007 									double cm = ConvertTemperature(atof(sd[1].c_str()), tempsign);
17008 									root["result"][ii]["ch"] = ch;
17009 									root["result"][ii]["cm"] = cm;
17010 								}
17011 								if (sendHum)
17012 								{
17013 									root["result"][ii]["hu"] = sd[2];
17014 								}
17015 								if (sendBaro)
17016 								{
17017 									if (dType == pTypeTEMP_HUM_BARO)
17018 									{
17019 										if (dSubType == sTypeTHBFloat)
17020 										{
17021 											sprintf(szTmp, "%.1f", atof(sd[3].c_str()) / 10.0f);
17022 											root["result"][ii]["ba"] = szTmp;
17023 										}
17024 										else
17025 											root["result"][ii]["ba"] = sd[3];
17026 									}
17027 									else if (dType == pTypeTEMP_BARO)
17028 									{
17029 										sprintf(szTmp, "%.1f", atof(sd[3].c_str()) / 10.0f);
17030 										root["result"][ii]["ba"] = szTmp;
17031 									}
17032 									else if ((dType == pTypeGeneral) && (dSubType == sTypeBaro))
17033 									{
17034 										sprintf(szTmp, "%.1f", atof(sd[3].c_str()) / 10.0f);
17035 										root["result"][ii]["ba"] = szTmp;
17036 									}
17037 								}
17038 								if (sendDew)
17039 								{
17040 									double dp = ConvertTemperature(atof(sd[5].c_str()), tempsign);
17041 									root["result"][ii]["dp"] = dp;
17042 								}
17043 								if (sendSet)
17044 								{
17045 									double se = ConvertTemperature(atof(sd[6].c_str()), tempsign);
17046 									root["result"][ii]["se"] = se;
17047 								}
17048 								ii++;
17049 							}
17050 						}
17051 					}
17052 					else
17053 					{
17054 						result = m_sql.safe_query(
17055 							"SELECT Temp_Min, Temp_Max, Chill_Min, Chill_Max,"
17056 							" Humidity, Barometer, Date, DewPoint, Temp_Avg,"
17057 							" SetPoint_Min, SetPoint_Max, SetPoint_Avg "
17058 							"FROM Temperature_Calendar "
17059 							"WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q'"
17060 							" AND Date<='%q') ORDER BY Date ASC",
17061 							idx, szDateStart.c_str(), szDateEnd.c_str());
17062 						int ii = 0;
17063 						if (!result.empty())
17064 						{
17065 							for (const auto & itt : result)
17066 							{
17067 								std::vector<std::string> sd = itt;
17068 
17069 								root["result"][ii]["d"] = sd[6].substr(0, 16);
17070 								if (sendTemp)
17071 								{
17072 									double te = ConvertTemperature(atof(sd[1].c_str()), tempsign);
17073 									double tm = ConvertTemperature(atof(sd[0].c_str()), tempsign);
17074 									double ta = ConvertTemperature(atof(sd[8].c_str()), tempsign);
17075 
17076 									root["result"][ii]["te"] = te;
17077 									root["result"][ii]["tm"] = tm;
17078 									root["result"][ii]["ta"] = ta;
17079 								}
17080 								if (sendChill)
17081 								{
17082 									double ch = ConvertTemperature(atof(sd[3].c_str()), tempsign);
17083 									double cm = ConvertTemperature(atof(sd[2].c_str()), tempsign);
17084 
17085 									root["result"][ii]["ch"] = ch;
17086 									root["result"][ii]["cm"] = cm;
17087 								}
17088 								if (sendHum)
17089 								{
17090 									root["result"][ii]["hu"] = sd[4];
17091 								}
17092 								if (sendBaro)
17093 								{
17094 									if (dType == pTypeTEMP_HUM_BARO)
17095 									{
17096 										if (dSubType == sTypeTHBFloat)
17097 										{
17098 											sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
17099 											root["result"][ii]["ba"] = szTmp;
17100 										}
17101 										else
17102 											root["result"][ii]["ba"] = sd[5];
17103 									}
17104 									else if (dType == pTypeTEMP_BARO)
17105 									{
17106 										sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
17107 										root["result"][ii]["ba"] = szTmp;
17108 									}
17109 									else if ((dType == pTypeGeneral) && (dSubType == sTypeBaro))
17110 									{
17111 										sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
17112 										root["result"][ii]["ba"] = szTmp;
17113 									}
17114 								}
17115 								if (sendDew)
17116 								{
17117 									double dp = ConvertTemperature(atof(sd[7].c_str()), tempsign);
17118 									root["result"][ii]["dp"] = dp;
17119 								}
17120 								if (sendSet)
17121 								{
17122 									double sm = ConvertTemperature(atof(sd[9].c_str()), tempsign);
17123 									double sx = ConvertTemperature(atof(sd[10].c_str()), tempsign);
17124 									double se = ConvertTemperature(atof(sd[11].c_str()), tempsign);
17125 									root["result"][ii]["sm"] = sm;
17126 									root["result"][ii]["se"] = se;
17127 									root["result"][ii]["sx"] = sx;
17128 									char szTmp[1024];
17129 									sprintf(szTmp, "%.1f %.1f %.1f", sm, se, sx);
17130 									_log.Log(LOG_STATUS, "%s", szTmp);
17131 
17132 								}
17133 								ii++;
17134 							}
17135 						}
17136 
17137 						//add today (have to calculate it)
17138 						result = m_sql.safe_query(
17139 							"SELECT MIN(Temperature), MAX(Temperature),"
17140 							" MIN(Chill), MAX(Chill), AVG(Humidity),"
17141 							" AVG(Barometer), MIN(DewPoint), AVG(Temperature),"
17142 							" MIN(SetPoint), MAX(SetPoint), AVG(SetPoint) "
17143 							"FROM Temperature WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
17144 							idx, szDateEnd.c_str());
17145 						if (!result.empty())
17146 						{
17147 							std::vector<std::string> sd = result[0];
17148 
17149 							root["result"][ii]["d"] = szDateEnd;
17150 							if (sendTemp)
17151 							{
17152 								double te = ConvertTemperature(atof(sd[1].c_str()), tempsign);
17153 								double tm = ConvertTemperature(atof(sd[0].c_str()), tempsign);
17154 								double ta = ConvertTemperature(atof(sd[7].c_str()), tempsign);
17155 
17156 								root["result"][ii]["te"] = te;
17157 								root["result"][ii]["tm"] = tm;
17158 								root["result"][ii]["ta"] = ta;
17159 							}
17160 							if (sendChill)
17161 							{
17162 								double ch = ConvertTemperature(atof(sd[3].c_str()), tempsign);
17163 								double cm = ConvertTemperature(atof(sd[2].c_str()), tempsign);
17164 								root["result"][ii]["ch"] = ch;
17165 								root["result"][ii]["cm"] = cm;
17166 							}
17167 							if (sendHum)
17168 							{
17169 								root["result"][ii]["hu"] = sd[4];
17170 							}
17171 							if (sendBaro)
17172 							{
17173 								if (dType == pTypeTEMP_HUM_BARO)
17174 								{
17175 									if (dSubType == sTypeTHBFloat)
17176 									{
17177 										sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
17178 										root["result"][ii]["ba"] = szTmp;
17179 									}
17180 									else
17181 										root["result"][ii]["ba"] = sd[5];
17182 								}
17183 								else if (dType == pTypeTEMP_BARO)
17184 								{
17185 									sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
17186 									root["result"][ii]["ba"] = szTmp;
17187 								}
17188 								else if ((dType == pTypeGeneral) && (dSubType == sTypeBaro))
17189 								{
17190 									sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
17191 									root["result"][ii]["ba"] = szTmp;
17192 								}
17193 							}
17194 							if (sendDew)
17195 							{
17196 								double dp = ConvertTemperature(atof(sd[6].c_str()), tempsign);
17197 								root["result"][ii]["dp"] = dp;
17198 							}
17199 							if (sendSet)
17200 							{
17201 								double sm = ConvertTemperature(atof(sd[8].c_str()), tempsign);
17202 								double sx = ConvertTemperature(atof(sd[9].c_str()), tempsign);
17203 								double se = ConvertTemperature(atof(sd[10].c_str()), tempsign);
17204 
17205 								root["result"][ii]["sm"] = sm;
17206 								root["result"][ii]["se"] = se;
17207 								root["result"][ii]["sx"] = sx;
17208 							}
17209 							ii++;
17210 						}
17211 					}
17212 				}
17213 				else if (sensor == "uv") {
17214 					root["status"] = "OK";
17215 					root["title"] = "Graph " + sensor + " " + srange;
17216 
17217 					result = m_sql.safe_query(
17218 						"SELECT Level, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ""
17219 						" AND Date>='%q' AND Date<='%q') ORDER BY Date ASC",
17220 						dbasetable.c_str(), idx, szDateStart.c_str(), szDateEnd.c_str());
17221 					int ii = 0;
17222 					if (!result.empty())
17223 					{
17224 						for (const auto & itt : result)
17225 						{
17226 							std::vector<std::string> sd = itt;
17227 
17228 							root["result"][ii]["d"] = sd[1].substr(0, 16);
17229 							root["result"][ii]["uvi"] = sd[0];
17230 							ii++;
17231 						}
17232 					}
17233 					//add today (have to calculate it)
17234 					result = m_sql.safe_query(
17235 						"SELECT MAX(Level) FROM UV WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
17236 						idx, szDateEnd.c_str());
17237 					if (!result.empty())
17238 					{
17239 						std::vector<std::string> sd = result[0];
17240 
17241 						root["result"][ii]["d"] = szDateEnd;
17242 						root["result"][ii]["uvi"] = sd[0];
17243 						ii++;
17244 					}
17245 				}
17246 				else if (sensor == "rain") {
17247 					root["status"] = "OK";
17248 					root["title"] = "Graph " + sensor + " " + srange;
17249 
17250 					result = m_sql.safe_query(
17251 						"SELECT Total, Rate, Date FROM %s "
17252 						"WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC",
17253 						dbasetable.c_str(), idx, szDateStart.c_str(), szDateEnd.c_str());
17254 					int ii = 0;
17255 					if (!result.empty())
17256 					{
17257 						for (const auto & itt : result)
17258 						{
17259 							std::vector<std::string> sd = itt;
17260 
17261 							root["result"][ii]["d"] = sd[2].substr(0, 16);
17262 							root["result"][ii]["mm"] = sd[0];
17263 							ii++;
17264 						}
17265 					}
17266 					//add today (have to calculate it)
17267 					if (dSubType == sTypeRAINWU || dSubType == sTypeRAINByRate)
17268 					{
17269 						result = m_sql.safe_query(
17270 							"SELECT Total, Total, Rate FROM Rain WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q') ORDER BY ROWID DESC LIMIT 1",
17271 							idx, szDateEnd.c_str());
17272 					}
17273 					else
17274 					{
17275 						result = m_sql.safe_query(
17276 							"SELECT MIN(Total), MAX(Total), MAX(Rate) FROM Rain WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
17277 							idx, szDateEnd.c_str());
17278 					}
17279 					if (!result.empty())
17280 					{
17281 						std::vector<std::string> sd = result[0];
17282 
17283 						float total_min = static_cast<float>(atof(sd[0].c_str()));
17284 						float total_max = static_cast<float>(atof(sd[1].c_str()));
17285 						//int rate = atoi(sd[2].c_str());
17286 
17287 						float total_real = 0;
17288 						if (dSubType == sTypeRAINWU || dSubType == sTypeRAINByRate)
17289 						{
17290 							total_real = total_max;
17291 						}
17292 						else
17293 						{
17294 							total_real = total_max - total_min;
17295 						}
17296 						sprintf(szTmp, "%.1f", total_real);
17297 						root["result"][ii]["d"] = szDateEnd;
17298 						root["result"][ii]["mm"] = szTmp;
17299 						ii++;
17300 					}
17301 				}
17302 				else if (sensor == "counter") {
17303 					root["status"] = "OK";
17304 					root["title"] = "Graph " + sensor + " " + srange;
17305 					root["ValueQuantity"] = options["ValueQuantity"];
17306 					root["ValueUnits"] = options["ValueUnits"];
17307 
17308 					int ii = 0;
17309 					if (dType == pTypeP1Power)
17310 					{
17311 						result = m_sql.safe_query(
17312 							"SELECT Value1,Value2,Value5,Value6, Date "
17313 							"FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q'"
17314 							" AND Date<='%q') ORDER BY Date ASC",
17315 							dbasetable.c_str(), idx, szDateStart.c_str(), szDateEnd.c_str());
17316 						if (!result.empty())
17317 						{
17318 							bool bHaveDeliverd = false;
17319 							for (const auto & itt : result)
17320 							{
17321 								std::vector<std::string> sd = itt;
17322 
17323 								root["result"][ii]["d"] = sd[4].substr(0, 16);
17324 
17325 								std::string szUsage1 = sd[0];
17326 								std::string szDeliv1 = sd[1];
17327 								std::string szUsage2 = sd[2];
17328 								std::string szDeliv2 = sd[3];
17329 
17330 								float fUsage = (float)(atof(szUsage1.c_str()) + atof(szUsage2.c_str()));
17331 								float fDeliv = (float)(atof(szDeliv1.c_str()) + atof(szDeliv2.c_str()));
17332 
17333 								if (fDeliv != 0)
17334 									bHaveDeliverd = true;
17335 								sprintf(szTmp, "%.3f", fUsage / divider);
17336 								root["result"][ii]["v"] = szTmp;
17337 								sprintf(szTmp, "%.3f", fDeliv / divider);
17338 								root["result"][ii]["v2"] = szTmp;
17339 								ii++;
17340 							}
17341 							if (bHaveDeliverd)
17342 							{
17343 								root["delivered"] = true;
17344 							}
17345 						}
17346 					}
17347 					else
17348 					{
17349 						result = m_sql.safe_query("SELECT Value, Date FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q' AND Date<='%q') ORDER BY Date ASC", dbasetable.c_str(), idx, szDateStart.c_str(), szDateEnd.c_str());
17350 						if (!result.empty())
17351 						{
17352 							for (const auto & itt : result)
17353 							{
17354 								std::vector<std::string> sd = itt;
17355 
17356 								std::string szValue = sd[0];
17357 								switch (metertype)
17358 								{
17359 								case MTYPE_ENERGY:
17360 								case MTYPE_ENERGY_GENERATED:
17361 									sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
17362 									szValue = szTmp;
17363 									break;
17364 								case MTYPE_GAS:
17365 									sprintf(szTmp, "%.2f", atof(szValue.c_str()) / divider);
17366 									szValue = szTmp;
17367 									break;
17368 								case MTYPE_WATER:
17369 									sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
17370 									szValue = szTmp;
17371 									break;
17372 								}
17373 								root["result"][ii]["d"] = sd[1].substr(0, 16);
17374 								root["result"][ii]["v"] = szValue;
17375 								ii++;
17376 							}
17377 						}
17378 					}
17379 					//add today (have to calculate it)
17380 					if (dType == pTypeP1Power)
17381 					{
17382 						result = m_sql.safe_query(
17383 							"SELECT MIN(Value1), MAX(Value1), MIN(Value2),"
17384 							" MAX(Value2),MIN(Value5), MAX(Value5),"
17385 							" MIN(Value6), MAX(Value6) "
17386 							"FROM MultiMeter WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
17387 							idx, szDateEnd.c_str());
17388 						bool bHaveDeliverd = false;
17389 						if (!result.empty())
17390 						{
17391 							std::vector<std::string> sd = result[0];
17392 
17393 							unsigned long long total_min_usage_1 = std::strtoull(sd[0].c_str(), nullptr, 10);
17394 							unsigned long long total_max_usage_1 = std::strtoull(sd[1].c_str(), nullptr, 10);
17395 							unsigned long long total_min_usage_2 = std::strtoull(sd[4].c_str(), nullptr, 10);
17396 							unsigned long long total_max_usage_2 = std::strtoull(sd[5].c_str(), nullptr, 10);
17397 							unsigned long long total_real_usage;
17398 
17399 							unsigned long long total_min_deliv_1 = std::strtoull(sd[2].c_str(), nullptr, 10);
17400 							unsigned long long total_max_deliv_1 = std::strtoull(sd[3].c_str(), nullptr, 10);
17401 							unsigned long long total_min_deliv_2 = std::strtoull(sd[6].c_str(), nullptr, 10);
17402 							unsigned long long total_max_deliv_2 = std::strtoull(sd[7].c_str(), nullptr, 10);
17403 							unsigned long long total_real_deliv;
17404 
17405 							total_real_usage = (total_max_usage_1 + total_max_usage_2) - (total_min_usage_1 + total_min_usage_2);
17406 							total_real_deliv = (total_max_deliv_1 + total_max_deliv_2) - (total_min_deliv_1 + total_min_deliv_2);
17407 
17408 							if (total_real_deliv != 0)
17409 								bHaveDeliverd = true;
17410 
17411 							root["result"][ii]["d"] = szDateEnd;
17412 
17413 							sprintf(szTmp, "%llu", total_real_usage);
17414 							std::string szValue = szTmp;
17415 							sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
17416 							root["result"][ii]["v"] = szTmp;
17417 							sprintf(szTmp, "%llu", total_real_deliv);
17418 							szValue = szTmp;
17419 							sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
17420 							root["result"][ii]["v2"] = szTmp;
17421 							ii++;
17422 							if (bHaveDeliverd)
17423 							{
17424 								root["delivered"] = true;
17425 							}
17426 						}
17427 					}
17428 					else if (!bIsManagedCounter)
17429 					{
17430 						result = m_sql.safe_query(
17431 							"SELECT MIN(Value), MAX(Value) FROM Meter WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
17432 							idx, szDateEnd.c_str());
17433 						if (!result.empty())
17434 						{
17435 							std::vector<std::string> sd = result[0];
17436 							unsigned long long total_min = std::strtoull(sd[0].c_str(), nullptr, 10);
17437 							unsigned long long total_max = std::strtoull(sd[1].c_str(), nullptr, 10);
17438 							unsigned long long total_real;
17439 
17440 							total_real = total_max - total_min;
17441 							sprintf(szTmp, "%llu", total_real);
17442 
17443 							std::string szValue = szTmp;
17444 							switch (metertype)
17445 							{
17446 							case MTYPE_ENERGY:
17447 							case MTYPE_ENERGY_GENERATED:
17448 								sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
17449 								szValue = szTmp;
17450 								break;
17451 							case MTYPE_GAS:
17452 								sprintf(szTmp, "%.2f", atof(szValue.c_str()) / divider);
17453 								szValue = szTmp;
17454 								break;
17455 							case MTYPE_WATER:
17456 								sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
17457 								szValue = szTmp;
17458 								break;
17459 							}
17460 
17461 							root["result"][ii]["d"] = szDateEnd;
17462 							root["result"][ii]["v"] = szValue;
17463 							ii++;
17464 						}
17465 					}
17466 				}
17467 				else if (sensor == "wind") {
17468 					root["status"] = "OK";
17469 					root["title"] = "Graph " + sensor + " " + srange;
17470 
17471 					int ii = 0;
17472 
17473 					result = m_sql.safe_query(
17474 						"SELECT Direction, Speed_Min, Speed_Max, Gust_Min,"
17475 						" Gust_Max, Date "
17476 						"FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q'"
17477 						" AND Date<='%q') ORDER BY Date ASC",
17478 						dbasetable.c_str(), idx, szDateStart.c_str(), szDateEnd.c_str());
17479 					if (!result.empty())
17480 					{
17481 						for (const auto & itt : result)
17482 						{
17483 							std::vector<std::string> sd = itt;
17484 
17485 							root["result"][ii]["d"] = sd[5].substr(0, 16);
17486 							root["result"][ii]["di"] = sd[0];
17487 
17488 							int intSpeed = atoi(sd[2].c_str());
17489 							int intGust = atoi(sd[4].c_str());
17490 							if (m_sql.m_windunit != WINDUNIT_Beaufort)
17491 							{
17492 								sprintf(szTmp, "%.1f", float(intSpeed) * m_sql.m_windscale);
17493 								root["result"][ii]["sp"] = szTmp;
17494 								sprintf(szTmp, "%.1f", float(intGust) * m_sql.m_windscale);
17495 								root["result"][ii]["gu"] = szTmp;
17496 							}
17497 							else
17498 							{
17499 								float windspeedms = float(intSpeed)*0.1f;
17500 								float windgustms = float(intGust)*0.1f;
17501 								sprintf(szTmp, "%d", MStoBeaufort(windspeedms));
17502 								root["result"][ii]["sp"] = szTmp;
17503 								sprintf(szTmp, "%d", MStoBeaufort(windgustms));
17504 								root["result"][ii]["gu"] = szTmp;
17505 							}
17506 							ii++;
17507 						}
17508 					}
17509 					//add today (have to calculate it)
17510 					result = m_sql.safe_query(
17511 						"SELECT AVG(Direction), MIN(Speed), MAX(Speed), MIN(Gust), MAX(Gust) FROM Wind WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q') ORDER BY Date ASC",
17512 						idx, szDateEnd.c_str());
17513 					if (!result.empty())
17514 					{
17515 						std::vector<std::string> sd = result[0];
17516 
17517 						root["result"][ii]["d"] = szDateEnd;
17518 						root["result"][ii]["di"] = sd[0];
17519 
17520 						int intSpeed = atoi(sd[2].c_str());
17521 						int intGust = atoi(sd[4].c_str());
17522 						if (m_sql.m_windunit != WINDUNIT_Beaufort)
17523 						{
17524 							sprintf(szTmp, "%.1f", float(intSpeed) * m_sql.m_windscale);
17525 							root["result"][ii]["sp"] = szTmp;
17526 							sprintf(szTmp, "%.1f", float(intGust) * m_sql.m_windscale);
17527 							root["result"][ii]["gu"] = szTmp;
17528 						}
17529 						else
17530 						{
17531 							float windspeedms = float(intSpeed)*0.1f;
17532 							float windgustms = float(intGust)*0.1f;
17533 							sprintf(szTmp, "%d", MStoBeaufort(windspeedms));
17534 							root["result"][ii]["sp"] = szTmp;
17535 							sprintf(szTmp, "%d", MStoBeaufort(windgustms));
17536 							root["result"][ii]["gu"] = szTmp;
17537 						}
17538 						ii++;
17539 					}
17540 				}
17541 			}//custom range
17542 		}
17543 
17544 		/**
17545 		 * Retrieve user session from store, without remote host.
17546 		 */
GetSession(const std::string & sessionId)17547 		const WebEmStoredSession CWebServer::GetSession(const std::string & sessionId) {
17548 			//_log.Log(LOG_STATUS, "SessionStore : get...");
17549 			WebEmStoredSession session;
17550 
17551 			if (sessionId.empty()) {
17552 				_log.Log(LOG_ERROR, "SessionStore : cannot get session without id.");
17553 			}
17554 			else {
17555 				std::vector<std::vector<std::string> > result;
17556 				result = m_sql.safe_query("SELECT SessionID, Username, AuthToken, ExpirationDate FROM UserSessions WHERE SessionID = '%q'",
17557 					sessionId.c_str());
17558 				if (!result.empty()) {
17559 					session.id = result[0][0].c_str();
17560 					session.username = base64_decode(result[0][1]);
17561 					session.auth_token = result[0][2].c_str();
17562 
17563 					std::string sExpirationDate = result[0][3];
17564 					//time_t now = mytime(NULL);
17565 					struct tm tExpirationDate;
17566 					ParseSQLdatetime(session.expires, tExpirationDate, sExpirationDate);
17567 					// RemoteHost is not used to restore the session
17568 					// LastUpdate is not used to restore the session
17569 				}
17570 			}
17571 
17572 			return session;
17573 		}
17574 
17575 		/**
17576 		 * Save user session.
17577 		 */
StoreSession(const WebEmStoredSession & session)17578 		void CWebServer::StoreSession(const WebEmStoredSession & session) {
17579 			//_log.Log(LOG_STATUS, "SessionStore : store...");
17580 			if (session.id.empty()) {
17581 				_log.Log(LOG_ERROR, "SessionStore : cannot store session without id.");
17582 				return;
17583 			}
17584 
17585 			char szExpires[30];
17586 			struct tm ltime;
17587 			localtime_r(&session.expires, &ltime);
17588 			strftime(szExpires, sizeof(szExpires), "%Y-%m-%d %H:%M:%S", &ltime);
17589 
17590 			std::string remote_host = (session.remote_host.size() <= 50) ? // IPv4 : 15, IPv6 : (39|45)
17591 				session.remote_host : session.remote_host.substr(0, 50);
17592 
17593 			WebEmStoredSession storedSession = GetSession(session.id);
17594 			if (storedSession.id.empty()) {
17595 				m_sql.safe_query(
17596 					"INSERT INTO UserSessions (SessionID, Username, AuthToken, ExpirationDate, RemoteHost) VALUES ('%q', '%q', '%q', '%q', '%q')",
17597 					session.id.c_str(),
17598 					base64_encode(session.username).c_str(),
17599 					session.auth_token.c_str(),
17600 					szExpires,
17601 					remote_host.c_str());
17602 			}
17603 			else {
17604 				m_sql.safe_query(
17605 					"UPDATE UserSessions set AuthToken = '%q', ExpirationDate = '%q', RemoteHost = '%q', LastUpdate = datetime('now', 'localtime') WHERE SessionID = '%q'",
17606 					session.auth_token.c_str(),
17607 					szExpires,
17608 					remote_host.c_str(),
17609 					session.id.c_str());
17610 			}
17611 		}
17612 
17613 		/**
17614 		 * Remove user session and expired sessions.
17615 		 */
RemoveSession(const std::string & sessionId)17616 		void CWebServer::RemoveSession(const std::string & sessionId) {
17617 			//_log.Log(LOG_STATUS, "SessionStore : remove...");
17618 			if (sessionId.empty()) {
17619 				return;
17620 			}
17621 			m_sql.safe_query(
17622 				"DELETE FROM UserSessions WHERE SessionID = '%q'",
17623 				sessionId.c_str());
17624 		}
17625 
17626 		/**
17627 		 * Remove all expired user sessions.
17628 		 */
CleanSessions()17629 		void CWebServer::CleanSessions() {
17630 			//_log.Log(LOG_STATUS, "SessionStore : clean...");
17631 			m_sql.safe_query(
17632 				"DELETE FROM UserSessions WHERE ExpirationDate < datetime('now', 'localtime')");
17633 		}
17634 
17635 		/**
17636 		 * Delete all user's session, except the session used to modify the username or password.
17637 		 * username must have been hashed
17638 		 *
17639 		 * Note : on the WebUserName modification, this method will not delete the session, but the session will be deleted anyway
17640 		 * because the username will be unknown (see cWebemRequestHandler::checkAuthToken).
17641 		 */
RemoveUsersSessions(const std::string & username,const WebEmSession & exceptSession)17642 		void CWebServer::RemoveUsersSessions(const std::string& username, const WebEmSession & exceptSession) {
17643 			m_sql.safe_query("DELETE FROM UserSessions WHERE (Username=='%q') and (SessionID!='%q')", username.c_str(), exceptSession.id.c_str());
17644 		}
17645 
17646 	} //server
17647 }//http
17648