diff --git a/library.properties b/library.properties index 2891225..817ebd1 100644 --- a/library.properties +++ b/library.properties @@ -3,7 +3,7 @@ version=1.0.0 author=Dawid Chyrzynski maintainer=Dawid Chyrzynski sentence=Home Assistant MQTT integration for Arduino -paragraph=This library provides easy to use API for integrating your Arduin-based device with Home Assistant. +paragraph=Lightweight library that provides easy to use API for integrating your Arduin-based device with Home Assistant. category=Communication url=https://github.com/dawidchyrzynski/arduino-home-assistant architectures=avr diff --git a/src/HAMqtt.cpp b/src/HAMqtt.cpp index da4fcd4..dcebf27 100644 --- a/src/HAMqtt.cpp +++ b/src/HAMqtt.cpp @@ -19,10 +19,15 @@ _devicesTypes(nullptr) const char* HAMqtt::DefaultDiscoveryPrefix = "homeassistant"; +HAMqtt* instance = nullptr; -void onMessageReceived(char* topic, char* message, uint32_t length) +void onMessageReceived(char* topic, uint8_t* payload, uint16_t length) { - // todo: process message + if (instance == nullptr) { + return; + } + + instance->processMessage(topic, payload, length); } HAMqtt::HAMqtt(const char* clientId, Client& netClient) : @@ -31,7 +36,7 @@ HAMqtt::HAMqtt(const char* clientId, Client& netClient) : _hasDevice(false), HAMQTT_INIT { - + instance = this; } HAMqtt::HAMqtt(const char* clientId, Client& netClient, HADevice& device) : @@ -41,7 +46,7 @@ HAMqtt::HAMqtt(const char* clientId, Client& netClient, HADevice& device) : _hasDevice(true), HAMQTT_INIT { - + instance = this; } bool HAMqtt::begin( @@ -108,7 +113,7 @@ void HAMqtt::addDeviceType(BaseDeviceType* deviceType) void HAMqtt::removeDeviceType(BaseDeviceType* deviceType) { - // to do + // todo: remove device from the list } bool HAMqtt::publish(const char* topic, const char* payload, bool retained) @@ -117,13 +122,13 @@ bool HAMqtt::publish(const char* topic, const char* payload, bool retained) return false; } - #if defined(ARDUINOHA_DEBUG) - Serial.print(F("Publishing message with topic: ")); - Serial.print(topic); - Serial.print(F(", payload length: ")); - Serial.print(strlen(payload)); - Serial.println(); - #endif +#if defined(ARDUINOHA_DEBUG) + Serial.print(F("Publishing message with topic: ")); + Serial.print(topic); + Serial.print(F(", payload length: ")); + Serial.print(strlen(payload)); + Serial.println(); +#endif _mqtt->beginPublish(topic, strlen(payload), retained); _mqtt->write(payload, strlen(payload)); @@ -136,13 +141,13 @@ bool HAMqtt::beginPublish( bool retained ) { - #if defined(ARDUINOHA_DEBUG) - Serial.print(F("Publishing message with topic: ")); - Serial.print(topic); - Serial.print(F(", payload length: ")); - Serial.print(payloadLength); - Serial.println(); - #endif +#if defined(ARDUINOHA_DEBUG) + Serial.print(F("Publishing message with topic: ")); + Serial.print(topic); + Serial.print(F(", payload length: ")); + Serial.print(payloadLength); + Serial.println(); +#endif return _mqtt->beginPublish(topic, payloadLength, retained); } @@ -167,15 +172,30 @@ bool HAMqtt::endPublish() bool HAMqtt::subscribe(const char* topic) { - #if defined(ARDUINOHA_DEBUG) - Serial.print(F("Subscribing topic: ")); - Serial.print(topic); - Serial.println(); - #endif +#if defined(ARDUINOHA_DEBUG) + Serial.print(F("Subscribing topic: ")); + Serial.print(topic); + Serial.println(); +#endif return _mqtt->subscribe(topic); } +void HAMqtt::processMessage(char* topic, uint8_t* payload, uint16_t length) +{ +#if defined(ARDUINOHA_DEBUG) + Serial.print(F("Received message on topic: ")); + Serial.print(topic); + Serial.print(F(", payload length: ")); + Serial.print(length); + Serial.println(); +#endif + + for (uint8_t i = 0; i < _devicesTypesNb; i++) { + _devicesTypes[i]->onMqttMessage(topic, payload, length); + } +} + void HAMqtt::connectToServer() { if (_lastConnectionAttemptAt > 0 && @@ -185,11 +205,11 @@ void HAMqtt::connectToServer() _lastConnectionAttemptAt = millis(); - #if defined(ARDUINOHA_DEBUG) - Serial.print(F("Connecting to the MQTT broker... Client ID: ")); - Serial.print(_clientId); - Serial.println(); - #endif +#if defined(ARDUINOHA_DEBUG) + Serial.print(F("Connecting to the MQTT broker... Client ID: ")); + Serial.print(_clientId); + Serial.println(); +#endif if (_username == nullptr || _password == nullptr) { _mqtt->connect(_clientId); diff --git a/src/HAMqtt.h b/src/HAMqtt.h index 15eefc5..cb7b685 100644 --- a/src/HAMqtt.h +++ b/src/HAMqtt.h @@ -95,6 +95,7 @@ public: bool writePayload_P(const char* src); bool endPublish(); bool subscribe(const char* topic); + void processMessage(char* topic, uint8_t* payload, uint16_t length); private: /** diff --git a/src/HAUtils.cpp b/src/HAUtils.cpp new file mode 100644 index 0000000..98d35f1 --- /dev/null +++ b/src/HAUtils.cpp @@ -0,0 +1,19 @@ +#include + +#include "HAUtils.h" + +bool HAUtils::endsWith(const char *str, const char *suffix) +{ + if (str == nullptr || suffix == nullptr) { + return false; + } + + uint16_t lenstr = strlen(str); + uint16_t lensuffix = strlen(suffix); + + if (lensuffix > lenstr) { + return false; + } + + return (strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0); +} diff --git a/src/HAUtils.h b/src/HAUtils.h new file mode 100644 index 0000000..023e113 --- /dev/null +++ b/src/HAUtils.h @@ -0,0 +1,10 @@ +#ifndef HAUTILS_H +#define HAUTILS_H + +class HAUtils +{ +public: + static bool endsWith(const char *str, const char *suffix); +}; + +#endif diff --git a/src/device-types/BaseDeviceType.h b/src/device-types/BaseDeviceType.h index fc4ecc6..4203ea4 100644 --- a/src/device-types/BaseDeviceType.h +++ b/src/device-types/BaseDeviceType.h @@ -15,8 +15,11 @@ public: virtual ~BaseDeviceType(); virtual void onMqttConnected() = 0; - - // to do: onMqttMessage + virtual void onMqttMessage( + const char* topic, + const char* payload, + const uint16_t& length + ) { }; protected: inline HAMqtt* mqtt() const diff --git a/src/device-types/HASwitch.cpp b/src/device-types/HASwitch.cpp index 20db3da..993a5d8 100644 --- a/src/device-types/HASwitch.cpp +++ b/src/device-types/HASwitch.cpp @@ -4,6 +4,7 @@ #include "../ArduinoHADefines.h" #include "../HAMqtt.h" #include "../HADevice.h" +#include "../HAUtils.h" // todo: move all variables to progmem static const char* HAComponentName = "switch"; @@ -12,11 +13,11 @@ static const char* StateTopic = "state"; static const char* StateOn = "ON"; static const char* StateOff = "OFF"; -HASwitch::HASwitch(const char* name, HAMqtt& mqtt) : +HASwitch::HASwitch(const char* name, bool initialState, HAMqtt& mqtt) : BaseDeviceType(mqtt), _name(name), _stateCallback(nullptr), - _currentState(false) + _currentState(initialState) { } @@ -33,9 +34,33 @@ void HASwitch::onMqttConnected() } publishConfig(); + publishCurrentState(); subscribeCommandTopic(); } +void HASwitch::onMqttMessage( + const char* topic, + const char* payload, + const uint16_t& length +) +{ + // todo: find a faster way of topic's verification + + static const char Slash[] PROGMEM = {"/"}; + uint8_t suffixLength = strlen(_name) + strlen(CommandTopic) + 3; // two slashes + null terminator + char suffix[suffixLength]; + + strcpy_P(suffix, Slash); + strcat(suffix, _name); + strcat_P(suffix, Slash); + strcat(suffix, CommandTopic); + + if (HAUtils::endsWith(topic, suffix)) { + bool onState = (strncmp(payload, StateOn, length) == 0); + setState(onState); + } +} + void HASwitch::setState(bool state) { if (state) { @@ -156,18 +181,19 @@ uint16_t HASwitch::calculateSerializedLength(const char* serializedDevice) const uint16_t size = 2 + // opening and closing bracket (without null terminator) - cmdTopicLength + 10 + - stateTopicLength + 12 + - strlen(_name) + 10; + cmdTopicLength + 10 + // 10 - length of the JSON data for this field + stateTopicLength + 12 + // 12 - length of the JSON data for this field + strlen(_name) + 10; // 10 - length of the JSON data for this field // unique ID if (serializedDevice == nullptr) { - size += strlen(_name) + 13; + size += strlen(_name) + 13; // 13 - length of the JSON data for this field } else { - size += strlen(mqtt()->getDevice()->getUniqueId()) + strlen(_name) + 14; + size += strlen(mqtt()->getDevice()->getUniqueId()); + size += strlen(_name) + 14; // 14 - length of the JSON data for this field // device - size += strlen(serializedDevice) + 7; + size += strlen(serializedDevice) + 7; // 7 - length of the JSON data for this field } return size; diff --git a/src/device-types/HASwitch.h b/src/device-types/HASwitch.h index 0237536..c4ac5c8 100644 --- a/src/device-types/HASwitch.h +++ b/src/device-types/HASwitch.h @@ -9,10 +9,19 @@ class HASwitch : public BaseDeviceType { public: - HASwitch(const char* name, HAMqtt& mqtt); + HASwitch( + const char* name, + bool initialState, + HAMqtt& mqtt + ); virtual ~HASwitch(); virtual void onMqttConnected() override; + virtual void onMqttMessage( + const char* topic, + const char* payload, + const uint16_t& length + ) override; void setState(bool state); void turnOn(); diff --git a/src/device-types/HATriggers.cpp b/src/device-types/HATriggers.cpp index d190b12..d031d45 100644 --- a/src/device-types/HATriggers.cpp +++ b/src/device-types/HATriggers.cpp @@ -159,12 +159,12 @@ uint16_t HATriggers::calculateSerializedLength( uint16_t size = 2 + // opening and closing bracket (without null terminator) 17 + // automation type - topicLength + 7 + - strlen(trigger->type) + 10 + - strlen(trigger->subtype) + 11; + topicLength + 7 + // 7 - length of the JSON data for this field + strlen(trigger->type) + 10 + // 10 - length of the JSON data for this field + strlen(trigger->subtype) + 11; // 11 - length of the JSON data for this field if (serializedDevice != nullptr) { - size += strlen(serializedDevice) + 7; + size += strlen(serializedDevice) + 7; // 7 - length of the JSON data for this field } return size;