Skip to content

Commit

Permalink
v1.2.41
Browse files Browse the repository at this point in the history
- Fixed possible memory leak in JSON-serialization of *Module* and *ModuleParameter* objects
- Added *MQTT Network* program: HG-Mini devices can now be controlled from anywhere via Internet
- Implemented *Modules.ParameterGet* API
- Added JSON array argument to *Modules.ParameterSet*
- Implemented *Programs.Enable* and *Programs.Disable* API
- Added *addModule(..)* public method to *HomeGenieHandler*
  • Loading branch information
genemars committed Dec 8, 2024
1 parent 05ee456 commit 429f1e9
Show file tree
Hide file tree
Showing 21 changed files with 1,001 additions and 78 deletions.
82 changes: 60 additions & 22 deletions src/HomeGenie.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,24 @@ namespace Service {
PowerManager::setWakeUpInterval(0);
PowerManager::init();
#endif

programs.setStatusCallback(this);
programs.load();
#ifndef DISABLE_MQTT_CLIENT
// create "MQTT Network" program if not already there
auto mqttNetwork = programs.getItem(MQTT_NETWORK_CONFIGURATION);
if (mqttNetwork == nullptr) {
mqttNetwork = new Program(MQTT_NETWORK_CONFIGURATION, "MQTT Network", "");
programs.addItem(mqttNetwork);
programs.save();
}
auto programStatus = mqttNetwork->getProperty(IOEventPaths::Program_Status);
if (programStatus == nullptr) {
mqttNetwork->properties.add(new ModuleParameter(IOEventPaths::Program_Status, ""));
}
homeGenieHandler->addModule(mqttNetwork);
#endif

Logger::info("+ Starting HomeGenie service");
}

Expand Down Expand Up @@ -115,6 +133,18 @@ namespace Service {
#endif

#endif

#ifndef DISABLE_MQTT_CLIENT
// Configure MQTT client
auto mqttProgram = programs.getItem(MQTT_NETWORK_CONFIGURATION);
if (mqttProgram != nullptr) {
netManager.getMQTTClient().configure(mqttProgram->properties);
if (mqttProgram->isEnabled) {
netManager.getMQTTClient().enable();
}
}
#endif

// Add System Diagnostics event handler
auto systemDiagnostics = new System::Diagnostics();
systemDiagnostics->setModule(getDefaultModule());
Expand Down Expand Up @@ -260,22 +290,24 @@ namespace Service {
String parameters = "";
for(int p = 0; p < module->properties.size(); p++) {
auto param = module->properties.get(p);
parameters += HomeGenie::createModuleParameter(param->name.c_str(), param->value.c_str(), param->updateTime.c_str());
auto json = HomeGenie::createModuleParameter(param->name.c_str(), param->value.c_str(), param->updateTime.c_str());
parameters += String(json);
free((void*)json);
if (p < module->properties.size() - 1) {
parameters += ",";
}
}
String out = HomeGenie::createModule(module->domain.c_str(), module->address.c_str(),
module->name.c_str(), module->description.c_str(), module->type.c_str(),
parameters.c_str());
return out.c_str();
return HomeGenie::createModule(module->domain.c_str(), module->address.c_str(),
module->name.c_str(), module->description.c_str(), module->type.c_str(),
parameters.c_str());
}

unsigned int HomeGenie::writeModuleJSON(ResponseCallback *responseCallback, String* domain, String* address) {
auto module = getModule(domain, address);
if (module != nullptr) {
String out = getModuleJSON(module);
responseCallback->write(out.c_str());
auto json = getModuleJSON(module);
responseCallback->write(json);
free((void*)json);
}
return responseCallback->contentLength;
}
Expand All @@ -296,7 +328,9 @@ namespace Service {
firstModule = false;
out = "";
}
out += getModuleJSON(module);
auto json = getModuleJSON(module);
out += String(json);
free((void*)json);
responseCallback->write(out.c_str());
}
}
Expand Down Expand Up @@ -434,27 +468,29 @@ namespace Service {
}
#endif

String HomeGenie::createModuleParameter(const char *name, const char *value, const char *timestamp) {
const char* HomeGenie::createModuleParameter(const char *name, const char *value, const char *timestamp) {
static const char *parameterTemplate = R"({
"Name": "%s",
"Value": "%s",
"Description": "%s",
"FieldType": "%s",
"UpdateTime": "%s"
})";
"Name": "%s",
"Value": "%s",
"Description": "%s",
"FieldType": "%s",
"UpdateTime": "%s"
})";
ssize_t size = snprintf(nullptr, 0, parameterTemplate,
name, value, "", "", timestamp
) + 1;
#ifdef BOARD_HAS_PSRAM
char *parameterJson = (char *) ps_malloc(size);
#else
char *parameterJson = (char *) malloc(size);
#endif
snprintf(parameterJson, size, parameterTemplate,
name, value, "", "", timestamp
);
auto p = String(parameterJson);
free(parameterJson);
return p;
return parameterJson;
}

String HomeGenie::createModule(const char *domain, const char *address, const char *name, const char *description,
const char* HomeGenie::createModule(const char *domain, const char *address, const char *name, const char *description,
const char *deviceType, const char *parameters) {
static const char *moduleTemplate = R"({
"Name": "%s",
Expand All @@ -469,15 +505,17 @@ namespace Service {
domain, address,
parameters
) + 1;
#ifdef BOARD_HAS_PSRAM
char *moduleJson = (char *) ps_malloc(size);
#else
char *moduleJson = (char *) malloc(size);
#endif
snprintf(moduleJson, size, moduleTemplate,
name, description, deviceType,
domain, address,
parameters
);
auto m = String(moduleJson);
free(moduleJson);
return m;
return moduleJson;
}

}
46 changes: 42 additions & 4 deletions src/HomeGenie.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "automation/ProgramEngine.h"
#include "automation/Scheduler.h"
#endif
#include "data/ProgramStore.h"

#include "data/Module.h"
#include "io/gpio/GPIOPort.h"
Expand All @@ -58,7 +59,8 @@
#include "service/EventRouter.h"


#define HOMEGENIEMINI_NS_PREFIX "Service::HomeGenie"
#define HOMEGENIEMINI_NS_PREFIX "Service::HomeGenie"
#define MQTT_NETWORK_CONFIGURATION "77"

namespace Service {

Expand All @@ -73,7 +75,7 @@ namespace Service {
using namespace Automation;
#endif

class HomeGenie: IIOEventReceiver, NetRequestHandler
class HomeGenie: IIOEventReceiver, NetRequestHandler, ProgramStatusListener
#ifndef DISABLE_AUTOMATION
, SchedulerListener
#endif
Expand Down Expand Up @@ -104,6 +106,40 @@ namespace Service {
void onSchedule(Schedule* schedule) override;
#endif

// ProgramStatusListener events
void onProgramEnabled(Program* program) override {
#ifndef DISABLE_MQTT_CLIENT
if (program->address == MQTT_NETWORK_CONFIGURATION) {
auto mqttNetwork = programs.getItem(MQTT_NETWORK_CONFIGURATION);
mqttNetwork->setProperty(IOEventPaths::Program_Status, "Running");
QueuedMessage m;
m.domain = IOEventDomains::HomeAutomation_HomeGenie_Automation;
m.sender = MQTT_NETWORK_CONFIGURATION;
m.event = IOEventPaths::Program_Status;
m.value = "Running";
eventRouter.signalEvent(m);
netManager.getMQTTClient().enable();
}
#endif
programs.save();
}
void onProgramDisabled(Program* program) override {
#ifndef DISABLE_MQTT_CLIENT
if (program->address == MQTT_NETWORK_CONFIGURATION) {
auto mqttNetwork = programs.getItem(MQTT_NETWORK_CONFIGURATION);
mqttNetwork->setProperty(IOEventPaths::Program_Status, "Stopped");
QueuedMessage m;
m.domain = IOEventDomains::HomeAutomation_HomeGenie_Automation;
m.sender = MQTT_NETWORK_CONFIGURATION;
m.event = IOEventPaths::Program_Status;
m.value = "Stopped";
eventRouter.signalEvent(m);
netManager.getMQTTClient().disable();
}
#endif
programs.save();
}

/**
*
* @param handler
Expand All @@ -130,6 +166,8 @@ namespace Service {
IOManager& getIOManager();
EventRouter& getEventRouter();

ProgramStore programs;

Module* getDefaultModule();
Module* getModule(String* domain, String* address);

Expand All @@ -140,8 +178,8 @@ namespace Service {
#ifndef DISABLE_DATA_PROCESSING
unsigned int writeParameterHistoryJSON(ModuleParameter* parameter, ResponseCallback *outputCallback, int pageNumber = 0, int pageSize = STATS_HISTORY_RESULTS_DEFAULT_PAGE_SIZE, double rangeStart = 0, double rangeEnd = 0, double maxWidth = 0);
#endif
static String createModule(const char *domain, const char *address, const char *name, const char* description, const char *deviceType, const char *parameters);
static String createModuleParameter(const char *name, const char* value, const char *timestamp);
static const char* createModule(const char *domain, const char *address, const char *name, const char* description, const char *deviceType, const char *parameters);
static const char* createModuleParameter(const char *name, const char* value, const char *timestamp);

private:
static HomeGenie* serviceInstance;
Expand Down
31 changes: 31 additions & 0 deletions src/data/JsonStore.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* 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 "JsonStore.h"

namespace Data {

}
112 changes: 112 additions & 0 deletions src/data/JsonStore.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* 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_JSONSTORE_H
#define HOMEGENIE_MINI_JSONSTORE_H

#include <LinkedList.h>
#include <LittleFS.h>

#include "Config.h"

namespace Data {

template<class T>
class JsonStore {
public:
LinkedList<T*> getItemList(){
return itemList;
}
void load() {
auto fs = LittleFS;
#ifdef ESP8266
if(true==fs.begin( )) {
#else
int maxFiles = 1;
if(true == fs.begin(true, "/littlefs", maxFiles , "spiffs") && fs.exists(fileName)) {
#endif
auto f = LittleFS.open(fileName, FILE_READ);
auto jsonItems = f.readString();
f.close();
JsonDocument doc;
deserializeJson(doc, jsonItems);

JsonArray array = doc.as<JsonArray>();
for(JsonVariant v : array) {

addItem(getItemFromJson(v));

}

} else {

// TODO: report error / disable scheduler

}
}
void save() {
auto fs = LittleFS;
#ifdef ESP8266
if(true==fs.begin( )) {
#else
int maxFiles = 1;
if(true==fs.begin( true, "/littlefs", maxFiles , "spiffs")) {
#endif
auto f = LittleFS.open(fileName, FILE_WRITE);
f.print(getJsonList());
f.close();

} else {

// TODO: report error / disable scheduler

}
}
String getJsonList() {
JsonDocument doc;
for (int i = 0; i < itemList.size(); i++) {
auto schedule = itemList.get(i);
auto jsonItems = doc.add<JsonObject>();
getJson(&jsonItems, schedule);
}
String output;
serializeJson(doc, output);
return output;
}

virtual void addItem(T* item) = 0;
virtual T* getItemFromJson(JsonVariant& json) = 0;
virtual void getJson(JsonObject* jsonItem, T* item) = 0;

protected:
LinkedList<T*> itemList;
const char* fileName;
};

}


#endif //HOMEGENIE_MINI_JSONSTORE_H
Loading

0 comments on commit 429f1e9

Please sign in to comment.