Prechádzať zdrojové kódy

Implement use of Inventory API

JDierkse 6 rokov pred
rodič
commit
5724bd45a7

+ 8 - 1
Application/PresenceDetection.cc

@@ -113,7 +113,14 @@ int main(int argc, char** argv)
 			else
 				throw std::runtime_error("UniFi CookieFile directive missing in ini file.");
 
-			pUniFiDevice = std::make_shared<PresenceDetection::UniFi::Device>(hostname, username, password, cookiefile, target);
+			std::string inventoryURL;
+			boost::optional<boost::property_tree::ptree&> bluetoothInventoryURL = pt.get_child_optional("UniFi.Inventory");
+			if (bluetoothInventoryURL)
+				inventoryURL = pt.get<std::string>("UniFi.Inventory");
+			else
+				throw std::runtime_error("UniFi Inventory directive missing in ini file.");
+
+			pUniFiDevice = std::make_shared<PresenceDetection::UniFi::Device>(hostname, username, password, cookiefile, inventoryURL, target);
 		}
 
 		std::shared_ptr<PresenceDetection::Bluetooth::Device> pBluetoothDevice;

+ 33 - 2
Application/Test.cc

@@ -1,3 +1,4 @@
+#include <iostream>
 #include <string>
 
 #include <stdio.h>
@@ -16,6 +17,8 @@
 #include <bluetooth/hci_lib.h>
 #include <bluetooth/l2cap.h>
 
+#include "Util/Timer.h"
+
 /* Defaults */
 static bdaddr_t bdaddr;
 static int size    = 44;
@@ -224,13 +227,41 @@ error:
 	exit(1);
 }
 
+void print()
+{
+	std::cout << "Print" << std::endl;
+}
+
 int main(int /*argc*/, char** /*argv[]*/)
 {
-	std::string address = "48:4B:AA:85:30:C5";
-	ping(address.c_str());
+	//std::string address = "48:4B:AA:85:30:C5";
+	//ping(address.c_str());
 
 	//address = "d0:c5:f3:84:99:de";
 	//ping(address.c_str());
 
+	PresenceDetection::Util::Timer timer;
+	std::function<void()> f_print = print;
+
+	std::cout << "---" << std::endl;
+	timer.StartContinuous(1000, f_print);
+	timer.Stop();
+	timer.Wait();
+	std::cout << "---" << std::endl << std::endl;
+
+	std::cout << "---" << std::endl;
+	timer.StartContinuous(3000, f_print);
+	timer.Abort();
+	timer.Wait();
+	std::cout << "---" << std::endl << std::endl;
+
+	std::cout << "---" << std::endl;
+	timer.StartContinuous(3000, f_print);
+	sleep(1);
+	timer.Abort();
+	timer.Wait();
+	std::cout << "---" << std::endl << std::endl;
+
 	return 0;
 }
+

+ 44 - 15
Bluetooth/Device.cpp

@@ -1,6 +1,6 @@
+#include <algorithm>
 #include <sstream>
 #include <syslog.h>
-#include <boost/algorithm/string.hpp>
 #include "Util/JSON.h"
 #include "Functions.h"
 #include "Device.h"
@@ -10,15 +10,10 @@ namespace PresenceDetection {
 namespace Bluetooth {
 
 Device::Device(const std::string& inventoryURL, const std::string& target) :
+	m_inventoryURL(inventoryURL),
 	m_target(target)
 {
-	std::string devices = m_httpClient.GetUrlContents(inventoryURL);
-	Util::JSON json = Util::JSON::parse(devices);
-
-	for (auto& element : json)
-		if (element["bluetooth"] != "")
-			m_devices.push_back(element["bluetooth"]);
-
+	UpdateDevicesFromInventory();
 	Start();
 }
 
@@ -28,17 +23,42 @@ Device::~Device()
 
 void Device::Start()
 {
-	m_timer.StartContinuous(10000, static_cast<std::function<void()>>(std::bind(&Device::UpdatePresentDevices, this)));
+	m_deviceTimer.StartContinuous(10000, static_cast<std::function<void()>>(std::bind(&Device::UpdatePresentDevices, this)));
+	m_inventoryTimer.StartContinuous(300000, static_cast<std::function<void()>>(std::bind(&Device::UpdateDevicesFromInventory, this)));
 }
 
 void Device::Stop()
 {
-	m_timer.Stop();
+	m_deviceTimer.Stop();
 }
 
 void Device::Wait()
 {
-	m_timer.Wait();
+	m_deviceTimer.Wait();
+}
+
+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["bluetooth"] != "")
+			{
+				std::string bluetooth = element["bluetooth"];
+				std::transform(bluetooth.begin(), bluetooth.end(), bluetooth.begin(), ::tolower);
+				m_devices.push_back(bluetooth);
+			}
+	}
+	catch (const std::exception& e)
+	{
+		std::stringstream ss;
+		ss << "Bluetooth::Device::GetDevicesFromInventory() - Error: " << e.what() << std::endl;
+		syslog(LOG_ERR, "%s", ss.str().c_str());
+	}
 }
 
 void Device::UpdatePresentDevices()
@@ -49,11 +69,20 @@ void Device::UpdatePresentDevices()
 
 	for (std::vector<std::string>::iterator it = m_devices.begin(); it != m_devices.end(); ++it)
 	{
-		if (Functions::Ping(*it))
+		try
+		{
+			if (Functions::Ping(*it))
+			{
+				if (std::find(m_presentDevices.begin(), m_presentDevices.end(), *it) == m_presentDevices.end())
+					SendStateChange(true, *it);
+				presentDevices.push_back(*it);
+			}
+		}
+		catch (const std::exception& e)
 		{
-			if (std::find(m_presentDevices.begin(), m_presentDevices.end(), *it) == m_presentDevices.end())
-				SendStateChange(true, *it);
-			presentDevices.push_back(*it);
+			std::stringstream ss;
+			ss << "Bluetooth::Device::UpdatePresentDevices() - Error: " << e.what() << std::endl;
+			syslog(LOG_ERR, "%s", ss.str().c_str());
 		}
 	}
 

+ 4 - 1
Bluetooth/Device.h

@@ -21,15 +21,18 @@ public:
 	void Wait();
 
 private:
+	void UpdateDevicesFromInventory();
 	void UpdatePresentDevices();
 	void SendStateChange(bool present, const std::string& macAddress);
 
 private:
-	Util::Timer m_timer;
+	Util::Timer m_deviceTimer;
+	Util::Timer m_inventoryTimer;
 	Util::HttpClient m_httpClient;
 
 	std::vector<std::string> m_devices;
 
+	std::string m_inventoryURL;
 	std::string m_target;
 
 	std::vector<std::string> m_presentDevices;

+ 1 - 0
PresenceDetection.ini

@@ -8,6 +8,7 @@ Hostname = UniFi
 Username = 
 Password = 
 CookieFile = /tmp/UniFi_Cookies
+Inventory = 
 
 [Bluetooth]
 Inventory = 

+ 52 - 31
UniFi/Device.cpp

@@ -1,22 +1,23 @@
+#include <sstream>
 #include <syslog.h>
-#include <boost/foreach.hpp>
-#include <boost/property_tree/ptree.hpp>
-#include <boost/property_tree/json_parser.hpp>
+#include "Util/JSON.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& target) :
+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_target(target)
+	m_inventoryURL(inventoryURL),
+	m_target(target),
+	m_offlineTimeout(120)
 {
-	m_offlineTimeout = 120;
+	UpdateDevicesFromInventory();
 	Start();
 }
 
@@ -27,17 +28,18 @@ Device::~Device()
 
 void Device::Start()
 {
-	m_timer.StartContinuous(5000, static_cast<std::function<void()>>(std::bind(&Device::UpdatePresentDevices, this)));
+	m_deviceTimer.StartContinuous(5000, static_cast<std::function<void()>>(std::bind(&Device::UpdatePresentDevices, this)));
+	m_inventoryTimer.StartContinuous(300000, static_cast<std::function<void()>>(std::bind(&Device::UpdateDevicesFromInventory, this)));
 }
 
 void Device::Stop()
 {
-	m_timer.Stop();
+	m_deviceTimer.Stop();
 }
 
 void Device::Wait()
 {
-	m_timer.Wait();
+	m_deviceTimer.Wait();
 }
 
 bool Device::Login()
@@ -45,23 +47,18 @@ 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);
+	Util::JSON json;
+	json["password"] = m_password;
+	json["username"] = m_username;
 
 	try
 	{
 		std::stringstream output;
-		output << m_httpClient.GetUrlPostContents(url.str(), m_cookieFile, json.str(), "application/json");
+		output << m_httpClient.GetUrlPostContents(url.str(), m_cookieFile, json.dump(), "application/json");
 
-		boost::property_tree::ptree pt;
-		boost::property_tree::read_json(output, pt);
+		Util::JSON outputJSON = Util::JSON::parse(output);
 
-		std::string result = pt.get_child("meta").get<std::string>("rc");
-
-		if (result != "ok")
+		if (outputJSON["meta"]["rc"] != "ok")
 		{
 			std::stringstream error;
 			error << "Login Failed - " << output.str();
@@ -103,6 +100,30 @@ void Device::Logout()
 	}
 }
 
+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;
+		syslog(LOG_ERR, "%s", ss.str().c_str());
+	}
+}
+
 void Device::UpdatePresentDevices()
 {
 	bool loggedIn;
@@ -128,28 +149,28 @@ void Device::UpdatePresentDevices()
 		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<std::string>("rc");
+		Util::JSON json = Util::JSON::parse(output);
 
-		if (result != "ok")
+		if (json["meta"]["rc"] != "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"))
+		for (auto& device : json["data"])
 		{
-			std::string macAddress = v.second.get<std::string>("mac");
-			int lastSeen = v.second.get<int>("last_seen");
+			std::string macAddress = device["mac"];
+			int lastSeen = device["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);
+				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);
+				}
 			}
 		}
 	}

+ 7 - 2
UniFi/Device.h

@@ -14,7 +14,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& target);
+	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();
 
 	void Start();
@@ -25,11 +25,13 @@ private:
 	bool Login();
 	void Logout();
 
+	void UpdateDevicesFromInventory();
 	void UpdatePresentDevices();
 	void SendStateChange(bool present, const std::string& macAddress);
 
 private:
-	Util::Timer m_timer;
+	Util::Timer m_deviceTimer;
+	Util::Timer m_inventoryTimer;
 	Util::HttpClient m_httpClient;
 
 	std::mutex m_mutex;
@@ -39,6 +41,9 @@ private:
 	std::string m_password;
 	std::string m_cookieFile;
 
+	std::vector<std::string> m_devices;
+
+	std::string m_inventoryURL;
 	std::string m_target;
 	int m_offlineTimeout;
 

+ 15 - 0
Util/Timer.cpp

@@ -1,3 +1,5 @@
+#include <iostream>
+
 #include <chrono>
 #include "Util.h"
 #include "Timer.h"
@@ -8,12 +10,14 @@ namespace Util {
 
 Timer::Timer() :
 	m_run(false),
+	m_aborted(false),
 	m_identifier(CreateRandomString(10))
 {
 }
 
 Timer::~Timer()
 {
+	Abort();
 	Wait();
 }
 
@@ -57,6 +61,17 @@ void Timer::Stop()
 	m_run.store(false, std::memory_order_release);
 }
 
+void Timer::Abort()
+{
+	std::cout << "Timer::Abort()" << std::endl;
+	m_run.store(false, std::memory_order_release);
+	{
+		std::unique_lock<std::mutex> lock(m_mutex);
+		m_aborted = true;
+	}
+	m_conditionVariable.notify_all();
+}
+
 bool Timer::IsRunning() const noexcept
 {
 	return (m_run.load(std::memory_order_acquire) && m_thread.joinable());

+ 20 - 10
Util/Timer.h

@@ -2,7 +2,9 @@
 #define UTIL_TIMER_H
 
 #include <atomic>
+#include <condition_variable>
 #include <functional>
+#include <mutex>
 #include <thread>
 #include "Helpers.h"
 
@@ -30,18 +32,19 @@ public:
 	std::string Identifier() const;
 
 	template <typename ...FunctionArguments, typename ...Arguments>
-	void StartSingle(int interval, std::function<void(FunctionArguments...)> const & function, Arguments && ...arguments)
+	void StartSingle(int milliSeconds, std::function<void(FunctionArguments...)> const & function, Arguments && ...arguments)
 	{
-		Start(TimerType::Single, interval, function, std::forward<Arguments>(arguments)...);
+		Start(TimerType::Single, milliSeconds, function, std::forward<Arguments>(arguments)...);
 	}
 
 	template <typename ...FunctionArguments, typename ...Arguments>
-	void StartContinuous(int interval, std::function<void(FunctionArguments...)> const & function, Arguments && ...arguments)
+	void StartContinuous(int milliSeconds, std::function<void(FunctionArguments...)> const & function, Arguments && ...arguments)
 	{
-		Start(TimerType::Continuous, interval, function, std::forward<Arguments>(arguments)...);
+		Start(TimerType::Continuous, milliSeconds, function, std::forward<Arguments>(arguments)...);
 	}
 
 	void Stop();
+	void Abort();
 	bool IsRunning() const noexcept;
 	void Wait();
 
@@ -59,7 +62,7 @@ private:
 
 private:
 	template <typename ...FunctionArguments, typename ...Arguments>
-	void Start(TimerType::type type, int interval, std::function<void(FunctionArguments...)> const & function, Arguments && ...arguments)
+	void Start(TimerType::type type, int milliSeconds, std::function<void(FunctionArguments...)> const & function, Arguments && ...arguments)
 	{
 		if (type == TimerType::Unknown)
 			return;
@@ -67,17 +70,21 @@ private:
 		if (m_run.load(std::memory_order_acquire))
 			Stop();
 
-		m_thread = std::thread([this, type, interval, function, &arguments...]()
+		m_thread = std::thread([this, type, milliSeconds, function, &arguments...]()
 			{
 				auto argumentsCopy = std::make_tuple(std::forward<Arguments>(arguments)...);
 				m_run.store(true, std::memory_order_release);
+				m_aborted = false;
 
 				while (m_run.load(std::memory_order_acquire))
 				{
-					std::this_thread::sleep_for(std::chrono::milliseconds(interval));
-					Helpers::execute(function, argumentsCopy);
-					if (type == TimerType::Single)
-						m_run.store(false, std::memory_order_release);
+					std::unique_lock<std::mutex> lock(m_mutex);
+					if (!m_conditionVariable.wait_for(lock, std::chrono::milliseconds(milliSeconds), [&]{return m_aborted;}))
+					{
+						Helpers::execute(function, argumentsCopy);
+						if (type == TimerType::Single)
+							m_run.store(false, std::memory_order_release);
+					}
 				}
 			});
 
@@ -88,6 +95,9 @@ private:
 
 private:
 	std::atomic<bool> m_run;
+	bool m_aborted;
+	std::mutex m_mutex;
+	std::condition_variable m_conditionVariable;
 	std::string m_identifier;
 	std::thread m_thread;
 };