Commit c48f494095881ac81fccca650ed41654f1cee0c5

Authored by Jojo-1000
Committed by Moritz Wirger
1 parent 068215b8

Add Scene functionality.

Does not yet have a way to create LightStates for Scenes.
include/hueplusplus/Hue.h
... ... @@ -39,6 +39,7 @@
39 39 #include "HueLight.h"
40 40 #include "IHttpHandler.h"
41 41 #include "ResourceList.h"
  42 +#include "Scene.h"
42 43 #include "Schedule.h"
43 44  
44 45 #include "json/json.hpp"
... ... @@ -332,6 +333,18 @@ public:
332 333 int createSchedule(const CreateSchedule& params);
333 334  
334 335 ///@}
  336 + ///! \name Scenes
  337 + ///@{
  338 +
  339 + std::vector<std::reference_wrapper<Scene>> getAllScenes();
  340 +
  341 + Scene& getScene(const std::string& id);
  342 +
  343 + bool sceneExists(const std::string& id) const;
  344 +
  345 + std::string createScene(const CreateScene& params);
  346 +
  347 + ///@}
335 348  
336 349 //! \brief Const function that returns the picture name of a given light id
337 350 //!
... ... @@ -363,8 +376,9 @@ private:
363 376 std::chrono::steady_clock::duration refreshDuration;
364 377 std::shared_ptr<APICache> stateCache;
365 378 ResourceList<HueLight, int> lights;
366   - CreateableResourceList<Group, int, CreateGroup> groups;
  379 + GroupResourceList<Group, CreateGroup> groups;
367 380 CreateableResourceList<Schedule, int, CreateSchedule> schedules;
  381 + CreateableResourceList<Scene, std::string, CreateScene> scenes;
368 382 };
369 383 } // namespace hueplusplus
370 384  
... ...
include/hueplusplus/ResourceList.h
... ... @@ -55,7 +55,7 @@ public:
55 55 //! Necessary if Resource is not constructible as described above.
56 56 ResourceList(std::shared_ptr<APICache> baseCache, const std::string& cacheEntry,
57 57 std::chrono::steady_clock::duration refreshDuration,
58   - const std::function<Resource(int, const nlohmann::json&)>& factory = nullptr)
  58 + const std::function<Resource(IdType, const nlohmann::json&)>& factory = nullptr)
59 59 : stateCache(baseCache, cacheEntry, refreshDuration), factory(factory), path(stateCache.getRequestPath() + '/')
60 60 {}
61 61 //! \brief Construct ResourceList with a separate cache and optional factory function
... ... @@ -275,6 +275,34 @@ public:
275 275 return IdType {};
276 276 }
277 277 };
  278 +
  279 +template <typename Resource, typename CreateType>
  280 +class GroupResourceList : public CreateableResourceList<Resource, int, CreateType>
  281 +{
  282 +public:
  283 + using CreateableResourceList<Resource, int, CreateType>::CreateableResourceList;
  284 + //! \brief Get group, specially handles group 0
  285 + //! \see ResourceList::get
  286 + Resource& get(const int& id)
  287 + {
  288 + auto pos = resources.find(id);
  289 + if (pos != resources.end())
  290 + {
  291 + pos->second.refresh();
  292 + return pos->second;
  293 + }
  294 + const nlohmann::json& state = stateCache.getValue();
  295 + std::string key = maybeToString(id);
  296 + if (!state.count(key) && id != 0)
  297 + {
  298 + throw HueException(FileInfo {__FILE__, __LINE__, __func__}, "Resource id is not valid");
  299 + }
  300 + return resources.emplace(id, construct(id, state[key])).first->second;
  301 + }
  302 + //! \brief Get group, specially handles group 0
  303 + //! \see ResourceList::exists
  304 + bool exists(int id) const { return id == 0 || CreateableResourceList<Resource, int, CreateType>::exists(id); }
  305 +};
278 306 } // namespace hueplusplus
279 307  
280 308 #endif
... ...
include/hueplusplus/Scene.h 0 → 100644
  1 +/**
  2 + \file Scene.h
  3 + Copyright Notice\n
  4 + Copyright (C) 2020 Jan Rogall - developer\n
  5 +
  6 + This file is part of hueplusplus.
  7 +
  8 + hueplusplus is free software: you can redistribute it and/or modify
  9 + it under the terms of the GNU Lesser General Public License as published by
  10 + the Free Software Foundation, either version 3 of the License, or
  11 + (at your option) any later version.
  12 +
  13 + hueplusplus is distributed in the hope that it will be useful,
  14 + but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16 + GNU Lesser General Public License for more details.
  17 +
  18 + You should have received a copy of the GNU Lesser General Public License
  19 + along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
  20 +**/
  21 +
  22 +#ifndef INCLUDE_HUEPLUSPLUS_SCENE_H
  23 +#define INCLUDE_HUEPLUSPLUS_SCENE_H
  24 +
  25 +#include <map>
  26 +#include <string>
  27 +#include <vector>
  28 +
  29 +#include <json/json.hpp>
  30 +
  31 +#include "APICache.h"
  32 +#include "TimePattern.h"
  33 +#include "Units.h"
  34 +
  35 +namespace hueplusplus
  36 +{
  37 +//! \brief Immutable state of a light
  38 +class LightState
  39 +{
  40 +public:
  41 +
  42 + LightState(const nlohmann::json& state);
  43 +
  44 + bool isOn() const;
  45 +
  46 + bool hasBrightness() const;
  47 + int getBrightness() const;
  48 +
  49 + bool hasHueSat() const;
  50 + HueSaturation getHueSat() const;
  51 +
  52 + bool hasXY() const;
  53 + XY getXY() const;
  54 +
  55 + bool hasCt() const;
  56 + int getCt() const;
  57 +
  58 + bool hasEffect() const;
  59 + bool getColorloop() const;
  60 +
  61 + int getTransitionTime() const;
  62 +
  63 + nlohmann::json toJson() const;
  64 +private:
  65 + nlohmann::json state;
  66 +};
  67 +
  68 +class Scene
  69 +{
  70 +public:
  71 + enum class Type
  72 + {
  73 + lightScene,
  74 + groupScene
  75 + };
  76 +
  77 + void refresh();
  78 + std::string getId() const;
  79 + std::string getName() const;
  80 + void setName(const std::string& name);
  81 +
  82 + Type getType() const;
  83 +
  84 + int getGroupId() const;
  85 + void setGroupId(int id);
  86 +
  87 + std::vector<int> getLightIds() const;
  88 + void setLightIds(const std::vector<int>& ids);
  89 +
  90 + std::string getOwner() const;
  91 + bool getRecycle() const;
  92 + bool isLocked() const;
  93 +
  94 + std::string getAppdata() const;
  95 + int getAppdataVersion() const;
  96 + void setAppdata(const std::string& data, int version);
  97 +
  98 + std::string getPicture() const;
  99 + time::AbsoluteTime getLastUpdated() const;
  100 + int getVersion() const;
  101 +
  102 + std::map<int, LightState> getLightStates() const;
  103 + void setLightStates(const std::map<int, LightState>& states);
  104 +
  105 + void storeCurrentLightState();
  106 + void storeCurrentLightState(int transition);
  107 +
  108 + void recall();
  109 +private:
  110 + void sendPutRequest(const std::string& path, const nlohmann::json& request, FileInfo fileInfo);
  111 +private:
  112 + int id;
  113 + APICache state;
  114 +};
  115 +
  116 +class CreateScene
  117 +{
  118 +public:
  119 + CreateScene& setName(const std::string& name);
  120 + CreateScene& setGroupId(int id);
  121 + CreateScene& setLightIds(const std::vector<int>& ids);
  122 + CreateScene& setRecycle(bool recycle);
  123 + CreateScene& setAppdata(const std::string& data, int version);
  124 + CreateScene& setLightStates(const std::map<int, LightState>& states);
  125 +
  126 + nlohmann::json getRequest() const;
  127 +
  128 +private:
  129 + nlohmann::json request;
  130 +};
  131 +} // namespace hueplusplus
  132 +
  133 +#endif
... ...
include/hueplusplus/Units.h
... ... @@ -23,22 +23,10 @@
23 23 #ifndef INCLUDE_HUEPLUSPLUS_UNITS_H
24 24 #define INCLUDE_HUEPLUSPLUS_UNITS_H
25 25  
26   -namespace hueplusplus
27   -{
28   -struct Kelvin
29   -{
30   - int value;
31   -};
  26 +#include <cstdint>
32 27  
33   -struct Mired
34   -{
35   - int value;
36   -};
37   -
38   -struct Brightness
  28 +namespace hueplusplus
39 29 {
40   - int value;
41   -};
42 30  
43 31 struct HueSaturation
44 32 {
... ...
src/CMakeLists.txt
... ... @@ -9,6 +9,7 @@ set(hueplusplus_SOURCES
9 9 HueDeviceTypes.cpp
10 10 HueException.cpp
11 11 HueLight.cpp
  12 + Scene.cpp
12 13 Schedule.cpp
13 14 SimpleBrightnessStrategy.cpp
14 15 SimpleColorHueStrategy.cpp
... ...
src/Hue.cpp
... ... @@ -143,7 +143,8 @@ Hue::Hue(const std::string&amp; ip, const int port, const std::string&amp; username,
143 143 [factory = HueLightFactory(stateCache->getCommandAPI(), refreshDuration)](
144 144 int id, const nlohmann::json& state) mutable { return factory.createLight(state, id); }),
145 145 groups(stateCache, "groups", refreshDuration),
146   - schedules(stateCache, "schedules", refreshDuration)
  146 + schedules(stateCache, "schedules", refreshDuration),
  147 + scenes(stateCache, "scenes", refreshDuration)
147 148 {}
148 149  
149 150 void Hue::refresh()
... ... @@ -284,6 +285,26 @@ int Hue::createSchedule(const CreateSchedule&amp; params)
284 285 return schedules.create(params);
285 286 }
286 287  
  288 +std::vector<std::reference_wrapper<Scene>> Hue::getAllScenes()
  289 +{
  290 + return scenes.getAll();
  291 +}
  292 +
  293 +Scene& Hue::getScene(const std::string& id)
  294 +{
  295 + return scenes.get(id);
  296 +}
  297 +
  298 +bool Hue::sceneExists(const std::string& id) const
  299 +{
  300 + return scenes.exists(id);
  301 +}
  302 +
  303 +std::string Hue::createScene(const CreateScene& params)
  304 +{
  305 + return scenes.create(params);
  306 +}
  307 +
287 308 std::string Hue::getPictureOfLight(int id)
288 309 {
289 310 std::string ret = "";
... ... @@ -425,7 +446,8 @@ void Hue::setHttpHandler(std::shared_ptr&lt;const IHttpHandler&gt; handler)
425 446 lights = ResourceList<HueLight, int>(stateCache, "lights", refreshDuration,
426 447 [factory = HueLightFactory(stateCache->getCommandAPI(), refreshDuration)](
427 448 int id, const nlohmann::json& state) mutable { return factory.createLight(state, id); });
428   - groups = CreateableResourceList<Group, int, CreateGroup>(stateCache, "groups", refreshDuration);
  449 + groups = GroupResourceList<Group, CreateGroup>(stateCache, "groups", refreshDuration);
429 450 schedules = CreateableResourceList<Schedule, int, CreateSchedule>(stateCache, "schedules", refreshDuration);
  451 + scenes = CreateableResourceList<Scene, std::string, CreateScene>(stateCache, "scenes", refreshDuration);
430 452 }
431 453 } // namespace hueplusplus
... ...
src/Scene.cpp 0 → 100644
  1 +/**
  2 + \file Scene.cpp
  3 + Copyright Notice\n
  4 + Copyright (C) 2020 Jan Rogall - developer\n
  5 +
  6 + This file is part of hueplusplus.
  7 +
  8 + hueplusplus is free software: you can redistribute it and/or modify
  9 + it under the terms of the GNU Lesser General Public License as published by
  10 + the Free Software Foundation, either version 3 of the License, or
  11 + (at your option) any later version.
  12 +
  13 + hueplusplus is distributed in the hope that it will be useful,
  14 + but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16 + GNU Lesser General Public License for more details.
  17 +
  18 + You should have received a copy of the GNU Lesser General Public License
  19 + along with hueplusplus. If not, see <http://www.gnu.org/licenses/>.
  20 +**/
  21 +
  22 +#include <hueplusplus/HueExceptionMacro.h>
  23 +#include <hueplusplus/Scene.h>
  24 +
  25 +namespace hueplusplus
  26 +{
  27 +LightState::LightState(const nlohmann::json& state) : state(state) {}
  28 +bool LightState::isOn() const
  29 +{
  30 + return state.value("on", false);
  31 +}
  32 +bool LightState::hasBrightness() const
  33 +{
  34 + return state.count("bri");
  35 +}
  36 +int LightState::getBrightness() const
  37 +{
  38 + return state.value("bri", 0);
  39 +}
  40 +bool LightState::hasHueSat() const
  41 +{
  42 + return state.count("hue") && state.count("sat");
  43 +}
  44 +HueSaturation LightState::getHueSat() const
  45 +{
  46 + return HueSaturation {state.value("hue", 0), state.value("sat", 0)};
  47 +}
  48 +bool LightState::hasXY() const
  49 +{
  50 + return state.count("xy");
  51 +}
  52 +XY LightState::getXY() const
  53 +{
  54 + const nlohmann::json& xy = state.at("xy");
  55 + return XY {xy[0].get<float>(), xy[1].get<float>()};
  56 +}
  57 +bool LightState::hasCt() const
  58 +{
  59 + return state.count("ct");
  60 +}
  61 +int LightState::getCt() const
  62 +{
  63 + return state.value("ct", 0);
  64 +}
  65 +bool LightState::hasEffect() const
  66 +{
  67 + return state.count("effect");
  68 +}
  69 +bool LightState::getColorloop() const
  70 +{
  71 + return state.value("effect", "") == "colorloop";
  72 +}
  73 +int LightState::getTransitionTime() const
  74 +{
  75 + return state.value("transitiontime", 4);
  76 +}
  77 +
  78 +nlohmann::json LightState::toJson() const
  79 +{
  80 + return state;
  81 +}
  82 +
  83 +void Scene::refresh()
  84 +{
  85 + state.refresh();
  86 +}
  87 +
  88 +std::string Scene::getId() const
  89 +{
  90 + return state.getValue().at("id").get<std::string>();
  91 +}
  92 +std::string Scene::getName() const
  93 +{
  94 + return state.getValue().at("name").get<std::string>();
  95 +}
  96 +void Scene::setName(const std::string& name)
  97 +{
  98 + sendPutRequest("", {{"name", name}}, CURRENT_FILE_INFO);
  99 +}
  100 +Scene::Type Scene::getType() const
  101 +{
  102 + std::string type = state.getValue().value("type", "LightScene");
  103 + if (type == "LightScene")
  104 + {
  105 + return Type::lightScene;
  106 + }
  107 + else if (type == "GroupScene")
  108 + {
  109 + return Type::groupScene;
  110 + }
  111 + throw HueException(CURRENT_FILE_INFO, "Unknown scene type: " + type);
  112 +}
  113 +int Scene::getGroupId() const
  114 +{
  115 + return std::stoi(state.getValue().value("group", "0"));
  116 +}
  117 +void Scene::setGroupId(int id)
  118 +{
  119 + sendPutRequest("", {{"group", std::to_string(id)}}, CURRENT_FILE_INFO);
  120 +}
  121 +std::vector<int> Scene::getLightIds() const
  122 +{
  123 + std::vector<int> result;
  124 + for (const nlohmann::json& id : state.getValue().at("lights"))
  125 + {
  126 + result.push_back(std::stoi(id.get<std::string>()));
  127 + }
  128 + return result;
  129 +}
  130 +void Scene::setLightIds(const std::vector<int>& ids)
  131 +{
  132 + nlohmann::json lightsJson;
  133 + for (int id : ids)
  134 + {
  135 + lightsJson.push_back(std::to_string(id));
  136 + }
  137 + sendPutRequest("", {{"lights", std::move(lightsJson)}}, CURRENT_FILE_INFO);
  138 +}
  139 +std::string Scene::getOwner() const
  140 +{
  141 + return state.getValue().at("owner").get<std::string>();
  142 +}
  143 +bool Scene::getRecycle() const
  144 +{
  145 + return state.getValue().at("recycle").get<bool>();
  146 +}
  147 +bool Scene::isLocked() const
  148 +{
  149 + return state.getValue().at("locked").get<bool>();
  150 +}
  151 +std::string Scene::getAppdata() const
  152 +{
  153 + return state.getValue().at("appdata").at("data").get<std::string>();
  154 +}
  155 +int Scene::getAppdataVersion() const
  156 +{
  157 + return state.getValue().at("appdata").at("version").get<int>();
  158 +}
  159 +void Scene::setAppdata(const std::string& data, int version)
  160 +{
  161 + sendPutRequest("", {{"appdata", {{"data", data}, {"version", version}}}}, CURRENT_FILE_INFO);
  162 +}
  163 +std::string Scene::getPicture() const
  164 +{
  165 + return state.getValue().value("picture", "");
  166 +}
  167 +time::AbsoluteTime Scene::getLastUpdated() const
  168 +{
  169 + return time::AbsoluteTime::parse(state.getValue().at("lastupdated").get<std::string>());
  170 +}
  171 +int Scene::getVersion() const
  172 +{
  173 + return state.getValue().at("version").get<int>();
  174 +}
  175 +std::map<int, LightState> Scene::getLightStates() const
  176 +{
  177 + if (state.getValue().count("lightstates") == 0)
  178 + {
  179 + return {};
  180 + }
  181 + const nlohmann::json& lightStates = state.getValue().at("lightstates");
  182 + std::map<int, LightState> result;
  183 + for (auto it = lightStates.begin(); it != lightStates.end(); ++it)
  184 + {
  185 + result.emplace(std::stoi(it.key()), LightState(it.value()));
  186 + }
  187 + return result;
  188 +}
  189 +void Scene::setLightStates(const std::map<int, LightState>& states)
  190 +{
  191 + nlohmann::json lightStates;
  192 + for (const auto& entry : states)
  193 + {
  194 + lightStates[entry.first] = entry.second.toJson();
  195 + }
  196 + sendPutRequest("", {{"lightstates", std::move(lightStates)}}, CURRENT_FILE_INFO);
  197 +}
  198 +void Scene::storeCurrentLightState()
  199 +{
  200 + sendPutRequest("", {{"storelightstate", true}}, CURRENT_FILE_INFO);
  201 +}
  202 +void Scene::storeCurrentLightState(int transition)
  203 +{
  204 + sendPutRequest("", {{"storelightstate", true}, {"transitiontime", transition}}, CURRENT_FILE_INFO);
  205 +}
  206 +void Scene::recall()
  207 +{
  208 + int groupId = 0;
  209 + if (getType() == Type::groupScene)
  210 + {
  211 + groupId = getGroupId();
  212 + }
  213 + state.getCommandAPI().PUTRequest("/groups/" + std::to_string(id) + "/action", {{"scene", id}}, CURRENT_FILE_INFO);
  214 +}
  215 +void Scene::sendPutRequest(const std::string& path, const nlohmann::json& request, FileInfo fileInfo)
  216 +{
  217 + state.getCommandAPI().PUTRequest("/scenes/" + id + path, request, std::move(fileInfo));
  218 +}
  219 +CreateScene& CreateScene::setName(const std::string& name)
  220 +{
  221 + return *this;
  222 +}
  223 +CreateScene& CreateScene::setGroupId(int id)
  224 +{
  225 + if (request.count("lights"))
  226 + {
  227 + throw HueException(CURRENT_FILE_INFO, "Can only set either group or lights");
  228 + }
  229 + request["group"] = std::to_string(id);
  230 + request["type"] = "GroupScene";
  231 + return *this;
  232 +}
  233 +CreateScene& CreateScene::setLightIds(const std::vector<int>& ids)
  234 +{
  235 + if (request.count("group"))
  236 + {
  237 + throw HueException(CURRENT_FILE_INFO, "Can only set either group or lights");
  238 + }
  239 + nlohmann::json lights;
  240 + for (int id : ids)
  241 + {
  242 + lights.push_back(std::to_string(id));
  243 + }
  244 + request["lights"] = std::move(lights);
  245 + request["type"] = "LightScene";
  246 + return *this;
  247 +}
  248 +CreateScene& CreateScene::setRecycle(bool recycle)
  249 +{
  250 + request["recycle"] = true;
  251 + return *this;
  252 +}
  253 +CreateScene& CreateScene::setAppdata(const std::string& data, int version)
  254 +{
  255 + request["appdata"] = {{"data", data}, {"version", version}};
  256 + return *this;
  257 +}
  258 +CreateScene& CreateScene::setLightStates(const std::map<int, LightState>& states)
  259 +{
  260 + nlohmann::json statesJson;
  261 + for (const auto& entry : states)
  262 + {
  263 + statesJson[std::to_string(entry.first)] = entry.second.toJson();
  264 + }
  265 + request["lightstates"] = std::move(statesJson);
  266 + return *this;
  267 +}
  268 +nlohmann::json CreateScene::getRequest() const
  269 +{
  270 + return request;
  271 +}
  272 +} // namespace hueplusplus
0 273 \ No newline at end of file
... ...