Advertisement
Guest User

Untitled

a guest
Aug 1st, 2016
258
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.62 KB | None | 0 0
  1. #include "stdafx.h"
  2. #include "AtagOne.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 "../json/json.h"
  9. #include "../main/RFXtrx.h"
  10. #include "../main/SQLHelper.h"
  11. #include "../httpclient/HTTPClient.h"
  12. #include "../main/mainworker.h"
  13. #include "../json/json.h"
  14.  
  15. extern http::server::CWebServerHelper m_webservers;
  16.  
  17. //Inspidred by https://github.com/kozmoz/atag-one-api
  18.  
  19. #define ATAGONE_URL_LOGIN "https://portal.atag-one.com/Account/Login"
  20. #define ATAGONE_URL_DEVICE_HOME "https://portal.atag-one.com/Home/Index/{0}"
  21. #define ATAGONE_URL_DIAGNOSTICS "https://portal.atag-one.com/Device/LatestReport"
  22. #define ATAGONE_URL_UPDATE_DEVICE_CONTROL "https://portal.atag-one.com/Home/UpdateDeviceControl/?deviceId={0}"
  23. #define ATAGONE_URL_DEVICE_SET_SETPOINT "https://portal.atag-one.com/Home/DeviceSetSetpoint"
  24. #define ATAGONE_URL_AUTOMATICMODE_CONTROL "https://portal.atag-one.com/Home/AutomaticMode/?deviceId={0}"
  25. #define ATAGONE_URL_DEVICE_CONTROL "https://portal.atag-one.com/Home/DeviceControl/{0}"
  26.  
  27. #define ATAGONE_TEMPERATURE_MIN 4
  28. #define ATAGONE_TEMPERATURE_MAX 27
  29.  
  30. #ifdef _DEBUG
  31. #define DEBUG_AtagOneThermostat
  32. #endif
  33.  
  34. #ifdef DEBUG_AtagOneThermostat
  35. void SaveString2Disk(std::string str, std::string filename)
  36. {
  37. FILE *fOut = fopen(filename.c_str(), "wb+");
  38. if (fOut)
  39. {
  40. fwrite(str.c_str(), 1, str.size(), fOut);
  41. fclose(fOut);
  42. }
  43. }
  44. #endif
  45. #ifdef DEBUG_AtagOneThermostat_read
  46. std::string ReadFile(std::string filename)
  47. {
  48. std::ifstream file;
  49. std::string sResult = "";
  50. file.open(filename.c_str());
  51. if (!file.is_open())
  52. return "";
  53. std::string sLine;
  54. while (!file.eof())
  55. {
  56. getline(file, sLine);
  57. sResult += sLine;
  58. }
  59. file.close();
  60. return sResult;
  61. }
  62. #endif
  63.  
  64. CAtagOne::CAtagOne(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)
  65. {
  66. m_UserName = Username;
  67. m_Password = Password;
  68. stdstring_trim(m_UserName);
  69. stdstring_trim(m_Password);
  70. m_HwdID=ID;
  71. m_OutsideTemperatureIdx = 0; //use build in
  72. m_LastMinute = -1;
  73. m_ThermostatID = "";
  74. SetModes(Mode1, Mode2, Mode3, Mode4, Mode5, Mode6);
  75. Init();
  76. }
  77.  
  78. CAtagOne::~CAtagOne(void)
  79. {
  80. }
  81.  
  82. void CAtagOne::SetModes(const int Mode1, const int Mode2, const int Mode3, const int Mode4, const int Mode5, const int Mode6)
  83. {
  84. m_OutsideTemperatureIdx = Mode1;
  85. }
  86.  
  87. void CAtagOne::Init()
  88. {
  89. m_ThermostatID = "";
  90. m_bDoLogin = true;
  91. m_stoprequested = false;
  92. }
  93.  
  94. bool CAtagOne::StartHardware()
  95. {
  96. Init();
  97. m_LastMinute = -1;
  98. //Start worker thread
  99. m_thread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&CAtagOne::Do_Work, this)));
  100. m_bIsStarted=true;
  101. sOnConnected(this);
  102. return (m_thread!=NULL);
  103. }
  104.  
  105. bool CAtagOne::StopHardware()
  106. {
  107. if (m_thread!=NULL)
  108. {
  109. assert(m_thread);
  110. m_stoprequested = true;
  111. m_thread->join();
  112. }
  113. m_bIsStarted=false;
  114. return true;
  115. }
  116.  
  117. std::string GetFirstDeviceID(const std::string &shtml)
  118. {
  119. std::string sResult = shtml;
  120. // Evsdd - Updated string due to webpage change
  121. // Original format: <tr onclick="javascript:changeDeviceAndRedirect('/Home/Index/{0}','6808-1401-3109_15-30-001-544');">
  122. // New format: "/Home/Index/6808-1401-3109_15-30-001-544"
  123. size_t tpos = sResult.find("/Home/Index");
  124. if (tpos == std::string::npos)
  125. return "";
  126. sResult = sResult.substr(tpos);
  127. tpos = sResult.find("x/");
  128. if (tpos == std::string::npos)
  129. return "";
  130. sResult = sResult.substr(tpos + 2);
  131. tpos = sResult.find("\"");
  132. if (tpos == std::string::npos)
  133. return "";
  134. sResult = sResult.substr(0, tpos);
  135. return sResult;
  136. }
  137.  
  138. std::string CAtagOne::GetRequestVerificationToken(const std::string &url)
  139. {
  140. std::string sResult;
  141. #ifdef DEBUG_AtagOneThermostat_read
  142. sResult = ReadFile("~/domoticz/AtagOne_requesttoken.txt");
  143. #else
  144. std::string sURL = url;
  145. stdreplace(sURL,"{0}", m_ThermostatID);
  146.  
  147. if (!HTTPClient::GET(sURL, sResult))
  148. {
  149. _log.Log(LOG_ERROR, "AtagOne: Error requesting token!");
  150. return "";
  151. }
  152. #ifdef DEBUG_AtagOneThermostat
  153. // SaveString2Disk(sResult, "E:\\AtagOne_requesttoken.txt");
  154. SaveString2Disk(sResult, "~/domoticz/AtagOne_requesttoken.txt");
  155.  
  156. #endif
  157. #endif
  158. // <input name="__RequestVerificationToken" type="hidden" value="lFVlMZlt2-YJKAwZWS_K_p3gsQWjZOvBNBZ3lM8io_nFGFL0oRsj4YwQUdqGfyrEqGwEUPmm0FgKH1lPWdk257tuTWQ1" />
  159. size_t tpos = sResult.find("__RequestVerificationToken");
  160. if (tpos == std::string::npos)
  161. {
  162. tpos = sResult.find("changeDeviceAndRedirect");
  163. if (tpos != std::string::npos)
  164. {
  165. m_ThermostatID = GetFirstDeviceID(sResult);
  166. }
  167. return "";
  168. }
  169. sResult = sResult.substr(tpos);
  170. tpos = sResult.find("value=\"");
  171. if (tpos == std::string::npos)
  172. return "";
  173. sResult = sResult.substr(tpos+7);
  174. tpos = sResult.find("\"");
  175. if (tpos == std::string::npos)
  176. return "";
  177. sResult = sResult.substr(0,tpos);
  178. return sResult;
  179. }
  180.  
  181. bool CAtagOne::Login()
  182. {
  183. if (!m_ThermostatID.empty())
  184. {
  185. Logout();
  186. }
  187. if (m_UserName.empty())
  188. return false;
  189. m_ThermostatID = "";
  190.  
  191. std::string sResult;
  192.  
  193. // We need a session (cookie) and a verification token, get them first.
  194. std::string requestVerificationToken = GetRequestVerificationToken(ATAGONE_URL_LOGIN);
  195. if (requestVerificationToken.empty())
  196. {
  197. if (!m_ThermostatID.empty())
  198. {
  199. m_bDoLogin = false;
  200. return true;
  201. }
  202. _log.Log(LOG_ERROR, "AtagOne: Error login!");
  203. return false;
  204. }
  205.  
  206. #ifdef DEBUG_AtagOneThermostat_read
  207. sResult = ReadFile("~/domoticz/AtagOne1.txt");
  208. #else
  209. std::stringstream sstr;
  210. sstr
  211. << "__RequestVerificationToken=" << requestVerificationToken
  212. << "&Email=" << m_UserName
  213. << "&Password=" << m_Password
  214. << "&RememberMe=false";
  215. std::string szPostdata = sstr.str();
  216. std::vector<std::string> ExtraHeaders;
  217.  
  218. //# 1. Login
  219. std::string sURL;
  220. sURL = ATAGONE_URL_LOGIN;
  221. if (!HTTPClient::POST(sURL, szPostdata, ExtraHeaders, sResult))
  222. {
  223. _log.Log(LOG_ERROR, "AtagOne: Error login!");
  224. return false;
  225. }
  226.  
  227. #ifdef DEBUG_AtagOneThermostat
  228. SaveString2Disk(sResult, "~/domoticz/AtagOne1.txt");
  229. #endif
  230. #endif
  231. //# 2. Extract DeviceID
  232. // <tr onclick="javascript:changeDeviceAndRedirect('/Home/Index/{0}','6808-1401-3109_15-30-001-544');">
  233. m_ThermostatID = GetFirstDeviceID(sResult);
  234. if (m_ThermostatID.empty())
  235. {
  236. _log.Log(LOG_ERROR, "AtagOne: Error getting device_id!");
  237. return false;
  238. }
  239. m_bDoLogin = false;
  240. return true;
  241. }
  242.  
  243. void CAtagOne::Logout()
  244. {
  245. if (m_bDoLogin)
  246. return; //we are not logged in
  247. m_ThermostatID = "";
  248. m_bDoLogin = true;
  249. }
  250.  
  251.  
  252. #define AtagOne_POLL_INTERVAL 60
  253.  
  254. void CAtagOne::Do_Work()
  255. {
  256. _log.Log(LOG_STATUS,"AtagOne: Worker started...");
  257. int sec_counter = AtagOne_POLL_INTERVAL-5;
  258. while (!m_stoprequested)
  259. {
  260. sleep_seconds(1);
  261. sec_counter++;
  262. if (sec_counter % 12 == 0) {
  263. m_LastHeartbeat=mytime(NULL);
  264. }
  265. if (sec_counter % AtagOne_POLL_INTERVAL == 0)
  266. {
  267. //SendOutsideTemperature();
  268. GetMeterDetails();
  269. }
  270. }
  271. _log.Log(LOG_STATUS,"AtagOne: Worker stopped...");
  272. }
  273.  
  274. bool CAtagOne::GetOutsideTemperatureFromDomoticz(float &tvalue)
  275. {
  276. if (m_OutsideTemperatureIdx == 0)
  277. return false;
  278. Json::Value tempjson;
  279. std::stringstream sstr;
  280. sstr << m_OutsideTemperatureIdx;
  281. m_webservers.GetJSonDevices(tempjson, "", "temp", "ID", sstr.str(), "", "", true, false, 0, "");
  282.  
  283. size_t tsize = tempjson.size();
  284. if (tsize < 1)
  285. return false;
  286.  
  287. Json::Value::const_iterator itt;
  288. Json::ArrayIndex rsize = tempjson["result"].size();
  289. if (rsize < 1)
  290. return false;
  291.  
  292. bool bHaveTimeout = tempjson["result"][0]["HaveTimeout"].asBool();
  293. if (bHaveTimeout)
  294. return false;
  295. tvalue = tempjson["result"][0]["Temp"].asFloat();
  296. return true;
  297. }
  298.  
  299. bool CAtagOne::WriteToHardware(const char *pdata, const unsigned char length)
  300. {
  301. const tRBUF *pCmd = reinterpret_cast<const tRBUF *>(pdata);
  302. if (pCmd->LIGHTING2.packettype == pTypeLighting2)
  303. {
  304. //Light command
  305.  
  306. int node_id = pCmd->LIGHTING2.id4;
  307. bool bIsOn = (pCmd->LIGHTING2.cmnd == light2_sOn);
  308. if (node_id == 1)
  309. {
  310. //Pause Switch
  311. SetPauseStatus(bIsOn);
  312. return true;
  313. }
  314. }
  315. return false;
  316. }
  317.  
  318. static std::string GetHTMLPageValue(const std::string &hpage, const std::string &svalueLng1, const std::string &svalueLng2, const bool asFloat)
  319. {
  320. std::vector<std::string > m_labels;
  321. if (!svalueLng1.empty())
  322. m_labels.push_back(svalueLng1);
  323. if (!svalueLng2.empty())
  324. m_labels.push_back(svalueLng2);
  325. std::vector<std::string >::const_iterator itt;
  326. // HTML structure of values in page.
  327. // <label class="col-xs-6 control-label">Apparaat alias</label>
  328. // <div class="col-xs-6">
  329. // <p class="form-control-static">CV-ketel</p>
  330. // </div>
  331. size_t tpos;
  332. std::string sstring;
  333. for (itt = m_labels.begin(); itt != m_labels.end(); ++itt)
  334. {
  335. std::string sresult = hpage;
  336. sstring = ">" + *itt + "</label>";
  337. tpos = sresult.find(sstring);
  338. if (tpos==std::string::npos)
  339. continue;
  340. sresult = sresult.substr(tpos + sstring.size());
  341. tpos = sresult.find("<p");
  342. if (tpos == std::string::npos)
  343. continue;
  344. sresult = sresult.substr(tpos+2);
  345. tpos = sresult.find(">");
  346. if (tpos == std::string::npos)
  347. continue;
  348. sresult = sresult.substr(tpos + 1);
  349. tpos = sresult.find("<");
  350. if (tpos == std::string::npos)
  351. continue;
  352. sresult = sresult.substr(0,tpos);
  353. stdstring_trim(sresult);
  354.  
  355. if (asFloat)
  356. stdreplace(sresult, ",", ".");
  357. return sresult;
  358. }
  359. return "";
  360. }
  361.  
  362. void CAtagOne::GetMeterDetails()
  363. {
  364. if (m_UserName.empty() || m_Password.empty() )
  365. return;
  366.  
  367. if (m_bDoLogin)
  368. {
  369. if (!Login())
  370. return;
  371. }
  372.  
  373. std::string sResult;
  374. #ifdef DEBUG_AtagOneThermostat_read
  375. sResult = ReadFile("~/domoticz/AtagOne_getdiag.txt");
  376. #else
  377. std::string sURL = std::string(ATAGONE_URL_DIAGNOSTICS) + "?deviceId=" + CURLEncode::URLEncode(m_ThermostatID);
  378. if (!HTTPClient::GET(sURL, sResult))
  379. {
  380. _log.Log(LOG_ERROR, "AtagOne: Error getting thermostat data!");
  381. m_bDoLogin = true;
  382. return;
  383. }
  384.  
  385. #ifdef DEBUG_AtagOneThermostat
  386. SaveString2Disk(sResult, "~/domoticz/AtagOne_getdiag.txt");
  387. #endif
  388. #endif
  389. //Extract all values from the HTML page, and put them in a json array
  390. Json::Value root;
  391. std::string sret;
  392. sret = GetHTMLPageValue(sResult, "Kamertemperatuur", "Room temperature", true);
  393. if (sret.empty())
  394. {
  395. _log.Log(LOG_ERROR, "AtagOne: Invalid/no data received...");
  396. return;
  397. }
  398. root["roomTemperature"] = static_cast<float>(atof(sret.c_str()));
  399. root["deviceAlias"] = GetHTMLPageValue(sResult, "Apparaat alias", "Device alias", false);
  400. root["latestReportTime"] = GetHTMLPageValue(sResult, "Laatste rapportagetijd", "Latest report time", false);
  401. root["connectedTo"] = GetHTMLPageValue(sResult, "Verbonden met", "Connected to", false);
  402. root["burningHours"] = static_cast<float>(atof(GetHTMLPageValue(sResult, "Branduren", "Burning hours", true).c_str()));
  403. root["boilerHeatingFor"] = GetHTMLPageValue(sResult, "Ketel in bedrijf voor", "Boiler heating for", false);
  404. sret= GetHTMLPageValue(sResult, "Brander status", "Flame status", false);
  405. root["flameStatus"] = ((sret == "Aan") || (sret == "On")) ? true : false;
  406. root["outsideTemperature"] = static_cast<float>(atof(GetHTMLPageValue(sResult, "Buitentemperatuur", "Outside temperature", true).c_str()));
  407. root["dhwSetpoint"] = static_cast<float>(atof(GetHTMLPageValue(sResult, "Setpoint warmwater", "DHW setpoint", true).c_str()));
  408. root["dhwWaterTemperature"] = static_cast<float>(atof(GetHTMLPageValue(sResult, "Warmwatertemperatuur", "DHW water temperature", true).c_str()));
  409. root["chSetpoint"] = static_cast<float>(atof(GetHTMLPageValue(sResult, "Setpoint cv", "CH setpoint", true).c_str()));
  410. root["chWaterTemperature"] = static_cast<float>(atof(GetHTMLPageValue(sResult, "CV-aanvoertemperatuur", "CH water temperature", true).c_str()));
  411. root["chWaterPressure"] = static_cast<float>(atof(GetHTMLPageValue(sResult, "CV-waterdruk", "CH water pressure", true).c_str()));
  412. root["chReturnTemperature"] = static_cast<float>(atof(GetHTMLPageValue(sResult, "CV retourtemperatuur", "CH return temperature", true).c_str()));
  413.  
  414. #ifdef DEBUG_AtagOneThermostat_read
  415. sResult = ReadFile("~/domoticz/AtagOne_gettargetsetpoint.txt");
  416. #else
  417. // We have to do an extra call to get the target temperature.
  418. sURL = ATAGONE_URL_UPDATE_DEVICE_CONTROL;
  419. stdreplace(sURL, "{0}", CURLEncode::URLEncode(m_ThermostatID));
  420. if (!HTTPClient::GET(sURL, sResult))
  421. {
  422. _log.Log(LOG_ERROR, "AtagOne: Error getting target setpoint data!");
  423. m_bDoLogin = true;
  424. return;
  425. }
  426. #ifdef DEBUG_AtagOneThermostat
  427. SaveString2Disk(sResult, "~/domoticz/AtagOne_gettargetsetpoint.txt");
  428. #endif
  429. #endif
  430. Json::Value root2;
  431. Json::Reader jReader;
  432. bool ret = jReader.parse(sResult, root2);
  433. if (!ret)
  434. {
  435. _log.Log(LOG_ERROR, "AtagOne: Invalid/no data received...");
  436. return;
  437. }
  438. if (root2["targetTemp"].empty())
  439. {
  440. _log.Log(LOG_ERROR, "AtagOne: Invalid/no data received...");
  441. return;
  442. }
  443. root["targetTemperature"] = static_cast<float>(atof(root2["targetTemp"].asString().c_str()));
  444. root["currentMode"] = root2["currentMode"].asString();
  445. root["vacationPlanned"] = root2["vacationPlanned"].asBool();
  446.  
  447. //Handle the Values
  448. float temperature;
  449. temperature = (float)root["targetTemperature"].asFloat();
  450. SendSetPointSensor(0, 0, 1, temperature, "Room Setpoint");
  451.  
  452. temperature = (float)root["roomTemperature"].asFloat();
  453. SendTempSensor(2, 255, temperature, "room Temperature");
  454.  
  455. if (!root["outsideTemperature"].empty())
  456. {
  457. temperature = (float)root["outsideTemperature"].asFloat();
  458. SendTempSensor(3, 255, temperature, "outside Temperature");
  459. }
  460.  
  461. //DHW
  462. if (!root["dhwSetpoint"].empty())
  463. {
  464. temperature = (float)root["dhwSetpoint"].asFloat();
  465. SendSetPointSensor(0, 0, 2, temperature, "DHW Setpoint");
  466. }
  467. if (!root["dhwWaterTemperature"].empty())
  468. {
  469. temperature = (float)root["dhwWaterTemperature"].asFloat();
  470. SendTempSensor(4, 255, temperature, "DHW Temperature");
  471. }
  472. //CH
  473. if (!root["chSetpoint"].empty())
  474. {
  475. temperature = (float)root["chSetpoint"].asFloat();
  476. SendSetPointSensor(0, 0, 3, temperature, "CH Setpoint");
  477. }
  478. if (!root["chWaterTemperature"].empty())
  479. {
  480. temperature = (float)root["chWaterTemperature"].asFloat();
  481. SendTempSensor(5, 255, temperature, "CH Temperature");
  482. }
  483. if (!root["chWaterPressure"].empty())
  484. {
  485. float pressure = (float)root["chWaterPressure"].asFloat();
  486. SendPressureSensor(1, 1, 255, pressure, "Pressure");
  487. }
  488. if (!root["chReturnTemperature"].empty())
  489. {
  490. temperature = (float)root["chReturnTemperature"].asFloat();
  491. SendTempSensor(6, 255, temperature, "CH Return Temperature");
  492. }
  493.  
  494. if (!root["currentMode"].empty())
  495. {
  496. std::string actSource = root["currentMode"].asString();
  497. bool bIsScheduleMode = (actSource == "schedule_active");
  498. SendSwitch(1, 1, 255, bIsScheduleMode, 0, "Thermostat Schedule Mode");
  499. }
  500. if (!root["flameStatus"].empty())
  501. {
  502. SendSwitch(2, 1, 255, root["flameStatus"].asBool(), 0, "Flame Status");
  503. }
  504.  
  505. }
  506.  
  507. void CAtagOne::SetSetpoint(const int idx, const float temp)
  508. {
  509. if (idx != 1)
  510. {
  511. _log.Log(LOG_ERROR, "AtagOne: Currently only Room Temperature Setpoint allowed!");
  512. return;
  513. }
  514.  
  515. int rtemp = int(temp*2.0f);
  516. float dtemp = float(rtemp) / 2.0f;
  517. if (
  518. (dtemp<ATAGONE_TEMPERATURE_MIN) ||
  519. (dtemp>ATAGONE_TEMPERATURE_MAX)
  520. )
  521. {
  522. _log.Log(LOG_ERROR, "AtagOne: Temperature should be between %d and %d", ATAGONE_TEMPERATURE_MIN, ATAGONE_TEMPERATURE_MAX);
  523. return;
  524. }
  525. char szTemp[20];
  526. sprintf(szTemp, "%.1f", dtemp);
  527. std::string sTemp = szTemp;
  528.  
  529. // Get updated request verification token first.
  530. std::string requestVerificationToken = GetRequestVerificationToken(ATAGONE_URL_DEVICE_HOME);
  531.  
  532. // https://portal.atag-one.com/Home/DeviceSetSetpoint/6808-1401-3109_15-30-001-544?temperature=18.5
  533. std::string sURL = std::string(ATAGONE_URL_DEVICE_SET_SETPOINT) + "/" + m_ThermostatID + "?temperature=" + sTemp;
  534.  
  535. std::stringstream sstr;
  536. if (!requestVerificationToken.empty())
  537. {
  538. sstr << "__RequestVerificationToken=" << requestVerificationToken;
  539. }
  540. std::string szPostdata = sstr.str();
  541. std::vector<std::string> ExtraHeaders;
  542. std::string sResult;
  543. if (!HTTPClient::POST(sURL, szPostdata, ExtraHeaders, sResult))
  544. {
  545. _log.Log(LOG_ERROR, "AtagOne: Error setting Setpoint!");
  546. return;
  547. }
  548. #ifdef DEBUG_AtagOneThermostat
  549. SaveString2Disk(sResult, "~/domoticz/AtagOne_setsetpoint.txt");
  550. #endif
  551. SendSetPointSensor(0,0,idx, dtemp, "");
  552. }
  553.  
  554. void CAtagOne::SetPauseStatus(const bool bIsPause)
  555. {
  556. }
  557.  
  558. void CAtagOne::SendOutsideTemperature()
  559. {
  560. float temp;
  561. if (!GetOutsideTemperatureFromDomoticz(temp))
  562. return;
  563. SetOutsideTemp(temp);
  564. }
  565.  
  566. void CAtagOne::SetOutsideTemp(const float temp)
  567. {
  568. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement