Device.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. #include <sstream>
  2. #include <syslog.h>
  3. #include "Util/JSON.h"
  4. #include "Device.h"
  5. namespace PresenceDetection {
  6. namespace UniFi {
  7. Device::Device(const std::string& hostname, const std::string& username, const std::string& password, const std::string& cookieFile, const std::string& inventoryURL, const std::string& target) :
  8. m_loggedIn(false),
  9. m_hostname(hostname),
  10. m_username(username),
  11. m_password(password),
  12. m_cookieFile(cookieFile),
  13. m_inventoryURL(inventoryURL),
  14. m_target(target),
  15. m_offlineTimeout(120)
  16. {
  17. UpdateDevicesFromInventory();
  18. Start();
  19. }
  20. Device::~Device()
  21. {
  22. Logout();
  23. }
  24. void Device::Start()
  25. {
  26. m_deviceTimer.StartContinuous(5000, static_cast<std::function<void()>>(std::bind(&Device::UpdatePresentDevices, this)));
  27. m_inventoryTimer.StartContinuous(300000, static_cast<std::function<void()>>(std::bind(&Device::UpdateDevicesFromInventory, this)));
  28. }
  29. void Device::Stop()
  30. {
  31. m_deviceTimer.Stop();
  32. }
  33. void Device::Wait()
  34. {
  35. m_deviceTimer.Wait();
  36. }
  37. bool Device::Login()
  38. {
  39. std::stringstream url;
  40. url << "https://" << m_hostname << "/api/login";
  41. Util::JSON json;
  42. json["password"] = m_password;
  43. json["username"] = m_username;
  44. try
  45. {
  46. std::stringstream output;
  47. output << m_httpClient.GetUrlPostContents(url.str(), m_cookieFile, json.dump(), "application/json");
  48. Util::JSON outputJSON = Util::JSON::parse(output);
  49. if (outputJSON["meta"]["rc"] != "ok")
  50. {
  51. std::stringstream error;
  52. error << "Login Failed - " << output.str();
  53. throw std::runtime_error(error.str());
  54. }
  55. }
  56. catch (const std::exception& e)
  57. {
  58. std::stringstream ss;
  59. ss << "UniFi::Device::Login() - Error: " << e.what() << std::endl;
  60. syslog(LOG_ERR, "%s", ss.str().c_str());
  61. std::lock_guard<std::mutex> lock(m_mutex);
  62. m_loggedIn = false;
  63. return m_loggedIn;
  64. }
  65. std::lock_guard<std::mutex> lock(m_mutex);
  66. m_loggedIn = true;
  67. return m_loggedIn;
  68. }
  69. void Device::Logout()
  70. {
  71. std::stringstream url;
  72. url << "https://" << m_hostname << "/logout";
  73. std::lock_guard<std::mutex> lock(m_mutex);
  74. m_loggedIn = false;
  75. try
  76. {
  77. m_httpClient.GetUrlSilent(url.str(), m_cookieFile);
  78. }
  79. catch (const std::exception& e)
  80. {
  81. std::stringstream ss;
  82. ss << "UniFi::Device::Logout() - Error: " << e.what() << std::endl;
  83. syslog(LOG_ERR, "%s", ss.str().c_str());
  84. }
  85. }
  86. void Device::UpdateDevicesFromInventory()
  87. {
  88. try
  89. {
  90. std::string devices = m_httpClient.GetUrlContents(m_inventoryURL);
  91. Util::JSON json = Util::JSON::parse(devices);
  92. m_devices.clear();
  93. for (auto& element : json)
  94. if (element["macaddress"] != "")
  95. {
  96. std::string macAddress = element["macaddress"];
  97. std::transform(macAddress.begin(), macAddress.end(), macAddress.begin(), ::tolower);
  98. m_devices.push_back(macAddress);
  99. }
  100. }
  101. catch (const std::exception& e)
  102. {
  103. std::stringstream ss;
  104. ss << "UniFi::Device::GetDevicesFromInventory() - Error: " << e.what() << std::endl;
  105. syslog(LOG_ERR, "%s", ss.str().c_str());
  106. }
  107. }
  108. void Device::UpdatePresentDevices()
  109. {
  110. bool loggedIn;
  111. {
  112. std::lock_guard<std::mutex> lock(m_mutex);
  113. loggedIn = m_loggedIn;
  114. }
  115. if (!loggedIn)
  116. if (!Login())
  117. return;
  118. std::stringstream url;
  119. url << "https://" << m_hostname << "/api/s/default/stat/sta";
  120. std::time_t timeStamp = std::time(nullptr);
  121. std::vector<std::string> presentDevices;
  122. std::vector<std::string> addedDevices;
  123. std::vector<std::string> removedDevices;
  124. try
  125. {
  126. std::stringstream output;
  127. output << m_httpClient.GetUrlContents(url.str(), m_cookieFile);
  128. Util::JSON json = Util::JSON::parse(output);
  129. if (json["meta"]["rc"] != "ok")
  130. {
  131. std::stringstream error;
  132. error << "Query Failed";
  133. throw std::runtime_error(error.str());
  134. }
  135. for (auto& device : json["data"])
  136. {
  137. std::string macAddress = device["mac"];
  138. int lastSeen = device["last_seen"];
  139. if ((timeStamp - lastSeen) < m_offlineTimeout)
  140. {
  141. if (std::find(m_devices.begin(), m_devices.end(), macAddress) != m_devices.end())
  142. {
  143. if (std::find(m_presentDevices.begin(), m_presentDevices.end(), macAddress) == m_presentDevices.end())
  144. addedDevices.push_back(macAddress);
  145. presentDevices.push_back(macAddress);
  146. }
  147. }
  148. }
  149. }
  150. catch (const std::exception& e)
  151. {
  152. std::stringstream ss;
  153. ss << "UniFi::Device::IsDevicePresent() - Error: " << e.what() << std::endl;
  154. syslog(LOG_ERR, "%s", ss.str().c_str());
  155. Logout();
  156. return;
  157. }
  158. for (std::vector<std::string>::iterator it = m_presentDevices.begin(); it != m_presentDevices.end(); ++it)
  159. if (std::find(presentDevices.begin(), presentDevices.end(), *it) == presentDevices.end())
  160. removedDevices.push_back(*it);
  161. for (std::vector<std::string>::iterator it = addedDevices.begin(); it != addedDevices.end(); ++it)
  162. SendStateChange(true, *it);
  163. for (std::vector<std::string>::iterator it = removedDevices.begin(); it != removedDevices.end(); ++it)
  164. SendStateChange(false, *it);
  165. m_presentDevices.assign(presentDevices.begin(), presentDevices.end());
  166. }
  167. void Device::SendStateChange(bool present, const std::string& macAddress)
  168. {
  169. char sign;
  170. if (present)
  171. sign = '+';
  172. else
  173. sign = '-';
  174. std::stringstream ss;
  175. ss << "UniFi: " << sign << " " << macAddress;
  176. syslog(LOG_INFO, "%s", ss.str().c_str());
  177. std::stringstream url;
  178. url << m_target << "/UniFi/" << sign << "/" << macAddress;
  179. try
  180. {
  181. m_httpClient.GetUrlSilent(url.str());
  182. }
  183. catch (const std::exception& e)
  184. {
  185. std::stringstream ss;
  186. ss << "UniFi::Device::SendStateChange() - Error: " << e.what() << std::endl;
  187. syslog(LOG_ERR, "%s", ss.str().c_str());
  188. }
  189. }
  190. } // namespace UniFi
  191. } // namespace PresenceDetection