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