diff --git a/Application/PresenceDetection.cc b/Application/PresenceDetection.cc index 8ec1b88..8965725 100644 --- a/Application/PresenceDetection.cc +++ b/Application/PresenceDetection.cc @@ -3,9 +3,11 @@ #include #include #include +#include #include #include #include "Util/clipp.h" +#include "Util/Device.h" #include "Util/INIh.h" #include "Util/Logger.h" #include "UniFi/Device.h" @@ -62,13 +64,60 @@ int main(int argc, char** argv) } std::string target = iniReader.Get("PresenceDetection", "Target", ""); - if (target.empty()) - throw std::runtime_error("Target directive missing in ini file."); bool unifi = iniReader.GetBoolean("PresenceDetection", "UniFi", false); bool bluetooth = iniReader.GetBoolean("PresenceDetection", "Bluetooth", false); + std::vector devices; + + auto sections = iniReader.Sections(); + for (auto section : sections) + { + if (section != "PresenceDetection" && + section != "UniFi" && + section != "Bluetooth") + { + std::string WifiMac = iniReader.Get(section, "WifiMac", ""); + std::string BluetoothMac = iniReader.Get(section, "BluetoothMac", ""); + + if (WifiMac.empty() && BluetoothMac.empty()) + { + std::stringstream ss; + ss << section << ": No Wifi or Bluetooth Mac address defined" << std::endl; + throw std::runtime_error(ss.str()); + } + + auto device = PresenceDetection::Util::Device(WifiMac, BluetoothMac); + + std::string OnlineURL = iniReader.Get(section, "OnlineURL", ""); + if (!OnlineURL.empty()) + device.SetOnlineURL(OnlineURL); + + std::string WifiOnlineURL = iniReader.Get(section, "WifiOnlineURL", ""); + if (!WifiOnlineURL.empty()) + device.SetWifiOnlineURL(WifiOnlineURL); + + std::string BluetoothOnlineURL = iniReader.Get(section, "BluetoothOnlineURL", ""); + if (!BluetoothOnlineURL.empty()) + device.SetBluetoothOnlineURL(BluetoothOnlineURL); + + std::string OfflineURL = iniReader.Get(section, "OfflineURL", ""); + if (!OfflineURL.empty()) + device.SetOfflineURL(OfflineURL); + + std::string WifiOfflineURL = iniReader.Get(section, "WifiOfflineURL", ""); + if (!WifiOfflineURL.empty()) + device.SetWifiOfflineURL(WifiOfflineURL); + + std::string BluetoothOfflineURL = iniReader.Get(section, "BluetoothOfflineURL", ""); + if (!BluetoothOfflineURL.empty()) + device.SetBluetoothOfflineURL(BluetoothOfflineURL); + + devices.push_back(device); + } + } + std::shared_ptr pUniFiDevice; if (unifi) { @@ -76,6 +125,8 @@ int main(int argc, char** argv) if (hostname.empty()) throw std::runtime_error("UniFi Hostname directive missing in ini file."); + int port = iniReader.GetInteger("UniFi", "Port", 8443); + std::string username = iniReader.Get("UniFi", "Username", ""); if (username.empty()) throw std::runtime_error("UniFi Username directive missing in ini file."); @@ -88,21 +139,21 @@ int main(int argc, char** argv) if (cookiefile.empty()) throw std::runtime_error("UniFi CookieFile directive missing in ini file."); - std::string inventoryURL = iniReader.Get("UniFi", "Inventory", ""); - if (inventoryURL.empty()) - throw std::runtime_error("UniFi Inventory directive missing in ini file."); + int timeout = iniReader.GetInteger("UniFi", "Timeout", 150); - pUniFiDevice = std::make_shared(hostname, username, password, cookiefile, inventoryURL, target); + std::string inventoryURL = iniReader.Get("UniFi", "Inventory", ""); + + pUniFiDevice = std::make_shared(hostname, port, username, password, cookiefile, timeout, inventoryURL, target, devices); } std::shared_ptr pBluetoothDevice; if (bluetooth) { - std::string inventoryURL = iniReader.Get("Bluetooth", "Inventory", ""); - if (inventoryURL.empty()) - throw std::runtime_error("Bluetooth Inventory directive missing in ini file."); + int timeout = iniReader.GetInteger("Bluetooth", "Timeout", 150); - pBluetoothDevice = std::make_shared(inventoryURL, target); + std::string inventoryURL = iniReader.Get("Bluetooth", "Inventory", ""); + + pBluetoothDevice = std::make_shared(timeout, inventoryURL, target, devices); } sigset_t wset; diff --git a/Bluetooth/Device.cpp b/Bluetooth/Device.cpp index f1b0589..a896a8a 100644 --- a/Bluetooth/Device.cpp +++ b/Bluetooth/Device.cpp @@ -9,11 +9,15 @@ namespace PresenceDetection { namespace Bluetooth { -Device::Device(const std::string& inventoryURL, const std::string& target) : +Device::Device(int timeout, const std::string& inventoryURL, const std::string& target, const std::vector& devices) : + m_timeout(timeout), m_inventoryURL(inventoryURL), - m_target(target) + m_target(target), + m_staticDevices(devices) { - UpdateDevicesFromInventory(); + if (!m_inventoryURL.empty()) + UpdateDevicesFromInventory(); + ClearDevices(); Start(); } @@ -35,6 +39,38 @@ void Device::Stop() void Device::Wait() { m_deviceTimer.Wait(); +void Device::ClearDevices() +{ + for (std::vector::iterator it = m_devices.begin(); it != m_devices.end(); ++it) + { + try + { + SendStateChange(false, *it); + } + catch (const std::exception& e) + { + std::stringstream ss; + ss << "Bluetooth::Device::ClearDevices() - Error: " << e.what() << std::endl; + Util::Logger::Log(Util::Logger::Severity::Error, ss.str()); + } + } + + for (std::vector::iterator it = m_staticDevices.begin(); it != m_staticDevices.end(); ++it) + { + if (it->HasBluetoothMac()) + { + try + { + SendStateChange(false, it->BluetoothMac()); + } + catch (const std::exception& e) + { + std::stringstream ss; + ss << "Bluetooth::Device::ClearDevices() - Error: " << e.what() << std::endl; + Util::Logger::Log(Util::Logger::Severity::Error, ss.str()); + } + } + } } void Device::UpdateDevicesFromInventory() @@ -86,6 +122,36 @@ void Device::UpdatePresentDevices() } } + for (std::vector::iterator it = m_staticDevices.begin(); it != m_staticDevices.end(); ++it) + { + if (it->HasBluetoothMac()) + { + try + { + bool pollDevice = false; + if (std::find(m_presentDevices.begin(), m_presentDevices.end(), it->BluetoothMac()) == m_presentDevices.end()) + pollDevice = true; + else + pollDevice = updateOnlineDevices; + + if (pollDevice && Functions::Ping(it->BluetoothMac())) + { + if (std::find(m_presentDevices.begin(), m_presentDevices.end(), it->BluetoothMac()) == m_presentDevices.end()) + SendStateChange(true, it->BluetoothMac()); + presentDevices.push_back(it->BluetoothMac()); + } + + if (!pollDevice) + ignoredDevices.push_back(it->BluetoothMac()); + } + catch (const std::exception& e) + { + std::stringstream ss; + ss << "Bluetooth::Device::UpdatePresentDevices() - Error: " << e.what() << std::endl; + Util::Logger::Log(Util::Logger::Severity::Error, ss.str()); + } + } + } for (std::vector::iterator it = m_presentDevices.begin(); it != m_presentDevices.end(); ++it) if (std::find(presentDevices.begin(), presentDevices.end(), *it) == presentDevices.end()) SendStateChange(false, *it); @@ -105,15 +171,60 @@ void Device::SendStateChange(bool present, const std::string& macAddress) ss << "Bluetooth: " << sign << " " << macAddress; Util::Logger::Log(Util::Logger::Severity::Info, ss.str()); - std::stringstream url; - url << m_target << "/Bluetooth/" << sign << "/" << macAddress; + if (!m_target.empty()) + { + std::stringstream url; + url << m_target << "/Bluetooth/" << sign << "/" << macAddress; - try - { - m_httpClient.GetUrlSilent(url.str()); + try + { + m_httpClient.GetUrlSilent(url.str()); + } + catch (const std::exception& e) + { + std::stringstream ss; + ss << "Bluetooth::Device::SendStateChange() - Error: " << e.what() << std::endl; + Util::Logger::Log(Util::Logger::Severity::Error, ss.str()); + } } - catch (const std::exception& e) + + for (std::vector::iterator it = m_staticDevices.begin(); it != m_staticDevices.end(); ++it) { + if (it->HasBluetoothMac() && it->BluetoothMac() == macAddress) + { + it->SetBluetoothState(present); + + std::vector urls; + + if (present) + { + if (!it->GetWifiState() && it->HasOnlineURL()) + urls.push_back(it->OnlineURL()); + if (it->HasBluetoothOnlineURL()) + urls.push_back(it->BluetoothOnlineURL()); + } + else if (!present) + { + if (!it->GetWifiState() && it->HasOfflineURL()) + urls.push_back(it->OfflineURL()); + if (it->HasBluetoothOfflineURL()) + urls.push_back(it->BluetoothOfflineURL()); + } + + for (auto& url : urls) + { + try + { + m_httpClient.GetUrlSilent(url); + } + catch (const std::exception& e) + { + std::stringstream ss; + ss << "Bluetooth::Device::SendStateChange() - Error: " << e.what() << std::endl; + Util::Logger::Log(Util::Logger::Severity::Error, ss.str()); + } + } + } } } diff --git a/Bluetooth/Device.h b/Bluetooth/Device.h index 6af8799..2706bd2 100644 --- a/Bluetooth/Device.h +++ b/Bluetooth/Device.h @@ -3,6 +3,7 @@ #include #include +#include "Util/Device.h" #include "Util/Timer.h" #include "Util/HttpClient.h" @@ -13,7 +14,7 @@ namespace Bluetooth { class Device { public: - Device(const std::string& inventoryURL, const std::string& target); + Device(int timeout, const std::string& inventoryURL, const std::string& target, const std::vector& devices); ~Device(); void Start(); @@ -21,6 +22,7 @@ public: void Wait(); private: + void ClearDevices(); void UpdateDevicesFromInventory(); void UpdatePresentDevices(); void SendStateChange(bool present, const std::string& macAddress); @@ -32,8 +34,10 @@ private: std::vector m_devices; + int m_timeout; std::string m_inventoryURL; std::string m_target; + std::vector m_staticDevices; std::vector m_presentDevices; }; diff --git a/PresenceDetection.ini b/PresenceDetection.ini index 0a112fa..59fe6c7 100644 --- a/PresenceDetection.ini +++ b/PresenceDetection.ini @@ -5,10 +5,24 @@ Bluetooth = On [UniFi] Hostname = UniFi +Port = Username = Password = CookieFile = /tmp/UniFi_Cookies +Timeout = Inventory = [Bluetooth] +Timeout = Inventory = + +[Device] +WifiMac = +BluetoothMac = +OnlineURL = +WifiOnlineURL = +BluetoothOnlineURL = +OfflineURL = +WifiOfflineURL = +BluetoothOfflineURL = + diff --git a/UniFi/Device.cpp b/UniFi/Device.cpp index 9380a4d..b259505 100644 --- a/UniFi/Device.cpp +++ b/UniFi/Device.cpp @@ -1,3 +1,4 @@ +#include #include #include "Util/JSON.h" #include "Util/Logger.h" @@ -7,17 +8,21 @@ 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) : +Device::Device(const std::string& hostname, int port, const std::string& username, const std::string& password, const std::string& cookieFile, int timeout, const std::string& inventoryURL, const std::string& target, const std::vector& devices) : m_loggedIn(false), m_hostname(hostname), + m_port(port), m_username(username), m_password(password), m_cookieFile(cookieFile), + m_timeout(timeout), m_inventoryURL(inventoryURL), m_target(target), - m_offlineTimeout(120) + m_staticDevices(devices) { - UpdateDevicesFromInventory(); + if (!m_inventoryURL.empty()) + UpdateDevicesFromInventory(); + ClearDevices(); Start(); } @@ -29,23 +34,28 @@ Device::~Device() 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))); + if (!m_inventoryURL.empty()) + m_inventoryTimer.StartContinuous(300000, static_cast>(std::bind(&Device::UpdateDevicesFromInventory, this))); } void Device::Stop() { m_deviceTimer.Stop(); + if (!m_inventoryURL.empty()) + m_inventoryTimer.Stop(); } void Device::Wait() { m_deviceTimer.Wait(); + if (!m_inventoryURL.empty()) + m_inventoryTimer.Stop(); } bool Device::Login() { std::stringstream url; - url << "https://" << m_hostname << "/api/login"; + url << "https://" << m_hostname << ":" << m_port << "/api/login"; Util::JSON json; json["password"] = m_password; @@ -83,7 +93,7 @@ bool Device::Login() void Device::Logout() { std::stringstream url; - url << "https://" << m_hostname << "/logout"; + url << "https://" << m_hostname << ":" << m_port << "/logout"; std::lock_guard lock(m_mutex); m_loggedIn = false; @@ -100,6 +110,40 @@ void Device::Logout() } } +void Device::ClearDevices() +{ + for (std::vector::iterator it = m_devices.begin(); it != m_devices.end(); ++it) + { + try + { + SendStateChange(false, *it); + } + catch (const std::exception& e) + { + std::stringstream ss; + ss << "UniFi::Device::ClearDevices() - Error: " << e.what() << std::endl; + Util::Logger::Log(Util::Logger::Severity::Error, ss.str()); + } + } + + for (std::vector::iterator it = m_staticDevices.begin(); it != m_staticDevices.end(); ++it) + { + if (it->HasWifiMac()) + { + try + { + SendStateChange(false, it->WifiMac()); + } + catch (const std::exception& e) + { + std::stringstream ss; + ss << "UniFi::Device::ClearDevices() - Error: " << e.what() << std::endl; + Util::Logger::Log(Util::Logger::Severity::Error, ss.str()); + } + } + } +} + void Device::UpdateDevicesFromInventory() { try @@ -137,7 +181,7 @@ void Device::UpdatePresentDevices() return; std::stringstream url; - url << "https://" << m_hostname << "/api/s/default/stat/sta"; + url << "https://" << m_hostname << ":" << m_port << "/api/s/default/stat/sta"; std::time_t timeStamp = std::time(nullptr); std::vector presentDevices; @@ -161,17 +205,31 @@ void Device::UpdatePresentDevices() for (auto& device : json["data"]) { std::string macAddress = device["mac"]; + std::transform(macAddress.begin(), macAddress.end(), macAddress.begin(), ::tolower); 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_devices.begin(), m_devices.end(), macAddress) != m_devices.end()) + if ((timeStamp - lastSeen) < m_timeout) { if (std::find(m_presentDevices.begin(), m_presentDevices.end(), macAddress) == m_presentDevices.end()) addedDevices.push_back(macAddress); presentDevices.push_back(macAddress); } } + + for (std::vector::iterator it = m_staticDevices.begin(); it != m_staticDevices.end(); ++it) + { + if ((timeStamp - lastSeen) < m_timeout) + { + if (it->HasWifiMac() && it->WifiMac() == macAddress) + { + 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) @@ -208,13 +266,60 @@ void Device::SendStateChange(bool present, const std::string& macAddress) ss << "UniFi: " << sign << " " << macAddress; Util::Logger::Log(Util::Logger::Severity::Info, ss.str()); + if (!m_target.empty()) + { + std::stringstream url; + url << m_target << "/UniFi/" << sign << "/" << macAddress; - try - { - m_httpClient.GetUrlSilent(url.str()); + try + { + m_httpClient.GetUrlSilent(url.str()); + } + catch (const std::exception& e) + { + std::stringstream ss; + ss << "UniFi::Device::SendStateChange() - Error: " << e.what() << std::endl; + Util::Logger::Log(Util::Logger::Severity::Error, ss.str()); + } } - catch (const std::exception& e) + + for (std::vector::iterator it = m_staticDevices.begin(); it != m_staticDevices.end(); ++it) { + if (it->HasWifiMac() && it->WifiMac() == macAddress) + { + it->SetWifiState(present); + + std::vector urls; + + if (present) + { + if (!it->GetBluetoothState() && it->HasOnlineURL()) + urls.push_back(it->OnlineURL()); + if (it->HasWifiOnlineURL()) + urls.push_back(it->WifiOnlineURL()); + } + else if (!present) + { + if (!it->GetBluetoothState() && it->HasOfflineURL()) + urls.push_back(it->OfflineURL()); + if (it->HasWifiOfflineURL()) + urls.push_back(it->WifiOfflineURL()); + } + + for (auto& url : urls) + { + try + { + m_httpClient.GetUrlSilent(url); + } + catch (const std::exception& e) + { + std::stringstream ss; + ss << "UniFi::Device::SendStateChange() - Error: " << e.what() << std::endl; + Util::Logger::Log(Util::Logger::Severity::Error, ss.str()); + } + } + } } } diff --git a/UniFi/Device.h b/UniFi/Device.h index 599ea90..5df2f82 100644 --- a/UniFi/Device.h +++ b/UniFi/Device.h @@ -4,6 +4,7 @@ #include #include #include +#include "Util/Device.h" #include "Util/Timer.h" #include "Util/HttpClient.h" @@ -14,7 +15,7 @@ namespace UniFi { class Device { public: - 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); + Device(const std::string& hostname, int port, const std::string& username, const std::string& password, const std::string& cookieFile, int timeout, const std::string& inventoryURL, const std::string& target, const std::vector& devices); ~Device(); void Start(); @@ -25,6 +26,7 @@ private: bool Login(); void Logout(); + void ClearDevices(); void UpdateDevicesFromInventory(); void UpdatePresentDevices(); void SendStateChange(bool present, const std::string& macAddress); @@ -37,15 +39,17 @@ private: std::mutex m_mutex; bool m_loggedIn; std::string m_hostname; + int m_port; std::string m_username; std::string m_password; std::string m_cookieFile; std::vector m_devices; + int m_timeout; std::string m_inventoryURL; std::string m_target; - int m_offlineTimeout; + std::vector m_staticDevices; std::vector m_presentDevices; }; diff --git a/Util/Device.cpp b/Util/Device.cpp new file mode 100644 index 0000000..99fcea8 --- /dev/null +++ b/Util/Device.cpp @@ -0,0 +1,167 @@ +#include +#include "Device.h" + + +namespace PresenceDetection { +namespace Util { + +Device::Device(const std::string& wifiMac, const std::string& bluetoothMac) : + m_wifiMac(wifiMac), + m_wifiState(false), + m_bluetoothMac(bluetoothMac), + m_bluetoothState(false) +{ + std::transform(m_wifiMac.begin(), m_wifiMac.end(), m_wifiMac.begin(), ::tolower); + std::transform(m_bluetoothMac.begin(), m_bluetoothMac.end(), m_bluetoothMac.begin(), ::tolower); +} + +Device::Device(const Device &other) : + m_wifiMac(other.m_wifiMac), + m_wifiState(other.m_wifiState), + m_bluetoothMac(other.m_bluetoothMac), + m_bluetoothState(other.m_bluetoothState), + m_onlineURL(other.m_onlineURL), + m_wifiOnlineURL(other.m_wifiOnlineURL), + m_bluetoothOnlineURL(other.m_bluetoothOnlineURL), + m_offlineURL(other.m_offlineURL), + m_wifiOfflineURL(other.m_wifiOfflineURL), + m_bluetoothOfflineURL(other.m_bluetoothOfflineURL) +{ +} + +Device::~Device() +{ +} + +bool Device::HasWifiMac() const +{ + return !m_wifiMac.empty(); +} + +std::string Device::WifiMac() const +{ + return m_wifiMac; +} + +void Device::SetWifiState(bool present) +{ + m_wifiState = present; +} + +bool Device::GetWifiState() const +{ + return m_wifiState; +} + +bool Device::HasBluetoothMac() const +{ + return !m_bluetoothMac.empty(); +} + +std::string Device::BluetoothMac() const +{ + return m_bluetoothMac; +} + +void Device::SetBluetoothState(bool present) +{ + m_bluetoothState = present; +} + +bool Device::GetBluetoothState() const +{ + return m_bluetoothState; +} + +bool Device::HasOnlineURL() const +{ + return !m_onlineURL.empty(); +} + +void Device::SetOnlineURL(const std::string& url) +{ + m_onlineURL = url; +} + +std::string Device::OnlineURL() const +{ + return m_onlineURL; +} + +bool Device::HasWifiOnlineURL() const +{ + return !m_wifiOnlineURL.empty(); +} + +void Device::SetWifiOnlineURL(const std::string& url) +{ + m_wifiOnlineURL = url; +} + +std::string Device::WifiOnlineURL() const +{ + return m_wifiOnlineURL; +} + +bool Device::HasBluetoothOnlineURL() const +{ + return !m_bluetoothOnlineURL.empty(); +} + +void Device::SetBluetoothOnlineURL(const std::string& url) +{ + m_bluetoothOnlineURL = url; +} + +std::string Device::BluetoothOnlineURL() const +{ + return m_bluetoothOnlineURL; +} + +bool Device::HasOfflineURL() const +{ + return !m_offlineURL.empty(); +} + +void Device::SetOfflineURL(const std::string& url) +{ + m_offlineURL = url; +} + +std::string Device::OfflineURL() const +{ + return m_offlineURL; +} + +bool Device::HasWifiOfflineURL() const +{ + return !m_wifiOfflineURL.empty(); +} + +void Device::SetWifiOfflineURL(const std::string& url) +{ + m_wifiOfflineURL = url; +} + +std::string Device::WifiOfflineURL() const +{ + return m_wifiOfflineURL; +} + +bool Device::HasBluetoothOfflineURL() const +{ + return !m_bluetoothOfflineURL.empty(); +} + +void Device::SetBluetoothOfflineURL(const std::string& url) +{ + m_bluetoothOfflineURL = url; +} + +std::string Device::BluetoothOfflineURL() const +{ + return m_bluetoothOfflineURL; +} + +} // namespace Util +} // namespace PresenceDetection diff --git a/Util/Device.h b/Util/Device.h new file mode 100644 index 0000000..f46b348 --- /dev/null +++ b/Util/Device.h @@ -0,0 +1,65 @@ +#ifndef UTIL_DEVICE_H +#define UTIL_DEVICE_H + +#include + + +namespace PresenceDetection { +namespace Util { + +class Device +{ +public: + Device(const std::string& wifiMac, const std::string& bluetoothMac); + Device(const Device &other); + ~Device(); + + bool HasWifiMac() const; + std::string WifiMac() const; + void SetWifiState(bool present); + bool GetWifiState() const; + + bool HasBluetoothMac() const; + std::string BluetoothMac() const; + void SetBluetoothState(bool present); + bool GetBluetoothState() const; + + bool HasOnlineURL() const; + void SetOnlineURL(const std::string& url); + std::string OnlineURL() const; + bool HasWifiOnlineURL() const; + void SetWifiOnlineURL(const std::string& url); + std::string WifiOnlineURL() const; + bool HasBluetoothOnlineURL() const; + void SetBluetoothOnlineURL(const std::string& url); + std::string BluetoothOnlineURL() const; + + bool HasOfflineURL() const; + void SetOfflineURL(const std::string& url); + std::string OfflineURL() const; + bool HasWifiOfflineURL() const; + void SetWifiOfflineURL(const std::string& url); + std::string WifiOfflineURL() const; + bool HasBluetoothOfflineURL() const; + void SetBluetoothOfflineURL(const std::string& url); + std::string BluetoothOfflineURL() const; + +private: + std::string m_wifiMac; + bool m_wifiState; + + std::string m_bluetoothMac; + bool m_bluetoothState; + + std::string m_onlineURL; + std::string m_wifiOnlineURL; + std::string m_bluetoothOnlineURL; + std::string m_offlineURL; + std::string m_wifiOfflineURL; + std::string m_bluetoothOfflineURL; +}; + +} // namespace Util +} // namespace PresenceDetection + +#endif // UTIL_DEVICE_H