diff --git a/examples/color-light/api/ColorLight.cpp b/examples/color-light/api/ColorLight.cpp
deleted file mode 100644
index a8f5247..0000000
--- a/examples/color-light/api/ColorLight.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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 .
- *
- *
- * Authors:
- * - Generoso Martello
- *
- */
-
-#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;
-}
-
diff --git a/examples/color-light/api/ColorLight.h b/examples/color-light/api/ColorLight.h
deleted file mode 100644
index 543243b..0000000
--- a/examples/color-light/api/ColorLight.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * 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 .
- *
- *
- * Authors:
- * - Generoso Martello
- *
- */
-
-#ifndef HOMEGENIE_MINI_COLORLIGHT_H
-#define HOMEGENIE_MINI_COLORLIGHT_H
-
-#include
-#include
-
-#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 callback) {
- setColorCallback = std::move(callback);
- }
-private:
- LightColor color;
- std::function setColorCallback = nullptr;
-
- void setColor(float h, float s, float v, float duration);
-};
-
-
-#endif //HOMEGENIE_MINI_COLORLIGHT_H
diff --git a/examples/color-light/api/Dimmer.cpp b/examples/color-light/api/Dimmer.cpp
deleted file mode 100644
index b7d2f4a..0000000
--- a/examples/color-light/api/Dimmer.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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 .
- *
- *
- * Authors:
- * - Generoso Martello
- *
- */
-
-#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;
-}
diff --git a/examples/color-light/api/Dimmer.h b/examples/color-light/api/Dimmer.h
deleted file mode 100644
index 43b197e..0000000
--- a/examples/color-light/api/Dimmer.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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 .
- *
- *
- * Authors:
- * - Generoso Martello
- *
- */
-
-#ifndef HOMEGENIE_MINI_DIMMER_H
-#define HOMEGENIE_MINI_DIMMER_H
-
-#include
-
-#include "Switch.h"
-
-using namespace Service;
-
-class DimmerLevel {
-public:
- unsigned long duration;
- bool isAnimating = false;
- void setLevel(float l, unsigned long transitionMs) {
- duration = transitionMs;
- ol = level;
- level = l;
- startTime = millis();
- isAnimating = true;
- }
- float getProgress() {
- float p = (float)(millis() - startTime) / (float)duration;
- if (p >= 1) {
- isAnimating = false;
- p = 1;
- }
- return p;
- }
- float getLevel() {
- return ol + ((level - ol) * getProgress());
- }
-private:
- float level;
- float ol;
- unsigned long startTime;
-
-};
-
-class Dimmer: public Switch {
-public:
- Dimmer(const char* domain, const char* address, const char* name);
- void loop() override;
-
- bool handleRequest(APIRequest*, ResponseCallback*) override;
-
- void onSetLevel(std::function callback) {
- setLevelCallback = std::move(callback);
- }
-private:
- DimmerLevel level;
- std::function setLevelCallback = nullptr;
-protected:
- const unsigned long defaultTransitionMs = 500;
-};
-
-#endif //HOMEGENIE_MINI_DIMMER_H
diff --git a/examples/color-light/api/Switch.cpp b/examples/color-light/api/Switch.cpp
deleted file mode 100644
index c1af1ab..0000000
--- a/examples/color-light/api/Switch.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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 .
- *
- *
- * Authors:
- * - Generoso Martello
- *
- */
-
-#include "Switch.h"
-
-Switch::Switch(const char* domain, const char* address, const char* name) {
- module = new Module();
- module->domain = domain;
- module->address = address;
- module->type = "Switch";
- module->name = name;
- // add properties
- auto propStatusLevel = new ModuleParameter(IOEventPaths::Status_Level);
- module->properties.add(propStatusLevel);
-
- moduleList.add(module);
-}
-
-void Switch::init() {
-}
-
-bool Switch::handleRequest(APIRequest *command, ResponseCallback* responseCallback) {
- auto m = getModule(command->Domain.c_str(), command->Address.c_str());
- if (m != nullptr) {
-
- auto eventPath = IOEventPaths::Status_Level;
- SwitchStatus s = SWITCH_STATUS_NOT_SET;
- if (command->Command == ControlApi::Control_On) {
- s = SWITCH_STATUS_ON;
- } else if (command->Command == ControlApi::Control_Off) {
- s = SWITCH_STATUS_OFF;
- } else if (command->Command == ControlApi::Control_Toggle) {
- s = status == SWITCH_STATUS_ON ? SWITCH_STATUS_OFF : SWITCH_STATUS_ON;
- }
-
- if (s != SWITCH_STATUS_NOT_SET) {
- status = s;
-
- if (setStatusCallback != nullptr) {
- setStatusCallback(status);
- }
-
- // Event Stream Message Enqueue (for MQTT/SSE/WebSocket propagation)
- float l = status == SWITCH_STATUS_ON ? onLevel : 0;
- 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);
-
- responseCallback->writeAll(R"({ "ResponseText": "OK" })");
-
- return true;
- }
-
- }
- // not handled
- return false;
-}
-
-bool Switch::canHandleDomain(String* domain) {
- return domain->equals(IO::IOEventDomains::HomeAutomation_HomeGenie);
-}
-
-bool Switch::handleEvent(IIOEventSender *sender,
- const char* domain, const char* address,
- const unsigned char *eventPath, void *eventData, IOEventDataType dataType) {
- return false;
-}
-
-Module* Switch::getModule(const char* domain, const char* address) {
- for (int i = 0; i < moduleList.size(); i++) {
- Module* m = moduleList.get(i);
- if (m->domain.equals(domain) && m->address.equals(address))
- return m;
- }
- return nullptr;
-}
-
-LinkedList* Switch::getModuleList() {
- return &moduleList;
-}
diff --git a/examples/color-light/api/Switch.h b/examples/color-light/api/Switch.h
deleted file mode 100644
index 71978bf..0000000
--- a/examples/color-light/api/Switch.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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 .
- *
- *
- * Authors:
- * - Generoso Martello
- *
- */
-
-#ifndef HOMEGENIE_MINI_SWITCH_H
-#define HOMEGENIE_MINI_SWITCH_H
-
-#include
-
-using namespace Service;
-
-enum SwitchStatus {
- SWITCH_STATUS_NOT_SET = -1,
- SWITCH_STATUS_OFF,
- SWITCH_STATUS_ON,
-};
-
-class Switch: public Task, public APIHandler {
-public:
- Switch(const char* domain, const char* address, const char* name);
- void init() override;
- bool canHandleDomain(String* domain) override;
- bool handleRequest(APIRequest*, ResponseCallback*) override;
- bool handleEvent(IIOEventSender*,
- const char* domain, const char* address,
- const unsigned char *eventPath, void* eventData, IOEventDataType) override;
-
- Module* getModule(const char* domain, const char* address) override;
- LinkedList* getModuleList() override;
- void onSetStatus(std::function callback) {
- setStatusCallback = std::move(callback);
- }
-private:
- LinkedList moduleList;
- std::function setStatusCallback = nullptr;
-protected:
- Module* module;
- SwitchStatus status = SWITCH_STATUS_NOT_SET;
- float onLevel = 1;
-};
-
-#endif //HOMEGENIE_MINI_SWITCH_H
diff --git a/examples/color-light/color-light.cpp b/examples/color-light/color-light.cpp
index 9f1d6da..a7da3f8 100644
--- a/examples/color-light/color-light.cpp
+++ b/examples/color-light/color-light.cpp
@@ -24,14 +24,15 @@
*/
#include
+#include
#include
#include "configuration.h"
-#include "api/ColorLight.h"
using namespace Service;
+using namespace Service::API::devices;
HomeGenie* homeGenie;
diff --git a/examples/color-light/configuration.h b/examples/color-light/configuration.h
index c60a017..a62fc18 100644
--- a/examples/color-light/configuration.h
+++ b/examples/color-light/configuration.h
@@ -1,11 +1,3 @@
-#define CONFIG_IRReceiverPin 5
-#define CONFIG_IRTransmitterPin 7
-
-#define CONFIG_IR_MODULE_ADDRESS "IR"
-
-#ifdef MINI_ESP32
-
-#define CONFIG_IRTransmitterPin 22
-#define CONFIG_IRReceiverPin 21
-
+#ifndef CONFIG_StatusLedNeoPixelPin
+#define CONFIG_StatusLedNeoPixelPin 10
#endif
diff --git a/examples/ir-transceiver/configuration.h b/examples/ir-transceiver/configuration.h
index c60a017..0c0b7cf 100644
--- a/examples/ir-transceiver/configuration.h
+++ b/examples/ir-transceiver/configuration.h
@@ -1,6 +1,10 @@
#define CONFIG_IRReceiverPin 5
#define CONFIG_IRTransmitterPin 7
+#ifndef CONFIG_StatusLedNeoPixelPin
+#define CONFIG_StatusLedNeoPixelPin 10
+#endif
+
#define CONFIG_IR_MODULE_ADDRESS "IR"
#ifdef MINI_ESP32
diff --git a/examples/ir-transceiver/ir-transceiver.cpp b/examples/ir-transceiver/ir-transceiver.cpp
index 6b327f8..049daf1 100644
--- a/examples/ir-transceiver/ir-transceiver.cpp
+++ b/examples/ir-transceiver/ir-transceiver.cpp
@@ -32,14 +32,20 @@
#include "io/IRTransmitter.h"
#include "api/IRTransceiverHandler.h"
+#ifdef ESP32_C3
+#include
+#endif
+
using namespace Service;
HomeGenie* homeGenie;
+
#ifdef ESP32_C3
+using namespace Service::API::devices;
#include
// Custom status led (builtin NeoPixel RGB on pin 10)
-Adafruit_NeoPixel pixels(1, 10, NEO_GRB + NEO_KHZ800);
+Adafruit_NeoPixel pixels(1, CONFIG_StatusLedNeoPixelPin, NEO_GRB + NEO_KHZ800);
void statusLedCallback(bool isLedOn) {
if (isLedOn) {
pixels.setPixelColor(0, Adafruit_NeoPixel::Color(50, 50, 0));
@@ -48,12 +54,16 @@ void statusLedCallback(bool isLedOn) {
}
pixels.show();
}
+unsigned long helloWorldDuration = 10000;
+bool helloWorldActive = true;
#endif
void setup() {
#ifdef ESP32_C3
// Custom status led (builtin NeoPixel RGB on pin 10)
- Config::statusLedCallback(&statusLedCallback);
+// if (!Config::isDeviceConfigured()) {
+ Config::statusLedCallback(&statusLedCallback);
+// }
pixels.begin();
#endif
@@ -67,10 +77,26 @@ void setup() {
auto transmitter = new IR::IRTransmitter(transmitterConfig);
homeGenie->addAPIHandler(new IRTransceiverHandler(transmitter, receiver));
+#ifdef ESP32_C3
+ auto colorLight = new ColorLight(IO::IOEventDomains::HomeAutomation_HomeGenie, "C1", "Demo Light");
+ colorLight->onSetColor([](float r, float g, float b) {
+ pixels.setPixelColor(0, r, g, b);
+ pixels.show();
+ });
+ homeGenie->addAPIHandler(colorLight);
+#endif
+
homeGenie->begin();
}
+
void loop()
{
homeGenie->loop();
+#ifdef ESP32_C3
+ if (helloWorldActive && millis() > helloWorldDuration && Config::isDeviceConfigured()) {
+ helloWorldActive = false;
+ Config::statusLedCallback(nullptr);
+ }
+#endif
}
diff --git a/examples/shutter/api/ShutterHandler.cpp b/examples/shutter/api/ShutterHandler.cpp
index a29f25b..c6e813c 100644
--- a/examples/shutter/api/ShutterHandler.cpp
+++ b/examples/shutter/api/ShutterHandler.cpp
@@ -45,6 +45,8 @@ namespace Service { namespace API {
shutterLevel = new ModuleParameter(IOEventPaths::Status_Level);
shutterModule->properties.add(shutterLevel);
+ shutterControl->setModule(shutterModule);
+
moduleList.add(shutterModule);
}
@@ -70,10 +72,24 @@ namespace Service { namespace API {
shutterControl->close();
+ responseCallback->writeAll(R"({ "ResponseText": "OK" })");
+
} else if (command->Command == "Control.Open" || command->Command == "Control.On") {
shutterControl->open();
+ responseCallback->writeAll(R"({ "ResponseText": "OK" })");
+
+ } else if (command->Command == "Shutter.Speed") {
+
+ shutterControl->setSpeed(command->OptionsString.toFloat());
+
+ responseCallback->writeAll(R"({ "ResponseText": "OK" })");
+
+ } else if (command->Command == "Shutter.Calibrate") {
+
+ // TODO: ...
+
} else {
return false;
@@ -106,6 +122,9 @@ namespace Service { namespace API {
case Float:
m.value = String(*(float *) eventData);
break;
+ case Text:
+ m.value = String(*(String *) eventData);
+ break;
default:
m.value = String(*(int32_t *) eventData);
}
diff --git a/examples/shutter/configuration.h b/examples/shutter/configuration.h
index e69de29..0c16ae2 100644
--- a/examples/shutter/configuration.h
+++ b/examples/shutter/configuration.h
@@ -0,0 +1,11 @@
+#ifndef CONFIG_StatusLedNeoPixelPin
+#define CONFIG_StatusLedNeoPixelPin 10
+#endif
+
+// TODO: #define CONFIG_StepperMotorPinXX YY
+
+#ifdef ESP32_C3
+ #define CONFIG_ServoMotorPin 5
+#else
+ #define CONFIG_ServoMotorPin 15
+#endif
diff --git a/examples/shutter/io/IShutterDriver.h b/examples/shutter/io/IShutterDriver.h
index 6464308..cf46bd8 100644
--- a/examples/shutter/io/IShutterDriver.h
+++ b/examples/shutter/io/IShutterDriver.h
@@ -47,6 +47,8 @@ namespace IO { namespace Components {
virtual void open() = 0;
virtual void close() = 0;
virtual void level(float) = 0;
+ virtual void calibrate() = 0;
+ virtual void speed(float) = 0;
IIOEventSender* eventSender = nullptr;
};
}}
diff --git a/examples/shutter/io/ShutterControl.cpp b/examples/shutter/io/ShutterControl.cpp
index d300efe..027a853 100644
--- a/examples/shutter/io/ShutterControl.cpp
+++ b/examples/shutter/io/ShutterControl.cpp
@@ -44,14 +44,12 @@ namespace IO { namespace Components {
void ShutterControl::setLevel(float level) {
shutterDriver->level(level);
}
+ void ShutterControl::setSpeed(float s) {
+ shutterDriver->speed(s);
+ }
void ShutterControl::calibrate() {
- // TODO: CALIBRATE WILL set the value of "totalTimeSpanMs" variable:
- // TODO: --> after starting "calibration" the shutter will go up
- // TODO: --> until the user confirms with any input (e.g. the "open" button)
- // TODO: --> that the roller/shades are fully open.
- // TODO: --> the time that the shutter took to go from position 0 to 100% will
- // TODO: ---> be stored to "totalTimeSpanMs" and calibration process will be complete
+ // TODO: ...
}
}}
diff --git a/examples/shutter/io/ShutterControl.h b/examples/shutter/io/ShutterControl.h
index ac85c40..329fe7a 100644
--- a/examples/shutter/io/ShutterControl.h
+++ b/examples/shutter/io/ShutterControl.h
@@ -43,8 +43,6 @@ namespace IO { namespace Components {
class ShutterControl : public IIOEventSender {
private:
IShutterDriver* shutterDriver;
- String domain = IO::IOEventDomains::Automation_Components;
- String address = SERVO_MODULE_ADDRESS;
public:
ShutterControl() {
shutterDriver = new ServoDriver();
@@ -57,6 +55,7 @@ namespace IO { namespace Components {
void open();
void close();
void setLevel(float level);
+ void setSpeed(float speed);
void calibrate(); // TODO:....
};
diff --git a/examples/shutter/io/drivers/ServoDriver.h b/examples/shutter/io/drivers/ServoDriver.h
index 32344dc..66fcd71 100644
--- a/examples/shutter/io/drivers/ServoDriver.h
+++ b/examples/shutter/io/drivers/ServoDriver.h
@@ -35,35 +35,46 @@
#ifndef HOMEGENIE_MINI_SERVODRIVER_H
#define HOMEGENIE_MINI_SERVODRIVER_H
-#include "examples/shutter/io/IShutterDriver.h"
+#include
-#include ".pio/libdeps/shutter/ESP32Servo/src/ESP32Servo.h"
+#include
+
+#include "../../configuration.h"
+#include "../../io/IShutterDriver.h"
-#include "io/Logger.h"
#define SERVO_DRIVER_NS_PREFIX "IO::Components:ShutterControl::ServoDriver"
namespace IO { namespace Components {
- class ServoDriver: public Task, public IShutterDriver {
+
+ static bool ServoDriver_triggered;
+
+
+ static int lastCommand = 0;
+ static int revolutions = 0;
+ static int revolutionsMax = 17;
+ static bool stopRequested = false;
+ static unsigned long lastTickMs = 0;
+ static float currentLevel = 0;
+ static float targetLevel = -1;
+ static int direction = -1; // reverse open/close
+
+
+ class ServoDriver: public Task, public IShutterDriver {
private:
String domain = IO::IOEventDomains::Automation_Components;
String address = SERVO_MODULE_ADDRESS;
Servo* servoDriver;
+
+ const int servoPin = CONFIG_ServoMotorPin;
+
const int frequency = 330; // Hz // Standard is 50(hz) servo
- const int servoPin = 15;
int minUs = 500;
int maxUs = 2500;
int stopUs = 1500;
- int stepSpeed = 300; // [1 .. 1000] microseconds
+ int stepSpeed = 800; // [1 .. 1000] microseconds
- int lastCommand = 0;
- long lastCommandTs = 0;
unsigned long lastEventMs = 0;
- float currentLevel = 0;
-
- float totalTimeSpanMs = 20000.0f;
- bool stopRequested = false;
- float stopTime;
public:
ServoDriver() {}
@@ -72,87 +83,110 @@ namespace IO { namespace Components {
servoDriver = new Servo();
servoDriver->setPeriodHertz(frequency);
servoDriver->attach(servoPin, minUs, maxUs);
+
+ ServoDriver_triggered = false;
+ pinMode(3, INPUT_PULLUP);
+ attachInterrupt(3, [] {
+
+// TODO: should consider speed?
+ ServoDriver_triggered = millis() - lastTickMs > 100;
+ lastTickMs = millis();
+
+ if (ServoDriver_triggered) {
+ if (lastCommand != SHUTTER_COMMAND_NONE) {
+ revolutions += (lastCommand == SHUTTER_COMMAND_OPEN ? 1 : -1);
+ float level = (float)revolutions / (float)revolutionsMax;
+ if (revolutions <= 0 || revolutions >= revolutionsMax || (targetLevel != -1 && level == targetLevel)) {
+ stopRequested = true;
+ }
+ }
+ }
+
+ }, CHANGE);
+
Logger::info("| ✔ %s", SERVO_DRIVER_NS_PREFIX);
}
+ void calibrate() override {
+
+ // TODO: ....
+
+ }
+ void speed(float s) override {
+ // s -> ]0.0 .. 1.0[
+ stepSpeed = s * 1000;
+ }
void stop() override {
servoDriver->write(stopUs);
}
void open() override {
- if (lastCommand != SHUTTER_COMMAND_NONE) {
+ if (lastCommand != SHUTTER_COMMAND_NONE || revolutions == revolutionsMax) {
stopRequested = true;
} else {
- servoDriver->write(stopUs + stepSpeed);
+ servoDriver->write(stopUs + stepSpeed * direction);
lastCommand = SHUTTER_COMMAND_OPEN;
- lastCommandTs = millis();
+ lastTickMs = millis();
}
}
void close() override {
- if (lastCommand != SHUTTER_COMMAND_NONE) {
+ if (lastCommand != SHUTTER_COMMAND_NONE || revolutions == 0) {
stopRequested = true;
} else {
- servoDriver->write(stopUs - stepSpeed);
+ servoDriver->write(stopUs - stepSpeed * direction);
lastCommand = SHUTTER_COMMAND_CLOSE;
- lastCommandTs = millis();
+ lastTickMs = millis();
}
}
void level(float level) override {
- float levelDiff = (level / 100.f) - currentLevel;
+ targetLevel = round((level / 100.f) * revolutionsMax) / revolutionsMax;
+ float levelDiff = targetLevel - currentLevel;
if (levelDiff < 0) {
- stopTime = millis() - (totalTimeSpanMs * levelDiff);
close();
} else if (levelDiff > 0) {
- stopTime = millis() + (totalTimeSpanMs * levelDiff);
open();
}
}
void loop() override {
- long elapsed = millis() - lastCommandTs;
- float percent = ((float)elapsed / totalTimeSpanMs);
- if (stopRequested || (stopTime != 0 && millis() >= stopTime)) {
+ if (lastCommand != SHUTTER_COMMAND_NONE && (millis() - lastTickMs > 1000)) {
+
+ // MOTOR ERROR!!!!! DID NOT RECEIVE "PULSE" for 1000ms
+
+ String err = "No pulse received from motor.";
+ Logger::info("@%s [%s %s]", SHUTTER_CONTROL_NS_PREFIX, (IOEventPaths::Status_Error), err.c_str());
+ eventSender->sendEvent((const uint8_t*)(IOEventPaths::Status_Error), &err, IOEventDataType::Text);
+
+ stopRequested = true;
+
+ // TODO: SHOULD GO BACK TO THE POSITION WHERE STARTED THE COMMAND
+ }
+
+ if (stopRequested) {
stopRequested = false;
- stopTime = 0;
stop();
- if (lastCommand == SHUTTER_COMMAND_OPEN) {
- currentLevel += percent;
- } else if (lastCommand == SHUTTER_COMMAND_CLOSE) {
- currentLevel -= percent;
- }
+ currentLevel = (float)revolutions / (float)revolutionsMax;
lastCommand = SHUTTER_COMMAND_NONE;
+ targetLevel = -1;
Logger::info("@%s [%s %.2f]", SHUTTER_CONTROL_NS_PREFIX, (IOEventPaths::Status_Level), currentLevel);
- eventSender->sendEvent(domain.c_str(), address.c_str(), (const uint8_t*)(IOEventPaths::Status_Level), ¤tLevel, IOEventDataType::Float);
+ eventSender->sendEvent((const uint8_t*)(IOEventPaths::Status_Level), ¤tLevel, IOEventDataType::Float);
} else if (lastCommand == SHUTTER_COMMAND_OPEN) {
- if (elapsed > (totalTimeSpanMs - (currentLevel * totalTimeSpanMs))) {
- lastCommand = SHUTTER_COMMAND_NONE;
- stop();
- currentLevel = 1;
- Logger::info("@%s [%s %.2f]", SHUTTER_CONTROL_NS_PREFIX, (IOEventPaths::Status_Level), currentLevel);
- eventSender->sendEvent(domain.c_str(), address.c_str(), (const uint8_t *) (IOEventPaths::Status_Level),
- ¤tLevel, IOEventDataType::Float);
- } else if (millis() - lastEventMs > EVENT_EMIT_FREQUENCY) {
- float level = currentLevel + percent;
+ if (millis() - lastEventMs > EVENT_EMIT_FREQUENCY) {
+ float level = (float)revolutions / (float)revolutionsMax;
+ lastEventMs = millis();
Logger::info("@%s [%s %.2f]", SHUTTER_CONTROL_NS_PREFIX, (IOEventPaths::Status_Level), level);
- eventSender->sendEvent(domain.c_str(), address.c_str(), (const uint8_t *) (IOEventPaths::Status_Level),
- &level, IOEventDataType::Float);
+ eventSender->sendEvent((const uint8_t *) (IOEventPaths::Status_Level), &level, IOEventDataType::Float);
}
} else if (lastCommand == SHUTTER_COMMAND_CLOSE) {
- if (elapsed > (currentLevel * totalTimeSpanMs)) {
- lastCommand = SHUTTER_COMMAND_NONE;
- stop();
- currentLevel = 0;
- Logger::info("@%s [%s %.2f]", SHUTTER_CONTROL_NS_PREFIX, (IOEventPaths::Status_Level), currentLevel);
- eventSender->sendEvent(domain.c_str(), address.c_str(), (const uint8_t*)(IOEventPaths::Status_Level), ¤tLevel, IOEventDataType::Float);
- } else if (millis() - lastEventMs > EVENT_EMIT_FREQUENCY) {
- float level = currentLevel - percent;
+ if (millis() - lastEventMs > EVENT_EMIT_FREQUENCY) {
+ float level = (float)revolutions / (float)revolutionsMax;
+ lastEventMs = millis();
Logger::info("@%s [%s %.2f]", SHUTTER_CONTROL_NS_PREFIX, (IOEventPaths::Status_Level), level);
- eventSender->sendEvent(domain.c_str(), address.c_str(), (const uint8_t *) (IOEventPaths::Status_Level),
- &level, IOEventDataType::Float);
+ eventSender->sendEvent((const uint8_t *) (IOEventPaths::Status_Level), &level, IOEventDataType::Float);
}
}
diff --git a/examples/shutter/shutter.cpp b/examples/shutter/shutter.cpp
index 6b064bc..5502de0 100644
--- a/examples/shutter/shutter.cpp
+++ b/examples/shutter/shutter.cpp
@@ -27,27 +27,67 @@
*
*/
-#include
-
#include "configuration.h"
+#include
+
#include "api/ShutterHandler.h"
+#ifdef ESP32_C3
+#include
+#endif
+
using namespace IO;
using namespace Service;
HomeGenie* homeGenie;
+
+#ifdef ESP32_C3
+using namespace Service::API::devices;
+#include
+// Custom status led (builtin NeoPixel RGB on pin 10)
+Adafruit_NeoPixel pixels(1, CONFIG_StatusLedNeoPixelPin, NEO_GRB + NEO_KHZ800);
+void statusLedCallback(bool isLedOn) {
+ if (isLedOn) {
+ pixels.setPixelColor(0, Adafruit_NeoPixel::Color(50, 50, 0));
+ } else {
+ pixels.setPixelColor(0, Adafruit_NeoPixel::Color(0, 0, 0));
+ }
+ pixels.show();
+}
+unsigned long helloWorldDuration = 10000;
+bool helloWorldActive = true;
+#endif
+
+
void setup() {
+#ifdef ESP32_C3
+ // Custom status led (builtin NeoPixel RGB on pin 10)
+// if (!Config::isDeviceConfigured()) {
+ Config::statusLedCallback(&statusLedCallback);
+// }
+ pixels.begin();
+#endif
homeGenie = HomeGenie::getInstance();
//auto miniModule = homeGenie->getDefaultModule();
auto shutterControl = new ShutterControl();
+ auto shutterHandler = new ShutterHandler(shutterControl);
homeGenie->addIOHandler(shutterControl);
- homeGenie->addAPIHandler(new ShutterHandler(shutterControl));
+ homeGenie->addAPIHandler(shutterHandler);
+
+#ifdef ESP32_C3
+ auto colorLight = new ColorLight(IO::IOEventDomains::HomeAutomation_HomeGenie, "C1", "Demo Light");
+ colorLight->onSetColor([](float r, float g, float b) {
+ pixels.setPixelColor(0, r, g, b);
+ pixels.show();
+ });
+ homeGenie->addAPIHandler(colorLight);
+#endif
homeGenie->begin();
@@ -56,5 +96,10 @@ void setup() {
void loop()
{
homeGenie->loop();
-
+#ifdef ESP32_C3
+ if (helloWorldActive && millis() > helloWorldDuration && Config::isDeviceConfigured()) {
+ helloWorldActive = false;
+ Config::statusLedCallback(nullptr);
+ }
+#endif
}
diff --git a/examples/smart-sensor-display/configuration.h b/examples/smart-sensor-display/configuration.h
index cceae9f..eb46e37 100644
--- a/examples/smart-sensor-display/configuration.h
+++ b/examples/smart-sensor-display/configuration.h
@@ -14,3 +14,5 @@
#define CONFIG_LightSensorPin 34
#endif
+
+#define CONFIG_MotionSensorPin 16
diff --git a/examples/smart-sensor-display/smart-sensor-display.cpp b/examples/smart-sensor-display/smart-sensor-display.cpp
index 32988fb..6b202d2 100644
--- a/examples/smart-sensor-display/smart-sensor-display.cpp
+++ b/examples/smart-sensor-display/smart-sensor-display.cpp
@@ -68,7 +68,7 @@ Dashboard* dashboard;
void setup() {
//uint8_t batterySensorPin = 1;
- uint8_t motionSensorPin = 16;
+ uint8_t motionSensorPin = CONFIG_MotionSensorPin;
PowerManager::setWakeUpGPIO((gpio_num_t)motionSensorPin);
diff --git a/platformio.ini b/platformio.ini
index 240bc8d..ea02112 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -159,7 +159,7 @@ lib_deps = ${env.lib_deps}
[env:color-light]
platform = espressif32@6.5.0
-build_flags = -Os -I examples -I src -D CONFIG_StatusLedNeoPixelPin=10
+build_flags = -Os -I examples -I src
build_src_filter = + - +
board_build.flash_size = 4MB
board_build.partitions = min_spiffs.csv
@@ -174,7 +174,7 @@ board = esp32-c3-devkitc-02
#board_build.mcu = esp32c3
; change MCU frequency
#board_build.f_cpu = 160000000L
-build_flags = -Os -I examples -I src -D ESP32_C3 -D CONFIG_StatusLedPin=-1 -D CONFIG_StatusLedNeoPixelPin=10 -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1
+build_flags = -Os -I examples -I src -D ESP32_C3 -D CONFIG_StatusLedPin=-1 -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1
build_src_filter = + - +
board_build.flash_size = 4MB
board_build.partitions = min_spiffs.csv
@@ -192,6 +192,23 @@ lib_deps = ${env.lib_deps}
arduino-libraries/Stepper@^1.1.3
madhephaestus/ESP32Servo@^1.1.1
+[env:shutter-c3]
+platform = espressif32@6.5.0
+board = esp32-c3-devkitc-02
+#board = esp32-c3-devkitm-1
+; change microcontroller
+#board_build.mcu = esp32c3
+; change MCU frequency
+#board_build.f_cpu = 160000000L
+build_flags = -Os -I examples -I src -D ESP32_C3 -D CONFIG_StatusLedPin=-1 -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1
+build_src_filter = + - +
+board_build.flash_size = 4MB
+board_build.partitions = min_spiffs.csv
+lib_deps = ${env.lib_deps}
+ arduino-libraries/Stepper@^1.1.3
+ madhephaestus/ESP32Servo@^1.1.1
+ https://github.com/adafruit/Adafruit_NeoPixel@1.12.0
+
[env:playground]
platform = espressif32@6.5.0
diff --git a/src/io/IOEventPaths.h b/src/io/IOEventPaths.h
index 4ec2263..82c84aa 100644
--- a/src/io/IOEventPaths.h
+++ b/src/io/IOEventPaths.h
@@ -41,6 +41,7 @@ namespace IO {
const char Sensor_Humidity[] PROGMEM = "Sensor.Humidity";
const char Sensor_MotionDetect[] PROGMEM = "Sensor.MotionDetect";
const char Status_Battery[] PROGMEM = "Status.Battery";
+ const char Status_Error[] PROGMEM = "Status.Error";
const char System_BytesFree[] PROGMEM = "System.BytesFree";
}
}
diff --git a/src/service/api/devices/ColorLight.cpp b/src/service/api/devices/ColorLight.cpp
new file mode 100644
index 0000000..f0eaba9
--- /dev/null
+++ b/src/service/api/devices/ColorLight.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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 .
+ *
+ *
+ * Authors:
+ * - Generoso Martello
+ *
+ */
+
+#include "ColorLight.h"
+
+namespace Service { namespace API { namespace devices {
+
+ 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);
+ propStatusColorHsb->setValue("1,1,0,.5");
+ 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;
+ }
+
+}}}
\ No newline at end of file
diff --git a/src/service/api/devices/ColorLight.h b/src/service/api/devices/ColorLight.h
new file mode 100644
index 0000000..e42aa1b
--- /dev/null
+++ b/src/service/api/devices/ColorLight.h
@@ -0,0 +1,117 @@
+/*
+ * 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 .
+ *
+ *
+ * Authors:
+ * - Generoso Martello
+ *
+ */
+
+#ifndef HOMEGENIE_MINI_COLORLIGHT_H
+#define HOMEGENIE_MINI_COLORLIGHT_H
+
+#include
+#include
+
+#include "Dimmer.h"
+
+namespace Service { namespace API { namespace devices {
+
+ 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 callback) {
+ setColorCallback = std::move(callback);
+ }
+ private:
+ LightColor color;
+ std::function setColorCallback = nullptr;
+
+ void setColor(float h, float s, float v, float duration);
+ };
+
+}}}
+
+#endif //HOMEGENIE_MINI_COLORLIGHT_H
diff --git a/src/service/api/devices/Dimmer.cpp b/src/service/api/devices/Dimmer.cpp
new file mode 100644
index 0000000..0a217f7
--- /dev/null
+++ b/src/service/api/devices/Dimmer.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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 .
+ *
+ *
+ * Authors:
+ * - Generoso Martello
+ *
+ */
+
+#include "Dimmer.h"
+
+namespace Service { namespace API { namespace devices {
+
+ 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;
+ }
+
+}}}
\ No newline at end of file
diff --git a/src/service/api/devices/Dimmer.h b/src/service/api/devices/Dimmer.h
new file mode 100644
index 0000000..ec57e59
--- /dev/null
+++ b/src/service/api/devices/Dimmer.h
@@ -0,0 +1,83 @@
+/*
+ * 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 .
+ *
+ *
+ * Authors:
+ * - Generoso Martello
+ *
+ */
+
+#ifndef HOMEGENIE_MINI_DIMMER_H
+#define HOMEGENIE_MINI_DIMMER_H
+
+#include
+
+#include "Switch.h"
+
+namespace Service { namespace API { namespace devices {
+
+ class DimmerLevel {
+ public:
+ unsigned long duration;
+ bool isAnimating = false;
+ void setLevel(float l, unsigned long transitionMs) {
+ duration = transitionMs;
+ ol = level;
+ level = l;
+ startTime = millis();
+ isAnimating = true;
+ }
+ float getProgress() {
+ float p = (float)(millis() - startTime) / (float)duration;
+ if (p >= 1) {
+ isAnimating = false;
+ p = 1;
+ }
+ return p;
+ }
+ float getLevel() {
+ return ol + ((level - ol) * getProgress());
+ }
+ private:
+ float level;
+ float ol;
+ unsigned long startTime;
+
+ };
+
+ class Dimmer: public Switch {
+ public:
+ Dimmer(const char* domain, const char* address, const char* name);
+ void loop() override;
+
+ bool handleRequest(APIRequest*, ResponseCallback*) override;
+
+ void onSetLevel(std::function callback) {
+ setLevelCallback = std::move(callback);
+ }
+ private:
+ DimmerLevel level;
+ std::function setLevelCallback = nullptr;
+ protected:
+ const unsigned long defaultTransitionMs = 500;
+ };
+
+}}}
+
+#endif //HOMEGENIE_MINI_DIMMER_H
diff --git a/src/service/api/devices/Switch.cpp b/src/service/api/devices/Switch.cpp
new file mode 100644
index 0000000..90f813f
--- /dev/null
+++ b/src/service/api/devices/Switch.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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 .
+ *
+ *
+ * Authors:
+ * - Generoso Martello
+ *
+ */
+
+#include "Switch.h"
+
+namespace Service { namespace API { namespace devices {
+
+ Switch::Switch(const char* domain, const char* address, const char* name) {
+ module = new Module();
+ module->domain = domain;
+ module->address = address;
+ module->type = "Switch";
+ module->name = name;
+ // add properties
+ auto propStatusLevel = new ModuleParameter(IOEventPaths::Status_Level);
+ propStatusLevel->value = "0";
+ module->properties.add(propStatusLevel);
+
+ moduleList.add(module);
+ }
+
+ void Switch::init() {
+ }
+
+ bool Switch::handleRequest(APIRequest *command, ResponseCallback* responseCallback) {
+ auto m = getModule(command->Domain.c_str(), command->Address.c_str());
+ if (m != nullptr) {
+
+ auto eventPath = IOEventPaths::Status_Level;
+ SwitchStatus s = SWITCH_STATUS_NOT_SET;
+ if (command->Command == ControlApi::Control_On) {
+ s = SWITCH_STATUS_ON;
+ } else if (command->Command == ControlApi::Control_Off) {
+ s = SWITCH_STATUS_OFF;
+ } else if (command->Command == ControlApi::Control_Toggle) {
+ s = status == SWITCH_STATUS_ON ? SWITCH_STATUS_OFF : SWITCH_STATUS_ON;
+ }
+
+ if (s != SWITCH_STATUS_NOT_SET) {
+ status = s;
+
+ if (setStatusCallback != nullptr) {
+ setStatusCallback(status);
+ }
+
+ // Event Stream Message Enqueue (for MQTT/SSE/WebSocket propagation)
+ float l = status == SWITCH_STATUS_ON ? onLevel : 0;
+ 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);
+
+ responseCallback->writeAll(R"({ "ResponseText": "OK" })");
+
+ return true;
+ }
+
+ }
+ // not handled
+ return false;
+ }
+
+ bool Switch::canHandleDomain(String* domain) {
+ return domain->equals(IO::IOEventDomains::HomeAutomation_HomeGenie);
+ }
+
+ bool Switch::handleEvent(IIOEventSender *sender,
+ const char* domain, const char* address,
+ const unsigned char *eventPath, void *eventData, IOEventDataType dataType) {
+ return false;
+ }
+
+ Module* Switch::getModule(const char* domain, const char* address) {
+ for (int i = 0; i < moduleList.size(); i++) {
+ Module* m = moduleList.get(i);
+ if (m->domain.equals(domain) && m->address.equals(address))
+ return m;
+ }
+ return nullptr;
+ }
+
+ LinkedList* Switch::getModuleList() {
+ return &moduleList;
+ }
+
+}}}
diff --git a/src/service/api/devices/Switch.h b/src/service/api/devices/Switch.h
new file mode 100644
index 0000000..f94b38c
--- /dev/null
+++ b/src/service/api/devices/Switch.h
@@ -0,0 +1,65 @@
+/*
+ * 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 .
+ *
+ *
+ * Authors:
+ * - Generoso Martello
+ *
+ */
+
+#ifndef HOMEGENIE_MINI_SWITCH_H
+#define HOMEGENIE_MINI_SWITCH_H
+
+#include
+
+namespace Service { namespace API { namespace devices {
+
+ enum SwitchStatus {
+ SWITCH_STATUS_NOT_SET = -1,
+ SWITCH_STATUS_OFF,
+ SWITCH_STATUS_ON,
+ };
+
+ class Switch: public Task, public APIHandler {
+ public:
+ Switch(const char* domain, const char* address, const char* name);
+ void init() override;
+ bool canHandleDomain(String* domain) override;
+ bool handleRequest(APIRequest*, ResponseCallback*) override;
+ bool handleEvent(IIOEventSender*,
+ const char* domain, const char* address,
+ const unsigned char *eventPath, void* eventData, IOEventDataType) override;
+
+ Module* getModule(const char* domain, const char* address) override;
+ LinkedList* getModuleList() override;
+ void onSetStatus(std::function callback) {
+ setStatusCallback = std::move(callback);
+ }
+ private:
+ LinkedList moduleList;
+ std::function setStatusCallback = nullptr;
+ protected:
+ Module* module;
+ SwitchStatus status = SWITCH_STATUS_NOT_SET;
+ float onLevel = 1;
+ };
+
+}}}
+
+#endif //HOMEGENIE_MINI_SWITCH_H