Skip to content

Commit

Permalink
v1.2.39
Browse files Browse the repository at this point in the history
- Fixed device name not saved
- Added preset `Motion.Blink` schedule
- Added support for HTTPS in *NetHelper API*
- Fixed minor memory leak in *Schedule.h*
- Added properties `alarmed` and `motionDetected` to *ModulesHelper API*
- Fixed Wi-Fi reconnection issues
  • Loading branch information
genemars committed Nov 22, 2024
1 parent 2834a91 commit 87d4b81
Show file tree
Hide file tree
Showing 12 changed files with 91 additions and 30 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ https://homegenie.it/mini
- Easy device configuration using Wi-Fi protected setup button (WPS) or Bluetooth
- Does not require an Internet connection to be configured or to work properly
- Time synchronization using internal RTC (ESP32), mobile app time or NTP
- Integrated actions scheduler supporting *extended cron expressions* and JavaScript
- Integrated actions scheduler supporting *extended Cron expressions* and JavaScript
- Device discovery via SSDP/UPnP with customizable device name
- Multi-channel I/O: HTTP, WebSocket, SSE, MQTT, Serial
- Built-in status LED logic
Expand Down
31 changes: 27 additions & 4 deletions examples/smart-sensor/CommonSensors.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#ifndef HOMEGENIE_MINI_COMMONSENSORS_H
#define HOMEGENIE_MINI_COMMONSENSORS_H

#define COLOR_LIGHT_ADDRESS "C1"

#include "HomeGenie.h"

#include "io/sensors/DS18B20.h"
Expand All @@ -18,13 +20,13 @@ using namespace IO::Sensors;
#ifndef DISABLE_AUTOMATION
void setupMotionSensorSchedules(Module* sensorModule) {

// Create default Dawn scene
// Create "Motion.Activated" schedule
if (Scheduler::get("Motion.Activated") == nullptr) {
auto s = new Schedule("Motion.Activated", "Turn on light if motion is detected and luminosity is low.", "", "", "$$.boundModules.on();");
s->onModuleEvent = true;
s->eventModules.add(sensorModule->getReference());
// UI state data
s->data = R"({"action":{"template":{"forEach":{"config":{},"enabled":true,"id":"command_turn_on"},"forEnd":{"config":{},"enabled":false,"id":null},"forStart":{"config":{},"enabled":false,"id":null}},"type":"template"},"event":[{"condition":">","module":"34b7da5-2220-6e69-654d-696e69dab734/HomeAutomation.HomeGenie/mini","property":"Sensor.MotionDetect","value":"0"},{"condition":"<","module":"34b7da5-2220-6e69-654d-696e69dab734/HomeAutomation.HomeGenie/mini","property":"Sensor.Luminance","value":"70"}],"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":[],"to":""})";
s->data = R"({"action":{"template":{"forEach":{"config":{},"enabled":true,"id":"command_turn_on"},"forEnd":{"config":{},"enabled":false,"id":null},"forStart":{"config":{},"enabled":false,"id":null}},"type":"template"},"event":[{"condition":">","module":"HomeAutomation.HomeGenie/mini","property":"Sensor.MotionDetect","value":"0"},{"condition":"<","module":"HomeAutomation.HomeGenie/mini","property":"Sensor.Luminance","value":"70"}],"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":[],"to":""})";
// Device types allowed
s->boundDevices.add(new String("Switch"));
s->boundDevices.add(new String("Light"));
Expand All @@ -33,18 +35,39 @@ void setupMotionSensorSchedules(Module* sensorModule) {
Scheduler::addSchedule(s);
}

// Create default Dusk scene
// Create "Motion.Timeout" schedule
if (Scheduler::get("Motion.Timeout") == nullptr) {
auto s = new Schedule("Motion.Timeout", "Turn off light if no motion is detected for 5 minutes.", "", "", "$$.boundModules.off();");
s->onModuleEvent = true;
s->eventModules.add(sensorModule->getReference());
// 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":[{"condition":">=","module":"34b7da5-2220-6e69-654d-696e69dab734/HomeAutomation.HomeGenie/mini","property":"Status.IdleTime","value":"5"},{"condition":">","module":"34b7da5-2220-6e69-654d-696e69dab734/HomeAutomation.HomeGenie/mini","property":"Sensor.Luminance","value":"50"}],"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":[],"to":""})";
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":[{"condition":">=","module":"HomeAutomation.HomeGenie/mini","property":"Status.IdleTime","value":"5"},{"condition":">","module":"HomeAutomation.HomeGenie/mini","property":"Sensor.Luminance","value":"50"}],"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":[],"to":""})";
// Device types allowed
s->boundDevices.add(new String("Switch"));
s->boundDevices.add(new String("Light"));
s->boundDevices.add(new String("Dimmer"));
s->boundDevices.add(new String("Color"));
Scheduler::addSchedule(s);
}

// Create "Motion.Blink" schedule
if (Scheduler::get("Motion.Blink") == nullptr) {
auto s = new Schedule("Motion.Blink", "Blink light when motion is detected.", "", "", "$$.boundModules.toggle();");
s->onModuleEvent = true;
s->eventModules.add(sensorModule->getReference());
// UI state data
s->data = R"({"action":{"template":{"forEach":{"config":{},"enabled":true,"id":"command_toggle"},"forEnd":{"config":{},"enabled":false,"id":null},"forStart":{"config":{},"enabled":false,"id":null}},"type":"template"},"event":[{"condition":">=","module":"HomeAutomation.HomeGenie/mini","property":"Sensor.MotionDetect","value":"0"}],"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":[],"to":""})";
// Device types allowed
s->boundDevices.add(new String("Switch"));
s->boundDevices.add(new String("Light"));
s->boundDevices.add(new String("Dimmer"));
s->boundDevices.add(new String("Color"));
// Custom status led (builtin NeoPixel RGB LED)
auto pin = Config::getSetting("stld-pin");
int statusLedPin = pin.isEmpty() ? -1 : pin.toInt();
if (statusLedPin >= 0) {
s->boundModules.add(new ModuleReference(IO::IOEventDomains::HomeAutomation_HomeGenie, COLOR_LIGHT_ADDRESS));
}
Scheduler::addSchedule(s);
}

Expand Down
7 changes: 3 additions & 4 deletions examples/smart-sensor/smart-sensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
*
*/


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

Expand All @@ -50,8 +49,6 @@ void setup() {
miniModule->setProperty("Widget.Implements.Scheduling", "1");
miniModule->setProperty("Widget.Implements.Scheduling.ModuleEvents", "1");

includeCommonSensors(homeGenie, miniModule);

// Get status LED config
auto pin = Config::getSetting("stld-pin");
int statusLedPin = pin.isEmpty() ? -1 : pin.toInt();
Expand All @@ -67,7 +64,7 @@ void setup() {
if (statusLED != nullptr) {

// Setup main LEDs control module
auto colorLight = new ColorLight(IO::IOEventDomains::HomeAutomation_HomeGenie, "C1", "Color Light");
auto colorLight = new ColorLight(IO::IOEventDomains::HomeAutomation_HomeGenie, COLOR_LIGHT_ADDRESS, "Status LED");
colorLight->module->setProperty("Widget.Implements.Scheduling", "1");
colorLight->module->setProperty("Widget.Implements.Scheduling.ModuleEvents", "1");
colorLight->module->setProperty("Widget.Preference.AudioLight", "true");
Expand All @@ -79,6 +76,8 @@ void setup() {

}

includeCommonSensors(homeGenie, miniModule);

homeGenie->begin();
}

Expand Down
3 changes: 3 additions & 0 deletions src/automation/Schedule.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ namespace Automation {
for (auto &&bm: boundModules) {
delete bm;
}
for (auto &&em: eventModules) {
delete em;
}
}

bool occursUpdate(time_t ts) {
Expand Down
6 changes: 6 additions & 0 deletions src/automation/ScheduledScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ namespace Automation {
" get isOff() {\n"
" return parseFloat(__boundModules_property_avg('Status.Level')) === 0;\n"
" },\n"
" get alarmed() {"
" return parseInt(__boundModules_property_avg('Sensor.Alarm')) > 0;"
" },"
" get motionDetected() {"
" return parseInt(__boundModules_property_avg('Sensor.MotionDetect')) > 0;"
" },"
" get temperature() {\n"
" return parseFloat(__boundModules_property_avg('Sensor.Temperature'));\n"
" },\n"
Expand Down
31 changes: 21 additions & 10 deletions src/automation/helpers/NetHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,32 @@ namespace Automation { namespace Helpers {
HTTPClient NetHelper::http;

String NetHelper::httpGet(String &url) {
WiFiClient* client;
if (url.startsWith("https://")) {
client = new WiFiClientSecure();
((WiFiClientSecure*)client)->setInsecure();
} else {
client = new WiFiClient();
}
String response;
http.begin(url.c_str());
//http.addHeader("Content-Type", "application/x-www-form-urlencoded");
int httpCode = http.GET();
if (httpCode > 0) {
// Server response
if (httpCode == HTTP_CODE_OK) {
response = http.getString();
if (http.begin(*client, url)) {
//http.addHeader("Content-Type", "application/x-www-form-urlencoded");
int httpCode = http.GET();
if (httpCode > 0) {
// Server response
if (httpCode == HTTP_CODE_OK) {
response = http.getString();
} else {
// Server reported error code
//Serial.printf("[HTTP] GET... code: %d\n", httpCode);
}
} else {
// Server reported error code
//Serial.printf("[HTTP] GET... code: %d\n", httpCode);
Serial.printf("[HTTP] GET... failed, error: %s\n", HTTPClient::errorToString(httpCode).c_str());
}
} else {
//Serial.printf("[HTTP] GET... failed, error: %s\n", HTTPClient::errorToString(httpCode).c_str());
// TODO: ...
}
delete client;
return response;
}

Expand Down
5 changes: 4 additions & 1 deletion src/automation/helpers/NetHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#ifndef HOMEGENIE_MINI_NETHELPER_H
#define HOMEGENIE_MINI_NETHELPER_H

#include <WiFiClientSecure.h>

#ifdef ESP8266
#include <ESP8266HTTPClient.h>
#else
Expand All @@ -41,12 +43,13 @@

namespace Automation { namespace Helpers {

class NetHelper{
class NetHelper {
public:
static String httpGet(String& url);
static bool ping(String& host);

private:
static WiFiClientSecure* wifiClientSecure;
static HTTPClient http;
};

Expand Down
4 changes: 2 additions & 2 deletions src/net/HTTPServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ namespace Net {
if (!ipAddress.equals(localIP) && !localIP.equals("0.0.0.0") && !localIP.isEmpty()) {
// New IP address
ipAddress = localIP;
//Logger::info("| ✔ New IP address %s", ipAddress.c_str());
Logger::info("| ✔ New IP address: %s", ipAddress.c_str());

// SSDP UDN uuid
Config::system.id = SSDPDevice.getId();
//Logger::info("| ✔ SSDP UDN uuid: ", Config::system.id.c_str());
Logger::info("| ✔ SSDP UDN uuid: %s", Config::system.id.c_str());

String ssdpUri = Config::system.id + String(".xml");
SSDPDevice.setSchemaURL(FPSTR(ssdpUri.c_str()));
Expand Down
15 changes: 14 additions & 1 deletion src/net/WiFiManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

namespace Net {
wl_status_t WiFiManager::wiFiStatus = WL_NO_SSID_AVAIL;
unsigned long WiFiManager::wiFiIdleTimestamp = 0;
#ifdef ESP32
esp_wps_config_t WiFiManager::wps_config;
bool WiFiManager::esp32_wps_started = false;
Expand Down Expand Up @@ -79,7 +80,19 @@ namespace Net {
}

checkWiFiStatus();
wiFiStatus = status;
wiFiStatus = ESP_WIFI_STATUS;
if (wiFiStatus == WL_DISCONNECTED) {
wiFiStatus = WL_NO_SSID_AVAIL;
}
} else if (wiFiStatus == WL_IDLE_STATUS) {
if (wiFiIdleTimestamp == 0) {
wiFiIdleTimestamp = millis();
} else if (millis() - wiFiIdleTimestamp > 15000) {
// force reconnect
wiFiStatus = WL_NO_SSID_AVAIL;
wiFiIdleTimestamp = 0;
connect();
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/net/WiFiManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ namespace Net {
#endif
private:
static wl_status_t wiFiStatus;
static unsigned long lastStatusCheckTs;
static unsigned long wiFiIdleTimestamp;
#ifdef ESP32
static unsigned long esp32_wps_waiting_connect_ts;
#endif
Expand Down
9 changes: 4 additions & 5 deletions src/service/EventRouter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,10 @@ namespace Service {
}

// Only supports events coming from this very system
// TODO: this might not work if MAC address changes
// if (!serverId.isEmpty() && !serverId.equals(Config::system.id)) {
// matchesConditionsProperty = false;
// break;
// }
if (!serverId.isEmpty() && !serverId.equals(Config::system.id)) {
matchesConditionsProperty = false;
break;
}

auto property = c["property"].as<String>();
if (strcmp(domain, moduleDomain.c_str()) == 0 &&
Expand Down
6 changes: 5 additions & 1 deletion src/service/api/HomeGenieHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -377,8 +377,12 @@ namespace Service { namespace API {
return true;
} else {
Config::system.friendlyName = deviceName;
Config::saveSetting(CONFIG_KEY_device_name, deviceName);
miniModule->name = deviceName;
// persist setting
Preferences preferences;
preferences.begin(CONFIG_SYSTEM_NAME, false);
preferences.putString(CONFIG_KEY_device_name, deviceName);
preferences.end();
}
}
if (doc.containsKey("group")) {
Expand Down

0 comments on commit 87d4b81

Please sign in to comment.