/** \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(); /*-------------------------------------------------*\ | Resize Entertainment Mode message buffer | \*-------------------------------------------------*/ entertainment_msg.resize(HUE_ENTERTAINMENT_HEADER_SIZE + (entertainment_num_lights * HUE_ENTERTAINMENT_LIGHT_SIZE)); /*-------------------------------------------------*\ | Fill in Entertainment Mode message header | \*-------------------------------------------------*/ memcpy(&entertainment_msg[0], "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); } bool EntertainmentMode::Connect() { /*-------------------------------------------------*\ | Signal the bridge to start streaming | | If successful, connect to the UDP port | \*-------------------------------------------------*/ 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); /*-------------------------------------------------*\ | If connecting failed, close and return false | \*-------------------------------------------------*/ if(ret != 0) { mbedtls_ssl_close_notify(&ssl); bridge.StopStreaming(std::to_string(group.getId())); return false; } /*-------------------------------------------------*\ | Configure defaults | \*-------------------------------------------------*/ ret = mbedtls_ssl_config_defaults( &conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_PRESET_DEFAULT); /*-------------------------------------------------*\ | If configuring failed, close and return false | \*-------------------------------------------------*/ if(ret != 0) { mbedtls_ssl_close_notify(&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); /*-------------------------------------------------*\ | 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()); /*-------------------------------------------------*\ | If configuring failed, close and return false | \*-------------------------------------------------*/ if(ret != 0) { mbedtls_ssl_close_notify(&ssl); bridge.StopStreaming(std::to_string(group.getId())); return false; } /*-------------------------------------------------*\ | Set up the SSL | \*-------------------------------------------------*/ ret = mbedtls_ssl_setup(&ssl, &conf); /*-------------------------------------------------*\ | If setup failed, close and return false | \*-------------------------------------------------*/ if(ret != 0) { mbedtls_ssl_close_notify(&ssl); bridge.StopStreaming(std::to_string(group.getId())); return false; } ret = mbedtls_ssl_set_hostname(&ssl, "localhost"); /*-------------------------------------------------*\ | If set hostname failed, close and return false | \*-------------------------------------------------*/ if(ret != 0) { mbedtls_ssl_close_notify(&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); /*-------------------------------------------------*\ | Handshake | \*-------------------------------------------------*/ do { ret = mbedtls_ssl_handshake(&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) { mbedtls_ssl_close_notify(&ssl); bridge.StopStreaming(std::to_string(group.getId())); return false; } return true; } else { return false; } } bool EntertainmentMode::Disconnect() { mbedtls_ssl_close_notify(&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) { 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 true; } else { return false; } } void EntertainmentMode::Update() { int ret; unsigned int total = 0; while(total < entertainment_msg.size()) { ret = mbedtls_ssl_write(&ssl, (const unsigned char*)&entertainment_msg[total], entertainment_msg.size()); if(ret < 0) { // Return if mbedtls_ssl_write errors return; } else { total += ret; } } } } // namespace hueplusplus