Compare commits

...

10 Commits
master ... dev

26 changed files with 844 additions and 60 deletions

2
.gitignore vendored
View File

@ -4,4 +4,4 @@
.vscode/launch.json
.vscode/ipch
src/config.h
include/config.hpp

14
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,14 @@
{
"files.associations": {
"array": "cpp",
"deque": "cpp",
"list": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"string_view": "cpp",
"initializer_list": "cpp",
"ranges": "cpp"
},
"clang-tidy.checks": ["readability-*", "modernize-*", "bugprone-*"]
}

3
include/DHTSensor.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
class DHTSensor {}

52
include/IRLight.h Normal file
View File

@ -0,0 +1,52 @@
#pragma once
#include <ESP8266WiFi.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <HALightColorTemperatureBrightness.hpp>
class IRLight: public HALightColorTemperatureBrightnessController {
private:
bool state;
short brightness;
byte color_temp;
IRsend *irsend;
const uint32_t RDOn = irsend->encodeNEC(0x80, 0x1);
const uint32_t RDOff = irsend->encodeNEC(0x80, 0x1A);
const uint32_t RDButtonCenter = irsend->encodeNEC(0x80, 0x5);
const uint32_t RDBrightnessUp = irsend->encodeNEC(0x80, 0x12);
const uint32_t RDBrightnessDown = irsend->encodeNEC(0x80, 0x1E);
const uint32_t RDButtonA = irsend->encodeNEC(0x80, 0x7);
const uint32_t RDButtonB = irsend->encodeNEC(0x80, 0x8);
public:
explicit IRLight(int pin);
explicit IRLight(IRsend *irsend);
void setState(bool state);
bool getState();
void on();
void off();
void setBrightness(int brightness);
int getBrightness();
void setColorTemperature(int color);
int getColorTemperature();
void brightnessUp();
void brightnessDown();
int getBrightnessScale();
void begin();
~IRLight();
};

View File

@ -1,3 +1,7 @@
/*
* Copyright 2023 Maxim Slipenko
*/
#pragma once
// Настройки WiFi
#define WIFI_SSID "SSID_HERE"
#define WIFI_PASS "PASS_HERE"
@ -11,6 +15,4 @@
#define DHTPIN D4 // pin gpio 2 in sensor
#define DHTTYPE DHT22 // DHT 22 Change this if you have a DHT11
#define CLIENT_ID "CLIENT_ID_HERE"
#define HUMIDITY_TOPIC "sensor/humidity"
#define TEMPERATURE_TOPIC "sensor/temperature"
#define HOMEASSISTANT_TOPIC "homeassistant/sensor/sensorDht"

6
include/utils.hpp Normal file
View File

@ -0,0 +1,6 @@
#ifndef UTILS_H
#define UTILS_H
double round2(double value);
#endif

View File

@ -0,0 +1,23 @@
#include <HAControllableDevice.hpp>
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);
};

View File

@ -0,0 +1,18 @@
#pragma once
#include <HADevice.hpp>
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;
};

View File

@ -0,0 +1,40 @@
#include <HADevice.hpp>
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");
}

View File

@ -0,0 +1,36 @@
#pragma once
#include <ArduinoJson.h>
#include <PubSubClient.h>
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;
};

View File

@ -0,0 +1,83 @@
#include <HALight.hpp>
HALight::HALight(PubSubClient &client, const char *name, const char *unique_id)
: HAControllableDevice(client, "light", name, unique_id) {}
HALight::HALight(PubSubClient &client, const char *name, const char *unique_id,
HALightController &baseLight)
: HAControllableDevice(client, "light", name, unique_id),
light(&baseLight) {}
JSON_DOCUMENT_TYPE HALight::createConfigJSON() {
JSON_DOCUMENT_TYPE doc;
doc["~"] = device_topic;
doc["name"] = name;
doc["unique_id"] = unique_id;
doc["cmd_t"] = "~/cmd";
doc["stat_t"] = "~/state";
doc["schema"] = "json";
patchConfigJSON(doc);
return doc;
}
JSON_DOCUMENT_TYPE HALight::patchConfigJSON(JSON_DOCUMENT_TYPE &doc) {
return doc;
}
JSON_DOCUMENT_TYPE HALight::createStateJSON() {
JSON_DOCUMENT_TYPE doc;
if (light->getState()) {
doc["state"] = "ON";
} else {
doc["state"] = "OFF";
}
patchStateJSON(doc);
return doc;
}
JSON_DOCUMENT_TYPE HALight::patchStateJSON(JSON_DOCUMENT_TYPE &doc) {
return doc;
}
void HALight::innerHandler(const JSON_DOCUMENT_TYPE &doc) {
if (strcmp(doc["state"], "ON") == 0) {
light->setState(true);
} else {
light->setState(false);
}
}
void HALight::sendState() {
JSON_DOCUMENT_TYPE doc = createStateJSON();
buffer[0] = '\0';
serializeJson(doc, buffer, buffer_size);
Serial.println(state_topic);
client.publish(state_topic, buffer, true);
}
void HALight::sendConfig() {
JSON_DOCUMENT_TYPE doc = createConfigJSON();
buffer[0] = '\0';
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;
JSON_DOCUMENT_TYPE doc;
deserializeJson(doc, (const byte *)payload, length);
innerHandler(doc);
sendState();
}

View File

@ -0,0 +1,30 @@
#pragma once
#include <HAControllableDevice.hpp>
class HALightController {
public:
virtual void setState(bool state) = 0;
virtual bool getState() = 0;
};
#define JSON_DOCUMENT_TYPE StaticJsonDocument<256>
class HALight : public HAControllableDevice {
private:
HALightController *light;
protected:
virtual JSON_DOCUMENT_TYPE createConfigJSON();
virtual JSON_DOCUMENT_TYPE patchConfigJSON(JSON_DOCUMENT_TYPE &doc);
virtual JSON_DOCUMENT_TYPE createStateJSON();
virtual JSON_DOCUMENT_TYPE patchStateJSON(JSON_DOCUMENT_TYPE &doc);
virtual void innerHandler(const JSON_DOCUMENT_TYPE &doc);
HALight(PubSubClient &client, const char *name, const char *unique_id);
public:
HALight(PubSubClient &client, const char *name, const char *unique_id,
HALightController &baseLight);
void handle(char *topic, byte *payload, unsigned int length) override;
void sendConfig() override;
void sendState() override;
};

View File

@ -0,0 +1,30 @@
#include <HALightBrightness.hpp>
HALightBrightness::HALightBrightness(PubSubClient &client, const char *name, const char *unique_id,
HALightBrightnessController &baseLight)
: HALight(client, name, unique_id, baseLight), light(&baseLight) {}
JSON_DOCUMENT_TYPE HALightBrightness::patchConfigJSON(JSON_DOCUMENT_TYPE &doc) {
doc["brightness"] = true;
doc["brightness_scale"] = light->getBrightnessScale();
return doc;
}
JSON_DOCUMENT_TYPE HALightBrightness::patchStateJSON(JSON_DOCUMENT_TYPE &doc) {
doc["brightness"] = light->getBrightness();
return doc;
}
void HALightBrightness::handleBrightness(const JSON_DOCUMENT_TYPE &doc) {
if (doc.containsKey("brightness")) {
light->setBrightness(doc["brightness"]);
}
}
void HALightBrightness::innerHandler(const JSON_DOCUMENT_TYPE &doc) {
handleBrightness(doc);
delay(50);
HALight::innerHandler(doc);
}

View File

@ -0,0 +1,27 @@
#pragma once
#include <HALight.hpp>
class HALightBrightnessController: public virtual HALightController {
public:
virtual void setBrightness(int value) = 0;
virtual int getBrightness() = 0;
virtual int getBrightnessScale() = 0;
};
class HALightBrightness : public virtual HALight {
private:
HALightBrightnessController *light;
protected:
virtual JSON_DOCUMENT_TYPE patchConfigJSON(JSON_DOCUMENT_TYPE &doc) override;
virtual JSON_DOCUMENT_TYPE patchStateJSON(JSON_DOCUMENT_TYPE &doc) override;
virtual void innerHandler(const JSON_DOCUMENT_TYPE &doc) override;
void handleBrightness(const JSON_DOCUMENT_TYPE &doc);
public:
HALightBrightness(PubSubClient &client, const char *name, const char *unique_id,
HALightBrightnessController &baseLight);
};

View File

@ -0,0 +1,28 @@
#include <HALightColorTemperature.hpp>
HALightColorTemperature::HALightColorTemperature(PubSubClient &client, const char *name, const char *unique_id,
HALightColorTemperatureController &baseLight)
: HALight(client, "light", name, baseLight),
light(&baseLight) {}
JSON_DOCUMENT_TYPE HALightColorTemperature::patchConfigJSON(JSON_DOCUMENT_TYPE &doc) {
doc["color_temp"] = true;
return doc;
}
JSON_DOCUMENT_TYPE HALightColorTemperature::patchStateJSON(JSON_DOCUMENT_TYPE &doc) {
doc["color_temp"] = light->getColorTemperature();
return doc;
}
void HALightColorTemperature::handleColorTemperature(const JSON_DOCUMENT_TYPE &doc) {
if (doc.containsKey("color_temp")) {
light->setColorTemperature(doc["color_temp"]);
}
}
void HALightColorTemperature::innerHandler(const JSON_DOCUMENT_TYPE &doc) {
handleColorTemperature(doc);
delay(50);
HALight::innerHandler(doc);
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <HALight.hpp>
class HALightColorTemperatureController: public virtual HALightController {
public:
virtual void setColorTemperature(int value) = 0;
virtual int getColorTemperature() = 0;
virtual int getBrightnessScale() = 0;
};
class HALightColorTemperature : public virtual HALight {
private:
HALightColorTemperatureController *light;
protected:
virtual JSON_DOCUMENT_TYPE patchConfigJSON(JSON_DOCUMENT_TYPE &doc) override;
virtual JSON_DOCUMENT_TYPE patchStateJSON(JSON_DOCUMENT_TYPE &doc) override;
virtual void innerHandler(const JSON_DOCUMENT_TYPE &doc) override;
void handleColorTemperature(const JSON_DOCUMENT_TYPE &doc);
public:
HALightColorTemperature(PubSubClient &client, const char *name, const char *unique_id,
HALightColorTemperatureController &baseLight);
};

View File

@ -0,0 +1,34 @@
#include <HALightColorTemperatureBrightness.hpp>
HALightColorTemperatureBrightness::HALightColorTemperatureBrightness(
PubSubClient &client,
const char *name,
const char *unique_id,
HALightColorTemperatureBrightnessController &baseLight
) :
HALightBrightness(client, name, unique_id, baseLight),
HALightColorTemperature(client, name, unique_id, baseLight),
HALight(client, name, unique_id, baseLight),
light(&baseLight)
{
}
JSON_DOCUMENT_TYPE HALightColorTemperatureBrightness::patchConfigJSON(JSON_DOCUMENT_TYPE &doc) {
HALightBrightness::patchConfigJSON(doc);
HALightColorTemperature::patchConfigJSON(doc);
return doc;
}
JSON_DOCUMENT_TYPE HALightColorTemperatureBrightness::patchStateJSON(JSON_DOCUMENT_TYPE &doc) {
HALightBrightness::patchStateJSON(doc);
HALightColorTemperature::patchStateJSON(doc);
return doc;
}
void HALightColorTemperatureBrightness::innerHandler(const JSON_DOCUMENT_TYPE &doc) {
handleBrightness(doc);
delay(50);
handleColorTemperature(doc);
delay(50);
HALight::innerHandler(doc);
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <HALightBrightness.hpp>
#include <HALightColorTemperature.hpp>
class HALightColorTemperatureBrightnessController:
public HALightBrightnessController,
public HALightColorTemperatureController
{
};
class HALightColorTemperatureBrightness :
public HALightBrightness,
public HALightColorTemperature
{
private:
HALightColorTemperatureBrightnessController *light;
protected:
JSON_DOCUMENT_TYPE patchConfigJSON(JSON_DOCUMENT_TYPE &doc) override;
JSON_DOCUMENT_TYPE patchStateJSON(JSON_DOCUMENT_TYPE &doc) override;
virtual void innerHandler(const JSON_DOCUMENT_TYPE &doc) override;
public:
HALightColorTemperatureBrightness(PubSubClient &client, const char *name, const char *unique_id,
HALightColorTemperatureBrightnessController &baseLight);
};

View File

@ -0,0 +1,6 @@
#pragma once
#include <HALight.hpp>
#include <HALightBrightness.hpp>
#include <HALightColorTemperature.hpp>
#include <HALightColorTemperatureBrightness.hpp>

View File

@ -14,7 +14,13 @@ 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
monitor_filters = default, esp8266_exception_decoder
check_tool = cppcheck, clangtidy
check_flags =
cppcheck: -v --suppress=*:*/.pio/*
clangtidy: --checks=-*,clang-diagnostic-*,-clang-diagnostic-unused-value,clang-analyzer-*,-*,bugprone-*,performance-*,readability-*,-readability-magic-numbers,-readability-braces-around-statements,-readability-inconsistent-declaration-parameter-name,-readability-named-parameter --fix

0
src/DHTSensor.cpp Normal file
View File

111
src/IRLight.cpp Normal file
View File

@ -0,0 +1,111 @@
#include <IRLight.h>
IRLight::IRLight(int pin) {
irsend = new IRsend(pin);
state = false;
}
IRLight::IRLight(IRsend *irsend) {
irsend = irsend;
state = false;
}
void IRLight::setState(bool state) {
if (state)
on();
else
off();
}
void IRLight::on() {
state = true;
irsend->sendNEC(RDOn);
}
void IRLight::off() {
state = false;
irsend->sendNEC(RDOff);
}
void IRLight::setBrightness(int newBrightness) {
if (newBrightness > brightness) {
for (int i = 0; i < newBrightness - brightness; i++) {
brightnessUp();
delay(250);
}
} else if (newBrightness < brightness) {
for (int i = 0; i < brightness - newBrightness; i++) {
brightnessDown();
delay(250);
}
}
brightness = newBrightness;
}
void IRLight::setColorTemperature(int color) {
if (color < 208) {
color_temp = 2;
brightness = 14;
irsend->sendNEC(RDButtonB);
} else if (color < 294) {
color_temp = 1;
brightness = 14;
irsend->sendNEC(RDButtonCenter);
} else {
color_temp = 0;
brightness = 14;
irsend->sendNEC(RDButtonA);
}
}
int IRLight::getColorTemperature() {
switch (color_temp)
{
case 0:
return 370;
case 1:
return 262;
case 2:
return 154;
}
return 0;
}
void IRLight::brightnessUp() {
if (brightness < getBrightnessScale()) {
brightness++;
irsend->sendNEC(RDBrightnessUp);
}
}
void IRLight::brightnessDown() {
if (brightness > 0) {
brightness--;
irsend->sendNEC(RDBrightnessDown);
}
}
void IRLight::begin() {
irsend->begin();
brightness = 14;
color_temp = 1;
irsend->sendNEC(RDButtonCenter);
delay(500);
off();
}
int IRLight::getBrightnessScale() {
return 14;
}
int IRLight::getBrightness() {
return brightness;
}
bool IRLight::getState() {
return state;
}
IRLight::~IRLight() {
delete irsend;
}

23
src/IRLightController.cpp Normal file
View File

@ -0,0 +1,23 @@
#include <IRLightController.hpp>
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();
}
void IRLightController::setBrightness(int value) {
}
IRLightController::~IRLightController() {}

21
src/IRLightController.hpp Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <HomeAssistantDevices.hpp>
#include <IRLight.h>
class IRLightController : public HALightBrightnessController {
private:
IRLight &irlight;
public:
IRLightController(IRLight &irlight);
void begin();
void setState(bool state);
bool getState();
void setBrightness(int value);
int getBrightness();
int getBrightnessScale();
~IRLightController();
};

View File

@ -1,24 +1,128 @@
#include <Arduino.h>
/*
* Copyright 2023 Maxim Slipenko
*/
#include <DHT.h>
#include <ESP8266WiFi.h>
#include <IRLight.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <PubSubClient.h>
#include <config.h>
#include <HomeAssistantDevices.hpp>
#include <IRLightController.hpp>
#include <config.hpp>
#include <utils.hpp>
static const char ISR_Root_x1[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----
)EOF";
BearSSL::X509List certISRG(ISR_Root_x1);
BearSSL::WiFiClientSecure espClient;
WiFiClient espClient;
PubSubClient client(espClient);
DHT dht(DHTPIN, DHTTYPE);
void setup_wifi() {
IRLight irlight(4);
HALightColorTemperatureBrightness halight(client, "light", "l-1", irlight);
void setup_devices();
void setup_wifi();
void setup_time();
void setup_mqtt();
void reconnect();
void mqtt_callback(char *topic, byte *payload, unsigned int length);
void mqtt_publish_config();
void mqtt_publish_state();
void mqtt_subscribe_to_commands();
float temp;
float hum;
long lastMsg = 0;
void setup()
{
Serial.begin(115200);
delay(1000);
setup_devices();
setup_wifi();
setup_time();
setup_mqtt();
}
void loop()
{
if (!client.connected())
{
reconnect();
}
client.loop();
/*
long now = millis();
if (now - lastMsg > 60000) {
lastMsg = now;
temp = round2(dht.readTemperature());
hum = round2(dht.readHumidity());
mqtt_publish_state();
}
*/
delay(100);
}
void setup_devices()
{
irlight.begin();
dht.begin();
}
void setup_wifi()
{
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
@ -27,18 +131,57 @@ void setup_wifi() {
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
WiFi.setAutoReconnect(true);
WiFi.persistent(true);
}
void reconnect() {
void setup_time()
{
configTime(0, 0, "pool.ntp.org");
Serial.print(F("Waiting for NTP time sync: "));
time_t nowSecs = time(nullptr);
while (nowSecs < 8 * 3600 * 2)
{
delay(500);
Serial.print(F("."));
yield();
nowSecs = time(nullptr);
}
Serial.println();
struct tm timeinfo;
gmtime_r(&nowSecs, &timeinfo);
Serial.print(F("Current time: "));
Serial.print(asctime(&timeinfo));
}
void setup_mqtt()
{
espClient.setTrustAnchors(&certISRG);
client.setServer(MQTT_SERVER, MQTT_PORT);
client.setCallback(mqtt_callback);
if (!client.connected())
{
reconnect();
}
}
void reconnect()
{
// Loop until we're reconnected
while (!client.connected()) {
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)) {
if (client.connect("ESP8266Client", MQTT_LOGIN, MQTT_PASS))
{
Serial.println("connected");
} else {
}
else
{
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
@ -46,51 +189,46 @@ void reconnect() {
delay(5000);
}
}
client.subscribe("homeassistant/status");
mqtt_publish_config();
delay(100);
mqtt_publish_state();
delay(100);
mqtt_subscribe_to_commands();
}
bool checkBound(float newValue, float prevValue, float maxDiff) {
return !isnan(newValue) &&
(newValue < prevValue - maxDiff || newValue > prevValue + maxDiff);
}
void mqtt_callback(char *topic, byte *payload, unsigned int length)
{
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
Serial.println();
long lastMsg = 0;
float temp = 0.0;
float hum = 0.0;
float diff = 1.0;
void setup() {
Serial.begin(115200);
setup_wifi();
client.setServer(MQTT_SERVER, MQTT_PORT);
dht.begin();
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
long now = millis();
if (now - lastMsg > 10000) {
lastMsg = now;
float newTemp = dht.readTemperature();
float newHum = dht.readHumidity();
if (checkBound(newTemp, temp, diff)) {
temp = newTemp;
Serial.print("New temperature:");
Serial.println(String(temp).c_str());
client.publish(TEMPERATURE_TOPIC, String(temp).c_str(), true);
}
if (checkBound(newHum, hum, diff)) {
hum = newHum;
Serial.print("New humidity:");
Serial.println(String(hum).c_str());
client.publish(HUMIDITY_TOPIC, String(hum).c_str(), true);
if (strcmp(topic, "homeassistant/status") == 0)
{
payload[length] = '\0';
if (strcmp(reinterpret_cast<char *>(payload), "online") == 0)
{
Serial.println("Home Assistant is online");
mqtt_publish_config();
}
}
halight.handle(topic, payload, length);
}
void mqtt_publish_config()
{
halight.sendConfig();
}
void mqtt_publish_state()
{
halight.sendState();
}
void mqtt_subscribe_to_commands()
{
halight.subscribeToCommandTopic();
}

1
src/utils.cpp Normal file
View File

@ -0,0 +1 @@
// double round2(double value) { return (int)(value * 100 + 0.5) / 100.0; }