Skip to content

Commit

Permalink
v1.2.3
Browse files Browse the repository at this point in the history
- 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
genemars committed Mar 7, 2024
1 parent 99f90b1 commit 2cb3e89
Show file tree
Hide file tree
Showing 50 changed files with 1,829 additions and 148 deletions.
54 changes: 10 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ based on *ESP32* or *ESP8266* chip.

## Features

- Easy Wi-Fi configuration using Bluetooth (ESP32) or WPA (ESP8266)
- Easy Wi-Fi configuration using Bluetooth (ESP32) or WPS (ESP8266)
- Does not require an Internet connection to be configured or to work properly
- Time synchronization using internal RTC (ESP32), mobile app time or NTP
- Device discovery through SNMP/UPnP advertising with customizable name
Expand All @@ -25,8 +25,8 @@ based on *ESP32* or *ESP8266* chip.

## Building and flashing the firmware

The firmware can be easily installed using [Platform.IO core](https://docs.platformio.org/en/latest/installation.html) CLI.
After installing *Platform.IO core*, download [HomeGenie Mini](https://github.com/genielabs/homegenie-mini/archive/refs/heads/master.zip) source code,
The firmware can be installed using [Platform.IO core](https://docs.platformio.org/en/latest/installation.html) CLI.
After installing *Platform.IO core*, download [HomeGenie Mini](https://github.com/genielabs/homegenie-mini/releases) source code,
unzip it and open a terminal with the current directory set to `homegenie-mini` folder.
Then enter the following commands to install libraries required to build the firmware:

Expand All @@ -47,28 +47,17 @@ pio run -e default -t upload

## Configuration environments

The option `-e default` shown in the above command is used to specify the configuration environment.
The **default** environment is for building the base firmware for a generic *ESP32* board.
The option `-e default` shown in the previous command is used to specify the configuration environment.
The **default** environment is for building the base firmware for a generic *ESP32* board.

The following configurations are available:

- `default`
Generic *ESP32* board.
- `d1-mini`
D1-Mini board with *ESP8266*.
- `d1-mini-esp32`
D1-Mini board with *ESP32*.
- `sonoff`
Configuration for *Sonoff 2-Gang Wi-Fi Smart Switch (DUALR3)*.

For example, to install *HomeGenie Mini* on a **Sonoff DUALR3 Smart Switch** device, the following command is used:
To list all available configurations enter the following command:

```bash
pio run -e sonoff -t upload
pio project config
```

It's possible to add custom configuration environment to build your own version of the firmware to support
different hardware and functionality as explained later in this file.
By editing the `platformio.ini` file is possible to add custom configurations to build your own version
of the firmware to support different hardware and functionality.


## Connecting the device
Expand Down Expand Up @@ -101,7 +90,7 @@ firmware that implements temperature and light sensor and 4 GPIO switches.
![HomeGenie Panel - Dashboard](data/images/phone/hg_panel_dashboard.png)


### Connecting to HomeGenie Automation Server
### Connecting to HomeGenie Server

HG-Mini devices can also be connected to [HomeGenie Server](https://github.com/genielabs/HomeGenie)
configuring the *MQTT client* as shown in the following picture.
Expand Down Expand Up @@ -248,29 +237,6 @@ pio run -e playground-c3 -t upload







<!-- TODO: ..
## Creating a smart device with HomeGenie Mini library
```
pio project init -b esp32dev --project-option="lib_deps=HomeGenieMini"
```
// TODO: brief API/SDK docs
-->








## HomeGenie API

HomeGenie Mini API is a subset of HomeGenie Server API that makes HomeGenie Mini a real
Expand Down
Empty file added examples/color-light/README.md
Empty file.
101 changes: 101 additions & 0 deletions examples/color-light/api/ColorLight.cpp
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;
}

116 changes: 116 additions & 0 deletions examples/color-light/api/ColorLight.h
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
78 changes: 78 additions & 0 deletions examples/color-light/api/Dimmer.cpp
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;
}
Loading

0 comments on commit 2cb3e89

Please sign in to comment.