diff --git a/include/hueplusplus/EntertainmentMode.h b/include/hueplusplus/EntertainmentMode.h new file mode 100644 index 0000000..3e4fe05 --- /dev/null +++ b/include/hueplusplus/EntertainmentMode.h @@ -0,0 +1,75 @@ +/** + \file EntertainmentMode.h + Copyright Notice\n + Copyright (C) 2020 Adam Honse - 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_HUE_ENTERTAINMENT_MODE_H +#define INCLUDE_HUEPLUSPLUS_HUE_ENTERTAINMENT_MODE_H + +#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 +{ +//! \brief Class for Hue Entertainment Mode +//! +//! Provides methods to initialize and control Entertainment groups. +class EntertainmentMode +{ +public: + EntertainmentMode(Bridge& bridge, Group& group); + + bool Connect(); + bool Disconnect(); + + bool SetColorRGB(unsigned int light_index, unsigned char red, unsigned char green, unsigned char blue); + + void Update(); + +protected: + Bridge& bridge; + Group& group; + + unsigned char* entertainment_msg; //!< buffer containing the entertainment mode packet data + unsigned int entertainment_msg_size; //!< size of the entertainment mode buffer + unsigned int 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; +}; +} // namespace hueplusplus + +#endif diff --git a/src/EntertainmentMode.cpp b/src/EntertainmentMode.cpp new file mode 100644 index 0000000..228c2ac --- /dev/null +++ b/src/EntertainmentMode.cpp @@ -0,0 +1,170 @@ +/** + \file EntertainmentMode.cpp + Copyright Notice\n + Copyright (C) 2020 Adam Honse - 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 "hueplusplus/EntertainmentMode.h" + +std::vector HexToBytes(const std::string& hex) +{ + std::vector bytes; + + 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); + bytes.push_back(byte); + } + + return bytes; +} + +namespace hueplusplus +{ +EntertainmentMode::EntertainmentMode(Bridge& bridge, Group& group):bridge(bridge),group(group) +{ + /*-------------------------------------------------*\ + | Signal the bridge to start streaming | + \*-------------------------------------------------*/ + bridge.StartStreaming(std::to_string(group.getId())); + + /*-------------------------------------------------*\ + | Get the number of lights from the group | + \*-------------------------------------------------*/ + entertainment_num_lights = group.getLightIds().size(); + + /*-------------------------------------------------*\ + | Create Entertainment Mode message buffer | + \*-------------------------------------------------*/ + entertainment_msg_size = HUE_ENTERTAINMENT_HEADER_SIZE + (entertainment_num_lights * HUE_ENTERTAINMENT_LIGHT_SIZE); + entertainment_msg = new unsigned char[entertainment_msg_size]; + + /*-------------------------------------------------*\ + | Fill in Entertainment Mode message header | + \*-------------------------------------------------*/ + memcpy(entertainment_msg, "HueStream", 9); + entertainment_msg[9] = 0x01; // Version Major (1) + entertainment_msg[10] = 0x00; // Version Minor (0) + entertainment_msg[11] = 0x00; // Sequence ID + entertainment_msg[12] = 0x00; // Reserved + entertainment_msg[13] = 0x00; // Reserved + entertainment_msg[14] = 0x00; // Color Space (RGB) + entertainment_msg[15] = 0x00; // Reserved + + /*-------------------------------------------------*\ + | Fill in Entertainment Mode light data | + \*-------------------------------------------------*/ + for (unsigned int light_idx = 0; light_idx < entertainment_num_lights; light_idx++) + { + 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 + 3] = 0x00; // Red MSB + entertainment_msg[msg_idx + 4] = 0x00; // Red LSB; + entertainment_msg[msg_idx + 5] = 0x00; // Green MSB; + entertainment_msg[msg_idx + 6] = 0x00; // Green LSB; + entertainment_msg[msg_idx + 7] = 0x00; // Blue MSB; + entertainment_msg[msg_idx + 8] = 0x00; // Blue LSB; + } + + /*-------------------------------------------------*\ + | 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); + + /*-------------------------------------------------*\ + | Seed the Deterministic Random Bit Generator (RNG) | + \*-------------------------------------------------*/ + int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0); + + /*-------------------------------------------------*\ + | Parse certificate | + \*-------------------------------------------------*/ + ret = mbedtls_x509_crt_parse(&cacert, (const unsigned char*)mbedtls_test_cas_pem, mbedtls_test_cas_pem_len); + + /*-------------------------------------------------*\ + | Connect to the Hue bridge UDP server | + \*-------------------------------------------------*/ + ret = mbedtls_net_connect(&server_fd, bridge.getBridgeIP().c_str(), "2100", MBEDTLS_NET_PROTO_UDP); + + /*-------------------------------------------------*\ + | Configure defaults | + \*-------------------------------------------------*/ + ret = mbedtls_ssl_config_defaults( + &conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_PRESET_DEFAULT); + + 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); + + /*-------------------------------------------------*\ + | Convert client key to binary array | + \*-------------------------------------------------*/ + 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()); + + /*-------------------------------------------------*\ + | Set up the SSL | + \*-------------------------------------------------*/ + ret = mbedtls_ssl_setup(&ssl, &conf); + + ret = mbedtls_ssl_set_hostname(&ssl, "localhost"); + + 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); + + do + { + ret = mbedtls_ssl_handshake(&ssl); + } while (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE); +} + +bool EntertainmentMode::SetColorRGB(unsigned int light_index, unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned int msg_idx = HUE_ENTERTAINMENT_HEADER_SIZE + (light_index * HUE_ENTERTAINMENT_LIGHT_SIZE); + + entertainment_msg[msg_idx + 3] = red; // Red MSB + entertainment_msg[msg_idx + 4] = red; // Red LSB; + entertainment_msg[msg_idx + 5] = green; // Green MSB; + entertainment_msg[msg_idx + 6] = green; // Green LSB; + entertainment_msg[msg_idx + 7] = blue; // Blue MSB; + entertainment_msg[msg_idx + 8] = blue; // Blue LSB; + + return false; +} + +void EntertainmentMode::Update() +{ + mbedtls_ssl_write(&ssl, (const unsigned char*)entertainment_msg, entertainment_msg_size); +} +} // namespace hueplusplus