mirror of
https://github.com/mrcory/arduino-home-assistant.git
synced 2026-08-02 15:24:26 -04:00
Release 1.2.0 (#21)
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
# Changelog
|
||||
|
||||
## 1.2.0
|
||||
|
||||
**Breaking changes:**
|
||||
* Refactored HASensor implementation. Please take a look at updated example in `examples/sensor/sensor.ino`
|
||||
|
||||
**New features:**
|
||||
* Added support for HVAC
|
||||
* Added support for excluding devices types from the compilation using defines (see `src/ArduinoHADefines.h`)
|
||||
* Added support for setting icon in HASwitch and HASensor
|
||||
* Added support for setting retain flag in HASwitch
|
||||
* Added support for text (const char*) payload in HASensor
|
||||
* Added support for fans (HAFan)
|
||||
* Added support for connecting to the MQTT broker using hostname
|
||||
* Added `onConnected()` method in the HAMqtt
|
||||
* Added `onConnectionFailed()` method in the HAMqtt
|
||||
* Added support for MQTT LWT (see `examples/advanced-availability/advanced-availability.ino`)
|
||||
|
||||
**Updates:**
|
||||
* Optimized codebase and logic in all devices types
|
||||
* Updated all examples
|
||||
* Fixed compilation warnings in all classes
|
||||
@@ -8,18 +8,23 @@ but I successfully use it on ESP8266/ESP8255 boards in my projects.
|
||||
## Features
|
||||
|
||||
* MQTT discovery (device is added to the Home Assistant panel automatically)
|
||||
* MQTT Last Will and Testament
|
||||
* Auto reconnect with MQTT broker
|
||||
|
||||
## Examples
|
||||
|
||||
* [Binary Sensor](examples/binary-sensor/binary-sensor.ino)
|
||||
* [Fan](examples/fan/fan.ino)
|
||||
* [LED switch](examples/led-switch/led-switch.ino)
|
||||
* [MQTT with credentials](examples/mqtt-with-credentials/mqtt-with-credentials.ino)
|
||||
* [Multi-state button](examples/multi-state-button/multi-state-button.ino)
|
||||
* [Sensor (temperature, humidity, etc.)](examples/sensor/sensor.ino)
|
||||
* [HVAC](examples/hvac/hvac.ino)
|
||||
* [NodeMCU Wi-Fi](examples/nodemcu/nodemcu.ino)
|
||||
* [Arduino Nano 33 IoT Wi-Fi (SAMD)](examples/nano33iot/nano33iot.ino)
|
||||
* [Availability feature](examples/availability)
|
||||
* [Advanced availability (MQTT LWT)](examples/advanced-availability/advanced-availability.ino)
|
||||
* [MQTT with credentials](examples/mqtt-with-credentials/mqtt-with-credentials.ino)
|
||||
* [MQTT events](examples/mqtt-events/mqtt-events.ino)
|
||||
|
||||
## Tested boards
|
||||
|
||||
@@ -39,10 +44,21 @@ but I successfully use it on ESP8266/ESP8255 boards in my projects.
|
||||
## Supported HA types
|
||||
|
||||
* Binary sensors
|
||||
* Fans
|
||||
* Device triggers
|
||||
* Switches
|
||||
* Sensors
|
||||
* Tag scanner
|
||||
* HVACs *(side note: HVACs requires more flash size than other HA types. It's not suitable for Arduino Nano/Uno)*
|
||||
|
||||
## Roadmap
|
||||
|
||||
* FAQ + Home Assistant setup instructions
|
||||
* Documentation of the library
|
||||
* Unit tests
|
||||
* Reduce flash memory usage
|
||||
* Add support for HA covers
|
||||
* Add support for HA lights
|
||||
|
||||
## Unsupported features
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#include <Ethernet.h>
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
#define INPUT_PIN 9
|
||||
#define BROKER_ADDR IPAddress(192,168,0,17)
|
||||
|
||||
byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A};
|
||||
unsigned long lastReadAt = millis();
|
||||
unsigned long lastAvailabilityToggleAt = millis();
|
||||
bool lastInputState = false;
|
||||
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
|
||||
// "input" may be anything you want to be displayed in HA panel
|
||||
// "door" is device class (based on the class HA displays different icons in the panel)
|
||||
// "true" is initial state of the sensor. In this example it's "true" as we use pullup resistor
|
||||
HABinarySensor sensor("input", "door", true);
|
||||
|
||||
void setup() {
|
||||
pinMode(INPUT_PIN, INPUT_PULLUP);
|
||||
lastInputState = digitalRead(INPUT_PIN);
|
||||
|
||||
// you don't need to verify return status
|
||||
Ethernet.begin(mac);
|
||||
|
||||
lastReadAt = millis();
|
||||
lastAvailabilityToggleAt = millis();
|
||||
|
||||
// set device's details (optional)
|
||||
device.setName("Arduino");
|
||||
device.setSoftwareVersion("1.0.0");
|
||||
|
||||
// This method enables availability for all device types registered on the device.
|
||||
// For example, if you have 5 sensors on the same device, you can enable
|
||||
// shared availability and change availability state of all sensors using
|
||||
// single method call "device.setAvailability(false|true)"
|
||||
device.enableSharedAvailability();
|
||||
|
||||
// Optionally, you can enable MQTT LWT feature. If device will lose connection
|
||||
// to the broker, all device types related to it will be marked as offline in
|
||||
// the Home Assistant Panel.
|
||||
device.enableLastWill();
|
||||
|
||||
mqtt.begin(BROKER_ADDR);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Ethernet.maintain();
|
||||
mqtt.loop();
|
||||
|
||||
if ((millis() - lastReadAt) > 30) { // read in 30ms interval
|
||||
// library produces MQTT message if a new state is different than the previous one
|
||||
sensor.setState(digitalRead(INPUT_PIN));
|
||||
lastInputState = sensor.getState();
|
||||
lastReadAt = millis();
|
||||
}
|
||||
|
||||
if ((millis() - lastAvailabilityToggleAt) > 5000) {
|
||||
device.setAvailability(!device.isOnline());
|
||||
lastAvailabilityToggleAt = millis();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ It's supported only by following device types:
|
||||
* Binary sensor
|
||||
* Sensor
|
||||
* Switch
|
||||
* HVACs
|
||||
* Fans
|
||||
|
||||
## Initialization and usage
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ HAMqtt mqtt(client, device);
|
||||
// "input" may be anything you want to be displayed in HA panel
|
||||
// "door" is device class (based on the class HA displays different icons in the panel)
|
||||
// "true" is initial state of the sensor. In this example it's "true" as we use pullup resistor
|
||||
HABinarySensor sensor("input", "door", true, mqtt);
|
||||
HABinarySensor sensor("input", "door", true);
|
||||
|
||||
void setup() {
|
||||
pinMode(INPUT_PIN, INPUT_PULLUP);
|
||||
@@ -26,6 +26,7 @@ void setup() {
|
||||
Ethernet.begin(mac);
|
||||
|
||||
// turn on "availability" feature
|
||||
// this method also sets initial availability so you can use "true" or "false"
|
||||
sensor.setAvailability(false);
|
||||
|
||||
lastReadAt = millis();
|
||||
|
||||
@@ -15,7 +15,7 @@ HAMqtt mqtt(client, device);
|
||||
// "input" may be anything you want to be displayed in HA panel
|
||||
// "door" is device class (based on the class HA displays different icons in the panel)
|
||||
// "true" is initial state of the sensor. In this example it's "true" as we use pullup resistor
|
||||
HABinarySensor sensor("input", "door", true, mqtt);
|
||||
HABinarySensor sensor("input", "door", true);
|
||||
|
||||
void setup() {
|
||||
pinMode(INPUT_PIN, INPUT_PULLUP);
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#include <Ethernet.h>
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
#define BROKER_ADDR IPAddress(192,168,0,17)
|
||||
|
||||
byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A};
|
||||
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
|
||||
// HAFan::SpeedsFeature enables support for setting different speeds of fan.
|
||||
// You can skip this argument if you don't need speed management.
|
||||
HAFan fan("ventilation", HAFan::SpeedsFeature);
|
||||
|
||||
void onStateChanged(bool state) {
|
||||
Serial.print("State: ");
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
void onSpeedChanged(HAFan::Speed speed) {
|
||||
Serial.print("Speed: ");
|
||||
if (speed == HAFan::OffSpeed) {
|
||||
Serial.print("off");
|
||||
} else if (speed == HAFan::LowSpeed) {
|
||||
Serial.print("low");
|
||||
} else if (speed == HAFan::MediumSpeed) {
|
||||
Serial.print("medium");
|
||||
} else if (speed == HAFan::HighSpeed) {
|
||||
Serial.print("high");
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
// you don't need to verify return status
|
||||
Ethernet.begin(mac);
|
||||
|
||||
// set device's details (optional)
|
||||
device.setName("Arduino");
|
||||
device.setSoftwareVersion("1.0.0");
|
||||
|
||||
// configure fan (optional)
|
||||
// default speeds are: Off | Low | Medium | High
|
||||
fan.setSpeeds(HAFan::OffSpeed | HAFan::LowSpeed | HAFan::HighSpeed);
|
||||
fan.setName("Bathroom");
|
||||
fan.setRetain(true);
|
||||
|
||||
// handle fan states
|
||||
fan.onStateChanged(onStateChanged);
|
||||
fan.onSpeedChanged(onSpeedChanged);
|
||||
|
||||
mqtt.begin(BROKER_ADDR);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Ethernet.maintain();
|
||||
mqtt.loop();
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
#define BROKER_ADDR IPAddress(192,168,0,17)
|
||||
#define WIFI_SSID "MyNetwork"
|
||||
#define WIFI_PASSWORD "MyPassword"
|
||||
|
||||
WiFiClient client;
|
||||
HADevice device;
|
||||
HAMqtt mqtt(client, device);
|
||||
|
||||
// see src/device-types/HAHVAC.h header for more details
|
||||
HAHVAC hvac("my_name", HAHVAC::AuxHeatingFeature | HAHVAC::AwayModeFeature | HAHVAC::HoldFeature);
|
||||
|
||||
unsigned long lastTempPublishAt = 0;
|
||||
double lastTemp = 0;
|
||||
|
||||
void onAuxHeatingStateChanged(bool state) {
|
||||
Serial.print("Aux heating: ");
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
void onAwayStateChanged(bool state) {
|
||||
Serial.print("Away state: ");
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
void onHoldStateChanged(bool state) {
|
||||
Serial.print("Hold state: ");
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
void onTargetTemperatureChanged(double temp) {
|
||||
Serial.print("Target temperature: ");
|
||||
Serial.println(temp);
|
||||
}
|
||||
|
||||
void onModeChanged(HAHVAC::Mode mode) {
|
||||
Serial.print("Mode: ");
|
||||
if (mode == HAHVAC::OffMode) {
|
||||
Serial.println("off");
|
||||
} else if (mode == HAHVAC::AutoMode) {
|
||||
Serial.println("auto");
|
||||
} else if (mode == HAHVAC::CoolMode) {
|
||||
Serial.println("cool");
|
||||
} else if (mode == HAHVAC::HeatMode) {
|
||||
Serial.println("heat");
|
||||
} else if (mode == HAHVAC::DryMode) {
|
||||
Serial.println("dry");
|
||||
} else if (mode == HAHVAC::FanOnlyMode) {
|
||||
Serial.println("fan only");
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
Serial.println("Starting...");
|
||||
|
||||
// Unique ID must be set!
|
||||
byte mac[WL_MAC_ADDR_LENGTH];
|
||||
WiFi.macAddress(mac);
|
||||
device.setUniqueId(mac, sizeof(mac));
|
||||
|
||||
// connect to wifi
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.print(".");
|
||||
delay(500); // waiting for the connection
|
||||
}
|
||||
Serial.println();
|
||||
Serial.println("Connected to the network");
|
||||
|
||||
// set device's details (optional)
|
||||
device.setName("NodeMCU");
|
||||
device.setSoftwareVersion("1.0.0");
|
||||
|
||||
// assign callbacks (optional)
|
||||
hvac.onAuxHeatingStateChanged(onAuxHeatingStateChanged);
|
||||
hvac.onAwayStateChanged(onAwayStateChanged);
|
||||
hvac.onHoldStateChanged(onHoldStateChanged);
|
||||
hvac.onTargetTemperatureChanged(onTargetTemperatureChanged);
|
||||
hvac.onModeChanged(onModeChanged);
|
||||
|
||||
// configure HVAC (optional)
|
||||
hvac.setName("My HVAC");
|
||||
hvac.setMinTemp(10);
|
||||
hvac.setMaxTemp(30);
|
||||
hvac.setTempStep(0.5);
|
||||
hvac.setRetain(true);
|
||||
|
||||
mqtt.begin(BROKER_ADDR);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
|
||||
if ((millis() - lastTempPublishAt) > 3000) {
|
||||
hvac.setCurrentTemperature(lastTemp);
|
||||
lastTempPublishAt = millis();
|
||||
lastTemp += 0.5;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A};
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
HASwitch led("led", false, mqtt); // you can use custom name in place of "led"
|
||||
HASwitch led("led", false); // you can use custom name in place of "led"
|
||||
|
||||
void onSwitchStateChanged(bool state, HASwitch* s)
|
||||
{
|
||||
@@ -27,6 +27,9 @@ void setup() {
|
||||
device.setName("Arduino");
|
||||
device.setSoftwareVersion("1.0.0");
|
||||
|
||||
// set icon (optional)
|
||||
led.setIcon("mdi:lightbulb");
|
||||
|
||||
// handle switch state
|
||||
led.onStateChanged(onSwitchStateChanged);
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#include <Ethernet.h>
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
#define BROKER_ADDR IPAddress(192,168,0,17)
|
||||
|
||||
byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A};
|
||||
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
|
||||
void onMqttConnected() {
|
||||
Serial.println("Connected to the broker!");
|
||||
}
|
||||
|
||||
void onMqttConnectionFailed() {
|
||||
Serial.println("Failed to connect to the broker!");
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
Ethernet.begin(mac);
|
||||
|
||||
mqtt.onConnected(onMqttConnected);
|
||||
mqtt.onConnectionFailed(onMqttConnectionFailed);
|
||||
mqtt.begin(BROKER_ADDR);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Ethernet.maintain();
|
||||
mqtt.loop();
|
||||
}
|
||||
@@ -11,7 +11,7 @@ byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A};
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
HASwitch led("led", false, mqtt); // you can use custom name in place of "led"
|
||||
HASwitch led("led", false); // you can use custom name in place of "led"
|
||||
|
||||
void onSwitchStateChanged(bool state, HASwitch* s)
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A};
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
HATriggers triggers(mqtt);
|
||||
HATriggers triggers;
|
||||
Button btn(BUTTON_PIN);
|
||||
bool holdingBtn = false;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
WiFiClient client;
|
||||
HADevice device;
|
||||
HAMqtt mqtt(client, device);
|
||||
HASwitch led("led", false, mqtt); // you can use custom name in place of "led"
|
||||
HASwitch led("led", false); // you can use custom name in place of "led"
|
||||
|
||||
void onSwitchStateChanged(bool state, HASwitch* s)
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
WiFiClient client;
|
||||
HADevice device;
|
||||
HAMqtt mqtt(client, device);
|
||||
HASwitch led("led", false, mqtt); // you can use custom name in place of "led"
|
||||
HASwitch led("led", false); // you can use custom name in place of "led"
|
||||
|
||||
void onSwitchStateChanged(bool state, HASwitch* s)
|
||||
{
|
||||
|
||||
+11
-16
@@ -10,21 +10,7 @@ double lastValue = 0;
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
|
||||
/**
|
||||
* Supported data types:
|
||||
* - uint8_t
|
||||
* - uint16_t
|
||||
* - uint32_t
|
||||
* - int8_t
|
||||
* - int16_t
|
||||
* - int32_t
|
||||
* - double
|
||||
* - float
|
||||
*/
|
||||
// you can use custom name in place of "temp"
|
||||
// "0" is initial value of the sensor
|
||||
HASensor<double> temp("temp", 0, mqtt);
|
||||
HASensor temp("temp");
|
||||
|
||||
void setup() {
|
||||
// you don't need to verify return status
|
||||
@@ -34,8 +20,10 @@ void setup() {
|
||||
device.setName("Arduino");
|
||||
device.setSoftwareVersion("1.0.0");
|
||||
|
||||
// you can set custom units for the sensor (optional)
|
||||
// configure sensor (optional)
|
||||
temp.setUnitOfMeasurement("°C");
|
||||
temp.setDeviceClass("temperature");
|
||||
temp.setIcon("mdi:home");
|
||||
|
||||
mqtt.begin(BROKER_ADDR);
|
||||
}
|
||||
@@ -48,5 +36,12 @@ void loop() {
|
||||
lastSentAt = millis();
|
||||
lastValue = lastValue + 0.5;
|
||||
temp.setValue(lastValue);
|
||||
|
||||
// Supported data types:
|
||||
// uint32_t (uint16_t, uint8_t)
|
||||
// int32_t (int16_t, int8_t)
|
||||
// double
|
||||
// float
|
||||
// const char*
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
name=home-assistant-integration
|
||||
version=1.1.1
|
||||
version=1.2.0
|
||||
author=Dawid Chyrzynski <dawid.chyrzynski@gmail.com>
|
||||
maintainer=Dawid Chyrzynski <dawid.chyrzynski@gmail.com>
|
||||
sentence=Home Assistant MQTT integration for Arduino
|
||||
|
||||
+2
-1
@@ -5,8 +5,9 @@
|
||||
#include "HAMqtt.h"
|
||||
#include "HAUtils.h"
|
||||
#include "device-types/HABinarySensor.h"
|
||||
#include "device-types/HAFan.h"
|
||||
#include "device-types/HAHVAC.h"
|
||||
#include "device-types/HASensor.h"
|
||||
#include "device-types/HASensor.cpp"
|
||||
#include "device-types/HASwitch.h"
|
||||
#include "device-types/HATagScanner.h"
|
||||
#include "device-types/HATriggers.h"
|
||||
|
||||
@@ -2,3 +2,12 @@
|
||||
// Please note that you need to initialize serial interface manually
|
||||
// by calling Serial.begin([baudRate]) before initializing ArduinoHA.
|
||||
// #define ARDUINOHA_DEBUG
|
||||
|
||||
// You can reduce Flash size of the compiled library by commenting unused components below
|
||||
#define ARDUINOHA_BINARY_SENSOR
|
||||
#define ARDUINOHA_FAN
|
||||
#define ARDUINOHA_HVAC
|
||||
#define ARDUINOHA_SENSOR
|
||||
#define ARDUINOHA_SWITCH
|
||||
#define ARDUINOHA_TAG_SCANNER
|
||||
#define ARDUINOHA_TRIGGERS
|
||||
|
||||
+81
-1
@@ -1,13 +1,19 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "ArduinoHADefines.h"
|
||||
#include "HADevice.h"
|
||||
#include "HAUtils.h"
|
||||
#include "HAMqtt.h"
|
||||
#include "device-types/DeviceTypeSerializer.h"
|
||||
|
||||
#define HADEVICE_INIT \
|
||||
_manufacturer(nullptr), \
|
||||
_model(nullptr), \
|
||||
_name(nullptr), \
|
||||
_softwareVersion(nullptr)
|
||||
_softwareVersion(nullptr), \
|
||||
_sharedAvailability(false), \
|
||||
_availabilityTopic(nullptr), \
|
||||
_available(true) // device will be available by default
|
||||
|
||||
HADevice::HADevice() :
|
||||
_uniqueId(nullptr),
|
||||
@@ -30,6 +36,58 @@ HADevice::HADevice(const byte* uniqueId, const uint16_t& length) :
|
||||
|
||||
}
|
||||
|
||||
void HADevice::setAvailability(bool online)
|
||||
{
|
||||
_available = online;
|
||||
publishAvailability();
|
||||
}
|
||||
|
||||
bool HADevice::enableSharedAvailability()
|
||||
{
|
||||
if (_sharedAvailability) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.println(F("Enabling shared availability in the device"));
|
||||
#endif
|
||||
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
nullptr,
|
||||
nullptr,
|
||||
DeviceTypeSerializer::AvailabilityTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_availabilityTopic = (char*)malloc(topicSize);
|
||||
_sharedAvailability = true;
|
||||
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
_availabilityTopic,
|
||||
nullptr,
|
||||
nullptr,
|
||||
DeviceTypeSerializer::AvailabilityTopic
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HADevice::enableLastWill()
|
||||
{
|
||||
HAMqtt* mqtt = HAMqtt::instance();
|
||||
if (mqtt == nullptr || _availabilityTopic == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mqtt->setLastWill(
|
||||
_availabilityTopic,
|
||||
DeviceTypeSerializer::Offline,
|
||||
false
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HADevice::setUniqueId(const byte* uniqueId, const uint16_t& length)
|
||||
{
|
||||
if (_uniqueId != nullptr) {
|
||||
@@ -40,6 +98,28 @@ bool HADevice::setUniqueId(const byte* uniqueId, const uint16_t& length)
|
||||
return true;
|
||||
}
|
||||
|
||||
void HADevice::publishAvailability()
|
||||
{
|
||||
if (!_sharedAvailability) {
|
||||
return;
|
||||
}
|
||||
|
||||
HAMqtt* mqtt = HAMqtt::instance();
|
||||
if (mqtt == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
mqtt->publish(
|
||||
_availabilityTopic,
|
||||
(
|
||||
_available ?
|
||||
DeviceTypeSerializer::Online :
|
||||
DeviceTypeSerializer::Offline
|
||||
),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
uint16_t HADevice::calculateSerializedLength() const
|
||||
{
|
||||
uint16_t size =
|
||||
|
||||
@@ -25,7 +25,17 @@ public:
|
||||
inline void setSoftwareVersion(const char* softwareVersion)
|
||||
{ _softwareVersion = softwareVersion; }
|
||||
|
||||
inline bool isSharedAvailabilityEnabled() const
|
||||
{ return _sharedAvailability; }
|
||||
|
||||
inline bool isOnline() const
|
||||
{ return _available; }
|
||||
|
||||
void setAvailability(bool online);
|
||||
bool enableSharedAvailability();
|
||||
bool enableLastWill();
|
||||
bool setUniqueId(const byte* uniqueId, const uint16_t& length);
|
||||
void publishAvailability();
|
||||
uint16_t calculateSerializedLength() const;
|
||||
uint16_t serialize(char* dst) const;
|
||||
|
||||
@@ -35,6 +45,9 @@ private:
|
||||
const char* _model;
|
||||
const char* _name;
|
||||
const char* _softwareVersion;
|
||||
bool _sharedAvailability;
|
||||
char* _availabilityTopic;
|
||||
bool _available;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+118
-42
@@ -5,43 +5,36 @@
|
||||
#include "ArduinoHADefines.h"
|
||||
#include "device-types/BaseDeviceType.h"
|
||||
|
||||
#define HAMQTT_INIT \
|
||||
_netClient(netClient), \
|
||||
_device(device), \
|
||||
_hasDevice(true), \
|
||||
_initialized(false), \
|
||||
_discoveryPrefix(DefaultDiscoveryPrefix), \
|
||||
_mqtt(new PubSubClient(netClient)), \
|
||||
_serverIp(new IPAddress()), \
|
||||
_serverPort(0), \
|
||||
_username(nullptr), \
|
||||
_password(nullptr), \
|
||||
_lastConnectionAttemptAt(0), \
|
||||
_devicesTypesNb(0), \
|
||||
_devicesTypes(nullptr)
|
||||
|
||||
static const char* DefaultDiscoveryPrefix = "homeassistant";
|
||||
static HAMqtt* instance = nullptr;
|
||||
HAMqtt* HAMqtt::_instance = nullptr;
|
||||
|
||||
void onMessageReceived(char* topic, uint8_t* payload, unsigned int length)
|
||||
{
|
||||
if (instance == nullptr || length > UINT16_MAX) {
|
||||
if (HAMqtt::instance() == nullptr || length > UINT16_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
instance->processMessage(topic, payload, static_cast<uint16_t>(length));
|
||||
HAMqtt::instance()->processMessage(topic, payload, static_cast<uint16_t>(length));
|
||||
}
|
||||
|
||||
HAMqtt::HAMqtt(Client& netClient, HADevice& device) :
|
||||
HAMQTT_INIT
|
||||
_netClient(netClient),
|
||||
_device(device),
|
||||
_connectedCallback(nullptr),
|
||||
_connectionFailedCallback(nullptr),
|
||||
_initialized(false),
|
||||
_discoveryPrefix(DefaultDiscoveryPrefix),
|
||||
_mqtt(new PubSubClient(netClient)),
|
||||
_username(nullptr),
|
||||
_password(nullptr),
|
||||
_lastConnectionAttemptAt(0),
|
||||
_devicesTypesNb(0),
|
||||
_devicesTypes(nullptr),
|
||||
_lastWillTopic(nullptr),
|
||||
_lastWillMessage(nullptr),
|
||||
_lastWillRetain(false)
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
|
||||
HAMqtt::HAMqtt(const char* clientId, Client& netClient, HADevice& device) :
|
||||
HAMQTT_INIT
|
||||
{
|
||||
instance = this;
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
bool HAMqtt::begin(
|
||||
@@ -76,13 +69,11 @@ bool HAMqtt::begin(
|
||||
return false;
|
||||
}
|
||||
|
||||
*_serverIp = serverIp;
|
||||
_serverPort = serverPort;
|
||||
_username = username;
|
||||
_password = password;
|
||||
_initialized = true;
|
||||
|
||||
_mqtt->setServer(*_serverIp, _serverPort);
|
||||
_mqtt->setServer(serverIp, serverPort);
|
||||
_mqtt->setCallback(onMessageReceived);
|
||||
|
||||
return true;
|
||||
@@ -94,16 +85,86 @@ bool HAMqtt::begin(
|
||||
const char* password
|
||||
)
|
||||
{
|
||||
return begin(serverIp, 1883, username, password);
|
||||
return begin(serverIp, HAMQTT_DEFAULT_PORT, username, password);
|
||||
}
|
||||
|
||||
bool HAMqtt::begin(
|
||||
const char* hostname,
|
||||
const uint16_t& serverPort,
|
||||
const char* username,
|
||||
const char* password
|
||||
)
|
||||
{
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.println(F("Initializing ArduinoHA"));
|
||||
Serial.print(F("Server address: "));
|
||||
Serial.print(hostname);
|
||||
Serial.print(F(":"));
|
||||
Serial.print(serverPort);
|
||||
Serial.println();
|
||||
#endif
|
||||
|
||||
if (_device.getUniqueId() == nullptr) {
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.println(F("Failed to initialize ArduinoHA. Missing device's unique ID."));
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_initialized) {
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.println(F("ArduinoHA is already initialized"));
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_username = username;
|
||||
_password = password;
|
||||
_initialized = true;
|
||||
|
||||
_mqtt->setServer(hostname, serverPort);
|
||||
_mqtt->setCallback(onMessageReceived);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HAMqtt::begin(
|
||||
const char* hostname,
|
||||
const char* username,
|
||||
const char* password
|
||||
)
|
||||
{
|
||||
return begin(hostname, HAMQTT_DEFAULT_PORT, username, password);
|
||||
}
|
||||
|
||||
bool HAMqtt::disconnect(bool sendLastWill)
|
||||
{
|
||||
if (!_initialized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.println(F("Closing connection with MQTT broker"));
|
||||
#endif
|
||||
|
||||
if (sendLastWill &&
|
||||
_lastWillTopic != nullptr &&
|
||||
_lastWillMessage != nullptr) {
|
||||
publish(_lastWillTopic, _lastWillMessage, _lastWillRetain);
|
||||
}
|
||||
|
||||
_initialized = false;
|
||||
_lastConnectionAttemptAt = 0;
|
||||
_mqtt->disconnect();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void HAMqtt::loop()
|
||||
{
|
||||
if (!_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_mqtt->loop()) {
|
||||
if (_initialized && !_mqtt->loop()) {
|
||||
connectToServer();
|
||||
}
|
||||
}
|
||||
@@ -222,27 +283,42 @@ void HAMqtt::connectToServer()
|
||||
Serial.println();
|
||||
#endif
|
||||
|
||||
if (_username == nullptr || _password == nullptr) {
|
||||
_mqtt->connect(_device.getUniqueId());
|
||||
} else {
|
||||
_mqtt->connect(_device.getUniqueId(), _username, _password);
|
||||
}
|
||||
_mqtt->connect(
|
||||
_device.getUniqueId(),
|
||||
_username,
|
||||
_password,
|
||||
_lastWillTopic,
|
||||
0,
|
||||
_lastWillRetain,
|
||||
_lastWillMessage,
|
||||
true
|
||||
);
|
||||
|
||||
if (isConnected()) {
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.println(F("Connected to the broker"));
|
||||
#endif
|
||||
|
||||
onConnected();
|
||||
onConnectedLogic();
|
||||
} else {
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.println(F("Failed to connect to the broker"));
|
||||
|
||||
if (_connectionFailedCallback) {
|
||||
_connectionFailedCallback();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void HAMqtt::onConnected()
|
||||
void HAMqtt::onConnectedLogic()
|
||||
{
|
||||
if (_connectedCallback) {
|
||||
_connectedCallback();
|
||||
}
|
||||
|
||||
_device.publishAvailability();
|
||||
|
||||
for (uint8_t i = 0; i < _devicesTypesNb; i++) {
|
||||
_devicesTypes[i]->onMqttConnected();
|
||||
}
|
||||
|
||||
+78
-18
@@ -4,6 +4,9 @@
|
||||
#include <Client.h>
|
||||
#include <IPAddress.h>
|
||||
|
||||
#define HAMQTT_CALLBACK(name) void (*name)()
|
||||
#define HAMQTT_DEFAULT_PORT 1883
|
||||
|
||||
class PubSubClient;
|
||||
class HADevice;
|
||||
class BaseDeviceType;
|
||||
@@ -13,8 +16,10 @@ class HAMqtt
|
||||
public:
|
||||
static const uint16_t ReconnectInterval = 5000; // ms
|
||||
|
||||
inline static HAMqtt* instance()
|
||||
{ return _instance; }
|
||||
|
||||
HAMqtt(Client& netClient, HADevice& device);
|
||||
HAMqtt(const char* clientId, Client& netClient, HADevice& device);
|
||||
|
||||
/**
|
||||
* Sets prefix for Home Assistant discovery.
|
||||
@@ -34,7 +39,23 @@ public:
|
||||
* Returns instance of the device assigned to the HAMqtt class.
|
||||
*/
|
||||
inline HADevice const* getDevice() const
|
||||
{ return (_hasDevice ? &_device : nullptr); }
|
||||
{ return &_device; }
|
||||
|
||||
/**
|
||||
* Given callback will be called each time the connection with broker is acquired.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onConnected(HAMQTT_CALLBACK(callback))
|
||||
{ _connectedCallback = callback; }
|
||||
|
||||
/**
|
||||
* Given callback will be called each time the library fails to connect to the broker.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onConnectionFailed(HAMQTT_CALLBACK(callback))
|
||||
{ _connectionFailedCallback = callback; }
|
||||
|
||||
/**
|
||||
* Sets parameters of the connection to the MQTT broker.
|
||||
@@ -48,27 +69,43 @@ public:
|
||||
*/
|
||||
bool begin(
|
||||
const IPAddress& serverIp,
|
||||
const uint16_t& serverPort = 1883,
|
||||
const uint16_t& serverPort = HAMQTT_DEFAULT_PORT,
|
||||
const char* username = nullptr,
|
||||
const char* password = nullptr
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets parameters of the connection to the MQTT broker on default port.
|
||||
* The library will try to connect to the broker in first loop cycle.
|
||||
* Please note that the library automatically reconnects to the broker if connection is lost.
|
||||
*
|
||||
* @param serverIp IP address of the MQTT broker.
|
||||
* @param serverPort Port of the MQTT broker.
|
||||
* @param username Username for authentication.
|
||||
* @param password Password for authentication.
|
||||
*/
|
||||
bool begin(
|
||||
const IPAddress& serverIp,
|
||||
const char* username,
|
||||
const char* password
|
||||
);
|
||||
|
||||
/**
|
||||
* Connects to the MQTT broker using hostname.
|
||||
*
|
||||
* @param hostname Hostname of the MQTT broker.
|
||||
* @param serverPort Port of the MQTT broker.
|
||||
* @param username Username for authentication.
|
||||
* @param password Password for authentication.
|
||||
*/
|
||||
bool begin(
|
||||
const char* hostname,
|
||||
const uint16_t& serverPort = HAMQTT_DEFAULT_PORT,
|
||||
const char* username = nullptr,
|
||||
const char* password = nullptr
|
||||
);
|
||||
bool begin(
|
||||
const char* hostname,
|
||||
const char* username,
|
||||
const char* password
|
||||
);
|
||||
|
||||
/**
|
||||
* Closes connection with the MQTT broker.
|
||||
*
|
||||
* @param sendLastWill Set to true if you want to publish device unavailability before closing the connection.
|
||||
*/
|
||||
bool disconnect(bool sendLastWill = true);
|
||||
|
||||
/**
|
||||
* ArduinoHA's ticker.
|
||||
*/
|
||||
@@ -116,6 +153,25 @@ public:
|
||||
*/
|
||||
bool subscribe(const char* topic);
|
||||
|
||||
/**
|
||||
* Enables last will message that will be produced when device disconnects from the broker.
|
||||
* If you want to change availability of the device in Home Assistant panel
|
||||
* please use enableLastWill() method in the HADevice object instead.
|
||||
*
|
||||
* @param lastWillTopic
|
||||
* @param lastWillMessage
|
||||
* @param lastWillRetain
|
||||
*/
|
||||
inline void setLastWill(
|
||||
const char* lastWillTopic,
|
||||
const char* lastWillMessage,
|
||||
bool lastWillRetain
|
||||
) {
|
||||
_lastWillTopic = lastWillTopic;
|
||||
_lastWillMessage = lastWillMessage;
|
||||
_lastWillRetain = lastWillRetain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes MQTT message received from the broker (subscription).
|
||||
*
|
||||
@@ -126,6 +182,8 @@ public:
|
||||
void processMessage(char* topic, uint8_t* payload, uint16_t length);
|
||||
|
||||
private:
|
||||
static HAMqtt* _instance;
|
||||
|
||||
/**
|
||||
* Attempts to connect to the MQTT broker.
|
||||
* The method uses properties passed to the "begin" method.
|
||||
@@ -135,21 +193,23 @@ private:
|
||||
/**
|
||||
* This method is called each time the connection with MQTT broker is acquired.
|
||||
*/
|
||||
void onConnected();
|
||||
void onConnectedLogic();
|
||||
|
||||
Client& _netClient;
|
||||
HADevice& _device;
|
||||
bool _hasDevice;
|
||||
HAMQTT_CALLBACK(_connectedCallback);
|
||||
HAMQTT_CALLBACK(_connectionFailedCallback);
|
||||
bool _initialized;
|
||||
const char* _discoveryPrefix;
|
||||
PubSubClient* _mqtt;
|
||||
IPAddress* _serverIp;
|
||||
uint16_t _serverPort;
|
||||
const char* _username;
|
||||
const char* _password;
|
||||
uint32_t _lastConnectionAttemptAt;
|
||||
uint8_t _devicesTypesNb;
|
||||
BaseDeviceType** _devicesTypes;
|
||||
const char* _lastWillTopic;
|
||||
const char* _lastWillMessage;
|
||||
bool _lastWillRetain;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+17
-29
@@ -1,3 +1,7 @@
|
||||
#ifdef ARDUINO_ARCH_SAMD
|
||||
#include <avr/dtostrf.h>
|
||||
#endif
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "HAUtils.h"
|
||||
@@ -46,34 +50,18 @@ char* HAUtils::byteArrayToStr(
|
||||
return dst;
|
||||
}
|
||||
|
||||
uint8_t HAUtils::getValueTypeLength(const ValueType& type)
|
||||
void HAUtils::tempToStr(
|
||||
char* dst,
|
||||
const double& temp
|
||||
)
|
||||
{
|
||||
switch(type) {
|
||||
case ValueTypeUint8:
|
||||
return sizeof(uint8_t);
|
||||
|
||||
case ValueTypeUint16:
|
||||
return sizeof(uint16_t);
|
||||
|
||||
case ValueTypeUint32:
|
||||
return sizeof(uint32_t);
|
||||
|
||||
case ValueTypeInt8:
|
||||
return sizeof(int8_t);
|
||||
|
||||
case ValueTypeInt16:
|
||||
return sizeof(int16_t);
|
||||
|
||||
case ValueTypeInt32:
|
||||
return sizeof(int32_t);
|
||||
|
||||
case ValueTypeDouble:
|
||||
return sizeof(double);
|
||||
|
||||
case ValueTypeFloat:
|
||||
return sizeof(float);
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
memset(dst, 0, sizeof(AHA_SERIALIZED_TEMP_SIZE));
|
||||
dtostrf(temp, 0, 2, dst);
|
||||
}
|
||||
|
||||
double HAUtils::strToTemp(
|
||||
const char* src
|
||||
)
|
||||
{
|
||||
return atof(src);
|
||||
}
|
||||
|
||||
+10
-35
@@ -3,24 +3,14 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#define AHA_SERIALIZED_TEMP_SIZE 8
|
||||
|
||||
class HAUtils
|
||||
{
|
||||
public:
|
||||
enum ValueType {
|
||||
ValueTypeUnknown = -1,
|
||||
ValueTypeUint8,
|
||||
ValueTypeUint16,
|
||||
ValueTypeUint32,
|
||||
ValueTypeInt8,
|
||||
ValueTypeInt16,
|
||||
ValueTypeInt32,
|
||||
ValueTypeDouble,
|
||||
ValueTypeFloat
|
||||
};
|
||||
|
||||
static bool endsWith(
|
||||
const char* str,
|
||||
const char* suffi
|
||||
const char* suffix
|
||||
);
|
||||
static void byteArrayToStr(
|
||||
char* dst,
|
||||
@@ -31,28 +21,13 @@ public:
|
||||
const byte* src,
|
||||
const uint16_t& length
|
||||
);
|
||||
|
||||
template <typename A, typename B >
|
||||
static bool compareType(A a, B b) { return false; }
|
||||
|
||||
template <typename A, typename B >
|
||||
static bool compareType(A a, A b) { return true; }
|
||||
|
||||
template <typename T>
|
||||
static ValueType determineValueType() {
|
||||
if (compareType<T, uint8_t>(0, (uint8_t)0)) return ValueTypeUint8;
|
||||
else if (compareType<T, uint16_t>(0, (uint16_t)0)) return ValueTypeUint16;
|
||||
else if (compareType<T, uint32_t>(0, (uint32_t)0)) return ValueTypeUint32;
|
||||
else if (compareType<T, int8_t>(0, (int8_t)0)) return ValueTypeInt8;
|
||||
else if (compareType<T, int16_t>(0, (int16_t)0)) return ValueTypeInt16;
|
||||
else if (compareType<T, int32_t>(0, (int32_t)0)) return ValueTypeInt32;
|
||||
else if (compareType<T, double>(0, (double)0)) return ValueTypeDouble;
|
||||
else if (compareType<T, float>(0, (float)0)) return ValueTypeFloat;
|
||||
|
||||
return ValueTypeUnknown;
|
||||
}
|
||||
|
||||
static uint8_t getValueTypeLength(const ValueType& type);
|
||||
static void tempToStr(
|
||||
char* dst,
|
||||
const double& temp
|
||||
);
|
||||
static double strToTemp(
|
||||
const char* src
|
||||
);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
#include "BaseDeviceType.h"
|
||||
#include "../HAMqtt.h"
|
||||
#include "../HADevice.h"
|
||||
#include "../HAUtils.h"
|
||||
|
||||
BaseDeviceType::BaseDeviceType(
|
||||
HAMqtt& mqtt,
|
||||
const char* componentName,
|
||||
const char* name
|
||||
) :
|
||||
_mqtt(mqtt),
|
||||
_componentName(componentName),
|
||||
_name(name),
|
||||
_availability(AvailabilityDefault)
|
||||
{
|
||||
_mqtt.addDeviceType(this);
|
||||
mqtt()->addDeviceType(this);
|
||||
}
|
||||
|
||||
BaseDeviceType::~BaseDeviceType()
|
||||
@@ -26,17 +25,84 @@ void BaseDeviceType::setAvailability(bool online)
|
||||
publishAvailability();
|
||||
}
|
||||
|
||||
HAMqtt* BaseDeviceType::mqtt() const
|
||||
{
|
||||
return HAMqtt::instance();
|
||||
}
|
||||
|
||||
void BaseDeviceType::onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t& length
|
||||
)
|
||||
{
|
||||
(void)topic;
|
||||
(void)payload;
|
||||
(void)length;
|
||||
}
|
||||
|
||||
void BaseDeviceType::publishConfig()
|
||||
{
|
||||
const HADevice* device = mqtt()->getDevice();
|
||||
if (device == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t& deviceLength = device->calculateSerializedLength();
|
||||
if (deviceLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char serializedDevice[deviceLength];
|
||||
if (device->serialize(serializedDevice) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
const uint16_t& dataLength = calculateSerializedLength(serializedDevice);
|
||||
|
||||
if (topicLength == 0 || dataLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char topic[topicLength];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mqtt()->beginPublish(topic, dataLength, true)) {
|
||||
writeSerializedData(serializedDevice);
|
||||
mqtt()->endPublish();
|
||||
}
|
||||
}
|
||||
|
||||
void BaseDeviceType::publishAvailability()
|
||||
{
|
||||
const HADevice* device = HAMqtt::instance()->getDevice();
|
||||
if (device == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_availability == AvailabilityDefault ||
|
||||
!_mqtt.isConnected() ||
|
||||
!mqtt()->isConnected() ||
|
||||
strlen(_name) == 0 ||
|
||||
strlen(_componentName) == 0) {
|
||||
strlen(_componentName) == 0 ||
|
||||
device->isSharedAvailabilityEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
_componentName,
|
||||
_name,
|
||||
DeviceTypeSerializer::AvailabilityTopic
|
||||
@@ -47,7 +113,6 @@ void BaseDeviceType::publishAvailability()
|
||||
|
||||
char topic[topicSize];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
_componentName,
|
||||
_name,
|
||||
@@ -68,3 +133,27 @@ void BaseDeviceType::publishAvailability()
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
bool BaseDeviceType::isMyTopic(const char* topic, const char* expectedTopic)
|
||||
{
|
||||
if (topic == nullptr || expectedTopic == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strlen(name()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char Slash[] PROGMEM = {"/"};
|
||||
|
||||
// name + cmd topic + two slashes + null terminator
|
||||
uint8_t suffixLength = strlen(name()) + strlen(expectedTopic) + 3;
|
||||
char suffix[suffixLength];
|
||||
|
||||
strcpy_P(suffix, Slash);
|
||||
strcat(suffix, name());
|
||||
strcat_P(suffix, Slash);
|
||||
strcat(suffix, expectedTopic);
|
||||
|
||||
return HAUtils::endsWith(topic, suffix);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../ArduinoHADefines.h"
|
||||
#include "DeviceTypeSerializer.h"
|
||||
|
||||
class HAMqtt;
|
||||
@@ -11,21 +12,11 @@ class BaseDeviceType
|
||||
{
|
||||
public:
|
||||
BaseDeviceType(
|
||||
HAMqtt& mqtt,
|
||||
const char* componentName,
|
||||
const char* name
|
||||
);
|
||||
virtual ~BaseDeviceType();
|
||||
|
||||
inline bool isOnline() const
|
||||
{ return (_availability == AvailabilityOnline); }
|
||||
|
||||
virtual void setAvailability(bool online);
|
||||
|
||||
protected:
|
||||
inline HAMqtt* mqtt() const
|
||||
{ return &_mqtt; }
|
||||
|
||||
inline const char* name() const
|
||||
{ return _name; }
|
||||
|
||||
@@ -35,14 +26,26 @@ protected:
|
||||
inline bool isAvailabilityConfigured() const
|
||||
{ return (_availability != AvailabilityDefault); }
|
||||
|
||||
inline bool isOnline() const
|
||||
{ return (_availability == AvailabilityOnline); }
|
||||
|
||||
virtual void setAvailability(bool online);
|
||||
|
||||
protected:
|
||||
HAMqtt* mqtt() const;
|
||||
|
||||
virtual void onMqttConnected() = 0;
|
||||
virtual void onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t& length
|
||||
) { };
|
||||
);
|
||||
|
||||
virtual void publishConfig();
|
||||
virtual void publishAvailability();
|
||||
virtual bool isMyTopic(const char* topic, const char* expectedTopic);
|
||||
virtual uint16_t calculateSerializedLength(const char* serializedDevice) const = 0;
|
||||
virtual bool writeSerializedData(const char* serializedDevice) const = 0;
|
||||
|
||||
const char* const _componentName;
|
||||
const char* const _name;
|
||||
@@ -54,7 +57,6 @@ private:
|
||||
AvailabilityOffline
|
||||
};
|
||||
|
||||
HAMqtt& _mqtt;
|
||||
Availability _availability;
|
||||
|
||||
friend class HAMqtt;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "DeviceTypeSerializer.h"
|
||||
#include "BaseDeviceType.h"
|
||||
#include "../HAMqtt.h"
|
||||
#include "../HADevice.h"
|
||||
|
||||
@@ -19,31 +20,31 @@ const char* DeviceTypeSerializer::StateOn = "ON";
|
||||
const char* DeviceTypeSerializer::StateOff = "OFF";
|
||||
|
||||
uint16_t DeviceTypeSerializer::calculateTopicLength(
|
||||
const HAMqtt* mqtt,
|
||||
const char* component,
|
||||
const char* objectId,
|
||||
const char* suffix,
|
||||
bool includeNullTerminator
|
||||
)
|
||||
{
|
||||
const char* prefix = mqtt->getDiscoveryPrefix();
|
||||
const char* prefix = HAMqtt::instance()->getDiscoveryPrefix();
|
||||
if (prefix == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t size =
|
||||
strlen(prefix) + 1 + // with slash
|
||||
strlen(component) + 1 + // with slash
|
||||
strlen(prefix) + 1 + // prefix with slash
|
||||
strlen(suffix);
|
||||
|
||||
if (component != nullptr) {
|
||||
size += strlen(component) + 1; // component with slash
|
||||
}
|
||||
|
||||
if (HAMqtt::instance()->getDevice() != nullptr) {
|
||||
size += strlen(HAMqtt::instance()->getDevice()->getUniqueId()) + 1; // device ID with slash
|
||||
}
|
||||
|
||||
if (objectId != nullptr) {
|
||||
size += strlen(objectId) + 1; // with slash
|
||||
} else {
|
||||
size += 1; // slash
|
||||
}
|
||||
|
||||
if (mqtt->getDevice() != nullptr) {
|
||||
size += strlen(mqtt->getDevice()->getUniqueId()) + 1; // with slash
|
||||
}
|
||||
|
||||
if (includeNullTerminator) {
|
||||
@@ -54,26 +55,35 @@ uint16_t DeviceTypeSerializer::calculateTopicLength(
|
||||
}
|
||||
|
||||
uint16_t DeviceTypeSerializer::generateTopic(
|
||||
const HAMqtt* mqtt,
|
||||
char* output,
|
||||
const char* component,
|
||||
const char* objectId,
|
||||
const char* suffix
|
||||
)
|
||||
{
|
||||
const char* prefix = mqtt->getDiscoveryPrefix();
|
||||
const char* prefix = HAMqtt::instance()->getDiscoveryPrefix();
|
||||
if (prefix == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
strcpy(output, prefix);
|
||||
strcat_P(output, CharSlash);
|
||||
strcat(output, component);
|
||||
strcat_P(output, CharSlash);
|
||||
|
||||
if (mqtt->getDevice() != nullptr) {
|
||||
strcat(output, mqtt->getDevice()->getUniqueId());
|
||||
if (component != nullptr) {
|
||||
strcat(output, component);
|
||||
strcat_P(output, CharSlash);
|
||||
}
|
||||
|
||||
if (HAMqtt::instance()->getDevice() != nullptr) {
|
||||
strcat(output, HAMqtt::instance()->getDevice()->getUniqueId());
|
||||
strcat_P(output, CharSlash);
|
||||
}
|
||||
|
||||
if (objectId != nullptr) {
|
||||
strcat(output, objectId);
|
||||
strcat_P(output, CharSlash);
|
||||
}
|
||||
|
||||
strcat(output, objectId);
|
||||
strcat_P(output, CharSlash);
|
||||
strcat(output, suffix);
|
||||
return strlen(output) + 1; // size with null terminator
|
||||
}
|
||||
@@ -94,10 +104,10 @@ uint16_t DeviceTypeSerializer::calculateNameFieldSize(const char* name)
|
||||
}
|
||||
|
||||
uint16_t DeviceTypeSerializer::calculateUniqueIdFieldSize(
|
||||
const HADevice* device,
|
||||
const char* name
|
||||
)
|
||||
{
|
||||
HADevice const* device = HAMqtt::instance()->getDevice();
|
||||
if (device == nullptr || name == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
@@ -111,19 +121,22 @@ uint16_t DeviceTypeSerializer::calculateUniqueIdFieldSize(
|
||||
}
|
||||
|
||||
uint16_t DeviceTypeSerializer::calculateAvailabilityFieldSize(
|
||||
const HAMqtt* mqtt,
|
||||
const char* componentName,
|
||||
const char* name
|
||||
const BaseDeviceType* const dt
|
||||
)
|
||||
{
|
||||
if (mqtt == nullptr || componentName == nullptr || name == nullptr) {
|
||||
const HADevice* device = HAMqtt::instance()->getDevice();
|
||||
if (device == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const bool& sharedAvailability = device->isSharedAvailabilityEnabled();
|
||||
if (!sharedAvailability && !dt->isAvailabilityConfigured()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint16_t& availabilityTopicLength = calculateTopicLength(
|
||||
mqtt,
|
||||
componentName,
|
||||
name,
|
||||
(sharedAvailability ? nullptr : dt->componentName()),
|
||||
(sharedAvailability ? nullptr : dt->name()),
|
||||
AvailabilityTopic,
|
||||
false
|
||||
);
|
||||
@@ -136,6 +149,16 @@ uint16_t DeviceTypeSerializer::calculateAvailabilityFieldSize(
|
||||
return availabilityTopicLength + 12; // 12 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
uint16_t DeviceTypeSerializer::calculateRetainFieldSize(bool retain)
|
||||
{
|
||||
if (!retain) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Field format: ,"ret":true
|
||||
return 11;
|
||||
}
|
||||
|
||||
uint16_t DeviceTypeSerializer::calculateDeviceFieldSize(
|
||||
const char* serializedDevice
|
||||
)
|
||||
@@ -148,85 +171,87 @@ uint16_t DeviceTypeSerializer::calculateDeviceFieldSize(
|
||||
return strlen(serializedDevice) + 7; // 7 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
void DeviceTypeSerializer::mqttWriteBeginningJson(HAMqtt* mqtt)
|
||||
void DeviceTypeSerializer::mqttWriteBeginningJson()
|
||||
{
|
||||
if (mqtt == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const char Data[] PROGMEM = {"{"};
|
||||
mqtt->writePayload_P(Data);
|
||||
HAMqtt::instance()->writePayload_P(Data);
|
||||
}
|
||||
|
||||
void DeviceTypeSerializer::mqttWriteEndJson(HAMqtt* mqtt)
|
||||
void DeviceTypeSerializer::mqttWriteEndJson()
|
||||
{
|
||||
if (mqtt == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const char Data[] PROGMEM = {"}"};
|
||||
mqtt->writePayload_P(Data);
|
||||
HAMqtt::instance()->writePayload_P(Data);
|
||||
}
|
||||
|
||||
void DeviceTypeSerializer::mqttWriteConstCharField(
|
||||
HAMqtt* mqtt,
|
||||
const char* prefix,
|
||||
const char* value
|
||||
const char* value,
|
||||
bool quoteSuffix
|
||||
)
|
||||
{
|
||||
if (mqtt == nullptr || prefix == nullptr || value == nullptr) {
|
||||
if (prefix == nullptr || value == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
mqtt->writePayload_P(prefix);
|
||||
mqtt->writePayload(value, strlen(value));
|
||||
mqtt->writePayload_P(CharQuotation);
|
||||
HAMqtt::instance()->writePayload_P(prefix);
|
||||
HAMqtt::instance()->writePayload(value, strlen(value));
|
||||
|
||||
if (quoteSuffix) {
|
||||
HAMqtt::instance()->writePayload_P(CharQuotation);
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceTypeSerializer::mqttWriteNameField(HAMqtt* mqtt, const char* name)
|
||||
void DeviceTypeSerializer::mqttWriteNameField(const char* name)
|
||||
{
|
||||
if (mqtt == nullptr || name == nullptr) {
|
||||
if (name == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const char Prefix[] PROGMEM = {",\"name\":\""};
|
||||
mqttWriteConstCharField(mqtt, Prefix, name);
|
||||
mqttWriteConstCharField(Prefix, name);
|
||||
}
|
||||
|
||||
void DeviceTypeSerializer::mqttWriteUniqueIdField(
|
||||
HAMqtt* mqtt,
|
||||
const char* name
|
||||
)
|
||||
{
|
||||
if (mqtt == nullptr || name == nullptr) {
|
||||
if (name == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
HADevice const* device = HAMqtt::instance()->getDevice();
|
||||
if (device == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const char Prefix[] PROGMEM = {",\"uniq_id\":\""};
|
||||
|
||||
uint8_t uniqueIdLength = strlen(name) + strlen(mqtt->getDevice()->getUniqueId()) + 2; // underscore and null temrinator
|
||||
uint8_t uniqueIdLength = strlen(name) + strlen(device->getUniqueId()) + 2; // underscore and null temrinator
|
||||
char uniqueId[uniqueIdLength];
|
||||
strcpy(uniqueId, name);
|
||||
strcat_P(uniqueId, CharUnderscore);
|
||||
strcat(uniqueId, mqtt->getDevice()->getUniqueId());
|
||||
strcat(uniqueId, device->getUniqueId());
|
||||
|
||||
mqttWriteConstCharField(mqtt, Prefix, uniqueId);
|
||||
mqttWriteConstCharField(Prefix, uniqueId);
|
||||
}
|
||||
|
||||
void DeviceTypeSerializer::mqttWriteAvailabilityField(
|
||||
HAMqtt* mqtt,
|
||||
const char* componentName,
|
||||
const char* name
|
||||
const BaseDeviceType* const dt
|
||||
)
|
||||
{
|
||||
if (mqtt == nullptr || componentName == nullptr || name == nullptr) {
|
||||
const HADevice* device = HAMqtt::instance()->getDevice();
|
||||
if (device == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool& sharedAvailability = device->isSharedAvailabilityEnabled();
|
||||
if (!sharedAvailability && !dt->isAvailabilityConfigured()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
mqtt,
|
||||
componentName,
|
||||
name,
|
||||
(sharedAvailability ? nullptr : dt->componentName()),
|
||||
(sharedAvailability ? nullptr : dt->name()),
|
||||
AvailabilityTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
@@ -235,10 +260,9 @@ void DeviceTypeSerializer::mqttWriteAvailabilityField(
|
||||
|
||||
char availabilityTopic[topicSize];
|
||||
generateTopic(
|
||||
mqtt,
|
||||
availabilityTopic,
|
||||
componentName,
|
||||
name,
|
||||
(sharedAvailability ? nullptr : dt->componentName()),
|
||||
(sharedAvailability ? nullptr : dt->name()),
|
||||
AvailabilityTopic
|
||||
);
|
||||
|
||||
@@ -247,20 +271,137 @@ void DeviceTypeSerializer::mqttWriteAvailabilityField(
|
||||
}
|
||||
|
||||
static const char Prefix[] PROGMEM = {",\"avty_t\":\""};
|
||||
mqttWriteConstCharField(mqtt, Prefix, availabilityTopic);
|
||||
mqttWriteConstCharField(Prefix, availabilityTopic);
|
||||
}
|
||||
|
||||
void DeviceTypeSerializer::mqttWriteRetainField(
|
||||
bool retain
|
||||
)
|
||||
{
|
||||
if (!retain) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const char Prefix[] PROGMEM = {",\"ret\":"};
|
||||
mqttWriteConstCharField(
|
||||
Prefix,
|
||||
"true",
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
void DeviceTypeSerializer::mqttWriteDeviceField(
|
||||
HAMqtt* mqtt,
|
||||
const char* serializedDevice
|
||||
)
|
||||
{
|
||||
if (mqtt == nullptr || serializedDevice == nullptr) {
|
||||
if (serializedDevice == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const char Data[] PROGMEM = {",\"dev\":"};
|
||||
|
||||
mqtt->writePayload_P(Data);
|
||||
mqtt->writePayload(serializedDevice, strlen(serializedDevice));
|
||||
HAMqtt::instance()->writePayload_P(Data);
|
||||
HAMqtt::instance()->writePayload(serializedDevice, strlen(serializedDevice));
|
||||
}
|
||||
|
||||
bool DeviceTypeSerializer::mqttWriteTopicField(
|
||||
const BaseDeviceType* const dt,
|
||||
const char* jsonPrefix,
|
||||
const char* topicSuffix
|
||||
)
|
||||
{
|
||||
if (jsonPrefix == nullptr || topicSuffix == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
dt->componentName(),
|
||||
dt->name() ,
|
||||
topicSuffix
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char topic[topicSize];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
topic,
|
||||
dt->componentName(),
|
||||
dt->name(),
|
||||
topicSuffix
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(jsonPrefix, topic);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceTypeSerializer::mqttPublishMessage(
|
||||
const BaseDeviceType* const dt,
|
||||
const char* topicSuffix,
|
||||
const char* data
|
||||
)
|
||||
{
|
||||
if (topicSuffix == nullptr || data == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
dt->componentName(),
|
||||
dt->name(),
|
||||
topicSuffix
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char topic[topicSize];
|
||||
generateTopic(
|
||||
topic,
|
||||
dt->componentName(),
|
||||
dt->name(),
|
||||
topicSuffix
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return HAMqtt::instance()->publish(
|
||||
topic,
|
||||
data,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
bool DeviceTypeSerializer::mqttSubscribeTopic(
|
||||
const BaseDeviceType* const dt,
|
||||
const char* topicSuffix
|
||||
)
|
||||
{
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
dt->componentName(),
|
||||
dt->name(),
|
||||
topicSuffix
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char topic[topicSize];
|
||||
generateTopic(
|
||||
topic,
|
||||
dt->componentName(),
|
||||
dt->name(),
|
||||
topicSuffix
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return HAMqtt::instance()->subscribe(topic);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class HAMqtt;
|
||||
class HADevice;
|
||||
class BaseDeviceType;
|
||||
|
||||
class DeviceTypeSerializer
|
||||
{
|
||||
@@ -21,7 +21,7 @@ public:
|
||||
|
||||
/**
|
||||
* Calculates length of the topic with given parameters.
|
||||
* Topic format: [discovery prefix]/[component]/[objectId]/[suffix]
|
||||
* Topic format: [discovery prefix]/[component]/[deviceId]/[objectId]/[suffix]
|
||||
*
|
||||
* @param component
|
||||
* @param objectId
|
||||
@@ -29,7 +29,6 @@ public:
|
||||
* @param includeNullTerminator
|
||||
*/
|
||||
static uint16_t calculateTopicLength(
|
||||
const HAMqtt* mqtt,
|
||||
const char* component,
|
||||
const char* objectId,
|
||||
const char* suffix,
|
||||
@@ -39,7 +38,7 @@ public:
|
||||
/**
|
||||
* Generates topic and saves it to the given buffer.
|
||||
* Please note that size of the buffer must be calculated by `calculateTopicLength` method first.
|
||||
* Topic format: [discovery prefix]/[component]/[objectId]/[suffix]
|
||||
* Topic format: [discovery prefix]/[component]/[deviceId]/[objectId]/[suffix]
|
||||
*
|
||||
* @param output
|
||||
* @param component
|
||||
@@ -48,7 +47,6 @@ public:
|
||||
* @param includeNullTerminator
|
||||
*/
|
||||
static uint16_t generateTopic(
|
||||
const HAMqtt* mqtt,
|
||||
char* output,
|
||||
const char* component,
|
||||
const char* objectId,
|
||||
@@ -60,39 +58,54 @@ public:
|
||||
const char* name
|
||||
);
|
||||
static uint16_t calculateUniqueIdFieldSize(
|
||||
const HADevice* device,
|
||||
const char* name
|
||||
);
|
||||
static uint16_t calculateAvailabilityFieldSize(
|
||||
const HAMqtt* mqtt,
|
||||
const char* componentName,
|
||||
const char* name
|
||||
const BaseDeviceType* const dt
|
||||
);
|
||||
static uint16_t calculateRetainFieldSize(
|
||||
bool retain
|
||||
);
|
||||
static uint16_t calculateDeviceFieldSize(
|
||||
const char* serializedDevice
|
||||
);
|
||||
|
||||
static void mqttWriteBeginningJson(HAMqtt* mqtt);
|
||||
static void mqttWriteEndJson(HAMqtt* mqtt);
|
||||
static void mqttWriteBeginningJson();
|
||||
static void mqttWriteEndJson();
|
||||
static void mqttWriteConstCharField(
|
||||
HAMqtt* mqtt,
|
||||
const char* prefix,
|
||||
const char* value
|
||||
const char* value,
|
||||
bool quoteSuffix = true
|
||||
);
|
||||
static void mqttWriteNameField(
|
||||
const char* name
|
||||
);
|
||||
static void mqttWriteNameField(HAMqtt* mqtt, const char* name);
|
||||
static void mqttWriteUniqueIdField(
|
||||
HAMqtt* mqtt,
|
||||
const char* name
|
||||
);
|
||||
static void mqttWriteAvailabilityField(
|
||||
HAMqtt* mqtt,
|
||||
const char* componentName,
|
||||
const char* name
|
||||
const BaseDeviceType* const dt
|
||||
);
|
||||
static void mqttWriteRetainField(
|
||||
bool retain
|
||||
);
|
||||
static void mqttWriteDeviceField(
|
||||
HAMqtt* mqtt,
|
||||
const char* serializedDevice
|
||||
);
|
||||
static bool mqttWriteTopicField(
|
||||
const BaseDeviceType* const dt,
|
||||
const char* jsonPrefix,
|
||||
const char* topicSuffix
|
||||
);
|
||||
static bool mqttPublishMessage(
|
||||
const BaseDeviceType* const dt,
|
||||
const char* topicSuffix,
|
||||
const char* data
|
||||
);
|
||||
static bool mqttSubscribeTopic(
|
||||
const BaseDeviceType* const dt,
|
||||
const char* topicSuffix
|
||||
);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,16 +1,38 @@
|
||||
#include "HABinarySensor.h"
|
||||
#ifdef ARDUINOHA_BINARY_SENSOR
|
||||
|
||||
#include "../ArduinoHADefines.h"
|
||||
#include "../HAMqtt.h"
|
||||
#include "../HADevice.h"
|
||||
#include "../HAUtils.h"
|
||||
|
||||
HABinarySensor::HABinarySensor(
|
||||
const char* name,
|
||||
bool initialState
|
||||
) :
|
||||
BaseDeviceType("binary_sensor", name),
|
||||
_class(nullptr),
|
||||
_currentState(initialState)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HABinarySensor::HABinarySensor(
|
||||
const char* name,
|
||||
bool initialState,
|
||||
HAMqtt& mqtt
|
||||
) :
|
||||
BaseDeviceType(mqtt, "binary_sensor", name),
|
||||
_class(nullptr),
|
||||
HABinarySensor(name, initialState)
|
||||
{
|
||||
(void)mqtt;
|
||||
}
|
||||
|
||||
HABinarySensor::HABinarySensor(
|
||||
const char* name,
|
||||
const char* deviceClass,
|
||||
bool initialState
|
||||
) :
|
||||
BaseDeviceType("binary_sensor", name),
|
||||
_class(deviceClass),
|
||||
_currentState(initialState)
|
||||
{
|
||||
|
||||
@@ -22,11 +44,9 @@ HABinarySensor::HABinarySensor(
|
||||
bool initialState,
|
||||
HAMqtt& mqtt
|
||||
) :
|
||||
BaseDeviceType(mqtt, "binary_sensor", name),
|
||||
_class(deviceClass),
|
||||
_currentState(initialState)
|
||||
HABinarySensor(name, deviceClass, initialState)
|
||||
{
|
||||
|
||||
(void)mqtt;
|
||||
}
|
||||
|
||||
void HABinarySensor::onMqttConnected()
|
||||
@@ -46,10 +66,6 @@ bool HABinarySensor::setState(bool state)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strlen(name()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (publishState(state)) {
|
||||
_currentState = state;
|
||||
return true;
|
||||
@@ -58,54 +74,6 @@ bool HABinarySensor::setState(bool state)
|
||||
return false;
|
||||
}
|
||||
|
||||
void HABinarySensor::publishConfig()
|
||||
{
|
||||
const HADevice* device = mqtt()->getDevice();
|
||||
if (device == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t& deviceLength = device->calculateSerializedLength();
|
||||
if (deviceLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char serializedDevice[deviceLength];
|
||||
if (device->serialize(serializedDevice) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
const uint16_t& dataLength = calculateSerializedLength(serializedDevice);
|
||||
|
||||
if (topicLength == 0 || dataLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char topic[topicLength];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mqtt()->beginPublish(topic, dataLength, true)) {
|
||||
writeSerializedData(serializedDevice);
|
||||
mqtt()->endPublish();
|
||||
}
|
||||
}
|
||||
|
||||
bool HABinarySensor::publishState(bool state)
|
||||
{
|
||||
if (strlen(name()) == 0) {
|
||||
@@ -113,7 +81,6 @@ bool HABinarySensor::publishState(bool state)
|
||||
}
|
||||
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
@@ -124,7 +91,6 @@ bool HABinarySensor::publishState(bool state)
|
||||
|
||||
char topic[topicSize];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
@@ -162,21 +128,13 @@ uint16_t HABinarySensor::calculateSerializedLength(
|
||||
uint16_t size = 0;
|
||||
size += DeviceTypeSerializer::calculateBaseJsonDataSize();
|
||||
size += DeviceTypeSerializer::calculateNameFieldSize(name());
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(device, name());
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(name());
|
||||
size += DeviceTypeSerializer::calculateDeviceFieldSize(serializedDevice);
|
||||
|
||||
if (isAvailabilityConfigured()) {
|
||||
size += DeviceTypeSerializer::calculateAvailabilityFieldSize(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name()
|
||||
);
|
||||
}
|
||||
size += DeviceTypeSerializer::calculateAvailabilityFieldSize(this);
|
||||
|
||||
// state topic
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic,
|
||||
@@ -206,56 +164,31 @@ bool HABinarySensor::writeSerializedData(const char* serializedDevice) const
|
||||
return false;
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteBeginningJson(mqtt());
|
||||
DeviceTypeSerializer::mqttWriteBeginningJson();
|
||||
|
||||
// state topic
|
||||
{
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char topic[topicSize];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char Prefix[] PROGMEM = {"\"stat_t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, topic);
|
||||
DeviceTypeSerializer::mqttWriteTopicField(
|
||||
this,
|
||||
Prefix,
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
}
|
||||
|
||||
// device class
|
||||
if (_class != nullptr) {
|
||||
static const char Prefix[] PROGMEM = {",\"dev_cla\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, _class);
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(Prefix, _class);
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteNameField(mqtt(), name());
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(mqtt(), name());
|
||||
|
||||
if (isAvailabilityConfigured()) {
|
||||
DeviceTypeSerializer::mqttWriteAvailabilityField(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name()
|
||||
);
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(mqtt(), serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson(mqtt());
|
||||
DeviceTypeSerializer::mqttWriteNameField(name());
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(name());
|
||||
DeviceTypeSerializer::mqttWriteAvailabilityField(this);
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,21 +3,27 @@
|
||||
|
||||
#include "BaseDeviceType.h"
|
||||
|
||||
#ifdef ARDUINOHA_BINARY_SENSOR
|
||||
|
||||
class HABinarySensor : public BaseDeviceType
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initializes binary sensor.
|
||||
*
|
||||
* @param name Name of the sensor. Recommendes characters: [a-z0-9\-_]
|
||||
* @param name Name of the sensor. Recommended characters: [a-z0-9\-_]
|
||||
* @param initialState Initial state of the sensor.
|
||||
It will be published right after "config" message in order to update HA state.
|
||||
*/
|
||||
HABinarySensor(
|
||||
const char* name,
|
||||
bool initialState
|
||||
);
|
||||
HABinarySensor(
|
||||
const char* name,
|
||||
bool initialState,
|
||||
HAMqtt& mqtt
|
||||
);
|
||||
); // legacy constructor
|
||||
|
||||
/**
|
||||
* Initializes binary sensor with the specified class.
|
||||
@@ -28,6 +34,11 @@ public:
|
||||
* @param initialState Initial state of the sensor.
|
||||
It will be published right after "config" message in order to update HA state.
|
||||
*/
|
||||
HABinarySensor(
|
||||
const char* name,
|
||||
const char* deviceClass,
|
||||
bool initialState
|
||||
);
|
||||
HABinarySensor(
|
||||
const char* name,
|
||||
const char* deviceClass,
|
||||
@@ -58,13 +69,13 @@ public:
|
||||
{ return _currentState; }
|
||||
|
||||
private:
|
||||
void publishConfig();
|
||||
bool publishState(bool state);
|
||||
uint16_t calculateSerializedLength(const char* serializedDevice) const;
|
||||
bool writeSerializedData(const char* serializedDevice) const;
|
||||
uint16_t calculateSerializedLength(const char* serializedDevice) const override;
|
||||
bool writeSerializedData(const char* serializedDevice) const override;
|
||||
|
||||
const char* _class;
|
||||
bool _currentState;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,418 @@
|
||||
#include "HAFan.h"
|
||||
#ifdef ARDUINOHA_FAN
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../HADevice.h"
|
||||
|
||||
const char* HAFan::SpeedCommandTopic = {"sct"};
|
||||
const char* HAFan::SpeedStateTopic = {"sst"};
|
||||
|
||||
static const char OffStr[] PROGMEM = {"off"};
|
||||
static const char LowStr[] PROGMEM = {"low"};
|
||||
static const char MediumStr[] PROGMEM = {"medium"};
|
||||
static const char HighStr[] PROGMEM = {"high"};
|
||||
|
||||
HAFan::HAFan(const char* uniqueId, uint8_t features) :
|
||||
BaseDeviceType("fan", uniqueId),
|
||||
_features(features),
|
||||
_speeds(OffSpeed | LowSpeed | MediumSpeed | HighSpeed),
|
||||
_currentState(false),
|
||||
_stateCallback(nullptr),
|
||||
_currentSpeed(OffSpeed),
|
||||
_speedCallback(nullptr),
|
||||
_label(nullptr),
|
||||
_retain(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HAFan::HAFan(const char* uniqueId, uint8_t features, HAMqtt& mqtt) :
|
||||
HAFan(uniqueId, features)
|
||||
{
|
||||
(void)mqtt;
|
||||
}
|
||||
|
||||
void HAFan::onMqttConnected()
|
||||
{
|
||||
if (strlen(name()) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishAvailability();
|
||||
|
||||
DeviceTypeSerializer::mqttSubscribeTopic(
|
||||
this,
|
||||
DeviceTypeSerializer::CommandTopic
|
||||
);
|
||||
|
||||
if (_features & SpeedsFeature) {
|
||||
DeviceTypeSerializer::mqttSubscribeTopic(
|
||||
this,
|
||||
SpeedCommandTopic
|
||||
);
|
||||
}
|
||||
|
||||
if (!_retain) {
|
||||
publishState(_currentState);
|
||||
publishSpeed(_currentSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
void HAFan::onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t& length
|
||||
)
|
||||
{
|
||||
(void)payload;
|
||||
|
||||
if (isMyTopic(topic, DeviceTypeSerializer::CommandTopic)) {
|
||||
bool state = (length == strlen(DeviceTypeSerializer::StateOn));
|
||||
setState(state, true);
|
||||
} else if (isMyTopic(topic, SpeedCommandTopic)) {
|
||||
char speed[length + 1];
|
||||
memset(speed, 0, sizeof(speed));
|
||||
memcpy(speed, payload, length);
|
||||
|
||||
setSpeedFromStr(speed);
|
||||
}
|
||||
}
|
||||
|
||||
bool HAFan::setState(bool state, bool force)
|
||||
{
|
||||
if (!force && _currentState == state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishState(state)) {
|
||||
_currentState = state;
|
||||
|
||||
if (_stateCallback) {
|
||||
_stateCallback(state);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HAFan::setSpeed(Speed speed)
|
||||
{
|
||||
if (speed == UnknownSpeed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (publishSpeed(speed)) {
|
||||
_currentSpeed = speed;
|
||||
|
||||
if (_speedCallback) {
|
||||
_speedCallback(_currentSpeed);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HAFan::setSpeedFromStr(const char* speed)
|
||||
{
|
||||
if (strcmp_P(speed, OffStr) == 0) {
|
||||
return setSpeed(OffSpeed);
|
||||
} else if (strcmp_P(speed, LowStr) == 0) {
|
||||
return setSpeed(LowSpeed);
|
||||
} else if (strcmp_P(speed, MediumStr) == 0) {
|
||||
return setSpeed(MediumSpeed);
|
||||
} else if (strcmp_P(speed, HighStr) == 0) {
|
||||
return setSpeed(HighSpeed);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HAFan::publishState(bool state)
|
||||
{
|
||||
if (strlen(name()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return DeviceTypeSerializer::mqttPublishMessage(
|
||||
this,
|
||||
DeviceTypeSerializer::StateTopic,
|
||||
(
|
||||
state ?
|
||||
DeviceTypeSerializer::StateOn :
|
||||
DeviceTypeSerializer::StateOff
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
bool HAFan::publishSpeed(Speed speed)
|
||||
{
|
||||
if (strlen(name()) == 0 || speed == UnknownSpeed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char speedStr[7];
|
||||
switch (speed) {
|
||||
case OffSpeed:
|
||||
strcpy_P(speedStr, OffStr);
|
||||
break;
|
||||
|
||||
case LowSpeed:
|
||||
strcpy_P(speedStr, LowStr);
|
||||
break;
|
||||
|
||||
case MediumSpeed:
|
||||
strcpy_P(speedStr, MediumStr);
|
||||
break;
|
||||
|
||||
case HighSpeed:
|
||||
strcpy_P(speedStr, HighStr);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return DeviceTypeSerializer::mqttPublishMessage(
|
||||
this,
|
||||
SpeedStateTopic,
|
||||
speedStr
|
||||
);
|
||||
}
|
||||
|
||||
uint16_t HAFan::calculateSerializedLength(const char* serializedDevice) const
|
||||
{
|
||||
if (serializedDevice == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t size = 0;
|
||||
size += DeviceTypeSerializer::calculateBaseJsonDataSize();
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(name());
|
||||
size += DeviceTypeSerializer::calculateDeviceFieldSize(serializedDevice);
|
||||
size += DeviceTypeSerializer::calculateAvailabilityFieldSize(this);
|
||||
size += DeviceTypeSerializer::calculateNameFieldSize(_label);
|
||||
size += DeviceTypeSerializer::calculateRetainFieldSize(_retain);
|
||||
|
||||
// command topic
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::CommandTopic,
|
||||
false
|
||||
);
|
||||
|
||||
if (topicLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Field format: "cmd_t":"[TOPIC]"
|
||||
size += topicLength + 10; // 10 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
// state topic
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic,
|
||||
false
|
||||
);
|
||||
|
||||
if (topicLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Field format: ,"stat_t":"[TOPIC]"
|
||||
size += topicLength + 12; // 12 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
// speeds
|
||||
if (_features & SpeedsFeature) {
|
||||
// command topic
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
SpeedCommandTopic,
|
||||
false
|
||||
);
|
||||
|
||||
if (topicLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Field format: ,"spd_cmd_t":"[TOPIC]"
|
||||
size += topicLength + 15; // 15 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
// state topic
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
SpeedStateTopic,
|
||||
false
|
||||
);
|
||||
|
||||
if (topicLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Field format: ,"spd_stat_t":"[TOPIC]"
|
||||
size += topicLength + 16; // 16 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
// speeds list
|
||||
// Format: ,"spds":[SPEEDS]
|
||||
size += calculateSpeedsLength() + 8; // 8 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
return size; // exludes null terminator
|
||||
}
|
||||
|
||||
uint16_t HAFan::calculateSpeedsLength() const
|
||||
{
|
||||
uint16_t length = 2; // opening and closing bracket
|
||||
|
||||
if (_speeds & OffSpeed) {
|
||||
// escape + comma
|
||||
length += 3 + strlen_P(OffStr);
|
||||
}
|
||||
|
||||
if (_speeds & LowSpeed) {
|
||||
// escape + comma
|
||||
length += 3 + strlen_P(LowStr);
|
||||
}
|
||||
|
||||
if (_speeds & MediumSpeed) {
|
||||
// escape + comma
|
||||
length += 3 + strlen_P(MediumStr);
|
||||
}
|
||||
|
||||
if (_speeds & HighSpeed) {
|
||||
// escape + comma
|
||||
length += 3 + strlen_P(HighStr);
|
||||
}
|
||||
|
||||
if (length > 2) {
|
||||
length--; // remove trailing comma
|
||||
}
|
||||
|
||||
return length; // excludes null terminator
|
||||
}
|
||||
|
||||
bool HAFan::writeSerializedData(const char* serializedDevice) const
|
||||
{
|
||||
if (serializedDevice == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteBeginningJson();
|
||||
|
||||
// command topic
|
||||
{
|
||||
static const char Prefix[] PROGMEM = {"\"cmd_t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteTopicField(
|
||||
this,
|
||||
Prefix,
|
||||
DeviceTypeSerializer::CommandTopic
|
||||
);
|
||||
}
|
||||
|
||||
// state topic
|
||||
{
|
||||
static const char Prefix[] PROGMEM = {",\"stat_t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteTopicField(
|
||||
this,
|
||||
Prefix,
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
}
|
||||
|
||||
// speeds
|
||||
if (_features & SpeedsFeature) {
|
||||
// command topic
|
||||
{
|
||||
static const char Prefix[] PROGMEM = {",\"spd_cmd_t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteTopicField(
|
||||
this,
|
||||
Prefix,
|
||||
SpeedCommandTopic
|
||||
);
|
||||
}
|
||||
|
||||
// state topic
|
||||
{
|
||||
static const char Prefix[] PROGMEM = {",\"spd_stat_t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteTopicField(
|
||||
this,
|
||||
Prefix,
|
||||
SpeedStateTopic
|
||||
);
|
||||
}
|
||||
|
||||
// supported speeds
|
||||
{
|
||||
static const char CharQuotation[] PROGMEM = {"\""};
|
||||
static const char CharQuotationComma[] PROGMEM = {"\","};
|
||||
static const char Prefix[] PROGMEM = {",\"spds\":"};
|
||||
|
||||
char speedStr[calculateSpeedsLength() + 1]; // plus null terminator
|
||||
memset(speedStr, 0, sizeof(speedStr));
|
||||
strcpy(speedStr, "[");
|
||||
|
||||
if (_speeds != 0) {
|
||||
if (_speeds & OffSpeed) {
|
||||
strcat_P(speedStr, CharQuotation);
|
||||
strcat_P(speedStr, OffStr);
|
||||
strcat_P(speedStr, CharQuotationComma);
|
||||
}
|
||||
|
||||
if (_speeds & LowSpeed) {
|
||||
strcat_P(speedStr, CharQuotation);
|
||||
strcat_P(speedStr, LowStr);
|
||||
strcat_P(speedStr, CharQuotationComma);
|
||||
}
|
||||
|
||||
if (_speeds & MediumSpeed) {
|
||||
strcat_P(speedStr, CharQuotation);
|
||||
strcat_P(speedStr, MediumStr);
|
||||
strcat_P(speedStr, CharQuotationComma);
|
||||
}
|
||||
|
||||
if (_speeds & HighSpeed) {
|
||||
strcat_P(speedStr, CharQuotation);
|
||||
strcat_P(speedStr, HighStr);
|
||||
strcat_P(speedStr, CharQuotationComma);
|
||||
}
|
||||
|
||||
speedStr[strlen(speedStr) - 1] = ']';
|
||||
} else {
|
||||
strcat(speedStr, "]");
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(
|
||||
Prefix,
|
||||
speedStr,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteRetainField(_retain);
|
||||
DeviceTypeSerializer::mqttWriteNameField(_label);
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(name());
|
||||
DeviceTypeSerializer::mqttWriteAvailabilityField(this);
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,151 @@
|
||||
#ifndef AHA_FAN_H
|
||||
#define AHA_FAN_H
|
||||
|
||||
#include "BaseDeviceType.h"
|
||||
|
||||
#ifdef ARDUINOHA_FAN
|
||||
|
||||
#define HAFAN_STATE_CALLBACK_BOOL(name) void (*name)(bool)
|
||||
#define HAFAN_STATE_CALLBACK_SPEED(name) void (*name)(Speed)
|
||||
|
||||
class HAFan : public BaseDeviceType
|
||||
{
|
||||
public:
|
||||
static const char* SpeedCommandTopic;
|
||||
static const char* SpeedStateTopic;
|
||||
|
||||
enum Features {
|
||||
DefaultFeatures = 0,
|
||||
SpeedsFeature = 1
|
||||
};
|
||||
|
||||
enum Speed {
|
||||
UnknownSpeed = 0,
|
||||
OffSpeed = 1,
|
||||
LowSpeed = 2,
|
||||
MediumSpeed = 4,
|
||||
HighSpeed = 8
|
||||
};
|
||||
|
||||
HAFan(const char* uniqueId, uint8_t features = DefaultFeatures);
|
||||
HAFan(const char* uniqueId, uint8_t features, HAMqtt& mqtt); // legacy constructor
|
||||
|
||||
virtual void onMqttConnected() override;
|
||||
virtual void onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t& length
|
||||
) override;
|
||||
|
||||
/**
|
||||
* Changes state of the fan and publishes MQTT message.
|
||||
* Please note that if a new value is the same as previous one,
|
||||
* the MQTT message won't be published.
|
||||
*
|
||||
* @param state New state of the fan (on - true, off - false).
|
||||
* @param force Forces to update state without comparing it to previous known state.
|
||||
* @returns Returns true if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setState(bool state, bool force = false);
|
||||
|
||||
/**
|
||||
* Alias for setState(true).
|
||||
*/
|
||||
inline bool turnOn()
|
||||
{ return setState(true); }
|
||||
|
||||
/**
|
||||
* Alias for setState(false).
|
||||
*/
|
||||
inline bool turnOff()
|
||||
{ return setState(false); }
|
||||
|
||||
/**
|
||||
* Returns last known state of the fan.
|
||||
* If setState method wasn't called the initial value will be returned.
|
||||
*/
|
||||
inline bool getState() const
|
||||
{ return _currentState; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the state of the fan changes.
|
||||
* Please note that it's not possible to register multiple callbacks for the same fan.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onStateChanged(HAFAN_STATE_CALLBACK_BOOL(callback))
|
||||
{ _stateCallback = callback; }
|
||||
|
||||
/**
|
||||
* Sets the list of supported fan's speeds.
|
||||
*
|
||||
* @param speeds
|
||||
*/
|
||||
inline void setSpeeds(uint8_t speeds)
|
||||
{ _speeds = speeds; }
|
||||
|
||||
/**
|
||||
* Sets speed of the fan.
|
||||
*
|
||||
* @param speed
|
||||
*/
|
||||
bool setSpeed(Speed speed);
|
||||
|
||||
/**
|
||||
* Sets speed of the fan based on the name of the mode.
|
||||
*
|
||||
* @param speed
|
||||
*/
|
||||
bool setSpeedFromStr(const char* speed);
|
||||
|
||||
/**
|
||||
* Returns current speed of the fan.
|
||||
*/
|
||||
inline Speed getSpeed() const
|
||||
{ return _currentSpeed; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the speed of the fan changes.
|
||||
* Please note that it's not possible to register multiple callbacks for the same fan.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onSpeedChanged(HAFAN_STATE_CALLBACK_SPEED(callback))
|
||||
{ _speedCallback = callback; }
|
||||
|
||||
/**
|
||||
* Sets name that wil be displayed in the Home Assistant panel.
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
inline void setName(const char* name)
|
||||
{ _label = name; } // it needs to be called "label" as "_name" is already in use
|
||||
|
||||
/**
|
||||
* Sets `retain` flag for commands published by Home Assistant.
|
||||
* By default it's set to false.
|
||||
*
|
||||
* @param retain
|
||||
*/
|
||||
inline void setRetain(bool retain)
|
||||
{ _retain = retain; }
|
||||
|
||||
protected:
|
||||
bool publishState(bool state);
|
||||
bool publishSpeed(Speed speed);
|
||||
uint16_t calculateSerializedLength(const char* serializedDevice) const override;
|
||||
uint16_t calculateSpeedsLength() const;
|
||||
bool writeSerializedData(const char* serializedDevice) const override;
|
||||
|
||||
const uint8_t _features;
|
||||
uint8_t _speeds;
|
||||
bool _currentState;
|
||||
HAFAN_STATE_CALLBACK_BOOL(_stateCallback);
|
||||
Speed _currentSpeed;
|
||||
HAFAN_STATE_CALLBACK_SPEED(_speedCallback);
|
||||
const char* _label;
|
||||
bool _retain;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,345 @@
|
||||
#ifndef AHA_HVAC_H
|
||||
#define AHA_HVAC_H
|
||||
|
||||
#include "BaseDeviceType.h"
|
||||
|
||||
#ifdef ARDUINOHA_HVAC
|
||||
|
||||
#define HAHVAC_STATE_CALLBACK_BOOL(name) void (*name)(bool)
|
||||
#define HAHVAC_STATE_CALLBACK_DOUBLE(name) void (*name)(double)
|
||||
#define HAHVAC_STATE_CALLBACK_MODE(name) void (*name)(HAHVAC::Mode)
|
||||
|
||||
class HAHVAC : public BaseDeviceType
|
||||
{
|
||||
public:
|
||||
static const char* ActionTopic;
|
||||
static const char* AuxCommandTopic;
|
||||
static const char* AuxStateTopic;
|
||||
static const char* AwayCommandTopic;
|
||||
static const char* AwayStateTopic;
|
||||
static const char* HoldCommandTopic;
|
||||
static const char* HoldStateTopic;
|
||||
static const char* TargetTemperatureCommandTopic;
|
||||
static const char* TargetTemperatureStateTopic;
|
||||
static const char* CurrentTemperatureTopic;
|
||||
static const char* ModeCommandTopic;
|
||||
static const char* ModeStateTopic;
|
||||
|
||||
enum Features {
|
||||
DefaultFeatures = 0,
|
||||
ActionFeature = 1,
|
||||
AuxHeatingFeature = 2,
|
||||
AwayModeFeature = 4,
|
||||
HoldFeature = 8
|
||||
};
|
||||
|
||||
enum Action {
|
||||
UnknownAction = 0,
|
||||
OffAction,
|
||||
HeatingAction,
|
||||
CoolingAction,
|
||||
DryingAction,
|
||||
IdleAction,
|
||||
FanAction
|
||||
};
|
||||
|
||||
enum Mode {
|
||||
UnknownMode = 0,
|
||||
OffMode = 1,
|
||||
AutoMode = 2,
|
||||
CoolMode = 4,
|
||||
HeatMode = 8,
|
||||
DryMode = 16,
|
||||
FanOnlyMode = 32
|
||||
};
|
||||
|
||||
enum TemperatureUnit {
|
||||
DefaultUnit = 1,
|
||||
CelsiusUnit,
|
||||
FahrenheitUnit
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes HVAC.
|
||||
*
|
||||
* @param uniqueId Unique ID of the HVAC. Recommendes characters: [a-z0-9\-_]
|
||||
* @param features The list of additional features (flag). For example: `HAHVAC::AuxHeatingFeature | HAHVAC::AwayModeFeature`
|
||||
*/
|
||||
HAHVAC(
|
||||
const char* uniqueId,
|
||||
uint8_t features = 0
|
||||
);
|
||||
HAHVAC(
|
||||
const char* uniqueId,
|
||||
uint8_t features,
|
||||
HAMqtt& mqtt
|
||||
); // legacy constructor
|
||||
|
||||
virtual void onMqttConnected() override;
|
||||
|
||||
virtual void onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t& length
|
||||
) override;
|
||||
|
||||
/**
|
||||
* Changes action of the HVAC (it's displayed in the Home Assistant panel).
|
||||
*
|
||||
* @param action New action.
|
||||
* @returns Returns true if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setAction(Action action);
|
||||
|
||||
/**
|
||||
* Returns action that was previously sent to MQTT.
|
||||
*/
|
||||
inline Action getAction() const
|
||||
{ return _action; }
|
||||
|
||||
/**
|
||||
* Changes default temperature unit.
|
||||
* Please note that this method must be called before `mqtt.begin(...)`.
|
||||
*
|
||||
* @param unit See TemperatureUnit enum above
|
||||
*/
|
||||
inline void setTemperatureUnit(TemperatureUnit unit)
|
||||
{ _temperatureUnit = unit; }
|
||||
|
||||
/**
|
||||
* Publishes aux heating state.
|
||||
* Please note that HAHVAC::AuxHeatingFeature must be set in the constructor.
|
||||
*
|
||||
* @param state ON (true) / OFF (false)
|
||||
* @returns Returns true if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setAuxHeatingState(bool state);
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the aux heating's state changes.
|
||||
* Please note that it's not possible to register multiple callbacks.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onAuxHeatingStateChanged(HAHVAC_STATE_CALLBACK_BOOL(callback))
|
||||
{ _auxHeatingCallback = callback; }
|
||||
|
||||
/**
|
||||
* Returns state of the aux heating.
|
||||
*
|
||||
* @returns ON (true) / OFF (false)
|
||||
*/
|
||||
inline bool getAuxHeatingState() const
|
||||
{ return _auxHeatingState; }
|
||||
|
||||
/**
|
||||
* Publishes away state.
|
||||
* Please note that HAHVAC::AwayModeFeature must be set in the constructor.
|
||||
*
|
||||
* @param state ON (true) / OFF (false)
|
||||
* @returns Returns true if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setAwayState(bool state);
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the away's state changes.
|
||||
* Please note that it's not possible to register multiple callbacks.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onAwayStateChanged(HAHVAC_STATE_CALLBACK_BOOL(callback))
|
||||
{ _awayCallback = callback; }
|
||||
|
||||
/**
|
||||
* Returns away's state.
|
||||
*
|
||||
* @returns ON (true) / OFF (false)
|
||||
*/
|
||||
inline bool getAwayState() const
|
||||
{ return _awayState; }
|
||||
|
||||
/**
|
||||
* Publishes hold state.
|
||||
* Please note that HAHVAC::HoldFeature must be set in the constructor.
|
||||
*
|
||||
* @param state ON (true) / OFF (false)
|
||||
* @returns Returns true if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setHoldState(bool state);
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the hold's state changes.
|
||||
* Please note that it's not possible to register multiple callbacks.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onHoldStateChanged(HAHVAC_STATE_CALLBACK_BOOL(callback))
|
||||
{ _holdCallback = callback; }
|
||||
|
||||
/**
|
||||
* Returns hold's state.
|
||||
*
|
||||
* @returns ON (true) / OFF (false)
|
||||
*/
|
||||
inline bool getHoldState() const
|
||||
{ return _holdState; }
|
||||
|
||||
/**
|
||||
* Publishes given target temperature.
|
||||
*
|
||||
* @param targetTemperature
|
||||
* @returns Returns true if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setTargetTemperature(double targetTemperature);
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the target temperature changes
|
||||
* Please note that it's not possible to register multiple callbacks.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onTargetTemperatureChanged(HAHVAC_STATE_CALLBACK_DOUBLE(callback))
|
||||
{ _targetTempCallback = callback; }
|
||||
|
||||
/**
|
||||
* Returns target temperature.
|
||||
*
|
||||
* @returns Target temperature or __DBL_MAX__ if temperature is not set.
|
||||
*/
|
||||
inline double getTargetTemperature() const
|
||||
{ return _targetTemperature; }
|
||||
|
||||
/**
|
||||
* Publishes current temperature.
|
||||
*
|
||||
* @param temperature
|
||||
* @returns Returns true if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setCurrentTemperature(double temperature);
|
||||
|
||||
/**
|
||||
* Return temperature that was previously set by `setCurrentTemperature` method.
|
||||
*
|
||||
* @returns Temperature or __DBL_MAX__ if temperature is not set.
|
||||
*/
|
||||
inline double getCurrentTemperature() const
|
||||
{ return _currentTemperature; }
|
||||
|
||||
/**
|
||||
* Publishes working mode of the HVAC.
|
||||
*
|
||||
* @param mode
|
||||
* @returns Returns true if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setMode(Mode mode);
|
||||
|
||||
/**
|
||||
* Same as above but input is the string representation of the mode.
|
||||
*
|
||||
* @param mode
|
||||
* @returns Returns true if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setModeFromStr(const char* mode);
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the mode changes.
|
||||
* Please note that it's not possible to register multiple callbacks.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onModeChanged(HAHVAC_STATE_CALLBACK_MODE(callback))
|
||||
{ _modeChangedCallback = callback; }
|
||||
|
||||
/**
|
||||
* Returns HVAC's mode.
|
||||
*
|
||||
* @returns It may be UnknownMode if it's not set.
|
||||
*/
|
||||
inline Mode getMode() const
|
||||
{ return _currentMode; }
|
||||
|
||||
/**
|
||||
* Sets name that wil be displayed in the Home Assistant panel.
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
inline void setName(const char* name)
|
||||
{ _label = name; } // it needs to be called "label" as "_name" is already in use
|
||||
|
||||
/**
|
||||
* Sets the list of supported modes. By default the list contains all available modes.
|
||||
* You can merge multiple modes as following: `setModes(HAHVAC::OffMode | HAHVAC::CoolMode)`
|
||||
*
|
||||
* @param modes
|
||||
*/
|
||||
inline void setModes(uint8_t modes)
|
||||
{ _modes = modes; }
|
||||
|
||||
/**
|
||||
* Sets `retain` flag for commands published by Home Assistant.
|
||||
* By default it's set to false.
|
||||
*
|
||||
* @param retain
|
||||
*/
|
||||
inline void setRetain(bool retain)
|
||||
{ _retain = retain; }
|
||||
|
||||
/**
|
||||
* Sets the minimum temperature that user will be able to select in Home Assistant panel.
|
||||
*
|
||||
* @param minTemp
|
||||
*/
|
||||
bool setMinTemp(double minTemp);
|
||||
|
||||
/**
|
||||
* Sets the maximum temperature that user will be able to select in Home Assistant panel.
|
||||
*
|
||||
* @param minTemp
|
||||
*/
|
||||
bool setMaxTemp(double maxTemp);
|
||||
|
||||
/**
|
||||
* Sets the step of the temperature's picker in the Home Assistant panel.
|
||||
*
|
||||
* @param tempStep
|
||||
*/
|
||||
bool setTempStep(double tempStep);
|
||||
|
||||
private:
|
||||
bool publishAction(Action action);
|
||||
bool publishAuxHeatingState(bool state);
|
||||
bool publishAwayState(bool state);
|
||||
bool publishHoldState(bool state);
|
||||
bool publishCurrentTemperature(double temperature);
|
||||
bool publishTargetTemperature(double temperature);
|
||||
bool publishMode(Mode mode);
|
||||
void subscribeTopics();
|
||||
uint16_t calculateSerializedLength(const char* serializedDevice) const override;
|
||||
uint16_t calculateModesLength() const;
|
||||
bool writeSerializedData(const char* serializedDevice) const override;
|
||||
|
||||
const char* _uniqueId;
|
||||
const uint8_t _features;
|
||||
TemperatureUnit _temperatureUnit;
|
||||
Action _action;
|
||||
HAHVAC_STATE_CALLBACK_BOOL(_auxHeatingCallback);
|
||||
bool _auxHeatingState;
|
||||
HAHVAC_STATE_CALLBACK_BOOL(_awayCallback);
|
||||
bool _awayState;
|
||||
HAHVAC_STATE_CALLBACK_BOOL(_holdCallback);
|
||||
bool _holdState;
|
||||
double _currentTemperature;
|
||||
double _minTemp;
|
||||
double _maxTemp;
|
||||
double _tempStep;
|
||||
HAHVAC_STATE_CALLBACK_DOUBLE(_targetTempCallback);
|
||||
double _targetTemperature;
|
||||
const char* _label;
|
||||
uint8_t _modes;
|
||||
HAHVAC_STATE_CALLBACK_MODE(_modeChangedCallback);
|
||||
Mode _currentMode;
|
||||
bool _retain;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
+89
-239
@@ -3,138 +3,90 @@
|
||||
#endif
|
||||
|
||||
#include "HASensor.h"
|
||||
#include "../ArduinoHADefines.h"
|
||||
#ifdef ARDUINOHA_SENSOR
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../HADevice.h"
|
||||
#include "../HAUtils.h"
|
||||
|
||||
template <typename T>
|
||||
HASensor<T>::HASensor(
|
||||
const char* name,
|
||||
T initialValue,
|
||||
HAMqtt& mqtt
|
||||
) :
|
||||
BaseDeviceType(mqtt, "sensor", name),
|
||||
HASensor::HASensor(const char* name) :
|
||||
BaseDeviceType("sensor", name),
|
||||
_class(nullptr),
|
||||
_units(nullptr),
|
||||
_valueType(HAUtils::determineValueType<T>()),
|
||||
_currentValue(initialValue)
|
||||
_icon(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
HASensor<T>::HASensor(
|
||||
const char* name,
|
||||
const char* deviceClass,
|
||||
T initialValue,
|
||||
HAMqtt& mqtt
|
||||
) :
|
||||
BaseDeviceType(mqtt, "sensor", name),
|
||||
_class(deviceClass),
|
||||
_units(nullptr),
|
||||
_valueType(HAUtils::determineValueType<T>()),
|
||||
_currentValue(initialValue)
|
||||
HASensor::HASensor(const char* name, HAMqtt& mqtt) :
|
||||
HASensor(name)
|
||||
{
|
||||
|
||||
(void)mqtt;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void HASensor<T>::onMqttConnected()
|
||||
void HASensor::onMqttConnected()
|
||||
{
|
||||
if (strlen(name()) == 0 ||
|
||||
_valueType == HAUtils::ValueTypeUnknown) {
|
||||
if (strlen(name()) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishValue(_currentValue);
|
||||
publishAvailability();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool HASensor<T>::setValue(T value)
|
||||
bool HASensor::setValue(const char* value)
|
||||
{
|
||||
if (strlen(name()) == 0 ||
|
||||
_valueType == HAUtils::ValueTypeUnknown) {
|
||||
return publishValue(value);
|
||||
}
|
||||
|
||||
bool HASensor::setValue(uint32_t value)
|
||||
{
|
||||
uint8_t digitsNb = floor(log10(value)) + 1;
|
||||
char str[digitsNb + 1]; // + null terminator
|
||||
memset(str, 0, sizeof(str));
|
||||
itoa(value, str, 10);
|
||||
|
||||
return publishValue(str);
|
||||
}
|
||||
|
||||
bool HASensor::setValue(int32_t value)
|
||||
{
|
||||
uint8_t digitsNb = floor(log10(value)) + 1;
|
||||
char str[digitsNb + 2]; // + null terminator and minus sign
|
||||
memset(str, 0, sizeof(str));
|
||||
itoa(value, str, 10);
|
||||
|
||||
return publishValue(str);
|
||||
}
|
||||
|
||||
bool HASensor::setValue(double value, uint8_t precision)
|
||||
{
|
||||
uint8_t digitsNb = floor(log10(floor(value))) + 1;
|
||||
char str[digitsNb + 3 + precision]; // null terminator, dot, minus sign
|
||||
dtostrf(value, 0, precision, str);
|
||||
|
||||
return publishValue(str);
|
||||
}
|
||||
|
||||
bool HASensor::setValue(float value, uint8_t precision)
|
||||
{
|
||||
uint8_t digitsNb = floor(log10(floor(value))) + 1;
|
||||
char str[digitsNb + 3 + precision]; // null terminator, dot, minus sign
|
||||
dtostrf(value, 0, precision, str);
|
||||
|
||||
return publishValue(str);
|
||||
}
|
||||
|
||||
bool HASensor::publishValue(const char* value)
|
||||
{
|
||||
if (strlen(name()) == 0 || value == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_currentValue == value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishValue(value)) {
|
||||
_currentValue = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void HASensor<T>::publishConfig()
|
||||
{
|
||||
if (_valueType == HAUtils::ValueTypeUnknown) {
|
||||
return;
|
||||
}
|
||||
|
||||
const HADevice* device = mqtt()->getDevice();
|
||||
if (device == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t& deviceLength = device->calculateSerializedLength();
|
||||
if (deviceLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char serializedDevice[deviceLength];
|
||||
if (device->serialize(serializedDevice) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
const uint16_t& dataLength = calculateSerializedLength(serializedDevice);
|
||||
|
||||
if (topicLength == 0 || dataLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char topic[topicLength];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mqtt()->beginPublish(topic, dataLength, true)) {
|
||||
writeSerializedData(serializedDevice);
|
||||
mqtt()->endPublish();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool HASensor<T>::publishValue(T value)
|
||||
{
|
||||
if (strlen(name()) == 0 ||
|
||||
_valueType == HAUtils::ValueTypeUnknown) {
|
||||
if (!mqtt()->isConnected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
@@ -145,7 +97,6 @@ bool HASensor<T>::publishValue(T value)
|
||||
|
||||
char topic[topicSize];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
@@ -156,26 +107,14 @@ bool HASensor<T>::publishValue(T value)
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t& valueLength = calculateValueLength();
|
||||
if (valueLength == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char valueStr[valueLength];
|
||||
if (!valueToStr(valueStr, value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return mqtt()->publish(topic, valueStr, true);
|
||||
return mqtt()->publish(topic, value, true);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
uint16_t HASensor<T>::calculateSerializedLength(
|
||||
uint16_t HASensor::calculateSerializedLength(
|
||||
const char* serializedDevice
|
||||
) const
|
||||
{
|
||||
if (serializedDevice == nullptr ||
|
||||
_valueType == HAUtils::ValueTypeUnknown) {
|
||||
if (serializedDevice == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -187,20 +126,12 @@ uint16_t HASensor<T>::calculateSerializedLength(
|
||||
uint16_t size = 0;
|
||||
size += DeviceTypeSerializer::calculateBaseJsonDataSize();
|
||||
size += DeviceTypeSerializer::calculateNameFieldSize(name());
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(device, name());
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(name());
|
||||
size += DeviceTypeSerializer::calculateDeviceFieldSize(serializedDevice);
|
||||
|
||||
if (isAvailabilityConfigured()) {
|
||||
size += DeviceTypeSerializer::calculateAvailabilityFieldSize(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name()
|
||||
);
|
||||
}
|
||||
size += DeviceTypeSerializer::calculateAvailabilityFieldSize(this);
|
||||
|
||||
{
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic,
|
||||
@@ -227,142 +158,61 @@ uint16_t HASensor<T>::calculateSerializedLength(
|
||||
size += strlen(_units) + 18; // 18 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
// icon
|
||||
if (_icon != nullptr) {
|
||||
// Field format: ,"ic":"[ICON]"
|
||||
size += strlen(_icon) + 8; // 8 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
return size; // exludes null terminator
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool HASensor<T>::writeSerializedData(const char* serializedDevice) const
|
||||
bool HASensor::writeSerializedData(const char* serializedDevice) const
|
||||
{
|
||||
if (serializedDevice == nullptr ||
|
||||
_valueType == HAUtils::ValueTypeUnknown) {
|
||||
if (serializedDevice == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteBeginningJson(mqtt());
|
||||
DeviceTypeSerializer::mqttWriteBeginningJson();
|
||||
|
||||
// state topic
|
||||
{
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char topic[topicSize];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char Prefix[] PROGMEM = {"\"stat_t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, topic);
|
||||
DeviceTypeSerializer::mqttWriteTopicField(
|
||||
this,
|
||||
Prefix,
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
}
|
||||
|
||||
// device class
|
||||
if (_class != nullptr) {
|
||||
static const char Prefix[] PROGMEM = {",\"dev_cla\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, _class);
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(Prefix, _class);
|
||||
}
|
||||
|
||||
// units of measurement
|
||||
if (_units != nullptr) {
|
||||
static const char Prefix[] PROGMEM = {",\"unit_of_meas\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, _units);
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(Prefix, _units);
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteNameField(mqtt(), name());
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(mqtt(), name());
|
||||
|
||||
if (isAvailabilityConfigured()) {
|
||||
DeviceTypeSerializer::mqttWriteAvailabilityField(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name()
|
||||
// icon
|
||||
if (_icon != nullptr) {
|
||||
static const char Prefix[] PROGMEM = {",\"ic\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(
|
||||
Prefix,
|
||||
_icon
|
||||
);
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(mqtt(), serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson(mqtt());
|
||||
DeviceTypeSerializer::mqttWriteNameField(name());
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(name());
|
||||
DeviceTypeSerializer::mqttWriteAvailabilityField(this);
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
uint16_t HASensor<T>::calculateValueLength() const
|
||||
{
|
||||
if (_valueType == HAUtils::ValueTypeUnknown) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t size = 0;
|
||||
|
||||
switch (_valueType) {
|
||||
case HAUtils::ValueTypeUint8:
|
||||
size = 3; // from 0 to 255
|
||||
break;
|
||||
|
||||
case HAUtils::ValueTypeUint16:
|
||||
size = 5; // from 0 to 65535
|
||||
break;
|
||||
|
||||
case HAUtils::ValueTypeUint32:
|
||||
size = 10; // from 0 to 4294967295
|
||||
break;
|
||||
|
||||
case HAUtils::ValueTypeInt8:
|
||||
size = 4; // from -128 to 127
|
||||
break;
|
||||
|
||||
case HAUtils::ValueTypeInt16:
|
||||
size = 6; // from -32768 to 32767
|
||||
break;
|
||||
|
||||
case HAUtils::ValueTypeInt32:
|
||||
size = 11; // from -2147483648 to 2147483647
|
||||
break;
|
||||
|
||||
case HAUtils::ValueTypeDouble:
|
||||
case HAUtils::ValueTypeFloat:
|
||||
// 3 digits per byte + dot separator + 2 precision digits
|
||||
return (HAUtils::getValueTypeLength(_valueType) * 3) + 3;
|
||||
}
|
||||
|
||||
return (size > 0 ? (size + 1) : 0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool HASensor<T>::valueToStr(char* dst, T value) const
|
||||
{
|
||||
if (_valueType == HAUtils::ValueTypeUnknown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (_valueType) {
|
||||
case HAUtils::ValueTypeUint8:
|
||||
case HAUtils::ValueTypeUint16:
|
||||
case HAUtils::ValueTypeUint32:
|
||||
case HAUtils::ValueTypeInt8:
|
||||
case HAUtils::ValueTypeInt16:
|
||||
case HAUtils::ValueTypeInt32:
|
||||
itoa(value, dst, 10);
|
||||
break;
|
||||
|
||||
case HAUtils::ValueTypeDouble:
|
||||
case HAUtils::ValueTypeFloat:
|
||||
dtostrf(value, 0, 2, dst);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
+43
-37
@@ -2,9 +2,9 @@
|
||||
#define AHA_HASENSOR_H
|
||||
|
||||
#include "BaseDeviceType.h"
|
||||
#include "../HAUtils.h"
|
||||
|
||||
template <typename T>
|
||||
#ifdef ARDUINOHA_SENSOR
|
||||
|
||||
class HASensor : public BaseDeviceType
|
||||
{
|
||||
public:
|
||||
@@ -12,30 +12,14 @@ public:
|
||||
* Initializes binary sensor.
|
||||
*
|
||||
* @param name Name of the sensor. Recommendes characters: [a-z0-9\-_]
|
||||
* @param initialValue Initial value of the sensor.
|
||||
It will be published right after "config" message in order to update HA state.
|
||||
*/
|
||||
HASensor(
|
||||
const char* name,
|
||||
T initialValue,
|
||||
HAMqtt& mqtt
|
||||
const char* name
|
||||
);
|
||||
|
||||
/**
|
||||
* Initializes binary sensor with the specified class.
|
||||
* You can find list of available values here: https://www.home-assistant.io/integrations/binary_sensor/#device-class
|
||||
*
|
||||
* @param name Name of the sensor. Recommendes characters: [a-z0-9\-_]
|
||||
* @param deviceClass Name of the class (lower case).
|
||||
* @param initialValue Initial value of the sensor.
|
||||
It will be published right after "config" message in order to update HA state.
|
||||
*/
|
||||
HASensor(
|
||||
const char* name,
|
||||
const char* deviceClass,
|
||||
T initialValue,
|
||||
HAMqtt& mqtt
|
||||
);
|
||||
); // legacy constructor
|
||||
|
||||
/**
|
||||
* Publishes configuration of the sensor to the MQTT.
|
||||
@@ -43,21 +27,38 @@ public:
|
||||
virtual void onMqttConnected() override;
|
||||
|
||||
/**
|
||||
* Changes state of the sensor and publishes MQTT message.
|
||||
* Please note that if a new value is the same as previous one,
|
||||
* the MQTT message won't be published.
|
||||
* Publishes new value of the sensor.
|
||||
* Please note that connection to MQTT broker must be acquired.
|
||||
* Otherwise method will return false.
|
||||
*
|
||||
* @param state New state of the sensor.
|
||||
* @param state Value to publish.
|
||||
* @returns Returns true if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setValue(T value);
|
||||
bool setValue(const char* value);
|
||||
bool setValue(uint32_t value);
|
||||
bool setValue(int32_t value);
|
||||
bool setValue(double value, uint8_t precision = 2);
|
||||
bool setValue(float value, uint8_t precision = 2);
|
||||
|
||||
inline bool setValue(uint8_t value)
|
||||
{ return setValue(static_cast<uint32_t>(value)); }
|
||||
|
||||
inline bool setValue(uint16_t value)
|
||||
{ return setValue(static_cast<uint32_t>(value)); }
|
||||
|
||||
inline bool setValue(int8_t value)
|
||||
{ return setValue(static_cast<int32_t>(value)); }
|
||||
|
||||
inline bool setValue(int16_t value)
|
||||
{ return setValue(static_cast<int32_t>(value)); }
|
||||
|
||||
/**
|
||||
* Returns last known state of the sensor.
|
||||
* If setState method wasn't called the initial value will be returned.
|
||||
* The type/class of the sensor to set the icon in the frontend.
|
||||
*
|
||||
* @param className https://www.home-assistant.io/integrations/sensor/#device-class
|
||||
*/
|
||||
inline T getValue() const
|
||||
{ return _currentValue; }
|
||||
inline void setDeviceClass(const char* className)
|
||||
{ _class = className; }
|
||||
|
||||
/**
|
||||
* Defines the units of measurement of the sensor, if any.
|
||||
@@ -67,18 +68,23 @@ public:
|
||||
inline void setUnitOfMeasurement(const char* units)
|
||||
{ _units = units; }
|
||||
|
||||
/**
|
||||
* Sets icon of the sensor, e.g. `mdi:home`.
|
||||
*
|
||||
* @param icon Material Design Icon name with mdi: prefix.
|
||||
*/
|
||||
inline void setIcon(const char* icon)
|
||||
{ _icon = icon; }
|
||||
|
||||
private:
|
||||
void publishConfig();
|
||||
bool publishValue(T value);
|
||||
uint16_t calculateSerializedLength(const char* serializedDevice) const;
|
||||
bool writeSerializedData(const char* serializedDevice) const;
|
||||
uint16_t calculateValueLength() const;
|
||||
bool valueToStr(char* dst, T value) const;
|
||||
bool publishValue(const char* value);
|
||||
uint16_t calculateSerializedLength(const char* serializedDevice) const override;
|
||||
bool writeSerializedData(const char* serializedDevice) const override;
|
||||
|
||||
const char* _class;
|
||||
const char* _units;
|
||||
HAUtils::ValueType _valueType;
|
||||
T _currentValue;
|
||||
const char* _icon;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+77
-213
@@ -1,17 +1,30 @@
|
||||
#include "HASwitch.h"
|
||||
#ifdef ARDUINOHA_SWITCH
|
||||
|
||||
#include "../ArduinoHADefines.h"
|
||||
#include "../HAMqtt.h"
|
||||
#include "../HADevice.h"
|
||||
#include "../HAUtils.h"
|
||||
|
||||
HASwitch::HASwitch(const char* name, bool initialState, HAMqtt& mqtt) :
|
||||
BaseDeviceType(mqtt, "switch", name),
|
||||
HASwitch::HASwitch(const char* name, bool initialState) :
|
||||
BaseDeviceType("switch", name),
|
||||
_stateCallback(nullptr),
|
||||
_currentState(initialState)
|
||||
_currentState(initialState),
|
||||
_icon(nullptr),
|
||||
_retain(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HASwitch::HASwitch(
|
||||
const char* name,
|
||||
bool initialState,
|
||||
HAMqtt& mqtt
|
||||
) :
|
||||
HASwitch(name, initialState)
|
||||
{
|
||||
(void)mqtt;
|
||||
}
|
||||
|
||||
void HASwitch::onMqttConnected()
|
||||
{
|
||||
if (strlen(name()) == 0) {
|
||||
@@ -19,9 +32,16 @@ void HASwitch::onMqttConnected()
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishState(_currentState);
|
||||
subscribeCommandTopic();
|
||||
publishAvailability();
|
||||
|
||||
if (!_retain) {
|
||||
publishState(_currentState);
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttSubscribeTopic(
|
||||
this,
|
||||
DeviceTypeSerializer::CommandTopic
|
||||
);
|
||||
}
|
||||
|
||||
void HASwitch::onMqttMessage(
|
||||
@@ -30,170 +50,50 @@ void HASwitch::onMqttMessage(
|
||||
const uint16_t& length
|
||||
)
|
||||
{
|
||||
if (strlen(name()) == 0) {
|
||||
return;
|
||||
}
|
||||
(void)payload;
|
||||
|
||||
static const char Slash[] PROGMEM = {"/"};
|
||||
// name + cmd topic + two slashes + null terminator
|
||||
uint8_t suffixLength = strlen(name()) + strlen(DeviceTypeSerializer::CommandTopic) + 3;
|
||||
char suffix[suffixLength];
|
||||
|
||||
strcpy_P(suffix, Slash);
|
||||
strcat(suffix, name());
|
||||
strcat_P(suffix, Slash);
|
||||
strcat(suffix, DeviceTypeSerializer::CommandTopic);
|
||||
|
||||
if (HAUtils::endsWith(topic, suffix)) {
|
||||
bool onState = (length == strlen(DeviceTypeSerializer::StateOn));
|
||||
setState(onState);
|
||||
if (isMyTopic(topic, DeviceTypeSerializer::CommandTopic)) {
|
||||
bool state = (length == strlen(DeviceTypeSerializer::StateOn));
|
||||
setState(state, true);
|
||||
}
|
||||
}
|
||||
|
||||
bool HASwitch::setState(bool state)
|
||||
bool HASwitch::setState(bool state, bool force)
|
||||
{
|
||||
if (_currentState == state) {
|
||||
if (!force && _currentState == state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strlen(name()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (publishState(state)) {
|
||||
_currentState = state;
|
||||
triggerCallback(_currentState);
|
||||
|
||||
if (_stateCallback) {
|
||||
_stateCallback(state, this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HASwitch::triggerCallback(bool state)
|
||||
{
|
||||
if (_stateCallback == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
_stateCallback(state, this);
|
||||
}
|
||||
|
||||
void HASwitch::publishConfig()
|
||||
{
|
||||
const HADevice* device = mqtt()->getDevice();
|
||||
if (device == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t& deviceLength = device->calculateSerializedLength();
|
||||
if (deviceLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char serializedDevice[deviceLength];
|
||||
if (device->serialize(serializedDevice) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
const uint16_t& dataLength = calculateSerializedLength(serializedDevice);
|
||||
|
||||
if (topicLength == 0 || dataLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char topic[topicLength];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mqtt()->beginPublish(topic, dataLength, true)) {
|
||||
writeSerializedData(serializedDevice);
|
||||
mqtt()->endPublish();
|
||||
}
|
||||
}
|
||||
|
||||
bool HASwitch::publishState(bool state)
|
||||
{
|
||||
if (strlen(name()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char topic[topicSize];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return mqtt()->publish(
|
||||
topic,
|
||||
return DeviceTypeSerializer::mqttPublishMessage(
|
||||
this,
|
||||
DeviceTypeSerializer::StateTopic,
|
||||
(
|
||||
state ?
|
||||
DeviceTypeSerializer::StateOn :
|
||||
DeviceTypeSerializer::StateOff
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void HASwitch::subscribeCommandTopic()
|
||||
{
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::CommandTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char topic[topicSize];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::CommandTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
mqtt()->subscribe(topic);
|
||||
}
|
||||
|
||||
uint16_t HASwitch::calculateSerializedLength(const char* serializedDevice) const
|
||||
{
|
||||
if (serializedDevice == nullptr) {
|
||||
@@ -208,21 +108,14 @@ uint16_t HASwitch::calculateSerializedLength(const char* serializedDevice) const
|
||||
uint16_t size = 0;
|
||||
size += DeviceTypeSerializer::calculateBaseJsonDataSize();
|
||||
size += DeviceTypeSerializer::calculateNameFieldSize(name());
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(device, name());
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(name());
|
||||
size += DeviceTypeSerializer::calculateDeviceFieldSize(serializedDevice);
|
||||
|
||||
if (isAvailabilityConfigured()) {
|
||||
size += DeviceTypeSerializer::calculateAvailabilityFieldSize(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name()
|
||||
);
|
||||
}
|
||||
size += DeviceTypeSerializer::calculateAvailabilityFieldSize(this);
|
||||
size += DeviceTypeSerializer::calculateRetainFieldSize(_retain);
|
||||
|
||||
// cmd topic
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::CommandTopic,
|
||||
@@ -240,7 +133,6 @@ uint16_t HASwitch::calculateSerializedLength(const char* serializedDevice) const
|
||||
// state topic
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic,
|
||||
@@ -255,6 +147,12 @@ uint16_t HASwitch::calculateSerializedLength(const char* serializedDevice) const
|
||||
size += topicLength + 12; // 12 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
// icon
|
||||
if (_icon != nullptr) {
|
||||
// Field format: ,"ic":"[ICON]"
|
||||
size += strlen(_icon) + 8; // 8 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
return size; // exludes null terminator
|
||||
}
|
||||
|
||||
@@ -264,79 +162,45 @@ bool HASwitch::writeSerializedData(const char* serializedDevice) const
|
||||
return false;
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteBeginningJson(mqtt());
|
||||
DeviceTypeSerializer::mqttWriteBeginningJson();
|
||||
|
||||
// command topic
|
||||
{
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::CommandTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char topic[topicSize];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::CommandTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char Prefix[] PROGMEM = {"\"cmd_t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, topic);
|
||||
DeviceTypeSerializer::mqttWriteTopicField(
|
||||
this,
|
||||
Prefix,
|
||||
DeviceTypeSerializer::CommandTopic
|
||||
);
|
||||
}
|
||||
|
||||
// state topic
|
||||
{
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char topic[topicSize];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char Prefix[] PROGMEM = {",\"stat_t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, topic);
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteNameField(mqtt(), name());
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(mqtt(), name());
|
||||
|
||||
if (isAvailabilityConfigured()) {
|
||||
DeviceTypeSerializer::mqttWriteAvailabilityField(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name()
|
||||
DeviceTypeSerializer::mqttWriteTopicField(
|
||||
this,
|
||||
Prefix,
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(mqtt(), serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson(mqtt());
|
||||
// icon
|
||||
if (_icon != nullptr) {
|
||||
static const char Prefix[] PROGMEM = {",\"ic\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(
|
||||
Prefix,
|
||||
_icon
|
||||
);
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteRetainField(_retain);
|
||||
DeviceTypeSerializer::mqttWriteNameField(name());
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(name());
|
||||
DeviceTypeSerializer::mqttWriteAvailabilityField(this);
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+36
-12
@@ -3,7 +3,9 @@
|
||||
|
||||
#include "BaseDeviceType.h"
|
||||
|
||||
#define HASWITCH_CALLBACK void (*callback)(bool, HASwitch*)
|
||||
#ifdef ARDUINOHA_SWITCH
|
||||
|
||||
#define HASWITCH_CALLBACK(name) void (*name)(bool, HASwitch*)
|
||||
|
||||
class HASwitch : public BaseDeviceType
|
||||
{
|
||||
@@ -15,11 +17,15 @@ public:
|
||||
* @param initialState Initial state of the switch.
|
||||
It will be published right after "config" message in order to update HA state.
|
||||
*/
|
||||
HASwitch(
|
||||
const char* name,
|
||||
bool initialState
|
||||
);
|
||||
HASwitch(
|
||||
const char* name,
|
||||
bool initialState,
|
||||
HAMqtt& mqtt
|
||||
);
|
||||
); // legacy constructor
|
||||
|
||||
/**
|
||||
* Publishes configuration of the sensor to the MQTT.
|
||||
@@ -48,9 +54,10 @@ public:
|
||||
* the MQTT message won't be published.
|
||||
*
|
||||
* @param state New state of the switch.
|
||||
* @param force Forces to update state without comparing it to previous known state.
|
||||
* @returns Returns true if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setState(bool state);
|
||||
bool setState(bool state, bool force = false);
|
||||
|
||||
/**
|
||||
* Alias for setState(true).
|
||||
@@ -77,19 +84,36 @@ public:
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onStateChanged(HASWITCH_CALLBACK)
|
||||
inline void onStateChanged(HASWITCH_CALLBACK(callback))
|
||||
{ _stateCallback = callback; }
|
||||
|
||||
private:
|
||||
void triggerCallback(bool state);
|
||||
void publishConfig();
|
||||
bool publishState(bool state);
|
||||
void subscribeCommandTopic();
|
||||
uint16_t calculateSerializedLength(const char* serializedDevice) const;
|
||||
bool writeSerializedData(const char* serializedDevice) const;
|
||||
/**
|
||||
* Sets icon of the switch, e.g. `mdi:home`.
|
||||
*
|
||||
* @param icon Material Design Icon name with mdi: prefix.
|
||||
*/
|
||||
inline void setIcon(const char* icon)
|
||||
{ _icon = icon; }
|
||||
|
||||
void (*_stateCallback)(bool, HASwitch*);
|
||||
/**
|
||||
* Sets `retain` flag for commands published by Home Assistant.
|
||||
* By default it's set to false.
|
||||
*
|
||||
* @param retain
|
||||
*/
|
||||
inline void setRetain(bool retain)
|
||||
{ _retain = retain; }
|
||||
|
||||
private:
|
||||
bool publishState(bool state);
|
||||
uint16_t calculateSerializedLength(const char* serializedDevice) const override;
|
||||
bool writeSerializedData(const char* serializedDevice) const override;
|
||||
|
||||
HASWITCH_CALLBACK(_stateCallback);
|
||||
bool _currentState;
|
||||
const char* _icon;
|
||||
bool _retain;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
#include "HATagScanner.h"
|
||||
#ifdef ARDUINOHA_TAG_SCANNER
|
||||
|
||||
#include "../ArduinoHADefines.h"
|
||||
#include "../HAMqtt.h"
|
||||
#include "../HADevice.h"
|
||||
#include "../HAUtils.h"
|
||||
|
||||
HATagScanner::HATagScanner(const char* name, HAMqtt& mqtt) :
|
||||
BaseDeviceType(mqtt, "tag", name)
|
||||
HATagScanner::HATagScanner(const char* name) :
|
||||
BaseDeviceType("tag", name)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HATagScanner::HATagScanner(const char* name, HAMqtt& mqtt) :
|
||||
HATagScanner(name)
|
||||
{
|
||||
(void)mqtt;
|
||||
}
|
||||
|
||||
void HATagScanner::onMqttConnected()
|
||||
{
|
||||
if (strlen(name()) == 0) {
|
||||
@@ -21,12 +28,11 @@ void HATagScanner::onMqttConnected()
|
||||
|
||||
bool HATagScanner::tagScanned(const char* tag)
|
||||
{
|
||||
if (tag == nullptr || strlen(tag) == 0) {
|
||||
if (tag == nullptr || strlen(tag) == 0 || strlen(name()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::EventTopic
|
||||
@@ -37,7 +43,6 @@ bool HATagScanner::tagScanned(const char* tag)
|
||||
|
||||
char topic[topicSize];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
@@ -51,54 +56,6 @@ bool HATagScanner::tagScanned(const char* tag)
|
||||
return mqtt()->publish(topic, tag);
|
||||
}
|
||||
|
||||
void HATagScanner::publishConfig()
|
||||
{
|
||||
const HADevice* device = mqtt()->getDevice();
|
||||
if (device == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t& deviceLength = device->calculateSerializedLength();
|
||||
if (deviceLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char serializedDevice[deviceLength];
|
||||
if (device->serialize(serializedDevice) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
const uint16_t& dataLength = calculateSerializedLength(serializedDevice);
|
||||
|
||||
if (topicLength == 0 || dataLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char topic[topicLength];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mqtt()->beginPublish(topic, dataLength, true)) {
|
||||
writeSerializedData(serializedDevice);
|
||||
mqtt()->endPublish();
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t HATagScanner::calculateSerializedLength(
|
||||
const char* serializedDevice
|
||||
) const
|
||||
@@ -114,7 +71,6 @@ uint16_t HATagScanner::calculateSerializedLength(
|
||||
// event topic
|
||||
{
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::EventTopic,
|
||||
@@ -134,31 +90,22 @@ bool HATagScanner::writeSerializedData(const char* serializedDevice) const
|
||||
return false;
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteBeginningJson(mqtt());
|
||||
DeviceTypeSerializer::mqttWriteBeginningJson();
|
||||
|
||||
// topic
|
||||
{
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::EventTopic
|
||||
);
|
||||
char topic[topicSize];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::EventTopic
|
||||
);
|
||||
|
||||
static const char Prefix[] PROGMEM = {"\"t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, topic);
|
||||
DeviceTypeSerializer::mqttWriteTopicField(
|
||||
this,
|
||||
Prefix,
|
||||
DeviceTypeSerializer::EventTopic
|
||||
);
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(mqtt(), serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson(mqtt());
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "BaseDeviceType.h"
|
||||
|
||||
#ifdef ARDUINOHA_TAG_SCANNER
|
||||
|
||||
class HATagScanner : public BaseDeviceType
|
||||
{
|
||||
public:
|
||||
@@ -11,7 +13,8 @@ public:
|
||||
*
|
||||
* @param name Name of the scanner. Recommendes characters: [a-z0-9\-_]
|
||||
*/
|
||||
HATagScanner(const char* name, HAMqtt& mqtt);
|
||||
HATagScanner(const char* name);
|
||||
HATagScanner(const char* name, HAMqtt& mqtt); // legacy constructor
|
||||
|
||||
/**
|
||||
* Publishes configuration of the sensor to the MQTT.
|
||||
@@ -21,7 +24,7 @@ public:
|
||||
/**
|
||||
* Tag scanner doesn't support availability. Nothing to do here.
|
||||
*/
|
||||
virtual void setAvailability(bool online) override { }
|
||||
virtual void setAvailability(bool online) override { (void)online; }
|
||||
|
||||
/**
|
||||
* Sends "tag scanned" event to the MQTT (Home Assistant).
|
||||
@@ -32,9 +35,9 @@ public:
|
||||
bool tagScanned(const char* tag);
|
||||
|
||||
private:
|
||||
void publishConfig();
|
||||
uint16_t calculateSerializedLength(const char* serializedDevice) const;
|
||||
bool writeSerializedData(const char* serializedDevice) const;
|
||||
uint16_t calculateSerializedLength(const char* serializedDevice) const override;
|
||||
bool writeSerializedData(const char* serializedDevice) const override;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
#include "HATriggers.h"
|
||||
#ifdef ARDUINOHA_TRIGGERS
|
||||
|
||||
#include "../ArduinoHADefines.h"
|
||||
#include "../HAMqtt.h"
|
||||
#include "../HADevice.h"
|
||||
|
||||
HATriggers::HATriggers(HAMqtt& mqtt) :
|
||||
BaseDeviceType(mqtt, "device_automation", nullptr),
|
||||
HATriggers::HATriggers() :
|
||||
BaseDeviceType("device_automation", nullptr),
|
||||
_triggers(nullptr),
|
||||
_triggersNb(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HATriggers::HATriggers(HAMqtt& mqtt) :
|
||||
HATriggers()
|
||||
{
|
||||
(void)mqtt;
|
||||
}
|
||||
|
||||
HATriggers::~HATriggers()
|
||||
{
|
||||
if (_triggers != nullptr) {
|
||||
@@ -87,6 +95,14 @@ bool HATriggers::trigger(const char* type, const char* subtype)
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.print(F("Triggering HATrigger: "));
|
||||
Serial.print(type);
|
||||
Serial.print(F(" "));
|
||||
Serial.print(subtype);
|
||||
Serial.println();
|
||||
#endif
|
||||
|
||||
return mqtt()->publish(topic, "");
|
||||
}
|
||||
|
||||
@@ -122,7 +138,6 @@ void HATriggers::publishConfig()
|
||||
trigger,
|
||||
serializedDevice
|
||||
);
|
||||
|
||||
if (topicLength == 0 || dataLength == 0) {
|
||||
continue;
|
||||
}
|
||||
@@ -153,9 +168,8 @@ uint16_t HATriggers::calculateTopicLength(
|
||||
bool includeNullTerminator
|
||||
) const
|
||||
{
|
||||
uint8_t length = strlen(trigger->type) + strlen(trigger->subtype) + 1; // + underscore
|
||||
uint8_t length = strlen(trigger->type) + strlen(trigger->subtype) + 2; // + underscore and slash
|
||||
return DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
component,
|
||||
nullptr,
|
||||
suffix,
|
||||
@@ -179,7 +193,6 @@ uint16_t HATriggers::generateTopic(
|
||||
strcat(objectId, trigger->type);
|
||||
|
||||
return DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
output,
|
||||
component,
|
||||
objectId,
|
||||
@@ -246,7 +259,7 @@ bool HATriggers::writeSerializedTrigger(
|
||||
return false;
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteBeginningJson(mqtt());
|
||||
DeviceTypeSerializer::mqttWriteBeginningJson();
|
||||
|
||||
// automation type
|
||||
{
|
||||
@@ -273,28 +286,29 @@ bool HATriggers::writeSerializedTrigger(
|
||||
DeviceTypeSerializer::EventTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char Prefix[] PROGMEM = {",\"t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, topic);
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(
|
||||
Prefix,
|
||||
topic
|
||||
);
|
||||
}
|
||||
|
||||
// type
|
||||
{
|
||||
static const char Prefix[] PROGMEM = {",\"type\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, trigger->type);
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(Prefix, trigger->type);
|
||||
}
|
||||
|
||||
// subtype
|
||||
{
|
||||
static const char Prefix[] PROGMEM = {",\"stype\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, trigger->subtype);
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(Prefix, trigger->subtype);
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(mqtt(), serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson(mqtt());
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "BaseDeviceType.h"
|
||||
|
||||
#ifdef ARDUINOHA_TRIGGERS
|
||||
|
||||
struct HATrigger {
|
||||
const char* type;
|
||||
const char* subtype;
|
||||
@@ -11,7 +13,8 @@ struct HATrigger {
|
||||
class HATriggers : public BaseDeviceType
|
||||
{
|
||||
public:
|
||||
HATriggers(HAMqtt& mqtt);
|
||||
HATriggers();
|
||||
HATriggers(HAMqtt& mqtt); // legacy constructor
|
||||
virtual ~HATriggers();
|
||||
|
||||
virtual void onMqttConnected() override;
|
||||
@@ -19,12 +22,22 @@ public:
|
||||
/**
|
||||
* Triggers dont't support availability. Nothing to do here.
|
||||
*/
|
||||
virtual void setAvailability(bool online) override { }
|
||||
virtual void setAvailability(bool online) override { (void)online; }
|
||||
|
||||
bool add(const char* type, const char* subtype);
|
||||
bool trigger(const char* type, const char* subtype);
|
||||
|
||||
protected:
|
||||
void publishConfig() override;
|
||||
|
||||
uint16_t calculateSerializedLength(
|
||||
const char* serializedDevice
|
||||
) const override { (void)serializedDevice; return 0; }
|
||||
|
||||
bool writeSerializedData(
|
||||
const char* serializedDevice
|
||||
) const override { (void)serializedDevice; return false; }
|
||||
|
||||
uint16_t calculateTopicLength(
|
||||
const char* component,
|
||||
const HATrigger *trigger,
|
||||
@@ -40,8 +53,6 @@ protected:
|
||||
) const;
|
||||
|
||||
private:
|
||||
void publishConfig();
|
||||
|
||||
uint16_t calculateSerializedLength(
|
||||
const HATrigger* trigger,
|
||||
const char* serializedDevice
|
||||
@@ -57,3 +68,4 @@ private:
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user