From 2780404a7309841b24fdff2ec8cab74c321f2f0d Mon Sep 17 00:00:00 2001 From: Moritz Wirger Date: Thu, 11 Mar 2021 21:47:54 +0100 Subject: [PATCH] Fix entertainment mode support --- .gitignore | 2 +- .gitmodules | 3 +++ CMakeLists.txt | 2 ++ include/hueplusplus/Bridge.h | 1 + include/hueplusplus/EntertainmentMode.h | 60 ++++++++++++++++++++++++++++++++++++------------------------ include/hueplusplus/LinHttpHandler.h | 2 +- lib/mbedtls | 1 + src/Bridge.cpp | 27 +++++++++++---------------- src/CMakeLists.txt | 3 +++ src/EntertainmentMode.cpp | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------------- src/StateTransaction.cpp | 7 +++---- test/test_Bridge.cpp | 4 ++-- 12 files changed, 159 insertions(+), 104 deletions(-) create mode 100644 .gitmodules create mode 160000 lib/mbedtls diff --git a/.gitignore b/.gitignore index 70c4447..307328e 100755 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ # build directory /build* /out +/bin # Generated documentation /doc/html @@ -44,7 +45,6 @@ # Icon must end with two \r Icon - # Thumbnails ._* diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..544374b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/mbedtls"] + path = lib/mbedtls + url = https://github.com/ARMmbed/mbedtls.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 5120b99..5e3eea8 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,8 @@ if (1 AND APPLE) set(CMAKE_MACOSX_RPATH 1) endif() +add_subdirectory("lib/mbedtls") + add_subdirectory(src) # if the user decided to use tests add the subdirectory diff --git a/include/hueplusplus/Bridge.h b/include/hueplusplus/Bridge.h index 838018d..1f77b0e 100644 --- a/include/hueplusplus/Bridge.h +++ b/include/hueplusplus/Bridge.h @@ -101,6 +101,7 @@ public: //! \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); diff --git a/include/hueplusplus/EntertainmentMode.h b/include/hueplusplus/EntertainmentMode.h index b63cffa..1a0867c 100644 --- a/include/hueplusplus/EntertainmentMode.h +++ b/include/hueplusplus/EntertainmentMode.h @@ -25,49 +25,61 @@ #include "Bridge.h" #include "Group.h" -#include "mbedtls/ssl.h" -#include "mbedtls/net_sockets.h" -#include "mbedtls/entropy.h" -#include "mbedtls/ctr_drbg.h" -#include "mbedtls/error.h" -#include "mbedtls/certs.h" -#include "mbedtls/debug.h" -#include "mbedtls/timing.h" - -#define HUE_ENTERTAINMENT_HEADER_SIZE 16 -#define HUE_ENTERTAINMENT_LIGHT_SIZE 9 - namespace hueplusplus { +struct TLSContext; + //! \brief Class for Hue Entertainment Mode //! //! Provides methods to initialize and control Entertainment groups. class EntertainmentMode { public: - EntertainmentMode(Bridge& bridge, Group& group); + //! @brief Constructor + //! + //! @param b Bridge reference + //! @param g Group to control in entertainment mode reference + 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); - void Update(); + //! @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; - Group& group; + Bridge* bridge; //!< Associated bridge + Group* group; //!< Associated group std::vector entertainment_msg; //!< buffer containing the entertainment mode packet data uint8_t entertainment_num_lights; //!< number of lights in entertainment mode group - mbedtls_ssl_context ssl; - mbedtls_net_context server_fd; - mbedtls_entropy_context entropy; - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_ssl_config conf; - mbedtls_x509_crt cacert; - mbedtls_timing_delay_context timer; + std::unique_ptr tls_context; //!< tls context }; } // namespace hueplusplus diff --git a/include/hueplusplus/LinHttpHandler.h b/include/hueplusplus/LinHttpHandler.h index 2d13faf..b4b96b2 100644 --- a/include/hueplusplus/LinHttpHandler.h +++ b/include/hueplusplus/LinHttpHandler.h @@ -44,7 +44,7 @@ public: //! 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; + 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. //! diff --git a/lib/mbedtls b/lib/mbedtls new file mode 160000 index 0000000..fc86f3f --- /dev/null +++ b/lib/mbedtls @@ -0,0 +1 @@ +Subproject commit fc86f3f147890a65ffa9b21eef6be96ce35371c5 diff --git a/src/Bridge.cpp b/src/Bridge.cpp index 6211150..fdf3b0d 100644 --- a/src/Bridge.cpp +++ b/src/Bridge.cpp @@ -250,6 +250,11 @@ std::string Bridge::requestUsername() bool Bridge::StartStreaming(std::string group_identifier) { + if (clientkey.empty()) + { + throw HueException(CURRENT_FILE_INFO, "Cannot stream without client key!"); + } + nlohmann::json request; request["stream"]["active"] = true; @@ -260,20 +265,10 @@ bool Bridge::StartStreaming(std::string group_identifier) answer = http_handler->PUTJson(uri, request, ip, port); - if(answer[0].contains("success")) - { - std::string key = "/groups/" + group_identifier + "/stream/active"; + std::string key = "/groups/" + group_identifier + "/stream/active"; + nlohmann::json success = utils::safeGetMember(answer, 0, "success", key); - if(answer[0]["success"].contains(key)) - { - if(answer[0]["success"][key] == true) - { - return true; - } - } - } - - return false; + return success == true; } bool Bridge::StopStreaming(std::string group_identifier) @@ -288,13 +283,13 @@ bool Bridge::StopStreaming(std::string group_identifier) answer = http_handler->PUTJson(uri, request, ip, port); - if(answer[0].contains("success")) + if (answer[0].contains("success")) { std::string key = "/groups/" + group_identifier + "/stream/active"; - if(answer[0]["success"].contains(key)) + if (answer[0]["success"].contains(key)) { - if(answer[0]["success"][key] == false) + if (answer[0]["success"][key] == false) { return true; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b90d1a2..5135e43 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,7 @@ set(hueplusplus_SOURCES BridgeConfig.cpp CLIPSensors.cpp ColorUnits.cpp + EntertainmentMode.cpp ExtendedColorHueStrategy.cpp ExtendedColorTemperatureStrategy.cpp Group.cpp @@ -58,12 +59,14 @@ set(hueplusplus_SOURCES ${_srcList} PARENT_SCOPE) # hueplusplus shared library add_library(hueplusplusshared SHARED ${hueplusplus_SOURCES}) +target_link_libraries(hueplusplusshared PRIVATE mbedtls) target_compile_features(hueplusplusshared PUBLIC cxx_std_14) target_include_directories(hueplusplusshared PUBLIC $ $) install(TARGETS hueplusplusshared DESTINATION lib) # hueplusplus static library add_library(hueplusplusstatic STATIC ${hueplusplus_SOURCES}) +target_link_libraries(hueplusplusstatic PRIVATE mbedtls) target_compile_features(hueplusplusstatic PUBLIC cxx_std_14) install(TARGETS hueplusplusstatic DESTINATION lib) target_include_directories(hueplusplusstatic PUBLIC $ $) diff --git a/src/EntertainmentMode.cpp b/src/EntertainmentMode.cpp index a788c58..0d381f9 100644 --- a/src/EntertainmentMode.cpp +++ b/src/EntertainmentMode.cpp @@ -20,6 +20,30 @@ **/ #include "hueplusplus/EntertainmentMode.h" +#include "mbedtls/certs.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/debug.h" +#include "mbedtls/entropy.h" +#include "mbedtls/error.h" +#include "mbedtls/net_sockets.h" +#include "mbedtls/ssl.h" +#include "mbedtls/timing.h" + +namespace hueplusplus +{ +constexpr uint8_t HUE_ENTERTAINMENT_HEADER_SIZE = 16; +constexpr uint8_t HUE_ENTERTAINMENT_LIGHT_SIZE = 9; + +struct TLSContext +{ + mbedtls_ssl_context ssl; + mbedtls_net_context server_fd; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_config conf; + mbedtls_x509_crt cacert; + mbedtls_timing_delay_context timer; +}; std::vector HexToBytes(const std::string& hex) { @@ -28,26 +52,25 @@ std::vector HexToBytes(const std::string& hex) for (unsigned int i = 0; i < hex.length(); i += 2) { std::string byteString = hex.substr(i, 2); - char byte = (char) strtol(byteString.c_str(), NULL, 16); + char byte = (char)strtol(byteString.c_str(), NULL, 16); bytes.push_back(byte); } return bytes; } -namespace hueplusplus -{ -EntertainmentMode::EntertainmentMode(Bridge& bridge, Group& group):bridge(bridge),group(group) +EntertainmentMode::EntertainmentMode(Bridge& b, Group& g) + : bridge(&b), group(&g), tls_context(std::make_unique(TLSContext {})) { /*-------------------------------------------------*\ | Signal the bridge to start streaming | \*-------------------------------------------------*/ - bridge.StartStreaming(std::to_string(group.getId())); + bridge->StartStreaming(std::to_string(group->getId())); /*-------------------------------------------------*\ | Get the number of lights from the group | \*-------------------------------------------------*/ - entertainment_num_lights = group.getLightIds().size(); + entertainment_num_lights = group->getLightIds().size(); /*-------------------------------------------------*\ | Resize Entertainment Mode message buffer | @@ -74,8 +97,8 @@ EntertainmentMode::EntertainmentMode(Bridge& bridge, Group& group):bridge(bridge unsigned int msg_idx = HUE_ENTERTAINMENT_HEADER_SIZE + (light_idx * HUE_ENTERTAINMENT_LIGHT_SIZE); entertainment_msg[msg_idx + 0] = 0x00; // Type (Light) - entertainment_msg[msg_idx + 1] = group.getLightIds()[light_idx] >> 8; // ID MSB - entertainment_msg[msg_idx + 2] = group.getLightIds()[light_idx] & 0xFF; // ID LSB + entertainment_msg[msg_idx + 1] = group->getLightIds()[light_idx] >> 8; // ID MSB + entertainment_msg[msg_idx + 2] = group->getLightIds()[light_idx] & 0xFF; // ID LSB entertainment_msg[msg_idx + 3] = 0x00; // Red MSB entertainment_msg[msg_idx + 4] = 0x00; // Red LSB; entertainment_msg[msg_idx + 5] = 0x00; // Green MSB; @@ -87,22 +110,33 @@ EntertainmentMode::EntertainmentMode(Bridge& bridge, Group& group):bridge(bridge /*-------------------------------------------------*\ | Initialize mbedtls contexts | \*-------------------------------------------------*/ - mbedtls_net_init(&server_fd); - mbedtls_ssl_init(&ssl); - mbedtls_ssl_config_init(&conf); - mbedtls_x509_crt_init(&cacert); - mbedtls_ctr_drbg_init(&ctr_drbg); - mbedtls_entropy_init(&entropy); + mbedtls_net_init(&tls_context->server_fd); + mbedtls_ssl_init(&tls_context->ssl); + mbedtls_ssl_config_init(&tls_context->conf); + mbedtls_x509_crt_init(&tls_context->cacert); + mbedtls_ctr_drbg_init(&tls_context->ctr_drbg); + mbedtls_entropy_init(&tls_context->entropy); /*-------------------------------------------------*\ | Seed the Deterministic Random Bit Generator (RNG) | \*-------------------------------------------------*/ - int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0); + int ret = mbedtls_ctr_drbg_seed(&tls_context->ctr_drbg, mbedtls_entropy_func, &tls_context->entropy, NULL, 0); /*-------------------------------------------------*\ | Parse certificate | \*-------------------------------------------------*/ - ret = mbedtls_x509_crt_parse(&cacert, (const unsigned char*)mbedtls_test_cas_pem, mbedtls_test_cas_pem_len); + ret = mbedtls_x509_crt_parse( + &tls_context->cacert, (const unsigned char*)mbedtls_test_cas_pem, mbedtls_test_cas_pem_len); +} + +EntertainmentMode::~EntertainmentMode() +{ + mbedtls_entropy_free(&tls_context->entropy); + mbedtls_ctr_drbg_free(&tls_context->ctr_drbg); + mbedtls_x509_crt_free(&tls_context->cacert); + mbedtls_ssl_config_free(&tls_context->conf); + mbedtls_ssl_free(&tls_context->ssl); + mbedtls_net_free(&tls_context->server_fd); } bool EntertainmentMode::Connect() @@ -111,20 +145,21 @@ bool EntertainmentMode::Connect() | Signal the bridge to start streaming | | If successful, connect to the UDP port | \*-------------------------------------------------*/ - if(bridge.StartStreaming(std::to_string(group.getId()))) + if (bridge->StartStreaming(std::to_string(group->getId()))) { /*-------------------------------------------------*\ | Connect to the Hue bridge UDP server | \*-------------------------------------------------*/ - int ret = mbedtls_net_connect(&server_fd, bridge.getBridgeIP().c_str(), "2100", MBEDTLS_NET_PROTO_UDP); + int ret = mbedtls_net_connect( + &tls_context->server_fd, bridge->getBridgeIP().c_str(), "2100", MBEDTLS_NET_PROTO_UDP); /*-------------------------------------------------*\ | If connecting failed, close and return false | \*-------------------------------------------------*/ - if(ret != 0) + if (ret != 0) { - mbedtls_ssl_close_notify(&ssl); - bridge.StopStreaming(std::to_string(group.getId())); + mbedtls_ssl_close_notify(&tls_context->ssl); + bridge->StopStreaming(std::to_string(group->getId())); return false; } @@ -132,90 +167,92 @@ bool EntertainmentMode::Connect() | Configure defaults | \*-------------------------------------------------*/ ret = mbedtls_ssl_config_defaults( - &conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_PRESET_DEFAULT); + &tls_context->conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_PRESET_DEFAULT); /*-------------------------------------------------*\ | If configuring failed, close and return false | \*-------------------------------------------------*/ - if(ret != 0) + if (ret != 0) { - mbedtls_ssl_close_notify(&ssl); - bridge.StopStreaming(std::to_string(group.getId())); + mbedtls_ssl_close_notify(&tls_context->ssl); + bridge->StopStreaming(std::to_string(group->getId())); return false; } - mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL); - mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL); - mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg); + mbedtls_ssl_conf_authmode(&tls_context->conf, MBEDTLS_SSL_VERIFY_OPTIONAL); + mbedtls_ssl_conf_ca_chain(&tls_context->conf, &tls_context->cacert, NULL); + mbedtls_ssl_conf_rng(&tls_context->conf, mbedtls_ctr_drbg_random, &tls_context->ctr_drbg); /*-------------------------------------------------*\ | Convert client key to binary array | \*-------------------------------------------------*/ - std::vector psk_binary = HexToBytes(bridge.getClientKey()); + std::vector psk_binary = HexToBytes(bridge->getClientKey()); /*-------------------------------------------------*\ | Configure SSL pre-shared key and identity | | PSK - binary array from client key | | Identity - username (ASCII) | \*-------------------------------------------------*/ - ret = mbedtls_ssl_conf_psk(&conf, (const unsigned char*)&psk_binary[0], psk_binary.size(), - (const unsigned char*)bridge.getUsername().c_str(), bridge.getUsername().length()); + ret = mbedtls_ssl_conf_psk(&tls_context->conf, (const unsigned char*)&psk_binary[0], psk_binary.size(), + (const unsigned char*)bridge->getUsername().c_str(), bridge->getUsername().length()); /*-------------------------------------------------*\ | If configuring failed, close and return false | \*-------------------------------------------------*/ - if(ret != 0) + if (ret != 0) { - mbedtls_ssl_close_notify(&ssl); - bridge.StopStreaming(std::to_string(group.getId())); + mbedtls_ssl_close_notify(&tls_context->ssl); + bridge->StopStreaming(std::to_string(group->getId())); return false; } /*-------------------------------------------------*\ | Set up the SSL | \*-------------------------------------------------*/ - ret = mbedtls_ssl_setup(&ssl, &conf); + ret = mbedtls_ssl_setup(&tls_context->ssl, &tls_context->conf); /*-------------------------------------------------*\ | If setup failed, close and return false | \*-------------------------------------------------*/ - if(ret != 0) + if (ret != 0) { - mbedtls_ssl_close_notify(&ssl); - bridge.StopStreaming(std::to_string(group.getId())); + mbedtls_ssl_close_notify(&tls_context->ssl); + bridge->StopStreaming(std::to_string(group->getId())); return false; } - ret = mbedtls_ssl_set_hostname(&ssl, "localhost"); + ret = mbedtls_ssl_set_hostname(&tls_context->ssl, "localhost"); /*-------------------------------------------------*\ | If set hostname failed, close and return false | \*-------------------------------------------------*/ - if(ret != 0) + if (ret != 0) { - mbedtls_ssl_close_notify(&ssl); - bridge.StopStreaming(std::to_string(group.getId())); + mbedtls_ssl_close_notify(&tls_context->ssl); + bridge->StopStreaming(std::to_string(group->getId())); return false; } - mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, mbedtls_net_recv_timeout); - mbedtls_ssl_set_timer_cb(&ssl, &timer, mbedtls_timing_set_delay, mbedtls_timing_get_delay); + mbedtls_ssl_set_bio( + &tls_context->ssl, &tls_context->server_fd, mbedtls_net_send, mbedtls_net_recv, mbedtls_net_recv_timeout); + mbedtls_ssl_set_timer_cb( + &tls_context->ssl, &tls_context->timer, mbedtls_timing_set_delay, mbedtls_timing_get_delay); /*-------------------------------------------------*\ | Handshake | \*-------------------------------------------------*/ do { - ret = mbedtls_ssl_handshake(&ssl); + ret = mbedtls_ssl_handshake(&tls_context->ssl); } while (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE); /*-------------------------------------------------*\ | If set hostname failed, close and return false | \*-------------------------------------------------*/ - if(ret != 0) + if (ret != 0) { - mbedtls_ssl_close_notify(&ssl); - bridge.StopStreaming(std::to_string(group.getId())); + mbedtls_ssl_close_notify(&tls_context->ssl); + bridge->StopStreaming(std::to_string(group->getId())); return false; } @@ -229,13 +266,13 @@ bool EntertainmentMode::Connect() bool EntertainmentMode::Disconnect() { - mbedtls_ssl_close_notify(&ssl); - return bridge.StopStreaming(std::to_string(group.getId())); + mbedtls_ssl_close_notify(&tls_context->ssl); + return bridge->StopStreaming(std::to_string(group->getId())); } bool EntertainmentMode::SetColorRGB(uint8_t light_index, uint8_t red, uint8_t green, uint8_t blue) { - if(light_index < entertainment_num_lights) + if (light_index < entertainment_num_lights) { unsigned int msg_idx = HUE_ENTERTAINMENT_HEADER_SIZE + (light_index * HUE_ENTERTAINMENT_LIGHT_SIZE); @@ -254,24 +291,26 @@ bool EntertainmentMode::SetColorRGB(uint8_t light_index, uint8_t red, uint8_t gr } } -void EntertainmentMode::Update() +bool EntertainmentMode::Update() { int ret; unsigned int total = 0; - while(total < entertainment_msg.size()) + while (total < entertainment_msg.size()) { - ret = mbedtls_ssl_write(&ssl, (const unsigned char*)&entertainment_msg[total], entertainment_msg.size()); + ret = mbedtls_ssl_write( + &tls_context->ssl, (const unsigned char*)&entertainment_msg[total], entertainment_msg.size()); - if(ret < 0) + if (ret < 0) { // Return if mbedtls_ssl_write errors - return; + return false; } else { total += ret; } } + return true; } } // namespace hueplusplus diff --git a/src/StateTransaction.cpp b/src/StateTransaction.cpp index 981552c..c72598d 100644 --- a/src/StateTransaction.cpp +++ b/src/StateTransaction.cpp @@ -25,14 +25,13 @@ #include #include "hueplusplus/HueExceptionMacro.h" -#include "hueplusplus/StateTransaction.h" #include "hueplusplus/Utils.h" namespace hueplusplus { StateTransaction::StateTransaction(const HueCommandAPI& commands, const std::string& path, nlohmann::json* currentState) : commands(commands), path(path), state(currentState), request(nlohmann::json::object()) -{} +{ } bool StateTransaction::commit(bool trimRequest) { @@ -41,8 +40,8 @@ bool StateTransaction::commit(bool trimRequest) if (!request.count("on")) { if (!stateJson.value("on", false) - && (request.value("bri", 0) != 0 || request.count("effect") || request.count("hue") - || request.count("sat") || request.count("xy") || request.count("ct"))) + && (request.value("bri", 0) != 0 || request.count("effect") || request.count("hue") || request.count("sat") + || request.count("xy") || request.count("ct"))) { // Turn on if it was turned off request["on"] = true; diff --git a/test/test_Bridge.cpp b/test/test_Bridge.cpp index df6c320..6e03593 100644 --- a/test/test_Bridge.cpp +++ b/test/test_Bridge.cpp @@ -88,7 +88,7 @@ TEST_F(BridgeFinderTest, FindBridges) TEST_F(BridgeFinderTest, GetBridge) { using namespace ::testing; - nlohmann::json request {{"devicetype":"HuePlusPlus#User","generateclientkey":true}}; + nlohmann::json request {{"devicetype", "HuePlusPlus#User"}, {"generateclientkey", true}}; nlohmann::json errorResponse = {{{"error", {{"type", 101}, {"address", ""}, {"description", "link button not pressed"}}}}}; @@ -158,7 +158,7 @@ TEST(Bridge, requestUsername) { using namespace ::testing; std::shared_ptr handler = std::make_shared(); - nlohmann::json request {{"devicetype":"HuePlusPlus#User","generateclientkey":true}}; + nlohmann::json request {{"devicetype", "HuePlusPlus#User"}, {"generateclientkey", true}}; { nlohmann::json errorResponse -- libgit2 0.21.4