1 #include "stdafx.h"
2 #include "Thermosmart.h"
3 #include "../main/Helper.h"
4 #include "../main/Logger.h"
5 #include "hardwaretypes.h"
6 #include "../main/localtime_r.h"
7 #include "../main/WebServerHelper.h"
8 #include "../main/RFXtrx.h"
9 #include "../main/SQLHelper.h"
10 #include "../httpclient/HTTPClient.h"
11 #include "../main/mainworker.h"
12 #include "../main/json_helper.h"
13
14 #define round(a) ( int ) ( a + .5 )
15
16 const std::string THERMOSMART_LOGIN_PATH = "https://api.thermosmart.com/login";
17 const std::string THERMOSMART_AUTHORISE_PATH = "https://api.thermosmart.com/oauth2/authorize?response_type=code&client_id=client123&redirect_uri=http://clientapp.com/done";
18 const std::string THERMOSMART_DECISION_PATH = "https://api.thermosmart.com/oauth2/authorize/decision";
19 const std::string THERMOSMART_TOKEN_PATH = "https://username:password@api.thermosmart.com/oauth2/token";
20 const std::string THERMOSMART_ACCESS_PATH = "https://api.thermosmart.com/thermostat/[TID]?access_token=[access_token]";
21 const std::string THERMOSMART_SETPOINT_PATH = "https://api.thermosmart.com/thermostat/[TID]?access_token=[access_token]";
22 const std::string THERMOSMART_SET_PAUZE = "https://api.thermosmart.com/thermostat/[TID]/pause?access_token=[access_token]";
23
24 extern http::server::CWebServerHelper m_webservers;
25
26 #ifdef _DEBUG
27 //#define DEBUG_ThermosmartThermostat_read
28 #endif
29
30 #ifdef DEBUG_ThermosmartThermostat
SaveString2Disk(std::string str,std::string filename)31 void SaveString2Disk(std::string str, std::string filename)
32 {
33 FILE *fOut = fopen(filename.c_str(), "wb+");
34 if (fOut)
35 {
36 fwrite(str.c_str(), 1, str.size(), fOut);
37 fclose(fOut);
38 }
39 }
40 #endif
41 #ifdef DEBUG_ThermosmartThermostat_read
ReadFile(std::string filename)42 std::string ReadFile(std::string filename)
43 {
44 std::ifstream file;
45 std::string sResult = "";
46 file.open(filename.c_str());
47 if (!file.is_open())
48 return "";
49 std::string sLine;
50 while (!file.eof())
51 {
52 getline(file, sLine);
53 sResult += sLine;
54 }
55 file.close();
56 return sResult;
57 }
58 #endif
59
CThermosmart(const int ID,const std::string & Username,const std::string & Password,const int Mode1,const int Mode2,const int Mode3,const int Mode4,const int Mode5,const int Mode6)60 CThermosmart::CThermosmart(const int ID, const std::string &Username, const std::string &Password, const int Mode1, const int Mode2, const int Mode3, const int Mode4, const int Mode5, const int Mode6)
61 {
62 if ((Password == "secret")|| (Password.empty()))
63 {
64 _log.Log(LOG_ERROR, "Thermosmart: Please update your username/password!...");
65 }
66 else
67 {
68 m_UserName = Username;
69 m_Password = Password;
70 stdstring_trim(m_UserName);
71 stdstring_trim(m_Password);
72 }
73 m_HwdID=ID;
74 m_OutsideTemperatureIdx = 0; //use build in
75 m_LastMinute = -1;
76 SetModes(Mode1, Mode2, Mode3, Mode4, Mode5, Mode6);
77 Init();
78 }
79
~CThermosmart(void)80 CThermosmart::~CThermosmart(void)
81 {
82 }
83
SetModes(const int Mode1,const int Mode2,const int Mode3,const int Mode4,const int Mode5,const int Mode6)84 void CThermosmart::SetModes(const int Mode1, const int Mode2, const int Mode3, const int Mode4, const int Mode5, const int Mode6)
85 {
86 m_OutsideTemperatureIdx = Mode1;
87 }
88
Init()89 void CThermosmart::Init()
90 {
91 m_AccessToken = "";
92 m_ThermostatID = "";
93 m_bDoLogin = true;
94 }
95
StartHardware()96 bool CThermosmart::StartHardware()
97 {
98 RequestStart();
99
100 Init();
101 m_LastMinute = -1;
102 //Start worker thread
103 m_thread = std::make_shared<std::thread>(&CThermosmart::Do_Work, this);
104 SetThreadNameInt(m_thread->native_handle());
105 m_bIsStarted=true;
106 sOnConnected(this);
107 return (m_thread != nullptr);
108 }
109
StopHardware()110 bool CThermosmart::StopHardware()
111 {
112 if (m_thread)
113 {
114 RequestStop();
115 m_thread->join();
116 m_thread.reset();
117 }
118 m_bIsStarted=false;
119 return true;
120 }
121
122 #define THERMOSMART_POLL_INTERVAL 30
123
Do_Work()124 void CThermosmart::Do_Work()
125 {
126 _log.Log(LOG_STATUS,"Thermosmart: Worker started...");
127 int sec_counter = THERMOSMART_POLL_INTERVAL-5;
128 while (!IsStopRequested(1000))
129 {
130 sec_counter++;
131 if (sec_counter % 12 == 0) {
132 m_LastHeartbeat=mytime(NULL);
133 }
134 if (sec_counter % THERMOSMART_POLL_INTERVAL == 0)
135 {
136 SendOutsideTemperature();
137 GetMeterDetails();
138 }
139 }
140 Logout();
141
142 _log.Log(LOG_STATUS,"Thermosmart: Worker stopped...");
143 }
144
GetOutsideTemperatureFromDomoticz(float & tvalue)145 bool CThermosmart::GetOutsideTemperatureFromDomoticz(float &tvalue)
146 {
147 if (m_OutsideTemperatureIdx == 0)
148 return false;
149 Json::Value tempjson;
150 m_webservers.GetJSonDevices(tempjson, "", "temp", "ID", std::to_string(m_OutsideTemperatureIdx), "", "", true, false, false, 0, "");
151
152 size_t tsize = tempjson.size();
153 if (tsize < 1)
154 return false;
155
156 Json::Value::const_iterator itt;
157 Json::ArrayIndex rsize = tempjson["result"].size();
158 if (rsize < 1)
159 return false;
160
161 bool bHaveTimeout = tempjson["result"][0]["HaveTimeout"].asBool();
162 if (bHaveTimeout)
163 return false;
164 tvalue = tempjson["result"][0]["Temp"].asFloat();
165 return true;
166 }
167
SendSetPointSensor(const unsigned char Idx,const float Temp,const std::string & defaultname)168 void CThermosmart::SendSetPointSensor(const unsigned char Idx, const float Temp, const std::string &defaultname)
169 {
170 _tThermostat thermos;
171 thermos.subtype=sTypeThermSetpoint;
172 thermos.id1=0;
173 thermos.id2=0;
174 thermos.id3=0;
175 thermos.id4=Idx;
176 thermos.dunit=0;
177 thermos.temp=Temp;
178 sDecodeRXMessage(this, (const unsigned char *)&thermos, "Setpoint", 255);
179 }
180
Login()181 bool CThermosmart::Login()
182 {
183 if (!m_AccessToken.empty())
184 {
185 Logout();
186 }
187 if (m_UserName.empty())
188 return false;
189 m_AccessToken = "";
190 m_ThermostatID = "";
191
192 std::string sURL;
193 std::stringstream sstr;
194 sstr << "username=" << m_UserName << "&password=" << m_Password;
195 std::string szPostdata=sstr.str();
196 std::vector<std::string> ExtraHeaders;
197 std::string sResult;
198
199 //# 1. Login
200
201 sURL = THERMOSMART_LOGIN_PATH;
202 if (!HTTPClient::POST(sURL, szPostdata, ExtraHeaders, sResult))
203 {
204 _log.Log(LOG_ERROR,"Thermosmart: Error login!");
205 return false;
206 }
207
208 #ifdef DEBUG_ThermosmartThermostat
209 SaveString2Disk(sResult, "E:\\thermosmart1.txt");
210 #endif
211
212 //# 2. Get Authorize Dialog
213 sURL = THERMOSMART_AUTHORISE_PATH;
214 stdreplace(sURL, "client123", "api-rob-b130d8f5123bf24b");
215 ExtraHeaders.clear();
216 if (!HTTPClient::GET(sURL, sResult))
217 {
218 _log.Log(LOG_ERROR, "Thermosmart: Error login!");
219 return false;
220 }
221
222 #ifdef DEBUG_ThermosmartThermostat
223 SaveString2Disk(sResult, "E:\\thermosmart2.txt");
224 #endif
225
226 size_t tpos = sResult.find("value=");
227 if (tpos == std::string::npos)
228 {
229 _log.Log(LOG_ERROR, "Thermosmart: Error login!, check username/password");
230 return false;
231 }
232 sResult = sResult.substr(tpos + 7);
233 tpos = sResult.find("\">");
234 if (tpos == std::string::npos)
235 {
236 _log.Log(LOG_ERROR, "Thermosmart: Error login!, check username/password");
237 return false;
238 }
239 std::string TID = sResult.substr(0, tpos);
240
241 //# 3. Authorize (read out transaction_id from the HTML form received in the previous step). transaction_id prevents from XSRF attacks.
242 szPostdata = "transaction_id=" + TID;
243 ExtraHeaders.clear();
244 sURL = THERMOSMART_DECISION_PATH;
245 if (!HTTPClient::POST(sURL, szPostdata, ExtraHeaders, sResult, false))
246 {
247 _log.Log(LOG_ERROR, "Thermosmart: Error login!, check username/password");
248 return false;
249 }
250
251 #ifdef DEBUG_ThermosmartThermostat
252 SaveString2Disk(sResult, "E:\\thermosmart3.txt");
253 #endif
254
255 tpos = sResult.find("code=");
256 if (tpos == std::string::npos)
257 {
258 _log.Log(LOG_ERROR, "Thermosmart: Error login!, check username/password");
259 return false;
260 }
261 std::string CODE = sResult.substr(tpos + 5);
262
263 //# 4. Exchange authorization code for Access token (read out the code from the previous response)
264 szPostdata = "grant_type=authorization_code&code=" + CODE + "&redirect_uri=http://clientapp.com/done";
265 sURL = THERMOSMART_TOKEN_PATH;
266
267 stdreplace(sURL, "username", "api-rob-b130d8f5123bf24b");
268 stdreplace(sURL, "password", "c1d91661eef0bc4fa2ac67fd");
269
270 if (!HTTPClient::POST(sURL, szPostdata, ExtraHeaders, sResult, false))
271 {
272 _log.Log(LOG_ERROR, "Thermosmart: Error login!, check username/password");
273 return false;
274 }
275
276 #ifdef DEBUG_ThermosmartThermostat
277 SaveString2Disk(sResult, "E:\\thermosmart4.txt");
278 #endif
279
280 Json::Value root;
281 bool ret = ParseJSon(sResult, root);
282 if ((!ret) || (!root.isObject()))
283 {
284 _log.Log(LOG_ERROR, "Thermosmart: Invalid/no data received...");
285 return false;
286 }
287
288 if (root["access_token"].empty()||root["thermostat"].empty())
289 {
290 _log.Log(LOG_ERROR, "Thermosmart: No access granted, check username/password...");
291 return false;
292 }
293
294 m_AccessToken = root["access_token"].asString();
295 m_ThermostatID = root["thermostat"].asString();
296
297 _log.Log(LOG_STATUS, "Thermosmart: Login successfull!...");
298
299 m_bDoLogin = false;
300 return true;
301 }
302
Logout()303 void CThermosmart::Logout()
304 {
305 if (m_bDoLogin)
306 return; //we are not logged in
307 m_AccessToken = "";
308 m_ThermostatID = "";
309 m_bDoLogin = true;
310 }
311
312
WriteToHardware(const char * pdata,const unsigned char length)313 bool CThermosmart::WriteToHardware(const char *pdata, const unsigned char length)
314 {
315 const tRBUF *pCmd = reinterpret_cast<const tRBUF *>(pdata);
316 if (pCmd->LIGHTING2.packettype == pTypeLighting2)
317 {
318 //Light command
319
320 int node_id = pCmd->LIGHTING2.id4;
321 bool bIsOn = (pCmd->LIGHTING2.cmnd == light2_sOn);
322 if (node_id == 1)
323 {
324 //Pause Switch
325 SetPauseStatus(bIsOn);
326 return true;
327 }
328 }
329 return false;
330 }
331
GetMeterDetails()332 void CThermosmart::GetMeterDetails()
333 {
334 if (m_UserName.empty() || m_Password.empty() )
335 return;
336 std::string sResult;
337 #ifdef DEBUG_ThermosmartThermostat_read
338 sResult = ReadFile("E:\\thermosmart_getdata.txt");
339 #else
340 if (m_bDoLogin)
341 {
342 if (!Login())
343 return;
344 }
345 std::string sURL = THERMOSMART_ACCESS_PATH;
346 stdreplace(sURL, "[TID]", m_ThermostatID);
347 stdreplace(sURL, "[access_token]", m_AccessToken);
348 if (!HTTPClient::GET(sURL, sResult))
349 {
350 _log.Log(LOG_ERROR, "Thermosmart: Error getting thermostat data!");
351 m_bDoLogin = true;
352 return;
353 }
354
355 #ifdef DEBUG_ThermosmartThermostat
356 SaveString2Disk(sResult, "E:\\thermosmart_getdata.txt");
357 #endif
358 #endif
359 Json::Value root;
360 bool ret = ParseJSon(sResult, root);
361 if ((!ret) || (!root.isObject()))
362 {
363 _log.Log(LOG_ERROR, "Thermosmart: Invalid/no data received...");
364 m_bDoLogin = true;
365 return;
366 }
367
368 if (root["target_temperature"].empty() || root["room_temperature"].empty())
369 {
370 _log.Log(LOG_ERROR, "Thermosmart: Invalid/no data received...");
371 m_bDoLogin = true;
372 return;
373 }
374
375 float temperature;
376 temperature = (float)root["target_temperature"].asFloat();
377 SendSetPointSensor(1, temperature, "target temperature");
378
379 temperature = (float)root["room_temperature"].asFloat();
380 SendTempSensor(2, 255, temperature, "room temperature");
381
382 if (!root["outside_temperature"].empty())
383 {
384 temperature = (float)root["outside_temperature"].asFloat();
385 SendTempSensor(3, 255, temperature, "outside temperature");
386 }
387 if (!root["source"].empty())
388 {
389 std::string actSource = root["source"].asString();
390 bool bPauzeOn = (actSource == "pause");
391 SendSwitch(1, 1, 255, bPauzeOn, 0, "Thermostat Pause");
392 }
393 }
394
SetSetpoint(const int idx,const float temp)395 void CThermosmart::SetSetpoint(const int idx, const float temp)
396 {
397 if (m_bDoLogin)
398 {
399 if (!Login())
400 return;
401 }
402
403 char szTemp[20];
404 sprintf(szTemp, "%.1f", temp);
405 std::string sTemp = szTemp;
406
407 std::string szPostdata = "target_temperature=" + sTemp;
408 std::vector<std::string> ExtraHeaders;
409 std::string sResult;
410
411 std::string sURL = THERMOSMART_SETPOINT_PATH;
412 stdreplace(sURL, "[TID]", m_ThermostatID);
413 stdreplace(sURL, "[access_token]", m_AccessToken);
414 if (!HTTPClient::PUT(sURL, szPostdata, ExtraHeaders, sResult))
415 {
416 _log.Log(LOG_ERROR, "Thermosmart: Error setting thermostat data!");
417 m_bDoLogin = true;
418 return;
419 }
420 SendSetPointSensor(1, temp, "target temperature");
421 }
422
SetPauseStatus(const bool bIsPause)423 void CThermosmart::SetPauseStatus(const bool bIsPause)
424 {
425 if (m_bDoLogin)
426 {
427 if (!Login())
428 return;
429 }
430
431 std::string szPostdata = "{\"pause\":";
432 szPostdata += (bIsPause) ? "true" : "false";
433 szPostdata += "}";
434
435 std::vector<std::string> ExtraHeaders;
436 ExtraHeaders.push_back("Content-Type: application/json");
437 std::string sResult;
438
439 std::string sURL = THERMOSMART_SET_PAUZE;
440 stdreplace(sURL, "[TID]", m_ThermostatID);
441 stdreplace(sURL, "[access_token]", m_AccessToken);
442
443 if (!HTTPClient::POST(sURL, szPostdata, ExtraHeaders, sResult))
444 {
445 _log.Log(LOG_ERROR, "Thermosmart: Error setting Pause status!");
446 m_bDoLogin = true;
447 return;
448 }
449 }
450
SendOutsideTemperature()451 void CThermosmart::SendOutsideTemperature()
452 {
453 float temp;
454 if (!GetOutsideTemperatureFromDomoticz(temp))
455 return;
456 SetOutsideTemp(temp);
457 }
458
SetOutsideTemp(const float temp)459 void CThermosmart::SetOutsideTemp(const float temp)
460 {
461 if (m_bDoLogin)
462 {
463 if (!Login())
464 return;
465 }
466
467 char szTemp[20];
468 sprintf(szTemp, "%.1f", temp);
469 std::string sTemp = szTemp;
470
471 std::string szPostdata = "outside_temperature=" + sTemp;
472 std::vector<std::string> ExtraHeaders;
473 std::string sResult;
474
475 std::string sURL = THERMOSMART_SETPOINT_PATH;
476 stdreplace(sURL, "[TID]", m_ThermostatID);
477 stdreplace(sURL, "[access_token]", m_AccessToken);
478 if (!HTTPClient::PUT(sURL, szPostdata, ExtraHeaders, sResult))
479 {
480 _log.Log(LOG_ERROR, "Thermosmart: Error setting thermostat data!");
481 m_bDoLogin = true;
482 return;
483 }
484 }
485
486