1 #include "stdafx.h"
2 #include "mainworker.h"
3 #include "SQLHelper.h"
4 #include "localtime_r.h"
5 #include "../hardware/hardwaretypes.h"
6 #include "../main/Logger.h"
7 #include "../main/WebServerHelper.h"
8 #include "../main/LuaTable.h"
9 #include "../main/json_helper.h"
10 #include "dzVents.h"
11 #define __STDC_FORMAT_MACROS
12 #include <inttypes.h>
13 #include "../webserver/Base64.h"
14 
15 extern "C" {
16 #include <lua.h>
17 #include <lualib.h>
18 #include <lauxlib.h>
19 }
20 
21 extern std::string szUserDataFolder, szWebRoot, szStartupFolder, szAppVersion;
22 extern http::server::CWebServerHelper m_webservers;
23 
24 CdzVents CdzVents::m_dzvents;
25 
CdzVents(void)26 CdzVents::CdzVents(void) :
27 	m_version("3.0.2")
28 {
29 	m_bdzVentsExist = false;
30 }
31 
~CdzVents(void)32 CdzVents::~CdzVents(void)
33 {
34 }
35 
GetVersion()36 const std::string CdzVents::GetVersion()
37 {
38 	return m_version;
39 }
40 
EvaluateDzVents(lua_State * lua_state,const std::vector<CEventSystem::_tEventQueue> & items,const int secStatus)41 void CdzVents::EvaluateDzVents(lua_State *lua_state, const std::vector<CEventSystem::_tEventQueue> &items, const int secStatus)
42 {
43 	// reroute print library to Domoticz logger
44 	luaL_openlibs(lua_state);
45 	lua_pushcfunction(lua_state, l_domoticz_print);
46 	lua_setglobal(lua_state, "print");
47 
48 	bool reasonTime = false;
49 	bool reasonURL = false;
50 	bool reasonSecurity = false;
51 	bool reasonNotification = false;
52 	std::vector<CEventSystem::_tEventQueue>::const_iterator itt;
53 	for (itt = items.begin(); itt != items.end(); ++itt)
54 	{
55 		if (itt->reason == m_mainworker.m_eventsystem.REASON_URL)
56 			reasonURL = true;
57 
58 		if (itt->reason == m_mainworker.m_eventsystem.REASON_SECURITY)
59 			reasonSecurity = true;
60 
61 		if (itt->reason == m_mainworker.m_eventsystem.REASON_TIME)
62 			reasonTime = true;
63 
64 		if (itt->reason == m_mainworker.m_eventsystem.REASON_NOTIFICATION)
65 		{
66 			// _log.Log(LOG_STATUS, "Notification raised: %d(%s)", itt->nValue, m_mainworker.m_notificationsystem.GetTypeString(itt->nValue).c_str());
67 			reasonNotification = true;
68 		}
69 	}
70 	ExportDomoticzDataToLua(lua_state, items);
71 	SetGlobalVariables(lua_state, reasonTime, secStatus);
72 
73 	if (reasonURL)
74 		ProcessHttpResponse(lua_state, items);
75 
76 	if (reasonSecurity)
77 		ProcessSecurity(lua_state, items);
78 
79 	if (reasonNotification)
80 		ProcessNotification(lua_state, items);
81 }
82 
ProcessNotificationItem(CLuaTable & luaTable,int & index,const CEventSystem::_tEventQueue & item)83 void CdzVents::ProcessNotificationItem(CLuaTable &luaTable, int &index, const CEventSystem::_tEventQueue& item)
84 {
85 	std::string type, status;
86 
87 	luaTable.OpenSubTableEntry(index, 1, 4);
88 	type = m_mainworker.m_notificationsystem.GetTypeString(item.nValue);
89 	status = m_mainworker.m_notificationsystem.GetStatusString(item.lastLevel);
90 
91 	if (item.sValue.empty())
92 	{
93 		luaTable.AddString("message", "");
94 	}
95 	else
96 	{
97 		luaTable.AddString("message", "");
98 		luaTable.OpenSubTableEntry("data", 0, 0);
99 		if (item.nValue >= Notification::HW_TIMEOUT && item.nValue <= Notification::HW_THREAD_ENDED)
100 		{
101 			Json::Value eventdata;
102 			if (ParseJSon(item.sValue, eventdata))
103 			{
104 				luaTable.AddInteger("id", eventdata["m_HwdID"].asInt());
105 				luaTable.AddString("name", eventdata["m_Name"].asString());
106 			}
107 		}
108 		else if (item.nValue == Notification::DZ_BACKUP_DONE)
109 		{
110 			Json::Value eventdata;
111 			if(ParseJSon(item.sValue, eventdata))
112 			{
113 				type = type + eventdata["type"].asString();
114 				luaTable.AddNumber("duration", eventdata["duration"].asFloat());
115 				luaTable.AddString("location", eventdata["location"].asString());
116 			}
117 		}
118 		else if (item.nValue == Notification::DZ_CUSTOM)
119 		{
120 			Json::Value eventdata;
121 			if (ParseJSon(item.sValue, eventdata))
122 			{
123 				luaTable.AddString("name", eventdata["name"].asString());
124 				luaTable.AddString("data", eventdata["data"].asString());
125 			}
126 		}
127 		luaTable.CloseSubTableEntry();
128 	}
129 	luaTable.AddString("type", type);
130 	luaTable.AddString("status", status);
131 	luaTable.CloseSubTableEntry();
132 	index++;
133 	//_log.Log(LOG_STATUS, "dzVents notification: type: %s, status: %s, message: %s", type.c_str(), status.c_str(), item.sValue.c_str());
134 }
135 
ProcessNotification(lua_State * lua_state,const std::vector<CEventSystem::_tEventQueue> & items)136 void CdzVents::ProcessNotification(lua_State* lua_state, const std::vector<CEventSystem::_tEventQueue>& items)
137 {
138 	int index = 1;
139 	bool bHardware = false;
140 	bool bCustomEvent = false;
141 	CLuaTable luaTable(lua_state, "notification");
142 
143 	luaTable.OpenSubTableEntry("domoticz", 0, 0);
144 	std::vector<CEventSystem::_tEventQueue>::const_iterator itt;
145 	for (itt = items.begin(); itt != items.end(); itt++)
146 	{
147 		if (itt->reason == m_mainworker.m_eventsystem.REASON_NOTIFICATION)
148 		{
149 			switch (itt->nValue)
150 			{
151 			case Notification::DZ_START:
152 			case Notification::DZ_STOP:
153 			case Notification::DZ_BACKUP_DONE:
154 			case Notification::DZ_NOTIFICATION:
155 				ProcessNotificationItem(luaTable, index, *itt);
156 				break;
157 			case Notification::DZ_CUSTOM:
158 				bCustomEvent = true;
159 				break;
160 			default:
161 				bHardware = true;
162 				break;
163 			}
164 		}
165 	}
166 	luaTable.CloseSubTableEntry();
167 
168 	luaTable.OpenSubTableEntry("hardware", 0, 0);
169 	if (bHardware)
170 	{
171 		for (itt = items.begin(); itt != items.end(); itt++)
172 		{
173 			switch (itt->nValue)
174 			{
175 			case Notification::HW_START:
176 			case Notification::HW_STOP:
177 			case Notification::HW_THREAD_ENDED:
178 			case Notification::HW_TIMEOUT:
179 				_log.Log(LOG_ERROR, "dzVents notification type: %s not yet supported", m_mainworker.m_notificationsystem.GetTypeString(itt->nValue).c_str());
180 				//ProcessNotificationItem(luaTable, index, *itt);
181 				break;
182 			default:
183 				break;
184 			}
185 		}
186 	}
187 	luaTable.CloseSubTableEntry();
188 
189 	luaTable.OpenSubTableEntry("customevent", 0, 0);
190 	if (bCustomEvent)
191 	{
192 		for (itt = items.begin(); itt != items.end(); itt++)
193 		{
194 			switch (itt->nValue)
195 			{
196 			case Notification::DZ_CUSTOM:
197 				ProcessNotificationItem(luaTable, index, *itt);
198 				break;
199 			default:
200 				break;
201 			}
202 		}
203 	}
204 	luaTable.CloseSubTableEntry();
205 
206 	luaTable.Publish();
207 }
208 
ProcessSecurity(lua_State * lua_state,const std::vector<CEventSystem::_tEventQueue> & items)209 void CdzVents::ProcessSecurity(lua_State *lua_state, const std::vector<CEventSystem::_tEventQueue> &items)
210 {
211 	int index = 1;
212 	int secstatus = 0;
213 	std::string secstatusw = "";
214 
215 	CLuaTable luaTable(lua_state, "securityupdates");
216 
217 	std::vector<CEventSystem::_tEventQueue>::const_iterator itt;
218 	for (itt = items.begin(); itt != items.end(); ++itt)
219 	{
220 		if (itt->reason == m_mainworker.m_eventsystem.REASON_SECURITY)
221 		{
222 			m_sql.GetPreferencesVar("SecStatus", secstatus);
223 			if (itt->nValue == 1)
224 				secstatusw = "Armed Home";
225 			else if (itt->nValue == 2)
226 				secstatusw = "Armed Away";
227 			else
228 				secstatusw = "Disarmed";
229 			luaTable.AddString(index, secstatusw);
230 			index++;
231 		}
232 	}
233 	luaTable.Publish();
234 }
235 
ProcessHttpResponse(lua_State * lua_state,const std::vector<CEventSystem::_tEventQueue> & items)236 void CdzVents::ProcessHttpResponse(lua_State* lua_state, const std::vector<CEventSystem::_tEventQueue>& items)
237 {
238 	int index = 1;
239 	int statusCode = 0;
240 
241 	std::string protocol;
242 	std::string statusText;
243 
244 	CLuaTable luaTable(lua_state, "httpresponse");
245 
246 	std::vector<CEventSystem::_tEventQueue>::const_iterator itt;
247 	for (itt = items.begin(); itt != items.end(); ++itt)
248 	{
249 		if (itt->reason == m_mainworker.m_eventsystem.REASON_URL)
250 		{
251 			luaTable.OpenSubTableEntry(index, 0, 0);
252 			luaTable.OpenSubTableEntry("headers", (int)itt->vData.size() + 2, 0); // status is split into 3 parts
253 			if (!itt->vData.empty())
254 			{
255 				std::vector<std::string>::const_iterator itt2;
256 				for (itt2 = itt->vData.begin(); itt2 != itt->vData.end(); ++itt2)
257 				{
258 					std::string header = (*itt2);
259 
260 					size_t pos = header.find(": ");
261 					if (pos != std::string::npos)
262 					{
263 						luaTable.AddString(header.substr(0, pos), header.substr(pos + 2));
264 					}
265 					else
266 					{
267 						if (header.find("HTTP/") == 0)
268 						{
269 							std::vector<std::string> results;
270 							StringSplit(header, " ", results);
271 							if (results.size() >= 2)
272 							{
273 								pos = header.find(results[0]);
274 								protocol = header.substr(0, pos + results[0].size());
275 								statusCode = atoi(results[1].c_str());
276 								if (results.size() >= 3)
277 								{
278 									statusText = header.substr(header.find(results[2]));
279 								}
280 								else
281 								{
282 									statusText = ((statusCode >= 200) && (statusCode <= 299)) ? "OK" : "No reason returned!";
283 								}
284 							}
285 						}
286 					}
287 				}
288 			}
289 			luaTable.CloseSubTableEntry(); // headers, why rawset was done here???
290 
291 			luaTable.AddString("protocol", protocol);
292 			luaTable.AddString("statusText", statusText);
293 			luaTable.AddInteger("statusCode", statusCode);
294 			luaTable.AddString("data", itt->sValue);
295 			luaTable.AddString("callback", itt->nValueWording);
296 			luaTable.CloseSubTableEntry(); // index entry
297 			index++;
298 		}
299 	}
300 	luaTable.Publish();
301 }
302 
OpenURL(lua_State * lua_state,const std::vector<_tLuaTableValues> & vLuaTable)303 bool CdzVents::OpenURL(lua_State *lua_state, const std::vector<_tLuaTableValues> &vLuaTable)
304 {
305 	float delayTime = 0;
306 	std::string URL, extraHeaders, method, postData, trigger;
307 
308 	std::vector<_tLuaTableValues>::const_iterator itt;
309 	for (itt = vLuaTable.begin(); itt != vLuaTable.end(); ++itt)
310 	{
311 		if (itt->isTable && itt->sValue == "headers" && itt != vLuaTable.end() - 1)
312 		{
313 			int tIndex = itt->tIndex;
314 			itt++;
315 			std::vector<_tLuaTableValues>::const_iterator itt2;
316 			for (itt2 = itt; itt2 != vLuaTable.end(); ++itt2)
317 			{
318 				if (itt2->tIndex != tIndex)
319 				{
320 					itt--;
321 					break;
322 				}
323 				extraHeaders += "!#" + itt2->name + ": " + itt2->sValue;
324 				if (itt != vLuaTable.end() - 1)
325 					itt++;
326 			}
327 		}
328 		else if (itt->type == TYPE_STRING)
329 		{
330 			if (itt->name == "URL")
331 				URL = itt->sValue;
332 			else if (itt->name == "method")
333 				method = itt->sValue;
334 			else if (itt->name == "postdata")
335 				postData = itt->sValue;
336 			else if (itt->name == "_trigger")
337 				trigger = itt->sValue;
338 		}
339 		else if (itt->type == TYPE_INTEGER)
340 		{
341 			if (itt->name == "_random")
342 				delayTime = static_cast<float>(GenerateRandomNumber(itt->iValue));
343 			else if (itt->name == "_after")
344 				delayTime = static_cast<float>(itt->iValue);
345 		}
346 	}
347 
348 	if (URL.empty())
349 	{
350 		_log.Log(LOG_ERROR, "dzVents: No URL supplied!");
351 		return false;
352 	}
353 
354 	// Handle situation where WebLocalNetworks is not open without password for dzVents
355 	if (URL.find("127.0.0.1") != std::string::npos)
356 	{
357 		std::string allowedNetworks;
358 		int rnvalue = 0;
359 		m_sql.GetPreferencesVar("WebLocalNetworks",rnvalue, allowedNetworks);
360 		if (allowedNetworks.find("127.0.0.") == std::string::npos)
361 		{
362 			_log.Log(LOG_ERROR, "dzVents: local netWork not open for dzVents openURL call !");
363 			_log.Log(LOG_ERROR, "dzVents: check dzVents wiki (look for 'Using dzVents with Domoticz')");
364 			return false;
365 		}
366 	}
367 
368 	HTTPClient::_eHTTPmethod eMethod = HTTPClient::HTTP_METHOD_GET; // defaults to GET
369 	if (!method.empty())
370 	{
371 		if (method == "GET")
372 			eMethod = HTTPClient::HTTP_METHOD_GET;
373 		else if (method == "POST")
374 			eMethod = HTTPClient::HTTP_METHOD_POST;
375 		else if (method == "PUT")
376 			eMethod = HTTPClient::HTTP_METHOD_PUT;
377 		else if (method == "DELETE")
378 			eMethod = HTTPClient::HTTP_METHOD_DELETE;
379 		else
380 		{
381 			_log.Log(LOG_ERROR, "dzVents: Invalid HTTP method '%s'", method.c_str());
382 			return false;
383 		}
384 	}
385 
386 	if (!postData.empty() && eMethod == HTTPClient::HTTP_METHOD_GET)
387 	{
388 		_log.Log(LOG_ERROR, "dzVents: You cannot use postdata with method GET.");
389 		return false;
390 	}
391 
392 	m_sql.AddTaskItem(_tTaskItem::GetHTTPPage(delayTime, URL, extraHeaders, eMethod, postData, trigger));
393 	return true;
394 }
395 
TriggerCustomEvent(lua_State * lua_state,const std::vector<_tLuaTableValues> & vLuaTable)396 bool CdzVents::TriggerCustomEvent(lua_State* lua_state, const std::vector<_tLuaTableValues>& vLuaTable)
397 {
398 	float delayTime = 0;
399 	std::string name;
400 	std::string sValue;
401 
402 	std::vector<_tLuaTableValues>::const_iterator itt;
403 	for (itt = vLuaTable.begin(); itt != vLuaTable.end(); ++itt)
404 	{
405 		if ((itt->type == TYPE_STRING) && (itt->name == "name"))
406 		{
407 			name = itt->sValue;
408 		}
409 		else if ((itt->type == TYPE_STRING) && (itt->name == "data"))
410 		{
411 			sValue = itt->sValue;
412 		}
413 		else if (itt->type == TYPE_INTEGER)
414 		{
415 			if (itt->name == "_random")
416 				delayTime = static_cast<float>(GenerateRandomNumber(itt->iValue));
417 			else if (itt->name == "_after")
418 			{
419 				delayTime = static_cast<float>(itt->iValue);
420 				// _log.Log(LOG_STATUS, "dzVents: delayed custom event for %d seconds", itt->iValue);
421 			}
422 		}
423 	}
424 	if (name.empty())
425 		return false;
426 
427 	m_sql.AddTaskItem(_tTaskItem::CustomEvent(delayTime, name, sValue));
428 
429 	return true;
430 }
UpdateDevice(lua_State * lua_state,const std::vector<_tLuaTableValues> & vLuaTable)431 bool CdzVents::UpdateDevice(lua_State *lua_state, const std::vector<_tLuaTableValues> &vLuaTable)
432 {
433 	bool bEventTrigger = false;
434 	int nValue = -1, Protected = -1;
435 	int idx = -1;
436 	float delayTime = 0;
437 	std::string sValue;
438 
439 	std::vector<_tLuaTableValues>::const_iterator itt;
440 	for (itt = vLuaTable.begin(); itt != vLuaTable.end(); ++itt)
441 	{
442 		if (itt->type == TYPE_INTEGER)
443 		{
444 			if (itt->name == "idx")
445 				idx = itt->iValue;
446 			else if (itt->name == "nValue")
447 				nValue = itt->iValue;
448 			else if (itt->name == "protected")
449 				Protected = itt->iValue;
450 			else if (itt->name == "_random")
451 				delayTime = static_cast<float>(GenerateRandomNumber(itt->iValue));
452 			else if (itt->name == "_after")
453 				delayTime = static_cast<float>(itt->iValue);
454 		}
455 		else if (itt->type == TYPE_STRING && itt->name == "sValue")
456 			sValue = itt->sValue;
457 		else if (itt->type == TYPE_BOOLEAN && itt->name == "_trigger")
458 			bEventTrigger = true;
459 	}
460 	if (idx == -1)
461 		return false;
462 
463 	m_sql.AddTaskItem(_tTaskItem::UpdateDevice(delayTime, idx, nValue, sValue, Protected, bEventTrigger), false);
464 	return true;
465 }
466 
TriggerIFTTT(lua_State * lua_state,const std::vector<_tLuaTableValues> & vLuaTable)467 bool CdzVents::TriggerIFTTT(lua_State *lua_state, const std::vector<_tLuaTableValues> &vLuaTable)
468 {
469 	std::string sID, sValue1, sValue2, sValue3 ;
470 	float delayTime = 1;
471 	int rnvalue = 0;
472 
473 	m_sql.GetPreferencesVar("IFTTTEnabled", rnvalue);
474 	if (rnvalue == 0)
475 	{
476 		_log.Log(LOG_ERROR, "dzVents: IFTTT not enabled" );
477 		return false;
478 	}
479 
480 	std::vector<_tLuaTableValues>::const_iterator itt;
481 	for (itt = vLuaTable.begin(); itt != vLuaTable.end(); ++itt)
482 	{
483 		if (itt->type == TYPE_INTEGER )
484 		{
485 			if (itt->name == "_random")
486 				delayTime = static_cast<float>(GenerateRandomNumber(itt->iValue));
487 
488 			else if (itt->name == "_after")
489 				delayTime = static_cast<float>(itt->iValue);
490 
491 			else if (itt->name == "sID")
492 				 sID = std::to_string(itt->iValue);
493 
494 			else if (itt->name == "sValue1")
495 				sValue1 = std::to_string(itt->iValue);
496 
497 			else if (itt->name == "sValue2")
498 				sValue2 = std::to_string(itt->iValue);
499 
500 			else if (itt->name == "sValue3")
501 				sValue2 = std::to_string(itt->iValue);
502 		}
503 		else if (itt->type == TYPE_STRING)
504 		{
505 			if (itt->name == "sID")
506 				sID = itt->sValue;
507 
508 			else if (itt->name == "sValue1")
509 				sValue1 = itt->sValue;
510 
511 			else if (itt->name == "sValue2")
512 				sValue2 = itt->sValue;
513 
514 			else if (itt->name == "sValue3")
515 				sValue3 = itt->sValue;
516 		}
517 	}
518 
519 	m_sql.AddTaskItem(_tTaskItem::SendIFTTTTrigger(delayTime, sID, sValue1, sValue2, sValue3));
520 	return true;
521 }
522 
UpdateVariable(lua_State * lua_state,const std::vector<_tLuaTableValues> & vLuaTable)523 bool CdzVents::UpdateVariable(lua_State *lua_state, const std::vector<_tLuaTableValues> &vLuaTable)
524 {
525 	std::string variableValue;
526 	float delayTime = 0;
527 	bool bEventTrigger = false;
528 	int idx = 0;
529 
530 	std::vector<_tLuaTableValues>::const_iterator itt;
531 	for (itt = vLuaTable.begin(); itt != vLuaTable.end(); ++itt)
532 	{
533 		if (itt->type == TYPE_INTEGER)
534 		{
535 			if (itt->name == "idx")
536 				idx = itt->iValue;
537 			else if (itt->name == "_random")
538 				delayTime = static_cast<float>(GenerateRandomNumber(itt->iValue));
539 			else if (itt->name == "_after")
540 				delayTime = static_cast<float>(itt->iValue);
541 		}
542 		else if (itt->type == TYPE_STRING && itt->name == "value")
543 			variableValue = itt->sValue;
544 
545 		else if (itt->type == TYPE_BOOLEAN && itt->name == "_trigger")
546 			bEventTrigger = true;
547 	}
548 	if (idx == 0)
549 		return false;
550 
551 	if (bEventTrigger)
552 		m_mainworker.m_eventsystem.SetEventTrigger(idx, m_mainworker.m_eventsystem.REASON_USERVARIABLE, delayTime);
553 
554 	m_sql.AddTaskItem(_tTaskItem::SetVariable(delayTime, idx, variableValue, false));
555 	return true;
556 }
557 
CancelItem(lua_State * lua_state,const std::vector<_tLuaTableValues> & vLuaTable)558 bool CdzVents::CancelItem(lua_State *lua_state, const std::vector<_tLuaTableValues> &vLuaTable)
559 {
560 	int idx = 0;
561 	std::string type;
562 
563 	std::vector<_tLuaTableValues>::const_iterator itt;
564 	for (itt = vLuaTable.begin(); itt != vLuaTable.end(); ++itt)
565 	{
566 		if (itt->type == TYPE_INTEGER && itt->name == "idx")
567 			idx = itt->iValue;
568 
569 		else if (itt->type == TYPE_STRING && itt->name == "type")
570 			type = itt->sValue;
571 	}
572 
573 	if (idx == 0)
574 		return false;
575 
576 	_tTaskItem tItem;
577 	tItem._idx = idx;
578 	tItem._DelayTime = 0;
579 	if (type == "device")
580 	{
581 		tItem._ItemType = TITEM_SWITCHCMD_EVENT;
582 		m_sql.AddTaskItem(tItem, true);
583 		tItem._ItemType = TITEM_UPDATEDEVICE;
584 	}
585 	else if (type == "scene")
586 		tItem._ItemType = TITEM_SWITCHCMD_SCENE;
587 	else if (type == "variable")
588 		tItem._ItemType = TITEM_SET_VARIABLE;
589 
590 	m_sql.AddTaskItem(tItem, true);
591 	return true;
592 }
593 
processLuaCommand(lua_State * lua_state,const std::string & filename,const int tIndex)594 bool CdzVents::processLuaCommand(lua_State *lua_state, const std::string &filename, const int tIndex)
595 {
596 	bool scriptTrue = false;
597 	std::string lCommand = std::string(lua_tostring(lua_state, -2));
598 	std::vector<_tLuaTableValues> vLuaTable;
599 	IterateTable(lua_state, tIndex, vLuaTable);
600 	if (vLuaTable.size() > 0)
601 	{
602 		if (lCommand == "OpenURL")
603 			scriptTrue = OpenURL(lua_state, vLuaTable);
604 
605 		else if (lCommand == "UpdateDevice")
606 			scriptTrue = UpdateDevice(lua_state, vLuaTable);
607 
608 		else if (lCommand == "Variable")
609 			scriptTrue = UpdateVariable(lua_state, vLuaTable);
610 
611 		else if (lCommand == "Cancel")
612 			scriptTrue = CancelItem(lua_state, vLuaTable);
613 
614 		else if (lCommand == "TriggerIFTTT")
615 			scriptTrue = TriggerIFTTT(lua_state, vLuaTable);
616 
617 		else if (lCommand == "CustomEvent")
618 			scriptTrue = TriggerCustomEvent(lua_state, vLuaTable);
619 	}
620 	return scriptTrue;
621 }
622 
IterateTable(lua_State * lua_state,const int tIndex,std::vector<_tLuaTableValues> & vLuaTable)623 void CdzVents::IterateTable(lua_State *lua_state, const int tIndex, std::vector<_tLuaTableValues> &vLuaTable)
624 {
625 	_tLuaTableValues item;
626 	item.isTable = false;
627 
628 	lua_pushnil(lua_state);
629 	while (lua_next(lua_state, tIndex) != 0)
630 	{
631 		item.type = TYPE_UNKNOWN;
632 		item.isTable = false;
633 		item.tIndex = tIndex;
634 		if (lua_istable(lua_state, -1))
635 		{
636 			if (std::string(luaL_typename(lua_state, -2)) == "string")
637 			{
638 				item.type = TYPE_STRING;
639 				item.sValue = std::string(lua_tostring(lua_state, -2));
640 			}
641 			else if (std::string(luaL_typename(lua_state, -2)) == "number")
642 			{
643 				item.type = TYPE_INTEGER;
644 				item.iValue = static_cast<int>(lua_tonumber(lua_state, -2));
645 			}
646 			else
647 			{
648 				lua_pop(lua_state, 1);
649 				continue;
650 			}
651 			item.isTable = true;
652 			item.tIndex += 2;
653 			vLuaTable.push_back(item);
654 			IterateTable(lua_state, item.tIndex, vLuaTable);
655 		}
656 		else if (std::string(luaL_typename(lua_state, -1)) == "string")
657 		{
658 			item.type = TYPE_STRING;
659 			item.name = std::string(lua_tostring(lua_state, -2));
660 			item.sValue = std::string(lua_tostring(lua_state, -1));
661 		}
662 		else if (std::string(luaL_typename(lua_state, -1)) == "number")
663 		{
664 			item.type = TYPE_INTEGER;
665 			item.iValue = (int)lua_tointeger(lua_state, -1);
666 			item.name = std::string(lua_tostring(lua_state, -2));
667 		}
668 		else if (std::string(luaL_typename(lua_state, -1)) == "boolean")
669 		{
670 			item.type = TYPE_BOOLEAN;
671 			item.iValue = lua_toboolean(lua_state, -1);
672 			item.name = std::string(lua_tostring(lua_state, -2));
673 		}
674 		if (!item.isTable && item.type != TYPE_UNKNOWN)
675 			vLuaTable.push_back(item);
676 
677 		lua_pop(lua_state, 1);
678 	}
679 }
680 
l_domoticz_print(lua_State * lua_state)681 int CdzVents::l_domoticz_print(lua_State* lua_state)
682 {
683 	int nargs = lua_gettop(lua_state);
684 
685 	for (int i = 1; i <= nargs; i++)
686 	{
687 		if (lua_isstring(lua_state, i))
688 		{
689 			std::string lstring = lua_tostring(lua_state, i);
690 			if (lstring.find("Error: ") != std::string::npos)
691 			{
692 				_log.Log(LOG_ERROR, "dzVents: %s", lstring.c_str());
693 			}
694 			else
695 			{
696 				_log.Log(LOG_STATUS, "dzVents: %s", lstring.c_str());
697 			}
698 		}
699 		else
700 		{
701 			/* non strings? */
702 		}
703 	}
704 	return 0;
705 }
706 
SetGlobalVariables(lua_State * lua_state,const bool reasonTime,const int secStatus)707 void CdzVents::SetGlobalVariables(lua_State *lua_state, const bool reasonTime, const int secStatus)
708 {
709 	std::stringstream lua_DirT, runtime_DirT;
710 
711 	lua_DirT << szUserDataFolder <<
712 #ifdef WIN32
713 	"scripts\\dzVents\\";
714 #else
715 	"scripts/dzVents/";
716 #endif
717 
718 	runtime_DirT << szStartupFolder <<
719 #ifdef WIN32
720 	"dzVents\\runtime\\";
721 #else
722 	"dzVents/runtime/";
723 #endif
724 
725 	CLuaTable luaTable(lua_state, "globalvariables");
726 	luaTable.AddString("Security", m_mainworker.m_eventsystem.m_szSecStatus[secStatus]);
727 	luaTable.AddString("script_path", lua_DirT.str());
728 	luaTable.AddString("runtime_path", runtime_DirT.str());
729 	luaTable.AddBool("isTimeEvent", reasonTime);
730 
731 	char szTmp[10];
732 	sprintf(szTmp, "%.02f", 1.23f);
733 	luaTable.AddString("radix_separator", std::string(1,szTmp[1]));
734 
735 	sprintf(szTmp, "%.02f", 1234.56f);
736 	if (szTmp[1] == '2')
737 	{
738 		luaTable.AddString("group_separator", "");
739 	}
740 	else
741 	{
742 		luaTable.AddString("group_separator", std::string(1,szTmp[1]));
743 	}
744 
745 	int rnvalue = 0;
746 	m_sql.GetPreferencesVar("DzVentsLogLevel", rnvalue);
747 	luaTable.AddInteger("dzVents_log_level", rnvalue);
748 
749 	std::string sTitle;
750 	m_sql.GetPreferencesVar("Title", sTitle);
751 	luaTable.AddString("domoticz_title", sTitle);
752 
753 	// Only when webLocalNetworks has local network defined
754 	// Add latitude / longitude to table
755 	std::string allowedNetworks;
756 	rnvalue = 0;
757 	m_sql.GetPreferencesVar("WebLocalNetworks",rnvalue, allowedNetworks);
758 	if (allowedNetworks.find("127.0.0.") != std::string::npos)
759 	{
760 		std::string location;
761 		std::vector<std::string> strarray;
762 		if (m_sql.GetPreferencesVar("Location", rnvalue, location))
763 			StringSplit(location, ";", strarray);
764 		if (strarray.size() == 2)
765 		{
766 			// Only when location entered in the settings
767 			// Add to table
768 			luaTable.AddString("latitude", strarray[0]);
769 			luaTable.AddString("longitude", strarray[1]);
770 		}
771 	}
772 
773 	luaTable.AddString("domoticz_listening_port", m_webservers.our_listener_port);
774 	luaTable.AddString("domoticz_webroot", szWebRoot);
775 	luaTable.AddString("domoticz_start_time", m_mainworker.m_eventsystem.m_szStartTime);
776 	luaTable.AddString("currentTime", TimeToString(NULL, TF_DateTimeMs));
777 	luaTable.AddInteger("systemUptime", SystemUptime());
778 	luaTable.AddString("domoticz_version", szAppVersion);
779 	luaTable.AddString("dzVents_version", GetVersion());
780 
781 	luaTable.Publish();
782 }
783 
ExportHardwareData(CLuaTable & luaTable,int & index,const std::vector<CEventSystem::_tEventQueue> & items)784 void CdzVents::ExportHardwareData(CLuaTable &luaTable, int& index, const std::vector<CEventSystem::_tEventQueue>& items)
785 {
786 	;// to be implemented when hardware notification support is added
787 }
788 
ExportDomoticzDataToLua(lua_State * lua_state,const std::vector<CEventSystem::_tEventQueue> & items)789 void CdzVents::ExportDomoticzDataToLua(lua_State *lua_state, const std::vector<CEventSystem::_tEventQueue> &items)
790 {
791 	boost::shared_lock<boost::shared_mutex> devicestatesMutexLock(m_mainworker.m_eventsystem.m_devicestatesMutex);
792 	int index = 1;
793 	time_t now = mytime(NULL);
794 	struct tm tm1;
795 	localtime_r(&now, &tm1);
796 	struct tm tLastUpdate;
797 	localtime_r(&now, &tLastUpdate);
798 	int SensorTimeOut = 60;
799 	m_sql.GetPreferencesVar("SensorTimeout", SensorTimeOut);
800 
801 	struct tm ntime;
802 	time_t checktime;
803 
804 	CLuaTable luaTable(lua_state, "domoticzData");
805 
806 	// First export all the devices.
807 	std::map<uint64_t, CEventSystem::_tDeviceStatus>::iterator iterator;
808 	for (iterator = m_mainworker.m_eventsystem.m_devicestates.begin(); iterator != m_mainworker.m_eventsystem.m_devicestates.end(); ++iterator)
809 	{
810 		CEventSystem::_tDeviceStatus sitem = iterator->second;
811 		const char *dev_type = RFX_Type_Desc(sitem.devType, 1);
812 		const char *sub_type = RFX_Type_SubType_Desc(sitem.devType, sitem.subType);
813 
814 		bool triggerDevice = false;
815 		std::vector<CEventSystem::_tEventQueue>::const_iterator itt;
816 		for (itt = items.begin(); itt != items.end(); ++itt)
817 		{
818 			if (sitem.ID == itt->id && itt->reason == m_mainworker.m_eventsystem.REASON_DEVICE)
819 			{
820 				triggerDevice = true;
821 				sitem.lastUpdate = itt->lastUpdate;
822 				sitem.lastLevel = itt->lastLevel;
823 				sitem.sValue = itt->sValue;
824 				sitem.nValueWording = itt->nValueWording;
825 				sitem.nValue = itt->nValue;
826 				if (itt->JsonMapString.size() > 0)
827 					sitem.JsonMapString = itt->JsonMapString;
828 				if (itt->JsonMapFloat.size() > 0)
829 					sitem.JsonMapFloat = itt->JsonMapFloat;
830 				if (itt->JsonMapInt.size() > 0)
831 					sitem.JsonMapInt = itt->JsonMapInt;
832 				if (itt->JsonMapBool.size() > 0)
833 					sitem.JsonMapBool = itt->JsonMapBool;
834 			}
835 		}
836 
837 		//_log.Log(LOG_STATUS, "Getting device with id: %s", rowid.c_str());
838 
839 		ParseSQLdatetime(checktime, ntime, sitem.lastUpdate, tm1.tm_isdst);
840 		bool timed_out = (now - checktime >= SensorTimeOut * 60);
841 
842 		luaTable.OpenSubTableEntry(index, 1, 12);
843 
844 		luaTable.AddString("name", sitem.deviceName);
845 		luaTable.AddBool("protected", (sitem.protection == 1) );
846 		luaTable.AddInteger("id", sitem.ID);
847 		luaTable.AddString("baseType","device");
848 		luaTable.AddString("deviceType", dev_type);
849 		luaTable.AddString("subType", sub_type);
850 		luaTable.AddString("switchType", Switch_Type_Desc((_eSwitchType)sitem.switchtype));
851 		luaTable.AddInteger("switchTypeValue", sitem.switchtype);
852 		luaTable.AddString("lastUpdate", sitem.lastUpdate);
853 		luaTable.AddInteger("lastLevel", sitem.lastLevel);
854 		luaTable.AddBool("changed", triggerDevice);
855 		luaTable.AddBool("timedOut", timed_out);
856 
857 		//get all svalues separate
858 		std::vector<std::string> strarray;
859 		StringSplit(sitem.sValue, ";", strarray);
860 
861 		luaTable.OpenSubTableEntry("rawData", 0, 0);
862 		for (uint8_t i = 0; i < strarray.size(); i++)
863 		{
864 			luaTable.AddString(i + 1, strarray[i]);
865 		}
866 		luaTable.CloseSubTableEntry(); // rawData table
867 
868 		luaTable.AddString("deviceID", sitem.deviceID);
869 		luaTable.AddString("description", sitem.description);
870 		luaTable.AddInteger("batteryLevel", sitem.batteryLevel);
871 		luaTable.AddInteger("signalLevel", sitem.signalLevel);
872 
873 		luaTable.OpenSubTableEntry("data", 0, 0);
874 		luaTable.AddString("_state", sitem.nValueWording);
875 		luaTable.AddInteger("_nValue", sitem.nValue);
876 		luaTable.AddInteger("hardwareID", sitem.hardwareID);
877 
878 		// Lux does not have it's own field yet.
879 		if (sitem.devType == pTypeLux && sitem.subType == sTypeLux)
880 		{
881 			int lux = 0;
882 			if (strarray.size() > 0)
883 				lux = atoi(strarray[0].c_str());
884 			luaTable.AddNumber("lux", lux);
885 		}
886 
887 		if (sitem.devType == pTypeGeneral && sitem.subType == sTypeKwh)
888 		{
889 			long double value = 0.0f;
890 			if (strarray.size() > 1)
891 				value = atof(strarray[1].c_str());
892 			luaTable.AddNumber("whTotal", value);
893 			value = 0.0f;
894 			if (strarray.size() > 0)
895 				value = atof(strarray[0].c_str());
896 			luaTable.AddNumber("whActual", value);
897 		}
898 
899 		// Now see if we have additional fields from the JSON data
900 		if (sitem.JsonMapString.size() > 0)
901 		{
902 			std::map<uint8_t, std::string>::const_iterator itt;
903 			for (itt = sitem.JsonMapString.begin(); itt != sitem.JsonMapString.end(); ++itt)
904 			{
905 				if (strcmp(m_mainworker.m_eventsystem.JsonMap[itt->first].szOriginal, "LevelNames") == 0 ||
906 					strcmp(m_mainworker.m_eventsystem.JsonMap[itt->first].szOriginal, "LevelActions") == 0)
907 					luaTable.AddString(
908 						m_mainworker.m_eventsystem.JsonMap[itt->first].szNew,
909 						base64_decode(itt->second));
910 				else
911 					luaTable.AddString(
912 						m_mainworker.m_eventsystem.JsonMap[itt->first].szNew,
913 						itt->second);
914 			}
915 		}
916 
917 		if (sitem.JsonMapFloat.size() > 0)
918 		{
919 			std::map<uint8_t, float>::const_iterator itt;
920 			for (itt = sitem.JsonMapFloat.begin(); itt != sitem.JsonMapFloat.end(); ++itt)
921 			{
922 				luaTable.AddNumber(m_mainworker.m_eventsystem.JsonMap[itt->first].szNew, itt->second);
923 			}
924 		}
925 
926 		if (sitem.JsonMapInt.size() > 0)
927 		{
928 			std::map<uint8_t, int>::const_iterator itt;
929 			for (itt = sitem.JsonMapInt.begin(); itt != sitem.JsonMapInt.end(); ++itt)
930 			{
931 				luaTable.AddInteger(m_mainworker.m_eventsystem.JsonMap[itt->first].szNew, itt->second);
932 			}
933 		}
934 
935 		if (sitem.JsonMapBool.size() > 0)
936 		{
937 			std::map<uint8_t, bool>::const_iterator itt;
938 			for (itt = sitem.JsonMapBool.begin(); itt != sitem.JsonMapBool.end(); ++itt)
939 			{
940 				luaTable.AddBool(m_mainworker.m_eventsystem.JsonMap[itt->first].szNew, itt->second);
941 			}
942 		}
943 
944 		luaTable.CloseSubTableEntry(); // data table
945 		luaTable.CloseSubTableEntry(); // device entry
946 		index++;
947 	}
948 	devicestatesMutexLock.unlock();
949 
950 	// Now do the scenes and groups.
951 	const char *description = "";
952 	boost::shared_lock<boost::shared_mutex> scenesgroupsMutexLock(m_mainworker.m_eventsystem.m_scenesgroupsMutex);
953 
954 	std::map<uint64_t, CEventSystem::_tScenesGroups>::const_iterator ittScenes;
955 	for (ittScenes = m_mainworker.m_eventsystem.m_scenesgroups.begin(); ittScenes != m_mainworker.m_eventsystem.m_scenesgroups.end(); ++ittScenes)
956 	{
957 		CEventSystem::_tScenesGroups sgitem = ittScenes->second;
958 		bool triggerScene = false;
959 		std::vector<CEventSystem::_tEventQueue>::const_iterator itt;
960 		for (itt = items.begin(); itt != items.end(); ++itt)
961 		{
962 			if (sgitem.ID == itt->id && itt->reason == m_mainworker.m_eventsystem.REASON_SCENEGROUP)
963 			{
964 				triggerScene = true;
965 				sgitem.lastUpdate = itt->lastUpdate;
966 				sgitem.scenesgroupValue = itt->sValue;
967 			}
968 		}
969 
970 		std::vector<std::vector<std::string> > result;
971 		result = m_sql.safe_query("SELECT Description FROM Scenes WHERE (ID=='%d')", sgitem.ID);
972 		if (result.empty())
973 			description = "";
974 		else
975 			description = result[0][0].c_str();
976 
977 		luaTable.OpenSubTableEntry(index, 1, 7);
978 
979 		luaTable.AddString("name", sgitem.scenesgroupName);
980 		luaTable.AddInteger("id", sgitem.ID);
981 		luaTable.AddString("description", description);
982 		luaTable.AddString("baseType", (sgitem.scenesgroupType == 0) ? "scene" : "group");
983 		luaTable.AddBool("protected", (lua_Number)sgitem.protection == 1);
984 		luaTable.AddString("lastUpdate", sgitem.lastUpdate);
985 		luaTable.AddBool("changed", triggerScene);
986 
987 		luaTable.OpenSubTableEntry("data", 0, 0);
988 
989 		luaTable.AddString("_state", sgitem.scenesgroupValue);
990 
991 		luaTable.CloseSubTableEntry(); // data, why rawset was done here???
992 
993 		luaTable.OpenSubTableEntry("deviceIDs", 0, 0);
994 		std::vector<uint64_t>::const_iterator itt2;
995 		if (sgitem.memberID.size() > 0)
996 		{
997 			int index = 1;
998 			for (itt2 = sgitem.memberID.begin(); itt2 != sgitem.memberID.end(); ++itt2)
999 			{
1000 				luaTable.AddInteger(index, *itt2);
1001 				index++;
1002 			}
1003 		}
1004 
1005 		luaTable.CloseSubTableEntry(); // device table
1006 		luaTable.CloseSubTableEntry(); // end entry
1007 		index++;
1008 	}
1009 	scenesgroupsMutexLock.unlock();
1010 
1011 	std::string vtype;
1012 
1013 	// Now do the user variables.
1014 	boost::shared_lock<boost::shared_mutex> uservariablesMutexLock(m_mainworker.m_eventsystem.m_uservariablesMutex);
1015 	std::map<uint64_t, CEventSystem::_tUserVariable>::const_iterator it_var;
1016 	for (it_var = m_mainworker.m_eventsystem.m_uservariables.begin(); it_var != m_mainworker.m_eventsystem.m_uservariables.end(); ++it_var)
1017 	{
1018 		CEventSystem::_tUserVariable uvitem = it_var->second;
1019 		bool triggerVar = false;
1020 		std::vector<CEventSystem::_tEventQueue>::const_iterator itt;
1021 		for (itt = items.begin(); itt != items.end(); ++itt)
1022 		{
1023 			if (uvitem.ID == itt->id && itt->reason == m_mainworker.m_eventsystem.REASON_USERVARIABLE)
1024 			{
1025 				triggerVar = true;
1026 				uvitem.lastUpdate = itt->lastUpdate;
1027 				uvitem.variableValue = itt->sValue;
1028 			}
1029 		}
1030 
1031 		luaTable.OpenSubTableEntry(index, 1, 5);
1032 
1033 		luaTable.AddString("name", uvitem.variableName);
1034 		luaTable.AddInteger("id", uvitem.ID);
1035 		luaTable.AddString("baseType", "uservariable");
1036 		luaTable.AddString("lastUpdate", uvitem.lastUpdate);
1037 		luaTable.AddBool("changed", triggerVar);
1038 
1039 		luaTable.OpenSubTableEntry("data", 0, 0);
1040 
1041 		if (uvitem.variableType == 0)
1042 		{
1043 			luaTable.AddInteger("value", atoi(uvitem.variableValue.c_str()));
1044 			vtype = "integer";
1045 		}
1046 		else if (uvitem.variableType == 1)
1047 		{
1048 			//Float
1049 			luaTable.AddNumber("value", atof(uvitem.variableValue.c_str()));
1050 			vtype = "float";
1051 		}
1052 		else
1053 		{
1054 			//String,Date,Time
1055 			luaTable.AddString("value", uvitem.variableValue);
1056 			if (uvitem.variableType == 2)
1057 				vtype = "string";
1058 			else if (uvitem.variableType == 3)
1059 				vtype = "date";
1060 			else if (uvitem.variableType == 4)
1061 				vtype = "time";
1062 			else
1063 				vtype = "unknown";
1064 		}
1065 
1066 		luaTable.CloseSubTableEntry(); // data table
1067 
1068 		luaTable.AddString("variableType", vtype);
1069 
1070 		luaTable.CloseSubTableEntry(); // end entry
1071 
1072 		index++;
1073 	}
1074 
1075 	// Now do the cameras.
1076 	std::vector<std::vector<std::string> > result;
1077 	result = m_sql.safe_query("SELECT ID, Name FROM Cameras where enabled = '1' ORDER BY ID ASC");
1078 	if (!result.empty())
1079 	{
1080 		for (const auto & itt : result)
1081 		{
1082 			std::vector<std::string> sd = itt;
1083 
1084 			luaTable.OpenSubTableEntry(index, 1, 3);
1085 			luaTable.AddString("name", sd[1]);
1086 			luaTable.AddInteger("id", atoi(sd[0].c_str()));
1087 			luaTable.AddString("baseType", "camera");
1088 			luaTable.CloseSubTableEntry(); // end entry
1089 
1090 			index++;
1091 		}
1092 	}
1093 	ExportHardwareData(luaTable, index, items);
1094 
1095 	luaTable.Publish();
1096 }
1097