#include #include #include #include #include "Device.h" namespace PresenceDetection { namespace UniFi { Device::Device(const std::string& hostname, const std::string& username, const std::string& password, const std::string& cookieFile, const std::string& target) : m_loggedIn(false), m_hostname(hostname), m_username(username), m_password(password), m_cookieFile(cookieFile), m_target(target) { m_offlineTimeout = 120; Start(); } Device::~Device() { Logout(); } void Device::Start() { m_timer.StartContinuous(5000, static_cast>(std::bind(&Device::UpdatePresentDevices, this))); } void Device::Stop() { m_timer.Stop(); } void Device::Wait() { m_timer.Wait(); } bool Device::Login() { std::stringstream url; url << "https://" << m_hostname << "/api/login"; std::ostringstream json; boost::property_tree::ptree root; root.put("password", m_password); root.put("username", m_username); boost::property_tree::write_json(json, root); try { std::stringstream output; output << m_httpClient.GetUrlPostContents(url.str(), m_cookieFile, json.str(), "application/json"); boost::property_tree::ptree pt; boost::property_tree::read_json(output, pt); std::string result = pt.get_child("meta").get("rc"); if (result != "ok") { std::stringstream error; error << "Login Failed - " << output.str(); throw std::runtime_error(error.str()); } } catch (const std::exception& e) { std::stringstream ss; ss << "UniFi::Device::Login() - Error: " << e.what() << std::endl; syslog(LOG_ERR, "%s", ss.str().c_str()); std::lock_guard lock(m_mutex); m_loggedIn = false; return m_loggedIn; } std::lock_guard lock(m_mutex); m_loggedIn = true; return m_loggedIn; } void Device::Logout() { std::stringstream url; url << "https://" << m_hostname << "/logout"; std::lock_guard lock(m_mutex); m_loggedIn = false; try { m_httpClient.GetUrlSilent(url.str(), m_cookieFile); } catch (const std::exception& e) { std::stringstream ss; ss << "UniFi::Device::Logout() - Error: " << e.what() << std::endl; syslog(LOG_ERR, "%s", ss.str().c_str()); } } void Device::UpdatePresentDevices() { bool loggedIn; { std::lock_guard lock(m_mutex); loggedIn = m_loggedIn; } if (!loggedIn) if (!Login()) return; std::stringstream url; url << "https://" << m_hostname << "/api/s/default/stat/sta"; std::time_t timeStamp = std::time(nullptr); std::vector presentDevices; std::vector addedDevices; std::vector removedDevices; try { std::stringstream output; output << m_httpClient.GetUrlContents(url.str(), m_cookieFile); boost::property_tree::ptree pt; boost::property_tree::read_json(output, pt); std::string result = pt.get_child("meta").get("rc"); if (result != "ok") { std::stringstream error; error << "Query Failed"; throw std::runtime_error(error.str()); } BOOST_FOREACH(boost::property_tree::ptree::value_type &v, pt.get_child("data")) { std::string macAddress = v.second.get("mac"); int lastSeen = v.second.get("last_seen"); if ((timeStamp - lastSeen) < m_offlineTimeout) { if (std::find(m_presentDevices.begin(), m_presentDevices.end(), macAddress) == m_presentDevices.end()) addedDevices.push_back(macAddress); presentDevices.push_back(macAddress); } } } catch (const std::exception& e) { std::stringstream ss; ss << "UniFi::Device::IsDevicePresent() - Error: " << e.what() << std::endl; syslog(LOG_ERR, "%s", ss.str().c_str()); Logout(); return; } for (std::vector::iterator it = m_presentDevices.begin(); it != m_presentDevices.end(); ++it) if (std::find(presentDevices.begin(), presentDevices.end(), *it) == presentDevices.end()) removedDevices.push_back(*it); for (std::vector::iterator it = addedDevices.begin(); it != addedDevices.end(); ++it) SendStateChange(true, *it); for (std::vector::iterator it = removedDevices.begin(); it != removedDevices.end(); ++it) SendStateChange(false, *it); m_presentDevices.assign(presentDevices.begin(), presentDevices.end()); } void Device::SendStateChange(bool present, const std::string& macAddress) { char sign; if (present) sign = '+'; else sign = '-'; std::stringstream ss; ss << "UniFi: " << sign << " " << macAddress; syslog(LOG_INFO, "%s", ss.str().c_str()); std::stringstream url; url << m_target << "/UniFi/" << sign << "/" << macAddress; try { m_httpClient.GetUrlSilent(url.str()); } catch (const std::exception& e) { std::stringstream ss; ss << "UniFi::Device::SendStateChange() - Error: " << e.what() << std::endl; syslog(LOG_ERR, "%s", ss.str().c_str()); } } } // namespace UniFi } // namespace PresenceDetection