Merge branch 'refactor' into huzzah
This commit is contained in:
commit
4b4f543117
@ -8,6 +8,7 @@
|
||||
|
||||
namespace Ha {
|
||||
uint16_t (*publisher)(const char* topic, const char* message);
|
||||
typedef void (*onMessage)(const char* msg);
|
||||
|
||||
struct DeviceConfig {
|
||||
const char* id = nullptr;
|
||||
@ -17,9 +18,11 @@ namespace Ha {
|
||||
const char* area = nullptr;
|
||||
DeviceConfig* parent = nullptr;
|
||||
|
||||
DeviceConfig() {}
|
||||
DeviceConfig(const char* id) : id(id) {}
|
||||
DeviceConfig(DeviceConfig& d) : id(d.id), name(d.name), model(d.model), manufacturer(d.manufacturer), area(d.area), parent(d.parent) {}
|
||||
|
||||
static DeviceConfig* create(const char* id) {
|
||||
return new DeviceConfig{ id };
|
||||
}
|
||||
|
||||
void buildConfig(JsonDocument& jsonDoc) {
|
||||
JsonObject device = jsonDoc.createNestedObject("device");
|
||||
@ -72,18 +75,15 @@ namespace Ha {
|
||||
components.add(this);
|
||||
}
|
||||
|
||||
virtual void buildUniqueId(char* uniqueId) = 0;
|
||||
|
||||
virtual void buildUniqueId(char* uniqueId) {
|
||||
sprintf(uniqueId, "%s_%s", MAIN_DEVICE_ID, id);
|
||||
}
|
||||
|
||||
virtual void buildConfigTopic() {
|
||||
if (String{"diagnostic"}.equals(entityCategory) && deviceClass) {
|
||||
sprintf(configTopic, "homeassistant/%s/%s/%s_%s/config", type, MAIN_DEVICE_ID, deviceClass, id);
|
||||
} else {
|
||||
sprintf(configTopic, "homeassistant/%s/%s/%s/config", type, MAIN_DEVICE_ID, id);
|
||||
}
|
||||
sprintf(configTopic, "homeassistant/%s/%s/%s/config", type, MAIN_DEVICE_ID, id);
|
||||
}
|
||||
|
||||
virtual void buildConfig(JsonDocument& jsonDoc) {
|
||||
buildConfigTopic();
|
||||
if (mainDevice) mainDevice->buildConfig(jsonDoc);
|
||||
if (entityCategory) jsonDoc["entity_category"] = entityCategory;
|
||||
if (deviceClass) jsonDoc["device_class"] = deviceClass;
|
||||
@ -91,52 +91,118 @@ namespace Ha {
|
||||
char uniqueId[50];
|
||||
buildUniqueId(uniqueId);
|
||||
jsonDoc["unique_id"] = uniqueId;
|
||||
buildConfigTopic();
|
||||
}
|
||||
|
||||
void publishConfig() {
|
||||
StaticJsonDocument<JSON_SIZE> jsonDoc;
|
||||
buildConfig(jsonDoc);
|
||||
|
||||
char message[JSON_SIZE];
|
||||
serializeJson(jsonDoc, message);
|
||||
|
||||
publisher(configTopic, message);
|
||||
}
|
||||
|
||||
void publishCleanupConfig() {
|
||||
publisher(configTopic, "");
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct EntityConfig : Component {
|
||||
|
||||
EntityConfig(const char* name, const char* id, const char* type) : Component(name, id, type) {
|
||||
}
|
||||
|
||||
T* asDevice(DeviceConfig* deviceConfig) {
|
||||
mainDevice = deviceConfig;
|
||||
return static_cast<T*>(this);
|
||||
}
|
||||
|
||||
T* ofDevice(const char* id) {
|
||||
mainDevice = new DeviceConfig{id};
|
||||
return static_cast<T*>(this);
|
||||
}
|
||||
};
|
||||
|
||||
List<Component> Component::components;
|
||||
|
||||
template <class T>
|
||||
struct Command : EntityConfig<T> {
|
||||
char commandTopic[TOPIC_SIZE];
|
||||
struct AbstractBuilder {
|
||||
static List<AbstractBuilder> builders;
|
||||
|
||||
Command(const char* name, const char* id, const char* type) : EntityConfig<T>(name, id, type) {
|
||||
sprintf(commandTopic, "homeassistant/%s/%s/%s", type, MAIN_DEVICE_ID, id);
|
||||
AbstractBuilder() {
|
||||
builders.add(this);
|
||||
}
|
||||
|
||||
void buildUniqueId(char* uniqueId) override {
|
||||
sprintf(uniqueId, "%s_%s", MAIN_DEVICE_ID, this->id);
|
||||
static void deleteAll() {
|
||||
List<AbstractBuilder>::exec(builders, [](AbstractBuilder* builder)
|
||||
{ delete builder; });
|
||||
}
|
||||
};
|
||||
List<AbstractBuilder> AbstractBuilder::builders;
|
||||
|
||||
template <class T>
|
||||
struct Builder : AbstractBuilder {
|
||||
T* cmp;
|
||||
|
||||
Builder(T* cmp) : AbstractBuilder(), cmp(cmp) {}
|
||||
|
||||
Builder(const char* id) : AbstractBuilder() {
|
||||
cmp = new T{id};
|
||||
}
|
||||
|
||||
static Builder& instance(T* c) {
|
||||
return *(new Builder<T>(c));
|
||||
}
|
||||
|
||||
static Builder& instance(const char* id) {
|
||||
return *(new Builder<T>(id));
|
||||
}
|
||||
|
||||
T* build() {
|
||||
return static_cast<T*>(cmp);
|
||||
}
|
||||
|
||||
Builder& withDeviceClass(const char* value) {
|
||||
cmp->deviceClass = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Builder& withUnitMseasure(const char* value) {
|
||||
cmp->unitMeasure = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Builder& withValueTemplate(const char* value) {
|
||||
cmp->valueTemplate = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Builder& withSecondary(Component* c) {
|
||||
c->mainDevice = new DeviceConfig{ cmp->id };
|
||||
return *this;
|
||||
}
|
||||
|
||||
Builder& withDiagnostic(Component* c) {
|
||||
c->entityCategory = "diagnostic";
|
||||
c->mainDevice = new DeviceConfig{ cmp->id };
|
||||
return *this;
|
||||
}
|
||||
|
||||
Builder& asDevice(DeviceConfig* deviceConfig) {
|
||||
cmp->mainDevice = deviceConfig;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct Command : Component {
|
||||
char commandTopic[TOPIC_SIZE];
|
||||
onMessage f;
|
||||
static unordered_map<string, Command*> mapCommands;
|
||||
|
||||
Command(const char* name, const char* id, const char* type, onMessage f) : Component(name, id, type), f(f) {
|
||||
sprintf(commandTopic, "homeassistant/%s/%s/%s/set", type, MAIN_DEVICE_ID, id);
|
||||
mapCommands.insert({ string(commandTopic), this });
|
||||
}
|
||||
|
||||
virtual void onCommand(const char* msg) {
|
||||
if (f) f(msg);
|
||||
}
|
||||
|
||||
void buildConfig(JsonDocument& jsonDoc) override {
|
||||
EntityConfig<T>::buildConfig(jsonDoc);
|
||||
Component::buildConfig(jsonDoc);
|
||||
jsonDoc["command_topic"] = commandTopic;
|
||||
}
|
||||
|
||||
};
|
||||
unordered_map<string, Command*> Command::mapCommands;
|
||||
|
||||
typedef void (*onMessage)(const char* msg);
|
||||
struct Button : Command<Button> {
|
||||
onMessage f;
|
||||
struct Button : Command {
|
||||
|
||||
Button(const char* name, const char* id, onMessage f) : Command(name, id, "button"), f(f) {}
|
||||
Button(const char* name, const char* id, onMessage f = nullptr) : Command(name, id, "button", f) {}
|
||||
|
||||
};
|
||||
|
||||
@ -150,18 +216,13 @@ namespace Ha {
|
||||
}
|
||||
};
|
||||
|
||||
struct Switch : Command<Switch>, StateConfig<Switch> {
|
||||
struct Switch : Command, StateConfig<Switch> {
|
||||
|
||||
virtual void onCommand(const char* msg){}
|
||||
|
||||
Switch(const char* name, const char* id) : Command(name, id, "switch") {
|
||||
sprintf(commandTopic, "homeassistant/%s/%s/%s/set", type, MAIN_DEVICE_ID, id);
|
||||
}
|
||||
Switch(const char* name, const char* id, onMessage f = nullptr) : Command(name, id, "switch", f) {}
|
||||
|
||||
void buildConfig(JsonDocument& jsonDoc) override {
|
||||
Command::buildConfig(jsonDoc);
|
||||
jsonDoc["name"] = nullptr;
|
||||
jsonDoc["device_class"] = "outlet";
|
||||
// jsonDoc["retain"] = true;
|
||||
if (stateTopic[0]) jsonDoc["state_topic"] = stateTopic;
|
||||
}
|
||||
@ -171,20 +232,36 @@ namespace Ha {
|
||||
}
|
||||
};
|
||||
|
||||
struct Sensor : EntityConfig<Sensor>, StateConfig<Sensor> {
|
||||
struct Sensor : Component, StateConfig<Sensor> {
|
||||
const char* unitMeasure = nullptr;
|
||||
const char* valueTemplate = nullptr;
|
||||
|
||||
Sensor(const char* name, const char* id) : EntityConfig(name, Protocol_2::buildId(id), "sensor") {
|
||||
Sensor() : Component(name, id, "sensor") {
|
||||
withStateTopic();
|
||||
}
|
||||
|
||||
Sensor(const char* name, const char* id) : Component(name, id, "sensor") {
|
||||
withStateTopic();
|
||||
}
|
||||
|
||||
void buildUniqueId(char* uniqueId) override {
|
||||
sprintf(uniqueId, "%s_%s", deviceClass, id);
|
||||
if (deviceClass) {
|
||||
sprintf(uniqueId, "%s_%s_%s", MAIN_DEVICE_ID, deviceClass, id);
|
||||
} else {
|
||||
Component::buildUniqueId(uniqueId);
|
||||
}
|
||||
}
|
||||
|
||||
void buildConfigTopic() override {
|
||||
if (deviceClass) {
|
||||
sprintf(configTopic, "homeassistant/%s/%s/%s_%s/config", type, MAIN_DEVICE_ID, deviceClass, id);
|
||||
} else {
|
||||
Component::buildConfigTopic();
|
||||
}
|
||||
}
|
||||
|
||||
void buildConfig(JsonDocument& jsonDoc) override {
|
||||
EntityConfig::buildConfig(jsonDoc);
|
||||
Component::buildConfig(jsonDoc);
|
||||
if (unitMeasure) jsonDoc["unit_of_measurement"] = unitMeasure;
|
||||
if (valueTemplate) jsonDoc["value_template"] = valueTemplate;
|
||||
jsonDoc["state_topic"] = stateTopic;
|
||||
@ -193,17 +270,15 @@ namespace Ha {
|
||||
};
|
||||
|
||||
struct TemperatureSensor : Sensor {
|
||||
TemperatureSensor(const char* id) : Sensor("Temperature", id) {
|
||||
TemperatureSensor(const char* id, const char* name = "Temperature") : Sensor(name, id) {
|
||||
deviceClass = "temperature";
|
||||
unitMeasure = "°C";
|
||||
valueTemplate = "{{ value_json.sensor.temperature }}";
|
||||
}
|
||||
};
|
||||
|
||||
struct VoltageSensor : Sensor {
|
||||
VoltageSensor(const char* id, const char* name, const char* valueTemplate) : Sensor(name, id) {
|
||||
this->valueTemplate = valueTemplate;
|
||||
entityCategory = "diagnostic";
|
||||
deviceClass = "voltage";
|
||||
unitMeasure = "V";
|
||||
}
|
||||
@ -212,27 +287,24 @@ namespace Ha {
|
||||
struct BatterySensor : Sensor {
|
||||
BatterySensor(const char* id, const char* name, const char* valueTemplate) : Sensor(name, id) {
|
||||
this->valueTemplate = valueTemplate;
|
||||
entityCategory = "diagnostic";
|
||||
deviceClass = "battery";
|
||||
unitMeasure = "%";
|
||||
}
|
||||
};
|
||||
|
||||
struct HumiditySensor : Sensor {
|
||||
HumiditySensor(const char* name, const char* id) : Sensor(name, id) {
|
||||
name = "Humidity";
|
||||
HumiditySensor(const char* id, const char* name = "Humidity") : Sensor(name, id) {
|
||||
deviceClass = "humidity";
|
||||
unitMeasure = "%";
|
||||
valueTemplate = "{{ value_json.sensor.humidity }}";
|
||||
// valueTemplate = "{{ value_json.sensor.humidity }}";
|
||||
}
|
||||
};
|
||||
|
||||
struct PressureSensor : Sensor {
|
||||
PressureSensor(const char* name, const char* id) : Sensor(name, id) {
|
||||
name = "Pressure";
|
||||
PressureSensor(const char* id, const char* name = "Pressure") : Sensor(name, id) {
|
||||
deviceClass = "pressure";
|
||||
unitMeasure = "hPa";
|
||||
valueTemplate = "{{ value_json.sensor.pressure }}";
|
||||
// valueTemplate = "{{ value_json.sensor.pressure }}";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -12,12 +12,11 @@ namespace Mqtt {
|
||||
|
||||
AsyncMqttClient client;
|
||||
|
||||
void publishInit();
|
||||
void disconnect();
|
||||
Task tReConnect(5 * TASK_MINUTE, TASK_FOREVER, []() {
|
||||
Serial.println("Connecting to MQTT...");
|
||||
client.connect();
|
||||
}, &ts);
|
||||
void publishInit();
|
||||
Task tPublishInit(TASK_IMMEDIATE, TASK_ONCE, publishInit, &ts);
|
||||
|
||||
const char* mainTopic = "homeassistant/+/rc-gateway/#";
|
||||
@ -31,68 +30,44 @@ namespace Mqtt {
|
||||
return client.publish(topic, 0, true, message);
|
||||
}
|
||||
|
||||
Ha::Button* buttons[] = {
|
||||
(new Ha::Button{"Restart", "restart",
|
||||
Command* commands[] = {
|
||||
Builder<Button>::instance(new Button{"Restart", "restart",
|
||||
[](const char* msg) {
|
||||
if (String { "PRESS" }.equals(msg)) ESP.restart();
|
||||
if (strcmp("PRESS", msg) == 0) ESP.restart();
|
||||
}
|
||||
})->asDevice(gatewayDevice)
|
||||
};
|
||||
|
||||
Ha::Switch* switches[] = {
|
||||
}).asDevice(gatewayDevice).build(),
|
||||
(new EasyHomeSwitch{"FritzBox", "easy_home_a", (unsigned long[4]) { 4483136, 4626800, 4661552, 4819632 }, (unsigned long[4]) { 4326544, 4537104, 4767520, 4972704 }, "Basement"})->withStateTopic(),
|
||||
(new EasyHomeSwitch{"Outside", "easy_home_b", (unsigned long[4]) { 4483140, 4626804, 4661556, 4819636 }, (unsigned long[4]) { 4326548, 4537108, 4767524, 4972708 }, "Basement"})->withStateTopic(),
|
||||
(new PollinSwitch{"Meeting sensor", "00001", 1, "Dining room"})->withStateTopic(),
|
||||
(new PollinSwitch{"Fire Tv", "00001", 2, "Living room"})->withStateTopic(),
|
||||
(new PollinSwitch{"Diningroom player", "00001", 3, "Dining room"})->withStateTopic(),
|
||||
(new PollinSwitch{"Train", "11111", 4, "Playroom"})->withStateTopic(),
|
||||
(new EasyHomeSwitch{"FritzBox", "easy_home_a", (unsigned long[4]) { 4483136, 4626800, 4661552, 4819632 }, (unsigned long[4]) { 4326544, 4537104, 4767520, 4972704 }, "Basement"})->withStateTopic(),
|
||||
(new EasyHomeSwitch{"Outside", "easy_home_b", (unsigned long[4]) { 4483140, 4626804, 4661556, 4819636 }, (unsigned long[4]) { 4326548, 4537108, 4767524, 4972708 }, "Basement"})->withStateTopic()
|
||||
(new PollinSwitch{"Train", "11111", 4, "Playroom"})->withStateTopic()
|
||||
};
|
||||
|
||||
Ha::Sensor* sensors[] = {
|
||||
OilTankRoomSensorBuilder::build("4"),
|
||||
OilSensorBuilder::build("7")
|
||||
Sensor* sensors[] = {
|
||||
OilTank::buildRoomSensor("4"),
|
||||
OilTank::buildTankSensor("7")
|
||||
};
|
||||
|
||||
void publishComponentConfig(Ha::Component& component) {
|
||||
StaticJsonDocument<JSON_SIZE> jsonDoc;
|
||||
component.buildConfig(jsonDoc);
|
||||
|
||||
char message[JSON_SIZE];
|
||||
serializeJson(jsonDoc, message);
|
||||
|
||||
publish(component.configTopic, message);
|
||||
}
|
||||
|
||||
void publishInit() {
|
||||
Ha::publisher = publish;
|
||||
for (List<Ha::Component>::Container* c = Ha::Component::components.first; c; c = c->next) {
|
||||
publishComponentConfig(*c->t);
|
||||
}
|
||||
List<Component>::exec(Component::components, [](Component* c)
|
||||
{ c->publishConfig(); });
|
||||
AbstractBuilder::deleteAll();
|
||||
ts.deleteTask(tPublishInit);
|
||||
}
|
||||
|
||||
void publishCleanupConfig() {
|
||||
for (List<Ha::Component>::Container* c = Ha::Component::components.first; c; c = c->next) {
|
||||
publish(c->t->configTopic, "");
|
||||
}
|
||||
List<Component>::exec(Component::components, [](Component* c)
|
||||
{ c->publishCleanupConfig(); });
|
||||
}
|
||||
|
||||
void onMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
|
||||
char msg[len + 1];
|
||||
memcpy(msg, payload, len);
|
||||
msg[len] = 0;
|
||||
for (Ha::Button* cmd : buttons) {
|
||||
if (String{ cmd->commandTopic }.equals(topic) && cmd->f != nullptr) {
|
||||
cmd->f(msg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (Ha::Switch* cmd : switches) {
|
||||
if (String{ cmd->commandTopic }.equals(topic)) {
|
||||
cmd->onCommand(msg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Command* cmd = Command::mapCommands[string{ topic }];
|
||||
if (cmd) cmd->onCommand(msg);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
|
||||
@ -17,4 +17,10 @@ struct List {
|
||||
last = c;
|
||||
}
|
||||
|
||||
static void exec(List list, void(*f)(T*)) {
|
||||
for (List::Container *c = list.first; c; c = c->next) {
|
||||
f(c->t);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user