mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2026-10-31 22:02:17 +01:00
refactor: move the transports into entryPoints and outputs
The HTTP servers and MQTT command handling go to src/entryPoints/, the socket emitter and the MQTT and socket output of shades, groups and rooms to src/outputs/. GLOSSARY.md names what they become once split: WebUiRoutes, ApiRoutes and MqttCommands in, MqttPublisher and SocketEmitter out. Only files and include paths change. esp32devdbg flash goes from 1,546,405 to 1,546,257 bytes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
7396786b8c
commit
e624a4177d
16 changed files with 42 additions and 15 deletions
357
src/entryPoints/MQTT.cpp
Normal file
357
src/entryPoints/MQTT.cpp
Normal file
|
|
@ -0,0 +1,357 @@
|
|||
#include <WiFi.h>
|
||||
#include <PubSubClient.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <esp_task_wdt.h>
|
||||
#include "esp_log.h"
|
||||
#include "ConfigSettings.h"
|
||||
#include "MQTT.h"
|
||||
#include "Somfy.h"
|
||||
#include "ESPNetwork.h"
|
||||
#include "Utils.h"
|
||||
|
||||
static const char *TAG = "MQTT";
|
||||
|
||||
WiFiClient tcpClient;
|
||||
PubSubClient mqttClient(tcpClient);
|
||||
|
||||
#define MQTT_MAX_RESPONSE 2048
|
||||
static char g_content[MQTT_MAX_RESPONSE];
|
||||
|
||||
extern ConfigSettings settings;
|
||||
extern SomfyShadeController somfy;
|
||||
extern ESPNetwork net;
|
||||
extern rebootDelay_t rebootDelay;
|
||||
|
||||
|
||||
bool MQTTClass::begin() {
|
||||
this->suspended = false;
|
||||
return true;
|
||||
}
|
||||
bool MQTTClass::end() {
|
||||
this->suspended = true;
|
||||
this->disconnect();
|
||||
return true;
|
||||
}
|
||||
void MQTTClass::reset() {
|
||||
this->disconnect();
|
||||
this->lastConnect = 0;
|
||||
this->connect();
|
||||
}
|
||||
bool MQTTClass::loop() {
|
||||
if(settings.MQTT.enabled && !rebootDelay.reboot && !this->suspended && !mqttClient.connected()) {
|
||||
esp_task_wdt_reset();
|
||||
if(!this->connected() && net.connected()) this->connect();
|
||||
}
|
||||
esp_task_wdt_reset();
|
||||
if(settings.MQTT.enabled) mqttClient.loop();
|
||||
return true;
|
||||
}
|
||||
void MQTTClass::receive(const char *topic, byte*payload, uint32_t length) {
|
||||
esp_task_wdt_reset(); // Make sure we do not reboot here.
|
||||
ESP_LOGD(TAG, "MQTT Topic:%s payload:%.*s", topic, (int)length, (const char *)payload);
|
||||
|
||||
// We need to start at the last slash in the data
|
||||
uint8_t len = strlen(topic);
|
||||
|
||||
uint8_t slashes = 0;
|
||||
uint16_t ndx = strlen(topic) - 1;
|
||||
while(ndx > 0) {
|
||||
if(topic[ndx] == '/') slashes++;
|
||||
if(slashes == 4) break;
|
||||
ndx--;
|
||||
}
|
||||
char entityId[4];
|
||||
char command[32];
|
||||
char entityType[7];
|
||||
char value[10];
|
||||
memset(command, 0x00, sizeof(command));
|
||||
memset(entityId, 0x00, sizeof(entityId));
|
||||
memset(entityType, 0x00, sizeof(entityType));
|
||||
memset(value, 0x00, sizeof(value));
|
||||
uint8_t i = 0;
|
||||
while(topic[ndx] == '/' && ndx < len) ndx++;
|
||||
while(ndx < len) {
|
||||
if(topic[ndx] != '/' && i < sizeof(entityType))
|
||||
entityType[i++] = topic[ndx];
|
||||
ndx++;
|
||||
if(topic[ndx] == '/') break;
|
||||
}
|
||||
i = 0;
|
||||
while(topic[ndx] == '/' && ndx < len) ndx++;
|
||||
while(ndx < len) {
|
||||
if(topic[ndx] != '/' && i < sizeof(entityId))
|
||||
entityId[i++] = topic[ndx];
|
||||
ndx++;
|
||||
if(topic[ndx] == '/') break;
|
||||
}
|
||||
i = 0;
|
||||
while(topic[ndx] == '/' && ndx < len) ndx++;
|
||||
while(ndx < len) {
|
||||
if(topic[ndx] != '/' && i < sizeof(command))
|
||||
command[i++] = topic[ndx];
|
||||
ndx++;
|
||||
if(topic[ndx] == '/') break;
|
||||
}
|
||||
for(uint8_t j = 0; j < length && j < sizeof(value); j++)
|
||||
value[j] = payload[j];
|
||||
|
||||
ESP_LOGD(TAG, "MQTT type:[%s] command:[%s] entityId:%s value:%s", entityType, command, entityId, value);
|
||||
if(strncmp(entityType, "shades", sizeof(entityType)) == 0) {
|
||||
SomfyShade* shade = somfy.getShadeById(atoi(entityId));
|
||||
if (shade) {
|
||||
int val = atoi(value);
|
||||
if(strncmp(command, "target", sizeof(command)) == 0) {
|
||||
if(val >= 0 && val <= 100) {
|
||||
ESP_LOGI(TAG, "MQTT shade %s target=%d", entityId, val);
|
||||
shade->moveToTarget(shade->transformPosition(atoi(value)));
|
||||
}
|
||||
}
|
||||
if(strncmp(command, "tiltTarget", sizeof(command)) == 0) {
|
||||
if(val >= 0 && val <= 100)
|
||||
shade->moveToTiltTarget(atoi(value));
|
||||
}
|
||||
else if(strncmp(command, "direction", sizeof(command)) == 0) {
|
||||
if(val < 0)
|
||||
shade->sendCommand(somfy_commands::Up);
|
||||
else if(val > 0)
|
||||
shade->sendCommand(somfy_commands::Down);
|
||||
else
|
||||
shade->sendCommand(somfy_commands::My);
|
||||
}
|
||||
else if(strncmp(command, "mypos", sizeof(command)) == 0) {
|
||||
if(val >= 0 && val <= 100)
|
||||
shade->setMyPosition(val);
|
||||
}
|
||||
else if(strncmp(command, "myTiltPos", sizeof(command)) == 0) {
|
||||
if(val >= 0 && val <= 100)
|
||||
shade->setMyPosition(shade->myPos, val);
|
||||
}
|
||||
else if(strncmp(command, "sunFlag", sizeof(command)) == 0) {
|
||||
shade->sendCommand(sunFlagCommand(val > 0));
|
||||
}
|
||||
else if(strncmp(command, "position", sizeof(command)) == 0) {
|
||||
if(val >= 0 && val <= 100) {
|
||||
shade->target = shade->currentPos = shade->transformPosition((float)val);
|
||||
shade->emitState();
|
||||
}
|
||||
}
|
||||
else if(strncmp(command, "tiltPosition", sizeof(command)) == 0) {
|
||||
if(val >= 0 && val <= 100) {
|
||||
shade->tiltTarget = shade->currentTiltPos = (float)val;
|
||||
shade->emitState();
|
||||
}
|
||||
}
|
||||
else if(strncmp(command, "sunny", sizeof(command)) == 0) {
|
||||
if(val >= 0) shade->sendSensorCommand(-1, val, shade->repeats);
|
||||
}
|
||||
else if(strncmp(command, "windy", sizeof(command)) == 0) {
|
||||
if(val >= 0) shade->sendSensorCommand(val, -1, shade->repeats);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(strncmp(entityType, "groups", sizeof(entityType)) == 0) {
|
||||
SomfyGroup* group = somfy.getGroupById(atoi(entityId));
|
||||
if (group) {
|
||||
int val = atoi(value);
|
||||
ESP_LOGI(TAG, "MQTT group %s command=%s value=%d", entityId, command, val);
|
||||
if(strncmp(command, "direction", sizeof(command)) == 0) {
|
||||
if(val < 0)
|
||||
group->sendCommand(somfy_commands::Up);
|
||||
else if(val > 0)
|
||||
group->sendCommand(somfy_commands::Down);
|
||||
else
|
||||
group->sendCommand(somfy_commands::My);
|
||||
}
|
||||
else if(strncmp(command, "sunFlag", sizeof(command)) == 0) {
|
||||
group->sendCommand(sunFlagCommand(val > 0));
|
||||
}
|
||||
else if(strncmp(command, "sunny", sizeof(command)) == 0) {
|
||||
if(val >= 0) group->sendSensorCommand(-1, val, group->repeats);
|
||||
}
|
||||
else if(strncmp(command, "windy", sizeof(command)) == 0) {
|
||||
if(val >= 0) group->sendSensorCommand(val, -1, group->repeats);
|
||||
}
|
||||
}
|
||||
}
|
||||
esp_task_wdt_reset(); // Make sure we do not reboot here.
|
||||
}
|
||||
bool MQTTClass::connect() {
|
||||
esp_task_wdt_reset(); // Make sure we do not reboot here.
|
||||
if(mqttClient.connected()) {
|
||||
if(!settings.MQTT.enabled || this->suspended)
|
||||
return this->disconnect();
|
||||
else
|
||||
return true;
|
||||
}
|
||||
if(settings.MQTT.enabled && !this->suspended) {
|
||||
if(this->lastConnect + 10000 > millis()) return false;
|
||||
uint64_t mac = ESP.getEfuseMac();
|
||||
snprintf(this->clientId, sizeof(this->clientId), "client-%08x%08x", (uint32_t)((mac >> 32) & 0xFFFFFFFF), (uint32_t)(mac & 0xFFFFFFFF));
|
||||
if(strlen(settings.MQTT.protocol) > 0 && strlen(settings.MQTT.hostname) > 0) {
|
||||
mqttClient.setServer(settings.MQTT.hostname, settings.MQTT.port);
|
||||
char lwtTopic[128] = "status";
|
||||
if(strlen(settings.MQTT.rootTopic) > 0)
|
||||
snprintf(lwtTopic, sizeof(lwtTopic), "%s/status", settings.MQTT.rootTopic);
|
||||
esp_task_wdt_reset();
|
||||
if(mqttClient.connect(this->clientId, settings.MQTT.username, settings.MQTT.password, lwtTopic, 0, true, "offline")) {
|
||||
ESP_LOGI(TAG, "Successfully connected MQTT client %s", this->clientId);
|
||||
this->publish("status", "online", true);
|
||||
this->publish("ipAddress", settings.IP.ip.toString().c_str(), true);
|
||||
this->publish("host", settings.hostname, true);
|
||||
this->publish("firmware", settings.fwVersion.name, true);
|
||||
this->publish("serverId", settings.serverId, true);
|
||||
this->publish("mac", net.mac.c_str());
|
||||
somfy.publish();
|
||||
this->subscribe("shades/+/target/set");
|
||||
this->subscribe("shades/+/tiltTarget/set");
|
||||
this->subscribe("shades/+/direction/set");
|
||||
this->subscribe("shades/+/mypos/set");
|
||||
this->subscribe("shades/+/myTiltPos/set");
|
||||
this->subscribe("shades/+/sunFlag/set");
|
||||
this->subscribe("shades/+/sunny/set");
|
||||
this->subscribe("shades/+/windy/set");
|
||||
this->subscribe("shades/+/position/set");
|
||||
this->subscribe("shades/+/tiltPosition/set");
|
||||
this->subscribe("groups/+/direction/set");
|
||||
this->subscribe("groups/+/sunFlag/set");
|
||||
this->subscribe("groups/+/sunny/set");
|
||||
this->subscribe("groups/+/windy/set");
|
||||
mqttClient.setCallback(MQTTClass::receive);
|
||||
ESP_LOGI(TAG, "MQTT Startup Completed");
|
||||
esp_task_wdt_reset();
|
||||
this->lastConnect = millis();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
ESP_LOGE(TAG, "MQTT Connection failed for: %d", mqttClient.state());
|
||||
this->lastConnect = millis();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool MQTTClass::disconnect() {
|
||||
if(mqttClient.connected()) {
|
||||
this->unsubscribe("shades/+/target/set");
|
||||
this->unsubscribe("shades/+/direction/set");
|
||||
this->unsubscribe("shades/+/tiltTarget/set");
|
||||
this->unsubscribe("shades/+/mypos/set");
|
||||
this->unsubscribe("shades/+/myTiltPos/set");
|
||||
this->unsubscribe("shades/+/sunFlag/set");
|
||||
this->unsubscribe("groups/+/direction/set");
|
||||
this->unsubscribe("shades/+/sunny/set");
|
||||
this->unsubscribe("shades/+/windy/set");
|
||||
this->unsubscribe("shades/+/position/set");
|
||||
this->unsubscribe("shades/+/tiltPosition/set");
|
||||
this->unsubscribe("groups/+/direction/set");
|
||||
this->unsubscribe("groups/+/sunFlag/set");
|
||||
this->unsubscribe("groups/+/sunny/set");
|
||||
this->unsubscribe("groups/+/windy/set");
|
||||
mqttClient.disconnect();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool MQTTClass::unsubscribe(const char *topic) {
|
||||
if(mqttClient.connected()) {
|
||||
char top[128];
|
||||
if(strlen(settings.MQTT.rootTopic) > 0)
|
||||
snprintf(top, sizeof(top), "%s/%s", settings.MQTT.rootTopic, topic);
|
||||
else
|
||||
strlcpy(top, topic, sizeof(top));
|
||||
ESP_LOGD(TAG, "MQTT Unsubscribed from:%s", top);
|
||||
return mqttClient.unsubscribe(top);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool MQTTClass::subscribe(const char *topic) {
|
||||
if(mqttClient.connected()) {
|
||||
esp_task_wdt_reset(); // Make sure we do not reboot here.
|
||||
char top[128];
|
||||
if(strlen(settings.MQTT.rootTopic) > 0)
|
||||
snprintf(top, sizeof(top), "%s/%s", settings.MQTT.rootTopic, topic);
|
||||
else
|
||||
strlcpy(top, topic, sizeof(top));
|
||||
ESP_LOGD(TAG, "MQTT Subscribed to:%s", top);
|
||||
return mqttClient.subscribe(top);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool MQTTClass::publish(const char *topic, const char *payload, bool retain) {
|
||||
if(mqttClient.connected()) {
|
||||
char top[128];
|
||||
if(strlen(settings.MQTT.rootTopic) > 0)
|
||||
snprintf(top, sizeof(top), "%s/%s", settings.MQTT.rootTopic, topic);
|
||||
else
|
||||
strlcpy(top, topic, sizeof(top));
|
||||
esp_task_wdt_reset(); // Make sure we do not reboot here.
|
||||
mqttClient.publish(top, payload, retain);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool MQTTClass::publish(const char *topic, uint32_t val, bool retain) {
|
||||
snprintf(g_content, sizeof(g_content), "%u", val);
|
||||
return this->publish(topic, g_content, retain);
|
||||
}
|
||||
bool MQTTClass::unpublish(const char *topic) {
|
||||
if(mqttClient.connected()) {
|
||||
char top[128];
|
||||
if(strlen(settings.MQTT.rootTopic) > 0)
|
||||
snprintf(top, sizeof(top), "%s/%s", settings.MQTT.rootTopic, topic);
|
||||
else
|
||||
strlcpy(top, topic, sizeof(top));
|
||||
esp_task_wdt_reset(); // Make sure we do not reboot here.
|
||||
mqttClient.publish(top, (const uint8_t *)"", 0, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
// mqttClient.beginPublish(topic, 0, true);
|
||||
// mqttClient.endPublish();
|
||||
}
|
||||
|
||||
bool MQTTClass::publishBuffer(const char *topic, uint8_t *data, uint16_t len, bool retain) {
|
||||
size_t res;
|
||||
uint16_t offset = 0;
|
||||
uint16_t to_write = len;
|
||||
uint16_t buff_len;
|
||||
esp_task_wdt_reset(); // Make sure we do not reboot here.
|
||||
mqttClient.beginPublish(topic, len, retain);
|
||||
do {
|
||||
buff_len = to_write;
|
||||
if(buff_len > 128) buff_len = 128;
|
||||
res = mqttClient.write(data+offset, buff_len);
|
||||
offset += buff_len;
|
||||
to_write -= buff_len;
|
||||
} while(res == buff_len && to_write > 0);
|
||||
mqttClient.endPublish();
|
||||
return true;
|
||||
}
|
||||
bool MQTTClass::publishDisco(const char *topic, JsonObject &obj, bool retain) {
|
||||
serializeJson(obj, g_content, sizeof(g_content));
|
||||
this->publishBuffer(topic, (uint8_t *)g_content, strlen(g_content), retain);
|
||||
return true;
|
||||
}
|
||||
bool MQTTClass::publish(const char *topic, int8_t val, bool retain) {
|
||||
snprintf(g_content, sizeof(g_content), "%d", val);
|
||||
return this->publish(topic, g_content, retain);
|
||||
}
|
||||
bool MQTTClass::publish(const char *topic, uint8_t val, bool retain) {
|
||||
snprintf(g_content, sizeof(g_content), "%u", val);
|
||||
return this->publish(topic, g_content, retain);
|
||||
}
|
||||
bool MQTTClass::publish(const char *topic, uint16_t val, bool retain) {
|
||||
snprintf(g_content, sizeof(g_content), "%u", val);
|
||||
return this->publish(topic, g_content, retain);
|
||||
}
|
||||
bool MQTTClass::publish(const char *topic, bool val, bool retain) {
|
||||
snprintf(g_content, sizeof(g_content), "%s", val ? "true" : "false");
|
||||
return this->publish(topic, g_content, retain);
|
||||
}
|
||||
bool MQTTClass::connected() {
|
||||
if(settings.MQTT.enabled) return mqttClient.connected();
|
||||
return false;
|
||||
}
|
||||
31
src/entryPoints/MQTT.h
Normal file
31
src/entryPoints/MQTT.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef MQTT_H
|
||||
#define MQTT_H
|
||||
#include <Arduino.h>
|
||||
#include <PubSubClient.h>
|
||||
#include <ArduinoJson.h>
|
||||
class MQTTClass {
|
||||
public:
|
||||
uint64_t lastConnect = 0;
|
||||
bool suspended = false;
|
||||
char clientId[32] = {'\0'};
|
||||
bool begin();
|
||||
bool loop();
|
||||
bool end();
|
||||
bool connect();
|
||||
bool disconnect();
|
||||
bool connected();
|
||||
void reset();
|
||||
bool unpublish(const char *topic);
|
||||
bool publish(const char *topic, const char *payload, bool retain = false);
|
||||
bool publish(const char *topic, uint8_t val, bool retain = false);
|
||||
bool publish(const char *topic, int8_t val, bool retain = false);
|
||||
bool publish(const char *topic, uint32_t val, bool retain = false);
|
||||
bool publish(const char *topic, uint16_t val, bool retain = false);
|
||||
bool publish(const char *topic, bool val, bool retain = false);
|
||||
bool publishBuffer(const char *topic, uint8_t *data, uint16_t len, bool retain = false);
|
||||
bool publishDisco(const char *topic, JsonObject &obj, bool retain = false);
|
||||
bool subscribe(const char *topic);
|
||||
bool unsubscribe(const char *topic);
|
||||
static void receive(const char *topic, byte *payload, uint32_t length);
|
||||
};
|
||||
#endif
|
||||
2786
src/entryPoints/Web.cpp
Normal file
2786
src/entryPoints/Web.cpp
Normal file
File diff suppressed because it is too large
Load diff
47
src/entryPoints/Web.h
Normal file
47
src/entryPoints/Web.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#include <ESPAsyncWebServer.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include "Somfy.h"
|
||||
#include "WebCommand.h"
|
||||
#ifndef webserver_h
|
||||
#define webserver_h
|
||||
|
||||
class Web {
|
||||
public:
|
||||
bool uploadSuccess = false;
|
||||
void startup();
|
||||
void begin();
|
||||
void loop();
|
||||
// Auth helpers
|
||||
bool createAPIToken(const IPAddress ipAddress, char *token);
|
||||
bool createAPIToken(const char *payload, char *token);
|
||||
bool createAPIPinToken(const IPAddress ipAddress, const char *pin, char *token);
|
||||
bool createAPIPasswordToken(const IPAddress ipAddress, const char *username, const char *password, char *token);
|
||||
bool isAuthenticated(AsyncWebServerRequest *request, bool cfg = false);
|
||||
|
||||
// Async API handlers
|
||||
void handleDiscovery(AsyncWebServerRequest *request);
|
||||
void handleGetRooms(AsyncWebServerRequest *request);
|
||||
void handleGetShades(AsyncWebServerRequest *request);
|
||||
void handleGetGroups(AsyncWebServerRequest *request);
|
||||
void handleController(AsyncWebServerRequest *request);
|
||||
void handleRoom(AsyncWebServerRequest *request);
|
||||
void handleShade(AsyncWebServerRequest *request);
|
||||
void handleGroup(AsyncWebServerRequest *request);
|
||||
void handleLogin(AsyncWebServerRequest *request, JsonVariant &json);
|
||||
void handleShadeCommand(AsyncWebServerRequest *request, JsonVariant &json);
|
||||
void handleGroupCommand(AsyncWebServerRequest *request, JsonVariant &json);
|
||||
void handleTiltCommand(AsyncWebServerRequest *request, JsonVariant &json);
|
||||
void handleRepeatCommand(AsyncWebServerRequest *request, JsonVariant &json);
|
||||
void handleSetPositions(AsyncWebServerRequest *request, JsonVariant &json);
|
||||
void handleSetSensor(AsyncWebServerRequest *request, JsonVariant &json);
|
||||
void handleDownloadFirmware(AsyncWebServerRequest *request);
|
||||
void handleBackup(AsyncWebServerRequest *request);
|
||||
void handleReboot(AsyncWebServerRequest *request);
|
||||
void handleNotFound(AsyncWebServerRequest *request);
|
||||
bool queueCommand(const web_command_t &cmd);
|
||||
private:
|
||||
void processQueue();
|
||||
};
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue