mirror of
https://github.com/mrcory/arduino-home-assistant.git
synced 2026-08-02 15:24:26 -04:00
Release 1.3.0 (#36)
* Add support for HVAC (#15) * remove HASwitch::triggerCallback method * fix typo in docs * HASwitch: simplify callback #define * work in progress: HVAC * replace local mqtt instances with singleton * device types optimization * cleanup * finished aux heating and away mode * wip: HVAC * finished HVAC implementation * HVAC implementation * add ifdefs for all HA components * wip: optimization * update readme * update examples * remove unused constructor from HAMqtt * update HAUtils * remove mqtt from constructors, fix compilation warnings * finished HVAC * add HVAC example, add changelog * update changelog, add documentation * Add support for icons in HASwitch and HASensor (#16) * add support for icon in HASwitch * add support for icon in HASensor * update changelog * add support for setting retain flag in HASwitch * Add support for text payload in HASensor (#17) * refactored HASensor * update changelog * fix sensor example * Add support for fans (HAFan) (#18) * finished basic implementation of the HAFan * add retain method to DeviceTypeSerializer * finished HAFan implementation * update HAHVAC * device types cleanup * add HAFan example * update changelog * update readme * update fan example * optimization, add support for ActionFeature in HVAC * Add support for MQTT LWT (#19) * add support for hostname in HAMqtt * add support for LWT and shared availability * minor fixes * added advanced availability example * update examples * update changelog * cleanup & bug fix * bump up version * minor improvements and fixes * update readme * add HAMqtt::onMessage method * Add support for covers (#28) * add HACovers * add cover example, update implementation * disable debug * update mqtt-events example * Add support for setting different prefix for non-discovery topics (#29) * add support for data prefix * cleanup * update comments and logs * update multi-state-button example * rename rexample from mqtt-events to mqtt-advanced * update changelog * update links in changelog * Home Assistant 2021.4.5 - MQTT upgrade (#33) * updae HAFan implementation * change lib version * update changelog * fix setSpeed method * update HAFan example * Separate uniqueId from name (#34) * rename name to uniqueId * remove legacy labels * update examples * rename isMyTopic to compareTopics * update changelog, rename name to uniqueId * Implement onBeforeStateChanged callback in HASwitch (#35) * add low latency mode to HASwitch * add onBeforeStateChanged callback to HASwitch * update readme * rename DEPRECATED macro * update advanced MQTT example
This commit is contained in:
+17
-3
@@ -1,13 +1,27 @@
|
||||
# Changelog
|
||||
|
||||
## 1.3.0
|
||||
|
||||
**New features:**
|
||||
* Added `onMessage()` method to HAMqtt class
|
||||
* Added support for HA Covers
|
||||
* Added support for setting different prefix for non-discovery topics (see [Advanced MQTT example](examples/mqtt-advanced/mqtt-advanced.ino))
|
||||
* Added `setName` method to HASensor
|
||||
* Added `setName` method to HASwitch
|
||||
* Added `onBeforeStateChanged` callback to HASwitch
|
||||
|
||||
**Updates:**
|
||||
* Removed legacy properties from HAFan (Home Assistant 2021.4.4). Deprecated methods will be removed after a quarter (2021.7)
|
||||
* Separated `uniqueID` field from `name` in all devices types
|
||||
|
||||
## 1.2.0
|
||||
|
||||
**Breaking changes:**
|
||||
* Refactored HASensor implementation. Please take a look at updated example in `examples/sensor/sensor.ino`
|
||||
* Refactored HASensor implementation. Please take a look at [updated example](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 excluding devices types from the compilation using defines (see [src/ArduinoHADefines.h](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
|
||||
@@ -15,7 +29,7 @@
|
||||
* 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`)
|
||||
* Added support for MQTT LWT (see [Advanced Availability example](examples/advanced-availability/advanced-availability.ino))
|
||||
|
||||
**Updates:**
|
||||
* Optimized codebase and logic in all devices types
|
||||
|
||||
@@ -9,11 +9,25 @@ but I successfully use it on ESP8266/ESP8255 boards in my projects.
|
||||
|
||||
* MQTT discovery (device is added to the Home Assistant panel automatically)
|
||||
* MQTT Last Will and Testament
|
||||
* Support for custom MQTT messages (publishing and subscribing)
|
||||
* Auto reconnect with MQTT broker
|
||||
* Reporting availability (online/offline states) of a device
|
||||
|
||||
## Supported HA types
|
||||
|
||||
* Binary sensors
|
||||
* Covers
|
||||
* 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)*
|
||||
|
||||
## Examples
|
||||
|
||||
* [Binary Sensor](examples/binary-sensor/binary-sensor.ino)
|
||||
* [Cover](examples/cover/cover.ino)
|
||||
* [Fan](examples/fan/fan.ino)
|
||||
* [LED switch](examples/led-switch/led-switch.ino)
|
||||
* [Multi-state button](examples/multi-state-button/multi-state-button.ino)
|
||||
@@ -24,40 +38,37 @@ but I successfully use it on ESP8266/ESP8255 boards in my projects.
|
||||
* [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)
|
||||
* [MQTT advanced](examples/mqtt-advanced/mqtt-advanced.ino)
|
||||
|
||||
## Tested boards
|
||||
|
||||
* Arduino Uno
|
||||
* Arduino Mega
|
||||
* Controllino Maxi (standard/pure/automation/power)
|
||||
* Controllino Mega (standard/pure)
|
||||
* NodeMCU
|
||||
* ESP-01
|
||||
* Generic ESP8266/ESP8255
|
||||
* Arduino Nano 33 IoT (SAMD)
|
||||
|
||||
## Tested devices
|
||||
|
||||
* Controllino Maxi (standard/pure/automation/power)
|
||||
* Controllino Mega (standard/pure)
|
||||
* Sonoff Dual R2
|
||||
* Sonoff Basic
|
||||
* Sonoff Mini
|
||||
* Tuya Wi-Fi switch module
|
||||
* Tuya Wi-Fi curtain module
|
||||
|
||||
## Tested Arduino Shields
|
||||
|
||||
* Arduino Ethernet Shield (WizNet W5100)
|
||||
|
||||
## 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
|
||||
|
||||
@@ -13,7 +13,7 @@ EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
|
||||
// "input" may be anything you want to be displayed in HA panel
|
||||
// "input" is unique ID of the sensor. You should define you own ID.
|
||||
// "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);
|
||||
@@ -32,6 +32,8 @@ void setup() {
|
||||
device.setName("Arduino");
|
||||
device.setSoftwareVersion("1.0.0");
|
||||
|
||||
sensor.setName("Door sensor"); // optional
|
||||
|
||||
// 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
|
||||
|
||||
@@ -13,7 +13,7 @@ EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
|
||||
// "input" may be anything you want to be displayed in HA panel
|
||||
// "input" is unique ID of the sensor. You should define you own ID.
|
||||
// "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);
|
||||
@@ -36,6 +36,8 @@ void setup() {
|
||||
device.setName("Arduino");
|
||||
device.setSoftwareVersion("1.0.0");
|
||||
|
||||
sensor.setName("Door sensor"); // optional
|
||||
|
||||
mqtt.begin(BROKER_ADDR);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
|
||||
// "input" may be anything you want to be displayed in HA panel
|
||||
// "input" is unique ID of the sensor. You should define you own ID.
|
||||
// "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);
|
||||
@@ -28,6 +28,8 @@ void setup() {
|
||||
device.setName("Arduino");
|
||||
device.setSoftwareVersion("1.0.0");
|
||||
|
||||
sensor.setName("Door sensor"); // optional
|
||||
|
||||
mqtt.begin(BROKER_ADDR);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#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);
|
||||
HACover cover("my-cover"); // "my-cover" is unique ID of the cover. You should define your own ID.
|
||||
|
||||
void onCoverCommand(HACover::CoverCommand cmd) {
|
||||
if (cmd == HACover::CommandOpen) {
|
||||
Serial.println("Command: Open");
|
||||
cover.setState(HACover::StateOpening);
|
||||
} else if (cmd == HACover::CommandClose) {
|
||||
Serial.println("Command: Close");
|
||||
cover.setState(HACover::StateClosing);
|
||||
} else if (cmd == HACover::CommandStop) {
|
||||
Serial.println("Command: Stop");
|
||||
cover.setState(HACover::StateStopped);
|
||||
}
|
||||
|
||||
// Available states:
|
||||
// HACover::StateClosed
|
||||
// HACover::StateClosing
|
||||
// HACover::StateOpen
|
||||
// HACover::StateOpening
|
||||
// HACover::StateStopped
|
||||
|
||||
// You can also report position using setPosition() method
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
Ethernet.begin(mac);
|
||||
|
||||
cover.onCommand(onCoverCommand);
|
||||
cover.setName("My cover"); // optional
|
||||
// cover.setRetain(true); // optionally you can set retain flag
|
||||
|
||||
mqtt.begin(BROKER_ADDR);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Ethernet.maintain();
|
||||
mqtt.loop();
|
||||
}
|
||||
+7
-12
@@ -11,6 +11,7 @@ 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.
|
||||
// "ventilation" is unique ID of the fan. You should define your own ID.
|
||||
HAFan fan("ventilation", HAFan::SpeedsFeature);
|
||||
|
||||
void onStateChanged(bool state) {
|
||||
@@ -18,20 +19,14 @@ void onStateChanged(bool state) {
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
void onSpeedChanged(HAFan::Speed speed) {
|
||||
void onSpeedChanged(uint16_t 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");
|
||||
}
|
||||
Serial.println(speed);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// you don't need to verify return status
|
||||
Ethernet.begin(mac);
|
||||
|
||||
@@ -40,10 +35,10 @@ void setup() {
|
||||
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);
|
||||
fan.setSpeedRangeMin(1);
|
||||
fan.setSpeedRangeMax(100);
|
||||
|
||||
// handle fan states
|
||||
fan.onStateChanged(onStateChanged);
|
||||
|
||||
@@ -9,7 +9,13 @@ byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A};
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
HASwitch led("led", false); // you can use custom name in place of "led"
|
||||
HASwitch led("led", false); // "led" is unique ID of the switch. You should define your own ID.
|
||||
|
||||
void onBeforeSwitchStateChanged(bool state, HASwitch* s)
|
||||
{
|
||||
// this callback will be called before publishing new state to HA
|
||||
// in some cases there may be delay before onStateChanged is called due to network latency
|
||||
}
|
||||
|
||||
void onSwitchStateChanged(bool state, HASwitch* s)
|
||||
{
|
||||
@@ -29,8 +35,10 @@ void setup() {
|
||||
|
||||
// set icon (optional)
|
||||
led.setIcon("mdi:lightbulb");
|
||||
led.setName("My LED");
|
||||
|
||||
// handle switch state
|
||||
led.onBeforeStateChanged(onBeforeSwitchStateChanged); // optional
|
||||
led.onStateChanged(onSwitchStateChanged);
|
||||
|
||||
mqtt.begin(BROKER_ADDR);
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#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 onMqttMessage(const char* topic, const uint8_t* payload, uint16_t length) {
|
||||
// This callback is called when message from MQTT broker is received.
|
||||
// Please note that you should always verify if the message's topic is the one you expect.
|
||||
// For example: if (memcmp(topic, "myCustomTopic") == 0) { ... }
|
||||
|
||||
Serial.print("New message on topic: ");
|
||||
Serial.println(topic);
|
||||
Serial.print("Data: ");
|
||||
Serial.println((const char*)payload);
|
||||
|
||||
mqtt.publish("myPublishTopic", "hello");
|
||||
}
|
||||
|
||||
void onMqttConnected() {
|
||||
Serial.println("Connected to the broker!");
|
||||
|
||||
// You can subscribe to custom topic if you need
|
||||
mqtt.subscribe("myCustomTopic");
|
||||
}
|
||||
|
||||
void onMqttConnectionFailed() {
|
||||
Serial.println("Failed to connect to the broker!");
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
Ethernet.begin(mac);
|
||||
|
||||
mqtt.onMessage(onMqttMessage);
|
||||
mqtt.onConnected(onMqttConnected);
|
||||
mqtt.onConnectionFailed(onMqttConnectionFailed);
|
||||
|
||||
// If you use custom discovery prefix you can change it as following:
|
||||
// mqtt.setDiscoveryPrefix("customPrefix");
|
||||
|
||||
// If you want to change prefix only for non-discovery prefix:
|
||||
// mqtt.setDataPrefix("data");
|
||||
|
||||
mqtt.begin(BROKER_ADDR);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Ethernet.maintain();
|
||||
mqtt.loop();
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
#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); // you can use custom name in place of "led"
|
||||
HASwitch led("led", false); // "led" is unique ID of the switch. You should define your own ID.
|
||||
|
||||
void onSwitchStateChanged(bool state, HASwitch* s)
|
||||
{
|
||||
@@ -31,6 +31,7 @@ void setup() {
|
||||
|
||||
// handle switch state
|
||||
led.onStateChanged(onSwitchStateChanged);
|
||||
led.setName("My LED"); // optional
|
||||
|
||||
mqtt.begin(BROKER_ADDR, BROKER_USERNAME, BROKER_PASSWORD);
|
||||
}
|
||||
|
||||
@@ -22,8 +22,6 @@ void setup() {
|
||||
// you don't need to verify return status
|
||||
Ethernet.begin(mac);
|
||||
|
||||
Serial.println(Ethernet.localIP());
|
||||
|
||||
// set device's details (optional)
|
||||
device.setName("Arduino");
|
||||
device.setSoftwareVersion("1.0.0");
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
WiFiClient client;
|
||||
HADevice device;
|
||||
HAMqtt mqtt(client, device);
|
||||
HASwitch led("led", false); // you can use custom name in place of "led"
|
||||
HASwitch led("led", false); // "led" is unique ID of the switch. You should define your own ID.
|
||||
|
||||
void onSwitchStateChanged(bool state, HASwitch* s)
|
||||
{
|
||||
@@ -43,6 +43,7 @@ void setup() {
|
||||
|
||||
// handle switch state
|
||||
led.onStateChanged(onSwitchStateChanged);
|
||||
led.setName("My LED"); // optional
|
||||
|
||||
mqtt.begin(BROKER_ADDR);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,13 @@
|
||||
WiFiClient client;
|
||||
HADevice device;
|
||||
HAMqtt mqtt(client, device);
|
||||
HASwitch led("led", false); // you can use custom name in place of "led"
|
||||
HASwitch led("led", false); // "led" is unique ID of the switch. You should define your own ID.
|
||||
|
||||
void onBeforeSwitchStateChanged(bool state, HASwitch* s)
|
||||
{
|
||||
// this callback will be called before publishing new state to HA
|
||||
// in some cases there may be delay before onStateChanged is called due to network latency
|
||||
}
|
||||
|
||||
void onSwitchStateChanged(bool state, HASwitch* s)
|
||||
{
|
||||
@@ -42,7 +48,9 @@ void setup() {
|
||||
device.setSoftwareVersion("1.0.0");
|
||||
|
||||
// handle switch state
|
||||
led.onBeforeStateChanged(onBeforeSwitchStateChanged); // optional
|
||||
led.onStateChanged(onSwitchStateChanged);
|
||||
led.setName("My LED"); // optional
|
||||
|
||||
mqtt.begin(BROKER_ADDR);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,13 @@ double lastValue = 0;
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
HASensor temp("temp");
|
||||
HASensor temp("temp"); // "temp" is unique ID of the sensor. You should define your own ID.
|
||||
|
||||
void onBeforeSwitchStateChanged(bool state, HASwitch* s)
|
||||
{
|
||||
// this callback will be called before publishing new state to HA
|
||||
// in some cases there may be delay before onStateChanged is called due to network latency
|
||||
}
|
||||
|
||||
void setup() {
|
||||
// you don't need to verify return status
|
||||
@@ -24,6 +30,7 @@ void setup() {
|
||||
temp.setUnitOfMeasurement("°C");
|
||||
temp.setDeviceClass("temperature");
|
||||
temp.setIcon("mdi:home");
|
||||
temp.setName("Home temperature");
|
||||
|
||||
mqtt.begin(BROKER_ADDR);
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
name=home-assistant-integration
|
||||
version=1.2.0
|
||||
version=1.3.0
|
||||
author=Dawid Chyrzynski <dawid.chyrzynski@gmail.com>
|
||||
maintainer=Dawid Chyrzynski <dawid.chyrzynski@gmail.com>
|
||||
sentence=Home Assistant MQTT integration for Arduino
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "HAMqtt.h"
|
||||
#include "HAUtils.h"
|
||||
#include "device-types/HABinarySensor.h"
|
||||
#include "device-types/HACover.h"
|
||||
#include "device-types/HAFan.h"
|
||||
#include "device-types/HAHVAC.h"
|
||||
#include "device-types/HASensor.h"
|
||||
|
||||
@@ -5,9 +5,19 @@
|
||||
|
||||
// You can reduce Flash size of the compiled library by commenting unused components below
|
||||
#define ARDUINOHA_BINARY_SENSOR
|
||||
#define ARDUINOHA_COVER
|
||||
#define ARDUINOHA_FAN
|
||||
#define ARDUINOHA_HVAC
|
||||
#define ARDUINOHA_SENSOR
|
||||
#define ARDUINOHA_SWITCH
|
||||
#define ARDUINOHA_TAG_SCANNER
|
||||
#define ARDUINOHA_TRIGGERS
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define AHA_DEPRECATED(func) func __attribute__ ((deprecated))
|
||||
#elif defined(_MSC_VER)
|
||||
#define AHA_DEPRECATED(func) __declspec(deprecated) func
|
||||
#else
|
||||
#warning "Arduino Home Assistant: You may miss deprecation warnings."
|
||||
#define AHA_DEPRECATED(func) func
|
||||
#endif
|
||||
|
||||
+10
-4
@@ -20,10 +20,12 @@ void onMessageReceived(char* topic, uint8_t* payload, unsigned int length)
|
||||
HAMqtt::HAMqtt(Client& netClient, HADevice& device) :
|
||||
_netClient(netClient),
|
||||
_device(device),
|
||||
_messageCallback(nullptr),
|
||||
_connectedCallback(nullptr),
|
||||
_connectionFailedCallback(nullptr),
|
||||
_initialized(false),
|
||||
_discoveryPrefix(DefaultDiscoveryPrefix),
|
||||
_dataPrefix(nullptr),
|
||||
_mqtt(new PubSubClient(netClient)),
|
||||
_username(nullptr),
|
||||
_password(nullptr),
|
||||
@@ -195,9 +197,9 @@ bool HAMqtt::publish(const char* topic, const char* payload, bool retained)
|
||||
}
|
||||
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.print(F("Publishing message with topic: "));
|
||||
Serial.print(F("Publishing: "));
|
||||
Serial.print(topic);
|
||||
Serial.print(F(", payload length: "));
|
||||
Serial.print(F(", len: "));
|
||||
Serial.print(strlen(payload));
|
||||
Serial.println();
|
||||
#endif
|
||||
@@ -214,9 +216,9 @@ bool HAMqtt::beginPublish(
|
||||
)
|
||||
{
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.print(F("Publishing message with topic: "));
|
||||
Serial.print(F("Publishing: "));
|
||||
Serial.print(topic);
|
||||
Serial.print(F(", payload length: "));
|
||||
Serial.print(F(", len: "));
|
||||
Serial.print(payloadLength);
|
||||
Serial.println();
|
||||
#endif
|
||||
@@ -263,6 +265,10 @@ void HAMqtt::processMessage(char* topic, uint8_t* payload, uint16_t length)
|
||||
Serial.println();
|
||||
#endif
|
||||
|
||||
if (_messageCallback) {
|
||||
_messageCallback(topic, payload, length);
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < _devicesTypesNb; i++) {
|
||||
_devicesTypes[i]->onMqttMessage(topic, payload, length);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <IPAddress.h>
|
||||
|
||||
#define HAMQTT_CALLBACK(name) void (*name)()
|
||||
#define HAMQTT_MESSAGE_CALLBACK(name) void (*name)(const char* topic, const uint8_t* payload, uint16_t length)
|
||||
#define HAMQTT_DEFAULT_PORT 1883
|
||||
|
||||
class PubSubClient;
|
||||
@@ -25,6 +26,8 @@ public:
|
||||
* Sets prefix for Home Assistant discovery.
|
||||
* It needs to match prefix set in the HA admin panel.
|
||||
* The default prefix is "homeassistant".
|
||||
*
|
||||
* @param prefix
|
||||
*/
|
||||
inline void setDiscoveryPrefix(const char* prefix)
|
||||
{ _discoveryPrefix = prefix; }
|
||||
@@ -35,12 +38,35 @@ public:
|
||||
inline const char* getDiscoveryPrefix() const
|
||||
{ return _discoveryPrefix; }
|
||||
|
||||
/**
|
||||
* Sets prefix that will be used for topics different than discovery.
|
||||
* It may be useful if you want to pass MQTT trafic through bridge.
|
||||
*
|
||||
* @param prefix
|
||||
*/
|
||||
inline void setDataPrefix(const char* prefix)
|
||||
{ _dataPrefix = prefix; }
|
||||
|
||||
/**
|
||||
* Returns data prefix.
|
||||
*/
|
||||
inline const char* getDataPrefix() const
|
||||
{ return _dataPrefix; }
|
||||
|
||||
/**
|
||||
* Returns instance of the device assigned to the HAMqtt class.
|
||||
*/
|
||||
inline HADevice const* getDevice() const
|
||||
{ return &_device; }
|
||||
|
||||
/**
|
||||
* Given callback will be called for each received message from the broker.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onMessage(HAMQTT_MESSAGE_CALLBACK(callback))
|
||||
{ _messageCallback = callback; }
|
||||
|
||||
/**
|
||||
* Given callback will be called each time the connection with broker is acquired.
|
||||
*
|
||||
@@ -197,10 +223,12 @@ private:
|
||||
|
||||
Client& _netClient;
|
||||
HADevice& _device;
|
||||
HAMQTT_MESSAGE_CALLBACK(_messageCallback);
|
||||
HAMQTT_CALLBACK(_connectedCallback);
|
||||
HAMQTT_CALLBACK(_connectionFailedCallback);
|
||||
bool _initialized;
|
||||
const char* _discoveryPrefix;
|
||||
const char* _dataPrefix;
|
||||
PubSubClient* _mqtt;
|
||||
const char* _username;
|
||||
const char* _password;
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
|
||||
BaseDeviceType::BaseDeviceType(
|
||||
const char* componentName,
|
||||
const char* name
|
||||
const char* uniqueId
|
||||
) :
|
||||
_componentName(componentName),
|
||||
_name(name),
|
||||
_availability(AvailabilityDefault)
|
||||
_uniqueId(uniqueId),
|
||||
_availability(AvailabilityDefault),
|
||||
_name(nullptr)
|
||||
{
|
||||
mqtt()->addDeviceType(this);
|
||||
}
|
||||
@@ -60,8 +61,10 @@ void BaseDeviceType::publishConfig()
|
||||
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
uniqueId(),
|
||||
DeviceTypeSerializer::ConfigTopic,
|
||||
true,
|
||||
true
|
||||
);
|
||||
const uint16_t& dataLength = calculateSerializedLength(serializedDevice);
|
||||
|
||||
@@ -73,8 +76,9 @@ void BaseDeviceType::publishConfig()
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
uniqueId(),
|
||||
DeviceTypeSerializer::ConfigTopic,
|
||||
true
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
@@ -96,15 +100,15 @@ void BaseDeviceType::publishAvailability()
|
||||
|
||||
if (_availability == AvailabilityDefault ||
|
||||
!mqtt()->isConnected() ||
|
||||
strlen(_name) == 0 ||
|
||||
strlen(_componentName) == 0 ||
|
||||
strlen(uniqueId()) == 0 ||
|
||||
strlen(componentName()) == 0 ||
|
||||
device->isSharedAvailabilityEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
_componentName,
|
||||
_name,
|
||||
componentName(),
|
||||
uniqueId(),
|
||||
DeviceTypeSerializer::AvailabilityTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
@@ -114,8 +118,8 @@ void BaseDeviceType::publishAvailability()
|
||||
char topic[topicSize];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
topic,
|
||||
_componentName,
|
||||
_name,
|
||||
componentName(),
|
||||
uniqueId(),
|
||||
DeviceTypeSerializer::AvailabilityTopic
|
||||
);
|
||||
|
||||
@@ -134,24 +138,24 @@ void BaseDeviceType::publishAvailability()
|
||||
);
|
||||
}
|
||||
|
||||
bool BaseDeviceType::isMyTopic(const char* topic, const char* expectedTopic)
|
||||
bool BaseDeviceType::compareTopics(const char* topic, const char* expectedTopic)
|
||||
{
|
||||
if (topic == nullptr || expectedTopic == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strlen(name()) == 0) {
|
||||
if (strlen(uniqueId()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char Slash[] PROGMEM = {"/"};
|
||||
|
||||
// name + cmd topic + two slashes + null terminator
|
||||
uint8_t suffixLength = strlen(name()) + strlen(expectedTopic) + 3;
|
||||
// unique ID + cmd topic + two slashes + null terminator
|
||||
uint8_t suffixLength = strlen(uniqueId()) + strlen(expectedTopic) + 3;
|
||||
char suffix[suffixLength];
|
||||
|
||||
strcpy_P(suffix, Slash);
|
||||
strcat(suffix, name());
|
||||
strcat(suffix, uniqueId());
|
||||
strcat_P(suffix, Slash);
|
||||
strcat(suffix, expectedTopic);
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ class BaseDeviceType
|
||||
public:
|
||||
BaseDeviceType(
|
||||
const char* componentName,
|
||||
const char* name
|
||||
const char* uniqueId
|
||||
);
|
||||
virtual ~BaseDeviceType();
|
||||
|
||||
inline const char* name() const
|
||||
{ return _name; }
|
||||
inline const char* uniqueId() const
|
||||
{ return _uniqueId; }
|
||||
|
||||
inline const char* componentName() const
|
||||
{ return _componentName; }
|
||||
@@ -29,6 +29,12 @@ public:
|
||||
inline bool isOnline() const
|
||||
{ return (_availability == AvailabilityOnline); }
|
||||
|
||||
inline void setName(const char* name)
|
||||
{ _name = name; }
|
||||
|
||||
inline const char* getName() const
|
||||
{ return _name; }
|
||||
|
||||
virtual void setAvailability(bool online);
|
||||
|
||||
protected:
|
||||
@@ -43,12 +49,12 @@ protected:
|
||||
|
||||
virtual void publishConfig();
|
||||
virtual void publishAvailability();
|
||||
virtual bool isMyTopic(const char* topic, const char* expectedTopic);
|
||||
virtual bool compareTopics(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;
|
||||
const char* const _uniqueId;
|
||||
|
||||
private:
|
||||
enum Availability {
|
||||
@@ -58,6 +64,7 @@ private:
|
||||
};
|
||||
|
||||
Availability _availability;
|
||||
const char* _name;
|
||||
|
||||
friend class HAMqtt;
|
||||
};
|
||||
|
||||
@@ -19,14 +19,27 @@ const char* DeviceTypeSerializer::Offline = "offline";
|
||||
const char* DeviceTypeSerializer::StateOn = "ON";
|
||||
const char* DeviceTypeSerializer::StateOff = "OFF";
|
||||
|
||||
const char* DeviceTypeSerializer::getTopicPrefix(bool isDiscoveryTopic)
|
||||
{
|
||||
if (!isDiscoveryTopic) {
|
||||
const char* dataPrefix = HAMqtt::instance()->getDataPrefix();
|
||||
if (dataPrefix != nullptr) {
|
||||
return dataPrefix;
|
||||
}
|
||||
}
|
||||
|
||||
return HAMqtt::instance()->getDiscoveryPrefix();
|
||||
}
|
||||
|
||||
uint16_t DeviceTypeSerializer::calculateTopicLength(
|
||||
const char* component,
|
||||
const char* objectId,
|
||||
const char* suffix,
|
||||
bool includeNullTerminator
|
||||
bool includeNullTerminator,
|
||||
bool isDiscoveryTopic
|
||||
)
|
||||
{
|
||||
const char* prefix = HAMqtt::instance()->getDiscoveryPrefix();
|
||||
const char* prefix = getTopicPrefix(isDiscoveryTopic);
|
||||
if (prefix == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
@@ -58,10 +71,11 @@ uint16_t DeviceTypeSerializer::generateTopic(
|
||||
char* output,
|
||||
const char* component,
|
||||
const char* objectId,
|
||||
const char* suffix
|
||||
const char* suffix,
|
||||
bool isDiscoveryTopic
|
||||
)
|
||||
{
|
||||
const char* prefix = HAMqtt::instance()->getDiscoveryPrefix();
|
||||
const char* prefix = getTopicPrefix(isDiscoveryTopic);
|
||||
if (prefix == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
@@ -104,18 +118,18 @@ uint16_t DeviceTypeSerializer::calculateNameFieldSize(const char* name)
|
||||
}
|
||||
|
||||
uint16_t DeviceTypeSerializer::calculateUniqueIdFieldSize(
|
||||
const char* name
|
||||
const char* uniqueId
|
||||
)
|
||||
{
|
||||
HADevice const* device = HAMqtt::instance()->getDevice();
|
||||
if (device == nullptr || name == nullptr) {
|
||||
if (device == nullptr || uniqueId == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Field format: ,"uniq_id":"[DEVICE ID]_[NAME]"
|
||||
// Field format: ,"uniq_id":"[DEVICE ID]_[UNIQUE ID]"
|
||||
return (
|
||||
strlen(device->getUniqueId()) +
|
||||
strlen(name) +
|
||||
strlen(uniqueId) +
|
||||
14 // 14 - length of the JSON decorators for this field
|
||||
);
|
||||
}
|
||||
@@ -136,7 +150,7 @@ uint16_t DeviceTypeSerializer::calculateAvailabilityFieldSize(
|
||||
|
||||
const uint16_t& availabilityTopicLength = calculateTopicLength(
|
||||
(sharedAvailability ? nullptr : dt->componentName()),
|
||||
(sharedAvailability ? nullptr : dt->name()),
|
||||
(sharedAvailability ? nullptr : dt->uniqueId()),
|
||||
AvailabilityTopic,
|
||||
false
|
||||
);
|
||||
@@ -212,10 +226,10 @@ void DeviceTypeSerializer::mqttWriteNameField(const char* name)
|
||||
}
|
||||
|
||||
void DeviceTypeSerializer::mqttWriteUniqueIdField(
|
||||
const char* name
|
||||
const char* uniqueId
|
||||
)
|
||||
{
|
||||
if (name == nullptr) {
|
||||
if (uniqueId == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -226,13 +240,13 @@ void DeviceTypeSerializer::mqttWriteUniqueIdField(
|
||||
|
||||
static const char Prefix[] PROGMEM = {",\"uniq_id\":\""};
|
||||
|
||||
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, device->getUniqueId());
|
||||
uint8_t uniqueIdLength = strlen(uniqueId) + strlen(device->getUniqueId()) + 2; // underscore + null temrinator
|
||||
char finalUniqueId[uniqueIdLength];
|
||||
strcpy(finalUniqueId, uniqueId);
|
||||
strcat_P(finalUniqueId, CharUnderscore);
|
||||
strcat(finalUniqueId, device->getUniqueId());
|
||||
|
||||
mqttWriteConstCharField(Prefix, uniqueId);
|
||||
mqttWriteConstCharField(Prefix, finalUniqueId);
|
||||
}
|
||||
|
||||
void DeviceTypeSerializer::mqttWriteAvailabilityField(
|
||||
@@ -251,7 +265,7 @@ void DeviceTypeSerializer::mqttWriteAvailabilityField(
|
||||
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
(sharedAvailability ? nullptr : dt->componentName()),
|
||||
(sharedAvailability ? nullptr : dt->name()),
|
||||
(sharedAvailability ? nullptr : dt->uniqueId()),
|
||||
AvailabilityTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
@@ -262,7 +276,7 @@ void DeviceTypeSerializer::mqttWriteAvailabilityField(
|
||||
generateTopic(
|
||||
availabilityTopic,
|
||||
(sharedAvailability ? nullptr : dt->componentName()),
|
||||
(sharedAvailability ? nullptr : dt->name()),
|
||||
(sharedAvailability ? nullptr : dt->uniqueId()),
|
||||
AvailabilityTopic
|
||||
);
|
||||
|
||||
@@ -316,7 +330,7 @@ bool DeviceTypeSerializer::mqttWriteTopicField(
|
||||
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
dt->componentName(),
|
||||
dt->name() ,
|
||||
dt->uniqueId() ,
|
||||
topicSuffix
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
@@ -327,7 +341,7 @@ bool DeviceTypeSerializer::mqttWriteTopicField(
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
topic,
|
||||
dt->componentName(),
|
||||
dt->name(),
|
||||
dt->uniqueId(),
|
||||
topicSuffix
|
||||
);
|
||||
|
||||
@@ -351,7 +365,7 @@ bool DeviceTypeSerializer::mqttPublishMessage(
|
||||
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
dt->componentName(),
|
||||
dt->name(),
|
||||
dt->uniqueId(),
|
||||
topicSuffix
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
@@ -362,7 +376,7 @@ bool DeviceTypeSerializer::mqttPublishMessage(
|
||||
generateTopic(
|
||||
topic,
|
||||
dt->componentName(),
|
||||
dt->name(),
|
||||
dt->uniqueId(),
|
||||
topicSuffix
|
||||
);
|
||||
|
||||
@@ -384,7 +398,7 @@ bool DeviceTypeSerializer::mqttSubscribeTopic(
|
||||
{
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
dt->componentName(),
|
||||
dt->name(),
|
||||
dt->uniqueId(),
|
||||
topicSuffix
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
@@ -395,7 +409,7 @@ bool DeviceTypeSerializer::mqttSubscribeTopic(
|
||||
generateTopic(
|
||||
topic,
|
||||
dt->componentName(),
|
||||
dt->name(),
|
||||
dt->uniqueId(),
|
||||
topicSuffix
|
||||
);
|
||||
|
||||
|
||||
@@ -19,38 +19,44 @@ public:
|
||||
static const char* StateOn;
|
||||
static const char* StateOff;
|
||||
|
||||
static const char* getTopicPrefix(bool isDiscoveryTopic);
|
||||
|
||||
/**
|
||||
* Calculates length of the topic with given parameters.
|
||||
* Topic format: [discovery prefix]/[component]/[deviceId]/[objectId]/[suffix]
|
||||
* Topic format: [prefix]/[component]/[deviceId]/[objectId]/[suffix]
|
||||
*
|
||||
* @param component
|
||||
* @param objectId
|
||||
* @param suffix
|
||||
* @param includeNullTerminator
|
||||
* @param isDiscoveryTopic Determines which prefix will be used for topic.
|
||||
*/
|
||||
static uint16_t calculateTopicLength(
|
||||
const char* component,
|
||||
const char* objectId,
|
||||
const char* suffix,
|
||||
bool includeNullTerminator = true
|
||||
bool includeNullTerminator = true,
|
||||
bool isDiscoveryTopic = false
|
||||
);
|
||||
|
||||
/**
|
||||
* 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]/[deviceId]/[objectId]/[suffix]
|
||||
* Topic format: [prefix]/[component]/[deviceId]/[objectId]/[suffix]
|
||||
*
|
||||
* @param output
|
||||
* @param component
|
||||
* @param objectId
|
||||
* @param suffix
|
||||
* @param includeNullTerminator
|
||||
* @param isDiscoveryTopic Determines which prefix will be used for topic.
|
||||
*/
|
||||
static uint16_t generateTopic(
|
||||
char* output,
|
||||
const char* component,
|
||||
const char* objectId,
|
||||
const char* suffix
|
||||
const char* suffix,
|
||||
bool isDiscoveryTopic = false
|
||||
);
|
||||
|
||||
static uint16_t calculateBaseJsonDataSize();
|
||||
@@ -58,7 +64,7 @@ public:
|
||||
const char* name
|
||||
);
|
||||
static uint16_t calculateUniqueIdFieldSize(
|
||||
const char* name
|
||||
const char* uniqueId
|
||||
);
|
||||
static uint16_t calculateAvailabilityFieldSize(
|
||||
const BaseDeviceType* const dt
|
||||
@@ -81,7 +87,7 @@ public:
|
||||
const char* name
|
||||
);
|
||||
static void mqttWriteUniqueIdField(
|
||||
const char* name
|
||||
const char* uniqueId
|
||||
);
|
||||
static void mqttWriteAvailabilityField(
|
||||
const BaseDeviceType* const dt
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
#include "../HADevice.h"
|
||||
|
||||
HABinarySensor::HABinarySensor(
|
||||
const char* name,
|
||||
const char* uniqueId,
|
||||
bool initialState
|
||||
) :
|
||||
BaseDeviceType("binary_sensor", name),
|
||||
BaseDeviceType("binary_sensor", uniqueId),
|
||||
_class(nullptr),
|
||||
_currentState(initialState)
|
||||
{
|
||||
@@ -17,21 +17,21 @@ HABinarySensor::HABinarySensor(
|
||||
}
|
||||
|
||||
HABinarySensor::HABinarySensor(
|
||||
const char* name,
|
||||
const char* uniqueId,
|
||||
bool initialState,
|
||||
HAMqtt& mqtt
|
||||
) :
|
||||
HABinarySensor(name, initialState)
|
||||
HABinarySensor(uniqueId, initialState)
|
||||
{
|
||||
(void)mqtt;
|
||||
}
|
||||
|
||||
HABinarySensor::HABinarySensor(
|
||||
const char* name,
|
||||
const char* uniqueId,
|
||||
const char* deviceClass,
|
||||
bool initialState
|
||||
) :
|
||||
BaseDeviceType("binary_sensor", name),
|
||||
BaseDeviceType("binary_sensor", uniqueId),
|
||||
_class(deviceClass),
|
||||
_currentState(initialState)
|
||||
{
|
||||
@@ -39,19 +39,19 @@ HABinarySensor::HABinarySensor(
|
||||
}
|
||||
|
||||
HABinarySensor::HABinarySensor(
|
||||
const char* name,
|
||||
const char* uniqueId,
|
||||
const char* deviceClass,
|
||||
bool initialState,
|
||||
HAMqtt& mqtt
|
||||
) :
|
||||
HABinarySensor(name, deviceClass, initialState)
|
||||
HABinarySensor(uniqueId, deviceClass, initialState)
|
||||
{
|
||||
(void)mqtt;
|
||||
}
|
||||
|
||||
void HABinarySensor::onMqttConnected()
|
||||
{
|
||||
if (strlen(name()) == 0) {
|
||||
if (strlen(uniqueId()) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -76,13 +76,13 @@ bool HABinarySensor::setState(bool state)
|
||||
|
||||
bool HABinarySensor::publishState(bool state)
|
||||
{
|
||||
if (strlen(name()) == 0) {
|
||||
if (strlen(uniqueId()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
@@ -93,7 +93,7 @@ bool HABinarySensor::publishState(bool state)
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
|
||||
@@ -127,8 +127,8 @@ uint16_t HABinarySensor::calculateSerializedLength(
|
||||
|
||||
uint16_t size = 0;
|
||||
size += DeviceTypeSerializer::calculateBaseJsonDataSize();
|
||||
size += DeviceTypeSerializer::calculateNameFieldSize(name());
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(name());
|
||||
size += DeviceTypeSerializer::calculateNameFieldSize(getName());
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(uniqueId());
|
||||
size += DeviceTypeSerializer::calculateDeviceFieldSize(serializedDevice);
|
||||
size += DeviceTypeSerializer::calculateAvailabilityFieldSize(this);
|
||||
|
||||
@@ -136,7 +136,7 @@ uint16_t HABinarySensor::calculateSerializedLength(
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
DeviceTypeSerializer::StateTopic,
|
||||
false
|
||||
);
|
||||
@@ -182,8 +182,8 @@ bool HABinarySensor::writeSerializedData(const char* serializedDevice) const
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(Prefix, _class);
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteNameField(name());
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(name());
|
||||
DeviceTypeSerializer::mqttWriteNameField(getName());
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(uniqueId());
|
||||
DeviceTypeSerializer::mqttWriteAvailabilityField(this);
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson();
|
||||
|
||||
@@ -11,16 +11,16 @@ public:
|
||||
/**
|
||||
* Initializes binary sensor.
|
||||
*
|
||||
* @param name Name of the sensor. Recommended characters: [a-z0-9\-_]
|
||||
* @param uniqueId Unique ID 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,
|
||||
const char* uniqueId,
|
||||
bool initialState
|
||||
);
|
||||
HABinarySensor(
|
||||
const char* name,
|
||||
const char* uniqueId,
|
||||
bool initialState,
|
||||
HAMqtt& mqtt
|
||||
); // legacy constructor
|
||||
@@ -29,18 +29,18 @@ public:
|
||||
* 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 uniqueId Unique ID of the sensor. Recommendes characters: [a-z0-9\-_]
|
||||
* @param deviceClass Name of the class (lower case).
|
||||
* @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* uniqueId,
|
||||
const char* deviceClass,
|
||||
bool initialState
|
||||
);
|
||||
HABinarySensor(
|
||||
const char* name,
|
||||
const char* uniqueId,
|
||||
const char* deviceClass,
|
||||
bool initialState,
|
||||
HAMqtt& mqtt
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
#include "HACover.h"
|
||||
#ifdef ARDUINOHA_COVER
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../HADevice.h"
|
||||
|
||||
static const char ClosedStateStr[] PROGMEM = {"closed"};
|
||||
static const char ClosingStateStr[] PROGMEM = {"closing"};
|
||||
static const char OpenStateStr[] PROGMEM = {"open"};
|
||||
static const char OpeningStateStr[] PROGMEM = {"opening"};
|
||||
static const char StoppedStateStr[] PROGMEM = {"stopped"};
|
||||
static const char OpenCommandStr[] PROGMEM = {"OPEN"};
|
||||
static const char CloseCommandStr[] PROGMEM = {"CLOSE"};
|
||||
static const char StopCommandStr[] PROGMEM = {"STOP"};
|
||||
|
||||
const char* HACover::PositionTopic = "ps";
|
||||
|
||||
HACover::HACover(const char* uniqueId) :
|
||||
BaseDeviceType("cover", uniqueId),
|
||||
_commandCallback(nullptr),
|
||||
_currentState(StateUnknown),
|
||||
_currentPosition(0),
|
||||
_retain(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HACover::HACover(const char* uniqueId, HAMqtt& mqtt) :
|
||||
HACover(uniqueId)
|
||||
{
|
||||
(void)mqtt;
|
||||
}
|
||||
|
||||
void HACover::onMqttConnected()
|
||||
{
|
||||
if (strlen(uniqueId()) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishAvailability();
|
||||
|
||||
DeviceTypeSerializer::mqttSubscribeTopic(
|
||||
this,
|
||||
DeviceTypeSerializer::CommandTopic
|
||||
);
|
||||
|
||||
if (!_retain) {
|
||||
publishState(_currentState);
|
||||
publishPosition(_currentPosition);
|
||||
}
|
||||
}
|
||||
|
||||
void HACover::onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t& length
|
||||
)
|
||||
{
|
||||
(void)payload;
|
||||
|
||||
if (compareTopics(topic, DeviceTypeSerializer::CommandTopic)) {
|
||||
char cmd[length + 1];
|
||||
memset(cmd, 0, sizeof(cmd));
|
||||
memcpy(cmd, payload, length);
|
||||
handleCommand(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
bool HACover::setState(CoverState state, bool force)
|
||||
{
|
||||
if (!force && _currentState == state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishState(state)) {
|
||||
_currentState = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HACover::setPosition(int16_t position)
|
||||
{
|
||||
if (_currentPosition == position) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishPosition(position)) {
|
||||
_currentPosition = position;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HACover::publishState(CoverState state)
|
||||
{
|
||||
if (strlen(uniqueId()) == 0 || state == StateUnknown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char stateStr[8];
|
||||
switch (state) {
|
||||
case StateClosed:
|
||||
strcpy_P(stateStr, ClosedStateStr);
|
||||
break;
|
||||
|
||||
case StateClosing:
|
||||
strcpy_P(stateStr, ClosingStateStr);
|
||||
break;
|
||||
|
||||
case StateOpen:
|
||||
strcpy_P(stateStr, OpenStateStr);
|
||||
break;
|
||||
|
||||
case StateOpening:
|
||||
strcpy_P(stateStr, OpeningStateStr);
|
||||
break;
|
||||
|
||||
case StateStopped:
|
||||
strcpy_P(stateStr, StoppedStateStr);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return DeviceTypeSerializer::mqttPublishMessage(
|
||||
this,
|
||||
DeviceTypeSerializer::StateTopic,
|
||||
stateStr
|
||||
);
|
||||
}
|
||||
|
||||
bool HACover::publishPosition(int16_t position)
|
||||
{
|
||||
if (strlen(uniqueId()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t digitsNb = floor(log10(position)) + 1;
|
||||
char str[digitsNb + 2]; // + null terminator + negative sign
|
||||
memset(str, 0, sizeof(str));
|
||||
itoa(position, str, 10);
|
||||
|
||||
return DeviceTypeSerializer::mqttPublishMessage(
|
||||
this,
|
||||
PositionTopic,
|
||||
str
|
||||
);
|
||||
}
|
||||
|
||||
uint16_t HACover::calculateSerializedLength(const char* serializedDevice) const
|
||||
{
|
||||
if (serializedDevice == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t size = 0;
|
||||
size += DeviceTypeSerializer::calculateBaseJsonDataSize();
|
||||
size += DeviceTypeSerializer::calculateNameFieldSize(getName());
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(uniqueId());
|
||||
size += DeviceTypeSerializer::calculateDeviceFieldSize(serializedDevice);
|
||||
size += DeviceTypeSerializer::calculateAvailabilityFieldSize(this);
|
||||
size += DeviceTypeSerializer::calculateRetainFieldSize(_retain);
|
||||
|
||||
// command topic
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
uniqueId(),
|
||||
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(),
|
||||
uniqueId(),
|
||||
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
|
||||
}
|
||||
|
||||
// position topic
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
uniqueId(),
|
||||
PositionTopic,
|
||||
false
|
||||
);
|
||||
|
||||
if (topicLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Field format: ,"pos_t":"[TOPIC]"
|
||||
size += topicLength + 11; // 11 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
return size; // exludes null terminator
|
||||
}
|
||||
|
||||
bool HACover::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
|
||||
);
|
||||
}
|
||||
|
||||
// position topic
|
||||
{
|
||||
static const char Prefix[] PROGMEM = {",\"pos_t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteTopicField(
|
||||
this,
|
||||
Prefix,
|
||||
PositionTopic
|
||||
);
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteNameField(getName());
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(uniqueId());
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteAvailabilityField(this);
|
||||
DeviceTypeSerializer::mqttWriteRetainField(_retain);
|
||||
DeviceTypeSerializer::mqttWriteEndJson();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void HACover::handleCommand(const char* cmd)
|
||||
{
|
||||
if (!_commandCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp_P(cmd, CloseCommandStr) == 0) {
|
||||
_commandCallback(CommandClose);
|
||||
} else if (strcmp_P(cmd, OpenCommandStr) == 0) {
|
||||
_commandCallback(CommandOpen);
|
||||
} else if (strcmp_P(cmd, StopCommandStr) == 0) {
|
||||
_commandCallback(CommandStop);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,113 @@
|
||||
#ifndef AHA_COVER_H
|
||||
#define AHA_COVER_H
|
||||
|
||||
#include "BaseDeviceType.h"
|
||||
|
||||
#ifdef ARDUINOHA_COVER
|
||||
|
||||
#define HACOVER_CALLBACK(name) void (*name)(CoverCommand cmd)
|
||||
|
||||
class HACover : public BaseDeviceType
|
||||
{
|
||||
public:
|
||||
static const char* PositionTopic;
|
||||
|
||||
enum CoverState {
|
||||
StateUnknown = 0,
|
||||
StateClosed,
|
||||
StateClosing,
|
||||
StateOpen,
|
||||
StateOpening,
|
||||
StateStopped
|
||||
};
|
||||
|
||||
enum CoverCommand {
|
||||
CommandOpen,
|
||||
CommandClose,
|
||||
CommandStop
|
||||
};
|
||||
|
||||
HACover(const char* uniqueId);
|
||||
HACover(const char* uniqueId, 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 cover 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 cover.
|
||||
* @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(CoverState state, bool force = false);
|
||||
|
||||
/**
|
||||
* Sets current state of the cover without pushing the state to Home Assistant.
|
||||
* This method may be useful if you want to change state before connection
|
||||
* with MQTT broker is acquired.
|
||||
*
|
||||
* @param state New state of the cover.
|
||||
*/
|
||||
inline void setCurrentState(CoverState state)
|
||||
{ _currentState = state; }
|
||||
|
||||
/**
|
||||
* Changes position of the cover 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 position New position of the cover.
|
||||
* @returns Returns true if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setPosition(int16_t position);
|
||||
|
||||
/**
|
||||
* Sets current position of the cover without pushing the value to Home Assistant.
|
||||
* This method may be useful if you want to change position before connection
|
||||
* with MQTT broker is acquired.
|
||||
*
|
||||
* @param position New position of the cover.
|
||||
*/
|
||||
inline void setCurrentPosition(int16_t position)
|
||||
{ _currentPosition = position; }
|
||||
|
||||
/**
|
||||
* 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; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the command from HA is received.
|
||||
* Please note that it's not possible to register multiple callbacks for the same covers.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onCommand(HACOVER_CALLBACK(callback))
|
||||
{ _commandCallback = callback; }
|
||||
|
||||
protected:
|
||||
bool publishState(CoverState state);
|
||||
bool publishPosition(int16_t position);
|
||||
uint16_t calculateSerializedLength(const char* serializedDevice) const override;
|
||||
bool writeSerializedData(const char* serializedDevice) const override;
|
||||
void handleCommand(const char* cmd);
|
||||
|
||||
HACOVER_CALLBACK(_commandCallback);
|
||||
CoverState _currentState;
|
||||
int16_t _currentPosition;
|
||||
bool _retain;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
+88
-163
@@ -4,24 +4,19 @@
|
||||
#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"};
|
||||
const char* HAFan::PercentageCommandTopic = {"sct"};
|
||||
const char* HAFan::PercentageStateTopic = {"sst"};
|
||||
|
||||
HAFan::HAFan(const char* uniqueId, uint8_t features) :
|
||||
BaseDeviceType("fan", uniqueId),
|
||||
_features(features),
|
||||
_speeds(OffSpeed | LowSpeed | MediumSpeed | HighSpeed),
|
||||
_currentState(false),
|
||||
_stateCallback(nullptr),
|
||||
_currentSpeed(OffSpeed),
|
||||
_currentSpeed(0),
|
||||
_speedCallback(nullptr),
|
||||
_label(nullptr),
|
||||
_retain(false)
|
||||
_retain(false),
|
||||
_speedRangeMin(1),
|
||||
_speedRangeMax(100)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -34,7 +29,7 @@ HAFan::HAFan(const char* uniqueId, uint8_t features, HAMqtt& mqtt) :
|
||||
|
||||
void HAFan::onMqttConnected()
|
||||
{
|
||||
if (strlen(name()) == 0) {
|
||||
if (strlen(uniqueId()) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -49,7 +44,7 @@ void HAFan::onMqttConnected()
|
||||
if (_features & SpeedsFeature) {
|
||||
DeviceTypeSerializer::mqttSubscribeTopic(
|
||||
this,
|
||||
SpeedCommandTopic
|
||||
PercentageCommandTopic
|
||||
);
|
||||
}
|
||||
|
||||
@@ -67,15 +62,17 @@ void HAFan::onMqttMessage(
|
||||
{
|
||||
(void)payload;
|
||||
|
||||
if (isMyTopic(topic, DeviceTypeSerializer::CommandTopic)) {
|
||||
if (compareTopics(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);
|
||||
} else if (compareTopics(topic, PercentageCommandTopic)) {
|
||||
char speedStr[length + 1];
|
||||
memset(speedStr, 0, sizeof(speedStr));
|
||||
memcpy(speedStr, payload, length);
|
||||
int32_t speed = atoi(speedStr);
|
||||
if (speed >= 0) {
|
||||
setSpeed(speed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,12 +95,8 @@ bool HAFan::setState(bool state, bool force)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HAFan::setSpeed(Speed speed)
|
||||
bool HAFan::setSpeed(uint16_t speed)
|
||||
{
|
||||
if (speed == UnknownSpeed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (publishSpeed(speed)) {
|
||||
_currentSpeed = speed;
|
||||
|
||||
@@ -117,24 +110,9 @@ bool HAFan::setSpeed(Speed speed)
|
||||
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) {
|
||||
if (strlen(uniqueId()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -149,38 +127,21 @@ bool HAFan::publishState(bool state)
|
||||
);
|
||||
}
|
||||
|
||||
bool HAFan::publishSpeed(Speed speed)
|
||||
bool HAFan::publishSpeed(uint16_t speed)
|
||||
{
|
||||
if (strlen(name()) == 0 || speed == UnknownSpeed) {
|
||||
if (strlen(uniqueId()) == 0) {
|
||||
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;
|
||||
}
|
||||
uint8_t digitsNb = floor(log10(speed)) + 1;
|
||||
char str[digitsNb + 1]; // + null terminator
|
||||
memset(str, 0, sizeof(str));
|
||||
itoa(speed, str, 10);
|
||||
|
||||
return DeviceTypeSerializer::mqttPublishMessage(
|
||||
this,
|
||||
SpeedStateTopic,
|
||||
speedStr
|
||||
PercentageStateTopic,
|
||||
str
|
||||
);
|
||||
}
|
||||
|
||||
@@ -192,17 +153,17 @@ uint16_t HAFan::calculateSerializedLength(const char* serializedDevice) const
|
||||
|
||||
uint16_t size = 0;
|
||||
size += DeviceTypeSerializer::calculateBaseJsonDataSize();
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(name());
|
||||
size += DeviceTypeSerializer::calculateNameFieldSize(getName());
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(uniqueId());
|
||||
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(),
|
||||
uniqueId(),
|
||||
DeviceTypeSerializer::CommandTopic,
|
||||
false
|
||||
);
|
||||
@@ -219,7 +180,7 @@ uint16_t HAFan::calculateSerializedLength(const char* serializedDevice) const
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
DeviceTypeSerializer::StateTopic,
|
||||
false
|
||||
);
|
||||
@@ -234,12 +195,12 @@ uint16_t HAFan::calculateSerializedLength(const char* serializedDevice) const
|
||||
|
||||
// speeds
|
||||
if (_features & SpeedsFeature) {
|
||||
// command topic
|
||||
// percentage command topic
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
SpeedCommandTopic,
|
||||
uniqueId(),
|
||||
PercentageCommandTopic,
|
||||
false
|
||||
);
|
||||
|
||||
@@ -247,16 +208,16 @@ uint16_t HAFan::calculateSerializedLength(const char* serializedDevice) const
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Field format: ,"spd_cmd_t":"[TOPIC]"
|
||||
// Field format: ,"pct_cmd_t":"[TOPIC]"
|
||||
size += topicLength + 15; // 15 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
// state topic
|
||||
// percentage state topic
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
SpeedStateTopic,
|
||||
uniqueId(),
|
||||
PercentageStateTopic,
|
||||
false
|
||||
);
|
||||
|
||||
@@ -264,47 +225,28 @@ uint16_t HAFan::calculateSerializedLength(const char* serializedDevice) const
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Field format: ,"spd_stat_t":"[TOPIC]"
|
||||
// Field format: ,"pct_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
|
||||
// speed range min
|
||||
if (_speedRangeMin != 1) {
|
||||
uint8_t digitsNb = floor(log10(_speedRangeMin)) + 1;
|
||||
|
||||
// Field format: ,"spd_rng_min":[VALUE]
|
||||
size += digitsNb + 15; // 15 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
// speed range max
|
||||
if (_speedRangeMax != 100) {
|
||||
uint8_t digitsNb = floor(log10(_speedRangeMax)) + 1;
|
||||
|
||||
// Field format: ,"spd_rng_max":[VALUE]
|
||||
size += digitsNb + 15; // 15 - 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
|
||||
return size; // excludes null terminator
|
||||
}
|
||||
|
||||
bool HAFan::writeSerializedData(const char* serializedDevice) const
|
||||
@@ -337,79 +279,62 @@ bool HAFan::writeSerializedData(const char* serializedDevice) const
|
||||
|
||||
// speeds
|
||||
if (_features & SpeedsFeature) {
|
||||
// command topic
|
||||
// percentage command topic
|
||||
{
|
||||
static const char Prefix[] PROGMEM = {",\"spd_cmd_t\":\""};
|
||||
static const char Prefix[] PROGMEM = {",\"pct_cmd_t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteTopicField(
|
||||
this,
|
||||
Prefix,
|
||||
SpeedCommandTopic
|
||||
PercentageCommandTopic
|
||||
);
|
||||
}
|
||||
|
||||
// state topic
|
||||
// percentage state topic
|
||||
{
|
||||
static const char Prefix[] PROGMEM = {",\"spd_stat_t\":\""};
|
||||
static const char Prefix[] PROGMEM = {",\"pct_stat_t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteTopicField(
|
||||
this,
|
||||
Prefix,
|
||||
SpeedStateTopic
|
||||
PercentageStateTopic
|
||||
);
|
||||
}
|
||||
|
||||
// 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, "]");
|
||||
}
|
||||
// speed range min
|
||||
if (_speedRangeMin != 1) {
|
||||
uint8_t digitsNb = floor(log10(_speedRangeMin)) + 1;
|
||||
char str[digitsNb + 1]; // + null terminator
|
||||
memset(str, 0, sizeof(str));
|
||||
itoa(_speedRangeMin, str, 10);
|
||||
|
||||
static const char Prefix[] PROGMEM = {",\"spd_rng_min\":"};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(
|
||||
Prefix,
|
||||
speedStr,
|
||||
str,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
// speed range max
|
||||
if (_speedRangeMax != 100) {
|
||||
uint8_t digitsNb = floor(log10(_speedRangeMax)) + 1;
|
||||
char str[digitsNb + 1]; // + null terminator
|
||||
memset(str, 0, sizeof(str));
|
||||
itoa(_speedRangeMax, str, 10);
|
||||
|
||||
static const char Prefix[] PROGMEM = {",\"spd_rng_max\":"};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(
|
||||
Prefix,
|
||||
str,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteRetainField(_retain);
|
||||
DeviceTypeSerializer::mqttWriteNameField(_label);
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(name());
|
||||
DeviceTypeSerializer::mqttWriteAvailabilityField(this);
|
||||
DeviceTypeSerializer::mqttWriteNameField(getName());
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(uniqueId());
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteAvailabilityField(this);
|
||||
DeviceTypeSerializer::mqttWriteRetainField(_retain);
|
||||
DeviceTypeSerializer::mqttWriteEndJson();
|
||||
|
||||
return true;
|
||||
|
||||
+31
-26
@@ -6,19 +6,21 @@
|
||||
#ifdef ARDUINOHA_FAN
|
||||
|
||||
#define HAFAN_STATE_CALLBACK_BOOL(name) void (*name)(bool)
|
||||
#define HAFAN_STATE_CALLBACK_SPEED(name) void (*name)(Speed)
|
||||
#define HAFAN_STATE_CALLBACK_SPEED(name) void (*name)(uint16_t)
|
||||
#define HAFAN_STATE_CALLBACK_SPEED_DEPRECATED(name) void (*name)(Speed)
|
||||
|
||||
class HAFan : public BaseDeviceType
|
||||
{
|
||||
public:
|
||||
static const char* SpeedCommandTopic;
|
||||
static const char* SpeedStateTopic;
|
||||
static const char* PercentageCommandTopic;
|
||||
static const char* PercentageStateTopic;
|
||||
|
||||
enum Features {
|
||||
DefaultFeatures = 0,
|
||||
SpeedsFeature = 1
|
||||
};
|
||||
|
||||
// @deprecated
|
||||
enum Speed {
|
||||
UnknownSpeed = 0,
|
||||
OffSpeed = 1,
|
||||
@@ -81,27 +83,20 @@ public:
|
||||
*
|
||||
* @param speeds
|
||||
*/
|
||||
inline void setSpeeds(uint8_t speeds)
|
||||
{ _speeds = speeds; }
|
||||
AHA_DEPRECATED(inline void setSpeeds(uint8_t speeds))
|
||||
{ (void)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);
|
||||
bool setSpeed(uint16_t speed);
|
||||
|
||||
/**
|
||||
* Returns current speed of the fan.
|
||||
*/
|
||||
inline Speed getSpeed() const
|
||||
inline uint16_t getSpeed() const
|
||||
{ return _currentSpeed; }
|
||||
|
||||
/**
|
||||
@@ -113,13 +108,8 @@ public:
|
||||
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
|
||||
AHA_DEPRECATED(inline void onSpeedChanged(HAFAN_STATE_CALLBACK_SPEED_DEPRECATED(callback)))
|
||||
{ (void)callback; }
|
||||
|
||||
/**
|
||||
* Sets `retain` flag for commands published by Home Assistant.
|
||||
@@ -130,21 +120,36 @@ public:
|
||||
inline void setRetain(bool retain)
|
||||
{ _retain = retain; }
|
||||
|
||||
/**
|
||||
* Sets minimum range for slider in the HA panel.
|
||||
*
|
||||
* @param min
|
||||
*/
|
||||
inline void setSpeedRangeMin(uint16_t min)
|
||||
{ _speedRangeMin = min; }
|
||||
|
||||
/**
|
||||
* Sets maximum range for slider in the HA panel.
|
||||
*
|
||||
* @param min
|
||||
*/
|
||||
inline void setSpeedRangeMax(uint16_t max)
|
||||
{ _speedRangeMax = max; }
|
||||
|
||||
protected:
|
||||
bool publishState(bool state);
|
||||
bool publishSpeed(Speed speed);
|
||||
bool publishSpeed(uint16_t 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;
|
||||
uint16_t _currentSpeed;
|
||||
HAFAN_STATE_CALLBACK_SPEED(_speedCallback);
|
||||
const char* _label;
|
||||
bool _retain;
|
||||
uint16_t _speedRangeMin;
|
||||
uint16_t _speedRangeMax;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+31
-32
@@ -44,7 +44,6 @@ HAHVAC::HAHVAC(const char* uniqueId, uint8_t features) :
|
||||
_tempStep(1),
|
||||
_targetTempCallback(nullptr),
|
||||
_targetTemperature(__DBL_MAX__),
|
||||
_label(nullptr),
|
||||
_modes(
|
||||
OffMode |
|
||||
AutoMode |
|
||||
@@ -72,7 +71,7 @@ HAHVAC::HAHVAC(
|
||||
|
||||
void HAHVAC::onMqttConnected()
|
||||
{
|
||||
if (strlen(name()) == 0) {
|
||||
if (strlen(uniqueId()) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -98,24 +97,24 @@ void HAHVAC::onMqttMessage(
|
||||
)
|
||||
{
|
||||
if ((_features & AuxHeatingFeature) &&
|
||||
isMyTopic(topic, AuxCommandTopic)) {
|
||||
compareTopics(topic, AuxCommandTopic)) {
|
||||
bool state = (length == strlen(DeviceTypeSerializer::StateOn));
|
||||
setAuxHeatingState(state);
|
||||
} else if ((_features & AwayModeFeature) &&
|
||||
isMyTopic(topic, AwayCommandTopic)) {
|
||||
compareTopics(topic, AwayCommandTopic)) {
|
||||
bool state = (length == strlen(DeviceTypeSerializer::StateOn));
|
||||
setAwayState(state);
|
||||
} else if ((_features & HoldFeature) &&
|
||||
isMyTopic(topic, HoldCommandTopic)) {
|
||||
compareTopics(topic, HoldCommandTopic)) {
|
||||
bool state = (length == strlen(DeviceTypeSerializer::StateOn));
|
||||
setHoldState(state);
|
||||
} else if (isMyTopic(topic, TargetTemperatureCommandTopic)) {
|
||||
} else if (compareTopics(topic, TargetTemperatureCommandTopic)) {
|
||||
char src[length + 1];
|
||||
memset(src, 0, sizeof(src));
|
||||
memcpy(src, payload, length);
|
||||
|
||||
setTargetTemperature(HAUtils::strToTemp(src));
|
||||
} else if (isMyTopic(topic, ModeCommandTopic)) {
|
||||
} else if (compareTopics(topic, ModeCommandTopic)) {
|
||||
char mode[length + 1];
|
||||
memset(mode, 0, sizeof(mode));
|
||||
memcpy(mode, payload, length);
|
||||
@@ -279,7 +278,7 @@ bool HAHVAC::setTempStep(double tempStep)
|
||||
bool HAHVAC::publishAction(Action action)
|
||||
{
|
||||
if (!(_features & ActionFeature) ||
|
||||
strlen(name()) == 0) {
|
||||
strlen(uniqueId()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -323,7 +322,7 @@ bool HAHVAC::publishAction(Action action)
|
||||
bool HAHVAC::publishAuxHeatingState(bool state)
|
||||
{
|
||||
if (!(_features & AuxHeatingFeature) ||
|
||||
strlen(name()) == 0) {
|
||||
strlen(uniqueId()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -341,7 +340,7 @@ bool HAHVAC::publishAuxHeatingState(bool state)
|
||||
bool HAHVAC::publishAwayState(bool state)
|
||||
{
|
||||
if (!(_features & AwayModeFeature) ||
|
||||
strlen(name()) == 0) {
|
||||
strlen(uniqueId()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -359,7 +358,7 @@ bool HAHVAC::publishAwayState(bool state)
|
||||
bool HAHVAC::publishHoldState(bool state)
|
||||
{
|
||||
if (!(_features & HoldFeature) ||
|
||||
strlen(name()) == 0) {
|
||||
strlen(uniqueId()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -376,7 +375,7 @@ bool HAHVAC::publishHoldState(bool state)
|
||||
|
||||
bool HAHVAC::publishCurrentTemperature(double temperature)
|
||||
{
|
||||
if (strlen(name()) == 0) {
|
||||
if (strlen(uniqueId()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -396,7 +395,7 @@ bool HAHVAC::publishCurrentTemperature(double temperature)
|
||||
|
||||
bool HAHVAC::publishTargetTemperature(double temperature)
|
||||
{
|
||||
if (strlen(name()) == 0) {
|
||||
if (strlen(uniqueId()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -416,7 +415,7 @@ bool HAHVAC::publishTargetTemperature(double temperature)
|
||||
|
||||
bool HAHVAC::publishMode(Mode mode)
|
||||
{
|
||||
if (strlen(name()) == 0 || mode == UnknownMode) {
|
||||
if (strlen(uniqueId()) == 0 || mode == UnknownMode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -509,17 +508,17 @@ uint16_t HAHVAC::calculateSerializedLength(const char* serializedDevice) const
|
||||
|
||||
uint16_t size = 0;
|
||||
size += DeviceTypeSerializer::calculateBaseJsonDataSize();
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(_uniqueId);
|
||||
size += DeviceTypeSerializer::calculateNameFieldSize(getName());
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(uniqueId());
|
||||
size += DeviceTypeSerializer::calculateDeviceFieldSize(serializedDevice);
|
||||
size += DeviceTypeSerializer::calculateAvailabilityFieldSize(this);
|
||||
size += DeviceTypeSerializer::calculateNameFieldSize(_label);
|
||||
size += DeviceTypeSerializer::calculateRetainFieldSize(_retain);
|
||||
|
||||
// current temperature
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
CurrentTemperatureTopic,
|
||||
false
|
||||
);
|
||||
@@ -536,7 +535,7 @@ uint16_t HAHVAC::calculateSerializedLength(const char* serializedDevice) const
|
||||
if (_features & ActionFeature) {
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
ActionTopic,
|
||||
false
|
||||
);
|
||||
@@ -555,7 +554,7 @@ uint16_t HAHVAC::calculateSerializedLength(const char* serializedDevice) const
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
AuxCommandTopic,
|
||||
false
|
||||
);
|
||||
@@ -572,7 +571,7 @@ uint16_t HAHVAC::calculateSerializedLength(const char* serializedDevice) const
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
AuxStateTopic,
|
||||
false
|
||||
);
|
||||
@@ -592,7 +591,7 @@ uint16_t HAHVAC::calculateSerializedLength(const char* serializedDevice) const
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
AwayCommandTopic,
|
||||
false
|
||||
);
|
||||
@@ -609,7 +608,7 @@ uint16_t HAHVAC::calculateSerializedLength(const char* serializedDevice) const
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
AwayStateTopic,
|
||||
false
|
||||
);
|
||||
@@ -629,7 +628,7 @@ uint16_t HAHVAC::calculateSerializedLength(const char* serializedDevice) const
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
HoldCommandTopic,
|
||||
false
|
||||
);
|
||||
@@ -646,7 +645,7 @@ uint16_t HAHVAC::calculateSerializedLength(const char* serializedDevice) const
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
HoldStateTopic,
|
||||
false
|
||||
);
|
||||
@@ -693,7 +692,7 @@ uint16_t HAHVAC::calculateSerializedLength(const char* serializedDevice) const
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
TargetTemperatureCommandTopic,
|
||||
false
|
||||
);
|
||||
@@ -710,7 +709,7 @@ uint16_t HAHVAC::calculateSerializedLength(const char* serializedDevice) const
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
TargetTemperatureStateTopic,
|
||||
false
|
||||
);
|
||||
@@ -737,7 +736,7 @@ uint16_t HAHVAC::calculateSerializedLength(const char* serializedDevice) const
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
ModeCommandTopic,
|
||||
false
|
||||
);
|
||||
@@ -754,7 +753,7 @@ uint16_t HAHVAC::calculateSerializedLength(const char* serializedDevice) const
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
ModeStateTopic,
|
||||
false
|
||||
);
|
||||
@@ -1066,11 +1065,11 @@ bool HAHVAC::writeSerializedData(const char* serializedDevice) const
|
||||
);
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteRetainField(_retain);
|
||||
DeviceTypeSerializer::mqttWriteNameField(_label);
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(_uniqueId);
|
||||
DeviceTypeSerializer::mqttWriteAvailabilityField(this);
|
||||
DeviceTypeSerializer::mqttWriteNameField(getName());
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(uniqueId());
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteAvailabilityField(this);
|
||||
DeviceTypeSerializer::mqttWriteRetainField(_retain);
|
||||
DeviceTypeSerializer::mqttWriteEndJson();
|
||||
|
||||
return true;
|
||||
|
||||
@@ -258,14 +258,6 @@ public:
|
||||
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)`
|
||||
@@ -334,7 +326,6 @@ private:
|
||||
double _tempStep;
|
||||
HAHVAC_STATE_CALLBACK_DOUBLE(_targetTempCallback);
|
||||
double _targetTemperature;
|
||||
const char* _label;
|
||||
uint8_t _modes;
|
||||
HAHVAC_STATE_CALLBACK_MODE(_modeChangedCallback);
|
||||
Mode _currentMode;
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#include "../HAMqtt.h"
|
||||
#include "../HADevice.h"
|
||||
|
||||
HASensor::HASensor(const char* name) :
|
||||
BaseDeviceType("sensor", name),
|
||||
HASensor::HASensor(const char* uniqueId) :
|
||||
BaseDeviceType("sensor", uniqueId),
|
||||
_class(nullptr),
|
||||
_units(nullptr),
|
||||
_icon(nullptr)
|
||||
@@ -17,15 +17,15 @@ HASensor::HASensor(const char* name) :
|
||||
|
||||
}
|
||||
|
||||
HASensor::HASensor(const char* name, HAMqtt& mqtt) :
|
||||
HASensor(name)
|
||||
HASensor::HASensor(const char* uniqueId, HAMqtt& mqtt) :
|
||||
HASensor(uniqueId)
|
||||
{
|
||||
(void)mqtt;
|
||||
}
|
||||
|
||||
void HASensor::onMqttConnected()
|
||||
{
|
||||
if (strlen(name()) == 0) {
|
||||
if (strlen(uniqueId()) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ bool HASensor::setValue(float value, uint8_t precision)
|
||||
|
||||
bool HASensor::publishValue(const char* value)
|
||||
{
|
||||
if (strlen(name()) == 0 || value == nullptr) {
|
||||
if (strlen(uniqueId()) == 0 || value == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ bool HASensor::publishValue(const char* value)
|
||||
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
@@ -99,7 +99,7 @@ bool HASensor::publishValue(const char* value)
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
|
||||
@@ -125,15 +125,15 @@ uint16_t HASensor::calculateSerializedLength(
|
||||
|
||||
uint16_t size = 0;
|
||||
size += DeviceTypeSerializer::calculateBaseJsonDataSize();
|
||||
size += DeviceTypeSerializer::calculateNameFieldSize(name());
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(name());
|
||||
size += DeviceTypeSerializer::calculateNameFieldSize(getName());
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(uniqueId());
|
||||
size += DeviceTypeSerializer::calculateDeviceFieldSize(serializedDevice);
|
||||
size += DeviceTypeSerializer::calculateAvailabilityFieldSize(this);
|
||||
|
||||
{
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
DeviceTypeSerializer::StateTopic,
|
||||
false
|
||||
);
|
||||
@@ -206,8 +206,8 @@ bool HASensor::writeSerializedData(const char* serializedDevice) const
|
||||
);
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteNameField(name());
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(name());
|
||||
DeviceTypeSerializer::mqttWriteNameField(getName());
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(uniqueId());
|
||||
DeviceTypeSerializer::mqttWriteAvailabilityField(this);
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson();
|
||||
|
||||
@@ -11,13 +11,13 @@ public:
|
||||
/**
|
||||
* Initializes binary sensor.
|
||||
*
|
||||
* @param name Name of the sensor. Recommendes characters: [a-z0-9\-_]
|
||||
* @param uniqueId Unique ID of the sensor. Recommendes characters: [a-z0-9\-_]
|
||||
*/
|
||||
HASensor(
|
||||
const char* name
|
||||
const char* uniqueId
|
||||
);
|
||||
HASensor(
|
||||
const char* name,
|
||||
const char* uniqueId,
|
||||
HAMqtt& mqtt
|
||||
); // legacy constructor
|
||||
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
#include "../HAMqtt.h"
|
||||
#include "../HADevice.h"
|
||||
|
||||
HASwitch::HASwitch(const char* name, bool initialState) :
|
||||
BaseDeviceType("switch", name),
|
||||
HASwitch::HASwitch(const char* uniqueId, bool initialState) :
|
||||
BaseDeviceType("switch", uniqueId),
|
||||
_stateCallback(nullptr),
|
||||
_beforeStateCallback(nullptr),
|
||||
_currentState(initialState),
|
||||
_icon(nullptr),
|
||||
_retain(false)
|
||||
@@ -16,18 +17,18 @@ HASwitch::HASwitch(const char* name, bool initialState) :
|
||||
}
|
||||
|
||||
HASwitch::HASwitch(
|
||||
const char* name,
|
||||
const char* uniqueId,
|
||||
bool initialState,
|
||||
HAMqtt& mqtt
|
||||
) :
|
||||
HASwitch(name, initialState)
|
||||
HASwitch(uniqueId, initialState)
|
||||
{
|
||||
(void)mqtt;
|
||||
}
|
||||
|
||||
void HASwitch::onMqttConnected()
|
||||
{
|
||||
if (strlen(name()) == 0) {
|
||||
if (strlen(uniqueId()) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -52,7 +53,7 @@ void HASwitch::onMqttMessage(
|
||||
{
|
||||
(void)payload;
|
||||
|
||||
if (isMyTopic(topic, DeviceTypeSerializer::CommandTopic)) {
|
||||
if (compareTopics(topic, DeviceTypeSerializer::CommandTopic)) {
|
||||
bool state = (length == strlen(DeviceTypeSerializer::StateOn));
|
||||
setState(state, true);
|
||||
}
|
||||
@@ -64,11 +65,15 @@ bool HASwitch::setState(bool state, bool force)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_beforeStateCallback) {
|
||||
_beforeStateCallback(state, this);
|
||||
}
|
||||
|
||||
if (publishState(state)) {
|
||||
_currentState = state;
|
||||
|
||||
if (_stateCallback) {
|
||||
_stateCallback(state, this);
|
||||
_stateCallback(_currentState, this);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -79,7 +84,7 @@ bool HASwitch::setState(bool state, bool force)
|
||||
|
||||
bool HASwitch::publishState(bool state)
|
||||
{
|
||||
if (strlen(name()) == 0) {
|
||||
if (strlen(uniqueId()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -107,8 +112,8 @@ uint16_t HASwitch::calculateSerializedLength(const char* serializedDevice) const
|
||||
|
||||
uint16_t size = 0;
|
||||
size += DeviceTypeSerializer::calculateBaseJsonDataSize();
|
||||
size += DeviceTypeSerializer::calculateNameFieldSize(name());
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(name());
|
||||
size += DeviceTypeSerializer::calculateNameFieldSize(getName());
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(uniqueId());
|
||||
size += DeviceTypeSerializer::calculateDeviceFieldSize(serializedDevice);
|
||||
size += DeviceTypeSerializer::calculateAvailabilityFieldSize(this);
|
||||
size += DeviceTypeSerializer::calculateRetainFieldSize(_retain);
|
||||
@@ -117,7 +122,7 @@ uint16_t HASwitch::calculateSerializedLength(const char* serializedDevice) const
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
DeviceTypeSerializer::CommandTopic,
|
||||
false
|
||||
);
|
||||
@@ -134,7 +139,7 @@ uint16_t HASwitch::calculateSerializedLength(const char* serializedDevice) const
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
DeviceTypeSerializer::StateTopic,
|
||||
false
|
||||
);
|
||||
@@ -194,8 +199,8 @@ bool HASwitch::writeSerializedData(const char* serializedDevice) const
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteRetainField(_retain);
|
||||
DeviceTypeSerializer::mqttWriteNameField(name());
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(name());
|
||||
DeviceTypeSerializer::mqttWriteNameField(getName());
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(uniqueId());
|
||||
DeviceTypeSerializer::mqttWriteAvailabilityField(this);
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson();
|
||||
|
||||
+14
-10
@@ -13,16 +13,16 @@ public:
|
||||
/**
|
||||
* Initializes switch.
|
||||
*
|
||||
* @param name Name of the switch. Recommendes characters: [a-z0-9\-_]
|
||||
* @param uniqueId Unique ID of the switch. Recommendes characters: [a-z0-9\-_]
|
||||
* @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,
|
||||
const char* uniqueId,
|
||||
bool initialState
|
||||
);
|
||||
HASwitch(
|
||||
const char* name,
|
||||
const char* uniqueId,
|
||||
bool initialState,
|
||||
HAMqtt& mqtt
|
||||
); // legacy constructor
|
||||
@@ -42,12 +42,6 @@ public:
|
||||
const uint16_t& length
|
||||
) override;
|
||||
|
||||
/**
|
||||
* Returns name of the switch assigned via constructor.
|
||||
*/
|
||||
inline const char* getName() const
|
||||
{ return _name; }
|
||||
|
||||
/**
|
||||
* Changes state of the switch and publishes MQTT message.
|
||||
* Please note that if a new value is the same as previous one,
|
||||
@@ -79,7 +73,7 @@ public:
|
||||
{ return _currentState; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the value of the switch changes.
|
||||
* Registers callback that will be called each time the state of the switch changes.
|
||||
* Please note that it's not possible to register multiple callbacks for the same switch.
|
||||
*
|
||||
* @param callback
|
||||
@@ -87,6 +81,15 @@ public:
|
||||
inline void onStateChanged(HASWITCH_CALLBACK(callback))
|
||||
{ _stateCallback = callback; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called before state of the switch changes.
|
||||
* The state passed to callback is a new state that is going to be set.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onBeforeStateChanged(HASWITCH_CALLBACK(callback))
|
||||
{ _beforeStateCallback = callback; }
|
||||
|
||||
/**
|
||||
* Sets icon of the switch, e.g. `mdi:home`.
|
||||
*
|
||||
@@ -110,6 +113,7 @@ private:
|
||||
bool writeSerializedData(const char* serializedDevice) const override;
|
||||
|
||||
HASWITCH_CALLBACK(_stateCallback);
|
||||
HASWITCH_CALLBACK(_beforeStateCallback);
|
||||
bool _currentState;
|
||||
const char* _icon;
|
||||
bool _retain;
|
||||
|
||||
@@ -5,21 +5,21 @@
|
||||
#include "../HAMqtt.h"
|
||||
#include "../HADevice.h"
|
||||
|
||||
HATagScanner::HATagScanner(const char* name) :
|
||||
BaseDeviceType("tag", name)
|
||||
HATagScanner::HATagScanner(const char* uniqueId) :
|
||||
BaseDeviceType("tag", uniqueId)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HATagScanner::HATagScanner(const char* name, HAMqtt& mqtt) :
|
||||
HATagScanner(name)
|
||||
HATagScanner::HATagScanner(const char* uniqueId, HAMqtt& mqtt) :
|
||||
HATagScanner(uniqueId)
|
||||
{
|
||||
(void)mqtt;
|
||||
}
|
||||
|
||||
void HATagScanner::onMqttConnected()
|
||||
{
|
||||
if (strlen(name()) == 0) {
|
||||
if (strlen(uniqueId()) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -28,13 +28,13 @@ void HATagScanner::onMqttConnected()
|
||||
|
||||
bool HATagScanner::tagScanned(const char* tag)
|
||||
{
|
||||
if (tag == nullptr || strlen(tag) == 0 || strlen(name()) == 0) {
|
||||
if (tag == nullptr || strlen(tag) == 0 || strlen(uniqueId()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
DeviceTypeSerializer::EventTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
@@ -45,7 +45,7 @@ bool HATagScanner::tagScanned(const char* tag)
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
DeviceTypeSerializer::EventTopic
|
||||
);
|
||||
|
||||
@@ -72,7 +72,7 @@ uint16_t HATagScanner::calculateSerializedLength(
|
||||
{
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
componentName(),
|
||||
name(),
|
||||
uniqueId(),
|
||||
DeviceTypeSerializer::EventTopic,
|
||||
false
|
||||
);
|
||||
|
||||
@@ -11,10 +11,10 @@ public:
|
||||
/**
|
||||
* Initializes tag scanner with the given name.
|
||||
*
|
||||
* @param name Name of the scanner. Recommendes characters: [a-z0-9\-_]
|
||||
* @param uniqueId Unique ID of the scanner. Recommendes characters: [a-z0-9\-_]
|
||||
*/
|
||||
HATagScanner(const char* name);
|
||||
HATagScanner(const char* name, HAMqtt& mqtt); // legacy constructor
|
||||
HATagScanner(const char* uniqueId);
|
||||
HATagScanner(const char* uniqueId, HAMqtt& mqtt); // legacy constructor
|
||||
|
||||
/**
|
||||
* Publishes configuration of the sensor to the MQTT.
|
||||
|
||||
@@ -132,7 +132,9 @@ void HATriggers::publishConfig()
|
||||
const uint16_t& topicLength = calculateTopicLength(
|
||||
componentName(),
|
||||
trigger,
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
DeviceTypeSerializer::ConfigTopic,
|
||||
true,
|
||||
true
|
||||
);
|
||||
const uint16_t& dataLength = calculateSerializedLength(
|
||||
trigger,
|
||||
@@ -147,7 +149,8 @@ void HATriggers::publishConfig()
|
||||
topic,
|
||||
componentName(),
|
||||
trigger,
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
DeviceTypeSerializer::ConfigTopic,
|
||||
true
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
@@ -165,15 +168,17 @@ uint16_t HATriggers::calculateTopicLength(
|
||||
const char* component,
|
||||
const HATrigger *trigger,
|
||||
const char* suffix,
|
||||
bool includeNullTerminator
|
||||
bool includeNullTerminator,
|
||||
bool isDiscoveryTopic
|
||||
) const
|
||||
{
|
||||
uint8_t length = strlen(trigger->type) + strlen(trigger->subtype) + 2; // + underscore and slash
|
||||
uint8_t length = strlen(trigger->type) + strlen(trigger->subtype) + 2; // underscore and slash
|
||||
return DeviceTypeSerializer::calculateTopicLength(
|
||||
component,
|
||||
nullptr,
|
||||
suffix,
|
||||
includeNullTerminator
|
||||
includeNullTerminator,
|
||||
isDiscoveryTopic
|
||||
) + length;
|
||||
}
|
||||
|
||||
@@ -181,11 +186,12 @@ uint16_t HATriggers::generateTopic(
|
||||
char* output,
|
||||
const char* component,
|
||||
const HATrigger *trigger,
|
||||
const char* suffix
|
||||
const char* suffix,
|
||||
bool isDiscoveryTopic
|
||||
) const
|
||||
{
|
||||
static const char Underscore[] PROGMEM = {"_"};
|
||||
uint8_t length = strlen(trigger->type) + strlen(trigger->subtype) + 2; // slash + null terminator
|
||||
uint8_t length = strlen(trigger->type) + strlen(trigger->subtype) + 2; // underscore + null terminator
|
||||
char objectId[length];
|
||||
|
||||
strcpy(objectId, trigger->subtype);
|
||||
@@ -196,7 +202,8 @@ uint16_t HATriggers::generateTopic(
|
||||
output,
|
||||
component,
|
||||
objectId,
|
||||
suffix
|
||||
suffix,
|
||||
isDiscoveryTopic
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,14 +42,16 @@ protected:
|
||||
const char* component,
|
||||
const HATrigger *trigger,
|
||||
const char* suffix,
|
||||
bool includeNullTerminator = true
|
||||
bool includeNullTerminator = true,
|
||||
bool isDiscoveryTopic = false
|
||||
) const;
|
||||
|
||||
uint16_t generateTopic(
|
||||
char* output,
|
||||
const char* component,
|
||||
const HATrigger *trigger,
|
||||
const char* suffix
|
||||
const char* suffix,
|
||||
bool isDiscoveryTopic = false
|
||||
) const;
|
||||
|
||||
private:
|
||||
|
||||
Reference in New Issue
Block a user