From 820aba5fd7e7c66eded4b8cd8b80c24ffc404694 Mon Sep 17 00:00:00 2001 From: Maxim Slipenko Date: Sun, 5 Jun 2022 10:14:23 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BD=D0=B0=D1=87=D0=B8=D0=BD=D0=B0=D0=B5?= =?UTF-8?q?=D1=82=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=B2=D0=B0=D1=82=D1=8C?= =?UTF-8?q?=20=D0=B1=D0=B8=D0=B1=D0=BB=D0=B8=D0=BE=D1=82=D0=B5=D0=BA=D1=83?= =?UTF-8?q?=20HA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/DHTSensor.h | 3 + include/IRLight.h | 37 +++++++++ .../HAControllableDevice.cpp | 23 ++++++ .../HAControllableDevice.hpp | 18 ++++ lib/HomeAssistantDevices/HADevice.cpp | 40 +++++++++ lib/HomeAssistantDevices/HADevice.hpp | 36 ++++++++ lib/HomeAssistantDevices/HALight.cpp | 55 +++++++++++++ lib/HomeAssistantDevices/HALight.hpp | 21 +++++ .../HomeAssistantDevices.hpp | 3 + platformio.ini | 2 +- src/DHTSensor.cpp | 0 src/IRLight.cpp | 27 ++++++ src/IRLightController.cpp | 17 ++++ src/IRLightController.hpp | 16 ++++ src/main.cpp | 82 +++++++++++++++---- src/utils.cpp | 4 +- 16 files changed, 363 insertions(+), 21 deletions(-) create mode 100644 include/DHTSensor.h create mode 100644 include/IRLight.h create mode 100644 lib/HomeAssistantDevices/HAControllableDevice.cpp create mode 100644 lib/HomeAssistantDevices/HAControllableDevice.hpp create mode 100644 lib/HomeAssistantDevices/HADevice.cpp create mode 100644 lib/HomeAssistantDevices/HADevice.hpp create mode 100644 lib/HomeAssistantDevices/HALight.cpp create mode 100644 lib/HomeAssistantDevices/HALight.hpp create mode 100644 lib/HomeAssistantDevices/HomeAssistantDevices.hpp create mode 100644 src/DHTSensor.cpp create mode 100644 src/IRLight.cpp create mode 100644 src/IRLightController.cpp create mode 100644 src/IRLightController.hpp diff --git a/include/DHTSensor.h b/include/DHTSensor.h new file mode 100644 index 0000000..ddb38ba --- /dev/null +++ b/include/DHTSensor.h @@ -0,0 +1,3 @@ +#pragma once + +class DHTSensor {} \ No newline at end of file diff --git a/include/IRLight.h b/include/IRLight.h new file mode 100644 index 0000000..107196b --- /dev/null +++ b/include/IRLight.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +class IRLight { +private: + bool state; + IRsend *irsend; + + const uint16_t rawDataOn[67] = { + 8980, 4420, 580, 520, 630, 470, 630, 520, 580, 520, 630, 470, + 630, 520, 580, 520, 630, 1570, 630, 1620, 580, 1620, 630, 1620, + 580, 1570, 680, 1570, 630, 1570, 630, 1570, 630, 520, 630, 1570, + 630, 470, 630, 520, 630, 470, 630, 470, 630, 470, 680, 470, + 630, 470, 630, 520, 630, 1570, 630, 1570, 630, 1620, 630, 1570, + 630, 1570, 630, 1570, 680, 1570, 630}; + + const uint16_t rawDataOff[67] = { + 8980, 4370, 680, 420, 730, 420, 680, 420, 680, 420, 730, 420, + 680, 420, 680, 420, 730, 1470, 730, 1520, 680, 1520, 730, 1470, + 730, 1470, 730, 1520, 680, 1520, 730, 1470, 730, 420, 680, 470, + 680, 1470, 730, 420, 680, 1520, 730, 1470, 730, 420, 680, 420, + 680, 470, 580, 1620, 630, 470, 630, 1570, 630, 520, 580, 520, + 630, 1570, 680, 1520, 730, 1520, 680}; + +public: + IRLight(int pin); + IRLight(IRsend *irsend); + void on(); + void off(); + void begin(); + + bool getState(); + + ~IRLight(); +}; \ No newline at end of file diff --git a/lib/HomeAssistantDevices/HAControllableDevice.cpp b/lib/HomeAssistantDevices/HAControllableDevice.cpp new file mode 100644 index 0000000..83b98e5 --- /dev/null +++ b/lib/HomeAssistantDevices/HAControllableDevice.cpp @@ -0,0 +1,23 @@ +#include + +HAControllableDevice::HAControllableDevice(PubSubClient &client, + const char *device_type, + const char *name, + const char *unique_id) + : HADevice(client, device_type, name, unique_id) { + setupCommandTopic(); +} + +void HAControllableDevice::setupCommandTopic() { + command_topic_size = strlen(device_topic) + 4 + 1; + command_topic = (char *)malloc(command_topic_size); + + strcpy(command_topic, device_topic); + strcat(command_topic, "/cmd"); +} + +void HAControllableDevice::subscribeToCommandTopic() { + Serial.print("Subscribe to "); + Serial.println(command_topic); + client.subscribe(command_topic); +}; \ No newline at end of file diff --git a/lib/HomeAssistantDevices/HAControllableDevice.hpp b/lib/HomeAssistantDevices/HAControllableDevice.hpp new file mode 100644 index 0000000..c656fe5 --- /dev/null +++ b/lib/HomeAssistantDevices/HAControllableDevice.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +class HAControllableDevice : public HADevice { +protected: + char *command_topic; + unsigned int command_topic_size; + + void setupCommandTopic(); + +public: + HAControllableDevice(PubSubClient &client, const char *device_type, + const char *name, const char *unique_id); + + void subscribeToCommandTopic(); + virtual void handle(char *topic, byte *payload, unsigned int length) = 0; +}; \ No newline at end of file diff --git a/lib/HomeAssistantDevices/HADevice.cpp b/lib/HomeAssistantDevices/HADevice.cpp new file mode 100644 index 0000000..c543e9b --- /dev/null +++ b/lib/HomeAssistantDevices/HADevice.cpp @@ -0,0 +1,40 @@ +#include + +HADevice::HADevice(PubSubClient &client, const char *device_type, + const char *name, const char *unique_id) + : client(client), device_type(device_type), name(name), + unique_id(unique_id) { + buffer = (char *)malloc(MQTT_MAX_PACKET_SIZE); + + setupDeviceTopic(); + setupConfigTopic(); + setupStateTopic(); +} + +void HADevice::setupDeviceTopic() { + device_topic_size = + strlen(base_topic) + 1 + strlen(device_type) + 1 + strlen(unique_id) + 1; + device_topic = (char *)malloc(device_topic_size); + + strcpy(device_topic, base_topic); + strcat(device_topic, "/"); + strcat(device_topic, device_type); + strcat(device_topic, "/"); + strcat(device_topic, unique_id); +} + +void HADevice::setupConfigTopic() { + config_topic_size = strlen(device_topic) + 7 + 1; + config_topic = (char *)malloc(config_topic_size); + + strcpy(config_topic, device_topic); + strcat(config_topic, "/config"); +} + +void HADevice::setupStateTopic() { + state_topic_size = strlen(device_topic) + 6 + 1; + state_topic = (char *)malloc(state_topic_size); + + strcpy(state_topic, device_topic); + strcat(state_topic, "/state"); +} \ No newline at end of file diff --git a/lib/HomeAssistantDevices/HADevice.hpp b/lib/HomeAssistantDevices/HADevice.hpp new file mode 100644 index 0000000..d10c023 --- /dev/null +++ b/lib/HomeAssistantDevices/HADevice.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +class HADevice { +protected: + PubSubClient &client; + const char *name; + const char *unique_id; + const char *device_type; + + static constexpr const char *base_topic = "homeassistant"; + + char *device_topic = nullptr; + unsigned int device_topic_size = 0; + + char *config_topic = nullptr; + unsigned int config_topic_size = 0; + + char *state_topic = nullptr; + unsigned int state_topic_size = 0; + + char *buffer; + unsigned int buffer_size = MQTT_MAX_PACKET_SIZE; + + void setupDeviceTopic(); + void setupConfigTopic(); + void setupStateTopic(); + +public: + HADevice(PubSubClient &client, const char *device_type, const char *name, + const char *unique_id); + virtual void sendConfig() = 0; + virtual void sendState() = 0; +}; \ No newline at end of file diff --git a/lib/HomeAssistantDevices/HALight.cpp b/lib/HomeAssistantDevices/HALight.cpp new file mode 100644 index 0000000..8c6ce3c --- /dev/null +++ b/lib/HomeAssistantDevices/HALight.cpp @@ -0,0 +1,55 @@ +#include + +HALight::HALight(PubSubClient &client, const char *name, const char *unique_id, + HALightController &baseLight) + : HAControllableDevice(client, "light", name, unique_id), + light(&baseLight) {} + +void HALight::sendState() { + StaticJsonDocument<256> doc; + + buffer[0] = '\0'; + + if (light->getState()) { + doc["state"] = "ON"; + } else { + doc["state"] = "OFF"; + } + + serializeJson(doc, buffer, buffer_size); + + Serial.println(state_topic); + client.publish(state_topic, buffer, true); +} + +void HALight::sendConfig() { + StaticJsonDocument<256> doc; + + buffer[0] = '\0'; + + doc["~"] = device_topic; + doc["name"] = name; + doc["unique_id"] = unique_id; + doc["cmd_t"] = "~/cmd"; + doc["stat_t"] = "~/state"; + doc["schema"] = "json"; + + serializeJson(doc, buffer, buffer_size); + client.publish(config_topic, buffer, true); +} + +void HALight::handle(char *topic, byte *payload, unsigned int length) { + if (strcmp(topic, command_topic) != 0) + return; + + StaticJsonDocument<256> doc; + deserializeJson(doc, (const byte *)payload, length); + + if (strcmp(doc["state"], "ON") == 0) { + light->setState(true); + } else { + light->setState(false); + } + + sendState(); +} \ No newline at end of file diff --git a/lib/HomeAssistantDevices/HALight.hpp b/lib/HomeAssistantDevices/HALight.hpp new file mode 100644 index 0000000..49b4112 --- /dev/null +++ b/lib/HomeAssistantDevices/HALight.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +class HALightController { +public: + virtual void setState(bool state) = 0; + virtual bool getState() = 0; +}; + +class HALight : public HAControllableDevice { +private: + HALightController *light; + +public: + HALight(PubSubClient &client, const char *name, const char *unique_id, + HALightController &baseLight); + void handle(char *topic, byte *payload, unsigned int length); + void sendConfig(); + void sendState(); +}; \ No newline at end of file diff --git a/lib/HomeAssistantDevices/HomeAssistantDevices.hpp b/lib/HomeAssistantDevices/HomeAssistantDevices.hpp new file mode 100644 index 0000000..d1142b5 --- /dev/null +++ b/lib/HomeAssistantDevices/HomeAssistantDevices.hpp @@ -0,0 +1,3 @@ +#pragma once + +#include \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index b360d33..1a8634d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -14,8 +14,8 @@ board = d1_mini_pro framework = arduino lib_deps = adafruit/DHT sensor library@^1.4.3 - jfturcot/SimpleTimer@0.0.0-alpha+sha.b30890b8f7 adafruit/Adafruit Unified Sensor@^1.1.5 knolleary/PubSubClient@^2.8 bblanchon/ArduinoJson@^6.19.4 + crankyoldgit/IRremoteESP8266@^2.8.2 monitor_speed = 115200 diff --git a/src/DHTSensor.cpp b/src/DHTSensor.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/IRLight.cpp b/src/IRLight.cpp new file mode 100644 index 0000000..9dd1f43 --- /dev/null +++ b/src/IRLight.cpp @@ -0,0 +1,27 @@ +#include + +IRLight::IRLight(int pin) { + irsend = new IRsend(pin); + state = false; +} + +IRLight::IRLight(IRsend *irsend) { + irsend = irsend; + state = false; +} + +void IRLight::on() { + state = true; + irsend->sendRaw(rawDataOn, 67, 38); +} + +void IRLight::off() { + state = false; + irsend->sendRaw(rawDataOff, 67, 38); +} + +void IRLight::begin() { irsend->begin(); } + +bool IRLight::getState() { return state; } + +IRLight::~IRLight() { delete irsend; } \ No newline at end of file diff --git a/src/IRLightController.cpp b/src/IRLightController.cpp new file mode 100644 index 0000000..8cf8a1c --- /dev/null +++ b/src/IRLightController.cpp @@ -0,0 +1,17 @@ +#include + +IRLightController::IRLightController(IRLight &irlight) : irlight(irlight) {} + +void IRLightController::begin() { irlight.begin(); }; + +void IRLightController::setState(bool state) { + if (state) { + irlight.on(); + } else { + irlight.off(); + } +} + +bool IRLightController::getState() { return irlight.getState(); } + +IRLightController::~IRLightController() {} \ No newline at end of file diff --git a/src/IRLightController.hpp b/src/IRLightController.hpp new file mode 100644 index 0000000..4d846d0 --- /dev/null +++ b/src/IRLightController.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +class IRLightController : public HALightController { +private: + IRLight &irlight; + +public: + IRLightController(IRLight &irlight); + void begin(); + void setState(bool state); + bool getState(); + ~IRLightController(); +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index cba1c62..a8fa2cf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,13 @@ -#include #include #include +#include +#include +#include #include -#include + +#include +#include + #include #include @@ -10,9 +15,14 @@ WiFiClient espClient; PubSubClient client(espClient); DHT dht(DHTPIN, DHTTYPE); +IRLight irlight(4); + +IRLightController light(irlight); +HALight halight(client, "light", "l-1", light); + void setup_wifi(); void reconnect(); -void mqtt_callback(char* topic, byte* payload, unsigned int length); +void mqtt_callback(char *topic, byte *payload, unsigned int length); void mqtt_publish_config(); void mqtt_publish_state(); @@ -33,17 +43,21 @@ void setup() { if (!client.connected()) { reconnect(); } - client.subscribe("homeassistant/status"); - - mqtt_publish_config(); dht.begin(); + light.begin(); + + client.subscribe("homeassistant/status"); + halight.subscribeToCommandTopic(); + + mqtt_publish_config(); if (WiFi.status() == WL_CONNECTED) { temp = dht.readTemperature(); hum = dht.readHumidity(); mqtt_publish_state(); + halight.sendState(); } } @@ -53,21 +67,34 @@ void loop() { } client.loop(); - temp = round2(dht.readTemperature()); - hum = round2(dht.readHumidity()); - mqtt_publish_state(); + long now = millis(); - delay(60000); + if (now - lastMsg > 60000) { + lastMsg = now; + temp = round2(dht.readTemperature()); + hum = round2(dht.readHumidity()); + mqtt_publish_state(); + } + + delay(100); } -void mqtt_callback(char* topic, byte* payload, unsigned int length) { +void mqtt_callback(char *topic, byte *payload, unsigned int length) { + + Serial.print("Message arrived ["); + Serial.print(topic); + Serial.print("] "); + Serial.println(); + if (strcmp(topic, "homeassistant/status") == 0) { payload[length] = '\0'; - if (strcmp((char*)payload, "online") == 0) { + if (strcmp((char *)payload, "online") == 0) { Serial.println("Home Assistant is online"); mqtt_publish_config(); } } + + halight.handle(topic, payload, length); } void setup_wifi() { @@ -110,33 +137,37 @@ void reconnect() { } void mqtt_publish_config() { + /* StaticJsonDocument<256> doc; char buffer[256]; doc["device_class"] = "temperature"; doc["name"] = "Temperature"; doc["unit_of_meas"] = "°C"; - doc["state_topic"] = HOMEASSISTANT_TOPIC"/state"; + doc["stat_t"] = HOMEASSISTANT_TOPIC "/state"; doc["value_template"] = "{{value_json.temperature}}"; doc["unique_id"] = "t"; serializeJson(doc, buffer); - client.publish(HOMEASSISTANT_TOPIC"/t/config", buffer, true); + client.publish(HOMEASSISTANT_TOPIC "/t/config", buffer, true); doc["device_class"] = "humidity"; doc["name"] = "Humidity"; doc["unit_of_meas"] = "%"; - doc["state_topic"] = HOMEASSISTANT_TOPIC"/state"; + doc["stat_t"] = HOMEASSISTANT_TOPIC "/state"; doc["value_template"] = "{{value_json.humidity}}"; doc["unique_id"] = "h"; serializeJson(doc, buffer); - client.publish(HOMEASSISTANT_TOPIC"/h/config", buffer, true); + client.publish(HOMEASSISTANT_TOPIC "/h/config", buffer, true); + */ + halight.sendConfig(); } void mqtt_publish_state() { + /* Serial.println("===== Sending Data ====="); StaticJsonDocument<256> doc; char buffer[256]; @@ -149,5 +180,22 @@ void mqtt_publish_state() { serializeJson(doc, buffer); - client.publish(HOMEASSISTANT_TOPIC"/state", buffer, true); + client.publish(HOMEASSISTANT_TOPIC "/state", buffer, true); + */ + // Loop until we're reconnected + while (!client.connected()) { + Serial.print("Attempting MQTT connection..."); + // Attempt to connect + // If you do not want to use a username and password, change next line to + // if (client.connect("ESP8266Client")) { + if (client.connect("ESP8266Client", MQTT_LOGIN, MQTT_PASS)) { + Serial.println("connected"); + } else { + Serial.print("failed, rc="); + Serial.print(client.state()); + Serial.println(" try again in 5 seconds"); + // Wait 5 seconds before retrying + delay(5000); + } + } } \ No newline at end of file diff --git a/src/utils.cpp b/src/utils.cpp index a8712a2..7dbe18a 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -1,3 +1 @@ -double round2 (double value) { - return (int)(value * 100 + 0.5) / 100.0; -} \ No newline at end of file +double round2(double value) { return (int)(value * 100 + 0.5) / 100.0; } \ No newline at end of file