Skip to content

Commit

Permalink
Merge branch 'master' of github.com:oatpp/example-iot-hue-ssdp
Browse files Browse the repository at this point in the history
  • Loading branch information
lganzzzo committed Oct 25, 2021
2 parents 7920c45 + 0ed8241 commit d009d9c
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 23 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,15 @@ It is called e.g. by Alexa if you tell it 🗣️"Alexa, turn on <device name
Finally it returns a "success" or "error" object.
See [Lights (burgestrand.se)](http://www.burgestrand.se/hue-api/api/lights/)
## Thanks
- To @DavidHamburg for spotting an issue with the old device id's that prevented Alexa from finding the devices
## Notes
The newest version of the example shows the correct icon for the device in the Amazon Alexa app.
Support for color-temperature and brightness was added too.
Setting a custom color is still not forking properly.
While you can set a custom color, the Alexa app will show you an error and won't show the color you have set.
Feel free to fix this and send us your PR!
83 changes: 73 additions & 10 deletions src/controller/HueDeviceController.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ class HueDeviceController : public oatpp::web::server::api::ApiController {
" <serialNumber>" << m_desc->mac << "</serialNumber>\n"
" <UDN>uuid:" + m_desc->uuid + "</UDN>\n"
" <presentationURL>index.html</presentationURL>\n"
" <serviceList>\n"
" <service>\n"
" <serviceType>(null)</serviceType>\n"
" <serviceId>(null)</serviceId>\n"
" <controlURL>(null)</controlURL>\n"
" <eventSubURL>(null)</eventSubURL>\n"
" <SCPDURL>(null)</SCPDURL>\n"
" </service>\n"
" </serviceList>\n"
" </device>\n"
"</root>";

Expand Down Expand Up @@ -133,11 +142,17 @@ class HueDeviceController : public oatpp::web::server::api::ApiController {
// list all
auto devices = m_database->getHueDevices();
auto response = Fields<oatpp::Object<HueDeviceDto>>::createShared();
for (unsigned long d = 0; d < devices->size(); ++d) {
for (auto device = devices->begin(); device != devices->end(); device++) {
char num[32];
memset(num, 0, 18);
snprintf(num, 18, "%lu", d+1);
response[num] = devices[d];
snprintf(num, 18, "%u", *(device->first.get()) + 1);
if (device->second->state->colormode == nullptr) {
device->second->state->colormode = "ct";
if (device->second->state->ct == nullptr) {
device->second->state->ct = 500;
}
}
response[num] = device->second;
}
return addHueHeaders(createDtoResponse(Status::CODE_200, response));
}
Expand Down Expand Up @@ -166,6 +181,12 @@ class HueDeviceController : public oatpp::web::server::api::ApiController {
responseDto->back()->error = {{oatpp::String(num), oatpp::String("Not Found")}};
auto response = createDtoResponse(Status::CODE_404, responseDto);
}
if (specific->state->colormode == nullptr) {
specific->state->colormode = "ct";
if (specific->state->ct == nullptr) {
specific->state->ct = 500;
}
}
return addHueHeaders(createDtoResponse(Status::CODE_200, specific));
}

Expand Down Expand Up @@ -198,16 +219,58 @@ class HueDeviceController : public oatpp::web::server::api::ApiController {
* ToDo: Implement your "light turning on/off" here!
* Better: Replace the Database with your state and control logic so the "database" is in sync to your logic.
*/
OATPP_LOGI("HueDeviceController", "updateState: Setting light %d %s", *hueId.get(), updated->state->on ? "on" : "off");

responseDto->push_back(oatpp::Object<ResponseTypeDto>::createShared());
memset(num, 0, 32);
snprintf(num, 32, "/lights/%d/state/on", *hueId.get());
responseDto->back()->success = {{oatpp::String(num), updated->state->on}};
if (state->on != nullptr) {
memset(num, 0, 32);
snprintf(num, 32, "/lights/%d/state/on", *hueId.get());
if (responseDto->back()->success.get() == nullptr) {
responseDto->back()->success = {{oatpp::String(num), updated->state->on}};
} else {
responseDto->back()->success->push_back({oatpp::String(num), updated->state->on});
}
}

responseDto->push_back(oatpp::Object<ResponseTypeDto>::createShared());
memset(num, 0, 32);
snprintf(num, 32, "/lights/%d/state/bri", *hueId.get());
responseDto->back()->success = {{"username", updated->state->bri}};
if (state->bri != nullptr) {
memset(num, 0, 32);
snprintf(num, 32, "/lights/%d/state/bri", *hueId.get());
if (responseDto->back()->success.get() == nullptr) {
responseDto->back()->success = {{oatpp::String(num), updated->state->bri}};
} else {
responseDto->back()->success->push_back({oatpp::String(num), updated->state->bri});
}
}

if (state->hue != nullptr) {
memset(num, 0, 32);
snprintf(num, 32, "/lights/%d/state/hue", *hueId.get());
if (responseDto->back()->success.get() == nullptr) {
responseDto->back()->success = {{oatpp::String(num), updated->state->hue}};
} else {
responseDto->back()->success->push_back({oatpp::String(num), updated->state->hue});
}
}

if (state->sat != nullptr) {
memset(num, 0, 32);
snprintf(num, 32, "/lights/%d/state/sat", *hueId.get());
if (responseDto->back()->success.get() == nullptr) {
responseDto->back()->success = {{oatpp::String(num), updated->state->sat}};
} else {
responseDto->back()->success->push_back({oatpp::String(num), updated->state->sat});
}
}

if (state->ct != nullptr) {
memset(num, 0, 32);
snprintf(num, 32, "/lights/%d/state/ct", *hueId.get());
if (responseDto->back()->success.get() == nullptr) {
responseDto->back()->success = {{oatpp::String(num), updated->state->ct}};
} else {
responseDto->back()->success->push_back({oatpp::String(num), updated->state->ct});
}
}

auto response = createDtoResponse(Status::CODE_200, responseDto);
return addHueHeaders(response);
Expand Down
39 changes: 31 additions & 8 deletions src/db/Database.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

#include "Database.hpp"
#include "oatpp/core/parser/Caret.hpp"
#include "oatpp/core/base/StrBuffer.hpp"

HueDevice Database::updateFromStateDto(v_int32 id, const oatpp::Object<HueDeviceStateDto> &hueDeviceStateDto) {
std::lock_guard<oatpp::concurrency::SpinLock> lock(m_lock);
Expand All @@ -16,18 +17,31 @@ HueDevice Database::updateFromStateDto(v_int32 id, const oatpp::Object<HueDevice

if (hueDeviceStateDto->on != nullptr) {
it->second.on = hueDeviceStateDto->on;
if (it->second.on) { // if "on" was set to true an brightness is 0, set it to max brightness
if (it->second.bri == 0) {
it->second.bri = 254;
}
}
}

if (hueDeviceStateDto->hue != nullptr) {
// if hue is set from the api-call, colormode "hue" is assumed
it->second.hue = hueDeviceStateDto->hue;
it->second.mode = "hue"; // is this realy the name?
}

if (hueDeviceStateDto->sat != nullptr) {
it->second.sat = hueDeviceStateDto->sat;
}

if (hueDeviceStateDto->ct != nullptr) {
// if ct is set from the api-call, colormode "ct" is assumed
it->second.ct = hueDeviceStateDto->ct;
it->second.mode = "ct";
}

if (hueDeviceStateDto->colormode != nullptr) {
it->second.mode = hueDeviceStateDto->colormode;
}

return it->second;
Expand All @@ -46,29 +60,38 @@ HueDevice Database::serializeFromDto(const oatpp::Object<HueDeviceDto>& hueDevic
hueDevice.sat = hueDeviceDto->state->sat;
if (hueDeviceDto->state->ct != nullptr)
hueDevice.ct = hueDeviceDto->state->ct;
if (hueDeviceDto->state->colormode != nullptr)
hueDevice.mode = hueDeviceDto->state->colormode;
}
if(hueDeviceDto->uniqueid){
oatpp::parser::Caret caret(hueDeviceDto->uniqueid);
if (!caret.findChar('-'))
throw std::runtime_error("Malformed uniqueid: '-' not found");
caret.inc();
caret.setPosition(hueDeviceDto->uniqueid->getSize() - 4);
v_int32 id = caret.parseInt();
if (caret.hasError())
throw std::runtime_error("Malformed uniqueid: Unable to parse id integer");
hueDevice.id = id;
hueDevice.id = id - 1;
}
return hueDevice;
}

oatpp::Object<HueDeviceDto> Database::deserializeToDto(const HueDevice& hueDevice){
auto dto = HueDeviceDto::createShared();
dto->uniqueid = oatpp::String("BE570A70CAFE") + "-" + oatpp::String(hueDevice.id + 1); // BEST OAT CAFE!
size_t namehash = std::hash<std::string>{}(hueDevice.name->std_str());
char idstr[32] = {0};
if (sizeof(size_t) == 8) {
// Mod with the largest prime under 2^32 to map the 64bit hash to 32bit
// This will not harm a good hash, yet certainly make weak hashes better.
namehash = namehash % 4294967291;
}
snprintf(idstr, 32, "%08zx%04d", namehash, hueDevice.id + 1);
dto->uniqueid = idstr;
dto->name = hueDevice.name;
dto->state->bri = hueDevice.bri;
dto->state->on = hueDevice.on;
dto->state->ct = hueDevice.ct;
dto->state->hue = hueDevice.hue;
dto->state->sat = hueDevice.sat;
dto->state->colormode = hueDevice.mode;
return dto;
}

Expand Down Expand Up @@ -109,12 +132,12 @@ oatpp::Object<HueDeviceDto> Database::getHueDeviceById(v_int32 id) {
return deserializeToDto(it->second);
}

oatpp::List<oatpp::Object<HueDeviceDto>> Database::getHueDevices(){
oatpp::PairList<oatpp::UInt32, oatpp::Object<HueDeviceDto>> Database::getHueDevices(){
std::lock_guard<oatpp::concurrency::SpinLock> lock(m_lock);
oatpp::List<oatpp::Object<HueDeviceDto>> result({});
oatpp::PairList<oatpp::UInt32, oatpp::Object<HueDeviceDto>> result({});
auto it = m_HueDevicesById.begin();
while (it != m_HueDevicesById.end()) {
result->push_back(deserializeToDto(it->second));
result->emplace_back(it->first, deserializeToDto(it->second));
it++;
}
return result;
Expand Down
8 changes: 4 additions & 4 deletions src/db/Database.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ class Database {
v_int32 m_idCounter; ///< counter to generate HueDeviceIds
std::unordered_map<v_int32, HueDevice> m_HueDevicesById; ///< Map HueDeviceId to HueDevice
private:
HueDevice serializeFromDto(const oatpp::Object<HueDeviceDto>& hueDeviceDto);
static HueDevice serializeFromDto(const oatpp::Object<HueDeviceDto>& hueDeviceDto);
HueDevice updateFromStateDto(v_int32 id, const oatpp::Object<HueDeviceStateDto> &hueDeviceStateDto);
oatpp::Object<HueDeviceDto> deserializeToDto(const HueDevice& hueDevice);
static oatpp::Object<HueDeviceDto> deserializeToDto(const HueDevice& hueDevice);
public:

Database()
Expand All @@ -36,13 +36,13 @@ class Database {
* @param bri - the initial brightness value
* @return - ID of the new 'light'
*/
v_int32 registerHueDevice(const oatpp::String &name, const oatpp::Boolean &on = false, const oatpp::Int32 &bri = 0);
v_int32 registerHueDevice(const oatpp::String &name, const oatpp::Boolean &on = false, const oatpp::Int32 &bri = 254);

oatpp::Object<HueDeviceDto> createHueDevice(const oatpp::Object<HueDeviceDto>& hueDeviceDto);
oatpp::Object<HueDeviceDto> updateHueDevice(const oatpp::Object<HueDeviceDto>& hueDeviceDto);
oatpp::Object<HueDeviceDto> updateHueDeviceState(v_int32 id, const oatpp::Object<HueDeviceStateDto>& hueDeviceStateDto);
oatpp::Object<HueDeviceDto> getHueDeviceById(v_int32 id);
oatpp::List<oatpp::Object<HueDeviceDto>> getHueDevices();
oatpp::PairList<oatpp::UInt32, oatpp::Object<HueDeviceDto>> getHueDevices();
bool deleteHueDevice(v_int32 id);

};
Expand Down
1 change: 1 addition & 0 deletions src/db/model/HueDevice.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class HueDevice {
public:
v_int32 id;
oatpp::String name;
oatpp::String mode;
oatpp::Boolean on = false;
oatpp::UInt8 bri = (v_uint8)0;
oatpp::UInt8 sat = (v_uint8)0;
Expand Down
6 changes: 5 additions & 1 deletion src/dto/HueDeviceDto.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@ class HueDeviceStateDto : public oatpp::DTO {
DTO_FIELD(UInt8, sat);
DTO_FIELD(UInt16, hue);
DTO_FIELD(UInt16, ct); // white color temperature, 154 (cold) - 500 (warm)
DTO_FIELD(String, colormode);

// Fixed values
DTO_FIELD(List<Int32>, xy) = {0,0};
DTO_FIELD(Boolean, reachable) = true;
DTO_FIELD(String, alert) = "none";
DTO_FIELD(String, effect) = "none";

};

class HueDeviceDto : public oatpp::DTO {
Expand All @@ -47,7 +51,7 @@ class HueDeviceDto : public oatpp::DTO {
DTO_FIELD(String, uniqueid); // name-number

// Fixed values
DTO_FIELD(String, type) = "Extended Color Light";
DTO_FIELD(String, type) = "Dimmable light";
DTO_FIELD(String, modelid) = "LCT007";
DTO_FIELD(String, swversion) = "5.105.0.21169";
DTO_FIELD(oatpp::Object<HueDeviceCapabilitiesDto>, capabilities) = HueDeviceCapabilitiesDto::createShared();
Expand Down

0 comments on commit d009d9c

Please sign in to comment.