-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- added status led callback to allow customizing system heartbeat - added `CONFIG_StatusLedPin` option value ``-1` to allow disabling default status led - fixes unique identifier for MQTT message topics - removed use of interrupt for service button handling - now allowing multiple API handlers for the same domain/address - implemented asynchronous response over WebSocket channel - implemented POST data handling for sending command options over HTTP channel - added abstraction classes for module types *Switch*, *Dimmer*, *Color* - added *IR Transceiver* example - added *Color Light* example - fixes UPnP unique identifier
- Loading branch information
Showing
50 changed files
with
1,829 additions
and
148 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/* | ||
* HomeGenie-Mini (c) 2018-2024 G-Labs | ||
* | ||
* | ||
* This file is part of HomeGenie-Mini (HGM). | ||
* | ||
* HomeGenie-Mini is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* HomeGenie-Mini 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 General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with HomeGenie-Mini. If not, see <http://www.gnu.org/licenses/>. | ||
* | ||
* | ||
* Authors: | ||
* - Generoso Martello <[email protected]> | ||
* | ||
*/ | ||
|
||
#include "ColorLight.h" | ||
|
||
|
||
ColorLight::ColorLight(const char* domain, const char* address, const char* name): Dimmer(domain, address, name) { | ||
module->type = "Color"; | ||
|
||
// add properties | ||
// auto propStatusColorHsb = new ModuleParameter(IOEventPaths::Status_ColorHsb); | ||
// module->properties.add(propStatusColorHsb); | ||
|
||
onSetLevel([this](float l) { | ||
color.setColor(color.getHue(), color.getSaturation(), l, defaultTransitionMs); | ||
}); | ||
} | ||
|
||
void ColorLight::loop() { | ||
Dimmer::loop(); // parent | ||
|
||
if (color.isAnimating) { | ||
if (setColorCallback != nullptr) { | ||
setColorCallback(color.getRed(), color.getGreen(), color.getBlue()); | ||
} | ||
} | ||
|
||
} | ||
|
||
bool ColorLight::handleRequest(APIRequest* command, ResponseCallback* responseCallback) { | ||
if (Dimmer::handleRequest(command, responseCallback)) return true; | ||
|
||
auto m = getModule(command->Domain.c_str(), command->Address.c_str()); | ||
if (m != nullptr && command->Command == ControlApi::Control_ColorHsb) { | ||
|
||
auto hsvString = command->getOption(0); | ||
|
||
float o[4]; int oi = 0; | ||
int ci; | ||
do { | ||
ci = hsvString.indexOf(","); | ||
if (ci <= 0) { | ||
o[oi] = hsvString.toFloat(); | ||
break; | ||
} | ||
o[oi++] = hsvString.substring(0, ci).toFloat(); | ||
hsvString = hsvString.substring(ci + 1); | ||
} while (oi < 4); | ||
|
||
|
||
color.setColor(o[0], o[1], o[2], o[3]*1000); | ||
|
||
|
||
// Event Stream Message Enqueue (for MQTT/SSE/WebSocket propagation) | ||
auto eventValue = command->getOption(0); | ||
auto msg = QueuedMessage(m, IOEventPaths::Status_ColorHsb, eventValue, nullptr, IOEventDataType::Undefined); | ||
m->setProperty(IOEventPaths::Status_ColorHsb, eventValue, nullptr, IOEventDataType::Undefined); | ||
HomeGenie::getInstance()->getEventRouter().signalEvent(msg); | ||
// level prop | ||
auto levelValue = String(o[2]); // TODO: use sprintf %.6f | ||
auto msg2 = QueuedMessage(m, IOEventPaths::Status_Level, levelValue, nullptr, IOEventDataType::Undefined); | ||
m->setProperty(IOEventPaths::Status_Level, levelValue, nullptr, IOEventDataType::Undefined); | ||
HomeGenie::getInstance()->getEventRouter().signalEvent(msg2); | ||
|
||
if (o[2] > 0) { | ||
Switch::status = SWITCH_STATUS_ON; | ||
Switch::onLevel = o[2]; | ||
} else { | ||
Switch::status = SWITCH_STATUS_OFF; | ||
} | ||
|
||
responseCallback->writeAll(R"({ "ResponseText": "OK" })"); | ||
return true; | ||
|
||
} | ||
// not handled | ||
return false; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
/* | ||
* HomeGenie-Mini (c) 2018-2024 G-Labs | ||
* | ||
* | ||
* This file is part of HomeGenie-Mini (HGM). | ||
* | ||
* HomeGenie-Mini is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* HomeGenie-Mini 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 General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with HomeGenie-Mini. If not, see <http://www.gnu.org/licenses/>. | ||
* | ||
* | ||
* Authors: | ||
* - Generoso Martello <[email protected]> | ||
* | ||
*/ | ||
|
||
#ifndef HOMEGENIE_MINI_COLORLIGHT_H | ||
#define HOMEGENIE_MINI_COLORLIGHT_H | ||
|
||
#include <HomeGenie.h> | ||
#include <Utility.h> | ||
|
||
#include "Dimmer.h" | ||
|
||
using namespace Service; | ||
|
||
class LightColor { | ||
public: | ||
unsigned long duration; | ||
bool isAnimating = false; | ||
void setColor(float hue, float saturation, float value, unsigned long transitionMs) { | ||
duration = transitionMs; | ||
oh = h; | ||
os = s; | ||
ov = v; | ||
h = hue; | ||
s = saturation; | ||
v = value; | ||
startTime = millis(); | ||
isAnimating = true; | ||
} | ||
float getProgress() { | ||
float p = (float)(millis() - startTime) / (float)duration; | ||
if (p >= 1) { | ||
isAnimating = false; | ||
p = 1; | ||
} | ||
return p; | ||
} | ||
float getHue() { | ||
return oh + ((h - oh) * getProgress()); | ||
} | ||
float getSaturation() { | ||
return os + ((s - os) * getProgress()); | ||
} | ||
float getValue() { | ||
return ov + ((v - ov) * getProgress()); | ||
} | ||
float getRed() { | ||
auto orgb = Utility::hsv2rgb(hfix(oh), os, ov); | ||
auto crgb = Utility::hsv2rgb(hfix(h), s, v); | ||
float r = orgb.r + ((crgb.r - orgb.r) * getProgress()); | ||
return r; | ||
} | ||
float getGreen() { | ||
auto orgb = Utility::hsv2rgb(hfix(oh), os, ov); | ||
auto crgb = Utility::hsv2rgb(hfix(h), s, v); | ||
float g = orgb.g + ((crgb.g - orgb.g) * getProgress()); | ||
return g; | ||
} | ||
float getBlue() { | ||
auto orgb = Utility::hsv2rgb(hfix(oh), os, ov); | ||
auto crgb = Utility::hsv2rgb(hfix(h), s, v); | ||
float b = orgb.b + ((crgb.b - orgb.b) * getProgress()); | ||
return b; | ||
} | ||
private: | ||
float h; | ||
float s; | ||
float v; | ||
float oh, os, ov; | ||
unsigned long startTime; | ||
float hfix(float h) { | ||
return 1.325f - h; | ||
} | ||
|
||
}; | ||
|
||
class ColorLight: public Dimmer { | ||
public: | ||
ColorLight(const char* domain, const char* address, const char* name); | ||
|
||
void loop() override; | ||
bool handleRequest(APIRequest*, ResponseCallback*) override; | ||
|
||
void onSetColor(std::function<void(float,float,float)> callback) { | ||
setColorCallback = std::move(callback); | ||
} | ||
private: | ||
LightColor color; | ||
std::function<void(float,float,float)> setColorCallback = nullptr; | ||
|
||
void setColor(float h, float s, float v, float duration); | ||
}; | ||
|
||
|
||
#endif //HOMEGENIE_MINI_COLORLIGHT_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/* | ||
* HomeGenie-Mini (c) 2018-2024 G-Labs | ||
* | ||
* | ||
* This file is part of HomeGenie-Mini (HGM). | ||
* | ||
* HomeGenie-Mini is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* HomeGenie-Mini 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 General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with HomeGenie-Mini. If not, see <http://www.gnu.org/licenses/>. | ||
* | ||
* | ||
* Authors: | ||
* - Generoso Martello <[email protected]> | ||
* | ||
*/ | ||
|
||
#include "Dimmer.h" | ||
|
||
|
||
Dimmer::Dimmer(const char* domain, const char* address, const char* name): Switch(domain, address, name) { | ||
setLoopInterval(10); // fixed transition frequency | ||
module->type = "Dimmer"; | ||
onSetStatus([this](SwitchStatus status) { | ||
level.setLevel(status == SWITCH_STATUS_ON ? 1 : 0, defaultTransitionMs); | ||
}); | ||
} | ||
|
||
void Dimmer::loop() { | ||
|
||
if (level.isAnimating) { | ||
if (setLevelCallback != nullptr) { | ||
setLevelCallback(level.getLevel()); | ||
} | ||
} | ||
|
||
} | ||
|
||
bool Dimmer::handleRequest(APIRequest *command, ResponseCallback* responseCallback) { | ||
if (Switch::handleRequest(command, responseCallback)) return true; | ||
|
||
auto m = getModule(command->Domain.c_str(), command->Address.c_str()); | ||
if (m != nullptr && command->Command == ControlApi::Control_Level) { | ||
|
||
auto l = command->getOption(0).toFloat() / 100.0F; // 0.00 - 1.00 0 = OFF, 1.00 = MAX | ||
auto transition = command->getOption(1).isEmpty() ? defaultTransitionMs : command->getOption(1).toFloat(); // ms | ||
|
||
level.setLevel(l, transition); | ||
|
||
// Event Stream Message Enqueue (for MQTT/SSE/WebSocket propagation) | ||
auto eventPath = IOEventPaths::Status_Level; | ||
auto eventValue = String(l); | ||
auto msg = QueuedMessage(m, eventPath, eventValue, &l, IOEventDataType::Float); | ||
m->setProperty(eventPath, eventValue, &l, IOEventDataType::Float); | ||
HomeGenie::getInstance()->getEventRouter().signalEvent(msg); | ||
|
||
if (l > 0) { | ||
Switch::status = SWITCH_STATUS_ON; | ||
Switch::onLevel = l; | ||
} else { | ||
Switch::status = SWITCH_STATUS_OFF; | ||
} | ||
|
||
responseCallback->writeAll(R"({ "ResponseText": "OK" })"); | ||
return true; | ||
|
||
} | ||
// not handled | ||
return false; | ||
} |
Oops, something went wrong.