1 #include "stdafx.h"
2 #include "../hardware/hardwaretypes.h"
3 #include "../main/localtime_r.h"
4 #include "../main/Logger.h"
5 #include "../main/mainworker.h"
6 #include "../main/Helper.h"
7 #include "../main/SQLHelper.h"
8 #include "../main/WebServer.h"
9 #include "../webserver/cWebem.h"
10 #include "../main/json_helper.h"
11 #include "XiaomiGateway.h"
12 #include "XiaomiHardware.h"
13 #include <openssl/aes.h>
14 #include <boost/asio.hpp>
15 #include <boost/bind.hpp>
16 
17 #ifndef WIN32
18 #include <ifaddrs.h>
19 #endif
20 
21 /*
22 Xiaomi (Aqara) makes a smart home gateway/hub that has support
23 for a variety of Xiaomi sensors.
24 They can be purchased on AliExpress or other stores at very
25 competitive prices.
26 Protocol is Zigbee and WiFi, and the gateway and
27 Domoticz need to be in the same network/subnet with multicast working
28 */
29 
30 #define round(a) ( int ) ( a + .5 )
31 // Removing this vector and use unitcode to tell what kind of device each is
32 //std::vector<std::string> arrAqara_Wired_ID;
33 
34 std::list<XiaomiGateway*> gatewaylist;
35 std::mutex gatewaylist_mutex;
36 
GatewayByIp(std::string ip)37 XiaomiGateway * XiaomiGateway::GatewayByIp(std::string ip)
38 {
39 	XiaomiGateway * ret = NULL;
40 	{
41 		std::unique_lock<std::mutex> lock(gatewaylist_mutex);
42 		std::list<XiaomiGateway*>::iterator    it = gatewaylist.begin();
43 		for (; it != gatewaylist.end(); it++)
44 		{
45 			if ((*it)->GetGatewayIp() == ip)
46 			{
47 				ret = (*it);
48 				break;
49 			};
50 		};
51 	}
52 	return ret;
53 }
54 
AddGatewayToList()55 void XiaomiGateway::AddGatewayToList()
56 {
57 	XiaomiGateway * maingw = NULL;
58 	{
59 		std::unique_lock<std::mutex> lock(gatewaylist_mutex);
60 		std::list<XiaomiGateway*>::iterator    it = gatewaylist.begin();
61 		for (; it != gatewaylist.end(); it++)
62 		{
63 			if ((*it)->IsMainGateway())
64 			{
65 				maingw = (*it);
66 				break;
67 			};
68 		};
69 
70 		if (!maingw)
71 		{
72 			SetAsMainGateway();
73 		}
74 		else
75 		{
76 			maingw->UnSetMainGateway();
77 		}
78 
79 		gatewaylist.push_back(this);
80 	}
81 
82 	if (maingw)
83 	{
84 		maingw->Restart();
85 	}
86 }
87 
RemoveFromGatewayList()88 void XiaomiGateway::RemoveFromGatewayList()
89 {
90 	XiaomiGateway * maingw = NULL;
91 	{
92 		std::unique_lock<std::mutex> lock(gatewaylist_mutex);
93 		gatewaylist.remove(this);
94 		if (IsMainGateway())
95 		{
96 			UnSetMainGateway();
97 
98 			if (gatewaylist.begin() != gatewaylist.end())
99 			{
100 				std::list<XiaomiGateway*>::iterator    it = gatewaylist.begin();
101 				maingw = (*it);
102 			}
103 		}
104 	}
105 
106 	if (maingw)
107 	{
108 		maingw->Restart();
109 	}
110 }
111 
112 // Use this function to get local ip addresses via getifaddrs when Boost.Asio approach fails
113 // Adds the addresses found to the supplied vector and returns the count
114 // Code from Stack Overflow - https://stackoverflow.com/questions/2146191
get_local_ipaddr(std::vector<std::string> & ip_addrs)115 int XiaomiGateway::get_local_ipaddr(std::vector<std::string>& ip_addrs)
116 {
117 #ifdef WIN32
118 	return 0;
119 #else
120 	struct ifaddrs *myaddrs, *ifa;
121 	void *in_addr;
122 	char buf[64];
123 	int count = 0;
124 
125 	if (getifaddrs(&myaddrs) != 0)
126 	{
127 		_log.Log(LOG_ERROR, "getifaddrs failed! (when trying to determine local ip address)");
128 		perror("getifaddrs");
129 		return 0;
130 	}
131 
132 	for (ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next)
133 	{
134 		if (ifa->ifa_addr == NULL)
135 			continue;
136 		if (!(ifa->ifa_flags & IFF_UP))
137 			continue;
138 
139 		switch (ifa->ifa_addr->sa_family)
140 		{
141 		case AF_INET:
142 		{
143 			struct sockaddr_in *s4 = (struct sockaddr_in *)ifa->ifa_addr;
144 			in_addr = &s4->sin_addr;
145 			break;
146 		}
147 
148 		case AF_INET6:
149 		{
150 			struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)ifa->ifa_addr;
151 			in_addr = &s6->sin6_addr;
152 			break;
153 		}
154 
155 		default:
156 			continue;
157 		}
158 
159 		if (!inet_ntop(ifa->ifa_addr->sa_family, in_addr, buf, sizeof(buf)))
160 		{
161 			_log.Log(LOG_ERROR, "Could not convert to IP address, inet_ntop failed for interface %s", ifa->ifa_name);
162 		}
163 		else
164 		{
165 			ip_addrs.push_back(buf);
166 			count++;
167 		}
168 	}
169 
170 	freeifaddrs(myaddrs);
171 	return count;
172 #endif
173 }
174 
XiaomiGateway(const int ID)175 XiaomiGateway::XiaomiGateway(const int ID)
176 {
177 	m_HwdID = ID;
178 	m_bDoRestart = false;
179 	m_ListenPort9898 = false;
180 }
181 
~XiaomiGateway(void)182 XiaomiGateway::~XiaomiGateway(void)
183 {
184 }
185 
WriteToHardware(const char * pdata,const unsigned char length)186 bool XiaomiGateway::WriteToHardware(const char * pdata, const unsigned char length)
187 {
188 	const tRBUF *pCmd = reinterpret_cast<const tRBUF *>(pdata);
189 	unsigned char packettype = pCmd->ICMND.packettype;
190 	unsigned char subtype = pCmd->ICMND.subtype;
191 	bool result = true;
192 	std::string message = "";
193 
194 	if (m_GatewaySID == "") {
195 		m_GatewaySID = XiaomiGatewayTokenManager::GetInstance().GetSID(m_GatewayIp);
196 	}
197 
198 	if (packettype == pTypeGeneralSwitch) {
199 		_tGeneralSwitch *xcmd = (_tGeneralSwitch*)pdata;
200 
201 		char szTmp[50];
202 		sprintf(szTmp, "%08X", (unsigned int)xcmd->id);
203 		std::string ID = szTmp;
204 		std::stringstream s_strid2;
205 		s_strid2 << std::hex << ID;
206 		std::string sid = s_strid2.str();
207 		std::transform(sid.begin(), sid.end(), sid.begin(), ::tolower);
208 		std::string cmdchannel = "";
209 		std::string cmdcommand = "";
210 		std::string cmddevice = "";
211 		std::string sidtemp = sid;
212 		sidtemp.insert(0, "158d00");
213 
214 		cmdchannel = DetermineChannel(xcmd->unitcode);
215 		cmddevice = DetermineDevice(xcmd->unitcode);
216 		cmdcommand = DetermineCommand(xcmd->cmnd);
217 
218 		if (xcmd->unitcode == XiaomiUnitCode::SELECTOR_WIRED_WALL_SINGLE || xcmd->unitcode == XiaomiUnitCode::SELECTOR_WIRED_WALL_DUAL_CHANNEL_0 ||
219 			xcmd->unitcode == XiaomiUnitCode::SELECTOR_WIRED_WALL_DUAL_CHANNEL_1 || ((xcmd->subtype == sSwitchGeneralSwitch) && (xcmd->unitcode == XiaomiUnitCode::ACT_ONOFF_PLUG))) {
220 			message = "{\"cmd\":\"write\",\"model\":\"" + cmddevice + "\",\"sid\":\"158d00" + sid + "\",\"short_id\":0,\"data\":\"{\\\"" + cmdchannel + "\\\":\\\"" + cmdcommand + "\\\",\\\"key\\\":\\\"@gatewaykey\\\"}\" }";
221 		}
222 		else if ((xcmd->subtype == sSwitchTypeSelector) && (xcmd->unitcode >= XiaomiUnitCode::GATEWAY_SOUND_ALARM_RINGTONE && xcmd->unitcode <= XiaomiUnitCode::GATEWAY_SOUND_DOORBELL) || (xcmd->subtype == sSwitchGeneralSwitch) && (xcmd->unitcode == XiaomiUnitCode::GATEWAY_SOUND_MP3)) {
223 			std::stringstream ss;
224 			if (xcmd->unitcode == XiaomiUnitCode::GATEWAY_SOUND_MP3) {
225 				if (xcmd->cmnd == 1) {
226 					std::vector<std::vector<std::string> > result;
227 					result = m_sql.safe_query("SELECT Value FROM UserVariables WHERE (Name == 'XiaomiMP3')");
228 					ss << result[0][0];
229 				}
230 				else {
231 					ss << "10000";
232 				}
233 			}
234 			else {
235 				int level = xcmd->level;
236 				if (level == 0) { level = 10000; }
237 				else {
238 					if (xcmd->unitcode == XiaomiUnitCode::GATEWAY_SOUND_ALARM_RINGTONE) {
239 						if (level > 0) { level = (level / 10) - 1; }
240 					}
241 					else if (xcmd->unitcode == XiaomiUnitCode::GATEWAY_SOUND_ALARM_CLOCK) {
242 						if (level > 0) { level = (level / 10) + 19; }
243 					}
244 					else if (xcmd->unitcode == XiaomiUnitCode::GATEWAY_SOUND_DOORBELL) {
245 						if (level > 0) { level = (level / 10) + 9; }
246 					}
247 				}
248 				ss << level;
249 			}
250 			m_GatewayMusicId = ss.str();
251 			//sid.insert(0, m_GatewayPrefix);
252 			message = "{\"cmd\":\"write\",\"model\":\"gateway\",\"sid\":\"" + m_GatewaySID + "\",\"short_id\":0,\"data\":\"{\\\"mid\\\":" + m_GatewayMusicId.c_str() + ",\\\"vol\\\":" + m_GatewayVolume.c_str() + ",\\\"key\\\":\\\"@gatewaykey\\\"}\" }";
253 		}
254 		else if (xcmd->subtype == sSwitchGeneralSwitch && xcmd->unitcode == XiaomiUnitCode::GATEWAY_SOUND_VOLUME_CONTROL) {
255 			m_GatewayVolume = std::to_string(xcmd->level);
256 			//sid.insert(0, m_GatewayPrefix);
257 			message = "{\"cmd\":\"write\",\"model\":\"gateway\",\"sid\":\"" + m_GatewaySID + "\",\"short_id\":0,\"data\":\"{\\\"mid\\\":" + m_GatewayMusicId.c_str() + ",\\\"vol\\\":" + m_GatewayVolume.c_str() + ",\\\"key\\\":\\\"@gatewaykey\\\"}\" }";
258 		}
259 		else if (xcmd->subtype == sSwitchBlindsT2) {
260 			int level = xcmd->level;
261 			if (xcmd->cmnd == 1) {
262 				level = 100;
263 			}
264 			message = "{\"cmd\":\"write\",\"model\":\"curtain\",\"sid\":\"158d00" + sid + "\",\"short_id\":9844,\"data\":\"{\\\"curtain_level\\\":\\\"" + std::to_string(level) + "\\\",\\\"key\\\":\\\"@gatewaykey\\\"}\" }";
265 		}
266 	}
267 	else if (packettype == pTypeColorSwitch) {
268 		// Gateway RGB Controller
269 		const _tColorSwitch *xcmd = reinterpret_cast<const _tColorSwitch*>(pdata);
270 
271 		if (xcmd->command == Color_LedOn) {
272 			m_GatewayBrightnessInt = 100;
273 			message = "{\"cmd\":\"write\",\"model\":\"gateway\",\"sid\":\"" + m_GatewaySID + "\",\"short_id\":0,\"data\":\"{\\\"rgb\\\":4294967295,\\\"key\\\":\\\"@gatewaykey\\\"}\" }";
274 		}
275 		else if (xcmd->command == Color_LedOff) {
276 			m_GatewayBrightnessInt = 0;
277 			message = "{\"cmd\":\"write\",\"model\":\"gateway\",\"sid\":\"" + m_GatewaySID + "\",\"short_id\":0,\"data\":\"{\\\"rgb\\\":0,\\\"key\\\":\\\"@gatewaykey\\\"}\" }";
278 		}
279 		else if (xcmd->command == Color_SetColor) {
280 			if (xcmd->color.mode == ColorModeRGB)
281 			{
282 				m_GatewayRgbR = xcmd->color.r;
283 				m_GatewayRgbG = xcmd->color.g;
284 				m_GatewayRgbB = xcmd->color.b;
285 				m_GatewayBrightnessInt = xcmd->value; //TODO: What is the valid range for XiaomiGateway, 0..100 or 0..255?
286 
287 				uint32_t value = (m_GatewayBrightnessInt << 24) | (m_GatewayRgbR << 16) | (m_GatewayRgbG << 8) | (m_GatewayRgbB);
288 
289 				std::stringstream ss;
290 				ss << "{\"cmd\":\"write\",\"model\":\"gateway\",\"sid\":\"" << m_GatewaySID << "\",\"short_id\":0,\"data\":\"{\\\"rgb\\\":" << value << ",\\\"key\\\":\\\"@gatewaykey\\\"}\" }";
291 				message = ss.str();
292 			}
293 			else
294 			{
295 				_log.Log(LOG_STATUS, "XiaomiGateway: SetRGBColour - Color mode '%d' is unhandled, if you have a suggestion for what it should do, please post on the Domoticz forum", xcmd->color.mode);
296 			}
297 		}
298 		else if ((xcmd->command == Color_SetBrightnessLevel) || (xcmd->command == Color_SetBrightUp) || (xcmd->command == Color_SetBrightDown)) {
299 			// Add the brightness
300 			if (xcmd->command == Color_SetBrightUp) {
301 				//m_GatewayBrightnessInt = std::min(m_GatewayBrightnessInt + 10, 100);
302 			}
303 			else if (xcmd->command == Color_SetBrightDown) {
304 				//m_GatewayBrightnessInt = std::max(m_GatewayBrightnessInt - 10, 0);
305 			}
306 			else {
307 				m_GatewayBrightnessInt = (int)xcmd->value; //TODO: What is the valid range for XiaomiGateway, 0..100 or 0..255?
308 			}
309 
310 			uint32_t value = (m_GatewayBrightnessInt << 24) | (m_GatewayRgbR << 16) | (m_GatewayRgbG << 8) | (m_GatewayRgbB);
311 
312 			std::stringstream ss;
313 			ss << "{\"cmd\":\"write\",\"model\":\"gateway\",\"sid\":\"" << m_GatewaySID << "\",\"short_id\":0,\"data\":\"{\\\"rgb\\\":" << value << ",\\\"key\\\":\\\"@gatewaykey\\\"}\" }";
314 			message = ss.str();
315 		}
316 		else if (xcmd->command == Color_SetColorToWhite) {
317 			// Ignore Color_SetColorToWhite
318 		}
319 		else {
320 			_log.Log(LOG_ERROR, "XiaomiGateway: Unknown command %d", xcmd->command);
321 		}
322 	}
323 	if (!message.empty()) {
324 		_log.Debug(DEBUG_HARDWARE, "XiaomiGateway: message: '%s'", message.c_str());
325 		result = SendMessageToGateway(message);
326 		if (result == false) {
327 			// Retry, send the message again
328 			_log.Log(LOG_STATUS, "XiaomiGateway: SendMessageToGateway failed on first attempt, will try again");
329 			sleep_milliseconds(100);
330 			result = SendMessageToGateway(message);
331 		}
332 	}
333 	return result;
334 }
335 
SendMessageToGateway(const std::string & controlmessage)336 bool XiaomiGateway::SendMessageToGateway(const std::string &controlmessage) {
337 	std::string message = controlmessage;
338 	bool result = true;
339 	boost::asio::io_service io_service;
340 	boost::asio::ip::udp::socket socket_(io_service, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 0));
341 	stdreplace(message, "@gatewaykey", GetGatewayKey());
342 	std::shared_ptr<std::string> message1(new std::string(message));
343 	boost::asio::ip::udp::endpoint remote_endpoint_;
344 	remote_endpoint_ = boost::asio::ip::udp::endpoint(boost::asio::ip::address::from_string(m_GatewayIp), 9898);
345 	socket_.send_to(boost::asio::buffer(*message1), remote_endpoint_);
346 	sleep_milliseconds(150);
347 	boost::array<char, 512> recv_buffer_;
348 	memset(&recv_buffer_[0], 0, sizeof(recv_buffer_));
349 #ifdef _DEBUG
350 	_log.Log(LOG_STATUS, "XiaomiGateway: request to %s - %s", m_GatewayIp.c_str(), message.c_str());
351 #endif
352 	while (socket_.available() > 0) {
353 		socket_.receive_from(boost::asio::buffer(recv_buffer_), remote_endpoint_);
354 		std::string receivedString(recv_buffer_.data());
355 
356 		Json::Value root;
357 		bool ret = ParseJSon(receivedString, root);
358 		if ((ret) && (root.isObject()))
359 		{
360 			std::string data = root["data"].asString();
361 			Json::Value root2;
362 			ret = ParseJSon(data.c_str(), root2);
363 			if ((ret) && (root2.isObject()))
364 			{
365 				std::string error = root2["error"].asString();
366 				if (error != "") {
367 					_log.Log(LOG_ERROR, "XiaomiGateway: unable to write command - %s", error.c_str());
368 					result = false;
369 				}
370 			}
371 		}
372 
373 #ifdef _DEBUG
374 		_log.Log(LOG_STATUS, "XiaomiGateway: response %s", receivedString.c_str());
375 #endif
376 	}
377 	socket_.close();
378 	return result;
379 }
380 
InsertUpdateTemperature(const std::string & nodeid,const std::string & Name,const float Temperature,const int battery)381 void XiaomiGateway::InsertUpdateTemperature(const std::string &nodeid, const std::string &Name, const float Temperature, const int battery)
382 {
383 	unsigned int sID = GetShortID(nodeid);
384 	if (sID > 0) {
385 		SendTempSensor(sID, battery, Temperature, Name);
386 	}
387 }
388 
InsertUpdateHumidity(const std::string & nodeid,const std::string & Name,const int Humidity,const int battery)389 void XiaomiGateway::InsertUpdateHumidity(const std::string &nodeid, const std::string &Name, const int Humidity, const int battery)
390 {
391 	unsigned int sID = GetShortID(nodeid);
392 	if (sID > 0) {
393 		SendHumiditySensor(sID, battery, Humidity, Name);
394 	}
395 }
396 
InsertUpdatePressure(const std::string & nodeid,const std::string & Name,const float Pressure,const int battery)397 void XiaomiGateway::InsertUpdatePressure(const std::string &nodeid, const std::string &Name, const float Pressure, const int battery)
398 {
399 	unsigned int sID = GetShortID(nodeid);
400 	if (sID > 0) {
401 		SendPressureSensor(sID, 1, battery, Pressure, Name);
402 	}
403 }
404 
InsertUpdateTempHumPressure(const std::string & nodeid,const std::string & Name,const float Temperature,const int Humidity,const float Pressure,const int battery)405 void XiaomiGateway::InsertUpdateTempHumPressure(const std::string &nodeid, const std::string &Name, const float Temperature, const int Humidity, const float Pressure, const int battery)
406 {
407 	unsigned int sID = GetShortID(nodeid);
408 	int barometric_forcast = baroForecastNoInfo;
409 	if (Pressure < 1000)
410 		barometric_forcast = baroForecastRain;
411 	else if (Pressure < 1020)
412 		barometric_forcast = baroForecastCloudy;
413 	else if (Pressure < 1030)
414 		barometric_forcast = baroForecastPartlyCloudy;
415 	else
416 		barometric_forcast = baroForecastSunny;
417 
418 	if (sID > 0) {
419 		SendTempHumBaroSensor(sID, battery, Temperature, Humidity, Pressure, barometric_forcast, Name);
420 	}
421 }
422 
InsertUpdateTempHum(const std::string & nodeid,const std::string & Name,const float Temperature,const int Humidity,const int battery)423 void XiaomiGateway::InsertUpdateTempHum(const std::string &nodeid, const std::string &Name, const float Temperature, const int Humidity, const int battery)
424 {
425 	unsigned int sID = GetShortID(nodeid);
426 	if (sID > 0) {
427 		SendTempHumSensor(sID, battery, Temperature, Humidity, Name);
428 	}
429 }
430 
InsertUpdateRGBGateway(const std::string & nodeid,const std::string & Name,const bool bIsOn,const int brightness,const int hue)431 void XiaomiGateway::InsertUpdateRGBGateway(const std::string & nodeid, const std::string & Name, const bool bIsOn, const int brightness, const int hue)
432 {
433 	if (nodeid.length() < 12) {
434 		_log.Log(LOG_ERROR, "XiaomiGateway: Node ID %s is too short", nodeid.c_str());
435 		return;
436 	}
437 	std::string str = nodeid.substr(4, 8);
438 	unsigned int sID;
439 	std::stringstream ss;
440 	ss << std::hex << str.c_str();
441 	ss >> sID;
442 
443 	char szDeviceID[300];
444 	if (sID == 1)
445 		sprintf(szDeviceID, "%d", 1);
446 	else
447 		sprintf(szDeviceID, "%08X", (unsigned int)sID);
448 
449 	int lastLevel = 0;
450 	int nvalue = 0;
451 	bool tIsOn = !(bIsOn);
452 	std::vector<std::vector<std::string> > result;
453 	result = m_sql.safe_query("SELECT nValue, LastLevel FROM DeviceStatus WHERE (HardwareID==%d) AND (DeviceID=='%q') AND (Type==%d) AND (SubType==%d)", m_HwdID, szDeviceID, pTypeColorSwitch, sTypeColor_RGB_W);
454 
455 	if (result.empty())
456 	{
457 		_log.Log(LOG_STATUS, "XiaomiGateway: New Gateway Found (%s/%s)", str.c_str(), Name.c_str());
458 		//int value = atoi(brightness.c_str());
459 		//int value = hue; // atoi(hue.c_str());
460 		int cmd = Color_LedOn;
461 		if (!bIsOn) {
462 			cmd = Color_LedOff;
463 		}
464 		_tColorSwitch ycmd;
465 		ycmd.subtype = sTypeColor_RGB_W;
466 		ycmd.id = sID;
467 		//ycmd.dunit = 0;
468 		ycmd.value = brightness;
469 		ycmd.command = cmd;
470 		m_mainworker.PushAndWaitRxMessage(this, (const unsigned char *)&ycmd, NULL, -1);
471 		m_sql.safe_query("UPDATE DeviceStatus SET Name='%q', SwitchType=%d, LastLevel=%d WHERE(HardwareID == %d) AND (DeviceID == '%s') AND (Type == %d)", Name.c_str(), (STYPE_Dimmer), brightness, m_HwdID, szDeviceID, pTypeColorSwitch);
472 	}
473 	else {
474 		nvalue = atoi(result[0][0].c_str());
475 		tIsOn = (nvalue != 0);
476 		lastLevel = atoi(result[0][1].c_str());
477 		//int value = atoi(brightness.c_str());
478 		if ((bIsOn != tIsOn) || (brightness != lastLevel))
479 		{
480 			int cmd = Color_LedOn;
481 			if (!bIsOn) {
482 				cmd = Color_LedOff;
483 			}
484 			_tColorSwitch ycmd;
485 			ycmd.subtype = sTypeColor_RGB_W;
486 			ycmd.id = sID;
487 			//ycmd.dunit = 0;
488 			ycmd.value = brightness;
489 			ycmd.command = cmd;
490 			m_mainworker.PushAndWaitRxMessage(this, (const unsigned char *)&ycmd, NULL, -1);
491 		}
492 	}
493 }
494 
InsertUpdateSwitch(const std::string & nodeid,const std::string & Name,const bool bIsOn,const _eSwitchType switchtype,const int unitcode,const int level,const std::string & messagetype,const std::string & load_power,const std::string & power_consumed,const int battery)495 void XiaomiGateway::InsertUpdateSwitch(const std::string &nodeid, const std::string &Name, const bool bIsOn, const _eSwitchType switchtype, const int unitcode, const int level, const std::string &messagetype, const std::string &load_power, const std::string &power_consumed, const int battery)
496 {
497 	unsigned int sID = GetShortID(nodeid);
498 
499 	char szTmp[300];
500 	if (sID == 1)
501 		sprintf(szTmp, "%d", 1);
502 	else
503 		sprintf(szTmp, "%08X", (unsigned int)sID);
504 	std::string ID = szTmp;
505 
506 	_tGeneralSwitch xcmd;
507 	xcmd.len = sizeof(_tGeneralSwitch) - 1;
508 	xcmd.id = sID;
509 	xcmd.type = pTypeGeneralSwitch;
510 	xcmd.subtype = sSwitchGeneralSwitch;
511 	xcmd.unitcode = unitcode;
512 	int customimage = 0;
513 
514 	if ((xcmd.unitcode >= XiaomiUnitCode::GATEWAY_SOUND_ALARM_RINGTONE) && (xcmd.unitcode <= XiaomiUnitCode::GATEWAY_SOUND_VOLUME_CONTROL)) {
515 		customimage = 8; // Speaker
516 	}
517 
518 	if (bIsOn) {
519 		xcmd.cmnd = gswitch_sOn;
520 	}
521 	else {
522 		xcmd.cmnd = gswitch_sOff;
523 	}
524 	if (switchtype == STYPE_Selector) {
525 		xcmd.subtype = sSwitchTypeSelector;
526 		if (level > 0) {
527 			xcmd.level = level;
528 		}
529 	}
530 	else if (switchtype == STYPE_SMOKEDETECTOR) {
531 		xcmd.level = level;
532 	}
533 	else if (switchtype == STYPE_BlindsPercentage) {
534 		xcmd.level = level;
535 		xcmd.subtype = sSwitchBlindsT2;
536 		xcmd.cmnd = gswitch_sSetLevel;
537 	}
538 
539 	// Check if this switch is already in the database
540 	std::vector<std::vector<std::string> > result;
541 
542 	// Block this device if it is already added for another gateway hardware id
543 	result = m_sql.safe_query("SELECT nValue FROM DeviceStatus WHERE (HardwareID!=%d) AND (DeviceID=='%q') AND (Type==%d) AND (Unit == '%d')", m_HwdID, ID.c_str(), xcmd.type, xcmd.unitcode);
544 	if (!result.empty()) {
545 		return;
546 	}
547 
548 	result = m_sql.safe_query("SELECT nValue, BatteryLevel FROM DeviceStatus WHERE (HardwareID==%d) AND (DeviceID=='%q') AND (Type==%d) AND (Unit == '%d')", m_HwdID, ID.c_str(), xcmd.type, xcmd.unitcode);
549 	if (result.empty())
550 	{
551 		_log.Log(LOG_STATUS, "XiaomiGateway: New %s Found (%s)", Name.c_str(), nodeid.c_str());
552 		m_mainworker.PushAndWaitRxMessage(this, (const unsigned char *)&xcmd, NULL, battery);
553 		if (customimage == 0) {
554 			if (switchtype == STYPE_OnOff) {
555 				customimage = 1; // Wall socket
556 			}
557 			else if (switchtype == STYPE_Selector) {
558 				customimage = 9;
559 			}
560 		}
561 
562 		m_sql.safe_query("UPDATE DeviceStatus SET Name='%q', SwitchType=%d, CustomImage=%i WHERE(HardwareID == %d) AND (DeviceID == '%q') AND (Unit == '%d')", Name.c_str(), (switchtype), customimage, m_HwdID, ID.c_str(), xcmd.unitcode);
563 
564 		if (switchtype == STYPE_Selector) {
565 			result = m_sql.safe_query("SELECT ID FROM DeviceStatus WHERE (HardwareID==%d) AND (DeviceID=='%q') AND (Type==%d) AND (Unit == '%d')", m_HwdID, ID.c_str(), xcmd.type, xcmd.unitcode);
566 			if (!result.empty()) {
567 				std::string Idx = result[0][0];
568 				if (Name == NAME_SELECTOR_WIRELESS_SINGLE) {
569 					m_sql.SetDeviceOptions(atoi(Idx.c_str()), m_sql.BuildDeviceOptions("SelectorStyle:0;LevelNames:Off|Click|Double Click|Long Click|Long Click Release", false));
570 				}
571 				else if (Name == NAME_SELECTOR_WIRELESS_SINGLE_SQUARE) {
572 					m_sql.SetDeviceOptions(atoi(Idx.c_str()), m_sql.BuildDeviceOptions("SelectorStyle:0;LevelNames:Off|Click|Double Click", false));
573 				}
574 				else if (Name == NAME_SELECTOR_WIRELESS_SINGLE_SMART_PUSH) {
575 					m_sql.SetDeviceOptions(atoi(Idx.c_str()), m_sql.BuildDeviceOptions("SelectorStyle:0;LevelNames:Off|Click|Shake", false));
576 				}
577 				else if (Name == NAME_SELECTOR_CUBE_V1) {
578 					m_sql.SetDeviceOptions(atoi(Idx.c_str()), m_sql.BuildDeviceOptions("SelectorStyle:0;LevelNames:Off|flip90|flip180|move|tap_twice|shake_air|swing|alert|free_fall|clock_wise|anti_clock_wise", false));
579 				}
580 				else if (Name == NAME_SELECTOR_CUBE_AQARA) {
581 					m_sql.SetDeviceOptions(atoi(Idx.c_str()), m_sql.BuildDeviceOptions("SelectorStyle:0;LevelNames:Off|flip90|flip180|move|tap_twice|shake_air|swing|alert|free_fall|rotate", false));
582 				}
583 				else if (Name == NAME_SENSOR_VIBRATION) {
584 					m_sql.SetDeviceOptions(atoi(Idx.c_str()), m_sql.BuildDeviceOptions("SelectorStyle:0;LevelNames:Off|Tilt|Vibrate|Free Fall", false));
585 				}
586 				else if (Name == NAME_SELECTOR_WIRELESS_WALL_DUAL) {
587 					m_sql.SetDeviceOptions(atoi(Idx.c_str()), m_sql.BuildDeviceOptions("SelectorStyle:0;LevelNames:Off|Switch 1|Switch 2|Both Click|Switch 1 Double Click|Switch 2 Double Click|Both Double Click|Switch 1 Long Click|Switch 2 Long Click|Both Long Click", false));
588 				}
589 				else if (Name == NAME_SELECTOR_WIRED_WALL_SINGLE) {
590 					m_sql.SetDeviceOptions(atoi(Idx.c_str()), m_sql.BuildDeviceOptions("SelectorStyle:0;LevelNames:Off|Switch1 On|Switch1 Off", false));
591 				}
592 				else if (Name == NAME_SELECTOR_WIRELESS_WALL_SINGLE) {
593 					m_sql.SetDeviceOptions(atoi(Idx.c_str()), m_sql.BuildDeviceOptions("SelectorStyle:0;LevelNames:Off|Click|Double Click|Long Click", false));
594 				}
595 				else if (Name == NAME_GATEWAY_SOUND_ALARM_RINGTONE) {
596 					m_sql.SetDeviceOptions(atoi(Idx.c_str()), m_sql.BuildDeviceOptions("SelectorStyle:1;LevelNames:Off|Police siren 1|Police siren 2|Accident tone|Missle countdown|Ghost|Sniper|War|Air Strike|Barking dogs", false));
597 				}
598 				else if (Name == NAME_GATEWAY_SOUND_ALARM_CLOCK) {
599 					m_sql.SetDeviceOptions(atoi(Idx.c_str()), m_sql.BuildDeviceOptions("SelectorStyle:1;LevelNames:Off|MiMix|Enthusiastic|GuitarClassic|IceWorldPiano|LeisureTime|Childhood|MorningStreamlet|MusicBox|Orange|Thinker", false));
600 				}
601 				else if (Name == NAME_GATEWAY_SOUND_DOORBELL) {
602 					m_sql.SetDeviceOptions(atoi(Idx.c_str()), m_sql.BuildDeviceOptions("SelectorStyle:1;LevelNames:Off|Doorbell ring tone|Knock on door|Hilarious|Alarm clock", false));
603 				}
604 			}
605 		}
606 		else if (switchtype == STYPE_OnOff && Name == NAME_GATEWAY_SOUND_MP3) {
607 			std::string errorMessage;
608 			m_sql.AddUserVariable("XiaomiMP3", USERVARTYPE_INTEGER, "10001", errorMessage);
609 		}
610 	}
611 	else {
612 		int nvalue = atoi(result[0][0].c_str());
613 		int BatteryLevel = atoi(result[0][1].c_str());
614 
615 		if (messagetype == "heartbeat") {
616 			if (battery != 255) {
617 				BatteryLevel = battery;
618 				m_sql.safe_query("UPDATE DeviceStatus SET BatteryLevel=%d WHERE(HardwareID == %d) AND (DeviceID == '%q') AND (Unit == '%d')", BatteryLevel, m_HwdID, ID.c_str(), xcmd.unitcode);
619 			}
620 		}
621 		else {
622 			if ((bIsOn == false && nvalue >= 1) || (bIsOn == true) || (Name == NAME_SELECTOR_WIRED_WALL_DUAL) || (Name == NAME_SELECTOR_WIRED_WALL_SINGLE) || (Name == NAME_ACT_BLINDS_CURTAIN)) {
623 				m_mainworker.PushAndWaitRxMessage(this, (const unsigned char *)&xcmd, NULL, BatteryLevel);
624 			}
625 		}
626 		if ((Name == NAME_ACT_ONOFF_PLUG) || (Name == NAME_ACT_ONOFF_PLUG_WALL)) {
627 			if (load_power != "" && power_consumed != "") {
628 				int power = atoi(load_power.c_str());
629 				int consumed = atoi(power_consumed.c_str()) / 1000;
630 				SendKwhMeter(sID, 1, 255, power, consumed, "Xiaomi Smart Plug Usage");
631 			}
632 		}
633 	}
634 }
635 
InsertUpdateCubeText(const std::string & nodeid,const std::string & Name,const std::string & degrees)636 void XiaomiGateway::InsertUpdateCubeText(const std::string & nodeid, const std::string & Name, const std::string &degrees)
637 {
638 	unsigned int sID = GetShortID(nodeid);
639 	if (sID > 0) {
640 		SendTextSensor(sID, sID, 255, degrees.c_str(), Name);
641 	}
642 }
643 
InsertUpdateVoltage(const std::string & nodeid,const std::string & Name,const int VoltageLevel)644 void XiaomiGateway::InsertUpdateVoltage(const std::string & nodeid, const std::string & Name, const int VoltageLevel)
645 {
646 	if (VoltageLevel < 3600) {
647 		unsigned int sID = GetShortID(nodeid);
648 		if (sID > 0) {
649 			int percent = ((VoltageLevel - 2200) / 10);
650 			float voltage = (float)VoltageLevel / 1000;
651 			SendVoltageSensor(sID, sID, percent, voltage, "Xiaomi Voltage");
652 		}
653 	}
654 }
655 
InsertUpdateLux(const std::string & nodeid,const std::string & Name,const int Illumination,const int battery)656 void XiaomiGateway::InsertUpdateLux(const std::string & nodeid, const std::string & Name, const int Illumination, const int battery)
657 {
658 	unsigned int sID = GetShortID(nodeid);
659 	if (sID > 0) {
660 		float lux = (float)Illumination;
661 		SendLuxSensor(sID, sID, battery, lux, Name);
662 	}
663 }
664 
StartHardware()665 bool XiaomiGateway::StartHardware()
666 {
667 	RequestStart();
668 
669 	m_bDoRestart = false;
670 
671 	// Force connect the next first time
672 	m_bIsStarted = true;
673 
674 	m_GatewayMusicId = "10000";
675 	m_GatewayVolume = "20";
676 
677 	std::vector<std::vector<std::string> > result;
678 	result = m_sql.safe_query("SELECT Password, Address FROM Hardware WHERE Type=%d AND ID=%d AND Enabled=1", HTYPE_XiaomiGateway, m_HwdID);
679 
680 	if (result.empty())
681 		return false;
682 
683 	m_GatewayPassword = result[0][0].c_str();
684 	m_GatewayIp = result[0][1].c_str();
685 
686 	m_GatewayRgbR = 255;
687 	m_GatewayRgbG = 255;
688 	m_GatewayRgbB = 255;
689 	m_GatewayBrightnessInt = 100;
690 	// Check for presence of Xiaomi user variable to enable message output
691 	m_OutputMessage = false;
692 	result = m_sql.safe_query("SELECT Value FROM UserVariables WHERE (Name == 'XiaomiMessage')");
693 	if (!result.empty()) {
694 		m_OutputMessage = true;
695 	}
696 	// Check for presence of Xiaomi user variable to enable additional voltage devices
697 	m_IncludeVoltage = false;
698 	result = m_sql.safe_query("SELECT Value FROM UserVariables WHERE (Name == 'XiaomiVoltage')");
699 	if (!result.empty()) {
700 		m_IncludeVoltage = true;
701 	}
702 	_log.Log(LOG_STATUS, "XiaomiGateway (ID=%d): Delaying worker startup...", m_HwdID);
703 	sleep_seconds(5);
704 
705 	XiaomiGatewayTokenManager::GetInstance();
706 
707 	AddGatewayToList();
708 
709 	if (m_ListenPort9898)
710 	{
711 		_log.Log(LOG_STATUS, "XiaomiGateway (ID=%d): Selected as main Gateway", m_HwdID);
712 	}
713 
714 	// Start worker thread
715 	m_thread = std::shared_ptr<std::thread>(new std::thread(&XiaomiGateway::Do_Work, this));
716 	SetThreadNameInt(m_thread->native_handle());
717 
718 	return (m_thread != nullptr);
719 }
720 
StopHardware()721 bool XiaomiGateway::StopHardware()
722 {
723 	if (m_thread)
724 	{
725 		RequestStop();
726 		m_thread->join();
727 		m_thread.reset();
728 	}
729 	m_bIsStarted = false;
730 	return true;
731 }
732 
Do_Work()733 void XiaomiGateway::Do_Work()
734 {
735 	_log.Log(LOG_STATUS, "XiaomiGateway (ID=%d): Worker started...", m_HwdID);
736 	boost::asio::io_service io_service;
737 	// Find the local ip address that is similar to the xiaomi gateway
738 	try {
739 		boost::asio::ip::udp::resolver resolver(io_service);
740 		boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), m_GatewayIp, "");
741 		boost::asio::ip::udp::resolver::iterator endpoints = resolver.resolve(query);
742 		boost::asio::ip::udp::endpoint ep = *endpoints;
743 		boost::asio::ip::udp::socket socket(io_service);
744 		socket.connect(ep);
745 		boost::asio::ip::address addr = socket.local_endpoint().address();
746 		std::string compareIp = m_GatewayIp.substr(0, (m_GatewayIp.length() - 3));
747 		std::size_t found = addr.to_string().find(compareIp);
748 		if (found != std::string::npos) {
749 			m_LocalIp = addr.to_string();
750 			_log.Log(LOG_STATUS, "XiaomiGateway (ID=%d): Using %s for local IP address.", m_HwdID, m_LocalIp.c_str());
751 		}
752 	}
753 	catch (std::exception& e) {
754 		_log.Log(LOG_STATUS, "XiaomiGateway (ID=%d): Could not detect local IP address using Boost.Asio: %s", m_HwdID, e.what());
755 	}
756 
757 	// Try finding local ip using ifaddrs when Boost.Asio fails
758 	if (m_LocalIp == "") {
759 		try {
760 			// Get first 2 octets of Xiaomi gateway ip to search for similar ip address
761 			std::string compareIp = m_GatewayIp.substr(0, (m_GatewayIp.length() - 3));
762 			_log.Log(LOG_STATUS, "XiaomiGateway (ID=%d): XiaomiGateway IP address starts with: %s", m_HwdID, compareIp.c_str());
763 
764 			std::vector<std::string> ip_addrs;
765 			if (XiaomiGateway::get_local_ipaddr(ip_addrs) > 0)
766 			{
767 				for (const std::string &addr : ip_addrs)
768 				{
769 					std::size_t found = addr.find(compareIp);
770 					if (found != std::string::npos)
771 					{
772 						m_LocalIp = addr;
773 						_log.Log(LOG_STATUS, "XiaomiGateway (ID=%d): Using %s for local IP address.", m_HwdID, m_LocalIp.c_str());
774 						break;
775 					}
776 				}
777 			}
778 			else
779 			{
780 				_log.Log(LOG_STATUS, "XiaomiGateway (ID=%d): Could not find local IP address with ifaddrs", m_HwdID);
781 			}
782 		}
783 		catch (std::exception& e) {
784 			_log.Log(LOG_STATUS, "XiaomiGateway (ID=%d): Could not find local IP address with ifaddrs: %s", m_HwdID, e.what());
785 		}
786 	}
787 
788 	XiaomiGateway::xiaomi_udp_server udp_server(io_service, m_HwdID, m_GatewayIp, m_LocalIp, m_ListenPort9898, m_OutputMessage, m_IncludeVoltage, this);
789 	boost::thread bt;
790 	if (m_ListenPort9898) {
791 		bt = boost::thread(boost::bind(&boost::asio::io_service::run, &io_service));
792 		SetThreadName(bt.native_handle(), "XiaomiGatewayIO");
793 	}
794 
795 	int sec_counter = 0;
796 	while (!IsStopRequested(1000))
797 	{
798 		sec_counter++;
799 		if (sec_counter % 12 == 0) {
800 			m_LastHeartbeat = mytime(NULL);
801 		}
802 		if (sec_counter % 60 == 0)
803 		{
804 			//_log.Log(LOG_STATUS, "sec_counter %d", sec_counter);
805 		}
806 	}
807 	io_service.stop();
808 	RemoveFromGatewayList();
809 	_log.Log(LOG_STATUS, "XiaomiGateway (ID=%d): stopped", m_HwdID);
810 }
811 
GetGatewayKey()812 std::string XiaomiGateway::GetGatewayKey()
813 {
814 #ifdef WWW_ENABLE_SSL
815 	const unsigned char *key = (unsigned char *)m_GatewayPassword.c_str();
816 	unsigned char iv[AES_BLOCK_SIZE] = { 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58, 0x56, 0x2e };
817 	std::string token = XiaomiGatewayTokenManager::GetInstance().GetToken(m_GatewayIp);
818 	unsigned char *plaintext = (unsigned char *)token.c_str();
819 	unsigned char ciphertext[128];
820 
821 	AES_KEY encryption_key;
822 	AES_set_encrypt_key(key, 128, &(encryption_key));
823 	AES_cbc_encrypt((unsigned char *)plaintext, ciphertext, sizeof(plaintext) * 8, &encryption_key, iv, AES_ENCRYPT);
824 
825 	char gatewaykey[128];
826 	for (int i = 0; i < 16; i++)
827 	{
828 		sprintf(&gatewaykey[i * 2], "%02X", ciphertext[i]);
829 	}
830 #ifdef _DEBUG
831 	_log.Log(LOG_STATUS, "XiaomiGateway: GetGatewayKey Password - %s", m_GatewayPassword.c_str());
832 	_log.Log(LOG_STATUS, "XiaomiGateway: GetGatewayKey key - %s", gatewaykey);
833 #endif
834 	return gatewaykey;
835 #else
836 	_log.Log(LOG_ERROR, "XiaomiGateway: GetGatewayKey NO SSL AVAILABLE");
837 	return std::string("");
838 #endif
839 }
840 
GetShortID(const std::string & nodeid)841 unsigned int XiaomiGateway::GetShortID(const std::string & nodeid)
842 {
843 	if (nodeid.length() < 12) {
844 		_log.Log(LOG_ERROR, "XiaomiGateway: Node ID %s is too short", nodeid.c_str());
845 		return -1;
846 	}
847 	std::string str;
848 	if (nodeid.length() < 14) {
849 		// Gateway
850 		str = nodeid.substr(4, 8);
851 	}
852 	else {
853 		// Device
854 		str = nodeid.substr(6, 8);
855 	}
856 	unsigned int sID;
857 	std::stringstream ss;
858 	ss << std::hex << str.c_str();
859 	ss >> sID;
860 	return sID;
861 }
862 
xiaomi_udp_server(boost::asio::io_service & io_service,int m_HwdID,const std::string & gatewayIp,const std::string & localIp,const bool listenPort9898,const bool outputMessage,const bool includeVoltage,XiaomiGateway * parent)863 XiaomiGateway::xiaomi_udp_server::xiaomi_udp_server(boost::asio::io_service& io_service, int m_HwdID, const std::string &gatewayIp, const std::string &localIp, const bool listenPort9898, const bool outputMessage, const bool includeVoltage, XiaomiGateway *parent)
864 	: socket_(io_service, boost::asio::ip::udp::v4())
865 {
866 	m_HardwareID = m_HwdID;
867 	m_XiaomiGateway = parent;
868 	m_gatewayip = gatewayIp;
869 	m_localip = localIp;
870 	m_OutputMessage = outputMessage;
871 	m_IncludeVoltage = includeVoltage;
872 	if (listenPort9898) {
873 		try {
874 			socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
875 			if (m_localip != "") {
876 				boost::system::error_code ec;
877 				boost::asio::ip::address listen_addr = boost::asio::ip::address::from_string(m_localip, ec);
878 				boost::asio::ip::address mcast_addr = boost::asio::ip::address::from_string("224.0.0.50", ec);
879 				boost::asio::ip::udp::endpoint listen_endpoint(mcast_addr, 9898);
880 
881 				socket_.bind(boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 9898));
882 				std::shared_ptr<std::string> message(new std::string("{\"cmd\":\"whois\"}"));
883 				boost::asio::ip::udp::endpoint remote_endpoint;
884 				remote_endpoint = boost::asio::ip::udp::endpoint(mcast_addr, 4321);
885 				socket_.send_to(boost::asio::buffer(*message), remote_endpoint);
886 				socket_.set_option(boost::asio::ip::multicast::join_group(mcast_addr.to_v4(), listen_addr.to_v4()), ec);
887 				socket_.bind(listen_endpoint, ec);
888 			}
889 			else {
890 				socket_.bind(boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 9898));
891 				std::shared_ptr<std::string> message(new std::string("{\"cmd\":\"whois\"}"));
892 				boost::asio::ip::udp::endpoint remote_endpoint;
893 				remote_endpoint = boost::asio::ip::udp::endpoint(boost::asio::ip::address::from_string("224.0.0.50"), 4321);
894 				socket_.send_to(boost::asio::buffer(*message), remote_endpoint);
895 				socket_.set_option(boost::asio::ip::multicast::join_group(boost::asio::ip::address::from_string("224.0.0.50")));
896 			}
897 		}
898 		catch (const boost::system::system_error& ex) {
899 			_log.Log(LOG_ERROR, "XiaomiGateway: %s", ex.code().category().name());
900 			m_XiaomiGateway->StopHardware();
901 			return;
902 		}
903 		start_receive();
904 	}
905 	else {
906 	}
907 }
908 
~xiaomi_udp_server()909 XiaomiGateway::xiaomi_udp_server::~xiaomi_udp_server()
910 {
911 }
912 
start_receive()913 void XiaomiGateway::xiaomi_udp_server::start_receive()
914 {
915 	//_log.Log(LOG_STATUS, "start_receive");
916 	memset(&data_[0], 0, sizeof(data_));
917 	socket_.async_receive_from(boost::asio::buffer(data_, max_length), remote_endpoint_, boost::bind(&xiaomi_udp_server::handle_receive, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
918 }
919 
handle_receive(const boost::system::error_code & error,std::size_t bytes_recvd)920 void XiaomiGateway::xiaomi_udp_server::handle_receive(const boost::system::error_code & error, std::size_t bytes_recvd)
921 {
922 	if (!error || error == boost::asio::error::message_size)
923 	{
924 		XiaomiGateway * TrueGateway = m_XiaomiGateway->GatewayByIp(remote_endpoint_.address().to_v4().to_string());
925 
926 		if (!TrueGateway)
927 		{
928 			_log.Log(LOG_ERROR, "XiaomiGateway: received data from  unregisted gateway!");
929 			start_receive();
930 			return;
931 		}
932 #ifdef _DEBUG
933 		_log.Log(LOG_STATUS, data_);
934 #endif
935 		Json::Value root;
936 		bool showmessage = true;
937 		bool ret = ParseJSon(data_, root);
938 		if ((!ret) || (!root.isObject()))
939 		{
940 			_log.Log(LOG_ERROR, "XiaomiGateway: invalid data received!");
941 			start_receive();
942 			return;
943 		}
944 		else {
945 			std::string cmd = root["cmd"].asString();
946 			std::string model = root["model"].asString();
947 			std::string sid = root["sid"].asString();
948 			std::string data = root["data"].asString();
949 			int unitcode = 1;
950 			if ((cmd == COMMAND_REPORT) || (cmd == COMMAND_READ_ACK) || (cmd == COMMAND_HEARTBEAT))
951 			{
952 				Json::Value root2;
953 				ret = ParseJSon(data.c_str(), root2);
954 				if ((ret) || (!root2.isObject()))
955 				{
956 					_eSwitchType type = STYPE_END;
957 					std::string name = NAME_UNKNOWN_XIAOMI;
958 					if (model == MODEL_SENSOR_MOTION_XIAOMI) {
959 						type = STYPE_Motion;
960 						name = NAME_SENSOR_MOTION_XIAOMI;
961 					}
962 					else if (model == MODEL_SENSOR_MOTION_AQARA) {
963 						type = STYPE_Motion;
964 						name = NAME_SENSOR_MOTION_AQARA;
965 					}
966 					else if ((model == MODEL_SELECTOR_WIRELESS_SINGLE_1) || (model == MODEL_SELECTOR_WIRELESS_SINGLE_2)) {
967 						type = STYPE_Selector;
968 						name = NAME_SELECTOR_WIRELESS_SINGLE;
969 					}
970 					else if (model == MODEL_SELECTOR_WIRELESS_SINGLE_SQUARE) {
971 						type = STYPE_Selector;
972 						name = NAME_SELECTOR_WIRELESS_SINGLE_SQUARE;
973 					}
974 					else if (model == MODEL_SELECTOR_WIRELESS_SINGLE_SMART_PUSH) {
975 						type = STYPE_Selector;
976 						name = NAME_SELECTOR_WIRELESS_SINGLE_SMART_PUSH;
977 					}
978 					else if ((model == MODEL_SENSOR_DOOR) || (model == MODEL_SENSOR_DOOR_AQARA)) {
979 						type = STYPE_Contact;
980 						name = NAME_SENSOR_DOOR;
981 					}
982 					else if (model == MODEL_ACT_ONOFF_PLUG) {
983 						type = STYPE_OnOff;
984 						name = NAME_ACT_ONOFF_PLUG;
985 					}
986 					else if (model == MODEL_ACT_ONOFF_PLUG_WALL_1 || model == MODEL_ACT_ONOFF_PLUG_WALL_2) {
987 						type = STYPE_OnOff;
988 						name = NAME_ACT_ONOFF_PLUG_WALL;
989 					}
990 					else if (model == MODEL_SENSOR_TEMP_HUM_V1) {
991 						name = NAME_SENSOR_TEMP_HUM_V1;
992 					}
993 					else if (model == MODEL_SENSOR_TEMP_HUM_AQARA) {
994 						name = NAME_SENSOR_TEMP_HUM_AQARA;
995 					}
996 					else if (model == MODEL_SELECTOR_CUBE_V1) {
997 						type = STYPE_Selector;
998 						name = NAME_SELECTOR_CUBE_V1;
999 					}
1000 					else if (model == MODEL_SELECTOR_CUBE_AQARA) {
1001 						type = STYPE_Selector;
1002 						name = NAME_SELECTOR_CUBE_AQARA;
1003 					}
1004 					else if (model == MODEL_SENSOR_VIBRATION) {
1005 						type = STYPE_Selector;
1006 						name = NAME_SENSOR_VIBRATION;
1007 					}
1008 					else if (model == MODEL_GATEWAY_1 || model == MODEL_GATEWAY_2 || model == MODEL_GATEWAY_3) {
1009 						name = NAME_GATEWAY;
1010 					}
1011 					else if (model == MODEL_SELECTOR_WIRED_WALL_SINGLE_1 || model == MODEL_SELECTOR_WIRED_WALL_SINGLE_2 || model == MODEL_SELECTOR_WIRED_WALL_SINGLE_3) {
1012 						type = STYPE_END; //type = STYPE_OnOff; // TODO: fix this hack
1013 						name = NAME_SELECTOR_WIRED_WALL_SINGLE;
1014 					}
1015 					else if (model == MODEL_SELECTOR_WIRED_WALL_DUAL_1 || model == MODEL_SELECTOR_WIRED_WALL_DUAL_2 || model == MODEL_SELECTOR_WIRED_WALL_DUAL_3) {
1016 						type = STYPE_END; //type = STYPE_OnOff; // TODO: fix this hack
1017 						name = NAME_SELECTOR_WIRED_WALL_DUAL;
1018 					}
1019 					else if (model == MODEL_SELECTOR_WIRELESS_WALL_SINGLE_1 || model == MODEL_SELECTOR_WIRELESS_WALL_SINGLE_2) {
1020 						type = STYPE_Selector;
1021 						name = NAME_SELECTOR_WIRELESS_WALL_SINGLE;
1022 					}
1023 					else if (model == MODEL_SELECTOR_WIRELESS_WALL_DUAL_1 || model == MODEL_SELECTOR_WIRELESS_WALL_DUAL_2) {
1024 						type = STYPE_Selector;
1025 						name = NAME_SELECTOR_WIRELESS_WALL_DUAL;
1026 					}
1027 					else if (model == MODEL_SENSOR_SMOKE) {
1028 						type = STYPE_SMOKEDETECTOR;
1029 						name = NAME_SENSOR_SMOKE;
1030 					}
1031 					else if (model == MODEL_SENSOR_GAS) {
1032 						type = STYPE_SMOKEDETECTOR;
1033 						name = NAME_SENSOR_GAS;
1034 					}
1035 					else if (model == MODEL_SENSOR_WATER) {
1036 						type = STYPE_SMOKEDETECTOR;
1037 						name = NAME_SENSOR_WATER;
1038 					}
1039 					else if (model == MODEL_ACT_BLINDS_CURTAIN) {
1040 						type = STYPE_BlindsPercentage;
1041 						name = NAME_ACT_BLINDS_CURTAIN;
1042 					}
1043 
1044 					std::string voltage = root2["voltage"].asString();
1045 					int battery = 255;
1046 					if (voltage != "" && voltage != "3600") {
1047 						battery = ((atoi(voltage.c_str()) - 2200) / 10);
1048 					}
1049 					if (type != STYPE_END)
1050 					{
1051 						std::string status = root2["status"].asString();
1052 						std::string no_close = root2["no_close"].asString();
1053 						std::string no_motion = root2["no_motion"].asString();
1054 						// Aqara's Wireless switch reports per channel
1055 						std::string aqara_wireless1 = root2[NAME_CHANNEL_0].asString();
1056 						std::string aqara_wireless2 = root2[NAME_CHANNEL_1].asString();
1057 						std::string aqara_wireless3 = root2["dual_channel"].asString();
1058 						// Smart plug usage
1059 						std::string load_power = root2["load_power"].asString();
1060 						std::string power_consumed = root2["power_consumed"].asString();
1061 						// Smoke or Gas Detector
1062 						std::string density = root2["density"].asString();
1063 						std::string alarm = root2["alarm"].asString();
1064 						// Aqara motion sensor
1065 						std::string lux = root2["lux"].asString();
1066 						// Curtain
1067 						std::string curtain = root2["curtain_level"].asString();
1068 						bool on = false;
1069 						int level = -1;
1070 						if (model == MODEL_SELECTOR_WIRELESS_SINGLE_1) {
1071 							level = 0;
1072 						}
1073 						else if (model == MODEL_SENSOR_SMOKE || model == MODEL_SENSOR_GAS|| model == MODEL_SENSOR_WATER || model == MODEL_SELECTOR_CUBE_AQARA) {
1074 							if (battery != 255 && (model ==  MODEL_SENSOR_WATER || model == MODEL_SELECTOR_CUBE_AQARA)) {
1075 								level = 0;
1076 							}
1077 							if ((alarm == "1") || (alarm == "2") || (status == STATE_WATER_LEAK_YES)) {
1078 								level = 0;
1079 								on = true;
1080 							}
1081 							else if ((alarm == "0") || (status == STATE_WATER_LEAK_NO) || (status == "iam")) {
1082 								level = 0;
1083 							}
1084 							if (density != "")
1085 								level = atoi(density.c_str());
1086 						}
1087 						if ((status == STATE_MOTION_YES) || (status == STATE_OPEN) || (status == "no_close") || (status == STATE_ON) || (no_close != "")) {
1088 							level = 0;
1089 							on = true;
1090 						}
1091 						else if ((status == STATE_MOTION_NO) || (status == STATE_CLOSE) || (status == STATE_OFF) || (no_motion != "")) {
1092 							level = 0;
1093 							on = false;
1094 						}
1095 						else if ((status == "click") || (status == "flip90") || (aqara_wireless1 == "click") || (status == "tilt")) {
1096 							level = 10;
1097 							on = true;
1098 						}
1099 						else if ((status == "double_click") || (status == "flip180") || (aqara_wireless2 == "click") || (status == "shake") || (status == "vibrate") ||
1100 							(name == "Xiaomi Wireless Single Wall Switch" && aqara_wireless1 == "double_click")) {
1101 							level = 20;
1102 							on = true;
1103 						}
1104 						else if ((status == "long_click_press") || (status == "move") || (aqara_wireless3 == "both_click") ||
1105 							(name == "Xiaomi Wireless Single Wall Switch" && aqara_wireless1 == "long_click")) {
1106 							level = 30;
1107 							on = true;
1108 						}
1109 						else if ((status == "tap_twice") || (status == "long_click_release") ||
1110 							(name == "Xiaomi Wireless Dual Wall Switch" && aqara_wireless1 == "double_click")) {
1111 							level = 40;
1112 							on = true;
1113 						}
1114 						else if ((status == "shake_air") || (aqara_wireless2 == "double_click")) {
1115 							level = 50;
1116 							on = true;
1117 						}
1118 						else if ((status == "swing") || (aqara_wireless3 == "double_both_click")) {
1119 							level = 60;
1120 							on = true;
1121 						}
1122 						else if ((status == "alert") || (name == "Xiaomi Wireless Dual Wall Switch" && aqara_wireless1 == "long_click")) {
1123 							level = 70;
1124 							on = true;
1125 						}
1126 						else if ((status == "free_fall") || (aqara_wireless2 == "long_click")) {
1127 							level = 80;
1128 							on = true;
1129 						}
1130 						else if (aqara_wireless3 == "long_both_click") {
1131 							level = 90;
1132 							on = true;
1133 						}
1134 						std::string rotate = root2["rotate"].asString();
1135 						if (rotate != "") {
1136 							int amount = atoi(rotate.c_str());
1137 							if (amount > 0) {
1138 								level = 90;
1139 							}
1140 							else {
1141 								level = 100;
1142 							}
1143 							on = true;
1144 							TrueGateway->InsertUpdateCubeText(sid.c_str(), name, rotate.c_str());
1145 							TrueGateway->InsertUpdateSwitch(sid.c_str(), name, on, type, unitcode, level, cmd, "", "", battery);
1146 						}
1147 						else {
1148 							if (model == MODEL_ACT_ONOFF_PLUG || model == MODEL_ACT_ONOFF_PLUG_WALL_1 || model == MODEL_ACT_ONOFF_PLUG_WALL_2) {
1149 								sleep_milliseconds(100); // Need to sleep here as the gateway will send 2 update messages, and need time for the database to update the state so that the event is not triggered twice
1150 								TrueGateway->InsertUpdateSwitch(sid.c_str(), name, on, type, unitcode, level, cmd, load_power, power_consumed, battery);
1151 							}
1152 							else if ((model == MODEL_ACT_BLINDS_CURTAIN) && (curtain != "")) {
1153 								level = atoi(curtain.c_str());
1154 								TrueGateway->InsertUpdateSwitch(sid.c_str(), name, on, type, unitcode, level, cmd, "", "", battery);
1155 							}
1156 							else {
1157 								if (level > -1) { // This should stop false updates when empty 'data' is received
1158 									TrueGateway->InsertUpdateSwitch(sid.c_str(), name, on, type, unitcode, level, cmd, "", "", battery);
1159 								}
1160 								if (lux != "") {
1161 									TrueGateway->InsertUpdateLux(sid.c_str(), name, atoi(lux.c_str()), battery);
1162 								}
1163 								if (voltage != "" && m_IncludeVoltage) {
1164 									TrueGateway->InsertUpdateVoltage(sid.c_str(), name, atoi(voltage.c_str()));
1165 								}
1166 							}
1167 						}
1168 					}
1169 					else if ((name == NAME_SELECTOR_WIRED_WALL_SINGLE) || (name == NAME_SELECTOR_WIRED_WALL_DUAL))
1170 					{
1171 						// Aqara wired dual switch, bidirectional communication support
1172 						type = STYPE_OnOff; // TODO: Needs to be set above but need different way of executing this code without hack
1173 						std::string aqara_wired1 = root2[NAME_CHANNEL_0].asString();
1174 						std::string aqara_wired2 = root2[NAME_CHANNEL_1].asString();
1175 						bool state = (aqara_wired1 == STATE_ON) || (aqara_wired2 == STATE_ON);
1176 
1177 						unitcode = XiaomiUnitCode::SELECTOR_WIRED_WALL_SINGLE;
1178 						if (name == NAME_SELECTOR_WIRED_WALL_SINGLE) {
1179 							unitcode = XiaomiUnitCode::SELECTOR_WIRED_WALL_SINGLE;
1180 						}
1181 						else {
1182 							unitcode = XiaomiUnitCode::SELECTOR_WIRED_WALL_DUAL_CHANNEL_0;
1183 							name = NAME_SELECTOR_WIRED_WALL_DUAL_CHANNEL_0;
1184 						}
1185 						if (aqara_wired1 != "") {
1186 							TrueGateway->InsertUpdateSwitch(sid.c_str(), name, state, type, unitcode, 0, cmd, "", "", battery);
1187 						}
1188 						else if (aqara_wired2 != "") {
1189 							unitcode = XiaomiUnitCode::SELECTOR_WIRED_WALL_DUAL_CHANNEL_1;
1190 							name = NAME_SELECTOR_WIRED_WALL_DUAL_CHANNEL_1;
1191 							TrueGateway->InsertUpdateSwitch(sid.c_str(), name, state, type, unitcode, 0, cmd, "", "", battery);
1192 						}
1193 					}
1194 					else if ((name == NAME_SENSOR_TEMP_HUM_V1) || (name == NAME_SENSOR_TEMP_HUM_AQARA))
1195 					{
1196 						std::string temperature = root2["temperature"].asString();
1197 						std::string humidity = root2["humidity"].asString();
1198 						float pressure = 0;
1199 
1200 						if (name == NAME_SENSOR_TEMP_HUM_AQARA) {
1201 							std::string szPressure = root2["pressure"].asString();
1202 							pressure = static_cast<float>(atof(szPressure.c_str())) / 100.0f;
1203 						}
1204 
1205 						if ((!temperature.empty()) && (!humidity.empty()) && (pressure != 0))
1206 						{
1207 							// Temp+Hum+Baro
1208 							float temp = std::stof(temperature) / 100.0f;
1209 							int hum = static_cast<int>((std::stof(humidity) / 100));
1210 							TrueGateway->InsertUpdateTempHumPressure(sid.c_str(), "Xiaomi TempHumBaro", temp, hum, pressure, battery);
1211 						}
1212 						else if ((!temperature.empty()) && (!humidity.empty()))
1213 						{
1214 							// Temp+Hum
1215 							float temp = std::stof(temperature) / 100.0f;
1216 							int hum = static_cast<int>((std::stof(humidity) / 100));
1217 							TrueGateway->InsertUpdateTempHum(sid.c_str(), "Xiaomi TempHum", temp, hum, battery);
1218 						}
1219 						else if (temperature != "") {
1220 							float temp = std::stof(temperature) / 100.0f;
1221 							if (temp < 99) {
1222 								TrueGateway->InsertUpdateTemperature(sid.c_str(), "Xiaomi Temperature", temp, battery);
1223 							}
1224 						}
1225 						else if (humidity != "") {
1226 							int hum = static_cast<int>((std::stof(humidity) / 100));
1227 							if (hum > 1) {
1228 								TrueGateway->InsertUpdateHumidity(sid.c_str(), "Xiaomi Humidity", hum, battery);
1229 							}
1230 						}
1231 					}
1232 					else if (name == NAME_GATEWAY)
1233 					{
1234 						std::string rgb = root2["rgb"].asString();
1235 						std::string illumination = root2["illumination"].asString();
1236 						if (rgb != "") {
1237 							// Only add in the gateway that matches the SID for this hardware.
1238 							if (TrueGateway->GetGatewaySid() == sid)
1239 							{
1240 								std::stringstream ss;
1241 								ss << std::hex << atoi(rgb.c_str());
1242 								std::string hexstring(ss.str());
1243 								if (hexstring.length() == 7) {
1244 									hexstring.insert(0, "0");
1245 								}
1246 								std::string bright_hex = hexstring.substr(0, 2);
1247 								std::stringstream ss2;
1248 								ss2 << std::hex << bright_hex.c_str();
1249 								int brightness = strtoul(bright_hex.c_str(), NULL, 16);
1250 								bool on = false;
1251 								if (rgb != "0") {
1252 									on = true;
1253 								}
1254 								TrueGateway->InsertUpdateRGBGateway(sid.c_str(), name + " (" + TrueGateway->GetGatewayIp() + ")", on, brightness, 0);
1255 								TrueGateway->InsertUpdateLux(sid.c_str(), NAME_GATEWAY_LUX, atoi(illumination.c_str()), 255);
1256 								TrueGateway->InsertUpdateSwitch(sid.c_str(), NAME_GATEWAY_SOUND_ALARM_RINGTONE, false, STYPE_Selector, 3, 0, cmd, "", "", 255);
1257 								TrueGateway->InsertUpdateSwitch(sid.c_str(), NAME_GATEWAY_SOUND_ALARM_CLOCK, false, STYPE_Selector, 4, 0, cmd, "", "", 255);
1258 								TrueGateway->InsertUpdateSwitch(sid.c_str(), NAME_GATEWAY_SOUND_DOORBELL, false, STYPE_Selector, 5, 0, cmd, "", "", 255);
1259 								TrueGateway->InsertUpdateSwitch(sid.c_str(), NAME_GATEWAY_SOUND_MP3, false, STYPE_OnOff, 6, 0, cmd, "", "", 255);
1260 								TrueGateway->InsertUpdateSwitch(sid.c_str(), NAME_GATEWAY_SOUND_VOLUME_CONTROL, false, STYPE_Dimmer, 7, 0, cmd, "", "", 255);
1261 							}
1262 						}
1263 						else {
1264 							// Check for token
1265 							std::string token = root["token"].asString();
1266 							std::string ip = root2["ip"].asString();
1267 
1268 							if ((token != "") && (ip != "")) {
1269 								XiaomiGatewayTokenManager::GetInstance().UpdateTokenSID(ip, token, sid);
1270 								showmessage = false;
1271 							}
1272 						}
1273 					}
1274 					else
1275 					{
1276 						_log.Log(LOG_STATUS, "XiaomiGateway (ID=%d): unhandled model: '%s', name: '%s'", TrueGateway->GetGatewayHardwareID(), model.c_str(), name.c_str());
1277 					}
1278 				}
1279 			}
1280 			else if (cmd == "get_id_list_ack")
1281 			{
1282 				Json::Value root2;
1283 				ret = ParseJSon(data.c_str(), root2);
1284 				if ((ret) || (!root2.isObject()))
1285 				{
1286 					for (int i = 0; i < (int)root2.size(); i++) {
1287 						std::string message = "{\"cmd\" : \"read\",\"sid\":\"";
1288 						message.append(root2[i].asString().c_str());
1289 						message.append("\"}");
1290 						std::shared_ptr<std::string> message1(new std::string(message));
1291 						boost::asio::ip::udp::endpoint remote_endpoint;
1292 						remote_endpoint = boost::asio::ip::udp::endpoint(boost::asio::ip::address::from_string(TrueGateway->GetGatewayIp().c_str()), 9898);
1293 						socket_.send_to(boost::asio::buffer(*message1), remote_endpoint);
1294 					}
1295 				}
1296 				showmessage = false;
1297 			}
1298 			else if (cmd == "iam")
1299 			{
1300 				if (model == MODEL_GATEWAY_1 || model == MODEL_GATEWAY_2 || model == MODEL_GATEWAY_3)
1301 				{
1302 					std::string ip = root["ip"].asString();
1303 					// Only add in the gateway that matches the IP address for this hardware.
1304 					if (ip == TrueGateway->GetGatewayIp())
1305 					{
1306 						_log.Log(LOG_STATUS, "XiaomiGateway: RGB Gateway Detected");
1307 						TrueGateway->InsertUpdateRGBGateway(sid.c_str(), "Xiaomi RGB Gateway (" + ip + ")", false, 0, 100);
1308 						TrueGateway->InsertUpdateSwitch(sid.c_str(), NAME_GATEWAY_SOUND_ALARM_RINGTONE, false, STYPE_Selector, 3, 0, cmd, "", "", 255);
1309 						TrueGateway->InsertUpdateSwitch(sid.c_str(), NAME_GATEWAY_SOUND_ALARM_CLOCK, false, STYPE_Selector, 4, 0, cmd, "", "", 255);
1310 						TrueGateway->InsertUpdateSwitch(sid.c_str(), NAME_GATEWAY_SOUND_DOORBELL, false, STYPE_Selector, 5, 0, cmd, "", "", 255);
1311 						TrueGateway->InsertUpdateSwitch(sid.c_str(), NAME_GATEWAY_SOUND_MP3, false, STYPE_OnOff, 6, 0, cmd, "", "", 255);
1312 						TrueGateway->InsertUpdateSwitch(sid.c_str(), NAME_GATEWAY_SOUND_VOLUME_CONTROL, false, STYPE_Dimmer, 7, 0, cmd, "", "", 255);
1313 
1314 						// Query for list of devices
1315 						std::string message = "{\"cmd\" : \"get_id_list\"}";
1316 						std::shared_ptr<std::string> message2(new std::string(message));
1317 						boost::asio::ip::udp::endpoint remote_endpoint;
1318 						remote_endpoint = boost::asio::ip::udp::endpoint(boost::asio::ip::address::from_string(TrueGateway->GetGatewayIp().c_str()), 9898);
1319 						socket_.send_to(boost::asio::buffer(*message2), remote_endpoint);
1320 					}
1321 				}
1322 				showmessage = false;
1323 			}
1324 			else
1325 			{
1326 				_log.Log(LOG_STATUS, "XiaomiGateway (ID=%d): unknown cmd received: '%s', model: '%s'", TrueGateway->GetGatewayHardwareID(), cmd.c_str(), model.c_str());
1327 			}
1328 		}
1329 		if (showmessage && m_OutputMessage) {
1330 			_log.Log(LOG_STATUS, "%s", data_);
1331 		}
1332 		start_receive();
1333 	}
1334 	else {
1335 		_log.Log(LOG_ERROR, "XiaomiGateway: error in handle_receive '%s'", error.message().c_str());
1336 	}
1337 }
1338 
1339 
GetInstance()1340 XiaomiGateway::XiaomiGatewayTokenManager& XiaomiGateway::XiaomiGatewayTokenManager::GetInstance()
1341 {
1342 	static XiaomiGateway::XiaomiGatewayTokenManager instance;
1343 	return instance;
1344 }
1345 
UpdateTokenSID(const std::string & ip,const std::string & token,const std::string & sid)1346 void XiaomiGateway::XiaomiGatewayTokenManager::UpdateTokenSID(const std::string & ip, const std::string & token, const std::string & sid)
1347 {
1348 	bool found = false;
1349 	std::unique_lock<std::mutex> lock(m_mutex);
1350 	for (unsigned i = 0; i < m_GatewayTokens.size(); i++) {
1351 		if (boost::get<0>(m_GatewayTokens[i]) == ip) {
1352 			boost::get<1>(m_GatewayTokens[i]) = token;
1353 			boost::get<2>(m_GatewayTokens[i]) = sid;
1354 			found = true;
1355 		}
1356 	}
1357 	if (!found) {
1358 		m_GatewayTokens.push_back(boost::make_tuple(ip, token, sid));
1359 	}
1360 
1361 }
1362 
GetToken(const std::string & ip)1363 std::string XiaomiGateway::XiaomiGatewayTokenManager::GetToken(const std::string & ip)
1364 {
1365 	std::string token = "";
1366 	bool found = false;
1367 	std::unique_lock<std::mutex> lock(m_mutex);
1368 	for (unsigned i = 0; i < m_GatewayTokens.size(); i++) {
1369 		if (boost::get<0>(m_GatewayTokens[i]) == ip) {
1370 			token = boost::get<1>(m_GatewayTokens[i]);
1371 		}
1372 	}
1373 	return token;
1374 }
1375 
GetSID(const std::string & ip)1376 std::string XiaomiGateway::XiaomiGatewayTokenManager::GetSID(const std::string & ip)
1377 {
1378 	std::string sid = "";
1379 	bool found = false;
1380 	std::unique_lock<std::mutex> lock(m_mutex);
1381 	for (unsigned i = 0; i < m_GatewayTokens.size(); i++) {
1382 		if (boost::get<0>(m_GatewayTokens[i]) == ip) {
1383 			sid = boost::get<2>(m_GatewayTokens[i]);
1384 		}
1385 	}
1386 	return sid;
1387 }
1388 
DetermineChannel(int32_t unitcode)1389 std::string XiaomiGateway::DetermineChannel(int32_t unitcode)
1390 {
1391 	std::string cmdchannel = "";
1392 	if (unitcode == XiaomiUnitCode::SELECTOR_WIRED_WALL_SINGLE || unitcode == XiaomiUnitCode::ACT_ONOFF_PLUG ||
1393 		unitcode == XiaomiUnitCode::SELECTOR_WIRED_WALL_DUAL_CHANNEL_0) {
1394 		cmdchannel = NAME_CHANNEL_0;
1395 	}
1396 	else if (unitcode == XiaomiUnitCode::SELECTOR_WIRED_WALL_DUAL_CHANNEL_1) {
1397 		cmdchannel = NAME_CHANNEL_1;
1398 	}
1399 	return cmdchannel;
1400 }
1401 
DetermineDevice(int32_t unitcode)1402 std::string XiaomiGateway::DetermineDevice(int32_t unitcode)
1403 {
1404 	std::string cmddevice = "";
1405 	if (unitcode == XiaomiUnitCode::SELECTOR_WIRED_WALL_SINGLE) {
1406 		cmddevice = "ctrl_neutral1";
1407 	}
1408 	else if (unitcode == XiaomiUnitCode::ACT_ONOFF_PLUG) {
1409 		cmddevice = "plug";
1410 	}
1411 	else if (unitcode == XiaomiUnitCode::SELECTOR_WIRED_WALL_DUAL_CHANNEL_0 || unitcode == XiaomiUnitCode::SELECTOR_WIRED_WALL_DUAL_CHANNEL_1) {
1412 		cmddevice = "ctrl_neutral2";
1413 	}
1414 	return cmddevice;
1415 }
1416 
DetermineCommand(uint8_t commandcode)1417 std::string XiaomiGateway::DetermineCommand(uint8_t commandcode)
1418 {
1419 	std::string command = "";
1420 	switch (commandcode) {
1421 		case gswitch_sOff:
1422 			command = STATE_OFF;
1423 			break;
1424 		case gswitch_sOn:
1425 			command = STATE_ON;
1426 			break;
1427 		default:
1428 			command = "unknown command";
1429 			break;
1430 	}
1431 	return command;
1432 }