Skip to content

Commit

Permalink
v1.2.49
Browse files Browse the repository at this point in the history
- Added new method `Module::addWidgetOption(..)` to easily add custom options to widgets UI. The method also supports registering listeners for option update events, and also supports automatic loading/saving configured values.
- *smart-sensor*: Added new UI option `DHT.adjust`, to allow adjusting the read temperature by adding or subtracting a given value.
- *shutter*: completed `shutter` device firmware, now with configurable motor type, gpio encoder, and other parameters
- Optimized TimeClient
- Upgraded to latest *[email protected]*
  • Loading branch information
genemars committed Feb 5, 2025
1 parent f12389c commit dee4d20
Show file tree
Hide file tree
Showing 53 changed files with 1,701 additions and 718 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/platformio.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ jobs:
with:
python-version: '3.9'

- name: Get Head Commit Message
id: get_head_commit_message
run: echo "HEAD_COMMIT_MESSAGE=$(git log -1 --no-merges --pretty=%B)" >> "$GITHUB_ENV"

- name: Install PlatformIO Core
run: pip install --upgrade platformio

Expand All @@ -32,5 +36,5 @@ jobs:
with:
files: |
./artifacts/*.zip
body: ${{ github.event.workflow_run.head_commit.message }}
body: ${{ env.HEAD_COMMIT_MESSAGE }}
generate_release_notes: true
9 changes: 5 additions & 4 deletions examples/color-light/StatusLed.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
#ifndef HOMEGENIE_MINI_STATUSLED_H
#define HOMEGENIE_MINI_STATUSLED_H


#include <service/api/CommonApi.h>
#include <service/api/devices/ColorLight.h>

#include <Adafruit_NeoPixel.h>
Expand All @@ -35,6 +35,7 @@


using namespace Service::API::devices;
using namespace Service::API::WidgetApi;

class StatusLed: Task {
private:
Expand Down Expand Up @@ -86,9 +87,9 @@ class StatusLed: Task {
#endif
// Setup master LED control module
colorLight = new ColorLight(IO::IOEventDomains::HomeAutomation_HomeGenie, COLOR_LIGHT_ADDRESS, "Color Light");
colorLight->module->setProperty("Widget.Implements.Scheduling", "1");
colorLight->module->setProperty("Widget.Implements.Scheduling.ModuleEvents", "1");
colorLight->module->setProperty("Widget.Preference.AudioLight", "true");
colorLight->module->setProperty(Implements::Scheduling, "true");
colorLight->module->setProperty(Implements::Scheduling_ModuleEvents, "true");
colorLight->module->setProperty(Preference::AudioLight, "true");

colorLight->onSetColor([this](LightColor c) {
if (statusLED != nullptr) {
Expand Down
102 changes: 23 additions & 79 deletions examples/color-light/color-light.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,19 @@ void setup() {
auto miniModule = homeGenie->getDefaultModule();
miniModule->name = "LED Controller";

// Add to the default module some configuration properties.
// Properties starting with `Widget.OptionField.` are special
// properties that are used in the HomeGenie Panel app to
// display custom buttons, sliders, selectors and other UI controls
// to configure parameters of this device implementation

// Number select control to set the number of LEDs of the strip
miniModule->setProperty("Widget.OptionField.LED.count",
"number:LED.count:1:1920:1:led_count");
// Number select control to set maximum percentage of power that can be drawn
miniModule->setProperty("Widget.OptionField.LED.power",
"number:LED.power:5:100:25:led_power");

// The following are the actual properties where
// UI controls implemented by `Widget.Options`
// read/write values from/to.
mpLedCount = new ModuleParameter("LED.count", "0");
miniModule->properties.add(mpLedCount);
mpMaxPower = new ModuleParameter("LED.power", "25");
miniModule->properties.add(mpMaxPower);

// Read stored config
ledsPin = (int16_t)Config::getSetting(Configuration::LedsPin).toInt();
pixelsType = (int16_t)Config::getSetting(Configuration::LedsType).toInt();
pixelsSpeed = (int16_t)Config::getSetting(Configuration::LedsSpeed).toInt();
// the following settings can also be configured from the app
ledsCount = abs(Config::getSetting(Configuration::LedsCount).toInt());
maxPower = Config::getSetting(Configuration::LedsPower).toInt();
if (maxPower <= 0) maxPower = DEFAULT_MAX_POWER;

// Add UI options to configure this device using the app
setupControllerOptions(miniModule);

// Setup builtin RGB status led
statusLed.setup();
colorLight = statusLed.getColorLight();

Expand All @@ -73,42 +65,22 @@ void setup() {

} else {

// Get LED strip config
ledsCount = abs(Config::getSetting("leds-cnt").toInt());
ledsPin = (int16_t)Config::getSetting("leds-pin").toInt();
pixelsType = (int16_t)Config::getSetting("leds-typ").toInt();
pixelsSpeed = (int16_t)Config::getSetting("leds-spd").toInt();
maxPower = Config::getSetting("leds-pwr").toInt();
if (maxPower <= 0) maxPower = DEFAULT_MAX_POWER;
createPixels();
// default values
mpLedCount->value = String(ledsCount);
mpMaxPower->value = String(maxPower);

// Setup main LEDs control module
// colorLight is the main control module
colorLight->onSetColor([](LightColor color) {
statusLed.setCurrentColor(color);
fx_reset(pixels, color);
});
homeGenie->addAPIHandler(colorLight);

auto module = colorLight->module;
module->name = "Smart Light";

// Add to the ColorLight module some configuration properties:
// - dropdown list control to select the light animation effect
module->setProperty("Widget.OptionField.FX.Rainbow",
"select:FX.Style:light_style:solid|rainbow|rainbow_2|white_stripes|white_stripes_2|kaleidoscope");
// - dropdown list control to enable the strobe effect
module->setProperty("Widget.OptionField.FX.Strobe",
"select:FX.Strobe:strobe_effect:off|slow|medium|fast");

mpFxStyle = new ModuleParameter("FX.Style", lightStyleNames[0]);
module->properties.add(mpFxStyle);
mpFxStrobe = new ModuleParameter("FX.Strobe", "off");
module->properties.add(mpFxStrobe);
// Add ColorLight UI options for setting
// animation style and strobe effects.
setupColorLightOptions(module);

// Setup control buttons
// Setup physical control buttons
setupControlButtons(module);

#ifndef DISABLE_AUTOMATION
Expand Down Expand Up @@ -140,34 +112,24 @@ void loop()

if (millis() - lastRefreshTs > refreshMs)
{
// Update current rendering style if changed
uint8_t selectedStyleIndex = getLightStyleIndex(mpFxStyle->value);
if (currentStyleIndex != selectedStyleIndex) {
currentStyleIndex = selectedStyleIndex;
}

// enable / disable strobe light
if (strobeFxTickMs > 0 && mpFxStrobe->value == "off") {
strobeFxTickMs = 0;
} else if (strobeFxTickMs == 0 && mpFxStrobe->value != "off") {
strobeFxTickMs = millis();
}

if (!strobeOff && strobeFxTickMs > 0 && millis() - strobeFxTickMs > ((strobeFxDurationMs + strobeFxIntervalMs) * (mpFxStrobe->value == "slow" ? 3 : mpFxStrobe->value == "medium" ? 2 : 1))) {

// strobe off
// Strobe tick OFF: hide strobe light
strobeOff = true;

} else if (strobeFxTickMs > 0 && (millis() - strobeFxTickMs <= strobeFxDurationMs || strobeOff)) {

// show strobe light for `strobeFxDurationMs` milliseconds (25)
// Strobe tick ON: show strobe light for `strobeFxDurationMs` milliseconds (25)
auto c = LightColor();
c.setColor(0, 0, 1, 0);
fx_solid(pixels, c, 0);
strobeOff = false;

} else {

// Apply selected effect (FX.Style)

auto currentColor = statusLed.getCurrentColor();
// apply selected light style
switch (currentStyleIndex) {
Expand Down Expand Up @@ -199,7 +161,7 @@ void loop()
refresh();

lastRefreshTs = millis();
if (strobeOff) {
if (strobeFxTickMs > 0 && strobeOff) {
strobeFxTickMs = lastRefreshTs;
}
}
Expand All @@ -213,23 +175,5 @@ void loop()
colorLight->bright(0);
}

// check if number of pixels was changed
if (ledsCount != mpLedCount->value.toInt()) {

disposePixels();

ledsCount = mpLedCount->value.toInt();
Config::saveSetting("leds-cnt", mpLedCount->value);

// re-create pixels with the new length
createPixels();

}
// check if max power value was changed
if (maxPower != mpMaxPower->value.toInt() && mpMaxPower->value.toInt() > 0) {
maxPower = mpMaxPower->value.toInt();
Config::saveSetting("leds-pwr", mpMaxPower->value);
}

}
}
143 changes: 132 additions & 11 deletions examples/color-light/color-light.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* HomeGenie-Mini (c) 2018-2024 G-Labs
* HomeGenie-Mini (c) 2018-2025 G-Labs
*
*
* This file is part of HomeGenie-Mini (HGM).
Expand Down Expand Up @@ -34,8 +34,33 @@
#include "StatusLed.h"
StatusLed statusLed;

namespace ColorLightApi {
namespace Options {
namespace Controller {
const char LED_count[] = "LED.count";
const char LED_power[] = "LED.power";
}
namespace Light {
const char FX_Style[] = "FX.Style";
const char FX_Strobe[] = "FX.Strobe";
}
}

namespace Configuration {
static const char LedsPin[] = "leds-pin";
static const char LedsType[] = "leds-typ";
static const char LedsSpeed[] = "leds-spd";
static const char LedsCount[] = "leds-cnt";
static const char LedsPower[] = "leds-pwr";
}

}

using namespace Service;
using namespace Service::API::devices;
using namespace Service::ModuleApi;

using namespace ColorLightApi;

HomeGenie* homeGenie;
bool isConfigured = false;
Expand Down Expand Up @@ -89,9 +114,6 @@ uint8_t getLightStyleIndex(String& styleName) {
return 0;
}

ModuleParameter* mpLedCount;
ModuleParameter* mpMaxPower;
ModuleParameter* mpFxStyle;
ModuleParameter* mpFxStrobe;

// Buttons state variables
Expand Down Expand Up @@ -246,8 +268,8 @@ void setupDefaultSchedules() {
// UI state data
s->data = R"({"action":{"template":{"forEach":{"config":{"color":"#FF9100|30.0"},"enabled":true,"id":"command_set_color"},"forEnd":{"config":{},"enabled":false,"id":null},"forStart":{"config":{},"enabled":false,"id":null}},"type":"template"},"event":[],"from":"","itemType":3,"occur_dayom_sel":[],"occur_dayom_type":1,"occur_dayow_sel":[],"occur_hour_sel":[],"occur_hour_step":12,"occur_hour_type":1,"occur_min_sel":[],"occur_min_step":30,"occur_min_type":1,"occur_month_sel":[],"occur_month_type":1,"time":[],"to":""})";
// Device types allowed
s->boundDevices.add(new String("Dimmer"));
s->boundDevices.add(new String("Color"));
s->boundDevices.add(new String(ModuleType::Dimmer));
s->boundDevices.add(new String(ModuleType::Color));
Scheduler::addSchedule(s);
}

Expand All @@ -259,8 +281,8 @@ void setupDefaultSchedules() {
// UI state data
s->data = R"({"action":{"template":{"forEach":{"config":{"color":"#0099FF|30.0"},"enabled":true,"id":"command_set_color"},"forEnd":{"config":{},"enabled":false,"id":null},"forStart":{"config":{},"enabled":false,"id":null}},"type":"template"},"event":[],"from":"","itemType":3,"occur_dayom_sel":[],"occur_dayom_type":1,"occur_dayow_sel":[],"occur_hour_sel":[],"occur_hour_step":12,"occur_hour_type":1,"occur_min_sel":[],"occur_min_step":30,"occur_min_type":1,"occur_month_sel":[],"occur_month_type":1,"time":[],"to":""})";
// Device types allowed
s->boundDevices.add(new String("Dimmer"));
s->boundDevices.add(new String("Color"));
s->boundDevices.add(new String(ModuleType::Dimmer));
s->boundDevices.add(new String(ModuleType::Color));
Scheduler::addSchedule(s);
}

Expand All @@ -272,9 +294,9 @@ void setupDefaultSchedules() {
// UI state data
s->data = R"({"action":{"template":{"forEach":{"config":{},"enabled":true,"id":"command_turn_off"},"forEnd":{"config":{},"enabled":false,"id":null},"forStart":{"config":{},"enabled":false,"id":null}},"type":"template"},"event":[],"from":"","itemType":1,"occur_dayom_sel":[],"occur_dayom_type":1,"occur_dayow_sel":[],"occur_hour_sel":[],"occur_hour_step":12,"occur_hour_type":1,"occur_min_sel":[],"occur_min_step":30,"occur_min_type":1,"occur_month_sel":[],"occur_month_type":1,"time":[{"end":"02:00","start":"02:00"}],"to":""})";
// Device types allowed
s->boundDevices.add(new String("Light"));
s->boundDevices.add(new String("Dimmer"));
s->boundDevices.add(new String("Color"));
s->boundDevices.add(new String(ModuleType::Light));
s->boundDevices.add(new String(ModuleType::Dimmer));
s->boundDevices.add(new String(ModuleType::Color));
Scheduler::addSchedule(s);
}

Expand All @@ -286,3 +308,102 @@ void setupDefaultSchedules() {

}
#endif // DISABLE_AUTOMATION


// UI options update listener
static class : public ModuleParameter::UpdateListener {
public:
void onUpdate(ModuleParameter* option) override {

if (option->is(Options::Light::FX_Style)) {

// Update current rendering style
auto style = getLightStyleIndex(option->value);
if (style != currentStyleIndex) currentStyleIndex = style;

} else if (option->is(Options::Light::FX_Strobe)) {

// enable / disable strobe light
if (strobeFxTickMs > 0 && option->value == "off") {
strobeFxTickMs = 0;
} else if (strobeFxTickMs == 0 && option->value != "off") {
strobeFxTickMs = millis();
}

} else if (option->is(Options::Controller::LED_count)) {

// number of pixels changed
disposePixels();
// update current leds count
ledsCount = option->value.toInt();
// re-create pixels with the new length
createPixels();

} else if (option->is(Options::Controller::LED_power)) {

// max power value changed, update it
maxPower = option->value.toInt();

}

}

} optionUpdateListener;

void setupControllerOptions(Module* miniModule) {
// Number select control to set the number of LEDs of the strip
miniModule->addWidgetOption(
// name, value
Options::Controller::LED_count, String(ledsCount).c_str(),
// type
UI_WIDGETS_FIELD_TYPE_NUMBER
// label
":led_count"
// min:max:default
":1:1920:1"
)->withConfigKey(Configuration::LedsCount)->addUpdateListener(&optionUpdateListener);
// Number select control to set maximum percentage of power that can be drawn
miniModule->addWidgetOption(
// name, value
Options::Controller::LED_power, String(maxPower).c_str(),
// type
UI_WIDGETS_FIELD_TYPE_NUMBER
// label
":led_power"
// min:max:default
":5:100:25"
)->withConfigKey(Configuration::LedsPower)->addUpdateListener(&optionUpdateListener);
}
void setupColorLightOptions(Module* module) {
// - dropdown list control to select the light animation effect
module->addWidgetOption(
// name, value
Options::Light::FX_Style, lightStyleNames[0],
// type
UI_WIDGETS_FIELD_TYPE_SELECT
// label
":light_style"
// options
":solid" // TODO¨: create from `lightStyleNames` array
"|rainbow"
"|rainbow_2"
"|white_stripes"
"|white_stripes_2"
"|kaleidoscope"
)->addUpdateListener(&optionUpdateListener);
// - dropdown list control to enable the strobe effect
mpFxStrobe = module->addWidgetOption(
// name, value
Options::Light::FX_Strobe, "off",
// type
UI_WIDGETS_FIELD_TYPE_SELECT
// label
":strobe_effect"
// options
":off"
"|slow"
"|medium"
"|fast"
);
mpFxStrobe->addUpdateListener(&optionUpdateListener);
}
Loading

0 comments on commit dee4d20

Please sign in to comment.