Commit message: Fixed possible SQL Injection Vulnerability (Thanks to Fabio Carretto!)
CVE: CVE-2019-10664
CWE: CWE-89
Vulnerability description: Domoticz before 4.10578 allows SQL Injection via the idx parameter in CWebServer::GetFloorplanImage in WebServer.cpp.

Project: domoticz

		void CWebServer::RType_HandleGraph(WebEmSession & session, const request& req, Json::Value &root)
		{
			uint64_t idx = 0;
			if (request::findValue(&req, "idx") != "")
			{
				idx = std::strtoull(request::findValue(&req, "idx").c_str(), nullptr, 10);
			}

			std::vector<std::vector<std::string> > result;
			char szTmp[300];

			std::string sensor = request::findValue(&req, "sensor");
			if (sensor == "")
				return;
			std::string srange = request::findValue(&req, "range");
			if (srange == "")
				return;

			time_t now = mytime(NULL);
			struct tm tm1;
			localtime_r(&now, &tm1);

			result = m_sql.safe_query("SELECT Type, SubType, SwitchType, AddjValue, AddjMulti, AddjValue2, Options FROM DeviceStatus WHERE (ID == %" PRIu64 ")",
				idx);
			if (result.empty())
				return;

			unsigned char dType = atoi(result[0][0].c_str());
			unsigned char dSubType = atoi(result[0][1].c_str());
			_eMeterType metertype = (_eMeterType)atoi(result[0][2].c_str());
			if (
				(dType == pTypeP1Power) ||
				(dType == pTypeENERGY) ||
				(dType == pTypePOWER) ||
				(dType == pTypeCURRENTENERGY) ||
				((dType == pTypeGeneral) && (dSubType == sTypeKwh))
				)
			{
				metertype = MTYPE_ENERGY;
			}
			else if (dType == pTypeP1Gas)
				metertype = MTYPE_GAS;
			else if ((dType == pTypeRego6XXValue) && (dSubType == sTypeRego6XXCounter))
				metertype = MTYPE_COUNTER;

			bool bIsManagedCounter = (dType == pTypeGeneral) && (dSubType == sTypeManagedCounter);

			double AddjValue = atof(result[0][3].c_str());
			double AddjMulti = atof(result[0][4].c_str());
			double AddjValue2 = atof(result[0][5].c_str());
			std::string sOptions = result[0][6].c_str();
			std::map<std::string, std::string> options = m_sql.BuildDeviceOptions(sOptions);

			float divider = m_sql.GetCounterDivider(int(metertype), int(dType), float(AddjValue2));

			std::string dbasetable = "";
			if (srange == "day") {
				if (sensor == "temp")
					dbasetable = "Temperature";
				else if (sensor == "rain")
					dbasetable = "Rain";
				else if (sensor == "Percentage")
					dbasetable = "Percentage";
				else if (sensor == "fan")
					dbasetable = "Fan";
				else if (sensor == "counter")
				{
					if ((dType == pTypeP1Power) || (dType == pTypeCURRENT) || (dType == pTypeCURRENTENERGY))
					{
						dbasetable = "MultiMeter";
					}
					else
					{
						dbasetable = "Meter";
					}
				}
				else if ((sensor == "wind") || (sensor == "winddir"))
					dbasetable = "Wind";
				else if (sensor == "uv")
					dbasetable = "UV";
				else
					return;
			}
			else
			{
				if (sensor == "temp")
					dbasetable = "Temperature_Calendar";
				else if (sensor == "rain")
					dbasetable = "Rain_Calendar";
				else if (sensor == "Percentage")
					dbasetable = "Percentage_Calendar";
				else if (sensor == "fan")
					dbasetable = "Fan_Calendar";
				else if (sensor == "counter")
				{
					if (
						(dType == pTypeP1Power) ||
						(dType == pTypeCURRENT) ||
						(dType == pTypeCURRENTENERGY) ||
						(dType == pTypeAirQuality) ||
						((dType == pTypeGeneral) && (dSubType == sTypeVisibility)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeDistance)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeSolarRadiation)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeSoilMoisture)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeLeafWetness)) ||
						((dType == pTypeRFXSensor) && (dSubType == sTypeRFXSensorAD)) ||
						((dType == pTypeRFXSensor) && (dSubType == sTypeRFXSensorVolt)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeVoltage)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeCurrent)) ||
						((dType == pTypeGeneral) && (dSubType == sTypePressure)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeSoundLevel)) ||
						(dType == pTypeLux) ||
						(dType == pTypeWEIGHT) ||
						(dType == pTypeUsage)
						)
						dbasetable = "MultiMeter_Calendar";
					else
						dbasetable = "Meter_Calendar";
				}
				else if ((sensor == "wind") || (sensor == "winddir"))
					dbasetable = "Wind_Calendar";
				else if (sensor == "uv")
					dbasetable = "UV_Calendar";
				else
					return;
			}
			unsigned char tempsign = m_sql.m_tempsign[0];
			int iPrev;

			if (srange == "day")
			{
				if (sensor == "temp") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;

					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);
					if (!result.empty())
					{
						int ii = 0;
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["result"][ii]["d"] = sd[4].substr(0, 16);
							if (
								(dType == pTypeRego6XXTemp) ||
								(dType == pTypeTEMP) ||
								(dType == pTypeTEMP_HUM) ||
								(dType == pTypeTEMP_HUM_BARO) ||
								(dType == pTypeTEMP_BARO) ||
								((dType == pTypeWIND) && (dSubType == sTypeWIND4)) ||
								((dType == pTypeUV) && (dSubType == sTypeUV3)) ||
								(dType == pTypeThermostat1) ||
								(dType == pTypeRadiator1) ||
								((dType == pTypeRFXSensor) && (dSubType == sTypeRFXSensorTemp)) ||
								((dType == pTypeGeneral) && (dSubType == sTypeSystemTemp)) ||
								((dType == pTypeGeneral) && (dSubType == sTypeBaro)) ||
								((dType == pTypeThermostat) && (dSubType == sTypeThermSetpoint)) ||
								(dType == pTypeEvohomeZone) ||
								(dType == pTypeEvohomeWater)
								)
							{
								double tvalue = ConvertTemperature(atof(sd[0].c_str()), tempsign);
								root["result"][ii]["te"] = tvalue;
							}
							if (
								((dType == pTypeWIND) && (dSubType == sTypeWIND4)) ||
								((dType == pTypeWIND) && (dSubType == sTypeWINDNoTemp))
								)
							{
								double tvalue = ConvertTemperature(atof(sd[1].c_str()), tempsign);
								root["result"][ii]["ch"] = tvalue;
							}
							if ((dType == pTypeHUM) || (dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO))
							{
								root["result"][ii]["hu"] = sd[2];
							}
							if (
								(dType == pTypeTEMP_HUM_BARO) ||
								(dType == pTypeTEMP_BARO) ||
								((dType == pTypeGeneral) && (dSubType == sTypeBaro))
								)
							{
								if (dType == pTypeTEMP_HUM_BARO)
								{
									if (dSubType == sTypeTHBFloat)
									{
										sprintf(szTmp, "%.1f", atof(sd[3].c_str()) / 10.0f);
										root["result"][ii]["ba"] = szTmp;
									}
									else
										root["result"][ii]["ba"] = sd[3];
								}
								else if (dType == pTypeTEMP_BARO)
								{
									sprintf(szTmp, "%.1f", atof(sd[3].c_str()) / 10.0f);
									root["result"][ii]["ba"] = szTmp;
								}
								else if ((dType == pTypeGeneral) && (dSubType == sTypeBaro))
								{
									sprintf(szTmp, "%.1f", atof(sd[3].c_str()) / 10.0f);
									root["result"][ii]["ba"] = szTmp;
								}
							}
							if ((dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater))
							{
								double se = ConvertTemperature(atof(sd[5].c_str()), tempsign);
								root["result"][ii]["se"] = se;
							}

							ii++;
						}
					}
				}
				else if (sensor == "Percentage") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;

					result = m_sql.safe_query("SELECT Percentage, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
					if (!result.empty())
					{
						int ii = 0;
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["result"][ii]["d"] = sd[1].substr(0, 16);
							root["result"][ii]["v"] = sd[0];
							ii++;
						}
					}
				}
				else if (sensor == "fan") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;

					result = m_sql.safe_query("SELECT Speed, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
					if (!result.empty())
					{
						int ii = 0;
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["result"][ii]["d"] = sd[1].substr(0, 16);
							root["result"][ii]["v"] = sd[0];
							ii++;
						}
					}
				}

				else if (sensor == "counter")
				{
					if (dType == pTypeP1Power)
					{
						root["status"] = "OK";
						root["title"] = "Graph " + sensor + " " + srange;

						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);
						if (!result.empty())
						{
							int ii = 0;
							bool bHaveDeliverd = false;
							bool bHaveFirstValue = false;
							long long lastUsage1, lastUsage2, lastDeliv1, lastDeliv2;
							time_t lastTime = 0;

							long long firstUsage1, firstUsage2, firstDeliv1, firstDeliv2;

							int nMeterType = 0;
							m_sql.GetPreferencesVar("SmartMeterType", nMeterType);

							int lastDay = 0;

							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								if (nMeterType == 0)
								{
									long long actUsage1 = std::strtoll(sd[0].c_str(), nullptr, 10);
									long long actUsage2 = std::strtoll(sd[4].c_str(), nullptr, 10);
									long long actDeliv1 = std::strtoll(sd[1].c_str(), nullptr, 10);
									long long actDeliv2 = std::strtoll(sd[5].c_str(), nullptr, 10);
									actDeliv1 = (actDeliv1 < 10) ? 0 : actDeliv1;
									actDeliv2 = (actDeliv2 < 10) ? 0 : actDeliv2;

									std::string stime = sd[6];
									struct tm ntime;
									time_t atime;
									ParseSQLdatetime(atime, ntime, stime, -1);
									if (lastDay != ntime.tm_mday)
									{
										lastDay = ntime.tm_mday;
										firstUsage1 = actUsage1;
										firstUsage2 = actUsage2;
										firstDeliv1 = actDeliv1;
										firstDeliv2 = actDeliv2;
									}

									if (bHaveFirstValue)
									{
										long curUsage1 = (long)(actUsage1 - lastUsage1);
										long curUsage2 = (long)(actUsage2 - lastUsage2);
										long curDeliv1 = (long)(actDeliv1 - lastDeliv1);
										long curDeliv2 = (long)(actDeliv2 - lastDeliv2);

										if ((curUsage1 < 0) || (curUsage1 > 100000))
											curUsage1 = 0;
										if ((curUsage2 < 0) || (curUsage2 > 100000))
											curUsage2 = 0;
										if ((curDeliv1 < 0) || (curDeliv1 > 100000))
											curDeliv1 = 0;
										if ((curDeliv2 < 0) || (curDeliv2 > 100000))
											curDeliv2 = 0;

										float tdiff = static_cast<float>(difftime(atime, lastTime));
										if (tdiff == 0)
											tdiff = 1;
										float tlaps = 3600.0f / tdiff;
										curUsage1 *= int(tlaps);
										curUsage2 *= int(tlaps);
										curDeliv1 *= int(tlaps);
										curDeliv2 *= int(tlaps);

										root["result"][ii]["d"] = sd[6].substr(0, 16);

										if ((curDeliv1 != 0) || (curDeliv2 != 0))
											bHaveDeliverd = true;

										sprintf(szTmp, "%ld", curUsage1);
										root["result"][ii]["v"] = szTmp;
										sprintf(szTmp, "%ld", curUsage2);
										root["result"][ii]["v2"] = szTmp;
										sprintf(szTmp, "%ld", curDeliv1);
										root["result"][ii]["r1"] = szTmp;
										sprintf(szTmp, "%ld", curDeliv2);
										root["result"][ii]["r2"] = szTmp;

										long pUsage1 = (long)(actUsage1 - firstUsage1);
										long pUsage2 = (long)(actUsage2 - firstUsage2);

										sprintf(szTmp, "%ld", pUsage1 + pUsage2);
										root["result"][ii]["eu"] = szTmp;
										if (bHaveDeliverd)
										{
											long pDeliv1 = (long)(actDeliv1 - firstDeliv1);
											long pDeliv2 = (long)(actDeliv2 - firstDeliv2);
											sprintf(szTmp, "%ld", pDeliv1 + pDeliv2);
											root["result"][ii]["eg"] = szTmp;
										}

										ii++;
									}
									else
									{
										bHaveFirstValue = true;
										if ((ntime.tm_hour != 0) && (ntime.tm_min != 0))
										{
											struct tm ltime;
											localtime_r(&atime, &tm1);
											getNoon(atime, ltime, ntime.tm_year + 1900, ntime.tm_mon + 1, ntime.tm_mday - 1); // We're only interested in finding the date
											int year = ltime.tm_year + 1900;
											int mon = ltime.tm_mon + 1;
											int day = ltime.tm_mday;
											sprintf(szTmp, "%04d-%02d-%02d", year, mon, day);
											std::vector<std::vector<std::string> > result2;
											result2 = m_sql.safe_query(
												"SELECT Counter1, Counter2, Counter3, Counter4 FROM Multimeter_Calendar WHERE (DeviceRowID==%" PRIu64 ") AND (Date=='%q')",
												idx, szTmp);
											if (!result2.empty())
											{
												std::vector<std::string> sd = result2[0];
												firstUsage1 = std::strtoll(sd[0].c_str(), nullptr, 10);
												firstDeliv1 = std::strtoll(sd[1].c_str(), nullptr, 10);
												firstUsage2 = std::strtoll(sd[2].c_str(), nullptr, 10);
												firstDeliv2 = std::strtoll(sd[3].c_str(), nullptr, 10);
												lastDay = ntime.tm_mday;
											}
										}

									}
									lastUsage1 = actUsage1;
									lastUsage2 = actUsage2;
									lastDeliv1 = actDeliv1;
									lastDeliv2 = actDeliv2;
									lastTime = atime;
								}
								else
								{
									root["result"][ii]["d"] = sd[6].substr(0, 16);

									if (sd[3] != "0")
										bHaveDeliverd = true;
									root["result"][ii]["v"] = sd[2];
									root["result"][ii]["r1"] = sd[3];
									ii++;

								}
							}
							if (bHaveDeliverd)
							{
								root["delivered"] = true;
							}
						}
					}
					else if (dType == pTypeAirQuality)
					{//day
						root["status"] = "OK";
						root["title"] = "Graph " + sensor + " " + srange;

						result = m_sql.safe_query("SELECT Value, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
						if (!result.empty())
						{
							int ii = 0;
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[1].substr(0, 16);
								root["result"][ii]["co2"] = sd[0];
								ii++;
							}
						}
					}
					else if ((dType == pTypeGeneral) && ((dSubType == sTypeSoilMoisture) || (dSubType == sTypeLeafWetness)))
					{//day
						root["status"] = "OK";
						root["title"] = "Graph " + sensor + " " + srange;

						result = m_sql.safe_query("SELECT Value, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
						if (!result.empty())
						{
							int ii = 0;
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[1].substr(0, 16);
								root["result"][ii]["v"] = sd[0];
								ii++;
							}
						}
					}
					else if (
						((dType == pTypeGeneral) && (dSubType == sTypeVisibility)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeDistance)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeSolarRadiation)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeVoltage)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeCurrent)) ||
						((dType == pTypeGeneral) && (dSubType == sTypePressure)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeSoundLevel))
						)
					{//day
						root["status"] = "OK";
						root["title"] = "Graph " + sensor + " " + srange;
						float vdiv = 10.0f;
						if (
							((dType == pTypeGeneral) && (dSubType == sTypeVoltage)) ||
							((dType == pTypeGeneral) && (dSubType == sTypeCurrent))
							)
						{
							vdiv = 1000.0f;
						}
						result = m_sql.safe_query("SELECT Value, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
						if (!result.empty())
						{
							int ii = 0;
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[1].substr(0, 16);
								float fValue = float(atof(sd[0].c_str())) / vdiv;
								if (metertype == 1)
									fValue *= 0.6214f;
								if ((dType == pTypeGeneral) && (dSubType == sTypeVoltage))
									sprintf(szTmp, "%.3f", fValue);
								else if ((dType == pTypeGeneral) && (dSubType == sTypeCurrent))
									sprintf(szTmp, "%.3f", fValue);
								else
									sprintf(szTmp, "%.1f", fValue);
								root["result"][ii]["v"] = szTmp;
								ii++;
							}
						}
					}
					else if ((dType == pTypeRFXSensor) && ((dSubType == sTypeRFXSensorAD) || (dSubType == sTypeRFXSensorVolt)))
					{//day
						root["status"] = "OK";
						root["title"] = "Graph " + sensor + " " + srange;

						result = m_sql.safe_query("SELECT Value, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
						if (!result.empty())
						{
							int ii = 0;
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[1].substr(0, 16);
								root["result"][ii]["v"] = sd[0];
								ii++;
							}
						}
					}
					else if (dType == pTypeLux)
					{//day
						root["status"] = "OK";
						root["title"] = "Graph " + sensor + " " + srange;

						result = m_sql.safe_query("SELECT Value, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
						if (!result.empty())
						{
							int ii = 0;
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[1].substr(0, 16);
								root["result"][ii]["lux"] = sd[0];
								ii++;
							}
						}
					}
					else if (dType == pTypeWEIGHT)
					{//day
						root["status"] = "OK";
						root["title"] = "Graph " + sensor + " " + srange;

						result = m_sql.safe_query("SELECT Value, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
						if (!result.empty())
						{
							int ii = 0;
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[1].substr(0, 16);
								sprintf(szTmp, "%.1f", m_sql.m_weightscale * atof(sd[0].c_str()) / 10.0f);
								root["result"][ii]["v"] = szTmp;
								ii++;
							}
						}
					}
					else if (dType == pTypeUsage)
					{//day
						root["status"] = "OK";
						root["title"] = "Graph " + sensor + " " + srange;

						result = m_sql.safe_query("SELECT Value, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
						if (!result.empty())
						{
							int ii = 0;
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[1].substr(0, 16);
								root["result"][ii]["u"] = atof(sd[0].c_str()) / 10.0f;
								ii++;
							}
						}
					}
					else if (dType == pTypeCURRENT)
					{
						root["status"] = "OK";
						root["title"] = "Graph " + sensor + " " + srange;

						int displaytype = 0;
						int voltage = 230;
						m_sql.GetPreferencesVar("CM113DisplayType", displaytype);
						m_sql.GetPreferencesVar("ElectricVoltage", voltage);

						root["displaytype"] = displaytype;

						result = m_sql.safe_query("SELECT Value1, Value2, Value3, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
						if (!result.empty())
						{
							int ii = 0;
							bool bHaveL1 = false;
							bool bHaveL2 = false;
							bool bHaveL3 = false;
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[3].substr(0, 16);

								float fval1 = static_cast<float>(atof(sd[0].c_str()) / 10.0f);
								float fval2 = static_cast<float>(atof(sd[1].c_str()) / 10.0f);
								float fval3 = static_cast<float>(atof(sd[2].c_str()) / 10.0f);

								if (fval1 != 0)
									bHaveL1 = true;
								if (fval2 != 0)
									bHaveL2 = true;
								if (fval3 != 0)
									bHaveL3 = true;

								if (displaytype == 0)
								{
									sprintf(szTmp, "%.1f", fval1);
									root["result"][ii]["v1"] = szTmp;
									sprintf(szTmp, "%.1f", fval2);
									root["result"][ii]["v2"] = szTmp;
									sprintf(szTmp, "%.1f", fval3);
									root["result"][ii]["v3"] = szTmp;
								}
								else
								{
									sprintf(szTmp, "%d", int(fval1*voltage));
									root["result"][ii]["v1"] = szTmp;
									sprintf(szTmp, "%d", int(fval2*voltage));
									root["result"][ii]["v2"] = szTmp;
									sprintf(szTmp, "%d", int(fval3*voltage));
									root["result"][ii]["v3"] = szTmp;
								}
								ii++;
							}
							if (
								(!bHaveL1) &&
								(!bHaveL2) &&
								(!bHaveL3)
								) {
								root["haveL1"] = true; //show at least something
							}
							else {
								if (bHaveL1)
									root["haveL1"] = true;
								if (bHaveL2)
									root["haveL2"] = true;
								if (bHaveL3)
									root["haveL3"] = true;
							}
						}
					}
					else if (dType == pTypeCURRENTENERGY)
					{
						root["status"] = "OK";
						root["title"] = "Graph " + sensor + " " + srange;

						int displaytype = 0;
						int voltage = 230;
						m_sql.GetPreferencesVar("CM113DisplayType", displaytype);
						m_sql.GetPreferencesVar("ElectricVoltage", voltage);

						root["displaytype"] = displaytype;

						result = m_sql.safe_query("SELECT Value1, Value2, Value3, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
						if (!result.empty())
						{
							int ii = 0;
							bool bHaveL1 = false;
							bool bHaveL2 = false;
							bool bHaveL3 = false;
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[3].substr(0, 16);

								float fval1 = static_cast<float>(atof(sd[0].c_str()) / 10.0f);
								float fval2 = static_cast<float>(atof(sd[1].c_str()) / 10.0f);
								float fval3 = static_cast<float>(atof(sd[2].c_str()) / 10.0f);

								if (fval1 != 0)
									bHaveL1 = true;
								if (fval2 != 0)
									bHaveL2 = true;
								if (fval3 != 0)
									bHaveL3 = true;

								if (displaytype == 0)
								{
									sprintf(szTmp, "%.1f", fval1);
									root["result"][ii]["v1"] = szTmp;
									sprintf(szTmp, "%.1f", fval2);
									root["result"][ii]["v2"] = szTmp;
									sprintf(szTmp, "%.1f", fval3);
									root["result"][ii]["v3"] = szTmp;
								}
								else
								{
									sprintf(szTmp, "%d", int(fval1*voltage));
									root["result"][ii]["v1"] = szTmp;
									sprintf(szTmp, "%d", int(fval2*voltage));
									root["result"][ii]["v2"] = szTmp;
									sprintf(szTmp, "%d", int(fval3*voltage));
									root["result"][ii]["v3"] = szTmp;
								}
								ii++;
							}
							if (
								(!bHaveL1) &&
								(!bHaveL2) &&
								(!bHaveL3)
								) {
								root["haveL1"] = true; //show at least something
							}
							else {
								if (bHaveL1)
									root["haveL1"] = true;
								if (bHaveL2)
									root["haveL2"] = true;
								if (bHaveL3)
									root["haveL3"] = true;
							}
						}
					}
					else if ((dType == pTypeENERGY) || (dType == pTypePOWER) || (dType == pTypeYouLess) || ((dType == pTypeGeneral) && (dSubType == sTypeKwh)))
					{
						root["status"] = "OK";
						root["title"] = "Graph " + sensor + " " + srange;
						root["ValueQuantity"] = options["ValueQuantity"];
						root["ValueUnits"] = options["ValueUnits"];

						bool bHaveUsage = true;
						result = m_sql.safe_query("SELECT MIN([Usage]), MAX([Usage]) FROM %s WHERE (DeviceRowID==%" PRIu64 ")", dbasetable.c_str(), idx);
						if (!result.empty())
						{
							long long minValue = std::strtoll(result[0][0].c_str(), nullptr, 10);
							long long maxValue = std::strtoll(result[0][1].c_str(), nullptr, 10);
							if ((minValue == 0) && (maxValue == 0))
							{
								bHaveUsage = false;
							}
						}

						int ii = 0;
						result = m_sql.safe_query("SELECT Value,[Usage], Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);

						int method = 0;
						std::string sMethod = request::findValue(&req, "method");
						if (sMethod.size() > 0)
							method = atoi(sMethod.c_str());
						if (bHaveUsage == false)
							method = 0;

						if ((dType == pTypeYouLess) && ((metertype == MTYPE_ENERGY) || (metertype == MTYPE_ENERGY_GENERATED)))
							method = 1;

						if (method != 0)
						{
							if ((dType == pTypeENERGY) || (dType == pTypePOWER))
								divider /= 100.0f;
						}
						root["method"] = method;
						bool bHaveFirstValue = false;
						bool bHaveFirstRealValue = false;
						float FirstValue = 0;
						long long ulFirstRealValue = 0;
						long long ulFirstValue = 0;
						long long ulLastValue = 0;
						std::string LastDateTime = "";
						time_t lastTime = 0;

						if (!result.empty())
						{
							std::vector<std::vector<std::string> >::const_iterator itt;
							for (itt = result.begin(); itt!=result.end(); ++itt)
							{
								std::vector<std::string> sd = *itt;

								{
									std::string actDateTimeHour = sd[2].substr(0, 13);
									long long actValue = std::strtoll(sd[0].c_str(), nullptr, 10);

									if (actValue >= ulLastValue)
										ulLastValue = actValue;

									if (actDateTimeHour != LastDateTime || ((method == 1) && (itt + 1 == result.end())))
									{
										if (bHaveFirstValue)
										{
											root["result"][ii]["d"] = LastDateTime + ":00";

											long long ulTotalValue = ulLastValue - ulFirstValue;
											if (ulTotalValue == 0)
											{
												ulTotalValue = ulLastValue - ulFirstRealValue;
											}
											ulFirstRealValue = ulLastValue;
											float TotalValue = float(ulTotalValue);
											switch (metertype)
											{
											case MTYPE_ENERGY:
											case MTYPE_ENERGY_GENERATED:
												sprintf(szTmp, "%.3f", (TotalValue / divider)*1000.0f);	//from kWh -> Watt
												break;
											case MTYPE_GAS:
												sprintf(szTmp, "%.3f", TotalValue / divider);
												break;
											case MTYPE_WATER:
												sprintf(szTmp, "%.3f", TotalValue / divider);
												break;
											case MTYPE_COUNTER:
												sprintf(szTmp, "%.1f", TotalValue);
												break;
											default:
												strcpy(szTmp, "0");
												break;
											}
											root["result"][ii][method == 1 ? "eu" : "v"] = szTmp;
											ii++;
										}
										LastDateTime = actDateTimeHour;
										bHaveFirstValue = false;
									}
									if (!bHaveFirstValue)
									{
										ulFirstValue = ulLastValue;
										bHaveFirstValue = true;
									}
									if (!bHaveFirstRealValue)
									{
										bHaveFirstRealValue = true;
										ulFirstRealValue = ulLastValue;
									}
								}

								if (method == 1)
								{
									long long actValue = std::strtoll(sd[1].c_str(), nullptr, 10);

									root["result"][ii]["d"] = sd[2].substr(0, 16);

									float TotalValue = float(actValue);
									if ((dType == pTypeGeneral) && (dSubType == sTypeKwh))
										TotalValue /= 10.0f;
									switch (metertype)
									{
									case MTYPE_ENERGY:
									case MTYPE_ENERGY_GENERATED:
										sprintf(szTmp, "%.3f", (TotalValue / divider)*1000.0f);	//from kWh -> Watt
										break;
									case MTYPE_GAS:
										sprintf(szTmp, "%.2f", TotalValue / divider);
										break;
									case MTYPE_WATER:
										sprintf(szTmp, "%.3f", TotalValue / divider);
										break;
									case MTYPE_COUNTER:
										sprintf(szTmp, "%.1f", TotalValue);
										break;
									default:
										strcpy(szTmp, "0");
										break;
									}
									root["result"][ii]["v"] = szTmp;
									ii++;
								}
							}
						}
					}
					else
					{
						root["status"] = "OK";
						root["title"] = "Graph " + sensor + " " + srange;
						root["ValueQuantity"] = options["ValueQuantity"];
						root["ValueUnits"] = options["ValueUnits"];

						int ii = 0;

						bool bHaveFirstValue = false;
						bool bHaveFirstRealValue = false;
						float FirstValue = 0;
						unsigned long long ulFirstRealValue = 0;
						unsigned long long ulFirstValue = 0;
						unsigned long long ulLastValue = 0;

						std::string LastDateTime = "";
						time_t lastTime = 0;

						if (bIsManagedCounter) {
							result = m_sql.safe_query("SELECT Usage, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
							bHaveFirstValue = true;
							bHaveFirstRealValue = true;
						}
						else {
							result = m_sql.safe_query("SELECT Value, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
						}

						int method = 0;
						std::string sMethod = request::findValue(&req, "method");
						if (sMethod.size() > 0)
							method = atoi(sMethod.c_str());

						if (!result.empty())
						{
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								if (method == 0)
								{

									unsigned long long actValue = std::strtoull(sd[0].c_str(), nullptr, 10);

									std::string actDateTimeHour = sd[1].substr(0, 13);
									if (actDateTimeHour != LastDateTime)
									{
										if (bHaveFirstValue)
										{
											struct tm ntime;
											time_t atime;
											if (actDateTimeHour.size() == 10)
												actDateTimeHour += " 00";
											constructTime(atime, ntime,
												atoi(actDateTimeHour.substr(0, 4).c_str()),
												atoi(actDateTimeHour.substr(5, 2).c_str()),
												atoi(actDateTimeHour.substr(8, 2).c_str()),
												atoi(actDateTimeHour.substr(11, 2).c_str()) - 1,
												0, 0, -1);

											char szTime[50];
											sprintf(szTime, "%04d-%02d-%02d %02d:00", ntime.tm_year + 1900, ntime.tm_mon + 1, ntime.tm_mday, ntime.tm_hour);
											root["result"][ii]["d"] = szTime;

											
											float TotalValue = (actValue >= ulFirstValue) ? float(actValue - ulFirstValue) : actValue;

											{
												switch (metertype)
												{
												case MTYPE_ENERGY:
												case MTYPE_ENERGY_GENERATED:
													sprintf(szTmp, "%.3f", (TotalValue / divider)*1000.0f);	//from kWh -> Watt
													break;
												case MTYPE_GAS:
													sprintf(szTmp, "%.3f", TotalValue / divider);
													break;
												case MTYPE_WATER:
													sprintf(szTmp, "%.3f", TotalValue / divider);
													break;
												case MTYPE_COUNTER:
													sprintf(szTmp, "%.1f", TotalValue);
													break;
												default:
													strcpy(szTmp, "0");
													break;
												}
												root["result"][ii]["v"] = szTmp;
												ii++;
											}
										}
										if (!bIsManagedCounter) {
											ulFirstValue = actValue;
										}
										LastDateTime = actDateTimeHour;
									}

									if (!bHaveFirstValue)
									{
										ulFirstValue = actValue;
										bHaveFirstValue = true;
									}
									ulLastValue = actValue;
								}
								else
								{
									unsigned long long actValue = std::strtoull(sd[0].c_str(), nullptr, 10);

									std::string stime = sd[1];
									struct tm ntime;
									time_t atime;
									ParseSQLdatetime(atime, ntime, stime, -1);
									if (bHaveFirstRealValue)
									{
										long long curValue = actValue - ulLastValue;

										float tdiff = static_cast<float>(difftime(atime, lastTime));
										if (tdiff == 0)
											tdiff = 1;
										float tlaps = 3600.0f / tdiff;
										curValue *= int(tlaps);

										root["result"][ii]["d"] = sd[1].substr(0, 16);

										float TotalValue = float(curValue);
										{
											switch (metertype)
											{
											case MTYPE_ENERGY:
											case MTYPE_ENERGY_GENERATED:
												sprintf(szTmp, "%.3f", (TotalValue / divider)*1000.0f);	//from kWh -> Watt
												break;
											case MTYPE_GAS:
												sprintf(szTmp, "%.2f", TotalValue / divider);
												break;
											case MTYPE_WATER:
												sprintf(szTmp, "%.3f", TotalValue / divider);
												break;
											case MTYPE_COUNTER:
												sprintf(szTmp, "%.1f", TotalValue);
												break;
											default:
												strcpy(szTmp, "0");
												break;
											}
											root["result"][ii]["v"] = szTmp;
											ii++;
										}

									}
									else
										bHaveFirstRealValue = true;
									if (!bIsManagedCounter) {
										ulLastValue = actValue;
									}
									lastTime = atime;
								}
							}
						}
						if ((!bIsManagedCounter) && (bHaveFirstValue) && (method == 0))
						{
							root["result"][ii]["d"] = LastDateTime + ":00";

							unsigned long long ulTotalValue = ulLastValue - ulFirstValue;

							float TotalValue = float(ulTotalValue);

							{
								switch (metertype)
								{
								case MTYPE_ENERGY:
								case MTYPE_ENERGY_GENERATED:
									sprintf(szTmp, "%.3f", (TotalValue / divider)*1000.0f);	//from kWh -> Watt
									break;
								case MTYPE_GAS:
									sprintf(szTmp, "%.3f", TotalValue / divider);
									break;
								case MTYPE_WATER:
									sprintf(szTmp, "%.3f", TotalValue / divider);
									break;
								case MTYPE_COUNTER:
									sprintf(szTmp, "%.1f", TotalValue);
									break;
								default:
									strcpy(szTmp, "0");
									break;
								}
								root["result"][ii]["v"] = szTmp;
								ii++;
							}
						}
					}
				}
				else if (sensor == "uv") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;

					result = m_sql.safe_query("SELECT Level, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
					if (!result.empty())
					{
						int ii = 0;
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["result"][ii]["d"] = sd[1].substr(0, 16);
							root["result"][ii]["uvi"] = sd[0];
							ii++;
						}
					}
				}
				else if (sensor == "rain") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;

					int LastHour = -1;
					float LastTotalPreviousHour = -1;

					float LastValue = -1;
					std::string LastDate = "";

					result = m_sql.safe_query("SELECT Total, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
					if (!result.empty())
					{
						int ii = 0;
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;
							float ActTotal = static_cast<float>(atof(sd[0].c_str()));
							int Hour = atoi(sd[1].substr(11, 2).c_str());
							if (Hour != LastHour)
							{
								if (LastHour != -1)
								{
									int NextCalculatedHour = (LastHour + 1) % 24;
									if (Hour != NextCalculatedHour)
									{
										root["result"][ii]["d"] = LastDate;
										double mmval = ActTotal - LastValue;
										mmval *= AddjMulti;
										sprintf(szTmp, "%.1f", mmval);
										root["result"][ii]["mm"] = szTmp;
										ii++;
									}
									else
									{
										root["result"][ii]["d"] = sd[1].substr(0, 16);
										double mmval = ActTotal - LastTotalPreviousHour;
										mmval *= AddjMulti;
										sprintf(szTmp, "%.1f", mmval);
										root["result"][ii]["mm"] = szTmp;
										ii++;
									}
								}
								LastHour = Hour;
								LastTotalPreviousHour = ActTotal;
							}
							LastValue = ActTotal;
							LastDate = sd[1];
						}
					}
				}
				else if (sensor == "wind") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;

					result = m_sql.safe_query("SELECT Direction, Speed, Gust, Date FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
					if (!result.empty())
					{
						int ii = 0;
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["result"][ii]["d"] = sd[3].substr(0, 16);
							root["result"][ii]["di"] = sd[0];

							int intSpeed = atoi(sd[1].c_str());
							int intGust = atoi(sd[2].c_str());
							if (m_sql.m_windunit != WINDUNIT_Beaufort)
							{
								sprintf(szTmp, "%.1f", float(intSpeed) * m_sql.m_windscale);
								root["result"][ii]["sp"] = szTmp;
								sprintf(szTmp, "%.1f", float(intGust) * m_sql.m_windscale);
								root["result"][ii]["gu"] = szTmp;
							}
							else
							{
								float windspeedms = float(intSpeed)*0.1f;
								float windgustms = float(intGust)*0.1f;
								sprintf(szTmp, "%d", MStoBeaufort(windspeedms));
								root["result"][ii]["sp"] = szTmp;
								sprintf(szTmp, "%d", MStoBeaufort(windgustms));
								root["result"][ii]["gu"] = szTmp;
							}
							ii++;
						}
					}
				}
				else if (sensor == "winddir") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;

					result = m_sql.safe_query("SELECT Direction, Speed, Gust FROM %s WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date ASC", dbasetable.c_str(), idx);
					if (!result.empty())
					{
						std::map<int, int> _directions;
						int wdirtabletemp[17][8];
						std::string szLegendLabels[7];
						int ii = 0;

						int totalvalues = 0;
						int idir;
						for (idir = 0; idir < 360 + 1; idir++)
							_directions[idir] = 0;
						for (ii = 0; ii < 17; ii++)
						{
							for (int jj = 0; jj < 8; jj++)
							{
								wdirtabletemp[ii][jj] = 0;
							}
						}

						if (m_sql.m_windunit == WINDUNIT_MS)
						{
							szLegendLabels[0] = "&lt; 0.5 " + m_sql.m_windsign;
							szLegendLabels[1] = "0.5-2 " + m_sql.m_windsign;
							szLegendLabels[2] = "2-4 " + m_sql.m_windsign;
							szLegendLabels[3] = "4-6 " + m_sql.m_windsign;
							szLegendLabels[4] = "6-8 " + m_sql.m_windsign;
							szLegendLabels[5] = "8-10 " + m_sql.m_windsign;
							szLegendLabels[6] = "&gt; 10" + m_sql.m_windsign;
						}
						else if (m_sql.m_windunit == WINDUNIT_KMH)
						{
							szLegendLabels[0] = "&lt; 2 " + m_sql.m_windsign;
							szLegendLabels[1] = "2-4 " + m_sql.m_windsign;
							szLegendLabels[2] = "4-6 " + m_sql.m_windsign;
							szLegendLabels[3] = "6-10 " + m_sql.m_windsign;
							szLegendLabels[4] = "10-20 " + m_sql.m_windsign;
							szLegendLabels[5] = "20-36 " + m_sql.m_windsign;
							szLegendLabels[6] = "&gt; 36" + m_sql.m_windsign;
						}
						else if (m_sql.m_windunit == WINDUNIT_MPH)
						{
							szLegendLabels[0] = "&lt; 3 " + m_sql.m_windsign;
							szLegendLabels[1] = "3-7 " + m_sql.m_windsign;
							szLegendLabels[2] = "7-12 " + m_sql.m_windsign;
							szLegendLabels[3] = "12-18 " + m_sql.m_windsign;
							szLegendLabels[4] = "18-24 " + m_sql.m_windsign;
							szLegendLabels[5] = "24-46 " + m_sql.m_windsign;
							szLegendLabels[6] = "&gt; 46" + m_sql.m_windsign;
						}
						else if (m_sql.m_windunit == WINDUNIT_Knots)
						{
							szLegendLabels[0] = "&lt; 3 " + m_sql.m_windsign;
							szLegendLabels[1] = "3-7 " + m_sql.m_windsign;
							szLegendLabels[2] = "7-17 " + m_sql.m_windsign;
							szLegendLabels[3] = "17-27 " + m_sql.m_windsign;
							szLegendLabels[4] = "27-34 " + m_sql.m_windsign;
							szLegendLabels[5] = "34-41 " + m_sql.m_windsign;
							szLegendLabels[6] = "&gt; 41" + m_sql.m_windsign;
						}
						else if (m_sql.m_windunit == WINDUNIT_Beaufort)
						{
							szLegendLabels[0] = "&lt; 2 " + m_sql.m_windsign;
							szLegendLabels[1] = "2-4 " + m_sql.m_windsign;
							szLegendLabels[2] = "4-6 " + m_sql.m_windsign;
							szLegendLabels[3] = "6-8 " + m_sql.m_windsign;
							szLegendLabels[4] = "8-10 " + m_sql.m_windsign;
							szLegendLabels[5] = "10-12 " + m_sql.m_windsign;
							szLegendLabels[6] = "&gt; 12" + m_sql.m_windsign;
						}
						else {
							szLegendLabels[0] = "&lt; 0.5 " + m_sql.m_windsign;
							szLegendLabels[1] = "0.5-2 " + m_sql.m_windsign;
							szLegendLabels[2] = "2-4 " + m_sql.m_windsign;
							szLegendLabels[3] = "4-6 " + m_sql.m_windsign;
							szLegendLabels[4] = "6-8 " + m_sql.m_windsign;
							szLegendLabels[5] = "8-10 " + m_sql.m_windsign;
							szLegendLabels[6] = "&gt; 10" + m_sql.m_windsign;
						}


						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;
							float fdirection = static_cast<float>(atof(sd[0].c_str()));
							if (fdirection >= 360)
								fdirection = 0;
							int direction = int(fdirection);
							float speedOrg = static_cast<float>(atof(sd[1].c_str()));
							float gustOrg = static_cast<float>(atof(sd[2].c_str()));
							if ((gustOrg == 0) && (speedOrg != 0))
								gustOrg = speedOrg;
							if (gustOrg == 0)
								continue; //no direction if wind is still
							float speed = speedOrg * m_sql.m_windscale;
							float gust = gustOrg * m_sql.m_windscale;
							int bucket = int(fdirection / 22.5f);

							int speedpos = 0;

							if (m_sql.m_windunit == WINDUNIT_MS)
							{
								if (gust < 0.5f) speedpos = 0;
								else if (gust < 2.0f) speedpos = 1;
								else if (gust < 4.0f) speedpos = 2;
								else if (gust < 6.0f) speedpos = 3;
								else if (gust < 8.0f) speedpos = 4;
								else if (gust < 10.0f) speedpos = 5;
								else speedpos = 6;
							}
							else if (m_sql.m_windunit == WINDUNIT_KMH)
							{
								if (gust < 2.0f) speedpos = 0;
								else if (gust < 4.0f) speedpos = 1;
								else if (gust < 6.0f) speedpos = 2;
								else if (gust < 10.0f) speedpos = 3;
								else if (gust < 20.0f) speedpos = 4;
								else if (gust < 36.0f) speedpos = 5;
								else speedpos = 6;
							}
							else if (m_sql.m_windunit == WINDUNIT_MPH)
							{
								if (gust < 3.0f) speedpos = 0;
								else if (gust < 7.0f) speedpos = 1;
								else if (gust < 12.0f) speedpos = 2;
								else if (gust < 18.0f) speedpos = 3;
								else if (gust < 24.0f) speedpos = 4;
								else if (gust < 46.0f) speedpos = 5;
								else speedpos = 6;
							}
							else if (m_sql.m_windunit == WINDUNIT_Knots)
							{
								if (gust < 3.0f) speedpos = 0;
								else if (gust < 7.0f) speedpos = 1;
								else if (gust < 17.0f) speedpos = 2;
								else if (gust < 27.0f) speedpos = 3;
								else if (gust < 34.0f) speedpos = 4;
								else if (gust < 41.0f) speedpos = 5;
								else speedpos = 6;
							}
							else if (m_sql.m_windunit == WINDUNIT_Beaufort)
							{
								float gustms = gustOrg * 0.1f;
								int iBeaufort = MStoBeaufort(gustms);
								if (iBeaufort < 2) speedpos = 0;
								else if (iBeaufort < 4) speedpos = 1;
								else if (iBeaufort < 6) speedpos = 2;
								else if (iBeaufort < 8) speedpos = 3;
								else if (iBeaufort < 10) speedpos = 4;
								else if (iBeaufort < 12) speedpos = 5;
								else speedpos = 6;
							}
							else
							{
								if (gust < 0.5f) speedpos = 0;
								else if (gust < 2.0f) speedpos = 1;
								else if (gust < 4.0f) speedpos = 2;
								else if (gust < 6.0f) speedpos = 3;
								else if (gust < 8.0f) speedpos = 4;
								else if (gust < 10.0f) speedpos = 5;
								else speedpos = 6;
							}
							wdirtabletemp[bucket][speedpos]++;
							_directions[direction]++;
							totalvalues++;
						}

						for (int jj = 0; jj < 7; jj++)
						{
							root["result_speed"][jj]["label"] = szLegendLabels[jj];

							for (ii = 0; ii < 16; ii++)
							{
								float svalue = 0;
								if (totalvalues > 0)
								{
									svalue = (100.0f / totalvalues)*wdirtabletemp[ii][jj];
								}
								sprintf(szTmp, "%.2f", svalue);
								root["result_speed"][jj]["sp"][ii] = szTmp;
							}
						}
						ii = 0;
						for (idir = 0; idir < 360 + 1; idir++)
						{
							if (_directions[idir] != 0)
							{
								root["result"][ii]["dig"] = idir;
								float percentage = 0;
								if (totalvalues > 0)
								{
									percentage = (float(100.0 / float(totalvalues))*float(_directions[idir]));
								}
								sprintf(szTmp, "%.2f", percentage);
								root["result"][ii]["div"] = szTmp;
								ii++;
							}
						}
					}
				}

			}//day
			else if (srange == "week")
			{
				if (sensor == "rain") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;

					char szDateStart[40];
					char szDateEnd[40];
					sprintf(szDateEnd, "%04d-%02d-%02d", tm1.tm_year + 1900, tm1.tm_mon + 1, tm1.tm_mday);

					time_t weekbefore;
					struct tm tm2;
					getNoon(weekbefore, tm2, tm1.tm_year + 1900, tm1.tm_mon + 1, tm1.tm_mday - 7); // We only want the date
					sprintf(szDateStart, "%04d-%02d-%02d", tm2.tm_year + 1900, tm2.tm_mon + 1, tm2.tm_mday);

					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);
					int ii = 0;
					if (!result.empty())
					{
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["result"][ii]["d"] = sd[2].substr(0, 16);
							double mmval = atof(sd[0].c_str());
							mmval *= AddjMulti;
							sprintf(szTmp, "%.1f", mmval);
							root["result"][ii]["mm"] = szTmp;
							ii++;
						}
					}
					if (dSubType != sTypeRAINWU)
					{
						result = m_sql.safe_query(
							"SELECT MIN(Total), MAX(Total), MAX(Rate) FROM Rain WHERE (DeviceRowID=%" PRIu64 " AND Date>='%q')",
							idx, szDateEnd);
					}
					else
					{
						result = m_sql.safe_query(
							"SELECT Total, Total, Rate FROM Rain WHERE (DeviceRowID=%" PRIu64 " AND Date>='%q') ORDER BY ROWID DESC LIMIT 1",
							idx, szDateEnd);
					}
					if (!result.empty())
					{
						std::vector<std::string> sd = result[0];

						float total_min = static_cast<float>(atof(sd[0].c_str()));
						float total_max = static_cast<float>(atof(sd[1].c_str()));
						int rate = atoi(sd[2].c_str());

						double total_real = 0;
						if (dSubType != sTypeRAINWU)
						{
							total_real = total_max - total_min;
						}
						else
						{
							total_real = total_max;
						}
						total_real *= AddjMulti;
						sprintf(szTmp, "%.1f", total_real);
						root["result"][ii]["d"] = szDateEnd;
						root["result"][ii]["mm"] = szTmp;
						ii++;
					}
				}
				else if (sensor == "counter")
				{
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;
					root["ValueQuantity"] = options["ValueQuantity"];
					root["ValueUnits"] = options["ValueUnits"];

					char szDateStart[40];
					char szDateEnd[40];
					sprintf(szDateEnd, "%04d-%02d-%02d", tm1.tm_year + 1900, tm1.tm_mon + 1, tm1.tm_mday);

					time_t weekbefore;
					struct tm tm2;
					getNoon(weekbefore, tm2, tm1.tm_year + 1900, tm1.tm_mon + 1, tm1.tm_mday - 7); // We only want the date
					sprintf(szDateStart, "%04d-%02d-%02d", tm2.tm_year + 1900, tm2.tm_mon + 1, tm2.tm_mday);

					int ii = 0;
					if (dType == pTypeP1Power)
					{
						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);
						if (!result.empty())
						{
							bool bHaveDeliverd = false;
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;
								root["result"][ii]["d"] = sd[4].substr(0, 16);
								std::string szValueUsage1 = sd[0];
								std::string szValueDeliv1 = sd[1];
								std::string szValueUsage2 = sd[2];
								std::string szValueDeliv2 = sd[3];

								float fUsage1 = (float)(atof(szValueUsage1.c_str()));
								float fUsage2 = (float)(atof(szValueUsage2.c_str()));
								float fDeliv1 = (float)(atof(szValueDeliv1.c_str()));
								float fDeliv2 = (float)(atof(szValueDeliv2.c_str()));

								fDeliv1 = (fDeliv1 < 10) ? 0 : fDeliv1;
								fDeliv2 = (fDeliv2 < 10) ? 0 : fDeliv2;

								if ((fDeliv1 != 0) || (fDeliv2 != 0))
									bHaveDeliverd = true;
								sprintf(szTmp, "%.3f", fUsage1 / divider);
								root["result"][ii]["v"] = szTmp;
								sprintf(szTmp, "%.3f", fUsage2 / divider);
								root["result"][ii]["v2"] = szTmp;
								sprintf(szTmp, "%.3f", fDeliv1 / divider);
								root["result"][ii]["r1"] = szTmp;
								sprintf(szTmp, "%.3f", fDeliv2 / divider);
								root["result"][ii]["r2"] = szTmp;
								ii++;
							}
							if (bHaveDeliverd)
							{
								root["delivered"] = true;
							}
						}
					}
					else
					{
						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);
						if (!result.empty())
						{
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[1].substr(0, 16);
								std::string szValue = sd[0];
								switch (metertype)
								{
								case MTYPE_ENERGY:
								case MTYPE_ENERGY_GENERATED:
									sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
									szValue = szTmp;
									break;
								case MTYPE_GAS:
									sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
									szValue = szTmp;
									break;
								case MTYPE_WATER:
									sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
									szValue = szTmp;
									break;
								case MTYPE_COUNTER:
									break;
								default:
									szValue = "0";
									break;
								}
								root["result"][ii]["v"] = szValue;
								ii++;
							}
						}
					}
					if (dType == pTypeP1Power)
					{
						result = m_sql.safe_query(
							"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')",
							idx, szDateEnd);
						if (!result.empty())
						{
							std::vector<std::string> sd = result[0];

							unsigned long long total_min_usage_1 = std::strtoull(sd[0].c_str(), nullptr, 10);
							unsigned long long total_max_usage_1 = std::strtoull(sd[1].c_str(), nullptr, 10);
							unsigned long long total_min_usage_2 = std::strtoull(sd[4].c_str(), nullptr, 10);
							unsigned long long total_max_usage_2 = std::strtoull(sd[5].c_str(), nullptr, 10);
							unsigned long long total_real_usage_1, total_real_usage_2;
							unsigned long long total_min_deliv_1 = std::strtoull(sd[2].c_str(), nullptr, 10);
							unsigned long long total_max_deliv_1 = std::strtoull(sd[3].c_str(), nullptr, 10);
							unsigned long long total_min_deliv_2 = std::strtoull(sd[6].c_str(), nullptr, 10);
							unsigned long long total_max_deliv_2 = std::strtoull(sd[7].c_str(), nullptr, 10);
							unsigned long long total_real_deliv_1, total_real_deliv_2;

							bool bHaveDeliverd = false;

							total_real_usage_1 = total_max_usage_1 - total_min_usage_1;
							total_real_usage_2 = total_max_usage_2 - total_min_usage_2;

							total_real_deliv_1 = total_max_deliv_1 - total_min_deliv_1;
							total_real_deliv_2 = total_max_deliv_2 - total_min_deliv_2;
							if ((total_real_deliv_1 != 0) || (total_real_deliv_2 != 0))
								bHaveDeliverd = true;

							root["result"][ii]["d"] = szDateEnd;

							sprintf(szTmp, "%llu", total_real_usage_1);
							std::string szValue = szTmp;
							sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
							root["result"][ii]["v"] = szTmp;
							sprintf(szTmp, "%llu", total_real_usage_2);
							szValue = szTmp;
							sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
							root["result"][ii]["v2"] = szTmp;

							sprintf(szTmp, "%llu", total_real_deliv_1);
							szValue = szTmp;
							sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
							root["result"][ii]["r1"] = szTmp;
							sprintf(szTmp, "%llu", total_real_deliv_2);
							szValue = szTmp;
							sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
							root["result"][ii]["r2"] = szTmp;

							ii++;
							if (bHaveDeliverd)
							{
								root["delivered"] = true;
							}
						}
					}
					else if (!bIsManagedCounter)
					{
						result = m_sql.safe_query("SELECT MIN(Value), MAX(Value) FROM Meter WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
							idx, szDateEnd);
						if (!result.empty())
						{
							std::vector<std::string> sd = result[0];

							unsigned long long total_min = std::strtoull(sd[0].c_str(), nullptr, 10);
							unsigned long long total_max = std::strtoull(sd[1].c_str(), nullptr, 10);
							unsigned long long total_real;

							total_real = total_max - total_min;
							sprintf(szTmp, "%llu", total_real);
							std::string szValue = szTmp;
							switch (metertype)
							{
							case MTYPE_ENERGY:
							case MTYPE_ENERGY_GENERATED:
								sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
								szValue = szTmp;
								break;
							case MTYPE_GAS:
								sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
								szValue = szTmp;
								break;
							case MTYPE_WATER:
								sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
								szValue = szTmp;
								break;
							case MTYPE_COUNTER:
								break;
							default:
								szValue = "0";
								break;
							}

							root["result"][ii]["d"] = szDateEnd;
							root["result"][ii]["v"] = szValue;
							ii++;
						}
					}
				}
			}//week
			else if ((srange == "month") || (srange == "year"))
			{
				char szDateStart[40];
				char szDateEnd[40];
				char szDateStartPrev[40];
				char szDateEndPrev[40];

				std::string sactmonth = request::findValue(&req, "actmonth");
				std::string sactyear = request::findValue(&req, "actyear");

				int actMonth = atoi(sactmonth.c_str());
				int actYear = atoi(sactyear.c_str());

				if ((sactmonth != "") && (sactyear != ""))
				{
					sprintf(szDateStart, "%04d-%02d-%02d", actYear, actMonth, 1);
					sprintf(szDateStartPrev, "%04d-%02d-%02d", actYear - 1, actMonth, 1);
					actMonth++;
					if (actMonth == 13)
					{
						actMonth = 1;
						actYear++;
					}
					sprintf(szDateEnd, "%04d-%02d-%02d", actYear, actMonth, 1);
					sprintf(szDateEndPrev, "%04d-%02d-%02d", actYear - 1, actMonth, 1);
				}
				else if (sactyear != "")
				{
					sprintf(szDateStart, "%04d-%02d-%02d", actYear, 1, 1);
					sprintf(szDateStartPrev, "%04d-%02d-%02d", actYear - 1, 1, 1);
					actYear++;
					sprintf(szDateEnd, "%04d-%02d-%02d", actYear, 1, 1);
					sprintf(szDateEndPrev, "%04d-%02d-%02d", actYear - 1, 1, 1);
				}
				else
				{
					sprintf(szDateEnd, "%04d-%02d-%02d", tm1.tm_year + 1900, tm1.tm_mon + 1, tm1.tm_mday);
					sprintf(szDateEndPrev, "%04d-%02d-%02d", tm1.tm_year + 1900 - 1, tm1.tm_mon + 1, tm1.tm_mday);

					struct tm tm2;
					if (srange == "month")
					{
						time_t monthbefore;
						getNoon(monthbefore, tm2, tm1.tm_year + 1900, tm1.tm_mon, tm1.tm_mday);
					}
					else
					{
						time_t yearbefore;
						getNoon(yearbefore, tm2, tm1.tm_year + 1900 - 1, tm1.tm_mon + 1, tm1.tm_mday);
					}

					sprintf(szDateStart, "%04d-%02d-%02d", tm2.tm_year + 1900, tm2.tm_mon + 1, tm2.tm_mday);
					sprintf(szDateStartPrev, "%04d-%02d-%02d", tm2.tm_year + 1900 - 1, tm2.tm_mon + 1, tm2.tm_mday);
				}

				if (sensor == "temp") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;

					result = m_sql.safe_query(
						"SELECT Temp_Min, Temp_Max, Chill_Min, Chill_Max,"
						" Humidity, Barometer, Temp_Avg, Date, SetPoint_Min,"
						" SetPoint_Max, SetPoint_Avg "
						"FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q'"
						" AND Date<='%q') ORDER BY Date ASC",
						dbasetable.c_str(), idx, szDateStart, szDateEnd);
					int ii = 0;
					if (!result.empty())
					{
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["result"][ii]["d"] = sd[7].substr(0, 16);

							if (
								(dType == pTypeRego6XXTemp) || (dType == pTypeTEMP) || (dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO) || (dType == pTypeTEMP_BARO) || (dType == pTypeWIND) || (dType == pTypeThermostat1) || (dType == pTypeRadiator1) ||
								((dType == pTypeRFXSensor) && (dSubType == sTypeRFXSensorTemp)) ||
								((dType == pTypeUV) && (dSubType == sTypeUV3)) ||
								((dType == pTypeGeneral) && (dSubType == sTypeSystemTemp)) ||
								((dType == pTypeThermostat) && (dSubType == sTypeThermSetpoint)) ||
								(dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater) ||
								((dType == pTypeGeneral) && (dSubType == sTypeBaro))
								)
							{
								bool bOK = true;
								if (dType == pTypeWIND)
								{
									bOK = ((dSubType != sTypeWINDNoTemp) && (dSubType != sTypeWINDNoTempNoChill));
								}
								if (bOK)
								{
									double te = ConvertTemperature(atof(sd[1].c_str()), tempsign);
									double tm = ConvertTemperature(atof(sd[0].c_str()), tempsign);
									double ta = ConvertTemperature(atof(sd[6].c_str()), tempsign);
									root["result"][ii]["te"] = te;
									root["result"][ii]["tm"] = tm;
									root["result"][ii]["ta"] = ta;
								}
							}
							if (
								((dType == pTypeWIND) && (dSubType == sTypeWIND4)) ||
								((dType == pTypeWIND) && (dSubType == sTypeWINDNoTemp))
								)
							{
								double ch = ConvertTemperature(atof(sd[3].c_str()), tempsign);
								double cm = ConvertTemperature(atof(sd[2].c_str()), tempsign);
								root["result"][ii]["ch"] = ch;
								root["result"][ii]["cm"] = cm;
							}
							if ((dType == pTypeHUM) || (dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO))
							{
								root["result"][ii]["hu"] = sd[4];
							}
							if (
								(dType == pTypeTEMP_HUM_BARO) ||
								(dType == pTypeTEMP_BARO) ||
								((dType == pTypeGeneral) && (dSubType == sTypeBaro))
								)
							{
								if (dType == pTypeTEMP_HUM_BARO)
								{
									if (dSubType == sTypeTHBFloat)
									{
										sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
										root["result"][ii]["ba"] = szTmp;
									}
									else
										root["result"][ii]["ba"] = sd[5];
								}
								else if (dType == pTypeTEMP_BARO)
								{
									sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
									root["result"][ii]["ba"] = szTmp;
								}
								else if ((dType == pTypeGeneral) && (dSubType == sTypeBaro))
								{
									sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
									root["result"][ii]["ba"] = szTmp;
								}
							}
							if ((dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater))
							{
								double sm = ConvertTemperature(atof(sd[8].c_str()), tempsign);
								double sx = ConvertTemperature(atof(sd[9].c_str()), tempsign);
								double se = ConvertTemperature(atof(sd[10].c_str()), tempsign);
								root["result"][ii]["sm"] = sm;
								root["result"][ii]["se"] = se;
								root["result"][ii]["sx"] = sx;
							}
							ii++;
						}
					}
					result = m_sql.safe_query(
						"SELECT MIN(Temperature), MAX(Temperature),"
						" MIN(Chill), MAX(Chill), AVG(Humidity),"
						" AVG(Barometer), AVG(Temperature), MIN(SetPoint),"
						" MAX(SetPoint), AVG(SetPoint) "
						"FROM Temperature WHERE (DeviceRowID==%" PRIu64 ""
						" AND Date>='%q')",
						idx, szDateEnd);
					if (!result.empty())
					{
						std::vector<std::string> sd = result[0];

						root["result"][ii]["d"] = szDateEnd;
						if (
							((dType == pTypeRego6XXTemp) || (dType == pTypeTEMP) || (dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO) || (dType == pTypeTEMP_BARO) || (dType == pTypeWIND) || (dType == pTypeThermostat1) || (dType == pTypeRadiator1)) ||
							((dType == pTypeUV) && (dSubType == sTypeUV3)) ||
							((dType == pTypeWIND) && (dSubType == sTypeWIND4)) ||
							(dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater)
							)
						{
							double te = ConvertTemperature(atof(sd[1].c_str()), tempsign);
							double tm = ConvertTemperature(atof(sd[0].c_str()), tempsign);
							double ta = ConvertTemperature(atof(sd[6].c_str()), tempsign);

							root["result"][ii]["te"] = te;
							root["result"][ii]["tm"] = tm;
							root["result"][ii]["ta"] = ta;
						}
						if (
							((dType == pTypeWIND) && (dSubType == sTypeWIND4)) ||
							((dType == pTypeWIND) && (dSubType == sTypeWINDNoTemp))
							)
						{
							double ch = ConvertTemperature(atof(sd[3].c_str()), tempsign);
							double cm = ConvertTemperature(atof(sd[2].c_str()), tempsign);
							root["result"][ii]["ch"] = ch;
							root["result"][ii]["cm"] = cm;
						}
						if ((dType == pTypeHUM) || (dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO))
						{
							root["result"][ii]["hu"] = sd[4];
						}
						if (
							(dType == pTypeTEMP_HUM_BARO) ||
							(dType == pTypeTEMP_BARO) ||
							((dType == pTypeGeneral) && (dSubType == sTypeBaro))
							)
						{
							if (dType == pTypeTEMP_HUM_BARO)
							{
								if (dSubType == sTypeTHBFloat)
								{
									sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
									root["result"][ii]["ba"] = szTmp;
								}
								else
									root["result"][ii]["ba"] = sd[5];
							}
							else if (dType == pTypeTEMP_BARO)
							{
								sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
								root["result"][ii]["ba"] = szTmp;
							}
							else if ((dType == pTypeGeneral) && (dSubType == sTypeBaro))
							{
								sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
								root["result"][ii]["ba"] = szTmp;
							}
						}
						if ((dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater))
						{
							double sx = ConvertTemperature(atof(sd[8].c_str()), tempsign);
							double sm = ConvertTemperature(atof(sd[7].c_str()), tempsign);
							double se = ConvertTemperature(atof(sd[9].c_str()), tempsign);
							root["result"][ii]["se"] = se;
							root["result"][ii]["sm"] = sm;
							root["result"][ii]["sx"] = sx;
						}
						ii++;
					}
					result = m_sql.safe_query(
						"SELECT Temp_Min, Temp_Max, Chill_Min, Chill_Max,"
						" Humidity, Barometer, Temp_Avg, Date, SetPoint_Min,"
						" SetPoint_Max, SetPoint_Avg "
						"FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q'"
						" AND Date<='%q') ORDER BY Date ASC",
						dbasetable.c_str(), idx, szDateStartPrev, szDateEndPrev);
					if (!result.empty())
					{
						iPrev = 0;
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["resultprev"][iPrev]["d"] = sd[7].substr(0, 16);

							if (
								(dType == pTypeRego6XXTemp) || (dType == pTypeTEMP) || (dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO) || (dType == pTypeTEMP_BARO) || (dType == pTypeWIND) || (dType == pTypeThermostat1) || (dType == pTypeRadiator1) ||
								((dType == pTypeRFXSensor) && (dSubType == sTypeRFXSensorTemp)) ||
								((dType == pTypeUV) && (dSubType == sTypeUV3)) ||
								((dType == pTypeGeneral) && (dSubType == sTypeSystemTemp)) ||
								((dType == pTypeThermostat) && (dSubType == sTypeThermSetpoint)) ||
								(dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater)
								)
							{
								bool bOK = true;
								if (dType == pTypeWIND)
								{
									bOK = ((dSubType == sTypeWIND4) || (dSubType == sTypeWINDNoTemp));
								}
								if (bOK)
								{
									double te = ConvertTemperature(atof(sd[1].c_str()), tempsign);
									double tm = ConvertTemperature(atof(sd[0].c_str()), tempsign);
									double ta = ConvertTemperature(atof(sd[6].c_str()), tempsign);
									root["resultprev"][iPrev]["te"] = te;
									root["resultprev"][iPrev]["tm"] = tm;
									root["resultprev"][iPrev]["ta"] = ta;
								}
							}
							if (
								((dType == pTypeWIND) && (dSubType == sTypeWIND4)) ||
								((dType == pTypeWIND) && (dSubType == sTypeWINDNoTemp))
								)
							{
								double ch = ConvertTemperature(atof(sd[3].c_str()), tempsign);
								double cm = ConvertTemperature(atof(sd[2].c_str()), tempsign);
								root["resultprev"][iPrev]["ch"] = ch;
								root["resultprev"][iPrev]["cm"] = cm;
							}
							if ((dType == pTypeHUM) || (dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO))
							{
								root["resultprev"][iPrev]["hu"] = sd[4];
							}
							if (
								(dType == pTypeTEMP_HUM_BARO) ||
								(dType == pTypeTEMP_BARO) ||
								((dType == pTypeGeneral) && (dSubType == sTypeBaro))
								)
							{
								if (dType == pTypeTEMP_HUM_BARO)
								{
									if (dSubType == sTypeTHBFloat)
									{
										sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
										root["resultprev"][iPrev]["ba"] = szTmp;
									}
									else
										root["resultprev"][iPrev]["ba"] = sd[5];
								}
								else if (dType == pTypeTEMP_BARO)
								{
									sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
									root["resultprev"][iPrev]["ba"] = szTmp;
								}
								else if ((dType == pTypeGeneral) && (dSubType == sTypeBaro))
								{
									sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
									root["resultprev"][iPrev]["ba"] = szTmp;
								}
							}
							if ((dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater))
							{
								double sx = ConvertTemperature(atof(sd[8].c_str()), tempsign);
								double sm = ConvertTemperature(atof(sd[7].c_str()), tempsign);
								double se = ConvertTemperature(atof(sd[9].c_str()), tempsign);
								root["resultprev"][iPrev]["se"] = se;
								root["resultprev"][iPrev]["sm"] = sm;
								root["resultprev"][iPrev]["sx"] = sx;
							}
							iPrev++;
						}
					}
				}
				else if (sensor == "Percentage") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;

					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);
					int ii = 0;
					if (!result.empty())
					{
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["result"][ii]["d"] = sd[3].substr(0, 16);
							root["result"][ii]["v_min"] = sd[0];
							root["result"][ii]["v_max"] = sd[1];
							root["result"][ii]["v_avg"] = sd[2];
							ii++;
						}
					}
					result = m_sql.safe_query(
						"SELECT MIN(Percentage), MAX(Percentage), AVG(Percentage) FROM Percentage WHERE (DeviceRowID=%" PRIu64 " AND Date>='%q')",
						idx, szDateEnd);
					if (!result.empty())
					{
						std::vector<std::string> sd = result[0];
						root["result"][ii]["d"] = szDateEnd;
						root["result"][ii]["v_min"] = sd[0];
						root["result"][ii]["v_max"] = sd[1];
						root["result"][ii]["v_avg"] = sd[2];
						ii++;
					}

				}
				else if (sensor == "fan") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;

					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);
					int ii = 0;
					if (!result.empty())
					{
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["result"][ii]["d"] = sd[2].substr(0, 16);
							root["result"][ii]["v_max"] = sd[1];
							root["result"][ii]["v_min"] = sd[0];
							ii++;
						}
					}
					result = m_sql.safe_query("SELECT MIN(Speed), MAX(Speed) FROM Fan WHERE (DeviceRowID=%" PRIu64 " AND Date>='%q')",
						idx, szDateEnd);
					if (!result.empty())
					{
						std::vector<std::string> sd = result[0];
						root["result"][ii]["d"] = szDateEnd;
						root["result"][ii]["v_max"] = sd[1];
						root["result"][ii]["v_min"] = sd[0];
						ii++;
					}

				}
				else if (sensor == "uv") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;

					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);
					int ii = 0;
					if (!result.empty())
					{
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["result"][ii]["d"] = sd[1].substr(0, 16);
							root["result"][ii]["uvi"] = sd[0];
							ii++;
						}
					}
					result = m_sql.safe_query(
						"SELECT MAX(Level) FROM UV WHERE (DeviceRowID=%" PRIu64 " AND Date>='%q')",
						idx, szDateEnd);
					if (!result.empty())
					{
						std::vector<std::string> sd = result[0];

						root["result"][ii]["d"] = szDateEnd;
						root["result"][ii]["uvi"] = sd[0];
						ii++;
					}
					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);
					if (!result.empty())
					{
						iPrev = 0;
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["resultprev"][iPrev]["d"] = sd[1].substr(0, 16);
							root["resultprev"][iPrev]["uvi"] = sd[0];
							iPrev++;
						}
					}
				}
				else if (sensor == "rain") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;

					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);
					int ii = 0;
					if (!result.empty())
					{
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["result"][ii]["d"] = sd[2].substr(0, 16);
							double mmval = atof(sd[0].c_str());
							mmval *= AddjMulti;
							sprintf(szTmp, "%.1f", mmval);
							root["result"][ii]["mm"] = szTmp;
							ii++;
						}
					}
					if (dSubType != sTypeRAINWU)
					{
						result = m_sql.safe_query(
							"SELECT MIN(Total), MAX(Total), MAX(Rate) FROM Rain WHERE (DeviceRowID=%" PRIu64 " AND Date>='%q')",
							idx, szDateEnd);
					}
					else
					{
						result = m_sql.safe_query(
							"SELECT Total, Total, Rate FROM Rain WHERE (DeviceRowID=%" PRIu64 " AND Date>='%q') ORDER BY ROWID DESC LIMIT 1",
							idx, szDateEnd);
					}
					if (!result.empty())
					{
						std::vector<std::string> sd = result[0];

						float total_min = static_cast<float>(atof(sd[0].c_str()));
						float total_max = static_cast<float>(atof(sd[1].c_str()));
						int rate = atoi(sd[2].c_str());

						double total_real = 0;
						if (dSubType != sTypeRAINWU)
						{
							total_real = total_max - total_min;
						}
						else
						{
							total_real = total_max;
						}
						total_real *= AddjMulti;
						sprintf(szTmp, "%.1f", total_real);
						root["result"][ii]["d"] = szDateEnd;
						root["result"][ii]["mm"] = szTmp;
						ii++;
					}
					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, szDateStartPrev, szDateEndPrev);
					if (!result.empty())
					{
						iPrev = 0;
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["resultprev"][iPrev]["d"] = sd[2].substr(0, 16);
							double mmval = atof(sd[0].c_str());
							mmval *= AddjMulti;
							sprintf(szTmp, "%.1f", mmval);
							root["resultprev"][iPrev]["mm"] = szTmp;
							iPrev++;
						}
					}
				}
				else if (sensor == "counter") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;
					root["ValueQuantity"] = options["ValueQuantity"];
					root["ValueUnits"] = options["ValueUnits"];

					int nValue = 0;
					std::string sValue = "";

					result = m_sql.safe_query("SELECT nValue, sValue FROM DeviceStatus WHERE (ID==%" PRIu64 ")",
						idx);
					if (!result.empty())
					{
						std::vector<std::string> sd = result[0];
						nValue = atoi(sd[0].c_str());
						sValue = sd[1];
					}

					int ii = 0;
					iPrev = 0;
					if (dType == pTypeP1Power)
					{
						result = m_sql.safe_query(
							"SELECT Value1,Value2,Value5,Value6, Date,"
							" Counter1, Counter2, Counter3, Counter4 "
							"FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q'"
							" AND Date<='%q') ORDER BY Date ASC",
							dbasetable.c_str(), idx, szDateStart, szDateEnd);
						if (!result.empty())
						{
							bool bHaveDeliverd = false;
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[4].substr(0, 16);

								double counter_1 = atof(sd[5].c_str());
								double counter_2 = atof(sd[6].c_str());
								double counter_3 = atof(sd[7].c_str());
								double counter_4 = atof(sd[8].c_str());

								std::string szUsage1 = sd[0];
								std::string szDeliv1 = sd[1];
								std::string szUsage2 = sd[2];
								std::string szDeliv2 = sd[3];

								float fUsage_1 = static_cast<float>(atof(szUsage1.c_str()));
								float fUsage_2 = static_cast<float>(atof(szUsage2.c_str()));
								float fDeliv_1 = static_cast<float>(atof(szDeliv1.c_str()));
								float fDeliv_2 = static_cast<float>(atof(szDeliv2.c_str()));

								fDeliv_1 = (fDeliv_1 < 10) ? 0 : fDeliv_1;
								fDeliv_2 = (fDeliv_2 < 10) ? 0 : fDeliv_2;

								if ((fDeliv_1 != 0) || (fDeliv_2 != 0))
									bHaveDeliverd = true;
								sprintf(szTmp, "%.3f", fUsage_1 / divider);
								root["result"][ii]["v"] = szTmp;
								sprintf(szTmp, "%.3f", fUsage_2 / divider);
								root["result"][ii]["v2"] = szTmp;
								sprintf(szTmp, "%.3f", fDeliv_1 / divider);
								root["result"][ii]["r1"] = szTmp;
								sprintf(szTmp, "%.3f", fDeliv_2 / divider);
								root["result"][ii]["r2"] = szTmp;

								if (counter_1 != 0)
								{
									sprintf(szTmp, "%.3f", (counter_1 - fUsage_1) / divider);
								}
								else
								{
									strcpy(szTmp, "0");
								}
								root["result"][ii]["c1"] = szTmp;

								if (counter_2 != 0)
								{
									sprintf(szTmp, "%.3f", (counter_2 - fDeliv_1) / divider);
								}
								else
								{
									strcpy(szTmp, "0");
								}
								root["result"][ii]["c2"] = szTmp;

								if (counter_3 != 0)
								{
									sprintf(szTmp, "%.3f", (counter_3 - fUsage_2) / divider);
								}
								else
								{
									strcpy(szTmp, "0");
								}
								root["result"][ii]["c3"] = szTmp;

								if (counter_4 != 0)
								{
									sprintf(szTmp, "%.3f", (counter_4 - fDeliv_2) / divider);
								}
								else
								{
									strcpy(szTmp, "0");
								}
								root["result"][ii]["c4"] = szTmp;

								ii++;
							}
							if (bHaveDeliverd)
							{
								root["delivered"] = true;
							}
						}
						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, szDateStartPrev, szDateEndPrev);
						if (!result.empty())
						{
							bool bHaveDeliverd = false;
							iPrev = 0;
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["resultprev"][iPrev]["d"] = sd[4].substr(0, 16);

								std::string szUsage1 = sd[0];
								std::string szDeliv1 = sd[1];
								std::string szUsage2 = sd[2];
								std::string szDeliv2 = sd[3];

								float fUsage_1 = static_cast<float>(atof(szUsage1.c_str()));
								float fUsage_2 = static_cast<float>(atof(szUsage2.c_str()));
								float fDeliv_1 = static_cast<float>(atof(szDeliv1.c_str()));
								float fDeliv_2 = static_cast<float>(atof(szDeliv2.c_str()));

								if ((fDeliv_1 != 0) || (fDeliv_2 != 0))
									bHaveDeliverd = true;
								sprintf(szTmp, "%.3f", fUsage_1 / divider);
								root["resultprev"][iPrev]["v"] = szTmp;
								sprintf(szTmp, "%.3f", fUsage_2 / divider);
								root["resultprev"][iPrev]["v2"] = szTmp;
								sprintf(szTmp, "%.3f", fDeliv_1 / divider);
								root["resultprev"][iPrev]["r1"] = szTmp;
								sprintf(szTmp, "%.3f", fDeliv_2 / divider);
								root["resultprev"][iPrev]["r2"] = szTmp;
								iPrev++;
							}
							if (bHaveDeliverd)
							{
								root["delivered"] = true;
							}
						}
					}
					else if (dType == pTypeAirQuality)
					{//month/year
						root["status"] = "OK";
						root["title"] = "Graph " + sensor + " " + srange;

						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);
						if (!result.empty())
						{
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[3].substr(0, 16);
								root["result"][ii]["co2_min"] = sd[0];
								root["result"][ii]["co2_max"] = sd[1];
								root["result"][ii]["co2_avg"] = sd[2];
								ii++;
							}
						}
						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);
						if (!result.empty())
						{
							iPrev = 0;
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["resultprev"][iPrev]["d"] = sd[1].substr(0, 16);
								root["resultprev"][iPrev]["co2_max"] = sd[0];
								iPrev++;
							}
						}
					}
					else if (
						((dType == pTypeGeneral) && ((dSubType == sTypeSoilMoisture) || (dSubType == sTypeLeafWetness))) ||
						((dType == pTypeRFXSensor) && ((dSubType == sTypeRFXSensorAD) || (dSubType == sTypeRFXSensorVolt)))
						)
					{//month/year
						root["status"] = "OK";
						root["title"] = "Graph " + sensor + " " + srange;

						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);
						if (!result.empty())
						{
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[2].substr(0, 16);
								root["result"][ii]["v_min"] = sd[0];
								root["result"][ii]["v_max"] = sd[1];
								ii++;
							}
						}
					}
					else if (
						((dType == pTypeGeneral) && (dSubType == sTypeVisibility)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeDistance)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeSolarRadiation)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeVoltage)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeCurrent)) ||
						((dType == pTypeGeneral) && (dSubType == sTypePressure)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeSoundLevel))
						)
					{//month/year
						root["status"] = "OK";
						root["title"] = "Graph " + sensor + " " + srange;

						float vdiv = 10.0f;
						if (
							((dType == pTypeGeneral) && (dSubType == sTypeVoltage)) ||
							((dType == pTypeGeneral) && (dSubType == sTypeCurrent))
							)
						{
							vdiv = 1000.0f;
						}

						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);
						if (!result.empty())
						{
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								float fValue1 = float(atof(sd[0].c_str())) / vdiv;
								float fValue2 = float(atof(sd[1].c_str())) / vdiv;
								float fValue3 = float(atof(sd[2].c_str())) / vdiv;
								root["result"][ii]["d"] = sd[3].substr(0, 16);

								if (metertype == 1)
								{
									fValue1 *= 0.6214f;
									fValue2 *= 0.6214f;
								}
								if (
									((dType == pTypeGeneral) && (dSubType == sTypeVoltage)) ||
									((dType == pTypeGeneral) && (dSubType == sTypeCurrent))
									)
								{
									sprintf(szTmp, "%.3f", fValue1);
									root["result"][ii]["v_min"] = szTmp;
									sprintf(szTmp, "%.3f", fValue2);
									root["result"][ii]["v_max"] = szTmp;
									if (fValue3 != 0)
									{
										sprintf(szTmp, "%.3f", fValue3);
										root["result"][ii]["v_avg"] = szTmp;
									}
								}
								else
								{
									sprintf(szTmp, "%.1f", fValue1);
									root["result"][ii]["v_min"] = szTmp;
									sprintf(szTmp, "%.1f", fValue2);
									root["result"][ii]["v_max"] = szTmp;
									if (fValue3 != 0)
									{
										sprintf(szTmp, "%.1f", fValue3);
										root["result"][ii]["v_avg"] = szTmp;
									}
								}
								ii++;
							}
						}
					}
					else if (dType == pTypeLux)
					{//month/year
						root["status"] = "OK";
						root["title"] = "Graph " + sensor + " " + srange;

						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);
						if (!result.empty())
						{
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[3].substr(0, 16);
								root["result"][ii]["lux_min"] = sd[0];
								root["result"][ii]["lux_max"] = sd[1];
								root["result"][ii]["lux_avg"] = sd[2];
								ii++;
							}
						}
					}
					else if (dType == pTypeWEIGHT)
					{//month/year
						root["status"] = "OK";
						root["title"] = "Graph " + sensor + " " + srange;

						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);
						if (!result.empty())
						{
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[2].substr(0, 16);
								sprintf(szTmp, "%.1f", m_sql.m_weightscale * atof(sd[0].c_str()) / 10.0f);
								root["result"][ii]["v_min"] = szTmp;
								sprintf(szTmp, "%.1f", m_sql.m_weightscale * atof(sd[1].c_str()) / 10.0f);
								root["result"][ii]["v_max"] = szTmp;
								ii++;
							}
						}
					}
					else if (dType == pTypeUsage)
					{//month/year
						root["status"] = "OK";
						root["title"] = "Graph " + sensor + " " + srange;

						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);
						if (!result.empty())
						{
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[2].substr(0, 16);
								root["result"][ii]["u_min"] = atof(sd[0].c_str()) / 10.0f;
								root["result"][ii]["u_max"] = atof(sd[1].c_str()) / 10.0f;
								ii++;
							}
						}
					}
					else if (dType == pTypeCURRENT)
					{
						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);
						if (!result.empty())
						{
							int displaytype = 0;
							int voltage = 230;
							m_sql.GetPreferencesVar("CM113DisplayType", displaytype);
							m_sql.GetPreferencesVar("ElectricVoltage", voltage);

							root["displaytype"] = displaytype;

							bool bHaveL1 = false;
							bool bHaveL2 = false;
							bool bHaveL3 = false;
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[6].substr(0, 16);

								float fval1 = static_cast<float>(atof(sd[0].c_str()) / 10.0f);
								float fval2 = static_cast<float>(atof(sd[1].c_str()) / 10.0f);
								float fval3 = static_cast<float>(atof(sd[2].c_str()) / 10.0f);
								float fval4 = static_cast<float>(atof(sd[3].c_str()) / 10.0f);
								float fval5 = static_cast<float>(atof(sd[4].c_str()) / 10.0f);
								float fval6 = static_cast<float>(atof(sd[5].c_str()) / 10.0f);

								if ((fval1 != 0) || (fval2 != 0))
									bHaveL1 = true;
								if ((fval3 != 0) || (fval4 != 0))
									bHaveL2 = true;
								if ((fval5 != 0) || (fval6 != 0))
									bHaveL3 = true;

								if (displaytype == 0)
								{
									sprintf(szTmp, "%.1f", fval1);
									root["result"][ii]["v1"] = szTmp;
									sprintf(szTmp, "%.1f", fval2);
									root["result"][ii]["v2"] = szTmp;
									sprintf(szTmp, "%.1f", fval3);
									root["result"][ii]["v3"] = szTmp;
									sprintf(szTmp, "%.1f", fval4);
									root["result"][ii]["v4"] = szTmp;
									sprintf(szTmp, "%.1f", fval5);
									root["result"][ii]["v5"] = szTmp;
									sprintf(szTmp, "%.1f", fval6);
									root["result"][ii]["v6"] = szTmp;
								}
								else
								{
									sprintf(szTmp, "%d", int(fval1*voltage));
									root["result"][ii]["v1"] = szTmp;
									sprintf(szTmp, "%d", int(fval2*voltage));
									root["result"][ii]["v2"] = szTmp;
									sprintf(szTmp, "%d", int(fval3*voltage));
									root["result"][ii]["v3"] = szTmp;
									sprintf(szTmp, "%d", int(fval4*voltage));
									root["result"][ii]["v4"] = szTmp;
									sprintf(szTmp, "%d", int(fval5*voltage));
									root["result"][ii]["v5"] = szTmp;
									sprintf(szTmp, "%d", int(fval6*voltage));
									root["result"][ii]["v6"] = szTmp;
								}

								ii++;
							}
							if (
								(!bHaveL1) &&
								(!bHaveL2) &&
								(!bHaveL3)
								) {
								root["haveL1"] = true; //show at least something
							}
							else {
								if (bHaveL1)
									root["haveL1"] = true;
								if (bHaveL2)
									root["haveL2"] = true;
								if (bHaveL3)
									root["haveL3"] = true;
							}
						}
					}
					else if (dType == pTypeCURRENTENERGY)
					{
						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);
						if (!result.empty())
						{
							int displaytype = 0;
							int voltage = 230;
							m_sql.GetPreferencesVar("CM113DisplayType", displaytype);
							m_sql.GetPreferencesVar("ElectricVoltage", voltage);

							root["displaytype"] = displaytype;

							bool bHaveL1 = false;
							bool bHaveL2 = false;
							bool bHaveL3 = false;
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[6].substr(0, 16);

								float fval1 = static_cast<float>(atof(sd[0].c_str()) / 10.0f);
								float fval2 = static_cast<float>(atof(sd[1].c_str()) / 10.0f);
								float fval3 = static_cast<float>(atof(sd[2].c_str()) / 10.0f);
								float fval4 = static_cast<float>(atof(sd[3].c_str()) / 10.0f);
								float fval5 = static_cast<float>(atof(sd[4].c_str()) / 10.0f);
								float fval6 = static_cast<float>(atof(sd[5].c_str()) / 10.0f);

								if ((fval1 != 0) || (fval2 != 0))
									bHaveL1 = true;
								if ((fval3 != 0) || (fval4 != 0))
									bHaveL2 = true;
								if ((fval5 != 0) || (fval6 != 0))
									bHaveL3 = true;

								if (displaytype == 0)
								{
									sprintf(szTmp, "%.1f", fval1);
									root["result"][ii]["v1"] = szTmp;
									sprintf(szTmp, "%.1f", fval2);
									root["result"][ii]["v2"] = szTmp;
									sprintf(szTmp, "%.1f", fval3);
									root["result"][ii]["v3"] = szTmp;
									sprintf(szTmp, "%.1f", fval4);
									root["result"][ii]["v4"] = szTmp;
									sprintf(szTmp, "%.1f", fval5);
									root["result"][ii]["v5"] = szTmp;
									sprintf(szTmp, "%.1f", fval6);
									root["result"][ii]["v6"] = szTmp;
								}
								else
								{
									sprintf(szTmp, "%d", int(fval1*voltage));
									root["result"][ii]["v1"] = szTmp;
									sprintf(szTmp, "%d", int(fval2*voltage));
									root["result"][ii]["v2"] = szTmp;
									sprintf(szTmp, "%d", int(fval3*voltage));
									root["result"][ii]["v3"] = szTmp;
									sprintf(szTmp, "%d", int(fval4*voltage));
									root["result"][ii]["v4"] = szTmp;
									sprintf(szTmp, "%d", int(fval5*voltage));
									root["result"][ii]["v5"] = szTmp;
									sprintf(szTmp, "%d", int(fval6*voltage));
									root["result"][ii]["v6"] = szTmp;
								}

								ii++;
							}
							if (
								(!bHaveL1) &&
								(!bHaveL2) &&
								(!bHaveL3)
								) {
								root["haveL1"] = true; //show at least something
							}
							else {
								if (bHaveL1)
									root["haveL1"] = true;
								if (bHaveL2)
									root["haveL2"] = true;
								if (bHaveL3)
									root["haveL3"] = true;
							}
						}
					}
					else
					{
						if (dType == pTypeP1Gas)
						{
							sprintf(szTmp, "%.3f", atof(sValue.c_str()) / 1000.0);
							root["counter"] = szTmp;
						}
						else if (dType == pTypeENERGY)
						{
							size_t spos = sValue.find(";");
							if (spos != std::string::npos)
							{
								float fvalue = static_cast<float>(atof(sValue.substr(spos + 1).c_str()));
								sprintf(szTmp, "%.3f", fvalue / (divider / 100.0f));
								root["counter"] = szTmp;
							}
						}
						else if ((dType == pTypeGeneral) && (dSubType == sTypeKwh))
						{
							size_t spos = sValue.find(";");
							if (spos != std::string::npos)
							{
								float fvalue = static_cast<float>(atof(sValue.substr(spos + 1).c_str()));
								sprintf(szTmp, "%.3f", fvalue / divider);
								root["counter"] = szTmp;
							}
						}
						else if (dType == pTypeRFXMeter)
						{
							float fvalue = static_cast<float>(atof(sValue.c_str()));
							switch (metertype)
							{
							case MTYPE_ENERGY:
							case MTYPE_ENERGY_GENERATED:
								sprintf(szTmp, "%.3f", AddjValue + (fvalue / divider));
								break;
							case MTYPE_GAS:
								sprintf(szTmp, "%.2f", AddjValue + (fvalue / divider));
								break;
							case MTYPE_WATER:
								sprintf(szTmp, "%.3f", AddjValue + (fvalue / divider));
								break;
							default:
								strcpy(szTmp, "");
								break;
							}
							root["counter"] = szTmp;
						}
						else if (dType == pTypeYouLess)
						{
							std::vector<std::string> results;
							StringSplit(sValue, ";", results);
							if (results.size() == 2)
							{
								float fvalue = static_cast<float>(atof(results[0].c_str()));
								switch (metertype)
								{
								case MTYPE_ENERGY:
								case MTYPE_ENERGY_GENERATED:
									sprintf(szTmp, "%.3f", fvalue / divider);
									break;
								case MTYPE_GAS:
									sprintf(szTmp, "%.2f", fvalue / divider);
									break;
								case MTYPE_WATER:
									sprintf(szTmp, "%.3f", fvalue / divider);
									break;
								default:
									strcpy(szTmp, "");
									break;
								}
								root["counter"] = szTmp;
							}
						}
						else if (!bIsManagedCounter)
						{
							sprintf(szTmp, "%d", atoi(sValue.c_str()));
							root["counter"] = szTmp;
						}
						else
						{
							root["counter"] = "0";
						}
						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);
						if (!result.empty())
						{
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[1].substr(0, 16);

								std::string szValue = sd[0];

								double fcounter = atof(sd[2].c_str());

								switch (metertype)
								{
								case MTYPE_ENERGY:
								case MTYPE_ENERGY_GENERATED:
									sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
									root["result"][ii]["v"] = szTmp;
									if (fcounter != 0)
										sprintf(szTmp, "%.3f", AddjValue + ((fcounter - atof(szValue.c_str())) / divider));
									else
										strcpy(szTmp, "0");
									root["result"][ii]["c"] = szTmp;
									break;
								case MTYPE_GAS:
									sprintf(szTmp, "%.2f", atof(szValue.c_str()) / divider);
									root["result"][ii]["v"] = szTmp;
									if (fcounter != 0)
										sprintf(szTmp, "%.2f", AddjValue + ((fcounter - atof(szValue.c_str())) / divider));
									else
										strcpy(szTmp, "0");
									root["result"][ii]["c"] = szTmp;
									break;
								case MTYPE_WATER:
									sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
									root["result"][ii]["v"] = szTmp;
									if (fcounter != 0)
										sprintf(szTmp, "%.3f", AddjValue + ((fcounter - atof(szValue.c_str())) / divider));
									else
										strcpy(szTmp, "0");
									root["result"][ii]["c"] = szTmp;
									break;
								case MTYPE_COUNTER:
									sprintf(szTmp, "%.0f", atof(szValue.c_str()));
									root["result"][ii]["v"] = szTmp;
									if (fcounter != 0)
										sprintf(szTmp, "%.0f", AddjValue + ((fcounter - atof(szValue.c_str()))));
									else
										strcpy(szTmp, "0");
									root["result"][ii]["c"] = szTmp;
									break;
								}
								ii++;
							}
						}
						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);
						if (!result.empty())
						{
							iPrev = 0;
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["resultprev"][iPrev]["d"] = sd[1].substr(0, 16);

								std::string szValue = sd[0];
								switch (metertype)
								{
								case MTYPE_ENERGY:
								case MTYPE_ENERGY_GENERATED:
									sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
									root["resultprev"][iPrev]["v"] = szTmp;
									break;
								case MTYPE_GAS:
									sprintf(szTmp, "%.2f", atof(szValue.c_str()) / divider);
									root["resultprev"][iPrev]["v"] = szTmp;
									break;
								case MTYPE_WATER:
									sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
									root["resultprev"][iPrev]["v"] = szTmp;
									break;
								case MTYPE_COUNTER:
									sprintf(szTmp, "%.0f", atof(szValue.c_str()));
									root["resultprev"][iPrev]["v"] = szTmp;
									break;
								}
								iPrev++;
							}
						}
					}

					if ((sactmonth != "") || (sactyear != ""))
					{
						struct tm loctime;
						time_t now = mytime(NULL);
						localtime_r(&now, &loctime);
						if ((sactmonth != "") && (sactyear != ""))
						{
							bool bIsThisMonth = (atoi(sactyear.c_str()) == loctime.tm_year + 1900) && (atoi(sactmonth.c_str()) == loctime.tm_mon + 1);
							if (bIsThisMonth)
							{
								sprintf(szDateEnd, "%04d-%02d-%02d", loctime.tm_year + 1900, loctime.tm_mon + 1, loctime.tm_mday);
							}
						}
						else if (sactyear != "")
						{
							bool bIsThisYear = (atoi(sactyear.c_str()) == loctime.tm_year + 1900);
							if (bIsThisYear)
							{
								sprintf(szDateEnd, "%04d-%02d-%02d", loctime.tm_year + 1900, loctime.tm_mon + 1, loctime.tm_mday);
							}

						}
					}

					if (dType == pTypeP1Power)
					{
						result = m_sql.safe_query(
							"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')",
							idx, szDateEnd);
						bool bHaveDeliverd = false;
						if (!result.empty())
						{
							std::vector<std::string> sd = result[0];
							unsigned long long total_min_usage_1 = std::strtoull(sd[0].c_str(), nullptr, 10);
							unsigned long long total_max_usage_1 = std::strtoull(sd[1].c_str(), nullptr, 10);
							unsigned long long total_min_usage_2 = std::strtoull(sd[4].c_str(), nullptr, 10);
							unsigned long long total_max_usage_2 = std::strtoull(sd[5].c_str(), nullptr, 10);
							unsigned long long total_real_usage_1, total_real_usage_2;
							unsigned long long total_min_deliv_1 = std::strtoull(sd[2].c_str(), nullptr, 10);
							unsigned long long total_max_deliv_1 = std::strtoull(sd[3].c_str(), nullptr, 10);
							unsigned long long total_min_deliv_2 = std::strtoull(sd[6].c_str(), nullptr, 10);
							unsigned long long total_max_deliv_2 = std::strtoull(sd[7].c_str(), nullptr, 10);
							unsigned long long total_real_deliv_1, total_real_deliv_2;

							total_real_usage_1 = total_max_usage_1 - total_min_usage_1;
							total_real_usage_2 = total_max_usage_2 - total_min_usage_2;

							total_real_deliv_1 = total_max_deliv_1 - total_min_deliv_1;
							total_real_deliv_2 = total_max_deliv_2 - total_min_deliv_2;

							if ((total_real_deliv_1 != 0) || (total_real_deliv_2 != 0))
								bHaveDeliverd = true;

							root["result"][ii]["d"] = szDateEnd;

							std::string szValue;

							sprintf(szTmp, "%llu", total_real_usage_1);
							szValue = szTmp;
							sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
							root["result"][ii]["v"] = szTmp;
							sprintf(szTmp, "%llu", total_real_usage_2);
							szValue = szTmp;
							sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
							root["result"][ii]["v2"] = szTmp;

							sprintf(szTmp, "%llu", total_real_deliv_1);
							szValue = szTmp;
							sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
							root["result"][ii]["r1"] = szTmp;
							sprintf(szTmp, "%llu", total_real_deliv_2);
							szValue = szTmp;
							sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
							root["result"][ii]["r2"] = szTmp;

							ii++;
						}
						if (bHaveDeliverd)
						{
							root["delivered"] = true;
						}
					}
					else if (dType == pTypeAirQuality)
					{
						result = m_sql.safe_query(
							"SELECT MIN(Value), MAX(Value), AVG(Value) FROM Meter WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
							idx, szDateEnd);
						if (!result.empty())
						{
							root["result"][ii]["d"] = szDateEnd;
							root["result"][ii]["co2_min"] = result[0][0];
							root["result"][ii]["co2_max"] = result[0][1];
							root["result"][ii]["co2_avg"] = result[0][2];
							ii++;
						}
					}
					else if (
						((dType == pTypeGeneral) && ((dSubType == sTypeSoilMoisture) || (dSubType == sTypeLeafWetness))) ||
						((dType == pTypeRFXSensor) && ((dSubType == sTypeRFXSensorAD) || (dSubType == sTypeRFXSensorVolt)))
						)
					{
						result = m_sql.safe_query(
							"SELECT MIN(Value), MAX(Value) FROM Meter WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
							idx, szDateEnd);
						if (!result.empty())
						{
							root["result"][ii]["d"] = szDateEnd;
							root["result"][ii]["v_min"] = result[0][0];
							root["result"][ii]["v_max"] = result[0][1];
							ii++;
						}
					}
					else if (
						((dType == pTypeGeneral) && (dSubType == sTypeVisibility)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeDistance)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeSolarRadiation)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeVoltage)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeCurrent)) ||
						((dType == pTypeGeneral) && (dSubType == sTypePressure)) ||
						((dType == pTypeGeneral) && (dSubType == sTypeSoundLevel))
						)
					{
						float vdiv = 10.0f;
						if (
							((dType == pTypeGeneral) && (dSubType == sTypeVoltage)) ||
							((dType == pTypeGeneral) && (dSubType == sTypeCurrent))
							)
						{
							vdiv = 1000.0f;
						}

						result = m_sql.safe_query(
							"SELECT MIN(Value), MAX(Value) FROM Meter WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
							idx, szDateEnd);
						if (!result.empty())
						{
							root["result"][ii]["d"] = szDateEnd;
							float fValue1 = float(atof(result[0][0].c_str())) / vdiv;
							float fValue2 = float(atof(result[0][1].c_str())) / vdiv;
							if (metertype == 1)
							{
								fValue1 *= 0.6214f;
								fValue2 *= 0.6214f;
							}

							if ((dType == pTypeGeneral) && (dSubType == sTypeVoltage))
								sprintf(szTmp, "%.3f", fValue1);
							else if ((dType == pTypeGeneral) && (dSubType == sTypeCurrent))
								sprintf(szTmp, "%.3f", fValue1);
							else
								sprintf(szTmp, "%.1f", fValue1);
							root["result"][ii]["v_min"] = szTmp;
							if ((dType == pTypeGeneral) && (dSubType == sTypeVoltage))
								sprintf(szTmp, "%.3f", fValue2);
							else if ((dType == pTypeGeneral) && (dSubType == sTypeCurrent))
								sprintf(szTmp, "%.3f", fValue2);
							else
								sprintf(szTmp, "%.1f", fValue2);
							root["result"][ii]["v_max"] = szTmp;
							ii++;
						}
					}
					else if (dType == pTypeLux)
					{
						result = m_sql.safe_query(
							"SELECT MIN(Value), MAX(Value), AVG(Value) FROM Meter WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
							idx, szDateEnd);
						if (!result.empty())
						{
							root["result"][ii]["d"] = szDateEnd;
							root["result"][ii]["lux_min"] = result[0][0];
							root["result"][ii]["lux_max"] = result[0][1];
							root["result"][ii]["lux_avg"] = result[0][2];
							ii++;
						}
					}
					else if (dType == pTypeWEIGHT)
					{
						result = m_sql.safe_query(
							"SELECT MIN(Value), MAX(Value) FROM Meter WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
							idx, szDateEnd);
						if (!result.empty())
						{
							root["result"][ii]["d"] = szDateEnd;
							sprintf(szTmp, "%.1f", m_sql.m_weightscale* atof(result[0][0].c_str()) / 10.0f);
							root["result"][ii]["v_min"] = szTmp;
							sprintf(szTmp, "%.1f", m_sql.m_weightscale * atof(result[0][1].c_str()) / 10.0f);
							root["result"][ii]["v_max"] = szTmp;
							ii++;
						}
					}
					else if (dType == pTypeUsage)
					{
						result = m_sql.safe_query(
							"SELECT MIN(Value), MAX(Value) FROM Meter WHERE (DeviceRowID=%" PRIu64 " AND Date>='%q')",
							idx, szDateEnd);
						if (!result.empty())
						{
							root["result"][ii]["d"] = szDateEnd;
							root["result"][ii]["u_min"] = atof(result[0][0].c_str()) / 10.0f;
							root["result"][ii]["u_max"] = atof(result[0][1].c_str()) / 10.0f;
							ii++;
						}
					}
					else if (!bIsManagedCounter)
					{
						result = m_sql.safe_query(
							"SELECT MIN(Value), MAX(Value) FROM Meter WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
							idx, szDateEnd);
						if (!result.empty())
						{
							std::vector<std::string> sd = result[0];
							unsigned long long total_min = std::strtoull(sd[0].c_str(), nullptr, 10);
							unsigned long long total_max = std::strtoull(sd[1].c_str(), nullptr, 10);
							unsigned long long total_real;

							total_real = total_max - total_min;
							sprintf(szTmp, "%llu", total_real);

							root["result"][ii]["d"] = szDateEnd;

							std::string szValue = szTmp;
							switch (metertype)
							{
							case MTYPE_ENERGY:
							case MTYPE_ENERGY_GENERATED:
							{
								sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
								root["result"][ii]["v"] = szTmp;

								std::vector<std::string> mresults;
								StringSplit(sValue, ";", mresults);
								if (mresults.size() == 2)
								{
									sValue = mresults[1];
								}
								if (dType == pTypeENERGY)
									sprintf(szTmp, "%.3f", AddjValue + (((atof(sValue.c_str())*100.0f) - atof(szValue.c_str())) / divider));
								else
									sprintf(szTmp, "%.3f", AddjValue + ((atof(sValue.c_str()) - atof(szValue.c_str())) / divider));
								root["result"][ii]["c"] = szTmp;
							}
							break;
							case MTYPE_GAS:
								sprintf(szTmp, "%.2f", atof(szValue.c_str()) / divider);
								root["result"][ii]["v"] = szTmp;
								sprintf(szTmp, "%.2f", AddjValue + ((atof(sValue.c_str()) - atof(szValue.c_str())) / divider));
								root["result"][ii]["c"] = szTmp;
								break;
							case MTYPE_WATER:
								sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
								root["result"][ii]["v"] = szTmp;
								sprintf(szTmp, "%.3f", AddjValue + ((atof(sValue.c_str()) - atof(szValue.c_str())) / divider));
								root["result"][ii]["c"] = szTmp;
								break;
							case MTYPE_COUNTER:
								sprintf(szTmp, "%.0f", atof(szValue.c_str()));
								root["result"][ii]["v"] = szTmp;
								sprintf(szTmp, "%.0f", AddjValue + ((atof(sValue.c_str()) - atof(szValue.c_str()))));
								root["result"][ii]["c"] = szTmp;
								break;
							}
							ii++;
						}
					}
				}
				else if (sensor == "wind") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;

					int ii = 0;

					result = m_sql.safe_query(
						"SELECT Direction, Speed_Min, Speed_Max, Gust_Min,"
						" Gust_Max, Date "
						"FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q'"
						" AND Date<='%q') ORDER BY Date ASC",
						dbasetable.c_str(), idx, szDateStart, szDateEnd);
					if (!result.empty())
					{
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["result"][ii]["d"] = sd[5].substr(0, 16);
							root["result"][ii]["di"] = sd[0];

							int intSpeed = atoi(sd[2].c_str());
							int intGust = atoi(sd[4].c_str());
							if (m_sql.m_windunit != WINDUNIT_Beaufort)
							{
								sprintf(szTmp, "%.1f", float(intSpeed) * m_sql.m_windscale);
								root["result"][ii]["sp"] = szTmp;
								sprintf(szTmp, "%.1f", float(intGust) * m_sql.m_windscale);
								root["result"][ii]["gu"] = szTmp;
							}
							else
							{
								float windspeedms = float(intSpeed)*0.1f;
								float windgustms = float(intGust)*0.1f;
								sprintf(szTmp, "%d", MStoBeaufort(windspeedms));
								root["result"][ii]["sp"] = szTmp;
								sprintf(szTmp, "%d", MStoBeaufort(windgustms));
								root["result"][ii]["gu"] = szTmp;
							}
							ii++;
						}
					}
					result = m_sql.safe_query(
						"SELECT AVG(Direction), MIN(Speed), MAX(Speed),"
						" MIN(Gust), MAX(Gust) "
						"FROM Wind WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q') ORDER BY Date ASC",
						idx, szDateEnd);
					if (!result.empty())
					{
						std::vector<std::string> sd = result[0];

						root["result"][ii]["d"] = szDateEnd;
						root["result"][ii]["di"] = sd[0];

						int intSpeed = atoi(sd[2].c_str());
						int intGust = atoi(sd[4].c_str());
						if (m_sql.m_windunit != WINDUNIT_Beaufort)
						{
							sprintf(szTmp, "%.1f", float(intSpeed) * m_sql.m_windscale);
							root["result"][ii]["sp"] = szTmp;
							sprintf(szTmp, "%.1f", float(intGust) * m_sql.m_windscale);
							root["result"][ii]["gu"] = szTmp;
						}
						else
						{
							float windspeedms = float(intSpeed)*0.1f;
							float windgustms = float(intGust)*0.1f;
							sprintf(szTmp, "%d", MStoBeaufort(windspeedms));
							root["result"][ii]["sp"] = szTmp;
							sprintf(szTmp, "%d", MStoBeaufort(windgustms));
							root["result"][ii]["gu"] = szTmp;
						}
						ii++;
					}
					result = m_sql.safe_query(
						"SELECT Direction, Speed_Min, Speed_Max, Gust_Min,"
						" Gust_Max, Date "
						"FROM %s WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q'"
						" AND Date<='%q') ORDER BY Date ASC",
						dbasetable.c_str(), idx, szDateStartPrev, szDateEndPrev);
					if (!result.empty())
					{
						iPrev = 0;
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["resultprev"][iPrev]["d"] = sd[5].substr(0, 16);
							root["resultprev"][iPrev]["di"] = sd[0];

							int intSpeed = atoi(sd[2].c_str());
							int intGust = atoi(sd[4].c_str());
							if (m_sql.m_windunit != WINDUNIT_Beaufort)
							{
								sprintf(szTmp, "%.1f", float(intSpeed) * m_sql.m_windscale);
								root["resultprev"][iPrev]["sp"] = szTmp;
								sprintf(szTmp, "%.1f", float(intGust) * m_sql.m_windscale);
								root["resultprev"][iPrev]["gu"] = szTmp;
							}
							else
							{
								float windspeedms = float(intSpeed)*0.1f;
								float windgustms = float(intGust)*0.1f;
								sprintf(szTmp, "%d", MStoBeaufort(windspeedms));
								root["resultprev"][iPrev]["sp"] = szTmp;
								sprintf(szTmp, "%d", MStoBeaufort(windgustms));
								root["resultprev"][iPrev]["gu"] = szTmp;
							}
							iPrev++;
						}
					}
				}
			}//month or year
			else if ((srange.substr(0, 1) == "2") && (srange.substr(10, 1) == "T") && (srange.substr(11, 1) == "2")) // custom range 2013-01-01T2013-12-31
			{
				std::string szDateStart = srange.substr(0, 10);
				std::string szDateEnd = srange.substr(11, 10);
				std::string sgraphtype = request::findValue(&req, "graphtype");
				std::string sgraphTemp = request::findValue(&req, "graphTemp");
				std::string sgraphChill = request::findValue(&req, "graphChill");
				std::string sgraphHum = request::findValue(&req, "graphHum");
				std::string sgraphBaro = request::findValue(&req, "graphBaro");
				std::string sgraphDew = request::findValue(&req, "graphDew");
				std::string sgraphSet = request::findValue(&req, "graphSet");

				if (sensor == "temp") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;

					bool sendTemp = false;
					bool sendChill = false;
					bool sendHum = false;
					bool sendBaro = false;
					bool sendDew = false;
					bool sendSet = false;

					if ((sgraphTemp == "true") &&
						((dType == pTypeRego6XXTemp) || (dType == pTypeTEMP) || (dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO) || (dType == pTypeTEMP_BARO) || (dType == pTypeWIND) || (dType == pTypeThermostat1) || (dType == pTypeRadiator1) ||
						((dType == pTypeUV) && (dSubType == sTypeUV3)) ||
							((dType == pTypeWIND) && (dSubType == sTypeWIND4)) ||
							((dType == pTypeRFXSensor) && (dSubType == sTypeRFXSensorTemp)) ||
							((dType == pTypeThermostat) && (dSubType == sTypeThermSetpoint)) ||
							(dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater)
							)
						)
					{
						sendTemp = true;
					}
					if ((sgraphSet == "true") &&
						((dType == pTypeEvohomeZone) || (dType == pTypeEvohomeWater))) //FIXME cheat for water setpoint is just on or off
					{
						sendSet = true;
					}
					if ((sgraphChill == "true") &&
						(((dType == pTypeWIND) && (dSubType == sTypeWIND4)) ||
						((dType == pTypeWIND) && (dSubType == sTypeWINDNoTemp)))
						)
					{
						sendChill = true;
					}
					if ((sgraphHum == "true") &&
						((dType == pTypeHUM) || (dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO))
						)
					{
						sendHum = true;
					}
					if ((sgraphBaro == "true") && (
						(dType == pTypeTEMP_HUM_BARO) ||
						(dType == pTypeTEMP_BARO) ||
						((dType == pTypeGeneral) && (dSubType == sTypeBaro))
						))
					{
						sendBaro = true;
					}
					if ((sgraphDew == "true") && ((dType == pTypeTEMP_HUM) || (dType == pTypeTEMP_HUM_BARO)))
					{
						sendDew = true;
					}

					if (sgraphtype == "1")
					{
						result = m_sql.safe_query(
							"SELECT Temperature, Chill, Humidity, Barometer,"
							" Date, DewPoint, SetPoint "
							"FROM Temperature WHERE (DeviceRowID==%" PRIu64 ""
							" AND Date>='%q' AND Date<='%q 23:59:59') ORDER BY Date ASC",
							idx, szDateStart.c_str(), szDateEnd.c_str());
						int ii = 0;
						if (!result.empty())
						{
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[4];//.substr(0,16);
								if (sendTemp)
								{
									double te = ConvertTemperature(atof(sd[0].c_str()), tempsign);
									double tm = ConvertTemperature(atof(sd[0].c_str()), tempsign);
									root["result"][ii]["te"] = te;
									root["result"][ii]["tm"] = tm;
								}
								if (sendChill)
								{
									double ch = ConvertTemperature(atof(sd[1].c_str()), tempsign);
									double cm = ConvertTemperature(atof(sd[1].c_str()), tempsign);
									root["result"][ii]["ch"] = ch;
									root["result"][ii]["cm"] = cm;
								}
								if (sendHum)
								{
									root["result"][ii]["hu"] = sd[2];
								}
								if (sendBaro)
								{
									if (dType == pTypeTEMP_HUM_BARO)
									{
										if (dSubType == sTypeTHBFloat)
										{
											sprintf(szTmp, "%.1f", atof(sd[3].c_str()) / 10.0f);
											root["result"][ii]["ba"] = szTmp;
										}
										else
											root["result"][ii]["ba"] = sd[3];
									}
									else if (dType == pTypeTEMP_BARO)
									{
										sprintf(szTmp, "%.1f", atof(sd[3].c_str()) / 10.0f);
										root["result"][ii]["ba"] = szTmp;
									}
									else if ((dType == pTypeGeneral) && (dSubType == sTypeBaro))
									{
										sprintf(szTmp, "%.1f", atof(sd[3].c_str()) / 10.0f);
										root["result"][ii]["ba"] = szTmp;
									}
								}
								if (sendDew)
								{
									double dp = ConvertTemperature(atof(sd[5].c_str()), tempsign);
									root["result"][ii]["dp"] = dp;
								}
								if (sendSet)
								{
									double se = ConvertTemperature(atof(sd[6].c_str()), tempsign);
									root["result"][ii]["se"] = se;
								}
								ii++;
							}
						}
					}
					else
					{
						result = m_sql.safe_query(
							"SELECT Temp_Min, Temp_Max, Chill_Min, Chill_Max,"
							" Humidity, Barometer, Date, DewPoint, Temp_Avg,"
							" SetPoint_Min, SetPoint_Max, SetPoint_Avg "
							"FROM Temperature_Calendar "
							"WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q'"
							" AND Date<='%q') ORDER BY Date ASC",
							idx, szDateStart.c_str(), szDateEnd.c_str());
						int ii = 0;
						if (!result.empty())
						{
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[6].substr(0, 16);
								if (sendTemp)
								{
									double te = ConvertTemperature(atof(sd[1].c_str()), tempsign);
									double tm = ConvertTemperature(atof(sd[0].c_str()), tempsign);
									double ta = ConvertTemperature(atof(sd[8].c_str()), tempsign);

									root["result"][ii]["te"] = te;
									root["result"][ii]["tm"] = tm;
									root["result"][ii]["ta"] = ta;
								}
								if (sendChill)
								{
									double ch = ConvertTemperature(atof(sd[3].c_str()), tempsign);
									double cm = ConvertTemperature(atof(sd[2].c_str()), tempsign);

									root["result"][ii]["ch"] = ch;
									root["result"][ii]["cm"] = cm;
								}
								if (sendHum)
								{
									root["result"][ii]["hu"] = sd[4];
								}
								if (sendBaro)
								{
									if (dType == pTypeTEMP_HUM_BARO)
									{
										if (dSubType == sTypeTHBFloat)
										{
											sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
											root["result"][ii]["ba"] = szTmp;
										}
										else
											root["result"][ii]["ba"] = sd[5];
									}
									else if (dType == pTypeTEMP_BARO)
									{
										sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
										root["result"][ii]["ba"] = szTmp;
									}
									else if ((dType == pTypeGeneral) && (dSubType == sTypeBaro))
									{
										sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
										root["result"][ii]["ba"] = szTmp;
									}
								}
								if (sendDew)
								{
									double dp = ConvertTemperature(atof(sd[7].c_str()), tempsign);
									root["result"][ii]["dp"] = dp;
								}
								if (sendSet)
								{
									double sm = ConvertTemperature(atof(sd[9].c_str()), tempsign);
									double sx = ConvertTemperature(atof(sd[10].c_str()), tempsign);
									double se = ConvertTemperature(atof(sd[11].c_str()), tempsign);
									root["result"][ii]["sm"] = sm;
									root["result"][ii]["se"] = se;
									root["result"][ii]["sx"] = sx;
									char szTmp[1024];
									sprintf(szTmp, "%.1f %.1f %.1f", sm, se, sx);
									_log.Log(LOG_STATUS, "%s", szTmp);

								}
								ii++;
							}
						}

						result = m_sql.safe_query(
							"SELECT MIN(Temperature), MAX(Temperature),"
							" MIN(Chill), MAX(Chill), AVG(Humidity),"
							" AVG(Barometer), MIN(DewPoint), AVG(Temperature),"
							" MIN(SetPoint), MAX(SetPoint), AVG(SetPoint) "
							"FROM Temperature WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
							idx, szDateEnd.c_str());
						if (!result.empty())
						{
							std::vector<std::string> sd = result[0];

							root["result"][ii]["d"] = szDateEnd;
							if (sendTemp)
							{
								double te = ConvertTemperature(atof(sd[1].c_str()), tempsign);
								double tm = ConvertTemperature(atof(sd[0].c_str()), tempsign);
								double ta = ConvertTemperature(atof(sd[7].c_str()), tempsign);

								root["result"][ii]["te"] = te;
								root["result"][ii]["tm"] = tm;
								root["result"][ii]["ta"] = ta;
							}
							if (sendChill)
							{
								double ch = ConvertTemperature(atof(sd[3].c_str()), tempsign);
								double cm = ConvertTemperature(atof(sd[2].c_str()), tempsign);
								root["result"][ii]["ch"] = ch;
								root["result"][ii]["cm"] = cm;
							}
							if (sendHum)
							{
								root["result"][ii]["hu"] = sd[4];
							}
							if (sendBaro)
							{
								if (dType == pTypeTEMP_HUM_BARO)
								{
									if (dSubType == sTypeTHBFloat)
									{
										sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
										root["result"][ii]["ba"] = szTmp;
									}
									else
										root["result"][ii]["ba"] = sd[5];
								}
								else if (dType == pTypeTEMP_BARO)
								{
									sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
									root["result"][ii]["ba"] = szTmp;
								}
								else if ((dType == pTypeGeneral) && (dSubType == sTypeBaro))
								{
									sprintf(szTmp, "%.1f", atof(sd[5].c_str()) / 10.0f);
									root["result"][ii]["ba"] = szTmp;
								}
							}
							if (sendDew)
							{
								double dp = ConvertTemperature(atof(sd[6].c_str()), tempsign);
								root["result"][ii]["dp"] = dp;
							}
							if (sendSet)
							{
								double sm = ConvertTemperature(atof(sd[8].c_str()), tempsign);
								double sx = ConvertTemperature(atof(sd[9].c_str()), tempsign);
								double se = ConvertTemperature(atof(sd[10].c_str()), tempsign);

								root["result"][ii]["sm"] = sm;
								root["result"][ii]["se"] = se;
								root["result"][ii]["sx"] = sx;
							}
							ii++;
						}
					}
				}
				else if (sensor == "uv") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;

					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.c_str(), szDateEnd.c_str());
					int ii = 0;
					if (!result.empty())
					{
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["result"][ii]["d"] = sd[1].substr(0, 16);
							root["result"][ii]["uvi"] = sd[0];
							ii++;
						}
					}
					result = m_sql.safe_query(
						"SELECT MAX(Level) FROM UV WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
						idx, szDateEnd.c_str());
					if (!result.empty())
					{
						std::vector<std::string> sd = result[0];

						root["result"][ii]["d"] = szDateEnd;
						root["result"][ii]["uvi"] = sd[0];
						ii++;
					}
				}
				else if (sensor == "rain") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;

					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.c_str(), szDateEnd.c_str());
					int ii = 0;
					if (!result.empty())
					{
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["result"][ii]["d"] = sd[2].substr(0, 16);
							root["result"][ii]["mm"] = sd[0];
							ii++;
						}
					}
					if (dSubType != sTypeRAINWU)
					{
						result = m_sql.safe_query(
							"SELECT MIN(Total), MAX(Total), MAX(Rate) FROM Rain WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
							idx, szDateEnd.c_str());
					}
					else
					{
						result = m_sql.safe_query(
							"SELECT Total, Total, Rate FROM Rain WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q') ORDER BY ROWID DESC LIMIT 1",
							idx, szDateEnd.c_str());
					}
					if (!result.empty())
					{
						std::vector<std::string> sd = result[0];

						float total_min = static_cast<float>(atof(sd[0].c_str()));
						float total_max = static_cast<float>(atof(sd[1].c_str()));
						int rate = atoi(sd[2].c_str());

						float total_real = 0;
						if (dSubType != sTypeRAINWU)
						{
							total_real = total_max - total_min;
						}
						else
						{
							total_real = total_max;
						}
						sprintf(szTmp, "%.1f", total_real);
						root["result"][ii]["d"] = szDateEnd;
						root["result"][ii]["mm"] = szTmp;
						ii++;
					}
				}
				else if (sensor == "counter") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;
					root["ValueQuantity"] = options["ValueQuantity"];
					root["ValueUnits"] = options["ValueUnits"];

					int ii = 0;
					if (dType == pTypeP1Power)
					{
						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.c_str(), szDateEnd.c_str());
						if (!result.empty())
						{
							bool bHaveDeliverd = false;
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								root["result"][ii]["d"] = sd[4].substr(0, 16);

								std::string szUsage1 = sd[0];
								std::string szDeliv1 = sd[1];
								std::string szUsage2 = sd[2];
								std::string szDeliv2 = sd[3];

								float fUsage = (float)(atof(szUsage1.c_str()) + atof(szUsage2.c_str()));
								float fDeliv = (float)(atof(szDeliv1.c_str()) + atof(szDeliv2.c_str()));

								if (fDeliv != 0)
									bHaveDeliverd = true;
								sprintf(szTmp, "%.3f", fUsage / divider);
								root["result"][ii]["v"] = szTmp;
								sprintf(szTmp, "%.3f", fDeliv / divider);
								root["result"][ii]["v2"] = szTmp;
								ii++;
							}
							if (bHaveDeliverd)
							{
								root["delivered"] = true;
							}
						}
					}
					else
					{
						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());
						if (!result.empty())
						{
							for (const auto & itt : result)
							{
								std::vector<std::string> sd = itt;

								std::string szValue = sd[0];
								switch (metertype)
								{
								case MTYPE_ENERGY:
								case MTYPE_ENERGY_GENERATED:
									sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
									szValue = szTmp;
									break;
								case MTYPE_GAS:
									sprintf(szTmp, "%.2f", atof(szValue.c_str()) / divider);
									szValue = szTmp;
									break;
								case MTYPE_WATER:
									sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
									szValue = szTmp;
									break;
								}
								root["result"][ii]["d"] = sd[1].substr(0, 16);
								root["result"][ii]["v"] = szValue;
								ii++;
							}
						}
					}
					if (dType == pTypeP1Power)
					{
						result = m_sql.safe_query(
							"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')",
							idx, szDateEnd.c_str());
						bool bHaveDeliverd = false;
						if (!result.empty())
						{
							std::vector<std::string> sd = result[0];

							unsigned long long total_min_usage_1 = std::strtoull(sd[0].c_str(), nullptr, 10);
							unsigned long long total_max_usage_1 = std::strtoull(sd[1].c_str(), nullptr, 10);
							unsigned long long total_min_usage_2 = std::strtoull(sd[4].c_str(), nullptr, 10);
							unsigned long long total_max_usage_2 = std::strtoull(sd[5].c_str(), nullptr, 10);
							unsigned long long total_real_usage;

							unsigned long long total_min_deliv_1 = std::strtoull(sd[2].c_str(), nullptr, 10);
							unsigned long long total_max_deliv_1 = std::strtoull(sd[3].c_str(), nullptr, 10);
							unsigned long long total_min_deliv_2 = std::strtoull(sd[6].c_str(), nullptr, 10);
							unsigned long long total_max_deliv_2 = std::strtoull(sd[7].c_str(), nullptr, 10);
							unsigned long long total_real_deliv;

							total_real_usage = (total_max_usage_1 + total_max_usage_2) - (total_min_usage_1 + total_min_usage_2);
							total_real_deliv = (total_max_deliv_1 + total_max_deliv_2) - (total_min_deliv_1 + total_min_deliv_2);

							if (total_real_deliv != 0)
								bHaveDeliverd = true;

							root["result"][ii]["d"] = szDateEnd;

							sprintf(szTmp, "%llu", total_real_usage);
							std::string szValue = szTmp;
							sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
							root["result"][ii]["v"] = szTmp;
							sprintf(szTmp, "%llu", total_real_deliv);
							szValue = szTmp;
							sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
							root["result"][ii]["v2"] = szTmp;
							ii++;
							if (bHaveDeliverd)
							{
								root["delivered"] = true;
							}
						}
					}
					else if (!bIsManagedCounter)
					{
						result = m_sql.safe_query(
							"SELECT MIN(Value), MAX(Value) FROM Meter WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q')",
							idx, szDateEnd.c_str());
						if (!result.empty())
						{
							std::vector<std::string> sd = result[0];
							unsigned long long total_min = std::strtoull(sd[0].c_str(), nullptr, 10);
							unsigned long long total_max = std::strtoull(sd[1].c_str(), nullptr, 10);
							unsigned long long total_real;

							total_real = total_max - total_min;
							sprintf(szTmp, "%llu", total_real);

							std::string szValue = szTmp;
							switch (metertype)
							{
							case MTYPE_ENERGY:
							case MTYPE_ENERGY_GENERATED:
								sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
								szValue = szTmp;
								break;
							case MTYPE_GAS:
								sprintf(szTmp, "%.2f", atof(szValue.c_str()) / divider);
								szValue = szTmp;
								break;
							case MTYPE_WATER:
								sprintf(szTmp, "%.3f", atof(szValue.c_str()) / divider);
								szValue = szTmp;
								break;
							}

							root["result"][ii]["d"] = szDateEnd;
							root["result"][ii]["v"] = szValue;
							ii++;
						}
					}
				}
				else if (sensor == "wind") {
					root["status"] = "OK";
					root["title"] = "Graph " + sensor + " " + srange;

					int ii = 0;

					result = m_sql.safe_query(
						"SELECT Direction, Speed_Min, Speed_Max, Gust_Min,"
						" Gust_Max, 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());
					if (!result.empty())
					{
						for (const auto & itt : result)
						{
							std::vector<std::string> sd = itt;

							root["result"][ii]["d"] = sd[5].substr(0, 16);
							root["result"][ii]["di"] = sd[0];

							int intSpeed = atoi(sd[2].c_str());
							int intGust = atoi(sd[4].c_str());
							if (m_sql.m_windunit != WINDUNIT_Beaufort)
							{
								sprintf(szTmp, "%.1f", float(intSpeed) * m_sql.m_windscale);
								root["result"][ii]["sp"] = szTmp;
								sprintf(szTmp, "%.1f", float(intGust) * m_sql.m_windscale);
								root["result"][ii]["gu"] = szTmp;
							}
							else
							{
								float windspeedms = float(intSpeed)*0.1f;
								float windgustms = float(intGust)*0.1f;
								sprintf(szTmp, "%d", MStoBeaufort(windspeedms));
								root["result"][ii]["sp"] = szTmp;
								sprintf(szTmp, "%d", MStoBeaufort(windgustms));
								root["result"][ii]["gu"] = szTmp;
							}
							ii++;
						}
					}
					result = m_sql.safe_query(
						"SELECT AVG(Direction), MIN(Speed), MAX(Speed), MIN(Gust), MAX(Gust) FROM Wind WHERE (DeviceRowID==%" PRIu64 " AND Date>='%q') ORDER BY Date ASC",
						idx, szDateEnd.c_str());
					if (!result.empty())
					{
						std::vector<std::string> sd = result[0];

						root["result"][ii]["d"] = szDateEnd;
						root["result"][ii]["di"] = sd[0];

						int intSpeed = atoi(sd[2].c_str());
						int intGust = atoi(sd[4].c_str());
						if (m_sql.m_windunit != WINDUNIT_Beaufort)
						{
							sprintf(szTmp, "%.1f", float(intSpeed) * m_sql.m_windscale);
							root["result"][ii]["sp"] = szTmp;
							sprintf(szTmp, "%.1f", float(intGust) * m_sql.m_windscale);
							root["result"][ii]["gu"] = szTmp;
						}
						else
						{
							float windspeedms = float(intSpeed)*0.1f;
							float windgustms = float(intGust)*0.1f;
							sprintf(szTmp, "%d", MStoBeaufort(windspeedms));
							root["result"][ii]["sp"] = szTmp;
							sprintf(szTmp, "%d", MStoBeaufort(windgustms));
							root["result"][ii]["gu"] = szTmp;
						}
						ii++;
					}
				}
			}//custom range
		}

Project: domoticz

		void CWebServer::RType_LightLog(WebEmSession & session, const request& req, Json::Value &root)
		{
			uint64_t idx = 0;
			if (request::findValue(&req, "idx") != "")
			{
				idx = std::strtoull(request::findValue(&req, "idx").c_str(), nullptr, 10);
			}
			std::vector<std::vector<std::string> > result;
			result = m_sql.safe_query("SELECT Type, SubType, SwitchType, Options FROM DeviceStatus WHERE (ID == %" PRIu64 ")",
				idx);
			if (result.empty())
				return;

			unsigned char dType = atoi(result[0][0].c_str());
			unsigned char dSubType = atoi(result[0][1].c_str());
			_eSwitchType switchtype = (_eSwitchType)atoi(result[0][2].c_str());
			std::map<std::string, std::string> options = m_sql.BuildDeviceOptions(result[0][3].c_str());

			if (
				(dType != pTypeLighting1) &&
				(dType != pTypeLighting2) &&
				(dType != pTypeLighting3) &&
				(dType != pTypeLighting4) &&
				(dType != pTypeLighting5) &&
				(dType != pTypeLighting6) &&
				(dType != pTypeFan) &&
				(dType != pTypeColorSwitch) &&
				(dType != pTypeSecurity1) &&
				(dType != pTypeSecurity2) &&
				(dType != pTypeEvohome) &&
				(dType != pTypeEvohomeRelay) &&
				(dType != pTypeCurtain) &&
				(dType != pTypeBlinds) &&
				(dType != pTypeRFY) &&
				(dType != pTypeRego6XXValue) &&
				(dType != pTypeChime) &&
				(dType != pTypeThermostat2) &&
				(dType != pTypeThermostat3) &&
				(dType != pTypeThermostat4) &&
				(dType != pTypeRemote) &&
				(dType != pTypeGeneralSwitch) &&
				(dType != pTypeHomeConfort) &&
				(dType != pTypeFS20) &&
				(!((dType == pTypeRadiator1) && (dSubType == sTypeSmartwaresSwitchRadiator)))
				)
				return; //no light device! we should not be here!

			root["status"] = "OK";
			root["title"] = "LightLog";

			result = m_sql.safe_query("SELECT ROWID, nValue, sValue, Date FROM LightingLog WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date DESC", idx);
			if (!result.empty())
			{
				std::map<std::string, std::string> selectorStatuses;
				if (switchtype == STYPE_Selector) {
					GetSelectorSwitchStatuses(options, selectorStatuses);
				}

				int ii = 0;
				for (const auto & itt : result)
				{
					std::vector<std::string> sd = itt;

					int nValue = atoi(sd[1].c_str());
					std::string sValue = sd[2];

					if ((switchtype == STYPE_Media) && (sValue == "0")) continue;

					root["result"][ii]["idx"] = sd[0];

					std::string lstatus = "";
					std::string ldata = "";
					int llevel = 0;
					bool bHaveDimmer = false;
					bool bHaveSelector = false;
					bool bHaveGroupCmd = false;
					int maxDimLevel = 0;

					if (switchtype == STYPE_Media) {
						lstatus = sValue;
						ldata = lstatus;

					}
					else if (switchtype == STYPE_Selector)
					{
						if (ii == 0) {
							bHaveSelector = true;
							maxDimLevel = selectorStatuses.size();
						}
						if (!selectorStatuses.empty()) {

							std::string sLevel = selectorStatuses[sValue];
							ldata = sLevel;
							lstatus = "Set Level: " + sLevel;
							llevel = atoi(sValue.c_str());
						}
					}
					else {
						GetLightStatus(dType, dSubType, switchtype, nValue, sValue, lstatus, llevel, bHaveDimmer, maxDimLevel, bHaveGroupCmd);
						ldata = lstatus;
					}

					if (ii == 0)
					{
						root["HaveDimmer"] = bHaveDimmer;
						root["result"][ii]["MaxDimLevel"] = maxDimLevel;
						root["HaveGroupCmd"] = bHaveGroupCmd;
						root["HaveSelector"] = bHaveSelector;
					}

					root["result"][ii]["Date"] = sd[3];
					root["result"][ii]["Data"] = ldata;
					root["result"][ii]["Status"] = lstatus;
					root["result"][ii]["Level"] = llevel;

					ii++;
				}
			}
		}

Project: domoticz
File: WebServer.cpp

		void CWebServer::RType_Mobiles(WebEmSession & session, const request& req, Json::Value &root)
		{
			bool bHaveUser = (session.username != "");
			int urights = 3;
			if (bHaveUser)
			{
				int iUser = FindUser(session.username.c_str());
				if (iUser != -1)
					urights = static_cast<int>(m_users[iUser].userrights);
			}
			if (urights < 2)
				return;

			root["status"] = "OK";
			root["title"] = "Mobiles";

			std::vector<std::vector<std::string> > result;
			result = m_sql.safe_query("SELECT ID, Active, Name, UUID, LastUpdate, DeviceType FROM MobileDevices ORDER BY Name ASC");
			if (!result.empty())
			{
				int ii = 0;
				for (const auto & itt : result)
				{
					std::vector<std::string> sd = itt;

					root["result"][ii]["idx"] = sd[0];
					root["result"][ii]["Enabled"] = (sd[1] == "1") ? "true" : "false";
					root["result"][ii]["Name"] = sd[2];
					root["result"][ii]["UUID"] = sd[3];
					root["result"][ii]["LastUpdate"] = sd[4];
					root["result"][ii]["DeviceType"] = sd[5];
					ii++;
				}
			}
		}

Project: domoticz
File: WebServer.cpp

		void CWebServer::RType_SceneLog(WebEmSession & session, const request& req, Json::Value &root)
		{
			uint64_t idx = 0;
			if (request::findValue(&req, "idx") != "")
			{
				idx = std::strtoull(request::findValue(&req, "idx").c_str(), nullptr, 10);
			}
			std::vector<std::vector<std::string> > result;

			root["status"] = "OK";
			root["title"] = "SceneLog";

			result = m_sql.safe_query("SELECT ROWID, nValue, Date FROM SceneLog WHERE (SceneRowID==%" PRIu64 ") ORDER BY Date DESC", idx);
			if (!result.empty())
			{
				int ii = 0;
				for (const auto & itt : result)
				{
					std::vector<std::string> sd = itt;

					int nValue = atoi(sd[1].c_str());
					root["result"][ii]["idx"] = sd[0];
					root["result"][ii]["Date"] = sd[2];
					root["result"][ii]["Data"] = (nValue == 0) ? "Off" : "On";
					ii++;
				}
			}
		}

Project: domoticz

		void CWebServer::RType_SetSharedUserDevices(WebEmSession & session, const request& req, Json::Value &root)
		{
			std::string idx = request::findValue(&req, "idx");
			std::string userdevices = request::findValue(&req, "devices");
			if (idx.empty())
				return;
			root["status"] = "OK";
			root["title"] = "SetSharedUserDevices";
			std::vector<std::string> strarray;
			StringSplit(userdevices, ";", strarray);

			m_sql.safe_query("DELETE FROM SharedDevices WHERE (SharedUserID == '%q')", idx.c_str());

			int nDevices = static_cast<int>(strarray.size());
			for (int ii = 0; ii < nDevices; ii++)
			{
				m_sql.safe_query("INSERT INTO SharedDevices (SharedUserID,DeviceRowID) VALUES ('%q','%q')", idx.c_str(), strarray[ii].c_str());
			}
			LoadUsers();
		}

Project: domoticz

		void CWebServer::RType_SetUsed(WebEmSession & session, const request& req, Json::Value &root)
		{
			if (session.rights != 2)
			{
				session.reply_status = reply::forbidden;
				return; //Only admin user allowed
			}

			std::string idx = request::findValue(&req, "idx");
			std::string deviceid = request::findValue(&req, "deviceid");
			std::string name = request::findValue(&req, "name");
			std::string description = request::findValue(&req, "description");
			std::string sused = request::findValue(&req, "used");
			std::string sswitchtype = request::findValue(&req, "switchtype");
			std::string maindeviceidx = request::findValue(&req, "maindeviceidx");
			std::string addjvalue = request::findValue(&req, "addjvalue");
			std::string addjmulti = request::findValue(&req, "addjmulti");
			std::string addjvalue2 = request::findValue(&req, "addjvalue2");
			std::string addjmulti2 = request::findValue(&req, "addjmulti2");
			std::string setPoint = request::findValue(&req, "setpoint");
			std::string state = request::findValue(&req, "state");
			std::string mode = request::findValue(&req, "mode");
			std::string until = request::findValue(&req, "until");
			std::string clock = request::findValue(&req, "clock");
			std::string tmode = request::findValue(&req, "tmode");
			std::string fmode = request::findValue(&req, "fmode");
			std::string sCustomImage = request::findValue(&req, "customimage");

			std::string strunit = request::findValue(&req, "unit");
			std::string strParam1 = base64_decode(request::findValue(&req, "strparam1"));
			std::string strParam2 = base64_decode(request::findValue(&req, "strparam2"));
			std::string tmpstr = request::findValue(&req, "protected");
			bool bHasstrParam1 = request::hasValue(&req, "strparam1");
			int iProtected = (tmpstr == "true") ? 1 : 0;

			std::string sOptions = base64_decode(request::findValue(&req, "options"));
			std::string devoptions = CURLEncode::URLDecode(request::findValue(&req, "devoptions"));
			std::string EnergyMeterMode = CURLEncode::URLDecode(request::findValue(&req, "EnergyMeterMode"));

			char szTmp[200];

			bool bHaveUser = (session.username != "");
			int iUser = -1;
			if (bHaveUser)
			{
				iUser = FindUser(session.username.c_str());
			}

			int switchtype = -1;
			if (sswitchtype != "")
				switchtype = atoi(sswitchtype.c_str());

			if ((idx.empty()) || (sused.empty()))
				return;
			int used = (sused == "true") ? 1 : 0;
			if (maindeviceidx != "")
				used = 0;

			int CustomImage = 0;
			if (sCustomImage != "")
				CustomImage = atoi(sCustomImage.c_str());

			name = stdstring_trim(name);

			description = stdstring_trim(description);

			std::vector<std::vector<std::string> > result;

			result = m_sql.safe_query("SELECT Type,SubType,HardwareID FROM DeviceStatus WHERE (ID == '%q')", idx.c_str());
			if (result.empty())
				return;
			std::vector<std::string> sd = result[0];

			unsigned char dType = atoi(sd[0].c_str());
			int HwdID = atoi(sd[2].c_str());
			std::string sHwdID = sd[2];

			if (setPoint != "" || state != "")
			{
				double tempcelcius = atof(setPoint.c_str());
				if (m_sql.m_tempunit == TEMPUNIT_F)
				{
					tempcelcius = ConvertToCelsius(tempcelcius);
				}
				sprintf(szTmp, "%.2f", tempcelcius);

				if (dType != pTypeEvohomeZone && dType != pTypeEvohomeWater)//sql update now done in setsetpoint for evohome devices
				{
					m_sql.safe_query("UPDATE DeviceStatus SET Used=%d, sValue='%q' WHERE (ID == '%q')",
						used, szTmp, idx.c_str());
				}
			}
			if (name.empty())
			{
				m_sql.safe_query("UPDATE DeviceStatus SET Used=%d WHERE (ID == '%q')",
					used, idx.c_str());
			}
			else
			{
				if (switchtype == -1)
				{
					m_sql.safe_query("UPDATE DeviceStatus SET Used=%d, Name='%q', Description='%q' WHERE (ID == '%q')",
						used, name.c_str(), description.c_str(), idx.c_str());
				}
				else
				{
					m_sql.safe_query(
						"UPDATE DeviceStatus SET Used=%d, Name='%q', Description='%q', SwitchType=%d, CustomImage=%d WHERE (ID == '%q')",
						used, name.c_str(), description.c_str(), switchtype, CustomImage, idx.c_str());
				}
			}

			if (bHasstrParam1)
			{
				m_sql.safe_query("UPDATE DeviceStatus SET StrParam1='%q', StrParam2='%q' WHERE (ID == '%q')",
					strParam1.c_str(), strParam2.c_str(), idx.c_str());
			}

			m_sql.safe_query("UPDATE DeviceStatus SET Protected=%d WHERE (ID == '%q')", iProtected, idx.c_str());

			if (!setPoint.empty() || !state.empty())
			{
				int urights = 3;
				if (bHaveUser)
				{
					int iUser = FindUser(session.username.c_str());
					if (iUser != -1)
					{
						urights = static_cast<int>(m_users[iUser].userrights);
						_log.Log(LOG_STATUS, "User: %s initiated a SetPoint command", m_users[iUser].Username.c_str());
					}
				}
				if (urights < 1)
					return;
				if (dType == pTypeEvohomeWater)
					m_mainworker.SetSetPoint(idx, (state == "On") ? 1.0f : 0.0f, mode, until);//FIXME float not guaranteed precise?
				else if (dType == pTypeEvohomeZone)
					m_mainworker.SetSetPoint(idx, static_cast<float>(atof(setPoint.c_str())), mode, until);
				else
					m_mainworker.SetSetPoint(idx, static_cast<float>(atof(setPoint.c_str())));
			}
			else if (!clock.empty())
			{
				int urights = 3;
				if (bHaveUser)
				{
					int iUser = FindUser(session.username.c_str());
					if (iUser != -1)
					{
						urights = static_cast<int>(m_users[iUser].userrights);
						_log.Log(LOG_STATUS, "User: %s initiated a SetClock command", m_users[iUser].Username.c_str());
					}
				}
				if (urights < 1)
					return;
				m_mainworker.SetClock(idx, clock);
			}
			else if (!tmode.empty())
			{
				int urights = 3;
				if (bHaveUser)
				{
					int iUser = FindUser(session.username.c_str());
					if (iUser != -1)
					{
						urights = static_cast<int>(m_users[iUser].userrights);
						_log.Log(LOG_STATUS, "User: %s initiated a Thermostat Mode command", m_users[iUser].Username.c_str());
					}
				}
				if (urights < 1)
					return;
				m_mainworker.SetZWaveThermostatMode(idx, atoi(tmode.c_str()));
			}
			else if (!fmode.empty())
			{
				int urights = 3;
				if (bHaveUser)
				{
					int iUser = FindUser(session.username.c_str());
					if (iUser != -1)
					{
						urights = static_cast<int>(m_users[iUser].userrights);
						_log.Log(LOG_STATUS, "User: %s initiated a Thermostat Fan Mode command", m_users[iUser].Username.c_str());
					}
				}
				if (urights < 1)
					return;
				m_mainworker.SetZWaveThermostatFanMode(idx, atoi(fmode.c_str()));
			}

			if (!strunit.empty())
			{
				bool bUpdateUnit = true;
#ifdef ENABLE_PYTHON
				std::vector<std::vector<std::string> > result;
				result = m_sql.safe_query("SELECT Type FROM Hardware WHERE (ID == %d)", HwdID);
				if (!result.empty())
				{
					std::vector<std::string> sd = result[0];
					_eHardwareTypes Type = (_eHardwareTypes)atoi(sd[0].c_str());
					if (Type == HTYPE_PythonPlugin)
					{
						bUpdateUnit = false;
						_log.Log(LOG_ERROR, "CWebServer::RType_SetUsed: Not allowed to change unit of device owned by plugin %u!", HwdID);
					}
				}
#endif
				if (bUpdateUnit)
				{
					m_sql.safe_query("UPDATE DeviceStatus SET Unit='%q' WHERE (ID == '%q')",
						strunit.c_str(), idx.c_str());
				}
			}
			if (!deviceid.empty())
			{
				m_sql.safe_query("UPDATE DeviceStatus SET DeviceID='%q' WHERE (ID == '%q')",
					deviceid.c_str(), idx.c_str());
			}
			if (!addjvalue.empty())
			{
				double faddjvalue = atof(addjvalue.c_str());
				m_sql.safe_query("UPDATE DeviceStatus SET AddjValue=%f WHERE (ID == '%q')",
					faddjvalue, idx.c_str());
			}
			if (!addjmulti.empty())
			{
				double faddjmulti = atof(addjmulti.c_str());
				if (faddjmulti == 0)
					faddjmulti = 1;
				m_sql.safe_query("UPDATE DeviceStatus SET AddjMulti=%f WHERE (ID == '%q')",
					faddjmulti, idx.c_str());
			}
			if (!addjvalue2.empty())
			{
				double faddjvalue2 = atof(addjvalue2.c_str());
				m_sql.safe_query("UPDATE DeviceStatus SET AddjValue2=%f WHERE (ID == '%q')",
					faddjvalue2, idx.c_str());
			}
			if (!addjmulti2.empty())
			{
				double faddjmulti2 = atof(addjmulti2.c_str());
				if (faddjmulti2 == 0)
					faddjmulti2 = 1;
				m_sql.safe_query("UPDATE DeviceStatus SET AddjMulti2=%f WHERE (ID == '%q')",
					faddjmulti2, idx.c_str());
			}
			if (!EnergyMeterMode.empty())
			{
				auto options = m_sql.GetDeviceOptions(idx);
				options["EnergyMeterMode"] = EnergyMeterMode;
				uint64_t ullidx = std::strtoull(idx.c_str(), nullptr, 10);
				m_sql.SetDeviceOptions(ullidx, options);
			}

			if (!devoptions.empty())
			{
				m_sql.safe_query("UPDATE DeviceStatus SET Options='%q' WHERE (ID == '%q')", devoptions.c_str(), idx.c_str());
			}

			if (used == 0)
			{
				bool bRemoveSubDevices = (request::findValue(&req, "RemoveSubDevices") == "true");

				if (bRemoveSubDevices)
				{
					m_sql.safe_query("DELETE FROM LightSubDevices WHERE (DeviceRowID == '%q')", idx.c_str());
				}
				m_sql.safe_query("DELETE FROM LightSubDevices WHERE (ParentID == '%q')", idx.c_str());

				m_sql.safe_query("DELETE FROM Timers WHERE (DeviceRowID == '%q')", idx.c_str());
			}

			if (!sOptions.empty())
			{
				uint64_t ullidx = std::strtoull(idx.c_str(), nullptr, 10);
				m_sql.SetDeviceOptions(ullidx, m_sql.BuildDeviceOptions(sOptions, false));
			}

			if (maindeviceidx != "")
			{
				if (maindeviceidx != idx)
				{
					result = m_sql.safe_query("SELECT ID FROM LightSubDevices WHERE (DeviceRowID=='%q') AND (ParentID =='%q')",
						idx.c_str(), maindeviceidx.c_str());
					if (result.empty())
					{
						m_sql.safe_query(
							"INSERT INTO LightSubDevices (DeviceRowID, ParentID) VALUES ('%q','%q')",
							idx.c_str(),
							maindeviceidx.c_str()
						);
					}
				}
			}
			if ((used == 0) && (maindeviceidx.empty()))
			{
				m_sql.DeleteDevices(idx);
			}
			else
			{
#ifdef ENABLE_PYTHON
				m_mainworker.m_pluginsystem.DeviceModified(atoi(idx.c_str()));
#endif
			}
			if (!result.empty())
			{
				root["status"] = "OK";
				root["title"] = "SetUsed";
			}
			if (m_sql.m_bEnableEventSystem)
				m_mainworker.m_eventsystem.GetCurrentStates();
		}

Project: domoticz

		void CWebServer::RType_Settings(WebEmSession & session, const request& req, Json::Value &root)
		{
			std::vector<std::vector<std::string> > result;
			char szTmp[100];

			result = m_sql.safe_query("SELECT Key, nValue, sValue FROM Preferences");
			if (result.empty())
				return;
			root["status"] = "OK";
			root["title"] = "settings";
#ifndef NOCLOUD
			root["cloudenabled"] = true;
#else
			root["cloudenabled"] = false;
#endif

			for (const auto & itt : result)
			{
				std::vector<std::string> sd = itt;
				std::string Key = sd[0];
				int nValue = atoi(sd[1].c_str());
				std::string sValue = sd[2];

				if (Key == "Location")
				{
					std::vector<std::string> strarray;
					StringSplit(sValue, ";", strarray);

					if (strarray.size() == 2)
					{
						root["Location"]["Latitude"] = strarray[0];
						root["Location"]["Longitude"] = strarray[1];
					}
				}
				/* RK: notification settings */
				if (m_notifications.IsInConfig(Key)) {
					if (sValue.empty() && nValue > 0) {
						root[Key] = nValue;
					}
					else {
						root[Key] = sValue;
					}
				}
				else if (Key == "DashboardType")
				{
					root["DashboardType"] = nValue;
				}
				else if (Key == "MobileType")
				{
					root["MobileType"] = nValue;
				}
				else if (Key == "LightHistoryDays")
				{
					root["LightHistoryDays"] = nValue;
				}
				else if (Key == "5MinuteHistoryDays")
				{
					root["ShortLogDays"] = nValue;
				}
				else if (Key == "ShortLogInterval")
				{
					root["ShortLogInterval"] = nValue;
				}
				else if (Key == "WebUserName")
				{
					root["WebUserName"] = base64_decode(sValue);
				}
				else if (Key == "SecPassword")
				{
					root["SecPassword"] = sValue;
				}
				else if (Key == "ProtectionPassword")
				{
					root["ProtectionPassword"] = sValue;
				}
				else if (Key == "WebLocalNetworks")
				{
					root["WebLocalNetworks"] = sValue;
				}
				else if (Key == "WebRemoteProxyIPs")
				{
					root["WebRemoteProxyIPs"] = sValue;
				}
				else if (Key == "RandomTimerFrame")
				{
					root["RandomTimerFrame"] = nValue;
				}
				else if (Key == "MeterDividerEnergy")
				{
					root["EnergyDivider"] = nValue;
				}
				else if (Key == "MeterDividerGas")
				{
					root["GasDivider"] = nValue;
				}
				else if (Key == "MeterDividerWater")
				{
					root["WaterDivider"] = nValue;
				}
				else if (Key == "ElectricVoltage")
				{
					root["ElectricVoltage"] = nValue;
				}
				else if (Key == "CM113DisplayType")
				{
					root["CM113DisplayType"] = nValue;
				}
				else if (Key == "UseAutoUpdate")
				{
					root["UseAutoUpdate"] = nValue;
				}
				else if (Key == "UseAutoBackup")
				{
					root["UseAutoBackup"] = nValue;
				}
				else if (Key == "Rego6XXType")
				{
					root["Rego6XXType"] = nValue;
				}
				else if (Key == "CostEnergy")
				{
					sprintf(szTmp, "%.4f", (float)(nValue) / 10000.0f);
					root["CostEnergy"] = szTmp;
				}
				else if (Key == "CostEnergyT2")
				{
					sprintf(szTmp, "%.4f", (float)(nValue) / 10000.0f);
					root["CostEnergyT2"] = szTmp;
				}
				else if (Key == "CostEnergyR1")
				{
					sprintf(szTmp, "%.4f", (float)(nValue) / 10000.0f);
					root["CostEnergyR1"] = szTmp;
				}
				else if (Key == "CostEnergyR2")
				{
					sprintf(szTmp, "%.4f", (float)(nValue) / 10000.0f);
					root["CostEnergyR2"] = szTmp;
				}
				else if (Key == "CostGas")
				{
					sprintf(szTmp, "%.4f", (float)(nValue) / 10000.0f);
					root["CostGas"] = szTmp;
				}
				else if (Key == "CostWater")
				{
					sprintf(szTmp, "%.4f", (float)(nValue) / 10000.0f);
					root["CostWater"] = szTmp;
				}
				else if (Key == "ActiveTimerPlan")
				{
					root["ActiveTimerPlan"] = nValue;
				}
				else if (Key == "DoorbellCommand")
				{
					root["DoorbellCommand"] = nValue;
				}
				else if (Key == "SmartMeterType")
				{
					root["SmartMeterType"] = nValue;
				}
				else if (Key == "EnableTabFloorplans")
				{
					root["EnableTabFloorplans"] = nValue;
				}
				else if (Key == "EnableTabLights")
				{
					root["EnableTabLights"] = nValue;
				}
				else if (Key == "EnableTabTemp")
				{
					root["EnableTabTemp"] = nValue;
				}
				else if (Key == "EnableTabWeather")
				{
					root["EnableTabWeather"] = nValue;
				}
				else if (Key == "EnableTabUtility")
				{
					root["EnableTabUtility"] = nValue;
				}
				else if (Key == "EnableTabScenes")
				{
					root["EnableTabScenes"] = nValue;
				}
				else if (Key == "EnableTabCustom")
				{
					root["EnableTabCustom"] = nValue;
				}
				else if (Key == "NotificationSensorInterval")
				{
					root["NotificationSensorInterval"] = nValue;
				}
				else if (Key == "NotificationSwitchInterval")
				{
					root["NotificationSwitchInterval"] = nValue;
				}
				else if (Key == "RemoteSharedPort")
				{
					root["RemoteSharedPort"] = nValue;
				}
				else if (Key == "Language")
				{
					root["Language"] = sValue;
				}
				else if (Key == "Title")
				{
					root["Title"] = sValue;
				}
				else if (Key == "WindUnit")
				{
					root["WindUnit"] = nValue;
				}
				else if (Key == "TempUnit")
				{
					root["TempUnit"] = nValue;
				}
				else if (Key == "WeightUnit")
				{
					root["WeightUnit"] = nValue;
				}
				else if (Key == "AuthenticationMethod")
				{
					root["AuthenticationMethod"] = nValue;
				}
				else if (Key == "ReleaseChannel")
				{
					root["ReleaseChannel"] = nValue;
				}
				else if (Key == "RaspCamParams")
				{
					root["RaspCamParams"] = sValue;
				}
				else if (Key == "UVCParams")
				{
					root["UVCParams"] = sValue;
				}
				else if (Key == "AcceptNewHardware")
				{
					root["AcceptNewHardware"] = nValue;
				}
				else if (Key == "HideDisabledHardwareSensors")
				{
					root["HideDisabledHardwareSensors"] = nValue;
				}
				else if (Key == "ShowUpdateEffect")
				{
					root["ShowUpdateEffect"] = nValue;
				}
				else if (Key == "DegreeDaysBaseTemperature")
				{
					root["DegreeDaysBaseTemperature"] = sValue;
				}
				else if (Key == "EnableEventScriptSystem")
				{
					root["EnableEventScriptSystem"] = nValue;
				}
				else if (Key == "DisableDzVentsSystem")
				{
					root["DisableDzVentsSystem"] = nValue;
				}
				else if (Key == "DzVentsLogLevel")
				{
					root["DzVentsLogLevel"] = nValue;
				}
				else if (Key == "LogEventScriptTrigger")
				{
					root["LogEventScriptTrigger"] = nValue;
				}
				else if (Key == "(1WireSensorPollPeriod")
				{
					root["1WireSensorPollPeriod"] = nValue;
				}
				else if (Key == "(1WireSwitchPollPeriod")
				{
					root["1WireSwitchPollPeriod"] = nValue;
				}
				else if (Key == "SecOnDelay")
				{
					root["SecOnDelay"] = nValue;
				}
				else if (Key == "AllowWidgetOrdering")
				{
					root["AllowWidgetOrdering"] = nValue;
				}
				else if (Key == "FloorplanPopupDelay")
				{
					root["FloorplanPopupDelay"] = nValue;
				}
				else if (Key == "FloorplanFullscreenMode")
				{
					root["FloorplanFullscreenMode"] = nValue;
				}
				else if (Key == "FloorplanAnimateZoom")
				{
					root["FloorplanAnimateZoom"] = nValue;
				}
				else if (Key == "FloorplanShowSensorValues")
				{
					root["FloorplanShowSensorValues"] = nValue;
				}
				else if (Key == "FloorplanShowSwitchValues")
				{
					root["FloorplanShowSwitchValues"] = nValue;
				}
				else if (Key == "FloorplanShowSceneNames")
				{
					root["FloorplanShowSceneNames"] = nValue;
				}
				else if (Key == "FloorplanRoomColour")
				{
					root["FloorplanRoomColour"] = sValue;
				}
				else if (Key == "FloorplanActiveOpacity")
				{
					root["FloorplanActiveOpacity"] = nValue;
				}
				else if (Key == "FloorplanInactiveOpacity")
				{
					root["FloorplanInactiveOpacity"] = nValue;
				}
				else if (Key == "SensorTimeout")
				{
					root["SensorTimeout"] = nValue;
				}
				else if (Key == "BatteryLowNotification")
				{
					root["BatterLowLevel"] = nValue;
				}
				else if (Key == "WebTheme")
				{
					root["WebTheme"] = sValue;
				}
#ifndef NOCLOUD
				else if (Key == "MyDomoticzInstanceId") {
					root["MyDomoticzInstanceId"] = sValue;
				}
				else if (Key == "MyDomoticzUserId") {
					root["MyDomoticzUserId"] = sValue;
				}
				else if (Key == "MyDomoticzPassword") {
					root["MyDomoticzPassword"] = sValue;
				}
				else if (Key == "MyDomoticzSubsystems") {
					root["MyDomoticzSubsystems"] = nValue;
				}
#endif
				else if (Key == "MyDomoticzSubsystems") {
					root["MyDomoticzSubsystems"] = nValue;
				}
				else if (Key == "SendErrorsAsNotification") {
					root["SendErrorsAsNotification"] = nValue;
				}
				else if (Key == "DeltaTemperatureLog") {
					root[Key] = sValue;
				}
				else if (Key == "IFTTTEnabled") {
					root["IFTTTEnabled"] = nValue;
				}
				else if (Key == "IFTTTAPI") {
					root["IFTTTAPI"] = sValue;
				}
			}
		}

Project: domoticz
File: WebServer.cpp

		void CWebServer::RType_TextLog(WebEmSession & session, const request& req, Json::Value &root)
		{
			uint64_t idx = 0;
			if (request::findValue(&req, "idx") != "")
			{
				idx = std::strtoull(request::findValue(&req, "idx").c_str(), nullptr, 10);
			}
			std::vector<std::vector<std::string> > result;

			root["status"] = "OK";
			root["title"] = "TextLog";

			result = m_sql.safe_query("SELECT ROWID, sValue, Date FROM LightingLog WHERE (DeviceRowID==%" PRIu64 ") ORDER BY Date DESC",
				idx);
			if (!result.empty())
			{
				int ii = 0;
				for (const auto & itt : result)
				{
					std::vector<std::string> sd = itt;

					root["result"][ii]["idx"] = sd[0];
					root["result"][ii]["Date"] = sd[2];
					root["result"][ii]["Data"] = sd[1];
					ii++;
				}
			}
		}

Project: domoticz

		void CWebServer::RType_TransferDevice(WebEmSession & session, const request& req, Json::Value &root)
		{
			std::string sidx = request::findValue(&req, "idx");
			if (sidx.empty())
				return;

			std::string newidx = request::findValue(&req, "newidx");
			if (newidx.empty())
				return;

			std::vector<std::vector<std::string> > result;


			time_t now = mytime(NULL);
			struct tm tm1;
			localtime_r(&now, &tm1);
			struct tm LastUpdateTime_A;
			struct tm LastUpdateTime_B;

			result = m_sql.safe_query(
				"SELECT A.LastUpdate, B.LastUpdate FROM DeviceStatus as A, DeviceStatus as B WHERE (A.ID == '%q') AND (B.ID == '%q')",
				sidx.c_str(), newidx.c_str());
			if (result.empty())
				return;

			std::string sLastUpdate_A = result[0][0];
			std::string sLastUpdate_B = result[0][1];

			time_t timeA, timeB;
			ParseSQLdatetime(timeA, LastUpdateTime_A, sLastUpdate_A, tm1.tm_isdst);
			ParseSQLdatetime(timeB, LastUpdateTime_B, sLastUpdate_B, tm1.tm_isdst);

			if (timeA < timeB)
			{
				sidx.swap(newidx);
			}

			result = m_sql.safe_query(
				"SELECT HardwareID, DeviceID, Unit, Name, Type, SubType, SignalLevel, BatteryLevel, nValue, sValue FROM DeviceStatus WHERE (ID == '%q')",
				newidx.c_str());
			if (result.empty())
				return;

			root["status"] = "OK";
			root["title"] = "TransferDevice";

			m_sql.TransferDevice(newidx, sidx);

			m_sql.DeleteDevices(newidx);

			m_mainworker.m_scheduler.ReloadSchedules();
		}

Project: domoticz
File: WebServer.cpp

		void CWebServer::RType_Users(WebEmSession & session, const request& req, Json::Value &root)
		{
			bool bHaveUser = (session.username != "");
			int urights = 3;
			if (bHaveUser)
			{
				int iUser = FindUser(session.username.c_str());
				if (iUser != -1)
					urights = static_cast<int>(m_users[iUser].userrights);
			}
			if (urights < 2)
				return;

			root["status"] = "OK";
			root["title"] = "Users";

			std::vector<std::vector<std::string> > result;
			result = m_sql.safe_query("SELECT ID, Active, Username, Password, Rights, RemoteSharing, TabsEnabled FROM USERS ORDER BY ID ASC");
			if (!result.empty())
			{
				int ii = 0;
				for (const auto & itt : result)
				{
					std::vector<std::string> sd = itt;

					root["result"][ii]["idx"] = sd[0];
					root["result"][ii]["Enabled"] = (sd[1] == "1") ? "true" : "false";
					root["result"][ii]["Username"] = base64_decode(sd[2]);
					root["result"][ii]["Password"] = sd[3];
					root["result"][ii]["Rights"] = atoi(sd[4].c_str());
					root["result"][ii]["RemoteSharing"] = atoi(sd[5].c_str());
					root["result"][ii]["TabsEnabled"] = atoi(sd[6].c_str());
					ii++;
				}
			}
		}

Project: domoticz
File: WebServer.cpp

		void CWebServer::RegisterCommandCode(const char* idname, webserver_response_function ResponseFunction, bool bypassAuthentication)
		{
			m_webcommands.insert(std::pair<std::string, webserver_response_function >(std::string(idname), ResponseFunction));
			if (bypassAuthentication)
			{
				m_pWebEm->RegisterWhitelistURLString(idname);
			}
		}

Project: domoticz
File: WebServer.cpp

		void CWebServer::RegisterRType(const char* idname, webserver_response_function ResponseFunction)
		{
			m_webrtypes.insert(std::pair<std::string, webserver_response_function >(std::string(idname), ResponseFunction));
		}

Project: domoticz

		void CWebServer::ReloadCustomSwitchIcons()
		{
			m_custom_light_icons.clear();
			m_custom_light_icons_lookup.clear();
			std::string sLine = "";

			std::ifstream infile;
			std::string switchlightsfile = szWWWFolder + "/switch_icons.txt";
			infile.open(switchlightsfile.c_str());
			if (infile.is_open())
			{
				int index = 0;
				while (!infile.eof())
				{
					getline(infile, sLine);
					if (sLine.size() != 0)
					{
						std::vector<std::string> results;
						StringSplit(sLine, ";", results);
						if (results.size() == 3)
						{
							_tCustomIcon cImage;
							cImage.idx = index++;
							cImage.RootFile = results[0];
							cImage.Title = results[1];
							cImage.Description = results[2];
							m_custom_light_icons.push_back(cImage);
							m_custom_light_icons_lookup[cImage.idx] = m_custom_light_icons.size() - 1;
						}
					}
				}
				infile.close();
			}
			std::vector<std::vector<std::string> > result;
			result = m_sql.safe_query("SELECT ID,Base,Name,Description FROM CustomImages");
			if (!result.empty())
			{
				int ii = 0;
				for (const auto & itt : result)
				{
					std::vector<std::string> sd = itt;

					int ID = atoi(sd[0].c_str());

					_tCustomIcon cImage;
					cImage.idx = 100 + ID;
					cImage.RootFile = sd[1];
					cImage.Title = sd[2];
					cImage.Description = sd[3];

					std::string IconFile16 = cImage.RootFile + ".png";
					std::string IconFile48On = cImage.RootFile + "48_On.png";
					std::string IconFile48Off = cImage.RootFile + "48_Off.png";

					std::map<std::string, std::string> _dbImageFiles;
					_dbImageFiles["IconSmall"] = szWWWFolder + "/images/" + IconFile16;
					_dbImageFiles["IconOn"] = szWWWFolder + "/images/" + IconFile48On;
					_dbImageFiles["IconOff"] = szWWWFolder + "/images/" + IconFile48Off;

					for (const auto & iItt : _dbImageFiles)
					{
						std::string TableField = iItt.first;
						std::string IconFile = iItt.second;

						if (!file_exist(IconFile.c_str()))
						{
							std::vector<std::vector<std::string> > result2;
							result2 = m_sql.safe_queryBlob("SELECT %s FROM CustomImages WHERE ID=%d", TableField.c_str(), ID);
							if (!result2.empty())
							{
								std::ofstream file;
								file.open(IconFile.c_str(), std::ios::out | std::ios::binary);
								if (!file.is_open())
									return;

								file << result2[0][0];
								file.close();
							}
						}
					}

					m_custom_light_icons.push_back(cImage);
					m_custom_light_icons_lookup[cImage.idx] = m_custom_light_icons.size() - 1;
					ii++;
				}
			}
		}

Project: domoticz

		void CWebServer::RemoveSession(const std::string & sessionId) {
			if (sessionId.empty()) {
				return;
			}
			m_sql.safe_query(
				"DELETE FROM UserSessions WHERE SessionID = '%q'",
				sessionId.c_str());
		}

Project: domoticz
File: WebServer.cpp

		void CWebServer::RemoveUsersSessions(const std::string& username, const WebEmSession & exceptSession) {
			m_sql.safe_query("DELETE FROM UserSessions WHERE (Username=='%q') and (SessionID!='%q')", username.c_str(), exceptSession.id.c_str());
		}

Project: domoticz
File: WebServer.cpp

		void CWebServer::RestoreDatabase(WebEmSession & session, const request& req, std::string & redirect_uri)
		{
			redirect_uri = "/index.html";
			if (session.rights != 2)
			{
				session.reply_status = reply::forbidden;
				return; //Only admin user allowed
			}

			std::string dbasefile = request::findValue(&req, "dbasefile");
			if (dbasefile.empty()) {
				return;
			}

			m_mainworker.StopDomoticzHardware();

			m_sql.RestoreDatabase(dbasefile);
			m_mainworker.AddAllDomoticzHardware();
		}

Project: domoticz
File: WebServer.cpp

		void CWebServer::SetAuthenticationMethod(const _eAuthenticationMethod amethod)
		{
			if (m_pWebEm == NULL)
				return;
			m_pWebEm->SetAuthenticationMethod(amethod);
		}

Project: domoticz
File: WebServer.cpp

		void CWebServer::SetWebCompressionMode(const _eWebCompressionMode gzmode)
		{
			if (m_pWebEm == NULL)
				return;
			m_pWebEm->SetWebCompressionMode(gzmode);
		}

Project: domoticz
File: WebServer.cpp

		void CWebServer::SetWebRoot(const std::string &webRoot)
		{
			if (m_pWebEm == NULL)
				return;
			m_pWebEm->SetWebRoot(webRoot);
		}

Project: domoticz
File: WebServer.cpp

		void CWebServer::SetWebTheme(const std::string &themename)
		{
			if (m_pWebEm == NULL)
				return;
			m_pWebEm->SetWebTheme(themename);
		}

Project: domoticz

		bool CWebServer::StartServer(server_settings & settings, const std::string & serverpath, const bool bIgnoreUsernamePassword)
		{
			m_server_alias = (settings.is_secure() == true) ? "SSL" : "HTTP";

			if (!settings.is_enabled())
				return true;

			ReloadCustomSwitchIcons();

			int tries = 0;
			bool exception = false;

			do {
				try {
					exception = false;
					m_pWebEm = new http::server::cWebem(settings, serverpath.c_str());
				}
				catch (std::exception& e) {
					exception = true;
					switch (tries) {
					case 0:
						settings.listening_address = "::";
						break;
					case 1:
						settings.listening_address = "0.0.0.0";
						break;
					case 2:
						_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());
						if (atoi(settings.listening_port.c_str()) < 1024)
							_log.Log(LOG_ERROR, "WebServer(%s) check privileges for opening ports below 1024", m_server_alias.c_str());
						else
							_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());
						return false;
					}
					tries++;
				}
			} while (exception);

			_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());

			m_pWebEm->SetDigistRealm("Domoticz.com");
			m_pWebEm->SetSessionStore(this);

			if (!bIgnoreUsernamePassword)
			{
				LoadUsers();
				std::string WebLocalNetworks;
				int nValue;
				if (m_sql.GetPreferencesVar("WebLocalNetworks", nValue, WebLocalNetworks))
				{
					std::vector<std::string> strarray;
					StringSplit(WebLocalNetworks, ";", strarray);
					for (const auto & itt : strarray)
						m_pWebEm->AddLocalNetworks(itt);
					m_pWebEm->AddLocalNetworks("");
				}
			}

			std::string WebRemoteProxyIPs;
			int nValue;
			if (m_sql.GetPreferencesVar("WebRemoteProxyIPs", nValue, WebRemoteProxyIPs))
			{
				std::vector<std::string> strarray;
				StringSplit(WebRemoteProxyIPs, ";", strarray);
				for (const auto & itt : strarray)
					m_pWebEm->AddRemoteProxyIPs(itt);
			}

			m_pWebEm->RegisterIncludeCode("switchtypes", boost::bind(&CWebServer::DisplaySwitchTypesCombo, this, _1));
			m_pWebEm->RegisterIncludeCode("metertypes", boost::bind(&CWebServer::DisplayMeterTypesCombo, this, _1));
			m_pWebEm->RegisterIncludeCode("timertypes", boost::bind(&CWebServer::DisplayTimerTypesCombo, this, _1));
			m_pWebEm->RegisterIncludeCode("combolanguage", boost::bind(&CWebServer::DisplayLanguageCombo, this, _1));

			m_pWebEm->RegisterPageCode("/json.htm", boost::bind(&CWebServer::GetJSonPage, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/uploadcustomicon", boost::bind(&CWebServer::Post_UploadCustomIcon, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/html5.appcache", boost::bind(&CWebServer::GetAppCache, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/camsnapshot.jpg", boost::bind(&CWebServer::GetCameraSnapshot, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/backupdatabase.php", boost::bind(&CWebServer::GetDatabaseBackup, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/raspberry.cgi", boost::bind(&CWebServer::GetInternalCameraSnapshot, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/uvccapture.cgi", boost::bind(&CWebServer::GetInternalCameraSnapshot, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/images/floorplans/plan", boost::bind(&CWebServer::GetFloorplanImage, this, _1, _2, _3));

			m_pWebEm->RegisterActionCode("storesettings", boost::bind(&CWebServer::PostSettings, this, _1, _2, _3));
			m_pWebEm->RegisterActionCode("setrfxcommode", boost::bind(&CWebServer::SetRFXCOMMode, this, _1, _2, _3));
			m_pWebEm->RegisterActionCode("rfxupgradefirmware", boost::bind(&CWebServer::RFXComUpgradeFirmware, this, _1, _2, _3));
			RegisterCommandCode("rfxfirmwaregetpercentage", boost::bind(&CWebServer::Cmd_RFXComGetFirmwarePercentage, this, _1, _2, _3), true);
			m_pWebEm->RegisterActionCode("setrego6xxtype", boost::bind(&CWebServer::SetRego6XXType, this, _1, _2, _3));
			m_pWebEm->RegisterActionCode("sets0metertype", boost::bind(&CWebServer::SetS0MeterType, this, _1, _2, _3));
			m_pWebEm->RegisterActionCode("setlimitlesstype", boost::bind(&CWebServer::SetLimitlessType, this, _1, _2, _3));

			m_pWebEm->RegisterActionCode("uploadfloorplanimage", boost::bind(&CWebServer::UploadFloorplanImage, this, _1, _2, _3));


			m_pWebEm->RegisterActionCode("setopenthermsettings", boost::bind(&CWebServer::SetOpenThermSettings, this, _1, _2, _3));
			RegisterCommandCode("sendopenthermcommand", boost::bind(&CWebServer::Cmd_SendOpenThermCommand, this, _1, _2, _3), true);

			m_pWebEm->RegisterActionCode("reloadpiface", boost::bind(&CWebServer::ReloadPiFace, this, _1, _2, _3));
			m_pWebEm->RegisterActionCode("setcurrentcostmetertype", boost::bind(&CWebServer::SetCurrentCostUSBType, this, _1, _2, _3));
			m_pWebEm->RegisterActionCode("restoredatabase", boost::bind(&CWebServer::RestoreDatabase, this, _1, _2, _3));
			m_pWebEm->RegisterActionCode("sbfspotimportolddata", boost::bind(&CWebServer::SBFSpotImportOldData, this, _1, _2, _3));

			m_pWebEm->RegisterActionCode("event_create", boost::bind(&CWebServer::EventCreate, this, _1, _2, _3));

			RegisterCommandCode("getlanguage", boost::bind(&CWebServer::Cmd_GetLanguage, this, _1, _2, _3), true);
			RegisterCommandCode("getthemes", boost::bind(&CWebServer::Cmd_GetThemes, this, _1, _2, _3), true);
			RegisterCommandCode("gettitle", boost::bind(&CWebServer::Cmd_GetTitle, this, _1, _2, _3), true);

			RegisterCommandCode("logincheck", boost::bind(&CWebServer::Cmd_LoginCheck, this, _1, _2, _3), true);
			RegisterCommandCode("getversion", boost::bind(&CWebServer::Cmd_GetVersion, this, _1, _2, _3), true);
			RegisterCommandCode("getlog", boost::bind(&CWebServer::Cmd_GetLog, this, _1, _2, _3));
			RegisterCommandCode("clearlog", boost::bind(&CWebServer::Cmd_ClearLog, this, _1, _2, _3));
			RegisterCommandCode("getauth", boost::bind(&CWebServer::Cmd_GetAuth, this, _1, _2, _3), true);
			RegisterCommandCode("getuptime", boost::bind(&CWebServer::Cmd_GetUptime, this, _1, _2, _3), true);


			RegisterCommandCode("gethardwaretypes", boost::bind(&CWebServer::Cmd_GetHardwareTypes, this, _1, _2, _3));
			RegisterCommandCode("addhardware", boost::bind(&CWebServer::Cmd_AddHardware, this, _1, _2, _3));
			RegisterCommandCode("updatehardware", boost::bind(&CWebServer::Cmd_UpdateHardware, this, _1, _2, _3));
			RegisterCommandCode("deletehardware", boost::bind(&CWebServer::Cmd_DeleteHardware, this, _1, _2, _3));

			RegisterCommandCode("addcamera", boost::bind(&CWebServer::Cmd_AddCamera, this, _1, _2, _3));
			RegisterCommandCode("updatecamera", boost::bind(&CWebServer::Cmd_UpdateCamera, this, _1, _2, _3));
			RegisterCommandCode("deletecamera", boost::bind(&CWebServer::Cmd_DeleteCamera, this, _1, _2, _3));

			RegisterCommandCode("wolgetnodes", boost::bind(&CWebServer::Cmd_WOLGetNodes, this, _1, _2, _3));
			RegisterCommandCode("woladdnode", boost::bind(&CWebServer::Cmd_WOLAddNode, this, _1, _2, _3));
			RegisterCommandCode("wolupdatenode", boost::bind(&CWebServer::Cmd_WOLUpdateNode, this, _1, _2, _3));
			RegisterCommandCode("wolremovenode", boost::bind(&CWebServer::Cmd_WOLRemoveNode, this, _1, _2, _3));
			RegisterCommandCode("wolclearnodes", boost::bind(&CWebServer::Cmd_WOLClearNodes, this, _1, _2, _3));

			RegisterCommandCode("mysensorsgetnodes", boost::bind(&CWebServer::Cmd_MySensorsGetNodes, this, _1, _2, _3));
			RegisterCommandCode("mysensorsgetchilds", boost::bind(&CWebServer::Cmd_MySensorsGetChilds, this, _1, _2, _3));
			RegisterCommandCode("mysensorsupdatenode", boost::bind(&CWebServer::Cmd_MySensorsUpdateNode, this, _1, _2, _3));
			RegisterCommandCode("mysensorsremovenode", boost::bind(&CWebServer::Cmd_MySensorsRemoveNode, this, _1, _2, _3));
			RegisterCommandCode("mysensorsremovechild", boost::bind(&CWebServer::Cmd_MySensorsRemoveChild, this, _1, _2, _3));
			RegisterCommandCode("mysensorsupdatechild", boost::bind(&CWebServer::Cmd_MySensorsUpdateChild, this, _1, _2, _3));

			RegisterCommandCode("pingersetmode", boost::bind(&CWebServer::Cmd_PingerSetMode, this, _1, _2, _3));
			RegisterCommandCode("pingergetnodes", boost::bind(&CWebServer::Cmd_PingerGetNodes, this, _1, _2, _3));
			RegisterCommandCode("pingeraddnode", boost::bind(&CWebServer::Cmd_PingerAddNode, this, _1, _2, _3));
			RegisterCommandCode("pingerupdatenode", boost::bind(&CWebServer::Cmd_PingerUpdateNode, this, _1, _2, _3));
			RegisterCommandCode("pingerremovenode", boost::bind(&CWebServer::Cmd_PingerRemoveNode, this, _1, _2, _3));
			RegisterCommandCode("pingerclearnodes", boost::bind(&CWebServer::Cmd_PingerClearNodes, this, _1, _2, _3));

			RegisterCommandCode("kodisetmode", boost::bind(&CWebServer::Cmd_KodiSetMode, this, _1, _2, _3));
			RegisterCommandCode("kodigetnodes", boost::bind(&CWebServer::Cmd_KodiGetNodes, this, _1, _2, _3));
			RegisterCommandCode("kodiaddnode", boost::bind(&CWebServer::Cmd_KodiAddNode, this, _1, _2, _3));
			RegisterCommandCode("kodiupdatenode", boost::bind(&CWebServer::Cmd_KodiUpdateNode, this, _1, _2, _3));
			RegisterCommandCode("kodiremovenode", boost::bind(&CWebServer::Cmd_KodiRemoveNode, this, _1, _2, _3));
			RegisterCommandCode("kodiclearnodes", boost::bind(&CWebServer::Cmd_KodiClearNodes, this, _1, _2, _3));
			RegisterCommandCode("kodimediacommand", boost::bind(&CWebServer::Cmd_KodiMediaCommand, this, _1, _2, _3));

			RegisterCommandCode("panasonicsetmode", boost::bind(&CWebServer::Cmd_PanasonicSetMode, this, _1, _2, _3));
			RegisterCommandCode("panasonicgetnodes", boost::bind(&CWebServer::Cmd_PanasonicGetNodes, this, _1, _2, _3));
			RegisterCommandCode("panasonicaddnode", boost::bind(&CWebServer::Cmd_PanasonicAddNode, this, _1, _2, _3));
			RegisterCommandCode("panasonicupdatenode", boost::bind(&CWebServer::Cmd_PanasonicUpdateNode, this, _1, _2, _3));
			RegisterCommandCode("panasonicremovenode", boost::bind(&CWebServer::Cmd_PanasonicRemoveNode, this, _1, _2, _3));
			RegisterCommandCode("panasonicclearnodes", boost::bind(&CWebServer::Cmd_PanasonicClearNodes, this, _1, _2, _3));
			RegisterCommandCode("panasonicmediacommand", boost::bind(&CWebServer::Cmd_PanasonicMediaCommand, this, _1, _2, _3));

			RegisterCommandCode("heossetmode", boost::bind(&CWebServer::Cmd_HEOSSetMode, this, _1, _2, _3));
			RegisterCommandCode("heosmediacommand", boost::bind(&CWebServer::Cmd_HEOSMediaCommand, this, _1, _2, _3));

			RegisterCommandCode("onkyoeiscpcommand", boost::bind(&CWebServer::Cmd_OnkyoEiscpCommand, this, _1, _2, _3));

			RegisterCommandCode("bleboxsetmode", boost::bind(&CWebServer::Cmd_BleBoxSetMode, this, _1, _2, _3));
			RegisterCommandCode("bleboxgetnodes", boost::bind(&CWebServer::Cmd_BleBoxGetNodes, this, _1, _2, _3));
			RegisterCommandCode("bleboxaddnode", boost::bind(&CWebServer::Cmd_BleBoxAddNode, this, _1, _2, _3));
			RegisterCommandCode("bleboxremovenode", boost::bind(&CWebServer::Cmd_BleBoxRemoveNode, this, _1, _2, _3));
			RegisterCommandCode("bleboxclearnodes", boost::bind(&CWebServer::Cmd_BleBoxClearNodes, this, _1, _2, _3));
			RegisterCommandCode("bleboxautosearchingnodes", boost::bind(&CWebServer::Cmd_BleBoxAutoSearchingNodes, this, _1, _2, _3));
			RegisterCommandCode("bleboxupdatefirmware", boost::bind(&CWebServer::Cmd_BleBoxUpdateFirmware, this, _1, _2, _3));

			RegisterCommandCode("lmssetmode", boost::bind(&CWebServer::Cmd_LMSSetMode, this, _1, _2, _3));
			RegisterCommandCode("lmsgetnodes", boost::bind(&CWebServer::Cmd_LMSGetNodes, this, _1, _2, _3));
			RegisterCommandCode("lmsgetplaylists", boost::bind(&CWebServer::Cmd_LMSGetPlaylists, this, _1, _2, _3));
			RegisterCommandCode("lmsmediacommand", boost::bind(&CWebServer::Cmd_LMSMediaCommand, this, _1, _2, _3));
			RegisterCommandCode("lmsdeleteunuseddevices", boost::bind(&CWebServer::Cmd_LMSDeleteUnusedDevices, this, _1, _2, _3));

			RegisterCommandCode("savefibarolinkconfig", boost::bind(&CWebServer::Cmd_SaveFibaroLinkConfig, this, _1, _2, _3));
			RegisterCommandCode("getfibarolinkconfig", boost::bind(&CWebServer::Cmd_GetFibaroLinkConfig, this, _1, _2, _3));
			RegisterCommandCode("getfibarolinks", boost::bind(&CWebServer::Cmd_GetFibaroLinks, this, _1, _2, _3));
			RegisterCommandCode("savefibarolink", boost::bind(&CWebServer::Cmd_SaveFibaroLink, this, _1, _2, _3));
			RegisterCommandCode("deletefibarolink", boost::bind(&CWebServer::Cmd_DeleteFibaroLink, this, _1, _2, _3));

			RegisterCommandCode("saveinfluxlinkconfig", boost::bind(&CWebServer::Cmd_SaveInfluxLinkConfig, this, _1, _2, _3));
			RegisterCommandCode("getinfluxlinkconfig", boost::bind(&CWebServer::Cmd_GetInfluxLinkConfig, this, _1, _2, _3));
			RegisterCommandCode("getinfluxlinks", boost::bind(&CWebServer::Cmd_GetInfluxLinks, this, _1, _2, _3));
			RegisterCommandCode("saveinfluxlink", boost::bind(&CWebServer::Cmd_SaveInfluxLink, this, _1, _2, _3));
			RegisterCommandCode("deleteinfluxlink", boost::bind(&CWebServer::Cmd_DeleteInfluxLink, this, _1, _2, _3));

			RegisterCommandCode("savehttplinkconfig", boost::bind(&CWebServer::Cmd_SaveHttpLinkConfig, this, _1, _2, _3));
			RegisterCommandCode("gethttplinkconfig", boost::bind(&CWebServer::Cmd_GetHttpLinkConfig, this, _1, _2, _3));
			RegisterCommandCode("gethttplinks", boost::bind(&CWebServer::Cmd_GetHttpLinks, this, _1, _2, _3));
			RegisterCommandCode("savehttplink", boost::bind(&CWebServer::Cmd_SaveHttpLink, this, _1, _2, _3));
			RegisterCommandCode("deletehttplink", boost::bind(&CWebServer::Cmd_DeleteHttpLink, this, _1, _2, _3));

			RegisterCommandCode("savegooglepubsublinkconfig", boost::bind(&CWebServer::Cmd_SaveGooglePubSubLinkConfig, this, _1, _2, _3));
			RegisterCommandCode("getgooglepubsublinkconfig", boost::bind(&CWebServer::Cmd_GetGooglePubSubLinkConfig, this, _1, _2, _3));
			RegisterCommandCode("getgooglepubsublinks", boost::bind(&CWebServer::Cmd_GetGooglePubSubLinks, this, _1, _2, _3));
			RegisterCommandCode("savegooglepubsublink", boost::bind(&CWebServer::Cmd_SaveGooglePubSubLink, this, _1, _2, _3));
			RegisterCommandCode("deletegooglepubsublink", boost::bind(&CWebServer::Cmd_DeleteGooglePubSubLink, this, _1, _2, _3));

			RegisterCommandCode("getdevicevalueoptions", boost::bind(&CWebServer::Cmd_GetDeviceValueOptions, this, _1, _2, _3));
			RegisterCommandCode("getdevicevalueoptionwording", boost::bind(&CWebServer::Cmd_GetDeviceValueOptionWording, this, _1, _2, _3));

			RegisterCommandCode("adduservariable", boost::bind(&CWebServer::Cmd_AddUserVariable, this, _1, _2, _3));
			RegisterCommandCode("updateuservariable", boost::bind(&CWebServer::Cmd_UpdateUserVariable, this, _1, _2, _3));
			RegisterCommandCode("deleteuservariable", boost::bind(&CWebServer::Cmd_DeleteUserVariable, this, _1, _2, _3));
			RegisterCommandCode("getuservariables", boost::bind(&CWebServer::Cmd_GetUserVariables, this, _1, _2, _3));
			RegisterCommandCode("getuservariable", boost::bind(&CWebServer::Cmd_GetUserVariable, this, _1, _2, _3));

			RegisterCommandCode("allownewhardware", boost::bind(&CWebServer::Cmd_AllowNewHardware, this, _1, _2, _3));

			RegisterCommandCode("addplan", boost::bind(&CWebServer::Cmd_AddPlan, this, _1, _2, _3));
			RegisterCommandCode("updateplan", boost::bind(&CWebServer::Cmd_UpdatePlan, this, _1, _2, _3));
			RegisterCommandCode("deleteplan", boost::bind(&CWebServer::Cmd_DeletePlan, this, _1, _2, _3));
			RegisterCommandCode("getunusedplandevices", boost::bind(&CWebServer::Cmd_GetUnusedPlanDevices, this, _1, _2, _3));
			RegisterCommandCode("addplanactivedevice", boost::bind(&CWebServer::Cmd_AddPlanActiveDevice, this, _1, _2, _3));
			RegisterCommandCode("getplandevices", boost::bind(&CWebServer::Cmd_GetPlanDevices, this, _1, _2, _3));
			RegisterCommandCode("deleteplandevice", boost::bind(&CWebServer::Cmd_DeletePlanDevice, this, _1, _2, _3));
			RegisterCommandCode("setplandevicecoords", boost::bind(&CWebServer::Cmd_SetPlanDeviceCoords, this, _1, _2, _3));
			RegisterCommandCode("deleteallplandevices", boost::bind(&CWebServer::Cmd_DeleteAllPlanDevices, this, _1, _2, _3));
			RegisterCommandCode("changeplanorder", boost::bind(&CWebServer::Cmd_ChangePlanOrder, this, _1, _2, _3));
			RegisterCommandCode("changeplandeviceorder", boost::bind(&CWebServer::Cmd_ChangePlanDeviceOrder, this, _1, _2, _3));

			RegisterCommandCode("gettimerplans", boost::bind(&CWebServer::Cmd_GetTimerPlans, this, _1, _2, _3));
			RegisterCommandCode("addtimerplan", boost::bind(&CWebServer::Cmd_AddTimerPlan, this, _1, _2, _3));
			RegisterCommandCode("updatetimerplan", boost::bind(&CWebServer::Cmd_UpdateTimerPlan, this, _1, _2, _3));
			RegisterCommandCode("deletetimerplan", boost::bind(&CWebServer::Cmd_DeleteTimerPlan, this, _1, _2, _3));
			RegisterCommandCode("duplicatetimerplan", boost::bind(&CWebServer::Cmd_DuplicateTimerPlan, this, _1, _2, _3));

			RegisterCommandCode("getactualhistory", boost::bind(&CWebServer::Cmd_GetActualHistory, this, _1, _2, _3));
			RegisterCommandCode("getnewhistory", boost::bind(&CWebServer::Cmd_GetNewHistory, this, _1, _2, _3));

			RegisterCommandCode("getconfig", boost::bind(&CWebServer::Cmd_GetConfig, this, _1, _2, _3), true);
			RegisterCommandCode("sendnotification", boost::bind(&CWebServer::Cmd_SendNotification, this, _1, _2, _3));
			RegisterCommandCode("emailcamerasnapshot", boost::bind(&CWebServer::Cmd_EmailCameraSnapshot, this, _1, _2, _3));
			RegisterCommandCode("udevice", boost::bind(&CWebServer::Cmd_UpdateDevice, this, _1, _2, _3));
			RegisterCommandCode("udevices", boost::bind(&CWebServer::Cmd_UpdateDevices, this, _1, _2, _3));
			RegisterCommandCode("thermostatstate", boost::bind(&CWebServer::Cmd_SetThermostatState, this, _1, _2, _3));
			RegisterCommandCode("system_shutdown", boost::bind(&CWebServer::Cmd_SystemShutdown, this, _1, _2, _3));
			RegisterCommandCode("system_reboot", boost::bind(&CWebServer::Cmd_SystemReboot, this, _1, _2, _3));
			RegisterCommandCode("execute_script", boost::bind(&CWebServer::Cmd_ExcecuteScript, this, _1, _2, _3));
			RegisterCommandCode("getcosts", boost::bind(&CWebServer::Cmd_GetCosts, this, _1, _2, _3));
			RegisterCommandCode("checkforupdate", boost::bind(&CWebServer::Cmd_CheckForUpdate, this, _1, _2, _3));
			RegisterCommandCode("downloadupdate", boost::bind(&CWebServer::Cmd_DownloadUpdate, this, _1, _2, _3));
			RegisterCommandCode("downloadready", boost::bind(&CWebServer::Cmd_DownloadReady, this, _1, _2, _3));
			RegisterCommandCode("deletedatapoint", boost::bind(&CWebServer::Cmd_DeleteDatePoint, this, _1, _2, _3));

			RegisterCommandCode("setactivetimerplan", boost::bind(&CWebServer::Cmd_SetActiveTimerPlan, this, _1, _2, _3));
			RegisterCommandCode("addtimer", boost::bind(&CWebServer::Cmd_AddTimer, this, _1, _2, _3));
			RegisterCommandCode("updatetimer", boost::bind(&CWebServer::Cmd_UpdateTimer, this, _1, _2, _3));
			RegisterCommandCode("deletetimer", boost::bind(&CWebServer::Cmd_DeleteTimer, this, _1, _2, _3));
			RegisterCommandCode("enabletimer", boost::bind(&CWebServer::Cmd_EnableTimer, this, _1, _2, _3));
			RegisterCommandCode("disabletimer", boost::bind(&CWebServer::Cmd_DisableTimer, this, _1, _2, _3));
			RegisterCommandCode("cleartimers", boost::bind(&CWebServer::Cmd_ClearTimers, this, _1, _2, _3));

			RegisterCommandCode("addscenetimer", boost::bind(&CWebServer::Cmd_AddSceneTimer, this, _1, _2, _3));
			RegisterCommandCode("updatescenetimer", boost::bind(&CWebServer::Cmd_UpdateSceneTimer, this, _1, _2, _3));
			RegisterCommandCode("deletescenetimer", boost::bind(&CWebServer::Cmd_DeleteSceneTimer, this, _1, _2, _3));
			RegisterCommandCode("enablescenetimer", boost::bind(&CWebServer::Cmd_EnableSceneTimer, this, _1, _2, _3));
			RegisterCommandCode("disablescenetimer", boost::bind(&CWebServer::Cmd_DisableSceneTimer, this, _1, _2, _3));
			RegisterCommandCode("clearscenetimers", boost::bind(&CWebServer::Cmd_ClearSceneTimers, this, _1, _2, _3));
			RegisterCommandCode("getsceneactivations", boost::bind(&CWebServer::Cmd_GetSceneActivations, this, _1, _2, _3));
			RegisterCommandCode("addscenecode", boost::bind(&CWebServer::Cmd_AddSceneCode, this, _1, _2, _3));
			RegisterCommandCode("removescenecode", boost::bind(&CWebServer::Cmd_RemoveSceneCode, this, _1, _2, _3));
			RegisterCommandCode("clearscenecodes", boost::bind(&CWebServer::Cmd_ClearSceneCodes, this, _1, _2, _3));
			RegisterCommandCode("renamescene", boost::bind(&CWebServer::Cmd_RenameScene, this, _1, _2, _3));

			RegisterCommandCode("setsetpoint", boost::bind(&CWebServer::Cmd_SetSetpoint, this, _1, _2, _3));
			RegisterCommandCode("addsetpointtimer", boost::bind(&CWebServer::Cmd_AddSetpointTimer, this, _1, _2, _3));
			RegisterCommandCode("updatesetpointtimer", boost::bind(&CWebServer::Cmd_UpdateSetpointTimer, this, _1, _2, _3));
			RegisterCommandCode("deletesetpointtimer", boost::bind(&CWebServer::Cmd_DeleteSetpointTimer, this, _1, _2, _3));
			RegisterCommandCode("enablesetpointtimer", boost::bind(&CWebServer::Cmd_EnableSetpointTimer, this, _1, _2, _3));
			RegisterCommandCode("disablesetpointtimer", boost::bind(&CWebServer::Cmd_DisableSetpointTimer, this, _1, _2, _3));
			RegisterCommandCode("clearsetpointtimers", boost::bind(&CWebServer::Cmd_ClearSetpointTimers, this, _1, _2, _3));

			RegisterCommandCode("serial_devices", boost::bind(&CWebServer::Cmd_GetSerialDevices, this, _1, _2, _3));
			RegisterCommandCode("devices_list", boost::bind(&CWebServer::Cmd_GetDevicesList, this, _1, _2, _3));
			RegisterCommandCode("devices_list_onoff", boost::bind(&CWebServer::Cmd_GetDevicesListOnOff, this, _1, _2, _3));

			RegisterCommandCode("registerhue", boost::bind(&CWebServer::Cmd_PhilipsHueRegister, this, _1, _2, _3));

			RegisterCommandCode("getcustomiconset", boost::bind(&CWebServer::Cmd_GetCustomIconSet, this, _1, _2, _3));
			RegisterCommandCode("deletecustomicon", boost::bind(&CWebServer::Cmd_DeleteCustomIcon, this, _1, _2, _3));
			RegisterCommandCode("updatecustomicon", boost::bind(&CWebServer::Cmd_UpdateCustomIcon, this, _1, _2, _3));

			RegisterCommandCode("renamedevice", boost::bind(&CWebServer::Cmd_RenameDevice, this, _1, _2, _3));
			RegisterCommandCode("setunused", boost::bind(&CWebServer::Cmd_SetUnused, this, _1, _2, _3));

			RegisterCommandCode("addlogmessage", boost::bind(&CWebServer::Cmd_AddLogMessage, this, _1, _2, _3));
			RegisterCommandCode("clearshortlog", boost::bind(&CWebServer::Cmd_ClearShortLog, this, _1, _2, _3));
			RegisterCommandCode("vacuumdatabase", boost::bind(&CWebServer::Cmd_VacuumDatabase, this, _1, _2, _3));

			RegisterCommandCode("addmobiledevice", boost::bind(&CWebServer::Cmd_AddMobileDevice, this, _1, _2, _3));
			RegisterCommandCode("updatemobiledevice", boost::bind(&CWebServer::Cmd_UpdateMobileDevice, this, _1, _2, _3));
			RegisterCommandCode("deletemobiledevice", boost::bind(&CWebServer::Cmd_DeleteMobileDevice, this, _1, _2, _3));

			RegisterCommandCode("addyeelight", boost::bind(&CWebServer::Cmd_AddYeeLight, this, _1, _2, _3));

			RegisterCommandCode("addArilux", boost::bind(&CWebServer::Cmd_AddArilux, this, _1, _2, _3));

			RegisterRType("graph", boost::bind(&CWebServer::RType_HandleGraph, this, _1, _2, _3));
			RegisterRType("lightlog", boost::bind(&CWebServer::RType_LightLog, this, _1, _2, _3));
			RegisterRType("textlog", boost::bind(&CWebServer::RType_TextLog, this, _1, _2, _3));
			RegisterRType("scenelog", boost::bind(&CWebServer::RType_SceneLog, this, _1, _2, _3));
			RegisterRType("settings", boost::bind(&CWebServer::RType_Settings, this, _1, _2, _3));
			RegisterRType("events", boost::bind(&CWebServer::RType_Events, this, _1, _2, _3));

			RegisterRType("hardware", boost::bind(&CWebServer::RType_Hardware, this, _1, _2, _3));
			RegisterRType("devices", boost::bind(&CWebServer::RType_Devices, this, _1, _2, _3));
			RegisterRType("deletedevice", boost::bind(&CWebServer::RType_DeleteDevice, this, _1, _2, _3));
			RegisterRType("cameras", boost::bind(&CWebServer::RType_Cameras, this, _1, _2, _3));
			RegisterRType("cameras_user", boost::bind(&CWebServer::RType_CamerasUser, this, _1, _2, _3));
			RegisterRType("users", boost::bind(&CWebServer::RType_Users, this, _1, _2, _3));
			RegisterRType("mobiles", boost::bind(&CWebServer::RType_Mobiles, this, _1, _2, _3));

			RegisterRType("timers", boost::bind(&CWebServer::RType_Timers, this, _1, _2, _3));
			RegisterRType("scenetimers", boost::bind(&CWebServer::RType_SceneTimers, this, _1, _2, _3));
			RegisterRType("setpointtimers", boost::bind(&CWebServer::RType_SetpointTimers, this, _1, _2, _3));

			RegisterRType("gettransfers", boost::bind(&CWebServer::RType_GetTransfers, this, _1, _2, _3));
			RegisterRType("transferdevice", boost::bind(&CWebServer::RType_TransferDevice, this, _1, _2, _3));
			RegisterRType("notifications", boost::bind(&CWebServer::RType_Notifications, this, _1, _2, _3));
			RegisterRType("schedules", boost::bind(&CWebServer::RType_Schedules, this, _1, _2, _3));
			RegisterRType("getshareduserdevices", boost::bind(&CWebServer::RType_GetSharedUserDevices, this, _1, _2, _3));
			RegisterRType("setshareduserdevices", boost::bind(&CWebServer::RType_SetSharedUserDevices, this, _1, _2, _3));
			RegisterRType("setused", boost::bind(&CWebServer::RType_SetUsed, this, _1, _2, _3));
			RegisterRType("scenes", boost::bind(&CWebServer::RType_Scenes, this, _1, _2, _3));
			RegisterRType("addscene", boost::bind(&CWebServer::RType_AddScene, this, _1, _2, _3));
			RegisterRType("deletescene", boost::bind(&CWebServer::RType_DeleteScene, this, _1, _2, _3));
			RegisterRType("updatescene", boost::bind(&CWebServer::RType_UpdateScene, this, _1, _2, _3));
			RegisterRType("createvirtualsensor", boost::bind(&CWebServer::RType_CreateMappedSensor, this, _1, _2, _3));
			RegisterRType("createdevice", boost::bind(&CWebServer::RType_CreateDevice, this, _1, _2, _3));

			RegisterRType("createevohomesensor", boost::bind(&CWebServer::RType_CreateEvohomeSensor, this, _1, _2, _3));
			RegisterRType("bindevohome", boost::bind(&CWebServer::RType_BindEvohome, this, _1, _2, _3));
			RegisterRType("createrflinkdevice", boost::bind(&CWebServer::RType_CreateRFLinkDevice, this, _1, _2, _3));

			RegisterRType("custom_light_icons", boost::bind(&CWebServer::RType_CustomLightIcons, this, _1, _2, _3));
			RegisterRType("plans", boost::bind(&CWebServer::RType_Plans, this, _1, _2, _3));
			RegisterRType("floorplans", boost::bind(&CWebServer::RType_FloorPlans, this, _1, _2, _3));
#ifdef WITH_OPENZWAVE
			RegisterCommandCode("updatezwavenode", boost::bind(&CWebServer::Cmd_ZWaveUpdateNode, this, _1, _2, _3));
			RegisterCommandCode("deletezwavenode", boost::bind(&CWebServer::Cmd_ZWaveDeleteNode, this, _1, _2, _3));
			RegisterCommandCode("zwaveinclude", boost::bind(&CWebServer::Cmd_ZWaveInclude, this, _1, _2, _3));
			RegisterCommandCode("zwaveexclude", boost::bind(&CWebServer::Cmd_ZWaveExclude, this, _1, _2, _3));

			RegisterCommandCode("zwaveisnodeincluded", boost::bind(&CWebServer::Cmd_ZWaveIsNodeIncluded, this, _1, _2, _3));
			RegisterCommandCode("zwaveisnodeexcluded", boost::bind(&CWebServer::Cmd_ZWaveIsNodeExcluded, this, _1, _2, _3));

			RegisterCommandCode("zwavesoftreset", boost::bind(&CWebServer::Cmd_ZWaveSoftReset, this, _1, _2, _3));
			RegisterCommandCode("zwavehardreset", boost::bind(&CWebServer::Cmd_ZWaveHardReset, this, _1, _2, _3));
			RegisterCommandCode("zwavenetworkheal", boost::bind(&CWebServer::Cmd_ZWaveNetworkHeal, this, _1, _2, _3));
			RegisterCommandCode("zwavenodeheal", boost::bind(&CWebServer::Cmd_ZWaveNodeHeal, this, _1, _2, _3));
			RegisterCommandCode("zwavenetworkinfo", boost::bind(&CWebServer::Cmd_ZWaveNetworkInfo, this, _1, _2, _3));
			RegisterCommandCode("zwaveremovegroupnode", boost::bind(&CWebServer::Cmd_ZWaveRemoveGroupNode, this, _1, _2, _3));
			RegisterCommandCode("zwaveaddgroupnode", boost::bind(&CWebServer::Cmd_ZWaveAddGroupNode, this, _1, _2, _3));
			RegisterCommandCode("zwavegroupinfo", boost::bind(&CWebServer::Cmd_ZWaveGroupInfo, this, _1, _2, _3));
			RegisterCommandCode("zwavecancel", boost::bind(&CWebServer::Cmd_ZWaveCancel, this, _1, _2, _3));
			RegisterCommandCode("applyzwavenodeconfig", boost::bind(&CWebServer::Cmd_ApplyZWaveNodeConfig, this, _1, _2, _3));
			RegisterCommandCode("requestzwavenodeconfig", boost::bind(&CWebServer::Cmd_ZWaveRequestNodeConfig, this, _1, _2, _3));
			RegisterCommandCode("zwavestatecheck", boost::bind(&CWebServer::Cmd_ZWaveStateCheck, this, _1, _2, _3));
			RegisterCommandCode("zwavereceiveconfigurationfromothercontroller", boost::bind(&CWebServer::Cmd_ZWaveReceiveConfigurationFromOtherController, this, _1, _2, _3));
			RegisterCommandCode("zwavesendconfigurationtosecondcontroller", boost::bind(&CWebServer::Cmd_ZWaveSendConfigurationToSecondaryController, this, _1, _2, _3));
			RegisterCommandCode("zwavetransferprimaryrole", boost::bind(&CWebServer::Cmd_ZWaveTransferPrimaryRole, this, _1, _2, _3));
			RegisterCommandCode("zwavestartusercodeenrollmentmode", boost::bind(&CWebServer::Cmd_ZWaveSetUserCodeEnrollmentMode, this, _1, _2, _3));
			RegisterCommandCode("zwavegetusercodes", boost::bind(&CWebServer::Cmd_ZWaveGetNodeUserCodes, this, _1, _2, _3));
			RegisterCommandCode("zwaveremoveusercode", boost::bind(&CWebServer::Cmd_ZWaveRemoveUserCode, this, _1, _2, _3));

			m_pWebEm->RegisterPageCode("/zwavegetconfig.php", boost::bind(&CWebServer::ZWaveGetConfigFile, this, _1, _2, _3));

			m_pWebEm->RegisterPageCode("/ozwcp/poll.xml", boost::bind(&CWebServer::ZWaveCPPollXml, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/ozwcp/cp.html", boost::bind(&CWebServer::ZWaveCPIndex, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/ozwcp/confparmpost.html", boost::bind(&CWebServer::ZWaveCPNodeGetConf, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/ozwcp/refreshpost.html", boost::bind(&CWebServer::ZWaveCPNodeGetValues, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/ozwcp/valuepost.html", boost::bind(&CWebServer::ZWaveCPNodeSetValue, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/ozwcp/buttonpost.html", boost::bind(&CWebServer::ZWaveCPNodeSetButton, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/ozwcp/admpost.html", boost::bind(&CWebServer::ZWaveCPAdminCommand, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/ozwcp/nodepost.html", boost::bind(&CWebServer::ZWaveCPNodeChange, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/ozwcp/savepost.html", boost::bind(&CWebServer::ZWaveCPSaveConfig, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/ozwcp/thpost.html", boost::bind(&CWebServer::ZWaveCPTestHeal, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/ozwcp/topopost.html", boost::bind(&CWebServer::ZWaveCPGetTopo, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/ozwcp/statpost.html", boost::bind(&CWebServer::ZWaveCPGetStats, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/ozwcp/grouppost.html", boost::bind(&CWebServer::ZWaveCPSetGroup, this, _1, _2, _3));
			m_pWebEm->RegisterPageCode("/ozwcp/scenepost.html", boost::bind(&CWebServer::ZWaveCPSceneCommand, this, _1, _2, _3));
			RegisterRType("openzwavenodes", boost::bind(&CWebServer::RType_OpenZWaveNodes, this, _1, _2, _3));
#endif
			RegisterCommandCode("tellstickApplySettings", boost::bind(&CWebServer::Cmd_TellstickApplySettings, this, _1, _2, _3));

			m_pWebEm->RegisterWhitelistURLString("/html5.appcache");
			m_pWebEm->RegisterWhitelistURLString("/images/floorplans/plan");

			m_bDoStop = false;
			m_thread = std::make_shared<std::thread>(&CWebServer::Do_Work, this);
			std::string server_name = "WebServer_" + settings.listening_port;
			SetThreadName(m_thread->native_handle(), server_name.c_str());
			return (m_thread != nullptr);
		}

Project: domoticz

		void CWebServer::StoreSession(const WebEmStoredSession & session) {
			if (session.id.empty()) {
				_log.Log(LOG_ERROR, "SessionStore : cannot store session without id.");
				return;
			}

			char szExpires[30];
			struct tm ltime;
			localtime_r(&session.expires, &ltime);
			strftime(szExpires, sizeof(szExpires), "%Y-%m-%d %H:%M:%S", &ltime);

			std::string remote_host = (session.remote_host.size() <= 50) ? // IPv4 : 15, IPv6 : (39|45)
				session.remote_host : session.remote_host.substr(0, 50);

			WebEmStoredSession storedSession = GetSession(session.id);
			if (storedSession.id.empty()) {
				m_sql.safe_query(
					"INSERT INTO UserSessions (SessionID, Username, AuthToken, ExpirationDate, RemoteHost) VALUES ('%q', '%q', '%q', '%q', '%q')",
					session.id.c_str(),
					base64_encode(session.username).c_str(),
					session.auth_token.c_str(),
					szExpires,
					remote_host.c_str());
			}
			else {
				m_sql.safe_query(
					"UPDATE UserSessions set AuthToken = '%q', ExpirationDate = '%q', RemoteHost = '%q', LastUpdate = datetime('now', 'localtime') WHERE SessionID = '%q'",
					session.auth_token.c_str(),
					szExpires,
					remote_host.c_str(),
					session.id.c_str());
			}
		}

Project: domoticz
File: WebServer.cpp

		void CWebServer::UploadFloorplanImage(WebEmSession & session, const request& req, std::string & redirect_uri)
		{
			redirect_uri = "/index.html";
			if (session.rights != 2)
			{
				session.reply_status = reply::forbidden;
				return; //Only admin user allowed
			}

			std::string planname = request::findValue(&req, "planname");
			std::string scalefactor = request::findValue(&req, "scalefactor");
			std::string imagefile = request::findValue(&req, "imagefile");

			std::vector<std::vector<std::string> > result;
			m_sql.safe_query("INSERT INTO Floorplans ([Name],[ScaleFactor]) VALUES('%s','%s')", planname.c_str(),scalefactor.c_str());
			result = m_sql.safe_query("SELECT MAX(ID) FROM Floorplans");
			if (!result.empty())
			{
				if (!m_sql.safe_UpdateBlobInTableWithID("Floorplans", "Image", result[0][0], imagefile))
					_log.Log(LOG_ERROR, "SQL: Problem inserting floorplan image into database! ");
			}
		}

Project: domoticz
File: WebServer.cpp

		bool compareIconsByName(const http::server::CWebServer::_tCustomIcon &a, const http::server::CWebServer::_tCustomIcon &b)
		{
			return a.Title < b.Title;
		}

Project: domoticz

		CWebServer::~CWebServer(void)
		{
			StopServer();
		}