aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Zinoviev <me@ch1p.io>2023-05-30 01:01:09 +0300
committerEvgeny Zinoviev <me@ch1p.io>2023-05-30 01:01:09 +0300
commitd1435e2b1acc31fba64d21994d40297ae900c549 (patch)
tree18665fe89efc7819ac4ad72f2dda00f5d4cb8ffb
parent0e021d0f1e17f4e2c07f927284cda4bd2754caf0 (diff)
platformio: make relay a library
-rw-r--r--platformio/common/libs/mqtt/homekit/mqtt/module.h6
-rw-r--r--platformio/common/libs/mqtt/homekit/mqtt/mqtt.cpp15
-rw-r--r--platformio/common/libs/mqtt/homekit/mqtt/mqtt.h6
-rw-r--r--platformio/common/libs/mqtt/library.json2
-rw-r--r--platformio/common/libs/mqtt_module_diagnostics/homekit/mqtt/module/diagnostics.cpp6
-rw-r--r--platformio/common/libs/mqtt_module_diagnostics/library.json5
-rw-r--r--platformio/common/libs/mqtt_module_ota/library.json5
-rw-r--r--platformio/common/libs/mqtt_module_relay/homekit/mqtt/module/relay.cpp43
-rw-r--r--platformio/common/libs/mqtt_module_relay/homekit/mqtt/module/relay.h23
-rw-r--r--platformio/common/libs/mqtt_module_relay/library.json11
-rw-r--r--platformio/common/libs/mqtt_module_temphum/homekit/mqtt/module/temphum.cpp4
-rw-r--r--platformio/common/libs/mqtt_module_temphum/library.json3
-rw-r--r--platformio/common/libs/relay/homekit/relay.cpp (renamed from platformio/relayctl/src/relay.h)16
-rw-r--r--platformio/common/libs/relay/homekit/relay.h13
-rw-r--r--platformio/common/libs/relay/library.json8
-rw-r--r--platformio/relayctl/src/leds.cpp11
-rw-r--r--platformio/relayctl/src/leds.h15
-rw-r--r--platformio/relayctl/src/main.cpp181
-rw-r--r--platformio/relayctl/src/mqtt.cpp345
-rw-r--r--platformio/relayctl/src/mqtt.h107
20 files changed, 156 insertions, 669 deletions
diff --git a/platformio/common/libs/mqtt/homekit/mqtt/module.h b/platformio/common/libs/mqtt/homekit/mqtt/module.h
index 7bf2522..e4a01f8 100644
--- a/platformio/common/libs/mqtt/homekit/mqtt/module.h
+++ b/platformio/common/libs/mqtt/homekit/mqtt/module.h
@@ -39,9 +39,13 @@ public:
initialized = true;
}
+ inline short getTickInterval() {
+ return tickInterval;
+ }
+
friend class Mqtt;
};
}
-#endif //HOMEKIT_LIB_MQTT_MODULE_H \ No newline at end of file
+#endif //HOMEKIT_LIB_MQTT_MODULE_H
diff --git a/platformio/common/libs/mqtt/homekit/mqtt/mqtt.cpp b/platformio/common/libs/mqtt/homekit/mqtt/mqtt.cpp
index 9d96f9f..cb2cea7 100644
--- a/platformio/common/libs/mqtt/homekit/mqtt/mqtt.cpp
+++ b/platformio/common/libs/mqtt/homekit/mqtt/mqtt.cpp
@@ -25,7 +25,7 @@ using namespace espMqttClientTypes;
Mqtt::Mqtt() {
auto cfg = config::read();
- homeId = String(cfg.flags.node_configured ? cfg.node_id : wifi::NODE_ID);
+ nodeId = String(cfg.flags.node_configured ? cfg.node_id : wifi::NODE_ID);
randomSeed(micros());
@@ -79,7 +79,7 @@ Mqtt::Mqtt() {
PRINTF("mqtt: message received, topic=%s, qos=%d, dup=%d, retain=%d, len=%ul, index=%ul, total=%ul\n",
topic, properties.qos, (int)properties.dup, (int)properties.retain, len, index, total);
- const char *ptr = topic + homeId.length() + 10;
+ const char *ptr = topic + nodeId.length() + 10;
String relevantTopic(ptr);
auto it = moduleSubscriptions.find(relevantTopic);
@@ -130,22 +130,24 @@ void Mqtt::disconnect() {
void Mqtt::loop() {
client.loop();
for (auto& module: modules) {
- module->tick(*this);
+ if (module->getTickInterval() != 0)
+ module->tick(*this);
}
}
uint16_t Mqtt::publish(const String& topic, uint8_t* payload, size_t length) {
- String fullTopic = "hk/" + homeId + "/temphum/" + topic;
+ String fullTopic = "hk/" + nodeId + "/" + topic;
return client.publish(fullTopic.c_str(), 1, false, payload, length);
}
uint16_t Mqtt::subscribe(const String& topic, uint8_t qos) {
- String fullTopic = "hk/" + homeId + "/temphum/" + topic;
+ String fullTopic = "hk/" + nodeId + "/" + topic;
PRINTF("mqtt: subscribing to %s...\n", fullTopic.c_str());
uint16_t packetId = client.subscribe(fullTopic.c_str(), qos);
if (!packetId)
PRINTF("error: failed to subscribe to %s\n", fullTopic.c_str());
+
return packetId;
}
@@ -157,8 +159,9 @@ void Mqtt::addModule(MqttModule* module) {
}
}
-void Mqtt::subscribeModule(String& topic, MqttModule* module) {
+void Mqtt::subscribeModule(String& topic, MqttModule* module, uint8_t qos) {
moduleSubscriptions[topic] = module;
+ subscribe(topic, qos);
}
}
diff --git a/platformio/common/libs/mqtt/homekit/mqtt/mqtt.h b/platformio/common/libs/mqtt/homekit/mqtt/mqtt.h
index 983cc5a..9e0c2be 100644
--- a/platformio/common/libs/mqtt/homekit/mqtt/mqtt.h
+++ b/platformio/common/libs/mqtt/homekit/mqtt/mqtt.h
@@ -22,7 +22,7 @@ class MqttModule;
class Mqtt {
private:
- String homeId;
+ String nodeId;
WiFiClientSecure httpsSecureClient;
espMqttClientSecure client;
Ticker reconnectTimer;
@@ -39,10 +39,10 @@ public:
void reconnect();
void loop();
void addModule(MqttModule* module);
- void subscribeModule(String& topic, MqttModule* module);
+ void subscribeModule(String& topic, MqttModule* module, uint8_t qos = 0);
uint16_t publish(const String& topic, uint8_t* payload, size_t length);
};
}
-#endif //HOMEKIT_LIB_MQTT_H \ No newline at end of file
+#endif //HOMEKIT_LIB_MQTT_H
diff --git a/platformio/common/libs/mqtt/library.json b/platformio/common/libs/mqtt/library.json
index 179b10a..d1ad420 100644
--- a/platformio/common/libs/mqtt/library.json
+++ b/platformio/common/libs/mqtt/library.json
@@ -1,6 +1,6 @@
{
"name": "homekit_mqtt",
- "version": "1.0.8",
+ "version": "1.0.9",
"build": {
"flags": "-I../../include"
}
diff --git a/platformio/common/libs/mqtt_module_diagnostics/homekit/mqtt/module/diagnostics.cpp b/platformio/common/libs/mqtt_module_diagnostics/homekit/mqtt/module/diagnostics.cpp
index f022f79..d36a7e9 100644
--- a/platformio/common/libs/mqtt_module_diagnostics/homekit/mqtt/module/diagnostics.cpp
+++ b/platformio/common/libs/mqtt_module_diagnostics/homekit/mqtt/module/diagnostics.cpp
@@ -4,8 +4,8 @@
namespace homekit::mqtt {
-static const char TOPIC_DIAGNOSTICS[] = "stat";
-static const char TOPIC_INITIAL_DIAGNOSTICS[] = "stat1";
+static const char TOPIC_DIAGNOSTICS[] = "diag";
+static const char TOPIC_INITIAL_DIAGNOSTICS[] = "d1ag";
void MqttDiagnosticsModule::init(Mqtt& mqtt) {}
@@ -44,4 +44,4 @@ void MqttDiagnosticsModule::tick(Mqtt& mqtt) {
}
}
-} \ No newline at end of file
+}
diff --git a/platformio/common/libs/mqtt_module_diagnostics/library.json b/platformio/common/libs/mqtt_module_diagnostics/library.json
index 81bf0fb..8df306d 100644
--- a/platformio/common/libs/mqtt_module_diagnostics/library.json
+++ b/platformio/common/libs/mqtt_module_diagnostics/library.json
@@ -1,7 +1,10 @@
{
"name": "homekit_mqtt_module_diagnostics",
- "version": "1.0.0",
+ "version": "1.0.1",
"build": {
"flags": "-I../../include"
+ },
+ "dependencies": {
+ "homekit_mqtt": "file://../common/libs/mqtt"
}
}
diff --git a/platformio/common/libs/mqtt_module_ota/library.json b/platformio/common/libs/mqtt_module_ota/library.json
index 1eeda1b..30db7d2 100644
--- a/platformio/common/libs/mqtt_module_ota/library.json
+++ b/platformio/common/libs/mqtt_module_ota/library.json
@@ -1,10 +1,11 @@
{
"name": "homekit_mqtt_module_ota",
- "version": "1.0.1",
+ "version": "1.0.2",
"build": {
"flags": "-I../../include"
},
"dependencies": {
- "homekit_led": "file://../common/libs/led"
+ "homekit_led": "file://../common/libs/led",
+ "homekit_mqtt": "file://../common/libs/mqtt"
}
}
diff --git a/platformio/common/libs/mqtt_module_relay/homekit/mqtt/module/relay.cpp b/platformio/common/libs/mqtt_module_relay/homekit/mqtt/module/relay.cpp
new file mode 100644
index 0000000..ab40727
--- /dev/null
+++ b/platformio/common/libs/mqtt_module_relay/homekit/mqtt/module/relay.cpp
@@ -0,0 +1,43 @@
+#include "./relay.h"
+#include <homekit/relay.h>
+#include <homekit/logging.h>
+
+namespace homekit::mqtt {
+
+static const char TOPIC_RELAY_SWITCH[] = "relay/switch";
+
+void MqttRelayModule::init(Mqtt &mqtt) {
+ String topic(TOPIC_RELAY_SWITCH);
+ mqtt.subscribeModule(topic, this, 1);
+}
+
+void MqttRelayModule::tick(homekit::mqtt::Mqtt& mqtt) {}
+
+void MqttRelayModule::handlePayload(Mqtt& mqtt, String& topic, uint16_t packetId, const uint8_t *payload, size_t length, size_t index, size_t total) {
+ if (topic != TOPIC_RELAY_SWITCH)
+ return;
+
+ if (length != sizeof(MqttRelaySwitchPayload)) {
+ PRINTF("error: size of payload (%ul) does not match expected (%ul)\n",
+ length, sizeof(MqttRelaySwitchPayload));
+ return;
+ }
+
+ auto pd = reinterpret_cast<const struct MqttRelaySwitchPayload*>(payload);
+ if (strncmp(pd->secret, MQTT_SECRET, sizeof(pd->secret)) != 0) {
+ PRINTLN("error: invalid secret");
+ return;
+ }
+
+ if (pd->state == 1) {
+ PRINTLN("mqtt: turning relay on");
+ relay::on();
+ } else if (pd->state == 0) {
+ PRINTLN("mqtt: turning relay off");
+ relay::off();
+ } else {
+ PRINTLN("error: unexpected state value");
+ }
+}
+
+}
diff --git a/platformio/common/libs/mqtt_module_relay/homekit/mqtt/module/relay.h b/platformio/common/libs/mqtt_module_relay/homekit/mqtt/module/relay.h
new file mode 100644
index 0000000..6420de1
--- /dev/null
+++ b/platformio/common/libs/mqtt_module_relay/homekit/mqtt/module/relay.h
@@ -0,0 +1,23 @@
+#ifndef HOMEKIT_LIB_MQTT_MODULE_RELAY_H
+#define HOMEKIT_LIB_MQTT_MODULE_RELAY_H
+
+#include <homekit/mqtt/module.h>
+
+namespace homekit::mqtt {
+
+struct MqttRelaySwitchPayload {
+ char secret[12];
+ uint8_t state;
+} __attribute__((packed));
+
+class MqttRelayModule : public MqttModule {
+public:
+ MqttRelayModule() : MqttModule(0) {}
+ void init(Mqtt& mqtt) override;
+ void tick(Mqtt& mqtt) override;
+ void handlePayload(Mqtt& mqtt, String& topic, uint16_t packetId, const uint8_t *payload, size_t length, size_t index, size_t total) override;
+};
+
+}
+
+#endif //HOMEKIT_LIB_MQTT_MODULE_RELAY_H
diff --git a/platformio/common/libs/mqtt_module_relay/library.json b/platformio/common/libs/mqtt_module_relay/library.json
new file mode 100644
index 0000000..e71cf95
--- /dev/null
+++ b/platformio/common/libs/mqtt_module_relay/library.json
@@ -0,0 +1,11 @@
+{
+ "name": "homekit_mqtt_module_relay",
+ "version": "1.0.3",
+ "build": {
+ "flags": "-I../../include"
+ },
+ "dependencies": {
+ "homekit_mqtt": "file://../common/libs/mqtt",
+ "homekit_relay": "file://../common/libs/relay"
+ }
+}
diff --git a/platformio/common/libs/mqtt_module_temphum/homekit/mqtt/module/temphum.cpp b/platformio/common/libs/mqtt_module_temphum/homekit/mqtt/module/temphum.cpp
index 8b7a2ef..82f1d74 100644
--- a/platformio/common/libs/mqtt_module_temphum/homekit/mqtt/module/temphum.cpp
+++ b/platformio/common/libs/mqtt_module_temphum/homekit/mqtt/module/temphum.cpp
@@ -2,7 +2,7 @@
namespace homekit::mqtt {
-static const char TOPIC_TEMPHUM_DATA[] = "data";
+static const char TOPIC_TEMPHUM_DATA[] = "temphum/data";
void MqttTemphumModule::init(Mqtt &mqtt) {}
@@ -20,4 +20,4 @@ void MqttTemphumModule::tick(homekit::mqtt::Mqtt& mqtt) {
mqtt.publish(TOPIC_TEMPHUM_DATA, reinterpret_cast<uint8_t*>(&payload), sizeof(payload));
}
-} \ No newline at end of file
+}
diff --git a/platformio/common/libs/mqtt_module_temphum/library.json b/platformio/common/libs/mqtt_module_temphum/library.json
index 30f5eb6..9bb8cf1 100644
--- a/platformio/common/libs/mqtt_module_temphum/library.json
+++ b/platformio/common/libs/mqtt_module_temphum/library.json
@@ -1,10 +1,11 @@
{
"name": "homekit_mqtt_module_temphum",
- "version": "1.0.8",
+ "version": "1.0.9",
"build": {
"flags": "-I../../include"
},
"dependencies": {
+ "homekit_mqtt": "file://../common/libs/mqtt",
"homekit_temphum": "file://../common/libs/temphum"
}
}
diff --git a/platformio/relayctl/src/relay.h b/platformio/common/libs/relay/homekit/relay.cpp
index 5287f09..b00a7a2 100644
--- a/platformio/relayctl/src/relay.h
+++ b/platformio/common/libs/relay/homekit/relay.cpp
@@ -1,26 +1,22 @@
-#ifndef HOMEKIT_RELAYCTL_RELAY_H
-#define HOMEKIT_RELAYCTL_RELAY_H
-
#include <Arduino.h>
+#include "./relay.h"
namespace homekit::relay {
-
-inline void init() {
+
+void init() {
pinMode(CONFIG_RELAY_GPIO, OUTPUT);
}
-inline bool getState() {
+bool state() {
return digitalRead(CONFIG_RELAY_GPIO) == HIGH;
}
-inline void setOn() {
+void on() {
digitalWrite(CONFIG_RELAY_GPIO, HIGH);
}
-inline void setOff() {
+void off() {
digitalWrite(CONFIG_RELAY_GPIO, LOW);
}
}
-
-#endif //HOMEKIT_RELAYCTL_RELAY_H \ No newline at end of file
diff --git a/platformio/common/libs/relay/homekit/relay.h b/platformio/common/libs/relay/homekit/relay.h
new file mode 100644
index 0000000..288cc05
--- /dev/null
+++ b/platformio/common/libs/relay/homekit/relay.h
@@ -0,0 +1,13 @@
+#ifndef HOMEKIT_LIB_RELAY_H
+#define HOMEKIT_LIB_RELAY_H
+
+namespace homekit::relay {
+
+void init();
+bool state();
+void on();
+void off();
+
+}
+
+#endif //HOMEKIT_LIB_RELAY_H
diff --git a/platformio/common/libs/relay/library.json b/platformio/common/libs/relay/library.json
new file mode 100644
index 0000000..e878248
--- /dev/null
+++ b/platformio/common/libs/relay/library.json
@@ -0,0 +1,8 @@
+{
+ "name": "homekit_relay",
+ "version": "1.0.0",
+ "build": {
+ "flags": "-I../../include"
+ }
+}
+
diff --git a/platformio/relayctl/src/leds.cpp b/platformio/relayctl/src/leds.cpp
deleted file mode 100644
index f0c9d51..0000000
--- a/platformio/relayctl/src/leds.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "leds.h"
-
-namespace homekit {
-
-#ifdef CONFIG_TARGET_NODEMCU
-Led* board_led = new Led(CONFIG_BOARD_LED_GPIO);
-#endif
-Led* mcu_led = new Led(CONFIG_MCU_LED_GPIO);
-
-}
-
diff --git a/platformio/relayctl/src/leds.h b/platformio/relayctl/src/leds.h
deleted file mode 100644
index 04c1f79..0000000
--- a/platformio/relayctl/src/leds.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef HOMEKIT_TEMPHUM_LEDS_H
-#define HOMEKIT_TEMPHUM_LEDS_H
-
-#include <homekit/led.h>
-
-namespace homekit {
-
-#ifdef CONFIG_TARGET_NODEMCU
-extern Led* board_led;
-#endif
-extern Led* mcu_led;
-
-}
-
-#endif //HOMEKIT_TEMPHUM_LEDS_H \ No newline at end of file
diff --git a/platformio/relayctl/src/main.cpp b/platformio/relayctl/src/main.cpp
index 13b00b3..c399641 100644
--- a/platformio/relayctl/src/main.cpp
+++ b/platformio/relayctl/src/main.cpp
@@ -1,176 +1,35 @@
#include <Arduino.h>
-#include <ESP8266WiFi.h>
-#include <DNSServer.h>
-#include <Ticker.h>
-
-#include <homekit/config.h>
-#include <homekit/logging.h>
-#include <homekit/http_server.h>
-#include <homekit/wifi.h>
-#include <homekit/stopwatch.h>
-
-#include "relay.h"
-#include "leds.h"
-#include "mqtt.h"
+#include <Wire.h>
+#include <homekit/main.h>
+#include <homekit/mqtt/mqtt.h>
+#include <homekit/mqtt/module/relay.h>
+#include <homekit/relay.h>
using namespace homekit;
+using main::LoopConfig;
+using mqtt::Mqtt;
+using mqtt::MqttRelayModule;
-enum class WorkingMode {
- RECOVERY, // AP mode, http server with configuration
- NORMAL, // MQTT client
-};
-static enum WorkingMode working_mode = WorkingMode::NORMAL;
-
-enum class WiFiConnectionState {
- WAITING = 0,
- JUST_CONNECTED = 1,
- CONNECTED = 2
-};
-
-static const uint16_t recovery_boot_detection_ms = 2000;
-static const uint8_t recovery_boot_delay_ms = 100;
-
-static volatile enum WiFiConnectionState wifi_state = WiFiConnectionState::WAITING;
-static void* service = nullptr;
-static WiFiEventHandler wifiConnectHandler, wifiDisconnectHandler;
-static Ticker wifiTimer;
-#if MQTT_BLINK
-static StopWatch blinkStopWatch;
-#endif
-
-static DNSServer* dnsServer = nullptr;
-
-static void onWifiConnected(const WiFiEventStationModeGotIP& event);
-static void onWifiDisconnected(const WiFiEventStationModeDisconnected& event);
-
-static void wifiConnect() {
- const char *ssid, *psk, *hostname;
- auto cfg = config::read();
- wifi::getConfig(cfg, &ssid, &psk, &hostname);
+MqttRelayModule* mqttRelayModule = nullptr;
- PRINTF("Wi-Fi STA creds: ssid=%s, psk=%s, hostname=%s\n", ssid, psk, hostname);
+static void onMqttCreated(Mqtt& mqtt);
- wifi_state = WiFiConnectionState::WAITING;
-
- WiFi.mode(WIFI_STA);
- WiFi.hostname(hostname);
- WiFi.begin(ssid, psk);
-
- PRINT("connecting to wifi..");
-}
-
-static void wifiHotspot() {
- mcu_led->on();
-
- auto scanResults = wifi::scan();
-
- WiFi.mode(WIFI_AP);
- WiFi.softAP(wifi::AP_SSID);
-
- dnsServer = new DNSServer();
- dnsServer->start(53, "*", WiFi.softAPIP());
-
- service = new HttpServer(scanResults);
- ((HttpServer*)service)->start();
-}
+LoopConfig loopConfig = {
+ .onMqttCreated = onMqttCreated
+};
void setup() {
- WiFi.disconnect();
-
-#ifdef DEBUG
- Serial.begin(115200);
-#endif
-
+ main::setup();
relay::init();
-
- pinMode(CONFIG_FLASH_GPIO, INPUT_PULLUP);
- for (uint16_t i = 0; i < recovery_boot_detection_ms; i += recovery_boot_delay_ms) {
- delay(recovery_boot_delay_ms);
- if (digitalRead(CONFIG_FLASH_GPIO) == LOW) {
- working_mode = WorkingMode::RECOVERY;
- break;
- }
- }
-
- auto cfg = config::read();
- if (config::isDirty(cfg)) {
- PRINTLN("config is dirty, erasing...");
- config::erase(cfg);
- board_led->blink(10, 50);
- }
-
- switch (working_mode) {
- case WorkingMode::RECOVERY:
- wifiHotspot();
- break;
-
- case WorkingMode::NORMAL:
- wifiConnectHandler = WiFi.onStationModeGotIP(onWifiConnected);
- wifiDisconnectHandler = WiFi.onStationModeDisconnected(onWifiDisconnected);
- wifiConnect();
- break;
- }
}
void loop() {
- if (working_mode == WorkingMode::NORMAL) {
- if (wifi_state == WiFiConnectionState::WAITING) {
- PRINT(".");
- mcu_led->blink(2, 50);
- delay(1000);
- return;
- }
-
- if (wifi_state == WiFiConnectionState::JUST_CONNECTED) {
- board_led->blink(3, 300);
- wifi_state = WiFiConnectionState::CONNECTED;
-
- if (service == nullptr)
- service = new mqtt::MQTT();
-
- ((mqtt::MQTT*)service)->connect();
-#if MQTT_BLINK
- blinkStopWatch.save();
-#endif
- }
-
- auto mqtt = (mqtt::MQTT*)service;
- if (static_cast<int>(wifi_state) >= 1 && mqtt != nullptr) {
- mqtt->loop();
-
- if (mqtt->ota.readyToRestart) {
- mqtt->disconnect();
- } else if (mqtt->diagnosticsStopWatch.elapsed(10000)) {
- mqtt->sendDiagnostics();
- }
-
-#if MQTT_BLINK
- // periodically blink board led
- if (blinkStopWatch.elapsed(5000)) {
- board_led->blink(1, 10);
- blinkStopWatch.save();
- }
-#endif
- }
- } else {
- if (dnsServer != nullptr)
- dnsServer->processNextRequest();
-
- auto httpServer = (HttpServer*)service;
- if (httpServer != nullptr)
- httpServer->loop();
- }
+ main::loop(&loopConfig);
}
-static void onWifiConnected(const WiFiEventStationModeGotIP& event) {
- PRINTF("connected (%s)\n", WiFi.localIP().toString().c_str());
- wifi_state = WiFiConnectionState::JUST_CONNECTED;
-}
-
-static void onWifiDisconnected(const WiFiEventStationModeDisconnected& event) {
- PRINTLN("disconnected from wi-fi");
- wifi_state = WiFiConnectionState::WAITING;
- if (service != nullptr)
- ((mqtt::MQTT*)service)->disconnect();
- wifiTimer.once(2, wifiConnect);
+static void onMqttCreated(Mqtt& mqtt) {
+ if (mqttRelayModule == nullptr) {
+ mqttRelayModule = new MqttRelayModule();
+ mqtt.addModule(mqttRelayModule);
+ }
} \ No newline at end of file
diff --git a/platformio/relayctl/src/mqtt.cpp b/platformio/relayctl/src/mqtt.cpp
deleted file mode 100644
index ad3caef..0000000
--- a/platformio/relayctl/src/mqtt.cpp
+++ /dev/null
@@ -1,345 +0,0 @@
-#include <ESP8266httpUpdate.h>
-
-#include <homekit/logging.h>
-#include <homekit/wifi.h>
-#include <homekit/util.h>
-#include <homekit/mqtt.h>
-
-#include "relay.h"
-#include "mqtt.h"
-#include "leds.h"
-
-namespace homekit::mqtt {
-
-static const char TOPIC_DIAGNOSTICS[] = "stat";
-static const char TOPIC_INITIAL_DIAGNOSTICS[] = "stat1";
-static const char TOPIC_OTA_RESPONSE[] = "otares";
-static const char TOPIC_RELAY_POWER[] = "power";
-static const char TOPIC_ADMIN_OTA[] = "admin/ota";
-static const uint16_t MQTT_KEEPALIVE = 30;
-
-enum class IncomingMessage {
- UNKNOWN,
- RELAY_POWER,
- OTA
-};
-
-using namespace espMqttClientTypes;
-
-#define MD5_SIZE 16
-
-MQTT::MQTT() {
- auto cfg = config::read();
- homeId = String(cfg.flags.node_configured ? cfg.node_id : wifi::NODE_ID);
-
- randomSeed(micros());
-
- client.onConnect([&](bool sessionPresent) {
- PRINTLN("mqtt: connected");
-
- sendInitialDiagnostics();
-
- subscribe(TOPIC_RELAY_POWER, 1);
- subscribe(TOPIC_ADMIN_OTA);
- });
-
- client.onDisconnect([&](DisconnectReason reason) {
- PRINTF("mqtt: disconnected, reason=%d\n", static_cast<int>(reason));
-#ifdef DEBUG
- if (reason == DisconnectReason::TLS_BAD_FINGERPRINT)
- PRINTLN("reason: bad fingerprint");
-#endif
-
- if (ota.started()) {
- PRINTLN("mqtt: update was in progress, canceling..");
- ota.clean();
- Update.end();
- Update.clearError();
- }
-
- if (ota.readyToRestart) {
- restartTimer.once(1, restart);
- } else {
- reconnectTimer.once(2, [&]() {
- reconnect();
- });
- }
- });
-
- client.onSubscribe([&](uint16_t packetId, const SubscribeReturncode* returncodes, size_t len) {
- PRINTF("mqtt: subscribe ack, packet_id=%d\n", packetId);
- for (size_t i = 0; i < len; i++) {
- PRINTF(" return code: %u\n", static_cast<unsigned int>(*(returncodes+i)));
- }
- });
-
- client.onUnsubscribe([&](uint16_t packetId) {
- PRINTF("mqtt: unsubscribe ack, packet_id=%d\n", packetId);
- });
-
- client.onMessage([&](const MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total) {
- PRINTF("mqtt: message received, topic=%s, qos=%d, dup=%d, retain=%d, len=%ul, index=%ul, total=%ul\n",
- topic, properties.qos, (int)properties.dup, (int)properties.retain, len, index, total);
-
- IncomingMessage msgType = IncomingMessage::UNKNOWN;
-
- const char *ptr = topic + homeId.length() + 10;
- String relevantTopic(ptr);
-
- if (relevantTopic == TOPIC_RELAY_POWER)
- msgType = IncomingMessage::RELAY_POWER;
- else if (relevantTopic == TOPIC_ADMIN_OTA)
- msgType = IncomingMessage::OTA;
-
- if (len != total && msgType != IncomingMessage::OTA) {
- PRINTLN("mqtt: received partial message, not supported");
- return;
- }
-
- switch (msgType) {
- case IncomingMessage::RELAY_POWER:
- handleRelayPowerPayload(payload, total);
- break;
-
- case IncomingMessage::OTA:
- if (ota.finished)
- break;
- handleAdminOtaPayload(properties.packetId, payload, len, index, total);
- break;
-
- case IncomingMessage::UNKNOWN:
- PRINTF("error: invalid topic %s\n", topic);
- break;
- }
- });
-
- client.onPublish([&](uint16_t packetId) {
- PRINTF("mqtt: publish ack, packet_id=%d\n", packetId);
-
- if (ota.finished && packetId == ota.publishResultPacketId) {
- ota.readyToRestart = true;
- }
- });
-
- client.setServer(MQTT_SERVER, MQTT_PORT);
- client.setClientId(MQTT_CLIENT_ID);
- client.setCredentials(MQTT_USERNAME, MQTT_PASSWORD);
- client.setCleanSession(true);
- client.setFingerprint(MQTT_CA_FINGERPRINT);
- client.setKeepAlive(MQTT_KEEPALIVE);
-}
-
-void MQTT::connect() {
- reconnect();
-}
-
-void MQTT::reconnect() {
- if (client.connected()) {
- PRINTLN("warning: already connected");
- return;
- }
- client.connect();
-}
-
-void MQTT::disconnect() {
- // TODO test how this works???
- reconnectTimer.detach();
- client.disconnect();
-}
-
-uint16_t MQTT::publish(const String &topic, uint8_t *payload, size_t length) {
- String fullTopic = "hk/" + homeId + "/relay/" + topic;
- return client.publish(fullTopic.c_str(), 1, false, payload, length);
-}
-
-void MQTT::loop() {
- client.loop();
-}
-
-uint16_t MQTT::subscribe(const String &topic, uint8_t qos) {
- String fullTopic = "hk/" + homeId + "/relay/" + topic;
- PRINTF("mqtt: subscribing to %s...\n", fullTopic.c_str());
-
- uint16_t packetId = client.subscribe(fullTopic.c_str(), qos);
- if (!packetId)
- PRINTF("error: failed to subscribe to %s\n", fullTopic.c_str());
- return packetId;
-}
-
-void MQTT::sendInitialDiagnostics() {
- auto cfg = config::read();
- InitialDiagnosticsPayload stat{
- .ip = wifi::getIPAsInteger(),
- .fw_version = CONFIG_FW_VERSION,
- .rssi = wifi::getRSSI(),
- .free_heap = ESP.getFreeHeap(),
- .flags = DiagnosticsFlags{
- .state = static_cast<uint8_t>(relay::getState() ? 1 : 0),
- .config_changed_value_present = 1,
- .config_changed = static_cast<uint8_t>(cfg.flags.node_configured ||
- cfg.flags.wifi_configured ? 1 : 0)
- }
- };
- publish(TOPIC_INITIAL_DIAGNOSTICS, reinterpret_cast<uint8_t*>(&stat), sizeof(stat));
- diagnosticsStopWatch.save();
-}
-
-void MQTT::sendDiagnostics() {
- DiagnosticsPayload stat{
- .rssi = wifi::getRSSI(),
- .free_heap = ESP.getFreeHeap(),
- .flags = DiagnosticsFlags{
- .state = static_cast<uint8_t>(relay::getState() ? 1 : 0),
- .config_changed_value_present = 0,
- .config_changed = 0
- }
- };
- publish(TOPIC_DIAGNOSTICS, reinterpret_cast<uint8_t*>(&stat), sizeof(stat));
- diagnosticsStopWatch.save();
-}
-
-uint16_t MQTT::sendOtaResponse(OTAResult status, uint8_t error_code) {
- OTAResponse resp{
- .status = status,
- .error_code = error_code
- };
- return publish(TOPIC_OTA_RESPONSE, reinterpret_cast<uint8_t*>(&resp), sizeof(resp));
-}
-
-void MQTT::handleRelayPowerPayload(const uint8_t *payload, uint32_t length) {
- if (length != sizeof(PowerPayload)) {
- PRINTF("error: size of payload (%ul) does not match expected (%ul)\n",
- length, sizeof(PowerPayload));
- return;
- }
-
- auto pd = reinterpret_cast<const struct PowerPayload*>(payload);
- if (strncmp(pd->secret, MQTT_SECRET, sizeof(pd->secret)) != 0) {
- PRINTLN("error: invalid secret");
- return;
- }
-
- if (pd->state == 1) {
- PRINTLN("mqtt: turning relay on");
- relay::setOn();
- } else if (pd->state == 0) {
- PRINTLN("mqtt: turning relay off");
- relay::setOff();
- } else {
- PRINTLN("error: unexpected state value");
- }
-
- sendDiagnostics();
-}
-
-void MQTT::handleAdminOtaPayload(uint16_t packetId, const uint8_t *payload, size_t length, size_t index, size_t total) {
- char md5[33];
- char* md5Ptr = md5;
-
- if (index != 0 && ota.dataPacketId != packetId) {
- PRINTLN("mqtt/ota: non-matching packet id");
- return;
- }
-
- Update.runAsync(true);
-
- if (index == 0) {
- if (length < CONFIG_NODE_SECRET_SIZE + MD5_SIZE) {
- PRINTLN("mqtt/ota: failed to check secret, first packet size is too small");
- return;
- }
-
- if (memcmp((const char*)payload, CONFIG_NODE_SECRET, CONFIG_NODE_SECRET_SIZE) != 0) {
- PRINTLN("mqtt/ota: invalid secret");
- return;
- }
-
- PRINTF("mqtt/ota: starting update, total=%ul\n", total-NODE_SECRET_SIZE);
- for (int i = 0; i < MD5_SIZE; i++) {
- md5Ptr += sprintf(md5Ptr, "%02x", *((unsigned char*)(payload+CONFIG_NODE_SECRET_SIZE+i)));
- }
- md5[32] = '\0';
- PRINTF("mqtt/ota: md5 is %s\n", md5);
- PRINTF("mqtt/ota: first packet is %ul bytes length\n", length);
-
- md5[32] = '\0';
-
- if (Update.isRunning()) {
- Update.end();
- Update.clearError();
- }
-
- if (!Update.setMD5(md5)) {
- PRINTLN("mqtt/ota: setMD5 failed");
- return;
- }
-
- ota.dataPacketId = packetId;
-
- if (!Update.begin(total - CONFIG_NODE_SECRET_SIZE - MD5_SIZE)) {
- ota.clean();
-#ifdef DEBUG
- Update.printError(Serial);
-#endif
- sendOtaResponse(OTAResult::UPDATE_ERROR, Update.getError());
- }
-
- ota.written = Update.write(const_cast<uint8_t*>(payload)+CONFIG_NODE_SECRET_SIZE + MD5_SIZE, length-CONFIG_NODE_SECRET_SIZE - MD5_SIZE);
- ota.written += CONFIG_NODE_SECRET_SIZE + MD5_SIZE;
-
- mcu_led->blink(1, 1);
- PRINTF("mqtt/ota: updating %u/%u\n", ota.written, Update.size());
-
- } else {
- if (!Update.isRunning()) {
- PRINTLN("mqtt/ota: update is not running");
- return;
- }
-
- if (index == ota.written) {
- size_t written;
- if ((written = Update.write(const_cast<uint8_t*>(payload), length)) != length) {
- PRINTF("mqtt/ota: error: tried to write %ul bytes, write() returned %ul\n",
- length, written);
- ota.clean();
- Update.end();
- Update.clearError();
- sendOtaResponse(OTAResult::WRITE_ERROR);
- return;
- }
- ota.written += length;
-
- mcu_led->blink(1, 1);
- PRINTF("mqtt/ota: updating %u/%u\n",
- ota.written - CONFIG_NODE_SECRET_SIZE - MD5_SIZE,
- Update.size());
- } else {
- PRINTF("mqtt/ota: position is invalid, expected %ul, got %ul\n", ota.written, index);
- ota.clean();
- Update.end();
- Update.clearError();
- }
- }
-
- if (Update.isFinished()) {
- ota.dataPacketId = 0;
-
- if (Update.end()) {
- ota.finished = true;
- ota.publishResultPacketId = sendOtaResponse(OTAResult::OK);
- PRINTF("mqtt/ota: ok, otares packet_id=%d\n", ota.publishResultPacketId);
- } else {
- ota.clean();
-
- PRINTF("mqtt/ota: error: %u\n", Update.getError());
-#ifdef DEBUG
- Update.printError(Serial);
-#endif
- Update.clearError();
-
- sendOtaResponse(OTAResult::UPDATE_ERROR, Update.getError());
- }
- }
-}
-
-} \ No newline at end of file
diff --git a/platformio/relayctl/src/mqtt.h b/platformio/relayctl/src/mqtt.h
deleted file mode 100644
index 74d637a..0000000
--- a/platformio/relayctl/src/mqtt.h
+++ /dev/null
@@ -1,107 +0,0 @@
-#ifndef HOMEKIT_RELAYCTL_MQTT_H
-#define HOMEKIT_RELAYCTL_MQTT_H
-
-#include <ESP8266WiFi.h>
-#include <espMqttClient.h>
-#include <Ticker.h>
-
-#include <homekit/stopwatch.h>
-
-namespace homekit::mqtt {
-
-enum class OTAResult: uint8_t {
- OK = 0,
- UPDATE_ERROR = 1,
- WRITE_ERROR = 2,
-};
-
-struct OTAStatus {
- uint16_t dataPacketId;
- uint16_t publishResultPacketId;
- bool finished;
- bool readyToRestart;
- size_t written;
-
- OTAStatus()
- : dataPacketId(0)
- , publishResultPacketId(0)
- , finished(false)
- , readyToRestart(false)
- , written(0)
- {}
-
- inline void clean() {
- dataPacketId = 0;
- publishResultPacketId = 0;
- finished = false;
- readyToRestart = false;
- written = 0;
- }
-
- inline bool started() const {
- return dataPacketId != 0;
- }
-};
-
-class MQTT {
-private:
- String homeId;
- WiFiClientSecure httpsSecureClient;
- espMqttClientSecure client;
- Ticker reconnectTimer;
- Ticker restartTimer;
-
- void handleRelayPowerPayload(const uint8_t* payload, uint32_t length);
- void handleAdminOtaPayload(uint16_t packetId, const uint8_t* payload, size_t length, size_t index, size_t total);
-
- uint16_t publish(const String& topic, uint8_t* payload, size_t length);
- uint16_t subscribe(const String& topic, uint8_t qos = 0);
- void sendInitialDiagnostics();
- uint16_t sendOtaResponse(OTAResult status, uint8_t error_code = 0);
-
-public:
- StopWatch diagnosticsStopWatch;
- OTAStatus ota;
-
- MQTT();
- void connect();
- void disconnect();
- void reconnect();
- void loop();
- void sendDiagnostics();
-};
-
-struct DiagnosticsFlags {
- uint8_t state: 1;
- uint8_t config_changed_value_present: 1;
- uint8_t config_changed: 1;
- uint8_t reserved: 5;
-} __attribute__((packed));
-
-struct InitialDiagnosticsPayload {
- uint32_t ip;
- uint8_t fw_version;
- int8_t rssi;
- uint32_t free_heap;
- DiagnosticsFlags flags;
-} __attribute__((packed));
-
-struct DiagnosticsPayload {
- int8_t rssi;
- uint32_t free_heap;
- DiagnosticsFlags flags;
-} __attribute__((packed));
-
-struct PowerPayload {
- char secret[12];
- uint8_t state;
-} __attribute__((packed));
-
-struct OTAResponse {
- OTAResult status;
- uint8_t error_code;
-} __attribute__((packed));
-
-}
-
-#endif //HOMEKIT_RELAYCTL_MQTT_H \ No newline at end of file