#include #include "Util/JSON.h" #include "Util/Logger.h" #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& inventoryURL, const std::string& target) : m_loggedIn(false), m_hostname(hostname), m_username(username), m_password(password), m_cookieFile(cookieFile), m_inventoryURL(inventoryURL), m_target(target), m_offlineTimeout(120) { UpdateDevicesFromInventory(); Start(); } Device::~Device() { Logout(); } void Device::Start() { m_deviceTimer.StartContinuous(5000, static_cast>(std::bind(&Device::UpdatePresentDevices, this))); m_inventoryTimer.StartContinuous(300000, static_cast>(std::bind(&Device::UpdateDevicesFromInventory, this))); } void Device::Stop() { m_deviceTimer.Stop(); } void Device::Wait() { m_deviceTimer.Wait(); } bool Device::Login() { std::stringstream url; url << "https://" << m_hostname << "/api/login"; Util::JSON json; json["password"] = m_password; json["username"] = m_username; try { std::stringstream output; output << m_httpClient.GetUrlPostContents(url.str(), m_cookieFile, json.dump(), "application/json"); Util::JSON outputJSON = Util::JSON::parse(output); if (outputJSON["meta"]["rc"] != "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; Util::Logger::Log(Util::Logger::Severity::Error, ss.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; Util::Logger::Log(Util::Logger::Severity::Error, ss.str()); } } void Device::UpdateDevicesFromInventory() { try { std::string devices = m_httpClient.GetUrlContents(m_inventoryURL); Util::JSON json = Util::JSON::parse(devices); m_devices.clear(); for (auto& element : json) if (element["macaddress"] != "") { std::string macAddress = element["macaddress"]; std::transform(macAddress.begin(), macAddress.end(), macAddress.begin(), ::tolower); m_devices.push_back(macAddress); } } catch (const std::exception& e) { std::stringstream ss; ss << "UniFi::Device::GetDevicesFromInventory() - Error: " << e.what() << std::endl; Util::Logger::Log(Util::Logger::Severity::Error, ss.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); Util::JSON json = Util::JSON::parse(output); if (json["meta"]["rc"] != "ok") { std::stringstream error; error << "Query Failed"; throw std::runtime_error(error.str()); } for (auto& device : json["data"]) { std::string macAddress = device["mac"]; int lastSeen = device["last_seen"]; if ((timeStamp - lastSeen) < m_offlineTimeout) { if (std::find(m_devices.begin(), m_devices.end(), macAddress) != m_devices.end()) { 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; Util::Logger::Log(Util::Logger::Severity::Error, ss.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; Util::Logger::Log(Util::Logger::Severity::Info, ss.str()); try { m_httpClient.GetUrlSilent(url.str()); } catch (const std::exception& e) { } } } // namespace UniFi } // namespace PresenceDetection