1 #include "stdafx.h"
2 #include "mainworker.h"
3 #include "Helper.h"
4 #include "SunRiseSet.h"
5 #include "localtime_r.h"
6 #include "Logger.h"
7 #include "WebServerHelper.h"
8 #include "SQLHelper.h"
9 #include "../push/FibaroPush.h"
10 #include "../push/HttpPush.h"
11 #include "../push/InfluxPush.h"
12 #include "../push/GooglePubSubPush.h"
13 
14 #include "../httpclient/HTTPClient.h"
15 #include "../webserver/Base64.h"
16 #include <boost/algorithm/string/join.hpp>
17 #include "../main/json_helper.h"
18 
19 #include <boost/crc.hpp>
20 #include <algorithm>
21 #include <set>
22 
23 //Hardware Devices
24 #include "../hardware/hardwaretypes.h"
25 #include "../hardware/RFXBase.h"
26 #include "../hardware/RFXComSerial.h"
27 #include "../hardware/RFXComTCP.h"
28 #include "../hardware/DomoticzTCP.h"
29 #include "../hardware/P1MeterBase.h"
30 #include "../hardware/P1MeterSerial.h"
31 #include "../hardware/P1MeterTCP.h"
32 #include "../hardware/YouLess.h"
33 #ifdef WITH_LIBUSB
34 #include "../hardware/TE923.h"
35 #include "../hardware/VolcraftCO20.h"
36 #endif
37 #include "../hardware/Rego6XXSerial.h"
38 #ifdef WITH_OPENZWAVE
39 #include "../hardware/OpenZWave.h"
40 #endif
41 #include "../hardware/DavisLoggerSerial.h"
42 #include "../hardware/1Wire.h"
43 #include "../hardware/I2C.h"
44 #include "../hardware/Wunderground.h"
45 #include "../hardware/DarkSky.h"
46 #include "../hardware/HardwareMonitor.h"
47 #include "../hardware/Dummy.h"
48 #include "../hardware/Tellstick.h"
49 #include "../hardware/PiFace.h"
50 #include "../hardware/S0MeterSerial.h"
51 #include "../hardware/S0MeterTCP.h"
52 #include "../hardware/OTGWSerial.h"
53 #include "../hardware/OTGWTCP.h"
54 #include "../hardware/TeleinfoBase.h"
55 #include "../hardware/TeleinfoSerial.h"
56 #include "../hardware/Limitless.h"
57 #include "../hardware/MochadTCP.h"
58 #include "../hardware/EnOceanESP2.h"
59 #include "../hardware/EnOceanESP3.h"
60 #include "../hardware/SBFSpot.h"
61 #include "../hardware/PhilipsHue/PhilipsHue.h"
62 #include "../hardware/ICYThermostat.h"
63 #include "../hardware/WOL.h"
64 #include "../hardware/Meteostick.h"
65 #include "../hardware/PVOutput_Input.h"
66 #include "../hardware/ToonThermostat.h"
67 #include "../hardware/HarmonyHub.h"
68 #include "../hardware/EcoDevices.h"
69 #include "../hardware/EvohomeBase.h"
70 #include "../hardware/EvohomeScript.h"
71 #include "../hardware/EvohomeSerial.h"
72 #include "../hardware/EvohomeTCP.h"
73 #include "../hardware/EvohomeWeb.h"
74 #include "../hardware/MySensorsSerial.h"
75 #include "../hardware/MySensorsTCP.h"
76 #include "../hardware/MySensorsMQTT.h"
77 #include "../hardware/MQTT.h"
78 #include "../hardware/FritzboxTCP.h"
79 #include "../hardware/ETH8020.h"
80 #include "../hardware/RFLinkSerial.h"
81 #include "../hardware/RFLinkTCP.h"
82 #include "../hardware/KMTronicSerial.h"
83 #include "../hardware/KMTronicTCP.h"
84 #include "../hardware/KMTronicUDP.h"
85 #include "../hardware/KMTronic433.h"
86 #include "../hardware/SolarMaxTCP.h"
87 #include "../hardware/Pinger.h"
88 #include "../hardware/Nest.h"
89 #include "../hardware/NestOAuthAPI.h"
90 #include "../hardware/Thermosmart.h"
91 #include "../hardware/Tado.h"
92 #include "../hardware/eVehicles/eVehicle.h"
93 #include "../hardware/Kodi.h"
94 #include "../hardware/Netatmo.h"
95 #include "../hardware/HttpPoller.h"
96 #include "../hardware/AnnaThermostat.h"
97 #include "../hardware/Winddelen.h"
98 #include "../hardware/SatelIntegra.h"
99 #include "../hardware/LogitechMediaServer.h"
100 #include "../hardware/Comm5TCP.h"
101 #include "../hardware/Comm5SMTCP.h"
102 #include "../hardware/Comm5Serial.h"
103 #include "../hardware/CurrentCostMeterSerial.h"
104 #include "../hardware/CurrentCostMeterTCP.h"
105 #include "../hardware/SolarEdgeAPI.h"
106 #include "../hardware/DomoticzInternal.h"
107 #include "../hardware/NefitEasy.h"
108 #include "../hardware/PanasonicTV.h"
109 #include "../hardware/OpenWebNetTCP.h"
110 #include "../hardware/AtagOne.h"
111 #include "../hardware/Sterbox.h"
112 #include "../hardware/RAVEn.h"
113 #include "../hardware/DenkoviDevices.h"
114 #include "../hardware/DenkoviUSBDevices.h"
115 #include "../hardware/DenkoviTCPDevices.h"
116 #include "../hardware/AccuWeather.h"
117 #include "../hardware/BleBox.h"
118 #include "../hardware/Ec3kMeterTCP.h"
119 #include "../hardware/OpenWeatherMap.h"
120 #include "../hardware/GoodweAPI.h"
121 #include "../hardware/Daikin.h"
122 #include "../hardware/HEOS.h"
123 #include "../hardware/MultiFun.h"
124 #include "../hardware/ZiBlueSerial.h"
125 #include "../hardware/ZiBlueTCP.h"
126 #include "../hardware/Yeelight.h"
127 #include "../hardware/XiaomiGateway.h"
128 #ifdef ENABLE_PYTHON
129 #include "../hardware/plugins/Plugins.h"
130 #endif
131 #include "../hardware/Arilux.h"
132 #include "../hardware/OpenWebNetUSB.h"
133 #include "../hardware/InComfort.h"
134 #include "../hardware/RelayNet.h"
135 #include "../hardware/SysfsGpio.h"
136 #include "../hardware/Rtl433.h"
137 #include "../hardware/OnkyoAVTCP.h"
138 #include "../hardware/USBtin.h"
139 #include "../hardware/USBtin_MultiblocV8.h"
140 #include "../hardware/EnphaseAPI.h"
141 #include "../hardware/eHouseTCP.h"
142 #include "../hardware/EcoCompteur.h"
143 #include "../hardware/Honeywell.h"
144 #include "../hardware/TTNMQTT.h"
145 #include "../hardware/Buienradar.h"
146 #include "../hardware/OctoPrintMQTT.h"
147 
148 // load notifications configuration
149 #include "../notifications/NotificationHelper.h"
150 
151 #ifdef WITH_GPIO
152 #include "../hardware/Gpio.h"
153 #include "../hardware/GpioPin.h"
154 #endif
155 
156 #ifdef WIN32
157 #include "../msbuild/WindowsHelper.h"
158 #include "dirent_windows.h"
159 #else
160 #include <sys/utsname.h>
161 #include <dirent.h>
162 #endif
163 
164 #include "mainstructs.h"
165 #define __STDC_FORMAT_MACROS
166 #include <inttypes.h>
167 
168 #ifdef _DEBUG
169 //#define PARSE_RFXCOM_DEVICE_LOG
170 //#define DEBUG_DOWNLOAD
171 //#define DEBUG_RXQUEUE
172 #endif
173 
174 #ifdef PARSE_RFXCOM_DEVICE_LOG
175 #include <iostream>
176 #include <fstream>
177 #endif
178 
179 #define round(a) ( int ) ( a + .5 )
180 
181 extern std::string szStartupFolder;
182 extern std::string szUserDataFolder;
183 extern std::string szWWWFolder;
184 extern int iAppRevision;
185 extern std::string szWebRoot;
186 extern bool g_bUseUpdater;
187 extern http::server::_eWebCompressionMode g_wwwCompressMode;
188 extern http::server::CWebServerHelper m_webservers;
189 extern bool g_bUseEventTrigger;
190 extern std::string szRandomUUID;
191 
192 CFibaroPush m_fibaropush;
193 CGooglePubSubPush m_googlepubsubpush;
194 CHttpPush m_httppush;
195 CInfluxPush m_influxpush;
196 
197 
198 namespace tcp {
199 	namespace server {
200 		class CTCPClient;
201 	} //namespace server
202 } //namespace tcp
203 
MainWorker()204 MainWorker::MainWorker()
205 {
206 	m_SecCountdown = -1;
207 
208 	m_bStartHardware = false;
209 	m_hardwareStartCounter = 0;
210 
211 	// Set default settings for web servers
212 	m_webserver_settings.listening_address = "::"; // listen to all network interfaces
213 	m_webserver_settings.listening_port = "8080";
214 #ifdef WWW_ENABLE_SSL
215 	m_secure_webserver_settings.listening_address = "::"; // listen to all network interfaces
216 	m_secure_webserver_settings.listening_port = "443";
217 	m_secure_webserver_settings.ssl_method = "sslv23";
218 	m_secure_webserver_settings.certificate_chain_file_path = "./server_cert.pem";
219 	m_secure_webserver_settings.ca_cert_file_path = m_secure_webserver_settings.certificate_chain_file_path; // not used
220 	m_secure_webserver_settings.cert_file_path = m_secure_webserver_settings.certificate_chain_file_path;
221 	m_secure_webserver_settings.private_key_file_path = m_secure_webserver_settings.certificate_chain_file_path;
222 	m_secure_webserver_settings.private_key_pass_phrase = "";
223 	m_secure_webserver_settings.ssl_options = "default_workarounds,no_sslv2,no_sslv3,no_tlsv1,no_tlsv1_1,single_dh_use";
224 	m_secure_webserver_settings.tmp_dh_file_path = m_secure_webserver_settings.certificate_chain_file_path;
225 	m_secure_webserver_settings.verify_peer = false;
226 	m_secure_webserver_settings.verify_fail_if_no_peer_cert = false;
227 	m_secure_webserver_settings.verify_file_path = "";
228 #endif
229 	m_bIgnoreUsernamePassword = false;
230 
231 	time_t atime = mytime(NULL);
232 	m_LastHeartbeat = atime;
233 	struct tm ltime;
234 	localtime_r(&atime, &ltime);
235 	m_ScheduleLastMinute = ltime.tm_min;
236 	m_ScheduleLastHour = ltime.tm_hour;
237 	m_ScheduleLastMinuteTime = 0;
238 	m_ScheduleLastHourTime = 0;
239 	m_ScheduleLastDayTime = 0;
240 	m_LastSunriseSet = "";
241 	m_DayLength = "";
242 
243 	m_bHaveDownloadedDomoticzUpdate = false;
244 	m_bHaveDownloadedDomoticzUpdateSuccessFull = false;
245 	m_bDoDownloadDomoticzUpdate = false;
246 	m_LastUpdateCheck = 0;
247 	m_bHaveUpdate = false;
248 	m_iRevision = 0;
249 
250 	m_SecStatus = SECSTATUS_DISARMED;
251 
252 	m_rxMessageIdx = 1;
253 	m_bForceLogNotificationCheck = false;
254 }
255 
~MainWorker()256 MainWorker::~MainWorker()
257 {
258 	Stop();
259 }
260 
AddAllDomoticzHardware()261 void MainWorker::AddAllDomoticzHardware()
262 {
263 	//Add Hardware devices
264 	std::vector<std::vector<std::string> > result;
265 	result = m_sql.safe_query(
266 		"SELECT ID, Name, Enabled, Type, Address, Port, SerialPort, Username, Password, Extra, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6, DataTimeout FROM Hardware ORDER BY ID ASC");
267 	if (!result.empty())
268 	{
269 		for (const auto& itt : result)
270 		{
271 			std::vector<std::string> sd = itt;
272 
273 			int ID = atoi(sd[0].c_str());
274 			std::string Name = sd[1];
275 			std::string sEnabled = sd[2];
276 			bool Enabled = (sEnabled == "1") ? true : false;
277 			_eHardwareTypes Type = (_eHardwareTypes)atoi(sd[3].c_str());
278 			std::string Address = sd[4];
279 			uint16_t Port = (uint16_t)atoi(sd[5].c_str());
280 			std::string SerialPort = sd[6];
281 			std::string Username = sd[7];
282 			std::string Password = sd[8];
283 			std::string Extra = sd[9];
284 			int mode1 = atoi(sd[10].c_str());
285 			int mode2 = atoi(sd[11].c_str());
286 			int mode3 = atoi(sd[12].c_str());
287 			int mode4 = atoi(sd[13].c_str());
288 			int mode5 = atoi(sd[14].c_str());
289 			int mode6 = atoi(sd[15].c_str());
290 			int DataTimeout = atoi(sd[16].c_str());
291 			AddHardwareFromParams(ID, Name, Enabled, Type, Address, Port, SerialPort, Username, Password, Extra, mode1, mode2, mode3, mode4, mode5, mode6, DataTimeout, false);
292 		}
293 		m_hardwareStartCounter = 0;
294 		m_bStartHardware = true;
295 	}
296 }
297 
StartDomoticzHardware()298 void MainWorker::StartDomoticzHardware()
299 {
300 	std::vector<CDomoticzHardwareBase*>::iterator itt;
301 	for (auto& itt : m_hardwaredevices)
302 	{
303 		if (!itt->IsStarted())
304 		{
305 			itt->Start();
306 		}
307 	}
308 }
309 
StopDomoticzHardware()310 void MainWorker::StopDomoticzHardware()
311 {
312 	// Separate the Stop() from the device removal from the vector.
313 	// Some actions the hardware might take during stop (e.g updating a device) can cause deadlocks on the m_devicemutex
314 	std::vector<CDomoticzHardwareBase*> OrgHardwaredevices;
315 	{
316 		std::lock_guard<std::mutex> l(m_devicemutex);
317 		for (auto& itt : m_hardwaredevices)
318 		{
319 			OrgHardwaredevices.push_back(itt);
320 		}
321 		m_hardwaredevices.clear();
322 	}
323 
324 	for (auto& itt : OrgHardwaredevices)
325 	{
326 #ifdef ENABLE_PYTHON
327 		m_pluginsystem.DeregisterPlugin(itt->m_HwdID);
328 #endif
329 		itt->Stop();
330 		delete itt;
331 	}
332 }
333 
GetAvailableWebThemes()334 void MainWorker::GetAvailableWebThemes()
335 {
336 	std::string ThemeFolder = szWWWFolder + "/styles/";
337 	m_webthemes.clear();
338 	DirectoryListing(m_webthemes, ThemeFolder, true, false);
339 
340 	//check if current theme is found, if not, select default
341 	bool bFound = false;
342 	std::string sValue;
343 	if (m_sql.GetPreferencesVar("WebTheme", sValue))
344 	{
345 		for (const auto& itt : m_webthemes)
346 		{
347 			if (itt == sValue)
348 			{
349 				bFound = true;
350 				break;
351 			}
352 		}
353 	}
354 	if (!bFound)
355 	{
356 		m_sql.UpdatePreferencesVar("WebTheme", "default");
357 	}
358 }
359 
AddDomoticzHardware(CDomoticzHardwareBase * pHardware)360 void MainWorker::AddDomoticzHardware(CDomoticzHardwareBase* pHardware)
361 {
362 	int devidx = FindDomoticzHardware(pHardware->m_HwdID);
363 	if (devidx != -1) //it is already there!, remove it
364 	{
365 		RemoveDomoticzHardware(m_hardwaredevices[devidx]);
366 	}
367 	std::lock_guard<std::mutex> l(m_devicemutex);
368 	pHardware->sDecodeRXMessage.connect(boost::bind(&MainWorker::DecodeRXMessage, this, _1, _2, _3, _4));
369 	pHardware->sOnConnected.connect(boost::bind(&MainWorker::OnHardwareConnected, this, _1));
370 	m_hardwaredevices.push_back(pHardware);
371 }
372 
RemoveDomoticzHardware(CDomoticzHardwareBase * pHardware)373 void MainWorker::RemoveDomoticzHardware(CDomoticzHardwareBase* pHardware)
374 {
375 	// Separate the Stop() from the device removal from the vector.
376 	// Some actions the hardware might take during stop (e.g updating a device) can cause deadlocks on the m_devicemutex
377 	CDomoticzHardwareBase* pOrgHardware = NULL;
378 	{
379 		std::lock_guard<std::mutex> l(m_devicemutex);
380 		std::vector<CDomoticzHardwareBase*>::iterator itt;
381 		for (itt = m_hardwaredevices.begin(); itt != m_hardwaredevices.end(); ++itt)
382 		{
383 			pOrgHardware = *itt;
384 			if (pOrgHardware == pHardware) {
385 				m_hardwaredevices.erase(itt);
386 				break;
387 			}
388 		}
389 	}
390 
391 	if (pOrgHardware == pHardware)
392 	{
393 		try
394 		{
395 			pOrgHardware->Stop();
396 			delete pOrgHardware;
397 		}
398 		catch (std::exception& e)
399 		{
400 			_log.Log(LOG_ERROR, "Mainworker: Exception: %s (%s:%d)", e.what(), std::string(__func__).substr(std::string(__func__).find_last_of("/\\") + 1).c_str(), __LINE__);
401 		}
402 		catch (...)
403 		{
404 			_log.Log(LOG_ERROR, "Mainworker: Exception catched! %s:%d", std::string(__func__).substr(std::string(__func__).find_last_of("/\\") + 1).c_str(), __LINE__);
405 		}
406 	}
407 }
408 
RemoveDomoticzHardware(int HwdId)409 void MainWorker::RemoveDomoticzHardware(int HwdId)
410 {
411 	int dpos = FindDomoticzHardware(HwdId);
412 	if (dpos == -1)
413 		return;
414 #ifdef ENABLE_PYTHON
415 	m_pluginsystem.DeregisterPlugin(HwdId);
416 #endif
417 	RemoveDomoticzHardware(m_hardwaredevices[dpos]);
418 }
419 
FindDomoticzHardware(int HwdId)420 int MainWorker::FindDomoticzHardware(int HwdId)
421 {
422 	std::lock_guard<std::mutex> l(m_devicemutex);
423 	int ii = 0;
424 	for (const auto& itt : m_hardwaredevices)
425 	{
426 		if (itt->m_HwdID == HwdId)
427 			return ii;
428 		ii++;
429 	}
430 	return -1;
431 }
432 
FindDomoticzHardwareByType(const _eHardwareTypes HWType)433 int MainWorker::FindDomoticzHardwareByType(const _eHardwareTypes HWType)
434 {
435 	std::lock_guard<std::mutex> l(m_devicemutex);
436 	int ii = 0;
437 	for (const auto& itt : m_hardwaredevices)
438 	{
439 		if (itt->HwdType == HWType)
440 			return ii;
441 		ii++;
442 	}
443 	return -1;
444 }
445 
GetHardware(int HwdId)446 CDomoticzHardwareBase* MainWorker::GetHardware(int HwdId)
447 {
448 	std::lock_guard<std::mutex> l(m_devicemutex);
449 	for (auto& itt : m_hardwaredevices)
450 	{
451 		if (itt->m_HwdID == HwdId)
452 			return itt;
453 	}
454 	return NULL;
455 }
456 
GetHardwareByIDType(const std::string & HwdId,const _eHardwareTypes HWType)457 CDomoticzHardwareBase* MainWorker::GetHardwareByIDType(const std::string& HwdId, const _eHardwareTypes HWType)
458 {
459 	if (HwdId == "")
460 		return NULL;
461 	int iHardwareID = atoi(HwdId.c_str());
462 	CDomoticzHardwareBase* pHardware = m_mainworker.GetHardware(iHardwareID);
463 	if (pHardware == NULL)
464 		return NULL;
465 	if (pHardware->HwdType != HWType)
466 		return NULL;
467 	return pHardware;
468 }
469 
GetHardwareByType(const _eHardwareTypes HWType)470 CDomoticzHardwareBase* MainWorker::GetHardwareByType(const _eHardwareTypes HWType)
471 {
472 	std::lock_guard<std::mutex> l(m_devicemutex);
473 	for (auto& itt : m_hardwaredevices)
474 	{
475 		if (itt->HwdType == HWType)
476 			return itt;
477 	}
478 	return NULL;
479 }
480 
481 // sunset/sunrise
482 // http://www.earthtools.org/sun/<latitude>/<longitude>/<day>/<month>/<timezone>/<dst>
483 // example:
484 // http://www.earthtools.org/sun/52.214268/5.171002/11/11/99/1
485 
GetSunSettings()486 bool MainWorker::GetSunSettings()
487 {
488 	int nValue;
489 	std::string sValue;
490 	std::vector<std::string> strarray;
491 	if (m_sql.GetPreferencesVar("Location", nValue, sValue))
492 		StringSplit(sValue, ";", strarray);
493 
494 	if (strarray.size() != 2)
495 	{
496 		// No location entered in the settings, lets just reload our schedules and return
497 		// Load non sun settings timers
498 		m_scheduler.ReloadSchedules();
499 		return false;
500 	}
501 
502 	std::string Latitude = strarray[0];
503 	std::string Longitude = strarray[1];
504 
505 	time_t atime = mytime(NULL);
506 	struct tm ltime;
507 	localtime_r(&atime, &ltime);
508 
509 	int year = ltime.tm_year + 1900;
510 	int month = ltime.tm_mon + 1;
511 	int day = ltime.tm_mday;
512 
513 	double dLatitude = atof(Latitude.c_str());
514 	double dLongitude = atof(Longitude.c_str());
515 
516 	SunRiseSet::_tSubRiseSetResults sresult;
517 	SunRiseSet::GetSunRiseSet(dLatitude, dLongitude, year, month, day, sresult);
518 
519 	std::string sunrise;
520 	std::string sunset;
521 	std::string daylength;
522 	std::string sunatsouth;
523 	std::string civtwstart;
524 	std::string civtwend;
525 	std::string nauttwstart;
526 	std::string nauttwend;
527 	std::string asttwstart;
528 	std::string asttwend;
529 
530 	char szRiseSet[30];
531 	sprintf(szRiseSet, "%02d:%02d:00", sresult.SunRiseHour, sresult.SunRiseMin);
532 	sunrise = szRiseSet;
533 	sprintf(szRiseSet, "%02d:%02d:00", sresult.SunSetHour, sresult.SunSetMin);
534 	sunset = szRiseSet;
535 	sprintf(szRiseSet, "%02d:%02d:00", sresult.DaylengthHours, sresult.DaylengthMins);
536 	daylength = szRiseSet;
537 	sprintf(szRiseSet, "%02d:%02d:00", sresult.SunAtSouthHour, sresult.SunAtSouthMin);
538 	sunatsouth = szRiseSet;
539 	sprintf(szRiseSet, "%02d:%02d:00", sresult.CivilTwilightStartHour, sresult.CivilTwilightStartMin);
540 	civtwstart = szRiseSet;
541 	sprintf(szRiseSet, "%02d:%02d:00", sresult.CivilTwilightEndHour, sresult.CivilTwilightEndMin);
542 	civtwend = szRiseSet;
543 	sprintf(szRiseSet, "%02d:%02d:00", sresult.NauticalTwilightStartHour, sresult.NauticalTwilightStartMin);
544 	nauttwstart = szRiseSet;
545 	sprintf(szRiseSet, "%02d:%02d:00", sresult.NauticalTwilightEndHour, sresult.NauticalTwilightEndMin);
546 	nauttwend = szRiseSet;
547 	sprintf(szRiseSet, "%02d:%02d:00", sresult.AstronomicalTwilightStartHour, sresult.AstronomicalTwilightStartMin);
548 	asttwstart = szRiseSet;
549 	sprintf(szRiseSet, "%02d:%02d:00", sresult.AstronomicalTwilightEndHour, sresult.AstronomicalTwilightEndMin);
550 	asttwend = szRiseSet;
551 
552 	m_scheduler.SetSunRiseSetTimers(sunrise, sunset, sunatsouth, civtwstart, civtwend, nauttwstart, nauttwend, asttwstart, asttwend); // Do not change the order
553 
554 	bool bFirstTime = m_LastSunriseSet.empty();
555 
556 	std::string riseset = sunrise.substr(0, sunrise.size() - 3) + ";" + sunset.substr(0, sunset.size() - 3) + ";" + sunatsouth.substr(0, sunatsouth.size() - 3) + ";" + civtwstart.substr(0, civtwstart.size() - 3) + ";" + civtwend.substr(0, civtwend.size() - 3) + ";" + nauttwstart.substr(0, nauttwstart.size() - 3) + ";" + nauttwend.substr(0, nauttwend.size() - 3) + ";" + asttwstart.substr(0, asttwstart.size() - 3) + ";" + asttwend.substr(0, asttwend.size() - 3) + ";" + daylength.substr(0, daylength.size() - 3); //make a short version
557 	if (m_LastSunriseSet != riseset)
558 	{
559 		m_DayLength = daylength;
560 		m_LastSunriseSet = riseset;
561 
562 		// Now store all the time stamps e.g. "08:42;09:12" etc, found in m_LastSunriseSet into
563 		// a new vector after that we've first converted them to minutes after midnight.
564 		std::vector<std::string> strarray;
565 		std::vector<std::string> hourMinItem;
566 		StringSplit(m_LastSunriseSet, ";", strarray);
567 		m_SunRiseSetMins.clear();
568 
569 		for (const auto& it : strarray)
570 		{
571 			StringSplit(it, ":", hourMinItem);
572 			int intMins = (atoi(hourMinItem[0].c_str()) * 60) + atoi(hourMinItem[1].c_str());
573 			m_SunRiseSetMins.push_back(intMins);
574 		}
575 
576 		if (sunrise == sunset)
577 			if (m_DayLength == "00:00:00")
578 				_log.Log(LOG_NORM, "Sun below horizon in the space of 24 hours");
579 			else
580 				_log.Log(LOG_NORM, "Sun above horizon in the space of 24 hours");
581 		else
582 			_log.Log(LOG_NORM, "Sunrise: %s SunSet: %s", sunrise.c_str(), sunset.c_str());
583 		_log.Log(LOG_NORM, "Day length: %s Sun at south: %s", daylength.c_str(), sunatsouth.c_str());
584 		if (civtwstart == civtwend)
585 			_log.Log(LOG_NORM, "There is no civil twilight in the space of 24 hours");
586 		else
587 			_log.Log(LOG_NORM, "Civil twilight start: %s Civil twilight end: %s", civtwstart.c_str(), civtwend.c_str());
588 		if (nauttwstart == nauttwend)
589 			_log.Log(LOG_NORM, "There is no nautical twilight in the space of 24 hours");
590 		else
591 			_log.Log(LOG_NORM, "Nautical twilight start: %s Nautical twilight end: %s", nauttwstart.c_str(), nauttwend.c_str());
592 		if (asttwstart == asttwend)
593 			_log.Log(LOG_NORM, "There is no astronomical twilight in the space of 24 hours");
594 		else
595 			_log.Log(LOG_NORM, "Astronomical twilight start: %s Astronomical twilight end: %s", asttwstart.c_str(), asttwend.c_str());
596 
597 		if (!bFirstTime)
598 			m_eventsystem.LoadEvents();
599 
600 		// FixMe: only reload schedules relative to sunset/sunrise to prevent race conditions
601 		// m_scheduler.ReloadSchedules(); // force reload of all schedules to adjust for changed sunrise/sunset values
602 	}
603 	return true;
604 }
605 
SetWebserverSettings(const http::server::server_settings & settings)606 void MainWorker::SetWebserverSettings(const http::server::server_settings& settings)
607 {
608 	m_webserver_settings.set(settings);
609 }
610 
GetWebserverAddress()611 std::string MainWorker::GetWebserverAddress()
612 {
613 	return m_webserver_settings.listening_address;
614 }
615 
GetWebserverPort()616 std::string MainWorker::GetWebserverPort()
617 {
618 	return m_webserver_settings.listening_port;
619 }
620 
621 #ifdef WWW_ENABLE_SSL
GetSecureWebserverPort()622 std::string MainWorker::GetSecureWebserverPort()
623 {
624 	return m_secure_webserver_settings.listening_port;
625 }
626 
SetSecureWebserverSettings(const http::server::ssl_server_settings & ssl_settings)627 void MainWorker::SetSecureWebserverSettings(const http::server::ssl_server_settings& ssl_settings)
628 {
629 	m_secure_webserver_settings.set(ssl_settings);
630 }
631 #endif
632 
RestartHardware(const std::string & idx)633 bool MainWorker::RestartHardware(const std::string& idx)
634 {
635 	std::vector<std::vector<std::string> > result;
636 	result = m_sql.safe_query(
637 		"SELECT Name, Enabled, Type, Address, Port, SerialPort, Username, Password, Extra, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6, DataTimeout FROM Hardware WHERE (ID=='%q')",
638 		idx.c_str());
639 	if (result.empty())
640 		return false;
641 	std::vector<std::string> sd = result[0];
642 	std::string Name = sd[0];
643 	std::string senabled = (sd[1] == "1") ? "true" : "false";
644 	_eHardwareTypes htype = (_eHardwareTypes)atoi(sd[2].c_str());
645 	std::string address = sd[3];
646 	uint16_t port = (uint16_t)atoi(sd[4].c_str());
647 	std::string serialport = sd[5];
648 	std::string username = sd[6];
649 	std::string password = sd[7];
650 	std::string extra = sd[8];
651 	int Mode1 = atoi(sd[9].c_str());
652 	int Mode2 = atoi(sd[10].c_str());
653 	int Mode3 = atoi(sd[11].c_str());
654 	int Mode4 = atoi(sd[12].c_str());
655 	int Mode5 = atoi(sd[13].c_str());
656 	int Mode6 = atoi(sd[14].c_str());
657 	int DataTimeout = atoi(sd[15].c_str());
658 	return AddHardwareFromParams(atoi(idx.c_str()), Name, (senabled == "true") ? true : false, htype, address, port, serialport, username, password, extra, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6, DataTimeout, true);
659 }
660 
AddHardwareFromParams(const int ID,const std::string & Name,const bool Enabled,const _eHardwareTypes Type,const std::string & Address,const uint16_t Port,const std::string & SerialPort,const std::string & Username,const std::string & Password,const std::string & Extra,const int Mode1,const int Mode2,const int Mode3,const int Mode4,const int Mode5,const int Mode6,const int DataTimeout,const bool bDoStart)661 bool MainWorker::AddHardwareFromParams(
662 	const int ID,
663 	const std::string& Name,
664 	const bool Enabled,
665 	const _eHardwareTypes Type,
666 	const std::string& Address, const uint16_t Port, const std::string& SerialPort,
667 	const std::string& Username, const std::string& Password,
668 	const std::string& Extra,
669 	const int Mode1,
670 	const int Mode2,
671 	const int Mode3,
672 	const int Mode4,
673 	const int Mode5,
674 	const int Mode6,
675 	const int DataTimeout,
676 	const bool bDoStart
677 )
678 {
679 	RemoveDomoticzHardware(ID);
680 
681 	if (!Enabled)
682 		return true;
683 
684 	CDomoticzHardwareBase* pHardware = NULL;
685 
686 	switch (Type)
687 	{
688 	case HTYPE_RFXtrx315:
689 	case HTYPE_RFXtrx433:
690 	case HTYPE_RFXtrx868:
691 		pHardware = new RFXComSerial(ID, SerialPort, 38400, (CRFXBase::_eRFXAsyncType)atoi(Extra.c_str()));
692 		break;
693 	case HTYPE_RFXLAN:
694 		pHardware = new RFXComTCP(ID, Address, Port, (CRFXBase::_eRFXAsyncType)atoi(Extra.c_str()));
695 		break;
696 	case HTYPE_P1SmartMeter:
697 		pHardware = new P1MeterSerial(ID, SerialPort, (Mode1 == 1) ? 115200 : 9600, (Mode2 != 0), Mode3);
698 		break;
699 	case HTYPE_Rego6XX:
700 		pHardware = new CRego6XXSerial(ID, SerialPort, Mode1);
701 		break;
702 	case HTYPE_DavisVantage:
703 		pHardware = new CDavisLoggerSerial(ID, SerialPort, 19200);
704 		break;
705 	case HTYPE_S0SmartMeterUSB:
706 		pHardware = new S0MeterSerial(ID, SerialPort, 9600);
707 		break;
708 	case HTYPE_S0SmartMeterTCP:
709 		//LAN
710 		pHardware = new S0MeterTCP(ID, Address, Port);
711 		break;
712 	case HTYPE_OpenThermGateway:
713 		pHardware = new OTGWSerial(ID, SerialPort, 9600, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6);
714 		break;
715 	case HTYPE_TeleinfoMeter:
716 		pHardware = new CTeleinfoSerial(ID, SerialPort, DataTimeout, Mode1, (Mode2 != 0), Mode3);
717 		break;
718 	case HTYPE_MySensorsUSB:
719 		pHardware = new MySensorsSerial(ID, SerialPort, Mode1);
720 		break;
721 	case HTYPE_KMTronicUSB:
722 		pHardware = new KMTronicSerial(ID, SerialPort);
723 		break;
724 	case HTYPE_KMTronic433:
725 		pHardware = new KMTronic433(ID, SerialPort);
726 		break;
727 	case HTYPE_OpenZWave:
728 #ifdef WITH_OPENZWAVE
729 		pHardware = new COpenZWave(ID, SerialPort);
730 #endif
731 		break;
732 	case HTYPE_EnOceanESP2:
733 		pHardware = new CEnOceanESP2(ID, SerialPort, Mode1);
734 		break;
735 	case HTYPE_EnOceanESP3:
736 		pHardware = new CEnOceanESP3(ID, SerialPort, Mode1);
737 		break;
738 	case HTYPE_Meteostick:
739 		pHardware = new Meteostick(ID, SerialPort, 115200);
740 		break;
741 	case HTYPE_EVOHOME_SERIAL:
742 		pHardware = new CEvohomeSerial(ID, SerialPort, Mode1, Extra);
743 		break;
744 	case HTYPE_EVOHOME_TCP:
745 		pHardware = new CEvohomeTCP(ID, Address, Port, Extra);
746 		break;
747 	case HTYPE_RFLINKUSB:
748 		pHardware = new CRFLinkSerial(ID, SerialPort);
749 		break;
750 	case HTYPE_ZIBLUEUSB:
751 		pHardware = new CZiBlueSerial(ID, SerialPort);
752 		break;
753 	case HTYPE_CurrentCostMeter:
754 		pHardware = new CurrentCostMeterSerial(ID, SerialPort, (Mode1 == 1) ? 57600 : 9600);
755 		break;
756 	case HTYPE_RAVEn:
757 		pHardware = new RAVEn(ID, SerialPort);
758 		break;
759 	case HTYPE_Comm5Serial:
760 		pHardware = new Comm5Serial(ID, SerialPort);
761 		break;
762 	case HTYPE_Domoticz:
763 		//LAN
764 		pHardware = new DomoticzTCP(ID, Address, Port, Username, Password);
765 		break;
766 	case HTYPE_P1SmartMeterLAN:
767 		//LAN
768 		pHardware = new P1MeterTCP(ID, Address, Port, (Mode2 != 0), Mode3);
769 		break;
770 	case HTYPE_WOL:
771 		//LAN
772 		pHardware = new CWOL(ID, Address, Port);
773 		break;
774 	case HTYPE_OpenThermGatewayTCP:
775 		//LAN
776 		pHardware = new OTGWTCP(ID, Address, Port, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6);
777 		break;
778 	case HTYPE_MySensorsTCP:
779 		//LAN
780 		pHardware = new MySensorsTCP(ID, Address, Port);
781 		break;
782 	case HTYPE_MySensorsMQTT:
783 		//LAN
784 		pHardware = new MySensorsMQTT(ID, Name, Address, Port, Username, Password, Extra, Mode2, Mode1, Mode3 != 0);
785 		break;
786 	case HTYPE_RFLINKTCP:
787 		//LAN
788 		pHardware = new CRFLinkTCP(ID, Address, Port);
789 		break;
790 	case HTYPE_ZIBLUETCP:
791 		//LAN
792 		pHardware = new CZiBlueTCP(ID, Address, Port);
793 		break;
794 	case HTYPE_MQTT:
795 		//LAN
796 		pHardware = new MQTT(ID, Address, Port, Username, Password, Extra, Mode2, Mode1, (std::string("Domoticz") + szRandomUUID).c_str(), Mode3 != 0);
797 		break;
798 	case HTYPE_eHouseTCP:
799 		//eHouse LAN, WiFi,Pro and other via eHousePRO gateway
800 		pHardware = new eHouseTCP(ID, Address, Port, Password, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6);
801 		break;
802 	case HTYPE_FRITZBOX:
803 		//LAN
804 		pHardware = new FritzboxTCP(ID, Address, Port);
805 		break;
806 	case HTYPE_SOLARMAXTCP:
807 		//LAN
808 		pHardware = new SolarMaxTCP(ID, Address, Port);
809 		break;
810 	case HTYPE_LimitlessLights:
811 		//LAN
812 	{
813 		int rmode1 = Mode1;
814 		if (rmode1 == 0)
815 			rmode1 = 1;
816 		pHardware = new CLimitLess(ID, rmode1, Mode2, Address, Port);
817 	}
818 	break;
819 	case HTYPE_YouLess:
820 		//LAN
821 		pHardware = new CYouLess(ID, Address, Port, Password);
822 		break;
823 	case HTYPE_WINDDELEN:
824 		pHardware = new CWinddelen(ID, Address, Port, Mode1);
825 		break;
826 	case HTYPE_ETH8020:
827 		//LAN
828 		pHardware = new CETH8020(ID, Address, Port, Username, Password);
829 		break;
830 	case HTYPE_RelayNet:
831 		//LAN
832 		pHardware = new RelayNet(ID, Address, Port, Username, Password, Mode1 != 0, Mode2 != 0, Mode3, Mode4, Mode5);
833 		break;
834 	case HTYPE_KMTronicTCP:
835 		//LAN
836 		pHardware = new KMTronicTCP(ID, Address, Port, Username, Password);
837 		break;
838 	case HTYPE_KMTronicUDP:
839 		//UDP
840 		pHardware = new KMTronicUDP(ID, Address, Port);
841 		break;
842 	case HTYPE_NefitEastLAN:
843 		pHardware = new CNefitEasy(ID, Address, Port);
844 		break;
845 	case HTYPE_ECODEVICES:
846 		//LAN
847 		pHardware = new CEcoDevices(ID, Address, Port, Username, Password, DataTimeout, Mode1, Mode2);
848 		break;
849 	case HTYPE_1WIRE:
850 		//1-Wire file system
851 		pHardware = new C1Wire(ID, Mode1, Mode2, Extra);
852 		break;
853 	case HTYPE_Pinger:
854 		//System Alive Checker (Ping)
855 		pHardware = new CPinger(ID, Mode1, Mode2);
856 		break;
857 	case HTYPE_Kodi:
858 		//Kodi Media Player
859 		pHardware = new CKodi(ID, Mode1, Mode2);
860 		break;
861 	case HTYPE_PanasonicTV:
862 		//Panasonic Viera TV's
863 		pHardware = new CPanasonic(ID, Mode1, Mode2);
864 		break;
865 	case HTYPE_Mochad:
866 		//LAN
867 		pHardware = new MochadTCP(ID, Address, Port);
868 		break;
869 	case HTYPE_SatelIntegra:
870 		pHardware = new SatelIntegra(ID, Address, Port, Password, Mode1);
871 		break;
872 	case HTYPE_LogitechMediaServer:
873 		//Logitech Media Server
874 		pHardware = new CLogitechMediaServer(ID, Address, Port, Username, Password, Mode1);
875 		break;
876 	case HTYPE_Sterbox:
877 		//LAN
878 		pHardware = new CSterbox(ID, Address, Port, Username, Password);
879 		break;
880 	case HTYPE_DenkoviHTTPDevices:
881 		//LAN
882 		pHardware = new CDenkoviDevices(ID, Address, Port, Password, Mode1, Mode2);
883 		break;
884 	case HTYPE_DenkoviUSBDevices:
885 		//USB
886 		pHardware = new CDenkoviUSBDevices(ID, SerialPort, Mode1);
887 		break;
888 	case HTYPE_DenkoviTCPDevices:
889 		//LAN
890 		pHardware = new CDenkoviTCPDevices(ID, Address, Port, Mode1, Mode2, Mode3);
891 		break;
892 	case HTYPE_HEOS:
893 		//HEOS by DENON
894 		pHardware = new CHEOS(ID, Address, Port, Username, Password, Mode1, Mode2);
895 		break;
896 	case HTYPE_MultiFun:
897 		//MultiFun LAN
898 		pHardware = new MultiFun(ID, Address, Port);
899 		break;
900 #ifndef WIN32
901 	case HTYPE_TE923:
902 		//TE923 compatible weather station
903 #ifdef WITH_LIBUSB
904 		pHardware = new CTE923(ID);
905 #endif
906 		break;
907 	case HTYPE_VOLCRAFTCO20:
908 		//Voltcraft CO-20 Air Quality
909 #ifdef WITH_LIBUSB
910 		pHardware = new CVolcraftCO20(ID);
911 #endif
912 		break;
913 #endif
914 	case HTYPE_RaspberryBMP085:
915 		pHardware = new I2C(ID, I2C::I2CTYPE_BMP085, Address, SerialPort, Mode1);
916 		break;
917 	case HTYPE_RaspberryHTU21D:
918 		pHardware = new I2C(ID, I2C::I2CTYPE_HTU21D, Address, SerialPort, Mode1);
919 		break;
920 	case HTYPE_RaspberryTSL2561:
921 		pHardware = new I2C(ID, I2C::I2CTYPE_TSL2561, Address, SerialPort, Mode1);
922 		break;
923 	case HTYPE_RaspberryPCF8574:
924 		pHardware = new I2C(ID, I2C::I2CTYPE_PCF8574, Address, SerialPort, Mode1);
925 		break;
926 	case HTYPE_RaspberryBME280:
927 		pHardware = new I2C(ID, I2C::I2CTYPE_BME280, Address, SerialPort, Mode1);
928 		break;
929 	case HTYPE_RaspberryMCP23017:
930 		pHardware = new I2C(ID, I2C::I2CTYPE_MCP23017, Address, SerialPort, Mode1);
931 		break;
932 	case HTYPE_Wunderground:
933 		pHardware = new CWunderground(ID, Username, Password);
934 		break;
935 	case HTYPE_HTTPPOLLER:
936 		pHardware = new CHttpPoller(ID, Username, Password, Address, Extra, Port);
937 		break;
938 	case HTYPE_DarkSky:
939 		pHardware = new CDarkSky(ID, Username, Password);
940 		break;
941 	case HTYPE_AccuWeather:
942 		pHardware = new CAccuWeather(ID, Username, Password);
943 		break;
944 	case HTYPE_SolarEdgeAPI:
945 		pHardware = new SolarEdgeAPI(ID, Username);
946 		break;
947 	case HTYPE_Netatmo:
948 		pHardware = new CNetatmo(ID, Username, Password);
949 		break;
950 	case HTYPE_Daikin:
951 		pHardware = new CDaikin(ID, Address, Port, Username, Password);
952 		break;
953 	case HTYPE_SBFSpot:
954 		pHardware = new CSBFSpot(ID, Username);
955 		break;
956 	case HTYPE_ICYTHERMOSTAT:
957 		pHardware = new CICYThermostat(ID, Username, Password);
958 		break;
959 	case HTYPE_TOONTHERMOSTAT:
960 		pHardware = new CToonThermostat(ID, Username, Password, Mode1);
961 		break;
962 	case HTYPE_AtagOne:
963 		pHardware = new CAtagOne(ID, Username, Password, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6);
964 		break;
965 	case HTYPE_NEST:
966 		pHardware = new CNest(ID, Username, Password);
967 		break;
968 	case HTYPE_Nest_OAuthAPI:
969 		pHardware = new CNestOAuthAPI(ID, Username, Extra);
970 		break;
971 	case HTYPE_ANNATHERMOSTAT:
972 		pHardware = new CAnnaThermostat(ID, Address, Port, Username, Password);
973 		break;
974 	case HTYPE_THERMOSMART:
975 		pHardware = new CThermosmart(ID, Username, Password, Mode1, Mode2, Mode3, Mode4, Mode5, Mode6);
976 		break;
977 	case HTYPE_Tado:
978 		pHardware = new CTado(ID, Username, Password);
979 		break;
980 	case HTYPE_Tesla:
981 		pHardware = new CeVehicle(ID, CeVehicle::Tesla, Username, Password, Mode1, Mode2, Mode3, Extra);
982 		break;
983 	case HTYPE_Honeywell:
984 		pHardware = new CHoneywell(ID, Username, Password, Extra);
985 		break;
986 	case HTYPE_Philips_Hue:
987 		pHardware = new CPhilipsHue(ID, Address, Port, Username, Mode1, Mode2);
988 		break;
989 	case HTYPE_HARMONY_HUB:
990 		pHardware = new CHarmonyHub(ID, Address, Port);
991 		break;
992 	case HTYPE_PVOUTPUT_INPUT:
993 		pHardware = new CPVOutputInput(ID, Username, Password);
994 		break;
995 	case HTYPE_Dummy:
996 		pHardware = new CDummy(ID);
997 		break;
998 	case HTYPE_Tellstick: {
999 		CTellstick* tellstick;
1000 		if (CTellstick::Create(&tellstick, ID, Mode1, Mode2)) {
1001 			pHardware = tellstick;
1002 		}
1003 	}
1004 						break;
1005 	case HTYPE_EVOHOME_SCRIPT:
1006 		pHardware = new CEvohomeScript(ID);
1007 		break;
1008 	case HTYPE_PiFace:
1009 		pHardware = new CPiFace(ID);
1010 		break;
1011 	case HTYPE_System:
1012 		pHardware = new CHardwareMonitor(ID);
1013 		break;
1014 	case HTYPE_RaspberryGPIO:
1015 		//Raspberry Pi GPIO port access
1016 #ifdef WITH_GPIO
1017 		pHardware = new CGpio(ID, Mode1, Mode2, Mode3);
1018 #endif
1019 		break;
1020 	case HTYPE_SysfsGpio:
1021 #ifdef WITH_GPIO
1022 		pHardware = new CSysfsGpio(ID, Mode1, Mode2);
1023 #endif
1024 		break;
1025 	case HTYPE_Comm5TCP:
1026 		//LAN
1027 		pHardware = new Comm5TCP(ID, Address, Port);
1028 		break;
1029 	case HTYPE_CurrentCostMeterLAN:
1030 		//LAN
1031 		pHardware = new CurrentCostMeterTCP(ID, Address, Port);
1032 		break;
1033 	case HTYPE_DomoticzInternal:
1034 		pHardware = new DomoticzInternal(ID);
1035 		break;
1036 	case HTYPE_OpenWebNetTCP:
1037 		pHardware = new COpenWebNetTCP(ID, Address, Port, Password, Mode1);
1038 		break;
1039 	case HTYPE_BleBox:
1040 		pHardware = new BleBox(ID, Mode1);
1041 		break;
1042 	case HTYPE_OpenWeatherMap:
1043 		pHardware = new COpenWeatherMap(ID, Username, Password);
1044 		break;
1045 	case HTYPE_Ec3kMeterTCP:
1046 		pHardware = new Ec3kMeterTCP(ID, Address, Port);
1047 		break;
1048 	case HTYPE_GoodweAPI:
1049 		pHardware = new GoodweAPI(ID, Username, Mode1);
1050 		break;
1051 	case HTYPE_Yeelight:
1052 		pHardware = new Yeelight(ID);
1053 		break;
1054 	case HTYPE_PythonPlugin:
1055 #ifdef ENABLE_PYTHON
1056 		pHardware = m_pluginsystem.RegisterPlugin(ID, Name, Extra);
1057 #endif
1058 		break;
1059 	case HTYPE_XiaomiGateway:
1060 		pHardware = new XiaomiGateway(ID);
1061 		break;
1062 	case HTYPE_Arilux:
1063 		pHardware = new Arilux(ID);
1064 		break;
1065 	case HTYPE_OpenWebNetUSB:
1066 		pHardware = new COpenWebNetUSB(ID, SerialPort, 115200);
1067 		break;
1068 	case HTYPE_IntergasInComfortLAN2RF:
1069 		pHardware = new CInComfort(ID, Address, Port);
1070 		break;
1071 	case HTYPE_EVOHOME_WEB:
1072 		pHardware = new CEvohomeWeb(ID, Username, Password, Mode1, Mode2, Mode3);
1073 		break;
1074 	case HTYPE_Rtl433:
1075 		pHardware = new CRtl433(ID, Extra);
1076 		break;
1077 	case HTYPE_OnkyoAVTCP:
1078 		pHardware = new OnkyoAVTCP(ID, Address, Port);
1079 		break;
1080 	case HTYPE_USBtinGateway:
1081 		pHardware = new USBtin(ID, SerialPort, Mode1, Mode2);
1082 		break;
1083 	case HTYPE_EnphaseAPI:
1084 		pHardware = new EnphaseAPI(ID, Address, Port);
1085 		break;
1086 	case HTYPE_Comm5SMTCP:
1087 		pHardware = new Comm5SMTCP(ID, Address, Port);
1088 		break;
1089 	case HTYPE_EcoCompteur:
1090 		pHardware = new CEcoCompteur(ID, Address, Port);
1091 		break;
1092 	case HTYPE_TTN_MQTT:
1093 		pHardware = new CTTNMQTT(ID, Address, Port, Username, Password, Extra);
1094 		break;
1095 	case HTYPE_BuienRadar:
1096 		pHardware = new CBuienRadar(ID, Mode1, Mode2, Password);
1097 		break;
1098 	case HTYPE_OctoPrint:
1099 		pHardware = new COctoPrintMQTT(ID, Address, Port, Username, Password, Extra);
1100 		break;
1101 	}
1102 
1103 	if (pHardware)
1104 	{
1105 		pHardware->HwdType = Type;
1106 		pHardware->m_Name = Name;
1107 		pHardware->m_ShortName = Hardware_Short_Desc(Type);
1108 		pHardware->m_DataTimeout = DataTimeout;
1109 		AddDomoticzHardware(pHardware);
1110 
1111 		if (bDoStart)
1112 			pHardware->Start();
1113 		return true;
1114 	}
1115 	return false;
1116 }
1117 
Start()1118 bool MainWorker::Start()
1119 {
1120 	utsname my_uname;
1121 	if (uname(&my_uname) == 0)
1122 	{
1123 		m_szSystemName = my_uname.sysname;
1124 		std::transform(m_szSystemName.begin(), m_szSystemName.end(), m_szSystemName.begin(), ::tolower);
1125 	}
1126 
1127 	if (!m_sql.OpenDatabase())
1128 	{
1129 		return false;
1130 	}
1131 
1132 	HTTPClient::SetUserAgent(GenerateUserAgent());
1133 	m_notifications.Init();
1134 	GetSunSettings();
1135 	GetAvailableWebThemes();
1136 #ifdef ENABLE_PYTHON
1137 	if (m_sql.m_bEnableEventSystem)
1138 	{
1139 		m_pluginsystem.StartPluginSystem();
1140 	}
1141 #endif
1142 	AddAllDomoticzHardware();
1143 	m_fibaropush.Start();
1144 	m_httppush.Start();
1145 	m_influxpush.Start();
1146 	m_googlepubsubpush.Start();
1147 #ifdef PARSE_RFXCOM_DEVICE_LOG
1148 	if (m_bStartHardware == false)
1149 		m_bStartHardware = true;
1150 #endif
1151 	// load notifications configuration
1152 	m_notifications.LoadConfig();
1153 
1154 	if (m_webserver_settings.is_enabled()
1155 #ifdef WWW_ENABLE_SSL
1156 		|| m_secure_webserver_settings.is_enabled()
1157 #endif
1158 		)
1159 	{
1160 		//Start WebServer
1161 #ifdef WWW_ENABLE_SSL
1162 		if (!m_webservers.StartServers(m_webserver_settings, m_secure_webserver_settings, szWWWFolder, m_bIgnoreUsernamePassword, &m_sharedserver))
1163 #else
1164 		if (!m_webservers.StartServers(m_webserver_settings, szWWWFolder, m_bIgnoreUsernamePassword, &m_sharedserver))
1165 #endif
1166 		{
1167 #ifdef WIN32
1168 			MessageBox(0, "Error starting webserver(s), check if ports are not in use!", MB_OK, MB_ICONERROR);
1169 #endif
1170 			return false;
1171 		}
1172 	}
1173 	int nValue = 0;
1174 	if (m_sql.GetPreferencesVar("AuthenticationMethod", nValue))
1175 	{
1176 		m_webservers.SetAuthenticationMethod((http::server::_eAuthenticationMethod)nValue);
1177 	}
1178 	std::string sValue;
1179 	if (m_sql.GetPreferencesVar("WebTheme", sValue))
1180 	{
1181 		m_webservers.SetWebTheme(sValue);
1182 	}
1183 
1184 	m_webservers.SetWebRoot(szWebRoot);
1185 	m_webservers.SetWebCompressionMode(g_wwwCompressMode);
1186 
1187 	//Start Scheduler
1188 	m_scheduler.StartScheduler();
1189 	m_cameras.ReloadCameras();
1190 
1191 	int rnvalue = 0;
1192 	m_sql.GetPreferencesVar("RemoteSharedPort", rnvalue);
1193 	if (rnvalue != 0)
1194 	{
1195 		char szPort[100];
1196 		sprintf(szPort, "%d", rnvalue);
1197 		m_sharedserver.sDecodeRXMessage.connect(boost::bind(&MainWorker::DecodeRXMessage, this, _1, _2, _3, _4));
1198 		m_sharedserver.StartServer("::", szPort);
1199 
1200 		LoadSharedUsers();
1201 	}
1202 
1203 	m_thread = std::make_shared<std::thread>(&MainWorker::Do_Work, this);
1204 	SetThreadName(m_thread->native_handle(), "MainWorker");
1205 	m_rxMessageThread = std::make_shared<std::thread>(&MainWorker::Do_Work_On_Rx_Messages, this);
1206 	SetThreadName(m_rxMessageThread->native_handle(), "MainWorkerRxMsg");
1207 	return (m_thread != nullptr) && (m_rxMessageThread != NULL);
1208 }
1209 
1210 
Stop()1211 bool MainWorker::Stop()
1212 {
1213 	if (m_thread)
1214 	{
1215 		m_notificationsystem.NotifyWait(Notification::DZ_STOP, Notification::STATUS_INFO); // blocking call
1216 	}
1217 
1218 	if (m_rxMessageThread) {
1219 		// Stop RxMessage thread before hardware to avoid NULL pointer exception
1220 		m_TaskRXMessage.RequestStop();
1221 		UnlockRxMessageQueue();
1222 		m_rxMessageThread->join();
1223 		m_rxMessageThread.reset();
1224 	}
1225 	if (m_thread)
1226 	{
1227 		m_webservers.StopServers();
1228 		m_sharedserver.StopServer();
1229 		_log.Log(LOG_STATUS, "Stopping all hardware...");
1230 		StopDomoticzHardware();
1231 		m_scheduler.StopScheduler();
1232 		m_eventsystem.StopEventSystem();
1233 		m_notificationsystem.Stop();
1234 		m_fibaropush.Stop();
1235 		m_httppush.Stop();
1236 		m_influxpush.Stop();
1237 		m_googlepubsubpush.Stop();
1238 #ifdef ENABLE_PYTHON
1239 		m_pluginsystem.StopPluginSystem();
1240 #endif
1241 
1242 		//    m_cameras.StopCameraGrabber();
1243 
1244 		HTTPClient::Cleanup();
1245 
1246 		RequestStop();
1247 		m_thread->join();
1248 		m_thread.reset();
1249 	}
1250 	return true;
1251 }
1252 
1253 #define HEX( x ) \
1254 	std::setw(2) << std::setfill('0') << std::hex << std::uppercase << (int)( x )
1255 
IsUpdateAvailable(const bool bIsForced)1256 bool MainWorker::IsUpdateAvailable(const bool bIsForced)
1257 {
1258 	if (!g_bUseUpdater)
1259 		return false;
1260 
1261 	if (!bIsForced)
1262 	{
1263 		int nValue = 0;
1264 		m_sql.GetPreferencesVar("UseAutoUpdate", nValue);
1265 		if (nValue != 1)
1266 		{
1267 			return false;
1268 		}
1269 	}
1270 
1271 	utsname my_uname;
1272 	if (uname(&my_uname) < 0)
1273 		return false;
1274 
1275 	std::string machine = my_uname.machine;
1276 	if (machine == "armv6l")
1277 	{
1278 		//Seems like old arm systems can also use the new arm build
1279 		machine = "armv7l";
1280 	}
1281 
1282 #ifdef DEBUG_DOWNLOAD
1283 	m_szSystemName = "linux";
1284 	machine = "armv7l";
1285 #endif
1286 
1287 	if ((m_szSystemName != "windows") && (machine != "armv6l") && (machine != "armv7l") && (machine != "x86_64") && (machine != "aarch64"))
1288 	{
1289 		//Only Raspberry Pi (Wheezy)/Ubuntu/windows/osx for now!
1290 		return false;
1291 	}
1292 	time_t atime = mytime(NULL);
1293 	if (!bIsForced)
1294 	{
1295 		if (atime - m_LastUpdateCheck < 12 * 3600)
1296 		{
1297 			return m_bHaveUpdate;
1298 		}
1299 	}
1300 	m_LastUpdateCheck = atime;
1301 
1302 	int nValue;
1303 	m_sql.GetPreferencesVar("ReleaseChannel", nValue);
1304 	bool bIsBetaChannel = (nValue != 0);
1305 
1306 	std::string szURL;
1307 	if (!bIsBetaChannel)
1308 	{
1309 		szURL = "https://www.domoticz.com/download.php?channel=stable&type=version&system=" + m_szSystemName + "&machine=" + machine;
1310 		m_szDomoticzUpdateURL = "https://www.domoticz.com/download.php?channel=stable&type=release&system=" + m_szSystemName + "&machine=" + machine;
1311 		m_szDomoticzUpdateChecksumURL = "https://www.domoticz.com/download.php?channel=stable&type=checksum&system=" + m_szSystemName + "&machine=" + machine;
1312 	}
1313 	else
1314 	{
1315 		szURL = "https://www.domoticz.com/download.php?channel=beta&type=version&system=" + m_szSystemName + "&machine=" + machine;
1316 		m_szDomoticzUpdateURL = "https://www.domoticz.com/download.php?channel=beta&type=release&system=" + m_szSystemName + "&machine=" + machine;
1317 		m_szDomoticzUpdateChecksumURL = "https://www.domoticz.com/download.php?channel=beta&type=checksum&system=" + m_szSystemName + "&machine=" + machine;
1318 	}
1319 
1320 	std::string revfile;
1321 
1322 	if (!HTTPClient::GET(szURL, revfile))
1323 		return false;
1324 
1325 	stdreplace(revfile, "\r\n", "\n");
1326 	std::vector<std::string> strarray;
1327 	StringSplit(revfile, "\n", strarray);
1328 	if (strarray.size() < 1)
1329 		return false;
1330 	StringSplit(strarray[0], " ", strarray);
1331 	if (strarray.size() != 3)
1332 		return false;
1333 
1334 	m_iRevision = atoi(strarray[2].c_str());
1335 #ifdef DEBUG_DOWNLOAD
1336 	m_bHaveUpdate = true;
1337 #else
1338 	m_bHaveUpdate = ((iAppRevision != m_iRevision) && (iAppRevision < m_iRevision));
1339 #endif
1340 	return m_bHaveUpdate;
1341 }
1342 
StartDownloadUpdate()1343 bool MainWorker::StartDownloadUpdate()
1344 {
1345 #ifndef DEBUG_DOWNLOAD
1346 #ifdef WIN32
1347 	return false; //managed by web gui
1348 #endif
1349 #endif
1350 
1351 	if (!IsUpdateAvailable(true))
1352 		return false; //no new version available
1353 
1354 	m_bHaveDownloadedDomoticzUpdate = false;
1355 	m_bHaveDownloadedDomoticzUpdateSuccessFull = false;
1356 	m_bDoDownloadDomoticzUpdate = true;
1357 	return true;
1358 }
1359 
HandleAutomaticBackups()1360 void MainWorker::HandleAutomaticBackups()
1361 {
1362 	int nValue = 0;
1363 	if (!m_sql.GetPreferencesVar("UseAutoBackup", nValue))
1364 		return;
1365 	if (nValue != 1)
1366 		return;
1367 
1368 	_log.Log(LOG_STATUS, "Starting automatic database backup procedure...");
1369 
1370 	std::stringstream backup_DirH;
1371 	std::stringstream backup_DirD;
1372 	std::stringstream backup_DirM;
1373 
1374 #ifdef WIN32
1375 	std::string sbackup_DirH = szUserDataFolder + "backups\\hourly\\";
1376 	std::string sbackup_DirD = szUserDataFolder + "backups\\daily\\";
1377 	std::string sbackup_DirM = szUserDataFolder + "backups\\monthly\\";
1378 #else
1379 	std::string sbackup_DirH = szUserDataFolder + "backups/hourly/";
1380 	std::string sbackup_DirD = szUserDataFolder + "backups/daily/";
1381 	std::string sbackup_DirM = szUserDataFolder + "backups/monthly/";
1382 #endif
1383 
1384 
1385 	//create folders if they not exists
1386 	mkdir_deep(sbackup_DirH.c_str(), 0755);
1387 	mkdir_deep(sbackup_DirD.c_str(), 0755);
1388 	mkdir_deep(sbackup_DirM.c_str(), 0755);
1389 
1390 	time_t now = mytime(NULL);
1391 	struct tm tm1;
1392 	localtime_r(&now, &tm1);
1393 	int hour = tm1.tm_hour;
1394 	int day = tm1.tm_mday;
1395 	int month = tm1.tm_mon;
1396 
1397 	int lastHourBackup = -1;
1398 	int lastDayBackup = -1;
1399 	int lastMonthBackup = -1;
1400 
1401 	m_sql.GetLastBackupNo("Hour", lastHourBackup);
1402 	m_sql.GetLastBackupNo("Day", lastDayBackup);
1403 	m_sql.GetLastBackupNo("Month", lastMonthBackup);
1404 
1405 	std::string szInstanceName = "domoticz";
1406 	std::string szVar;
1407 	if (m_sql.GetPreferencesVar("Title", szVar))
1408 	{
1409 		stdreplace(szVar, " ", "_");
1410 		stdreplace(szVar, "/", "_");
1411 		stdreplace(szVar, "\\", "_");
1412 		if (!szVar.empty()) {
1413 			szInstanceName = szVar;
1414 		}
1415 	}
1416 
1417 	DIR* lDir;
1418 	Notification::_eStatus backupStatus;
1419 	//struct dirent *ent;
1420 
1421 
1422 	if ((lastHourBackup == -1) || (lastHourBackup != hour)) {
1423 
1424 		if ((lDir = opendir(sbackup_DirH.c_str())) != NULL)
1425 		{
1426 			Json::Value backupInfo;
1427 			std::stringstream sTmp;
1428 			sTmp << "backup-hour-" << std::setw(2) << std::setfill('0') << hour << "-" << szInstanceName << ".db";
1429 
1430 			backupInfo["type"] = "Hour";
1431 			backupInfo["location"] = sbackup_DirH + sTmp.str();
1432 			if (m_sql.BackupDatabase(backupInfo["location"].asString())) {
1433 				m_sql.SetLastBackupNo(backupInfo["type"].asString().c_str(), hour);
1434 
1435 				backupStatus=Notification::STATUS_OK;
1436 			}
1437 			else {
1438 				backupStatus = Notification::STATUS_ERROR;
1439 				_log.Log(LOG_ERROR, "Error writing automatic hourly backup file");
1440 			}
1441 			closedir(lDir);
1442 			backupInfo["duration"] = difftime(mytime(NULL),now);
1443 			m_mainworker.m_notificationsystem.Notify(Notification::DZ_BACKUP_DONE, backupStatus, JSonToRawString(backupInfo));
1444 		}
1445 		else {
1446 			_log.Log(LOG_ERROR, "Error accessing automatic backup directories");
1447 		}
1448 	}
1449 	if ((lastDayBackup == -1) || (lastDayBackup != day)) {
1450 
1451 		if ((lDir = opendir(sbackup_DirD.c_str())) != NULL)
1452 		{
1453 			now = mytime(NULL);
1454 			Json::Value backupInfo;
1455 			std::stringstream sTmp;
1456 			sTmp << "backup-day-" << std::setw(2) << std::setfill('0') << day << "-" << szInstanceName << ".db";
1457 
1458 			backupInfo["type"] = "Day";
1459 			backupInfo["location"] = sbackup_DirD + sTmp.str();
1460 			if (m_sql.BackupDatabase(backupInfo["location"].asString())) {
1461 				m_sql.SetLastBackupNo(backupInfo["type"].asString().c_str(), day);
1462 				backupStatus = Notification::STATUS_OK;
1463 			}
1464 			else {
1465 				backupStatus = Notification::STATUS_ERROR;
1466 				_log.Log(LOG_ERROR, "Error writing automatic daily backup file");
1467 			}
1468 			closedir(lDir);
1469 			backupInfo["duration"] = difftime(mytime(NULL),now);
1470 			m_mainworker.m_notificationsystem.Notify(Notification::DZ_BACKUP_DONE, backupStatus, JSonToRawString(backupInfo));
1471 		}
1472 		else {
1473 			_log.Log(LOG_ERROR, "Error accessing automatic backup directories");
1474 		}
1475 	}
1476 	if ((lastMonthBackup == -1) || (lastMonthBackup != month)) {
1477 		if ((lDir = opendir(sbackup_DirM.c_str())) != NULL)
1478 		{
1479 			now = mytime(NULL);
1480 			Json::Value backupInfo;
1481 			std::stringstream sTmp;
1482 			sTmp << "backup-month-" << std::setw(2) << std::setfill('0') << month + 1 << "-" << szInstanceName << ".db";
1483 
1484 			backupInfo["type"] = "Month";
1485 			backupInfo["location"] = sbackup_DirM + sTmp.str();
1486 			if (m_sql.BackupDatabase(backupInfo["location"].asString())) {
1487 				m_sql.SetLastBackupNo(backupInfo["type"].asString().c_str(), month);
1488 				backupStatus = Notification::STATUS_OK;
1489 			}
1490 			else {
1491 				backupStatus = Notification::STATUS_ERROR;
1492 				_log.Log(LOG_ERROR, "Error writing automatic monthly backup file");
1493 			}
1494 			closedir(lDir);
1495 			backupInfo["duration"] = difftime(mytime(NULL),now);
1496 			m_mainworker.m_notificationsystem.Notify(Notification::DZ_BACKUP_DONE, backupStatus, JSonToRawString(backupInfo));
1497 		}
1498 		else {
1499 			_log.Log(LOG_ERROR, "Error accessing automatic backup directories");
1500 		}
1501 	}
1502 	_log.Log(LOG_STATUS, "Ending automatic database backup procedure...");
1503 }
1504 
ParseRFXLogFile()1505 void MainWorker::ParseRFXLogFile()
1506 {
1507 #ifdef PARSE_RFXCOM_DEVICE_LOG
1508 	std::vector<std::string> _lines;
1509 	std::ifstream myfile("C:\\RFXtrxLog.txt");
1510 	if (myfile.is_open())
1511 	{
1512 		while (myfile.good())
1513 		{
1514 			std::string _line;
1515 			getline(myfile, _line);
1516 			size_t tpos = _line.find("=");
1517 			if (tpos != std::string::npos)
1518 			{
1519 				_line = _line.substr(tpos + 1);
1520 				tpos = _line.find(" ");
1521 				if (tpos == 0)
1522 				{
1523 					_line = _line.substr(1);
1524 				}
1525 			}
1526 			stdreplace(_line, " ", "");
1527 			_lines.push_back(_line);
1528 		}
1529 		myfile.close();
1530 	}
1531 	int HWID = 999;
1532 	//m_sql.DeleteHardware("999");
1533 
1534 	CDomoticzHardwareBase* pHardware = GetHardware(HWID);
1535 	if (pHardware == NULL)
1536 	{
1537 		pHardware = new CDummy(HWID);
1538 		//pHardware->sDecodeRXMessage.connect(boost::bind(&MainWorker::DecodeRXMessage, this, _1, _2, _3, _4));
1539 		AddDomoticzHardware(pHardware);
1540 	}
1541 
1542 	std::vector<std::string>::iterator itt;
1543 	unsigned char rxbuffer[600];
1544 	static const char* const lut = "0123456789ABCDEF";
1545 	for (itt = _lines.begin(); itt != _lines.end(); ++itt)
1546 	{
1547 		std::string hexstring = *itt;
1548 		if (hexstring.size() % 2 != 0)
1549 			continue;//illegal
1550 		int totbytes = hexstring.size() / 2;
1551 		int ii = 0;
1552 		for (ii = 0; ii < totbytes; ii++)
1553 		{
1554 			std::string hbyte = hexstring.substr((ii * 2), 2);
1555 
1556 			char a = hbyte[0];
1557 			const char* p = std::lower_bound(lut, lut + 16, a);
1558 			if (*p != a) throw std::invalid_argument("not a hex digit");
1559 
1560 			char b = hbyte[1];
1561 			const char* q = std::lower_bound(lut, lut + 16, b);
1562 			if (*q != b) throw std::invalid_argument("not a hex digit");
1563 
1564 			unsigned char uchar = ((p - lut) << 4) | (q - lut);
1565 			rxbuffer[ii] = uchar;
1566 		}
1567 		if (ii == 0)
1568 			continue;
1569 		if (CRFXBase::CheckValidRFXData((const uint8_t*)&rxbuffer))
1570 		{
1571 			//pHardware->WriteToHardware((const char *)&rxbuffer, totbytes);
1572 			DecodeRXMessage(pHardware, (const unsigned char*)&rxbuffer, NULL, 255);
1573 			sleep_milliseconds(300);
1574 		}
1575 		else
1576 		{
1577 			_log.Log(LOG_ERROR, "Invalid data/length!");
1578 		}
1579 	}
1580 #endif
1581 }
1582 
Do_Work()1583 void MainWorker::Do_Work()
1584 {
1585 	int second_counter = 0;
1586 	int heartbeat_counter = 0;
1587 	while (!IsStopRequested(500))
1588 	{
1589 		if (m_bDoDownloadDomoticzUpdate)
1590 		{
1591 			m_bDoDownloadDomoticzUpdate = false;
1592 
1593 			_log.Log(LOG_STATUS, "Starting Upgrade progress...");
1594 #ifdef WIN32
1595 			std::string outfile;
1596 
1597 			//First download the checksum file
1598 			outfile = szStartupFolder + "update.tgz.sha256sum";
1599 			bool bHaveDownloadedChecksum = HTTPClient::GETBinaryToFile(m_szDomoticzUpdateChecksumURL.c_str(), outfile.c_str());
1600 			if (bHaveDownloadedChecksum)
1601 			{
1602 				//Next download the actual update
1603 				outfile = szStartupFolder + "update.tgz";
1604 				m_bHaveDownloadedDomoticzUpdateSuccessFull = HTTPClient::GETBinaryToFile(m_szDomoticzUpdateURL.c_str(), outfile.c_str());
1605 				if (!m_bHaveDownloadedDomoticzUpdateSuccessFull)
1606 				{
1607 					m_UpdateStatusMessage = "Problem downloading update file!";
1608 				}
1609 			}
1610 			else
1611 				m_UpdateStatusMessage = "Problem downloading checksum file!";
1612 #else
1613 			int nValue;
1614 			m_sql.GetPreferencesVar("ReleaseChannel", nValue);
1615 			bool bIsBetaChannel = (nValue != 0);
1616 
1617 			std::string scriptname = szUserDataFolder + "scripts/download_update.sh";
1618 			std::string strparm = szUserDataFolder;
1619 			if (bIsBetaChannel)
1620 				strparm += " /beta";
1621 
1622 			std::string lscript = scriptname + " " + strparm;
1623 			_log.Log(LOG_STATUS, "Starting: %s", lscript.c_str());
1624 			int ret = system(lscript.c_str());
1625 			m_bHaveDownloadedDomoticzUpdateSuccessFull = (ret == 0);
1626 #endif
1627 			m_bHaveDownloadedDomoticzUpdate = true;
1628 		}
1629 
1630 		second_counter++;
1631 		if (second_counter < 2)
1632 			continue;
1633 		second_counter = 0;
1634 
1635 		if (m_bStartHardware)
1636 		{
1637 			m_hardwareStartCounter++;
1638 			if (m_hardwareStartCounter >= 2)
1639 			{
1640 				m_bStartHardware = false;
1641 				StartDomoticzHardware();
1642 #ifdef ENABLE_PYTHON
1643 				m_pluginsystem.AllPluginsStarted();
1644 #endif
1645 				ParseRFXLogFile();
1646 				m_notificationsystem.Start();
1647 				m_eventsystem.SetEnabled(m_sql.m_bEnableEventSystem);
1648 				m_eventsystem.StartEventSystem();
1649 				m_notificationsystem.Notify(Notification::DZ_START, Notification::STATUS_INFO);
1650 			}
1651 		}
1652 		if (m_devicestorestart.size() > 0)
1653 		{
1654 			for (const auto& itt : m_devicestorestart)
1655 			{
1656 				int hwid = itt;
1657 				std::stringstream sstr;
1658 				sstr << hwid;
1659 				std::string idx = sstr.str();
1660 
1661 				std::vector<std::vector<std::string> > result;
1662 				result = m_sql.safe_query("SELECT Name FROM Hardware WHERE (ID=='%q')",
1663 					idx.c_str());
1664 				if (!result.empty())
1665 				{
1666 					std::vector<std::string> sd = result[0];
1667 					std::string Name = sd[0];
1668 					_log.Log(LOG_ERROR, "Restarting: %s", Name.c_str());
1669 					RestartHardware(idx);
1670 				}
1671 			}
1672 			m_devicestorestart.clear();
1673 		}
1674 
1675 		if (m_SecCountdown > 0)
1676 		{
1677 			m_SecCountdown--;
1678 			if (m_SecCountdown == 0)
1679 			{
1680 				SetInternalSecStatus();
1681 			}
1682 		}
1683 
1684 		time_t atime = mytime(NULL);
1685 		struct tm ltime;
1686 		localtime_r(&atime, &ltime);
1687 
1688 		if (ltime.tm_min != m_ScheduleLastMinute)
1689 		{
1690 			if (difftime(atime, m_ScheduleLastMinuteTime) > 30) //avoid RTC/NTP clock drifts
1691 			{
1692 				m_ScheduleLastMinuteTime = atime;
1693 				m_ScheduleLastMinute = ltime.tm_min;
1694 
1695 				tzset(); //this because localtime_r/localtime_s does not update for DST
1696 
1697 						 //check for 5 minute schedule
1698 				if (ltime.tm_min % m_sql.m_ShortLogInterval == 0)
1699 				{
1700 					m_sql.ScheduleShortlog();
1701 				}
1702 				std::string szPwdResetFile = szStartupFolder + "resetpwd";
1703 				if (file_exist(szPwdResetFile.c_str()))
1704 				{
1705 					m_webservers.ClearUserPasswords();
1706 					m_sql.UpdatePreferencesVar("WebUserName", "");
1707 					m_sql.UpdatePreferencesVar("WebPassword", "");
1708 					std::remove(szPwdResetFile.c_str());
1709 				}
1710 				m_notifications.CheckAndHandleLastUpdateNotification();
1711 			}
1712 			if (_log.NotificationLogsEnabled())
1713 			{
1714 				if ((ltime.tm_min % 5 == 0) || (m_bForceLogNotificationCheck))
1715 				{
1716 					m_bForceLogNotificationCheck = false;
1717 					HandleLogNotifications();
1718 				}
1719 			}
1720 		}
1721 		if (ltime.tm_hour != m_ScheduleLastHour)
1722 		{
1723 			if (difftime(atime, m_ScheduleLastHourTime) > 30 * 60) //avoid RTC/NTP clock drifts
1724 			{
1725 				m_ScheduleLastHourTime = atime;
1726 				m_ScheduleLastHour = ltime.tm_hour;
1727 				GetSunSettings();
1728 
1729 				m_sql.CheckDeviceTimeout();
1730 				m_sql.CheckBatteryLow();
1731 
1732 				//check for daily schedule
1733 				if (ltime.tm_hour == 0)
1734 				{
1735 					if (atime - m_ScheduleLastDayTime > 12 * 60 * 60)
1736 					{
1737 						m_ScheduleLastDayTime = atime;
1738 						m_sql.ScheduleDay();
1739 					}
1740 				}
1741 #ifdef WITH_OPENZWAVE
1742 				if (ltime.tm_hour == 4)
1743 				{
1744 					//Heal the OpenZWave network
1745 					std::lock_guard<std::mutex> l(m_devicemutex);
1746 					std::vector<CDomoticzHardwareBase*>::iterator itt;
1747 					for (itt = m_hardwaredevices.begin(); itt != m_hardwaredevices.end(); ++itt)
1748 					{
1749 						CDomoticzHardwareBase* pHardware = (*itt);
1750 						if (pHardware->HwdType == HTYPE_OpenZWave)
1751 						{
1752 							COpenZWave* pZWave = reinterpret_cast<COpenZWave*>(pHardware);
1753 							pZWave->NightlyNodeHeal();
1754 						}
1755 					}
1756 				}
1757 #endif
1758 				if ((ltime.tm_hour == 5) || (ltime.tm_hour == 17))
1759 				{
1760 					IsUpdateAvailable(true);//check for update
1761 				}
1762 				HandleAutomaticBackups();
1763 			}
1764 		}
1765 		if (heartbeat_counter++ > 12)
1766 		{
1767 			heartbeat_counter = 0;
1768 			m_LastHeartbeat = mytime(NULL);
1769 			HeartbeatCheck();
1770 		}
1771 	}
1772 	_log.Log(LOG_STATUS, "Mainworker Stopped...");
1773 }
1774 
WriteToHardware(const int HwdID,const char * pdata,const uint8_t length)1775 bool MainWorker::WriteToHardware(const int HwdID, const char* pdata, const uint8_t length)
1776 {
1777 	int hindex = FindDomoticzHardware(HwdID);
1778 
1779 	if (hindex == -1)
1780 		return false;
1781 
1782 	return m_hardwaredevices[hindex]->WriteToHardware(pdata, length);
1783 }
1784 
WriteMessageStart()1785 void MainWorker::WriteMessageStart()
1786 {
1787 	_log.LogSequenceStart();
1788 }
1789 
WriteMessageEnd()1790 void MainWorker::WriteMessageEnd()
1791 {
1792 	_log.LogSequenceEnd(LOG_NORM);
1793 }
1794 
WriteMessage(const char * szMessage)1795 void MainWorker::WriteMessage(const char* szMessage)
1796 {
1797 	_log.LogSequenceAdd(szMessage);
1798 }
1799 
WriteMessage(const char * szMessage,bool linefeed)1800 void MainWorker::WriteMessage(const char* szMessage, bool linefeed)
1801 {
1802 	if (linefeed)
1803 		_log.LogSequenceAdd(szMessage);
1804 	else
1805 		_log.LogSequenceAddNoLF(szMessage);
1806 }
1807 
OnHardwareConnected(CDomoticzHardwareBase * pHardware)1808 void MainWorker::OnHardwareConnected(CDomoticzHardwareBase* pHardware)
1809 {
1810 	if (
1811 		(pHardware->HwdType != HTYPE_RFXtrx315) &&
1812 		(pHardware->HwdType != HTYPE_RFXtrx433) &&
1813 		(pHardware->HwdType != HTYPE_RFXtrx868) &&
1814 		(pHardware->HwdType != HTYPE_RFXLAN)
1815 		)
1816 	{
1817 		//enable receive
1818 		pHardware->m_bEnableReceive = true;
1819 		return;
1820 	}
1821 	CRFXBase* pRFXBase = reinterpret_cast<CRFXBase*>(pHardware);
1822 	pRFXBase->SendResetCommand();
1823 }
1824 
PerformRealActionFromDomoticzClient(const uint8_t * pRXCommand,CDomoticzHardwareBase ** pOriginalHardware)1825 uint64_t MainWorker::PerformRealActionFromDomoticzClient(const uint8_t* pRXCommand, CDomoticzHardwareBase** pOriginalHardware)
1826 {
1827 	*pOriginalHardware = NULL;
1828 	uint8_t devType = pRXCommand[1];
1829 	uint8_t subType = pRXCommand[2];
1830 	std::string ID = "";
1831 	uint8_t Unit = 0;
1832 	const tRBUF* pResponse = reinterpret_cast<const tRBUF*>(pRXCommand);
1833 	char szTmp[300];
1834 	std::vector<std::vector<std::string> > result;
1835 
1836 	switch (devType) {
1837 	case pTypeLighting1:
1838 		sprintf(szTmp, "%d", pResponse->LIGHTING1.housecode);
1839 		ID = szTmp;
1840 		Unit = pResponse->LIGHTING1.unitcode;
1841 		break;
1842 	case pTypeLighting2:
1843 		sprintf(szTmp, "%X%02X%02X%02X", pResponse->LIGHTING2.id1, pResponse->LIGHTING2.id2, pResponse->LIGHTING2.id3, pResponse->LIGHTING2.id4);
1844 		ID = szTmp;
1845 		Unit = pResponse->LIGHTING2.unitcode;
1846 		break;
1847 	case pTypeLighting5:
1848 		if (subType != sTypeEMW100)
1849 			sprintf(szTmp, "%02X%02X%02X", pResponse->LIGHTING5.id1, pResponse->LIGHTING5.id2, pResponse->LIGHTING5.id3);
1850 		else
1851 			sprintf(szTmp, "%02X%02X", pResponse->LIGHTING5.id2, pResponse->LIGHTING5.id3);
1852 		ID = szTmp;
1853 		Unit = pResponse->LIGHTING5.unitcode;
1854 		break;
1855 	case pTypeLighting6:
1856 		sprintf(szTmp, "%02X%02X%02X", pResponse->LIGHTING6.id1, pResponse->LIGHTING6.id2, pResponse->LIGHTING6.groupcode);
1857 		ID = szTmp;
1858 		Unit = pResponse->LIGHTING6.unitcode;
1859 		break;
1860 	case pTypeHomeConfort:
1861 		sprintf(szTmp, "%02X%02X%02X%02X", pResponse->HOMECONFORT.id1, pResponse->HOMECONFORT.id2, pResponse->HOMECONFORT.id3, pResponse->HOMECONFORT.housecode);
1862 		ID = szTmp;
1863 		Unit = pResponse->HOMECONFORT.unitcode;
1864 		break;
1865 	case pTypeRadiator1:
1866 		if (subType == sTypeSmartwaresSwitchRadiator)
1867 		{
1868 			sprintf(szTmp, "%X%02X%02X%02X", pResponse->RADIATOR1.id1, pResponse->RADIATOR1.id2, pResponse->RADIATOR1.id3, pResponse->RADIATOR1.id4);
1869 			ID = szTmp;
1870 			Unit = pResponse->RADIATOR1.unitcode;
1871 		}
1872 		break;
1873 	case pTypeColorSwitch:
1874 	{
1875 		const _tColorSwitch* pLed = reinterpret_cast<const _tColorSwitch*>(pResponse);
1876 		ID = "1";
1877 		Unit = pLed->dunit;
1878 	}
1879 	break;
1880 	case pTypeFS20:
1881 		sprintf(szTmp, "%02X%02X", pResponse->FS20.hc1, pResponse->FS20.hc2);
1882 		ID = szTmp;
1883 		Unit = pResponse->FS20.addr;
1884 		break;
1885 	case pTypeCurtain:
1886 		sprintf(szTmp, "%d", pResponse->CURTAIN1.housecode);
1887 		ID = szTmp;
1888 		Unit = pResponse->CURTAIN1.unitcode;
1889 		break;
1890 	case pTypeBlinds:
1891 		sprintf(szTmp, "%02X%02X%02X", pResponse->BLINDS1.id1, pResponse->BLINDS1.id2, pResponse->BLINDS1.id3);
1892 		ID = szTmp;
1893 		Unit = pResponse->BLINDS1.unitcode;
1894 		break;
1895 	case pTypeRFY:
1896 		sprintf(szTmp, "%02X%02X%02X", pResponse->RFY.id1, pResponse->RFY.id2, pResponse->RFY.id3);
1897 		ID = szTmp;
1898 		Unit = pResponse->RFY.unitcode;
1899 		break;
1900 	case pTypeSecurity1:
1901 		sprintf(szTmp, "%02X%02X%02X", pResponse->SECURITY1.id1, pResponse->SECURITY1.id2, pResponse->SECURITY1.id3);
1902 		ID = szTmp;
1903 		Unit = 0;
1904 		break;
1905 	case pTypeSecurity2:
1906 		sprintf(szTmp, "%02X%02X%02X%02X%02X%02X%02X%02X", pResponse->SECURITY2.id1, pResponse->SECURITY2.id2, pResponse->SECURITY2.id3, pResponse->SECURITY2.id4, pResponse->SECURITY2.id5, pResponse->SECURITY2.id6, pResponse->SECURITY2.id7, pResponse->SECURITY2.id8);
1907 		ID = szTmp;
1908 		Unit = 0;
1909 		break;
1910 	case pTypeChime:
1911 		sprintf(szTmp, "%02X%02X", pResponse->CHIME.id1, pResponse->CHIME.id2);
1912 		ID = szTmp;
1913 		Unit = pResponse->CHIME.sound;
1914 		break;
1915 	case pTypeThermostat:
1916 	{
1917 		const _tThermostat* pMeter = reinterpret_cast<const _tThermostat*>(pResponse);
1918 		sprintf(szTmp, "%X%02X%02X%02X", pMeter->id1, pMeter->id2, pMeter->id3, pMeter->id4);
1919 		ID = szTmp;
1920 		Unit = pMeter->dunit;
1921 	}
1922 	break;
1923 	case pTypeThermostat2:
1924 		ID = "1";
1925 		Unit = pResponse->THERMOSTAT2.unitcode;
1926 		break;
1927 	case pTypeThermostat3:
1928 		sprintf(szTmp, "%02X%02X%02X", pResponse->THERMOSTAT3.unitcode1, pResponse->THERMOSTAT3.unitcode2, pResponse->THERMOSTAT3.unitcode3);
1929 		ID = szTmp;
1930 		Unit = 0;
1931 		break;
1932 	case pTypeThermostat4:
1933 		sprintf(szTmp, "%02X%02X%02X", pResponse->THERMOSTAT4.unitcode1, pResponse->THERMOSTAT4.unitcode2, pResponse->THERMOSTAT4.unitcode3);
1934 		ID = szTmp;
1935 		Unit = 0;
1936 		break;
1937 	case pTypeGeneralSwitch:
1938 	{
1939 		const _tGeneralSwitch* pSwitch = reinterpret_cast<const _tGeneralSwitch*>(pResponse);
1940 		sprintf(szTmp, "%08X", pSwitch->id);
1941 		ID = szTmp;
1942 		Unit = pSwitch->unitcode;
1943 	}
1944 	break;
1945 	default:
1946 		return -1;
1947 	}
1948 
1949 	if (ID != "")
1950 	{
1951 		// find our original hardware
1952 		// if it is not a domoticz type, perform the actual command
1953 
1954 		result = m_sql.safe_query(
1955 			"SELECT HardwareID,ID,Name,StrParam1,StrParam2,nValue,sValue FROM DeviceStatus WHERE (DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)",
1956 			ID.c_str(), Unit, devType, subType);
1957 		if (result.size() == 1)
1958 		{
1959 			std::vector<std::string> sd = result[0];
1960 
1961 			CDomoticzHardwareBase* pHardware = GetHardware(atoi(sd[0].c_str()));
1962 			if (pHardware != NULL)
1963 			{
1964 				if (pHardware->HwdType != HTYPE_Domoticz)
1965 				{
1966 					*pOriginalHardware = pHardware;
1967 					pHardware->WriteToHardware((const char*)pRXCommand, pRXCommand[0] + 1);
1968 					std::stringstream s_strid;
1969 					s_strid << std::dec << sd[1];
1970 					uint64_t ullID;
1971 					s_strid >> ullID;
1972 					return ullID;
1973 				}
1974 			}
1975 		}
1976 	}
1977 	return -1;
1978 }
1979 
DecodeRXMessage(const CDomoticzHardwareBase * pHardware,const uint8_t * pRXCommand,const char * defaultName,const int BatteryLevel)1980 void MainWorker::DecodeRXMessage(const CDomoticzHardwareBase* pHardware, const uint8_t* pRXCommand, const char* defaultName, const int BatteryLevel)
1981 {
1982 	if ((pHardware == NULL) || (pRXCommand == NULL))
1983 		return;
1984 	if ((pHardware->HwdType == HTYPE_Domoticz) && (pHardware->m_HwdID == 8765))
1985 	{
1986 		//Directly process the command
1987 		std::lock_guard<std::mutex> l(m_decodeRXMessageMutex);
1988 		ProcessRXMessage(pHardware, pRXCommand, defaultName, BatteryLevel);
1989 	}
1990 	else
1991 	{
1992 		// Submit command without waiting for the command to be processed
1993 		PushRxMessage(pHardware, pRXCommand, defaultName, BatteryLevel);
1994 	}
1995 }
1996 
PushRxMessage(const CDomoticzHardwareBase * pHardware,const uint8_t * pRXCommand,const char * defaultName,const int BatteryLevel)1997 void MainWorker::PushRxMessage(const CDomoticzHardwareBase* pHardware, const uint8_t* pRXCommand, const char* defaultName, const int BatteryLevel)
1998 {
1999 	// Check command, submit it without waiting for it to be processed
2000 	CheckAndPushRxMessage(pHardware, pRXCommand, defaultName, BatteryLevel, false);
2001 }
2002 
PushAndWaitRxMessage(const CDomoticzHardwareBase * pHardware,const uint8_t * pRXCommand,const char * defaultName,const int BatteryLevel)2003 void MainWorker::PushAndWaitRxMessage(const CDomoticzHardwareBase* pHardware, const uint8_t* pRXCommand, const char* defaultName, const int BatteryLevel)
2004 {
2005 	// Check command, submit it and wait for it to be processed
2006 	CheckAndPushRxMessage(pHardware, pRXCommand, defaultName, BatteryLevel, true);
2007 }
2008 
CheckAndPushRxMessage(const CDomoticzHardwareBase * pHardware,const uint8_t * pRXCommand,const char * defaultName,const int BatteryLevel,const bool wait)2009 void MainWorker::CheckAndPushRxMessage(const CDomoticzHardwareBase* pHardware, const uint8_t* pRXCommand, const char* defaultName, const int BatteryLevel, const bool wait)
2010 {
2011 	if ((pHardware == NULL) || (pRXCommand == NULL)) {
2012 		_log.Log(LOG_ERROR, "RxQueue: cannot push message with undefined hardware (%s) or command (%s)",
2013 			(pHardware == NULL) ? "null" : "not null",
2014 			(pRXCommand == NULL) ? "null" : "not null");
2015 		return;
2016 	}
2017 	if (pHardware->m_HwdID < 1) {
2018 		_log.Log(LOG_ERROR, "RxQueue: cannot push message with invalid hardware id (id=%d, type=%d, name=%s)",
2019 			pHardware->m_HwdID,
2020 			pHardware->HwdType,
2021 			pHardware->m_Name.c_str());
2022 		return;
2023 	}
2024 
2025 	// Build queue item
2026 	_tRxQueueItem rxMessage;
2027 	if (defaultName != NULL)
2028 	{
2029 		rxMessage.Name = defaultName;
2030 	}
2031 	rxMessage.BatteryLevel = BatteryLevel;
2032 	rxMessage.rxMessageIdx = m_rxMessageIdx++;
2033 	rxMessage.hardwareId = pHardware->m_HwdID;
2034 	// defensive copy of the command
2035 	rxMessage.vrxCommand.resize(pRXCommand[0] + 1);
2036 	rxMessage.vrxCommand.insert(rxMessage.vrxCommand.begin(), pRXCommand, pRXCommand + pRXCommand[0] + 1);
2037 	rxMessage.crc = 0x0;
2038 #ifdef DEBUG_RXQUEUE
2039 	// CRC
2040 	boost::crc_optimal<16, 0x1021, 0xFFFF, 0, false, false> crc_ccitt2;
2041 	crc_ccitt2 = std::for_each(pRXCommand, pRXCommand + pRXCommand[0] + 1, crc_ccitt2);
2042 	rxMessage.crc = crc_ccitt2();
2043 #endif
2044 
2045 	if (m_TaskRXMessage.IsStopRequested(0)) {
2046 		// Server is stopping
2047 		return;
2048 	}
2049 
2050 	// Trigger
2051 	rxMessage.trigger = NULL; // Should be initialized to NULL if trigger is no used
2052 	if (wait) { // add trigger to wait for the message to be processed
2053 		rxMessage.trigger = new queue_element_trigger();
2054 	}
2055 
2056 #ifdef DEBUG_RXQUEUE
2057 	_log.Log(LOG_STATUS, "RxQueue: push a rxMessage(%lu) (hrdwId=%d, hrdwType=%d, hrdwName=%s, type=%02X, subtype=%02X)",
2058 		rxMessage.rxMessageIdx,
2059 		pHardware->m_HwdID,
2060 		pHardware->HwdType,
2061 		pHardware->Name.c_str(),
2062 		pRXCommand[1],
2063 		pRXCommand[2]);
2064 #endif
2065 
2066 	// Push item to queue
2067 	m_rxMessageQueue.push(rxMessage);
2068 
2069 	if (rxMessage.trigger != NULL) {
2070 #ifdef DEBUG_RXQUEUE
2071 		_log.Log(LOG_STATUS, "RxQueue: wait for rxMessage(%lu) to be processed...", rxMessage.rxMessageIdx);
2072 #endif
2073 		while (!rxMessage.trigger->timed_wait(std::chrono::duration<int>(1))) {
2074 #ifdef DEBUG_RXQUEUE
2075 			_log.Log(LOG_STATUS, "RxQueue: wait 1s for rxMessage(%lu) to be processed...", rxMessage.rxMessageIdx);
2076 #endif
2077 			if (m_TaskRXMessage.IsStopRequested(0)) {
2078 				// Server is stopping
2079 				break;
2080 			}
2081 		}
2082 #ifdef DEBUG_RXQUEUE
2083 		if (moreThanTimeout) {
2084 			_log.Log(LOG_STATUS, "RxQueue: rxMessage(%lu) processed", rxMessage.rxMessageIdx);
2085 		}
2086 #endif
2087 		delete rxMessage.trigger;
2088 	}
2089 }
2090 
UnlockRxMessageQueue()2091 void MainWorker::UnlockRxMessageQueue()
2092 {
2093 #ifdef DEBUG_RXQUEUE
2094 	_log.Log(LOG_STATUS, "RxQueue: unlock queue using dummy message");
2095 #endif
2096 	// Push dummy message to unlock queue
2097 	_tRxQueueItem rxMessage;
2098 	rxMessage.rxMessageIdx = m_rxMessageIdx++;
2099 	rxMessage.hardwareId = -1;
2100 	rxMessage.trigger = NULL;
2101 	rxMessage.BatteryLevel = 0;
2102 	m_rxMessageQueue.push(rxMessage);
2103 }
2104 
Do_Work_On_Rx_Messages()2105 void MainWorker::Do_Work_On_Rx_Messages()
2106 {
2107 	_log.Log(LOG_STATUS, "RxQueue: queue worker started...");
2108 
2109 	while (!m_TaskRXMessage.IsStopRequested(0))
2110 	{
2111 		// Wait and pop next message or timeout
2112 		_tRxQueueItem rxQItem;
2113 		bool hasPopped = m_rxMessageQueue.timed_wait_and_pop<std::chrono::duration<int> >(rxQItem, std::chrono::duration<int>(5));
2114 		// (if no message for 5 seconds, returns anyway to check m_TaskRXMessage.IsStopRequested)
2115 
2116 		if (!hasPopped) {
2117 			// Timeout occurred : queue is empty
2118 #ifdef DEBUG_RXQUEUE
2119 			//_log.Log(LOG_STATUS, "RxQueue: the queue has been empty for five seconds");
2120 #endif
2121 			continue;
2122 		}
2123 		if (rxQItem.hardwareId == -1) {
2124 			// dummy message
2125 #ifdef DEBUG_RXQUEUE
2126 			_log.Log(LOG_STATUS, "RxQueue: dummy message popped");
2127 #endif
2128 			continue;
2129 		}
2130 		if (rxQItem.hardwareId < 1) {
2131 			_log.Log(LOG_ERROR, "RxQueue: cannot process invalid hardware id: (%d)", rxQItem.hardwareId);
2132 			// cannot process message with invalid id or null message
2133 			if (rxQItem.trigger != NULL) rxQItem.trigger->popped();
2134 			continue;
2135 		}
2136 
2137 		const CDomoticzHardwareBase* pHardware = GetHardware(rxQItem.hardwareId);
2138 
2139 		// Check pointers
2140 		if (pHardware == NULL) {
2141 			_log.Log(LOG_ERROR, "RxQueue: cannot retrieve hardware with id: %d", rxQItem.hardwareId);
2142 			if (rxQItem.trigger != NULL) rxQItem.trigger->popped();
2143 			continue;
2144 		}
2145 		if (rxQItem.vrxCommand.empty()) {
2146 			_log.Log(LOG_ERROR, "RxQueue: cannot retrieve command with id: %d", rxQItem.hardwareId);
2147 			if (rxQItem.trigger != NULL) rxQItem.trigger->popped();
2148 			continue;
2149 		}
2150 
2151 		const uint8_t* pRXCommand = &rxQItem.vrxCommand[0];
2152 
2153 #ifdef DEBUG_RXQUEUE
2154 		// CRC
2155 		boost::uint16_t crc = rxQItem.crc;
2156 		boost::crc_optimal<16, 0x1021, 0xFFFF, 0, false, false> crc_ccitt2;
2157 		crc_ccitt2 = std::for_each(pRXCommand, pRXCommand + rxQItem.vrxCommand.size(), crc_ccitt2);
2158 		if (crc != crc_ccitt2()) {
2159 			_log.Log(LOG_ERROR, "RxQueue: cannot process invalid rxMessage(%lu) from hardware with id=%d (type %d)",
2160 				rxQItem.rxMessageIdx,
2161 				rxQItem.hardwareId,
2162 				pHardware->HwdType);
2163 			if (rxQItem.trigger != NULL) rxQItem.trigger->popped();
2164 			continue;
2165 		}
2166 
2167 		_log.Log(LOG_STATUS, "RxQueue: process a rxMessage(%lu) (hrdwId=%d, hrdwType=%d, hrdwName=%s, type=%02X, subtype=%02X)",
2168 			rxQItem.rxMessageIdx,
2169 			pHardware->m_HwdID,
2170 			pHardware->HwdType,
2171 			pHardware->Name.c_str(),
2172 			pRXCommand[1],
2173 			pRXCommand[2]);
2174 #endif
2175 		ProcessRXMessage(pHardware, pRXCommand, rxQItem.Name.c_str(), rxQItem.BatteryLevel);
2176 		if (rxQItem.trigger != NULL)
2177 		{
2178 			rxQItem.trigger->popped();
2179 		}
2180 	}
2181 
2182 	_log.Log(LOG_STATUS, "RxQueue: queue worker stopped...");
2183 }
2184 
ProcessRXMessage(const CDomoticzHardwareBase * pHardware,const uint8_t * pRXCommand,const char * defaultName,const int BatteryLevel)2185 void MainWorker::ProcessRXMessage(const CDomoticzHardwareBase* pHardware, const uint8_t* pRXCommand, const char* defaultName, const int BatteryLevel)
2186 {
2187 	// current date/time based on current system
2188 	//size_t Len = pRXCommand[0] + 1;
2189 
2190 	const_cast<CDomoticzHardwareBase*>(pHardware)->SetHeartbeatReceived();
2191 
2192 	uint64_t DeviceRowIdx = (uint64_t)-1;
2193 	std::string DeviceName = "";
2194 	tcp::server::CTCPClient* pClient2Ignore = NULL;
2195 
2196 	if (pHardware->HwdType == HTYPE_Domoticz)
2197 	{
2198 		if (pHardware->m_HwdID == 8765) //did we receive it from our master?
2199 		{
2200 			CDomoticzHardwareBase* pOrgHardware = NULL;
2201 			switch (pRXCommand[1])
2202 			{
2203 			case pTypeLighting1:
2204 			case pTypeLighting2:
2205 			case pTypeLighting3:
2206 			case pTypeLighting4:
2207 			case pTypeLighting5:
2208 			case pTypeLighting6:
2209 			case pTypeColorSwitch:
2210 			case pTypeCurtain:
2211 			case pTypeBlinds:
2212 			case pTypeRFY:
2213 			case pTypeSecurity1:
2214 			case pTypeSecurity2:
2215 			case pTypeChime:
2216 			case pTypeThermostat:
2217 			case pTypeThermostat2:
2218 			case pTypeThermostat3:
2219 			case pTypeThermostat4:
2220 			case pTypeRadiator1:
2221 			case pTypeGeneralSwitch:
2222 			case pTypeHomeConfort:
2223 			case pTypeFan:
2224 			case pTypeFS20:
2225 			case pTypeHunter:
2226 				//we received a control message from a domoticz client,
2227 				//and should actually perform this command ourself switch
2228 				DeviceRowIdx = PerformRealActionFromDomoticzClient(pRXCommand, &pOrgHardware);
2229 				if (DeviceRowIdx != (uint64_t)-1)
2230 				{
2231 					if (pOrgHardware != NULL)
2232 					{
2233 						DeviceRowIdx = -1;
2234 						pClient2Ignore = (tcp::server::CTCPClient*)pHardware->m_pUserData;
2235 						pHardware = pOrgHardware;
2236 					}
2237 					WriteMessage("Control Command, ", (pOrgHardware == NULL));
2238 				}
2239 				break;
2240 			}
2241 		}
2242 	}
2243 
2244 	_tRxMessageProcessingResult procResult;
2245 	procResult.DeviceName = "";
2246 	procResult.DeviceRowIdx = -1;
2247 	procResult.bProcessBatteryValue = true;
2248 	if (DeviceRowIdx == (uint64_t)-1)
2249 	{
2250 		switch (pRXCommand[1])
2251 		{
2252 		case pTypeInterfaceMessage:
2253 			decode_InterfaceMessage(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2254 			break;
2255 		case pTypeInterfaceControl:
2256 			decode_InterfaceControl(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2257 			break;
2258 		case pTypeRecXmitMessage:
2259 			decode_RecXmitMessage(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2260 			break;
2261 		case pTypeUndecoded:
2262 			decode_UNDECODED(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2263 			break;
2264 		case pTypeLighting1:
2265 			decode_Lighting1(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2266 			break;
2267 		case pTypeLighting2:
2268 			decode_Lighting2(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2269 			break;
2270 		case pTypeLighting3:
2271 			decode_Lighting3(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2272 			break;
2273 		case pTypeLighting4:
2274 			decode_Lighting4(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2275 			break;
2276 		case pTypeLighting5:
2277 			decode_Lighting5(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2278 			break;
2279 		case pTypeLighting6:
2280 			decode_Lighting6(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2281 			break;
2282 		case pTypeFan:
2283 			decode_Fan(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2284 			break;
2285 		case pTypeCurtain:
2286 			decode_Curtain(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2287 			break;
2288 		case pTypeBlinds:
2289 			decode_BLINDS1(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2290 			break;
2291 		case pTypeRFY:
2292 			decode_RFY(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2293 			break;
2294 		case pTypeSecurity1:
2295 			decode_Security1(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2296 			break;
2297 		case pTypeSecurity2:
2298 			decode_Security2(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2299 			break;
2300 		case pTypeEvohome:
2301 			decode_evohome1(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2302 			break;
2303 		case pTypeEvohomeZone:
2304 		case pTypeEvohomeWater:
2305 			decode_evohome2(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2306 			break;
2307 		case pTypeEvohomeRelay:
2308 			decode_evohome3(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2309 			break;
2310 		case pTypeCamera:
2311 			decode_Camera1(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2312 			break;
2313 		case pTypeRemote:
2314 			decode_Remote(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2315 			break;
2316 		case pTypeThermostat: //own type
2317 			decode_Thermostat(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2318 			break;
2319 		case pTypeThermostat1:
2320 			decode_Thermostat1(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2321 			break;
2322 		case pTypeThermostat2:
2323 			decode_Thermostat2(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2324 			break;
2325 		case pTypeThermostat3:
2326 			decode_Thermostat3(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2327 			break;
2328 		case pTypeThermostat4:
2329 			decode_Thermostat4(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2330 			break;
2331 		case pTypeRadiator1:
2332 			decode_Radiator1(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2333 			break;
2334 		case pTypeTEMP:
2335 			decode_Temp(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2336 			break;
2337 		case pTypeHUM:
2338 			decode_Hum(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2339 			break;
2340 		case pTypeTEMP_HUM:
2341 			decode_TempHum(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2342 			break;
2343 		case pTypeTEMP_RAIN:
2344 			decode_TempRain(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2345 			break;
2346 		case pTypeBARO:
2347 			decode_Baro(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2348 			break;
2349 		case pTypeTEMP_HUM_BARO:
2350 			decode_TempHumBaro(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2351 			break;
2352 		case pTypeTEMP_BARO:
2353 			decode_TempBaro(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2354 			break;
2355 		case pTypeRAIN:
2356 			decode_Rain(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2357 			break;
2358 		case pTypeWIND:
2359 			decode_Wind(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2360 			break;
2361 		case pTypeUV:
2362 			decode_UV(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2363 			break;
2364 		case pTypeDT:
2365 			decode_DateTime(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2366 			break;
2367 		case pTypeCURRENT:
2368 			decode_Current(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2369 			break;
2370 		case pTypeENERGY:
2371 			decode_Energy(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2372 			break;
2373 		case pTypeCURRENTENERGY:
2374 			decode_Current_Energy(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2375 			break;
2376 		case pTypeGAS:
2377 			decode_Gas(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2378 			break;
2379 		case pTypeWATER:
2380 			decode_Water(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2381 			break;
2382 		case pTypeWEIGHT:
2383 			decode_Weight(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2384 			break;
2385 		case pTypeRFXSensor:
2386 			decode_RFXSensor(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2387 			break;
2388 		case pTypeRFXMeter:
2389 			decode_RFXMeter(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2390 			break;
2391 		case pTypeP1Power:
2392 			decode_P1MeterPower(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2393 			break;
2394 		case pTypeP1Gas:
2395 			decode_P1MeterGas(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2396 			break;
2397 		case pTypeUsage:
2398 			decode_Usage(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2399 			break;
2400 		case pTypeYouLess:
2401 			decode_YouLessMeter(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2402 			break;
2403 		case pTypeAirQuality:
2404 			decode_AirQuality(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2405 			break;
2406 		case pTypeRego6XXTemp:
2407 			decode_Rego6XXTemp(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2408 			break;
2409 		case pTypeRego6XXValue:
2410 			decode_Rego6XXValue(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2411 			break;
2412 		case pTypeFS20:
2413 			decode_FS20(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2414 			break;
2415 		case pTypeLux:
2416 			decode_Lux(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2417 			break;
2418 		case pTypeGeneral:
2419 			decode_General(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2420 			break;
2421 		case pTypeChime:
2422 			decode_Chime(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2423 			break;
2424 		case pTypeBBQ:
2425 			decode_BBQ(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2426 			break;
2427 		case pTypePOWER:
2428 			decode_Power(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2429 			break;
2430 		case pTypeColorSwitch:
2431 			decode_ColorSwitch(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2432 			break;
2433 		case pTypeGeneralSwitch:
2434 			decode_GeneralSwitch(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2435 			break;
2436 		case pTypeHomeConfort:
2437 			decode_HomeConfort(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2438 			break;
2439 		case pTypeCARTELECTRONIC:
2440 			decode_Cartelectronic(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2441 			break;
2442 		case pTypeASYNCPORT:
2443 			decode_ASyncPort(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2444 			break;
2445 		case pTypeASYNCDATA:
2446 			decode_ASyncData(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2447 			break;
2448 		case pTypeWEATHER:
2449 			decode_Weather(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2450 			break;
2451 		case pTypeSOLAR:
2452 			decode_Solar(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2453 			break;
2454 		case pTypeHunter:
2455 			decode_Hunter(pHardware, reinterpret_cast<const tRBUF*>(pRXCommand), procResult);
2456 			break;
2457 		default:
2458 			_log.Log(LOG_ERROR, "UNHANDLED PACKET TYPE:      FS20 %02X", pRXCommand[1]);
2459 			return;
2460 		}
2461 		DeviceRowIdx = procResult.DeviceRowIdx;
2462 		DeviceName = procResult.DeviceName;
2463 	}
2464 
2465 	if (DeviceRowIdx == (uint64_t)-1)
2466 		return;
2467 
2468 	if ((BatteryLevel != -1) && (procResult.bProcessBatteryValue))
2469 	{
2470 		m_sql.safe_query("UPDATE DeviceStatus SET BatteryLevel=%d WHERE (ID==%" PRIu64 ")", BatteryLevel, DeviceRowIdx);
2471 		m_eventsystem.UpdateBatteryLevel(DeviceRowIdx, BatteryLevel); //GizMoCuz, temporarily...
2472 	}
2473 
2474 	if ((defaultName != NULL) && ((DeviceName == "Unknown") || (DeviceName.empty())))
2475 	{
2476 		if (strlen(defaultName) > 0)
2477 		{
2478 			DeviceName = defaultName;
2479 			m_sql.safe_query("UPDATE DeviceStatus SET Name='%q' WHERE (ID==%" PRIu64 ")", defaultName, DeviceRowIdx);
2480 		}
2481 	}
2482 
2483 	if (pHardware->m_bOutputLog)
2484 	{
2485 		std::string sdevicetype = RFX_Type_Desc(pRXCommand[1], 1);
2486 		if (pRXCommand[1] == pTypeGeneral)
2487 		{
2488 			const _tGeneralDevice* pMeter = reinterpret_cast<const _tGeneralDevice*>(pRXCommand);
2489 			sdevicetype += "/" + std::string(RFX_Type_SubType_Desc(pMeter->type, pMeter->subtype));
2490 		}
2491 		std::stringstream sTmp;
2492 		sTmp << "(" << pHardware->m_Name << ") " << sdevicetype << " (" << DeviceName << ")";
2493 		WriteMessageStart();
2494 		WriteMessage(sTmp.str().c_str());
2495 		WriteMessageEnd();
2496 	}
2497 
2498 	//TODO: Notify plugin?
2499 
2500 	//Send to connected Sharing Users
2501 	m_sharedserver.SendToAll(pHardware->m_HwdID, DeviceRowIdx, (const char*)pRXCommand, pRXCommand[0] + 1, pClient2Ignore);
2502 
2503 	sOnDeviceReceived(pHardware->m_HwdID, DeviceRowIdx, DeviceName, pRXCommand);
2504 }
2505 
decode_InterfaceMessage(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)2506 void MainWorker::decode_InterfaceMessage(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
2507 {
2508 	char szTmp[100];
2509 
2510 	WriteMessageStart();
2511 
2512 	switch (pResponse->IRESPONSE.subtype)
2513 	{
2514 	case sTypeInterfaceCommand:
2515 	{
2516 		int mlen = pResponse->IRESPONSE.packetlength;
2517 		WriteMessage("subtype           = Interface Response");
2518 		sprintf(szTmp, "Sequence nbr      = %d", pResponse->IRESPONSE.seqnbr);
2519 		WriteMessage(szTmp);
2520 		switch (pResponse->IRESPONSE.cmnd)
2521 		{
2522 		case cmdSTATUS:
2523 		case cmdSETMODE:
2524 		case trxType310:
2525 		case trxType315:
2526 		case recType43392:
2527 		case trxType43392:
2528 		case trxType868:
2529 		{
2530 			WriteMessage("response on cmnd  = ", false);
2531 			switch (pResponse->IRESPONSE.cmnd)
2532 			{
2533 			case cmdSTATUS:
2534 				WriteMessage("Get Status");
2535 				break;
2536 			case cmdSETMODE:
2537 				WriteMessage("Set Mode");
2538 				break;
2539 			case trxType310:
2540 				WriteMessage("Select 310MHz");
2541 				break;
2542 			case trxType315:
2543 				WriteMessage("Select 315MHz");
2544 				break;
2545 			case recType43392:
2546 				WriteMessage("Select 433.92MHz");
2547 				break;
2548 			case trxType43392:
2549 				WriteMessage("Select 433.92MHz (E)");
2550 				break;
2551 			case trxType868:
2552 				WriteMessage("Select 868.00MHz");
2553 				break;
2554 			default:
2555 				WriteMessage("Error: unknown response");
2556 				break;
2557 			}
2558 
2559 			m_sql.UpdateRFXCOMHardwareDetails(pHardware->m_HwdID, pResponse->IRESPONSE.msg1, pResponse->IRESPONSE.msg2, pResponse->ICMND.msg3, pResponse->ICMND.msg4, pResponse->ICMND.msg5, pResponse->ICMND.msg6);
2560 
2561 			switch (pResponse->IRESPONSE.msg1)
2562 			{
2563 			case trxType310:
2564 				WriteMessage("Transceiver type  = 310MHz");
2565 				break;
2566 			case trxType315:
2567 				WriteMessage("Receiver type     = 315MHz");
2568 				break;
2569 			case recType43392:
2570 				WriteMessage("Receiver type     = 433.92MHz (receive only)");
2571 				break;
2572 			case trxType43392:
2573 				WriteMessage("Transceiver type  = 433.92MHz");
2574 				break;
2575 			case trxType868:
2576 				WriteMessage("Receiver type     = 868.00MHz");
2577 				break;
2578 			default:
2579 				WriteMessage("Receiver type     = unknown");
2580 				break;
2581 			}
2582 			int FWType = 0;
2583 			int FWVersion = 0;
2584 			int NoiseLevel = 0;
2585 			if (mlen > 13)
2586 			{
2587 				FWType = pResponse->IRESPONSE.msg10;
2588 				FWVersion = pResponse->IRESPONSE.msg2 + 1000;
2589 			}
2590 			else
2591 			{
2592 				FWVersion = pResponse->IRESPONSE.msg2;
2593 				if ((pResponse->IRESPONSE.msg1 == recType43392) && (FWVersion < 162))
2594 					FWType = 0; //Type1 RFXrec
2595 				else if ((pResponse->IRESPONSE.msg1 == recType43392) && (FWVersion < 162))
2596 					FWType = 1; //Type1
2597 				else if ((pResponse->IRESPONSE.msg1 == recType43392) && ((FWVersion > 162) && (FWVersion < 225)))
2598 					FWType = 2; //Type2
2599 				else
2600 					FWType = 3; //Ext
2601 			}
2602 			sprintf(szTmp, "Firmware version  = %d", FWVersion);
2603 			WriteMessage(szTmp);
2604 
2605 			if (
2606 				(pResponse->IRESPONSE.msg1 == recType43392) ||
2607 				(pResponse->IRESPONSE.msg1 == trxType43392)
2608 				)
2609 			{
2610 				WriteMessage("Firmware type     = ", false);
2611 				switch (FWType)
2612 				{
2613 				case FWtyperec:
2614 					strcpy(szTmp, "Type1 RX");
2615 					break;
2616 				case FWtype1:
2617 					strcpy(szTmp, "Type1");
2618 					break;
2619 				case FWtype2:
2620 					strcpy(szTmp, "Type2");
2621 					break;
2622 				case FWtypeExt:
2623 					strcpy(szTmp, "Ext");
2624 					break;
2625 				case FWtypeExt2:
2626 					strcpy(szTmp, "Ext2");
2627 					break;
2628 				case FWtypePro1:
2629 					strcpy(szTmp, "Pro1");
2630 					NoiseLevel = static_cast<int>(pResponse->IRESPONSE.msg11);
2631 					break;
2632 				case FWtypePro2:
2633 					strcpy(szTmp, "Pro2");
2634 					NoiseLevel = static_cast<int>(pResponse->IRESPONSE.msg11);
2635 					break;
2636 				case FWtypeProXL1:
2637 					strcpy(szTmp, "Pro XL1");
2638 					NoiseLevel = static_cast<int>(pResponse->IRESPONSE.msg11);
2639 					break;
2640 				default:
2641 					strcpy(szTmp, "?");
2642 					break;
2643 				}
2644 				WriteMessage(szTmp);
2645 			}
2646 
2647 			CRFXBase* pMyHardware = (CRFXBase*)pHardware;
2648 			if (pMyHardware)
2649 			{
2650 				pMyHardware->m_Version = std::string(szTmp) + std::string("/") + std::to_string(FWVersion);
2651 
2652 				if (FWType >= FWtypePro1)
2653 				{
2654 					pMyHardware->m_NoiseLevel = NoiseLevel;
2655 					sprintf(szTmp, "Noise Level: %d", pMyHardware->m_NoiseLevel);
2656 					WriteMessage(szTmp);
2657 				}
2658 				if (FWType == FWtypeProXL1)
2659 				{
2660 					pMyHardware->SetAsyncType(pMyHardware->m_AsyncType);
2661 				}
2662 			}
2663 
2664 
2665 			sprintf(szTmp, "Hardware version  = %d.%d", pResponse->IRESPONSE.msg7, pResponse->IRESPONSE.msg8);
2666 			WriteMessage(szTmp);
2667 
2668 			if (pResponse->IRESPONSE.msg1 != trxType868)
2669 			{
2670 				if (pResponse->IRESPONSE.UNDECODEDenabled)
2671 					WriteMessage("Undec             on");
2672 				else
2673 					WriteMessage("Undec             off");
2674 
2675 				if (pResponse->IRESPONSE.X10enabled)
2676 					WriteMessage("X10               enabled");
2677 				else
2678 					WriteMessage("X10               disabled");
2679 
2680 				if (pResponse->IRESPONSE.ARCenabled)
2681 					WriteMessage("ARC               enabled");
2682 				else
2683 					WriteMessage("ARC               disabled");
2684 
2685 				if (pResponse->IRESPONSE.ACenabled)
2686 					WriteMessage("AC                enabled");
2687 				else
2688 					WriteMessage("AC                disabled");
2689 
2690 				if (pResponse->IRESPONSE.HEEUenabled)
2691 					WriteMessage("HomeEasy EU       enabled");
2692 				else
2693 					WriteMessage("HomeEasy EU       disabled");
2694 
2695 				if (pResponse->IRESPONSE.MEIANTECHenabled)
2696 					WriteMessage("Meiantech/Atlantic enabled");
2697 				else
2698 					WriteMessage("Meiantech/Atlantic disabled");
2699 
2700 				if (pResponse->IRESPONSE.OREGONenabled)
2701 					WriteMessage("Oregon Scientific enabled");
2702 				else
2703 					WriteMessage("Oregon Scientific disabled");
2704 
2705 				if (pResponse->IRESPONSE.ATIenabled)
2706 					WriteMessage("ATI/Cartelectronic enabled");
2707 				else
2708 					WriteMessage("ATI/Cartelectronic disabled");
2709 
2710 				if (pResponse->IRESPONSE.VISONICenabled)
2711 					WriteMessage("Visonic           enabled");
2712 				else
2713 					WriteMessage("Visonic           disabled");
2714 
2715 				if (pResponse->IRESPONSE.MERTIKenabled)
2716 					WriteMessage("Mertik            enabled");
2717 				else
2718 					WriteMessage("Mertik            disabled");
2719 
2720 				if (pResponse->IRESPONSE.LWRFenabled)
2721 					WriteMessage("AD                enabled");
2722 				else
2723 					WriteMessage("AD                disabled");
2724 
2725 				if (pResponse->IRESPONSE.HIDEKIenabled)
2726 					WriteMessage("Hideki            enabled");
2727 				else
2728 					WriteMessage("Hideki            disabled");
2729 
2730 				if (pResponse->IRESPONSE.LACROSSEenabled)
2731 					WriteMessage("La Crosse         enabled");
2732 				else
2733 					WriteMessage("La Crosse         disabled");
2734 
2735 				if (pResponse->IRESPONSE.LEGRANDenabled)
2736 					WriteMessage("Legrand           enabled");
2737 				else
2738 					WriteMessage("Legrand           disabled");
2739 
2740 				if (pResponse->IRESPONSE.MSG4Reserved5)
2741 					WriteMessage("MSG4Reserved5     enabled");
2742 				else
2743 					WriteMessage("MSG4Reserved5     disabled");
2744 
2745 				if (pResponse->IRESPONSE.BLINDST0enabled)
2746 					WriteMessage("BlindsT0          enabled");
2747 				else
2748 					WriteMessage("BlindsT0          disabled");
2749 
2750 				if (pResponse->IRESPONSE.BLINDST1enabled)
2751 					WriteMessage("BlindsT1          enabled");
2752 				else
2753 					WriteMessage("BlindsT1          disabled");
2754 
2755 				if (pResponse->IRESPONSE.AEenabled)
2756 					WriteMessage("AE                enabled");
2757 				else
2758 					WriteMessage("AE                disabled");
2759 
2760 				if (pResponse->IRESPONSE.RUBICSONenabled)
2761 					WriteMessage("RUBiCSON          enabled");
2762 				else
2763 					WriteMessage("RUBiCSON          disabled");
2764 
2765 				if (pResponse->IRESPONSE.FINEOFFSETenabled)
2766 					WriteMessage("FineOffset        enabled");
2767 				else
2768 					WriteMessage("FineOffset        disabled");
2769 
2770 				if (pResponse->IRESPONSE.LIGHTING4enabled)
2771 					WriteMessage("Lighting4         enabled");
2772 				else
2773 					WriteMessage("Lighting4         disabled");
2774 
2775 				if (pResponse->IRESPONSE.RSLenabled)
2776 					WriteMessage("Conrad RSL        enabled");
2777 				else
2778 					WriteMessage("Conrad RSL        disabled");
2779 
2780 				if (pResponse->IRESPONSE.SXenabled)
2781 					WriteMessage("ByronSX           enabled");
2782 				else
2783 					WriteMessage("ByronSX           disabled");
2784 
2785 				if (pResponse->IRESPONSE.IMAGINTRONIXenabled)
2786 					WriteMessage("IMAGINTRONIX      enabled");
2787 				else
2788 					WriteMessage("IMAGINTRONIX      disabled");
2789 
2790 				if (pResponse->IRESPONSE.KEELOQenabled)
2791 					WriteMessage("KEELOQ            enabled");
2792 				else
2793 					WriteMessage("KEELOQ            disabled");
2794 
2795 				if (pResponse->IRESPONSE.HCEnabled)
2796 					WriteMessage("Home Confort      enabled");
2797 				else
2798 					WriteMessage("Home Confort      disabled");
2799 			}
2800 			else
2801 			{
2802 				//868
2803 				if (pResponse->IRESPONSE868.UNDECODEDenabled)
2804 					WriteMessage("Undec             on");
2805 				else
2806 					WriteMessage("Undec             off");
2807 
2808 				if (pResponse->IRESPONSE868.ALECTOenabled)
2809 					WriteMessage("Alecto ACH2010    enabled");
2810 
2811 				if (pResponse->IRESPONSE868.ALECTO5500enabled)
2812 					WriteMessage("Alecto WS5500     enabled");
2813 
2814 				if (pResponse->IRESPONSE868.LACROSSEenabled)
2815 					WriteMessage("LA Crosse         enabled");
2816 
2817 				if (pResponse->IRESPONSE868.DAVISEUenabled)
2818 					WriteMessage("Davis EU          enabled");
2819 
2820 				if (pResponse->IRESPONSE868.DAVISUSenabled)
2821 					WriteMessage("Davis US          enabled");
2822 
2823 				if (pResponse->IRESPONSE868.DAVISAUenabled)
2824 					WriteMessage("Davis AU          enabled");
2825 
2826 				if (pResponse->IRESPONSE868.FS20enabled)
2827 					WriteMessage("FS20              enabled");
2828 
2829 				if (pResponse->IRESPONSE868.LWRFenabled)
2830 					WriteMessage("LightwaveRF       enabled");
2831 
2832 				if (pResponse->IRESPONSE868.EDISIOenabled)
2833 					WriteMessage("Edisio            enabled");
2834 
2835 				if (pResponse->IRESPONSE868.VISONICenabled)
2836 					WriteMessage("Visonic           enabled");
2837 
2838 				if (pResponse->IRESPONSE868.MEIANTECHenabled)
2839 					WriteMessage("Meiantech         enabled");
2840 
2841 				if (pResponse->IRESPONSE868.KEELOQenabled)
2842 					WriteMessage("Keeloq            enabled");
2843 
2844 				if (pResponse->IRESPONSE868.PROGUARDenabled)
2845 					WriteMessage("Proguard          enabled");
2846 
2847 				if (pResponse->IRESPONSE868.ITHOenabled)
2848 					WriteMessage("Itho CVE RFT      enabled");
2849 
2850 				if (pResponse->IRESPONSE868.ITHOecoenabled)
2851 					WriteMessage("Itho CVE ECO RFT  enabled");
2852 
2853 				if (pResponse->IRESPONSE868.HONEYWELLenabled)
2854 					WriteMessage("Honeywell Chime   enabled");
2855 
2856 			}
2857 		}
2858 		break;
2859 		case cmdSAVE:
2860 			WriteMessage("response on cmnd  = Save");
2861 			break;
2862 		}
2863 		break;
2864 	}
2865 	break;
2866 	case sTypeUnknownRFYremote:
2867 		WriteMessage("subtype           = Unknown RFY remote! Use the Program command to create a remote in the RFXtrx433Ext");
2868 		sprintf(szTmp, "Sequence nbr      = %d", pResponse->IRESPONSE.seqnbr);
2869 		WriteMessage(szTmp);
2870 		break;
2871 	case sTypeExtError:
2872 		WriteMessage("subtype           = No RFXtrx433E hardware detected");
2873 		sprintf(szTmp, "Sequence nbr      = %d", pResponse->IRESPONSE.seqnbr);
2874 		WriteMessage(szTmp);
2875 		break;
2876 	case sTypeRFYremoteList:
2877 		if ((pResponse->ICMND.xmitpwr == 0) && (pResponse->ICMND.msg3 == 0) && (pResponse->ICMND.msg4 == 0) && (pResponse->ICMND.msg5 == 0))
2878 		{
2879 			sprintf(szTmp, "subtype           = RFY remote: %d is empty", pResponse->ICMND.freqsel);
2880 			WriteMessage(szTmp);
2881 		}
2882 		else
2883 		{
2884 			sprintf(szTmp, "subtype           = RFY remote: %d, ID: %02d%02d%02d, unitnbr: %d",
2885 				pResponse->ICMND.freqsel,
2886 				pResponse->ICMND.xmitpwr,
2887 				pResponse->ICMND.msg3,
2888 				pResponse->ICMND.msg4,
2889 				pResponse->ICMND.msg5);
2890 			WriteMessage(szTmp);
2891 		}
2892 		break;
2893 	case sTypeASAremoteList:
2894 		if ((pResponse->ICMND.xmitpwr == 0) && (pResponse->ICMND.msg3 == 0) && (pResponse->ICMND.msg4 == 0) && (pResponse->ICMND.msg5 == 0))
2895 		{
2896 			sprintf(szTmp, "subtype           = ASA remote: %d is empty", pResponse->ICMND.freqsel);
2897 			WriteMessage(szTmp);
2898 		}
2899 		else
2900 		{
2901 			sprintf(szTmp, "subtype           = ASA remote: %d, ID: %02d%02d%02d, unitnbr: %d",
2902 				pResponse->ICMND.freqsel,
2903 				pResponse->ICMND.xmitpwr,
2904 				pResponse->ICMND.msg3,
2905 				pResponse->ICMND.msg4,
2906 				pResponse->ICMND.msg5);
2907 			WriteMessage(szTmp);
2908 		}
2909 		break;
2910 	case sTypeInterfaceWrongCommand:
2911 		if (pResponse->IRESPONSE.cmnd == 0x07)
2912 		{
2913 			WriteMessage("Please upgrade your RFXTrx Firmware!");
2914 		}
2915 		else
2916 		{
2917 			sprintf(szTmp, "subtype          = Wrong command received from application (%d)", pResponse->IRESPONSE.cmnd);
2918 			WriteMessage(szTmp);
2919 			sprintf(szTmp, "Sequence nbr      = %d", pResponse->IRESPONSE.seqnbr);
2920 			WriteMessage(szTmp);
2921 		}
2922 		break;
2923 	}
2924 	WriteMessageEnd();
2925 	procResult.DeviceRowIdx = -1;
2926 }
2927 
decode_InterfaceControl(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)2928 void MainWorker::decode_InterfaceControl(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
2929 {
2930 	char szTmp[100];
2931 	WriteMessageStart();
2932 	switch (pResponse->IRESPONSE.subtype)
2933 	{
2934 	case sTypeInterfaceCommand:
2935 		WriteMessage("subtype           = Interface Command");
2936 		sprintf(szTmp, "Sequence nbr      = %d", pResponse->IRESPONSE.seqnbr);
2937 		WriteMessage(szTmp);
2938 		switch (pResponse->IRESPONSE.cmnd)
2939 		{
2940 		case cmdRESET:
2941 			WriteMessage("reset the receiver/transceiver");
2942 			break;
2943 		case cmdSTATUS:
2944 			WriteMessage("return firmware versions and configuration of the interface");
2945 			break;
2946 		case cmdSETMODE:
2947 			WriteMessage("set configuration of the interface");
2948 			break;
2949 		case cmdSAVE:
2950 			WriteMessage("save receiving modes of the receiver/transceiver in non-volatile memory");
2951 			break;
2952 		case cmdStartRec:
2953 			WriteMessage("start RFXtrx receiver");
2954 			break;
2955 		case trxType310:
2956 			WriteMessage("select 310MHz in the 310/315 transceiver");
2957 			break;
2958 		case trxType315:
2959 			WriteMessage("select 315MHz in the 310/315 transceiver");
2960 			break;
2961 		case recType43392:
2962 		case trxType43392:
2963 			WriteMessage("select 433.92MHz in the 433 transceiver");
2964 			break;
2965 		case trxType868:
2966 			WriteMessage("select 868MHz in the 868 transceiver");
2967 			break;
2968 		}
2969 		break;
2970 	}
2971 	WriteMessageEnd();
2972 	procResult.DeviceRowIdx = -1;
2973 }
2974 
decode_BateryLevel(bool bIsInPercentage,uint8_t level)2975 void MainWorker::decode_BateryLevel(bool bIsInPercentage, uint8_t level)
2976 {
2977 	if (bIsInPercentage)
2978 	{
2979 		switch (level)
2980 		{
2981 		case 0:
2982 			WriteMessage("Battery       = 10%");
2983 			break;
2984 		case 1:
2985 			WriteMessage("Battery       = 20%");
2986 			break;
2987 		case 2:
2988 			WriteMessage("Battery       = 30%");
2989 			break;
2990 		case 3:
2991 			WriteMessage("Battery       = 40%");
2992 			break;
2993 		case 4:
2994 			WriteMessage("Battery       = 50%");
2995 			break;
2996 		case 5:
2997 			WriteMessage("Battery       = 60%");
2998 			break;
2999 		case 6:
3000 			WriteMessage("Battery       = 70%");
3001 			break;
3002 		case 7:
3003 			WriteMessage("Battery       = 80%");
3004 			break;
3005 		case 8:
3006 			WriteMessage("Battery       = 90%");
3007 			break;
3008 		case 9:
3009 			WriteMessage("Battery       = 100%");
3010 			break;
3011 		}
3012 	}
3013 	else
3014 	{
3015 		if (level == 0)
3016 		{
3017 			WriteMessage("Battery       = Low");
3018 		}
3019 		else
3020 		{
3021 			WriteMessage("Battery       = OK");
3022 		}
3023 	}
3024 }
3025 
get_BateryLevel(const _eHardwareTypes HwdType,bool bIsInPercentage,uint8_t level)3026 uint8_t MainWorker::get_BateryLevel(const _eHardwareTypes HwdType, bool bIsInPercentage, uint8_t level)
3027 {
3028 	if (HwdType == HTYPE_OpenZWave)
3029 	{
3030 		bIsInPercentage = true;
3031 	}
3032 	uint8_t ret = 0;
3033 	if (bIsInPercentage)
3034 	{
3035 		if (level >= 0 && level <= 9)
3036 			ret = (level + 1) * 10;
3037 	}
3038 	else
3039 	{
3040 		if (level == 0)
3041 		{
3042 			ret = 0;
3043 		}
3044 		else
3045 		{
3046 			ret = 100;
3047 		}
3048 	}
3049 	return ret;
3050 }
3051 
decode_Rain(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)3052 void MainWorker::decode_Rain(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
3053 {
3054 	char szTmp[100];
3055 	uint8_t devType = pTypeRAIN;
3056 	uint8_t subType = pResponse->RAIN.subtype;
3057 	std::string ID;
3058 	sprintf(szTmp, "%d", (pResponse->RAIN.id1 * 256) + pResponse->RAIN.id2);
3059 	ID = szTmp;
3060 	uint8_t Unit = 0;
3061 	uint8_t cmnd = 0;
3062 	uint8_t SignalLevel = pResponse->RAIN.rssi;
3063 	uint8_t BatteryLevel = get_BateryLevel(pHardware->HwdType, pResponse->RAIN.subtype == sTypeRAIN1, pResponse->RAIN.battery_level & 0x0F);
3064 
3065 	int Rainrate = (pResponse->RAIN.rainrateh * 256) + pResponse->RAIN.rainratel;
3066 
3067 	float TotalRain = float((pResponse->RAIN.raintotal1 * 65535) + (pResponse->RAIN.raintotal2 * 256) + pResponse->RAIN.raintotal3) / 10.0f;
3068 
3069 	if (subType == sTypeRAINByRate)
3070 	{
3071 		//calculate new Total
3072 		TotalRain = 0;
3073 
3074 		std::vector<std::vector<std::string>> result;
3075 
3076 		//Get our index
3077 		result = m_sql.safe_query(
3078 			"SELECT ID FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)", pHardware->m_HwdID, ID.c_str(), Unit, devType, subType);
3079 		if (!result.empty())
3080 		{
3081 			uint64_t ulID = std::strtoull(result[0][0].c_str(), nullptr, 10);
3082 
3083 			time_t now = mytime(NULL);
3084 			struct tm ltime;
3085 			localtime_r(&now, &ltime);
3086 
3087 			std::vector<std::vector<std::string>> result;
3088 			result = m_sql.safe_query(
3089 				"SELECT Rate, Date FROM Rain WHERE (DeviceRowID=%" PRIu64 " AND Date>='%04d-%02d-%02d') ORDER BY ROWID ASC",
3090 				ulID, ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
3091 			if (!result.empty())
3092 			{
3093 				time_t countTime;
3094 				struct tm midnightTime;
3095 				getMidnight(countTime, midnightTime);
3096 
3097 				for (const auto& itt : result)
3098 				{
3099 					std::vector<std::string> sd = itt;
3100 
3101 					float rate = (float)atof(sd[0].c_str()) / 10000.0f;
3102 					std::string date = sd[1];
3103 
3104 					time_t rowtime;
3105 					struct tm rowTimetm;
3106 					ParseSQLdatetime(rowtime, rowTimetm, date);
3107 
3108 					int pastSeconds = (int)(rowtime - countTime);
3109 
3110 					float rateAdd = (rate / (float)3600 * (float)pastSeconds);
3111 					TotalRain += rateAdd;
3112 
3113 					countTime = rowtime;
3114 				}
3115 			}
3116 		}
3117 	}
3118 	else if (subType != sTypeRAINWU)
3119 	{
3120 		Rainrate = 0;
3121 		//Calculate our own rainrate
3122 		std::vector<std::vector<std::string>> result;
3123 
3124 		//Get our index
3125 		result = m_sql.safe_query(
3126 			"SELECT ID FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)", pHardware->m_HwdID, ID.c_str(), Unit, devType, subType);
3127 		if (!result.empty())
3128 		{
3129 			uint64_t ulID = std::strtoull(result[0][0].c_str(), nullptr, 10);
3130 
3131 			//Get Counter from one Hour ago
3132 			time_t now = mytime(NULL);
3133 			now -= 3600; //subtract one hour
3134 			struct tm ltime;
3135 			localtime_r(&now, &ltime);
3136 
3137 			std::vector<std::vector<std::string>> result;
3138 			result = m_sql.safe_query(
3139 				"SELECT MIN(Total) FROM Rain WHERE (DeviceRowID=%" PRIu64 " AND Date>='%04d-%02d-%02d %02d:%02d:%02d')",
3140 				ulID, ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday, ltime.tm_hour, ltime.tm_min, ltime.tm_sec);
3141 			if (result.size() == 1)
3142 			{
3143 				float totalRainFallLastHour = TotalRain - static_cast<float>(atof(result[0][0].c_str()));
3144 				Rainrate = round(totalRainFallLastHour * 100.0f);
3145 			}
3146 		}
3147 	}
3148 
3149 	sprintf(szTmp, "%d;%.1f", Rainrate, TotalRain);
3150 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
3151 	if (DevRowIdx == (uint64_t)-1)
3152 		return;
3153 
3154 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, cmnd, szTmp);
3155 
3156 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
3157 	{
3158 		WriteMessageStart();
3159 		switch (subType)
3160 		{
3161 		case sTypeRAIN1:
3162 			WriteMessage("subtype       = RAIN1 - RGR126/682/918/928");
3163 			break;
3164 		case sTypeRAIN2:
3165 			WriteMessage("subtype       = RAIN2 - PCR800");
3166 			break;
3167 		case sTypeRAIN3:
3168 			WriteMessage("subtype       = RAIN3 - TFA");
3169 			break;
3170 		case sTypeRAIN4:
3171 			WriteMessage("subtype       = RAIN4 - UPM RG700");
3172 			break;
3173 		case sTypeRAIN5:
3174 			WriteMessage("subtype       = RAIN5 - LaCrosse WS2300");
3175 			break;
3176 		case sTypeRAIN6:
3177 			WriteMessage("subtype       = RAIN6 - LaCrosse TX5");
3178 			break;
3179 		case sTypeRAIN7:
3180 			WriteMessage("subtype       = RAIN7 - Alecto");
3181 			break;
3182 		case sTypeRAIN8:
3183 			WriteMessage("subtype       = RAIN8 - Davis");
3184 			break;
3185 		case sTypeRAIN9:
3186 			WriteMessage("subtype       = RAIN9 - TFA 30.3233.01");
3187 			break;
3188 		case sTypeRAINWU:
3189 			WriteMessage("subtype       = Weather Underground (Total Rain)");
3190 			break;
3191 		default:
3192 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X : %02X", pResponse->RAIN.packettype, pResponse->RAIN.subtype);
3193 			WriteMessage(szTmp);
3194 			break;
3195 		}
3196 
3197 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->RAIN.seqnbr);
3198 		WriteMessage(szTmp);
3199 
3200 		sprintf(szTmp, "ID            = %s", ID.c_str());
3201 		WriteMessage(szTmp);
3202 
3203 		if (pResponse->RAIN.subtype == sTypeRAIN1)
3204 		{
3205 			sprintf(szTmp, "Rain rate     = %d mm/h", Rainrate);
3206 			WriteMessage(szTmp);
3207 		}
3208 		else if (pResponse->RAIN.subtype == sTypeRAIN2)
3209 		{
3210 			sprintf(szTmp, "Rain rate     = %d mm/h", Rainrate);
3211 			WriteMessage(szTmp);
3212 		}
3213 
3214 		sprintf(szTmp, "Total rain    = %.1f mm", TotalRain);
3215 		WriteMessage(szTmp);
3216 		sprintf(szTmp, "Signal level  = %d", pResponse->RAIN.rssi);
3217 		WriteMessage(szTmp);
3218 
3219 		decode_BateryLevel(pResponse->RAIN.subtype == sTypeRAIN1, pResponse->RAIN.battery_level & 0x0F);
3220 		WriteMessageEnd();
3221 	}
3222 	procResult.DeviceRowIdx = DevRowIdx;
3223 }
3224 
decode_Wind(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)3225 void MainWorker::decode_Wind(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
3226 {
3227 	char szTmp[300];
3228 	uint8_t devType = pTypeWIND;
3229 	uint8_t subType = pResponse->WIND.subtype;
3230 	uint16_t windID = (pResponse->WIND.id1 * 256) + pResponse->WIND.id2;
3231 	sprintf(szTmp, "%d", windID);
3232 	std::string ID = szTmp;
3233 	uint8_t Unit = 0;
3234 
3235 	uint8_t cmnd = 0;
3236 	uint8_t SignalLevel = pResponse->WIND.rssi;
3237 	uint8_t BatteryLevel = get_BateryLevel(pHardware->HwdType, pResponse->WIND.subtype == sTypeWIND3, pResponse->WIND.battery_level & 0x0F);
3238 
3239 	double dDirection;
3240 	dDirection = (double)(pResponse->WIND.directionh * 256) + pResponse->WIND.directionl;
3241 	dDirection = m_wind_calculator[windID].AddValueAndReturnAvarage(dDirection);
3242 
3243 	std::string strDirection;
3244 	if (dDirection > 348.75 || dDirection < 11.26)
3245 		strDirection = "N";
3246 	else if (dDirection < 33.76)
3247 		strDirection = "NNE";
3248 	else if (dDirection < 56.26)
3249 		strDirection = "NE";
3250 	else if (dDirection < 78.76)
3251 		strDirection = "ENE";
3252 	else if (dDirection < 101.26)
3253 		strDirection = "E";
3254 	else if (dDirection < 123.76)
3255 		strDirection = "ESE";
3256 	else if (dDirection < 146.26)
3257 		strDirection = "SE";
3258 	else if (dDirection < 168.76)
3259 		strDirection = "SSE";
3260 	else if (dDirection < 191.26)
3261 		strDirection = "S";
3262 	else if (dDirection < 213.76)
3263 		strDirection = "SSW";
3264 	else if (dDirection < 236.26)
3265 		strDirection = "SW";
3266 	else if (dDirection < 258.76)
3267 		strDirection = "WSW";
3268 	else if (dDirection < 281.26)
3269 		strDirection = "W";
3270 	else if (dDirection < 303.76)
3271 		strDirection = "WNW";
3272 	else if (dDirection < 326.26)
3273 		strDirection = "NW";
3274 	else if (dDirection < 348.76)
3275 		strDirection = "NNW";
3276 	else
3277 		strDirection = "---";
3278 
3279 	dDirection = round(dDirection);
3280 
3281 	int intSpeed = (pResponse->WIND.av_speedh * 256) + pResponse->WIND.av_speedl;
3282 	int intGust = (pResponse->WIND.gusth * 256) + pResponse->WIND.gustl;
3283 
3284 	if (pResponse->WIND.subtype == sTypeWIND6)
3285 	{
3286 		//LaCrosse WS2300
3287 		//This sensor is only reporting gust, speed=gust
3288 		intSpeed = intGust;
3289 	}
3290 
3291 	m_wind_calculator[windID].SetSpeedGust(intSpeed, intGust);
3292 
3293 	float temp = 0, chill = 0;
3294 	if (subType != sTypeWINDNoTempNoChill)
3295 	{
3296 		if (pResponse->WIND.subtype == sTypeWIND4)
3297 		{
3298 			if (!pResponse->WIND.tempsign)
3299 			{
3300 				temp = float((pResponse->WIND.temperatureh * 256) + pResponse->WIND.temperaturel) / 10.0f;
3301 			}
3302 			else
3303 			{
3304 				temp = -(float(((pResponse->WIND.temperatureh & 0x7F) * 256) + pResponse->WIND.temperaturel) / 10.0f);
3305 			}
3306 			if ((temp < -200) || (temp > 380))
3307 			{
3308 				WriteMessage(" Invalid Temperature");
3309 				return;
3310 			}
3311 
3312 			float AddjValue = 0.0f;
3313 			float AddjMulti = 1.0f;
3314 			m_sql.GetAddjustment(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, AddjValue, AddjMulti);
3315 			temp += AddjValue;
3316 
3317 			if (!pResponse->WIND.chillsign)
3318 			{
3319 				chill = float((pResponse->WIND.chillh * 256) + pResponse->WIND.chilll) / 10.0f;
3320 			}
3321 			else
3322 			{
3323 				chill = -(float(((pResponse->WIND.chillh) & 0x7F) * 256 + pResponse->WIND.chilll) / 10.0f);
3324 			}
3325 			chill += AddjValue;
3326 		}
3327 		else if (pResponse->WIND.subtype == sTypeWINDNoTemp)
3328 		{
3329 			float AddjValue = 0.0f;
3330 			float AddjMulti = 1.0f;
3331 			m_sql.GetAddjustment(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, AddjValue, AddjMulti);
3332 			temp += AddjValue;
3333 
3334 			if (!pResponse->WIND.chillsign)
3335 			{
3336 				chill = float((pResponse->WIND.chillh * 256) + pResponse->WIND.chilll) / 10.0f;
3337 			}
3338 			else
3339 			{
3340 				chill = -(float(((pResponse->WIND.chillh) & 0x7F) * 256 + pResponse->WIND.chilll) / 10.0f);
3341 			}
3342 			chill += AddjValue;
3343 		}
3344 		if (chill == 0)
3345 		{
3346 			float wspeedms = float(intSpeed) / 10.0f;
3347 			if ((temp < 10.0) && (wspeedms >= 1.4))
3348 			{
3349 				float chillJatTI = 13.12f + 0.6215f * temp - 11.37f * pow(wspeedms * 3.6f, 0.16f) + 0.3965f * temp * pow(wspeedms * 3.6f, 0.16f);
3350 				chill = chillJatTI;
3351 			}
3352 		}
3353 	}
3354 
3355 	sprintf(szTmp, "%.2f;%s;%d;%d;%.1f;%.1f", dDirection, strDirection.c_str(), intSpeed, intGust, temp, chill);
3356 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
3357 	if (DevRowIdx == (uint64_t)-1)
3358 		return;
3359 
3360 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, cmnd, szTmp);
3361 
3362 	uint64_t tID = ((uint64_t)(pHardware->m_HwdID & 0x7FFFFFFF) << 32) | (DevRowIdx & 0x7FFFFFFF);
3363 	m_trend_calculator[tID].AddValueAndReturnTendency(static_cast<double>(chill), _tTrendCalculator::TAVERAGE_TEMP);
3364 
3365 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
3366 	{
3367 		WriteMessageStart();
3368 		switch (pResponse->WIND.subtype)
3369 		{
3370 		case sTypeWIND1:
3371 			WriteMessage("subtype       = WIND1 - WTGR800");
3372 			break;
3373 		case sTypeWIND2:
3374 			WriteMessage("subtype       = WIND2 - WGR800");
3375 			break;
3376 		case sTypeWIND3:
3377 			WriteMessage("subtype       = WIND3 - STR918/928, WGR918");
3378 			break;
3379 		case sTypeWIND4:
3380 			WriteMessage("subtype       = WIND4 - TFA");
3381 			break;
3382 		case sTypeWIND5:
3383 			WriteMessage("subtype       = WIND5 - UPM WDS500");
3384 			break;
3385 		case sTypeWIND6:
3386 			WriteMessage("subtype       = WIND6 - LaCrosse WS2300");
3387 			break;
3388 		case sTypeWIND7:
3389 			WriteMessage("subtype       = WIND7 - Alecto WS4500");
3390 			break;
3391 		case sTypeWINDNoTemp:
3392 			WriteMessage("subtype       = Weather Station");
3393 			break;
3394 		case sTypeWINDNoTempNoChill:
3395 			WriteMessage("subtype       = Wind (No Temp or Chill sensors");
3396 			break;
3397 		default:
3398 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->WIND.packettype, pResponse->WIND.subtype);
3399 			WriteMessage(szTmp);
3400 			break;
3401 		}
3402 
3403 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->WIND.seqnbr);
3404 		WriteMessage(szTmp);
3405 		sprintf(szTmp, "ID            = %s", ID.c_str());
3406 		WriteMessage(szTmp);
3407 
3408 		sprintf(szTmp, "Direction     = %d degrees %s", int(dDirection), strDirection.c_str());
3409 		WriteMessage(szTmp);
3410 
3411 		if (pResponse->WIND.subtype != sTypeWIND5)
3412 		{
3413 			sprintf(szTmp, "Average speed = %.1f mtr/sec, %.2f km/hr, %.2f mph", float(intSpeed) / 10.0f, (float(intSpeed) * 0.36f), (float(intSpeed) * 0.223693629f));
3414 			WriteMessage(szTmp);
3415 		}
3416 
3417 		sprintf(szTmp, "Wind gust     = %.1f mtr/sec, %.2f km/hr, %.2f mph", float(intGust) / 10.0f, (float(intGust) * 0.36f), (float(intGust) * 0.223693629f));
3418 		WriteMessage(szTmp);
3419 
3420 		if (pResponse->WIND.subtype == sTypeWIND4)
3421 		{
3422 			sprintf(szTmp, "Temperature   = %.1f C", temp);
3423 			WriteMessage(szTmp);
3424 
3425 			sprintf(szTmp, "Chill         = %.1f C", chill);
3426 			WriteMessage(szTmp);
3427 		}
3428 		if (pResponse->WIND.subtype == sTypeWINDNoTemp)
3429 		{
3430 			sprintf(szTmp, "Chill         = %.1f C", chill);
3431 			WriteMessage(szTmp);
3432 		}
3433 
3434 		sprintf(szTmp, "Signal level  = %d", pResponse->WIND.rssi);
3435 		WriteMessage(szTmp);
3436 
3437 		decode_BateryLevel(pResponse->WIND.subtype == sTypeWIND3, pResponse->WIND.battery_level & 0x0F);
3438 		WriteMessageEnd();
3439 	}
3440 	procResult.DeviceRowIdx = DevRowIdx;
3441 }
3442 
decode_Temp(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)3443 void MainWorker::decode_Temp(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
3444 {
3445 	char szTmp[100];
3446 	uint8_t devType = pTypeTEMP;
3447 	uint8_t subType = pResponse->TEMP.subtype;
3448 	sprintf(szTmp, "%d", (pResponse->TEMP.id1 * 256) + pResponse->TEMP.id2);
3449 	std::string ID = szTmp;
3450 	uint8_t Unit = pResponse->TEMP.id2;
3451 
3452 	uint8_t cmnd = 0;
3453 	uint8_t SignalLevel = pResponse->TEMP.rssi;
3454 	uint8_t BatteryLevel = 0;
3455 	if ((pResponse->TEMP.battery_level & 0x0F) == 0)
3456 		BatteryLevel = 0;
3457 	else
3458 		BatteryLevel = 100;
3459 
3460 	//Override battery level if hardware supports it
3461 	if (pHardware->HwdType == HTYPE_OpenZWave)
3462 	{
3463 		BatteryLevel = pResponse->TEMP.battery_level * 10;
3464 	}
3465 	else if ((pHardware->HwdType == HTYPE_EnOceanESP2) || (pHardware->HwdType == HTYPE_EnOceanESP3))
3466 	{
3467 		BatteryLevel = 255;
3468 		SignalLevel = 12;
3469 		Unit = (pResponse->TEMP.rssi << 4) | pResponse->TEMP.battery_level;
3470 	}
3471 
3472 	float temp;
3473 	if (!pResponse->TEMP.tempsign)
3474 	{
3475 		temp = float((pResponse->TEMP.temperatureh * 256) + pResponse->TEMP.temperaturel) / 10.0f;
3476 	}
3477 	else
3478 	{
3479 		temp = -(float(((pResponse->TEMP.temperatureh & 0x7F) * 256) + pResponse->TEMP.temperaturel) / 10.0f);
3480 	}
3481 	if ((temp < -200) || (temp > 380))
3482 	{
3483 		WriteMessage(" Invalid Temperature");
3484 		return;
3485 	}
3486 
3487 	float AddjValue = 0.0f;
3488 	float AddjMulti = 1.0f;
3489 	m_sql.GetAddjustment(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, AddjValue, AddjMulti);
3490 	temp += AddjValue;
3491 
3492 	sprintf(szTmp, "%.1f", temp);
3493 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
3494 	if (DevRowIdx == (uint64_t)-1)
3495 		return;
3496 
3497 	uint64_t tID = ((uint64_t)(pHardware->m_HwdID & 0x7FFFFFFF) << 32) | (DevRowIdx & 0x7FFFFFFF);
3498 	m_trend_calculator[tID].AddValueAndReturnTendency(static_cast<double>(temp), _tTrendCalculator::TAVERAGE_TEMP);
3499 
3500 	bool bHandledNotification = false;
3501 	uint8_t humidity = 0;
3502 	if (pResponse->TEMP.subtype == sTypeTEMP5)
3503 	{
3504 		//check if we already had a humidity for this device, if so, keep it!
3505 		char szTmp[300];
3506 		std::vector<std::vector<std::string> > result;
3507 
3508 		result = m_sql.safe_query(
3509 			"SELECT nValue,sValue FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)",
3510 			pHardware->m_HwdID, ID.c_str(), 1, pTypeHUM, sTypeHUM1);
3511 		if (result.size() == 1)
3512 		{
3513 			m_sql.GetAddjustment(pHardware->m_HwdID, ID.c_str(), 2, pTypeTEMP_HUM, sTypeTH_LC_TC, AddjValue, AddjMulti);
3514 			temp += AddjValue;
3515 			humidity = atoi(result[0][0].c_str());
3516 			uint8_t humidity_status = atoi(result[0][1].c_str());
3517 			sprintf(szTmp, "%.1f;%d;%d", temp, humidity, humidity_status);
3518 			DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), 2, pTypeTEMP_HUM, sTypeTH_LC_TC, SignalLevel, BatteryLevel, 0, szTmp, procResult.DeviceName);
3519 			m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, pTypeTEMP_HUM, sTypeTH_LC_TC, szTmp);
3520 
3521 			bHandledNotification = true;
3522 		}
3523 	}
3524 
3525 	if (!bHandledNotification)
3526 		m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, temp);
3527 
3528 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
3529 	{
3530 		WriteMessageStart();
3531 		switch (pResponse->TEMP.subtype)
3532 		{
3533 		case sTypeTEMP1:
3534 			WriteMessage("subtype       = TEMP1 - THR128/138, THC138");
3535 			sprintf(szTmp, "                channel %d", pResponse->TEMP.id2);
3536 			WriteMessage(szTmp);
3537 			break;
3538 		case sTypeTEMP2:
3539 			WriteMessage("subtype       = TEMP2 - THC238/268,THN132,THWR288,THRN122,THN122,AW129/131");
3540 			sprintf(szTmp, "                channel %d", pResponse->TEMP.id2);
3541 			WriteMessage(szTmp);
3542 			break;
3543 		case sTypeTEMP3:
3544 			WriteMessage("subtype       = TEMP3 - THWR800");
3545 			break;
3546 		case sTypeTEMP4:
3547 			WriteMessage("subtype       = TEMP4 - RTHN318");
3548 			sprintf(szTmp, "                channel %d", pResponse->TEMP.id2);
3549 			WriteMessage(szTmp);
3550 			break;
3551 		case sTypeTEMP5:
3552 			WriteMessage("subtype       = TEMP5 - LaCrosse TX2, TX3, TX4, TX17");
3553 			break;
3554 		case sTypeTEMP6:
3555 			WriteMessage("subtype       = TEMP6 - TS15C");
3556 			break;
3557 		case sTypeTEMP7:
3558 			WriteMessage("subtype       = TEMP7 - Viking 02811, Proove TSS330");
3559 			break;
3560 		case sTypeTEMP8:
3561 			WriteMessage("subtype       = TEMP8 - LaCrosse WS2300");
3562 			break;
3563 		case sTypeTEMP9:
3564 			if (pResponse->TEMP.id2 & 0xFF)
3565 				WriteMessage("subtype       = TEMP9 - RUBiCSON 48659 stektermometer");
3566 			else
3567 				WriteMessage("subtype       = TEMP9 - RUBiCSON 48695");
3568 			break;
3569 		case sTypeTEMP10:
3570 			WriteMessage("subtype       = TEMP10 - TFA 30.3133");
3571 			break;
3572 		case sTypeTEMP11:
3573 			WriteMessage("subtype       = WT0122 pool sensor");
3574 			break;
3575 		case sTypeTEMP_SYSTEM:
3576 			WriteMessage("subtype       = System");
3577 			break;
3578 		default:
3579 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->TEMP.packettype, pResponse->TEMP.subtype);
3580 			WriteMessage(szTmp);
3581 			break;
3582 		}
3583 
3584 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->TEMP.seqnbr);
3585 		WriteMessage(szTmp);
3586 		sprintf(szTmp, "ID            = %d", (pResponse->TEMP.id1 * 256) + pResponse->TEMP.id2);
3587 		WriteMessage(szTmp);
3588 
3589 		sprintf(szTmp, "Temperature   = %.1f C", temp);
3590 		WriteMessage(szTmp);
3591 
3592 		sprintf(szTmp, "Signal level  = %d", pResponse->TEMP.rssi);
3593 		WriteMessage(szTmp);
3594 
3595 		if ((pResponse->TEMP.battery_level & 0x0F) == 0)
3596 			WriteMessage("Battery       = Low");
3597 		else
3598 			WriteMessage("Battery       = OK");
3599 		WriteMessageEnd();
3600 	}
3601 	procResult.DeviceRowIdx = DevRowIdx;
3602 }
3603 
decode_Hum(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)3604 void MainWorker::decode_Hum(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
3605 {
3606 	char szTmp[100];
3607 	uint8_t devType = pTypeHUM;
3608 	uint8_t subType = pResponse->HUM.subtype;
3609 	sprintf(szTmp, "%d", (pResponse->HUM.id1 * 256) + pResponse->HUM.id2);
3610 	std::string ID = szTmp;
3611 	uint8_t Unit = 1;
3612 
3613 	uint8_t SignalLevel = pResponse->HUM.rssi;
3614 	uint8_t BatteryLevel = 0;
3615 	if ((pResponse->HUM.battery_level & 0x0F) == 0)
3616 		BatteryLevel = 0;
3617 	else
3618 		BatteryLevel = 100;
3619 	//Override battery level if hardware supports it
3620 	if (pHardware->HwdType == HTYPE_OpenZWave)
3621 	{
3622 		BatteryLevel = pResponse->TEMP.battery_level;
3623 	}
3624 
3625 	uint8_t humidity = pResponse->HUM.humidity;
3626 	if (humidity > 100)
3627 	{
3628 		WriteMessage(" Invalid Humidity");
3629 		return;
3630 	}
3631 
3632 	sprintf(szTmp, "%d", pResponse->HUM.humidity_status);
3633 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, humidity, szTmp, procResult.DeviceName);
3634 	if (DevRowIdx == (uint64_t)-1)
3635 		return;
3636 
3637 	bool bHandledNotification = false;
3638 	float temp = 0;
3639 	if (pResponse->HUM.subtype == sTypeHUM1)
3640 	{
3641 		//check if we already had a humidity for this device, if so, keep it!
3642 		char szTmp[300];
3643 		std::vector<std::vector<std::string> > result;
3644 
3645 		result = m_sql.safe_query(
3646 			"SELECT sValue FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)",
3647 			pHardware->m_HwdID, ID.c_str(), 0, pTypeTEMP, sTypeTEMP5);
3648 		if (result.size() == 1)
3649 		{
3650 			temp = static_cast<float>(atof(result[0][0].c_str()));
3651 			float AddjValue = 0.0f;
3652 			float AddjMulti = 1.0f;
3653 			m_sql.GetAddjustment(pHardware->m_HwdID, ID.c_str(), 2, pTypeTEMP_HUM, sTypeTH_LC_TC, AddjValue, AddjMulti);
3654 			temp += AddjValue;
3655 			sprintf(szTmp, "%.1f;%d;%d", temp, humidity, pResponse->HUM.humidity_status);
3656 			DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), 2, pTypeTEMP_HUM, sTypeTH_LC_TC, SignalLevel, BatteryLevel, 0, szTmp, procResult.DeviceName);
3657 			m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, pTypeTEMP_HUM, sTypeTH_LC_TC, subType, szTmp);
3658 			bHandledNotification = true;
3659 		}
3660 	}
3661 	if (!bHandledNotification)
3662 		m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, (const int)humidity);
3663 
3664 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
3665 	{
3666 		WriteMessageStart();
3667 		switch (pResponse->HUM.subtype)
3668 		{
3669 		case sTypeHUM1:
3670 			WriteMessage("subtype       = HUM1 - LaCrosse TX3");
3671 			break;
3672 		case sTypeHUM2:
3673 			WriteMessage("subtype       = HUM2 - LaCrosse WS2300");
3674 			break;
3675 		default:
3676 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->HUM.packettype, pResponse->HUM.subtype);
3677 			WriteMessage(szTmp);
3678 			break;
3679 		}
3680 
3681 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->HUM.seqnbr);
3682 		WriteMessage(szTmp);
3683 		sprintf(szTmp, "ID            = %d", (pResponse->HUM.id1 * 256) + pResponse->HUM.id2);
3684 		WriteMessage(szTmp);
3685 
3686 		sprintf(szTmp, "Humidity      = %d %%", pResponse->HUM.humidity);
3687 		WriteMessage(szTmp);
3688 
3689 		switch (pResponse->HUM.humidity_status)
3690 		{
3691 		case humstat_normal:
3692 			WriteMessage("Status        = Normal");
3693 			break;
3694 		case humstat_comfort:
3695 			WriteMessage("Status        = Comfortable");
3696 			break;
3697 		case humstat_dry:
3698 			WriteMessage("Status        = Dry");
3699 			break;
3700 		case humstat_wet:
3701 			WriteMessage("Status        = Wet");
3702 			break;
3703 		}
3704 
3705 		sprintf(szTmp, "Signal level  = %d", pResponse->HUM.rssi);
3706 		WriteMessage(szTmp);
3707 
3708 		if ((pResponse->HUM.battery_level & 0x0F) == 0)
3709 			WriteMessage("Battery       = Low");
3710 		else
3711 			WriteMessage("Battery       = OK");
3712 		WriteMessageEnd();
3713 	}
3714 	procResult.DeviceRowIdx = DevRowIdx;
3715 }
3716 
decode_TempHum(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)3717 void MainWorker::decode_TempHum(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
3718 {
3719 	char szTmp[100];
3720 	uint8_t devType = pTypeTEMP_HUM;
3721 	uint8_t subType = pResponse->TEMP_HUM.subtype;
3722 	std::string ID;
3723 	sprintf(szTmp, "%d", (pResponse->TEMP_HUM.id1 * 256) + pResponse->TEMP_HUM.id2);
3724 	ID = szTmp;
3725 	uint8_t Unit = 0;
3726 
3727 	uint8_t cmnd = 0;
3728 	uint8_t SignalLevel = pResponse->TEMP_HUM.rssi;
3729 	uint8_t BatteryLevel = get_BateryLevel(pHardware->HwdType, pResponse->TEMP_HUM.subtype == sTypeTH8, pResponse->TEMP_HUM.battery_level);
3730 
3731 	//Get Channel(Unit)
3732 	switch (pResponse->TEMP_HUM.subtype)
3733 	{
3734 	case sTypeTH1:
3735 	case sTypeTH2:
3736 	case sTypeTH3:
3737 	case sTypeTH4:
3738 	case sTypeTH6:
3739 	case sTypeTH8:
3740 	case sTypeTH10:
3741 	case sTypeTH11:
3742 	case sTypeTH12:
3743 	case sTypeTH13:
3744 	case sTypeTH14:
3745 		Unit = pResponse->TEMP_HUM.id2;
3746 		break;
3747 	case sTypeTH5:
3748 	case sTypeTH9:
3749 		//no channel
3750 		break;
3751 	case sTypeTH7:
3752 		if (pResponse->TEMP_HUM.id1 < 0x40)
3753 			Unit = 1;
3754 		else if (pResponse->TEMP_HUM.id1 < 0x60)
3755 			Unit = 2;
3756 		else if (pResponse->TEMP_HUM.id1 < 0x80)
3757 			Unit = 3;
3758 		else if ((pResponse->TEMP_HUM.id1 > 0x9F) && (pResponse->TEMP_HUM.id1 < 0xC0))
3759 			Unit = 4;
3760 		else if (pResponse->TEMP_HUM.id1 < 0xE0)
3761 			Unit = 5;
3762 		break;
3763 	}
3764 
3765 	float temp;
3766 	if (!pResponse->TEMP_HUM.tempsign)
3767 	{
3768 		temp = float((pResponse->TEMP_HUM.temperatureh * 256) + pResponse->TEMP_HUM.temperaturel) / 10.0f;
3769 	}
3770 	else
3771 	{
3772 		temp = -(float(((pResponse->TEMP_HUM.temperatureh & 0x7F) * 256) + pResponse->TEMP_HUM.temperaturel) / 10.0f);
3773 	}
3774 	if ((temp < -200) || (temp > 380))
3775 	{
3776 		WriteMessage(" Invalid Temperature");
3777 		return;
3778 	}
3779 
3780 	float AddjValue = 0.0f;
3781 	float AddjMulti = 1.0f;
3782 	m_sql.GetAddjustment(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, AddjValue, AddjMulti);
3783 	temp += AddjValue;
3784 
3785 	int Humidity = (int)pResponse->TEMP_HUM.humidity;
3786 	uint8_t HumidityStatus = pResponse->TEMP_HUM.humidity_status;
3787 
3788 	if (Humidity > 100)
3789 	{
3790 		WriteMessage(" Invalid Humidity");
3791 		return;
3792 	}
3793 	/*
3794 	AddjValue=0.0f;
3795 	AddjMulti=1.0f;
3796 	m_sql.GetAddjustment2(pHardware->m_HwdID, ID.c_str(),Unit,devType,subType,AddjValue,AddjMulti);
3797 	Humidity+=int(AddjValue);
3798 	if (Humidity>100)
3799 	Humidity=100;
3800 	if (Humidity<0)
3801 	Humidity=0;
3802 	*/
3803 	sprintf(szTmp, "%.1f;%d;%d", temp, Humidity, HumidityStatus);
3804 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
3805 	if (DevRowIdx == (uint64_t)-1)
3806 		return;
3807 
3808 	uint64_t tID = ((uint64_t)(pHardware->m_HwdID & 0x7FFFFFFF) << 32) | (DevRowIdx & 0x7FFFFFFF);
3809 	m_trend_calculator[tID].AddValueAndReturnTendency(static_cast<double>(temp), _tTrendCalculator::TAVERAGE_TEMP);
3810 
3811 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, cmnd, szTmp);
3812 
3813 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
3814 	{
3815 		WriteMessageStart();
3816 		switch (pResponse->TEMP_HUM.subtype)
3817 		{
3818 		case sTypeTH1:
3819 			WriteMessage("subtype       = TH1 - THGN122/123/132,THGR122/228/238/268");
3820 			sprintf(szTmp, "                channel %d", pResponse->TEMP_HUM.id2);
3821 			WriteMessage(szTmp);
3822 			break;
3823 		case sTypeTH2:
3824 			WriteMessage("subtype       = TH2 - THGR810,THGN800");
3825 			sprintf(szTmp, "                channel %d", pResponse->TEMP_HUM.id2);
3826 			WriteMessage(szTmp);
3827 			break;
3828 		case sTypeTH3:
3829 			WriteMessage("subtype       = TH3 - RTGR328");
3830 			sprintf(szTmp, "                channel %d", pResponse->TEMP_HUM.id2);
3831 			WriteMessage(szTmp);
3832 			break;
3833 		case sTypeTH4:
3834 			WriteMessage("subtype       = TH4 - THGR328");
3835 			sprintf(szTmp, "                channel %d", pResponse->TEMP_HUM.id2);
3836 			WriteMessage(szTmp);
3837 			break;
3838 		case sTypeTH5:
3839 			WriteMessage("subtype       = TH5 - WTGR800");
3840 			break;
3841 		case sTypeTH6:
3842 			WriteMessage("subtype       = TH6 - THGR918/928,THGRN228,THGN500");
3843 			sprintf(szTmp, "                channel %d", pResponse->TEMP_HUM.id2);
3844 			WriteMessage(szTmp);
3845 			break;
3846 		case sTypeTH7:
3847 			WriteMessage("subtype       = TH7 - Cresta, TFA TS34C");
3848 			if (pResponse->TEMP_HUM.id1 < 0x40)
3849 				WriteMessage("                channel 1");
3850 			else if (pResponse->TEMP_HUM.id1 < 0x60)
3851 				WriteMessage("                channel 2");
3852 			else if (pResponse->TEMP_HUM.id1 < 0x80)
3853 				WriteMessage("                channel 3");
3854 			else if ((pResponse->TEMP_HUM.id1 > 0x9F) && (pResponse->TEMP_HUM.id1 < 0xC0))
3855 				WriteMessage("                channel 4");
3856 			else if (pResponse->TEMP_HUM.id1 < 0xE0)
3857 				WriteMessage("                channel 5");
3858 			else
3859 				WriteMessage("                channel ??");
3860 			break;
3861 		case sTypeTH8:
3862 			WriteMessage("subtype       = TH8 - WT260,WT260H,WT440H,WT450,WT450H");
3863 			sprintf(szTmp, "                channel %d", pResponse->TEMP_HUM.id2);
3864 			WriteMessage(szTmp);
3865 			break;
3866 		case sTypeTH9:
3867 			WriteMessage("subtype       = TH9 - Viking 02038, 02035 (02035 has no humidity), TSS320");
3868 			break;
3869 		case sTypeTH10:
3870 			WriteMessage("subtype       = TH10 - Rubicson/IW008T/TX95");
3871 			sprintf(szTmp, "                channel %d", pResponse->TEMP_HUM.id2);
3872 			WriteMessage(szTmp);
3873 			break;
3874 		case sTypeTH11:
3875 			WriteMessage("subtype       = TH11 - Oregon EW109");
3876 			sprintf(szTmp, "                channel %d", pResponse->TEMP_HUM.id2);
3877 			WriteMessage(szTmp);
3878 			break;
3879 		case sTypeTH12:
3880 			WriteMessage("subtype       = TH12 - Imagintronix/Opus TX300");
3881 			sprintf(szTmp, "                channel %d", pResponse->TEMP_HUM.id2);
3882 			WriteMessage(szTmp);
3883 			break;
3884 		case sTypeTH13:
3885 			WriteMessage("subtype       = TH13 - Alecto WS1700 and compatibles");
3886 			sprintf(szTmp, "                channel %d", pResponse->TEMP_HUM.id2);
3887 			WriteMessage(szTmp);
3888 			break;
3889 		case sTypeTH14:
3890 			WriteMessage("subtype       = TH14 - Alecto");
3891 			sprintf(szTmp, "                channel %d", pResponse->TEMP_HUM.id2);
3892 			WriteMessage(szTmp);
3893 			break;
3894 		default:
3895 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->TEMP_HUM.packettype, pResponse->TEMP_HUM.subtype);
3896 			WriteMessage(szTmp);
3897 			break;
3898 		}
3899 
3900 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->TEMP_HUM.seqnbr);
3901 		WriteMessage(szTmp);
3902 		sprintf(szTmp, "ID            = %s", ID.c_str());
3903 		WriteMessage(szTmp);
3904 
3905 		double tvalue = ConvertTemperature(temp, m_sql.m_tempsign[0]);
3906 		sprintf(szTmp, "Temperature   = %.1f C", tvalue);
3907 		WriteMessage(szTmp);
3908 		sprintf(szTmp, "Humidity      = %d %%", Humidity);
3909 		WriteMessage(szTmp);
3910 
3911 		switch (pResponse->TEMP_HUM.humidity_status)
3912 		{
3913 		case humstat_normal:
3914 			WriteMessage("Status        = Normal");
3915 			break;
3916 		case humstat_comfort:
3917 			WriteMessage("Status        = Comfortable");
3918 			break;
3919 		case humstat_dry:
3920 			WriteMessage("Status        = Dry");
3921 			break;
3922 		case humstat_wet:
3923 			WriteMessage("Status        = Wet");
3924 			break;
3925 		}
3926 
3927 		sprintf(szTmp, "Signal level  = %d", pResponse->TEMP_HUM.rssi);
3928 		WriteMessage(szTmp);
3929 
3930 		decode_BateryLevel(pResponse->TEMP_HUM.subtype == sTypeTH8, pResponse->TEMP_HUM.battery_level);
3931 		WriteMessageEnd();
3932 	}
3933 	procResult.DeviceRowIdx = DevRowIdx;
3934 }
3935 
decode_TempHumBaro(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)3936 void MainWorker::decode_TempHumBaro(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
3937 {
3938 	char szTmp[100];
3939 	uint8_t devType = pTypeTEMP_HUM_BARO;
3940 	uint8_t subType = pResponse->TEMP_HUM_BARO.subtype;
3941 	sprintf(szTmp, "%d", (pResponse->TEMP_HUM_BARO.id1 * 256) + pResponse->TEMP_HUM_BARO.id2);
3942 	std::string ID = szTmp;
3943 	uint8_t Unit = pResponse->TEMP_HUM_BARO.id2;
3944 	uint8_t cmnd = 0;
3945 	uint8_t SignalLevel = pResponse->TEMP_HUM_BARO.rssi;
3946 	uint8_t BatteryLevel;
3947 	if ((pResponse->TEMP_HUM_BARO.battery_level & 0x0F) == 0)
3948 		BatteryLevel = 0;
3949 	else
3950 		BatteryLevel = 100;
3951 	//Override battery level if hardware supports it
3952 	if (pHardware->HwdType == HTYPE_OpenZWave)
3953 	{
3954 		BatteryLevel = pResponse->TEMP.battery_level;
3955 	}
3956 
3957 	float temp;
3958 	if (!pResponse->TEMP_HUM_BARO.tempsign)
3959 	{
3960 		temp = float((pResponse->TEMP_HUM_BARO.temperatureh * 256) + pResponse->TEMP_HUM_BARO.temperaturel) / 10.0f;
3961 	}
3962 	else
3963 	{
3964 		temp = -(float(((pResponse->TEMP_HUM_BARO.temperatureh & 0x7F) * 256) + pResponse->TEMP_HUM_BARO.temperaturel) / 10.0f);
3965 	}
3966 	if ((temp < -200) || (temp > 380))
3967 	{
3968 		WriteMessage(" Invalid Temperature");
3969 		return;
3970 	}
3971 
3972 	float AddjValue = 0.0f;
3973 	float AddjMulti = 1.0f;
3974 	m_sql.GetAddjustment(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, AddjValue, AddjMulti);
3975 	temp += AddjValue;
3976 
3977 	uint8_t Humidity = pResponse->TEMP_HUM_BARO.humidity;
3978 	uint8_t HumidityStatus = pResponse->TEMP_HUM_BARO.humidity_status;
3979 
3980 	if (Humidity > 100)
3981 	{
3982 		WriteMessage(" Invalid Humidity");
3983 		return;
3984 	}
3985 
3986 	int barometer = (pResponse->TEMP_HUM_BARO.baroh * 256) + pResponse->TEMP_HUM_BARO.barol;
3987 
3988 	int forcast = pResponse->TEMP_HUM_BARO.forecast;
3989 	float fbarometer = (float)barometer;
3990 
3991 	m_sql.GetAddjustment2(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, AddjValue, AddjMulti);
3992 	barometer += int(AddjValue);
3993 
3994 	if (pResponse->TEMP_HUM_BARO.subtype == sTypeTHBFloat)
3995 	{
3996 		if ((barometer < 8000) || (barometer > 12000))
3997 		{
3998 			WriteMessage(" Invalid Barometer");
3999 			return;
4000 		}
4001 		fbarometer = float((pResponse->TEMP_HUM_BARO.baroh * 256) + pResponse->TEMP_HUM_BARO.barol) / 10.0f;
4002 		fbarometer += AddjValue;
4003 		sprintf(szTmp, "%.1f;%d;%d;%.1f;%d", temp, Humidity, HumidityStatus, fbarometer, forcast);
4004 	}
4005 	else
4006 	{
4007 		if ((barometer < 800) || (barometer > 1200))
4008 		{
4009 			WriteMessage(" Invalid Barometer");
4010 			return;
4011 		}
4012 		sprintf(szTmp, "%.1f;%d;%d;%d;%d", temp, Humidity, HumidityStatus, barometer, forcast);
4013 	}
4014 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
4015 	if (DevRowIdx == (uint64_t)-1)
4016 		return;
4017 
4018 	uint64_t tID = ((uint64_t)(pHardware->m_HwdID & 0x7FFFFFFF) << 32) | (DevRowIdx & 0x7FFFFFFF);
4019 	m_trend_calculator[tID].AddValueAndReturnTendency(static_cast<double>(temp), _tTrendCalculator::TAVERAGE_TEMP);
4020 
4021 	//calculate Altitude
4022 	//float seaLevelPressure=101325.0f;
4023 	//float altitude = 44330.0f * (1.0f - pow(fbarometer / seaLevelPressure, 0.1903f));
4024 
4025 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, cmnd, szTmp);
4026 
4027 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
4028 	{
4029 		WriteMessageStart();
4030 		switch (pResponse->TEMP_HUM_BARO.subtype)
4031 		{
4032 		case sTypeTHB1:
4033 			WriteMessage("subtype       = THB1 - BTHR918, BTHGN129");
4034 			sprintf(szTmp, "                channel %d", pResponse->TEMP_HUM_BARO.id2);
4035 			WriteMessage(szTmp);
4036 			break;
4037 		case sTypeTHB2:
4038 			WriteMessage("subtype       = THB2 - BTHR918N, BTHR968");
4039 			sprintf(szTmp, "                channel %d", pResponse->TEMP_HUM_BARO.id2);
4040 			WriteMessage(szTmp);
4041 			break;
4042 		case sTypeTHBFloat:
4043 			WriteMessage("subtype       = Weather Station");
4044 			break;
4045 		default:
4046 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->TEMP_HUM_BARO.packettype, pResponse->TEMP_HUM_BARO.subtype);
4047 			WriteMessage(szTmp);
4048 			break;
4049 		}
4050 
4051 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->TEMP_HUM_BARO.seqnbr);
4052 		WriteMessage(szTmp);
4053 
4054 		sprintf(szTmp, "ID            = %d", (pResponse->TEMP_HUM_BARO.id1 * 256) + pResponse->TEMP_HUM_BARO.id2);
4055 		WriteMessage(szTmp);
4056 
4057 		double tvalue = ConvertTemperature(temp, m_sql.m_tempsign[0]);
4058 		sprintf(szTmp, "Temperature   = %.1f C", tvalue);
4059 		WriteMessage(szTmp);
4060 
4061 		sprintf(szTmp, "Humidity      = %d %%", pResponse->TEMP_HUM_BARO.humidity);
4062 		WriteMessage(szTmp);
4063 
4064 		switch (pResponse->TEMP_HUM_BARO.humidity_status)
4065 		{
4066 		case humstat_normal:
4067 			WriteMessage("Status        = Normal");
4068 			break;
4069 		case humstat_comfort:
4070 			WriteMessage("Status        = Comfortable");
4071 			break;
4072 		case humstat_dry:
4073 			WriteMessage("Status        = Dry");
4074 			break;
4075 		case humstat_wet:
4076 			WriteMessage("Status        = Wet");
4077 			break;
4078 		}
4079 
4080 		if (pResponse->TEMP_HUM_BARO.subtype == sTypeTHBFloat)
4081 			sprintf(szTmp, "Barometer     = %.1f hPa", float((pResponse->TEMP_HUM_BARO.baroh * 256) + pResponse->TEMP_HUM_BARO.barol) / 10.0f);
4082 		else
4083 			sprintf(szTmp, "Barometer     = %d hPa", (pResponse->TEMP_HUM_BARO.baroh * 256) + pResponse->TEMP_HUM_BARO.barol);
4084 		WriteMessage(szTmp);
4085 
4086 		switch (pResponse->TEMP_HUM_BARO.forecast)
4087 		{
4088 		case baroForecastNoInfo:
4089 			WriteMessage("Forecast      = No information available");
4090 			break;
4091 		case baroForecastSunny:
4092 			WriteMessage("Forecast      = Sunny");
4093 			break;
4094 		case baroForecastPartlyCloudy:
4095 			WriteMessage("Forecast      = Partly Cloudy");
4096 			break;
4097 		case baroForecastCloudy:
4098 			WriteMessage("Forecast      = Cloudy");
4099 			break;
4100 		case baroForecastRain:
4101 			WriteMessage("Forecast      = Rain");
4102 			break;
4103 		}
4104 
4105 		sprintf(szTmp, "Signal level  = %d", pResponse->TEMP_HUM_BARO.rssi);
4106 		WriteMessage(szTmp);
4107 
4108 		if ((pResponse->TEMP_HUM_BARO.battery_level & 0x0F) == 0)
4109 			WriteMessage("Battery       = Low");
4110 		else
4111 			WriteMessage("Battery       = OK");
4112 		WriteMessageEnd();
4113 	}
4114 	procResult.DeviceRowIdx = DevRowIdx;
4115 }
4116 
decode_TempBaro(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)4117 void MainWorker::decode_TempBaro(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
4118 {
4119 	char szTmp[100];
4120 	uint8_t devType = pTypeTEMP_BARO;
4121 	uint8_t subType = sTypeBMP085;
4122 	_tTempBaro* pTempBaro = (_tTempBaro*)pResponse;
4123 
4124 	sprintf(szTmp, "%d", pTempBaro->id1);
4125 	std::string ID = szTmp;
4126 	uint8_t Unit = 1;
4127 	uint8_t cmnd = 0;
4128 	uint8_t SignalLevel = 12;
4129 	uint8_t BatteryLevel;
4130 	BatteryLevel = 100;
4131 
4132 	float temp = pTempBaro->temp;
4133 	if ((temp < -200) || (temp > 380))
4134 	{
4135 		WriteMessage(" Invalid Temperature");
4136 		return;
4137 	}
4138 
4139 	float AddjValue = 0.0f;
4140 	float AddjMulti = 1.0f;
4141 	m_sql.GetAddjustment(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, AddjValue, AddjMulti);
4142 	temp += AddjValue;
4143 
4144 	float fbarometer = pTempBaro->baro;
4145 	int forcast = pTempBaro->forecast;
4146 	m_sql.GetAddjustment2(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, AddjValue, AddjMulti);
4147 	fbarometer += AddjValue;
4148 
4149 	sprintf(szTmp, "%.1f;%.1f;%d;%.2f", temp, fbarometer, forcast, pTempBaro->altitude);
4150 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
4151 	if (DevRowIdx == (uint64_t)-1)
4152 		return;
4153 
4154 	uint64_t tID = ((uint64_t)(pHardware->m_HwdID & 0x7FFFFFFF) << 32) | (DevRowIdx & 0x7FFFFFFF);
4155 	m_trend_calculator[tID].AddValueAndReturnTendency(static_cast<double>(temp), _tTrendCalculator::TAVERAGE_TEMP);
4156 
4157 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, cmnd, szTmp);
4158 
4159 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
4160 	{
4161 		WriteMessageStart();
4162 		switch (pResponse->TEMP_HUM_BARO.subtype)
4163 		{
4164 		case sTypeBMP085:
4165 			WriteMessage("subtype       = BMP085 I2C");
4166 			sprintf(szTmp, "                channel %d", pTempBaro->id1);
4167 			WriteMessage(szTmp);
4168 			break;
4169 		default:
4170 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", devType, subType);
4171 			WriteMessage(szTmp);
4172 			break;
4173 		}
4174 
4175 		sprintf(szTmp, "Temperature   = %.1f C", temp);
4176 		WriteMessage(szTmp);
4177 
4178 		sprintf(szTmp, "Barometer     = %.1f hPa", fbarometer);
4179 		WriteMessage(szTmp);
4180 
4181 		switch (pTempBaro->forecast)
4182 		{
4183 		case baroForecastNoInfo:
4184 			WriteMessage("Forecast      = No information available");
4185 			break;
4186 		case baroForecastSunny:
4187 			WriteMessage("Forecast      = Sunny");
4188 			break;
4189 		case baroForecastPartlyCloudy:
4190 			WriteMessage("Forecast      = Partly Cloudy");
4191 			break;
4192 		case baroForecastCloudy:
4193 			WriteMessage("Forecast      = Cloudy");
4194 			break;
4195 		case baroForecastRain:
4196 			WriteMessage("Forecast      = Rain");
4197 			break;
4198 		}
4199 
4200 		if (pResponse->TEMP_HUM_BARO.subtype == sTypeBMP085)
4201 		{
4202 			sprintf(szTmp, "Altitude   = %.2f meter", pTempBaro->altitude);
4203 			WriteMessage(szTmp);
4204 		}
4205 		WriteMessageEnd();
4206 	}
4207 	procResult.DeviceRowIdx = DevRowIdx;
4208 }
4209 
decode_TempRain(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)4210 void MainWorker::decode_TempRain(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
4211 {
4212 	char szTmp[100];
4213 
4214 	//We are (also) going to split this device into two separate sensors (temp + rain)
4215 
4216 	uint8_t devType = pTypeTEMP_RAIN;
4217 	uint8_t subType = pResponse->TEMP_RAIN.subtype;
4218 
4219 	sprintf(szTmp, "%d", (pResponse->TEMP_RAIN.id1 * 256) + pResponse->TEMP_RAIN.id2);
4220 	std::string ID = szTmp;
4221 	int Unit = pResponse->TEMP_RAIN.id2;
4222 	int cmnd = 0;
4223 
4224 	uint8_t SignalLevel = pResponse->TEMP_RAIN.rssi;
4225 	uint8_t BatteryLevel = 0;
4226 	if ((pResponse->TEMP_RAIN.battery_level & 0x0F) == 0)
4227 		BatteryLevel = 0;
4228 	else
4229 		BatteryLevel = 100;
4230 
4231 	float temp;
4232 	if (!pResponse->TEMP_RAIN.tempsign)
4233 	{
4234 		temp = float((pResponse->TEMP_RAIN.temperatureh * 256) + pResponse->TEMP_RAIN.temperaturel) / 10.0f;
4235 	}
4236 	else
4237 	{
4238 		temp = -(float(((pResponse->TEMP_RAIN.temperatureh & 0x7F) * 256) + pResponse->TEMP_RAIN.temperaturel) / 10.0f);
4239 	}
4240 
4241 	float AddjValue = 0.0f;
4242 	float AddjMulti = 1.0f;
4243 	m_sql.GetAddjustment(pHardware->m_HwdID, ID.c_str(), Unit, pTypeTEMP, sTypeTEMP3, AddjValue, AddjMulti);
4244 	temp += AddjValue;
4245 
4246 	if ((temp < -200) || (temp > 380))
4247 	{
4248 		WriteMessage(" Invalid Temperature");
4249 		return;
4250 	}
4251 	float TotalRain = float((pResponse->TEMP_RAIN.raintotal1 * 256) + pResponse->TEMP_RAIN.raintotal2) / 10.0f;
4252 
4253 	sprintf(szTmp, "%.1f;%.1f", temp, TotalRain);
4254 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
4255 	if (DevRowIdx == (uint64_t)-1)
4256 		return;
4257 
4258 	uint64_t tID = ((uint64_t)(pHardware->m_HwdID & 0x7FFFFFFF) << 32) | (DevRowIdx & 0x7FFFFFFF);
4259 	m_trend_calculator[tID].AddValueAndReturnTendency(static_cast<double>(temp), _tTrendCalculator::TAVERAGE_TEMP);
4260 
4261 	sprintf(szTmp, "%.1f", temp);
4262 	uint64_t DevRowIdxTemp = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, pTypeTEMP, sTypeTEMP3, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
4263 	m_notifications.CheckAndHandleNotification(DevRowIdxTemp, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, pTypeTEMP, sTypeTEMP3, temp);
4264 
4265 	sprintf(szTmp, "%d;%.1f", 0, TotalRain);
4266 	uint64_t DevRowIdxRain = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, pTypeRAIN, sTypeRAIN3, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
4267 	m_notifications.CheckAndHandleNotification(DevRowIdxRain, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, pTypeRAIN, sTypeRAIN3, cmnd, szTmp);
4268 
4269 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
4270 	{
4271 		WriteMessageStart();
4272 		switch (pResponse->TEMP_RAIN.subtype)
4273 		{
4274 		case sTypeTR1:
4275 			WriteMessage("Subtype       = Alecto WS1200");
4276 			break;
4277 		}
4278 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->TEMP_RAIN.seqnbr);
4279 		WriteMessage(szTmp);
4280 		sprintf(szTmp, "ID            = %d", (pResponse->TEMP_RAIN.id1 * 256) + pResponse->TEMP_RAIN.id2);
4281 		WriteMessage(szTmp);
4282 
4283 		sprintf(szTmp, "Temperature   = %.1f C", temp);
4284 		WriteMessage(szTmp);
4285 		sprintf(szTmp, "Total Rain    = %.1f mm", TotalRain);
4286 		WriteMessage(szTmp);
4287 
4288 		sprintf(szTmp, "Signal level  = %d", pResponse->TEMP_RAIN.rssi);
4289 		WriteMessage(szTmp);
4290 
4291 		if ((pResponse->TEMP_RAIN.battery_level & 0x0F) == 0)
4292 			WriteMessage("Battery       = Low");
4293 		else
4294 			WriteMessage("Battery       = OK");
4295 		WriteMessageEnd();
4296 	}
4297 
4298 	procResult.DeviceRowIdx = DevRowIdx;
4299 }
4300 
decode_UV(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)4301 void MainWorker::decode_UV(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
4302 {
4303 	char szTmp[100];
4304 	uint8_t devType = pTypeUV;
4305 	uint8_t subType = pResponse->UV.subtype;
4306 	std::string ID;
4307 	sprintf(szTmp, "%d", (pResponse->UV.id1 * 256) + pResponse->UV.id2);
4308 	ID = szTmp;
4309 	uint8_t Unit = 0;
4310 	uint8_t cmnd = 0;
4311 	uint8_t SignalLevel = pResponse->UV.rssi;
4312 	uint8_t BatteryLevel;
4313 	if ((pResponse->UV.battery_level & 0x0F) == 0)
4314 		BatteryLevel = 0;
4315 	else
4316 		BatteryLevel = 100;
4317 	float Level = float(pResponse->UV.uv) / 10.0f;
4318 	if (Level > 30)
4319 	{
4320 		WriteMessage(" Invalid UV");
4321 		return;
4322 	}
4323 	float temp = 0;
4324 	if (pResponse->UV.subtype == sTypeUV3)
4325 	{
4326 		if (!pResponse->UV.tempsign)
4327 		{
4328 			temp = float((pResponse->UV.temperatureh * 256) + pResponse->UV.temperaturel) / 10.0f;
4329 		}
4330 		else
4331 		{
4332 			temp = -(float(((pResponse->UV.temperatureh & 0x7F) * 256) + pResponse->UV.temperaturel) / 10.0f);
4333 		}
4334 		if ((temp < -200) || (temp > 380))
4335 		{
4336 			WriteMessage(" Invalid Temperature");
4337 			return;
4338 		}
4339 
4340 		float AddjValue = 0.0f;
4341 		float AddjMulti = 1.0f;
4342 		m_sql.GetAddjustment(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, AddjValue, AddjMulti);
4343 		temp += AddjValue;
4344 	}
4345 
4346 	sprintf(szTmp, "%.1f;%.1f", Level, temp);
4347 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
4348 	if (DevRowIdx == (uint64_t)-1)
4349 		return;
4350 
4351 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, cmnd, szTmp);
4352 
4353 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
4354 	{
4355 		WriteMessageStart();
4356 		switch (pResponse->UV.subtype)
4357 		{
4358 		case sTypeUV1:
4359 			WriteMessage("Subtype       = UV1 - UVN128, UV138");
4360 			break;
4361 		case sTypeUV2:
4362 			WriteMessage("Subtype       = UV2 - UVN800");
4363 			break;
4364 		case sTypeUV3:
4365 			WriteMessage("Subtype       = UV3 - TFA");
4366 			break;
4367 		default:
4368 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->UV.packettype, pResponse->UV.subtype);
4369 			WriteMessage(szTmp);
4370 			break;
4371 		}
4372 
4373 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->UV.seqnbr);
4374 		WriteMessage(szTmp);
4375 		sprintf(szTmp, "ID            = %d", (pResponse->UV.id1 * 256) + pResponse->UV.id2);
4376 		WriteMessage(szTmp);
4377 
4378 		sprintf(szTmp, "Level         = %.1f UVI", Level);
4379 		WriteMessage(szTmp);
4380 
4381 		if (pResponse->UV.subtype == sTypeUV3)
4382 		{
4383 			sprintf(szTmp, "Temperature   = %.1f C", temp);
4384 			WriteMessage(szTmp);
4385 		}
4386 
4387 		if (pResponse->UV.uv < 3)
4388 			WriteMessage("Description = Low");
4389 		else if (pResponse->UV.uv < 6)
4390 			WriteMessage("Description = Medium");
4391 		else if (pResponse->UV.uv < 8)
4392 			WriteMessage("Description = High");
4393 		else if (pResponse->UV.uv < 11)
4394 			WriteMessage("Description = Very high");
4395 		else
4396 			WriteMessage("Description = Dangerous");
4397 
4398 		sprintf(szTmp, "Signal level  = %d", pResponse->UV.rssi);
4399 		WriteMessage(szTmp);
4400 
4401 		if ((pResponse->UV.battery_level & 0x0F) == 0)
4402 			WriteMessage("Battery       = Low");
4403 		else
4404 			WriteMessage("Battery       = OK");
4405 		WriteMessageEnd();
4406 	}
4407 	procResult.DeviceRowIdx = DevRowIdx;
4408 }
4409 
decode_FS20(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)4410 void MainWorker::decode_FS20(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
4411 {
4412 	//uint8_t devType=pTypeFS20;
4413 
4414 	char szTmp[100];
4415 	uint8_t devType = pTypeFS20;
4416 	uint8_t subType = pResponse->FS20.subtype;
4417 
4418 	sprintf(szTmp, "%02X%02X", pResponse->FS20.hc1, pResponse->FS20.hc2);
4419 	std::string ID = szTmp;
4420 
4421 	uint8_t Unit = pResponse->FS20.addr;
4422 	uint8_t cmnd = pResponse->FS20.cmd1;
4423 	uint8_t SignalLevel = pResponse->FS20.rssi;
4424 
4425 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, -1, cmnd, procResult.DeviceName);
4426 	if (DevRowIdx == (uint64_t)-1)
4427 		return;
4428 	CheckSceneCode(DevRowIdx, devType, subType, cmnd, "", procResult.DeviceName);
4429 
4430 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
4431 	{
4432 		WriteMessageStart();
4433 		switch (pResponse->FS20.subtype)
4434 		{
4435 		case sTypeFS20:
4436 			WriteMessage("subtype       = FS20");
4437 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->FS20.seqnbr);
4438 			WriteMessage(szTmp);
4439 			sprintf(szTmp, "House code    = %02X%02X", pResponse->FS20.hc1, pResponse->FS20.hc2);
4440 			WriteMessage(szTmp);
4441 			sprintf(szTmp, "Address       = %02X", pResponse->FS20.addr);
4442 			WriteMessage(szTmp);
4443 
4444 			WriteMessage("Cmd1          = ", false);
4445 
4446 			switch (pResponse->FS20.cmd1 & 0x1F)
4447 			{
4448 			case 0x0:
4449 				WriteMessage("Off");
4450 				break;
4451 			case 0x1:
4452 				WriteMessage("dim level 1 = 6.25%");
4453 				break;
4454 			case 0x2:
4455 				WriteMessage("dim level 2 = 12.5%");
4456 				break;
4457 			case 0x3:
4458 				WriteMessage("dim level 3 = 18.75%");
4459 				break;
4460 			case 0x4:
4461 				WriteMessage("dim level 4 = 25%");
4462 				break;
4463 			case 0x5:
4464 				WriteMessage("dim level 5 = 31.25%");
4465 				break;
4466 			case 0x6:
4467 				WriteMessage("dim level 6 = 37.5%");
4468 				break;
4469 			case 0x7:
4470 				WriteMessage("dim level 7 = 43.75%");
4471 				break;
4472 			case 0x8:
4473 				WriteMessage("dim level 8 = 50%");
4474 				break;
4475 			case 0x9:
4476 				WriteMessage("dim level 9 = 56.25%");
4477 				break;
4478 			case 0xA:
4479 				WriteMessage("dim level 10 = 62.5%");
4480 				break;
4481 			case 0xB:
4482 				WriteMessage("dim level 11 = 68.75%");
4483 				break;
4484 			case 0xC:
4485 				WriteMessage("dim level 12 = 75%");
4486 				break;
4487 			case 0xD:
4488 				WriteMessage("dim level 13 = 81.25%");
4489 				break;
4490 			case 0xE:
4491 				WriteMessage("dim level 14 = 87.5%");
4492 				break;
4493 			case 0xF:
4494 				WriteMessage("dim level 15 = 93.75%");
4495 				break;
4496 			case 0x10:
4497 				WriteMessage("On (100%)");
4498 				break;
4499 			case 0x11:
4500 				WriteMessage("On ( at last dim level set)");
4501 				break;
4502 			case 0x12:
4503 				WriteMessage("Toggle between Off and On (last dim level set)");
4504 				break;
4505 			case 0x13:
4506 				WriteMessage("Bright one step");
4507 				break;
4508 			case 0x14:
4509 				WriteMessage("Dim one step");
4510 				break;
4511 			case 0x15:
4512 				WriteMessage("Start dim cycle");
4513 				break;
4514 			case 0x16:
4515 				WriteMessage("Program(Timer)");
4516 				break;
4517 			case 0x17:
4518 				WriteMessage("Request status from a bidirectional device");
4519 				break;
4520 			case 0x18:
4521 				WriteMessage("Off for timer period");
4522 				break;
4523 			case 0x19:
4524 				WriteMessage("On (100%) for timer period");
4525 				break;
4526 			case 0x1A:
4527 				WriteMessage("On ( at last dim level set) for timer period");
4528 				break;
4529 			case 0x1B:
4530 				WriteMessage("Reset");
4531 				break;
4532 			default:
4533 				sprintf(szTmp, "ERROR: Unknown command = %02X", pResponse->FS20.cmd1);
4534 				WriteMessage(szTmp);
4535 				break;
4536 			}
4537 
4538 			if ((pResponse->FS20.cmd1 & 0x80) == 0)
4539 				WriteMessage("                command to receiver");
4540 			else
4541 				WriteMessage("                response from receiver");
4542 
4543 			if ((pResponse->FS20.cmd1 & 0x40) == 0)
4544 				WriteMessage("                unidirectional command");
4545 			else
4546 				WriteMessage("                bidirectional command");
4547 
4548 			if ((pResponse->FS20.cmd1 & 0x20) == 0)
4549 				WriteMessage("                additional cmd2 byte not present");
4550 			else
4551 				WriteMessage("                additional cmd2 byte present");
4552 
4553 			if ((pResponse->FS20.cmd1 & 0x20) != 0)
4554 			{
4555 				sprintf(szTmp, "Cmd2          = %02X", pResponse->FS20.cmd2);
4556 				WriteMessage(szTmp);
4557 			}
4558 			break;
4559 		case sTypeFHT8V:
4560 			WriteMessage("subtype       = FHT 8V valve");
4561 
4562 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->FS20.seqnbr);
4563 			WriteMessage(szTmp);
4564 			sprintf(szTmp, "House code    = %02X%02X", pResponse->FS20.hc1, pResponse->FS20.hc2);
4565 			WriteMessage(szTmp);
4566 			sprintf(szTmp, "Address       = %02X", pResponse->FS20.addr);
4567 			WriteMessage(szTmp);
4568 
4569 			WriteMessage("Cmd1          = ", false);
4570 
4571 			if ((pResponse->FS20.cmd1 & 0x80) == 0)
4572 				WriteMessage("new command");
4573 			else
4574 				WriteMessage("repeated command");
4575 
4576 			if ((pResponse->FS20.cmd1 & 0x40) == 0)
4577 				WriteMessage("                unidirectional command");
4578 			else
4579 				WriteMessage("                bidirectional command");
4580 
4581 			if ((pResponse->FS20.cmd1 & 0x20) == 0)
4582 				WriteMessage("                additional cmd2 byte not present");
4583 			else
4584 				WriteMessage("                additional cmd2 byte present");
4585 
4586 			if ((pResponse->FS20.cmd1 & 0x10) == 0)
4587 				WriteMessage("                battery empty beep not enabled");
4588 			else
4589 				WriteMessage("                enable battery empty beep");
4590 
4591 			switch (pResponse->FS20.cmd1 & 0xF)
4592 			{
4593 			case 0x0:
4594 				WriteMessage("                Synchronize now");
4595 				sprintf(szTmp, "Cmd2          = valve position: %02X is %.2f %%", pResponse->FS20.cmd2, float(pResponse->FS20.cmd2) / 2.55f);
4596 				WriteMessage(szTmp);
4597 				break;
4598 			case 0x1:
4599 				WriteMessage("                open valve");
4600 				break;
4601 			case 0x2:
4602 				WriteMessage("                close valve");
4603 				break;
4604 			case 0x6:
4605 				WriteMessage("                open valve at percentage level");
4606 				sprintf(szTmp, "Cmd2          = valve position: %02X is %.2f %%", pResponse->FS20.cmd2, float(pResponse->FS20.cmd2) / 2.55f);
4607 				WriteMessage(szTmp);
4608 				break;
4609 			case 0x8:
4610 				WriteMessage("                relative offset (cmd2 bit 7=direction, bit 5-0 offset value)");
4611 				break;
4612 			case 0xA:
4613 				WriteMessage("                decalcification cycle");
4614 				sprintf(szTmp, "Cmd2          = valve position: %02X is %.2f %%", pResponse->FS20.cmd2, float(pResponse->FS20.cmd2) / 2.55f);
4615 				WriteMessage(szTmp);
4616 				break;
4617 			case 0xC:
4618 				WriteMessage("                synchronization active");
4619 				sprintf(szTmp, "Cmd2          = count down is %d seconds", pResponse->FS20.cmd2 >> 1);
4620 				WriteMessage(szTmp);
4621 				break;
4622 			case 0xE:
4623 				WriteMessage("                test, drive valve and produce an audible signal");
4624 				break;
4625 			case 0xF:
4626 				WriteMessage("                pair valve (cmd2 bit 7-1 is count down in seconds, bit 0=1)");
4627 				sprintf(szTmp, "Cmd2          = count down is %d seconds", pResponse->FS20.cmd2 >> 1);
4628 				WriteMessage(szTmp);
4629 				break;
4630 			default:
4631 				sprintf(szTmp, "ERROR: Unknown command = %02X", pResponse->FS20.cmd1);
4632 				WriteMessage(szTmp);
4633 				break;
4634 			}
4635 			break;
4636 		case sTypeFHT80:
4637 			WriteMessage("subtype       = FHT80 door/window sensor");
4638 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->FS20.seqnbr);
4639 			WriteMessage(szTmp);
4640 			sprintf(szTmp, "House code    = %02X%02X", pResponse->FS20.hc1, pResponse->FS20.hc2);
4641 			WriteMessage(szTmp);
4642 			sprintf(szTmp, "Address       = %02X", pResponse->FS20.addr);
4643 			WriteMessage(szTmp);
4644 
4645 			WriteMessage("Cmd1          = ", false);
4646 
4647 			switch (pResponse->FS20.cmd1 & 0xF)
4648 			{
4649 			case 0x1:
4650 				WriteMessage("sensor opened");
4651 				break;
4652 			case 0x2:
4653 				WriteMessage("sensor closed");
4654 				break;
4655 			case 0xC:
4656 				WriteMessage("synchronization active");
4657 				break;
4658 			default:
4659 				sprintf(szTmp, "ERROR: Unknown command = %02X", pResponse->FS20.cmd1);
4660 				WriteMessage(szTmp);
4661 				break;
4662 			}
4663 
4664 			if ((pResponse->FS20.cmd1 & 0x80) == 0)
4665 				WriteMessage("                new command");
4666 			else
4667 				WriteMessage("                repeated command");
4668 			break;
4669 		default:
4670 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->FS20.packettype, pResponse->FS20.subtype);
4671 			WriteMessage(szTmp);
4672 			break;
4673 		}
4674 		sprintf(szTmp, "Signal level  = %d", pResponse->FS20.rssi);
4675 		WriteMessage(szTmp);
4676 		WriteMessageEnd();
4677 	}
4678 
4679 	procResult.DeviceRowIdx = DevRowIdx;
4680 }
4681 
decode_Lighting1(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)4682 void MainWorker::decode_Lighting1(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
4683 {
4684 	char szTmp[100];
4685 	uint8_t devType = pTypeLighting1;
4686 	uint8_t subType = pResponse->LIGHTING1.subtype;
4687 	sprintf(szTmp, "%d", pResponse->LIGHTING1.housecode);
4688 	std::string ID = szTmp;
4689 	uint8_t Unit = pResponse->LIGHTING1.unitcode;
4690 	uint8_t cmnd = pResponse->LIGHTING1.cmnd;
4691 	uint8_t SignalLevel = pResponse->LIGHTING1.rssi;
4692 
4693 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, -1, cmnd, procResult.DeviceName);
4694 	if (DevRowIdx == (uint64_t)-1)
4695 		return;
4696 	CheckSceneCode(DevRowIdx, devType, subType, cmnd, "", procResult.DeviceName);
4697 
4698 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
4699 	{
4700 		WriteMessageStart();
4701 		switch (pResponse->LIGHTING1.subtype)
4702 		{
4703 		case sTypeX10:
4704 			WriteMessage("subtype       = X10");
4705 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING1.seqnbr);
4706 			WriteMessage(szTmp);
4707 			sprintf(szTmp, "housecode     = %c", pResponse->LIGHTING1.housecode);
4708 			WriteMessage(szTmp);
4709 			sprintf(szTmp, "unitcode      = %d", pResponse->LIGHTING1.unitcode);
4710 			WriteMessage(szTmp);
4711 			WriteMessage("Command       = ", false);
4712 			switch (pResponse->LIGHTING1.cmnd)
4713 			{
4714 			case light1_sOff:
4715 				WriteMessage("Off");
4716 				break;
4717 			case light1_sOn:
4718 				WriteMessage("On");
4719 				break;
4720 			case light1_sDim:
4721 				WriteMessage("Dim");
4722 				break;
4723 			case light1_sBright:
4724 				WriteMessage("Bright");
4725 				break;
4726 			case light1_sAllOn:
4727 				WriteMessage("All On");
4728 				break;
4729 			case light1_sAllOff:
4730 				WriteMessage("All Off");
4731 				break;
4732 			default:
4733 				WriteMessage("UNKNOWN");
4734 				break;
4735 			}
4736 			break;
4737 		case sTypeARC:
4738 			WriteMessage("subtype       = ARC");
4739 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING1.seqnbr);
4740 			WriteMessage(szTmp);
4741 			sprintf(szTmp, "housecode     = %c", pResponse->LIGHTING1.housecode);
4742 			WriteMessage(szTmp);
4743 			sprintf(szTmp, "unitcode      = %d", pResponse->LIGHTING1.unitcode);
4744 			WriteMessage(szTmp);
4745 			WriteMessage("Command       = ", false);
4746 			switch (pResponse->LIGHTING1.cmnd)
4747 			{
4748 			case light1_sOff:
4749 				WriteMessage("Off");
4750 				break;
4751 			case light1_sOn:
4752 				WriteMessage("On");
4753 				break;
4754 			case light1_sAllOn:
4755 				WriteMessage("All On");
4756 				break;
4757 			case light1_sAllOff:
4758 				WriteMessage("All Off");
4759 				break;
4760 			case light1_sChime:
4761 				WriteMessage("Chime");
4762 				break;
4763 			default:
4764 				WriteMessage("UNKNOWN");
4765 				break;
4766 			}
4767 			break;
4768 		case sTypeAB400D:
4769 		case sTypeWaveman:
4770 		case sTypeEMW200:
4771 		case sTypeIMPULS:
4772 		case sTypeRisingSun:
4773 		case sTypeEnergenie:
4774 		case sTypeEnergenie5:
4775 		case sTypeGDR2:
4776 		case sTypeHQ:
4777 		case sTypeOase:
4778 			//decoding of these types is only implemented for use by simulate and verbose
4779 			//these types are not received by the RFXtrx433
4780 			switch (pResponse->LIGHTING1.subtype)
4781 			{
4782 			case sTypeAB400D:
4783 				WriteMessage("subtype       = ELRO AB400");
4784 				break;
4785 			case sTypeWaveman:
4786 				WriteMessage("subtype       = Waveman");
4787 				break;
4788 			case sTypeEMW200:
4789 				WriteMessage("subtype       = EMW200");
4790 				break;
4791 			case sTypeIMPULS:
4792 				WriteMessage("subtype       = IMPULS");
4793 				break;
4794 			case sTypeRisingSun:
4795 				WriteMessage("subtype       = RisingSun");
4796 				break;
4797 			case sTypeEnergenie:
4798 				WriteMessage("subtype       = Energenie-ENER010");
4799 				break;
4800 			case sTypeEnergenie5:
4801 				WriteMessage("subtype       = Energenie 5-gang");
4802 				break;
4803 			case sTypeGDR2:
4804 				WriteMessage("subtype       = COCO GDR2");
4805 				break;
4806 			case sTypeHQ:
4807 				WriteMessage("subtype       = HQ COCO-20");
4808 				break;
4809 			case sTypeOase:
4810 				WriteMessage("subtype       = Oase Inscenio");
4811 				break;
4812 			}
4813 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING1.seqnbr);
4814 			WriteMessage(szTmp);
4815 			sprintf(szTmp, "housecode     = %c", pResponse->LIGHTING1.housecode);
4816 			WriteMessage(szTmp);
4817 			sprintf(szTmp, "unitcode      = %d", pResponse->LIGHTING1.unitcode);
4818 			WriteMessage(szTmp);
4819 			WriteMessage("Command       = ", false);
4820 
4821 			switch (pResponse->LIGHTING1.cmnd)
4822 			{
4823 			case light1_sOff:
4824 				WriteMessage("Off");
4825 				break;
4826 			case light1_sOn:
4827 				WriteMessage("On");
4828 				break;
4829 			default:
4830 				WriteMessage("UNKNOWN");
4831 				break;
4832 			}
4833 			break;
4834 		case sTypePhilips:
4835 			//decoding of this type is only implemented for use by simulate and verbose
4836 			//this type is not received by the RFXtrx433
4837 			WriteMessage("subtype       = Philips SBC");
4838 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING1.seqnbr);
4839 			WriteMessage(szTmp);
4840 			sprintf(szTmp, "housecode     = %c", pResponse->LIGHTING1.housecode);
4841 			WriteMessage(szTmp);
4842 			sprintf(szTmp, "unitcode      = %d", pResponse->LIGHTING1.unitcode);
4843 			WriteMessage(szTmp);
4844 			WriteMessage("Command       = ", false);
4845 
4846 			switch (pResponse->LIGHTING1.cmnd)
4847 			{
4848 			case light1_sOff:
4849 				WriteMessage("Off");
4850 				break;
4851 			case light1_sOn:
4852 				WriteMessage("On");
4853 				break;
4854 			case light1_sAllOff:
4855 				WriteMessage("All Off");
4856 				break;
4857 			case light1_sAllOn:
4858 				WriteMessage("All On");
4859 				break;
4860 			default:
4861 				WriteMessage("UNKNOWN");
4862 				break;
4863 			}
4864 			break;
4865 		default:
4866 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->LIGHTING1.packettype, pResponse->LIGHTING1.subtype);
4867 			WriteMessage(szTmp);
4868 			break;
4869 		}
4870 		sprintf(szTmp, "Signal level  = %d", pResponse->LIGHTING1.rssi);
4871 		WriteMessage(szTmp);
4872 		WriteMessageEnd();
4873 	}
4874 	procResult.DeviceRowIdx = DevRowIdx;
4875 }
4876 
decode_Lighting2(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)4877 void MainWorker::decode_Lighting2(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
4878 {
4879 	char szTmp[100];
4880 	uint8_t devType = pTypeLighting2;
4881 	uint8_t subType = pResponse->LIGHTING2.subtype;
4882 	sprintf(szTmp, "%X%02X%02X%02X", pResponse->LIGHTING2.id1, pResponse->LIGHTING2.id2, pResponse->LIGHTING2.id3, pResponse->LIGHTING2.id4);
4883 	std::string ID = szTmp;
4884 	uint8_t Unit = pResponse->LIGHTING2.unitcode;
4885 	uint8_t cmnd = pResponse->LIGHTING2.cmnd;
4886 	uint8_t level = pResponse->LIGHTING2.level;
4887 	uint8_t SignalLevel = pResponse->LIGHTING2.rssi;
4888 
4889 	sprintf(szTmp, "%d", level);
4890 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, -1, cmnd, szTmp, procResult.DeviceName);
4891 
4892 	bool isGroupCommand = ((cmnd == light2_sGroupOff) || (cmnd == light2_sGroupOn));
4893 	uint8_t single_cmnd = cmnd;
4894 
4895 	if (isGroupCommand)
4896 	{
4897 		single_cmnd = (cmnd == light2_sGroupOff) ? light2_sOff : light2_sOn;
4898 
4899 		// We write the GROUP_CMD into the log to differentiate between manual turn off/on and group_off/group_on
4900 		m_sql.UpdateValueLighting2GroupCmd(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, -1, cmnd, szTmp, procResult.DeviceName);
4901 
4902 		//set the status of all lights with the same code to on/off
4903 		m_sql.Lighting2GroupCmd(ID, subType, single_cmnd);
4904 	}
4905 
4906 	if (DevRowIdx == (uint64_t)-1) {
4907 		// not found nothing to do
4908 		return;
4909 	}
4910 	CheckSceneCode(DevRowIdx, devType, subType, single_cmnd, szTmp, procResult.DeviceName);
4911 
4912 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
4913 	{
4914 		WriteMessageStart();
4915 		switch (pResponse->LIGHTING2.subtype)
4916 		{
4917 		case sTypeAC:
4918 		case sTypeHEU:
4919 		case sTypeANSLUT:
4920 			switch (pResponse->LIGHTING2.subtype)
4921 			{
4922 			case sTypeAC:
4923 				WriteMessage("subtype       = AC");
4924 				break;
4925 			case sTypeHEU:
4926 				WriteMessage("subtype       = HomeEasy EU");
4927 				break;
4928 			case sTypeANSLUT:
4929 				WriteMessage("subtype       = ANSLUT");
4930 				break;
4931 			}
4932 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING2.seqnbr);
4933 			WriteMessage(szTmp);
4934 			sprintf(szTmp, "ID            = %s", ID.c_str());
4935 			WriteMessage(szTmp);
4936 			sprintf(szTmp, "Unit          = %d", Unit);
4937 			WriteMessage(szTmp);
4938 			WriteMessage("Command       = ", false);
4939 			switch (pResponse->LIGHTING2.cmnd)
4940 			{
4941 			case light2_sOff:
4942 				WriteMessage("Off");
4943 				break;
4944 			case light2_sOn:
4945 				WriteMessage("On");
4946 				break;
4947 			case light2_sSetLevel:
4948 				sprintf(szTmp, "Set Level: %d", level);
4949 				WriteMessage(szTmp);
4950 				break;
4951 			case light2_sGroupOff:
4952 				WriteMessage("Group Off");
4953 				break;
4954 			case light2_sGroupOn:
4955 				WriteMessage("Group On");
4956 				break;
4957 			case light2_sSetGroupLevel:
4958 				sprintf(szTmp, "Set Group Level: %d", level);
4959 				WriteMessage(szTmp);
4960 				break;
4961 			case gswitch_sStop:
4962 				WriteMessage("Stop");
4963 				break;
4964 			default:
4965 				WriteMessage("UNKNOWN");
4966 				break;
4967 			}
4968 			break;
4969 		default:
4970 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->LIGHTING2.packettype, pResponse->LIGHTING2.subtype);
4971 			WriteMessage(szTmp);
4972 			break;
4973 		}
4974 		WriteMessageEnd();
4975 	}
4976 	procResult.DeviceRowIdx = DevRowIdx;
4977 }
4978 
4979 //not in dbase yet
decode_Lighting3(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)4980 void MainWorker::decode_Lighting3(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
4981 {
4982 	WriteMessageStart();
4983 	WriteMessage("");
4984 
4985 	char szTmp[100];
4986 
4987 	switch (pResponse->LIGHTING3.subtype)
4988 	{
4989 	case sTypeKoppla:
4990 		WriteMessage("subtype       = Ikea Koppla");
4991 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING3.seqnbr);
4992 		WriteMessage(szTmp);
4993 		WriteMessage("Command       = ", false);
4994 		switch (pResponse->LIGHTING3.cmnd)
4995 		{
4996 		case 0x0:
4997 			WriteMessage("Off");
4998 			break;
4999 		case 0x1:
5000 			WriteMessage("On");
5001 			break;
5002 		case 0x20:
5003 			sprintf(szTmp, "Set Level: %d", pResponse->LIGHTING3.channel10_9);
5004 			WriteMessage(szTmp);
5005 			break;
5006 		case 0x21:
5007 			WriteMessage("Program");
5008 			break;
5009 		default:
5010 			if ((pResponse->LIGHTING3.cmnd >= 0x10) && (pResponse->LIGHTING3.cmnd < 0x18))
5011 				WriteMessage("Dim");
5012 			else if ((pResponse->LIGHTING3.cmnd >= 0x18) && (pResponse->LIGHTING3.cmnd < 0x20))
5013 				WriteMessage("Bright");
5014 			else
5015 				WriteMessage("UNKNOWN");
5016 			break;
5017 		}
5018 		break;
5019 	default:
5020 		sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->LIGHTING3.packettype, pResponse->LIGHTING3.subtype);
5021 		WriteMessage(szTmp);
5022 		break;
5023 	}
5024 	sprintf(szTmp, "Signal level  = %d", pResponse->LIGHTING3.rssi);
5025 	WriteMessage(szTmp);
5026 	WriteMessageEnd();
5027 	procResult.DeviceRowIdx = -1;
5028 }
5029 
decode_Lighting4(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)5030 void MainWorker::decode_Lighting4(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
5031 {
5032 	char szTmp[100];
5033 	uint8_t devType = pTypeLighting4;
5034 	uint8_t subType = pResponse->LIGHTING4.subtype;
5035 	sprintf(szTmp, "%02X%02X%02X", pResponse->LIGHTING4.cmd1, pResponse->LIGHTING4.cmd2, pResponse->LIGHTING4.cmd3);
5036 	std::string ID = szTmp;
5037 	int Unit = 0;
5038 	uint8_t cmnd = 1; //only 'On' supported
5039 	uint8_t SignalLevel = pResponse->LIGHTING4.rssi;
5040 	sprintf(szTmp, "%d", (pResponse->LIGHTING4.pulseHigh * 256) + pResponse->LIGHTING4.pulseLow);
5041 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, -1, cmnd, szTmp, procResult.DeviceName);
5042 	if (DevRowIdx == (uint64_t)-1)
5043 		return;
5044 	CheckSceneCode(DevRowIdx, devType, subType, cmnd, szTmp, procResult.DeviceName);
5045 
5046 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
5047 	{
5048 		WriteMessageStart();
5049 		switch (pResponse->LIGHTING4.subtype)
5050 		{
5051 		case sTypePT2262:
5052 			WriteMessage("subtype       = PT2262");
5053 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING4.seqnbr);
5054 			WriteMessage(szTmp);
5055 			sprintf(szTmp, "Code          = %02X%02X%02X", pResponse->LIGHTING4.cmd1, pResponse->LIGHTING4.cmd2, pResponse->LIGHTING4.cmd3);
5056 			WriteMessage(szTmp);
5057 
5058 			WriteMessage("S1- S24  = ", false);
5059 			if ((pResponse->LIGHTING4.cmd1 & 0x80) == 0)
5060 				WriteMessage("0", false);
5061 			else
5062 				WriteMessage("1", false);
5063 
5064 			if ((pResponse->LIGHTING4.cmd1 & 0x40) == 0)
5065 				WriteMessage("0", false);
5066 			else
5067 				WriteMessage("1", false);
5068 
5069 			if ((pResponse->LIGHTING4.cmd1 & 0x20) == 0)
5070 				WriteMessage("0", false);
5071 			else
5072 				WriteMessage("1", false);
5073 
5074 			if ((pResponse->LIGHTING4.cmd1 & 0x10) == 0)
5075 				WriteMessage("0 ", false);
5076 			else
5077 				WriteMessage("1 ", false);
5078 
5079 
5080 			if ((pResponse->LIGHTING4.cmd1 & 0x08) == 0)
5081 				WriteMessage("0", false);
5082 			else
5083 				WriteMessage("1", false);
5084 
5085 			if ((pResponse->LIGHTING4.cmd1 & 0x04) == 0)
5086 				WriteMessage("0", false);
5087 			else
5088 				WriteMessage("1", false);
5089 
5090 			if ((pResponse->LIGHTING4.cmd1 & 0x02) == 0)
5091 				WriteMessage("0", false);
5092 			else
5093 				WriteMessage("1", false);
5094 
5095 			if ((pResponse->LIGHTING4.cmd1 & 0x01) == 0)
5096 				WriteMessage("0 ", false);
5097 			else
5098 				WriteMessage("1 ", false);
5099 
5100 
5101 			if ((pResponse->LIGHTING4.cmd2 & 0x80) == 0)
5102 				WriteMessage("0", false);
5103 			else
5104 				WriteMessage("1", false);
5105 
5106 			if ((pResponse->LIGHTING4.cmd2 & 0x40) == 0)
5107 				WriteMessage("0", false);
5108 			else
5109 				WriteMessage("1", false);
5110 
5111 			if ((pResponse->LIGHTING4.cmd2 & 0x20) == 0)
5112 				WriteMessage("0", false);
5113 			else
5114 				WriteMessage("1", false);
5115 
5116 			if ((pResponse->LIGHTING4.cmd2 & 0x10) == 0)
5117 				WriteMessage("0 ", false);
5118 			else
5119 				WriteMessage("1 ", false);
5120 
5121 
5122 			if ((pResponse->LIGHTING4.cmd2 & 0x08) == 0)
5123 				WriteMessage("0", false);
5124 			else
5125 				WriteMessage("1", false);
5126 
5127 			if ((pResponse->LIGHTING4.cmd2 & 0x04) == 0)
5128 				WriteMessage("0", false);
5129 			else
5130 				WriteMessage("1", false);
5131 
5132 			if ((pResponse->LIGHTING4.cmd2 & 0x02) == 0)
5133 				WriteMessage("0", false);
5134 			else
5135 				WriteMessage("1", false);
5136 
5137 			if ((pResponse->LIGHTING4.cmd2 & 0x01) == 0)
5138 				WriteMessage("0 ", false);
5139 			else
5140 				WriteMessage("1 ", false);
5141 
5142 
5143 			if ((pResponse->LIGHTING4.cmd3 & 0x80) == 0)
5144 				WriteMessage("0", false);
5145 			else
5146 				WriteMessage("1", false);
5147 
5148 			if ((pResponse->LIGHTING4.cmd3 & 0x40) == 0)
5149 				WriteMessage("0", false);
5150 			else
5151 				WriteMessage("1", false);
5152 
5153 			if ((pResponse->LIGHTING4.cmd3 & 0x20) == 0)
5154 				WriteMessage("0", false);
5155 			else
5156 				WriteMessage("1", false);
5157 
5158 			if ((pResponse->LIGHTING4.cmd3 & 0x10) == 0)
5159 				WriteMessage("0 ", false);
5160 			else
5161 				WriteMessage("1 ", false);
5162 
5163 
5164 			if ((pResponse->LIGHTING4.cmd3 & 0x08) == 0)
5165 				WriteMessage("0", false);
5166 			else
5167 				WriteMessage("1", false);
5168 
5169 			if ((pResponse->LIGHTING4.cmd3 & 0x04) == 0)
5170 				WriteMessage("0", false);
5171 			else
5172 				WriteMessage("1", false);
5173 
5174 			if ((pResponse->LIGHTING4.cmd3 & 0x02) == 0)
5175 				WriteMessage("0", false);
5176 			else
5177 				WriteMessage("1", false);
5178 
5179 			if ((pResponse->LIGHTING4.cmd3 & 0x01) == 0)
5180 				WriteMessage("0");
5181 			else
5182 				WriteMessage("1");
5183 
5184 			sprintf(szTmp, "Pulse         = %d usec", (pResponse->LIGHTING4.pulseHigh * 256) + pResponse->LIGHTING4.pulseLow);
5185 			WriteMessage(szTmp);
5186 			break;
5187 		default:
5188 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->LIGHTING4.packettype, pResponse->LIGHTING4.subtype);
5189 			WriteMessage(szTmp);
5190 			break;
5191 		}
5192 		sprintf(szTmp, "Signal level  = %d", pResponse->LIGHTING4.rssi);
5193 		WriteMessage(szTmp);
5194 		WriteMessageEnd();
5195 	}
5196 	procResult.DeviceRowIdx = DevRowIdx;
5197 }
5198 
decode_Lighting5(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)5199 void MainWorker::decode_Lighting5(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
5200 {
5201 	char szTmp[100];
5202 	uint8_t devType = pTypeLighting5;
5203 	uint8_t subType = pResponse->LIGHTING5.subtype;
5204 	if ((subType != sTypeEMW100) && (subType != sTypeLivolo) && (subType != sTypeLivolo1to10) && (subType != sTypeRGB432W) && (subType != sTypeKangtai))
5205 		sprintf(szTmp, "%02X%02X%02X", pResponse->LIGHTING5.id1, pResponse->LIGHTING5.id2, pResponse->LIGHTING5.id3);
5206 	else
5207 		sprintf(szTmp, "%02X%02X", pResponse->LIGHTING5.id2, pResponse->LIGHTING5.id3);
5208 	std::string ID = szTmp;
5209 	uint8_t Unit = pResponse->LIGHTING5.unitcode;
5210 	uint8_t cmnd = pResponse->LIGHTING5.cmnd;
5211 	float flevel;
5212 	if (subType == sTypeLivolo)
5213 		flevel = (100.0f / 7.0f) * float(pResponse->LIGHTING5.level);
5214 	else if (subType == sTypeLightwaveRF)
5215 		flevel = (100.0f / 31.0f) * float(pResponse->LIGHTING5.level);
5216 	else
5217 		flevel = (100.0f / 7.0f) * float(pResponse->LIGHTING5.level);
5218 	uint8_t SignalLevel = pResponse->LIGHTING5.rssi;
5219 
5220 	bool bDoUpdate = true;
5221 	if ((subType == sTypeTRC02) || (subType == sTypeTRC02_2) || (subType == sTypeAoke) || (subType == sTypeEurodomest))
5222 	{
5223 		if (
5224 			(pResponse->LIGHTING5.cmnd != light5_sOff) &&
5225 			(pResponse->LIGHTING5.cmnd != light5_sOn) &&
5226 			(pResponse->LIGHTING5.cmnd != light5_sGroupOff) &&
5227 			(pResponse->LIGHTING5.cmnd != light5_sGroupOn)
5228 			)
5229 		{
5230 			bDoUpdate = false;
5231 		}
5232 	}
5233 	uint64_t DevRowIdx = -1;
5234 	if (bDoUpdate)
5235 	{
5236 		sprintf(szTmp, "%d", pResponse->LIGHTING5.level);
5237 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, -1, cmnd, szTmp, procResult.DeviceName);
5238 		if (DevRowIdx == (uint64_t)-1)
5239 			return;
5240 		CheckSceneCode(DevRowIdx, devType, subType, cmnd, szTmp, procResult.DeviceName);
5241 	}
5242 
5243 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
5244 	{
5245 		WriteMessageStart();
5246 		switch (pResponse->LIGHTING5.subtype)
5247 		{
5248 		case sTypeLightwaveRF:
5249 			WriteMessage("subtype       = LightwaveRF");
5250 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING5.seqnbr);
5251 			WriteMessage(szTmp);
5252 			sprintf(szTmp, "ID            = %02X%02X%02X", pResponse->LIGHTING5.id1, pResponse->LIGHTING5.id2, pResponse->LIGHTING5.id3);
5253 			WriteMessage(szTmp);
5254 			sprintf(szTmp, "Unit          = %d", pResponse->LIGHTING5.unitcode);
5255 			WriteMessage(szTmp);
5256 			WriteMessage("Command       = ", false);
5257 			switch (pResponse->LIGHTING5.cmnd)
5258 			{
5259 			case light5_sOff:
5260 				WriteMessage("Off");
5261 				break;
5262 			case light5_sOn:
5263 				WriteMessage("On");
5264 				break;
5265 			case light5_sGroupOff:
5266 				WriteMessage("Group Off");
5267 				break;
5268 			case light5_sMood1:
5269 				WriteMessage("Group Mood 1");
5270 				break;
5271 			case light5_sMood2:
5272 				WriteMessage("Group Mood 2");
5273 				break;
5274 			case light5_sMood3:
5275 				WriteMessage("Group Mood 3");
5276 				break;
5277 			case light5_sMood4:
5278 				WriteMessage("Group Mood 4");
5279 				break;
5280 			case light5_sMood5:
5281 				WriteMessage("Group Mood 5");
5282 				break;
5283 			case light5_sUnlock:
5284 				WriteMessage("Unlock");
5285 				break;
5286 			case light5_sLock:
5287 				WriteMessage("Lock");
5288 				break;
5289 			case light5_sAllLock:
5290 				WriteMessage("All lock");
5291 				break;
5292 			case light5_sClose:
5293 				WriteMessage("Close inline relay");
5294 				break;
5295 			case light5_sStop:
5296 				WriteMessage("Stop inline relay");
5297 				break;
5298 			case light5_sOpen:
5299 				WriteMessage("Open inline relay");
5300 				break;
5301 			case light5_sSetLevel:
5302 				sprintf(szTmp, "Set dim level to: %.2f %%", flevel);
5303 				WriteMessage(szTmp);
5304 				break;
5305 			case light5_sColourPalette:
5306 				if (pResponse->LIGHTING5.level == 0)
5307 					WriteMessage("Color Palette (Even command)");
5308 				else
5309 					WriteMessage("Color Palette (Odd command)");
5310 				break;
5311 			case light5_sColourTone:
5312 				if (pResponse->LIGHTING5.level == 0)
5313 					WriteMessage("Color Tone (Even command)");
5314 				else
5315 					WriteMessage("Color Tone (Odd command)");
5316 				break;
5317 			case light5_sColourCycle:
5318 				if (pResponse->LIGHTING5.level == 0)
5319 					WriteMessage("Color Cycle (Even command)");
5320 				else
5321 					WriteMessage("Color Cycle (Odd command)");
5322 				break;
5323 			default:
5324 				WriteMessage("UNKNOWN");
5325 				break;
5326 			}
5327 			break;
5328 		case sTypeEMW100:
5329 			WriteMessage("subtype       = EMW100");
5330 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING5.seqnbr);
5331 			WriteMessage(szTmp);
5332 			sprintf(szTmp, "ID            = %02X%02X", pResponse->LIGHTING5.id2, pResponse->LIGHTING5.id3);
5333 			WriteMessage(szTmp);
5334 			sprintf(szTmp, "Unit          = %d", pResponse->LIGHTING5.unitcode);
5335 			WriteMessage(szTmp);
5336 			WriteMessage("Command       = ", false);
5337 			switch (pResponse->LIGHTING5.cmnd)
5338 			{
5339 			case light5_sOff:
5340 				WriteMessage("Off");
5341 				break;
5342 			case light5_sOn:
5343 				WriteMessage("On");
5344 				break;
5345 			case light5_sLearn:
5346 				WriteMessage("Learn");
5347 				break;
5348 			default:
5349 				WriteMessage("UNKNOWN");
5350 				break;
5351 			}
5352 			break;
5353 		case sTypeBBSB:
5354 			WriteMessage("subtype       = BBSB new");
5355 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING5.seqnbr);
5356 			WriteMessage(szTmp);
5357 			sprintf(szTmp, "ID            = %02X%02X%02X", pResponse->LIGHTING5.id1, pResponse->LIGHTING5.id2, pResponse->LIGHTING5.id3);
5358 			WriteMessage(szTmp);
5359 			sprintf(szTmp, "Unit          = %d", pResponse->LIGHTING5.unitcode);
5360 			WriteMessage(szTmp);
5361 			WriteMessage("Command       = ", false);
5362 			switch (pResponse->LIGHTING5.cmnd)
5363 			{
5364 			case light5_sOff:
5365 				WriteMessage("Off");
5366 				break;
5367 			case light5_sOn:
5368 				WriteMessage("On");
5369 				break;
5370 			case light5_sGroupOff:
5371 				WriteMessage("Group Off");
5372 				break;
5373 			case light5_sGroupOn:
5374 				WriteMessage("Group On");
5375 				break;
5376 			default:
5377 				WriteMessage("UNKNOWN");
5378 				break;
5379 			}
5380 			break;
5381 		case sTypeRSL:
5382 			WriteMessage("subtype       = Conrad RSL");
5383 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING5.seqnbr);
5384 			WriteMessage(szTmp);
5385 			sprintf(szTmp, "ID            = %02X%02X%02X", pResponse->LIGHTING5.id1, pResponse->LIGHTING5.id2, pResponse->LIGHTING5.id3);
5386 			WriteMessage(szTmp);
5387 			sprintf(szTmp, "Unit          = %d", pResponse->LIGHTING5.unitcode);
5388 			WriteMessage(szTmp);
5389 			WriteMessage("Command       = ", false);
5390 			switch (pResponse->LIGHTING5.cmnd)
5391 			{
5392 			case light5_sOff:
5393 				WriteMessage("Off");
5394 				break;
5395 			case light5_sOn:
5396 				WriteMessage("On");
5397 				break;
5398 			case light5_sGroupOff:
5399 				WriteMessage("Group Off");
5400 				break;
5401 			case light5_sGroupOn:
5402 				WriteMessage("Group On");
5403 				break;
5404 			default:
5405 				WriteMessage("UNKNOWN");
5406 				break;
5407 			}
5408 			break;
5409 		case sTypeLivolo:
5410 			WriteMessage("subtype       = Livolo Dimmer");
5411 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING5.seqnbr);
5412 			WriteMessage(szTmp);
5413 			sprintf(szTmp, "ID            = %02X%02X%02X", pResponse->LIGHTING5.id1, pResponse->LIGHTING5.id2, pResponse->LIGHTING5.id3);
5414 			WriteMessage(szTmp);
5415 			sprintf(szTmp, "Unit          = %d", pResponse->LIGHTING5.unitcode);
5416 			WriteMessage(szTmp);
5417 			WriteMessage("Command       = ", false);
5418 			switch (pResponse->LIGHTING5.cmnd)
5419 			{
5420 			case light5_sOff:
5421 				WriteMessage("Off");
5422 				break;
5423 			case light5_sOn:
5424 				WriteMessage("On");
5425 				break;
5426 			case light5_sGroupOff:
5427 				WriteMessage("Group Off");
5428 				break;
5429 			case light5_sGroupOn:
5430 				WriteMessage("Group On");
5431 				break;
5432 			default:
5433 				WriteMessage("UNKNOWN");
5434 				break;
5435 			}
5436 			break;
5437 		case sTypeLivolo1to10:
5438 			WriteMessage("subtype       = Livolo On/Off module");
5439 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING5.seqnbr);
5440 			WriteMessage(szTmp);
5441 			sprintf(szTmp, "ID            = %02X%02X", pResponse->LIGHTING5.id2, pResponse->LIGHTING5.id3);
5442 			WriteMessage(szTmp);
5443 			sprintf(szTmp, "Unit          = %d", pResponse->LIGHTING5.unitcode);
5444 			WriteMessage(szTmp);
5445 			WriteMessage("Command       = ", false);
5446 			switch (pResponse->LIGHTING5.cmnd)
5447 			{
5448 			case light5_sLivoloAllOff:
5449 				WriteMessage("Off");
5450 				break;
5451 			case light5_sLivoloGang1Toggle:
5452 				WriteMessage("Toggle");
5453 				break;
5454 			default:
5455 				WriteMessage("UNKNOWN");
5456 				break;
5457 			}
5458 			break;
5459 		case sTypeRGB432W:
5460 			WriteMessage("subtype       = RGB432W");
5461 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING5.seqnbr);
5462 			WriteMessage(szTmp);
5463 			sprintf(szTmp, "ID            = %02X%02X", pResponse->LIGHTING5.id2, pResponse->LIGHTING5.id3);
5464 			WriteMessage(szTmp);
5465 			sprintf(szTmp, "Unit          = %d", pResponse->LIGHTING5.unitcode);
5466 			WriteMessage(szTmp);
5467 			WriteMessage("Command       = ", false);
5468 			switch (pResponse->LIGHTING5.cmnd)
5469 			{
5470 			case light5_sRGBoff:
5471 				WriteMessage("Off");
5472 				break;
5473 			case light5_sRGBon:
5474 				WriteMessage("On");
5475 				break;
5476 			case light5_sRGBbright:
5477 				WriteMessage("Bright+");
5478 				break;
5479 			case light5_sRGBdim:
5480 				WriteMessage("Bright-");
5481 				break;
5482 			case light5_sRGBcolorplus:
5483 				WriteMessage("Color+");
5484 				break;
5485 			case light5_sRGBcolormin:
5486 				WriteMessage("Color-");
5487 				break;
5488 			default:
5489 				sprintf(szTmp, "Color =          = %d", pResponse->LIGHTING5.cmnd);
5490 				WriteMessage(szTmp);
5491 				break;
5492 			}
5493 			break;
5494 		case sTypeTRC02:
5495 		case sTypeTRC02_2:
5496 			if (pResponse->LIGHTING5.subtype == sTypeTRC02)
5497 				WriteMessage("subtype       = TRC02 (RGB)");
5498 			else
5499 				WriteMessage("subtype       = TRC02_2 (RGB)");
5500 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING5.seqnbr);
5501 			WriteMessage(szTmp);
5502 			sprintf(szTmp, "ID            = %02X%02X%02X", pResponse->LIGHTING5.id1, pResponse->LIGHTING5.id2, pResponse->LIGHTING5.id3);
5503 			WriteMessage(szTmp);
5504 			sprintf(szTmp, "Unit          = %d", pResponse->LIGHTING5.unitcode);
5505 			WriteMessage(szTmp);
5506 			WriteMessage("Command       = ", false);
5507 			switch (pResponse->LIGHTING5.cmnd)
5508 			{
5509 			case light5_sOff:
5510 				WriteMessage("Off");
5511 				break;
5512 			case light5_sOn:
5513 				WriteMessage("On");
5514 				break;
5515 			case light5_sRGBbright:
5516 				WriteMessage("Bright+");
5517 				break;
5518 			case light5_sRGBdim:
5519 				WriteMessage("Bright-");
5520 				break;
5521 			case light5_sRGBcolorplus:
5522 				WriteMessage("Color+");
5523 				break;
5524 			case light5_sRGBcolormin:
5525 				WriteMessage("Color-");
5526 				break;
5527 			default:
5528 				sprintf(szTmp, "Color = %d", pResponse->LIGHTING5.cmnd);
5529 				WriteMessage(szTmp);
5530 				break;
5531 			}
5532 			break;
5533 		case sTypeAoke:
5534 			WriteMessage("subtype       = Aoke");
5535 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING5.seqnbr);
5536 			WriteMessage(szTmp);
5537 			sprintf(szTmp, "ID            = %02X%02X", pResponse->LIGHTING5.id2, pResponse->LIGHTING5.id3);
5538 			WriteMessage(szTmp);
5539 			sprintf(szTmp, "Unit          = %d", pResponse->LIGHTING5.unitcode);
5540 			WriteMessage(szTmp);
5541 			WriteMessage("Command       = ", false);
5542 			switch (pResponse->LIGHTING5.cmnd)
5543 			{
5544 			case light5_sOff:
5545 				WriteMessage("Off");
5546 				break;
5547 			case light5_sOn:
5548 				WriteMessage("On");
5549 				break;
5550 			default:
5551 				WriteMessage("UNKNOWN");
5552 				break;
5553 			}
5554 			break;
5555 		case sTypeEurodomest:
5556 			WriteMessage("subtype       = Eurodomest");
5557 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING5.seqnbr);
5558 			WriteMessage(szTmp);
5559 			sprintf(szTmp, "ID            = %02X%02X", pResponse->LIGHTING5.id2, pResponse->LIGHTING5.id3);
5560 			WriteMessage(szTmp);
5561 			sprintf(szTmp, "Unit          = %d", pResponse->LIGHTING5.unitcode);
5562 			WriteMessage(szTmp);
5563 			WriteMessage("Command       = ", false);
5564 			switch (pResponse->LIGHTING5.cmnd)
5565 			{
5566 			case light5_sOff:
5567 				WriteMessage("Off");
5568 				break;
5569 			case light5_sOn:
5570 				WriteMessage("On");
5571 				break;
5572 			case light5_sGroupOff:
5573 				WriteMessage("Group Off");
5574 				break;
5575 			case light5_sGroupOn:
5576 				WriteMessage("Group On");
5577 				break;
5578 			default:
5579 				WriteMessage("UNKNOWN");
5580 				break;
5581 			}
5582 			break;
5583 		case sTypeLegrandCAD:
5584 			WriteMessage("subtype       = Legrand CAD");
5585 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING5.seqnbr);
5586 			WriteMessage(szTmp);
5587 			sprintf(szTmp, "ID            = %02X%02X", pResponse->LIGHTING5.id2, pResponse->LIGHTING5.id3);
5588 			WriteMessage(szTmp);
5589 			WriteMessage("Command       = Toggle");
5590 			break;
5591 		case sTypeAvantek:
5592 			WriteMessage("subtype       = Avantek");
5593 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING5.seqnbr);
5594 			WriteMessage(szTmp);
5595 			sprintf(szTmp, "ID            = %02X%02X%02X", pResponse->LIGHTING5.id1, pResponse->LIGHTING5.id2, pResponse->LIGHTING5.id3);
5596 			WriteMessage(szTmp);
5597 			sprintf(szTmp, "Unit          = %d", pResponse->LIGHTING5.unitcode);
5598 			WriteMessage(szTmp);
5599 			WriteMessage("Command       = ", false);
5600 			switch (pResponse->LIGHTING5.cmnd)
5601 			{
5602 			case light5_sOff:
5603 				WriteMessage("Off");
5604 				break;
5605 			case light5_sOn:
5606 				WriteMessage("On");
5607 				break;
5608 			case light5_sGroupOff:
5609 				WriteMessage("Group Off");
5610 				break;
5611 			case light5_sGroupOn:
5612 				WriteMessage("Group On");
5613 				break;
5614 			default:
5615 				WriteMessage("Unknown");
5616 				break;
5617 			}
5618 			break;
5619 		case sTypeIT:
5620 			WriteMessage("subtype       = Intertek,FA500,PROmax");
5621 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING5.seqnbr);
5622 			WriteMessage(szTmp);
5623 			sprintf(szTmp, "ID            = %02X%02X%02X", pResponse->LIGHTING5.id1, pResponse->LIGHTING5.id2, pResponse->LIGHTING5.id3);
5624 			WriteMessage(szTmp);
5625 			sprintf(szTmp, "Unit          = %d", pResponse->LIGHTING5.unitcode);
5626 			WriteMessage(szTmp);
5627 			WriteMessage("Command       = ", false);
5628 			switch (pResponse->LIGHTING5.cmnd)
5629 			{
5630 			case light5_sOff:
5631 				WriteMessage("Off");
5632 				break;
5633 			case light5_sOn:
5634 				WriteMessage("On");
5635 				break;
5636 			case light5_sGroupOff:
5637 				WriteMessage("Group Off");
5638 				break;
5639 			case light5_sGroupOn:
5640 				WriteMessage("Group On");
5641 				break;
5642 			case light5_sSetLevel:
5643 				sprintf(szTmp, "Set dim level to: %.2f %%", flevel);
5644 				WriteMessage(szTmp);
5645 				break;
5646 			default:
5647 				WriteMessage("UNKNOWN");
5648 				break;
5649 			}
5650 			break;
5651 		case sTypeKangtai:
5652 			WriteMessage("subtype       = Kangtai / Cotech");
5653 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING5.seqnbr);
5654 			WriteMessage(szTmp);
5655 			sprintf(szTmp, "ID            = %02X%02X%02X", pResponse->LIGHTING5.id1, pResponse->LIGHTING5.id2, pResponse->LIGHTING5.id3);
5656 			WriteMessage(szTmp);
5657 			sprintf(szTmp, "Unit          = %d", pResponse->LIGHTING5.unitcode);
5658 			WriteMessage(szTmp);
5659 			WriteMessage("Command       = ", false);
5660 			switch (pResponse->LIGHTING5.cmnd)
5661 			{
5662 			case light5_sOff:
5663 				WriteMessage("Off");
5664 				break;
5665 			case light5_sOn:
5666 				WriteMessage("On");
5667 				break;
5668 			case light5_sGroupOff:
5669 				WriteMessage("Group Off");
5670 				break;
5671 			case light5_sGroupOn:
5672 				WriteMessage("Group On");
5673 				break;
5674 			default:
5675 				WriteMessage("UNKNOWN");
5676 				break;
5677 			}
5678 			break;
5679 		default:
5680 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->LIGHTING5.packettype, pResponse->LIGHTING5.subtype);
5681 			WriteMessage(szTmp);
5682 			break;
5683 		}
5684 		sprintf(szTmp, "Signal level  = %d", pResponse->LIGHTING5.rssi);
5685 		WriteMessage(szTmp);
5686 		WriteMessageEnd();
5687 	}
5688 	procResult.DeviceRowIdx = DevRowIdx;
5689 }
5690 
decode_Lighting6(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)5691 void MainWorker::decode_Lighting6(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
5692 {
5693 	char szTmp[100];
5694 	uint8_t devType = pTypeLighting6;
5695 	uint8_t subType = pResponse->LIGHTING6.subtype;
5696 	sprintf(szTmp, "%02X%02X%02X", pResponse->LIGHTING6.id1, pResponse->LIGHTING6.id2, pResponse->LIGHTING6.groupcode);
5697 	std::string ID = szTmp;
5698 	uint8_t Unit = pResponse->LIGHTING6.unitcode;
5699 	uint8_t cmnd = pResponse->LIGHTING6.cmnd;
5700 	uint8_t rfu = pResponse->LIGHTING6.seqnbr2;
5701 	uint8_t SignalLevel = pResponse->LIGHTING6.rssi;
5702 
5703 	sprintf(szTmp, "%d", rfu);
5704 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, -1, cmnd, szTmp, procResult.DeviceName);
5705 	if (DevRowIdx == (uint64_t)-1)
5706 		return;
5707 	CheckSceneCode(DevRowIdx, devType, subType, cmnd, szTmp, procResult.DeviceName);
5708 
5709 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
5710 	{
5711 		WriteMessageStart();
5712 		switch (pResponse->LIGHTING6.subtype)
5713 		{
5714 		case sTypeBlyss:
5715 			WriteMessage("subtype       = Blyss");
5716 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->LIGHTING6.seqnbr);
5717 			WriteMessage(szTmp);
5718 			sprintf(szTmp, "ID            = %02X%02X", pResponse->LIGHTING6.id1, pResponse->LIGHTING6.id2);
5719 			WriteMessage(szTmp);
5720 			sprintf(szTmp, "groupcode     = %d", pResponse->LIGHTING6.groupcode);
5721 			WriteMessage(szTmp);
5722 			sprintf(szTmp, "unitcode      = %d", pResponse->LIGHTING6.unitcode);
5723 			WriteMessage(szTmp);
5724 			WriteMessage("Command       = ", false);
5725 			switch (pResponse->LIGHTING6.cmnd)
5726 			{
5727 			case light6_sOff:
5728 				WriteMessage("Off");
5729 				break;
5730 			case light6_sOn:
5731 				WriteMessage("On");
5732 				break;
5733 			case light6_sGroupOff:
5734 				WriteMessage("Group Off");
5735 				break;
5736 			case light6_sGroupOn:
5737 				WriteMessage("Group On");
5738 				break;
5739 			default:
5740 				WriteMessage("UNKNOWN");
5741 				break;
5742 			}
5743 			sprintf(szTmp, "Command seqnbr= %d", pResponse->LIGHTING6.cmndseqnbr);
5744 			WriteMessage(szTmp);
5745 			sprintf(szTmp, "seqnbr2       = %d", pResponse->LIGHTING6.seqnbr2);
5746 			WriteMessage(szTmp);
5747 			break;
5748 		default:
5749 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->LIGHTING6.packettype, pResponse->LIGHTING6.subtype);
5750 			WriteMessage(szTmp);
5751 			break;
5752 		}
5753 		sprintf(szTmp, "Signal level  = %d", pResponse->LIGHTING6.rssi);
5754 		WriteMessage(szTmp);
5755 		WriteMessageEnd();
5756 	}
5757 	procResult.DeviceRowIdx = DevRowIdx;
5758 }
5759 
decode_Fan(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)5760 void MainWorker::decode_Fan(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
5761 {
5762 	char szTmp[100];
5763 	uint8_t devType = pTypeFan;
5764 	uint8_t subType = pResponse->FAN.subtype;
5765 	sprintf(szTmp, "%02X%02X%02X", pResponse->FAN.id1, pResponse->FAN.id2, pResponse->FAN.id3);
5766 	std::string ID = szTmp;
5767 	uint8_t Unit = 0;
5768 	uint8_t cmnd = pResponse->FAN.cmnd;
5769 	uint8_t SignalLevel = pResponse->FAN.rssi;
5770 
5771 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, -1, cmnd, procResult.DeviceName);
5772 	if (DevRowIdx == (uint64_t)-1)
5773 		return;
5774 	CheckSceneCode(DevRowIdx, devType, subType, cmnd, szTmp, procResult.DeviceName);
5775 
5776 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
5777 	{
5778 		WriteMessageStart();
5779 		switch (pResponse->FAN.subtype)
5780 		{
5781 		case sTypeSiemensSF01:
5782 			WriteMessage("subtype       = Siemens SF01");
5783 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->FAN.seqnbr);
5784 			WriteMessage(szTmp);
5785 			sprintf(szTmp, "ID            = %02X%02X", pResponse->FAN.id2, pResponse->FAN.id3);
5786 			WriteMessage(szTmp);
5787 			WriteMessage("Command       = ", false);
5788 			switch (pResponse->FAN.cmnd)
5789 			{
5790 			case fan_sTimer:
5791 				WriteMessage("Timer");
5792 				break;
5793 			case fan_sMin:
5794 				WriteMessage("-");
5795 				break;
5796 			case fan_sLearn:
5797 				WriteMessage("Learn");
5798 				break;
5799 			case fan_sPlus:
5800 				WriteMessage("+");
5801 				break;
5802 			case fan_sConfirm:
5803 				WriteMessage("Confirm");
5804 				break;
5805 			case fan_sLight:
5806 				WriteMessage("Light");
5807 				break;
5808 			default:
5809 				WriteMessage("UNKNOWN");
5810 				break;
5811 			}
5812 			break;
5813 		case sTypeItho:
5814 			WriteMessage("subtype       = Itho CVE RFT");
5815 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->FAN.seqnbr);
5816 			WriteMessage(szTmp);
5817 			sprintf(szTmp, "ID            = %02X%02X%02X", pResponse->FAN.id1, pResponse->FAN.id2, pResponse->FAN.id3);
5818 			WriteMessage(szTmp);
5819 			WriteMessage("Command       = ", false);
5820 			switch (pResponse->FAN.cmnd)
5821 			{
5822 			case fan_Itho1:
5823 				WriteMessage("1");
5824 				break;
5825 			case fan_Itho2:
5826 				WriteMessage("2");
5827 				break;
5828 			case fan_Itho3:
5829 				WriteMessage("3");
5830 				break;
5831 			case fan_IthoTimer:
5832 				WriteMessage("Timer");
5833 				break;
5834 			case fan_IthoNotAtHome:
5835 				WriteMessage("Not at Home");
5836 				break;
5837 			case fan_IthoLearn:
5838 				WriteMessage("Learn");
5839 				break;
5840 			case fan_IthoEraseAll:
5841 				WriteMessage("Erase all remotes");
5842 				break;
5843 			default:
5844 				WriteMessage("UNKNOWN");
5845 				break;
5846 			}
5847 			break;
5848 		case sTypeLucciAir:
5849 			WriteMessage("subtype       = Lucci Air");
5850 			break;
5851 		case sTypeSeavTXS4:
5852 			WriteMessage("subtype       = SEAV TXS4");
5853 			break;
5854 		case sTypeWestinghouse:
5855 			WriteMessage("subtype       = Westinghouse");
5856 			break;
5857 		case sTypeLucciAirDC:
5858 			WriteMessage("subtype       = Lucci Air DC");
5859 			break;
5860 		case sTypeCasafan:
5861 			WriteMessage("subtype       = Casafan");
5862 			break;
5863 		case sTypeFT1211R:
5864 			WriteMessage("subtype       = FT1211R");
5865 			break;
5866 		case sTypeFalmec:
5867 			WriteMessage("subtype       = Falmec");
5868 			break;
5869 		case sTypeLucciAirDCII:
5870 			WriteMessage("subtype       = Lucci Air DC II");
5871 			break;
5872 		default:
5873 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->LIGHTING6.packettype, pResponse->LIGHTING6.subtype);
5874 			WriteMessage(szTmp);
5875 			break;
5876 		}
5877 		sprintf(szTmp, "Signal level  = %d", pResponse->LIGHTING6.rssi);
5878 		WriteMessage(szTmp);
5879 		WriteMessageEnd();
5880 	}
5881 	procResult.DeviceRowIdx = DevRowIdx;
5882 }
5883 
decode_HomeConfort(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)5884 void MainWorker::decode_HomeConfort(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
5885 {
5886 	char szTmp[100];
5887 	uint8_t devType = pTypeHomeConfort;
5888 	uint8_t subType = pResponse->HOMECONFORT.subtype;
5889 	sprintf(szTmp, "%02X%02X%02X%02X", pResponse->HOMECONFORT.id1, pResponse->HOMECONFORT.id2, pResponse->HOMECONFORT.id3, pResponse->HOMECONFORT.housecode);
5890 	std::string ID = szTmp;
5891 	uint8_t Unit = pResponse->HOMECONFORT.unitcode;
5892 	uint8_t cmnd = pResponse->HOMECONFORT.cmnd;
5893 	uint8_t SignalLevel = pResponse->HOMECONFORT.rssi;
5894 
5895 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, -1, cmnd, procResult.DeviceName);
5896 
5897 	bool isGroupCommand = ((cmnd == HomeConfort_sGroupOff) || (cmnd == HomeConfort_sGroupOn));
5898 	uint8_t single_cmnd = cmnd;
5899 
5900 	if (isGroupCommand)
5901 	{
5902 		single_cmnd = (cmnd == HomeConfort_sGroupOff) ? HomeConfort_sOff : HomeConfort_sOn;
5903 
5904 		// We write the GROUP_CMD into the log to differentiate between manual turn off/on and group_off/group_on
5905 		m_sql.UpdateValueHomeConfortGroupCmd(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, -1, cmnd, szTmp, procResult.DeviceName);
5906 
5907 		//set the status of all lights with the same code to on/off
5908 		m_sql.HomeConfortGroupCmd(ID, subType, single_cmnd);
5909 	}
5910 
5911 	if (DevRowIdx == (uint64_t)-1)
5912 		return;
5913 	CheckSceneCode(DevRowIdx, devType, subType, cmnd, "", procResult.DeviceName);
5914 
5915 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
5916 	{
5917 		WriteMessageStart();
5918 		switch (pResponse->HOMECONFORT.subtype)
5919 		{
5920 		case sTypeHomeConfortTEL010:
5921 			WriteMessage("subtype       = TEL-010");
5922 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->HOMECONFORT.seqnbr);
5923 			WriteMessage(szTmp);
5924 			sprintf(szTmp, "ID            = %02X%02X%02X", pResponse->HOMECONFORT.id1, pResponse->HOMECONFORT.id2, pResponse->HOMECONFORT.id3);
5925 			WriteMessage(szTmp);
5926 			sprintf(szTmp, "housecode     = %d", pResponse->HOMECONFORT.housecode);
5927 			WriteMessage(szTmp);
5928 			sprintf(szTmp, "unitcode      = %d", pResponse->HOMECONFORT.unitcode);
5929 			WriteMessage(szTmp);
5930 			WriteMessage("Command       = ", false);
5931 			switch (pResponse->HOMECONFORT.cmnd)
5932 			{
5933 			case HomeConfort_sOff:
5934 				WriteMessage("Off");
5935 				break;
5936 			case HomeConfort_sOn:
5937 				WriteMessage("On");
5938 				break;
5939 			case HomeConfort_sGroupOff:
5940 				WriteMessage("Group Off");
5941 				break;
5942 			case HomeConfort_sGroupOn:
5943 				WriteMessage("Group On");
5944 				break;
5945 			default:
5946 				WriteMessage("UNKNOWN");
5947 				break;
5948 			}
5949 			break;
5950 		default:
5951 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->HOMECONFORT.packettype, pResponse->HOMECONFORT.subtype);
5952 			WriteMessage(szTmp);
5953 			break;
5954 		}
5955 		sprintf(szTmp, "Signal level  = %d", pResponse->HOMECONFORT.rssi);
5956 		WriteMessage(szTmp);
5957 		WriteMessageEnd();
5958 	}
5959 	procResult.DeviceRowIdx = DevRowIdx;
5960 }
5961 
decode_ColorSwitch(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)5962 void MainWorker::decode_ColorSwitch(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
5963 {
5964 	char szTmp[300];
5965 	const _tColorSwitch* pLed = reinterpret_cast<const _tColorSwitch*>(pResponse);
5966 	uint8_t devType = pTypeColorSwitch;
5967 	uint8_t subType = pLed->subtype;
5968 	if (pLed->id == 1)
5969 		sprintf(szTmp, "%d", 1);
5970 	else
5971 		sprintf(szTmp, "%08X", (unsigned int)pLed->id);
5972 	std::string ID = szTmp;
5973 	uint8_t Unit = pLed->dunit;
5974 	uint8_t cmnd = pLed->command;
5975 	uint32_t value = pLed->value;
5976 	_tColor color = pLed->color;
5977 
5978 	char szValueTmp[100];
5979 	sprintf(szValueTmp, "%u", value);
5980 	std::string sValue = szValueTmp;
5981 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, 12, -1, cmnd, sValue.c_str(), procResult.DeviceName);
5982 	if (DevRowIdx == (uint64_t)-1)
5983 		return;
5984 	CheckSceneCode(DevRowIdx, devType, subType, cmnd, szTmp, procResult.DeviceName);
5985 
5986 	// TODO: Why don't we let database update be handled by CSQLHelper::UpdateValue?
5987 	if (cmnd == Color_SetBrightnessLevel || cmnd == Color_SetColor)
5988 	{
5989 		std::vector<std::vector<std::string> > result;
5990 		result = m_sql.safe_query(
5991 			"SELECT ID,Name FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)",
5992 			pHardware->m_HwdID, ID.c_str(), Unit, devType, subType);
5993 		if (!result.empty())
5994 		{
5995 			uint64_t ulID = std::strtoull(result[0][0].c_str(), nullptr, 10);
5996 
5997 			//store light level
5998 			m_sql.safe_query(
5999 				"UPDATE DeviceStatus SET LastLevel='%d' WHERE (ID = %" PRIu64 ")",
6000 				value,
6001 				ulID);
6002 		}
6003 
6004 	}
6005 
6006 	if (cmnd == Color_SetColor)
6007 	{
6008 		std::vector<std::vector<std::string> > result;
6009 		result = m_sql.safe_query(
6010 			"SELECT ID,Name FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)",
6011 			pHardware->m_HwdID, ID.c_str(), Unit, devType, subType);
6012 		if (!result.empty())
6013 		{
6014 			uint64_t ulID = std::strtoull(result[0][0].c_str(), nullptr, 10);
6015 
6016 			//store color in database
6017 			m_sql.safe_query(
6018 				"UPDATE DeviceStatus SET Color='%q' WHERE (ID = %" PRIu64 ")",
6019 				color.toJSONString().c_str(),
6020 				ulID);
6021 		}
6022 
6023 	}
6024 
6025 	procResult.DeviceRowIdx = DevRowIdx;
6026 }
6027 
decode_Chime(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)6028 void MainWorker::decode_Chime(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
6029 {
6030 	char szTmp[100];
6031 	uint8_t devType = pTypeChime;
6032 	uint8_t subType = pResponse->CHIME.subtype;
6033 	sprintf(szTmp, "%02X%02X", pResponse->CHIME.id1, pResponse->CHIME.id2);
6034 	std::string ID = szTmp;
6035 	uint8_t Unit = pResponse->CHIME.sound;
6036 	uint8_t cmnd = pResponse->CHIME.sound;
6037 	uint8_t SignalLevel = pResponse->CHIME.rssi;
6038 
6039 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, -1, cmnd, procResult.DeviceName);
6040 	if (DevRowIdx == (uint64_t)-1)
6041 		return;
6042 	CheckSceneCode(DevRowIdx, devType, subType, cmnd, "", procResult.DeviceName);
6043 
6044 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
6045 	{
6046 		WriteMessageStart();
6047 		switch (pResponse->CHIME.subtype)
6048 		{
6049 		case sTypeByronSX:
6050 			WriteMessage("subtype       = Byron SX");
6051 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->CHIME.seqnbr);
6052 			WriteMessage(szTmp);
6053 			sprintf(szTmp, "ID            = %02X%02X", pResponse->CHIME.id1, pResponse->CHIME.id2);
6054 			WriteMessage(szTmp);
6055 			WriteMessage("Sound          = ", false);
6056 			switch (pResponse->CHIME.sound)
6057 			{
6058 			case chime_sound0:
6059 			case chime_sound4:
6060 				WriteMessage("Tubular 3 notes");
6061 				break;
6062 			case chime_sound1:
6063 			case chime_sound5:
6064 				WriteMessage("Big Ben");
6065 				break;
6066 			case chime_sound2:
6067 			case chime_sound6:
6068 				WriteMessage("Tubular 3 notes");
6069 				break;
6070 			case chime_sound3:
6071 			case chime_sound7:
6072 				WriteMessage("Solo");
6073 				break;
6074 			default:
6075 				WriteMessage("UNKNOWN?");
6076 				break;
6077 			}
6078 			break;
6079 		case sTypeByronMP001:
6080 			WriteMessage("subtype       = Byron MP001");
6081 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->CHIME.seqnbr);
6082 			WriteMessage(szTmp);
6083 			sprintf(szTmp, "ID            = %02X%02X", pResponse->CHIME.id1, pResponse->CHIME.id2);
6084 			WriteMessage(szTmp);
6085 			sprintf(szTmp, "Sound          = %d", pResponse->CHIME.sound);
6086 			WriteMessage(szTmp);
6087 
6088 			if ((pResponse->CHIME.id1 & 0x40) == 0x40)
6089 				WriteMessage("Switch 1      = Off");
6090 			else
6091 				WriteMessage("Switch 1      = On");
6092 
6093 			if ((pResponse->CHIME.id1 & 0x10) == 0x10)
6094 				WriteMessage("Switch 2      = Off");
6095 			else
6096 				WriteMessage("Switch 2      = On");
6097 
6098 			if ((pResponse->CHIME.id1 & 0x04) == 0x04)
6099 				WriteMessage("Switch 3      = Off");
6100 			else
6101 				WriteMessage("Switch 3      = On");
6102 
6103 			if ((pResponse->CHIME.id1 & 0x01) == 0x01)
6104 				WriteMessage("Switch 4      = Off");
6105 			else
6106 				WriteMessage("Switch 4      = On");
6107 
6108 			if ((pResponse->CHIME.id2 & 0x40) == 0x40)
6109 				WriteMessage("Switch 5      = Off");
6110 			else
6111 				WriteMessage("Switch 5      = On");
6112 
6113 			if ((pResponse->CHIME.id2 & 0x10) == 0x10)
6114 				WriteMessage("Switch 6      = Off");
6115 			else
6116 				WriteMessage("Switch 6      = On");
6117 			break;
6118 		case sTypeSelectPlus:
6119 			WriteMessage("subtype       = SelectPlus200689101");
6120 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->CHIME.seqnbr);
6121 			WriteMessage(szTmp);
6122 			sprintf(szTmp, "ID            = %02X%02X", pResponse->CHIME.id1, pResponse->CHIME.id2);
6123 			WriteMessage(szTmp);
6124 			break;
6125 		case sTypeByronBY:
6126 			WriteMessage("subtype       = SelectPlus200689103");
6127 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->CHIME.seqnbr);
6128 			WriteMessage(szTmp);
6129 			sprintf(szTmp, "ID            = %02X%02X", pResponse->CHIME.id1, pResponse->CHIME.id2);
6130 			WriteMessage(szTmp);
6131 			break;
6132 		case sTypeEnvivo:
6133 			WriteMessage("subtype       = Envivo");
6134 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->CHIME.seqnbr);
6135 			WriteMessage(szTmp);
6136 			sprintf(szTmp, "ID            = %02X%02X", pResponse->CHIME.id1, pResponse->CHIME.id2);
6137 			WriteMessage(szTmp);
6138 			break;
6139 		case sTypeAlfawise:
6140 			WriteMessage("subtype       = Alfawise");
6141 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->CHIME.seqnbr);
6142 			WriteMessage(szTmp);
6143 			sprintf(szTmp, "ID            = %02X%02X", pResponse->CHIME.id1, pResponse->CHIME.id2);
6144 			WriteMessage(szTmp);
6145 			break;
6146 		default:
6147 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->CHIME.packettype, pResponse->CHIME.subtype);
6148 			WriteMessage(szTmp);
6149 			break;
6150 		}
6151 		sprintf(szTmp, "Signal level  = %d", pResponse->CHIME.rssi);
6152 		WriteMessage(szTmp);
6153 		WriteMessageEnd();
6154 	}
6155 	procResult.DeviceRowIdx = DevRowIdx;
6156 }
6157 
6158 
decode_UNDECODED(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)6159 void MainWorker::decode_UNDECODED(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
6160 {
6161 	char szTmp[100];
6162 
6163 	WriteMessage("UNDECODED ", false);
6164 
6165 	switch (pResponse->UNDECODED.subtype)
6166 	{
6167 	case sTypeUac:
6168 		WriteMessage("AC:", false);
6169 		break;
6170 	case sTypeUarc:
6171 		WriteMessage("ARC:", false);
6172 		break;
6173 	case sTypeUati:
6174 		WriteMessage("ATI:", false);
6175 		break;
6176 	case sTypeUhideki:
6177 		WriteMessage("HIDEKI:", false);
6178 		break;
6179 	case sTypeUlacrosse:
6180 		WriteMessage("LACROSSE:", false);
6181 		break;
6182 	case sTypeUad:
6183 		WriteMessage("AD:", false);
6184 		break;
6185 	case sTypeUmertik:
6186 		WriteMessage("MERTIK:", false);
6187 		break;
6188 	case sTypeUoregon1:
6189 		WriteMessage("OREGON1:", false);
6190 		break;
6191 	case sTypeUoregon2:
6192 		WriteMessage("OREGON2:", false);
6193 		break;
6194 	case sTypeUoregon3:
6195 		WriteMessage("OREGON3:", false);
6196 		break;
6197 	case sTypeUproguard:
6198 		WriteMessage("PROGUARD:", false);
6199 		break;
6200 	case sTypeUvisonic:
6201 		WriteMessage("VISONIC:", false);
6202 		break;
6203 	case sTypeUnec:
6204 		WriteMessage("NEC:", false);
6205 		break;
6206 	case sTypeUfs20:
6207 		WriteMessage("FS20:", false);
6208 		break;
6209 	case sTypeUblinds:
6210 		WriteMessage("Blinds:", false);
6211 		break;
6212 	case sTypeUrubicson:
6213 		WriteMessage("RUBICSON:", false);
6214 		break;
6215 	case sTypeUae:
6216 		WriteMessage("AE:", false);
6217 		break;
6218 	case sTypeUfineoffset:
6219 		WriteMessage("FineOffset:", false);
6220 		break;
6221 	case sTypeUrgb:
6222 		WriteMessage("RGB:", false);
6223 		break;
6224 	case sTypeUrfy:
6225 		WriteMessage("RFY:", false);
6226 		break;
6227 	default:
6228 		sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->UNDECODED.packettype, pResponse->UNDECODED.subtype);
6229 		WriteMessage(szTmp);
6230 		break;
6231 	}
6232 	std::stringstream sHexDump;
6233 	uint8_t* pRXBytes = (uint8_t*)&pResponse->UNDECODED.msg1;
6234 	for (int i = 0; i < pResponse->UNDECODED.packetlength - 3; i++)
6235 	{
6236 		sHexDump << HEX(pRXBytes[i]);
6237 	}
6238 	WriteMessage(sHexDump.str().c_str());
6239 	procResult.DeviceRowIdx = -1;
6240 }
6241 
6242 
decode_RecXmitMessage(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)6243 void MainWorker::decode_RecXmitMessage(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
6244 {
6245 	char szTmp[100];
6246 
6247 	switch (pResponse->RXRESPONSE.subtype)
6248 	{
6249 	case sTypeReceiverLockError:
6250 		WriteMessage("subtype           = Receiver lock error");
6251 		sprintf(szTmp, "Sequence nbr      = %d", pResponse->RXRESPONSE.seqnbr);
6252 		WriteMessage(szTmp);
6253 		break;
6254 	case sTypeTransmitterResponse:
6255 		WriteMessage("subtype           = Transmitter Response");
6256 		sprintf(szTmp, "Sequence nbr      = %d", pResponse->RXRESPONSE.seqnbr);
6257 		WriteMessage(szTmp);
6258 
6259 		switch (pResponse->RXRESPONSE.msg)
6260 		{
6261 		case 0x0:
6262 			WriteMessage("response          = ACK, data correct transmitted");
6263 			break;
6264 		case 0x1:
6265 			WriteMessage("response          = ACK, but transmit started after 6 seconds delay anyway with RF receive data detected");
6266 			break;
6267 		case 0x2:
6268 			WriteMessage("response          = NAK, transmitter did not lock on the requested transmit frequency");
6269 			break;
6270 		case 0x3:
6271 			WriteMessage("response          = NAK, AC address zero in id1-id4 not allowed");
6272 			break;
6273 		default:
6274 			sprintf(szTmp, "ERROR: Unexpected message type= %02X", pResponse->RXRESPONSE.msg);
6275 			WriteMessage(szTmp);
6276 			break;
6277 		}
6278 		break;
6279 	default:
6280 		sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->RXRESPONSE.packettype, pResponse->RXRESPONSE.subtype);
6281 		WriteMessage(szTmp);
6282 		break;
6283 	}
6284 	procResult.DeviceRowIdx = -1;
6285 }
6286 
decode_Curtain(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)6287 void MainWorker::decode_Curtain(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
6288 {
6289 	char szTmp[100];
6290 	uint8_t devType = pTypeCurtain;
6291 	uint8_t subType = pResponse->CURTAIN1.subtype;
6292 	sprintf(szTmp, "%d", pResponse->CURTAIN1.housecode);
6293 	std::string ID = szTmp;
6294 	uint8_t Unit = pResponse->CURTAIN1.unitcode;
6295 	uint8_t cmnd = pResponse->CURTAIN1.cmnd;
6296 	uint8_t SignalLevel = 9;
6297 
6298 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, -1, cmnd, procResult.DeviceName);
6299 	if (DevRowIdx == (uint64_t)-1)
6300 		return;
6301 
6302 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
6303 	{
6304 		WriteMessageStart();
6305 		char szTmp[100];
6306 
6307 		switch (pResponse->CURTAIN1.subtype)
6308 		{
6309 		case sTypeHarrison:
6310 			WriteMessage("subtype       = Harrison");
6311 			break;
6312 		default:
6313 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X:", pResponse->CURTAIN1.packettype, pResponse->CURTAIN1.subtype);
6314 			WriteMessage(szTmp);
6315 			break;
6316 		}
6317 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->CURTAIN1.seqnbr);
6318 		WriteMessage(szTmp);
6319 
6320 		sprintf(szTmp, "Housecode         = %d", pResponse->CURTAIN1.housecode);
6321 		WriteMessage(szTmp);
6322 
6323 		sprintf(szTmp, "Unit          = %d", pResponse->CURTAIN1.unitcode);
6324 		WriteMessage(szTmp);
6325 
6326 		WriteMessage("Command       = ", false);
6327 
6328 		switch (pResponse->CURTAIN1.cmnd)
6329 		{
6330 		case curtain_sOpen:
6331 			WriteMessage("Open");
6332 			break;
6333 		case curtain_sStop:
6334 			WriteMessage("Stop");
6335 			break;
6336 		case curtain_sClose:
6337 			WriteMessage("Close");
6338 			break;
6339 		default:
6340 			WriteMessage("UNKNOWN");
6341 			break;
6342 		}
6343 		WriteMessageEnd();
6344 	}
6345 	procResult.DeviceRowIdx = DevRowIdx;
6346 }
6347 
decode_BLINDS1(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)6348 void MainWorker::decode_BLINDS1(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
6349 {
6350 	char szTmp[100];
6351 	uint8_t devType = pTypeBlinds;
6352 	uint8_t subType = pResponse->BLINDS1.subtype;
6353 
6354 	sprintf(szTmp, "%02X%02X%02X%02X", pResponse->BLINDS1.id1, pResponse->BLINDS1.id2, pResponse->BLINDS1.id3, pResponse->BLINDS1.id4);
6355 
6356 	std::string ID = szTmp;
6357 	uint8_t Unit = pResponse->BLINDS1.unitcode;
6358 	uint8_t cmnd = pResponse->BLINDS1.cmnd;
6359 	uint8_t SignalLevel = pResponse->BLINDS1.rssi;
6360 
6361 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, -1, cmnd, procResult.DeviceName);
6362 	if (DevRowIdx == (uint64_t)-1)
6363 		return;
6364 	CheckSceneCode(DevRowIdx, devType, subType, cmnd, szTmp, procResult.DeviceName);
6365 
6366 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
6367 	{
6368 		WriteMessageStart();
6369 		char szTmp[100];
6370 
6371 		switch (pResponse->BLINDS1.subtype)
6372 		{
6373 		case sTypeBlindsT0:
6374 			WriteMessage("subtype       = Safy / RollerTrol / Hasta new");
6375 			break;
6376 		case sTypeBlindsT1:
6377 			WriteMessage("subtype       = Hasta old");
6378 			break;
6379 		case sTypeBlindsT2:
6380 			WriteMessage("subtype       = A-OK RF01");
6381 			break;
6382 		case sTypeBlindsT3:
6383 			WriteMessage("subtype       = A-OK AC114");
6384 			break;
6385 		case sTypeBlindsT4:
6386 			WriteMessage("subtype       = RAEX");
6387 			break;
6388 		case sTypeBlindsT5:
6389 			WriteMessage("subtype       = Media Mount");
6390 			break;
6391 		case sTypeBlindsT6:
6392 			WriteMessage("subtype       = DC106, YOOHA, Rohrmotor24 RMF");
6393 			break;
6394 		case sTypeBlindsT7:
6395 			WriteMessage("subtype       = Forest");
6396 			break;
6397 		case sTypeBlindsT8:
6398 			WriteMessage("subtype       = Chamberlain CS4330CN");
6399 			break;
6400 		case sTypeBlindsT9:
6401 			WriteMessage("subtype       = Sunpery");
6402 			break;
6403 		case sTypeBlindsT10:
6404 			WriteMessage("subtype       = Dolat DLM-1");
6405 			break;
6406 		case sTypeBlindsT11:
6407 			WriteMessage("subtype       = ASP");
6408 			break;
6409 		case sTypeBlindsT12:
6410 			WriteMessage("subtype       = Confexx");
6411 			break;
6412 		case sTypeBlindsT13:
6413 			WriteMessage("subtype       = Screenline");
6414 			break;
6415 		case sTypeBlindsT14:
6416 			WriteMessage("subtype       = Hualite");
6417 			break;
6418 		case sTypeBlindsT15:
6419 			WriteMessage("subtype       = RFU");
6420 			break;
6421 		case sTypeBlindsT16:
6422 			WriteMessage("subtype       = Zemismart");
6423 			break;
6424 		case sTypeBlindsT17:
6425 			WriteMessage("subtype       = Gaposa");
6426 			break;
6427 		case sTypeBlindsT18:
6428 			WriteMessage("subtype       = Cherubini");
6429 			break;
6430 		default:
6431 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X:", pResponse->BLINDS1.packettype, pResponse->BLINDS1.subtype);
6432 			WriteMessage(szTmp);
6433 			break;
6434 		}
6435 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->BLINDS1.seqnbr);
6436 		WriteMessage(szTmp);
6437 
6438 		sprintf(szTmp, "id1-3         = %02X%02X%02X", pResponse->BLINDS1.id1, pResponse->BLINDS1.id2, pResponse->BLINDS1.id3);
6439 		WriteMessage(szTmp);
6440 
6441 		if ((subType == sTypeBlindsT6) || (subType == sTypeBlindsT7))
6442 		{
6443 			sprintf(szTmp, "id4         = %02X", pResponse->BLINDS1.id4);
6444 			WriteMessage(szTmp);
6445 		}
6446 
6447 		if (pResponse->BLINDS1.unitcode == 0)
6448 			WriteMessage("Unit          = All");
6449 		else
6450 		{
6451 			sprintf(szTmp, "Unit          = %d", pResponse->BLINDS1.unitcode);
6452 			WriteMessage(szTmp);
6453 		}
6454 
6455 		WriteMessage("Command       = ", false);
6456 
6457 		switch (pResponse->BLINDS1.cmnd)
6458 		{
6459 		case blinds_sOpen:
6460 			WriteMessage("Open");
6461 			break;
6462 		case blinds_sStop:
6463 			WriteMessage("Stop");
6464 			break;
6465 		case blinds_sClose:
6466 			WriteMessage("Close");
6467 			break;
6468 		case blinds_sConfirm:
6469 			WriteMessage("Confirm");
6470 			break;
6471 		case blinds_sLimit:
6472 			WriteMessage("Set Limit");
6473 			if (pResponse->BLINDS1.subtype == sTypeBlindsT4)
6474 				WriteMessage("Set Upper Limit");
6475 			else
6476 				WriteMessage("Set Limit");
6477 			break;
6478 		case blinds_slowerLimit:
6479 			WriteMessage("Set Lower Limit");
6480 			break;
6481 		case blinds_sDeleteLimits:
6482 			WriteMessage("Delete Limits");
6483 			break;
6484 		case blinds_sChangeDirection:
6485 			WriteMessage("Change Direction");
6486 			break;
6487 		case blinds_sLeft:
6488 			WriteMessage("Left");
6489 			break;
6490 		case blinds_sRight:
6491 			WriteMessage("Right");
6492 			break;
6493 		default:
6494 			WriteMessage("UNKNOWN");
6495 			break;
6496 		}
6497 		sprintf(szTmp, "Signal level  = %d", pResponse->BLINDS1.rssi);
6498 		WriteMessage(szTmp);
6499 		WriteMessageEnd();
6500 	}
6501 	procResult.DeviceRowIdx = DevRowIdx;
6502 }
6503 
decode_RFY(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)6504 void MainWorker::decode_RFY(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
6505 {
6506 	char szTmp[100];
6507 	uint8_t devType = pTypeRFY;
6508 	uint8_t subType = pResponse->RFY.subtype;
6509 	sprintf(szTmp, "%02X%02X%02X", pResponse->RFY.id1, pResponse->RFY.id2, pResponse->RFY.id3);
6510 	std::string ID = szTmp;
6511 	uint8_t Unit = pResponse->RFY.unitcode;
6512 	uint8_t cmnd = pResponse->RFY.cmnd;
6513 	uint8_t SignalLevel = pResponse->RFY.rssi;
6514 
6515 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, -1, cmnd, procResult.DeviceName);
6516 	if (DevRowIdx == (uint64_t)-1)
6517 		return;
6518 	CheckSceneCode(DevRowIdx, devType, subType, cmnd, szTmp, procResult.DeviceName);
6519 
6520 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
6521 	{
6522 		WriteMessageStart();
6523 		char szTmp[100];
6524 
6525 		switch (pResponse->RFY.subtype)
6526 		{
6527 		case sTypeRFY:
6528 			WriteMessage("subtype       = RFY");
6529 			break;
6530 		case sTypeRFY2:
6531 			WriteMessage("subtype       = RFY2");
6532 			break;
6533 		case sTypeRFYext:
6534 			WriteMessage("subtype       = RFY-Ext");
6535 			break;
6536 		case sTypeASA:
6537 			WriteMessage("subtype       = ASA");
6538 			break;
6539 		default:
6540 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X:", pResponse->RFY.packettype, pResponse->RFY.subtype);
6541 			WriteMessage(szTmp);
6542 			break;
6543 		}
6544 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->RFY.seqnbr);
6545 		WriteMessage(szTmp);
6546 
6547 		sprintf(szTmp, "id1-3         = %02X%02X%02X", pResponse->RFY.id1, pResponse->RFY.id2, pResponse->RFY.id3);
6548 		WriteMessage(szTmp);
6549 
6550 		if (pResponse->RFY.unitcode == 0)
6551 			WriteMessage("Unit          = All");
6552 		else
6553 		{
6554 			sprintf(szTmp, "Unit          = %d", pResponse->RFY.unitcode);
6555 			WriteMessage(szTmp);
6556 		}
6557 
6558 		sprintf(szTmp, "rfu1         = %02X", pResponse->RFY.rfu1);
6559 		WriteMessage(szTmp);
6560 		sprintf(szTmp, "rfu2         = %02X", pResponse->RFY.rfu2);
6561 		WriteMessage(szTmp);
6562 		sprintf(szTmp, "rfu3         = %02X", pResponse->RFY.rfu3);
6563 		WriteMessage(szTmp);
6564 
6565 		WriteMessage("Command       = ", false);
6566 
6567 		switch (pResponse->RFY.cmnd)
6568 		{
6569 		case rfy_sStop:
6570 			WriteMessage("Stop");
6571 			break;
6572 		case rfy_sUp:
6573 			WriteMessage("Up");
6574 			break;
6575 		case rfy_sUpStop:
6576 			WriteMessage("Up + Stop");
6577 			break;
6578 		case rfy_sDown:
6579 			WriteMessage("Down");
6580 			break;
6581 		case rfy_sDownStop:
6582 			WriteMessage("Down + Stop");
6583 			break;
6584 		case rfy_sUpDown:
6585 			WriteMessage("Up + Down");
6586 			break;
6587 		case rfy_sListRemotes:
6588 			WriteMessage("List remotes");
6589 		case rfy_sProgram:
6590 			WriteMessage("Program");
6591 			break;
6592 		case rfy_s2SecProgram:
6593 			WriteMessage("2 seconds: Program");
6594 			break;
6595 		case rfy_s7SecProgram:
6596 			WriteMessage("7 seconds: Program");
6597 			break;
6598 		case rfy_s2SecStop:
6599 			WriteMessage("2 seconds: Stop");
6600 			break;
6601 		case rfy_s5SecStop:
6602 			WriteMessage("5 seconds: Stop");
6603 			break;
6604 		case rfy_s5SecUpDown:
6605 			WriteMessage("5 seconds: Up + Down");
6606 			break;
6607 		case rfy_sEraseThis:
6608 			WriteMessage("Erase this remote");
6609 			break;
6610 		case rfy_sEraseAll:
6611 			WriteMessage("Erase all remotes");
6612 			break;
6613 		case rfy_s05SecUp:
6614 			WriteMessage("< 0.5 seconds: up");
6615 			break;
6616 		case rfy_s05SecDown:
6617 			WriteMessage("< 0.5 seconds: down");
6618 			break;
6619 		case rfy_s2SecUp:
6620 			WriteMessage("> 2 seconds: up");
6621 			break;
6622 		case rfy_s2SecDown:
6623 			WriteMessage("> 2 seconds: down");
6624 			break;
6625 		default:
6626 			WriteMessage("UNKNOWN");
6627 			break;
6628 		}
6629 		sprintf(szTmp, "Signal level  = %d", pResponse->RFY.rssi);
6630 		WriteMessage(szTmp);
6631 		WriteMessageEnd();
6632 	}
6633 	procResult.DeviceRowIdx = DevRowIdx;
6634 }
6635 
decode_evohome1(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)6636 void MainWorker::decode_evohome1(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
6637 {
6638 	char szTmp[100];
6639 	const _tEVOHOME1* pEvo = reinterpret_cast<const _tEVOHOME1*>(pResponse);
6640 	uint8_t devType = pTypeEvohome;
6641 	uint8_t subType = pEvo->subtype;
6642 	std::stringstream szID;
6643 	if (pHardware->HwdType == HTYPE_EVOHOME_SERIAL || pHardware->HwdType == HTYPE_EVOHOME_TCP)
6644 		szID << std::hex << (int)RFX_GETID3(pEvo->id1, pEvo->id2, pEvo->id3);
6645 	else //GB3: web based evohome uses decimal device ID's
6646 		szID << std::dec << (int)RFX_GETID3(pEvo->id1, pEvo->id2, pEvo->id3);
6647 	std::string ID(szID.str());
6648 	uint8_t Unit = 0;
6649 	uint8_t cmnd = pEvo->status;
6650 	uint8_t SignalLevel = 255;//Unknown
6651 	uint8_t BatteryLevel = 255;//Unknown
6652 
6653 	std::string szUntilDate;
6654 	if (pEvo->mode == CEvohomeBase::cmTmp)//temporary
6655 		szUntilDate = CEvohomeDateTime::GetISODate(pEvo);
6656 
6657 	CEvohomeBase* pEvoHW = (CEvohomeBase*)pHardware;
6658 
6659 	//FIXME A similar check is also done in switch modal do we want to forward the ooc flag and rely on this check entirely?
6660 	std::vector<std::vector<std::string> > result;
6661 	result = m_sql.safe_query(
6662 		"SELECT HardwareID, DeviceID,Unit,Type,SubType,SwitchType,StrParam1,nValue,sValue FROM DeviceStatus WHERE (HardwareID==%d) AND (DeviceID == '%q')",
6663 		pHardware->m_HwdID, ID.c_str());
6664 	bool bNewDev = false;
6665 	std::string name;
6666 	if (!result.empty())
6667 	{
6668 		std::vector<std::string> sd = result[0];
6669 		if (atoi(sd[7].c_str()) == cmnd && sd[8] == szUntilDate)
6670 			return;
6671 	}
6672 	else
6673 	{
6674 		bNewDev = true;
6675 		if (!pEvoHW)
6676 			return;
6677 		name = pEvoHW->GetControllerName();
6678 		if (name.empty())
6679 			return;
6680 	}
6681 
6682 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szUntilDate.c_str(), procResult.DeviceName, pEvo->action != 0);
6683 	if (DevRowIdx == (uint64_t)-1)
6684 		return;
6685 	if (bNewDev)
6686 	{
6687 		m_sql.safe_query("UPDATE DeviceStatus SET Name='%q' WHERE (ID == %" PRIu64 ")",
6688 			name.c_str(), DevRowIdx);
6689 		procResult.DeviceName = name;
6690 	}
6691 
6692 	CheckSceneCode(DevRowIdx, devType, subType, cmnd, "", procResult.DeviceName);
6693 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
6694 	{
6695 		WriteMessageStart();
6696 		switch (pEvo->subtype)
6697 		{
6698 		case sTypeEvohome:
6699 			WriteMessage("subtype       = Evohome");
6700 			break;
6701 		default:
6702 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pEvo->type, pEvo->subtype);
6703 			WriteMessage(szTmp);
6704 			break;
6705 		}
6706 
6707 		if (pHardware->HwdType == HTYPE_EVOHOME_SERIAL || pHardware->HwdType == HTYPE_EVOHOME_TCP)
6708 			sprintf(szTmp, "id            = %02X:%02X:%02X", pEvo->id1, pEvo->id2, pEvo->id3);
6709 		else //GB3: web based evohome uses decimal device ID's
6710 			sprintf(szTmp, "id            = %u", (int)RFX_GETID3(pEvo->id1, pEvo->id2, pEvo->id3));
6711 		WriteMessage(szTmp);
6712 		sprintf(szTmp, "action        = %d", (int)pEvo->action);
6713 		WriteMessage(szTmp);
6714 		WriteMessage("status        = ");
6715 		WriteMessage(pEvoHW->GetControllerModeName(pEvo->status));
6716 
6717 		WriteMessageEnd();
6718 	}
6719 	procResult.DeviceRowIdx = DevRowIdx;
6720 }
6721 
decode_evohome2(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)6722 void MainWorker::decode_evohome2(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
6723 {
6724 	char szTmp[100];
6725 	const _tEVOHOME2* pEvo = reinterpret_cast<const _tEVOHOME2*>(pResponse);
6726 	uint8_t cmnd = 0;
6727 	uint8_t SignalLevel = 255;//Unknown
6728 	uint8_t BatteryLevel = 255;//Unknown
6729 
6730 									 //Get Device details
6731 	std::vector<std::vector<std::string> > result;
6732 	if (pEvo->type == pTypeEvohomeZone && pEvo->zone > 12) //Allow for additional Zone Temp devices which require DeviceID
6733 	{
6734 		result = m_sql.safe_query(
6735 			"SELECT HardwareID, DeviceID,Unit,Type,SubType,sValue,BatteryLevel "
6736 			"FROM DeviceStatus WHERE (HardwareID==%d) AND (DeviceID == '%x') AND (Type==%d)",
6737 			pHardware->m_HwdID, (int)RFX_GETID3(pEvo->id1, pEvo->id2, pEvo->id3), (int)pEvo->type);
6738 	}
6739 	else if (pEvo->zone)//if unit number is available the id3 will be the controller device id
6740 	{
6741 		result = m_sql.safe_query(
6742 			"SELECT HardwareID, DeviceID,Unit,Type,SubType,sValue,BatteryLevel "
6743 			"FROM DeviceStatus WHERE (HardwareID==%d) AND (Unit == %d) AND (Type==%d)",
6744 			pHardware->m_HwdID, (int)pEvo->zone, (int)pEvo->type);
6745 	}
6746 	else//unit number not available then id3 should be the zone device id
6747 	{
6748 		result = m_sql.safe_query(
6749 			"SELECT HardwareID, DeviceID,Unit,Type,SubType,sValue,BatteryLevel "
6750 			"FROM DeviceStatus WHERE (HardwareID==%d) AND (DeviceID == '%x') AND (Type==%d)",
6751 			pHardware->m_HwdID, (int)RFX_GETID3(pEvo->id1, pEvo->id2, pEvo->id3), (int)pEvo->type);
6752 	}
6753 	if (result.size() < 1 && !pEvo->zone)
6754 		return;
6755 
6756 	CEvohomeBase* pEvoHW = (CEvohomeBase*)pHardware;
6757 	bool bNewDev = false;
6758 	std::string name, szDevID;
6759 	std::stringstream szID;
6760 	uint8_t Unit;
6761 	uint8_t dType;
6762 	uint8_t dSubType;
6763 	std::string szUpdateStat;
6764 	if (!result.empty())
6765 	{
6766 		std::vector<std::string> sd = result[0];
6767 		szDevID = sd[1];
6768 		Unit = atoi(sd[2].c_str());
6769 		dType = atoi(sd[3].c_str());
6770 		dSubType = atoi(sd[4].c_str());
6771 		szUpdateStat = sd[5];
6772 		BatteryLevel = atoi(sd[6].c_str());
6773 	}
6774 	else
6775 	{
6776 		bNewDev = true;
6777 		Unit = pEvo->zone;//should always be non zero
6778 		dType = pEvo->type;
6779 		dSubType = pEvo->subtype;
6780 
6781 		szID << std::hex << (int)RFX_GETID3(pEvo->id1, pEvo->id2, pEvo->id3);
6782 		szDevID = szID.str();
6783 
6784 		if (!pEvoHW)
6785 			return;
6786 		if (dType == pTypeEvohomeWater)
6787 			name = "Hot Water";
6788 		else if (dType == pTypeEvohomeZone && !szDevID.empty())
6789 			name = "Zone Temp";
6790 		else
6791 			name = pEvoHW->GetZoneName(Unit - 1);
6792 		if (name.empty())
6793 			return;
6794 		szUpdateStat = "0.0;0.0;Auto";
6795 	}
6796 
6797 	if (pEvo->updatetype == CEvohomeBase::updBattery)
6798 		BatteryLevel = pEvo->battery_level;
6799 	else
6800 	{
6801 		if (dType == pTypeEvohomeWater && pEvo->updatetype == pEvoHW->updSetPoint)
6802 			sprintf(szTmp, "%s", pEvo->temperature ? "On" : "Off");
6803 		else
6804 			sprintf(szTmp, "%.2f", pEvo->temperature / 100.0f);
6805 
6806 		std::vector<std::string> strarray;
6807 		StringSplit(szUpdateStat, ";", strarray);
6808 		if (strarray.size() >= 3)
6809 		{
6810 			if (pEvo->updatetype == pEvoHW->updSetPoint)//SetPoint
6811 			{
6812 				strarray[1] = szTmp;
6813 				if (pEvo->mode <= pEvoHW->zmTmp)//for the moment only update this if we get a valid setpoint mode as we can now send setpoint on its own
6814 				{
6815 					int nControllerMode = pEvo->controllermode;
6816 					if (dType == pTypeEvohomeWater && (nControllerMode == pEvoHW->cmEvoHeatingOff || nControllerMode == pEvoHW->cmEvoAutoWithEco || nControllerMode == pEvoHW->cmEvoCustom))//dhw has no economy mode and does not turn off for heating off also appears custom does not support the dhw zone
6817 						nControllerMode = pEvoHW->cmEvoAuto;
6818 					if (pEvo->mode == pEvoHW->zmAuto || nControllerMode == pEvoHW->cmEvoHeatingOff)//if zonemode is auto (followschedule) or controllermode is heatingoff
6819 						strarray[2] = pEvoHW->GetWebAPIModeName(nControllerMode);//the web front end ultimately uses these names for images etc.
6820 					else
6821 						strarray[2] = pEvoHW->GetZoneModeName(pEvo->mode);
6822 					if (pEvo->mode == pEvoHW->zmTmp)
6823 					{
6824 						std::string szISODate(CEvohomeDateTime::GetISODate(pEvo));
6825 						if (strarray.size() < 4) //add or set until
6826 							strarray.push_back(szISODate);
6827 						else
6828 							strarray[3] = szISODate;
6829 					}
6830 					else if ((pEvo->mode == pEvoHW->zmAuto) && (pHardware->HwdType == HTYPE_EVOHOME_WEB))
6831 					{
6832 						strarray[2] = "FollowSchedule";
6833 						if ((pEvo->year != 0) && (pEvo->year != 0xFFFF))
6834 						{
6835 							std::string szISODate(CEvohomeDateTime::GetISODate(pEvo));
6836 							if (strarray.size() < 4) //add or set until
6837 								strarray.push_back(szISODate);
6838 							else
6839 								strarray[3] = szISODate;
6840 						}
6841 
6842 					}
6843 					else
6844 						if (strarray.size() >= 4) //remove until
6845 							strarray.resize(3);
6846 				}
6847 			}
6848 			else if (pEvo->updatetype == pEvoHW->updOverride)
6849 			{
6850 				strarray[2] = pEvoHW->GetZoneModeName(pEvo->mode);
6851 				if (strarray.size() >= 4) //remove until
6852 					strarray.resize(3);
6853 			}
6854 			else
6855 				strarray[0] = szTmp;
6856 			szUpdateStat = boost::algorithm::join(strarray, ";");
6857 		}
6858 	}
6859 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, szDevID.c_str(), Unit, dType, dSubType, SignalLevel, BatteryLevel, cmnd, szUpdateStat.c_str(), procResult.DeviceName);
6860 	if (DevRowIdx == (uint64_t)-1)
6861 		return;
6862 	if (bNewDev)
6863 	{
6864 		m_sql.safe_query("UPDATE DeviceStatus SET Name='%q' WHERE (ID == %" PRIu64 ")",
6865 			name.c_str(), DevRowIdx);
6866 		procResult.DeviceName = name;
6867 	}
6868 	procResult.DeviceRowIdx = DevRowIdx;
6869 }
6870 
decode_evohome3(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)6871 void MainWorker::decode_evohome3(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
6872 {
6873 	char szTmp[100];
6874 	const _tEVOHOME3* pEvo = reinterpret_cast<const _tEVOHOME3*>(pResponse);
6875 	uint8_t devType = pTypeEvohomeRelay;
6876 	uint8_t subType = pEvo->subtype;
6877 	std::stringstream szID;
6878 	int nDevID = (int)RFX_GETID3(pEvo->id1, pEvo->id2, pEvo->id3);
6879 	szID << std::hex << nDevID;
6880 	std::string ID(szID.str());
6881 	uint8_t Unit = pEvo->devno;
6882 	uint8_t cmnd = (pEvo->demand > 0) ? light1_sOn : light1_sOff;
6883 	sprintf(szTmp, "%d", pEvo->demand);
6884 	std::string szDemand(szTmp);
6885 	uint8_t SignalLevel = 255;//Unknown
6886 	uint8_t BatteryLevel = 255;//Unknown
6887 
6888 	if (Unit == 0xFF && nDevID == 0)
6889 		return;
6890 	//Get Device details (devno or devid not available)
6891 	bool bNewDev = false;
6892 	std::vector<std::vector<std::string> > result;
6893 	if (Unit == 0xFF)
6894 		result = m_sql.safe_query(
6895 			"SELECT HardwareID,DeviceID,Unit,Type,SubType,nValue,sValue,BatteryLevel "
6896 			"FROM DeviceStatus WHERE (HardwareID==%d) AND (DeviceID == '%q')",
6897 			pHardware->m_HwdID, ID.c_str());
6898 	else
6899 		result = m_sql.safe_query(
6900 			"SELECT HardwareID,DeviceID,Unit,Type,SubType,nValue,sValue,BatteryLevel "
6901 			"FROM DeviceStatus WHERE (HardwareID==%d) AND (Unit == '%d') AND (Type==%d) AND (DeviceID == '%q')",
6902 			pHardware->m_HwdID, (int)Unit, (int)pEvo->type, ID.c_str());
6903 	if (!result.empty())
6904 	{
6905 		if (pEvo->demand == 0xFF)//we sometimes get a 0418 message after the initial device creation but it will mess up the logging as we don't have a demand
6906 			return;
6907 		uint8_t cur_cmnd = atoi(result[0][5].c_str());
6908 		BatteryLevel = atoi(result[0][7].c_str());
6909 
6910 		if (pEvo->updatetype == CEvohomeBase::updBattery)
6911 		{
6912 			BatteryLevel = pEvo->battery_level;
6913 			szDemand = result[0][6];
6914 			cmnd = (atoi(szDemand.c_str()) > 0) ? light1_sOn : light1_sOff;
6915 		}
6916 		if (Unit == 0xFF)
6917 		{
6918 			Unit = atoi(result[0][2].c_str());
6919 			szDemand = result[0][6];
6920 			if (cmnd == cur_cmnd)
6921 				return;
6922 		}
6923 		else if (nDevID == 0)
6924 		{
6925 			ID = result[0][1];
6926 		}
6927 	}
6928 	else
6929 	{
6930 		if (Unit == 0xFF || (nDevID == 0 && Unit > 12))
6931 			return;
6932 		bNewDev = true;
6933 		if (pEvo->demand == 0xFF)//0418 allows us to associate unit and deviceid but no state information other messages only contain one or the other
6934 			szDemand = "0";
6935 		if (pEvo->updatetype == CEvohomeBase::updBattery)
6936 			BatteryLevel = pEvo->battery_level;
6937 	}
6938 
6939 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szDemand.c_str(), procResult.DeviceName);
6940 	if (DevRowIdx == (uint64_t)-1)
6941 		return;
6942 
6943 	if (bNewDev && (Unit == 0xF9 || Unit == 0xFA || Unit == 0xFC || Unit <= 12))
6944 	{
6945 		if (Unit == 0xF9)
6946 			procResult.DeviceName = "CH Valve";
6947 		else if (Unit == 0xFA)
6948 			procResult.DeviceName = "DHW Valve";
6949 		else if (Unit == 0xFC)
6950 		{
6951 			if (pEvo->id1 >> 2 == CEvohomeID::devBridge) // Evohome OT Bridge
6952 				procResult.DeviceName = "Boiler (OT Bridge)";
6953 			else
6954 				procResult.DeviceName = "Boiler";
6955 		}
6956 		else if (Unit <= 12)
6957 			procResult.DeviceName = "Zone";
6958 		std::vector<std::vector<std::string> > result;
6959 		result = m_sql.safe_query(
6960 			"UPDATE DeviceStatus SET Name='%q' WHERE (ID == %" PRIu64 ")",
6961 			procResult.DeviceName.c_str(), DevRowIdx);
6962 	}
6963 
6964 	CheckSceneCode(DevRowIdx, devType, subType, cmnd, "", procResult.DeviceName);
6965 	procResult.DeviceRowIdx = DevRowIdx;
6966 }
6967 
decode_Security1(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)6968 void MainWorker::decode_Security1(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
6969 {
6970 	char szTmp[100];
6971 	uint8_t devType = pTypeSecurity1;
6972 	uint8_t subType = pResponse->SECURITY1.subtype;
6973 	std::string ID;
6974 	sprintf(szTmp, "%02X%02X%02X", pResponse->SECURITY1.id1, pResponse->SECURITY1.id2, pResponse->SECURITY1.id3);
6975 	ID = szTmp;
6976 	uint8_t Unit = 0;
6977 	uint8_t cmnd = pResponse->SECURITY1.status;
6978 	uint8_t SignalLevel = pResponse->SECURITY1.rssi;
6979 	uint8_t BatteryLevel = get_BateryLevel(pHardware->HwdType, false, pResponse->SECURITY1.battery_level & 0x0F);
6980 	if (
6981 		(pResponse->SECURITY1.subtype == sTypeKD101) ||
6982 		(pResponse->SECURITY1.subtype == sTypeSA30) ||
6983 		(pResponse->SECURITY1.subtype == sTypeRM174RF) ||
6984 		(pResponse->SECURITY1.subtype == sTypeDomoticzSecurity)
6985 		)
6986 	{
6987 		//KD101 & SA30 do not support battery low indication
6988 		BatteryLevel = 255;
6989 	}
6990 
6991 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, procResult.DeviceName);
6992 	if (DevRowIdx == (uint64_t)-1)
6993 		return;
6994 	CheckSceneCode(DevRowIdx, devType, subType, cmnd, "", procResult.DeviceName);
6995 
6996 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
6997 	{
6998 		WriteMessageStart();
6999 		switch (pResponse->SECURITY1.subtype)
7000 		{
7001 		case sTypeSecX10:
7002 			WriteMessage("subtype       = X10 security");
7003 			break;
7004 		case sTypeSecX10M:
7005 			WriteMessage("subtype       = X10 security motion");
7006 			break;
7007 		case sTypeSecX10R:
7008 			WriteMessage("subtype       = X10 security remote");
7009 			break;
7010 		case sTypeKD101:
7011 			WriteMessage("subtype       = KD101 smoke detector");
7012 			break;
7013 		case sTypePowercodeSensor:
7014 			WriteMessage("subtype       = Visonic PowerCode sensor - primary contact");
7015 			break;
7016 		case sTypePowercodeMotion:
7017 			WriteMessage("subtype       = Visonic PowerCode motion");
7018 			break;
7019 		case sTypeCodesecure:
7020 			WriteMessage("subtype       = Visonic CodeSecure");
7021 			break;
7022 		case sTypePowercodeAux:
7023 			WriteMessage("subtype       = Visonic PowerCode sensor - auxiliary contact");
7024 			break;
7025 		case sTypeMeiantech:
7026 			WriteMessage("subtype       = Meiantech/Atlantic/Aidebao");
7027 			break;
7028 		case sTypeSA30:
7029 			WriteMessage("subtype       = Alecto SA30 smoke detector");
7030 			break;
7031 		case sTypeDomoticzSecurity:
7032 			WriteMessage("subtype       = Security Panel");
7033 			break;
7034 		default:
7035 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->SECURITY1.packettype, pResponse->SECURITY1.subtype);
7036 			WriteMessage(szTmp);
7037 			break;
7038 		}
7039 
7040 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->SECURITY1.seqnbr);
7041 		WriteMessage(szTmp);
7042 		sprintf(szTmp, "id1-3         = %02X:%02X:%02X", pResponse->SECURITY1.id1, pResponse->SECURITY1.id2, pResponse->SECURITY1.id3);
7043 		WriteMessage(szTmp);
7044 
7045 		WriteMessage("status        = ", false);
7046 
7047 		switch (pResponse->SECURITY1.status)
7048 		{
7049 		case sStatusNormal:
7050 			WriteMessage("Normal");
7051 			break;
7052 		case sStatusNormalDelayed:
7053 			WriteMessage("Normal Delayed");
7054 			break;
7055 		case sStatusAlarm:
7056 			WriteMessage("Alarm");
7057 			break;
7058 		case sStatusAlarmDelayed:
7059 			WriteMessage("Alarm Delayed");
7060 			break;
7061 		case sStatusMotion:
7062 			WriteMessage("Motion");
7063 			break;
7064 		case sStatusNoMotion:
7065 			WriteMessage("No Motion");
7066 			break;
7067 		case sStatusPanic:
7068 			WriteMessage("Panic");
7069 			break;
7070 		case sStatusPanicOff:
7071 			WriteMessage("Panic End");
7072 			break;
7073 		case sStatusArmAway:
7074 			if (pResponse->SECURITY1.subtype == sTypeMeiantech)
7075 				WriteMessage("Group2 or ", false);
7076 			WriteMessage("Arm Away");
7077 			break;
7078 		case sStatusArmAwayDelayed:
7079 			WriteMessage("Arm Away Delayed");
7080 			break;
7081 		case sStatusArmHome:
7082 			if (pResponse->SECURITY1.subtype == sTypeMeiantech)
7083 				WriteMessage("Group3 or ", false);
7084 			WriteMessage("Arm Home");
7085 			break;
7086 		case sStatusArmHomeDelayed:
7087 			WriteMessage("Arm Home Delayed");
7088 			break;
7089 		case sStatusDisarm:
7090 			if (pResponse->SECURITY1.subtype == sTypeMeiantech)
7091 				WriteMessage("Group1 or ", false);
7092 			WriteMessage("Disarm");
7093 			break;
7094 		case sStatusLightOff:
7095 			WriteMessage("Light Off");
7096 			break;
7097 		case sStatusLightOn:
7098 			WriteMessage("Light On");
7099 			break;
7100 		case sStatusLight2Off:
7101 			WriteMessage("Light 2 Off");
7102 			break;
7103 		case sStatusLight2On:
7104 			WriteMessage("Light 2 On");
7105 			break;
7106 		case sStatusDark:
7107 			WriteMessage("Dark detected");
7108 			break;
7109 		case sStatusLight:
7110 			WriteMessage("Light Detected");
7111 			break;
7112 		case sStatusBatLow:
7113 			WriteMessage("Battery low MS10 or XX18 sensor");
7114 			break;
7115 		case sStatusPairKD101:
7116 			WriteMessage("Pair KD101");
7117 			break;
7118 		case sStatusNormalTamper:
7119 			WriteMessage("Normal + Tamper");
7120 			break;
7121 		case sStatusNormalDelayedTamper:
7122 			WriteMessage("Normal Delayed + Tamper");
7123 			break;
7124 		case sStatusAlarmTamper:
7125 			WriteMessage("Alarm + Tamper");
7126 			break;
7127 		case sStatusAlarmDelayedTamper:
7128 			WriteMessage("Alarm Delayed + Tamper");
7129 			break;
7130 		case sStatusMotionTamper:
7131 			WriteMessage("Motion + Tamper");
7132 			break;
7133 		case sStatusNoMotionTamper:
7134 			WriteMessage("No Motion + Tamper");
7135 			break;
7136 		}
7137 
7138 		if (
7139 			(pResponse->SECURITY1.subtype != sTypeKD101) &&		//KD101 & SA30 does not support battery low indication
7140 			(pResponse->SECURITY1.subtype != sTypeSA30)
7141 			)
7142 		{
7143 			if ((pResponse->SECURITY1.battery_level & 0xF) == 0)
7144 				WriteMessage("battery level = Low");
7145 			else
7146 				WriteMessage("battery level = OK");
7147 		}
7148 		sprintf(szTmp, "Signal level  = %d", pResponse->SECURITY1.rssi);
7149 		WriteMessage(szTmp);
7150 		WriteMessageEnd();
7151 	}
7152 	procResult.DeviceRowIdx = DevRowIdx;
7153 }
7154 
decode_Security2(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)7155 void MainWorker::decode_Security2(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
7156 {
7157 	char szTmp[100];
7158 	uint8_t devType = pTypeSecurity2;
7159 	uint8_t subType = pResponse->SECURITY2.subtype;
7160 	std::string ID;
7161 	sprintf(szTmp, "%02X%02X%02X%02X%02X%02X%02X%02X", pResponse->SECURITY2.id1, pResponse->SECURITY2.id2, pResponse->SECURITY2.id3, pResponse->SECURITY2.id4, pResponse->SECURITY2.id5, pResponse->SECURITY2.id6, pResponse->SECURITY2.id7, pResponse->SECURITY2.id8);
7162 	ID = szTmp;
7163 	uint8_t Unit = 0;
7164 	uint8_t cmnd = 0;// pResponse->SECURITY2.cmnd;
7165 	uint8_t SignalLevel = pResponse->SECURITY2.rssi;
7166 	uint8_t BatteryLevel = get_BateryLevel(pHardware->HwdType, false, pResponse->SECURITY2.battery_level & 0x0F);
7167 
7168 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, procResult.DeviceName);
7169 	if (DevRowIdx == (uint64_t)-1)
7170 		return;
7171 	CheckSceneCode(DevRowIdx, devType, subType, cmnd, "", procResult.DeviceName);
7172 
7173 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
7174 	{
7175 		WriteMessageStart();
7176 		switch (pResponse->SECURITY2.subtype)
7177 		{
7178 		case sTypeSec2Classic:
7179 			WriteMessage("subtype       = Keeloq Classic");
7180 			break;
7181 		default:
7182 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->SECURITY2.packettype, pResponse->SECURITY2.subtype);
7183 			WriteMessage(szTmp);
7184 			break;
7185 		}
7186 
7187 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->SECURITY2.seqnbr);
7188 		WriteMessage(szTmp);
7189 		sprintf(szTmp, "id1-8         = %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", pResponse->SECURITY2.id1, pResponse->SECURITY2.id2, pResponse->SECURITY2.id3, pResponse->SECURITY2.id4, pResponse->SECURITY2.id5, pResponse->SECURITY2.id6, pResponse->SECURITY2.id7, pResponse->SECURITY2.id8);
7190 		WriteMessage(szTmp);
7191 
7192 		if ((pResponse->SECURITY2.battery_level & 0xF) == 0)
7193 			WriteMessage("battery level = Low");
7194 		else
7195 			WriteMessage("battery level = OK");
7196 
7197 		sprintf(szTmp, "Signal level  = %d", pResponse->SECURITY2.rssi);
7198 		WriteMessage(szTmp);
7199 		WriteMessageEnd();
7200 	}
7201 	procResult.DeviceRowIdx = DevRowIdx;
7202 }
7203 
7204 //not in dbase yet
decode_Camera1(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)7205 void MainWorker::decode_Camera1(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
7206 {
7207 	WriteMessage("");
7208 
7209 	//uint8_t devType=pTypeCamera;
7210 
7211 	char szTmp[100];
7212 
7213 	switch (pResponse->CAMERA1.subtype)
7214 	{
7215 	case sTypeNinja:
7216 		WriteMessage("subtype       = X10 Ninja/Robocam");
7217 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->CAMERA1.seqnbr);
7218 		WriteMessage(szTmp);
7219 
7220 		WriteMessage("Command       = ", false);
7221 
7222 		switch (pResponse->CAMERA1.cmnd)
7223 		{
7224 		case camera_sLeft:
7225 			WriteMessage("Left");
7226 			break;
7227 		case camera_sRight:
7228 			WriteMessage("Right");
7229 			break;
7230 		case camera_sUp:
7231 			WriteMessage("Up");
7232 			break;
7233 		case camera_sDown:
7234 			WriteMessage("Down");
7235 			break;
7236 		case camera_sPosition1:
7237 			WriteMessage("Position 1");
7238 			break;
7239 		case camera_sProgramPosition1:
7240 			WriteMessage("Position 1 program");
7241 			break;
7242 		case camera_sPosition2:
7243 			WriteMessage("Position 2");
7244 			break;
7245 		case camera_sProgramPosition2:
7246 			WriteMessage("Position 2 program");
7247 			break;
7248 		case camera_sPosition3:
7249 			WriteMessage("Position 3");
7250 			break;
7251 		case camera_sProgramPosition3:
7252 			WriteMessage("Position 3 program");
7253 			break;
7254 		case camera_sPosition4:
7255 			WriteMessage("Position 4");
7256 			break;
7257 		case camera_sProgramPosition4:
7258 			WriteMessage("Position 4 program");
7259 			break;
7260 		case camera_sCenter:
7261 			WriteMessage("Center");
7262 			break;
7263 		case camera_sProgramCenterPosition:
7264 			WriteMessage("Center program");
7265 			break;
7266 		case camera_sSweep:
7267 			WriteMessage("Sweep");
7268 			break;
7269 		case camera_sProgramSweep:
7270 			WriteMessage("Sweep program");
7271 			break;
7272 		default:
7273 			WriteMessage("UNKNOWN");
7274 			break;
7275 		}
7276 		sprintf(szTmp, "Housecode     = %d", pResponse->CAMERA1.housecode);
7277 		WriteMessage(szTmp);
7278 		break;
7279 	default:
7280 		sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->CAMERA1.packettype, pResponse->CAMERA1.subtype);
7281 		WriteMessage(szTmp);
7282 		break;
7283 	}
7284 	sprintf(szTmp, "Signal level  = %d", pResponse->CAMERA1.rssi);
7285 	WriteMessage(szTmp);
7286 	procResult.DeviceRowIdx = -1;
7287 }
7288 
decode_Remote(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)7289 void MainWorker::decode_Remote(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
7290 {
7291 	char szTmp[100];
7292 	uint8_t devType = pTypeRemote;
7293 	uint8_t subType = pResponse->REMOTE.subtype;
7294 	sprintf(szTmp, "%d", pResponse->REMOTE.id);
7295 	std::string ID = szTmp;
7296 	uint8_t Unit = pResponse->REMOTE.cmnd;
7297 	uint8_t cmnd = light2_sOn;
7298 	uint8_t SignalLevel = pResponse->REMOTE.rssi;
7299 
7300 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, -1, cmnd, procResult.DeviceName);
7301 	if (DevRowIdx == (uint64_t)-1)
7302 		return;
7303 	CheckSceneCode(DevRowIdx, devType, subType, cmnd, "", procResult.DeviceName);
7304 
7305 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
7306 	{
7307 		WriteMessageStart();
7308 		switch (pResponse->REMOTE.subtype)
7309 		{
7310 		case sTypeATI:
7311 			WriteMessage("subtype       = ATI Remote Wonder");
7312 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->REMOTE.seqnbr);
7313 			WriteMessage(szTmp);
7314 			sprintf(szTmp, "ID            = %d", pResponse->REMOTE.id);
7315 			WriteMessage(szTmp);
7316 			switch (pResponse->REMOTE.cmnd)
7317 			{
7318 			case 0x0:
7319 				WriteMessage("Command       = A");
7320 				break;
7321 			case 0x1:
7322 				WriteMessage("Command       = B");
7323 				break;
7324 			case 0x2:
7325 				WriteMessage("Command       = power");
7326 				break;
7327 			case 0x3:
7328 				WriteMessage("Command       = TV");
7329 				break;
7330 			case 0x4:
7331 				WriteMessage("Command       = DVD");
7332 				break;
7333 			case 0x5:
7334 				WriteMessage("Command       = ?");
7335 				break;
7336 			case 0x6:
7337 				WriteMessage("Command       = Guide");
7338 				break;
7339 			case 0x7:
7340 				WriteMessage("Command       = Drag");
7341 				break;
7342 			case 0x8:
7343 				WriteMessage("Command       = VOL+");
7344 				break;
7345 			case 0x9:
7346 				WriteMessage("Command       = VOL-");
7347 				break;
7348 			case 0xA:
7349 				WriteMessage("Command       = MUTE");
7350 				break;
7351 			case 0xB:
7352 				WriteMessage("Command       = CHAN+");
7353 				break;
7354 			case 0xC:
7355 				WriteMessage("Command       = CHAN-");
7356 				break;
7357 			case 0xD:
7358 				WriteMessage("Command       = 1");
7359 				break;
7360 			case 0xE:
7361 				WriteMessage("Command       = 2");
7362 				break;
7363 			case 0xF:
7364 				WriteMessage("Command       = 3");
7365 				break;
7366 			case 0x10:
7367 				WriteMessage("Command       = 4");
7368 				break;
7369 			case 0x11:
7370 				WriteMessage("Command       = 5");
7371 				break;
7372 			case 0x12:
7373 				WriteMessage("Command       = 6");
7374 				break;
7375 			case 0x13:
7376 				WriteMessage("Command       = 7");
7377 				break;
7378 			case 0x14:
7379 				WriteMessage("Command       = 8");
7380 				break;
7381 			case 0x15:
7382 				WriteMessage("Command       = 9");
7383 				break;
7384 			case 0x16:
7385 				WriteMessage("Command       = txt");
7386 				break;
7387 			case 0x17:
7388 				WriteMessage("Command       = 0");
7389 				break;
7390 			case 0x18:
7391 				WriteMessage("Command       = snapshot ESC");
7392 				break;
7393 			case 0x19:
7394 				WriteMessage("Command       = C");
7395 				break;
7396 			case 0x1A:
7397 				WriteMessage("Command       = ^");
7398 				break;
7399 			case 0x1B:
7400 				WriteMessage("Command       = D");
7401 				break;
7402 			case 0x1C:
7403 				WriteMessage("Command       = TV/RADIO");
7404 				break;
7405 			case 0x1D:
7406 				WriteMessage("Command       = <");
7407 				break;
7408 			case 0x1E:
7409 				WriteMessage("Command       = OK");
7410 				break;
7411 			case 0x1F:
7412 				WriteMessage("Command       = >");
7413 				break;
7414 			case 0x20:
7415 				WriteMessage("Command       = <-");
7416 				break;
7417 			case 0x21:
7418 				WriteMessage("Command       = E");
7419 				break;
7420 			case 0x22:
7421 				WriteMessage("Command       = v");
7422 				break;
7423 			case 0x23:
7424 				WriteMessage("Command       = F");
7425 				break;
7426 			case 0x24:
7427 				WriteMessage("Command       = Rewind");
7428 				break;
7429 			case 0x25:
7430 				WriteMessage("Command       = Play");
7431 				break;
7432 			case 0x26:
7433 				WriteMessage("Command       = Fast forward");
7434 				break;
7435 			case 0x27:
7436 				WriteMessage("Command       = Record");
7437 				break;
7438 			case 0x28:
7439 				WriteMessage("Command       = Stop");
7440 				break;
7441 			case 0x29:
7442 				WriteMessage("Command       = Pause");
7443 				break;
7444 			case 0x2C:
7445 				WriteMessage("Command       = TV");
7446 				break;
7447 			case 0x2D:
7448 				WriteMessage("Command       = VCR");
7449 				break;
7450 			case 0x2E:
7451 				WriteMessage("Command       = RADIO");
7452 				break;
7453 			case 0x2F:
7454 				WriteMessage("Command       = TV Preview");
7455 				break;
7456 			case 0x30:
7457 				WriteMessage("Command       = Channel list");
7458 				break;
7459 			case 0x31:
7460 				WriteMessage("Command       = Video Desktop");
7461 				break;
7462 			case 0x32:
7463 				WriteMessage("Command       = red");
7464 				break;
7465 			case 0x33:
7466 				WriteMessage("Command       = green");
7467 				break;
7468 			case 0x34:
7469 				WriteMessage("Command       = yellow");
7470 				break;
7471 			case 0x35:
7472 				WriteMessage("Command       = blue");
7473 				break;
7474 			case 0x36:
7475 				WriteMessage("Command       = rename TAB");
7476 				break;
7477 			case 0x37:
7478 				WriteMessage("Command       = Acquire image");
7479 				break;
7480 			case 0x38:
7481 				WriteMessage("Command       = edit image");
7482 				break;
7483 			case 0x39:
7484 				WriteMessage("Command       = Full screen");
7485 				break;
7486 			case 0x3A:
7487 				WriteMessage("Command       = DVD Audio");
7488 				break;
7489 			case 0x70:
7490 				WriteMessage("Command       = Cursor-left");
7491 				break;
7492 			case 0x71:
7493 				WriteMessage("Command       = Cursor-right");
7494 				break;
7495 			case 0x72:
7496 				WriteMessage("Command       = Cursor-up");
7497 				break;
7498 			case 0x73:
7499 				WriteMessage("Command       = Cursor-down");
7500 				break;
7501 			case 0x74:
7502 				WriteMessage("Command       = Cursor-up-left");
7503 				break;
7504 			case 0x75:
7505 				WriteMessage("Command       = Cursor-up-right");
7506 				break;
7507 			case 0x76:
7508 				WriteMessage("Command       = Cursor-down-right");
7509 				break;
7510 			case 0x77:
7511 				WriteMessage("Command       = Cursor-down-left");
7512 				break;
7513 			case 0x78:
7514 				WriteMessage("Command       = V");
7515 				break;
7516 			case 0x79:
7517 				WriteMessage("Command       = V-End");
7518 				break;
7519 			case 0x7C:
7520 				WriteMessage("Command       = X");
7521 				break;
7522 			case 0x7D:
7523 				WriteMessage("Command       = X-End");
7524 				break;
7525 			default:
7526 				WriteMessage("Command       = unknown");
7527 				break;
7528 			}
7529 			break;
7530 		case sTypeATIplus:
7531 			WriteMessage("subtype       = ATI Remote Wonder Plus");
7532 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->REMOTE.seqnbr);
7533 			WriteMessage(szTmp);
7534 			sprintf(szTmp, "ID            = %d", pResponse->REMOTE.id);
7535 			WriteMessage(szTmp);
7536 
7537 			WriteMessage("Command       = ", false);
7538 			switch (pResponse->REMOTE.cmnd)
7539 			{
7540 			case 0x0:
7541 				WriteMessage("A", false);
7542 				break;
7543 			case 0x1:
7544 				WriteMessage("B", false);
7545 				break;
7546 			case 0x2:
7547 				WriteMessage("power", false);
7548 				break;
7549 			case 0x3:
7550 				WriteMessage("TV", false);
7551 				break;
7552 			case 0x4:
7553 				WriteMessage("DVD", false);
7554 				break;
7555 			case 0x5:
7556 				WriteMessage("?", false);
7557 				break;
7558 			case 0x6:
7559 				WriteMessage("Guide", false);
7560 				break;
7561 			case 0x7:
7562 				WriteMessage("Drag", false);
7563 				break;
7564 			case 0x8:
7565 				WriteMessage("VOL+", false);
7566 				break;
7567 			case 0x9:
7568 				WriteMessage("VOL-", false);
7569 				break;
7570 			case 0xA:
7571 				WriteMessage("MUTE", false);
7572 				break;
7573 			case 0xB:
7574 				WriteMessage("CHAN+", false);
7575 				break;
7576 			case 0xC:
7577 				WriteMessage("CHAN-", false);
7578 				break;
7579 			case 0xD:
7580 				WriteMessage("1", false);
7581 				break;
7582 			case 0xE:
7583 				WriteMessage("2", false);
7584 				break;
7585 			case 0xF:
7586 				WriteMessage("3", false);
7587 				break;
7588 			case 0x10:
7589 				WriteMessage("4", false);
7590 				break;
7591 			case 0x11:
7592 				WriteMessage("5", false);
7593 				break;
7594 			case 0x12:
7595 				WriteMessage("6", false);
7596 				break;
7597 			case 0x13:
7598 				WriteMessage("7", false);
7599 				break;
7600 			case 0x14:
7601 				WriteMessage("8", false);
7602 				break;
7603 			case 0x15:
7604 				WriteMessage("9", false);
7605 				break;
7606 			case 0x16:
7607 				WriteMessage("txt", false);
7608 				break;
7609 			case 0x17:
7610 				WriteMessage("0", false);
7611 				break;
7612 			case 0x18:
7613 				WriteMessage("Open Setup Menu", false);
7614 				break;
7615 			case 0x19:
7616 				WriteMessage("C", false);
7617 				break;
7618 			case 0x1A:
7619 				WriteMessage("^", false);
7620 				break;
7621 			case 0x1B:
7622 				WriteMessage("D", false);
7623 				break;
7624 			case 0x1C:
7625 				WriteMessage("FM", false);
7626 				break;
7627 			case 0x1D:
7628 				WriteMessage("<", false);
7629 				break;
7630 			case 0x1E:
7631 				WriteMessage("OK", false);
7632 				break;
7633 			case 0x1F:
7634 				WriteMessage(">", false);
7635 				break;
7636 			case 0x20:
7637 				WriteMessage("Max/Restore window", false);
7638 				break;
7639 			case 0x21:
7640 				WriteMessage("E", false);
7641 				break;
7642 			case 0x22:
7643 				WriteMessage("v", false);
7644 				break;
7645 			case 0x23:
7646 				WriteMessage("F", false);
7647 				break;
7648 			case 0x24:
7649 				WriteMessage("Rewind", false);
7650 				break;
7651 			case 0x25:
7652 				WriteMessage("Play", false);
7653 				break;
7654 			case 0x26:
7655 				WriteMessage("Fast forward", false);
7656 				break;
7657 			case 0x27:
7658 				WriteMessage("Record", false);
7659 				break;
7660 			case 0x28:
7661 				WriteMessage("Stop", false);
7662 				break;
7663 			case 0x29:
7664 				WriteMessage("Pause", false);
7665 				break;
7666 			case 0x2A:
7667 				WriteMessage("TV2", false);
7668 				break;
7669 			case 0x2B:
7670 				WriteMessage("Clock", false);
7671 				break;
7672 			case 0x2C:
7673 				WriteMessage("i", false);
7674 				break;
7675 			case 0x2D:
7676 				WriteMessage("ATI", false);
7677 				break;
7678 			case 0x2E:
7679 				WriteMessage("RADIO", false);
7680 				break;
7681 			case 0x2F:
7682 				WriteMessage("TV Preview", false);
7683 				break;
7684 			case 0x30:
7685 				WriteMessage("Channel list", false);
7686 				break;
7687 			case 0x31:
7688 				WriteMessage("Video Desktop", false);
7689 				break;
7690 			case 0x32:
7691 				WriteMessage("red", false);
7692 				break;
7693 			case 0x33:
7694 				WriteMessage("green", false);
7695 				break;
7696 			case 0x34:
7697 				WriteMessage("yellow", false);
7698 				break;
7699 			case 0x35:
7700 				WriteMessage("blue", false);
7701 				break;
7702 			case 0x36:
7703 				WriteMessage("rename TAB", false);
7704 				break;
7705 			case 0x37:
7706 				WriteMessage("Acquire image", false);
7707 				break;
7708 			case 0x38:
7709 				WriteMessage("edit image", false);
7710 				break;
7711 			case 0x39:
7712 				WriteMessage("Full screen", false);
7713 				break;
7714 			case 0x3A:
7715 				WriteMessage("DVD Audio", false);
7716 				break;
7717 			case 0x70:
7718 				WriteMessage("Cursor-left", false);
7719 				break;
7720 			case 0x71:
7721 				WriteMessage("Cursor-right", false);
7722 				break;
7723 			case 0x72:
7724 				WriteMessage("Cursor-up", false);
7725 				break;
7726 			case 0x73:
7727 				WriteMessage("Cursor-down", false);
7728 				break;
7729 			case 0x74:
7730 				WriteMessage("Cursor-up-left", false);
7731 				break;
7732 			case 0x75:
7733 				WriteMessage("Cursor-up-right", false);
7734 				break;
7735 			case 0x76:
7736 				WriteMessage("Cursor-down-right", false);
7737 				break;
7738 			case 0x77:
7739 				WriteMessage("Cursor-down-left", false);
7740 				break;
7741 			case 0x78:
7742 				WriteMessage("Left Mouse Button", false);
7743 				break;
7744 			case 0x79:
7745 				WriteMessage("V-End", false);
7746 				break;
7747 			case 0x7C:
7748 				WriteMessage("Right Mouse Button", false);
7749 				break;
7750 			case 0x7D:
7751 				WriteMessage("X-End", false);
7752 				break;
7753 			default:
7754 				WriteMessage("unknown", false);
7755 				break;
7756 			}
7757 			if ((pResponse->REMOTE.toggle & 1) == 1)
7758 				WriteMessage("  (button press = odd)");
7759 			else
7760 				WriteMessage("  (button press = even)");
7761 			break;
7762 		case sTypeATIrw2:
7763 			WriteMessage("subtype       = ATI Remote Wonder II");
7764 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->REMOTE.seqnbr);
7765 			WriteMessage(szTmp);
7766 			sprintf(szTmp, "ID            = %d", pResponse->REMOTE.id);
7767 			WriteMessage(szTmp);
7768 			WriteMessage("Command type  = ", false);
7769 
7770 			switch (pResponse->REMOTE.cmndtype & 0x0E)
7771 			{
7772 			case 0x0:
7773 				WriteMessage("PC");
7774 				break;
7775 			case 0x2:
7776 				WriteMessage("AUX1");
7777 				break;
7778 			case 0x4:
7779 				WriteMessage("AUX2");
7780 				break;
7781 			case 0x6:
7782 				WriteMessage("AUX3");
7783 				break;
7784 			case 0x8:
7785 				WriteMessage("AUX4");
7786 				break;
7787 			default:
7788 				WriteMessage("unknown");
7789 				break;
7790 			}
7791 			WriteMessage("Command       = ", false);
7792 			switch (pResponse->REMOTE.cmnd)
7793 			{
7794 			case 0x0:
7795 				WriteMessage("A", false);
7796 				break;
7797 			case 0x1:
7798 				WriteMessage("B", false);
7799 				break;
7800 			case 0x2:
7801 				WriteMessage("power", false);
7802 				break;
7803 			case 0x3:
7804 				WriteMessage("TV", false);
7805 				break;
7806 			case 0x4:
7807 				WriteMessage("DVD", false);
7808 				break;
7809 			case 0x5:
7810 				WriteMessage("?", false);
7811 				break;
7812 			case 0x7:
7813 				WriteMessage("Drag", false);
7814 				break;
7815 			case 0x8:
7816 				WriteMessage("VOL+", false);
7817 				break;
7818 			case 0x9:
7819 				WriteMessage("VOL-", false);
7820 				break;
7821 			case 0xA:
7822 				WriteMessage("MUTE", false);
7823 				break;
7824 			case 0xB:
7825 				WriteMessage("CHAN+", false);
7826 				break;
7827 			case 0xC:
7828 				WriteMessage("CHAN-", false);
7829 				break;
7830 			case 0xD:
7831 				WriteMessage("1", false);
7832 				break;
7833 			case 0xE:
7834 				WriteMessage("2", false);
7835 				break;
7836 			case 0xF:
7837 				WriteMessage("3", false);
7838 				break;
7839 			case 0x10:
7840 				WriteMessage("4", false);
7841 				break;
7842 			case 0x11:
7843 				WriteMessage("5", false);
7844 				break;
7845 			case 0x12:
7846 				WriteMessage("6", false);
7847 				break;
7848 			case 0x13:
7849 				WriteMessage("7", false);
7850 				break;
7851 			case 0x14:
7852 				WriteMessage("8", false);
7853 				break;
7854 			case 0x15:
7855 				WriteMessage("9", false);
7856 				break;
7857 			case 0x16:
7858 				WriteMessage("txt", false);
7859 				break;
7860 			case 0x17:
7861 				WriteMessage("0", false);
7862 				break;
7863 			case 0x18:
7864 				WriteMessage("Open Setup Menu", false);
7865 				break;
7866 			case 0x19:
7867 				WriteMessage("C", false);
7868 				break;
7869 			case 0x1A:
7870 				WriteMessage("^", false);
7871 				break;
7872 			case 0x1B:
7873 				WriteMessage("D", false);
7874 				break;
7875 			case 0x1C:
7876 				WriteMessage("TV/RADIO", false);
7877 				break;
7878 			case 0x1D:
7879 				WriteMessage("<", false);
7880 				break;
7881 			case 0x1E:
7882 				WriteMessage("OK", false);
7883 				break;
7884 			case 0x1F:
7885 				WriteMessage(">", false);
7886 				break;
7887 			case 0x20:
7888 				WriteMessage("Max/Restore window", false);
7889 				break;
7890 			case 0x21:
7891 				WriteMessage("E", false);
7892 				break;
7893 			case 0x22:
7894 				WriteMessage("v", false);
7895 				break;
7896 			case 0x23:
7897 				WriteMessage("F", false);
7898 				break;
7899 			case 0x24:
7900 				WriteMessage("Rewind", false);
7901 				break;
7902 			case 0x25:
7903 				WriteMessage("Play", false);
7904 				break;
7905 			case 0x26:
7906 				WriteMessage("Fast forward", false);
7907 				break;
7908 			case 0x27:
7909 				WriteMessage("Record", false);
7910 				break;
7911 			case 0x28:
7912 				WriteMessage("Stop", false);
7913 				break;
7914 			case 0x29:
7915 				WriteMessage("Pause", false);
7916 				break;
7917 			case 0x2C:
7918 				WriteMessage("i", false);
7919 				break;
7920 			case 0x2D:
7921 				WriteMessage("ATI", false);
7922 				break;
7923 			case 0x3B:
7924 				WriteMessage("PC", false);
7925 				break;
7926 			case 0x3C:
7927 				WriteMessage("AUX1", false);
7928 				break;
7929 			case 0x3D:
7930 				WriteMessage("AUX2", false);
7931 				break;
7932 			case 0x3E:
7933 				WriteMessage("AUX3", false);
7934 				break;
7935 			case 0x3F:
7936 				WriteMessage("AUX4", false);
7937 				break;
7938 			case 0x70:
7939 				WriteMessage("Cursor-left", false);
7940 				break;
7941 			case 0x71:
7942 				WriteMessage("Cursor-right", false);
7943 				break;
7944 			case 0x72:
7945 				WriteMessage("Cursor-up", false);
7946 				break;
7947 			case 0x73:
7948 				WriteMessage("Cursor-down", false);
7949 				break;
7950 			case 0x74:
7951 				WriteMessage("Cursor-up-left", false);
7952 				break;
7953 			case 0x75:
7954 				WriteMessage("Cursor-up-right", false);
7955 				break;
7956 			case 0x76:
7957 				WriteMessage("Cursor-down-right", false);
7958 				break;
7959 			case 0x77:
7960 				WriteMessage("Cursor-down-left", false);
7961 				break;
7962 			case 0x78:
7963 				WriteMessage("Left Mouse Button", false);
7964 				break;
7965 			case 0x7C:
7966 				WriteMessage("Right Mouse Button", false);
7967 				break;
7968 			default:
7969 				WriteMessage("unknown", false);
7970 				break;
7971 			}
7972 			if ((pResponse->REMOTE.toggle & 1) == 1)
7973 				WriteMessage("  (button press = odd)");
7974 			else
7975 				WriteMessage("  (button press = even)");
7976 			break;
7977 		case sTypeMedion:
7978 			WriteMessage("subtype       = Medion Remote");
7979 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->REMOTE.seqnbr);
7980 			WriteMessage(szTmp);
7981 			sprintf(szTmp, "ID            = %d", pResponse->REMOTE.id);
7982 			WriteMessage(szTmp);
7983 
7984 			WriteMessage("Command       = ", false);
7985 
7986 			switch (pResponse->REMOTE.cmnd)
7987 			{
7988 			case 0x0:
7989 				WriteMessage("Mute");
7990 				break;
7991 			case 0x1:
7992 				WriteMessage("B");
7993 				break;
7994 			case 0x2:
7995 				WriteMessage("power");
7996 				break;
7997 			case 0x3:
7998 				WriteMessage("TV");
7999 				break;
8000 			case 0x4:
8001 				WriteMessage("DVD");
8002 				break;
8003 			case 0x5:
8004 				WriteMessage("Photo");
8005 				break;
8006 			case 0x6:
8007 				WriteMessage("Music");
8008 				break;
8009 			case 0x7:
8010 				WriteMessage("Drag");
8011 				break;
8012 			case 0x8:
8013 				WriteMessage("VOL-");
8014 				break;
8015 			case 0x9:
8016 				WriteMessage("VOL+");
8017 				break;
8018 			case 0xA:
8019 				WriteMessage("MUTE");
8020 				break;
8021 			case 0xB:
8022 				WriteMessage("CHAN+");
8023 				break;
8024 			case 0xC:
8025 				WriteMessage("CHAN-");
8026 				break;
8027 			case 0xD:
8028 				WriteMessage("1");
8029 				break;
8030 			case 0xE:
8031 				WriteMessage("2");
8032 				break;
8033 			case 0xF:
8034 				WriteMessage("3");
8035 				break;
8036 			case 0x10:
8037 				WriteMessage("4");
8038 				break;
8039 			case 0x11:
8040 				WriteMessage("5");
8041 				break;
8042 			case 0x12:
8043 				WriteMessage("6");
8044 				break;
8045 			case 0x13:
8046 				WriteMessage("7");
8047 				break;
8048 			case 0x14:
8049 				WriteMessage("8");
8050 				break;
8051 			case 0x15:
8052 				WriteMessage("9");
8053 				break;
8054 			case 0x16:
8055 				WriteMessage("txt");
8056 				break;
8057 			case 0x17:
8058 				WriteMessage("0");
8059 				break;
8060 			case 0x18:
8061 				WriteMessage("snapshot ESC");
8062 				break;
8063 			case 0x19:
8064 				WriteMessage("DVD MENU");
8065 				break;
8066 			case 0x1A:
8067 				WriteMessage("^");
8068 				break;
8069 			case 0x1B:
8070 				WriteMessage("Setup");
8071 				break;
8072 			case 0x1C:
8073 				WriteMessage("TV/RADIO");
8074 				break;
8075 			case 0x1D:
8076 				WriteMessage("<");
8077 				break;
8078 			case 0x1E:
8079 				WriteMessage("OK");
8080 				break;
8081 			case 0x1F:
8082 				WriteMessage(">");
8083 				break;
8084 			case 0x20:
8085 				WriteMessage("<-");
8086 				break;
8087 			case 0x21:
8088 				WriteMessage("E");
8089 				break;
8090 			case 0x22:
8091 				WriteMessage("v");
8092 				break;
8093 			case 0x23:
8094 				WriteMessage("F");
8095 				break;
8096 			case 0x24:
8097 				WriteMessage("Rewind");
8098 				break;
8099 			case 0x25:
8100 				WriteMessage("Play");
8101 				break;
8102 			case 0x26:
8103 				WriteMessage("Fast forward");
8104 				break;
8105 			case 0x27:
8106 				WriteMessage("Record");
8107 				break;
8108 			case 0x28:
8109 				WriteMessage("Stop");
8110 				break;
8111 			case 0x29:
8112 				WriteMessage("Pause");
8113 				break;
8114 			case 0x2C:
8115 				WriteMessage("TV");
8116 				break;
8117 			case 0x2D:
8118 				WriteMessage("VCR");
8119 				break;
8120 			case 0x2E:
8121 				WriteMessage("RADIO");
8122 				break;
8123 			case 0x2F:
8124 				WriteMessage("TV Preview");
8125 				break;
8126 			case 0x30:
8127 				WriteMessage("Channel list");
8128 				break;
8129 			case 0x31:
8130 				WriteMessage("Video Desktop");
8131 				break;
8132 			case 0x32:
8133 				WriteMessage("red");
8134 				break;
8135 			case 0x33:
8136 				WriteMessage("green");
8137 				break;
8138 			case 0x34:
8139 				WriteMessage("yellow");
8140 				break;
8141 			case 0x35:
8142 				WriteMessage("blue");
8143 				break;
8144 			case 0x36:
8145 				WriteMessage("rename TAB");
8146 				break;
8147 			case 0x37:
8148 				WriteMessage("Acquire image");
8149 				break;
8150 			case 0x38:
8151 				WriteMessage("edit image");
8152 				break;
8153 			case 0x39:
8154 				WriteMessage("Full screen");
8155 				break;
8156 			case 0x3A:
8157 				WriteMessage("DVD Audio");
8158 				break;
8159 			case 0x70:
8160 				WriteMessage("Cursor-left");
8161 				break;
8162 			case 0x71:
8163 				WriteMessage("Cursor-right");
8164 				break;
8165 			case 0x72:
8166 				WriteMessage("Cursor-up");
8167 				break;
8168 			case 0x73:
8169 				WriteMessage("Cursor-down");
8170 				break;
8171 			case 0x74:
8172 				WriteMessage("Cursor-up-left");
8173 				break;
8174 			case 0x75:
8175 				WriteMessage("Cursor-up-right");
8176 				break;
8177 			case 0x76:
8178 				WriteMessage("Cursor-down-right");
8179 				break;
8180 			case 0x77:
8181 				WriteMessage("Cursor-down-left");
8182 				break;
8183 			case 0x78:
8184 				WriteMessage("V");
8185 				break;
8186 			case 0x79:
8187 				WriteMessage("V-End");
8188 				break;
8189 			case 0x7C:
8190 				WriteMessage("X");
8191 				break;
8192 			case 0x7D:
8193 				WriteMessage("X-End");
8194 				break;
8195 			default:
8196 				WriteMessage("unknown");
8197 				break;
8198 			}
8199 			break;
8200 		case sTypePCremote:
8201 			WriteMessage("subtype       = PC Remote");
8202 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->REMOTE.seqnbr);
8203 			WriteMessage(szTmp);
8204 			sprintf(szTmp, "ID            = %d", pResponse->REMOTE.id);
8205 			WriteMessage(szTmp);
8206 			WriteMessage("Command       = ", false);
8207 			switch (pResponse->REMOTE.cmnd)
8208 			{
8209 			case 0x2:
8210 				WriteMessage("0");
8211 				break;
8212 			case 0x82:
8213 				WriteMessage("1");
8214 				break;
8215 			case 0xD1:
8216 				WriteMessage("MP3");
8217 				break;
8218 			case 0x42:
8219 				WriteMessage("2");
8220 				break;
8221 			case 0xD2:
8222 				WriteMessage("DVD");
8223 				break;
8224 			case 0xC2:
8225 				WriteMessage("3");
8226 				break;
8227 			case 0xD3:
8228 				WriteMessage("CD");
8229 				break;
8230 			case 0x22:
8231 				WriteMessage("4");
8232 				break;
8233 			case 0xD4:
8234 				WriteMessage("PC or SHIFT-4");
8235 				break;
8236 			case 0xA2:
8237 				WriteMessage("5");
8238 				break;
8239 			case 0xD5:
8240 				WriteMessage("SHIFT-5");
8241 				break;
8242 			case 0x62:
8243 				WriteMessage("6");
8244 				break;
8245 			case 0xE2:
8246 				WriteMessage("7");
8247 				break;
8248 			case 0x12:
8249 				WriteMessage("8");
8250 				break;
8251 			case 0x92:
8252 				WriteMessage("9");
8253 				break;
8254 			case 0xC0:
8255 				WriteMessage("CH-");
8256 				break;
8257 			case 0x40:
8258 				WriteMessage("CH+");
8259 				break;
8260 			case 0xE0:
8261 				WriteMessage("VOL-");
8262 				break;
8263 			case 0x60:
8264 				WriteMessage("VOL+");
8265 				break;
8266 			case 0xA0:
8267 				WriteMessage("MUTE");
8268 				break;
8269 			case 0x3A:
8270 				WriteMessage("INFO");
8271 				break;
8272 			case 0x38:
8273 				WriteMessage("REW");
8274 				break;
8275 			case 0xB8:
8276 				WriteMessage("FF");
8277 				break;
8278 			case 0xB0:
8279 				WriteMessage("PLAY");
8280 				break;
8281 			case 0x64:
8282 				WriteMessage("PAUSE");
8283 				break;
8284 			case 0x63:
8285 				WriteMessage("STOP");
8286 				break;
8287 			case 0xB6:
8288 				WriteMessage("MENU");
8289 				break;
8290 			case 0xFF:
8291 				WriteMessage("REC");
8292 				break;
8293 			case 0xC9:
8294 				WriteMessage("EXIT");
8295 				break;
8296 			case 0xD8:
8297 				WriteMessage("TEXT");
8298 				break;
8299 			case 0xD9:
8300 				WriteMessage("SHIFT-TEXT");
8301 				break;
8302 			case 0xF2:
8303 				WriteMessage("TELETEXT");
8304 				break;
8305 			case 0xD7:
8306 				WriteMessage("SHIFT-TELETEXT");
8307 				break;
8308 			case 0xBA:
8309 				WriteMessage("A+B");
8310 				break;
8311 			case 0x52:
8312 				WriteMessage("ENT");
8313 				break;
8314 			case 0xD6:
8315 				WriteMessage("SHIFT-ENT");
8316 				break;
8317 			case 0x70:
8318 				WriteMessage("Cursor-left");
8319 				break;
8320 			case 0x71:
8321 				WriteMessage("Cursor-right");
8322 				break;
8323 			case 0x72:
8324 				WriteMessage("Cursor-up");
8325 				break;
8326 			case 0x73:
8327 				WriteMessage("Cursor-down");
8328 				break;
8329 			case 0x74:
8330 				WriteMessage("Cursor-up-left");
8331 				break;
8332 			case 0x75:
8333 				WriteMessage("Cursor-up-right");
8334 				break;
8335 			case 0x76:
8336 				WriteMessage("Cursor-down-right");
8337 				break;
8338 			case 0x77:
8339 				WriteMessage("Cursor-down-left");
8340 				break;
8341 			case 0x78:
8342 				WriteMessage("Left mouse");
8343 				break;
8344 			case 0x79:
8345 				WriteMessage("Left mouse-End");
8346 				break;
8347 			case 0x7B:
8348 				WriteMessage("Drag");
8349 				break;
8350 			case 0x7C:
8351 				WriteMessage("Right mouse");
8352 				break;
8353 			case 0x7D:
8354 				WriteMessage("Right mouse-End");
8355 				break;
8356 			default:
8357 				WriteMessage("unknown");
8358 				break;
8359 			}
8360 			break;
8361 		default:
8362 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->REMOTE.packettype, pResponse->REMOTE.subtype);
8363 			WriteMessage(szTmp);
8364 			break;
8365 		}
8366 		sprintf(szTmp, "Signal level  = %d", pResponse->REMOTE.rssi);
8367 		WriteMessage(szTmp);
8368 		WriteMessageEnd();
8369 	}
8370 	procResult.DeviceRowIdx = DevRowIdx;
8371 }
8372 
decode_Thermostat1(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)8373 void MainWorker::decode_Thermostat1(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
8374 {
8375 	char szTmp[100];
8376 	uint8_t devType = pTypeThermostat1;
8377 	uint8_t subType = pResponse->THERMOSTAT1.subtype;
8378 	std::string ID;
8379 	sprintf(szTmp, "%d", (pResponse->THERMOSTAT1.id1 * 256) + pResponse->THERMOSTAT1.id2);
8380 	ID = szTmp;
8381 	uint8_t Unit = 0;
8382 	uint8_t cmnd = 0;
8383 	uint8_t SignalLevel = pResponse->THERMOSTAT1.rssi;
8384 	uint8_t BatteryLevel = 255;
8385 
8386 	uint8_t temp = pResponse->THERMOSTAT1.temperature;
8387 	uint8_t set_point = pResponse->THERMOSTAT1.set_point;
8388 	uint8_t mode = (pResponse->THERMOSTAT1.mode & 0x80);
8389 	uint8_t status = (pResponse->THERMOSTAT1.status & 0x03);
8390 
8391 	sprintf(szTmp, "%d;%d;%d;%d", temp, set_point, mode, status);
8392 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
8393 	if (DevRowIdx == (uint64_t)-1)
8394 		return;
8395 
8396 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
8397 	{
8398 		WriteMessageStart();
8399 		switch (pResponse->THERMOSTAT1.subtype)
8400 		{
8401 		case sTypeDigimax:
8402 			WriteMessage("subtype       = Digimax");
8403 			break;
8404 		case sTypeDigimaxShort:
8405 			WriteMessage("subtype       = Digimax with short format");
8406 			break;
8407 		default:
8408 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->THERMOSTAT1.packettype, pResponse->THERMOSTAT1.subtype);
8409 			WriteMessage(szTmp);
8410 			break;
8411 		}
8412 
8413 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->THERMOSTAT1.seqnbr);
8414 		WriteMessage(szTmp);
8415 		sprintf(szTmp, "ID            = %d", (pResponse->THERMOSTAT1.id1 * 256) + pResponse->THERMOSTAT1.id2);
8416 		WriteMessage(szTmp);
8417 		sprintf(szTmp, "Temperature   = %d C", pResponse->THERMOSTAT1.temperature);
8418 		WriteMessage(szTmp);
8419 
8420 		if (pResponse->THERMOSTAT1.subtype == sTypeDigimax)
8421 		{
8422 			sprintf(szTmp, "Set           = %d C", pResponse->THERMOSTAT1.set_point);
8423 			WriteMessage(szTmp);
8424 
8425 			if ((pResponse->THERMOSTAT1.mode & 0x80) == 0)
8426 				WriteMessage("Mode          = heating");
8427 			else
8428 				WriteMessage("Mode          = Cooling");
8429 
8430 			switch (pResponse->THERMOSTAT1.status & 0x03)
8431 			{
8432 			case 0:
8433 				WriteMessage("Status        = no status available");
8434 				break;
8435 			case 1:
8436 				WriteMessage("Status        = demand");
8437 				break;
8438 			case 2:
8439 				WriteMessage("Status        = no demand");
8440 				break;
8441 			case 3:
8442 				WriteMessage("Status        = initializing");
8443 				break;
8444 			}
8445 		}
8446 
8447 		sprintf(szTmp, "Signal level  = %d", pResponse->THERMOSTAT1.rssi);
8448 		WriteMessage(szTmp);
8449 		WriteMessageEnd();
8450 	}
8451 	procResult.DeviceRowIdx = DevRowIdx;
8452 }
8453 
decode_Thermostat2(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)8454 void MainWorker::decode_Thermostat2(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
8455 {
8456 	char szTmp[100];
8457 	uint8_t devType = pTypeThermostat2;
8458 	uint8_t subType = pResponse->THERMOSTAT2.subtype;
8459 	std::string ID;
8460 	ID = "1";
8461 	uint8_t Unit = pResponse->THERMOSTAT2.unitcode;
8462 	uint8_t cmnd = pResponse->THERMOSTAT2.cmnd;
8463 	uint8_t SignalLevel = pResponse->THERMOSTAT2.rssi;
8464 	uint8_t BatteryLevel = 255;
8465 
8466 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, procResult.DeviceName);
8467 	if (DevRowIdx == (uint64_t)-1)
8468 		return;
8469 	CheckSceneCode(DevRowIdx, devType, subType, cmnd, "", procResult.DeviceName);
8470 
8471 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
8472 	{
8473 		WriteMessageStart();
8474 		switch (pResponse->THERMOSTAT2.subtype)
8475 		{
8476 		case sTypeHE105:
8477 			WriteMessage("subtype       = HE105");
8478 			break;
8479 		case sTypeRTS10:
8480 			WriteMessage("subtype       = RTS10");
8481 			break;
8482 		default:
8483 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->THERMOSTAT2.packettype, pResponse->THERMOSTAT2.subtype);
8484 			WriteMessage(szTmp);
8485 			break;
8486 		}
8487 
8488 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->THERMOSTAT2.seqnbr);
8489 		WriteMessage(szTmp);
8490 
8491 		sprintf(szTmp, "Unit code        = 0x%02X", pResponse->THERMOSTAT2.unitcode);
8492 		WriteMessage(szTmp);
8493 
8494 		switch (pResponse->THERMOSTAT2.cmnd)
8495 		{
8496 		case thermostat2_sOff:
8497 			WriteMessage("Command       = Off");
8498 			break;
8499 		case thermostat2_sOn:
8500 			WriteMessage("Command       = On");
8501 			break;
8502 		default:
8503 			WriteMessage("Command       = unknown");
8504 			break;
8505 		}
8506 
8507 		sprintf(szTmp, "Signal level  = %d", pResponse->THERMOSTAT2.rssi);
8508 		WriteMessage(szTmp);
8509 		WriteMessageEnd();
8510 	}
8511 	procResult.DeviceRowIdx = DevRowIdx;
8512 }
8513 
decode_Thermostat3(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)8514 void MainWorker::decode_Thermostat3(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
8515 {
8516 	char szTmp[100];
8517 	uint8_t devType = pTypeThermostat3;
8518 	uint8_t subType = pResponse->THERMOSTAT3.subtype;
8519 	std::string ID;
8520 	sprintf(szTmp, "%02X%02X%02X", pResponse->THERMOSTAT3.unitcode1, pResponse->THERMOSTAT3.unitcode2, pResponse->THERMOSTAT3.unitcode3);
8521 	ID = szTmp;
8522 	uint8_t Unit = 0;
8523 	uint8_t cmnd = pResponse->THERMOSTAT3.cmnd;
8524 	uint8_t SignalLevel = pResponse->THERMOSTAT3.rssi;
8525 	uint8_t BatteryLevel = 255;
8526 
8527 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, procResult.DeviceName);
8528 	if (DevRowIdx == (uint64_t)-1)
8529 		return;
8530 	CheckSceneCode(DevRowIdx, devType, subType, cmnd, "", procResult.DeviceName);
8531 
8532 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
8533 	{
8534 		WriteMessageStart();
8535 		switch (pResponse->THERMOSTAT3.subtype)
8536 		{
8537 		case sTypeMertikG6RH4T1:
8538 			WriteMessage("subtype       = Mertik G6R-H4T1");
8539 			break;
8540 		case sTypeMertikG6RH4TB:
8541 			WriteMessage("subtype       = Mertik G6R-H4TB");
8542 			break;
8543 		case sTypeMertikG6RH4TD:
8544 			WriteMessage("subtype       = Mertik G6R-H4TD");
8545 			break;
8546 		case sTypeMertikG6RH4S:
8547 			WriteMessage("subtype       = Mertik G6R-H4S");
8548 			break;
8549 		default:
8550 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->THERMOSTAT3.packettype, pResponse->THERMOSTAT3.subtype);
8551 			WriteMessage(szTmp);
8552 			break;
8553 		}
8554 
8555 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->THERMOSTAT3.seqnbr);
8556 		WriteMessage(szTmp);
8557 
8558 		sprintf(szTmp, "ID            = 0x%02X%02X%02X", pResponse->THERMOSTAT3.unitcode1, pResponse->THERMOSTAT3.unitcode2, pResponse->THERMOSTAT3.unitcode3);
8559 		WriteMessage(szTmp);
8560 
8561 		switch (pResponse->THERMOSTAT3.cmnd)
8562 		{
8563 		case thermostat3_sOff:
8564 			WriteMessage("Command       = Off");
8565 			break;
8566 		case thermostat3_sOn:
8567 			WriteMessage("Command       = On");
8568 			break;
8569 		case thermostat3_sUp:
8570 			WriteMessage("Command       = Up");
8571 			break;
8572 		case thermostat3_sDown:
8573 			WriteMessage("Command       = Down");
8574 			break;
8575 		case thermostat3_sRunUp:
8576 			if (pResponse->THERMOSTAT3.subtype == sTypeMertikG6RH4T1)
8577 				WriteMessage("Command       = Run Up");
8578 			else
8579 				WriteMessage("Command       = 2nd Off");
8580 			break;
8581 		case thermostat3_sRunDown:
8582 			if (pResponse->THERMOSTAT3.subtype == sTypeMertikG6RH4T1)
8583 				WriteMessage("Command       = Run Down");
8584 			else
8585 				WriteMessage("Command       = 2nd On");
8586 			break;
8587 		case thermostat3_sStop:
8588 			if (pResponse->THERMOSTAT3.subtype == sTypeMertikG6RH4T1)
8589 				WriteMessage("Command       = Stop");
8590 			else
8591 				WriteMessage("Command       = unknown");
8592 		default:
8593 			WriteMessage("Command       = unknown");
8594 			break;
8595 		}
8596 
8597 		sprintf(szTmp, "Signal level  = %d", pResponse->THERMOSTAT3.rssi);
8598 		WriteMessage(szTmp);
8599 		WriteMessageEnd();
8600 	}
8601 	procResult.DeviceRowIdx = DevRowIdx;
8602 }
8603 
decode_Thermostat4(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)8604 void MainWorker::decode_Thermostat4(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
8605 {
8606 	char szTmp[100];
8607 	uint8_t devType = pTypeThermostat4;
8608 	uint8_t subType = pResponse->THERMOSTAT4.subtype;
8609 	std::string ID;
8610 	sprintf(szTmp, "%02X%02X%02X", pResponse->THERMOSTAT4.unitcode1, pResponse->THERMOSTAT4.unitcode2, pResponse->THERMOSTAT4.unitcode3);
8611 	ID = szTmp;
8612 	uint8_t Unit = 0;
8613 	uint8_t SignalLevel = pResponse->THERMOSTAT4.rssi;
8614 	uint8_t BatteryLevel = 255;
8615 	sprintf(szTmp, "%d;%d;%d;%d;%d;%d",
8616 		pResponse->THERMOSTAT4.beep,
8617 		pResponse->THERMOSTAT4.fan1_speed,
8618 		pResponse->THERMOSTAT4.fan2_speed,
8619 		pResponse->THERMOSTAT4.fan3_speed,
8620 		pResponse->THERMOSTAT4.flame_power,
8621 		pResponse->THERMOSTAT4.mode
8622 	);
8623 
8624 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, szTmp, procResult.DeviceName);
8625 	if (DevRowIdx == (uint64_t)-1)
8626 		return;
8627 
8628 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
8629 	{
8630 		WriteMessageStart();
8631 		switch (pResponse->THERMOSTAT4.subtype)
8632 		{
8633 		case sTypeMCZ1:
8634 			WriteMessage("subtype       = MCZ pellet stove 1 fan model");
8635 			break;
8636 		case sTypeMCZ2:
8637 			WriteMessage("subtype       = MCZ pellet stove 2 fan model");
8638 			break;
8639 		case sTypeMCZ3:
8640 			WriteMessage("subtype       = MCZ pellet stove 3 fan model");
8641 			break;
8642 		default:
8643 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->THERMOSTAT4.packettype, pResponse->THERMOSTAT4.subtype);
8644 			WriteMessage(szTmp);
8645 			break;
8646 		}
8647 
8648 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->THERMOSTAT4.seqnbr);
8649 		WriteMessage(szTmp);
8650 
8651 		sprintf(szTmp, "ID            = 0x%02X%02X%02X", pResponse->THERMOSTAT4.unitcode1, pResponse->THERMOSTAT4.unitcode2, pResponse->THERMOSTAT4.unitcode3);
8652 		WriteMessage(szTmp);
8653 
8654 		if (pResponse->THERMOSTAT4.beep)
8655 			WriteMessage("Beep          = Yes");
8656 		else
8657 			WriteMessage("Beep          = No");
8658 
8659 		if (pResponse->THERMOSTAT4.fan1_speed == 6)
8660 			strcpy(szTmp, "Fan 1 Speed   = Auto");
8661 		else
8662 			sprintf(szTmp, "Fan 1 Speed   = %d", pResponse->THERMOSTAT4.fan1_speed);
8663 		WriteMessage(szTmp);
8664 
8665 		if (pResponse->THERMOSTAT4.fan2_speed == 6)
8666 			strcpy(szTmp, "Fan 2 Speed   = Auto");
8667 		else
8668 			sprintf(szTmp, "Fan 2 Speed   = %d", pResponse->THERMOSTAT4.fan2_speed);
8669 		WriteMessage(szTmp);
8670 
8671 		if (pResponse->THERMOSTAT4.fan3_speed == 6)
8672 			strcpy(szTmp, "Fan 3 Speed   = Auto");
8673 		else
8674 			sprintf(szTmp, "Fan 3 Speed   = %d", pResponse->THERMOSTAT4.fan3_speed);
8675 		WriteMessage(szTmp);
8676 
8677 		sprintf(szTmp, "Flame power   = %d", pResponse->THERMOSTAT4.flame_power);
8678 		WriteMessage(szTmp);
8679 
8680 		switch (pResponse->THERMOSTAT4.mode)
8681 		{
8682 		case thermostat4_sOff:
8683 			WriteMessage("Command       = Off");
8684 			break;
8685 		case thermostat4_sManual:
8686 			WriteMessage("Command       = Manual");
8687 			break;
8688 		case thermostat4_sAuto:
8689 			WriteMessage("Command       = Auto");
8690 			break;
8691 		case thermostat4_sEco:
8692 			WriteMessage("Command       = Eco");
8693 			break;
8694 		default:
8695 			WriteMessage("Command       = unknown");
8696 			break;
8697 		}
8698 
8699 		sprintf(szTmp, "Signal level  = %d", pResponse->THERMOSTAT3.rssi);
8700 		WriteMessage(szTmp);
8701 		WriteMessageEnd();
8702 	}
8703 	procResult.DeviceRowIdx = DevRowIdx;
8704 }
8705 
decode_Radiator1(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)8706 void MainWorker::decode_Radiator1(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
8707 {
8708 	char szTmp[100];
8709 	uint8_t devType = pTypeRadiator1;
8710 	uint8_t subType = pResponse->RADIATOR1.subtype;
8711 	std::string ID;
8712 	sprintf(szTmp, "%X%02X%02X%02X", pResponse->RADIATOR1.id1, pResponse->RADIATOR1.id2, pResponse->RADIATOR1.id3, pResponse->RADIATOR1.id4);
8713 	ID = szTmp;
8714 	uint8_t Unit = pResponse->RADIATOR1.unitcode;
8715 	uint8_t cmnd = pResponse->RADIATOR1.cmnd;
8716 	uint8_t SignalLevel = pResponse->RADIATOR1.rssi;
8717 	uint8_t BatteryLevel = 255;
8718 
8719 	sprintf(szTmp, "%d.%d", pResponse->RADIATOR1.temperature, pResponse->RADIATOR1.tempPoint5);
8720 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
8721 	if (DevRowIdx == (uint64_t)-1)
8722 		return;
8723 
8724 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
8725 	{
8726 		WriteMessageStart();
8727 		switch (pResponse->RADIATOR1.subtype)
8728 		{
8729 		case sTypeSmartwares:
8730 			WriteMessage("subtype       = Smartwares");
8731 			break;
8732 		case sTypeSmartwaresSwitchRadiator:
8733 			WriteMessage("subtype       = Smartwares Radiator Switch");
8734 			break;
8735 		default:
8736 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->RADIATOR1.packettype, pResponse->RADIATOR1.subtype);
8737 			WriteMessage(szTmp);
8738 			break;
8739 		}
8740 
8741 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->THERMOSTAT3.seqnbr);
8742 		WriteMessage(szTmp);
8743 
8744 		sprintf(szTmp, "ID            = %X%02X%02X%02X", pResponse->RADIATOR1.id1, pResponse->RADIATOR1.id2, pResponse->RADIATOR1.id3, pResponse->RADIATOR1.id4);
8745 		WriteMessage(szTmp);
8746 		sprintf(szTmp, "Unit          = %d", pResponse->RADIATOR1.unitcode);
8747 		WriteMessage(szTmp);
8748 
8749 		switch (pResponse->RADIATOR1.cmnd)
8750 		{
8751 		case Radiator1_sNight:
8752 			WriteMessage("Command       = Night/Off");
8753 			break;
8754 		case Radiator1_sDay:
8755 			WriteMessage("Command       = Day/On");
8756 			break;
8757 		case Radiator1_sSetTemp:
8758 			WriteMessage("Command       = Set Temperature");
8759 			break;
8760 		default:
8761 			WriteMessage("Command       = unknown");
8762 			break;
8763 		}
8764 
8765 		sprintf(szTmp, "Temp          = %d.%d", pResponse->RADIATOR1.temperature, pResponse->RADIATOR1.tempPoint5);
8766 		WriteMessage(szTmp);
8767 		sprintf(szTmp, "Signal level  = %d", pResponse->RADIATOR1.rssi);
8768 		WriteMessage(szTmp);
8769 		WriteMessageEnd();
8770 	}
8771 	procResult.DeviceRowIdx = DevRowIdx;
8772 }
8773 
8774 //not in dbase yet
decode_Baro(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)8775 void MainWorker::decode_Baro(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
8776 {
8777 	//uint8_t devType=pTypeBARO;
8778 
8779 	WriteMessageStart();
8780 	WriteMessage("Not implemented");
8781 	WriteMessageEnd();
8782 	procResult.DeviceRowIdx = -1;
8783 }
8784 
8785 //not in dbase yet
decode_DateTime(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)8786 void MainWorker::decode_DateTime(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
8787 {
8788 	WriteMessage("");
8789 
8790 	//uint8_t devType=pTypeDT;
8791 
8792 	char szTmp[100];
8793 
8794 	switch (pResponse->DT.subtype)
8795 	{
8796 	case sTypeDT1:
8797 		WriteMessage("Subtype       = DT1 - RTGR328N");
8798 		break;
8799 	default:
8800 		sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->DT.packettype, pResponse->DT.subtype);
8801 		WriteMessage(szTmp);
8802 		break;
8803 	}
8804 	sprintf(szTmp, "Sequence nbr  = %d", pResponse->DT.seqnbr);
8805 	WriteMessage(szTmp);
8806 	sprintf(szTmp, "ID            = %d", (pResponse->DT.id1 * 256) + pResponse->DT.id2);
8807 	WriteMessage(szTmp);
8808 
8809 	WriteMessage("Day of week   = ", false);
8810 
8811 	switch (pResponse->DT.dow)
8812 	{
8813 	case 1:
8814 		WriteMessage(" Sunday");
8815 		break;
8816 	case 2:
8817 		WriteMessage(" Monday");
8818 		break;
8819 	case 3:
8820 		WriteMessage(" Tuesday");
8821 		break;
8822 	case 4:
8823 		WriteMessage(" Wednesday");
8824 		break;
8825 	case 5:
8826 		WriteMessage(" Thursday");
8827 		break;
8828 	case 6:
8829 		WriteMessage(" Friday");
8830 		break;
8831 	case 7:
8832 		WriteMessage(" Saturday");
8833 		break;
8834 	}
8835 	sprintf(szTmp, "Date yy/mm/dd = %02d/%02d/%02d", pResponse->DT.yy, pResponse->DT.mm, pResponse->DT.dd);
8836 	WriteMessage(szTmp);
8837 	sprintf(szTmp, "Time          = %02d:%02d:%02d", pResponse->DT.hr, pResponse->DT.min, pResponse->DT.sec);
8838 	WriteMessage(szTmp);
8839 	sprintf(szTmp, "Signal level  = %d", pResponse->DT.rssi);
8840 	WriteMessage(szTmp);
8841 	if ((pResponse->DT.battery_level & 0x0F) == 0)
8842 		WriteMessage("Battery       = Low");
8843 	else
8844 		WriteMessage("Battery       = OK");
8845 	procResult.DeviceRowIdx = -1;
8846 }
8847 
decode_Current(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)8848 void MainWorker::decode_Current(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
8849 {
8850 	char szTmp[100];
8851 	uint8_t devType = pTypeCURRENT;
8852 	uint8_t subType = pResponse->CURRENT.subtype;
8853 	std::string ID;
8854 	sprintf(szTmp, "%d", (pResponse->CURRENT.id1 * 256) + pResponse->CURRENT.id2);
8855 	ID = szTmp;
8856 	uint8_t Unit = 0;
8857 	uint8_t cmnd = 0;
8858 	uint8_t SignalLevel = pResponse->CURRENT.rssi;
8859 	uint8_t BatteryLevel = get_BateryLevel(pHardware->HwdType, false, pResponse->CURRENT.battery_level & 0x0F);
8860 
8861 	float CurrentChannel1 = float((pResponse->CURRENT.ch1h * 256) + pResponse->CURRENT.ch1l) / 10.0f;
8862 	float CurrentChannel2 = float((pResponse->CURRENT.ch2h * 256) + pResponse->CURRENT.ch2l) / 10.0f;
8863 	float CurrentChannel3 = float((pResponse->CURRENT.ch3h * 256) + pResponse->CURRENT.ch3l) / 10.0f;
8864 	sprintf(szTmp, "%.1f;%.1f;%.1f", CurrentChannel1, CurrentChannel2, CurrentChannel3);
8865 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
8866 	if (DevRowIdx == (uint64_t)-1)
8867 		return;
8868 
8869 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, cmnd, szTmp);
8870 
8871 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
8872 	{
8873 		WriteMessageStart();
8874 		switch (pResponse->CURRENT.subtype)
8875 		{
8876 		case sTypeELEC1:
8877 			WriteMessage("subtype       = ELEC1 - OWL CM113, Electrisave, cent-a-meter");
8878 			break;
8879 		default:
8880 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->CURRENT.packettype, pResponse->CURRENT.subtype);
8881 			WriteMessage(szTmp);
8882 			break;
8883 		}
8884 
8885 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->CURRENT.seqnbr);
8886 		WriteMessage(szTmp);
8887 		sprintf(szTmp, "ID            = %d", (pResponse->CURRENT.id1 * 256) + pResponse->CURRENT.id2);
8888 		WriteMessage(szTmp);
8889 		sprintf(szTmp, "Count         = %d", pResponse->CURRENT.id2);//m_rxbuffer[5]);
8890 		WriteMessage(szTmp);
8891 		sprintf(szTmp, "Channel 1     = %.1f ampere", CurrentChannel1);
8892 		WriteMessage(szTmp);
8893 		sprintf(szTmp, "Channel 2     = %.1f ampere", CurrentChannel2);
8894 		WriteMessage(szTmp);
8895 		sprintf(szTmp, "Channel 3     = %.1f ampere", CurrentChannel3);
8896 		WriteMessage(szTmp);
8897 
8898 		sprintf(szTmp, "Signal level  = %d", pResponse->CURRENT.rssi);
8899 		WriteMessage(szTmp);
8900 
8901 		if ((pResponse->CURRENT.battery_level & 0xF) == 0)
8902 			WriteMessage("Battery       = Low");
8903 		else
8904 			WriteMessage("Battery       = OK");
8905 		WriteMessageEnd();
8906 	}
8907 	procResult.DeviceRowIdx = DevRowIdx;
8908 }
8909 
decode_Energy(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)8910 void MainWorker::decode_Energy(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
8911 {
8912 	uint8_t subType = pResponse->ENERGY.subtype;
8913 	uint8_t SignalLevel = pResponse->ENERGY.rssi;
8914 	uint8_t BatteryLevel = get_BateryLevel(pHardware->HwdType, false, pResponse->ENERGY.battery_level & 0x0F);
8915 
8916 	long instant = (pResponse->ENERGY.instant1 * 0x1000000) + (pResponse->ENERGY.instant2 * 0x10000) + (pResponse->ENERGY.instant3 * 0x100) + pResponse->ENERGY.instant4;
8917 
8918 	double total = (
8919 		double(pResponse->ENERGY.total1) * 0x10000000000ULL +
8920 		double(pResponse->ENERGY.total2) * 0x100000000ULL +
8921 		double(pResponse->ENERGY.total3) * 0x1000000 +
8922 		double(pResponse->ENERGY.total4) * 0x10000 +
8923 		double(pResponse->ENERGY.total5) * 0x100 +
8924 		double(pResponse->ENERGY.total6)
8925 		) / 223.666;
8926 
8927 	if (pResponse->ENERGY.subtype == sTypeELEC3)
8928 	{
8929 		if (total == 0)
8930 		{
8931 			char szTmp[20];
8932 			std::string ID;
8933 			sprintf(szTmp, "%08X", (pResponse->ENERGY.id1 * 256) + pResponse->ENERGY.id2);
8934 			ID = szTmp;
8935 
8936 			//Retrieve last total from current record
8937 			int nValue;
8938 			subType = sTypeKwh; // sensor type changed during recording
8939 			uint8_t devType = pTypeGeneral; // Device reported as General and not Energy
8940 			uint8_t Unit = 1; // in decode_general() Unit is set to 1
8941 			std::string sValue;
8942 			struct tm LastUpdateTime;
8943 			if (!m_sql.GetLastValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, nValue, sValue, LastUpdateTime))
8944 				return;
8945 			std::vector<std::string> strarray;
8946 			StringSplit(sValue, ";", strarray);
8947 			if (strarray.size() != 2)
8948 				return;
8949 			total = atof(strarray[1].c_str());
8950 		}
8951 	}
8952 
8953 	//Translate this sensor type to the new kWh sensor type
8954 	_tGeneralDevice gdevice;
8955 	gdevice.intval1 = (pResponse->ENERGY.id1 * 256) + pResponse->ENERGY.id2;
8956 	gdevice.subtype = sTypeKwh;
8957 	gdevice.floatval1 = (float)instant;
8958 	gdevice.floatval2 = (float)total;
8959 
8960 	int voltage = 230;
8961 	m_sql.GetPreferencesVar("ElectricVoltage", voltage);
8962 	if (voltage != 230)
8963 	{
8964 		float mval = float(voltage) / 230.0f;
8965 		gdevice.floatval1 *= mval;
8966 		gdevice.floatval2 *= mval;
8967 	}
8968 
8969 	decode_General(pHardware, (const tRBUF*)&gdevice, procResult, SignalLevel, BatteryLevel);
8970 	procResult.bProcessBatteryValue = false;
8971 }
8972 
decode_Power(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)8973 void MainWorker::decode_Power(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
8974 {
8975 	char szTmp[100];
8976 	uint8_t devType = pTypePOWER;
8977 	uint8_t subType = pResponse->POWER.subtype;
8978 	std::string ID;
8979 	sprintf(szTmp, "%d", (pResponse->POWER.id1 * 256) + pResponse->POWER.id2);
8980 	ID = szTmp;
8981 	uint8_t Unit = 0;
8982 	uint8_t cmnd = 0;
8983 	uint8_t SignalLevel = pResponse->POWER.rssi;
8984 	uint8_t BatteryLevel = 255;
8985 
8986 	float Voltage = (float)pResponse->POWER.voltage;
8987 	double current = ((pResponse->POWER.currentH * 256) + pResponse->POWER.currentL) / 100.0;
8988 	double instant = ((pResponse->POWER.powerH * 256) + pResponse->POWER.powerL) / 10.0;// Watt
8989 	double usage = ((pResponse->POWER.energyH * 256) + pResponse->POWER.energyL) / 100.0; //kWh
8990 	double powerfactor = pResponse->POWER.pf / 100.0;
8991 	float frequency = (float)pResponse->POWER.freq; //Hz
8992 
8993 	sprintf(szTmp, "%ld;%.2f", long(round(instant)), usage * 1000.0);
8994 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
8995 	if (DevRowIdx == (uint64_t)-1)
8996 		return;
8997 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, cmnd, szTmp);
8998 
8999 	//Voltage
9000 	sprintf(szTmp, "%.3f", Voltage);
9001 	std::string tmpDevName;
9002 	uint64_t DevRowIdxAlt = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), 1, pTypeGeneral, sTypeVoltage, SignalLevel, BatteryLevel, cmnd, szTmp, tmpDevName);
9003 	if (DevRowIdxAlt == (uint64_t)-1)
9004 		return;
9005 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, tmpDevName, 1, pTypeGeneral, sTypeVoltage, Voltage);
9006 
9007 	//Powerfactor
9008 	sprintf(szTmp, "%.2f", (float)powerfactor);
9009 	DevRowIdxAlt = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), 2, pTypeGeneral, sTypePercentage, SignalLevel, BatteryLevel, cmnd, szTmp, tmpDevName);
9010 	if (DevRowIdxAlt == (uint64_t)-1)
9011 		return;
9012 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, tmpDevName, 2, pTypeGeneral, sTypePercentage, (float)powerfactor);
9013 
9014 	//Frequency
9015 	sprintf(szTmp, "%.2f", (float)frequency);
9016 	DevRowIdxAlt = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), 3, pTypeGeneral, sTypePercentage, SignalLevel, BatteryLevel, cmnd, szTmp, tmpDevName);
9017 	if (DevRowIdxAlt == (uint64_t)-1)
9018 		return;
9019 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, tmpDevName, 3, pTypeGeneral, sTypePercentage, frequency);
9020 
9021 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
9022 	{
9023 		WriteMessageStart();
9024 		switch (pResponse->POWER.subtype)
9025 		{
9026 		case sTypeELEC5:
9027 			WriteMessage("subtype       = ELEC5 - Revolt");
9028 			break;
9029 		}
9030 
9031 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->POWER.seqnbr);
9032 		WriteMessage(szTmp);
9033 		sprintf(szTmp, "ID            = %d", (pResponse->POWER.id1 * 256) + pResponse->POWER.id2);
9034 		WriteMessage(szTmp);
9035 
9036 		sprintf(szTmp, "Voltage       = %g Volt", Voltage);
9037 		WriteMessage(szTmp);
9038 		sprintf(szTmp, "Current       = %.2f Ampere", current);
9039 		WriteMessage(szTmp);
9040 
9041 		sprintf(szTmp, "Instant usage = %.2f Watt", instant);
9042 		WriteMessage(szTmp);
9043 		sprintf(szTmp, "total usage   = %.2f kWh", usage);
9044 		WriteMessage(szTmp);
9045 
9046 		sprintf(szTmp, "Power factor  = %.2f", powerfactor);
9047 		WriteMessage(szTmp);
9048 		sprintf(szTmp, "Frequency     = %g Hz", frequency);
9049 		WriteMessage(szTmp);
9050 
9051 		sprintf(szTmp, "Signal level  = %d", pResponse->POWER.rssi);
9052 		WriteMessage(szTmp);
9053 		WriteMessageEnd();
9054 	}
9055 	procResult.DeviceRowIdx = DevRowIdx;
9056 }
9057 
decode_Current_Energy(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)9058 void MainWorker::decode_Current_Energy(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
9059 {
9060 	char szTmp[100];
9061 	uint8_t devType = pTypeCURRENTENERGY;
9062 	uint8_t subType = pResponse->CURRENT_ENERGY.subtype;
9063 	std::string ID;
9064 	sprintf(szTmp, "%d", (pResponse->CURRENT_ENERGY.id1 * 256) + pResponse->CURRENT_ENERGY.id2);
9065 	ID = szTmp;
9066 	uint8_t Unit = 0;
9067 	uint8_t cmnd = 0;
9068 	uint8_t SignalLevel = pResponse->CURRENT_ENERGY.rssi;
9069 	uint8_t BatteryLevel = get_BateryLevel(pHardware->HwdType, false, pResponse->CURRENT_ENERGY.battery_level & 0x0F);
9070 
9071 	float CurrentChannel1 = float((pResponse->CURRENT_ENERGY.ch1h * 256) + pResponse->CURRENT_ENERGY.ch1l) / 10.0f;
9072 	float CurrentChannel2 = float((pResponse->CURRENT_ENERGY.ch2h * 256) + pResponse->CURRENT_ENERGY.ch2l) / 10.0f;
9073 	float CurrentChannel3 = float((pResponse->CURRENT_ENERGY.ch3h * 256) + pResponse->CURRENT_ENERGY.ch3l) / 10.0f;
9074 
9075 	double usage = 0;
9076 
9077 	if (pResponse->CURRENT_ENERGY.count != 0)
9078 	{
9079 		//no usage provided, get the last usage
9080 		std::vector<std::vector<std::string> > result2;
9081 		result2 = m_sql.safe_query(
9082 			"SELECT nValue,sValue FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)",
9083 			pHardware->m_HwdID, ID.c_str(), int(Unit), int(devType), int(subType));
9084 		if (!result2.empty())
9085 		{
9086 			std::vector<std::string> strarray;
9087 			StringSplit(result2[0][1], ";", strarray);
9088 			if (strarray.size() == 4)
9089 			{
9090 				usage = atof(strarray[3].c_str());
9091 			}
9092 		}
9093 	}
9094 	else
9095 	{
9096 		usage = (
9097 			double(pResponse->CURRENT_ENERGY.total1) * 0x10000000000ULL +
9098 			double(pResponse->CURRENT_ENERGY.total2) * 0x100000000ULL +
9099 			double(pResponse->CURRENT_ENERGY.total3) * 0x1000000 +
9100 			double(pResponse->CURRENT_ENERGY.total4) * 0x10000 +
9101 			double(pResponse->CURRENT_ENERGY.total5) * 0x100 +
9102 			pResponse->CURRENT_ENERGY.total6
9103 			) / 223.666;
9104 
9105 		if (usage == 0)
9106 		{
9107 			//That should not be, let's get the previous value
9108 			//no usage provided, get the last usage
9109 			std::vector<std::vector<std::string> > result2;
9110 			result2 = m_sql.safe_query(
9111 				"SELECT nValue,sValue FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)",
9112 				pHardware->m_HwdID, ID.c_str(), int(Unit), int(devType), int(subType));
9113 			if (!result2.empty())
9114 			{
9115 				std::vector<std::string> strarray;
9116 				StringSplit(result2[0][1], ";", strarray);
9117 				if (strarray.size() == 4)
9118 				{
9119 					usage = atof(strarray[3].c_str());
9120 				}
9121 			}
9122 		}
9123 
9124 		int voltage = 230;
9125 		m_sql.GetPreferencesVar("ElectricVoltage", voltage);
9126 
9127 		sprintf(szTmp, "%ld;%.2f", (long)round((CurrentChannel1 + CurrentChannel2 + CurrentChannel3) * voltage), usage);
9128 		m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, pTypeENERGY, sTypeELEC3, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
9129 	}
9130 	sprintf(szTmp, "%.1f;%.1f;%.1f;%.3f", CurrentChannel1, CurrentChannel2, CurrentChannel3, usage);
9131 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
9132 	if (DevRowIdx == (uint64_t)-1)
9133 		return;
9134 
9135 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, cmnd, szTmp);
9136 
9137 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
9138 	{
9139 		WriteMessageStart();
9140 		switch (pResponse->CURRENT_ENERGY.subtype)
9141 		{
9142 		case sTypeELEC4:
9143 			WriteMessage("subtype       = ELEC4 - OWL CM180i");
9144 			break;
9145 		default:
9146 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->CURRENT_ENERGY.packettype, pResponse->CURRENT_ENERGY.subtype);
9147 			WriteMessage(szTmp);
9148 			break;
9149 		}
9150 
9151 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->CURRENT_ENERGY.seqnbr);
9152 		WriteMessage(szTmp);
9153 		sprintf(szTmp, "ID            = %d", (pResponse->CURRENT_ENERGY.id1 * 256) + pResponse->CURRENT_ENERGY.id2);
9154 		WriteMessage(szTmp);
9155 		sprintf(szTmp, "Count         = %d", pResponse->CURRENT_ENERGY.count);
9156 		WriteMessage(szTmp);
9157 		float ampereChannel1, ampereChannel2, ampereChannel3;
9158 		ampereChannel1 = float((pResponse->CURRENT_ENERGY.ch1h * 256) + pResponse->CURRENT_ENERGY.ch1l) / 10.0f;
9159 		ampereChannel2 = float((pResponse->CURRENT_ENERGY.ch2h * 256) + pResponse->CURRENT_ENERGY.ch2l) / 10.0f;
9160 		ampereChannel3 = float((pResponse->CURRENT_ENERGY.ch3h * 256) + pResponse->CURRENT_ENERGY.ch3l) / 10.0f;
9161 		sprintf(szTmp, "Channel 1     = %.1f ampere", ampereChannel1);
9162 		WriteMessage(szTmp);
9163 		sprintf(szTmp, "Channel 2     = %.1f ampere", ampereChannel2);
9164 		WriteMessage(szTmp);
9165 		sprintf(szTmp, "Channel 3     = %.1f ampere", ampereChannel3);
9166 		WriteMessage(szTmp);
9167 
9168 		if (pResponse->CURRENT_ENERGY.count == 0)
9169 		{
9170 			sprintf(szTmp, "total usage   = %.3f Wh", usage);
9171 			WriteMessage(szTmp);
9172 		}
9173 
9174 		sprintf(szTmp, "Signal level  = %d", pResponse->CURRENT_ENERGY.rssi);
9175 		WriteMessage(szTmp);
9176 
9177 		if ((pResponse->CURRENT_ENERGY.battery_level & 0xF) == 0)
9178 			WriteMessage("Battery       = Low");
9179 		else
9180 			WriteMessage("Battery       = OK");
9181 		WriteMessageEnd();
9182 	}
9183 	procResult.DeviceRowIdx = DevRowIdx;
9184 }
9185 
9186 //not in dbase yet
decode_Gas(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)9187 void MainWorker::decode_Gas(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
9188 {
9189 	WriteMessage("");
9190 
9191 	//uint8_t devType=pTypeGAS;
9192 
9193 	WriteMessage("Not implemented");
9194 
9195 	procResult.DeviceRowIdx = -1;
9196 }
9197 
9198 //not in dbase yet
decode_Water(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)9199 void MainWorker::decode_Water(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
9200 {
9201 	WriteMessage("");
9202 
9203 	//uint8_t devType=pTypeWATER;
9204 
9205 	WriteMessage("Not implemented");
9206 
9207 	procResult.DeviceRowIdx = -1;
9208 }
9209 
decode_Weight(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)9210 void MainWorker::decode_Weight(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
9211 {
9212 	char szTmp[100];
9213 	uint8_t devType = pTypeWEIGHT;
9214 	uint8_t subType = pResponse->WEIGHT.subtype;
9215 	uint16_t weightID = (pResponse->WEIGHT.id1 * 256) + pResponse->WEIGHT.id2;
9216 	std::string ID;
9217 	sprintf(szTmp, "%d", weightID);
9218 	ID = szTmp;
9219 	uint8_t Unit = 0;
9220 	uint8_t cmnd = 0;
9221 	uint8_t SignalLevel = pResponse->WEIGHT.rssi;
9222 	uint8_t BatteryLevel = 255;
9223 	float weight = (float(pResponse->WEIGHT.weighthigh) * 25.6f) + (float(pResponse->WEIGHT.weightlow) / 10.0f);
9224 
9225 	float AddjValue = 0.0f;
9226 	float AddjMulti = 1.0f;
9227 	m_sql.GetAddjustment(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, AddjValue, AddjMulti);
9228 	weight += AddjValue;
9229 
9230 	sprintf(szTmp, "%.1f", weight);
9231 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
9232 	if (DevRowIdx == (uint64_t)-1)
9233 		return;
9234 
9235 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, weight);
9236 
9237 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
9238 	{
9239 		WriteMessageStart();
9240 		switch (pResponse->WEIGHT.subtype)
9241 		{
9242 		case sTypeWEIGHT1:
9243 			WriteMessage("subtype       = BWR102");
9244 			break;
9245 		case sTypeWEIGHT2:
9246 			WriteMessage("subtype       = GR101");
9247 			break;
9248 		default:
9249 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type = %02X:%02X", pResponse->WEIGHT.packettype, pResponse->WEIGHT.subtype);
9250 			WriteMessage(szTmp);
9251 			break;
9252 		}
9253 
9254 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->WEIGHT.seqnbr);
9255 		WriteMessage(szTmp);
9256 		sprintf(szTmp, "ID            = %d", (pResponse->WEIGHT.id1 * 256) + pResponse->WEIGHT.id2);
9257 		WriteMessage(szTmp);
9258 		sprintf(szTmp, "Weight        = %.1f kg", (float(pResponse->WEIGHT.weighthigh) * 25.6f) + (float(pResponse->WEIGHT.weightlow) / 10));
9259 		WriteMessage(szTmp);
9260 		sprintf(szTmp, "Signal level  = %d", pResponse->WEIGHT.rssi);
9261 		WriteMessage(szTmp);
9262 		WriteMessageEnd();
9263 	}
9264 	procResult.DeviceRowIdx = DevRowIdx;
9265 }
9266 
decode_RFXSensor(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)9267 void MainWorker::decode_RFXSensor(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
9268 {
9269 	char szTmp[100];
9270 	uint8_t devType = pTypeRFXSensor;
9271 	uint8_t subType = pResponse->RFXSENSOR.subtype;
9272 	std::string ID;
9273 	sprintf(szTmp, "%d", pResponse->RFXSENSOR.id);
9274 	ID = szTmp;
9275 	uint8_t Unit = 0;
9276 	uint8_t cmnd = 0;
9277 	uint8_t SignalLevel = pResponse->RFXSENSOR.rssi;
9278 	uint8_t BatteryLevel = 255;
9279 
9280 	if ((pHardware->HwdType == HTYPE_EnOceanESP2) || (pHardware->HwdType == HTYPE_EnOceanESP3))
9281 	{
9282 		BatteryLevel = 255;
9283 		SignalLevel = 12;
9284 		Unit = (pResponse->RFXSENSOR.rssi << 4) | pResponse->RFXSENSOR.filler;
9285 	}
9286 
9287 	float temp = 0;
9288 	int volt = 0;
9289 	switch (pResponse->RFXSENSOR.subtype)
9290 	{
9291 	case sTypeRFXSensorTemp:
9292 	{
9293 		if ((pResponse->RFXSENSOR.msg1 & 0x80) == 0) //positive temperature?
9294 			temp = float((pResponse->RFXSENSOR.msg1 * 256) + pResponse->RFXSENSOR.msg2) / 100.0f;
9295 		else
9296 			temp = -(float(((pResponse->RFXSENSOR.msg1 & 0x7F) * 256) + pResponse->RFXSENSOR.msg2) / 100.0f);
9297 		float AddjValue = 0.0f;
9298 		float AddjMulti = 1.0f;
9299 		m_sql.GetAddjustment(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, AddjValue, AddjMulti);
9300 		temp += AddjValue;
9301 		sprintf(szTmp, "%.1f", temp);
9302 	}
9303 	break;
9304 	case sTypeRFXSensorAD:
9305 	case sTypeRFXSensorVolt:
9306 	{
9307 		volt = (pResponse->RFXSENSOR.msg1 * 256) + pResponse->RFXSENSOR.msg2;
9308 		if (
9309 			(pHardware->HwdType == HTYPE_RFXLAN) ||
9310 			(pHardware->HwdType == HTYPE_RFXtrx315) ||
9311 			(pHardware->HwdType == HTYPE_RFXtrx433) ||
9312 			(pHardware->HwdType == HTYPE_RFXtrx868)
9313 			)
9314 		{
9315 			volt *= 10;
9316 		}
9317 		sprintf(szTmp, "%d", volt);
9318 	}
9319 	break;
9320 	}
9321 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
9322 	if (DevRowIdx == (uint64_t)-1)
9323 		return;
9324 
9325 	switch (pResponse->RFXSENSOR.subtype)
9326 	{
9327 	case sTypeRFXSensorTemp:
9328 		m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, temp);
9329 		break;
9330 	case sTypeRFXSensorAD:
9331 	case sTypeRFXSensorVolt:
9332 		m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, (float)volt);
9333 		break;
9334 	}
9335 
9336 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
9337 	{
9338 		WriteMessageStart();
9339 		switch (pResponse->RFXSENSOR.subtype)
9340 		{
9341 		case sTypeRFXSensorTemp:
9342 			WriteMessage("subtype       = Temperature");
9343 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->RFXSENSOR.seqnbr);
9344 			WriteMessage(szTmp);
9345 			sprintf(szTmp, "ID            = %d", pResponse->RFXSENSOR.id);
9346 			WriteMessage(szTmp);
9347 
9348 			if ((pResponse->RFXSENSOR.msg1 & 0x80) == 0) //positive temperature?
9349 			{
9350 				sprintf(szTmp, "Temperature   = %.2f C", float((pResponse->RFXSENSOR.msg1 * 256) + pResponse->RFXSENSOR.msg2) / 100.0f);
9351 				WriteMessage(szTmp);
9352 			}
9353 			else
9354 			{
9355 				sprintf(szTmp, "Temperature   = -%.2f C", float(((pResponse->RFXSENSOR.msg1 & 0x7F) * 256) + pResponse->RFXSENSOR.msg2) / 100.0f);
9356 				WriteMessage(szTmp);
9357 			}
9358 			break;
9359 		case sTypeRFXSensorAD:
9360 			WriteMessage("subtype       = A/D");
9361 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->RFXSENSOR.seqnbr);
9362 			WriteMessage(szTmp);
9363 			sprintf(szTmp, "ID            = %d", pResponse->RFXSENSOR.id);
9364 			WriteMessage(szTmp);
9365 			sprintf(szTmp, "volt          = %d mV", (pResponse->RFXSENSOR.msg1 * 256) + pResponse->RFXSENSOR.msg2);
9366 			WriteMessage(szTmp);
9367 			break;
9368 		case sTypeRFXSensorVolt:
9369 			WriteMessage("subtype       = Voltage");
9370 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->RFXSENSOR.seqnbr);
9371 			WriteMessage(szTmp);
9372 			sprintf(szTmp, "ID            = %d", pResponse->RFXSENSOR.id);
9373 			WriteMessage(szTmp);
9374 			sprintf(szTmp, "volt          = %d mV", (pResponse->RFXSENSOR.msg1 * 256) + pResponse->RFXSENSOR.msg2);
9375 			WriteMessage(szTmp);
9376 			break;
9377 		case sTypeRFXSensorMessage:
9378 			WriteMessage("subtype       = Message");
9379 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->RFXSENSOR.seqnbr);
9380 			WriteMessage(szTmp);
9381 			sprintf(szTmp, "ID            = %d", pResponse->RFXSENSOR.id);
9382 			WriteMessage(szTmp);
9383 			switch (pResponse->RFXSENSOR.msg2)
9384 			{
9385 			case 0x1:
9386 				WriteMessage("msg           = sensor addresses incremented");
9387 				break;
9388 			case 0x2:
9389 				WriteMessage("msg           = battery low detected");
9390 				break;
9391 			case 0x81:
9392 				WriteMessage("msg           = no 1-wire device connected");
9393 				break;
9394 			case 0x82:
9395 				WriteMessage("msg           = 1-Wire ROM CRC error");
9396 				break;
9397 			case 0x83:
9398 				WriteMessage("msg           = 1-Wire device connected is not a DS18B20 or DS2438");
9399 				break;
9400 			case 0x84:
9401 				WriteMessage("msg           = no end of read signal received from 1-Wire device");
9402 				break;
9403 			case 0x85:
9404 				WriteMessage("msg           = 1-Wire scratch pad CRC error");
9405 				break;
9406 			default:
9407 				WriteMessage("ERROR: unknown message");
9408 				break;
9409 			}
9410 			sprintf(szTmp, "msg           = %d", (pResponse->RFXSENSOR.msg1 * 256) + pResponse->RFXSENSOR.msg2);
9411 			WriteMessage(szTmp);
9412 			break;
9413 		default:
9414 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->RFXSENSOR.packettype, pResponse->RFXSENSOR.subtype);
9415 			WriteMessage(szTmp);
9416 			break;
9417 		}
9418 		sprintf(szTmp, "Signal level  = %d", pResponse->RFXSENSOR.rssi);
9419 		WriteMessage(szTmp);
9420 		WriteMessageEnd();
9421 	}
9422 	procResult.DeviceRowIdx = DevRowIdx;
9423 }
9424 
decode_RFXMeter(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)9425 void MainWorker::decode_RFXMeter(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
9426 {
9427 	uint64_t DevRowIdx = -1;
9428 	char szTmp[100];
9429 	uint8_t devType = pTypeRFXMeter;
9430 	uint8_t subType = pResponse->RFXMETER.subtype;
9431 	if (subType == sTypeRFXMeterCount)
9432 	{
9433 		std::string ID;
9434 		sprintf(szTmp, "%d", (pResponse->RFXMETER.id1 * 256) + pResponse->RFXMETER.id2);
9435 		ID = szTmp;
9436 		uint8_t Unit = 0;
9437 		uint8_t cmnd = 0;
9438 		uint8_t SignalLevel = pResponse->RFXMETER.rssi;
9439 		uint8_t BatteryLevel = 255;
9440 
9441 		unsigned long counter = (pResponse->RFXMETER.count1 << 24) + (pResponse->RFXMETER.count2 << 16) + (pResponse->RFXMETER.count3 << 8) + pResponse->RFXMETER.count4;
9442 		//float RFXPwr = float(counter) / 1000.0f;
9443 
9444 		sprintf(szTmp, "%lu", counter);
9445 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
9446 		if (DevRowIdx == (uint64_t)-1)
9447 			return;
9448 	}
9449 
9450 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
9451 	{
9452 		WriteMessageStart();
9453 		unsigned long counter;
9454 
9455 		switch (pResponse->RFXMETER.subtype)
9456 		{
9457 		case sTypeRFXMeterCount:
9458 			WriteMessage("subtype       = RFXMeter counter");
9459 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->RFXMETER.seqnbr);
9460 			WriteMessage(szTmp);
9461 			sprintf(szTmp, "ID            = %d", (pResponse->RFXMETER.id1 * 256) + pResponse->RFXMETER.id2);
9462 			WriteMessage(szTmp);
9463 			counter = (pResponse->RFXMETER.count1 << 24) + (pResponse->RFXMETER.count2 << 16) + (pResponse->RFXMETER.count3 << 8) + pResponse->RFXMETER.count4;
9464 			sprintf(szTmp, "Counter       = %lu", counter);
9465 			WriteMessage(szTmp);
9466 			sprintf(szTmp, "if RFXPwr     = %.3f kWh", float(counter) / 1000.0f);
9467 			WriteMessage(szTmp);
9468 			break;
9469 		case sTypeRFXMeterInterval:
9470 			WriteMessage("subtype       = RFXMeter new interval time set");
9471 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->RFXMETER.seqnbr);
9472 			WriteMessage(szTmp);
9473 			sprintf(szTmp, "ID            = %d", (pResponse->RFXMETER.id1 * 256) + pResponse->RFXMETER.id2);
9474 			WriteMessage(szTmp);
9475 			WriteMessage("Interval time = ", false);
9476 
9477 			switch (pResponse->RFXMETER.count3)
9478 			{
9479 			case 0x1:
9480 				WriteMessage("30 seconds");
9481 				break;
9482 			case 0x2:
9483 				WriteMessage("1 minute");
9484 				break;
9485 			case 0x4:
9486 				WriteMessage("6 minutes");
9487 				break;
9488 			case 0x8:
9489 				WriteMessage("12 minutes");
9490 				break;
9491 			case 0x10:
9492 				WriteMessage("15 minutes");
9493 				break;
9494 			case 0x20:
9495 				WriteMessage("30 minutes");
9496 				break;
9497 			case 0x40:
9498 				WriteMessage("45 minutes");
9499 				break;
9500 			case 0x80:
9501 				WriteMessage("1 hour");
9502 				break;
9503 			}
9504 			break;
9505 		case sTypeRFXMeterCalib:
9506 			switch (pResponse->RFXMETER.count2 & 0xC0)
9507 			{
9508 			case 0x0:
9509 				WriteMessage("subtype       = Calibrate mode for channel 1");
9510 				break;
9511 			case 0x40:
9512 				WriteMessage("subtype       = Calibrate mode for channel 2");
9513 				break;
9514 			case 0x80:
9515 				WriteMessage("subtype       = Calibrate mode for channel 3");
9516 				break;
9517 			}
9518 			sprintf(szTmp, "ID            = %d", (pResponse->RFXMETER.id1 * 256) + pResponse->RFXMETER.id2);
9519 			WriteMessage(szTmp);
9520 			counter = (((pResponse->RFXMETER.count2 & 0x3F) << 16) + (pResponse->RFXMETER.count3 << 8) + pResponse->RFXMETER.count4) / 1000;
9521 			sprintf(szTmp, "Calibrate cnt = %lu msec", counter);
9522 			WriteMessage(szTmp);
9523 
9524 			sprintf(szTmp, "RFXPwr        = %.3f kW", 1.0f / (float(16 * counter) / (3600000.0f / 62.5f)));
9525 			WriteMessage(szTmp);
9526 			break;
9527 		case sTypeRFXMeterAddr:
9528 			WriteMessage("subtype       = New address set, push button for next address");
9529 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->RFXMETER.seqnbr);
9530 			WriteMessage(szTmp);
9531 			sprintf(szTmp, "ID            = %d", (pResponse->RFXMETER.id1 * 256) + pResponse->RFXMETER.id2);
9532 			WriteMessage(szTmp);
9533 			break;
9534 		case sTypeRFXMeterCounterReset:
9535 			switch (pResponse->RFXMETER.count2 & 0xC0)
9536 			{
9537 			case 0x0:
9538 				WriteMessage("subtype       = Push the button for next mode within 5 seconds or else RESET COUNTER channel 1 will be executed");
9539 				break;
9540 			case 0x40:
9541 				WriteMessage("subtype       = Push the button for next mode within 5 seconds or else RESET COUNTER channel 2 will be executed");
9542 				break;
9543 			case 0x80:
9544 				WriteMessage("subtype       = Push the button for next mode within 5 seconds or else RESET COUNTER channel 3 will be executed");
9545 				break;
9546 			}
9547 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->RFXMETER.seqnbr);
9548 			WriteMessage(szTmp);
9549 			sprintf(szTmp, "ID            = %d", (pResponse->RFXMETER.id1 * 256) + pResponse->RFXMETER.id2);
9550 			WriteMessage(szTmp);
9551 			break;
9552 		case sTypeRFXMeterCounterSet:
9553 			switch (pResponse->RFXMETER.count2 & 0xC0)
9554 			{
9555 			case 0x0:
9556 				WriteMessage("subtype       = Counter channel 1 is reset to zero");
9557 				break;
9558 			case 0x40:
9559 				WriteMessage("subtype       = Counter channel 2 is reset to zero");
9560 				break;
9561 			case 0x80:
9562 				WriteMessage("subtype       = Counter channel 3 is reset to zero");
9563 				break;
9564 			}
9565 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->RFXMETER.seqnbr);
9566 			WriteMessage(szTmp);
9567 			sprintf(szTmp, "ID            = %d", (pResponse->RFXMETER.id1 * 256) + pResponse->RFXMETER.id2);
9568 			WriteMessage(szTmp);
9569 			counter = (pResponse->RFXMETER.count1 << 24) + (pResponse->RFXMETER.count2 << 16) + (pResponse->RFXMETER.count3 << 8) + pResponse->RFXMETER.count4;
9570 			sprintf(szTmp, "Counter       = %lu", counter);
9571 			WriteMessage(szTmp);
9572 			break;
9573 		case sTypeRFXMeterSetInterval:
9574 			WriteMessage("subtype       = Push the button for next mode within 5 seconds or else SET INTERVAL MODE will be entered");
9575 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->RFXMETER.seqnbr);
9576 			WriteMessage(szTmp);
9577 			sprintf(szTmp, "ID            = %d", (pResponse->RFXMETER.id1 * 256) + pResponse->RFXMETER.id2);
9578 			WriteMessage(szTmp);
9579 			break;
9580 		case sTypeRFXMeterSetCalib:
9581 			switch (pResponse->RFXMETER.count2 & 0xC0)
9582 			{
9583 			case 0x0:
9584 				WriteMessage("subtype       = Push the button for next mode within 5 seconds or else CALIBRATION mode for channel 1 will be executed");
9585 				break;
9586 			case 0x40:
9587 				WriteMessage("subtype       = Push the button for next mode within 5 seconds or else CALIBRATION mode for channel 2 will be executed");
9588 				break;
9589 			case 0x80:
9590 				WriteMessage("subtype       = Push the button for next mode within 5 seconds or else CALIBRATION mode for channel 3 will be executed");
9591 				break;
9592 			}
9593 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->RFXMETER.seqnbr);
9594 			WriteMessage(szTmp);
9595 			sprintf(szTmp, "ID            = %d", (pResponse->RFXMETER.id1 * 256) + pResponse->RFXMETER.id2);
9596 			WriteMessage(szTmp);
9597 			break;
9598 		case sTypeRFXMeterSetAddr:
9599 			WriteMessage("subtype       = Push the button for next mode within 5 seconds or else SET ADDRESS MODE will be entered");
9600 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->RFXMETER.seqnbr);
9601 			WriteMessage(szTmp);
9602 			sprintf(szTmp, "ID            = %d", (pResponse->RFXMETER.id1 * 256) + pResponse->RFXMETER.id2);
9603 			WriteMessage(szTmp);
9604 			break;
9605 		case sTypeRFXMeterIdent:
9606 			WriteMessage("subtype       = RFXMeter identification");
9607 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->RFXMETER.seqnbr);
9608 			WriteMessage(szTmp);
9609 			sprintf(szTmp, "ID            = %d", (pResponse->RFXMETER.id1 * 256) + pResponse->RFXMETER.id2);
9610 			WriteMessage(szTmp);
9611 			sprintf(szTmp, "FW version    = %02X", pResponse->RFXMETER.count3);
9612 			WriteMessage(szTmp);
9613 			WriteMessage("Interval time = ", false);
9614 
9615 			switch (pResponse->RFXMETER.count4)
9616 			{
9617 			case 0x1:
9618 				WriteMessage("30 seconds");
9619 				break;
9620 			case 0x2:
9621 				WriteMessage("1 minute");
9622 				break;
9623 			case 0x4:
9624 				WriteMessage("6 minutes");
9625 				break;
9626 			case 0x8:
9627 				WriteMessage("12 minutes");
9628 				break;
9629 			case 0x10:
9630 				WriteMessage("15 minutes");
9631 				break;
9632 			case 0x20:
9633 				WriteMessage("30 minutes");
9634 				break;
9635 			case 0x40:
9636 				WriteMessage("45 minutes");
9637 				break;
9638 			case 0x80:
9639 				WriteMessage("1 hour");
9640 				break;
9641 			}
9642 			break;
9643 		default:
9644 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->RFXMETER.packettype, pResponse->RFXMETER.subtype);
9645 			WriteMessage(szTmp);
9646 			break;
9647 		}
9648 		sprintf(szTmp, "Signal level  = %d", pResponse->RFXMETER.rssi);
9649 		WriteMessage(szTmp);
9650 		WriteMessageEnd();
9651 	}
9652 	procResult.DeviceRowIdx = DevRowIdx;
9653 }
9654 
decode_P1MeterPower(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)9655 void MainWorker::decode_P1MeterPower(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
9656 {
9657 	char szTmp[200];
9658 	const _tP1Power* p1Power = reinterpret_cast<const _tP1Power*>(pResponse);
9659 
9660 	if (p1Power->len != sizeof(_tP1Power) - 1)
9661 		return;
9662 
9663 	uint8_t devType = p1Power->type;
9664 	uint8_t subType = p1Power->subtype;
9665 	std::string ID;
9666 	sprintf(szTmp, "%d", p1Power->ID);
9667 	ID = szTmp;
9668 
9669 	uint8_t Unit = subType;
9670 	uint8_t cmnd = 0;
9671 	uint8_t SignalLevel = 12;
9672 	uint8_t BatteryLevel = 255;
9673 
9674 	sprintf(szTmp, "%u;%u;%u;%u;%u;%u",
9675 		p1Power->powerusage1,
9676 		p1Power->powerusage2,
9677 		p1Power->powerdeliv1,
9678 		p1Power->powerdeliv2,
9679 		p1Power->usagecurrent,
9680 		p1Power->delivcurrent
9681 	);
9682 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
9683 	if (DevRowIdx == (uint64_t)-1)
9684 		return;
9685 
9686 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, cmnd, szTmp);
9687 
9688 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
9689 	{
9690 		WriteMessageStart();
9691 		switch (p1Power->subtype)
9692 		{
9693 		case sTypeP1Power:
9694 			WriteMessage("subtype       = P1 Smart Meter Power");
9695 
9696 			sprintf(szTmp, "powerusage1 = %.3f kWh", float(p1Power->powerusage1) / 1000.0f);
9697 			WriteMessage(szTmp);
9698 			sprintf(szTmp, "powerusage2 = %.3f kWh", float(p1Power->powerusage2) / 1000.0f);
9699 			WriteMessage(szTmp);
9700 
9701 			sprintf(szTmp, "powerdeliv1 = %.3f kWh", float(p1Power->powerdeliv1) / 1000.0f);
9702 			WriteMessage(szTmp);
9703 			sprintf(szTmp, "powerdeliv2 = %.3f kWh", float(p1Power->powerdeliv2) / 1000.0f);
9704 			WriteMessage(szTmp);
9705 
9706 			sprintf(szTmp, "current usage = %03u Watt", p1Power->usagecurrent);
9707 			WriteMessage(szTmp);
9708 			sprintf(szTmp, "current deliv = %03u Watt", p1Power->delivcurrent);
9709 			WriteMessage(szTmp);
9710 			break;
9711 		default:
9712 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", p1Power->type, p1Power->subtype);
9713 			WriteMessage(szTmp);
9714 			break;
9715 		}
9716 		WriteMessageEnd();
9717 	}
9718 	procResult.DeviceRowIdx = DevRowIdx;
9719 }
9720 
decode_P1MeterGas(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)9721 void MainWorker::decode_P1MeterGas(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
9722 {
9723 	char szTmp[200];
9724 	const _tP1Gas* p1Gas = reinterpret_cast<const _tP1Gas*>(pResponse);
9725 	if (p1Gas->len != sizeof(_tP1Gas) - 1)
9726 		return;
9727 
9728 	uint8_t devType = p1Gas->type;
9729 	uint8_t subType = p1Gas->subtype;
9730 	std::string ID;
9731 	sprintf(szTmp, "%d", p1Gas->ID);
9732 	ID = szTmp;
9733 	uint8_t Unit = subType;
9734 	uint8_t cmnd = 0;
9735 	uint8_t SignalLevel = 12;
9736 	uint8_t BatteryLevel = 255;
9737 
9738 	sprintf(szTmp, "%u", p1Gas->gasusage);
9739 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
9740 	if (DevRowIdx == (uint64_t)-1)
9741 		return;
9742 
9743 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
9744 	{
9745 		WriteMessageStart();
9746 		switch (p1Gas->subtype)
9747 		{
9748 		case sTypeP1Gas:
9749 			WriteMessage("subtype       = P1 Smart Meter Gas");
9750 
9751 			sprintf(szTmp, "gasusage = %.3f m3", float(p1Gas->gasusage) / 1000.0f);
9752 			WriteMessage(szTmp);
9753 			break;
9754 		default:
9755 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", p1Gas->type, p1Gas->subtype);
9756 			WriteMessage(szTmp);
9757 			break;
9758 		}
9759 		WriteMessageEnd();
9760 	}
9761 	procResult.DeviceRowIdx = DevRowIdx;
9762 }
9763 
decode_YouLessMeter(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)9764 void MainWorker::decode_YouLessMeter(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
9765 {
9766 	char szTmp[200];
9767 	const CYouLess::YouLessMeter* pMeter = reinterpret_cast<const CYouLess::YouLessMeter*>(pResponse);
9768 	uint8_t devType = pMeter->type;
9769 	uint8_t subType = pMeter->subtype;
9770 	sprintf(szTmp, "%d", pMeter->ID1);
9771 	std::string ID = szTmp;
9772 	uint8_t Unit = subType;
9773 	uint8_t cmnd = 0;
9774 	uint8_t SignalLevel = 12;
9775 	uint8_t BatteryLevel = 255;
9776 
9777 	sprintf(szTmp, "%lu;%lu",
9778 		pMeter->powerusage,
9779 		pMeter->usagecurrent
9780 	);
9781 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
9782 	if (DevRowIdx == (uint64_t)-1)
9783 		return;
9784 
9785 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, cmnd, szTmp);
9786 
9787 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
9788 	{
9789 		WriteMessageStart();
9790 		switch (pMeter->subtype)
9791 		{
9792 		case sTypeYouLess:
9793 			WriteMessage("subtype       = YouLess Meter");
9794 
9795 			sprintf(szTmp, "powerusage = %.3f kWh", float(pMeter->powerusage) / 1000.0f);
9796 			WriteMessage(szTmp);
9797 			sprintf(szTmp, "current usage = %03lu Watt", pMeter->usagecurrent);
9798 			WriteMessage(szTmp);
9799 			break;
9800 		default:
9801 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pMeter->type, pMeter->subtype);
9802 			WriteMessage(szTmp);
9803 			break;
9804 		}
9805 		WriteMessageEnd();
9806 	}
9807 	procResult.DeviceRowIdx = DevRowIdx;
9808 }
9809 
decode_Rego6XXTemp(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)9810 void MainWorker::decode_Rego6XXTemp(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
9811 {
9812 	char szTmp[200];
9813 	const _tRego6XXTemp* pRego = reinterpret_cast<const _tRego6XXTemp*>(pResponse);
9814 	uint8_t devType = pRego->type;
9815 	uint8_t subType = pRego->subtype;
9816 	std::string ID = pRego->ID;
9817 	uint8_t Unit = subType;
9818 	uint8_t cmnd = 0;
9819 	uint8_t SignalLevel = 12;
9820 	uint8_t BatteryLevel = 255;
9821 
9822 	sprintf(szTmp, "%.1f",
9823 		pRego->temperature
9824 	);
9825 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
9826 	if (DevRowIdx == (uint64_t)-1)
9827 		return;
9828 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, pRego->temperature);
9829 
9830 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
9831 	{
9832 		WriteMessageStart();
9833 		WriteMessage("subtype       = Rego6XX Temp");
9834 
9835 		sprintf(szTmp, "Temp = %.1f", pRego->temperature);
9836 		WriteMessage(szTmp);
9837 		WriteMessageEnd();
9838 	}
9839 	procResult.DeviceRowIdx = DevRowIdx;
9840 }
9841 
decode_Rego6XXValue(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)9842 void MainWorker::decode_Rego6XXValue(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
9843 {
9844 	char szTmp[200];
9845 	const _tRego6XXStatus* pRego = reinterpret_cast<const _tRego6XXStatus*>(pResponse);
9846 	uint8_t devType = pRego->type;
9847 	uint8_t subType = pRego->subtype;
9848 	std::string ID = pRego->ID;
9849 	uint8_t Unit = subType;
9850 	int numValue = pRego->value;
9851 	uint8_t SignalLevel = 12;
9852 	uint8_t BatteryLevel = 255;
9853 
9854 	sprintf(szTmp, "%d",
9855 		pRego->value
9856 	);
9857 
9858 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, numValue, szTmp, procResult.DeviceName);
9859 	if (DevRowIdx == (uint64_t)-1)
9860 		return;
9861 
9862 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
9863 	{
9864 		WriteMessageStart();
9865 		switch (pRego->subtype)
9866 		{
9867 		case sTypeRego6XXStatus:
9868 			WriteMessage("subtype       = Rego6XX Status");
9869 			sprintf(szTmp, "Status = %d", pRego->value);
9870 			WriteMessage(szTmp);
9871 			break;
9872 		case sTypeRego6XXCounter:
9873 			WriteMessage("subtype       = Rego6XX Counter");
9874 			sprintf(szTmp, "Counter = %d", pRego->value);
9875 			WriteMessage(szTmp);
9876 			break;
9877 		}
9878 		WriteMessageEnd();
9879 	}
9880 	procResult.DeviceRowIdx = DevRowIdx;
9881 }
9882 
decode_AirQuality(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)9883 void MainWorker::decode_AirQuality(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
9884 {
9885 	char szTmp[200];
9886 	const _tAirQualityMeter* pMeter = reinterpret_cast<const _tAirQualityMeter*>(pResponse);
9887 	uint8_t devType = pMeter->type;
9888 	uint8_t subType = pMeter->subtype;
9889 	sprintf(szTmp, "%d", pMeter->id1);
9890 	std::string ID = szTmp;
9891 	uint8_t Unit = pMeter->id2;
9892 	//uint8_t cmnd=0;
9893 	uint8_t SignalLevel = 12;
9894 	uint8_t BatteryLevel = 255;
9895 
9896 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, pMeter->airquality, procResult.DeviceName);
9897 	if (DevRowIdx == (uint64_t)-1)
9898 		return;
9899 
9900 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, (const int)pMeter->airquality);
9901 
9902 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
9903 	{
9904 		WriteMessageStart();
9905 		switch (pMeter->subtype)
9906 		{
9907 		case sTypeVoltcraft:
9908 			WriteMessage("subtype       = Voltcraft CO-20");
9909 
9910 			sprintf(szTmp, "CO2 = %d ppm", pMeter->airquality);
9911 			WriteMessage(szTmp);
9912 			if (pMeter->airquality < 700)
9913 				strcpy(szTmp, "Quality = Excellent");
9914 			else if (pMeter->airquality < 900)
9915 				strcpy(szTmp, "Quality = Good");
9916 			else if (pMeter->airquality < 1100)
9917 				strcpy(szTmp, "Quality = Fair");
9918 			else if (pMeter->airquality < 1600)
9919 				strcpy(szTmp, "Quality = Mediocre");
9920 			else
9921 				strcpy(szTmp, "Quality = Bad");
9922 			WriteMessage(szTmp);
9923 			break;
9924 		default:
9925 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pMeter->type, pMeter->subtype);
9926 			WriteMessage(szTmp);
9927 			break;
9928 		}
9929 		WriteMessageEnd();
9930 	}
9931 	procResult.DeviceRowIdx = DevRowIdx;
9932 }
9933 
decode_Usage(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)9934 void MainWorker::decode_Usage(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
9935 {
9936 	char szTmp[200];
9937 	const _tUsageMeter* pMeter = reinterpret_cast<const _tUsageMeter*>(pResponse);
9938 	uint8_t devType = pMeter->type;
9939 	uint8_t subType = pMeter->subtype;
9940 	sprintf(szTmp, "%X%02X%02X%02X", pMeter->id1, pMeter->id2, pMeter->id3, pMeter->id4);
9941 	std::string ID = szTmp;
9942 	uint8_t Unit = pMeter->dunit;
9943 	uint8_t cmnd = 0;
9944 	uint8_t SignalLevel = 12;
9945 	uint8_t BatteryLevel = 255;
9946 
9947 	sprintf(szTmp, "%.1f", pMeter->fusage);
9948 
9949 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
9950 	if (DevRowIdx == (uint64_t)-1)
9951 		return;
9952 
9953 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, pMeter->fusage);
9954 
9955 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
9956 	{
9957 		WriteMessageStart();
9958 		switch (pMeter->subtype)
9959 		{
9960 		case sTypeElectric:
9961 			WriteMessage("subtype       = Electric");
9962 
9963 			sprintf(szTmp, "Usage = %.1f W", pMeter->fusage);
9964 			WriteMessage(szTmp);
9965 			break;
9966 		default:
9967 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pMeter->type, pMeter->subtype);
9968 			WriteMessage(szTmp);
9969 			break;
9970 		}
9971 		WriteMessageEnd();
9972 	}
9973 	procResult.DeviceRowIdx = DevRowIdx;
9974 }
9975 
decode_Lux(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)9976 void MainWorker::decode_Lux(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
9977 {
9978 	char szTmp[200];
9979 	const _tLightMeter* pMeter = reinterpret_cast<const _tLightMeter*>(pResponse);
9980 	uint8_t devType = pMeter->type;
9981 	uint8_t subType = pMeter->subtype;
9982 	sprintf(szTmp, "%X%02X%02X%02X", pMeter->id1, pMeter->id2, pMeter->id3, pMeter->id4);
9983 	std::string ID = szTmp;
9984 	uint8_t Unit = pMeter->dunit;
9985 	uint8_t cmnd = 0;
9986 	uint8_t SignalLevel = 12;
9987 	uint8_t BatteryLevel = pMeter->battery_level;
9988 
9989 	sprintf(szTmp, "%.0f", pMeter->fLux);
9990 
9991 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
9992 	if (DevRowIdx == (uint64_t)-1)
9993 		return;
9994 
9995 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, pMeter->fLux);
9996 
9997 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
9998 	{
9999 		WriteMessageStart();
10000 		switch (pMeter->subtype)
10001 		{
10002 		case sTypeLux:
10003 			WriteMessage("subtype       = Lux");
10004 
10005 			sprintf(szTmp, "Lux = %.1f W", pMeter->fLux);
10006 			WriteMessage(szTmp);
10007 			break;
10008 		default:
10009 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pMeter->type, pMeter->subtype);
10010 			WriteMessage(szTmp);
10011 			break;
10012 		}
10013 		WriteMessageEnd();
10014 	}
10015 	procResult.DeviceRowIdx = DevRowIdx;
10016 }
10017 
decode_Thermostat(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)10018 void MainWorker::decode_Thermostat(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
10019 {
10020 	char szTmp[200];
10021 	const _tThermostat* pMeter = reinterpret_cast<const _tThermostat*>(pResponse);
10022 	uint8_t devType = pMeter->type;
10023 	uint8_t subType = pMeter->subtype;
10024 	sprintf(szTmp, "%X%02X%02X%02X", pMeter->id1, pMeter->id2, pMeter->id3, pMeter->id4);
10025 	std::string ID = szTmp;
10026 	uint8_t Unit = pMeter->dunit;
10027 	uint8_t cmnd = 0;
10028 	uint8_t SignalLevel = 12;
10029 	uint8_t BatteryLevel = pMeter->battery_level;
10030 
10031 	switch (pMeter->subtype)
10032 	{
10033 	case sTypeThermSetpoint:
10034 		sprintf(szTmp, "%.2f", pMeter->temp);
10035 		break;
10036 	default:
10037 		sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pMeter->type, pMeter->subtype);
10038 		WriteMessage(szTmp);
10039 		return;
10040 	}
10041 
10042 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10043 	if (DevRowIdx == (uint64_t)-1)
10044 		return;
10045 
10046 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, pMeter->temp);
10047 
10048 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
10049 	{
10050 		WriteMessageStart();
10051 		double tvalue = ConvertTemperature(pMeter->temp, m_sql.m_tempsign[0]);
10052 		switch (pMeter->subtype)
10053 		{
10054 		case sTypeThermSetpoint:
10055 			WriteMessage("subtype       = SetPoint");
10056 			sprintf(szTmp, "Temp = %.2f", tvalue);
10057 			WriteMessage(szTmp);
10058 			break;
10059 		default:
10060 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pMeter->type, pMeter->subtype);
10061 			WriteMessage(szTmp);
10062 			break;
10063 		}
10064 		WriteMessageEnd();
10065 	}
10066 	procResult.DeviceRowIdx = DevRowIdx;
10067 }
10068 
decode_General(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult,const uint8_t SignalLevel,const uint8_t BatteryLevel)10069 void MainWorker::decode_General(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult, const uint8_t SignalLevel, const uint8_t BatteryLevel)
10070 {
10071 	char szTmp[200];
10072 	const _tGeneralDevice* pMeter = reinterpret_cast<const _tGeneralDevice*>(pResponse);
10073 	uint8_t devType = pMeter->type;
10074 	uint8_t subType = pMeter->subtype;
10075 
10076 	if (
10077 		(subType == sTypeVoltage) ||
10078 		(subType == sTypeCurrent) ||
10079 		(subType == sTypePercentage) ||
10080 		(subType == sTypeWaterflow) ||
10081 		(subType == sTypePressure) ||
10082 		(subType == sTypeZWaveClock) ||
10083 		(subType == sTypeZWaveThermostatMode) ||
10084 		(subType == sTypeZWaveThermostatFanMode) ||
10085 		(subType == sTypeZWaveThermostatOperatingState) ||
10086 		(subType == sTypeFan) ||
10087 		(subType == sTypeTextStatus) ||
10088 		(subType == sTypeSoundLevel) ||
10089 		(subType == sTypeBaro) ||
10090 		(subType == sTypeDistance) ||
10091 		(subType == sTypeSoilMoisture) ||
10092 		(subType == sTypeCustom) ||
10093 		(subType == sTypeKwh) ||
10094 		(subType == sTypeZWaveAlarm)
10095 		)
10096 	{
10097 		sprintf(szTmp, "%08X", (unsigned int)pMeter->intval1);
10098 	}
10099 	else
10100 	{
10101 		sprintf(szTmp, "%d", pMeter->id);
10102 
10103 	}
10104 	std::string ID = szTmp;
10105 	uint8_t Unit = 1;
10106 	uint8_t cmnd = 0;
10107 	strcpy(szTmp, "");
10108 
10109 	uint64_t DevRowIdx = -1;
10110 
10111 	if (subType == sTypeVisibility)
10112 	{
10113 		sprintf(szTmp, "%.1f", pMeter->floatval1);
10114 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10115 		if (DevRowIdx == (uint64_t)-1)
10116 			return;
10117 	}
10118 	else if (subType == sTypeDistance)
10119 	{
10120 		sprintf(szTmp, "%.1f", pMeter->floatval1);
10121 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10122 		if (DevRowIdx == (uint64_t)-1)
10123 			return;
10124 	}
10125 	else if (subType == sTypeSolarRadiation)
10126 	{
10127 		sprintf(szTmp, "%.1f", pMeter->floatval1);
10128 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10129 		if (DevRowIdx == (uint64_t)-1)
10130 			return;
10131 	}
10132 	else if (subType == sTypeSoilMoisture)
10133 	{
10134 		cmnd = pMeter->intval2;
10135 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, procResult.DeviceName);
10136 		if (DevRowIdx == (uint64_t)-1)
10137 			return;
10138 	}
10139 	else if (subType == sTypeLeafWetness)
10140 	{
10141 		cmnd = pMeter->intval1;
10142 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, procResult.DeviceName);
10143 		if (DevRowIdx == (uint64_t)-1)
10144 			return;
10145 	}
10146 	else if (subType == sTypeVoltage)
10147 	{
10148 		sprintf(szTmp, "%.3f", pMeter->floatval1);
10149 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10150 		if (DevRowIdx == (uint64_t)-1)
10151 			return;
10152 	}
10153 	else if (subType == sTypeCurrent)
10154 	{
10155 		sprintf(szTmp, "%.3f", pMeter->floatval1);
10156 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10157 		if (DevRowIdx == (uint64_t)-1)
10158 			return;
10159 	}
10160 	else if (subType == sTypeBaro)
10161 	{
10162 		sprintf(szTmp, "%.02f;%d", pMeter->floatval1, pMeter->intval2);
10163 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10164 		if (DevRowIdx == (uint64_t)-1)
10165 			return;
10166 	}
10167 	else if (subType == sTypePressure)
10168 	{
10169 		sprintf(szTmp, "%.1f", pMeter->floatval1);
10170 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10171 		if (DevRowIdx == (uint64_t)-1)
10172 			return;
10173 	}
10174 	else if (subType == sTypePercentage)
10175 	{
10176 		sprintf(szTmp, "%.2f", pMeter->floatval1);
10177 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10178 		if (DevRowIdx == (uint64_t)-1)
10179 			return;
10180 	}
10181 	else if (subType == sTypeWaterflow)
10182 	{
10183 		sprintf(szTmp, "%.2f", pMeter->floatval1);
10184 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10185 		if (DevRowIdx == (uint64_t)-1)
10186 			return;
10187 	}
10188 	else if (subType == sTypeFan)
10189 	{
10190 		sprintf(szTmp, "%d", pMeter->intval2);
10191 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10192 		if (DevRowIdx == (uint64_t)-1)
10193 			return;
10194 	}
10195 	else if (subType == sTypeSoundLevel)
10196 	{
10197 		sprintf(szTmp, "%d", pMeter->intval2);
10198 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10199 		if (DevRowIdx == (uint64_t)-1)
10200 			return;
10201 	}
10202 	else if (subType == sTypeZWaveClock)
10203 	{
10204 		int tintval = pMeter->intval2;
10205 		int day = tintval / (24 * 60); tintval -= (day * 24 * 60);
10206 		int hour = tintval / (60); tintval -= (hour * 60);
10207 		int minute = tintval;
10208 		sprintf(szTmp, "%d;%d;%d", day, hour, minute);
10209 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10210 	}
10211 	else if ((subType == sTypeZWaveThermostatMode) || (subType == sTypeZWaveThermostatFanMode) || (subType == sTypeZWaveThermostatOperatingState))
10212 	{
10213 		cmnd = (uint8_t)pMeter->intval2;
10214 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, procResult.DeviceName);
10215 	}
10216 	else if (subType == sTypeKwh)
10217 	{
10218 		sprintf(szTmp, "%.3f;%.3f", pMeter->floatval1, pMeter->floatval2);
10219 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10220 	}
10221 	else if (subType == sTypeAlert)
10222 	{
10223 		if (strcmp(pMeter->text, ""))
10224 			sprintf(szTmp, "(%d) %s", pMeter->intval1, pMeter->text);
10225 		else
10226 			sprintf(szTmp, "%d", pMeter->intval1);
10227 		cmnd = pMeter->intval1;
10228 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10229 		if (DevRowIdx == (uint64_t)-1)
10230 			return;
10231 	}
10232 	else if (subType == sTypeCustom)
10233 	{
10234 		sprintf(szTmp, "%.4f", pMeter->floatval1);
10235 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10236 		if (DevRowIdx == (uint64_t)-1)
10237 			return;
10238 	}
10239 	else if (subType == sTypeZWaveAlarm)
10240 	{
10241 		Unit = pMeter->id;
10242 		cmnd = pMeter->intval2;
10243 		strcpy(szTmp, pMeter->text);
10244 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10245 		if (DevRowIdx == (uint64_t)-1)
10246 			return;
10247 	}
10248 	else if (subType == sTypeTextStatus)
10249 	{
10250 		strcpy(szTmp, pMeter->text);
10251 		DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, szTmp, procResult.DeviceName);
10252 	}
10253 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, cmnd, szTmp);
10254 
10255 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
10256 	{
10257 		WriteMessageStart();
10258 		switch (pMeter->subtype)
10259 		{
10260 		case sTypeVisibility:
10261 			WriteMessage("subtype       = Visibility");
10262 			sprintf(szTmp, "Visibility = %.1f km", pMeter->floatval1);
10263 			WriteMessage(szTmp);
10264 			break;
10265 		case sTypeDistance:
10266 			WriteMessage("subtype       = Distance");
10267 			sprintf(szTmp, "Distance = %.1f cm", pMeter->floatval1);
10268 			WriteMessage(szTmp);
10269 			break;
10270 		case sTypeSolarRadiation:
10271 			WriteMessage("subtype       = Solar Radiation");
10272 			sprintf(szTmp, "Radiation = %.1f Watt/m2", pMeter->floatval1);
10273 			WriteMessage(szTmp);
10274 			break;
10275 		case sTypeSoilMoisture:
10276 			WriteMessage("subtype       = Soil Moisture");
10277 			sprintf(szTmp, "Moisture = %d cb", pMeter->intval2);
10278 			WriteMessage(szTmp);
10279 			break;
10280 		case sTypeLeafWetness:
10281 			WriteMessage("subtype       = Leaf Wetness");
10282 			sprintf(szTmp, "Wetness = %d", pMeter->intval1);
10283 			WriteMessage(szTmp);
10284 			break;
10285 		case sTypeVoltage:
10286 			WriteMessage("subtype       = Voltage");
10287 			sprintf(szTmp, "Voltage = %.3f V", pMeter->floatval1);
10288 			WriteMessage(szTmp);
10289 			break;
10290 		case sTypeCurrent:
10291 			WriteMessage("subtype       = Current");
10292 			sprintf(szTmp, "Current = %.3f V", pMeter->floatval1);
10293 			WriteMessage(szTmp);
10294 			break;
10295 		case sTypePressure:
10296 			WriteMessage("subtype       = Pressure");
10297 			sprintf(szTmp, "Pressure = %.1f bar", pMeter->floatval1);
10298 			WriteMessage(szTmp);
10299 			break;
10300 		case sTypeBaro:
10301 			WriteMessage("subtype       = Barometric Pressure");
10302 			sprintf(szTmp, "Pressure = %.1f hPa", pMeter->floatval1);
10303 			WriteMessage(szTmp);
10304 			break;
10305 		case sTypeFan:
10306 			WriteMessage("subtype       = Fan");
10307 			sprintf(szTmp, "Speed = %d RPM", pMeter->intval2);
10308 			WriteMessage(szTmp);
10309 			break;
10310 		case sTypeSoundLevel:
10311 			WriteMessage("subtype       = Sound Level");
10312 			sprintf(szTmp, "Sound Level = %d dB", pMeter->intval2);
10313 			WriteMessage(szTmp);
10314 			break;
10315 		case sTypeZWaveClock:
10316 		{
10317 			int tintval = pMeter->intval2;
10318 			int day = tintval / (24 * 60); tintval -= (day * 24 * 60);
10319 			int hour = tintval / (60); tintval -= (hour * 60);
10320 			int minute = tintval;
10321 			WriteMessage("subtype       = Thermostat Clock");
10322 			sprintf(szTmp, "Clock = %s %02d:%02d", ZWave_Clock_Days(day), hour, minute);
10323 			WriteMessage(szTmp);
10324 		}
10325 		break;
10326 		case sTypeZWaveThermostatMode:
10327 			WriteMessage("subtype       = Thermostat Mode");
10328 			//sprintf(szTmp, "Mode = %d (%s)", pMeter->intval2, ZWave_Thermostat_Modes[pMeter->intval2]);
10329 			sprintf(szTmp, "Mode = %d", pMeter->intval2);
10330 			WriteMessage(szTmp);
10331 			break;
10332 		case sTypeZWaveThermostatFanMode:
10333 			WriteMessage("subtype       = Thermostat Fan Mode");
10334 			sprintf(szTmp, "Mode = %d (%s)", pMeter->intval2, ZWave_Thermostat_Fan_Modes[pMeter->intval2]);
10335 			WriteMessage(szTmp);
10336 			break;
10337 		case sTypeZWaveThermostatOperatingState:
10338 			WriteMessage("subtype       = Thermostat Operating State");
10339 			sprintf(szTmp, "State = %d", pMeter->intval2);
10340 			WriteMessage(szTmp);
10341 			break;
10342 		case sTypePercentage:
10343 			WriteMessage("subtype       = Percentage");
10344 			sprintf(szTmp, "Percentage = %.2f", pMeter->floatval1);
10345 			WriteMessage(szTmp);
10346 			break;
10347 		case sTypeWaterflow:
10348 			WriteMessage("subtype       = Waterflow");
10349 			sprintf(szTmp, "l/min = %.2f", pMeter->floatval1);
10350 			WriteMessage(szTmp);
10351 			break;
10352 		case sTypeKwh:
10353 			WriteMessage("subtype       = kWh");
10354 			sprintf(szTmp, "Instant = %.3f", pMeter->floatval1);
10355 			WriteMessage(szTmp);
10356 			sprintf(szTmp, "Counter = %.3f", pMeter->floatval2 / 1000.0f);
10357 			WriteMessage(szTmp);
10358 			break;
10359 		case sTypeAlert:
10360 			WriteMessage("subtype       = Alert");
10361 			sprintf(szTmp, "Alert = %d", pMeter->intval1);
10362 			WriteMessage(szTmp);
10363 			break;
10364 		case sTypeCustom:
10365 			WriteMessage("subtype       = Custom Sensor");
10366 			sprintf(szTmp, "Value = %g", pMeter->floatval1);
10367 			WriteMessage(szTmp);
10368 			break;
10369 		case sTypeZWaveAlarm:
10370 			WriteMessage("subtype       = Alarm");
10371 			sprintf(szTmp, "Level = %d (0x%02X) %s", pMeter->intval2, pMeter->intval2, pMeter->text);
10372 			WriteMessage(szTmp);
10373 			break;
10374 		case sTypeTextStatus:
10375 			WriteMessage("subtype       = Text");
10376 			sprintf(szTmp, "Text = %s", pMeter->text);
10377 			WriteMessage(szTmp);
10378 			break;
10379 		default:
10380 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pMeter->type, pMeter->subtype);
10381 			WriteMessage(szTmp);
10382 			break;
10383 		}
10384 		WriteMessageEnd();
10385 	}
10386 	procResult.DeviceRowIdx = DevRowIdx;
10387 }
10388 
decode_GeneralSwitch(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)10389 void MainWorker::decode_GeneralSwitch(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
10390 {
10391 	char szTmp[200];
10392 	const _tGeneralSwitch* pSwitch = reinterpret_cast<const _tGeneralSwitch*>(pResponse);
10393 	uint8_t devType = pSwitch->type;
10394 	uint8_t subType = pSwitch->subtype;
10395 
10396 	sprintf(szTmp, "%08X", pSwitch->id);
10397 	std::string ID = szTmp;
10398 	uint8_t Unit = pSwitch->unitcode;
10399 	uint8_t cmnd = pSwitch->cmnd;
10400 	uint8_t level = pSwitch->level;
10401 	uint8_t SignalLevel = pSwitch->rssi;
10402 
10403 	sprintf(szTmp, "%d", level);
10404 	uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, -1, cmnd, szTmp, procResult.DeviceName);
10405 	if (DevRowIdx == (uint64_t)-1)
10406 		return;
10407 	uint8_t check_cmnd = cmnd;
10408 	if ((cmnd == gswitch_sGroupOff) || (cmnd == gswitch_sGroupOn))
10409 		check_cmnd = (cmnd == gswitch_sGroupOff) ? gswitch_sOff : gswitch_sOn;
10410 	CheckSceneCode(DevRowIdx, devType, subType, check_cmnd, szTmp, procResult.DeviceName);
10411 
10412 	if ((cmnd == gswitch_sGroupOff) || (cmnd == gswitch_sGroupOn))
10413 	{
10414 		//set the status of all lights with the same code to on/off
10415 		m_sql.GeneralSwitchGroupCmd(ID, subType, (cmnd == gswitch_sGroupOff) ? gswitch_sOff : gswitch_sOn);
10416 	}
10417 
10418 	procResult.DeviceRowIdx = DevRowIdx;
10419 }
10420 
10421 //BBQ sensor has two temperature sensors, add them as two temperature devices
decode_BBQ(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)10422 void MainWorker::decode_BBQ(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
10423 {
10424 	char szTmp[100];
10425 	uint8_t devType = pTypeBBQ;
10426 	uint8_t subType = pResponse->BBQ.subtype;
10427 
10428 	sprintf(szTmp, "%d", 1);//(pResponse->BBQ.id1 * 256) + pResponse->BBQ.id2); //this because every time you turn the device on, you get a new ID
10429 	std::string ID = szTmp;
10430 
10431 	//The transmitter and receiver are negotiating a new ID every 15/20 minutes,
10432 	//for this we need to work with fixed ID's
10433 	uint8_t Unit = 1;// pResponse->BBQ.id2;
10434 
10435 	uint8_t cmnd = 0;
10436 	uint8_t SignalLevel = pResponse->BBQ.rssi;
10437 	uint8_t BatteryLevel = 0;
10438 	if ((pResponse->BBQ.battery_level & 0x0F) == 0)
10439 		BatteryLevel = 0;
10440 	else
10441 		BatteryLevel = 100;
10442 
10443 	uint64_t DevRowIdx = 0;
10444 
10445 	float temp1, temp2;
10446 	temp1 = float((pResponse->BBQ.sensor1h * 256) + pResponse->BBQ.sensor1l);// / 10.0f;
10447 	temp2 = float((pResponse->BBQ.sensor2h * 256) + pResponse->BBQ.sensor2l);// / 10.0f;
10448 
10449 	sprintf(szTmp, "%.0f;%.0f", temp1, temp2);
10450 	DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10451 	if (DevRowIdx == (uint64_t)-1)
10452 		return;
10453 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
10454 	{
10455 		WriteMessageStart();
10456 		switch (pResponse->BBQ.subtype)
10457 		{
10458 		case sTypeBBQ1:
10459 			WriteMessage("subtype       = Maverick ET-732/733");
10460 			break;
10461 		default:
10462 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->BBQ.packettype, pResponse->BBQ.subtype);
10463 			WriteMessage(szTmp);
10464 			break;
10465 		}
10466 
10467 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->BBQ.seqnbr);
10468 		WriteMessage(szTmp);
10469 		sprintf(szTmp, "ID            = %d", (pResponse->BBQ.id1 * 256) + pResponse->BBQ.id2);
10470 		WriteMessage(szTmp);
10471 
10472 		sprintf(szTmp, "Sensor1 Temp  = %.1f C", temp1);
10473 		WriteMessage(szTmp);
10474 		sprintf(szTmp, "Sensor2 Temp  = %.1f C", temp2);
10475 		WriteMessage(szTmp);
10476 
10477 		sprintf(szTmp, "Signal level  = %d", pResponse->BBQ.rssi);
10478 		WriteMessage(szTmp);
10479 
10480 		if ((pResponse->BBQ.battery_level & 0x0F) == 0)
10481 			WriteMessage("Battery       = Low");
10482 		else
10483 			WriteMessage("Battery       = OK");
10484 		WriteMessageEnd();
10485 	}
10486 
10487 	//Send the two sensors
10488 
10489 	//Temp
10490 	RBUF tsen;
10491 	memset(&tsen, 0, sizeof(RBUF));
10492 	tsen.TEMP.packetlength = sizeof(tsen.TEMP) - 1;
10493 	tsen.TEMP.packettype = pTypeTEMP;
10494 	tsen.TEMP.subtype = sTypeTEMP6;
10495 	tsen.TEMP.battery_level = 9;
10496 	tsen.TEMP.rssi = SignalLevel;
10497 	tsen.TEMP.id1 = 0;
10498 	tsen.TEMP.id2 = 1;
10499 
10500 	tsen.TEMP.tempsign = (temp1 >= 0) ? 0 : 1;
10501 	int at10 = round(std::abs(temp1 * 10.0f));
10502 	tsen.TEMP.temperatureh = (BYTE)(at10 / 256);
10503 	at10 -= (tsen.TEMP.temperatureh * 256);
10504 	tsen.TEMP.temperaturel = (BYTE)(at10);
10505 	_tRxMessageProcessingResult tmpProcResult1;
10506 	tmpProcResult1.DeviceName = "";
10507 	tmpProcResult1.DeviceRowIdx = -1;
10508 	decode_Temp(pHardware, (const tRBUF*)&tsen.TEMP, tmpProcResult1);
10509 
10510 	tsen.TEMP.id1 = 0;
10511 	tsen.TEMP.id2 = 2;
10512 
10513 	tsen.TEMP.tempsign = (temp2 >= 0) ? 0 : 1;
10514 	at10 = round(std::abs(temp2 * 10.0f));
10515 	tsen.TEMP.temperatureh = (BYTE)(at10 / 256);
10516 	at10 -= (tsen.TEMP.temperatureh * 256);
10517 	tsen.TEMP.temperaturel = (BYTE)(at10);
10518 	_tRxMessageProcessingResult tmpProcResult2;
10519 	tmpProcResult2.DeviceName = "";
10520 	tmpProcResult2.DeviceRowIdx = -1;
10521 	decode_Temp(pHardware, (const tRBUF*)&tsen.TEMP, tmpProcResult2);
10522 
10523 	procResult.DeviceRowIdx = DevRowIdx;
10524 }
10525 
decode_Cartelectronic(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)10526 void MainWorker::decode_Cartelectronic(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
10527 {
10528 	char szTmp[100];
10529 	std::string ID;
10530 
10531 	sprintf(szTmp, "%llu", ((unsigned long long)(pResponse->TIC.id1) << 32) + (pResponse->TIC.id2 << 24) + (pResponse->TIC.id3 << 16) + (pResponse->TIC.id4 << 8) + (pResponse->TIC.id5));
10532 	ID = szTmp;
10533 	//uint8_t Unit = 0;
10534 	//uint8_t cmnd = 0;
10535 
10536 	uint8_t subType = pResponse->TIC.subtype;
10537 
10538 	switch (subType)
10539 	{
10540 	case sTypeTIC:
10541 		decode_CartelectronicTIC(pHardware, pResponse, procResult);
10542 		WriteMessage("Cartelectronic TIC received");
10543 		break;
10544 	case sTypeCEencoder:
10545 		decode_CartelectronicEncoder(pHardware, pResponse, procResult);
10546 		WriteMessage("Cartelectronic Encoder received");
10547 		break;
10548 	default:
10549 		WriteMessage("Cartelectronic protocol not supported");
10550 		break;
10551 	}
10552 }
10553 
decode_ASyncPort(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)10554 void MainWorker::decode_ASyncPort(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
10555 {
10556 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
10557 	{
10558 		WriteMessageStart();
10559 
10560 		char szTmp[100];
10561 		//uint8_t subType = pResponse->ASYNCPORT.subtype;
10562 
10563 		switch (pResponse->ASYNCPORT.subtype)
10564 		{
10565 		case sTypeASYNCconfig:
10566 			WriteMessage("subtype       = Async port configure");
10567 			break;
10568 		default:
10569 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->ASYNCPORT.packettype, pResponse->ASYNCPORT.subtype);
10570 			WriteMessage(szTmp);
10571 			break;
10572 		}
10573 
10574 		sprintf(szTmp, "Sequence nbr  = %d", pResponse->ASYNCPORT.seqnbr);
10575 		WriteMessage(szTmp);
10576 
10577 		WriteMessage("Command     = ", false);
10578 		switch (pResponse->ASYNCPORT.cmnd)
10579 		{
10580 		case asyncdisable:
10581 			WriteMessage("Disable");
10582 			break;
10583 		case asyncreceiveP1:
10584 			WriteMessage("Enable P1 Receive");
10585 			break;
10586 		case asyncreceiveTeleinfo:
10587 			WriteMessage("Enable Teleinfo Recieve");
10588 			break;
10589 		case asyncreceiveRAW:
10590 			WriteMessage("Enable Raw Receive");
10591 			break;
10592 		default:
10593 			sprintf(szTmp, "ERROR: Unknown command type for Packet type= %02X:%02X", pResponse->ASYNCPORT.packettype, pResponse->ASYNCPORT.subtype);
10594 			WriteMessage(szTmp);
10595 			break;
10596 		}
10597 
10598 		if (pResponse->ASYNCPORT.cmnd != asyncdisable)
10599 		{
10600 			WriteMessage("Baudrate    = ", false);
10601 			switch (pResponse->ASYNCPORT.baudrate)
10602 			{
10603 			case asyncbaud110:
10604 				WriteMessage("110");
10605 				break;
10606 			case asyncbaud300:
10607 				WriteMessage("300");
10608 				break;
10609 			case asyncbaud600:
10610 				WriteMessage("600");
10611 				break;
10612 			case asyncbaud1200:
10613 				WriteMessage("1200");
10614 				break;
10615 			case asyncbaud2400:
10616 				WriteMessage("2400");
10617 				break;
10618 			case asyncbaud4800:
10619 				WriteMessage("4800");
10620 				break;
10621 			case asyncbaud9600:
10622 				WriteMessage("9600");
10623 				break;
10624 			case asyncbaud14400:
10625 				WriteMessage("14400");
10626 				break;
10627 			case asyncbaud19200:
10628 				WriteMessage("19200");
10629 				break;
10630 			case asyncbaud38400:
10631 				WriteMessage("38400");
10632 				break;
10633 			case asyncbaud57600:
10634 				WriteMessage("57600");
10635 				break;
10636 			case asyncbaud115200:
10637 				WriteMessage("115200");
10638 				break;
10639 			default:
10640 				sprintf(szTmp, "ERROR: Unknown baudrate type for Packet type= %02X:%02X", pResponse->ASYNCPORT.packettype, pResponse->ASYNCPORT.subtype);
10641 				WriteMessage(szTmp);
10642 				break;
10643 			}
10644 			WriteMessage("Parity      = ", false);
10645 			switch (pResponse->ASYNCPORT.parity)
10646 			{
10647 			case asyncParityNo:
10648 				WriteMessage("None");
10649 				break;
10650 			case asyncParityOdd:
10651 				WriteMessage("Odd");
10652 				break;
10653 			case asyncParityEven:
10654 				WriteMessage("Even");
10655 				break;
10656 			default:
10657 				sprintf(szTmp, "ERROR: Unknown partity type for Packet type= %02X:%02X", pResponse->ASYNCPORT.packettype, pResponse->ASYNCPORT.subtype);
10658 				WriteMessage(szTmp);
10659 				break;
10660 			}
10661 			WriteMessage("Databits    = ", false);
10662 			switch (pResponse->ASYNCPORT.databits)
10663 			{
10664 			case asyncDatabits7:
10665 				WriteMessage("7");
10666 				break;
10667 			case asyncDatabits8:
10668 				WriteMessage("8");
10669 				break;
10670 			default:
10671 				sprintf(szTmp, "ERROR: Unknown databits type for Packet type= %02X:%02X", pResponse->ASYNCPORT.packettype, pResponse->ASYNCPORT.subtype);
10672 				WriteMessage(szTmp);
10673 				break;
10674 			}
10675 			WriteMessage("Stopbits    = ", false);
10676 			switch (pResponse->ASYNCPORT.stopbits)
10677 			{
10678 			case asyncStopbits1:
10679 				WriteMessage("1");
10680 				break;
10681 			case asyncStopbits2:
10682 				WriteMessage("2");
10683 				break;
10684 			default:
10685 				sprintf(szTmp, "ERROR: Unknown stopbits type for Packet type= %02X:%02X", pResponse->ASYNCPORT.packettype, pResponse->ASYNCPORT.subtype);
10686 				WriteMessage(szTmp);
10687 				break;
10688 			}
10689 			WriteMessage("Polarity    = ", false);
10690 			switch (pResponse->ASYNCPORT.polarity)
10691 			{
10692 			case asyncPolarityNormal:
10693 				WriteMessage("Normal");
10694 				break;
10695 			case asyncPolarityInvers:
10696 				WriteMessage("Inverted");
10697 				break;
10698 			default:
10699 				sprintf(szTmp, "ERROR: Unknown stopbits type for Packet type= %02X:%02X", pResponse->ASYNCPORT.packettype, pResponse->ASYNCPORT.subtype);
10700 				WriteMessage(szTmp);
10701 				break;
10702 			}
10703 		}
10704 		WriteMessageEnd();
10705 	}
10706 }
10707 
decode_ASyncData(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)10708 void MainWorker::decode_ASyncData(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
10709 {
10710 	if (
10711 		(pHardware->m_HwdID == 999) ||
10712 		(pHardware->HwdType == HTYPE_RFXtrx315) ||
10713 		(pHardware->HwdType == HTYPE_RFXtrx433) ||
10714 		(pHardware->HwdType == HTYPE_RFXtrx868) ||
10715 		(pHardware->HwdType == HTYPE_RFXLAN)
10716 		)
10717 	{
10718 		CRFXBase* pRFXBase = (CRFXBase*)pHardware;
10719 		const uint8_t* pData = reinterpret_cast<const uint8_t*>(&pResponse->ASYNCDATA.datachar[0]);
10720 		int Len = pResponse->ASYNCDATA.packetlength - 3;
10721 		pRFXBase->Parse_Async_Data(pData, Len);
10722 	}
10723 }
10724 
decode_CartelectronicTIC(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)10725 void MainWorker::decode_CartelectronicTIC(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
10726 {
10727 	//Contract Options
10728 	typedef enum {
10729 		OP_NOT_DEFINED,
10730 		OP_BASE,
10731 		OP_CREUSE,
10732 		OP_EJP,
10733 		OP_TEMPO
10734 	} Contract;
10735 
10736 	//Running time
10737 	typedef enum {
10738 		PER_NOT_DEFINED,
10739 		PER_ALL_HOURS,             //TH..
10740 		PER_LOWCOST_HOURS,         //HC..
10741 		PER_HIGHCOST_HOURS,        //HP..
10742 		PER_NORMAL_HOURS,          //HN..
10743 		PER_MOBILE_PEAK_HOURS,     //PM..
10744 		PER_BLUE_LOWCOST_HOURS,    //HCJB
10745 		PER_WHITE_LOWCOST_HOURS,   //HCJW
10746 		PER_RED_LOWCOST_HOURS,     //HCJR
10747 		PER_BLUE_HIGHCOST_HOURS,   //HPJB
10748 		PER_WHITE_HIGHCOST_HOURS,  //HPJW
10749 		PER_RED_HIGHCOST_HOURS     //HPJR
10750 	} PeriodTime;
10751 
10752 	char szTmp[100];
10753 	uint32_t counter1 = 0;
10754 	uint32_t counter2 = 0;
10755 	unsigned int apparentPower = 0;
10756 	unsigned int counter1ApparentPower = 0;
10757 	unsigned int counter2ApparentPower = 0;
10758 	uint8_t unitCounter1 = 0;
10759 	uint8_t unitCounter2 = 1;
10760 	uint8_t cmnd = 0;
10761 	std::string ID;
10762 
10763 	uint8_t devType = pTypeGeneral;
10764 	uint8_t subType = sTypeKwh;
10765 	uint8_t SignalLevel = pResponse->TIC.rssi;
10766 
10767 	uint8_t BatteryLevel = (pResponse->TIC.battery_level + 1) * 10;
10768 
10769 	// If no error
10770 	if ((pResponse->TIC.state & 0x04) == 0)
10771 	{
10772 		// Id of the counter
10773 		uint64_t uint64ID = ((uint64_t)pResponse->TIC.id1 << 32) +
10774 			((uint64_t)pResponse->TIC.id2 << 24) +
10775 			((uint64_t)pResponse->TIC.id3 << 16) +
10776 			((uint64_t)pResponse->TIC.id4 << 8) +
10777 			(uint64_t)(pResponse->TIC.id5);
10778 
10779 		sprintf(szTmp, "%.12" PRIu64, uint64ID);
10780 		ID = szTmp;
10781 
10782 		// Contract Type
10783 		Contract contractType = (Contract)(pResponse->TIC.contract_type >> 4);
10784 
10785 		// Period
10786 		PeriodTime period = (PeriodTime)(pResponse->TIC.contract_type & 0x0f);
10787 
10788 		// Apparent Power
10789 		if ((pResponse->TIC.state & 0x02) != 0) // Only if this one is present
10790 		{
10791 			apparentPower = (pResponse->TIC.power_H << 8) + pResponse->TIC.power_L;
10792 
10793 			sprintf(szTmp, "%u", apparentPower);
10794 			uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), 1, pTypeUsage, sTypeElectric, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10795 
10796 			if (DevRowIdx == (uint64_t)-1)
10797 				return;
10798 
10799 			m_notifications.CheckAndHandleNotification(DevRowIdx, procResult.DeviceName, pTypeUsage, sTypeElectric, NTYPE_ENERGYINSTANT, (const float)apparentPower);
10800 		}
10801 
10802 		switch (contractType)
10803 		{
10804 			// BASE CONTRACT
10805 		case OP_BASE:
10806 		{
10807 			unitCounter1 = 0;
10808 			counter1ApparentPower = apparentPower;
10809 		}
10810 		break;
10811 
10812 		// LOW/HIGH PERIOD
10813 		case OP_CREUSE:
10814 		{
10815 			unitCounter1 = 0;
10816 
10817 			if (period == PER_LOWCOST_HOURS)
10818 			{
10819 				counter1ApparentPower = apparentPower;
10820 				counter2ApparentPower = 0;
10821 			}
10822 			if (period == PER_HIGHCOST_HOURS)
10823 			{
10824 				counter1ApparentPower = 0;
10825 				counter2ApparentPower = apparentPower;
10826 			}
10827 
10828 			// Counter 2
10829 			counter2 = (pResponse->TIC.counter2_0 << 24) + (pResponse->TIC.counter2_1 << 16) + (pResponse->TIC.counter2_2 << 8) + (pResponse->TIC.counter2_3);
10830 			sprintf(szTmp, "%u;%d", counter2ApparentPower, counter2);
10831 
10832 			uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), 1, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10833 
10834 			if (DevRowIdx == (uint64_t)-1)
10835 				return;
10836 
10837 			m_notifications.CheckAndHandleNotification(DevRowIdx, procResult.DeviceName, devType, subType, NTYPE_TODAYENERGY, (float)counter2);
10838 			//----------------------------
10839 		}
10840 		break;
10841 
10842 		// EJP
10843 		case OP_EJP:
10844 		{
10845 			unitCounter1 = 0;
10846 			// Counter 2
10847 			counter2 = (pResponse->TIC.counter2_0 << 24) + (pResponse->TIC.counter2_1 << 16) + (pResponse->TIC.counter2_2 << 8) + (pResponse->TIC.counter2_3);
10848 
10849 			sprintf(szTmp, "%u;%d", counter2ApparentPower, counter2);
10850 			uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), 1, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10851 
10852 			if (DevRowIdx == (uint64_t)-1)
10853 				return;
10854 
10855 			m_notifications.CheckAndHandleNotification(DevRowIdx, procResult.DeviceName, devType, subType, NTYPE_TODAYENERGY, (float)counter2);
10856 			//----------------------------
10857 		}
10858 		break;
10859 
10860 		// TEMPO Contracts
10861 		// Total of 6 counters. Theses counters depends of the time period received. For each period time, only 2 counters are sended.
10862 		case OP_TEMPO:
10863 		{
10864 			switch (period)
10865 			{
10866 			case PER_BLUE_LOWCOST_HOURS:
10867 				counter1ApparentPower = apparentPower;
10868 				counter2ApparentPower = 0;
10869 				unitCounter1 = 0;
10870 				unitCounter2 = 1;
10871 				break;
10872 			case PER_BLUE_HIGHCOST_HOURS:
10873 				counter1ApparentPower = 0;
10874 				counter2ApparentPower = apparentPower;
10875 				unitCounter1 = 0;
10876 				unitCounter2 = 1;
10877 				break;
10878 			case PER_WHITE_LOWCOST_HOURS:
10879 				counter1ApparentPower = apparentPower;
10880 				counter2ApparentPower = 0;
10881 				unitCounter1 = 2;
10882 				unitCounter2 = 3;
10883 				break;
10884 			case PER_WHITE_HIGHCOST_HOURS:
10885 				counter1ApparentPower = 0;
10886 				counter2ApparentPower = apparentPower;
10887 				unitCounter1 = 2;
10888 				unitCounter2 = 3;
10889 				break;
10890 			case PER_RED_LOWCOST_HOURS:
10891 				counter1ApparentPower = apparentPower;
10892 				counter2ApparentPower = 0;
10893 				unitCounter1 = 4;
10894 				unitCounter2 = 5;
10895 				break;
10896 			case PER_RED_HIGHCOST_HOURS:
10897 				counter1ApparentPower = 0;
10898 				counter2ApparentPower = apparentPower;
10899 				unitCounter1 = 4;
10900 				unitCounter2 = 5;
10901 				break;
10902 			default:
10903 				break;
10904 			}
10905 
10906 			// Counter 2
10907 			counter2 = (pResponse->TIC.counter2_0 << 24) + (pResponse->TIC.counter2_1 << 16) + (pResponse->TIC.counter2_2 << 8) + (pResponse->TIC.counter2_3);
10908 
10909 			sprintf(szTmp, "%u;%d", counter2ApparentPower, counter2);
10910 			uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), unitCounter2, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10911 
10912 			if (DevRowIdx == (uint64_t)-1)
10913 				return;
10914 
10915 			m_notifications.CheckAndHandleNotification(DevRowIdx, procResult.DeviceName, devType, subType, NTYPE_TODAYENERGY, (float)counter2);
10916 			//----------------------------
10917 		}
10918 		break;
10919 		default:
10920 			break;
10921 		}
10922 
10923 		// Counter 1
10924 		counter1 = (pResponse->TIC.counter1_0 << 24) + (pResponse->TIC.counter1_1 << 16) + (pResponse->TIC.counter1_2 << 8) + (pResponse->TIC.counter1_3);
10925 
10926 		sprintf(szTmp, "%u;%d", counter1ApparentPower, counter1);
10927 		uint64_t DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), unitCounter1, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10928 
10929 		if (DevRowIdx == (uint64_t)-1)
10930 			return;
10931 
10932 		m_notifications.CheckAndHandleNotification(DevRowIdx, procResult.DeviceName, devType, subType, NTYPE_TODAYENERGY, (float)counter1);
10933 		//----------------------------
10934 	}
10935 	else
10936 	{
10937 		WriteMessage("TeleInfo not connected");
10938 	}
10939 }
10940 
decode_CartelectronicEncoder(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)10941 void MainWorker::decode_CartelectronicEncoder(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
10942 {
10943 	char szTmp[100];
10944 	uint32_t counter1 = 0;
10945 	uint32_t counter2 = 0;
10946 	//int apparentPower = 0;
10947 	uint8_t Unit = 0;
10948 	uint8_t cmnd = 0;
10949 	uint64_t DevRowIdx = 0;
10950 	std::string ID;
10951 
10952 	uint8_t devType = pTypeRFXMeter;
10953 	uint8_t subType = sTypeRFXMeterCount;
10954 	uint8_t SignalLevel = pResponse->CEENCODER.rssi;
10955 
10956 	uint8_t BatteryLevel = (pResponse->CEENCODER.battery_level + 1) * 10;
10957 
10958 	// Id of the module
10959 	sprintf(szTmp, "%d", ((uint32_t)(pResponse->CEENCODER.id1 << 24) + (pResponse->CEENCODER.id2 << 16) + (pResponse->CEENCODER.id3 << 8) + pResponse->CEENCODER.id4));
10960 	ID = szTmp;
10961 
10962 	// Counter 1
10963 	counter1 = (pResponse->CEENCODER.counter1_0 << 24) + (pResponse->CEENCODER.counter1_1 << 16) + (pResponse->CEENCODER.counter1_2 << 8) + (pResponse->CEENCODER.counter1_3);
10964 
10965 	sprintf(szTmp, "%d", counter1);
10966 	DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10967 	if (DevRowIdx == (uint64_t)-1)
10968 		return;
10969 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, Unit, devType, subType, (float)counter1);
10970 
10971 	// Counter 2
10972 	counter2 = (pResponse->CEENCODER.counter2_0 << 24) + (pResponse->CEENCODER.counter2_1 << 16) + (pResponse->CEENCODER.counter2_2 << 8) + (pResponse->CEENCODER.counter2_3);
10973 
10974 	sprintf(szTmp, "%d", counter2);
10975 	DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), 1, devType, subType, SignalLevel, BatteryLevel, cmnd, szTmp, procResult.DeviceName);
10976 	if (DevRowIdx == (uint64_t)-1)
10977 		return;
10978 	m_notifications.CheckAndHandleNotification(DevRowIdx, pHardware->m_HwdID, ID, procResult.DeviceName, 1, devType, subType, (float)counter2);
10979 }
10980 
decode_Weather(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)10981 void MainWorker::decode_Weather(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
10982 {
10983 	uint16_t windID = (pResponse->WEATHER.id1 * 256) + pResponse->WEATHER.id2;
10984 	char szTmp[100];
10985 	sprintf(szTmp, "%d", windID);
10986 	std::string ID = szTmp;
10987 
10988 	//uint8_t devType = pTypeWEATHER;
10989 	uint8_t subType = pResponse->WEATHER.subtype;
10990 	//uint8_t Unit = 0;
10991 	//uint8_t cmnd = 0;
10992 	uint8_t SignalLevel = pResponse->WEATHER.rssi;
10993 	uint8_t BatteryLevel = get_BateryLevel(pHardware->HwdType, false, pResponse->WEATHER.battery_level & 0x0F);
10994 
10995 	procResult.DeviceRowIdx = -1;
10996 
10997 	CRFXBase* pRFXDevice = (CRFXBase*)pHardware;
10998 
10999 	//Wind
11000 	int intDirection = (pResponse->WEATHER.directionhigh * 256) + pResponse->WEATHER.directionlow;
11001 	float intSpeed = float((pResponse->WEATHER.av_speedhigh * 256) + pResponse->WEATHER.av_speedlow) / 10.0f;
11002 	float intGust = float((pResponse->WEATHER.gusthigh * 256) + pResponse->WEATHER.gustlow) / 10.0f;
11003 
11004 	float temp = 0, chill = 0;
11005 	if (!pResponse->WEATHER.temperaturesign)
11006 	{
11007 		temp = float((pResponse->WEATHER.temperaturehigh * 256) + pResponse->WEATHER.temperaturelow) / 10.0f;
11008 	}
11009 	else
11010 	{
11011 		temp = -(float(((pResponse->WEATHER.temperaturehigh & 0x7F) * 256) + pResponse->WEATHER.temperaturelow) / 10.0f);
11012 	}
11013 	if ((temp < -200) || (temp > 380))
11014 	{
11015 		WriteMessage(" Invalid Temperature");
11016 		return;
11017 	}
11018 
11019 	bool bHaveChill = false;
11020 	if (1 == 0)// subType == sTypeWEATHERx)
11021 	{
11022 		if (!pResponse->WEATHER.chillsign)
11023 		{
11024 			chill = float((pResponse->WEATHER.chillhigh * 256) + pResponse->WEATHER.chilllow) / 10.0f;
11025 		}
11026 		else
11027 		{
11028 			chill = -(float(((pResponse->WEATHER.chillhigh) & 0x7F) * 256 + pResponse->WEATHER.chilllow) / 10.0f);
11029 		}
11030 		bHaveChill = true;
11031 	}
11032 	pRFXDevice->SendWind(windID, BatteryLevel, intDirection, intSpeed, intGust, temp, chill, true, bHaveChill, procResult.DeviceName, SignalLevel);
11033 
11034 	if (subType == sTypeWEATHER2)
11035 	{
11036 		int Humidity = (int)pResponse->WEATHER.humidity;
11037 		//uint8_t HumidityStatus = pResponse->WEATHER.humidity_status;
11038 
11039 		//MySensorsBase *pMySensorDevice = reinterpret_cast<MySensorsBase*>(pHardware);
11040 
11041 		pRFXDevice->SendTempHumSensor(windID, BatteryLevel, temp, Humidity, procResult.DeviceName, SignalLevel);
11042 	}
11043 
11044 	//Rain
11045 	if ((subType == sTypeWEATHER1) || (subType == sTypeWEATHER2))
11046 	{
11047 		float TotalRain = 0;
11048 
11049 		if (subType == sTypeWEATHER1)
11050 		{
11051 			TotalRain = float((pResponse->WEATHER.raintotal2 * 256) + (pResponse->WEATHER.raintotal3)) * 0.3f;
11052 		}
11053 		else if (subType == sTypeWEATHER2)
11054 		{
11055 			TotalRain = float((pResponse->WEATHER.raintotal2 * 256) + (pResponse->WEATHER.raintotal3)) * 0.254f;
11056 		}
11057 		pRFXDevice->SendRainSensor(windID, BatteryLevel, TotalRain, procResult.DeviceName, SignalLevel);
11058 	}
11059 
11060 	//UV
11061 	if (subType == sTypeWEATHER2)
11062 	{
11063 		float UV = (float)pResponse->WEATHER.uv;
11064 		pRFXDevice->SendUVSensor(windID, 1, BatteryLevel, UV, procResult.DeviceName, SignalLevel);
11065 	}
11066 
11067 	//Solar
11068 	if (subType == sTypeWEATHER2)
11069 	{
11070 		float radiation = (float)((pResponse->WEATHER.solarhigh * 256) + pResponse->WEATHER.solarlow);
11071 		pRFXDevice->SendSolarRadiationSensor((const uint8_t)windID, BatteryLevel, radiation, procResult.DeviceName);
11072 	}
11073 }
11074 
decode_Solar(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)11075 void MainWorker::decode_Solar(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
11076 {
11077 	uint8_t SignalLevel = pResponse->SOLAR.rssi;
11078 	uint8_t BatteryLevel = get_BateryLevel(pHardware->HwdType, false, pResponse->SOLAR.battery_level & 0x0F);
11079 
11080 	_tGeneralDevice gdevice;
11081 	gdevice.subtype = sTypeSolarRadiation;
11082 	gdevice.intval1 = (pResponse->SOLAR.id1 * 256) + pResponse->SOLAR.id2;
11083 	gdevice.id = (uint8_t)gdevice.intval1;
11084 	gdevice.floatval1 = float((pResponse->SOLAR.solarhigh * 256) + float(pResponse->SOLAR.solarlow)) / 100.f;
11085 	decode_General(pHardware, pResponse, procResult, SignalLevel, BatteryLevel);
11086 	procResult.bProcessBatteryValue = false;
11087 }
11088 
decode_Hunter(const CDomoticzHardwareBase * pHardware,const tRBUF * pResponse,_tRxMessageProcessingResult & procResult)11089 void MainWorker::decode_Hunter(const CDomoticzHardwareBase* pHardware, const tRBUF* pResponse, _tRxMessageProcessingResult& procResult)
11090 {
11091 	uint8_t devType = pResponse->HUNTER.packettype;
11092 	uint8_t subType = pResponse->HUNTER.subtype;
11093 	uint8_t SignalLevel = pResponse->HUNTER.rssi;
11094 	uint8_t BatteryLevel = 255;
11095 
11096 	uint8_t Unit = 0;
11097 	uint8_t cmnd = pResponse->HUNTER.cmnd;
11098 	uint64_t DevRowIdx = 0;
11099 	std::string ID;
11100 
11101 	char szTmp[50];
11102 	sprintf(szTmp, "%02X%02X%02X%02X%02X%02X", pResponse->HUNTER.id1, pResponse->HUNTER.id2, pResponse->HUNTER.id3, pResponse->HUNTER.id4, pResponse->HUNTER.id5, pResponse->HUNTER.id6);
11103 	ID = szTmp;
11104 
11105 	DevRowIdx = m_sql.UpdateValue(pHardware->m_HwdID, ID.c_str(), Unit, devType, subType, SignalLevel, BatteryLevel, cmnd, procResult.DeviceName);
11106 	if (DevRowIdx == (uint64_t)-1)
11107 		return;
11108 
11109 	CheckSceneCode(DevRowIdx, devType, subType, cmnd, szTmp, procResult.DeviceName);
11110 
11111 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
11112 	{
11113 		WriteMessageStart();
11114 		switch (pResponse->HUNTER.subtype)
11115 		{
11116 		case sTypeHunterfan:
11117 			WriteMessage("subtype       = Hunter Fan");
11118 			sprintf(szTmp, "Sequence nbr  = %d", pResponse->HUNTER.seqnbr);
11119 			WriteMessage(szTmp);
11120 			sprintf(szTmp, "ID            = %s", ID.c_str());
11121 			WriteMessage(szTmp);
11122 			WriteMessage("Command       = ", false);
11123 			switch (pResponse->HUNTER.cmnd)
11124 			{
11125 			case HunterOff:
11126 				WriteMessage("Off");
11127 				break;
11128 			case HunterLight:
11129 				WriteMessage("Light");
11130 				break;
11131 			case HunterSpeed1:
11132 				WriteMessage("Low");
11133 				break;
11134 			case HunterSpeed2:
11135 				WriteMessage("Medium");
11136 				break;
11137 			case HunterSpeed3:
11138 				WriteMessage("High");
11139 				break;
11140 			case HunterProgram:
11141 				WriteMessage("Learn");
11142 				break;
11143 			default:
11144 				WriteMessage("UNKNOWN");
11145 				break;
11146 			}
11147 			break;
11148 		default:
11149 			sprintf(szTmp, "ERROR: Unknown Sub type for Packet type= %02X:%02X", pResponse->LIGHTING6.packettype, pResponse->LIGHTING6.subtype);
11150 			WriteMessage(szTmp);
11151 			break;
11152 		}
11153 		sprintf(szTmp, "Signal level  = %d", pResponse->LIGHTING6.rssi);
11154 		WriteMessage(szTmp);
11155 		WriteMessageEnd();
11156 	}
11157 	procResult.DeviceRowIdx = DevRowIdx;
11158 }
11159 
GetSensorData(const uint64_t idx,int & nValue,std::string & sValue)11160 bool MainWorker::GetSensorData(const uint64_t idx, int& nValue, std::string& sValue)
11161 {
11162 	std::vector<std::vector<std::string> > result;
11163 	char szTmp[100];
11164 	result = m_sql.safe_query("SELECT [Type],[SubType],[nValue],[sValue],[SwitchType] FROM DeviceStatus WHERE (ID==%" PRIu64 ")", idx);
11165 	if (result.empty())
11166 		return false;
11167 	std::vector<std::string> sd = result[0];
11168 	int devType = atoi(sd[0].c_str());
11169 	int subType = atoi(sd[1].c_str());
11170 	nValue = atoi(sd[2].c_str());
11171 	sValue = sd[3];
11172 	_eMeterType metertype = (_eMeterType)atoi(sd[4].c_str());
11173 
11174 	//Special cases
11175 	if ((devType == pTypeP1Power) && (subType == sTypeP1Power))
11176 	{
11177 		std::vector<std::string> results;
11178 		StringSplit(sValue, ";", results);
11179 		if (results.size() < 6)
11180 			return false; //invalid data
11181 						  //Return usage or delivery
11182 		long usagecurrent = atol(results[4].c_str());
11183 		long delivcurrent = atol(results[5].c_str());
11184 		std::stringstream ssvalue;
11185 		if (delivcurrent > 0)
11186 		{
11187 			ssvalue << "-" << delivcurrent;
11188 		}
11189 		else
11190 		{
11191 			ssvalue << usagecurrent;
11192 		}
11193 		nValue = 0;
11194 		sValue = ssvalue.str();
11195 	}
11196 	else if ((devType == pTypeP1Gas) && (subType == sTypeP1Gas))
11197 	{
11198 		float GasDivider = 1000.0f;
11199 		//get lowest value of today
11200 		time_t now = mytime(NULL);
11201 		struct tm tm1;
11202 		localtime_r(&now, &tm1);
11203 
11204 		struct tm ltime;
11205 		ltime.tm_isdst = tm1.tm_isdst;
11206 		ltime.tm_hour = 0;
11207 		ltime.tm_min = 0;
11208 		ltime.tm_sec = 0;
11209 		ltime.tm_year = tm1.tm_year;
11210 		ltime.tm_mon = tm1.tm_mon;
11211 		ltime.tm_mday = tm1.tm_mday;
11212 
11213 		char szDate[40];
11214 		sprintf(szDate, "%04d-%02d-%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
11215 
11216 		std::vector<std::vector<std::string> > result2;
11217 		strcpy(szTmp, "0");
11218 		result2 = m_sql.safe_query("SELECT MIN(Value) FROM Meter WHERE (DeviceRowID='%" PRIu64 "' AND Date>='%q')", idx, szDate);
11219 		if (!result2.empty())
11220 		{
11221 			std::vector<std::string> sd2 = result2[0];
11222 			unsigned long long total_min_gas = std::strtoull(sd2[0].c_str(), nullptr, 10);
11223 			unsigned long long gasactual = std::strtoull(sValue.c_str(), nullptr, 10);
11224 			unsigned long long total_real_gas = gasactual - total_min_gas;
11225 			float musage = float(total_real_gas) / GasDivider;
11226 			sprintf(szTmp, "%.03f", musage);
11227 		}
11228 		else
11229 		{
11230 			sprintf(szTmp, "%.03f", 0.0f);
11231 		}
11232 		nValue = 0;
11233 		sValue = szTmp;
11234 	}
11235 	else if (devType == pTypeRFXMeter)
11236 	{
11237 		float EnergyDivider = 1000.0f;
11238 		float GasDivider = 100.0f;
11239 		//float WaterDivider = 100.0f;
11240 		int tValue;
11241 		if (m_sql.GetPreferencesVar("MeterDividerEnergy", tValue))
11242 		{
11243 			EnergyDivider = float(tValue);
11244 		}
11245 		if (m_sql.GetPreferencesVar("MeterDividerGas", tValue))
11246 		{
11247 			GasDivider = float(tValue);
11248 		}
11249 		//if (m_sql.GetPreferencesVar("MeterDividerWater", tValue))
11250 		//{
11251 		//WaterDivider = float(tValue);
11252 		//}
11253 
11254 		//get value of today
11255 		time_t now = mytime(NULL);
11256 		struct tm tm1;
11257 		localtime_r(&now, &tm1);
11258 
11259 		struct tm ltime;
11260 		ltime.tm_isdst = tm1.tm_isdst;
11261 		ltime.tm_hour = 0;
11262 		ltime.tm_min = 0;
11263 		ltime.tm_sec = 0;
11264 		ltime.tm_year = tm1.tm_year;
11265 		ltime.tm_mon = tm1.tm_mon;
11266 		ltime.tm_mday = tm1.tm_mday;
11267 
11268 		char szDate[40];
11269 		sprintf(szDate, "%04d-%02d-%02d", ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday);
11270 
11271 		std::vector<std::vector<std::string> > result2;
11272 		strcpy(szTmp, "0");
11273 		result2 = m_sql.safe_query("SELECT MIN(Value), MAX(Value) FROM Meter WHERE (DeviceRowID='%" PRIu64 "' AND Date>='%q')", idx, szDate);
11274 		if (!result2.empty())
11275 		{
11276 			std::vector<std::string> sd2 = result2[0];
11277 
11278 			unsigned long long total_min = std::strtoull(sd2[0].c_str(), nullptr, 10);
11279 			unsigned long long total_max = std::strtoull(sd2[1].c_str(), nullptr, 10);
11280 			unsigned long long total_real = total_max - total_min;
11281 			sprintf(szTmp, "%llu", total_real);
11282 
11283 			float musage = 0;
11284 			switch (metertype)
11285 			{
11286 			case MTYPE_ENERGY:
11287 			case MTYPE_ENERGY_GENERATED:
11288 				musage = float(total_real) / EnergyDivider;
11289 				sprintf(szTmp, "%.03f", musage);
11290 				break;
11291 			case MTYPE_GAS:
11292 				musage = float(total_real) / GasDivider;
11293 				sprintf(szTmp, "%.03f", musage);
11294 				break;
11295 			case MTYPE_WATER:
11296 				sprintf(szTmp, "%llu", total_real);
11297 				break;
11298 			case MTYPE_COUNTER:
11299 				sprintf(szTmp, "%llu", total_real);
11300 				break;
11301 				/*
11302 				default:
11303 				strcpy(szTmp, "?");
11304 				break;
11305 				*/
11306 			}
11307 		}
11308 		nValue = 0;
11309 		sValue = szTmp;
11310 	}
11311 	return true;
11312 }
11313 
SwitchLightInt(const std::vector<std::string> & sd,std::string switchcmd,int level,const _tColor color,const bool IsTesting,const std::string & User)11314 bool MainWorker::SwitchLightInt(const std::vector<std::string>& sd, std::string switchcmd, int level, const _tColor color, const bool IsTesting, const std::string& User)
11315 {
11316 	unsigned long ID;
11317 	std::stringstream s_strid;
11318 	s_strid << std::hex << sd[1];
11319 	s_strid >> ID;
11320 	uint8_t ID1 = (uint8_t)((ID & 0xFF000000) >> 24);
11321 	uint8_t ID2 = (uint8_t)((ID & 0x00FF0000) >> 16);
11322 	uint8_t ID3 = (uint8_t)((ID & 0x0000FF00) >> 8);
11323 	uint8_t ID4 = (uint8_t)((ID & 0x000000FF));
11324 
11325 	int HardwareID = atoi(sd[0].c_str());
11326 
11327 	_log.Debug(DEBUG_NORM, "MAIN SwitchLightInt : switchcmd:%s level:%d HWid:%d  sd:%s %s %s %s %s %s", switchcmd.c_str(), level, HardwareID,
11328 		sd[0].c_str(), sd[1].c_str(), sd[2].c_str(), sd[3].c_str(), sd[4].c_str(), sd[5].c_str());
11329 
11330 	int hindex = FindDomoticzHardware(HardwareID);
11331 	if (hindex == -1)
11332 	{
11333 		_log.Log(LOG_ERROR, "Switch command not send!, Hardware device disabled or not found!");
11334 		return false;
11335 	}
11336 	CDomoticzHardwareBase* pHardware = GetHardware(HardwareID);
11337 	if (pHardware == NULL)
11338 		return false;
11339 
11340 	m_szLastSwitchUser = User;
11341 
11342 	if (pHardware->HwdType == HTYPE_DomoticzInternal)
11343 	{
11344 		//Special cases
11345 		if (ID == 0x00148702)
11346 		{
11347 			int iSecStatus = 2;
11348 			if (switchcmd == "Disarm")
11349 				iSecStatus = 0;
11350 			else if (switchcmd == "Arm Home")
11351 				iSecStatus = 1;
11352 			else if (switchcmd == "Arm Away")
11353 				iSecStatus = 2;
11354 			else
11355 				return false;
11356 			UpdateDomoticzSecurityStatus(iSecStatus);
11357 			return true;
11358 		}
11359 	}
11360 
11361 	uint8_t Unit = atoi(sd[2].c_str());
11362 	uint8_t dType = atoi(sd[3].c_str());
11363 	uint8_t dSubType = atoi(sd[4].c_str());
11364 	_eSwitchType switchtype = (_eSwitchType)atoi(sd[5].c_str());
11365 	std::map<std::string, std::string> options = m_sql.BuildDeviceOptions(sd[10].c_str());
11366 
11367 	//when asking for Toggle, just switch to the opposite value
11368 	if (switchcmd == "Toggle") {
11369 		//Request current state of switch
11370 		std::string lstatus = "";
11371 		int llevel = 0;
11372 		bool bHaveDimmer = false;
11373 		bool bHaveGroupCmd = false;
11374 		int maxDimLevel = 0;
11375 
11376 		int nValue = atoi(sd[7].c_str());
11377 		std::string sValue = sd[8];
11378 
11379 		GetLightStatus(dType, dSubType, switchtype, nValue, sValue, lstatus, llevel, bHaveDimmer, maxDimLevel, bHaveGroupCmd);
11380 		//Flip the status
11381 		switchcmd = (IsLightSwitchOn(lstatus) == true) ? "Off" : "On";
11382 	}
11383 
11384 	// If dimlevel is 0 or no dimlevel, turn switch off
11385 	if (level <= 0 && switchcmd == "Set Level")
11386 		switchcmd = "Off";
11387 
11388 	//when level is invalid or command is "On", replace level with "LastLevel"
11389 	if (switchcmd == "On" || level < 0)
11390 	{
11391 		//Get LastLevel
11392 		std::vector<std::vector<std::string> > result;
11393 		result = m_sql.safe_query(
11394 			"SELECT LastLevel FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)", HardwareID, sd[1].c_str(), Unit, int(dType), int(dSubType));
11395 		if (result.size() == 1)
11396 		{
11397 			level = atoi(result[0][0].c_str());
11398 		}
11399 		_log.Debug(DEBUG_NORM, "MAIN SwitchLightInt : switchcmd==\"On\" || level < 0, new level:%d", level);
11400 	}
11401 	// TODO: Something smarter if level is not valid?
11402 	level = std::max(level, 0);
11403 
11404 	//
11405 	//	For plugins all the specific logic below is irrelevent
11406 	//	so just send the full details to the plugin so that it can take appropriate action
11407 	//
11408 	if (pHardware->HwdType == HTYPE_PythonPlugin)
11409 	{
11410 #ifdef ENABLE_PYTHON
11411 		// Special case when color is passed from timer or scene
11412 		if ((switchcmd == "On") || (switchcmd == "Set Level"))
11413 		{
11414 			if (color.mode != ColorModeNone)
11415 			{
11416 				switchcmd = "Set Color";
11417 			}
11418 		}
11419 		((Plugins::CPlugin*)m_hardwaredevices[hindex])->SendCommand(Unit, switchcmd, level, color);
11420 #endif
11421 		return true;
11422 	}
11423 
11424 	switch (dType)
11425 	{
11426 	case pTypeLighting1:
11427 	{
11428 		tRBUF lcmd;
11429 		lcmd.LIGHTING1.packetlength = sizeof(lcmd.LIGHTING1) - 1;
11430 		lcmd.LIGHTING1.packettype = dType;
11431 		lcmd.LIGHTING1.subtype = dSubType;
11432 		lcmd.LIGHTING1.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
11433 		lcmd.LIGHTING1.housecode = atoi(sd[1].c_str());
11434 		lcmd.LIGHTING1.unitcode = Unit;
11435 		lcmd.LIGHTING1.filler = 0;
11436 		lcmd.LIGHTING1.rssi = 12;
11437 
11438 		if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.LIGHTING1.cmnd, options))
11439 			return false;
11440 		if (switchtype == STYPE_Doorbell)
11441 		{
11442 			int rnvalue = 0;
11443 			m_sql.GetPreferencesVar("DoorbellCommand", rnvalue);
11444 			if (rnvalue == 0)
11445 				lcmd.LIGHTING1.cmnd = light1_sChime;
11446 			else
11447 				lcmd.LIGHTING1.cmnd = light1_sOn;
11448 		}
11449 
11450 		if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.LIGHTING1)))
11451 			return false;
11452 		if (!IsTesting) {
11453 			//send to internal for now (later we use the ACK)
11454 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
11455 		}
11456 		return true;
11457 	}
11458 	break;
11459 	case pTypeLighting2:
11460 	{
11461 		tRBUF lcmd;
11462 		lcmd.LIGHTING2.packetlength = sizeof(lcmd.LIGHTING2) - 1;
11463 		lcmd.LIGHTING2.packettype = dType;
11464 		lcmd.LIGHTING2.subtype = dSubType;
11465 		lcmd.LIGHTING2.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
11466 		lcmd.LIGHTING2.id1 = ID1;
11467 		lcmd.LIGHTING2.id2 = ID2;
11468 		lcmd.LIGHTING2.id3 = ID3;
11469 		lcmd.LIGHTING2.id4 = ID4;
11470 		lcmd.LIGHTING2.unitcode = Unit;
11471 		lcmd.LIGHTING2.filler = 0;
11472 		lcmd.LIGHTING2.rssi = 12;
11473 
11474 		if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.LIGHTING2.cmnd, options))
11475 			return false;
11476 		if (switchtype == STYPE_Doorbell) {
11477 			int rnvalue = 0;
11478 			m_sql.GetPreferencesVar("DoorbellCommand", rnvalue);
11479 			if (rnvalue == 0)
11480 				lcmd.LIGHTING2.cmnd = light2_sGroupOn;
11481 			else
11482 				lcmd.LIGHTING2.cmnd = light2_sOn;
11483 			level = 15;
11484 		}
11485 		else if (switchtype == STYPE_X10Siren) {
11486 			level = 15;
11487 		}
11488 		else if ((switchtype == STYPE_BlindsPercentage) || (switchtype == STYPE_BlindsPercentageInverted)) {
11489 			if (lcmd.LIGHTING2.cmnd == light2_sSetLevel)
11490 			{
11491 				if (level == 15)
11492 				{
11493 					lcmd.LIGHTING2.cmnd = light2_sOn;
11494 				}
11495 				else if (level == 0)
11496 				{
11497 					lcmd.LIGHTING2.cmnd = light2_sOff;
11498 				}
11499 			}
11500 		}
11501 		else if (switchtype == STYPE_Media)
11502 		{
11503 			if (switchcmd == "Set Volume") {
11504 				level = (level < 0) ? 0 : level;
11505 				level = (level > 100) ? 100 : level;
11506 			}
11507 		}
11508 		else
11509 			level = (level > 15) ? 15 : level;
11510 
11511 		lcmd.LIGHTING2.level = (uint8_t)level;
11512 		//Special Teach-In for EnOcean Dimmers
11513 		if ((pHardware->HwdType == HTYPE_EnOceanESP2) && (IsTesting) && (switchtype == STYPE_Dimmer))
11514 		{
11515 			CEnOceanESP2* pEnocean = reinterpret_cast<CEnOceanESP2*>(pHardware);
11516 			pEnocean->SendDimmerTeachIn((const char*)&lcmd, sizeof(lcmd.LIGHTING1));
11517 		}
11518 		else if ((pHardware->HwdType == HTYPE_EnOceanESP3) && (IsTesting) && (switchtype == STYPE_Dimmer))
11519 		{
11520 			CEnOceanESP3* pEnocean = reinterpret_cast<CEnOceanESP3*>(pHardware);
11521 			pEnocean->SendDimmerTeachIn((const char*)&lcmd, sizeof(lcmd.LIGHTING1));
11522 		}
11523 		else
11524 		{
11525 			if (switchtype != STYPE_Motion) //dont send actual motion off command
11526 			{
11527 				if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.LIGHTING2)))
11528 					return false;
11529 			}
11530 		}
11531 
11532 		if (!IsTesting) {
11533 			//send to internal for now (later we use the ACK)
11534 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
11535 		}
11536 		return true;
11537 	}
11538 	break;
11539 	case pTypeLighting3:
11540 		if (level > 9)
11541 			level = 9;
11542 		break;
11543 	case pTypeLighting4:
11544 	{
11545 		tRBUF lcmd;
11546 		lcmd.LIGHTING4.packetlength = sizeof(lcmd.LIGHTING4) - 1;
11547 		lcmd.LIGHTING4.packettype = dType;
11548 		lcmd.LIGHTING4.subtype = dSubType;
11549 		lcmd.LIGHTING4.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
11550 		lcmd.LIGHTING4.cmd1 = ID2;
11551 		lcmd.LIGHTING4.cmd2 = ID3;
11552 		lcmd.LIGHTING4.cmd3 = ID4;
11553 		lcmd.LIGHTING4.filler = 0;
11554 		lcmd.LIGHTING4.rssi = 12;
11555 
11556 		//Get Pulse timing
11557 		std::vector<std::vector<std::string> > result;
11558 		result = m_sql.safe_query(
11559 			"SELECT sValue FROM DeviceStatus WHERE (HardwareID=%d AND DeviceID='%q' AND Unit=%d AND Type=%d AND SubType=%d)", HardwareID, sd[1].c_str(), Unit, int(dType), int(dSubType));
11560 		if (result.size() == 1)
11561 		{
11562 			int pulsetimeing = atoi(result[0][0].c_str());
11563 			lcmd.LIGHTING4.pulseHigh = pulsetimeing / 256;
11564 			lcmd.LIGHTING4.pulseLow = pulsetimeing & 0xFF;
11565 			if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.LIGHTING4)))
11566 				return false;
11567 			if (!IsTesting) {
11568 				//send to internal for now (later we use the ACK)
11569 				PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
11570 			}
11571 			return true;
11572 		}
11573 		return false;
11574 	}
11575 	break;
11576 	case pTypeLighting5:
11577 	{
11578 		tRBUF lcmd;
11579 		lcmd.LIGHTING5.packetlength = sizeof(lcmd.LIGHTING5) - 1;
11580 		lcmd.LIGHTING5.packettype = dType;
11581 		lcmd.LIGHTING5.subtype = dSubType;
11582 		lcmd.LIGHTING5.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
11583 		lcmd.LIGHTING5.id1 = ID2;
11584 		lcmd.LIGHTING5.id2 = ID3;
11585 		lcmd.LIGHTING5.id3 = ID4;
11586 		lcmd.LIGHTING5.unitcode = Unit;
11587 		lcmd.LIGHTING5.filler = 0;
11588 		lcmd.LIGHTING5.rssi = 12;
11589 
11590 		if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.LIGHTING5.cmnd, options))
11591 			return false;
11592 		if (switchtype == STYPE_Doorbell)
11593 		{
11594 			int rnvalue = 0;
11595 			m_sql.GetPreferencesVar("DoorbellCommand", rnvalue);
11596 			if (rnvalue == 0)
11597 				lcmd.LIGHTING5.cmnd = light5_sGroupOn;
11598 			else
11599 				lcmd.LIGHTING5.cmnd = light5_sOn;
11600 			level = 31;
11601 		}
11602 		else if (switchtype == STYPE_X10Siren)
11603 		{
11604 			level = 31;
11605 		}
11606 		if (level > 31)
11607 			level = 31;
11608 		lcmd.LIGHTING5.level = (uint8_t)level;
11609 		if (dSubType == sTypeLivolo)
11610 		{
11611 			if ((switchcmd == "Set Level") && (level == 0))
11612 			{
11613 				switchcmd = "Off";
11614 				GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.LIGHTING5.cmnd, options);
11615 			}
11616 			if (switchcmd != "Off")
11617 			{
11618 				//Special Case, turn off first
11619 				uint8_t oldCmd = lcmd.LIGHTING5.cmnd;
11620 				lcmd.LIGHTING5.cmnd = light5_sLivoloAllOff;
11621 				if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.LIGHTING5)))
11622 					return false;
11623 				lcmd.LIGHTING5.cmnd = oldCmd;
11624 			}
11625 			if (switchcmd == "Set Level")
11626 			{
11627 				//dim value we have to send multiple times
11628 				for (int iDim = 0; iDim < level; iDim++)
11629 				{
11630 					if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.LIGHTING5)))
11631 						return false;
11632 				}
11633 			}
11634 			else
11635 			{
11636 				if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.LIGHTING5)))
11637 					return false;
11638 			}
11639 		}
11640 		else if ((dSubType == sTypeTRC02) || (dSubType == sTypeTRC02_2))
11641 		{
11642 			//int oldlevel = level;
11643 			if (switchcmd != "Off")
11644 			{
11645 				if (color.mode == ColorModeRGB)
11646 				{
11647 					switchcmd = "Set Color";
11648 				}
11649 			}
11650 			if ((switchcmd == "Off") ||
11651 				(switchcmd == "On") ||      //Special Case, turn off first to ensure light is in normal mode
11652 				(switchcmd == "Set Color"))
11653 			{
11654 				uint8_t oldCmd = lcmd.LIGHTING5.cmnd;
11655 				lcmd.LIGHTING5.cmnd = light5_sRGBoff;
11656 				if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.LIGHTING5)))
11657 					return false;
11658 				lcmd.LIGHTING5.cmnd = oldCmd;
11659 				sleep_milliseconds(100);
11660 			}
11661 			if ((switchcmd == "On") || (switchcmd == "Set Color"))
11662 			{
11663 				//turn on
11664 				lcmd.LIGHTING5.cmnd = light5_sRGBon;
11665 				if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.LIGHTING5)))
11666 					return false;
11667 				sleep_milliseconds(100);
11668 
11669 				if (switchcmd == "Set Color")
11670 				{
11671 					if (color.mode == ColorModeRGB)
11672 					{
11673 						float hsb[3];
11674 						rgb2hsb(color.r, color.g, color.b, hsb);
11675 						switchcmd = "Set Color";
11676 
11677 						float dval = 126.0f * hsb[0]; // Color Range is 0x06..0x84
11678 						lcmd.LIGHTING5.cmnd = light5_sRGBcolormin + 1 + round(dval);
11679 						if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.LIGHTING5)))
11680 							return false;
11681 					}
11682 				}
11683 			}
11684 		}
11685 		else
11686 		{
11687 			if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.LIGHTING5)))
11688 				return false;
11689 		}
11690 		if (!IsTesting) {
11691 			//send to internal for now (later we use the ACK)
11692 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
11693 		}
11694 		return true;
11695 	}
11696 	break;
11697 	case pTypeLighting6:
11698 	{
11699 		tRBUF lcmd;
11700 		lcmd.LIGHTING6.packetlength = sizeof(lcmd.LIGHTING6) - 1;
11701 		lcmd.LIGHTING6.packettype = dType;
11702 		lcmd.LIGHTING6.subtype = dSubType;
11703 		lcmd.LIGHTING6.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
11704 		lcmd.LIGHTING6.seqnbr2 = 0;
11705 		lcmd.LIGHTING6.id1 = ID2;
11706 		lcmd.LIGHTING6.id2 = ID3;
11707 		lcmd.LIGHTING6.groupcode = ID4;
11708 		lcmd.LIGHTING6.unitcode = Unit;
11709 		lcmd.LIGHTING6.cmndseqnbr = m_hardwaredevices[hindex]->m_SeqNr % 4;
11710 		lcmd.LIGHTING6.filler = 0;
11711 		lcmd.LIGHTING6.rssi = 12;
11712 
11713 		if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.LIGHTING6.cmnd, options))
11714 			return false;
11715 		if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.LIGHTING6)))
11716 			return false;
11717 		if (!IsTesting) {
11718 			//send to internal for now (later we use the ACK)
11719 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
11720 		}
11721 		return true;
11722 	}
11723 	break;
11724 	case pTypeFS20:
11725 	{
11726 		tRBUF lcmd;
11727 		lcmd.FS20.packetlength = sizeof(lcmd.FS20) - 1;
11728 		lcmd.FS20.packettype = dType;
11729 		lcmd.FS20.subtype = dSubType;
11730 		lcmd.FS20.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
11731 		lcmd.FS20.hc1 = ID3;
11732 		lcmd.FS20.hc2 = ID4;
11733 		lcmd.FS20.addr = Unit;
11734 		lcmd.FS20.filler = 0;
11735 		lcmd.FS20.rssi = 12;
11736 		lcmd.FS20.cmd2 = 0;
11737 		if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.FS20.cmd1, options))
11738 			return false;
11739 		level = (level > 15) ? 15 : level;
11740 
11741 		if (level > 0)
11742 		{
11743 			lcmd.FS20.cmd1 = fs20_sDimlevel_1 + level;
11744 		}
11745 
11746 		if (switchtype != STYPE_Motion) //dont send actual motion off command
11747 		{
11748 			if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.FS20)))
11749 				return false;
11750 		}
11751 
11752 		if (!IsTesting) {
11753 			//send to internal for now (later we use the ACK)
11754 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
11755 		}
11756 		return true;
11757 	}
11758 	break;
11759 	case pTypeHomeConfort:
11760 	{
11761 		tRBUF lcmd;
11762 		lcmd.HOMECONFORT.packetlength = sizeof(lcmd.HOMECONFORT) - 1;
11763 		lcmd.HOMECONFORT.packettype = dType;
11764 		lcmd.HOMECONFORT.subtype = dSubType;
11765 		lcmd.HOMECONFORT.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
11766 		lcmd.HOMECONFORT.id1 = ID1;
11767 		lcmd.HOMECONFORT.id2 = ID2;
11768 		lcmd.HOMECONFORT.id3 = ID3;
11769 		lcmd.HOMECONFORT.housecode = ID4;
11770 		lcmd.HOMECONFORT.unitcode = Unit;
11771 		lcmd.HOMECONFORT.filler = 0;
11772 		lcmd.HOMECONFORT.rssi = 12;
11773 
11774 		if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.HOMECONFORT.cmnd, options))
11775 			return false;
11776 		if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.HOMECONFORT)))
11777 			return false;
11778 		if (!IsTesting) {
11779 			//send to internal for now (later we use the ACK)
11780 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
11781 		}
11782 		return true;
11783 	}
11784 	break;
11785 	case pTypeFan:
11786 	{
11787 		tRBUF lcmd;
11788 		lcmd.FAN.packetlength = sizeof(lcmd.FAN) - 1;
11789 		lcmd.FAN.packettype = dType;
11790 		lcmd.FAN.subtype = dSubType;
11791 		lcmd.FAN.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
11792 		lcmd.FAN.id1 = ID2;
11793 		lcmd.FAN.id2 = ID3;
11794 		lcmd.FAN.id3 = ID4;
11795 		lcmd.FAN.filler = 0;
11796 		lcmd.FAN.rssi = 12;
11797 
11798 		if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.FAN.cmnd, options))
11799 			return false;
11800 		if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.FAN)))
11801 			return false;
11802 		if (!IsTesting) {
11803 			//send to internal for now (later we use the ACK)
11804 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
11805 		}
11806 		return true;
11807 	}
11808 	break;
11809 	case pTypeColorSwitch:
11810 	{
11811 		_tColorSwitch lcmd;
11812 		lcmd.subtype = dSubType;
11813 		lcmd.id = ID;
11814 		lcmd.dunit = Unit;
11815 		lcmd.color = color;
11816 		level = std::min(100, level);
11817 		level = std::max(0, level);
11818 		lcmd.value = level;
11819 
11820 		//Special case when color is passed from timer or scene
11821 		if ((switchcmd == "On") || (switchcmd == "Set Level"))
11822 		{
11823 			if (color.mode != ColorModeNone)
11824 			{
11825 				switchcmd = "Set Color";
11826 			}
11827 		}
11828 		if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.command, options))
11829 			return false;
11830 		if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(_tColorSwitch)))
11831 			return false;
11832 		if (!IsTesting) {
11833 			//send to internal for now (later we use the ACK)
11834 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
11835 		}
11836 		return true;
11837 	}
11838 	break;
11839 	case pTypeSecurity1:
11840 	{
11841 		tRBUF lcmd;
11842 		lcmd.SECURITY1.packetlength = sizeof(lcmd.SECURITY1) - 1;
11843 		lcmd.SECURITY1.packettype = dType;
11844 		lcmd.SECURITY1.subtype = dSubType;
11845 		lcmd.SECURITY1.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
11846 		lcmd.SECURITY1.battery_level = 9;
11847 		lcmd.SECURITY1.id1 = ID2;
11848 		lcmd.SECURITY1.id2 = ID3;
11849 		lcmd.SECURITY1.id3 = ID4;
11850 		lcmd.SECURITY1.rssi = 12;
11851 		switch (dSubType)
11852 		{
11853 		case sTypeKD101:
11854 		case sTypeSA30:
11855 		{
11856 			if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.SECURITY1.status, options))
11857 				return false;
11858 			//send it twice
11859 			if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.SECURITY1)))
11860 				return false;
11861 			sleep_milliseconds(500);
11862 			if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.SECURITY1)))
11863 				return false;
11864 			if (!IsTesting) {
11865 				//send to internal for now (later we use the ACK)
11866 				PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
11867 			}
11868 		}
11869 		break;
11870 		case sTypeSecX10M:
11871 		case sTypeSecX10R:
11872 		case sTypeSecX10:
11873 		case sTypeMeiantech:
11874 		{
11875 			if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.SECURITY1.status, options))
11876 				return false;
11877 			if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.SECURITY1)))
11878 				return false;
11879 			if (!IsTesting) {
11880 				//send to internal for now (later we use the ACK)
11881 				PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
11882 			}
11883 		}
11884 		break;
11885 		}
11886 		return true;
11887 	}
11888 	break;
11889 	case pTypeSecurity2:
11890 	{
11891 		BYTE kCodes[9];
11892 		if (sd[1].size() < 8 * 2)
11893 		{
11894 			return false;
11895 		}
11896 		for (int ii = 0; ii < 8; ii++)
11897 		{
11898 			std::string sHex = sd[1].substr(ii * 2, 2);
11899 			std::stringstream s_strid;
11900 			int iHex = 0;
11901 			s_strid << std::hex << sHex;
11902 			s_strid >> iHex;
11903 			kCodes[ii] = (BYTE)iHex;
11904 
11905 		}
11906 		tRBUF lcmd;
11907 		lcmd.SECURITY2.packetlength = sizeof(lcmd.SECURITY2) - 1;
11908 		lcmd.SECURITY2.packettype = dType;
11909 		lcmd.SECURITY2.subtype = dSubType;
11910 		lcmd.SECURITY2.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
11911 		lcmd.SECURITY2.id1 = kCodes[0];
11912 		lcmd.SECURITY2.id2 = kCodes[1];
11913 		lcmd.SECURITY2.id3 = kCodes[2];
11914 		lcmd.SECURITY2.id4 = kCodes[3];
11915 		lcmd.SECURITY2.id5 = kCodes[4];
11916 		lcmd.SECURITY2.id6 = kCodes[5];
11917 		lcmd.SECURITY2.id7 = kCodes[6];
11918 		lcmd.SECURITY2.id8 = kCodes[7];
11919 
11920 		lcmd.SECURITY2.id9 = 0;//bat full
11921 		lcmd.SECURITY2.battery_level = 9;
11922 		lcmd.SECURITY2.rssi = 12;
11923 
11924 		if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.SECURITY2)))
11925 			return false;
11926 		if (!IsTesting) {
11927 			//send to internal for now (later we use the ACK)
11928 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
11929 		}
11930 		return true;
11931 	}
11932 	break;
11933 	case pTypeHunter:
11934 	{
11935 		BYTE kCodes[6];
11936 		if (sd[1].size() < 6 * 2)
11937 		{
11938 			return false;
11939 		}
11940 		for (int ii = 0; ii < 6; ii++)
11941 		{
11942 			std::string sHex = sd[1].substr(ii * 2, 2);
11943 			std::stringstream s_strid;
11944 			int iHex = 0;
11945 			s_strid << std::hex << sHex;
11946 			s_strid >> iHex;
11947 			kCodes[ii] = (BYTE)iHex;
11948 		}
11949 		tRBUF lcmd;
11950 		lcmd.HUNTER.packetlength = sizeof(lcmd.HUNTER) - 1;
11951 		lcmd.HUNTER.packettype = dType;
11952 		lcmd.HUNTER.subtype = dSubType;
11953 		lcmd.HUNTER.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
11954 		lcmd.HUNTER.id1 = kCodes[0];
11955 		lcmd.HUNTER.id2 = kCodes[1];
11956 		lcmd.HUNTER.id3 = kCodes[2];
11957 		lcmd.HUNTER.id4 = kCodes[3];
11958 		lcmd.HUNTER.id5 = kCodes[4];
11959 		lcmd.HUNTER.id6 = kCodes[5];
11960 		if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.HUNTER.cmnd, options))
11961 			return false;
11962 		lcmd.HUNTER.rssi = 12;
11963 
11964 		if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.SECURITY2)))
11965 			return false;
11966 		if (!IsTesting) {
11967 			//send to internal for now (later we use the ACK)
11968 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
11969 		}
11970 		return true;
11971 	}
11972 	break;
11973 	case pTypeCurtain:
11974 	{
11975 		tRBUF lcmd;
11976 		lcmd.CURTAIN1.packetlength = sizeof(lcmd.CURTAIN1) - 1;
11977 		lcmd.CURTAIN1.packettype = dType;
11978 		lcmd.CURTAIN1.subtype = dSubType;
11979 		lcmd.CURTAIN1.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
11980 		lcmd.CURTAIN1.housecode = atoi(sd[1].c_str());
11981 		lcmd.CURTAIN1.unitcode = Unit;
11982 		if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.CURTAIN1.cmnd, options))
11983 			return false;
11984 		lcmd.CURTAIN1.filler = 0;
11985 
11986 		if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.CURTAIN1)))
11987 			return false;
11988 		if (!IsTesting) {
11989 			//send to internal for now (later we use the ACK)
11990 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
11991 		}
11992 		return true;
11993 	}
11994 	break;
11995 	case pTypeBlinds:
11996 	{
11997 		tRBUF lcmd;
11998 		lcmd.BLINDS1.packetlength = sizeof(lcmd.BLINDS1) - 1;
11999 		lcmd.BLINDS1.packettype = dType;
12000 		lcmd.BLINDS1.subtype = dSubType;
12001 		lcmd.BLINDS1.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
12002 		lcmd.BLINDS1.id1 = ID1;
12003 		lcmd.BLINDS1.id2 = ID2;
12004 		lcmd.BLINDS1.id3 = ID3;
12005 		lcmd.BLINDS1.id4 = 0;
12006 		if (
12007 			(dSubType == sTypeBlindsT0)
12008 			|| (dSubType == sTypeBlindsT1)
12009 			|| (dSubType == sTypeBlindsT3)
12010 			|| (dSubType == sTypeBlindsT8)
12011 			|| (dSubType == sTypeBlindsT12)
12012 			|| (dSubType == sTypeBlindsT13)
12013 			|| (dSubType == sTypeBlindsT14)
12014 			|| (dSubType == sTypeBlindsT15)
12015 			|| (dSubType == sTypeBlindsT16)
12016 			|| (dSubType == sTypeBlindsT17)
12017 			|| (dSubType == sTypeBlindsT18)
12018 			)
12019 		{
12020 			lcmd.BLINDS1.unitcode = Unit;
12021 		}
12022 		else if ((dSubType == sTypeBlindsT6) || (dSubType == sTypeBlindsT7) || (dSubType == sTypeBlindsT9))
12023 		{
12024 			lcmd.BLINDS1.unitcode = Unit;
12025 			lcmd.BLINDS1.id4 = ID4;
12026 		}
12027 		else
12028 		{
12029 			lcmd.BLINDS1.unitcode = 0;
12030 		}
12031 		if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.BLINDS1.cmnd, options))
12032 			return false;
12033 		level = 15;
12034 		lcmd.BLINDS1.filler = 0;
12035 		lcmd.BLINDS1.rssi = 12;
12036 		if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.BLINDS1)))
12037 			return false;
12038 		if (!IsTesting) {
12039 			//send to internal for now (later we use the ACK)
12040 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
12041 		}
12042 		return true;
12043 	}
12044 	break;
12045 	case pTypeRFY:
12046 	{
12047 		tRBUF lcmd;
12048 		lcmd.BLINDS1.packetlength = sizeof(lcmd.RFY) - 1;
12049 		lcmd.BLINDS1.packettype = dType;
12050 		lcmd.BLINDS1.subtype = dSubType;
12051 		lcmd.RFY.id1 = ID2;
12052 		lcmd.RFY.id2 = ID3;
12053 		lcmd.RFY.id3 = ID4;
12054 		lcmd.RFY.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
12055 		lcmd.RFY.unitcode = Unit;
12056 
12057 		if (IsTesting)
12058 		{
12059 			lcmd.RFY.cmnd = rfy_sProgram;
12060 		}
12061 		else
12062 		{
12063 			if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.RFY.cmnd, options))
12064 				return false;
12065 		}
12066 
12067 		if (lcmd.BLINDS1.subtype == sTypeRFY2)
12068 		{
12069 			//Special case for protocol version 2
12070 			lcmd.BLINDS1.subtype = sTypeRFY;
12071 			if (lcmd.RFY.cmnd == rfy_sUp)
12072 				lcmd.RFY.cmnd = rfy_s2SecUp;
12073 			else if (lcmd.RFY.cmnd == rfy_sDown)
12074 				lcmd.RFY.cmnd = rfy_s2SecDown;
12075 		}
12076 
12077 		level = 15;
12078 		lcmd.RFY.filler = 0;
12079 		lcmd.RFY.rssi = 12;
12080 		if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.RFY)))
12081 			return false;
12082 		if (!IsTesting) {
12083 			//send to internal for now (later we use the ACK)
12084 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
12085 		}
12086 		return true;
12087 	}
12088 	break;
12089 	case pTypeChime:
12090 	{
12091 		tRBUF lcmd;
12092 		lcmd.CHIME.packetlength = sizeof(lcmd.CHIME) - 1;
12093 		lcmd.CHIME.packettype = dType;
12094 		lcmd.CHIME.subtype = dSubType;
12095 		lcmd.CHIME.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
12096 		lcmd.CHIME.id1 = ID3;
12097 		lcmd.CHIME.id2 = ID4;
12098 		level = 15;
12099 		lcmd.CHIME.sound = Unit;
12100 		lcmd.CHIME.filler = 0;
12101 		lcmd.CHIME.rssi = 12;
12102 		if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.CHIME)))
12103 			return false;
12104 		if (!IsTesting) {
12105 			//send to internal for now (later we use the ACK)
12106 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
12107 		}
12108 		return true;
12109 	}
12110 	break;
12111 	case pTypeThermostat2:
12112 	{
12113 		tRBUF lcmd;
12114 		lcmd.THERMOSTAT2.packetlength = sizeof(lcmd.REMOTE) - 1;
12115 		lcmd.THERMOSTAT2.packettype = dType;
12116 		lcmd.THERMOSTAT2.subtype = dSubType;
12117 		lcmd.THERMOSTAT2.unitcode = Unit;
12118 		lcmd.THERMOSTAT2.cmnd = Unit;
12119 		lcmd.THERMOSTAT2.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
12120 
12121 		if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.THERMOSTAT2.cmnd, options))
12122 			return false;
12123 
12124 		lcmd.THERMOSTAT2.filler = 0;
12125 		lcmd.THERMOSTAT2.rssi = 12;
12126 		if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.THERMOSTAT2)))
12127 			return false;
12128 		if (!IsTesting) {
12129 			//send to internal for now (later we use the ACK)
12130 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
12131 		}
12132 		return true;
12133 	}
12134 	break;
12135 	case pTypeThermostat3:
12136 	{
12137 		tRBUF lcmd;
12138 		lcmd.THERMOSTAT3.packetlength = sizeof(lcmd.THERMOSTAT3) - 1;
12139 		lcmd.THERMOSTAT3.packettype = dType;
12140 		lcmd.THERMOSTAT3.subtype = dSubType;
12141 		lcmd.THERMOSTAT3.unitcode1 = ID2;
12142 		lcmd.THERMOSTAT3.unitcode2 = ID3;
12143 		lcmd.THERMOSTAT3.unitcode3 = ID4;
12144 		lcmd.THERMOSTAT3.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
12145 		if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.THERMOSTAT3.cmnd, options))
12146 			return false;
12147 		level = 15;
12148 		lcmd.THERMOSTAT3.filler = 0;
12149 		lcmd.THERMOSTAT3.rssi = 12;
12150 		if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.THERMOSTAT3)))
12151 			return false;
12152 		if (!IsTesting) {
12153 			//send to internal for now (later we use the ACK)
12154 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
12155 		}
12156 		return true;
12157 	}
12158 	break;
12159 	case pTypeThermostat4:
12160 	{
12161 		_log.Log(LOG_ERROR, "Thermostat 4 not implemented yet!");
12162 		/*
12163 		tRBUF lcmd;
12164 		lcmd.THERMOSTAT4.packetlength = sizeof(lcmd.THERMOSTAT3) - 1;
12165 		lcmd.THERMOSTAT4.packettype = dType;
12166 		lcmd.THERMOSTAT4.subtype = dSubType;
12167 		lcmd.THERMOSTAT4.unitcode1 = ID2;
12168 		lcmd.THERMOSTAT4.unitcode2 = ID3;
12169 		lcmd.THERMOSTAT4.unitcode3 = ID4;
12170 		lcmd.THERMOSTAT4.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
12171 		if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.THERMOSTAT4.mode, options))
12172 		return false;
12173 		level = 15;
12174 		lcmd.THERMOSTAT4.filler = 0;
12175 		lcmd.THERMOSTAT4.rssi = 12;
12176 		if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.THERMOSTAT4)))
12177 		return false;
12178 		if (!IsTesting) {
12179 		//send to internal for now (later we use the ACK)
12180 		PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t *)&lcmd, NULL, -1);
12181 		}
12182 		*/
12183 		return true;
12184 	}
12185 	break;
12186 	case pTypeRemote:
12187 	{
12188 		tRBUF lcmd;
12189 		lcmd.REMOTE.packetlength = sizeof(lcmd.REMOTE) - 1;
12190 		lcmd.REMOTE.packettype = dType;
12191 		lcmd.REMOTE.subtype = dSubType;
12192 		lcmd.REMOTE.id = ID4;
12193 		lcmd.REMOTE.cmnd = Unit;
12194 		lcmd.REMOTE.cmndtype = 0;
12195 		lcmd.REMOTE.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
12196 		lcmd.REMOTE.toggle = 0;
12197 		lcmd.REMOTE.rssi = 12;
12198 		if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.REMOTE)))
12199 			return false;
12200 		if (!IsTesting) {
12201 			//send to internal for now (later we use the ACK)
12202 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
12203 		}
12204 		return true;
12205 	}
12206 	break;
12207 	case pTypeEvohomeRelay:
12208 	{
12209 		_tEVOHOME3 lcmd;
12210 		memset(&lcmd, 0, sizeof(_tEVOHOME3));
12211 		lcmd.len = sizeof(_tEVOHOME3) - 1;
12212 		lcmd.type = pTypeEvohomeRelay;
12213 		lcmd.subtype = sTypeEvohomeRelay;
12214 		RFX_SETID3(ID, lcmd.id1, lcmd.id2, lcmd.id3)
12215 			lcmd.devno = Unit;
12216 		if (switchcmd == "On")
12217 			lcmd.demand = 200;
12218 		else
12219 			lcmd.demand = level;
12220 
12221 		if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(_tEVOHOME3)))
12222 			return false;
12223 		if (!IsTesting) {
12224 			//send to internal for now (later we use the ACK)
12225 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
12226 		}
12227 		return true;
12228 	}
12229 	break;
12230 	case pTypeRadiator1:
12231 		tRBUF lcmd;
12232 		lcmd.RADIATOR1.packetlength = sizeof(lcmd.RADIATOR1) - 1;
12233 		lcmd.RADIATOR1.packettype = pTypeRadiator1;
12234 		lcmd.RADIATOR1.subtype = sTypeSmartwares;
12235 		lcmd.RADIATOR1.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
12236 		lcmd.RADIATOR1.id1 = ID1;
12237 		lcmd.RADIATOR1.id2 = ID2;
12238 		lcmd.RADIATOR1.id3 = ID3;
12239 		lcmd.RADIATOR1.id4 = ID4;
12240 		lcmd.RADIATOR1.unitcode = Unit;
12241 		if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, lcmd.RADIATOR1.cmnd, options))
12242 			return false;
12243 		if (level > 15)
12244 			level = 15;
12245 		lcmd.RADIATOR1.temperature = 0;
12246 		lcmd.RADIATOR1.tempPoint5 = 0;
12247 		lcmd.RADIATOR1.filler = 0;
12248 		lcmd.RADIATOR1.rssi = 12;
12249 		if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.RADIATOR1)))
12250 			return false;
12251 
12252 		if (!IsTesting) {
12253 			//send to internal for now (later we use the ACK)
12254 			lcmd.RADIATOR1.subtype = sTypeSmartwaresSwitchRadiator;
12255 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&lcmd, NULL, -1);
12256 		}
12257 		return true;
12258 	case pTypeGeneralSwitch:
12259 	{
12260 
12261 		_tGeneralSwitch gswitch;
12262 		gswitch.type = dType;
12263 		gswitch.subtype = dSubType;
12264 		gswitch.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
12265 		gswitch.id = ID;
12266 		gswitch.unitcode = Unit;
12267 
12268 		if (!GetLightCommand(dType, dSubType, switchtype, switchcmd, gswitch.cmnd, options))
12269 			return false;
12270 
12271 		if ((switchtype != STYPE_Selector) && (dSubType != sSwitchGeneralSwitch))
12272 		{
12273 			level = (level > 99) ? 99 : level;
12274 		}
12275 
12276 		if (switchtype == STYPE_Doorbell) {
12277 			int rnvalue = 0;
12278 			m_sql.GetPreferencesVar("DoorbellCommand", rnvalue);
12279 			if (rnvalue == 0)
12280 				lcmd.LIGHTING2.cmnd = gswitch_sGroupOn;
12281 			else
12282 				lcmd.LIGHTING2.cmnd = gswitch_sOn;
12283 			level = 15;
12284 		}
12285 		else if (switchtype == STYPE_Selector)
12286 		{
12287 			if ((switchcmd == "Set Level") || (switchcmd == "Set Group Level")) {
12288 				std::map<std::string, std::string> statuses;
12289 				GetSelectorSwitchStatuses(options, statuses);
12290 				int maxLevel = statuses.size() * 10;
12291 
12292 				level = (level < 0) ? 0 : level;
12293 				level = (level > maxLevel) ? maxLevel : level;
12294 
12295 				std::stringstream sslevel;
12296 				sslevel << level;
12297 				if (statuses[sslevel.str()].empty()) {
12298 					_log.Log(LOG_ERROR, "Setting a wrong level value %d to Selector device %lu", level, ID);
12299 				}
12300 			}
12301 		}
12302 		else if (((switchtype == STYPE_BlindsPercentage) ||
12303 			(switchtype == STYPE_BlindsPercentageInverted)) &&
12304 			(gswitch.cmnd == gswitch_sSetLevel) && (level == 100))
12305 			gswitch.cmnd = gswitch_sOn;
12306 
12307 		gswitch.level = (uint8_t)level;
12308 		gswitch.rssi = 12;
12309 		if (switchtype != STYPE_Motion) //dont send actual motion off command
12310 		{
12311 			if (!WriteToHardware(HardwareID, (const char*)&gswitch, sizeof(_tGeneralSwitch)))
12312 				return false;
12313 		}
12314 		if (!IsTesting) {
12315 			//send to internal for now (later we use the ACK)
12316 			PushAndWaitRxMessage(m_hardwaredevices[hindex], (const uint8_t*)&gswitch, NULL, -1);
12317 		}
12318 	}
12319 	return true;
12320 	}
12321 	return false;
12322 }
12323 
SwitchModal(const std::string & idx,const std::string & status,const std::string & action,const std::string & ooc,const std::string & until)12324 bool MainWorker::SwitchModal(const std::string& idx, const std::string& status, const std::string& action, const std::string& ooc, const std::string& until)
12325 {
12326 	//Get Device details
12327 	std::vector<std::vector<std::string> > result;
12328 	result = m_sql.safe_query(
12329 		"SELECT HardwareID, DeviceID,Unit,Type,SubType,SwitchType,StrParam1,nValue FROM DeviceStatus WHERE (ID == '%q')",
12330 		idx.c_str());
12331 	if (result.empty())
12332 		return false;
12333 	std::vector<std::string> sd = result[0];
12334 
12335 	int nStatus = 0;
12336 	if (status == "Away")
12337 		nStatus = CEvohomeBase::cmEvoAway;
12338 	else if (status == "AutoWithEco")
12339 		nStatus = CEvohomeBase::cmEvoAutoWithEco;
12340 	else if (status == "DayOff")
12341 		nStatus = CEvohomeBase::cmEvoDayOff;
12342 	else if (status == "Custom")
12343 		nStatus = CEvohomeBase::cmEvoCustom;
12344 	else if (status == "Auto")
12345 		nStatus = CEvohomeBase::cmEvoAuto;
12346 	else if (status == "HeatingOff")
12347 		nStatus = CEvohomeBase::cmEvoHeatingOff;
12348 
12349 	int nValue = atoi(sd[7].c_str());
12350 	if (ooc == "1" && nValue == nStatus)
12351 		return false;//FIXME not an error ... status = (already set)
12352 
12353 	int HardwareID = atoi(sd[0].c_str());
12354 	int hindex = FindDomoticzHardware(HardwareID);
12355 	if (hindex == -1)
12356 		return false;
12357 
12358 	//uint8_t Unit = atoi(sd[2].c_str());
12359 	//uint8_t dType = atoi(sd[3].c_str());
12360 	//uint8_t dSubType = atoi(sd[4].c_str());
12361 	//_eSwitchType switchtype = (_eSwitchType)atoi(sd[5].c_str());
12362 
12363 	CDomoticzHardwareBase* pHardware = GetHardware(HardwareID);
12364 	if (pHardware == NULL)
12365 		return false;
12366 
12367 
12368 	unsigned long ID;
12369 	std::stringstream s_strid;
12370 	if (pHardware->HwdType == HTYPE_EVOHOME_SERIAL || pHardware->HwdType == HTYPE_EVOHOME_TCP)
12371 		s_strid << std::hex << sd[1];
12372 	else  //GB3: web based evohome uses decimal device ID's. We need to convert those to hex here to fit the 3-byte ID defined in the message struct
12373 		s_strid << std::hex << std::dec << sd[1];
12374 	s_strid >> ID;
12375 
12376 	//Update Domoticz evohome Device
12377 	_tEVOHOME1 tsen;
12378 	memset(&tsen, 0, sizeof(_tEVOHOME1));
12379 	tsen.len = sizeof(_tEVOHOME1) - 1;
12380 	tsen.type = pTypeEvohome;
12381 	tsen.subtype = sTypeEvohome;
12382 	RFX_SETID3(ID, tsen.id1, tsen.id2, tsen.id3)
12383 		tsen.action = (action == "1") ? 1 : 0;
12384 	tsen.status = nStatus;
12385 
12386 	tsen.mode = until.empty() ? CEvohomeBase::cmPerm : CEvohomeBase::cmTmp;
12387 	if (tsen.mode == CEvohomeBase::cmTmp)
12388 		CEvohomeDateTime::DecodeISODate(tsen, until.c_str());
12389 	WriteToHardware(HardwareID, (const char*)&tsen, sizeof(_tEVOHOME1));
12390 
12391 	//the latency on the scripted solution is quite bad so it's good to see the update happening...ideally this would go to an 'updating' status (also useful to update database if we ever use this as a pure virtual device)
12392 	PushRxMessage(pHardware, (const uint8_t*)&tsen, NULL, 255);
12393 	return true;
12394 }
12395 
SwitchLight(const std::string & idx,const std::string & switchcmd,const std::string & level,const std::string & color,const std::string & ooc,const int ExtraDelay,const std::string & User)12396 bool MainWorker::SwitchLight(const std::string& idx, const std::string& switchcmd, const std::string& level, const std::string& color, const std::string& ooc, const int ExtraDelay, const std::string& User)
12397 {
12398 	uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
12399 	int ilevel = -1;
12400 	if (level != "")
12401 		ilevel = atoi(level.c_str());
12402 
12403 	return SwitchLight(ID, switchcmd, ilevel, _tColor(color), atoi(ooc.c_str()) != 0, ExtraDelay, User);
12404 }
12405 
SwitchLight(const uint64_t idx,const std::string & switchcmd,const int level,_tColor color,const bool ooc,const int ExtraDelay,const std::string & User)12406 bool MainWorker::SwitchLight(const uint64_t idx, const std::string& switchcmd, const int level, _tColor color, const bool ooc, const int ExtraDelay, const std::string& User)
12407 {
12408 	//Get Device details
12409 	_log.Debug(DEBUG_NORM, "MAIN SwitchLight idx:%" PRId64 " cmd:%s lvl:%d ", idx, switchcmd.c_str(), level);
12410 	std::vector<std::vector<std::string> > result;
12411 	result = m_sql.safe_query(
12412 		"SELECT HardwareID,DeviceID,Unit,Type,SubType,SwitchType,AddjValue2,nValue,sValue,Name,Options FROM DeviceStatus WHERE (ID == %" PRIu64 ")",
12413 		idx);
12414 	if (result.empty())
12415 		return false;
12416 
12417 	std::vector<std::string> sd = result[0];
12418 
12419 	//uint8_t dType = atoi(sd[3].c_str());
12420 	//uint8_t dSubType = atoi(sd[4].c_str());
12421 	_eSwitchType switchtype = (_eSwitchType)atoi(sd[5].c_str());
12422 	int iOnDelay = atoi(sd[6].c_str());
12423 	int nValue = atoi(sd[7].c_str());
12424 	std::string sValue = sd[8].c_str();
12425 	std::string devName = sd[9].c_str();
12426 	//std::string sOptions = sd[10].c_str();
12427 
12428 	bool bIsOn = IsLightSwitchOn(switchcmd);
12429 	if (ooc)//Only on change
12430 	{
12431 		int nNewVal = bIsOn ? 1 : 0;//Is that everything we need here
12432 		if ((switchtype == STYPE_Selector) && (nValue == nNewVal) && (level == atoi(sValue.c_str()))) {
12433 			return true;
12434 		}
12435 		else if (nValue == nNewVal) {
12436 			return true;//FIXME no return code for already set
12437 		}
12438 	}
12439 	//Check if we have an On-Delay, if yes, add it to the tasker
12440 	if (((bIsOn) && (iOnDelay != 0)) || ExtraDelay)
12441 	{
12442 		if (iOnDelay + ExtraDelay != 0)
12443 		{
12444 			_log.Log(LOG_NORM, "Delaying switch [%s] action (%s) for %d seconds", devName.c_str(), switchcmd.c_str(), iOnDelay + ExtraDelay);
12445 		}
12446 		m_sql.AddTaskItem(_tTaskItem::SwitchLightEvent(static_cast<float>(iOnDelay + ExtraDelay), idx, switchcmd, level, color, "Switch with Delay", User));
12447 		return true;
12448 	}
12449 	else
12450 		return SwitchLightInt(sd, switchcmd, level, color, false, User);
12451 }
12452 
SetSetPoint(const std::string & idx,const float TempValue,const std::string & newMode,const std::string & until)12453 bool MainWorker::SetSetPoint(const std::string& idx, const float TempValue, const std::string& newMode, const std::string& until)
12454 {
12455 	//Get Device details
12456 	std::vector<std::vector<std::string> > result;
12457 	result = m_sql.safe_query(
12458 		"SELECT HardwareID, DeviceID,Unit,Type,SubType,SwitchType,StrParam1,ID FROM DeviceStatus WHERE (ID == '%q')",
12459 		idx.c_str());
12460 	if (result.empty())
12461 		return false;
12462 
12463 	std::vector<std::string> sd = result[0];
12464 	int HardwareID = atoi(sd[0].c_str());
12465 	int hindex = FindDomoticzHardware(HardwareID);
12466 	if (hindex == -1)
12467 		return false;
12468 
12469 	CDomoticzHardwareBase* pHardware = GetHardware(HardwareID);
12470 	if (pHardware == NULL)
12471 		return false;
12472 
12473 	if (pHardware->HwdType != HTYPE_EVOHOME_SCRIPT && pHardware->HwdType != HTYPE_EVOHOME_SERIAL && pHardware->HwdType != HTYPE_EVOHOME_WEB && pHardware->HwdType != HTYPE_EVOHOME_TCP)
12474 		return SetSetPointInt(sd, TempValue);
12475 
12476 	int nEvoMode = 0;
12477 	if (newMode == "PermanentOverride" || newMode.empty())
12478 		nEvoMode = CEvohomeBase::zmPerm;
12479 	else if (newMode == "TemporaryOverride")
12480 		nEvoMode = CEvohomeBase::zmTmp;
12481 
12482 	//_log.Log(LOG_DEBUG, "Set point %s %f '%s' '%s'", idx.c_str(), TempValue, newMode.c_str(), until.c_str());
12483 
12484 	unsigned long ID;
12485 	std::stringstream s_strid;
12486 	if (pHardware->HwdType == HTYPE_EVOHOME_SERIAL || pHardware->HwdType == HTYPE_EVOHOME_TCP)
12487 		s_strid << std::hex << sd[1];
12488 	else //GB3: web based evohome uses decimal device ID's. We need to convert those to hex here to fit the 3-byte ID defined in the message struct
12489 		s_strid << std::hex << std::dec << sd[1];
12490 	s_strid >> ID;
12491 
12492 
12493 	uint8_t Unit = atoi(sd[2].c_str());
12494 	uint8_t dType = atoi(sd[3].c_str());
12495 	uint8_t dSubType = atoi(sd[4].c_str());
12496 	//_eSwitchType switchtype=(_eSwitchType)atoi(sd[5].c_str());
12497 
12498 	if (pHardware->HwdType == HTYPE_EVOHOME_SCRIPT || pHardware->HwdType == HTYPE_EVOHOME_SERIAL || pHardware->HwdType == HTYPE_EVOHOME_WEB || pHardware->HwdType == HTYPE_EVOHOME_TCP)
12499 	{
12500 		_tEVOHOME2 tsen;
12501 		memset(&tsen, 0, sizeof(_tEVOHOME2));
12502 		tsen.len = sizeof(_tEVOHOME2) - 1;
12503 		tsen.type = dType;
12504 		tsen.subtype = dSubType;
12505 		RFX_SETID3(ID, tsen.id1, tsen.id2, tsen.id3)
12506 			tsen.zone = Unit;//controller is 0 so let our zones start from 1...
12507 		tsen.updatetype = CEvohomeBase::updSetPoint;//setpoint
12508 		tsen.temperature = static_cast<int16_t>((dType == pTypeEvohomeWater) ? TempValue : TempValue * 100.0f);
12509 		tsen.mode = nEvoMode;
12510 		if (nEvoMode == CEvohomeBase::zmTmp)
12511 			CEvohomeDateTime::DecodeISODate(tsen, until.c_str());
12512 		WriteToHardware(HardwareID, (const char*)&tsen, sizeof(_tEVOHOME2));
12513 
12514 		//Pass across the current controller mode if we're going to update as per the hw device
12515 		result = m_sql.safe_query(
12516 			"SELECT Name,DeviceID,nValue FROM DeviceStatus WHERE (HardwareID==%d) AND (Unit==0)",
12517 			HardwareID);
12518 		if (!result.empty())
12519 		{
12520 			sd = result[0];
12521 			tsen.controllermode = atoi(sd[2].c_str());
12522 		}
12523 		//the latency on the scripted solution is quite bad so it's good to see the update happening...ideally this would go to an 'updating' status (also useful to update database if we ever use this as a pure virtual device)
12524 		PushAndWaitRxMessage(pHardware, (const uint8_t*)&tsen, NULL, -1);
12525 	}
12526 	return true;
12527 }
12528 
SetSetPointInt(const std::vector<std::string> & sd,const float TempValue)12529 bool MainWorker::SetSetPointInt(const std::vector<std::string>& sd, const float TempValue)
12530 {
12531 	int HardwareID = atoi(sd[0].c_str());
12532 	int hindex = FindDomoticzHardware(HardwareID);
12533 	if (hindex == -1)
12534 		return false;
12535 
12536 	unsigned long ID;
12537 	std::stringstream s_strid;
12538 	s_strid << std::hex << sd[1];
12539 	s_strid >> ID;
12540 	uint8_t ID1 = (uint8_t)((ID & 0xFF000000) >> 24);
12541 	uint8_t ID2 = (uint8_t)((ID & 0x00FF0000) >> 16);
12542 	uint8_t ID3 = (uint8_t)((ID & 0x0000FF00) >> 8);
12543 	uint8_t ID4 = (uint8_t)((ID & 0x000000FF));
12544 
12545 	uint8_t Unit = atoi(sd[2].c_str());
12546 	uint8_t dType = atoi(sd[3].c_str());
12547 	uint8_t dSubType = atoi(sd[4].c_str());
12548 	//_eSwitchType switchtype = (_eSwitchType)atoi(sd[5].c_str());
12549 
12550 	CDomoticzHardwareBase* pHardware = GetHardware(HardwareID);
12551 	if (pHardware == NULL)
12552 		return false;
12553 	//
12554 	//	For plugins all the specific logic below is irrelevent
12555 	//	so just send the full details to the plugin so that it can take appropriate action
12556 	//
12557 	if (pHardware->HwdType == HTYPE_PythonPlugin)
12558 	{
12559 #ifdef ENABLE_PYTHON
12560 		((Plugins::CPlugin*)pHardware)->SendCommand(Unit, "Set Level", TempValue);
12561 #endif
12562 	}
12563 	else if (
12564 		(pHardware->HwdType == HTYPE_OpenThermGateway) ||
12565 		(pHardware->HwdType == HTYPE_OpenThermGatewayTCP) ||
12566 		(pHardware->HwdType == HTYPE_ICYTHERMOSTAT) ||
12567 		(pHardware->HwdType == HTYPE_TOONTHERMOSTAT) ||
12568 		(pHardware->HwdType == HTYPE_AtagOne) ||
12569 		(pHardware->HwdType == HTYPE_NEST) ||
12570 		(pHardware->HwdType == HTYPE_Nest_OAuthAPI) ||
12571 		(pHardware->HwdType == HTYPE_ANNATHERMOSTAT) ||
12572 		(pHardware->HwdType == HTYPE_THERMOSMART) ||
12573 		(pHardware->HwdType == HTYPE_Tado) ||
12574 		(pHardware->HwdType == HTYPE_EVOHOME_SCRIPT) ||
12575 		(pHardware->HwdType == HTYPE_EVOHOME_SERIAL) ||
12576 		(pHardware->HwdType == HTYPE_EVOHOME_TCP) ||
12577 		(pHardware->HwdType == HTYPE_EVOHOME_WEB) ||
12578 		(pHardware->HwdType == HTYPE_Netatmo) ||
12579 		(pHardware->HwdType == HTYPE_NefitEastLAN) ||
12580 		(pHardware->HwdType == HTYPE_IntergasInComfortLAN2RF) ||
12581 		(pHardware->HwdType == HTYPE_OpenWebNetTCP)
12582 		)
12583 	{
12584 		if (pHardware->HwdType == HTYPE_OpenThermGateway)
12585 		{
12586 			OTGWSerial* pGateway = reinterpret_cast<OTGWSerial*>(pHardware);
12587 			pGateway->SetSetpoint(ID4, TempValue);
12588 		}
12589 		else if (pHardware->HwdType == HTYPE_OpenThermGatewayTCP)
12590 		{
12591 			OTGWTCP* pGateway = reinterpret_cast<OTGWTCP*>(pHardware);
12592 			pGateway->SetSetpoint(ID4, TempValue);
12593 		}
12594 		else if (pHardware->HwdType == HTYPE_ICYTHERMOSTAT)
12595 		{
12596 			CICYThermostat* pGateway = reinterpret_cast<CICYThermostat*>(pHardware);
12597 			pGateway->SetSetpoint(ID4, TempValue);
12598 		}
12599 		else if (pHardware->HwdType == HTYPE_TOONTHERMOSTAT)
12600 		{
12601 			CToonThermostat* pGateway = reinterpret_cast<CToonThermostat*>(pHardware);
12602 			pGateway->SetSetpoint(ID4, TempValue);
12603 		}
12604 		else if (pHardware->HwdType == HTYPE_AtagOne)
12605 		{
12606 			CAtagOne* pGateway = reinterpret_cast<CAtagOne*>(pHardware);
12607 			pGateway->SetSetpoint(ID4, TempValue);
12608 		}
12609 		else if (pHardware->HwdType == HTYPE_NEST)
12610 		{
12611 			CNest* pGateway = reinterpret_cast<CNest*>(pHardware);
12612 			pGateway->SetSetpoint(ID4, TempValue);
12613 		}
12614 		else if (pHardware->HwdType == HTYPE_Nest_OAuthAPI)
12615 		{
12616 			CNestOAuthAPI* pGateway = reinterpret_cast<CNestOAuthAPI*>(pHardware);
12617 			pGateway->SetSetpoint(ID4, TempValue);
12618 		}
12619 		else if (pHardware->HwdType == HTYPE_ANNATHERMOSTAT)
12620 		{
12621 			CAnnaThermostat* pGateway = reinterpret_cast<CAnnaThermostat*>(pHardware);
12622 			pGateway->SetSetpoint(ID4, TempValue);
12623 		}
12624 		else if (pHardware->HwdType == HTYPE_THERMOSMART)
12625 		{
12626 			CThermosmart* pGateway = reinterpret_cast<CThermosmart*>(pHardware);
12627 			pGateway->SetSetpoint(ID4, TempValue);
12628 		}
12629 		else if (pHardware->HwdType == HTYPE_Tado)
12630 		{
12631 			CTado* pGateway = reinterpret_cast<CTado*>(pHardware);
12632 			pGateway->SetSetpoint(ID4, TempValue);
12633 		}
12634 		else if (pHardware->HwdType == HTYPE_Netatmo)
12635 		{
12636 			CNetatmo* pGateway = reinterpret_cast<CNetatmo*>(pHardware);
12637 			pGateway->SetSetpoint(ID, TempValue);
12638 		}
12639 		else if (pHardware->HwdType == HTYPE_NefitEastLAN)
12640 		{
12641 			CNefitEasy* pGateway = reinterpret_cast<CNefitEasy*>(pHardware);
12642 			pGateway->SetSetpoint(ID2, TempValue);
12643 		}
12644 		else if (pHardware->HwdType == HTYPE_EVOHOME_SCRIPT || pHardware->HwdType == HTYPE_EVOHOME_SERIAL || pHardware->HwdType == HTYPE_EVOHOME_WEB || pHardware->HwdType == HTYPE_EVOHOME_TCP)
12645 		{
12646 			SetSetPoint(sd[7], TempValue, "PermanentOverride", "");
12647 		}
12648 		else if (pHardware->HwdType == HTYPE_IntergasInComfortLAN2RF)
12649 		{
12650 			CInComfort* pGateway = reinterpret_cast<CInComfort*>(pHardware);
12651 			pGateway->SetSetpoint(ID4, TempValue);
12652 		}
12653 		else if (pHardware->HwdType == HTYPE_OpenWebNetTCP)
12654 		{
12655 			COpenWebNetTCP* pGateway = reinterpret_cast<COpenWebNetTCP*>(pHardware);
12656 			return pGateway->SetSetpoint(ID, TempValue);
12657 		}
12658 	}
12659 	else
12660 	{
12661 		if (dType == pTypeRadiator1)
12662 		{
12663 			tRBUF lcmd;
12664 			lcmd.RADIATOR1.packetlength = sizeof(lcmd.RADIATOR1) - 1;
12665 			lcmd.RADIATOR1.packettype = dType;
12666 			lcmd.RADIATOR1.subtype = dSubType;
12667 			lcmd.RADIATOR1.seqnbr = m_hardwaredevices[hindex]->m_SeqNr++;
12668 			lcmd.RADIATOR1.id1 = ID1;
12669 			lcmd.RADIATOR1.id2 = ID2;
12670 			lcmd.RADIATOR1.id3 = ID3;
12671 			lcmd.RADIATOR1.id4 = ID4;
12672 			lcmd.RADIATOR1.unitcode = Unit;
12673 			lcmd.RADIATOR1.filler = 0;
12674 			lcmd.RADIATOR1.rssi = 12;
12675 			lcmd.RADIATOR1.cmnd = Radiator1_sSetTemp;
12676 
12677 			char szTemp[20];
12678 			sprintf(szTemp, "%.1f", TempValue);
12679 			std::vector<std::string> strarray;
12680 			StringSplit(szTemp, ".", strarray);
12681 			lcmd.RADIATOR1.temperature = (uint8_t)atoi(strarray[0].c_str());
12682 			lcmd.RADIATOR1.tempPoint5 = (uint8_t)atoi(strarray[1].c_str());
12683 			if (!WriteToHardware(HardwareID, (const char*)&lcmd, sizeof(lcmd.RADIATOR1)))
12684 				return false;
12685 			PushAndWaitRxMessage(pHardware, (const uint8_t*)&lcmd, NULL, -1);
12686 		}
12687 		else
12688 		{
12689 			float tempDest = TempValue;
12690 			unsigned char tSign = m_sql.m_tempsign[0];
12691 			if (tSign == 'F')
12692 			{
12693 				//Convert to Celsius
12694 				tempDest = static_cast<float>(ConvertToCelsius(tempDest));
12695 			}
12696 
12697 			_tThermostat tmeter;
12698 			tmeter.subtype = sTypeThermSetpoint;
12699 			tmeter.id1 = ID1;
12700 			tmeter.id2 = ID2;
12701 			tmeter.id3 = ID3;
12702 			tmeter.id4 = ID4;
12703 			tmeter.dunit = 1;
12704 			tmeter.temp = tempDest;
12705 			if (!WriteToHardware(HardwareID, (const char*)&tmeter, sizeof(_tThermostat)))
12706 				return false;
12707 			if (pHardware->HwdType == HTYPE_Dummy)
12708 			{
12709 				//Also put it in the database, as this devices does not send updates
12710 				PushAndWaitRxMessage(pHardware, (const uint8_t*)&tmeter, NULL, -1);
12711 			}
12712 		}
12713 	}
12714 	return true;
12715 }
12716 
SetSetPoint(const std::string & idx,const float TempValue)12717 bool MainWorker::SetSetPoint(const std::string& idx, const float TempValue)
12718 {
12719 	//Get Device details
12720 	std::vector<std::vector<std::string> > result;
12721 	result = m_sql.safe_query(
12722 		"SELECT HardwareID, DeviceID,Unit,Type,SubType,SwitchType,StrParam1,ID FROM DeviceStatus WHERE (ID == '%q')",
12723 		idx.c_str());
12724 	if (result.empty())
12725 		return false;
12726 
12727 	std::vector<std::string> sd = result[0];
12728 	return SetSetPointInt(sd, TempValue);
12729 }
12730 
SetClockInt(const std::vector<std::string> & sd,const std::string & clockstr)12731 bool MainWorker::SetClockInt(const std::vector<std::string>& sd, const std::string& clockstr)
12732 {
12733 #ifdef WITH_OPENZWAVE
12734 	int HardwareID = atoi(sd[0].c_str());
12735 	int hindex = FindDomoticzHardware(HardwareID);
12736 	if (hindex == -1)
12737 		return false;
12738 
12739 	unsigned long ID;
12740 	std::stringstream s_strid;
12741 	s_strid << std::hex << sd[1];
12742 	s_strid >> ID;
12743 	CDomoticzHardwareBase* pHardware = GetHardware(HardwareID);
12744 	if (pHardware == NULL)
12745 		return false;
12746 	if (pHardware->HwdType == HTYPE_OpenZWave)
12747 	{
12748 		std::vector<std::string> splitresults;
12749 		StringSplit(clockstr, ";", splitresults);
12750 		if (splitresults.size() != 3)
12751 			return false;
12752 		int day = atoi(splitresults[0].c_str());
12753 		int hour = atoi(splitresults[1].c_str());
12754 		int minute = atoi(splitresults[2].c_str());
12755 
12756 		_tGeneralDevice tmeter;
12757 		tmeter.subtype = sTypeZWaveClock;
12758 		tmeter.intval1 = ID;
12759 		tmeter.intval2 = (day * (24 * 60)) + (hour * 60) + minute;
12760 		if (!WriteToHardware(HardwareID, (const char*)&tmeter, sizeof(_tGeneralDevice)))
12761 			return false;
12762 	}
12763 #endif
12764 	return true;
12765 }
12766 
SetClock(const std::string & idx,const std::string & clockstr)12767 bool MainWorker::SetClock(const std::string& idx, const std::string& clockstr)
12768 {
12769 	//Get Device details
12770 	std::vector<std::vector<std::string> > result;
12771 	result = m_sql.safe_query(
12772 		"SELECT HardwareID, DeviceID,Unit,Type,SubType,SwitchType FROM DeviceStatus WHERE (ID == '%q')",
12773 		idx.c_str());
12774 	if (result.empty())
12775 		return false;
12776 
12777 	std::vector<std::string> sd = result[0];
12778 	return SetClockInt(sd, clockstr);
12779 }
12780 
SetZWaveThermostatModeInt(const std::vector<std::string> & sd,const int tMode)12781 bool MainWorker::SetZWaveThermostatModeInt(const std::vector<std::string>& sd, const int tMode)
12782 {
12783 #ifdef WITH_OPENZWAVE
12784 	int HardwareID = atoi(sd[0].c_str());
12785 	int hindex = FindDomoticzHardware(HardwareID);
12786 	if (hindex == -1)
12787 		return false;
12788 
12789 	unsigned long ID;
12790 	std::stringstream s_strid;
12791 	s_strid << std::hex << sd[1];
12792 	s_strid >> ID;
12793 	CDomoticzHardwareBase* pHardware = GetHardware(HardwareID);
12794 	if (pHardware == NULL)
12795 		return false;
12796 	if (pHardware->HwdType == HTYPE_OpenZWave)
12797 	{
12798 		_tGeneralDevice tmeter;
12799 		tmeter.subtype = sTypeZWaveThermostatMode;
12800 		tmeter.intval1 = ID;
12801 		tmeter.intval2 = tMode;
12802 		if (!WriteToHardware(HardwareID, (const char*)&tmeter, sizeof(_tGeneralDevice)))
12803 			return false;
12804 	}
12805 #endif
12806 	return true;
12807 }
12808 
SetZWaveThermostatFanModeInt(const std::vector<std::string> & sd,const int fMode)12809 bool MainWorker::SetZWaveThermostatFanModeInt(const std::vector<std::string>& sd, const int fMode)
12810 {
12811 #ifdef WITH_OPENZWAVE
12812 	int HardwareID = atoi(sd[0].c_str());
12813 	int hindex = FindDomoticzHardware(HardwareID);
12814 	if (hindex == -1)
12815 		return false;
12816 
12817 	unsigned long ID;
12818 	std::stringstream s_strid;
12819 	s_strid << std::hex << sd[1];
12820 	s_strid >> ID;
12821 	CDomoticzHardwareBase* pHardware = GetHardware(HardwareID);
12822 	if (pHardware == NULL)
12823 		return false;
12824 	if (pHardware->HwdType == HTYPE_OpenZWave)
12825 	{
12826 		_tGeneralDevice tmeter;
12827 		tmeter.subtype = sTypeZWaveThermostatFanMode;
12828 		tmeter.intval1 = ID;
12829 		tmeter.intval2 = fMode;
12830 		if (!WriteToHardware(HardwareID, (const char*)&tmeter, sizeof(_tGeneralDevice)))
12831 			return false;
12832 	}
12833 #endif
12834 	return true;
12835 }
12836 
SetZWaveThermostatMode(const std::string & idx,const int tMode)12837 bool MainWorker::SetZWaveThermostatMode(const std::string& idx, const int tMode)
12838 {
12839 	//Get Device details
12840 	std::vector<std::vector<std::string> > result;
12841 	result = m_sql.safe_query(
12842 		"SELECT HardwareID, DeviceID,Unit,Type,SubType,SwitchType FROM DeviceStatus WHERE (ID == '%q')",
12843 		idx.c_str());
12844 	if (result.empty())
12845 		return false;
12846 
12847 	std::vector<std::string> sd = result[0];
12848 	return SetZWaveThermostatModeInt(sd, tMode);
12849 }
12850 
SetZWaveThermostatFanMode(const std::string & idx,const int fMode)12851 bool MainWorker::SetZWaveThermostatFanMode(const std::string& idx, const int fMode)
12852 {
12853 	//Get Device details
12854 	std::vector<std::vector<std::string> > result;
12855 	result = m_sql.safe_query(
12856 		"SELECT HardwareID, DeviceID,Unit,Type,SubType,SwitchType FROM DeviceStatus WHERE (ID == '%q')",
12857 		idx.c_str());
12858 	if (result.empty())
12859 		return false;
12860 
12861 	std::vector<std::string> sd = result[0];
12862 	return SetZWaveThermostatFanModeInt(sd, fMode);
12863 }
12864 
SetThermostatState(const std::string & idx,const int newState)12865 bool MainWorker::SetThermostatState(const std::string& idx, const int newState)
12866 {
12867 	//Get Device details
12868 	std::vector<std::vector<std::string> > result;
12869 	result = m_sql.safe_query(
12870 		"SELECT HardwareID, DeviceID,Unit,Type,SubType,SwitchType FROM DeviceStatus WHERE (ID == '%q')",
12871 		idx.c_str());
12872 	if (result.empty())
12873 		return false;
12874 	int HardwareID = atoi(result[0][0].c_str());
12875 	int hindex = FindDomoticzHardware(HardwareID);
12876 	if (hindex == -1)
12877 		return false;
12878 
12879 	CDomoticzHardwareBase* pHardware = GetHardware(HardwareID);
12880 	if (pHardware == NULL)
12881 		return false;
12882 	if (pHardware->HwdType == HTYPE_TOONTHERMOSTAT)
12883 	{
12884 		CToonThermostat* pGateway = reinterpret_cast<CToonThermostat*>(pHardware);
12885 		pGateway->SetProgramState(newState);
12886 		return true;
12887 	}
12888 	if (pHardware->HwdType == HTYPE_AtagOne)
12889 	{
12890 		//CAtagOne *pGateway = reinterpret_cast<CAtagOne*>(pHardware);
12891 		//pGateway->SetProgramState(newState);
12892 		return true;
12893 	}
12894 	else if (pHardware->HwdType == HTYPE_NEST)
12895 	{
12896 		CNest* pGateway = reinterpret_cast<CNest*>(pHardware);
12897 		pGateway->SetProgramState(newState);
12898 		return true;
12899 	}
12900 	else if (pHardware->HwdType == HTYPE_Nest_OAuthAPI)
12901 	{
12902 		CNestOAuthAPI* pGateway = reinterpret_cast<CNestOAuthAPI*>(pHardware);
12903 		pGateway->SetProgramState(newState);
12904 		return true;
12905 	}
12906 	else if (pHardware->HwdType == HTYPE_ANNATHERMOSTAT)
12907 	{
12908 		CAnnaThermostat* pGateway = reinterpret_cast<CAnnaThermostat*>(pHardware);
12909 		pGateway->SetProgramState(newState);
12910 		return true;
12911 	}
12912 	else if (pHardware->HwdType == HTYPE_THERMOSMART)
12913 	{
12914 		//CThermosmart *pGateway = reinterpret_cast<CThermosmart *>(pHardware);
12915 		//pGateway->SetProgramState(newState);
12916 		return true;
12917 	}
12918 	else if (pHardware->HwdType == HTYPE_Netatmo)
12919 	{
12920 		CNetatmo* pGateway = reinterpret_cast<CNetatmo*>(pHardware);
12921 		int tIndex = atoi(idx.c_str());
12922 		pGateway->SetProgramState(tIndex, newState);
12923 		return true;
12924 	}
12925 	else if (pHardware->HwdType == HTYPE_IntergasInComfortLAN2RF)
12926 	{
12927 		CInComfort* pGateway = reinterpret_cast<CInComfort*>(pHardware);
12928 		pGateway->SetProgramState(newState);
12929 		return true;
12930 	}
12931 	return false;
12932 }
12933 
12934 
12935 //returns if a device activates a scene
DoesDeviceActiveAScene(const uint64_t DevRowIdx,const int Cmnd)12936 bool MainWorker::DoesDeviceActiveAScene(const uint64_t DevRowIdx, const int Cmnd)
12937 {
12938 	//check for scene code
12939 	std::vector<std::vector<std::string> > result;
12940 
12941 	result = m_sql.safe_query("SELECT Activators, SceneType FROM Scenes WHERE (Activators!='')");
12942 	if (!result.empty())
12943 	{
12944 		for (const auto& itt : result)
12945 		{
12946 			std::vector<std::string> sd = itt;
12947 
12948 			int SceneType = atoi(sd[1].c_str());
12949 
12950 			std::vector<std::string> arrayActivators;
12951 			StringSplit(sd[0], ";", arrayActivators);
12952 			for (const auto& ittAct : arrayActivators)
12953 			{
12954 				std::string sCodeCmd = ittAct;
12955 
12956 				std::vector<std::string> arrayCode;
12957 				StringSplit(sCodeCmd, ":", arrayCode);
12958 
12959 				std::string sID = arrayCode[0];
12960 				std::string sCode = "";
12961 				if (arrayCode.size() == 2)
12962 				{
12963 					sCode = arrayCode[1];
12964 				}
12965 
12966 				uint64_t aID = std::strtoull(sID.c_str(), nullptr, 10);
12967 				if (aID == DevRowIdx)
12968 				{
12969 					if ((SceneType == SGTYPE_GROUP) || (sCode.empty()))
12970 						return true;
12971 					int iCode = atoi(sCode.c_str());
12972 					if (iCode == Cmnd)
12973 						return true;
12974 				}
12975 			}
12976 		}
12977 	}
12978 	return false;
12979 }
12980 
SwitchScene(const std::string & idx,const std::string & switchcmd,const std::string & User)12981 bool MainWorker::SwitchScene(const std::string& idx, const std::string& switchcmd, const std::string& User)
12982 {
12983 	uint64_t ID = std::strtoull(idx.c_str(), nullptr, 10);
12984 
12985 	return SwitchScene(ID, switchcmd, User);
12986 }
12987 
SwitchScene(const uint64_t idx,std::string switchcmd,const std::string & User)12988 bool MainWorker::SwitchScene(const uint64_t idx, std::string switchcmd, const std::string& User)
12989 {
12990 	std::vector<std::vector<std::string> > result;
12991 	int nValue = (switchcmd == "On") ? 1 : 0;
12992 
12993 	std::string Name = "Unknown?";
12994 	_eSceneGroupType scenetype = SGTYPE_SCENE;
12995 	std::string onaction = "";
12996 	std::string offaction = "";
12997 	std::string status = "";
12998 
12999 	//Get Scene details
13000 	result = m_sql.safe_query("SELECT Name, SceneType, OnAction, OffAction, nValue FROM Scenes WHERE (ID == %" PRIu64 ")", idx);
13001 	if (!result.empty())
13002 	{
13003 		std::vector<std::string> sds = result[0];
13004 		Name = sds[0];
13005 		scenetype = (_eSceneGroupType)atoi(sds[1].c_str());
13006 		onaction = sds[2];
13007 		offaction = sds[3];
13008 		status = sds[4];
13009 
13010 		if (scenetype == SGTYPE_GROUP)
13011 		{
13012 			//when asking for Toggle, just switch to the opposite value
13013 			if (switchcmd == "Toggle") {
13014 				nValue = (atoi(status.c_str()) == 0 ? 1 : 0);
13015 				switchcmd = (nValue == 1 ? "On" : "Off");
13016 			}
13017 		}
13018 		m_sql.HandleOnOffAction((nValue == 1), onaction, offaction);
13019 	}
13020 
13021 	m_sql.safe_query("INSERT INTO SceneLog (SceneRowID, nValue, User) VALUES ('%" PRIu64 "', '%d', '%q')", idx, nValue, User.c_str());
13022 
13023 	std::string szLastUpdate = TimeToString(NULL, TF_DateTime);
13024 	m_sql.safe_query("UPDATE Scenes SET nValue=%d, LastUpdate='%q' WHERE (ID == %" PRIu64 ")",
13025 		nValue,
13026 		szLastUpdate.c_str(),
13027 		idx);
13028 
13029 	//Check if we need to email a snapshot of a Camera
13030 	std::string emailserver;
13031 	int n2Value;
13032 	if (m_sql.GetPreferencesVar("EmailServer", n2Value, emailserver))
13033 	{
13034 		if (emailserver != "")
13035 		{
13036 			result = m_sql.safe_query(
13037 				"SELECT CameraRowID, DevSceneDelay FROM CamerasActiveDevices WHERE (DevSceneType==1) AND (DevSceneRowID==%" PRIu64 ") AND (DevSceneWhen==%d)",
13038 				idx,
13039 				!nValue
13040 			);
13041 			if (!result.empty())
13042 			{
13043 				for (const auto& ittCam : result)
13044 				{
13045 					std::vector<std::string> sd = ittCam;
13046 					std::string camidx = sd[0];
13047 					int delay = atoi(sd[1].c_str());
13048 					std::string subject;
13049 					if (scenetype == SGTYPE_SCENE)
13050 						subject = Name + " Activated";
13051 					else
13052 						subject = Name + " Status: " + switchcmd;
13053 					m_sql.AddTaskItem(_tTaskItem::EmailCameraSnapshot(static_cast<float>(delay + 1), camidx, subject));
13054 				}
13055 			}
13056 		}
13057 	}
13058 
13059 	_log.Log(LOG_NORM, "Activating Scene/Group: [%s]", Name.c_str());
13060 
13061 	bool bEventTrigger = true;
13062 	if (m_sql.m_bEnableEventSystem)
13063 		bEventTrigger = m_eventsystem.UpdateSceneGroup(idx, nValue, szLastUpdate);
13064 
13065 	// Notify listeners
13066 	sOnSwitchScene(idx, Name);
13067 
13068 	//now switch all attached devices, and only the onces that do not trigger a scene
13069 	result = m_sql.safe_query(
13070 		"SELECT DeviceRowID, Cmd, Level, Color, OnDelay, OffDelay FROM SceneDevices WHERE (SceneRowID == %" PRIu64 ") ORDER BY [Order] ASC", idx);
13071 	if (result.empty())
13072 		return true; //no devices in the scene
13073 
13074 	for (const auto& itt : result)
13075 	{
13076 		std::vector<std::string> sd = itt;
13077 
13078 		int cmd = atoi(sd[1].c_str());
13079 		int level = atoi(sd[2].c_str());
13080 		_tColor color(sd[3]);
13081 		int ondelay = atoi(sd[4].c_str());
13082 		int offdelay = atoi(sd[5].c_str());
13083 		std::vector<std::vector<std::string> > result2;
13084 		result2 = m_sql.safe_query(
13085 			"SELECT HardwareID, DeviceID,Unit,Type,SubType,SwitchType, nValue, sValue, Name FROM DeviceStatus WHERE (ID == '%q')", sd[0].c_str());
13086 		if (!result2.empty())
13087 		{
13088 			std::vector<std::string> sd2 = result2[0];
13089 			//uint8_t rnValue = atoi(sd2[6].c_str());
13090 			std::string sValue = sd2[7];
13091 			//uint8_t Unit = atoi(sd2[2].c_str());
13092 			uint8_t dType = atoi(sd2[3].c_str());
13093 			uint8_t dSubType = atoi(sd2[4].c_str());
13094 			std::string DeviceName = sd2[8];
13095 			_eSwitchType switchtype = (_eSwitchType)atoi(sd2[5].c_str());
13096 
13097 			//Check if this device will not activate a scene
13098 			uint64_t dID = std::strtoull(sd[0].c_str(), nullptr, 10);
13099 			if (DoesDeviceActiveAScene(dID, cmd))
13100 			{
13101 				_log.Log(LOG_ERROR, "Skipping sensor '%s' because this triggers another scene!", DeviceName.c_str());
13102 				continue;
13103 			}
13104 
13105 			std::string lstatus = switchcmd;
13106 			int llevel = 0;
13107 			bool bHaveDimmer = false;
13108 			bool bHaveGroupCmd = false;
13109 			int maxDimLevel = 0;
13110 
13111 			GetLightStatus(dType, dSubType, switchtype, cmd, sValue, lstatus, llevel, bHaveDimmer, maxDimLevel, bHaveGroupCmd);
13112 
13113 			if (scenetype == SGTYPE_GROUP)
13114 			{
13115 				lstatus = ((switchcmd == "On") || (switchcmd == "Group On") || (switchcmd == "Chime") || (switchcmd == "All On")) ? "On" : "Off";
13116 			}
13117 			_log.Log(LOG_NORM, "Activating Scene/Group Device: %s (%s)", DeviceName.c_str(), lstatus.c_str());
13118 
13119 
13120 			int ilevel = maxDimLevel - 1; // Why -1?
13121 
13122 			if (
13123 				((switchtype == STYPE_Dimmer) ||
13124 				(switchtype == STYPE_BlindsPercentage) ||
13125 					(switchtype == STYPE_BlindsPercentageInverted) ||
13126 					(switchtype == STYPE_Selector)
13127 					) && (maxDimLevel != 0))
13128 			{
13129 				if (lstatus == "On")
13130 				{
13131 					lstatus = "Set Level";
13132 					float fLevel = (maxDimLevel / 100.0f) * level;
13133 					if (fLevel > 100)
13134 						fLevel = 100;
13135 					ilevel = round(fLevel);
13136 				}
13137 				if (switchtype == STYPE_Selector) {
13138 					if (lstatus != "Set Level") {
13139 						ilevel = 0;
13140 					}
13141 					ilevel = round(ilevel / 10.0f) * 10; // select only multiples of 10
13142 					if (ilevel == 0) {
13143 						lstatus = "Off";
13144 					}
13145 				}
13146 			}
13147 
13148 			int idx = atoi(sd[0].c_str());
13149 			if (switchtype != STYPE_PushOn)
13150 			{
13151 				int delay = (lstatus == "Off") ? offdelay : ondelay;
13152 				if (m_sql.m_bEnableEventSystem && !bEventTrigger)
13153 					m_eventsystem.SetEventTrigger(idx, m_eventsystem.REASON_DEVICE, static_cast<float>(delay));
13154 				SwitchLight(idx, lstatus, ilevel, color, false, delay, User);
13155 				if (scenetype == SGTYPE_SCENE)
13156 				{
13157 					if ((lstatus != "Off") && (offdelay > 0))
13158 					{
13159 						//switch with on delay, and off delay
13160 						if (m_sql.m_bEnableEventSystem && !bEventTrigger)
13161 							m_eventsystem.SetEventTrigger(idx, m_eventsystem.REASON_DEVICE, static_cast<float>(ondelay + offdelay));
13162 						SwitchLight(idx, "Off", ilevel, color, false, ondelay + offdelay, User);
13163 					}
13164 				}
13165 			}
13166 			else
13167 			{
13168 				if (m_sql.m_bEnableEventSystem && !bEventTrigger)
13169 					m_eventsystem.SetEventTrigger(idx, m_eventsystem.REASON_DEVICE, static_cast<float>(ondelay));
13170 				SwitchLight(idx, "On", ilevel, color, false, ondelay, User);
13171 			}
13172 			sleep_milliseconds(50);
13173 		}
13174 	}
13175 	return true;
13176 }
13177 
CheckSceneCode(const uint64_t DevRowIdx,const uint8_t dType,const uint8_t dSubType,const int nValue,const char * sValue,const std::string & User)13178 void MainWorker::CheckSceneCode(const uint64_t DevRowIdx, const uint8_t dType, const uint8_t dSubType, const int nValue, const char* sValue, const std::string& User)
13179 {
13180 	//check for scene code
13181 	std::vector<std::vector<std::string> > result;
13182 
13183 	result = m_sql.safe_query("SELECT ID, Activators, SceneType FROM Scenes WHERE (Activators!='')");
13184 	if (!result.empty())
13185 	{
13186 		for (const auto& itt : result)
13187 		{
13188 			std::vector<std::string> sd = itt;
13189 
13190 			std::vector<std::string> arrayActivators;
13191 			StringSplit(sd[1], ";", arrayActivators);
13192 			for (const auto& ittAct : arrayActivators)
13193 			{
13194 				std::string sCodeCmd = ittAct;
13195 
13196 				std::vector<std::string> arrayCode;
13197 				StringSplit(sCodeCmd, ":", arrayCode);
13198 
13199 				std::string sID = arrayCode[0];
13200 				std::string sCode = "";
13201 				if (arrayCode.size() == 2)
13202 				{
13203 					sCode = arrayCode[1];
13204 				}
13205 
13206 				uint64_t aID = std::strtoull(sID.c_str(), nullptr, 10);
13207 				if (aID == DevRowIdx)
13208 				{
13209 					uint64_t ID = std::strtoull(sd[0].c_str(), nullptr, 10);
13210 					int scenetype = atoi(sd[2].c_str());
13211 					int rnValue = nValue;
13212 
13213 					if ((scenetype == SGTYPE_SCENE) && (!sCode.empty()))
13214 					{
13215 						//Also check code
13216 						int iCode = atoi(sCode.c_str());
13217 						if (iCode != nValue)
13218 							continue;
13219 						rnValue = 1; //A Scene can only be activated (On)
13220 					}
13221 
13222 					std::string lstatus = "";
13223 					int llevel = 0;
13224 					bool bHaveDimmer = false;
13225 					bool bHaveGroupCmd = false;
13226 					int maxDimLevel = 0;
13227 
13228 					GetLightStatus(dType, dSubType, STYPE_OnOff, rnValue, sValue, lstatus, llevel, bHaveDimmer, maxDimLevel, bHaveGroupCmd);
13229 					std::string switchcmd = (IsLightSwitchOn(lstatus) == true) ? "On" : "Off";
13230 
13231 					m_sql.AddTaskItem(_tTaskItem::SwitchSceneEvent(0.2f, ID, switchcmd, "SceneTrigger", User));
13232 				}
13233 			}
13234 		}
13235 	}
13236 }
13237 
LoadSharedUsers()13238 void MainWorker::LoadSharedUsers()
13239 {
13240 	std::vector<tcp::server::_tRemoteShareUser> users;
13241 
13242 	std::vector<std::vector<std::string> > result;
13243 	std::vector<std::vector<std::string> > result2;
13244 
13245 	result = m_sql.safe_query("SELECT ID, Username, Password FROM USERS WHERE ((RemoteSharing==1) AND (Active==1))");
13246 	if (!result.empty())
13247 	{
13248 		for (const auto& itt : result)
13249 		{
13250 			std::vector<std::string> sd = itt;
13251 			tcp::server::_tRemoteShareUser suser;
13252 			suser.Username = base64_decode(sd[1]);
13253 			suser.Password = sd[2];
13254 
13255 			//Get User Devices
13256 			result2 = m_sql.safe_query("SELECT DeviceRowID FROM SharedDevices WHERE (SharedUserID == '%q')", sd[0].c_str());
13257 			if (!result2.empty())
13258 			{
13259 				for (const auto& itt2 : result2)
13260 				{
13261 					std::vector<std::string> sd2 = itt2;
13262 					uint64_t ID = std::strtoull(sd2[0].c_str(), nullptr, 10);
13263 					suser.Devices.push_back(ID);
13264 				}
13265 			}
13266 			users.push_back(suser);
13267 		}
13268 	}
13269 	m_sharedserver.SetRemoteUsers(users);
13270 	m_sharedserver.stopAllClients();
13271 }
13272 
SetInternalSecStatus()13273 void MainWorker::SetInternalSecStatus()
13274 {
13275 	m_eventsystem.WWWUpdateSecurityState(m_SecStatus);
13276 
13277 	//Update Domoticz Security Device
13278 	RBUF tsen;
13279 	memset(&tsen, 0, sizeof(RBUF));
13280 	tsen.SECURITY1.packetlength = sizeof(tsen.TEMP) - 1;
13281 	tsen.SECURITY1.packettype = pTypeSecurity1;
13282 	tsen.SECURITY1.subtype = sTypeDomoticzSecurity;
13283 	tsen.SECURITY1.battery_level = 9;
13284 	tsen.SECURITY1.rssi = 12;
13285 	tsen.SECURITY1.id1 = 0x14;
13286 	tsen.SECURITY1.id2 = 0x87;
13287 	tsen.SECURITY1.id3 = 0x02;
13288 	tsen.SECURITY1.seqnbr = 1;
13289 	if (m_SecStatus == SECSTATUS_DISARMED)
13290 		tsen.SECURITY1.status = sStatusNormal;
13291 	else if (m_SecStatus == SECSTATUS_ARMEDHOME)
13292 		tsen.SECURITY1.status = sStatusArmHome;
13293 	else
13294 		tsen.SECURITY1.status = sStatusArmAway;
13295 
13296 	if (_log.IsDebugLevelEnabled(DEBUG_RECEIVED))
13297 	{
13298 		_log.Log(LOG_NORM, "(System) Domoticz Security Status");
13299 	}
13300 
13301 	CDomoticzHardwareBase* pHardware = GetHardwareByType(HTYPE_DomoticzInternal);
13302 	PushAndWaitRxMessage(pHardware, (const uint8_t*)&tsen, "Domoticz Security Panel", -1);
13303 }
13304 
UpdateDomoticzSecurityStatus(const int iSecStatus)13305 void MainWorker::UpdateDomoticzSecurityStatus(const int iSecStatus)
13306 {
13307 	m_SecCountdown = -1; //cancel possible previous delay
13308 	m_SecStatus = iSecStatus;
13309 
13310 	m_sql.UpdatePreferencesVar("SecStatus", iSecStatus);
13311 
13312 	int nValue = 0;
13313 	m_sql.GetPreferencesVar("SecOnDelay", nValue);
13314 
13315 	if ((nValue == 0) || (iSecStatus == SECSTATUS_DISARMED))
13316 	{
13317 		//Do it Directly
13318 		SetInternalSecStatus();
13319 	}
13320 	else
13321 	{
13322 		//Schedule It
13323 		m_SecCountdown = nValue;
13324 	}
13325 }
13326 
ForceLogNotificationCheck()13327 void MainWorker::ForceLogNotificationCheck()
13328 {
13329 	m_bForceLogNotificationCheck = true;
13330 }
13331 
HandleLogNotifications()13332 void MainWorker::HandleLogNotifications()
13333 {
13334 	std::list<CLogger::_tLogLineStruct> _loglines = _log.GetNotificationLogs();
13335 	if (_loglines.empty())
13336 		return;
13337 	//Assemble notification message
13338 
13339 	std::stringstream sstr;
13340 	std::list<CLogger::_tLogLineStruct>::const_iterator itt;
13341 	std::string sTopic;
13342 
13343 	if (_loglines.size() > 1)
13344 	{
13345 		sTopic = "Domoticz: Multiple errors received in the last 5 minutes";
13346 		sstr << "Multiple errors received in the last 5 minutes:<br><br>";
13347 	}
13348 	else
13349 	{
13350 		itt = _loglines.begin();
13351 		sTopic = "Domoticz: " + itt->logmessage;
13352 	}
13353 
13354 	for (itt = _loglines.begin(); itt != _loglines.end(); ++itt)
13355 	{
13356 		sstr << itt->logmessage << "<br>";
13357 	}
13358 	m_sql.AddTaskItem(_tTaskItem::SendEmail(1, sTopic, sstr.str()));
13359 }
13360 
HeartbeatUpdate(const std::string & component,bool critical)13361 void MainWorker::HeartbeatUpdate(const std::string& component, bool critical /*= true*/)
13362 {
13363 	std::lock_guard<std::mutex> l(m_heartbeatmutex);
13364 	time_t now = time(0);
13365 	auto itt = m_componentheartbeats.find(component);
13366 	if (itt != m_componentheartbeats.end()) {
13367 		itt->second.first = now;
13368 	}
13369 	else {
13370 		m_componentheartbeats[component] = std::make_pair(now, critical);
13371 	}
13372 }
13373 
HeartbeatRemove(const std::string & component)13374 void MainWorker::HeartbeatRemove(const std::string& component)
13375 {
13376 	std::lock_guard<std::mutex> l(m_heartbeatmutex);
13377 	auto itt = m_componentheartbeats.find(component);
13378 	if (itt != m_componentheartbeats.end()) {
13379 		m_componentheartbeats.erase(itt);
13380 	}
13381 }
13382 
HeartbeatCheck()13383 void MainWorker::HeartbeatCheck()
13384 {
13385 	std::lock_guard<std::mutex> l(m_heartbeatmutex);
13386 	std::lock_guard<std::mutex> l2(m_devicemutex);
13387 
13388 	m_devicestorestart.clear();
13389 
13390 	time_t now;
13391 	mytime(&now);
13392 
13393 	for (const auto& itt : m_componentheartbeats)
13394 	{
13395 		double diff = difftime(now, itt.second.first);
13396 		if (diff > 60)
13397 		{
13398 			_log.Log(LOG_ERROR, "%s thread seems to have ended unexpectedly (last update %f seconds ago)", itt.first.c_str(), diff);
13399 			/* GizMoCuz: This causes long operations to crash (Like Issue #3011)
13400 						if (itt.second.second) // If the stalled component is marked as critical, call abort / raise signal
13401 						{
13402 							if (!IsDebuggerPresent())
13403 							{
13404 			#ifdef WIN32
13405 								abort();
13406 			#else
13407 								raise(SIGUSR1);
13408 			#endif
13409 							}
13410 						}
13411 			*/
13412 		}
13413 	}
13414 
13415 	//Check hardware heartbeats
13416 	std::vector<CDomoticzHardwareBase*>::const_iterator itt;
13417 	for (itt = m_hardwaredevices.begin(); itt != m_hardwaredevices.end(); ++itt)
13418 	{
13419 		CDomoticzHardwareBase* pHardware = (CDomoticzHardwareBase*)(*itt);
13420 		if (!pHardware->m_bSkipReceiveCheck)
13421 		{
13422 			//Skip Dummy Hardware
13423 			bool bDoCheck = (pHardware->HwdType != HTYPE_Dummy) && (pHardware->HwdType != HTYPE_EVOHOME_SCRIPT);
13424 			if (bDoCheck)
13425 			{
13426 				//Check Thread Timeout
13427 				double diff = difftime(now, pHardware->m_LastHeartbeat);
13428 				//_log.Log(LOG_STATUS, "%d last checking  %.2lf seconds ago", iterator->first, dif);
13429 				if (diff > 60)
13430 				{
13431 					std::vector<std::vector<std::string> > result;
13432 					result = m_sql.safe_query("SELECT Name FROM Hardware WHERE (ID='%d')", pHardware->m_HwdID);
13433 					if (result.size() == 1)
13434 					{
13435 						std::vector<std::string> sd = result[0];
13436 						_log.Log(LOG_ERROR, "%s hardware (%d) thread seems to have ended unexpectedly", sd[0].c_str(), pHardware->m_HwdID);
13437 					}
13438 				}
13439 			}
13440 
13441 			if (pHardware->m_DataTimeout > 0)
13442 			{
13443 				//Check Receive Timeout
13444 				double diff = difftime(now, pHardware->m_LastHeartbeatReceive);
13445 				bool bHaveDataTimeout = (diff > pHardware->m_DataTimeout);
13446 				if (!bHaveDataTimeout)
13447 				{
13448 					if (
13449 						(pHardware->HwdType == HTYPE_RFXLAN)
13450 						|| (pHardware->HwdType == HTYPE_RFXtrx315)
13451 						|| (pHardware->HwdType == HTYPE_RFXtrx433)
13452 						|| (pHardware->HwdType == HTYPE_RFXtrx868)
13453 						)
13454 					{
13455 						const CRFXBase* pRFXBase = static_cast<CRFXBase*>(pHardware);
13456 						if (pRFXBase->m_LastP1Received != 0)
13457 						{
13458 							diff = difftime(now, pRFXBase->m_LastP1Received);
13459 							bHaveDataTimeout = (diff > pHardware->m_DataTimeout);
13460 						}
13461 					}
13462 				}
13463 				if (bHaveDataTimeout)
13464 				{
13465 					std::string sDataTimeout = "";
13466 					int totNum = 0;
13467 					if (pHardware->m_DataTimeout < 60) {
13468 						totNum = pHardware->m_DataTimeout;
13469 						sDataTimeout = "Seconds";
13470 					}
13471 					else if (pHardware->m_DataTimeout < 3600) {
13472 						totNum = pHardware->m_DataTimeout / 60;
13473 						if (totNum == 1) {
13474 							sDataTimeout = "Minute";
13475 						}
13476 						else {
13477 							sDataTimeout = "Minutes";
13478 						}
13479 					}
13480 					else if (pHardware->m_DataTimeout < 86400) {
13481 						totNum = pHardware->m_DataTimeout / 3600;
13482 						if (totNum == 1) {
13483 							sDataTimeout = "Hour";
13484 						}
13485 						else {
13486 							sDataTimeout = "Hours";
13487 						}
13488 					}
13489 					else {
13490 						totNum = pHardware->m_DataTimeout / 60;
13491 						if (totNum == 1) {
13492 							sDataTimeout = "Day";
13493 						}
13494 						else {
13495 							sDataTimeout = "Days";
13496 						}
13497 					}
13498 
13499 					_log.Log(LOG_ERROR, "%s hardware (%d) nothing received for more than %d %s!....", pHardware->m_Name.c_str(), pHardware->m_HwdID, totNum, sDataTimeout.c_str());
13500 					m_devicestorestart.push_back(pHardware->m_HwdID);
13501 				}
13502 			}
13503 
13504 		}
13505 	}
13506 }
13507 
UpdateDevice(const int DevIdx,int nValue,std::string & sValue,const int signallevel,const int batterylevel,const bool parseTrigger)13508 bool MainWorker::UpdateDevice(const int DevIdx, int nValue, std::string& sValue, const int signallevel, const int batterylevel, const bool parseTrigger)
13509 {
13510 	// Get the raw device parameters
13511 	std::vector<std::vector<std::string> > result;
13512 	result = m_sql.safe_query("SELECT HardwareID, DeviceID, Unit, Type, SubType FROM DeviceStatus WHERE (ID==%d)", DevIdx);
13513 	if (result.empty())
13514 		return false;
13515 
13516 	int HardwareID = std::stoi(result[0][0]);
13517 	std::string DeviceID = result[0][1];
13518 	int unit = std::stoi(result[0][2]);
13519 	int devType = std::stoi(result[0][3]);
13520 	int subType = std::stoi(result[0][4]);
13521 
13522 	return UpdateDevice(HardwareID, DeviceID, unit, devType, subType, nValue, sValue, signallevel, batterylevel, parseTrigger);
13523 }
13524 
UpdateDevice(const int HardwareID,const std::string & DeviceID,const int unit,const int devType,const int subType,int nValue,std::string & sValue,const int signallevel,const int batterylevel,const bool parseTrigger)13525 bool MainWorker::UpdateDevice(const int HardwareID, const std::string& DeviceID, const int unit, const int devType, const int subType, int nValue, std::string& sValue, const int signallevel, const int batterylevel, const bool parseTrigger)
13526 {
13527 	CDomoticzHardwareBase* pHardware = GetHardware(HardwareID);
13528 
13529 	// Prevent hazardous modification of DB from JSON calls
13530 	if (!m_sql.DoesDeviceExist(HardwareID, DeviceID.c_str(), unit, devType, subType))
13531 		return false;
13532 
13533 	g_bUseEventTrigger = parseTrigger;
13534 
13535 	unsigned long ID = 0;
13536 	std::stringstream s_strid;
13537 	s_strid << std::hex << DeviceID;
13538 	s_strid >> ID;
13539 
13540 	float temp = 12345.0f;
13541 
13542 	if (pHardware)
13543 	{
13544 		if (devType == pTypeLighting2)
13545 		{
13546 			//Update as Lighting 2
13547 			unsigned long ID;
13548 			std::stringstream s_strid;
13549 			s_strid << std::hex << DeviceID;
13550 			s_strid >> ID;
13551 			uint8_t ID1 = (uint8_t)((ID & 0xFF000000) >> 24);
13552 			uint8_t ID2 = (uint8_t)((ID & 0x00FF0000) >> 16);
13553 			uint8_t ID3 = (uint8_t)((ID & 0x0000FF00) >> 8);
13554 			uint8_t ID4 = (uint8_t)((ID & 0x000000FF));
13555 
13556 			tRBUF lcmd;
13557 			memset(&lcmd, 0, sizeof(RBUF));
13558 			lcmd.LIGHTING2.packetlength = sizeof(lcmd.LIGHTING2) - 1;
13559 			lcmd.LIGHTING2.packettype = pTypeLighting2;
13560 			lcmd.LIGHTING2.subtype = subType;
13561 			lcmd.LIGHTING2.id1 = ID1;
13562 			lcmd.LIGHTING2.id2 = ID2;
13563 			lcmd.LIGHTING2.id3 = ID3;
13564 			lcmd.LIGHTING2.id4 = ID4;
13565 			lcmd.LIGHTING2.unitcode = (uint8_t)unit;
13566 			lcmd.LIGHTING2.cmnd = (uint8_t)nValue;
13567 			lcmd.LIGHTING2.level = (uint8_t)atoi(sValue.c_str());
13568 			lcmd.LIGHTING2.filler = 0;
13569 			lcmd.LIGHTING2.rssi = signallevel;
13570 			DecodeRXMessage(pHardware, (const uint8_t*)&lcmd.LIGHTING2, NULL, batterylevel);
13571 			g_bUseEventTrigger = true;
13572 			return true;
13573 		}
13574 
13575 		if (
13576 			(devType == pTypeTEMP)
13577 			|| (devType == pTypeTEMP_HUM)
13578 			|| (devType == pTypeTEMP_HUM_BARO)
13579 			|| (devType == pTypeBARO)
13580 			)
13581 		{
13582 			//Adjustment value
13583 			float AddjValue = 0.0f;
13584 			float AddjMulti = 1.0f;
13585 			m_sql.GetAddjustment(HardwareID, DeviceID.c_str(), unit, devType, subType, AddjValue, AddjMulti);
13586 
13587 			char szTmp[100];
13588 			std::vector<std::string> strarray;
13589 
13590 			if (devType == pTypeTEMP)
13591 			{
13592 				temp = static_cast<float>(atof(sValue.c_str()));
13593 				temp += AddjValue;
13594 				sprintf(szTmp, "%.2f", temp);
13595 				sValue = szTmp;
13596 			}
13597 			else if (devType == pTypeTEMP_HUM)
13598 			{
13599 				StringSplit(sValue, ";", strarray);
13600 				if (strarray.size() == 3)
13601 				{
13602 					temp = static_cast<float>(atof(strarray[0].c_str()));
13603 					temp += AddjValue;
13604 					sprintf(szTmp, "%.2f;%s;%s", temp, strarray[1].c_str(), strarray[2].c_str());
13605 					sValue = szTmp;
13606 				}
13607 			}
13608 			else if (devType == pTypeTEMP_HUM_BARO)
13609 			{
13610 				StringSplit(sValue, ";", strarray);
13611 				if (strarray.size() == 5)
13612 				{
13613 					temp = static_cast<float>(atof(strarray[0].c_str()));
13614 					float fbarometer = static_cast<float>(atof(strarray[3].c_str()));
13615 					temp += AddjValue;
13616 
13617 					AddjValue = 0.0f;
13618 					AddjMulti = 1.0f;
13619 					m_sql.GetAddjustment2(HardwareID, DeviceID.c_str(), unit, devType, subType, AddjValue, AddjMulti);
13620 					fbarometer += AddjValue;
13621 
13622 					if (subType == sTypeTHBFloat)
13623 					{
13624 						sprintf(szTmp, "%.2f;%s;%s;%.1f;%s", temp, strarray[1].c_str(), strarray[2].c_str(), fbarometer, strarray[4].c_str());
13625 					}
13626 					else
13627 					{
13628 						sprintf(szTmp, "%.2f;%s;%s;%d;%s", temp, strarray[1].c_str(), strarray[2].c_str(), (int)rint(fbarometer), strarray[4].c_str());
13629 					}
13630 					sValue = szTmp;
13631 				}
13632 			}
13633 		}
13634 	}
13635 
13636 	std::string devname = "Unknown";
13637 	const uint64_t devidx = m_sql.UpdateValue(
13638 		HardwareID,
13639 		DeviceID.c_str(),
13640 		(const uint8_t)unit,
13641 		(const uint8_t)devType,
13642 		(const uint8_t)subType,
13643 		signallevel,//signal level,
13644 		batterylevel,//battery level
13645 		nValue,
13646 		sValue.c_str(),
13647 		devname,
13648 		false
13649 	);
13650 	if (devidx == (uint64_t)-1)
13651 	{
13652 		g_bUseEventTrigger = true;
13653 		return false;
13654 	}
13655 
13656 	if (pHardware)
13657 	{
13658 		if (
13659 			(pHardware->HwdType == HTYPE_MySensorsUSB) ||
13660 			(pHardware->HwdType == HTYPE_MySensorsTCP) ||
13661 			(pHardware->HwdType == HTYPE_MySensorsMQTT)
13662 			)
13663 		{
13664 			unsigned long ID;
13665 			std::stringstream s_strid;
13666 			s_strid << std::hex << DeviceID;
13667 			s_strid >> ID;
13668 			uint8_t NodeID = (uint8_t)((ID & 0x0000FF00) >> 8);
13669 			uint8_t ChildID = (uint8_t)((ID & 0x000000FF));
13670 
13671 			MySensorsBase* pMySensorDevice = reinterpret_cast<MySensorsBase*>(pHardware);
13672 			pMySensorDevice->SendTextSensorValue(NodeID, ChildID, sValue);
13673 		}
13674 	}
13675 
13676 	//Calculate temperature trend
13677 	if (temp != 12345.0f)
13678 	{
13679 		uint64_t tID = ((uint64_t)(HardwareID & 0x7FFFFFFF) << 32) | (devidx & 0x7FFFFFFF);
13680 		m_trend_calculator[tID].AddValueAndReturnTendency(static_cast<double>(temp), _tTrendCalculator::TAVERAGE_TEMP);
13681 	}
13682 
13683 #ifdef ENABLE_PYTHON
13684 	// notify plugin
13685 	m_pluginsystem.DeviceModified(devidx);
13686 #endif
13687 
13688 	// signal connected devices (MQTT, fibaro, http push ... ) about the update
13689 	sOnDeviceReceived(HardwareID, devidx, devname, nullptr);
13690 
13691 	std::stringstream sidx;
13692 	sidx << devidx;
13693 
13694 	if (
13695 		((devType == pTypeThermostat) && (subType == sTypeThermSetpoint)) ||
13696 		((devType == pTypeRadiator1) && (subType == sTypeSmartwares))
13697 		)
13698 	{
13699 		_log.Log(LOG_NORM, "Sending SetPoint to device....");
13700 		SetSetPoint(sidx.str(), static_cast<float>(atof(sValue.c_str())));
13701 	}
13702 	else if ((devType == pTypeGeneral) && (subType == sTypeZWaveThermostatMode))
13703 	{
13704 		_log.Log(LOG_NORM, "Sending Thermostat Mode to device....");
13705 		SetZWaveThermostatMode(sidx.str(), nValue);
13706 	}
13707 	else if ((devType == pTypeGeneral) && (subType == sTypeZWaveThermostatFanMode))
13708 	{
13709 		_log.Log(LOG_NORM, "Sending Thermostat Fan Mode to device....");
13710 		SetZWaveThermostatFanMode(sidx.str(), nValue);
13711 	}
13712 	else if (pHardware) {
13713 		//Handle Notification
13714 		m_notifications.CheckAndHandleNotification(devidx, HardwareID, DeviceID, devname, unit, devType, subType, nValue, sValue);
13715 	}
13716 
13717 	g_bUseEventTrigger = true;
13718 
13719 	return true;
13720 }
13721