1 #include "stdafx.h"
2 #include "MQTT.h"
3 #include "MySensorsMQTT.h"
4 #include "../main/Logger.h"
5 #include "../main/Helper.h"
6 #include <iostream>
7 #include "../main/localtime_r.h"
8 #include "../main/mainworker.h"
9 
10 #define TOPIC_DEFAULT	"MyMQTT"
11 #define TOPIC_IN		"domoticz/in/"
12 #define TOPIC_OUT		"domoticz/out/"
13 
14 extern const char* szTLSVersions[3];
15 
MySensorsMQTT(const int ID,const std::string & Name,const std::string & IPAddress,const unsigned short usIPPort,const std::string & Username,const std::string & Password,const std::string & CAfilename,const int TLS_Version,const int Topics,const bool PreventLoop)16 MySensorsMQTT::MySensorsMQTT(
17 	const int ID,
18 	const std::string &Name,
19 	const std::string &IPAddress, const unsigned short usIPPort,
20 	const std::string &Username, const std::string &Password, const std::string &CAfilename, const int TLS_Version,
21 	const int Topics,
22 	const bool PreventLoop) :
23 	MQTT(
24 		ID,
25 		IPAddress, usIPPort,
26 		Username, Password, CAfilename, TLS_Version,
27 		(int)MQTT::PT_out, (std::string("Domoticz-MySensors") +  std::string(GenerateUUID())).c_str(), PreventLoop),
28 	MyTopicIn(TOPIC_IN),
29 	MyTopicOut(TOPIC_OUT)
30 {
31 
32 	/**
33 	 *
34 	 * There's no way to know the state of the CAfilename storage, so always try to separate
35 	 * out the topic in/out prefixes.
36 	 *
37 	 **/
38 
39 	size_t nextPiece = std::string::npos;
40 	std::string CustomTopicIn = "";
41 	std::string CustomTopicOut = "";
42 
43 	do {
44 		// Locate the last delimiter in the CAfilename string.
45 		nextPiece = m_CAFilename.rfind('#');
46 		if (std::string::npos == nextPiece)
47 		{
48 			// No delimiter; It's just a CA filename.
49 			break;
50 		}
51 
52 		// First delimiter present; Store the MyTopicOut prefix.
53 		CustomTopicOut = m_CAFilename.substr(nextPiece + 1, m_CAFilename.length());
54 		// And remove it from the CAfilename string.
55 		m_CAFilename.erase(nextPiece, m_CAFilename.length());
56 
57 		// Locate the second to last delimiter in the CAfilename string.
58 		nextPiece = m_CAFilename.rfind('#');
59 		if (std::string::npos == nextPiece)
60 		{
61 			// No second to last delimiter? Shouldn't happen.
62 			_log.Log(LOG_ERROR, "MySensorsMQTT: Truncating CAfilename; Stray topic was present.");
63 			break;
64 		}
65 
66 		// Second to last delimiter present; Store the MyTopicIn prefix.
67 		CustomTopicIn = m_CAFilename.substr(nextPiece + 1, m_CAFilename.length());
68 		// And remove it from the CAfilename string.
69 		m_CAFilename.erase(nextPiece, m_CAFilename.length());
70 
71 	} while (0);
72 
73 	switch (Topics) {
74 		case 2:
75 			MyTopicIn = CustomTopicIn;
76 			MyTopicOut = CustomTopicOut;
77 			break;
78 		case 1:
79 			MyTopicIn += Name;
80 			MyTopicOut += Name;
81 			break;
82 		case 0:
83 		default:
84 			MyTopicIn += TOPIC_DEFAULT;
85 			MyTopicOut += TOPIC_DEFAULT;
86 			break;
87 	}
88 	m_TopicInWithoutHash = MyTopicIn;
89 	m_TopicIn = m_TopicInWithoutHash + "/#";
90 	m_TopicOut = MyTopicOut;
91 
92 }
93 
~MySensorsMQTT(void)94 MySensorsMQTT::~MySensorsMQTT(void)
95 {
96 }
97 
StartHardware()98 bool MySensorsMQTT::StartHardware()
99 {
100 	RequestStart();
101 
102 	m_LineReceived.clear();
103 
104 	LoadDevicesFromDatabase();
105 
106 	bool result = MQTT::StartHardware();
107 	StartSendQueue();
108 
109 	return result;
110 }
111 
StopHardware()112 bool MySensorsMQTT::StopHardware()
113 {
114 	StopSendQueue();
115 	return MQTT::StopHardware();
116 }
117 
on_message(const struct mosquitto_message * message)118 void MySensorsMQTT::on_message(const struct mosquitto_message *message)
119 {
120 	std::string topic = message->topic;
121 	std::string qMessage = std::string((char*)message->payload, (char*)message->payload + message->payloadlen);
122 
123 	_log.Log(LOG_NORM, "MySensorsMQTT: Topic: %s, Message: %s", topic.c_str(), qMessage.c_str());
124 
125 	if (topic.empty() && qMessage.empty())
126 		return;
127 
128 	std::string sMessage = ConvertMessageToMySensorsLine(topic, qMessage);
129 	ParseLine(sMessage);
130 }
131 
ConvertMessageToMySensorsLine(const std::string & topic,const std::string & qMessage)132 std::string MySensorsMQTT::ConvertMessageToMySensorsLine(const std::string &topic, const std::string &qMessage)
133 {
134 	std::string sMessage = topic + "/" + qMessage;
135 	boost::replace_all(sMessage, m_TopicInWithoutHash, "");
136 	boost::replace_all(sMessage, "/", ";");
137 	if (sMessage[0] == ';')
138 	{
139 		sMessage = sMessage.substr(1);
140 	}
141 
142 	return sMessage;
143 }
144 
on_connect(int rc)145 void MySensorsMQTT::on_connect(int rc)
146 {
147 	MQTT::on_connect(rc);
148 
149 	if (m_IsConnected)
150 	{
151 		_log.Log(LOG_STATUS, "MySensorsMQTT: connected to: %s:%d", m_szIPAddress.c_str(), m_usIPPort);
152 
153 		//Request gateway version
154 		std::string sRequest = "0;0;3;0;2;";
155 		WriteInt(sRequest);
156 	}
157 }
158 
SendHeartbeat()159 void MySensorsMQTT::SendHeartbeat()
160 {
161 	//Send a MySensors Heartbeat message to the gateway
162 	std::string sRequest = "0;0;3;0;18;PING";
163 	WriteInt(sRequest);
164 }
165 
WriteInt(const std::string & sendStr)166 void MySensorsMQTT::WriteInt(const std::string &sendStr)
167 {
168 	std::string sTopic;
169 	std::string sPayload;
170 	ConvertMySensorsLineToMessage(sendStr, sTopic, sPayload);
171 
172 	SendMessage(sTopic, sPayload);
173 }
174 
ConvertMySensorsLineToMessage(const std::string & sLine,std::string & sTopic,std::string & sPayload)175 void MySensorsMQTT::ConvertMySensorsLineToMessage(const std::string &sLine, std::string &sTopic, std::string &sPayload)
176 {
177 	if (sLine.size() < 2)
178 	{
179 		return;
180 	}
181 
182 	size_t indexLastSeperator = sLine.find_last_of(';');
183 	if (indexLastSeperator == std::string::npos)
184 	{
185 		return;
186 	}
187 
188 	sTopic = std::string(sLine.substr(0, indexLastSeperator).c_str());
189 	boost::replace_all(sTopic, ";", "/");
190 	sTopic.insert(0, m_TopicOut + "/");
191 
192 	sPayload = std::string(sLine.substr(indexLastSeperator + 1).c_str());
193 	if (!sPayload.empty() &&
194 		sPayload[sPayload.length() - 1] == '\n')
195 	{
196 		sPayload.resize(sPayload.length() - 1);
197 	}
198 }
199