From c48f494095881ac81fccca650ed41654f1cee0c5 Mon Sep 17 00:00:00 2001 From: Jojo-1000 <33495614+Jojo-1000@users.noreply.github.com> Date: Thu, 7 May 2020 20:21:33 +0200 Subject: [PATCH] Add Scene functionality. --- include/hueplusplus/Hue.h | 16 +++++++++++++++- include/hueplusplus/ResourceList.h | 30 +++++++++++++++++++++++++++++- include/hueplusplus/Scene.h | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/hueplusplus/Units.h | 16 ++-------------- src/CMakeLists.txt | 1 + src/Hue.cpp | 26 ++++++++++++++++++++++++-- src/Scene.cpp | 272 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 476 insertions(+), 18 deletions(-) create mode 100644 include/hueplusplus/Scene.h create mode 100644 src/Scene.cpp diff --git a/include/hueplusplus/Hue.h b/include/hueplusplus/Hue.h index 7e6ef27..d9ced10 100644 --- a/include/hueplusplus/Hue.h +++ b/include/hueplusplus/Hue.h @@ -39,6 +39,7 @@ #include "HueLight.h" #include "IHttpHandler.h" #include "ResourceList.h" +#include "Scene.h" #include "Schedule.h" #include "json/json.hpp" @@ -332,6 +333,18 @@ public: int createSchedule(const CreateSchedule& params); ///@} + ///! \name Scenes + ///@{ + + std::vector> getAllScenes(); + + Scene& getScene(const std::string& id); + + bool sceneExists(const std::string& id) const; + + std::string createScene(const CreateScene& params); + + ///@} //! \brief Const function that returns the picture name of a given light id //! @@ -363,8 +376,9 @@ private: std::chrono::steady_clock::duration refreshDuration; std::shared_ptr stateCache; ResourceList lights; - CreateableResourceList groups; + GroupResourceList groups; CreateableResourceList schedules; + CreateableResourceList scenes; }; } // namespace hueplusplus diff --git a/include/hueplusplus/ResourceList.h b/include/hueplusplus/ResourceList.h index 47eee44..717ea52 100644 --- a/include/hueplusplus/ResourceList.h +++ b/include/hueplusplus/ResourceList.h @@ -55,7 +55,7 @@ public: //! Necessary if Resource is not constructible as described above. ResourceList(std::shared_ptr baseCache, const std::string& cacheEntry, std::chrono::steady_clock::duration refreshDuration, - const std::function& factory = nullptr) + const std::function& factory = nullptr) : stateCache(baseCache, cacheEntry, refreshDuration), factory(factory), path(stateCache.getRequestPath() + '/') {} //! \brief Construct ResourceList with a separate cache and optional factory function @@ -275,6 +275,34 @@ public: return IdType {}; } }; + +template +class GroupResourceList : public CreateableResourceList +{ +public: + using CreateableResourceList::CreateableResourceList; + //! \brief Get group, specially handles group 0 + //! \see ResourceList::get + Resource& get(const int& id) + { + auto pos = resources.find(id); + if (pos != resources.end()) + { + pos->second.refresh(); + return pos->second; + } + const nlohmann::json& state = stateCache.getValue(); + std::string key = maybeToString(id); + if (!state.count(key) && id != 0) + { + throw HueException(FileInfo {__FILE__, __LINE__, __func__}, "Resource id is not valid"); + } + return resources.emplace(id, construct(id, state[key])).first->second; + } + //! \brief Get group, specially handles group 0 + //! \see ResourceList::exists + bool exists(int id) const { return id == 0 || CreateableResourceList::exists(id); } +}; } // namespace hueplusplus #endif diff --git a/include/hueplusplus/Scene.h b/include/hueplusplus/Scene.h new file mode 100644 index 0000000..148e01d --- /dev/null +++ b/include/hueplusplus/Scene.h @@ -0,0 +1,133 @@ +/** + \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 . +**/ + +#ifndef INCLUDE_HUEPLUSPLUS_SCENE_H +#define INCLUDE_HUEPLUSPLUS_SCENE_H + +#include +#include +#include + +#include + +#include "APICache.h" +#include "TimePattern.h" +#include "Units.h" + +namespace hueplusplus +{ +//! \brief Immutable state of a light +class LightState +{ +public: + + LightState(const nlohmann::json& state); + + bool isOn() const; + + bool hasBrightness() const; + int getBrightness() const; + + bool hasHueSat() const; + HueSaturation getHueSat() const; + + bool hasXY() const; + XY getXY() const; + + bool hasCt() const; + int getCt() const; + + bool hasEffect() const; + bool getColorloop() const; + + int getTransitionTime() const; + + nlohmann::json toJson() const; +private: + nlohmann::json state; +}; + +class Scene +{ +public: + enum class Type + { + lightScene, + groupScene + }; + + void refresh(); + std::string getId() const; + std::string getName() const; + void setName(const std::string& name); + + Type getType() const; + + int getGroupId() const; + void setGroupId(int id); + + std::vector getLightIds() const; + void setLightIds(const std::vector& ids); + + std::string getOwner() const; + bool getRecycle() const; + bool isLocked() const; + + std::string getAppdata() const; + int getAppdataVersion() const; + void setAppdata(const std::string& data, int version); + + std::string getPicture() const; + time::AbsoluteTime getLastUpdated() const; + int getVersion() const; + + std::map getLightStates() const; + void setLightStates(const std::map& states); + + void storeCurrentLightState(); + void storeCurrentLightState(int transition); + + void recall(); +private: + void sendPutRequest(const std::string& path, const nlohmann::json& request, FileInfo fileInfo); +private: + int id; + APICache state; +}; + +class CreateScene +{ +public: + CreateScene& setName(const std::string& name); + CreateScene& setGroupId(int id); + CreateScene& setLightIds(const std::vector& ids); + CreateScene& setRecycle(bool recycle); + CreateScene& setAppdata(const std::string& data, int version); + CreateScene& setLightStates(const std::map& states); + + nlohmann::json getRequest() const; + +private: + nlohmann::json request; +}; +} // namespace hueplusplus + +#endif diff --git a/include/hueplusplus/Units.h b/include/hueplusplus/Units.h index eb32e2f..5d62352 100644 --- a/include/hueplusplus/Units.h +++ b/include/hueplusplus/Units.h @@ -23,22 +23,10 @@ #ifndef INCLUDE_HUEPLUSPLUS_UNITS_H #define INCLUDE_HUEPLUSPLUS_UNITS_H -namespace hueplusplus -{ -struct Kelvin -{ - int value; -}; +#include -struct Mired -{ - int value; -}; - -struct Brightness +namespace hueplusplus { - int value; -}; struct HueSaturation { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b9ea7f6..47ff103 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,7 @@ set(hueplusplus_SOURCES HueDeviceTypes.cpp HueException.cpp HueLight.cpp + Scene.cpp Schedule.cpp SimpleBrightnessStrategy.cpp SimpleColorHueStrategy.cpp diff --git a/src/Hue.cpp b/src/Hue.cpp index 8c8bb7c..13ad06d 100644 --- a/src/Hue.cpp +++ b/src/Hue.cpp @@ -143,7 +143,8 @@ Hue::Hue(const std::string& ip, const int port, const std::string& username, [factory = HueLightFactory(stateCache->getCommandAPI(), refreshDuration)]( int id, const nlohmann::json& state) mutable { return factory.createLight(state, id); }), groups(stateCache, "groups", refreshDuration), - schedules(stateCache, "schedules", refreshDuration) + schedules(stateCache, "schedules", refreshDuration), + scenes(stateCache, "scenes", refreshDuration) {} void Hue::refresh() @@ -284,6 +285,26 @@ int Hue::createSchedule(const CreateSchedule& params) return schedules.create(params); } +std::vector> Hue::getAllScenes() +{ + return scenes.getAll(); +} + +Scene& Hue::getScene(const std::string& id) +{ + return scenes.get(id); +} + +bool Hue::sceneExists(const std::string& id) const +{ + return scenes.exists(id); +} + +std::string Hue::createScene(const CreateScene& params) +{ + return scenes.create(params); +} + std::string Hue::getPictureOfLight(int id) { std::string ret = ""; @@ -425,7 +446,8 @@ void Hue::setHttpHandler(std::shared_ptr handler) lights = ResourceList(stateCache, "lights", refreshDuration, [factory = HueLightFactory(stateCache->getCommandAPI(), refreshDuration)]( int id, const nlohmann::json& state) mutable { return factory.createLight(state, id); }); - groups = CreateableResourceList(stateCache, "groups", refreshDuration); + groups = GroupResourceList(stateCache, "groups", refreshDuration); schedules = CreateableResourceList(stateCache, "schedules", refreshDuration); + scenes = CreateableResourceList(stateCache, "scenes", refreshDuration); } } // namespace hueplusplus diff --git a/src/Scene.cpp b/src/Scene.cpp new file mode 100644 index 0000000..bb51cdd --- /dev/null +++ b/src/Scene.cpp @@ -0,0 +1,272 @@ +/** + \file Scene.cpp + 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 . +**/ + +#include +#include + +namespace hueplusplus +{ +LightState::LightState(const nlohmann::json& state) : state(state) {} +bool LightState::isOn() const +{ + return state.value("on", false); +} +bool LightState::hasBrightness() const +{ + return state.count("bri"); +} +int LightState::getBrightness() const +{ + return state.value("bri", 0); +} +bool LightState::hasHueSat() const +{ + return state.count("hue") && state.count("sat"); +} +HueSaturation LightState::getHueSat() const +{ + return HueSaturation {state.value("hue", 0), state.value("sat", 0)}; +} +bool LightState::hasXY() const +{ + return state.count("xy"); +} +XY LightState::getXY() const +{ + const nlohmann::json& xy = state.at("xy"); + return XY {xy[0].get(), xy[1].get()}; +} +bool LightState::hasCt() const +{ + return state.count("ct"); +} +int LightState::getCt() const +{ + return state.value("ct", 0); +} +bool LightState::hasEffect() const +{ + return state.count("effect"); +} +bool LightState::getColorloop() const +{ + return state.value("effect", "") == "colorloop"; +} +int LightState::getTransitionTime() const +{ + return state.value("transitiontime", 4); +} + +nlohmann::json LightState::toJson() const +{ + return state; +} + +void Scene::refresh() +{ + state.refresh(); +} + +std::string Scene::getId() const +{ + return state.getValue().at("id").get(); +} +std::string Scene::getName() const +{ + return state.getValue().at("name").get(); +} +void Scene::setName(const std::string& name) +{ + sendPutRequest("", {{"name", name}}, CURRENT_FILE_INFO); +} +Scene::Type Scene::getType() const +{ + std::string type = state.getValue().value("type", "LightScene"); + if (type == "LightScene") + { + return Type::lightScene; + } + else if (type == "GroupScene") + { + return Type::groupScene; + } + throw HueException(CURRENT_FILE_INFO, "Unknown scene type: " + type); +} +int Scene::getGroupId() const +{ + return std::stoi(state.getValue().value("group", "0")); +} +void Scene::setGroupId(int id) +{ + sendPutRequest("", {{"group", std::to_string(id)}}, CURRENT_FILE_INFO); +} +std::vector Scene::getLightIds() const +{ + std::vector result; + for (const nlohmann::json& id : state.getValue().at("lights")) + { + result.push_back(std::stoi(id.get())); + } + return result; +} +void Scene::setLightIds(const std::vector& ids) +{ + nlohmann::json lightsJson; + for (int id : ids) + { + lightsJson.push_back(std::to_string(id)); + } + sendPutRequest("", {{"lights", std::move(lightsJson)}}, CURRENT_FILE_INFO); +} +std::string Scene::getOwner() const +{ + return state.getValue().at("owner").get(); +} +bool Scene::getRecycle() const +{ + return state.getValue().at("recycle").get(); +} +bool Scene::isLocked() const +{ + return state.getValue().at("locked").get(); +} +std::string Scene::getAppdata() const +{ + return state.getValue().at("appdata").at("data").get(); +} +int Scene::getAppdataVersion() const +{ + return state.getValue().at("appdata").at("version").get(); +} +void Scene::setAppdata(const std::string& data, int version) +{ + sendPutRequest("", {{"appdata", {{"data", data}, {"version", version}}}}, CURRENT_FILE_INFO); +} +std::string Scene::getPicture() const +{ + return state.getValue().value("picture", ""); +} +time::AbsoluteTime Scene::getLastUpdated() const +{ + return time::AbsoluteTime::parse(state.getValue().at("lastupdated").get()); +} +int Scene::getVersion() const +{ + return state.getValue().at("version").get(); +} +std::map Scene::getLightStates() const +{ + if (state.getValue().count("lightstates") == 0) + { + return {}; + } + const nlohmann::json& lightStates = state.getValue().at("lightstates"); + std::map result; + for (auto it = lightStates.begin(); it != lightStates.end(); ++it) + { + result.emplace(std::stoi(it.key()), LightState(it.value())); + } + return result; +} +void Scene::setLightStates(const std::map& states) +{ + nlohmann::json lightStates; + for (const auto& entry : states) + { + lightStates[entry.first] = entry.second.toJson(); + } + sendPutRequest("", {{"lightstates", std::move(lightStates)}}, CURRENT_FILE_INFO); +} +void Scene::storeCurrentLightState() +{ + sendPutRequest("", {{"storelightstate", true}}, CURRENT_FILE_INFO); +} +void Scene::storeCurrentLightState(int transition) +{ + sendPutRequest("", {{"storelightstate", true}, {"transitiontime", transition}}, CURRENT_FILE_INFO); +} +void Scene::recall() +{ + int groupId = 0; + if (getType() == Type::groupScene) + { + groupId = getGroupId(); + } + state.getCommandAPI().PUTRequest("/groups/" + std::to_string(id) + "/action", {{"scene", id}}, CURRENT_FILE_INFO); +} +void Scene::sendPutRequest(const std::string& path, const nlohmann::json& request, FileInfo fileInfo) +{ + state.getCommandAPI().PUTRequest("/scenes/" + id + path, request, std::move(fileInfo)); +} +CreateScene& CreateScene::setName(const std::string& name) +{ + return *this; +} +CreateScene& CreateScene::setGroupId(int id) +{ + if (request.count("lights")) + { + throw HueException(CURRENT_FILE_INFO, "Can only set either group or lights"); + } + request["group"] = std::to_string(id); + request["type"] = "GroupScene"; + return *this; +} +CreateScene& CreateScene::setLightIds(const std::vector& ids) +{ + if (request.count("group")) + { + throw HueException(CURRENT_FILE_INFO, "Can only set either group or lights"); + } + nlohmann::json lights; + for (int id : ids) + { + lights.push_back(std::to_string(id)); + } + request["lights"] = std::move(lights); + request["type"] = "LightScene"; + return *this; +} +CreateScene& CreateScene::setRecycle(bool recycle) +{ + request["recycle"] = true; + return *this; +} +CreateScene& CreateScene::setAppdata(const std::string& data, int version) +{ + request["appdata"] = {{"data", data}, {"version", version}}; + return *this; +} +CreateScene& CreateScene::setLightStates(const std::map& states) +{ + nlohmann::json statesJson; + for (const auto& entry : states) + { + statesJson[std::to_string(entry.first)] = entry.second.toJson(); + } + request["lightstates"] = std::move(statesJson); + return *this; +} +nlohmann::json CreateScene::getRequest() const +{ + return request; +} +} // namespace hueplusplus \ No newline at end of file -- libgit2 0.21.4