Publish LumaOps source

This commit is contained in:
LumaOps release export
2026-09-03 01:18:36 +02:00
commit 7f1c0e5f71
2363 changed files with 501543 additions and 0 deletions
@@ -0,0 +1,111 @@
/**
\file APICache.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_API_CACHE_H
#define INCLUDE_API_CACHE_H
#include <chrono>
#include <string>
#include "HueCommandAPI.h"
namespace hueplusplus
{
//! \brief Maximum duration, used to indicate that the cache should never be refreshed automatically.
constexpr std::chrono::steady_clock::duration c_refreshNever = std::chrono::steady_clock::duration::max();
//! \brief Caches API GET requests and refreshes regularly.
class APICache
{
public:
//! \brief Constructs APICache which forwards to a base cache
//! \param baseCache Base cache providing a parent state, must not be nullptr
//! \param subEntry Key of the child to use in the base cache
//! \param refresh Interval between cache refreshing. May be 0 to always refresh.
//! This is independent from the base cache refresh rate.
//!
//! Refreshes only part of the base cache.
APICache(
std::shared_ptr<APICache> baseCache, const std::string& subEntry, std::chrono::steady_clock::duration refresh);
//! \brief Constructs APICache with an own internal json cache
//! \param path URL appended after username, may be empty.
//! \param commands HueCommandAPI for making API requests.
//! \param refresh Interval between cache refreshing. May be 0 to always refresh.
//! \param initial Initial value, may be null. If present, assumes the value is up to date.
APICache(const std::string& path, const HueCommandAPI& commands, std::chrono::steady_clock::duration refresh,
const nlohmann::json& initial);
//! \brief Refresh cache now.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
//!
//! If there is a base cache, refreshes only the used part of that cache.
void refresh();
//! \brief Get cached value, refresh if necessary.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
nlohmann::json& getValue();
//! \brief Get cached value, does not refresh.
//! \throws HueException when no previous request was cached
const nlohmann::json& getValue() const;
//! \brief Set duration after which the cache is refreshed.
//! \param refreshDuration Interval between cache refreshing.
//! May be 0 to always refresh, or \ref c_refreshNever to never refresh.
//!
//! If the new refresh duration is exceeded, does not refresh immediately.
//! Instead, the next non-const getValue() call will refresh the value.
//! This is to reduce the number of unneccessary requests.
void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration);
//! \brief Get duration between refreshes.
std::chrono::steady_clock::duration getRefreshDuration() const;
//! \brief Get HueCommandAPI used for requests
HueCommandAPI& getCommandAPI();
//! \brief Get HueCommandAPI used for requests
const HueCommandAPI& getCommandAPI() const;
//! \brief Get path the cache is refreshed from
//! \returns Request path as passed to HueCommandAPI::GETRequest
std::string getRequestPath() const;
private:
bool needsRefresh();
private:
std::shared_ptr<APICache> base;
std::string path;
HueCommandAPI commands;
std::chrono::steady_clock::duration refreshDuration;
std::chrono::steady_clock::time_point lastRefresh;
nlohmann::json value;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,73 @@
/**
\file Action.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_ACTION_H
#define INCLUDE_HUEPLUSPLUS_ACTION_H
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! \brief Action executed by the bridge, e.g. as a Schedule command
//!
//! The action makes either a POST, PUT or DELETE request with a given body
//! to an address on the bridge.
//!
//! The Action can also be created by StateTransaction::toAction().
class Action
{
public:
//! \brief Create Action from json
//! \param json JSON object with address, method and body
explicit Action(const nlohmann::json& json);
//! \brief Method used for the command
enum class Method
{
post, //!< POST request
put, //!< PUT request
deleteMethod //!< DELETE request
};
//! \brief Get address the request is made to
std::string getAddress() const;
//! \brief Get request method
Method getMethod() const;
//! \brief Get request body
const nlohmann::json& getBody() const;
//! \brief Get json object of command
const nlohmann::json& toJson() const;
public:
//! \brief Parse Method from string
//! \param s \c POST, \c PUT or \c DELETE
static Method parseMethod(const std::string& s);
//! \brief Get string from Method
//! \returns \c POST, \c PUT or \c DELETE
static std::string methodToString(Method m);
private:
nlohmann::json json;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,159 @@
/**
\file BaseDevice.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_HUE_THING_H
#define INCLUDE_HUEPLUSPLUS_HUE_THING_H
#include <memory>
#include "APICache.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! \brief Base class for physical devices connected to the bridge (sensor or light).
class BaseDevice
{
public:
//! \brief Virtual destructor
virtual ~BaseDevice() = default;
//! \brief Const function that returns the id of this device
//!
//! \return integer representing the device id
virtual int getId() const;
//! \brief Const function that returns the device type
//!
//! \return String containing the type
virtual std::string getType() const;
//! \brief Function that returns the name of the device.
//!
//! \return String containig the name of the device
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual std::string getName();
//! \brief Const function that returns the name of the device.
//!
//! \note This will not refresh the device state
//! \return String containig the name of the thing
virtual std::string getName() const;
//! \brief Const function that returns the modelid of the device
//!
//! \return String containing the modelid
virtual std::string getModelId() const;
//! \brief Const function that returns the uniqueid of the device
//!
//! \note Only working on bridges with versions starting at 1.4
//! \return String containing the uniqueid or an empty string when the function is not supported
virtual std::string getUId() const;
//! \brief Const function that returns the manufacturername of the device
//!
//! \note Only working on bridges with versions starting at 1.7
//! \return String containing the manufacturername or an empty string when the function is not supported
virtual std::string getManufacturername() const;
//! \brief Const function that returns the productname of the device
//!
//! \note Only working on bridges with versions starting at 1.24
//! \return String containing the productname or an empty string when the function is not supported
virtual std::string getProductname() const;
//! \brief Function that returns the software version of the device
//!
//! \return String containing the software version
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual std::string getSwVersion();
//! \brief Const function that returns the software version of the device
//!
//! \note This will not refresh the device state
//! \return String containing the software version
virtual std::string getSwVersion() const;
//! \brief Function that sets the name of the device
//!
//! \return Bool that is true on success
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual bool setName(const std::string& name);
//! \brief Refreshes internal cached state.
//! \param force \c true forces a refresh, regardless of how long the last refresh was ago.
//! \c false to only refresh when enough time has passed (needed e.g. when calling only const methods).
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual void refresh(bool force = false);
//! \brief Sets custom refresh interval for this device.
//! \param refreshDuration The new minimum duration between refreshes. May be 0 or \ref c_refreshNever.
virtual void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration);
protected:
//! \brief Protected ctor that is used by subclasses, construct with shared cache.
//! \param id Integer that specifies the id of this device
//! \param baseCache Cache of the ResourceList containing this device (must not be null).
BaseDevice(int id, const std::shared_ptr<APICache>& baseCache);
//! \brief Protected ctor that is used by subclasses.
//!
//! \param id Integer that specifies the id of this device
//! \param commands HueCommandAPI for communication with the bridge
//! \param path Base path for the resource type, ending with a '/'. Example: \c "/lights/"
//! \param refreshDuration Time between refreshing the cached state.
//! \param currentState Current state of the device, may be null.
BaseDevice(int id, const HueCommandAPI& commands, const std::string& path,
std::chrono::steady_clock::duration refreshDuration, const nlohmann::json& currentState);
//! \brief Utility function to send a put request to the device.
//!
//! \param subPath A path that is appended to the uri, note it should always start with a slash ("/")
//! \param request A nlohmann::json aka the request to send
//! \param fileInfo FileInfo from calling function for exception details.
//! \return The parsed reply
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual nlohmann::json sendPutRequest(const std::string& subPath, const nlohmann::json& request, FileInfo fileInfo);
protected:
int id; //!< holds the id of the device
APICache state; //!< holds the current state of the device
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,178 @@
/**
\file BaseHttpHandler.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_BASE_HTTP_HANDLER_H
#define INCLUDE_HUEPLUSPLUS_BASE_HTTP_HANDLER_H
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include "IHttpHandler.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! Base class for classes that handle http requests and multicast requests
class BaseHttpHandler : public IHttpHandler
{
public:
//! \brief Virtual dtor
virtual ~BaseHttpHandler() = default;
//! \brief Send a message to a specified host and return the body of the response.
//!
//! \param msg The message that should sent to the specified address
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return The body of the response of the host as a string
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
std::string sendGetHTTPBody(const std::string& msg, const std::string& adr, int port = 80) const override;
//! \brief Send a HTTP request with the given method to the specified host and return the body of the response.
//!
//! \param method HTTP method type e.g. GET, HEAD, POST, PUT, DELETE, ...
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
std::string sendHTTPRequest(const std::string& method, const std::string& uri, const std::string& contentType,
const std::string& body, const std::string& adr, int port = 80) const override;
//! \brief Send a HTTP GET request to the specified host and return the body of the response.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! that specifies the port to which the request is sent to. Default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
std::string GETString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port = 80) const override;
//! \brief Send a HTTP POST request to the specified host and return the body of the response.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! that specifies the port to which the request is sent to. Default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
std::string POSTString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port = 80) const override;
//! \brief Send a HTTP PUT request to the specified host and return the body of the response.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! that specifies the port to which the request is sent to. Default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
std::string PUTString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port = 80) const override;
//! \brief Send a HTTP DELETE request to the specified host and return the body of the response.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! that specifies the port to which the request is sent to. Default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
std::string DELETEString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port = 80) const override;
//! \brief Send a HTTP GET request to the specified host and return the body of the response parsed as JSON.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Parsed body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws nlohmann::json::parse_error when the body could not be parsed
nlohmann::json GETJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port = 80) const override;
//! \brief Send a HTTP POST request to the specified host and return the body of the response parsed as JSON.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Parsed body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws nlohmann::json::parse_error when the body could not be parsed
nlohmann::json POSTJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port = 80) const override;
//! \brief Send a HTTP PUT request to the specified host and return the body of the response parsed as JSON.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Parsed body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws nlohmann::json::parse_error when the body could not be parsed
nlohmann::json PUTJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port = 80) const override;
//! \brief Send a HTTP DELETE request to the specified host and return the body of the response parsed as JSON.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Parsed body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws nlohmann::json::parse_error when the body could not be parsed
nlohmann::json DELETEJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port = 80) const override;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,308 @@
/**
\file Bridge.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_HUE_H
#define INCLUDE_HUEPLUSPLUS_HUE_H
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "APICache.h"
#include "BridgeConfig.h"
#include "BrightnessStrategy.h"
#include "ColorHueStrategy.h"
#include "ColorTemperatureStrategy.h"
#include "Group.h"
#include "HueCommandAPI.h"
#include "HueDeviceTypes.h"
#include "IHttpHandler.h"
#include "Light.h"
#include "ResourceList.h"
#include "Rule.h"
#include "Scene.h"
#include "Schedule.h"
#include "Sensor.h"
#include "SensorList.h"
#include "Utils.h"
#include <nlohmann/json.hpp>
//! \brief Namespace for the hueplusplus library
namespace hueplusplus
{
// forward declarations
class Bridge;
//!
//! Class to find all Hue bridges on the network and create usernames for them.
//!
class BridgeFinder
{
public:
struct BridgeIdentification
{
std::string ip;
int port = 80;
std::string mac;
};
public:
//! \brief Constructor of BridgeFinder class
//!
//! \param handler HttpHandler of type \ref IHttpHandler for communication with the bridge
BridgeFinder(std::shared_ptr<const IHttpHandler> handler);
//! \brief Finds all bridges in the network and returns them.
//!
//! The user should be given the opportunity to select the correct one based on the mac address.
//! \return vector containing ip and mac of all found bridges
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
std::vector<BridgeIdentification> findBridges() const;
//! \brief Gets a Hue bridge based on its identification
//!
//! \param identification \ref BridgeIdentification that specifies a bridge
//! \param sharedState Uses a single, shared cache for all objects on the bridge.
//! \return \ref Bridge class object
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body or username could not be requested
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
Bridge getBridge(const BridgeIdentification& identification, bool sharedState = false);
//! \brief Function that adds a username to the usernames map
//!
//! \param mac MAC address of Hue bridge
//! \param username Username that is used to control the Hue bridge
void addUsername(const std::string& mac, const std::string& username);
//! \brief Function that adds a client key to the clientkeys map
//!
//! The client key is only needed for entertainment mode, otherwise it is optional.
//! \param mac MAC address of Hue bridge
//! \param clientkey Client key that is used to control the Hue bridge in entertainment mode
void addClientKey(const std::string& mac, const std::string& clientkey);
//! \brief Function that returns a map of mac addresses and usernames.
//!
//! Note these should be saved at the end and re-loaded with \ref addUsername
//! next time, so only one username is generated per bridge. \returns A map
//! mapping mac address to username for every bridge
const std::map<std::string, std::string>& getAllUsernames() const;
//! \brief Normalizes mac address to plain hex number.
//! \returns \p input without separators and whitespace, in lower case.
static std::string normalizeMac(std::string input);
private:
//! \brief Parses mac address from description.xml
//!
//! \param description Content of description.xml file as returned by GET request.
//! \returns Content of xml element \c serialNumber if description matches a Hue bridge, otherwise an empty
//! string.
static std::string parseDescription(const std::string& description);
std::map<std::string, std::string> usernames; //!< Maps all macs to usernames added by \ref
//!< BridgeFinder::addUsername
std::map<std::string, std::string> clientkeys; //!< Maps all macs to clientkeys added by \ref
//!< BridgeFinder::addClientKey
std::shared_ptr<const IHttpHandler> http_handler;
};
//! \brief Bridge class for a bridge.
//!
//! This is the main class used to interact with the Hue bridge.
class Bridge
{
friend class BridgeFinder;
public:
using LightList = SearchableResourceList<Light>;
using GroupList = GroupResourceList<Group, CreateGroup>;
using ScheduleList = CreateableResourceList<ResourceList<Schedule, int>, CreateSchedule>;
using SceneList = CreateableResourceList<ResourceList<Scene, std::string>, CreateScene>;
using RuleList = CreateableResourceList<ResourceList<Rule, int>, CreateRule>;
public:
//! \brief Constructor of Bridge class
//!
//! \param ip IP address in dotted decimal notation like "192.168.2.1"
//! \param port Port of the hue bridge
//! \param username String that specifies the username that is used to control
//! the bridge. Can be left empty and acquired in \ref requestUsername.
//! \param handler HttpHandler for communication with the bridge
//! \param clientkey Optional client key for streaming
//! \param refreshDuration Time between refreshing the cached state.
//! \param sharedState Uses a single, shared cache for all objects on the bridge.
Bridge(const std::string& ip, const int port, const std::string& username,
std::shared_ptr<const IHttpHandler> handler, const std::string& clientkey = "",
std::chrono::steady_clock::duration refreshDuration = std::chrono::seconds(10), bool sharedState = false);
//! \brief Refreshes the bridge state.
//!
//! Should only be called rarely, as a full refresh is costly and usually not necessary.
//! Instead refresh only the parts you are interested in or rely on periodic refreshes
//! that happen automatically when calling non-const methods.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void refresh();
//! \brief Sets refresh interval for the whole bridge state.
//! \param refreshDuration The new minimum duration between refreshes. May be 0 or \ref c_refreshNever.
//!
//! Also sets refresh duration on all resource lists on the bridge, but not on already existing lights.
//! The resource lists (such as lights()) can have their own durations, but those must be set after calling this function.
void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration);
//! \brief Function to get the ip address of the hue bridge
//!
//! \return string containing ip
std::string getBridgeIP() const;
//! \brief Function to set stream mode to active for entertainment mode
//!
//! \return bool - whether stream request was successful
bool startStreaming(std::string group_identifier);
//! \brief Function to set stream mode to active for entertainment mode
//!
//! \return bool - whether stream request was successful
bool stopStreaming(std::string group_identifier);
//! \brief Function to get the port of the hue bridge
//!
//! \return integer containing port
int getBridgePort() const;
//! \brief Send a username request to the Hue bridge.
//!
//! Blocks for about 30 seconds and 5 seconds to prepare.
//! It automatically sets the username variable according to the username received and returns the username
//! received. This function should only be called once to acquire a username to control the bridge and the
//! username should be saved for future use. \return username for API usage \throws std::system_error when
//! system or socket operations fail \throws HueException when response contained no body \throws
//! HueAPIResponseException when response contains an error except link button not pressed. \throws
//! nlohmann::json::parse_error when response could not be parsed
std::string requestUsername();
//! \brief Function that returns the username
//!
//! \return The username used for API access
std::string getUsername() const;
//! \brief Function that returns the client key
//!
//! \return The client key used for Entertainment Mode API access
std::string getClientKey() const;
//! \brief Function to set the ip address of this class representing a bridge
//!
//! \param ip String that specifies the ip in dotted decimal notation like "192.168.2.1"
void setIP(const std::string& ip);
//! \brief Function to set the port of this class representing a bridge
//!
//! \param port Integer that specifies the port of an address like
//! "192.168.2.1:8080"
void setPort(const int port);
//! \brief Provides access to the configuration of the bridge.
BridgeConfig& config();
//! \brief Provides access to the configuration of the bridge.
//! \note Does not refresh state.
const BridgeConfig& config() const;
//! \brief Provides access to the Light%s on the bridge.
LightList& lights();
//! \brief Provides access to the Light%s on the bridge.
//! \note Does not refresh state.
const LightList& lights() const;
//! \brief Provides access to the Group%s on the bridge.
GroupList& groups();
//! \brief Provides access to the Group%s on the bridge.
//! \note Does not refresh state.
const GroupList& groups() const;
//! \brief Provides access to the Schedule%s on the bridge.
ScheduleList& schedules();
//! \brief Provides access to the Schedule%s on the bridge.
//! \note Does not refresh state.
const ScheduleList& schedules() const;
//! \brief Provides access to the Scene%s on the bridge.
SceneList& scenes();
//! \brief Provides access to the Scene%s on the bridge.
//! \note Does not refresh state.
const SceneList& scenes() const;
//! \brief Provides access to the Sensor%s on the bridge.
SensorList& sensors();
//! \brief Provides access to the Sensor%s on the bridge.
//! \note Does not refresh state.
const SensorList& sensors() const;
//! \brief Provides access to the Rule%s on the bridge.
RuleList& rules();
//! \brief Provides access to the Rule%s on the bridge
//! \note Does not refresh state.
const RuleList& rules() const;
private:
//! \brief Function that sets the HttpHandler and updates the HueCommandAPI.
//! \param handler a HttpHandler of type \ref IHttpHandler
//!
//! The HttpHandler and HueCommandAPI are used for bridge communication.
//! Resetting the HttpHandler should only be done when the username is first set,
//! before Bridge is used.
//! Resets all caches and resource lists.
void setHttpHandler(std::shared_ptr<const IHttpHandler> handler);
private:
std::string ip; //!< IP-Address of the hue bridge in dotted decimal notation
//!< like "192.168.2.1"
std::string username; //!< Username that is ussed to access the hue bridge
std::string clientkey; //!< Client key that is used for entertainment mode
int port;
std::shared_ptr<const IHttpHandler> http_handler; //!< A IHttpHandler that is used to communicate with the
//!< bridge
std::chrono::steady_clock::duration refreshDuration;
std::shared_ptr<APICache> stateCache;
detail::MakeCopyable<LightList> lightList;
detail::MakeCopyable<GroupList> groupList;
detail::MakeCopyable<ScheduleList> scheduleList;
detail::MakeCopyable<SceneList> sceneList;
detail::MakeCopyable<SensorList> sensorList;
detail::MakeCopyable<RuleList> ruleList;
detail::MakeCopyable<BridgeConfig> bridgeConfig;
bool sharedState;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,115 @@
/**
\file BridgeConfig.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_BRIDGE_CONFIG_H
#define INCLUDE_HUEPLUSPLUS_BRIDGE_CONFIG_H
#include <string>
#include <vector>
#include "APICache.h"
#include "TimePattern.h"
namespace hueplusplus
{
//! \brief API version consisting of major, minor and patch version
struct Version
{
int major;
int minor;
int patch;
};
//! \brief User that is whitelisted for Hue API usage
struct WhitelistedUser
{
//! \brief API username of the user
std::string key;
//! \brief Name provided on user creation
std::string name;
//! \brief Last time the user was used
time::AbsoluteTime lastUsed;
//! \brief Time the user was created
time::AbsoluteTime created;
};
//! \brief General bridge configuration properties.
class BridgeConfig
{
public:
//! \brief Construct BridgeConfig
BridgeConfig(std::shared_ptr<APICache> baseCache, std::chrono::steady_clock::duration refreshDuration);
//! \brief Refreshes internal cached state.
//! \param force \c true forces a refresh, regardless of how long the last refresh was ago.
//! \c false to only refresh when enough time has passed (needed e.g. when calling only const methods).
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void refresh(bool force = false);
//! \brief Sets custom refresh interval for the config.
//! \param refreshDuration The new minimum duration between refreshes. May be 0 or \ref c_refreshNever.
void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration);
//! \brief Get the list of whitelisted users
//! \returns All users authorized for API access
std::vector<WhitelistedUser> getWhitelistedUsers() const;
//! \brief Remove user from the whitelist
//! \param userKey The API username of the user to remove
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void removeUser(const std::string& userKey);
//! \brief Get link button state
//! \returns true when link button was pressed in the last 30 seconds.
//!
//! Indicates whether new users can be added currently.
bool getLinkButton() const;
//! \brief Set the link button state to pressed
void pressLinkButton();
//! \brief Add the closest lamp to the network
void touchLink();
//! \brief Get bridge MAC address
std::string getMACAddress() const;
//! \brief Get current (of last refresh) UTC time of the bridge
time::AbsoluteTime getUTCTime() const;
//! \brief Get configured timezone for the bridge
//! \note For times not in UTC, the timezone of the program and the bridge are assumed to be identical.
std::string getTimezone() const;
protected:
BridgeConfig(const BridgeConfig&) = default;
BridgeConfig(BridgeConfig&&) = default;
BridgeConfig& operator=(const BridgeConfig&) = default;
BridgeConfig& operator=(BridgeConfig&&) = default;
private:
APICache cache;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,61 @@
/**
\file BrightnessStrategy.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_BRIGHTNESS_STRATEGY_H
#define INCLUDE_HUEPLUSPLUS_BRIGHTNESS_STRATEGY_H
#include <cstdint>
namespace hueplusplus
{
class Light;
//! Virtual base class for all BrightnessStrategies
class BrightnessStrategy
{
public:
//! \brief Virtual function for changing a lights brightness with a specified
//! transition.
//!
//! \param bri The brightness raning from 0 = off to 255 = fully lit
//! \param transition The time it takes to fade to the new brightness in
//! multiples of 100ms, 4 = 400ms and should be seen as the default \param
//! light A reference of the light
virtual bool setBrightness(unsigned int bri, uint8_t transition, Light& light) const = 0;
//! \brief Virtual function that returns the current brightnessof the light
//!
//! Should update the lights state by calling refreshState()
//! \param light A reference of the light
//! \return Unsigned int representing the brightness
virtual unsigned int getBrightness(Light& light) const = 0;
//! \brief Virtual function that returns the current brightness of the light
//!
//! \note This should not update the lights state
//! \param light A const reference of the light
//! \return Unsigned int representing the brightness
virtual unsigned int getBrightness(const Light& light) const = 0;
//! \brief Virtual dtor
virtual ~BrightnessStrategy() = default;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,293 @@
/**
\file CLIPSensors.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDE_HUEPLUSPLUS_CLIP_SENSORS_H
#define INCLUDE_HUEPLUSPLUS_CLIP_SENSORS_H
#include "Sensor.h"
namespace hueplusplus
{
namespace sensors
{
//! \brief Common methods for CLIP sensors
class BaseCLIP : public BaseDevice
{
public:
//! \brief Check if sensor is on
//!
//! Sensors which are off do not change their status
bool isOn() const;
//! \brief Enable or disable sensor
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setOn(bool on);
//! \brief Check whether the sensor has a battery state
bool hasBatteryState() const;
//! \brief Get battery state
//! \returns Battery state in percent
//! \throws nlohmann::json::out_of_range when sensor has no battery state.
int getBatteryState() const;
//! \brief Set battery state
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setBatteryState(int percent);
//! \brief Check whether the sensor is reachable
//! \note Reachable verification is not implemented for CLIP sensors yet
bool isReachable() const;
//! \brief Check whether the sensor has a URL
bool hasURL() const;
//! \brief Get sensor URL
std::string getURL() const;
//! \brief Set sensor URL
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setURL(const std::string& url);
//! \brief Get time of last status update
//! \returns The last update time, or a time with a zero duration from epoch
//! if the last update time is not set.
time::AbsoluteTime getLastUpdated() const;
protected:
//! \brief Protected constructor to be used by subclasses
explicit BaseCLIP(Sensor sensor) : BaseDevice(std::move(sensor)) { }
};
//! \brief CLIP sensor for button presses
class CLIPSwitch : public BaseCLIP
{
public:
//! \brief Construct from generic sensor
explicit CLIPSwitch(Sensor sensor) : BaseCLIP(std::move(sensor)) { }
//! \brief Get the code of the last switch event.
int getButtonEvent() const;
//! \brief Set the button event code
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setButtonEvent(int code);
//! \brief CLIPSwitch sensor type name
static constexpr const char* typeStr = "CLIPSwitch";
};
//! \brief CLIP sensor detecting whether a contact is open or closed
class CLIPOpenClose : public BaseCLIP
{
public:
//! \brief Construct from generic sensor
explicit CLIPOpenClose(Sensor sensor) : BaseCLIP(std::move(sensor)) { }
//! \brief Check whether the switch is open
bool isOpen() const;
//! \brief Set switch state
//!
//! The sensor needs to stay in a state for at least 1s.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setOpen(bool open);
//! \brief CLIPOpenClose sensor type name
static constexpr const char* typeStr = "CLIPOpenClose";
};
detail::ConditionHelper<bool> makeCondition(const CLIPOpenClose& sensor);
//! \brief CLIP sensor to detect presence
class CLIPPresence : public BaseCLIP
{
public:
//! \brief Construct from generic sensor
explicit CLIPPresence(Sensor sensor) : BaseCLIP(std::move(sensor)) { }
//! \brief Check whether presence was detected
bool getPresence() const;
//! \brief Set presence state
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setPresence(bool presence);
//! \brief CLIPPresence sensor type name
static constexpr const char* typeStr = "CLIPPresence";
};
//! \brief CLIP sensor for temperature
class CLIPTemperature : public BaseCLIP
{
public:
//! \brief Construct from generic sensor
explicit CLIPTemperature(Sensor sensor) : BaseCLIP(std::move(sensor)) { }
//! \brief Get measured temperature
//! \returns Temperature in 0.01 degrees Celsius.
int getTemperature() const;
//! \brief Set temperature
//! \param temperature Temperature in 0.01 degreese Celsius.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setTemperature(int temperature);
//! \brief CLIPTemperature sensor type name
static constexpr const char* typeStr = "CLIPTemperature";
};
//! \brief CLIP sensor for humidity
class CLIPHumidity : public BaseCLIP
{
public:
//! \brief Construct from generic sensor
explicit CLIPHumidity(Sensor sensor) : BaseCLIP(std::move(sensor)) { }
//! \brief Get measured humidity
//! \returns Humidity in 0.01% steps
int getHumidity() const;
//! \brief Set humidity
//! \param humidity Humidity in 0.01% steps
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setHumidity(int humidity);
//! \brief CLIPHumidity sensor type name
static constexpr const char* typeStr = "CLIPHumidity";
};
detail::ConditionHelper<int> makeCondition(const CLIPHumidity& sensor);
//! \brief CLIP sensor for light level
class CLIPLightLevel : public BaseCLIP
{
public:
//! \brief Construct from generic sensor
explicit CLIPLightLevel(Sensor sensor) : BaseCLIP(std::move(sensor)) { }
//! \brief Get threshold to detect darkness
int getDarkThreshold() const;
//! \brief Set threshold to detect darkness
//! \param threshold Light level as reported by \ref getLightLevel
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setDarkThreshold(int threshold);
//! \brief Get offset over dark threshold to detect daylight
int getThresholdOffset() const;
//! \brief Set offset to detect daylight
//! \param offset Offset to dark threshold to detect daylight. Must be greater than 1.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setThresholdOffset(int offset);
//! \brief Get measured light level
//! \returns Light level in <code>10000*log10(lux)+1</code> (logarithmic scale)
int getLightLevel() const;
//! \brief Set measured light level
//! \param level Light level in <code>10000*log10(lux)+1</code>
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setLightLevel(int level);
//! \brief Check whether light level is below dark threshold
bool isDark() const;
//! \brief Check whether light level is above light threshold
//!
//! Light threshold is dark threshold + offset
bool isDaylight() const;
//! \brief CLIPLightLevel sensor type name
static constexpr const char* typeStr = "CLIPLightLevel";
};
//! \brief CLIP sensor for a generic 3rd party sensor.
//!
//! Can be created by POST.
class CLIPGenericFlag : public BaseCLIP
{
public:
//! \brief Construct from generic sensor
explicit CLIPGenericFlag(Sensor sensor) : BaseCLIP(std::move(sensor)) { }
//! \brief Get boolean flag
bool getFlag() const;
//! \brief Set flag
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setFlag(bool flag);
//! \brief CLIPGenericFlag sensor type name
static constexpr const char* typeStr = "CLIPGenericFlag";
};
detail::ConditionHelper<bool> makeCondition(const CLIPGenericFlag& sensor);
//! \brief CLIP sensor for a generic 3rd party status
//!
//! Can be created by POST.
class CLIPGenericStatus : public BaseCLIP
{
public:
//! \brief Construct from generic sensor
explicit CLIPGenericStatus(Sensor sensor) : BaseCLIP(std::move(sensor)) { }
//! \brief Get sensor status
int getStatus() const;
//! \brief Set sensor status
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setStatus(int status);
//! \brief CLIPGenericStatus sensor type name
static constexpr const char* typeStr = "CLIPGenericStatus";
};
detail::ConditionHelper<int> makeCondition(const CLIPGenericStatus& sensor);
} // namespace sensors
} // namespace hueplusplus
#endif
@@ -0,0 +1,128 @@
/**
\file ColorHueStrategy.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef _COLOR_HUE_STRATEGY_H
#define _COLOR_HUE_STRATEGY_H
#include <cstdint>
#include <memory>
#include "ColorUnits.h"
namespace hueplusplus
{
class Light;
//! Virtual base class for all ColorHueStrategies
class ColorHueStrategy
{
public:
//! \brief Virtual function for changing a lights color in hue with a
//! specified transition.
//!
//! The hue ranges from 0 to 65535, whereas 65535 and 0 are red, 25500 is
//! green and 46920 is blue. \param hue The hue of the color \param transition
//! The time it takes to fade to the new color in multiples of 100ms, 4 =
//! 400ms and should be seen as the default \param light A reference of the
//! light
virtual bool setColorHue(uint16_t hue, uint8_t transition, Light& light) const = 0;
//! \brief Virtual function for changing a lights color in saturation with a
//! specified transition.
//!
//! The saturation ranges from 0 to 254, whereas 0 is least saturated (white)
//! and 254 is most saturated (vibrant). \param sat The saturation of the
//! color \param transition The time it takes to fade to the new color in
//! multiples of 100ms, 4 = 400ms and should be seen as the default \param
//! light A reference of the light
virtual bool setColorSaturation(uint8_t sat, uint8_t transition, Light& light) const = 0;
//! \brief Virtual function for changing a lights color in hue and saturation
//! format with a specified transition.
//!
//! \param hueSat Color in hue and satuation.
//! \param transition The time it takes to fade to the new color in multiples of
//! 100ms, 4 = 400ms and should be seen as the default
//! \param light A reference of the light
virtual bool setColorHueSaturation(const HueSaturation& hueSat, uint8_t transition, Light& light) const = 0;
//! \brief Virtual function for changing a lights color in CIE format with a
//! specified transition.
//!
//! \param xy The color in XY and brightness
//! \param transition The time it takes to fade to the new color in multiples
//! of 100ms, 4 = 400ms and should be seen as the default \param light A
//! reference of the light
virtual bool setColorXY(const XYBrightness& xy, uint8_t transition, Light& light) const = 0;
//! \brief Virtual function for turning on/off the color loop feature of a
//! light.
//!
//! Can be theoretically set for any light, but it only works for lights that
//! support this feature. When this feature is activated the light will fade
//! through every color on the current hue and saturation settings. Notice
//! that none of the setter functions check whether this feature is enabled
//! and the colorloop can only be disabled with this function or by simply
//! calling off() and then on(), so you could
//! alternatively call off() and then use any of the setter functions. \param
//! on Boolean to turn this feature on or off, true/1 for on and false/0 for
//! off \param light A reference of the light
virtual bool setColorLoop(bool on, Light& light) const = 0;
//! \brief Virtual function that lets the light perform one breath cycle in
//! the specified color.
//!
//! \param hueSat The color in hue and saturation
//! \param light A reference of the light
virtual bool alertHueSaturation(const HueSaturation& hueSat, Light& light) const = 0;
//! \brief Virtual function that lets the light perform one breath cycle in
//! the specified color.
//!
//! \param xy The color in XY and brightness
//! \param light A reference of the light
virtual bool alertXY(const XYBrightness& xy, Light& light) const = 0;
//! \brief Virtual function that returns the current color of the light as hue
//! and saturation
//!
//! Should update the lights state by calling refreshState()
//! \param light A reference of the light
virtual HueSaturation getColorHueSaturation(Light& light) const = 0;
//! \brief Virtual function that returns the current color of the light as hue
//! and saturation
//!
//! \note This should not update the lights state
//! \param light A const reference of the light
virtual HueSaturation getColorHueSaturation(const Light& light) const = 0;
//! \brief Virtual function that returns the current color of the light as xy
//!
//! Should update the lights state by calling refreshState()
//! \param light A reference of the light
//! \return XY and brightness of current color
virtual XYBrightness getColorXY(Light& light) const = 0;
//! \brief Virtual function that returns the current color of the light as xy
//!
//! \note This should not update the lights state
//! \param light A const reference of the light
//! \return XY and brightness of current color
virtual XYBrightness getColorXY(const Light& light) const = 0;
//! \brief Virtual dtor
virtual ~ColorHueStrategy() = default;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,73 @@
/**
\file ColorTemperatureStrategy.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_COLOR_TEMPERATURE_STRATEGY_H
#define INCLUDE_HUEPLUSPLUS_COLOR_TEMPERATURE_STRATEGY_H
#include <stdint.h>
namespace hueplusplus
{
class Light;
//! Virtual base class for all ColorTemperatureStrategies
class ColorTemperatureStrategy
{
public:
//! \brief Virtual function for changing a lights color temperature in mired
//! with a specified transition.
//!
//! The color temperature in mired ranges from 153 to 500 whereas 153 is cold
//! and 500 is warm. \param mired The color temperature in mired \param
//! transition The time it takes to fade to the new color in multiples of
//! 100ms, 4 = 400ms and should be seen as the default \param light A
//! reference of the light
virtual bool setColorTemperature(unsigned int mired, uint8_t transition, Light& light) const = 0;
//! \brief Virtual function that lets the light perform one breath cycle in
//! the specified color.
//!
//! The color temperature in mired ranges from 153 to 500 whereas 153 is cold
//! and 500 is warm. \param mired The color temperature in mired \param light
//! A reference of the light
virtual bool alertTemperature(unsigned int mired, Light& light) const = 0;
//! \brief Virtual function that returns the current color temperature of the
//! light
//!
//! Should update the lights state by calling refreshState()
//! The color temperature in mired ranges from 153 to 500 whereas 153 is cold
//! and 500 is warm. \param light A reference of the light \return Unsigned
//! int representing the color temperature in mired
virtual unsigned int getColorTemperature(Light& light) const = 0;
//! \brief Virtual function that returns the current color temperature of the
//! light
//!
//! The color temperature in mired ranges from 153 to 500 whereas 153 is cold
//! and 500 is warm. \note This should not update the lights state \param
//! light A const reference of the light \return Unsigned int representing the
//! color temperature in mired
virtual unsigned int getColorTemperature(const Light& light) const = 0;
//! \brief Virtual dtor
virtual ~ColorTemperatureStrategy() = default;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,164 @@
/**
\file ColorUnits.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_UNITS_H
#define INCLUDE_HUEPLUSPLUS_UNITS_H
#include <cstdint>
namespace hueplusplus
{
//! \brief Color in hue and saturation
struct HueSaturation
{
//! \brief Color hue
//!
//! Ranges from 0 to 65535 (16 bit), where 65535 and 0 are red, 25500 is green and 46920 is blue.
int hue;
//! \brief Color saturation
//!
//! Ranges from 0 to 254 (8 bit), where 0 is least saturated (white) and 254 is most saturated (vibrant).
int saturation;
bool operator==(const HueSaturation& other) const { return hue == other.hue && saturation == other.saturation; }
bool operator!=(const HueSaturation& other) const { return !(*this == other); }
};
//! \brief Color in CIE x and y coordinates
struct XY
{
//! \brief x coordinate in CIE, 0 to 1
float x;
//! \brief y coordinate in CIE, 0 to 1
float y;
bool operator==(const XY& other) const { return x == other.x && y == other.y; }
bool operator!=(const XY& other) const { return !(*this == other); }
};
//! \brief Color and brightness in CIE
//!
//! The brightness is needed to convert back to RGB colors if necessary.
//! \note brightness is not the actual luminance of the color, but instead the brightness the light is set to.
struct XYBrightness
{
//! \brief XY color
XY xy;
//! \brief Brightness from 0 to 1
float brightness;
bool operator==(const XYBrightness& other) const { return xy == other.xy && brightness == other.brightness; }
bool operator!=(const XYBrightness& other) const { return !(*this == other); }
};
//! \brief Triangle of representable colors in CIE
//!
//! \note Red, green and blue corner are oriented counter clockwise.
//! \see https://en.wikipedia.org/wiki/Chromaticity
struct ColorGamut
{
//! \brief Red corner in the color triangle
XY redCorner;
//! \brief Green corner in the color triangle
XY greenCorner;
//! \brief Blue corner in the color triangle
XY blueCorner;
//! \brief Check whether \c xy is representable.
bool contains(const XY& xy) const;
//! \brief Correct \c xy to closest representable color.
//! \returns \c xy if it is in the triangle, otherwise the closest point on the border.
XY corrected(const XY& xy) const;
};
//! \brief Predefined ColorGamut%s for Hue API
namespace gamut
{
//! \brief Gamut A, used by most Color Lights
constexpr ColorGamut gamutA {{0.704f, 0.296f}, {0.2151f, 0.7106f}, {0.138f, 0.08f}};
//! \brief Gamut B, used by older Extended Color Lights
constexpr ColorGamut gamutB {{0.675f, 0.322f}, {0.409f, 0.518f}, {0.167f, 0.04f}};
//! \brief Gamut C, used by newer Extended Color Lights
constexpr ColorGamut gamutC {{0.692f, 0.308f}, {0.17f, 0.7f}, {0.153f, 0.048f}};
//! \brief Maximal gamut to be used when unknown
//!
//! \note Most of this triangle is outside of visible colors.
constexpr ColorGamut maxGamut {{1.f, 0.f}, {0.f, 1.f}, {0.f, 0.f}};
} // namespace gamut
//! \brief Color in RGB
struct RGB
{
//! \brief Red amount from 0 to 255
uint8_t r;
//! \brief Green amount from 0 to 255
uint8_t g;
//! \brief Blue amount from 0 to 255
uint8_t b;
bool operator==(const RGB& other) const { return r == other.r && g == other.g && b == other.b; }
bool operator!=(const RGB& other) const { return !(*this == other); }
//! \brief Convert to XYBrightness without clamping
//!
//! Performs gamma correction so the light color matches the screen color better.
XYBrightness toXY() const;
//! \brief Convert to XYBrightness and clip to \c gamut
//!
//! Performs gamma correction so the light color matches the screen color better.
XYBrightness toXY(const ColorGamut& gamut) const;
//! \brief Convert to HueSaturation
//!
//! To get the correct color, set brightness to max(r,g,b).
HueSaturation toHueSaturation() const;
//! \brief Create from XYBrightness
//!
//! Performs gamma correction so the light color matches the screen color better.
//! \note The conversion formula is not exact, it can be off by up to 9 for each channel.
//! This is because the color luminosity is not saved.
static RGB fromXY(const XYBrightness& xy);
//! \brief Create from XYBrightness and clip to \c gamut
//!
//! A light may have XY set out of its range. Then this function returns the actual color
//! the light shows rather than what it is set to.
//! Performs gamma correction so the light color matches the screen color better.
//! \note The conversion formula is not exact, it can be off by up to 9 for each channel.
//! This is because the color luminosity is not saved.
static RGB fromXY(const XYBrightness& xy, const ColorGamut& gamut);
};
//! \brief Const function that converts Kelvin to Mired.
//!
//! \param kelvin Unsigned integer value in Kelvin
//! \return Unsigned integer value in Mired
unsigned int kelvinToMired(unsigned int kelvin);
//! \brief Const function that converts Mired to Kelvin.
//!
//! \param mired Unsigned integer value in Mired
//! \return Unsigned integer value in Kelvin
unsigned int miredToKelvin(unsigned int mired);
} // namespace hueplusplus
#endif
@@ -0,0 +1,168 @@
/**
\file Condition.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_CONDITION_H
#define INCLUDE_HUEPLUSPLUS_CONDITION_H
#include "TimePattern.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! \brief Condition for a Rule
//!
//! The condition checks whether a resource attribute (usually a Sensor value) matches the
//! specified Operator.
//!
//! Conditions from sensors can be created more easily using the makeCondition() helper functions.
class Condition
{
public:
//! \brief Specifies which operation is used to check the condition
enum class Operator
{
eq, //!< Attribute is equal to specified value (for bool and int)
gt, //!< Attribute is greater than specified value (for int)
lt, //!< Attribute is less than specified value (for int)
dx, //!< Attribute has changed (no value given)
ddx, //!< Delayed attribute has changed (no value given)
stable, //!< Stable for a given time. Does not trigger a rule change
notStable, //!< Not stable for a given time. Does not trigger a rule change
in, //!< Time is in the given interval (triggered on start time, local time)
notIn //!< Time is not in the interval (triggered on end time, local time)
};
public:
//! \brief Create a condition from any address on the bridge
//! \param address Path to an attribute of the bridge
//! \param op Operator used for comparison.
//! \param value String representation of the value to check against. Empty for some operators.
Condition(const std::string& address, Operator op, const std::string& value);
//! \brief Get address on the bridge
std::string getAddress() const;
//! \brief Get used operator
Operator getOperator() const;
//! \brief Get value the attribute is checked against
std::string getValue() const;
//! \brief Create the json form of the condition
//! \returns A json object with address, operator and value
nlohmann::json toJson() const;
//! \brief Parse condition from json value
//! \param json Json object with address, operator and value
//! \returns The parsed condition with the same values
//! \throws HueException when the operator is unknown.
static Condition parse(const nlohmann::json& json);
private:
std::string address;
Operator op;
std::string value;
};
namespace detail
{
//! Helper class to make creating conditions more convenient.
//! Specializations for each data type provide methods for the supported operators.
//! This allows the user to write <code>makeCondition(sensor).eq(value)</code>
template <typename T>
class ConditionHelper
{ };
//! General operators supported by all data types
class GeneralConditionHelper
{
public:
explicit GeneralConditionHelper(const std::string& address) : address(address) { }
Condition dx() { return Condition(address, Condition::Operator::dx, ""); }
Condition ddx() { return Condition(address, Condition::Operator::ddx, ""); }
//! Docs does not say anything about format of stable value
//! \todo Change to either duration or int for seconds
Condition stable(const std::string& value) { return Condition(address, Condition::Operator::dx, value); }
protected:
std::string address;
};
//! Operators supported by int conditions
template <>
class ConditionHelper<int> : public GeneralConditionHelper
{
public:
using GeneralConditionHelper::GeneralConditionHelper;
Condition eq(int value) { return create(Condition::Operator::eq, value); }
Condition gt(int value) { return create(Condition::Operator::gt, value); }
Condition lt(int value) { return create(Condition::Operator::eq, value); }
Condition create(Condition::Operator op, int value) { return Condition(address, op, std::to_string(value)); }
};
//! Operators supported by bool conditions
template <>
class ConditionHelper<bool> : public GeneralConditionHelper
{
public:
using GeneralConditionHelper::GeneralConditionHelper;
Condition eq(bool value) { return create(Condition::Operator::eq, value); }
Condition create(Condition::Operator op, bool value) { return Condition(address, op, value ? "true" : "false"); }
};
//! Operators supported by timestamp conditions
template <>
class ConditionHelper<time::AbsoluteTime> : public GeneralConditionHelper
{
public:
using GeneralConditionHelper::GeneralConditionHelper;
Condition in(const time::TimeInterval& interval) { return create(Condition::Operator::in, interval); }
Condition notIn(const time::TimeInterval& interval) { return create(Condition::Operator::notIn, interval); }
Condition create(Condition::Operator op, const time::AbsoluteTime& value)
{
return Condition(address, op, value.toString());
}
Condition create(Condition::Operator op, const time::TimeInterval& interval)
{
return Condition(address, op, interval.toString());
}
};
template <typename... Ts>
struct make_void
{
typedef void type;
};
//! c++17 void_t
template <typename... Ts>
using void_t = typename make_void<Ts...>::type;
} // namespace detail
} // namespace hueplusplus
#endif
@@ -0,0 +1,90 @@
/**
\file EntertainmentMode.h
Copyright Notice\n
Copyright (C) 2020 Adam Honse - developer\n
Copyright (C) 2021 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_HUE_ENTERTAINMENT_MODE_H
#define INCLUDE_HUEPLUSPLUS_HUE_ENTERTAINMENT_MODE_H
#include "Bridge.h"
#include "Group.h"
namespace hueplusplus
{
struct TLSContext;
//! \brief Class for Hue Entertainment Mode
//!
//! Provides methods to initialize and control Entertainment groups.
class EntertainmentMode
{
public:
//! \brief Constructor
//!
//! \param b Bridge reference
//! \param g Group to control in entertainment mode reference
//!
//! \note References are held to both \c b and \c g.
//! They must stay valid until EntertainmentMode ist destroyed.
EntertainmentMode(Bridge& b, Group& g);
//! \brief Destroy the Entertainment Mode object
~EntertainmentMode();
//! \brief Connect and start streaming
//!
//! \return true If conected and ready to receive commands
//! \return false If an error occured
bool connect();
//! \brief Disconnect and stop streaming
//!
//! \return true If disconnected successfully
//! \return false If an error occurred
bool disconnect();
//! \brief Set the color of the given light in RGB format
//!
//! \param light_index Light index inside the group
//! \param red Red color value (0-255)
//! \param green Green color value (0-255)
//! \param blue Blue color value (0-255)
//! \return true If light_index was valid
//! \return false If light_index was invalid
bool setColorRGB(uint8_t light_index, uint8_t red, uint8_t green, uint8_t blue);
//! \brief Update all set colors by \ref setColorRGB
//!
//! \return true If all color values for all lights have ben written/sent
//! \return false If there was an error while writing
bool update();
protected:
Bridge* bridge; //!< Associated bridge
Group* group; //!< Associated group
std::vector<uint8_t> entertainment_msg; //!< buffer containing the entertainment mode packet data
uint8_t entertainment_num_lights; //!< number of lights in entertainment mode group
std::unique_ptr<TLSContext> tls_context; //!< tls context
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,54 @@
/**
\file ExtendedColorHueStrategy.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_EXTENDED_COLOR_HUE_STRATEGY_H
#define INCLUDE_HUEPLUSPLUS_EXTENDED_COLOR_HUE_STRATEGY_H
#include "Light.h"
#include "SimpleColorHueStrategy.h"
namespace hueplusplus
{
//! Class extending the implementation of SimpleColorHueStrategy
//!
//! To be used for lights that have both color and color temperature.
class ExtendedColorHueStrategy : public SimpleColorHueStrategy
{
public:
//! \brief Function that lets the light perform one breath cycle in the
//! specified color.
//! \param hueSat The color in hue and saturation
//! \param light A reference of the light
//!
//! Blocks for the time a \ref Light::alert() needs
bool alertHueSaturation(const HueSaturation& hueSat, Light& light) const override;
//! \brief Function that lets the light perform one breath cycle in the
//! specified color.
//! \param xy The color in XY and brightness
//! \param light A reference of the light
//!
//! Blocks for the time a \ref Light::alert() needs
bool alertXY(const XYBrightness& xy, Light& light) const override;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,46 @@
/**
\file ExtendedColorTemperatureStrategy.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_EXTENDED_COLOR_TEMPERATURE_STRATEGY_H
#define INCLUDE_HUEPLUSPLUS_EXTENDED_COLOR_TEMPERATURE_STRATEGY_H
#include "Light.h"
#include "SimpleColorTemperatureStrategy.h"
namespace hueplusplus
{
//! Class implementing the functions of ColorTemperatureStrategy
class ExtendedColorTemperatureStrategy : public SimpleColorTemperatureStrategy
{
public:
//! \brief Function that lets the light perform one breath cycle in the
//! specified color.
//!
//! It uses this_thread::sleep_for to accomodate for the time an \ref
//! Light::alert() needs The color temperature in mired ranges from 153 to
//! 500 whereas 153 is cold and 500 is warm. \param mired The color
//! temperature in mired \param light A reference of the light
bool alertTemperature(unsigned int mired, Light& light) const override;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,364 @@
/**
\file Group.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_GROUP_H
#define INCLUDE_HUEPLUSPLUS_GROUP_H
#include <string>
#include <vector>
#include "APICache.h"
#include "Action.h"
#include "HueCommandAPI.h"
#include "StateTransaction.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! \brief Class for Groups of lights.
//!
//! Provides methods to control groups.
class Group
{
public:
//! \brief Creates group with shared cache
//! \param id Group id in the bridge
//! \param baseCache Cache of the group list.
Group(int id, const std::shared_ptr<APICache>& baseCache);
//! \brief Creates group with id
//! \param id Group id in the bridge
//! \param commands HueCommandAPI for requests
//! \param refreshDuration Time between refreshing the cached state.
//! \param currentState The current state, may be null.
Group(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration, const nlohmann::json& currentState);
//! \brief Refreshes internal cached state.
//! \param force \c true forces a refresh, regardless of how long the last refresh was ago.
//! \c false to only refresh when enough time has passed (needed e.g. when calling only const methods).
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void refresh(bool force = false);
//! \brief Sets custom refresh interval for this group.
//! \param refreshDuration The new minimum duration between refreshes. May be 0 or \ref c_refreshNever.
void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration);
//! \name General information
///@{
//! \brief Get the group id.
int getId() const;
//! \brief Get the group name.
std::string getName() const;
//! \brief Get the group type.
//!
//! The type is specified on creation and cannot be changed.
//!
//! Possible types:
//! \li <code>0</code>: Special group containing all lights, cannot be modified.
//! \li <code>Luminaire</code>, <code>Lightsource</code>: Automatically created groups for multisource luminaires.
//! \li <code>LightGroup</code>: Standard, user created group, not empty.
//! \li <code>Room</code>: User created room, has room type.
//! \li <code>Entertainment</code>: User created entertainment setup.
//! \li <code>Zone</code>: User created Zone.
std::string getType() const;
//! \brief Get lights in the group.
//! \returns Ids of the lights in the group.
std::vector<int> getLightIds() const;
//! \brief Set group name.
//! \param name New name for the group.
//! Must be unique for all groups, otherwise a number is added.
void setName(const std::string& name);
//! \brief Set group lights.
//! \param ids New light ids. May or may not be empty depending on type.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setLights(const std::vector<int>& ids);
//! \brief Get room type, only for type room.
//! \returns Room type/class of the group.
std::string getRoomType() const;
//! \brief Set room type, only for type room.
//! \param type New room class, case sensitive.
//! Only specific values are allowed.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setRoomType(const std::string& type);
//! \brief Get luminaire model id, only for type luminaire.
//! \returns Unique id for the hardware model.
std::string getModelId() const;
//! \brief Get luminaire model id, only for type luminaire or lightsource.
//! \returns Unique id in <code>AA:BB:CC:DD</code> format for luminaire groups
//! or <code>AA:BB:CC:DD-XX</code> for Lightsource groups.
std::string getUniqueId() const;
//! \brief Get whether all lights are on.
//! \returns true when all lights are on.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
bool getAllOn();
//! \brief Get whether all lights are on.
//! \returns true when all lights are on.
//! \note Does not refresh the state.
bool getAllOn() const;
//! \brief Get whether any light is on.
//! \returns true when any light is on.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
bool getAnyOn();
//! \brief Get whether any light is on.
//! \returns true when any light is on.
//! \note Does not refresh the state.
bool getAnyOn() const;
///@}
//! \name Query Action
//! The action is the state of one light in the group.
//! It can be accessed using these methods.
///@{
//! \brief Get on state of one light in the group.
//! \returns True if the light is on.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
bool getActionOn();
//! \brief Get on state of one light in the group.
//! \returns True if the light is on.
//! \note Does not refresh the state.
bool getActionOn() const;
//! \brief Get hue and saturation of one light in the group.
//! \returns Pair of hue, saturation.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
std::pair<uint16_t, uint8_t> getActionHueSaturation();
//! \brief Get hue and saturation of one light in the group.
//! \returns Pair of hue, saturation.
//! \note Does not refresh the state.
std::pair<uint16_t, uint8_t> getActionHueSaturation() const;
//! \brief Get brightness of one light in the group.
//! \returns Brightness (0-254).
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
unsigned int getActionBrightness();
//! \brief Get brightness of one light in the group.
//! \returns Brightness (0-254).
//! \note Does not refresh the state.
unsigned int getActionBrightness() const;
//! \brief Get color temperature of one light in the group.
//! \returns Color temperature in mired.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
unsigned int getActionColorTemperature();
//! \brief Get color temperature of one light in the group.
//! \returns Color temperature in mired.
//! \note Does not refresh the state.
unsigned int getActionColorTemperature() const;
//! \brief Get color coordinates of one light in the group.
//! \returns Pair of x and y color coordinates.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
std::pair<float, float> getActionColorXY();
//! \brief Get color coordinates of one light in the group.
//! \returns Pair of x and y color coordinates.
//! \note Does not refresh the state.
std::pair<float, float> getActionColorXY() const;
//! \brief Get color mode of one light in the group.
//!
//! The color mode is the currently used way to specify the color (hs,ct or xy).
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
std::string getActionColorMode();
//! \brief Get color mode of one light in the group.
//!
//! The color mode is the currently used way to specify the color (hs,ct or xy).
//! \note Does not refresh the state.
std::string getActionColorMode() const;
///@}
//! \name Change lights
///@{
//! \brief Create a transaction for this group.
//!
//! The transaction can be used to change more than one value in one request.
//!
//! Example usage: \code
//! group.transaction().setBrightness(240).setColorHue(5000).commit();
//! \endcode
StateTransaction transaction();
//! \brief Convenience function to turn lights on.
//! \see StateTransaction::setOn
void setOn(bool on, uint8_t transition = 4);
//! \brief Convenience function to set brightness.
//! \see StateTransaction::setBrightness
void setBrightness(uint8_t brightness, uint8_t transition = 4);
//! \brief Convenience function to set hue and saturation.
//! \see StateTransaction::setColor(const HueSaturation&)
void setColor(const HueSaturation& hueSat, uint8_t transition = 4);
//! \brief Convenience function to set color xy.
//! \see StateTransaction::setColor(const XYBrightness&)
void setColor(const XYBrightness& xy, uint8_t transition = 4);
//! \brief Convenience function to set color temperature.
//! \see StateTransaction::setColorTemperature
void setColorTemperature(unsigned int mired, uint8_t transition = 4);
//! \brief Convenience function to set color loop.
//! \see StateTransaction::setColorLoop
void setColorLoop(bool on, uint8_t transition = 4);
//! \brief Recall scene for the group.
//!
//! Scenes are saved configurations for the lights in a group.
//! \param scene Scene name.
void setScene(const std::string& scene);
//! \brief Get Action to set scene
//! \param scene Scene name
//! \returns A Action that can be used to set the scene on a Schedule
//!
//! To set other light properties in a scene, use transaction().
Action createSceneAction(const std::string& scene) const;
///@}
protected:
//! \brief Utility function to send a put request to the group.
//!
//! \param request The request to send
//! \param subPath A path that is appended to the uri, note it should always start with a slash ("/")
//! \param fileInfo FileInfo from calling function for exception details.
//! \returns The parsed reply
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
nlohmann::json sendPutRequest(const std::string& subPath, const nlohmann::json& request, FileInfo fileInfo);
protected:
int id;
APICache state;
};
//! \brief Parameters necessary for creating a new Group.
//!
//! Provides static functions for each group type that can be created by the user.
//! \note These are not all types that Group::getType() can return,
//! because some types cannot be created manually.
class CreateGroup
{
public:
//! \brief Create a LightGroup.
//!
//! LightGroup is the default type for groups. Empty LightGroups will be deleted.
//! \param lights List of light ids, must not be empty.
//! \param name Name of the new group, optional.
static CreateGroup LightGroup(const std::vector<int>& lights, const std::string& name = "");
//! \brief Create a Room group.
//!
//! Rooms can have a room class and can be empty. Every light can only be in one room.
//! \param lights List of light ids, may be empty.
//! \param name Name of the room, optional.
//! \param roomType Class of the room (case sensitive), optional.
//! Refer to Hue developer documentation for a list of possible room classes.
static CreateGroup Room(
const std::vector<int>& lights, const std::string& name = "", const std::string& roomType = "");
//! \brief Create an Entertainment group.
//!
//! The lights are used in an entertainment setup and can have relative positions.
//! The group can be empty.
//! \param lights List of light ids, may be empty.
//! \param name Name of the group, optional.
static CreateGroup Entertainment(const std::vector<int>& lights, const std::string& name = "");
//! \brief Create a Zone.
//!
//! Zones can be empty, a light can be in multiple zones.
//! \param lights List of light ids, may be empty.
//! \param name Name of the Zone, optional.
static CreateGroup Zone(const std::vector<int>& lights, const std::string& name = "");
//! \brief Get request to create the group.
//! \returns JSON request for a POST to create the new group
nlohmann::json getRequest() const;
protected:
//! \brief Protected constructor, should not be called directly.
//! \param lights List of light ids for the group.
//! \param name Name of the group, empty for default name.
//! \param type Type of the group, empty for default type.
//! \param roomType Room class if type is room, empty for default class or if type is not room.
CreateGroup(
const std::vector<int>& lights, const std::string& name, const std::string& type, const std::string& roomType);
private:
std::vector<int> lights;
std::string name;
std::string type;
std::string roomType;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,146 @@
/**
\file HueCommandAPI.h
Copyright Notice\n
Copyright (C) 2018 Jan Rogall - developer\n
Copyright (C) 2018 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_HUECOMMANDAPI_H
#define INCLUDE_HUEPLUSPLUS_HUECOMMANDAPI_H
#include <atomic>
#include <chrono>
#include <mutex>
#include "HueException.h"
#include "IHttpHandler.h"
namespace hueplusplus
{
//! Handles communication to the bridge via IHttpHandler and enforces a timeout
//! between each request
class HueCommandAPI
{
public:
//! \brief Construct from ip, username and HttpHandler
//!
//! \param ip ip address of the Hue bridge in dotted decimal notation like "192.168.2.1"
//! \param port of the hue bridge
//! \param username username that is used to control the bridge
//! \param httpHandler HttpHandler for communication with the bridge
HueCommandAPI(
const std::string& ip, int port, const std::string& username, std::shared_ptr<const IHttpHandler> httpHandler);
//! \brief Copy construct from other HueCommandAPI
//! \note All copies refer to the same timeout data, so even calls from different objects will be delayed
HueCommandAPI(const HueCommandAPI&) = default;
//! \brief Move construct from other HueCommandAPI
//! \note All copies refer to the same timeout data, so even calls from different objects will be delayed
HueCommandAPI(HueCommandAPI&&) = default;
//! \brief Copy assign from other HueCommandAPI
//! \note All copies refer to the same timeout data, so even calls from different objects will be delayed
HueCommandAPI& operator=(const HueCommandAPI&) = default;
//! \brief Move assign from other HueCommandAPI
//! \note All copies refer to the same timeout data, so even calls from different objects will be delayed
HueCommandAPI& operator=(HueCommandAPI&&) = default;
//! \brief Sends a HTTP PUT request to the bridge and returns the response
//!
//! This function will block until at least Config::getBridgeRequestDelay() has passed to any previous request
//! \param path API request path (appended after /api/{username})
//! \param request Request to the api, may be empty
//! \param fileInfo File information for thrown exceptions.
//! \returns The return value of the underlying \ref IHttpHandler::PUTJson call
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contains no body
//! \throws HueAPIResponseException when response contains an error
nlohmann::json PUTRequest(const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const;
//! \overload
nlohmann::json PUTRequest(const std::string& path, const nlohmann::json& request) const;
//! \brief Sends a HTTP GET request to the bridge and returns the response
//!
//! This function will block until at least Config::getBridgeRequestDelay() has passed to any previous request
//! \param path API request path (appended after /api/{username})
//! \param request Request to the api, may be empty
//! \param fileInfo File information for thrown exceptions.
//! \returns The return value of the underlying \ref IHttpHandler::GETJson call
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contains no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
nlohmann::json GETRequest(const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const;
//! \overload
nlohmann::json GETRequest(const std::string& path, const nlohmann::json& request) const;
//! \brief Sends a HTTP DELETE request to the bridge and returns the response
//!
//! This function will block until at least Config::getBridgeRequestDelay() has passed to any previous request
//! \param path API request path (appended after /api/{username})
//! \param request Request to the api, may be empty
//! \param fileInfo File information for thrown exceptions.
//! \returns The return value of the underlying \ref IHttpHandler::DELETEJson call
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contains no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
nlohmann::json DELETERequest(const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const;
//! \overload
nlohmann::json DELETERequest(const std::string& path, const nlohmann::json& request) const;
//! \brief Sends a HTTP POST request to the bridge and returns the response
//!
//! This function will block until at least Config::getBridgeRequestDelay() has passed to any previous request
//! \param path API request path (appended after /api/{username})
//! \param request Request to the api, may be empty
//! \param fileInfo File information for thrown exceptions.
//! \returns The return value of the underlying \ref IHttpHandler::POSTJson call
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contains no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
nlohmann::json POSTRequest(const std::string& path, const nlohmann::json& request, FileInfo fileInfo) const;
//! \overload
nlohmann::json POSTRequest(const std::string& path, const nlohmann::json& request) const;
//! \brief Combines path with api prefix and username
//! \returns "/api/<username>/<path>"
std::string combinedPath(const std::string& path) const;
private:
struct TimeoutData
{
std::chrono::steady_clock::time_point timeout;
std::mutex mutex;
};
//! \brief Throws an exception if response contains an error, passes though value
//! \throws HueAPIResponseException when response contains an error
//! \returns \ref response if there is no error
nlohmann::json HandleError(FileInfo fileInfo, const nlohmann::json& response) const;
private:
std::string ip;
int port;
std::string username;
std::shared_ptr<const IHttpHandler> httpHandler;
std::shared_ptr<TimeoutData> timeout;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,72 @@
/**
\file HueDeviceTypes.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_HUEDEVICETYPES_H
#define INCLUDE_HUEPLUSPLUS_HUEDEVICETYPES_H
#include <memory>
#include <string>
#include "Light.h"
namespace hueplusplus
{
class LightFactory
{
public:
//! \brief Create a factory for Light%s
//! \param commands HueCommandAPI for communication with the bridge
//! \param refreshDuration Time between refreshing the cached light state.
LightFactory(const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration);
//! \brief Create a Light with the correct type from the JSON state.
//! \param lightState Light JSON as returned from the bridge (not only the "state" part of it).
//! \param id Light id.
//! \param baseCache Optional shared cache for the light.
//! \returns Light with matching id, strategies and \ref ColorType.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when light type is unknown
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
Light createLight(const nlohmann::json& lightState, int id, const std::shared_ptr<APICache>& baseCache = {});
private:
//! \brief Get color type from light JSON.
//! \param lightState Light JSON as returned from the bridge (not only the "state" part of it).
//! \param hasCt Whether the light has color temperature control.
//! \returns The color gamut specified in the light capabilities or,
//! if that does not exist, from a set of known models. Returns GAMUT_X_TEMPERATURE when \ref hasCt is true.
//! \throws HueException when the light has no capabilities and the model is not known.
ColorType getColorType(const nlohmann::json& lightState, bool hasCt) const;
private:
HueCommandAPI commands;
std::chrono::steady_clock::duration refreshDuration;
std::shared_ptr<BrightnessStrategy> simpleBrightness;
std::shared_ptr<ColorTemperatureStrategy> simpleColorTemperature;
std::shared_ptr<ColorTemperatureStrategy> extendedColorTemperature;
std::shared_ptr<ColorHueStrategy> simpleColorHue;
std::shared_ptr<ColorHueStrategy> extendedColorHue;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,121 @@
/**
\file HueException.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_HUE_EXCEPTION_H
#define INCLUDE_HUEPLUSPLUS_HUE_EXCEPTION_H
#include <exception>
#include <string>
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! \brief Contains information about error location, use \ref CURRENT_FILE_INFO to create
struct FileInfo
{
//! \brief Current file name from __FILE__. Empty if unknown
std::string filename;
//! \brief Current line number from __LINE__. -1 if unknown
int line = -1;
//! \brief Current function from __func__. Empty if unknown
std::string func;
//! \brief String representation of func, file and line.
//! \returns "<func> in <filename>:<line>" or "Unknown file" if unknown.
std::string ToString() const;
};
//! \brief Exception class with file information. Base class of all custom exception classes
class HueException : public std::exception
{
public:
//! \brief Creates HueException with information about the error and source
//! \param fileInfo Source of the error. Must not always be the throw location,
//! can also be a calling function which matches the cause better.
//! \param message Human readable error message.
HueException(FileInfo fileInfo, const std::string& message);
//! \brief What message of the exception
//! \returns exception name, file info and constructor message as char* into member string
const char* what() const noexcept override;
//! \brief Filename and line where the exception was thrown or caused by
const FileInfo& GetFile() const noexcept;
protected:
//! \brief Creates HueException with child class name
//!
//! Should be used by subclasses which can append additional information to the end of whatMessage.
//! \param exceptionName class name of the subclass
//! \param fileInfo Source of the error. Must not always be the throw location,
//! can also be a calling function which matches the cause better.
//! \param message Human readable error message
HueException(const char* exceptionName, FileInfo fileInfo, const std::string& message);
private:
std::string whatMessage;
FileInfo fileInfo;
};
//! \brief Exception caused by a Hue API "error" response with additional information
//!
//! Refer to Hue developer documentation for more detail on specific error codes.
class HueAPIResponseException : public HueException
{
public:
//! \brief Create exception with info from Hue API error
//! \param fileInfo Source of the error. Must not always be the throw location,
//! can also be a calling function which matches the cause better.
//! \param error Hue API error code from error response.
//! \param address URI the API call referred to from error response.
//! \param description Error description from response.
HueAPIResponseException(FileInfo fileInfo, int error, std::string address, std::string description);
//! \brief Error number from Hue API error response.
//!
//! Refer to Hue developer documentation for meaning of error codes.
int GetErrorNumber() const noexcept;
//! \brief Address the API call tried to access.
const std::string& GetAddress() const noexcept;
//! \brief Error description
const std::string& GetDescription() const noexcept;
//! \brief Creates exception from API response.
//! \param fileInfo Location of the cause
//! \param response Hue API response. Must contain a member "error" with "type", "address" and "description".
//! \returns HueAPIResponseException with info from the response.
//! If response does not contain the required members, they are defaulted to -1 or "".
static HueAPIResponseException Create(FileInfo fileInfo, const nlohmann::json& response);
private:
//! \brief Creates exception message containing the given information
static std::string GetMessage(int error, const std::string& addr, const std::string& description);
private:
int error;
std::string address;
std::string description;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,29 @@
/**
\file HueException.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#include "HueException.h"
//! \def CURRENT_FILE_INFO
//! \brief Creates the FileInfo for the current line.
#ifndef CURRENT_FILE_INFO
#define CURRENT_FILE_INFO (::hueplusplus::FileInfo{__FILE__, __LINE__, __func__})
#endif
@@ -0,0 +1,200 @@
/**
\file IHttpHandler.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_IHTTPHANDLER_H
#define INCLUDE_HUEPLUSPLUS_IHTTPHANDLER_H
#include <chrono>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! Abstract class for classes that handle http requests and multicast requests
class IHttpHandler
{
public:
//! \brief Virtual dtor
virtual ~IHttpHandler() = default;
//! \brief Send a message to a specified host and return the response.
//!
//! \param msg The message that should be sent to the specified address
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return The response of the host as a string
//! \throws std::system_error when system or socket operations fail
virtual std::string send(const std::string& msg, const std::string& adr, int port = 80) const = 0;
//! \brief Send a message to a specified host and return the body of the response.
//!
//! \param msg The message that should sent to the specified address
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return The body of the response of the host as a string
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
virtual std::string sendGetHTTPBody(const std::string& msg, const std::string& adr, int port = 80) const = 0;
//! \brief Send a multicast request with a specified message.
//!
//! \param msg The message that should sent to the specified multicast address
//! \param adr Optional ip or hostname in dotted decimal notation, default is "239.255.255.250"
//! \param port Optional port the request is sent to, default is 1900
//! \param timeout Optional time to wait for responses, default is 5 seconds
//!
//! Blocks for the duration of the timeout.
//!
//! \return vector of strings containing each received answer
//! \throws std::system_error when system or socket operations fail
virtual std::vector<std::string> sendMulticast(const std::string& msg, const std::string& adr = "239.255.255.250",
int port = 1900, std::chrono::steady_clock::duration timeout = std::chrono::seconds(5)) const = 0;
//! \brief Send a HTTP request with the given method to the specified host and return the body of the response.
//!
//! \param method HTTP method type e.g. GET, HEAD, POST, PUT, DELETE, ...
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
virtual std::string sendHTTPRequest(const std::string& method, const std::string& uri,
const std::string& contentType, const std::string& body, const std::string& adr, int port = 80) const = 0;
//! \brief Send a HTTP GET request to the specified host and return the body of the response.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! that specifies the port to which the request is sent to. Default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
virtual std::string GETString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port = 80) const = 0;
//! \brief Send a HTTP POST request to the specified host and return the body of the response.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! that specifies the port to which the request is sent to. Default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
virtual std::string POSTString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port = 80) const = 0;
//! \brief Send a HTTP PUT request to the specified host and return the body of the response.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! that specifies the port to which the request is sent to. Default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
virtual std::string PUTString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port = 80) const = 0;
//! \brief Send a HTTP DELETE request to the specified host and return the body of the response.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param contentType MIME type of the body data e.g. "text/html", "application/json", ...
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! that specifies the port to which the request is sent to. Default is 80
//! \return Body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
virtual std::string DELETEString(const std::string& uri, const std::string& contentType, const std::string& body,
const std::string& adr, int port = 80) const = 0;
//! \brief Send a HTTP GET request to the specified host and return the body of the response parsed as JSON.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Parsed body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws nlohmann::json::parse_error when the body could not be parsed
virtual nlohmann::json GETJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port = 80) const = 0;
//! \brief Send a HTTP POST request to the specified host and return the body of the response parsed as JSON.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Parsed body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws nlohmann::json::parse_error when the body could not be parsed
virtual nlohmann::json POSTJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port = 80) const = 0;
//! \brief Send a HTTP PUT request to the specified host and return the body of the response parsed as JSON.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Parsed body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws nlohmann::json::parse_error when the body could not be parsed
virtual nlohmann::json PUTJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port = 80) const = 0;
//! \brief Send a HTTP DELETE request to the specified host and return the body of the response parsed as JSON.
//!
//! \param uri Uniform Resource Identifier in the request
//! \param body Request body, may be empty
//! \param adr Ip or hostname in dotted decimal notation like "192.168.2.1"
//! \param port Optional port the request is sent to, default is 80
//! \return Parsed body of the response of the host
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws nlohmann::json::parse_error when the body could not be parsed
virtual nlohmann::json DELETEJson(
const std::string& uri, const nlohmann::json& body, const std::string& adr, int port = 80) const = 0;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,75 @@
/**
\file LibConfig.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_HUE_CONFIG_H
#define INCLUDE_HUEPLUSPLUS_HUE_CONFIG_H
#include <chrono>
namespace hueplusplus
{
//! \brief Configurable delays
//!
//! Used to set all delays to zero when running tests.
class Config
{
private:
using duration = std::chrono::steady_clock::duration;
public:
//! \brief Delay for advanced alerts before the actual alert
duration getPreAlertDelay() const { return preAlertDelay; }
//! \brief Delay for advanced alerts after the actual alert
duration getPostAlertDelay() const { return postAlertDelay; }
//! \brief Timeout for UPnP multicast request
duration getUPnPTimeout() const { return upnpTimeout; }
//! \brief Delay between bridge requests
duration getBridgeRequestDelay() const { return bridgeRequestDelay; }
//! \brief Timeout for Bridge::requestUsername, waits until link button was pressed
duration getRequestUsernameTimeout() const { return requestUsernameDelay; }
//! \brief Interval in which username requests are attempted
duration getRequestUsernameAttemptInterval() const { return requestUsernameAttemptInterval; }
//! \brief Get config instance
static Config& instance()
{
static Config c;
return c;
}
protected:
Config() = default;
protected:
duration preAlertDelay = std::chrono::milliseconds(120);
duration postAlertDelay = std::chrono::milliseconds(1600);
duration upnpTimeout = std::chrono::seconds(5);
duration bridgeRequestDelay = std::chrono::milliseconds(100);
duration requestUsernameDelay = std::chrono::seconds(35);
duration requestUsernameAttemptInterval = std::chrono::seconds(1);
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,624 @@
/**
\file Light.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_HUE_LIGHT_H
#define INCLUDE_HUEPLUSPLUS_HUE_LIGHT_H
#include <memory>
#include "APICache.h"
#include "BaseDevice.h"
#include "BrightnessStrategy.h"
#include "ColorHueStrategy.h"
#include "ColorTemperatureStrategy.h"
#include "HueCommandAPI.h"
#include "StateTransaction.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! enum that specifies the color type of all HueLights
enum class ColorType
{
UNDEFINED, //!< ColorType for this light is unknown or undefined
NONE, //!< light has no specific ColorType
GAMUT_A, //!< light uses Gamut A
GAMUT_B, //!< light uses Gamut B
GAMUT_C, //!< light uses Gamut C
TEMPERATURE, //!< light has color temperature control
GAMUT_A_TEMPERATURE, //!< light uses Gamut A and has color temperature control
GAMUT_B_TEMPERATURE, //!< light uses Gamut B and has color temperature control
GAMUT_C_TEMPERATURE, //!< light uses Gamut C and has color temperature control
GAMUT_OTHER, //!< light uses capabilities to specify a different gamut
GAMUT_OTHER_TEMPERATURE //!< light uses capabilities to specify a different gamut and has color temperature control
};
//! \brief Class for Hue Light fixtures
//!
//! Provides methods to query and control lights.
class Light : public BaseDevice
{
friend class LightFactory;
friend class SimpleBrightnessStrategy;
friend class SimpleColorHueStrategy;
friend class ExtendedColorHueStrategy;
friend class SimpleColorTemperatureStrategy;
friend class ExtendedColorTemperatureStrategy;
public:
//! \name General information
///@{
//! \brief Const function that returns the luminaireuniqueid of the light
//!
//! \note Only working on bridges with versions starting at 1.9
//! \return String containing the luminaireuniqueid or an empty string when the function is not supported
virtual std::string getLuminaireUId() const;
//! \brief Const function that returns the color type of the light.
//!
//! \return ColorType containig the color type of the light
virtual ColorType getColorType() const;
//! \brief Get gamut space of possible light colors
//! \returns Used gamut, or \ref gamut::maxGamut when unknown.
ColorGamut getColorGamut() const;
///@}
//! \name Light state
///@{
//! \brief Function that turns the light on.
//!
//! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms
//! \return true on success
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual bool on(uint8_t transition = 4);
//! \brief Function that turns the light off.
//!
//! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms
//! \return Bool that is true on success
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual bool off(uint8_t transition = 4);
//! \brief Function to check whether a light is on or off
//!
//! \return Bool that is true, when the light is on and false, when off
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual bool isOn();
//! \brief Const function to check whether a light is on or off
//!
//! \note This will not refresh the light state
//! \return Bool that is true, when the light is on and false, when off
virtual bool isOn() const;
//! \brief Const function to check whether this light has brightness control
//!
//! \return Bool that is true when the light has specified abilities and false
//! when not
virtual bool hasBrightnessControl() const { return brightnessStrategy != nullptr; };
//! \brief Const function to check whether this light has color temperature
//! control
//!
//! \return Bool that is true when the light has specified abilities and false
//! when not
virtual bool hasTemperatureControl() const { return colorTemperatureStrategy != nullptr; };
//! \brief Connst function to check whether this light has full color control
//!
//! \return Bool that is true when the light has specified abilities and false
//! when not
virtual bool hasColorControl() const { return colorHueStrategy != nullptr; };
//! \brief Function that sets the brightness of this light.
//!
//! \note The brightness will only be set if the light has a reference to a
//! specific \ref BrightnessStrategy. The brightness can range from 0 = off to
//! 254 = fully lit.
//! \param bri Unsigned int that specifies the brightness
//! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms
//! \return Bool that is true on success
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual bool setBrightness(unsigned int bri, uint8_t transition = 4)
{
if (brightnessStrategy)
{
return brightnessStrategy->setBrightness(bri, transition, *this);
}
return false;
};
//! \brief Const function that returns the brightness of this light.
//!
//! \note The brightness will only be returned if the light has a reference to
//! a specific \ref BrightnessStrategy. \note This will not refresh the light
//! state The brightness can range from 0 = off to 254 = fully lit. \return
//! Unsigned int that is 0 when function failed
virtual unsigned int getBrightness() const
{
if (brightnessStrategy)
{
return brightnessStrategy->getBrightness(*this);
}
return 0;
};
//! \brief Function that returns the brightness of this light.
//!
//! \note The brightness will only be returned if the light has a reference to
//! a specific \ref BrightnessStrategy. The brightness can range from 0 = off
//! to 254 = fully lit.
//! \return Unsigned int that is 0 when function failed
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual unsigned int getBrightness()
{
if (brightnessStrategy)
{
return brightnessStrategy->getBrightness(*this);
}
return 0;
};
//! \brief Function that sets the color temperature of this light in mired.
//!
//! \note The color temperature will only be set if the light has a reference
//! to a specific \ref ColorTemperatureStrategy. The color temperature can
//! range from 153 to 500.
//! \param mired Unsigned int that specifies the color temperature in Mired
//! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms
//! \return Bool that is true on success
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual bool setColorTemperature(unsigned int mired, uint8_t transition = 4)
{
if (colorTemperatureStrategy)
{
return colorTemperatureStrategy->setColorTemperature(mired, transition, *this);
}
return false;
};
//! \brief Const function that returns the current color temperature of the
//! light
//!
//! \note The color temperature will only be returned when the light has a
//! reference to a specific \ref ColorTemperatureStrategy.
//! \note This will not refresh the light state
//! The color temperature in mired ranges from 153 to 500 whereas 153 is cold
//! and 500 is warm.
//! \return Unsigned int representing the color temperature in mired or 0 when failed
virtual unsigned int getColorTemperature() const
{
if (colorTemperatureStrategy)
{
return colorTemperatureStrategy->getColorTemperature(*this);
}
return 0;
};
//! \brief Function that returns the current color temperature of the light
//!
//! \note The color temperature will only be returned when the light has a
//! reference to a specific \ref ColorTemperatureStrategy.
//! Updates the lights state by calling refreshState()
//! The color temperature in mired ranges from 153 to 500 whereas 153 is cold
//! and 500 is warm.
//! \return Unsigned int representing the color temperature in mired or 0 when failed
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual unsigned int getColorTemperature()
{
if (colorTemperatureStrategy)
{
return colorTemperatureStrategy->getColorTemperature(*this);
}
return 0;
};
//! \brief Function to set the color of this light with specified hue.
//!
//! \note The color will only be set if the light has a reference to a
//! specific \ref ColorHueStrategy. The hue can range from 0 to 65535, whereas
//! 65535 and 0 are red, 25500 is green and 46920 is blue.
//! \param hue uint16_t that specifies the hue
//! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms
//! \return Bool that is true on success
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual bool setColorHue(uint16_t hue, uint8_t transition = 4)
{
if (colorHueStrategy)
{
return colorHueStrategy->setColorHue(hue, transition, *this);
}
return false;
};
//! \brief Function to set the color of this light with specified saturation.
//!
//! \note The color will only be set if the light has a reference to a
//! specific \ref ColorHueStrategy. The saturation can range from 0 to 254,
//! whereas 0 is least saturated (white) and 254 is most saturated.
//! \param sat uint8_t that specifies the saturation
//! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms
//! \return Bool that is true on success
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual bool setColorSaturation(uint8_t sat, uint8_t transition = 4)
{
if (colorHueStrategy)
{
return colorHueStrategy->setColorSaturation(sat, transition, *this);
}
return false;
};
//! \brief Function to set the color of this light with specified hue and
//! saturation.
//!
//! \note The color will only be set if the light has a reference to a
//! specific \ref ColorHueStrategy.
//! \param hueSat Color in hue and satuation.
//! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms.
//! \return Bool that is true on success
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual bool setColorHueSaturation(const HueSaturation& hueSat, uint8_t transition = 4)
{
if (colorHueStrategy)
{
return colorHueStrategy->setColorHueSaturation(hueSat, transition, *this);
}
return false;
};
//! \brief Const function that returns the current color of the light as hue
//! and saturation
//!
//! \note The color hue and saturation will only be returned when the light
//! has a reference to a specific \ref ColorHueStrategy.
//! \note This will not refresh the light state
//! \return Current hue and saturation or {0,0} when failed
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual HueSaturation getColorHueSaturation() const
{
if (colorHueStrategy)
{
return colorHueStrategy->getColorHueSaturation(*this);
}
return {};
};
//! \brief Function that returns the current color of the light as hue and
//! saturation
//!
//! \note The color hue and saturation will only be returned when the light
//! has a reference to a specific \ref ColorHueStrategy. Updates the lights
//! state by calling refreshState()
//! \return Current hue and saturation or {0,0} when failed
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual HueSaturation getColorHueSaturation()
{
if (colorHueStrategy)
{
return colorHueStrategy->getColorHueSaturation(*this);
}
return {};
};
//! \brief Function to set the color of this light in CIE with specified x y.
//!
//! \note The color will only be set if the light has a reference to a
//! specific \ref ColorHueStrategy. The values of x and y are ranging from 0 to 1.
//! \param xy The color in XY and brightness
//! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms
//! \return Bool that is true on success
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual bool setColorXY(const XYBrightness& xy, uint8_t transition = 4)
{
if (colorHueStrategy)
{
return colorHueStrategy->setColorXY(xy, transition, *this);
}
return false;
};
//! \brief Const function that returns the current color of the light as xy
//!
//! \note The color x and y will only be returned when the light has a
//! reference to a specific \ref ColorHueStrategy.
//! \note This does not update the lights state
//! \return XYBrightness with x, y and brightness or an empty one (all 0) when failed
virtual XYBrightness getColorXY() const
{
if (colorHueStrategy)
{
return colorHueStrategy->getColorXY(*this);
}
return {};
};
//! \brief Function that returns the current color of the light as xy
//!
//! \note The color x and y will only be returned when the light has a
//! reference to a specific \ref ColorHueStrategy.
//! Updates the lights state by calling refreshState()
//! \return XYBrightness with x, y and brightness or an empty one (all 0) when failed
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual XYBrightness getColorXY()
{
if (colorHueStrategy)
{
return colorHueStrategy->getColorXY(*this);
}
return {};
}
//! \brief Function to set the color of this light with red green and blue
//! values.
//!
//! \note The color will only be set if the light has a reference to a
//! specific \ref ColorHueStrategy. The values of red, green and blue are
//! ranging from 0 to 255.
//! \param rgb RGB color that will be mapped to the available color space
//! \param transition Optional parameter to set the transition from current state to new, standard is 4 = 400ms
//! \return Bool that is true on success
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual bool setColorRGB(const RGB& rgb, uint8_t transition = 4)
{
if (colorHueStrategy)
{
return colorHueStrategy->setColorXY(rgb.toXY(getColorGamut()), transition, *this);
}
return false;
}
//! \brief Function that lets the light perform one breath cycle.
//!
//! Can be used for locating a light.
//! \return bool that is true on success
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual bool alert();
//! \brief Function that lets the light perform one breath cycle in specified
//! color temperature.
//!
//! \note The breath cylce will only be performed if the light has a reference
//! to a specific \ref ColorTemperatureStrategy.
//! \param mired Color temperature in mired
//! \return Bool that is true on success
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual bool alertTemperature(unsigned int mired)
{
if (colorTemperatureStrategy)
{
return colorTemperatureStrategy->alertTemperature(mired, *this);
}
return false;
}
//! \brief Function that lets the light perform one breath cycle in specified
//! color.
//!
//! \note The breath cylce will only be performed if the light has a reference
//! to a specific \ref ColorHueStrategy.
//! \param hueSat Color in hue and saturation
//! \return Bool that is true on success
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual bool alertHueSaturation(const HueSaturation& hueSat)
{
if (colorHueStrategy)
{
return colorHueStrategy->alertHueSaturation(hueSat, *this);
}
return false;
}
//! \brief Function that lets the light perform one breath cycle in specified
//! color.
//!
//! \note The breath cylce will only be performed if the light has a reference
//! to a specific \ref ColorHueStrategy.
//! \param xy The x,y coordinates in CIE and brightness
//! \return Bool that is true on success
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual bool alertXY(const XYBrightness& xy)
{
if (colorHueStrategy)
{
return colorHueStrategy->alertXY(xy, *this);
}
return false;
}
//! \brief Function to turn colorloop effect on/off.
//!
//! Notice this function will only be performed light has a reference to a
//! specific \ref ColorHueStrategy. The colorloop effect will loop through all
//! colors on current hue and saturation levels. Notice that none of the
//! setter functions check whether this feature is enabled and the colorloop
//! can only be disabled with this function or by simply calling
//! off() and then on(), so you could
//! alternatively call off() and then use any of the setter functions.
//! \param on bool that enables this feature when true and disables it when false
//! \return Bool that is true on success
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
virtual bool setColorLoop(bool on)
{
if (colorHueStrategy)
{
return colorHueStrategy->setColorLoop(on, *this);
}
return false;
}
//! \brief Create a transaction for this light.
//!
//! The transaction can be used to change more than one value in one request.
//! Only use the functions supported by the current light type.
//!
//! Example usage: \code
//! light.transaction().setBrightness(240).setColorHue(5000).commit();
//! \endcode
virtual StateTransaction transaction();
///@}
protected:
//! \brief Protected ctor that is used by \ref LightFactory.
//!
//! \param id Integer that specifies the id of this light
//! \param commands HueCommandAPI for communication with the bridge
//!
//! leaves strategies unset
Light(int id, const HueCommandAPI& commands);
//! \brief Protected ctor that is used by \ref LightFactory.
//!
//! \param id Integer that specifies the id of this light
//! \param baseCache Cache of the light list (must not be null).
//!
//! leaves strategies unset
Light(int id, const std::shared_ptr<APICache>& baseCache);
//! \brief Protected ctor that is used by \ref LightFactory, also sets
//! strategies.
//!
//! \param id Integer that specifies the id of this light
//! \param commands HueCommandAPI for communication with the bridge
//! \param brightnessStrategy Strategy for brightness. May be nullptr.
//! \param colorTempStrategy Strategy for color temperature. May be nullptr.
//! \param colorHueStrategy Strategy for color hue/saturation. May be nullptr.
//! \param refreshDuration Time between refreshing the cached state.
//! Can be 0 to always refresh, or steady_clock::duration::max() to never refresh.
//! \param currentState The current light state, may be null.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
Light(int id, const HueCommandAPI& commands, std::shared_ptr<const BrightnessStrategy> brightnessStrategy,
std::shared_ptr<const ColorTemperatureStrategy> colorTempStrategy,
std::shared_ptr<const ColorHueStrategy> colorHueStrategy, std::chrono::steady_clock::duration refreshDuration,
const nlohmann::json& currentState);
//! \brief Protected function that sets the brightness strategy.
//!
//! The strategy defines how specific commands that deal with brightness
//! control are executed \param strat a strategy of type \ref
//! BrightnessStrategy
virtual void setBrightnessStrategy(std::shared_ptr<const BrightnessStrategy> strat)
{
brightnessStrategy = std::move(strat);
};
//! \brief Protected function that sets the colorTemperature strategy.
//!
//! The strategy defines how specific commands that deal with colortemperature
//! control are executed \param strat a strategy of type \ref
//! ColorTemperatureStrategy
virtual void setColorTemperatureStrategy(std::shared_ptr<const ColorTemperatureStrategy> strat)
{
colorTemperatureStrategy = std::move(strat);
};
//! \brief Protected function that sets the colorHue strategy.
//!
//! The strategy defines how specific commands that deal with color control
//! are executed \param strat a strategy of type \ref ColorHueStrategy
virtual void setColorHueStrategy(std::shared_ptr<const ColorHueStrategy> strat)
{
colorHueStrategy = std::move(strat);
};
protected:
ColorType colorType; //!< holds the \ref ColorType of the light
std::shared_ptr<const BrightnessStrategy>
brightnessStrategy; //!< holds a reference to the strategy that handles brightness commands
std::shared_ptr<const ColorTemperatureStrategy>
colorTemperatureStrategy; //!< holds a reference to the strategy that handles colortemperature commands
std::shared_ptr<const ColorHueStrategy>
colorHueStrategy; //!< holds a reference to the strategy that handles all color commands
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,63 @@
/**
\file LinHttpHandler.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_LINHTTPHANDLER_H
#define INCLUDE_HUEPLUSPLUS_LINHTTPHANDLER_H
#include <string>
#include <vector>
#include "BaseHttpHandler.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! Class to handle http requests and multicast requests on linux systems
class LinHttpHandler : public BaseHttpHandler
{
public:
//! \brief Function that sends a given message to the specified host and
//! returns the response.
//!
//! \param msg String that contains the message that is sent to the specified
//! address \param adr String that contains an ip or hostname in dotted
//! decimal notation like "192.168.2.1" \param port Optional integer that
//! specifies the port to which the request is sent to. Default is 80 \return
//! String containing the response of the host
virtual std::string send(const std::string& msg, const std::string& adr, int port = 80) const override;
//! \brief Function that sends a multicast request with the specified message.
//!
//! \param msg String that contains the request that is sent to the specified
//! address \param adr Optional String that contains an ip or hostname in
//! dotted decimal notation, default is "239.255.255.250" \param port Optional
//! integer that specifies the port to which the request is sent. Default is
//! 1900 \param timeout Optional The timeout of the
//! request. Default is 5 seconds \return Vector containing strings of each
//! answer received
std::vector<std::string> sendMulticast(const std::string& msg, const std::string& adr = "239.255.255.250",
int port = 1900, std::chrono::steady_clock::duration timeout = std::chrono::seconds(5)) const override;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,40 @@
/**
\file ModelPictures.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_MODEL_PICTURES_H
#define INCLUDE_HUEPLUSPLUS_MODEL_PICTURES_H
#include <string>
namespace hueplusplus
{
//! \brief Get the picture name of a given model id
//!
//! \note This function will only return the filename without extension,
//! because Philips provides different file types.
//! \param modelId Model Id of a device to get the picture of
//! \returns String that either contains the filename of the picture of the device
//! or an empty string if it was not found.
std::string getPictureOfModel(const std::string& modelId);
}
#endif
@@ -0,0 +1,69 @@
/**
\file NewDeviceList.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_NEW_DEVICE_LIST_H
#define INCLUDE_HUEPLUSPLUS_NEW_DEVICE_LIST_H
#include <map>
#include <string>
#include "TimePattern.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! \brief List of new devices found during the last scan
class NewDeviceList
{
public:
//! \brief Construct from data
NewDeviceList(const std::string& lastScan, const std::map<int, std::string>& devices);
//! \brief Get a map of id and name of new devices
const std::map<int, std::string>& getNewDevices() const;
//! \brief Get whether a last scan time is available
//!
//! This can be false if there was no scan since the last restart
//! or if the scan is still running.
bool hasLastScanTime() const;
//! \brief Get whether scan is currently active
//!
//! When scan is active, no last scan time is available
bool isScanActive();
//! \brief Get time when last scan was completed
//! \throws HueException when no time is available or timestamp is invalid
//! \note Must only be called when \ref hasLastScanTime() is true.
time::AbsoluteTime getLastScanTime() const;
//! \brief Parse from json response
//! \throws std::invalid_argument when json is invalid.
//! \throws nlohmann::json::exception when json is invalid.
static NewDeviceList parse(const nlohmann::json& json);
private:
std::string lastScan;
std::map<int, std::string> devices;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,361 @@
/**
\file ResourceList.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_RESOURCE_LIST_H
#define INCLUDE_HUEPLUSPLUS_RESOURCE_LIST_H
#include <functional>
#include <map>
#include <string>
#include <vector>
#include "APICache.h"
#include "HueException.h"
#include "NewDeviceList.h"
#include "Utils.h"
namespace hueplusplus
{
//! \brief Handles a list of a certain API resource
//! \tparam Resource Resource type that is in the list
//! \tparam IdT Type of the resource id. int or std::string
//!
//! The resources are assumed to be in an object with ids as keys.
//! The Resource class needs a constructor that accepts \c id, HueCommandAPI, \c refreshDuration and \c state;
//! otherwise a factory function needs to be provided that takes \c id, \c state
//! and a base cache that is null when shared state is disabled.
template <typename Resource, typename IdT>
class ResourceList
{
public:
using ResourceType = Resource;
using IdType = IdT;
static_assert(std::is_integral<IdType>::value || std::is_same<std::string, IdType>::value,
"IdType must be integral or string");
//! \brief Construct ResourceList using a base cache and optional factory function
//! \param baseCache Base cache which holds the parent state, not nullptr
//! \param cacheEntry Entry name of the list state in the base cache
//! \param refreshDuration Interval between refreshing the cache
//! \param sharedState Whether created resources should share the same base cache.
//! \param factory Optional factory function to create Resources.
//! Necessary if Resource is not constructible as described above.
ResourceList(std::shared_ptr<APICache> baseCache, const std::string& cacheEntry,
std::chrono::steady_clock::duration refreshDuration, bool sharedState = false,
const std::function<Resource(IdType, const nlohmann::json&, const std::shared_ptr<APICache>&)>& factory
= nullptr)
: stateCache(std::make_shared<APICache>(baseCache, cacheEntry, refreshDuration)),
factory(factory),
path(stateCache->getRequestPath() + '/'),
sharedState(sharedState)
{ }
//! \brief Construct ResourceList with a separate cache and optional factory function
//! \param commands HueCommandAPI for requests
//! \param path Path of the resource list
//! \param refreshDuration Interval between refreshing the cache
//! \param factory Optional factory function to create Resources.
//! Necessary if Resource is not constructible as described above.
ResourceList(const HueCommandAPI& commands, const std::string& path,
std::chrono::steady_clock::duration refreshDuration,
const std::function<Resource(IdType, const nlohmann::json&, const std::shared_ptr<APICache>&)>& factory
= nullptr)
: stateCache(std::make_shared<APICache>(path, commands, refreshDuration, nullptr)),
factory(factory),
path(path + '/'),
sharedState(false)
{ }
//! \brief Deleted copy constructor
ResourceList(const ResourceList&) = delete;
//! \brief Deleted copy assignment
ResourceList& operator=(const ResourceList&) = delete;
//! \brief Refreshes internal state now
void refresh() { stateCache->refresh(); }
//! \brief Sets custom refresh interval for this list and all resources created.
//! \param refreshDuration The new minimum duration between refreshes. May be 0 or \ref c_refreshNever.
void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration)
{
stateCache->setRefreshDuration(refreshDuration);
}
//! \brief Get all resources that exist
//! \returns A vector of references to every Resource
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contains no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
std::vector<Resource> getAll()
{
nlohmann::json& state = stateCache->getValue();
std::vector<Resource> result;
result.reserve(state.size());
for (auto it = state.begin(); it != state.end(); ++it)
{
result.emplace_back(construct(maybeStoi(it.key()), it.value()));
}
return result;
}
//! \brief Get resource specified by id
//! \param id Identifier of the resource
//! \returns The resource matching the id
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when id does not exist
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
Resource get(const IdType& id)
{
const nlohmann::json& state = stateCache->getValue();
std::string key = maybeToString(id);
if (!state.count(key))
{
throw HueException(FileInfo {__FILE__, __LINE__, __func__}, "Resource id is not valid");
}
return construct(id, state[key]);
}
//! \brief Checks whether resource with id exists
//! \param id Identifier of the resource to check
//! \returns true when the resource with given id exists
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contains no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
bool exists(const IdType& id) { return stateCache->getValue().count(maybeToString(id)) != 0; }
//! \brief Checks whether resource with id exists
//! \param id Identifier of the resource to check
//! \returns true when the resource with given id exists
//! \note This will not update the cache
//! \throws HueException when the cache is empty
bool exists(const IdType& id) const { return stateCache->getValue().count(maybeToString(id)) != 0; }
//! \brief Removes the resource
//! \param id Identifier of the resource to remove
//! \returns true on success
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contains no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
//!
//! If successful, invalidates references to the Resource removed.
bool remove(const IdType& id)
{
std::string requestPath = path + maybeToString(id);
nlohmann::json result = stateCache->getCommandAPI().DELETERequest(
requestPath, nlohmann::json::object(), FileInfo {__FILE__, __LINE__, __func__});
bool success = utils::safeGetMember(result, 0, "success") == requestPath + " deleted";
return success;
}
protected:
//! \brief Calls std::stoi if IdType is int
static IdType maybeStoi(const std::string& key) { return maybeStoi(key, std::is_integral<IdType> {}); }
//! \brief Calls std::to_string if IdType is int
static std::string maybeToString(const IdType& id) { return maybeToString(id, std::is_integral<IdType> {}); }
//! \brief Constructs resource using factory or constructor, if available
//! \throws HueException when factory is nullptr and Resource cannot be constructed as specified above.
Resource construct(const IdType& id, const nlohmann::json& state)
{
return construct(id, state,
std::is_constructible<Resource, IdType, HueCommandAPI, std::chrono::steady_clock::duration,
const nlohmann::json&> {});
}
//! \brief Protected defaulted move constructor
ResourceList(ResourceList&&) = default;
//! \brief Protected defaulted move assignment
ResourceList& operator=(ResourceList&&) = default;
private:
// Resource is constructible
Resource construct(const IdType& id, const nlohmann::json& state, std::true_type)
{
if (factory)
{
return factory(id, state, sharedState ? stateCache : std::shared_ptr<APICache>());
}
else
{
if (sharedState)
{
return Resource(id, stateCache);
}
else
{
return Resource(id, stateCache->getCommandAPI(), stateCache->getRefreshDuration(), state);
}
}
}
// Resource is not constructible
Resource construct(const IdType& id, const nlohmann::json& state, std::false_type)
{
if (!factory)
{
throw HueException(FileInfo {__FILE__, __LINE__, __func__},
"Resource is not constructable with default parameters, but no factory given");
}
return factory(id, state, sharedState ? stateCache : std::shared_ptr<APICache>());
}
private:
static IdType maybeStoi(const std::string& key, std::true_type) { return std::stoi(key); }
static IdType maybeStoi(const std::string& key, std::false_type) { return key; }
static std::string maybeToString(IdType id, std::true_type) { return std::to_string(id); }
static std::string maybeToString(const IdType& id, std::false_type) { return id; }
protected:
std::shared_ptr<APICache> stateCache;
std::function<Resource(IdType, const nlohmann::json&, const std::shared_ptr<APICache>&)> factory;
std::string path;
bool sharedState;
};
//! \brief Handles a ResourceList of physical devices which can be searched for
//! \tparam Resource Resource type that is in the list
template <typename Resource>
class SearchableResourceList : public ResourceList<Resource, int>
{
public:
using ResourceList<Resource, int>::ResourceList;
//! \brief Start search for new devices
//! \param deviceIds Serial numbers of the devices to search for (max. 10)
//!
//! Takes more than 40s. If many devices were found a second search command might be necessary.
void search(const std::vector<std::string>& deviceIds = {})
{
std::string requestPath = this->path;
// Remove trailing slash
requestPath.pop_back();
if (deviceIds.empty())
{
this->stateCache->getCommandAPI().POSTRequest(
requestPath, nlohmann::json::object(), FileInfo {__FILE__, __LINE__, __func__});
}
else
{
this->stateCache->getCommandAPI().POSTRequest(
requestPath, nlohmann::json {{"deviceid", deviceIds}}, FileInfo {__FILE__, __LINE__, __func__});
}
}
//! \brief Get devices found in last search
NewDeviceList getNewDevices() const
{
nlohmann::json response = this->stateCache->getCommandAPI().GETRequest(
this->path + "new", nlohmann::json::object(), FileInfo {__FILE__, __LINE__, __func__});
return NewDeviceList::parse(response);
}
protected:
//! \brief Protected defaulted move constructor
SearchableResourceList(SearchableResourceList&&) = default;
//! \brief Protected defaulted move assignment
SearchableResourceList& operator=(SearchableResourceList&&) = default;
};
//! \brief Handles a ResourceList where Resources can be added by the user
//! \tparam BaseResourceList Base resource list type (ResourceList or SearchableResourceList).
//! \tparam CreateType Type that provides parameters for creation.
//! Must have a const getRequest() function returning the JSON for the POST request.
template <typename BaseResourceList, typename CreateType>
class CreateableResourceList : public BaseResourceList
{
public:
using BaseResourceList::BaseResourceList;
//! \brief Create a new resource
//! \param params Parameters for the new resource
//! \returns The id of the created resource or 0/an empty string if failed.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contains no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
//! \throws std::invalid_argument when IdType is int and std::stoi fails
typename BaseResourceList::IdType create(const CreateType& params)
{
std::string requestPath = this->path;
// Remove slash
requestPath.pop_back();
nlohmann::json response = this->stateCache->getCommandAPI().POSTRequest(
requestPath, params.getRequest(), FileInfo {__FILE__, __LINE__, __func__});
nlohmann::json id = utils::safeGetMember(response, 0, "success", "id");
if (id.is_string())
{
std::string idStr = id.get<std::string>();
if (idStr.find(this->path) == 0)
{
idStr.erase(0, this->path.size());
}
this->stateCache->refresh();
return this->maybeStoi(idStr);
}
return typename BaseResourceList::IdType {};
}
protected:
//! \brief Protected defaulted move constructor
CreateableResourceList(CreateableResourceList&&) = default;
//! \brief Protected defaulted move assignment
CreateableResourceList& operator=(CreateableResourceList&&) = default;
};
//! \brief Handles a group list with the special group 0
//! \tparam Resource Resource type that is in the list
//! \tparam CreateType Type that provides parameters for creation.
//! Must have a const getRequest() function returning the JSON for the POST request.
template <typename Resource, typename CreateType>
class GroupResourceList : public CreateableResourceList<ResourceList<Resource, int>, CreateType>
{
using Base = CreateableResourceList<ResourceList<Resource, int>, CreateType>;
public:
using Base::Base;
//! \brief Get group, specially handles group 0
//! \see ResourceList::get
Resource get(const int& id)
{
const nlohmann::json& state = this->stateCache->getValue();
std::string key = this->maybeToString(id);
if (!state.count(key) && id != 0)
{
throw HueException(FileInfo {__FILE__, __LINE__, __func__}, "Resource id is not valid");
}
return this->construct(id, id == 0 ? nlohmann::json {nullptr} : state[key]);
}
//! \brief Get group, specially handles group 0
//! \see ResourceList::exists
bool exists(int id) const { return id == 0 || Base::exists(id); }
protected:
//! \brief Protected defaulted move constructor
GroupResourceList(GroupResourceList&&) = default;
//! \brief Protected defaulted move assignment
GroupResourceList& operator=(GroupResourceList&&) = default;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,175 @@
/**
\file Rule.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_RULE_H
#define INCLUDE_HUEPLUSPLUS_RULE_H
#include <string>
#include "APICache.h"
#include "Action.h"
#include "Condition.h"
#include "HueCommandAPI.h"
#include "TimePattern.h"
namespace hueplusplus
{
//! \brief Rule stored in the bridge.
//!
//! Rules are used to automatically trigger Action%s when certain events happen.
//! The bridge can only support a limited number of rules, conditions and actions.
//!
//! They are deactivated if any errors occur when they are evaluated.
class Rule
{
public:
//! \brief Creates rule with shared cache
//! \param id Rule id in the bridge
//! \param baseCache Cache of the rule list.
Rule(int id, const std::shared_ptr<APICache>& baseCache);
//! \brief Creates rule with id
//! \param id Rule id in the bridge
//! \param commands HueCommandAPI for requests
//! \param refreshDuration Time between refreshing the cached state.
//! \param currentState The current state, may be null.
Rule(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration, const nlohmann::json& currentState);
//! \brief Refreshes internal cached state.
//! \param force \c true forces a refresh, regardless of how long the last refresh was ago.
//! \c false to only refresh when enough time has passed (needed e.g. when calling only const methods).
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void refresh(bool force = false);
//! \brief Sets custom refresh interval for this rule.
//! \param refreshDuration The new minimum duration between refreshes. May be 0 or \ref c_refreshNever.
void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration);
//! \brief Get rule identifier
int getId() const;
//! \brief Get rule name
//!
//! The rule name is always unique for the bridge.
std::string getName() const;
//! \brief Set rule name.
//! \param name New name for the rule.
//! Must be unique for all rules, otherwise a number is added.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setName(const std::string& name);
//! \brief Get created time
time::AbsoluteTime getCreated() const;
//! \brief Get time the rule was last triggered
time::AbsoluteTime getLastTriggered() const;
//! \brief Get the number of times the rule was triggered
int getTimesTriggered() const;
//! \brief Get whether rule is enabled or disabled
bool isEnabled() const;
//! \brief Enable or disable rule.
//! \param enabled whether the rule is triggered.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setEnabled(bool enabled);
//! \brief Get user that created or last changed the rule.
std::string getOwner() const;
//! \brief Get the conditions that have to be met
//!
//! The rule triggers the actions when all conditions are true.
//! At least one condition must exist.
std::vector<Condition> getConditions() const;
//! \brief Get the actions that are executed
//!
//! At least one action must exist.
std::vector<Action> getActions() const;
//! \brief Set conditions for the rule
//! \param conditions All conditions that need to be fulfilled. Must not be empty.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setConditions(const std::vector<Condition>& conditions);
//! \brief Set actions for the rule
//! \param actions The actions that are triggered when the conditions are met.
//! Must not be empty.
void setActions(const std::vector<Action>& actions);
private:
//! \brief Utility function to send a put request to the group.
//!
//! \param request The request to send
//! \param fileInfo FileInfo from calling function for exception details.
//! \returns The parsed reply
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
nlohmann::json sendPutRequest(const nlohmann::json& request, FileInfo fileInfo);
private:
int id;
APICache state;
};
//! \brief Parameters for creating a new Rule.
//!
//! Can be used like a builder object with chained calls.
class CreateRule
{
public:
//! \brief Construct with necessary parameters
//! \param conditions Conditions for the rule. Must not be empty
//! \param actions Actions for the rule. Must not be empty
CreateRule(const std::vector<Condition>& conditions, const std::vector<Action>& actions);
//! \brief Set name
//! \see Rule::setName
CreateRule& setName(const std::string& name);
//! \brief Set status
//! \see Rule::setEnabled
CreateRule& setStatus(bool enabled);
//! \brief Get request to create the rule.
//! \returns JSON request for a POST to create the new rule.
nlohmann::json getRequest() const;
private:
nlohmann::json request;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,305 @@
/**
\file Scene.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_SCENE_H
#define INCLUDE_HUEPLUSPLUS_SCENE_H
#include <map>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include "APICache.h"
#include "ColorUnits.h"
#include "TimePattern.h"
namespace hueplusplus
{
//! \brief Immutable state of a light
class LightState
{
public:
//! \brief Create LightState from json
//! \note Use LightStateBuilder for easier creation.
explicit LightState(const nlohmann::json& state);
//! \brief Get whether the light is on
bool isOn() const;
//! \brief Get whether a brightness is stored
bool hasBrightness() const;
//! \brief Get brightness of the light
//! \returns Stored brightness, or 0
int getBrightness() const;
//! \brief Get whether hue and saturation is stored
bool hasHueSat() const;
//! \brief Get hue and saturation of the light
//! \returns Stored hue and saturation, or {0,0} if not stored
HueSaturation getHueSat() const;
//! \brief Get whether xy color is stored
bool hasXY() const;
//! \brief Get xy color of the light
//! \returns Stored x,y and brightness, or zeros if not stored
XYBrightness getXY() const;
//! \brief Get whether color temperature is stored
bool hasCt() const;
//! \brief Get color temperature of the light
//! \returns Stored color temperature in mired, or 0 if not stored
int getCt() const;
//! \brief Get whether effect is stored
bool hasEffect() const;
//! \brief Get whether colorloop effect is active
//! \returns true when colorloop is enabled, false otherwise or if not stored
bool getColorloop() const;
//! \brief Get transition time to this light state
//! \returns Stored transition time or 4 by default
int getTransitionTime() const;
//! \brief Convert to json representation
nlohmann::json toJson() const;
//! \brief Equality comparison
bool operator==(const LightState& other) const;
//! \brief Inequality comparison
bool operator!=(const LightState& other) const;
private:
nlohmann::json state;
};
//! \brief Builder to create LightState
class LightStateBuilder
{
public:
LightStateBuilder& setOn(bool on);
LightStateBuilder& setBrightness(int brightness);
LightStateBuilder& setHueSat(const HueSaturation& hueSat);
LightStateBuilder& setXY(const XY& xy);
LightStateBuilder& setCt(int mired);
LightStateBuilder& setColorloop(bool enabled);
LightStateBuilder& setTransitionTime(int time);
LightState create();
private:
nlohmann::json state;
};
//! \brief Scene stored in the bridge
//!
//! Scenes bundle the state of multiple lights so it can be recalled later.
class Scene
{
public:
//! \brief Type of the scen
enum class Type
{
lightScene, //!< The scene affects specific lights
groupScene //!< The scene affects all light of a specific group
};
public:
//! \brief Creates scene with shared cache
//! \param id Scene id in the bridge
//! \param baseCache Cache of the scene list.
Scene(const std::string& id, const std::shared_ptr<APICache>& baseCache);
//! \brief Construct existing Scene
//! \param id Scene id
//! \param commands HueCommandAPI for requests
//! \param refreshDuration Time between refreshing the cached state
//! \param currentState The current state, may be null.
Scene(const std::string& id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration, const nlohmann::json& currentState);
//! \brief Refreshes internal cached state
//! \param force \c true forces a refresh, regardless of how long the last refresh was ago.
//! \c false to only refresh when enough time has passed (needed e.g. when calling only const methods).
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void refresh(bool force = false);
//! \brief Sets custom refresh interval for this group.
//! \param refreshDuration The new minimum duration between refreshes. May be 0 or \ref c_refreshNever.
void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration);
//! \brief Get scene identifier
std::string getId() const;
//! \brief Get scene name
//!
//! The scene name is always unique for the bridge. It defaults to the id.
std::string getName() const;
//! \brief Set scene name
//! \param name New name for the scene.
//! Must be unique for all schedules, otherwise a number is added.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setName(const std::string& name);
//! \brief Get scene type
//!
//! GroupScenes are deleted when the group is deleted.
Type getType() const;
//! \brief Get group id for a GroupScene
//! \returns Group id or 0 if the scene is a LightScene.
int getGroupId() const;
//! \brief Get light ids
//!
//! For a GroupScene, the light ids are the lights in the group.
std::vector<int> getLightIds() const;
//! \brief Set light ids for LightScene
//! \param ids New light ids
//!
//! Light ids cannot be changed on GroupScene. Change the lights in the group instead.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setLightIds(const std::vector<int>& ids);
//! \brief Get user that created or last changed the scene.
std::string getOwner() const;
//! \brief Get whether the scene can be automatically deleted
bool getRecycle() const;
//! \brief Get whether scene is locked by a rule or schedule
bool isLocked() const;
//! \brief Get app specific data
std::string getAppdata() const;
//! \brief Get version of app specific data
int getAppdataVersion() const;
//! \brief Set app specific data
//! \param data Custom data in any format, max length 16.
//! \param version Version of the data
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setAppdata(const std::string& data, int version);
//! \brief Get picture, reserved for future use.
//!
//! Currently always an empty string.
std::string getPicture() const;
//! \brief Get time the scene was created/updated.
time::AbsoluteTime getLastUpdated() const;
//! \brief Get version of the scene
//! \returns 1 for legacy scene without lightstates
//! \returns 2 for updated scenes with lightstates
int getVersion() const;
//! \brief Get stored states of the lights
//! \returns LightStates for each light in the scene, or an empty map for legacy scenes.
std::map<int, LightState> getLightStates() const;
//! \brief Set light states
//! \param states New states for each light in the scene.
//! Should contain exactly the lights in the scene. Additional states might cause an error.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setLightStates(const std::map<int, LightState>& states);
//! \brief Store current light state of every light in the scene
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void storeCurrentLightState();
//! \brief Store current light state and update transition time
//! \param transition The updated transition time to this scene
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void storeCurrentLightState(int transition);
//! \brief Recall scene, putting every light in the stored state
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void recall();
private:
//! \brief Send put request to specified sub path
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void sendPutRequest(const std::string& path, const nlohmann::json& request, FileInfo fileInfo);
private:
std::string id;
APICache state;
};
//! \brief Parameters for creating a new Scene
//!
//! Can be used like a builder object with chained calls.
class CreateScene
{
public:
//! \brief Set name
//! \see Scene::setName
CreateScene& setName(const std::string& name);
//! \brief Set group id, making the scene a GroupScene
//! \param id Group id for the scene, not 0
//!
//! The group id cannot be changed after the scene was created.
//! \throws HueException when used after setLightIds
CreateScene& setGroupId(int id);
//! \brief Set light ids, making the scene a LightScene
//! \param ids Ids of lights in the scene
//! \throws HueException when used after setGroupId
CreateScene& setLightIds(const std::vector<int>& ids);
//! \brief Set whether the scene can be automatically deleted
//!
//! Cannot be changed after the scene was created.
CreateScene& setRecycle(bool recycle);
//! \brief Set app specific data
//! \see Scene::setAppdata
CreateScene& setAppdata(const std::string& data, int version);
//! \brief Set light states of the scene
//!
//! When omitted, the current light states are stored.
//! \see Scene::setLightStates
CreateScene& setLightStates(const std::map<int, LightState>& states);
//! \brief Get request to create the scene.
//! \returns JSON request for a POST to create the new scene
nlohmann::json getRequest() const;
private:
nlohmann::json request;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,186 @@
/**
\file Schedule.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_SCHEDULE_H
#define INCLUDE_HUEPLUSPLUS_SCHEDULE_H
#include "APICache.h"
#include "Action.h"
#include "TimePattern.h"
namespace hueplusplus
{
//! \brief Schedule stored in the bridge
//!
//! A schedule can be created by the user to trigger actions at specific times.
class Schedule
{
public:
//! \brief Creates schedule with shared cache
//! \param id Schedule id in the bridge
//! \param baseCache Cache of the schedule list.
Schedule(int id, const std::shared_ptr<APICache>& baseCache);
//! \brief Construct Schedule that exists in the bridge
//! \param id Schedule ID
//! \param commands HueCommandAPI for requests
//! \param refreshDuration Time between refreshing the cached state
//! \param currentState The current state, may be null.
Schedule(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration, const nlohmann::json& currentState);
//! \brief Refreshes internal cached state
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void refresh();
//! \brief Sets custom refresh interval for this schedule.
//! \param refreshDuration The new minimum duration between refreshes. May be 0 or \ref c_refreshNever.
void setRefreshDuration(std::chrono::steady_clock::duration refreshDuration);
//! \brief Get schedule identifier
int getId() const;
//! \brief Get schedule name
//!
//! The schedule name is always unique for the bridge.
std::string getName() const;
//! \brief Get schedule description
std::string getDescription() const;
//! \brief Get schedule command
Action getCommand() const;
//! \brief Get time when the event(s) will occur
//! \returns TimePattern in local timezone
time::TimePattern getTime() const;
//! \brief Check whether schedule is enabled or disabled
bool isEnabled() const;
//! \brief Get autodelete
//!
//! When autodelete is set to true, the schedule is removed after it expires.
//! Only for non-recurring schedules.
bool getAutodelete() const;
//! \brief Get created time
//! \returns AbsoluteTime without variation
time::AbsoluteTime getCreated() const;
//! \brief Get start time for timers
//! \returns AbsoluteTime without variation when the timer was started.
//! \throws nlohmann::json::out_of_range when the schedule does not have a start time
time::AbsoluteTime getStartTime() const;
//! \brief Set schedule name
//! \param name New name for the schedule. Max size is 32.
//! Must be unique for all schedules, otherwise a number is added.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setName(const std::string& name);
//! \brief Set schedule description
//! \param description New description, may be empty. Max size is 64.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setDescription(const std::string& description);
//! \brief Set schedule command
//! \param command New action that is executed when the time event occurs.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setCommand(const Action& command);
//! \brief Set new time when the event will occur
//! \param timePattern Any possible value of TimePattern
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setTime(const time::TimePattern& timePattern);
//! \brief Enable or disable schedule
//! \param enabled true to enable, false to disable.
//!
//! Can be used to reset a timer by setting to disabled and enabled again.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setEnabled(bool enabled);
//! \brief Set autodelete
//! \param autodelete Whether to delete the schedule after it expires
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setAutodelete(bool autodelete);
private:
//! \brief Utility function to send put request to the schedule.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void sendPutRequest(const nlohmann::json& request, FileInfo fileInfo);
private:
int id;
APICache state;
};
//! \brief Parameters for creating a new Schedule.
//!
//! Can be used like a builder object with chained calls.
class CreateSchedule
{
public:
//! \brief Set name
//! \see Schedule::setName
CreateSchedule& setName(const std::string& name);
//! \brief Set description
//! \see Schedule::setDescription
CreateSchedule& setDescription(const std::string& description);
//! \brief Set command
//! \see Schedule::setCommand
CreateSchedule& setCommand(const Action& command);
//! \brief Set time
//! \see Schedule::setTime
CreateSchedule& setTime(const time::TimePattern& time);
//! \brief Set status
//! \see Schedule::setEnabled
CreateSchedule& setStatus(bool enabled);
//! \brief Set autodelete
//! \see Schedule::setAutodelete
CreateSchedule& setAutodelete(bool autodelete);
//! \brief Set recycle
//!
//! When recycle is true, it is deleted when no resourcelinks refer to it.
CreateSchedule& setRecycle(bool recycle);
//! \brief Get request to create the schedule.
//! \returns JSON request for a POST to create the new schedule
nlohmann::json getRequest() const;
private:
nlohmann::json request;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,394 @@
/**
\file Sensor.h
Copyright Notice\n
Copyright (C) 2020 Stefan Herbrechtsmeier - developer\n
Copyright (C) 2020 Jan Rogall - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_HUE_SENSOR_H
#define INCLUDE_HUEPLUSPLUS_HUE_SENSOR_H
#include <memory>
#include "BaseDevice.h"
#include "Condition.h"
#include "HueCommandAPI.h"
#include "TimePattern.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! \brief Specifies light alert modes
enum class Alert
{
none, //!< No alert
select, //!< Select alert (breathe cycle)
lselect //!< Long select alert (15s breathe)
};
//! \brief Convert alert to string form
//! \param alert Enum value
//! \returns "none", "select" or "lselect"
std::string alertToString(Alert alert);
//! \brief Convert string to Alert enum
//! \param s String representation
//! \returns Alert::select or Alert::lselect when \c s matches, otherwise Alert::none
Alert alertFromString(const std::string& s);
//! \brief Class for generic or unknown sensor types
//!
//! It is recommended to instead use the classes for specific types in \ref sensors.
//! This class should only be used if the type cannot be known or is not supported.
class Sensor : public BaseDevice
{
public:
//! \brief Construct Sensor with shared cache
//! \param id Integer that specifies the id of this sensor
//! \param baseCache Cache of the SensorList.
Sensor(int id, const std::shared_ptr<APICache>& baseCache);
//! \brief Construct Sensor.
//! \param id Integer that specifies the id of this sensor
//! \param commands HueCommandAPI for communication with the bridge
//! \param refreshDuration Time between refreshing the cached state.
//! \param currentState The current state, may be null.
Sensor(int id, const HueCommandAPI& commands, std::chrono::steady_clock::duration refreshDuration, const nlohmann::json& currentState);
//!\name Config attributes
///@{
//! \brief Check whether the sensor has an on attribute
bool hasOn() const;
//! \brief check whether the sensor is turned on
//!
//! Sensors which are off do not change their status
//! \throws nlohmann::json::out_of_range when on attribute does not exist.
bool isOn() const;
//! \brief Turn sensor on or off
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setOn(bool on);
//! \brief Check whether the sensor has a battery state
bool hasBatteryState() const;
//! \brief Get battery state
//! \returns Battery state in percent
//! \throws nlohmann::json::out_of_range when sensor has no battery status.
int getBatteryState() const;
//! \brief Set battery state
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setBatteryState(int percent);
//! \brief Check whether the sensor has alerts
bool hasAlert() const;
//! \brief Get last sent alert
//! \note This is not cleared when the alert ends.
//! \throws nlohmann::json::out_of_range when sensor has no alert.
Alert getLastAlert() const;
//! \brief Send alert
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void sendAlert(Alert type);
//! \brief Check whether the sensor has reachable validation
bool hasReachable() const;
//! \brief Get whether sensor is reachable
//! \throws nlohmann::json::out_of_range when sensor has no reachable validation
bool isReachable() const;
//! \brief Check whether the sensor has a user test mode
bool hasUserTest() const;
//! \brief Enable or disable user test mode
//!
//! In user test mode, changes are reported more frequently.#
//! It remains on for 120 seconds or until turned off.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setUserTest(bool enabled);
//! \brief Check whether the sensor has a URL
bool hasURL() const;
//! \brief Get sensor URL
//!
//! Only CLIP sensors can have a URL.
std::string getURL() const;
//! \brief Set sensor URL
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setURL(const std::string& url);
//! \brief Get pending config entries, if they exist
//! \returns The keys of config entries which have been modified,
//! but were not committed to the device.
//!
//! Attempts to set pending config entries may cause errors.
std::vector<std::string> getPendingConfig() const;
//! \brief Check whether the sensor has an LED indicator
bool hasLEDIndication() const;
//! \brief Get whether the indicator LED is on
//! \throws nlohmann::json::out_of_range when sensor has no LED
bool getLEDIndication() const;
//! \brief Turn LED indicator on or off
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setLEDIndication(bool on);
//! \brief Get entire config object
//! \returns A json object with the sensor configuration.
nlohmann::json getConfig() const;
//! \brief Set attribute in the sensor config
//! \param key Key of the config attribute
//! \param value Any value to set the attribute to
//!
//! Can be used to configure sensors with additional config entries.
void setConfigAttribute(const std::string& key, const nlohmann::json& value);
///@}
//! \brief Get time of last status update
//! \returns The last update time, or a time with a zero duration from epoch
//! if the last update time is not set.
time::AbsoluteTime getLastUpdated() const;
//! \brief Get state object
nlohmann::json getState() const;
//! \brief Set part of the sensor state
//! \param key Key in the state object
//! \param value New value
//!
//! The state can usually only be set on CLIP sensors, not on physical devices.
void setStateAttribute(const std::string& key, const nlohmann::json& value);
//! \brief Get address of the given state attribute, used for conditions
//! \param key Key in the state object
//! \returns \c key prefixed with the path to the sensor state
std::string getStateAddress(const std::string& key) const;
//! \brief Check if the sensor is Hue certified
bool isCertified() const;
//! \brief Check if the sensor is primary sensor of the device
//!
//! When there are multiple sensors on one physical device (same MAC address),
//! the primary device is used for the device information.
bool isPrimary() const;
//! \brief Convert sensor to a specific type
//! \tparam T Sensor type to convert to (from \ref sensors)
//! \throws HueException when sensor type does not match requested type
template <typename T>
T asSensorType() const&
{
if (getType() != T::typeStr)
{
throw HueException(FileInfo {__FILE__, __LINE__, __func__}, "Sensor type does not match: " + getType());
}
return T(*this);
}
//! \brief Convert sensor to a specific type
//! \tparam T Sensor type to convert to (from \ref sensors)
//! \throws HueException when sensor type does not match requested type
//!
//! Move construct \c T to be more efficient when the type is wanted directly.
template <typename T>
T asSensorType() &&
{
if (getType() != T::typeStr)
{
throw HueException(FileInfo {__FILE__, __LINE__, __func__}, "Sensor type does not match: " + getType());
}
return T(std::move(*this));
}
};
//! \brief Parameters for creating a new Sensor
//!
//! Can be used like a builder object with chained calls.
class CreateSensor
{
public:
//! \brief Construct with necessary parameters
//! \param name Human readable name
//! \param modelid Model id of the sensor
//! \param swversion Software version, may be empty
//! \param type Sensor type name (see types in \ref sensors)
//! \param uniqueid Globally unique ID
//! (MAC address of the device, extended with a unique endpoint id)
//! \param manufacturername Name of the device manufacturer
CreateSensor(const std::string& name, const std::string& modelid, const std::string& swversion,
const std::string& type, const std::string& uniqueid, const std::string& manufacturername);
//! \brief Set state object
//! \param state Sensor state, contents depend on the type.
//! \returns this object for chaining calls
CreateSensor& setState(const nlohmann::json& state);
//! \brief Set config object
//! \param config Sensor config, configs depend on the type. See getters in Sensor for examples.
//! \returns this object for chaining calls
CreateSensor& setConfig(const nlohmann::json& config);
//! \brief Enable recycling, delete automatically when not referenced
//! \returns this object for chaining calls
CreateSensor& setRecycle(bool recycle);
//! \brief Get request to create the sensor
//! \returns JSON request for a POST to create the new sensor
nlohmann::json getRequest() const;
protected:
nlohmann::json request;
};
//! \brief Classes for specific sensor types
//!
//! Classes should have a typeStr member with the type name.
namespace sensors
{
//! \brief Daylight sensor to detect sunrise and sunset
//!
//! Every bridge has a daylight sensor always available.
class DaylightSensor : public BaseDevice
{
public:
//! \brief Construct from generic sensor
explicit DaylightSensor(Sensor sensor) : BaseDevice(std::move(sensor)) { }
//! \brief Check if the sensor is on
//!
//! Sensors which are off do not change their status
bool isOn() const;
//! \brief Enable or disable sensor
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setOn(bool on);
//! \brief Check whether the sensor has a battery state
bool hasBatteryState() const;
//! \brief Get battery state
//! \returns Battery state in percent
//! \throws nlohmann::json::out_of_range when sensor has no battery state.
int getBatteryState() const;
//! \brief Set battery state
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setBatteryState(int percent);
//! \brief Set GPS coordinates for the calculation
//! \param latitude Decimal latitude coordinate "DDD.DDDD{N|S}" with leading zeros ending with N or S.
//! "none" to reset. (Empty string is null, which may be used instead of none in the future)
//! \param longitude Longitude coordinate (same format as latitude), ending with W or E
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setCoordinates(const std::string& latitude, const std::string& longitude);
//! \brief Check whether coordinates are configured
//!
//! There is no way to retrieve the configured coordinates.
bool isConfigured() const;
//! \brief Get time offset in minutes to sunrise
//!
//! The daylight is true if it is \c offset minutes after sunrise.
int getSunriseOffset() const;
//! \brief Set sunrise offset time
//! \param minutes Minutes from -120 to 120
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setSunriseOffset(int minutes);
//! \brief Get time offset in minutes to sunset
//!
//! The daylight is false if it is \c offset minutes after sunset.
int getSunsetOffset() const;
//! \brief Set sunset offset time
//! \param minutes Minutes from -120 to 120
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setSunsetOffset(int minutes);
//! \brief Check whether it is daylight or not
bool isDaylight() const;
//! \brief Get time of last status update
//! \returns The last update time, or a time with a zero duration from epoch
//! if the last update time is not set.
time::AbsoluteTime getLastUpdated() const;
//! \brief Daylight sensor type name
static constexpr const char* typeStr = "Daylight";
};
detail::ConditionHelper<bool> makeCondition(const DaylightSensor& sensor);
template <typename SensorT, detail::void_t<decltype(std::declval<const SensorT>().getLastUpdated())>* = nullptr>
detail::ConditionHelper<time::AbsoluteTime> makeConditionLastUpdate(const SensorT& sensor)
{
return detail::ConditionHelper<time::AbsoluteTime>(
"/sensors/" + std::to_string(sensor.getId()) + "/state/lastupdated");
}
template <typename ButtonSensor, detail::void_t<decltype(std::declval<const ButtonSensor>().getButtonEvent())>* = nullptr>
detail::ConditionHelper<int> makeCondition(const ButtonSensor& sensor)
{
return detail::ConditionHelper<int>(
"/sensors/" + std::to_string(sensor.getId()) + "/state/buttonevent");
}
template <typename PresenceSensor, detail::void_t<decltype(std::declval<const PresenceSensor>().getPresence())>* = nullptr>
detail::ConditionHelper<bool> makeCondition(const PresenceSensor& sensor)
{
return detail::ConditionHelper<bool>(
"/sensors/" + std::to_string(sensor.getId()) + "/state/presence");
}
template <typename TemperatureSensor, detail::void_t<decltype(std::declval<const TemperatureSensor>().getPresence())>* = nullptr>
detail::ConditionHelper<int> makeCondition(const TemperatureSensor& sensor)
{
return detail::ConditionHelper<int>(
"/sensors/" + std::to_string(sensor.getId()) + "/state/temperature");
}
} // namespace sensors
} // namespace hueplusplus
#endif
@@ -0,0 +1,82 @@
/**
\file SensorList.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_SENSOR_LIST_H
#define INCLUDE_HUEPLUSPLUS_SENSOR_LIST_H
#include "ResourceList.h"
#include "Sensor.h"
namespace hueplusplus
{
//! \brief Handles a list of Sensor%s with type specific getters
//!
//! Allows to directly get the requested sensor type or all sensors of a given type.
class SensorList : public CreateableResourceList<SearchableResourceList<Sensor>, CreateSensor>
{
public:
using CreateableResourceList::CreateableResourceList;
//! \brief Get sensor specified by id, convert to \c T
//! \param id Sensor id
//! \tparam T Sensor type to convert to (from \ref sensors)
//! \returns The sensor matching the id and type
//! \throws HueException when id does not exist or type does not match
//! \throws std::system_error when system or socket operations fail
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
template <typename T>
T getAsType(int id)
{
return get(id).asSensorType<T>();
}
//! \brief Get all sensors of type \c T
//! \tparam T Sensor type to get (from \ref sensors)
//! \returns All sensors matching the type
//! \throws HueException when response contains no body
//! \throws std::system_error when system or socket operations fail
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
template <typename T>
std::vector<T> getAllByType()
{
nlohmann::json state = this->stateCache->getValue();
std::vector<T> result;
for (auto it = state.begin(); it != state.end(); ++it)
{
// Only parse the sensors with the correct type
if (it->value("type", "") == T::typeStr)
{
result.push_back(get(maybeStoi(it.key())).asSensorType<T>());
}
}
return result;
}
protected:
//! \brief Protected defaulted move constructor
SensorList(SensorList&&) = default;
//! \brief Protected defaulted move assignment
SensorList& operator=(SensorList&&) = default;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,58 @@
/**
\file SimpleBrightnessStrategy.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_SIMPLE_BRIGHTNESS_STRATEGY_H
#define INCLUDE_HUEPLUSPLUS_SIMPLE_BRIGHTNESS_STRATEGY_H
#include "BrightnessStrategy.h"
#include "Light.h"
namespace hueplusplus
{
//! Class implementing the functions of BrightnessStrategy
class SimpleBrightnessStrategy : public BrightnessStrategy
{
public:
//! \brief Function for changing a lights brightness with a specified
//! transition.
//!
//! \param bri The brightness raning from 0 = off to 255 = fully lit
//! \param transition The time it takes to fade to the new brightness in
//! multiples of 100ms, 4 = 400ms and should be seen as the default \param
//! light A reference of the light
bool setBrightness(unsigned int bri, uint8_t transition, Light& light) const override;
//! \brief Function that returns the current brightness of the light
//!
//! Updates the lights state by calling refreshState()
//! \param light A reference of the light
//! \return Unsigned int representing the brightness
unsigned int getBrightness(Light& light) const override;
//! \brief Function that returns the current brightness of the light
//!
//! \note This does not update the lights state
//! \param light A const reference of the light
//! \return Unsigned int representing the brightness
unsigned int getBrightness(const Light& light) const override;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,127 @@
/**
\file SimpleColorHueStrategy.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_SIMPLE_COLOR_HUE_STRATEGY_H
#define INCLUDE_HUEPLUSPLUS_SIMPLE_COLOR_HUE_STRATEGY_H
#include "ColorHueStrategy.h"
#include "Light.h"
namespace hueplusplus
{
//! Class implementing the functions of ColorHueStrategy
//!
//! To be used for lights that have only color and no color temperature.
class SimpleColorHueStrategy : public ColorHueStrategy
{
public:
//! \brief Function for changing a lights color in hue with a specified
//! transition.
//!
//! The hue ranges from 0 to 65535, whereas 65535 and 0 are red, 25500 is
//! green and 46920 is blue. \param hue The hue of the color \param transition
//! The time it takes to fade to the new color in multiples of 100ms, 4 =
//! 400ms and should be seen as the default \param light A reference of the
//! light
bool setColorHue(uint16_t hue, uint8_t transition, Light& light) const override;
//! \brief Function for changing a lights color in saturation with a specified
//! transition.
//!
//! The saturation ranges from 0 to 254, whereas 0 is least saturated (white)
//! and 254 is most saturated (vibrant). \param sat The saturation of the
//! color \param transition The time it takes to fade to the new color in
//! multiples of 100ms, 4 = 400ms and should be seen as the default \param
//! light A reference of the light
bool setColorSaturation(uint8_t sat, uint8_t transition, Light& light) const override;
//! \brief Function for changing a lights color in hue and saturation format
//! with a specified transition.
//!
//! \param hueSat Color in hue and satuation.
//! \param transition The time it takes to fade to the new color in multiples of
//! 100ms, 4 = 400ms and should be seen as the default
//! \param light A reference of the light
bool setColorHueSaturation(const HueSaturation& hueSat, uint8_t transition, Light& light) const override;
//! \brief Function for changing a lights color in CIE format with a specified
//! transition.
//!
//! \param xy The color in XY and brightness
//! \param transition The time it takes to fade to the new color in multiples
//! of 100ms, 4 = 400ms and should be seen as the default \param light A
//! reference of the light
bool setColorXY(const XYBrightness& xy, uint8_t transition, Light& light) const override;
//! \brief Function for turning on/off the color loop feature of a light.
//!
//! Can be theoretically set for any light, but it only works for lights that
//! support this feature. When this feature is activated the light will fade
//! through every color on the current hue and saturation settings. Notice
//! that none of the setter functions check whether this feature is enabled
//! and the colorloop can only be disabled with this function or by simply
//! calling off() and then on(), so you could
//! alternatively call off() and then use any of the setter functions.
//! \param on Boolean to turn this feature on or off, true/1 for on and
//! false/0 for off \param light A reference of the light
bool setColorLoop(bool on, Light& light) const override;
//! \brief Function that lets the light perform one breath cycle in the
//! specified color.
//! \param hueSat The color in hue and saturation
//! \param light A reference of the light
//!
//! Blocks for the time a \ref Light::alert() needs
bool alertHueSaturation(const HueSaturation& hueSat, Light& light) const override;
//! \brief Function that lets the light perform one breath cycle in the
//! specified color.
//! \param xy The color in XY and brightness
//! \param light A reference of the light
bool alertXY(const XYBrightness& xy, Light& light) const override;
//! \brief Function that returns the current color of the light as hue and
//! saturation
//!
//! Updates the lights state by calling refreshState()
//! \param light A reference of the light
//! \return Pair containing the hue as first value and saturation as second
//! value
HueSaturation getColorHueSaturation(Light& light) const override;
//! \brief Function that returns the current color of the light as hue and
//! saturation
//!
//! \note This does not update the lights state
//! \param light A const reference of the light
//! \return Pair containing the hue as first value and saturation as second
//! value
HueSaturation getColorHueSaturation(const Light& light) const override;
//! \brief Function that returns the current color of the light as xy
//!
//! Updates the lights state by calling refreshState()
//! \param light A reference of the light
//! \return XY and brightness of current color
XYBrightness getColorXY(Light& light) const override;
//! \brief Function that returns the current color of the light as xy
//!
//! \note This does not update the lights state
//! \param light A const reference of the light
//! \return XY and brightness of current color
XYBrightness getColorXY(const Light& light) const override;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,69 @@
/**
\file SimpleColorTemperatureStrategy.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_SIMPLE_COLOR_TEMPERATURE_STRATEGY_H
#define INCLUDE_HUEPLUSPLUS_SIMPLE_COLOR_TEMPERATURE_STRATEGY_H
#include "ColorTemperatureStrategy.h"
#include "Light.h"
namespace hueplusplus
{
//! Class implementing the functions of ColorTemperatureStrategy
class SimpleColorTemperatureStrategy : public ColorTemperatureStrategy
{
public:
//! \brief Function for changing a lights color temperature in mired with a
//! specified transition.
//!
//! The color temperature in mired ranges from 153 to 500 whereas 153 is cold
//! and 500 is warm. \param mired The color temperature in mired \param
//! transition The time it takes to fade to the new color in multiples of
//! 100ms, 4 = 400ms and should be seen as the default \param light A
//! reference of the light
bool setColorTemperature(unsigned int mired, uint8_t transition, Light& light) const override;
//! \brief Function that lets the light perform one breath cycle in the
//! specified color.
//!
//! It uses this_thread::sleep_for to accomodate for the time an \ref
//! Light::alert() needs The color temperature in mired ranges from 153 to
//! 500 whereas 153 is cold and 500 is warm. \param mired The color
//! temperature in mired \param light A reference of the light
bool alertTemperature(unsigned int mired, Light& light) const override;
//! \brief Function that returns the current color temperature of the light
//!
//! Updates the lights state by calling refreshState()
//! The color temperature in mired ranges from 153 to 500 whereas 153 is cold
//! and 500 is warm. \param light A reference of the light \return Unsigned
//! int representing the color temperature in mired
unsigned int getColorTemperature(Light& light) const override;
//! \brief Function that returns the current color temperature of the light
//!
//! The color temperature in mired ranges from 153 to 500 whereas 153 is cold
//! and 500 is warm. \note This does not update the lights state \param light
//! A const reference of the light \return Unsigned int representing the color
//! temperature in mired
unsigned int getColorTemperature(const Light& light) const override;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,203 @@
/**
\file StateTransaction.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_STATE_TRANSACTION_H
#define INCLUDE_HUEPLUSPLUS_STATE_TRANSACTION_H
#include <string>
#include "Action.h"
#include "ColorUnits.h"
#include "HueCommandAPI.h"
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! \brief Transaction class which can be used for either light or group state.
//!
//! This is intended to be used in-line, all calls are chained until a \ref commit() call.
//! \code
//! light.transaction().setOn(true).setBrightness(29).setColorHue(3000).setColorSaturation(128).commit();
//! \endcode
//! \note The transaction has an internal reference to the light state.
//! You must not cause a refresh of the state between creating and committing the transaction
//! (e.g. non-const getters/setters), because that invalidates the reference.
//!
//! <h3>Advanced usage</h3>
//! Another way to use the transaction is by storing it and building up the calls separately.
//! \code
//! hueplusplus::StateTransaction t = light.transaction();
//! if(shouldTurnOn)
//! t.setOn(true);
//! t.commit();
//! \endcode
//! In this case, it is especially important that the light and the state of the light MUST NOT invalidate.
//! That means
//! \li the light variable has to live longer than the transaction
//! \li especially no non-const method calls on the light while the transaction is open,
//! or committing other transactions
//!
//! In general, this method is easier to screw up and should only be used when really necessary.
class StateTransaction
{
public:
//! \brief Creates a StateTransaction to a group or light state
//! \param commands HueCommandAPI for making requests
//! \param path Path to which the final PUT request is made (without username)
//! \param currentState Optional, the current state to check whether changes are needed.
//! Pass nullptr to always include all requests (for groups, because individual lights might be different).
StateTransaction(const HueCommandAPI& commands, const std::string& path, nlohmann::json* currentState);
//! \brief Deleted copy constructor, do not store StateTransaction in a variable.
StateTransaction(const StateTransaction&) = delete;
StateTransaction(StateTransaction&&) = default;
//! \brief Commit transaction and make request.
//! \param trimRequest Optional. When true, request parameters that are unneccessary based on
//! the current state are removed. This reduces load on the bridge. On the other hand, an outdated
//! state might cause requests to be dropped unexpectedly. Has no effect on groups.
//! \returns true on success or when no change was requested.
//! \note After changing the state of a Light or Group,
//! refresh() must be called if the updated values are needed immediately.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contains no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
bool commit(bool trimRequest = true);
//! \brief Create an Action from the transaction
//! \returns An Action that can be used to execute this transaction on a Schedule or Rule.
Action toAction();
//! \brief Turn light on or off.
//! \param on true for on, false for off
//! \returns This transaction for chaining calls
StateTransaction& setOn(bool on);
//! \brief Set light brightness.
//! \param brightness Brightness from 0 = off to 254 = fully lit.
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have brightness control.
//! \note Brightness 0 will also turn off the light if nothing else is specified,
//! any other value will also turn on the light.
StateTransaction& setBrightness(uint8_t brightness);
//! \brief Set light hue.
//! \param hue Color hue from 0 to 65535
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have rgb color control.
//! \note Will also turn on the light if nothing else is specified
StateTransaction& setColorHue(uint16_t hue);
//! \brief Set light saturation.
//! \param saturation Color saturation from 0 to 254
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have rgb color control.
//! \note Will also turn on the light if nothing else is specified
StateTransaction& setColorSaturation(uint8_t saturation);
//! \brief Set light color in hue and saturation
//! \param hueSat Color in hue and saturation
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have rgb color control.
//! \note Will also turn on the light if nothing else is specified
StateTransaction& setColor(const HueSaturation& hueSat);
//! \brief Set light color in xy space (without brightness).
//! \param xy x and y coordinates in CIE color space
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have rgb color control.
//! \note Will also turn on the light if nothing else is specified
StateTransaction& setColor(const XY& xy);
//! \brief Set light color and brightness in xy space
//! \param xy x,y and brightness in CIE color space
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have rgb color control.
//! \note Will also turn on the light if nothing else is specified
StateTransaction& setColor(const XYBrightness& xy);
//! \brief Set light color temperature.
//! \param mired Color temperature in mired from 153 to 500
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have color temperature control.
//! \note Will also turn on the light if nothing else is specified
StateTransaction& setColorTemperature(unsigned int mired);
//! \brief Enables or disables color loop.
//! \param on true to enable, false to disable color loop.
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have rgb color control.
//! \note Will also turn on the light if nothing else is specified
StateTransaction& setColorLoop(bool on);
//! \brief Increment/Decrement brightness.
//! \param increment Brightness change from -254 to 254.
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have brightness control.
StateTransaction& incrementBrightness(int increment);
//! \brief Increment/Decrement saturaction.
//! \param increment Saturation change from -254 to 254.
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have rgb color control.
StateTransaction& incrementSaturation(int increment);
//! \brief Increment/Decrement hue.
//! \param increment Hue change from -65535 to 65535.
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have rgb color control.
StateTransaction& incrementHue(int increment);
//! \brief Increment/Decrement color temperature.
//! \param increment Color temperature change in mired from -65535 to 65535.
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have color temperature control.
StateTransaction& incrementColorTemperature(int increment);
//! \brief Increment/Decrement color xy.
//! \param xInc x color coordinate change from -0.5 to 0.5.
//! \param yInc y color coordinate change from -0.5 to 0.5.
//! \returns This transaction for chaining calls
//! \note If this transaction is for a light, the light needs to have rgb color control.
StateTransaction& incrementColorXY(float xInc, float yInc);
//! \brief Set transition time for the request.
//! \param transition Transition time in 100ms, default for any request is 400ms.
//! \returns This transaction for chaining calls
//! \note The transition only applies to the current request.
//! A request without any changes only containing a transition is pointless and is not sent.
StateTransaction& setTransition(uint16_t transition);
//! \brief Trigger an alert.
//!
//! The light performs one breathe cycle.
//! \returns This transaction for chaining calls
StateTransaction& alert();
//! \brief Trigger a long alert (15s).
//! \returns This transaction for chaining calls
StateTransaction& longAlert();
//! \brief Stop an ongoing long alert.
//! \returns This transaction for chaining calls
StateTransaction& stopAlert();
protected:
//! \brief Remove parts from request that are already set in state
void trimRequest();
protected:
const HueCommandAPI& commands;
std::string path;
nlohmann::json* state;
nlohmann::json request;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,436 @@
/**
\file TimePattern.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_TIME_PATTERN
#define INCLUDE_HUEPLUSPLUS_TIME_PATTERN
#include <chrono>
#include <string>
#include <cstddef>
namespace hueplusplus
{
//! \brief Namespace for time/date related classes and functions
namespace time
{
//! \brief Converts a time_point to a timestamp string
//! \param time Time to convert
//! \returns Date and time in the format
//! <code>YYYY-MM-DD</code><strong>T</strong><code>hh:mm:ss</code>.
//!
//! Returns the time in the local time zone.
//! \throws HueException when time could not be converted
std::string timepointToTimestamp(std::chrono::system_clock::time_point time);
//! \brief Converts a timestamp to a time_point
//! \param timestamp Timestamp from the local time zone in the format
//! <code>YYYY-MM-DD</code><strong>T</strong><code>hh:mm:ss</code>
//! \returns time_point of the local system clock
//! \throws std::invalid_argument when integer conversion fails
//! \throws HueException when time cannot be represented as time_point
std::chrono::system_clock::time_point parseTimestamp(const std::string& timestamp);
//! \brief Converts an UTC timestamp to a time_point
//! \param timestamp UTC Timestamp the format
//! <code>YYYY-MM-DD</code><strong>T</strong><code>hh:mm:ss</code>
//! \returns time_point of the local system clock
//! \throws std::invalid_argument when integer conversion fails
//! \throws HueException when time cannot be represented as time_point
std::chrono::system_clock::time_point parseUTCTimestamp(const std::string& timestamp);
//! \brief Converts duration to a time string
//! \param duration Duration or time of day to format. Must be less than 24 hours
//! \returns Duration string in the format <code>hh:mm:ss</code>
//! \throws HueException when \c duration longer than 24 hours.
std::string durationTo_hh_mm_ss(std::chrono::system_clock::duration duration);
//! \brief Converts time string to a duration
//! \param hourMinSec Time/duration in the format <code>hh:mm:ss</code>
//! \returns Duration (hours, minutes and seconds) from the string
//! \throws std::invalid_argument when integer conversion fails
std::chrono::system_clock::duration parseDuration(const std::string& hourMinSec);
//! \brief One-time, absolute time point
class AbsoluteTime
{
using clock = std::chrono::system_clock;
public:
//! \brief Create absolute time point
//! \param baseTime Absolute time point
explicit AbsoluteTime(clock::time_point baseTime);
//! \brief Get base time point
//!
//! Can be used for calculation with other system_clock time_points
clock::time_point getBaseTime() const;
//! \brief Get formatted string as expected by Hue API
//! \returns Timestamp in the format
//! <code>YYYY-MM-DD</code><strong>T</strong><code>hh:mm:ss</code> in local timezone
std::string toString() const;
//! \brief Parse AbsoluteTime from formatted string in local timezone
//! \param s Timestamp in the same format as returned by \ref toString()
//! \returns AbsoluteTime with base time and variation from \c s
static AbsoluteTime parse(const std::string& s);
//! \brief Parse AbsoluteTime from formatted UTC string
//! \param s Timestamp in the same format as returned by \ref toString()
//! \returns AbsoluteTime with base time and variation from \c s
static AbsoluteTime parseUTC(const std::string& s);
private:
clock::time_point base;
};
//! One-time, absolute time point with possible random variation
//!
//! Can be either used to represent a specific date and time,
//! or a date and time with a random variation.
class AbsoluteVariedTime : public AbsoluteTime
{
using clock = std::chrono::system_clock;
public:
//! \brief Create absolute time point
//! \param baseTime Absolute time point
//! \param variation Random variation, optional. When not zero, the time is randomly chosen between
//! <code>baseTime - variation</code> and <code>baseTime + variation</code>
explicit AbsoluteVariedTime(clock::time_point baseTime, clock::duration variation = std::chrono::seconds(0));
//! \brief Get random variation or zero
//!
//! The time can vary up to this amount in both directions.
clock::duration getRandomVariation() const;
//! \brief Get formatted string as expected by Hue API
//! \returns when variation is 0: Timestamp in the format
//! <code>YYYY-MM-DD</code><strong>T</strong><code>hh:mm:ss</code>
//! \returns when there is variation: Timestamp in the format
//! <code>YYYY-MM-DD</code><strong>T</strong><code>hh:mm:ss</code><strong>A</strong><code>hh:mm:ss</code>
//! with base time first, variation second
std::string toString() const;
//! \brief Parse AbsoluteTime from formatted string in local timezone
//! \param s Timestamp in the same format as returned by \ref toString()
//! \returns AbsoluteVariedTime with base time and variation from \c s
static AbsoluteVariedTime parse(const std::string& s);
private:
clock::duration variation;
};
//! \brief Any number of days of the week
//!
//! Can be used to represent weekly repetitions only on certain days.
class Weekdays
{
public:
//! \brief Create with no days
Weekdays() : bitmask(0) { }
//! \brief Create with the day \c num
//! \param num Day of the week, from monday (0) to sunday (6)
explicit Weekdays(int num) : bitmask(1 << num) { }
//! \brief Check if no days are set
bool isNone() const;
//! \brief Check if all days are set
bool isAll() const;
//! \brief Check if Monday is contained
bool isMonday() const;
//! \brief Check if Tuesday is contained
bool isTuesday() const;
//! \brief Check if Wednesday is contained
bool isWednesday() const;
//! \brief Check if Thursday is contained
bool isThursday() const;
//! \brief Check if Friday is contained
bool isFriday() const;
//! \brief Check if Saturday is contained
bool isSaturday() const;
//! \brief Check if Sunday is contained
bool isSunday() const;
//! \brief Create set union with other Weekdays
//! \param other Second set of days to combine with
//! \returns A set of days containing all days of either \c this or \c other
Weekdays unionWith(Weekdays other) const;
//! \brief Create set union with other Weekdays
//! \see unionWith
Weekdays operator|(Weekdays other) const { return unionWith(other); }
//! \brief Create a formatted, numeric string
//! \returns A three digit code for the days as a bitmask
std::string toString() const;
//! \brief Creates an empty Weekdays
static Weekdays none();
//! \brief Creates set of all days
static Weekdays all();
//! \brief Creates Monday
static Weekdays monday();
//! \brief Creates Tuesday
static Weekdays tuesday();
//! \brief Creates Wednesday
static Weekdays wednesday();
//! \brief Creates Thursday
static Weekdays thursday();
//! \brief Creates Friday
static Weekdays friday();
//! \brief Creates Saturday
static Weekdays saturday();
//! \brief Creates Sunday
static Weekdays sunday();
//! \brief Parse from three digit code
//! \param s Bitmask of days as a string
//! \returns Parsed set of weekdays
static Weekdays parse(const std::string& s);
//! \brief Check whether all days are equal
bool operator==(const Weekdays& other) const { return bitmask == other.bitmask; }
//! \brief Check whether not all days are equal
bool operator!=(const Weekdays& other) const { return bitmask != other.bitmask; }
private:
int bitmask;
};
//! \brief Time repeated weekly to daily, with possible random variation.
//!
//! Can be used to represent a time on one or multiple days per week.
//! It can also have a random variation of up to 12 hours.
class RecurringTime
{
using clock = std::chrono::system_clock;
public:
//! \brief Create recurring time
//! \param daytime Time of day, duration from the start of the day.
//! \param days Days to repeat on, should not be Weekdays::none()
//! \param variation Random variation, optional. Must be less than 12 hours. When not zero, the time is randomly
//! chosen between <code>daytime - variation</code> and <code>daytime + variation</code>
explicit RecurringTime(clock::duration daytime, Weekdays days, clock::duration variation = std::chrono::seconds(0));
//! \brief Get time of day
clock::duration getDaytime() const;
//! \brief Get random variation
//!
//! The time can vary up to this amount in both directions.
clock::duration getRandomVariation() const;
//! \brief Get days on which the repetition will happen
Weekdays getWeekdays() const;
//! \brief Get formatted string as expected by Hue API
//! \returns with no variation:
//! <strong>W</strong><code>bbb</code><strong>/T</strong><code>hh:mm:ss</code>
//! \returns with variation:
//! <strong>W</strong><code>bbb</code><strong>/T</strong><code>hh:mm:ss</code><strong>A</strong><code>hh:mm:ss</code>,
//! where daytime is first and variation is second.
std::string toString() const;
private:
clock::duration time;
clock::duration variation;
Weekdays days;
};
//! \brief Time interval repeated daily to weekly.
//!
//! Can be used to represent an interval of time on one or multiple days per week.
//! The maximum interval length is 23 hours.
class TimeInterval
{
using clock = std::chrono::system_clock;
public:
//! \brief Create time interval
//! \param start Start time, duration from the start of the day
//! \param end End time, duration from the start of the day
//! \param days Active days, optional. Defaults to daily repetition.
TimeInterval(clock::duration start, clock::duration end, Weekdays days = Weekdays::all());
//! \brief Get start time of the interval
clock::duration getStartTime() const;
//! \brief Get end time of the interval
clock::duration getEndTime() const;
//! \brief Get active days
Weekdays getWeekdays() const;
//! \brief Get formatted string as expected by Hue API
//! \returns with daily repetition:
//! <strong>T</strong><code>hh:mm:ss</code><strong>/T</strong><code>hh:mm:ss</code>,
//! with start time first and end time second.
//! \returns with repetition that is not daily:
//! <strong>W</strong><code>bbb</code><strong>/T</strong><code>hh:mm:ss</code><strong>/T</strong><code>hh:mm:ss</code>
std::string toString() const;
private:
clock::duration start;
clock::duration end;
Weekdays days;
};
//! \brief Timer that is started and triggers after specified delay
//!
//! The timer can have a random variation in the expiry time.
//! It can be one-off, repeated a set number of times or repeated indefinitely.
class Timer
{
using clock = std::chrono::system_clock;
public:
// \brief Used to represent infinite repetitions
static constexpr int infiniteExecutions = 0;
//! \brief Create one-off timer
//! \param duration Expiry time of the timer, max 24 hours.
//! \param variation Random variation of expiry time, optional.
Timer(clock::duration duration, clock::duration variation = std::chrono::seconds(0));
//! \brief Create a repeated timer.
//! \param duration Expiry time of the timer, max 24 hours.
//! \param numExecutions Number of executions, 1 or higher, or \ref infiniteExecutions to always repeat.
//! \param variation Random variation of expiry time, optional.
Timer(clock::duration duration, int numExecutions, clock::duration variation = std::chrono::seconds(0));
//! \brief Returns true when the timer is executed more than once
bool isRecurring() const;
//! \brief Get number of executions
//! \returns Number of executions, or \ref infiniteExecutions
int getNumberOfExecutions() const;
//! \brief Get expiry time
clock::duration getExpiryTime() const;
//! \brief Get random variation of expiry time
//!
//! The expiry time can vary up to this value in both directions.
clock::duration getRandomVariation() const;
//! \brief Get formatted string as expected by Hue API
//! \returns one-off timer: <strong>PT</strong><code>hh:mm:ss</code>
//! \returns one-off timer with variation:
//! <strong>PT</strong><code>hh:mm:ss</code><strong>A</strong><code>hh:mm:ss</code>,
//! with expiry time first and variation second.
//! \returns recurring timer: <strong>R/PT</strong><code>hh:mm:ss</code>
//! \returns recurring timer with n repetitions:
//! <strong>R</strong><code>nn</code><strong>/PT</strong><code>hh:mm:ss</code>
//! \returns recurring timer with random variation:
//! <strong>R</strong><code>nn</code><strong>/PT</strong><code>hh:mm:ss</code><strong>A</strong><code>hh:mm:ss</code>
//! \returns infinite recurring timer with random variation:
//! <strong>R</strong><strong>/PT</strong><code>hh:mm:ss</code><strong>A</strong><code>hh:mm:ss</code>
std::string toString() const;
private:
clock::duration expires;
clock::duration variation;
int numExecutions;
};
//! \brief Holds different time representations
//!
//! Holds either AbsoluteTime, RecurringTime, TimeInterval, Timer or an undefined state.
//! TimePattern is used to specify the occurrance of Schedule%s.
class TimePattern
{
public:
//! \brief Currently active type
enum class Type
{
undefined, //!< \brief No active type
absolute, //!< \brief Active type is AbsoluteVariedTime
recurring, //!< \brief Active type is RecurringTime
interval, //!< \brief Active type is TimeInterval
timer //!< \brief Active type is Timer
};
//! \brief Create empty TimePattern
TimePattern();
//! \brief Destructor for union.
~TimePattern();
//! \brief Create TimePattern from AbsoluteVariedTime
explicit TimePattern(const AbsoluteVariedTime& absolute);
//! \brief Create TimePattern from RecurringTime
explicit TimePattern(const RecurringTime& recurring);
//! \brief Create TimePattern from TimeInterval
explicit TimePattern(const TimeInterval& interval);
//! \brief Create TimePattern from Timer
explicit TimePattern(const Timer& timer);
//! \brief Copy constructor for union
TimePattern(const TimePattern& other);
//! \brief Copy assignment for union
TimePattern& operator=(const TimePattern& other);
//! \brief Get currently active type
//! \note Only the currently active type may be accessed,
//! anything else is undefined behavior.
Type getType() const;
//! \brief Get contained absolute time
//! \pre getType() == Type::absolute
AbsoluteVariedTime asAbsolute() const;
//! \brief Get contained recurring time
//! \pre getType() == Type::recurring
RecurringTime asRecurring() const;
//! \brief Get contained time interval
//! \pre getType() == Type::interval
TimeInterval asInterval() const;
//! \brief Get contained timer
//! \pre getType() == Type::timer
Timer asTimer() const;
//! \brief Get formatted string of the contained value as expected by Hue API
//! \returns Empty string when type is undefined, otherwise toString() of the active type.
//! \see AbsoluteTime::toString, RecurringTime::toString, TimeInterval::toString, Timer::toString
std::string toString() const;
//! \brief Parses TimePattern from formatted string as returned by Hue API
//! \param s Empty string, "none", or in one of the formats the contained types
//! return in their toString() method.
//! \returns TimePattern with the matching type that is given in \c s
//! \see AbsoluteTime::toString, RecurringTime::toString, TimeInterval::toString, Timer::toString
//! \throws HueException when the format does not match or a parsing error occurs
//! \throws std::invalid_argument when an integer conversion fails
static TimePattern parse(const std::string& s);
private:
//! \brief Calls destructor of active union member
void destroy();
private:
Type type;
union
{
std::nullptr_t undefined;
AbsoluteVariedTime absolute;
RecurringTime recurring;
TimeInterval interval;
Timer timer;
};
};
} // namespace time
} // namespace hueplusplus
#endif
@@ -0,0 +1,49 @@
/**
\file UPnP.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_UPNP_H
#define INCLUDE_HUEPLUSPLUS_UPNP_H
#include <memory>
#include <string>
#include <vector>
#include "IHttpHandler.h"
namespace hueplusplus
{
//! Class that looks for UPnP devices using an m-search package
class UPnP
{
public:
//! \brief Searches for UPnP devices and returns all found ones.
//!
//! It does it by sending an m-search packet and waits for all responses.
//! Since responses can be received multiple times this function conveniently removes all duplicates.
//! \param handler HttpHandler for communication
//! \return A vector containing pairs of address and name of all found devices
//! \throws std::system_error when system or socket operations fail
std::vector<std::pair<std::string, std::string>> getDevices(std::shared_ptr<const IHttpHandler> handler);
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,117 @@
/**
\file Utils.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_UTILS_H
#define INCLUDE_HUEPLUSPLUS_UTILS_H
#include <nlohmann/json.hpp>
namespace hueplusplus
{
//! \brief Utility functions used in multiple places.
namespace utils
{
namespace detail
{
// Forward declaration
template <typename KeyT, typename... Paths>
nlohmann::json safeGetMemberHelper(const nlohmann::json& json, std::size_t index, Paths&&... otherPaths);
inline nlohmann::json safeGetMemberHelper(const nlohmann::json& json)
{
return json;
}
template <typename KeyT, typename... Paths,
std::enable_if_t<!std::is_integral<std::remove_reference_t<KeyT>>::value>* = nullptr>
nlohmann::json safeGetMemberHelper(const nlohmann::json& json, KeyT&& key, Paths&&... otherPaths)
{
auto memberIt = json.find(std::forward<KeyT>(key));
if (memberIt == json.end())
{
return nullptr;
}
return safeGetMemberHelper(*memberIt, std::forward<Paths>(otherPaths)...);
}
// Needs to be after the other safeGetMemberHelper, otherwise another forward declaration is needed
template <typename... Paths>
nlohmann::json safeGetMemberHelper(const nlohmann::json& json, std::size_t index, Paths&&... otherPaths)
{
if (!json.is_array() || json.size() <= index)
{
return nullptr;
}
return safeGetMemberHelper(json[index], std::forward<Paths>(otherPaths)...);
}
} // namespace detail
//! \brief Function for validating that a request was executed correctly
//!
//! \param path The path the PUT request was made to
//! \param request The request that was sent initially
//! \param reply The reply that was received
//! \return True if request was executed correctly
bool validatePUTReply(const std::string& path, const nlohmann::json& request, const nlohmann::json& reply);
bool validateReplyForLight(const nlohmann::json& request, const nlohmann::json& reply, int lightId);
//! \brief Checks equality to 4 decimal places
//!
//! Floats in Hue json responses are rounded to 4 decimal places.
inline bool floatEquals(float lhs, float rhs)
{
return std::abs(lhs - rhs) <= 1E-4f;
}
//! \brief Returns the object/array member or null if it does not exist
//!
//! \param json The base json value
//! \param paths Any number of child accesses (e.g. 0, "key" would access json[0]["key"])
//! \returns The specified member or null if any intermediate object does not contain the specified child.
template <typename... Paths>
nlohmann::json safeGetMember(const nlohmann::json& json, Paths&&... paths)
{
return detail::safeGetMemberHelper(json, std::forward<Paths>(paths)...);
}
} // namespace utils
namespace detail
{
//! \brief Makes a class with protected copy constructor copyable.
//!
//! Used in private members to expose mutable references to \c T
//! while not allowing them to be assigned to.
//! Make sure \c T is actually designed to be used this way!
template <typename T>
class MakeCopyable : public T
{
public:
// Make copy constructor and assignment operator public
using T::T;
using T::operator=;
};
} // namespace detail
} // namespace hueplusplus
#endif
@@ -0,0 +1,72 @@
/**
\file WinHttpHandler.h
Copyright Notice\n
Copyright (C) 2017 Jan Rogall - developer\n
Copyright (C) 2017 Moritz Wirger - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_WINHTTPHANDLER_H
#define INCLUDE_HUEPLUSPLUS_WINHTTPHANDLER_H
#include <string>
#include <vector>
#include <winsock2.h>
#include "BaseHttpHandler.h"
namespace hueplusplus
{
//! Class to handle http requests and multicast requests on windows systems
class WinHttpHandler : public BaseHttpHandler
{
public:
//! \brief Ctor needed for initializing wsaData
WinHttpHandler();
//! \brief Dtor needed for wsaData cleanup
~WinHttpHandler();
//! \brief Function that sends a given message to the specified host and
//! returns the response.
//!
//! \param msg String that contains the message that is sent to the specified
//! address \param adr String that contains an ip or hostname in dotted
//! decimal notation like "192.168.2.1" \param port Optional integer that
//! specifies the port to which the request is sent to. Default is 80 \return
//! String containing the response of the host
std::string send(const std::string& msg, const std::string& adr, int port = 80) const override;
//! \brief Function that sends a multicast request with the specified message.
//!
//! \param msg String that contains the request that is sent to the specified
//! address \param adr Optional String that contains an ip or hostname in
//! dotted decimal notation, default is "239.255.255.250" \param port Optional
//! integer that specifies the port to which the request is sent. Default is
//! 1900 \param timeout Optional The timeout of the
//! request. Default is 5 seconds \return Vector containing strings of each
//! answer received
std::vector<std::string> sendMulticast(const std::string& msg, const std::string& adr = "239.255.255.250",
int port = 1900, std::chrono::steady_clock::duration timeout = std::chrono::seconds(5)) const override;
private:
WSADATA wsaData;
};
} // namespace hueplusplus
#endif
@@ -0,0 +1,329 @@
/**
\file ZLLSensors.h
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
This file is part of hueplusplus.
hueplusplus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
hueplusplus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDE_HUEPLUSPLUS_ZLL_SENSORS_H
#define INCLUDE_HUEPLUSPLUS_ZLL_SENSORS_H
#include "Sensor.h"
namespace hueplusplus
{
namespace sensors
{
//! \brief ZigBee Green Power sensor for button presses
class ZGPSwitch : public BaseDevice
{
public:
//! \brief Construct from generic sensor
explicit ZGPSwitch(Sensor sensor) : BaseDevice(std::move(sensor)) { }
//! \brief Check if sensor is on
//!
//! Sensors which are off do not change their status
bool isOn() const;
//! \brief Enable or disable sensor
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setOn(bool on);
//! \brief Get the code of the last switch event.
//!
//! Possible values are \ref c_button1 etc., or any other value.
int getButtonEvent() const;
//! \brief Code for tap button 1
static constexpr int c_button1 = 34;
//! \brief Code for tap button 2
static constexpr int c_button2 = 16;
//! \brief Code for tap button 3
static constexpr int c_button3 = 17;
//! \brief Code for tap button 4
static constexpr int c_button4 = 18;
//! \brief ZGPSwitch sensor type name
static constexpr const char* typeStr = "ZGPSwitch";
};
//! \brief ZigBee sensor reporting button presses
class ZLLSwitch : public BaseDevice
{
public:
//! \brief Construct from generic sensor
explicit ZLLSwitch(Sensor sensor) : BaseDevice(std::move(sensor)) { }
//! \brief Check if sensor is on
//!
//! Sensors which are off do not change their status
bool isOn() const;
//! \brief Enable or disable sensor
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setOn(bool on);
//! \brief Check whether the sensor has a battery state
bool hasBatteryState() const;
//! \brief Get battery state
//! \returns Battery state in percent
int getBatteryState() const;
//! \brief Get last sent alert
//! \note This is not cleared when the alert ends.
Alert getLastAlert() const;
//! \brief Send alert
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void sendAlert(Alert type);
//! \brief Check whether the sensor is reachable
bool isReachable() const;
//! \brief Get the code of the last switch event.
//!
//! Possible values are \ref c_ON_INITIAL_PRESS etc., or any other value.
int getButtonEvent() const;
//! \brief Get time of last status update
//! \returns The last update time, or a time with a zero duration from epoch
//! if the last update time is not set.
time::AbsoluteTime getLastUpdated() const;
//! \brief Button 1 (ON) pressed
static constexpr int c_ON_INITIAL_PRESS = 1000;
//! \brief Button 1 (ON) held
static constexpr int c_ON_HOLD = 1001;
//! \brief Button 1 (ON) released short press
static constexpr int c_ON_SHORT_RELEASED = 1002;
//! \brief Button 1 (ON) released long press
static constexpr int c_ON_LONG_RELEASED = 1003;
//! \brief Button 2 (DIM UP) pressed
static constexpr int c_UP_INITIAL_PRESS = 2000;
//! \brief Button 2 (DIM UP) held
static constexpr int c_UP_HOLD = 2001;
//! \brief Button 2 (DIM UP) released short press
static constexpr int c_UP_SHORT_RELEASED = 2002;
//! \brief Button 2 (DIM UP) released long press
static constexpr int c_UP_LONG_RELEASED = 2003;
//! \brief Button 3 (DIM DOWN) pressed
static constexpr int c_DOWN_INITIAL_PRESS = 3000;
//! \brief Button 3 (DIM DOWN) held
static constexpr int c_DOWN_HOLD = 3001;
//! \brief Button 3 (DIM DOWN) released short press
static constexpr int c_DOWN_SHORT_RELEASED = 3002;
//! \brief Button 3 (DIM DOWN) released long press
static constexpr int c_DOWN_LONG_RELEASED = 3003;
//! \brief Button 4 (OFF) pressed
static constexpr int c_OFF_INITIAL_PRESS = 4000;
//! \brief Button 4 (OFF) held
static constexpr int c_OFF_HOLD = 4001;
//! \brief Button 4 (OFF) released short press
static constexpr int c_OFF_SHORT_RELEASED = 4002;
//! \brief Button 4 (OFF) released long press
static constexpr int c_OFF_LONG_RELEASED = 4003;
//! \brief ZLLSwitch sensor type name
static constexpr const char* typeStr = "ZLLSwitch";
};
//! \brief Sensor detecting presence in the vicinity
class ZLLPresence : public BaseDevice
{
public:
//! \brief Construct from generic sensor
explicit ZLLPresence(Sensor sensor) : BaseDevice(std::move(sensor)) { }
//! \brief Check if sensor is on
//!
//! Sensors which are off do not change their status
bool isOn() const;
//! \brief Enable or disable sensor
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setOn(bool on);
//! \brief Check whether the sensor has a battery state
bool hasBatteryState() const;
//! \brief Get battery state
//! \returns Battery state in percent
int getBatteryState() const;
//! \brief Get last sent alert
//! \note This is not cleared when the alert ends.
Alert getLastAlert() const;
//! \brief Send alert
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void sendAlert(Alert type);
//! \brief Check whether the sensor is reachable
bool isReachable() const;
//! \brief Get sensor sensitivity
int getSensitivity() const;
//! \brief Get maximum sensitivity
int getMaxSensitivity() const;
//! \brief Set sensor sensitivity
//! \param sensitivity Sensitivity from 0 to max sensitivity (inclusive)
void setSensitivity(int sensitivity);
//! \brief Get presence status
bool getPresence() const;
//! \brief Get time of last status update
//! \returns The last update time, or a time with a zero duration from epoch
//! if the last update time is not set.
time::AbsoluteTime getLastUpdated() const;
//! \brief ZLLPresence sensor type name
static constexpr const char* typeStr = "ZLLPresence";
};
//! \brief ZigBee temperature sensor
class ZLLTemperature : public BaseDevice
{
public:
//! \brief Construct from generic sensor
explicit ZLLTemperature(Sensor sensor) : BaseDevice(std::move(sensor)) { }
//! \brief Check if sensor is on
//!
//! Sensors which are off do not change their status
bool isOn() const;
//! \brief Enable or disable sensor
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setOn(bool on);
//! \brief Check whether the sensor has a battery state
bool hasBatteryState() const;
//! \brief Get battery state
//! \returns Battery state in percent
int getBatteryState() const;
//! \brief Get last sent alert
//! \note This is not cleared when the alert ends.
Alert getLastAlert() const;
//! \brief Send alert
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void sendAlert(Alert type);
//! \brief Check whether the sensor is reachable
bool isReachable() const;
//! \brief Get recorded temperature
//! \returns Temperature in 0.01 degrees Celsius.
int getTemperature() const;
//! \brief Get time of last status update
//! \returns The last update time, or a time with a zero duration from epoch
//! if the last update time is not set.
time::AbsoluteTime getLastUpdated() const;
//! \brief ZLLTemperature sensor type name
static constexpr const char* typeStr = "ZLLTemperature";
};
//! \brief ZigBee sensor detecting ambient light level
class ZLLLightLevel : public BaseDevice
{
public:
//! \brief Construct from generic sensor
explicit ZLLLightLevel(Sensor sensor) : BaseDevice(std::move(sensor)) { }
//! \brief Check if sensor is on
//!
//! Sensors which are off do not change their status
bool isOn() const;
//! \brief Enable or disable sensor
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setOn(bool on);
//! \brief Check whether the sensor has a battery state
bool hasBatteryState() const;
//! \brief Get battery state
//! \returns Battery state in percent
int getBatteryState() const;
//! \brief Check whether the sensor is reachable
bool isReachable() const;
//! \brief Get threshold to detect darkness
int getDarkThreshold() const;
//! \brief Set threshold to detect darkness
//! \param threshold Light level as reported by \ref getLightLevel
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setDarkThreshold(int threshold);
//! \brief Get offset over dark threshold to detect daylight
int getThresholdOffset() const;
//! \brief Set offset to detect daylight
//! \param offset Offset to dark threshold to detect daylight. Must be greater than 1.
//! \throws std::system_error when system or socket operations fail
//! \throws HueException when response contained no body
//! \throws HueAPIResponseException when response contains an error
//! \throws nlohmann::json::parse_error when response could not be parsed
void setThresholdOffset(int offset);
//! \brief Get measured light level
//! \returns Light level in <code>10000*log10(lux)+1</code> (logarithmic scale)
int getLightLevel() const;
//! \brief Check whether light level is below dark threshold
bool isDark() const;
//! \brief Check whether light level is above light threshold
//!
//! Light threshold is dark threshold + offset
bool isDaylight() const;
//! \brief Get time of last status update
//! \returns The last update time, or a time with a zero duration from epoch
//! if the last update time is not set.
time::AbsoluteTime getLastUpdated() const;
//! \brief ZLLLightLevel sensor type name
static constexpr const char* typeStr = "ZLLLightLevel";
};
detail::ConditionHelper<bool> makeConditionDark(const ZLLLightLevel& sensor);
detail::ConditionHelper<bool> makeConditionDaylight(const ZLLLightLevel& sensor);
detail::ConditionHelper<int> makeConditionLightLevel(const ZLLLightLevel& sensor);
} // namespace sensors
} // namespace hueplusplus
#endif