release
This commit is contained in:
3
lib/home-assistant-integration/.gitignore
vendored
Normal file
3
lib/home-assistant-integration/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
docsrc/xml
|
||||
docsrc/build
|
||||
.DS_Store
|
||||
1
lib/home-assistant-integration/.piopm
Normal file
1
lib/home-assistant-integration/.piopm
Normal file
@@ -0,0 +1 @@
|
||||
{"type": "library", "name": "home-assistant-integration", "version": "2.0.0", "spec": {"owner": "dawidchyrzynski", "id": 11661, "name": "home-assistant-integration", "requirements": null, "uri": null}}
|
||||
85
lib/home-assistant-integration/CHANGELOG.md
Normal file
85
lib/home-assistant-integration/CHANGELOG.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# Changelog
|
||||
|
||||
## 2.0.0
|
||||
|
||||
**New features:**
|
||||
|
||||
* Added support for the `icon` property in the `HABinarySensor` (you can set the icon using `HABinarySensor::setIcon("iconName")`)
|
||||
* Added support for changing the current state of the `HABinarySensor` using `HABinarySensor::setCurrentState` method
|
||||
* Added support for forcing `setState` in `HABinarySensor` using a second argument as follows `HABinarySensor::setState(true, true)`
|
||||
* Added support for the `device_class` property in the `HACover` (you can set the class using `HACover::setDeviceClass("className")`
|
||||
* Added support for the `icon` property in the `HACover` (you can set the icon using `HACover::setIcon("iconName")`)
|
||||
* Added pointer of the sender to the `HACover` callback function
|
||||
* Added support for `optimistic` property in the `HACover` (you can change the mode using `HACover::setOptimistic(true)`)
|
||||
* Added support for forcing `setPosition` in `HACover` using a second argument as follows `HACover::setPosition(100, true)`
|
||||
* Added support for the `device_class` property in the `HASwitch` (you can set the class using `HASwitch::setDeviceClass("className")`
|
||||
* Added support for the `optimistic` property in the `HASwitch` (you can change the mode using `HASwitch::setOptimistic(true)`)
|
||||
* Added support for the `force_update` property in the `HASensor` (you can set the mode using `HASensor::setForceUpdate(true)`)
|
||||
* Added support for the `HAButton` device type
|
||||
* Added support for the `HADeviceTracker` device type
|
||||
* Added support for the `HACamera` device type
|
||||
* Added support for the `HALock` device type
|
||||
* Added support for the `HASelect` device type
|
||||
* Added support for the `HANumber` device type
|
||||
* Added support for the `HAScene` device type
|
||||
* Added support for the `HALight` device type
|
||||
|
||||
**Bugs fixes:**
|
||||
* Last Will Message is now retained (#70)
|
||||
* Compilation error on SAMD family (#82)
|
||||
|
||||
**New examples:**
|
||||
* [Button](examples/button/button.ino) - adding simple buttons to the Home Assistant panel.
|
||||
|
||||
**Breaking changes:**
|
||||
|
||||
* Changed structure of all MQTT topics used in the library.
|
||||
* Changed constructor of the `HABinarySensor` class (removed `deviceClass` and `initialState` arguments)
|
||||
* Renamed `HABinarySensor::getState()` method to `HABinarySensor::getCurrentState()`
|
||||
* Replaced `HATriggers` with `HADeviceTrigger` - the new implementation is not backward compatible. Please check the updated example of the `multi-state-button`.
|
||||
* Renamed `HADevice::isOnline()` method to `HADevice::isAvailable()`
|
||||
* Renamed `HASwitch::onStateChanged` method to `HASwitch::onCommand`.
|
||||
* Renamed `HAFan::onStateChanged` method to `HAFan::onStateCommand`.
|
||||
* Renamed `HAFan::onSpeedChanged` method to `HAFan::onSpeedCommand`.
|
||||
* Changed logic of the `HASwitch` callback. Please check the `led-switch` example.
|
||||
* Refactored `HASensor` logic. It's now divided into two different classes: `HASensor` and `HASensorNumber`.
|
||||
* Removed all legacy constructors with `HAMqtt` argument
|
||||
* Removed `onConnectionFailed` callback from the `HAMqtt` class
|
||||
* The position in the `HACover` is now available as configurable feature. It's disabled by default.
|
||||
* Refactored `HAHVAC` class to support more features of the MQTT discovery. Please check the update example.
|
||||
|
||||
## 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
|
||||
|
||||
**Improvements:**
|
||||
* 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](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](src/ArduinoHADefines.h))
|
||||
* Added support for setting icon in HASwitch and HASensor
|
||||
* Added support for setting retain flag in HASwitch
|
||||
* Added support for text (const char*) payload in HASensor
|
||||
* Added support for fans (HAFan)
|
||||
* Added support for connecting to the MQTT broker using hostname
|
||||
* Added `onConnected()` method in the HAMqtt
|
||||
* Added `onConnectionFailed()` method in the HAMqtt
|
||||
* Added support for MQTT LWT (see [Advanced Availability example](examples/advanced-availability/advanced-availability.ino))
|
||||
|
||||
**Improvements:**
|
||||
* Optimized codebase and logic in all devices types
|
||||
* Updated all examples
|
||||
* Fixed compilation warnings in all classes
|
||||
661
lib/home-assistant-integration/LICENSE
Normal file
661
lib/home-assistant-integration/LICENSE
Normal file
@@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
99
lib/home-assistant-integration/README.md
Normal file
99
lib/home-assistant-integration/README.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# Arduino Home Assistant integration 🏠
|
||||
|
||||
ArduinoHA allows to integrate an Arduino/ESP based device with Home Assistant using MQTT.
|
||||
The library is designed to use as low resources (RAM/flash) as possible.
|
||||
Initially, it was optimized to work on Arduino Uno with Ethernet Shield,
|
||||
but I successfully use it on ESP8266/ESP8255 boards in my projects.
|
||||
|
||||
[📘 Documentation](https://dawidchyrzynski.github.io/arduino-home-assistant/)
|
||||
|
||||
## Features
|
||||
|
||||
* Two-way communication (state reporting and command execution)
|
||||
* 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
|
||||
* Doxygen documentation for all classes
|
||||
* Covered by unit tests (AUnit + EpoxyDuino + AUniter)
|
||||
|
||||
## Supported HA types
|
||||
|
||||
| Home Assistant type | Supported |
|
||||
| ------------------- | :--------: |
|
||||
| Binary sensor | ✅ |
|
||||
| Button | ✅ |
|
||||
| Camera | ✅ |
|
||||
| Cover | ✅ |
|
||||
| Device tracker | ✅ |
|
||||
| Device trigger | ✅ |
|
||||
| Fan | ✅ |
|
||||
| Humidifier | ❌ |
|
||||
| HVAC | ✅ |
|
||||
| Light | ✅ |
|
||||
| Lock | ✅ |
|
||||
| Number | ✅ |
|
||||
| Scene | ✅ |
|
||||
| Select | ✅ |
|
||||
| Sensor | ✅ |
|
||||
| Switch | ✅ |
|
||||
| Tag scanner | ✅ |
|
||||
| Vacuum | ❌ |
|
||||
|
||||
## Examples
|
||||
|
||||
|Example|Description |
|
||||
|-------|-----------------------------|
|
||||
|[Binary sensor](examples/binary-sensor/binary-sensor.ino)|Using the binary sensor as a door contact sensor.|
|
||||
|[Button](examples/button/button.ino)|Adding simple buttons to the Home Assistant panel.|
|
||||
|[Camera](examples/esp32-cam/esp32-cam.ino)|Publishing the preview from the ESP32-CAM module.|
|
||||
|[Cover](examples/cover/cover.ino)|Controlling a window cover (open / close / stop).|
|
||||
|[Device trigger](examples/multi-state-button/multi-state-button.ino)|Implementation of a simple wall switch that reports press and hold states.|
|
||||
|[Fan](examples/fan/fan.ino)|Controlling a simple fan (state + speed).|
|
||||
|[HVAC](examples/hvac/hvac.ino)|HVAC controller with multiple modes, power control and target temperature.|
|
||||
|[Lock](examples/lock/lock.ino)|A simple door lock that's controlled by the Home Assistant.|
|
||||
|[Light](examples/light/light.ino)|A simple light that allows changing brightness, color temperature and RGB color.|
|
||||
|[Number](examples/number/number.ino)|Adding an interactive numeric slider in the Home Assistant panel.|
|
||||
|[Scene](examples/scene/scene.ino)|Adding a custom scene in the Home Assistant panel. |
|
||||
|[Select](examples/select/select.ino)|A dropdown selector that's displayed in the Home Assistant panel.|
|
||||
|[Sensor](examples/sensor/sensor.ino)|A simple sensor that reports a state in a string representation (open / opening / close).|
|
||||
|[Analog sensor](examples/sensor-analog/sensor-analog.ino)|Reporting the analog pin's voltage to the Home Assistant.|
|
||||
|[Integer sensor](examples/sensor-integer/sensor-integer.ino)|Reporting the device's uptime to the Home Assistant.|
|
||||
|[Switch](examples/led-switch/led-switch.ino)|The LED that's controlled by the Home Assistant.|
|
||||
|[Multi-switch](examples/multi-switch/multi-switch.ino)|Multiple switches controlled by the Home Assistant.|
|
||||
|[Tag scanner](examples/tag-scanner/tag-scanner.ino)|Scanning RFID tags using the MFRC522 module.|
|
||||
|[Availability](examples/availability/availability.ino)|Reporting entities' availability (online / offline) to the Home Assistant.|
|
||||
|[Advanced availability](examples/advanced-availability/advanced-availability.ino)|Advanced availability reporting with MQTT LWT (Last Will and Testament).|
|
||||
|[MQTT advanced](examples/mqtt-advanced/mqtt-advanced.ino)|Subscribing to custom topics and publishing custom messages.|
|
||||
|[MQTT with credentials](examples/mqtt-with-credentials/mqtt-with-credentials.ino)|Establishing connection with a MQTT broker using the credentials. |
|
||||
|[NodeMCU (ESP8266)](examples/nodemcu/nodemcu.ino)|Basic example for ESP8266 devices.|
|
||||
|[Arduino Nano 33 IoT](examples/nano33iot/nano33iot.ino)|Basic example for Arduino Nano 33 IoT (SAMD family).|
|
||||
|[mDNS discovery](examples/mdns/mdns.ino)|Make your ESP8266 discoverable via the mDNS.|
|
||||
|
||||
## Compatible hardware
|
||||
|
||||
The library uses the Arduino Ethernet Client API for interacting with the network hardware.
|
||||
It should work fine as long as the `Client` class is available.
|
||||
|
||||
Here is the list of devices on which the library was tested:
|
||||
|
||||
* Arduino Uno
|
||||
* Arduino Mega
|
||||
* Arduino Nano
|
||||
* Arduino Pro Mini
|
||||
* Arduino Nano 33 IoT
|
||||
* NodeMCU
|
||||
* Controllino Mega (Pure)
|
||||
* Controllino Maxi (Pure)
|
||||
* ESP-01
|
||||
* ESP32-CAM
|
||||
* Sonoff Dual R2
|
||||
* Sonoff Dual R3
|
||||
* Sonoff Basic
|
||||
* Sonoff Mini
|
||||
* Tuya Wi-Fi switch module
|
||||
* Tuya Wi-Fi curtain module
|
||||
|
||||
Please note that it's not the complete list of supported devices.
|
||||
You may try to use the library on any device that uses Arduino core.
|
||||
31
lib/home-assistant-integration/docsrc/Makefile
Normal file
31
lib/home-assistant-integration/docsrc/Makefile
Normal file
@@ -0,0 +1,31 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
# Build Github pages
|
||||
github:
|
||||
@make clean
|
||||
@rm -rf xml
|
||||
@doxygen source/Doxyfile
|
||||
@make html
|
||||
@mkdir -p ../docs
|
||||
@rm -rf ../docs/*
|
||||
@touch ../docs/.nojekyll
|
||||
@cp -a build/html/. ../docs
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
17
lib/home-assistant-integration/docsrc/Pipfile
Normal file
17
lib/home-assistant-integration/docsrc/Pipfile
Normal file
@@ -0,0 +1,17 @@
|
||||
[[source]]
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
exhale = "0.3.4"
|
||||
breathe = "4.34.0"
|
||||
sphinx = "5.0.2"
|
||||
lxml = "4.8.0"
|
||||
sphinx-press-theme = "0.8.0"
|
||||
sphinxcontrib-email = "0.3.5"
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[requires]
|
||||
python_version = "3.9"
|
||||
389
lib/home-assistant-integration/docsrc/Pipfile.lock
generated
Normal file
389
lib/home-assistant-integration/docsrc/Pipfile.lock
generated
Normal file
@@ -0,0 +1,389 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "06cbc4b751660842e50f1cd113469dcedc03e5f6ed5be43ab74f58b69b09e7f1"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.9"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"alabaster": {
|
||||
"hashes": [
|
||||
"sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359",
|
||||
"sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"
|
||||
],
|
||||
"version": "==0.7.12"
|
||||
},
|
||||
"babel": {
|
||||
"hashes": [
|
||||
"sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51",
|
||||
"sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.10.3"
|
||||
},
|
||||
"beautifulsoup4": {
|
||||
"hashes": [
|
||||
"sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30",
|
||||
"sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==4.11.1"
|
||||
},
|
||||
"breathe": {
|
||||
"hashes": [
|
||||
"sha256:48804dcf0e607a89fb6ad88c729ef12743a42db03ae9489be4ef8f7c4011774a",
|
||||
"sha256:ac0768a5e84addad3e632028fe67749c567aba2b29088493b64c2c1634bcdba1"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.34.0"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d",
|
||||
"sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2022.6.15"
|
||||
},
|
||||
"charset-normalizer": {
|
||||
"hashes": [
|
||||
"sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5",
|
||||
"sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.1.0"
|
||||
},
|
||||
"docutils": {
|
||||
"hashes": [
|
||||
"sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125",
|
||||
"sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==0.17.1"
|
||||
},
|
||||
"exhale": {
|
||||
"hashes": [
|
||||
"sha256:0871fa29ff9ac91c14e0dd69ad40db798068b4407e2e7ba5f650e07cedd6f365",
|
||||
"sha256:8fe83b2d96ef41e0f921b865c1bf46c40c6907cbeff0207ab9b445fd54539a16"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.3.4"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
|
||||
"sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==3.3"
|
||||
},
|
||||
"imagesize": {
|
||||
"hashes": [
|
||||
"sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b",
|
||||
"sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.4.1"
|
||||
},
|
||||
"importlib-metadata": {
|
||||
"hashes": [
|
||||
"sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670",
|
||||
"sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"
|
||||
],
|
||||
"markers": "python_version < '3.10'",
|
||||
"version": "==4.12.0"
|
||||
},
|
||||
"jinja2": {
|
||||
"hashes": [
|
||||
"sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852",
|
||||
"sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.1.2"
|
||||
},
|
||||
"lxml": {
|
||||
"hashes": [
|
||||
"sha256:04da965dfebb5dac2619cb90fcf93efdb35b3c6994fea58a157a834f2f94b318",
|
||||
"sha256:0538747a9d7827ce3e16a8fdd201a99e661c7dee3c96c885d8ecba3c35d1032c",
|
||||
"sha256:0645e934e940107e2fdbe7c5b6fb8ec6232444260752598bc4d09511bd056c0b",
|
||||
"sha256:079b68f197c796e42aa80b1f739f058dcee796dc725cc9a1be0cdb08fc45b000",
|
||||
"sha256:0f3f0059891d3254c7b5fb935330d6db38d6519ecd238ca4fce93c234b4a0f73",
|
||||
"sha256:10d2017f9150248563bb579cd0d07c61c58da85c922b780060dcc9a3aa9f432d",
|
||||
"sha256:1355755b62c28950f9ce123c7a41460ed9743c699905cbe664a5bcc5c9c7c7fb",
|
||||
"sha256:13c90064b224e10c14dcdf8086688d3f0e612db53766e7478d7754703295c7c8",
|
||||
"sha256:1423631e3d51008871299525b541413c9b6c6423593e89f9c4cfbe8460afc0a2",
|
||||
"sha256:1436cf0063bba7888e43f1ba8d58824f085410ea2025befe81150aceb123e345",
|
||||
"sha256:1a7c59c6ffd6ef5db362b798f350e24ab2cfa5700d53ac6681918f314a4d3b94",
|
||||
"sha256:1e1cf47774373777936c5aabad489fef7b1c087dcd1f426b621fda9dcc12994e",
|
||||
"sha256:206a51077773c6c5d2ce1991327cda719063a47adc02bd703c56a662cdb6c58b",
|
||||
"sha256:21fb3d24ab430fc538a96e9fbb9b150029914805d551deeac7d7822f64631dfc",
|
||||
"sha256:27e590352c76156f50f538dbcebd1925317a0f70540f7dc8c97d2931c595783a",
|
||||
"sha256:287605bede6bd36e930577c5925fcea17cb30453d96a7b4c63c14a257118dbb9",
|
||||
"sha256:2aaf6a0a6465d39b5ca69688fce82d20088c1838534982996ec46633dc7ad6cc",
|
||||
"sha256:32a73c53783becdb7eaf75a2a1525ea8e49379fb7248c3eeefb9412123536387",
|
||||
"sha256:41fb58868b816c202e8881fd0f179a4644ce6e7cbbb248ef0283a34b73ec73bb",
|
||||
"sha256:4780677767dd52b99f0af1f123bc2c22873d30b474aa0e2fc3fe5e02217687c7",
|
||||
"sha256:4878e667ebabe9b65e785ac8da4d48886fe81193a84bbe49f12acff8f7a383a4",
|
||||
"sha256:487c8e61d7acc50b8be82bda8c8d21d20e133c3cbf41bd8ad7eb1aaeb3f07c97",
|
||||
"sha256:49a866923e69bc7da45a0565636243707c22752fc38f6b9d5c8428a86121022c",
|
||||
"sha256:4beea0f31491bc086991b97517b9683e5cfb369205dac0148ef685ac12a20a67",
|
||||
"sha256:4cfbe42c686f33944e12f45a27d25a492cc0e43e1dc1da5d6a87cbcaf2e95627",
|
||||
"sha256:4d5bae0a37af799207140652a700f21a85946f107a199bcb06720b13a4f1f0b7",
|
||||
"sha256:4e285b5f2bf321fc0857b491b5028c5f276ec0c873b985d58d7748ece1d770dd",
|
||||
"sha256:57e4d637258703d14171b54203fd6822fda218c6c2658a7d30816b10995f29f3",
|
||||
"sha256:5974895115737a74a00b321e339b9c3f45c20275d226398ae79ac008d908bff7",
|
||||
"sha256:5ef87fca280fb15342726bd5f980f6faf8b84a5287fcc2d4962ea8af88b35130",
|
||||
"sha256:603a464c2e67d8a546ddaa206d98e3246e5db05594b97db844c2f0a1af37cf5b",
|
||||
"sha256:6653071f4f9bac46fbc30f3c7838b0e9063ee335908c5d61fb7a4a86c8fd2036",
|
||||
"sha256:6ca2264f341dd81e41f3fffecec6e446aa2121e0b8d026fb5130e02de1402785",
|
||||
"sha256:6d279033bf614953c3fc4a0aa9ac33a21e8044ca72d4fa8b9273fe75359d5cca",
|
||||
"sha256:6d949f53ad4fc7cf02c44d6678e7ff05ec5f5552b235b9e136bd52e9bf730b91",
|
||||
"sha256:6daa662aba22ef3258934105be2dd9afa5bb45748f4f702a3b39a5bf53a1f4dc",
|
||||
"sha256:6eafc048ea3f1b3c136c71a86db393be36b5b3d9c87b1c25204e7d397cee9536",
|
||||
"sha256:830c88747dce8a3e7525defa68afd742b4580df6aa2fdd6f0855481e3994d391",
|
||||
"sha256:86e92728ef3fc842c50a5cb1d5ba2bc66db7da08a7af53fb3da79e202d1b2cd3",
|
||||
"sha256:8caf4d16b31961e964c62194ea3e26a0e9561cdf72eecb1781458b67ec83423d",
|
||||
"sha256:8d1a92d8e90b286d491e5626af53afef2ba04da33e82e30744795c71880eaa21",
|
||||
"sha256:8f0a4d179c9a941eb80c3a63cdb495e539e064f8054230844dcf2fcb812b71d3",
|
||||
"sha256:9232b09f5efee6a495a99ae6824881940d6447debe272ea400c02e3b68aad85d",
|
||||
"sha256:927a9dd016d6033bc12e0bf5dee1dde140235fc8d0d51099353c76081c03dc29",
|
||||
"sha256:93e414e3206779ef41e5ff2448067213febf260ba747fc65389a3ddaa3fb8715",
|
||||
"sha256:98cafc618614d72b02185ac583c6f7796202062c41d2eeecdf07820bad3295ed",
|
||||
"sha256:9c3a88d20e4fe4a2a4a84bf439a5ac9c9aba400b85244c63a1ab7088f85d9d25",
|
||||
"sha256:9f36de4cd0c262dd9927886cc2305aa3f2210db437aa4fed3fb4940b8bf4592c",
|
||||
"sha256:a60f90bba4c37962cbf210f0188ecca87daafdf60271f4c6948606e4dabf8785",
|
||||
"sha256:a614e4afed58c14254e67862456d212c4dcceebab2eaa44d627c2ca04bf86837",
|
||||
"sha256:ae06c1e4bc60ee076292e582a7512f304abdf6c70db59b56745cca1684f875a4",
|
||||
"sha256:b122a188cd292c4d2fcd78d04f863b789ef43aa129b233d7c9004de08693728b",
|
||||
"sha256:b570da8cd0012f4af9fa76a5635cd31f707473e65a5a335b186069d5c7121ff2",
|
||||
"sha256:bcaa1c495ce623966d9fc8a187da80082334236a2a1c7e141763ffaf7a405067",
|
||||
"sha256:bd34f6d1810d9354dc7e35158aa6cc33456be7706df4420819af6ed966e85448",
|
||||
"sha256:be9eb06489bc975c38706902cbc6888f39e946b81383abc2838d186f0e8b6a9d",
|
||||
"sha256:c4b2e0559b68455c085fb0f6178e9752c4be3bba104d6e881eb5573b399d1eb2",
|
||||
"sha256:c62e8dd9754b7debda0c5ba59d34509c4688f853588d75b53c3791983faa96fc",
|
||||
"sha256:c852b1530083a620cb0de5f3cd6826f19862bafeaf77586f1aef326e49d95f0c",
|
||||
"sha256:d9fc0bf3ff86c17348dfc5d322f627d78273eba545db865c3cd14b3f19e57fa5",
|
||||
"sha256:dad7b164905d3e534883281c050180afcf1e230c3d4a54e8038aa5cfcf312b84",
|
||||
"sha256:e5f66bdf0976ec667fc4594d2812a00b07ed14d1b44259d19a41ae3fff99f2b8",
|
||||
"sha256:e8f0c9d65da595cfe91713bc1222af9ecabd37971762cb830dea2fc3b3bb2acf",
|
||||
"sha256:edffbe3c510d8f4bf8640e02ca019e48a9b72357318383ca60e3330c23aaffc7",
|
||||
"sha256:eea5d6443b093e1545ad0210e6cf27f920482bfcf5c77cdc8596aec73523bb7e",
|
||||
"sha256:ef72013e20dd5ba86a8ae1aed7f56f31d3374189aa8b433e7b12ad182c0d2dfb",
|
||||
"sha256:f05251bbc2145349b8d0b77c0d4e5f3b228418807b1ee27cefb11f69ed3d233b",
|
||||
"sha256:f1be258c4d3dc609e654a1dc59d37b17d7fef05df912c01fc2e15eb43a9735f3",
|
||||
"sha256:f9ced82717c7ec65a67667bb05865ffe38af0e835cdd78728f1209c8fffe0cad",
|
||||
"sha256:fe17d10b97fdf58155f858606bddb4e037b805a60ae023c009f760d8361a4eb8",
|
||||
"sha256:fe749b052bb7233fe5d072fcb549221a8cb1a16725c47c37e42b0b9cb3ff2c3f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.9.1"
|
||||
},
|
||||
"markupsafe": {
|
||||
"hashes": [
|
||||
"sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003",
|
||||
"sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88",
|
||||
"sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5",
|
||||
"sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7",
|
||||
"sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a",
|
||||
"sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603",
|
||||
"sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1",
|
||||
"sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135",
|
||||
"sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247",
|
||||
"sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6",
|
||||
"sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601",
|
||||
"sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77",
|
||||
"sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02",
|
||||
"sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e",
|
||||
"sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63",
|
||||
"sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f",
|
||||
"sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980",
|
||||
"sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b",
|
||||
"sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812",
|
||||
"sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff",
|
||||
"sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96",
|
||||
"sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1",
|
||||
"sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925",
|
||||
"sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a",
|
||||
"sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6",
|
||||
"sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e",
|
||||
"sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f",
|
||||
"sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4",
|
||||
"sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f",
|
||||
"sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3",
|
||||
"sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c",
|
||||
"sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a",
|
||||
"sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417",
|
||||
"sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a",
|
||||
"sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a",
|
||||
"sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37",
|
||||
"sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452",
|
||||
"sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933",
|
||||
"sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a",
|
||||
"sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.1.1"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
|
||||
"sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==21.3"
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb",
|
||||
"sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.12.0"
|
||||
},
|
||||
"pyparsing": {
|
||||
"hashes": [
|
||||
"sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb",
|
||||
"sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.8'",
|
||||
"version": "==3.0.9"
|
||||
},
|
||||
"pytz": {
|
||||
"hashes": [
|
||||
"sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7",
|
||||
"sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"
|
||||
],
|
||||
"version": "==2022.1"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983",
|
||||
"sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"
|
||||
],
|
||||
"markers": "python_version >= '3.7' and python_version < '4'",
|
||||
"version": "==2.28.1"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
|
||||
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.16.0"
|
||||
},
|
||||
"snowballstemmer": {
|
||||
"hashes": [
|
||||
"sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1",
|
||||
"sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"
|
||||
],
|
||||
"version": "==2.2.0"
|
||||
},
|
||||
"soupsieve": {
|
||||
"hashes": [
|
||||
"sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759",
|
||||
"sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.3.2.post1"
|
||||
},
|
||||
"sphinx": {
|
||||
"hashes": [
|
||||
"sha256:7bf8ca9637a4ee15af412d1a1d9689fec70523a68ca9bb9127c2f3eeb344e2e6",
|
||||
"sha256:ebf612653238bcc8f4359627a9b7ce44ede6fdd75d9d30f68255c7383d3a6226"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.5.0"
|
||||
},
|
||||
"sphinx-press-theme": {
|
||||
"hashes": [
|
||||
"sha256:2884caab1dc01ecb11d158d4dd6d3179e2dd97cd48516c769cc27360272e62b3",
|
||||
"sha256:ddf877d414a2c66e13396d726115aa3f0c94d1ac9133b4df028c261bf388ab25"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.8.0"
|
||||
},
|
||||
"sphinxcontrib-applehelp": {
|
||||
"hashes": [
|
||||
"sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a",
|
||||
"sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.0.2"
|
||||
},
|
||||
"sphinxcontrib-devhelp": {
|
||||
"hashes": [
|
||||
"sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e",
|
||||
"sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.0.2"
|
||||
},
|
||||
"sphinxcontrib-email": {
|
||||
"hashes": [
|
||||
"sha256:2387fc0691f3a4ed9c0b7d32acce5a42495340cc45f6e759a1b38e1eb86fe888",
|
||||
"sha256:74738235afd602b7f692d2b62b4582034e8bb32dbcaa7ecab34be985d8bae590"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.3.5"
|
||||
},
|
||||
"sphinxcontrib-htmlhelp": {
|
||||
"hashes": [
|
||||
"sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07",
|
||||
"sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.0.0"
|
||||
},
|
||||
"sphinxcontrib-jsmath": {
|
||||
"hashes": [
|
||||
"sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178",
|
||||
"sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.0.1"
|
||||
},
|
||||
"sphinxcontrib-qthelp": {
|
||||
"hashes": [
|
||||
"sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72",
|
||||
"sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.0.3"
|
||||
},
|
||||
"sphinxcontrib-serializinghtml": {
|
||||
"hashes": [
|
||||
"sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd",
|
||||
"sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.1.5"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14",
|
||||
"sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
|
||||
"version": "==1.26.9"
|
||||
},
|
||||
"zipp": {
|
||||
"hashes": [
|
||||
"sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad",
|
||||
"sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.8.0"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
}
|
||||
41
lib/home-assistant-integration/docsrc/README.md
Normal file
41
lib/home-assistant-integration/docsrc/README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Documentation
|
||||
|
||||
This document describes how to generate documentation of the library from the sources.
|
||||
If you're looking for the documentation please [follow this link](#).
|
||||
|
||||
## Prerequsites
|
||||
|
||||
1. Doxygen
|
||||
2. Python 3.9+
|
||||
3. Pipenv
|
||||
|
||||
## Installation
|
||||
|
||||
The first time setup is required before generating the documentation. Follow these steps:
|
||||
1. Open terminal in the `docsrc` directory
|
||||
2. Run `pipenv install`
|
||||
|
||||
### Apple Silicon M1
|
||||
|
||||
On Apple M1 processors you may get this error when trying to build the documentation: `Could not parse the contents of index.xml as an xml.`
|
||||
|
||||
Most likely this issue is caused by an architecture mismatch of the `lxml` package.
|
||||
To fix this issue open the Pipenv shell by running `pipenv shell` and follow these steps:
|
||||
1. Run `pip uninstall lxml`
|
||||
2. Run `arch -arm64 pip install lxml --no-binary lxml`
|
||||
|
||||
This solution forces `lxml` to be built from the sources.
|
||||
|
||||
## Generating the documentation
|
||||
|
||||
1. Open terminal in the `docsrc` directory
|
||||
2. Run the Doxygen command `doxygen`
|
||||
3. Open the Pipenv shell `pipenv shell`
|
||||
4. Run the build `make html`
|
||||
|
||||
The generated documentation will be available in the `docsrc/build/html` directory.
|
||||
|
||||
## Github pages
|
||||
|
||||
In order to build the documentation that's going to be published as Github pages run `make github`.
|
||||
The generated HTML build will be automatically moved to the `docs` directory.
|
||||
35
lib/home-assistant-integration/docsrc/make.bat
Normal file
35
lib/home-assistant-integration/docsrc/make.bat
Normal file
@@ -0,0 +1,35 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=source
|
||||
set BUILDDIR=build
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.https://www.sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
||||
2603
lib/home-assistant-integration/docsrc/source/Doxyfile
Normal file
2603
lib/home-assistant-integration/docsrc/source/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
90
lib/home-assistant-integration/docsrc/source/conf.py
Normal file
90
lib/home-assistant-integration/docsrc/source/conf.py
Normal file
@@ -0,0 +1,90 @@
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'ArduinoHA'
|
||||
copyright = '2022, Dawid Chyrzynski'
|
||||
author = 'Dawid Chyrzynski'
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = '2.0.0'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'breathe',
|
||||
'sphinx.ext.extlinks',
|
||||
'sphinxcontrib.email'
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
# templates_path = ['templates']
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = []
|
||||
|
||||
# Setup the breathe extension
|
||||
breathe_projects = {
|
||||
'ArduinoHA': '../xml'
|
||||
}
|
||||
breathe_default_project = 'ArduinoHA'
|
||||
breathe_show_include = True
|
||||
breathe_default_members = ('members', 'undoc-members')
|
||||
|
||||
# Tell sphinx what the primary language being documented is.
|
||||
primary_domain = 'cpp'
|
||||
|
||||
# Tell sphinx what the pygments highlight language should be.
|
||||
highlight_language = 'cpp'
|
||||
|
||||
extlinks = {
|
||||
'example': ('https://github.com/dawidchyrzynski/arduino-home-assistant/tree/main/examples/', None)
|
||||
}
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
html_scaled_image_link = False
|
||||
html_copy_source = False
|
||||
html_theme = 'press-ext'
|
||||
html_theme_path = ['themes']
|
||||
html_title = 'ArduinoHA'
|
||||
html_sidebars = {'**': ['util/searchbox.html', 'sidetoc.html']}
|
||||
html_theme_options = {
|
||||
'external_links': [
|
||||
('Github', 'https://github.com/dawidchyrzynski/arduino-home-assistant')
|
||||
]
|
||||
}
|
||||
html_js_files = [
|
||||
'custom.js'
|
||||
]
|
||||
html_css_files = [
|
||||
'custom.css'
|
||||
]
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named 'default.css' will overwrite the builtin 'default.css'.
|
||||
html_static_path = ['static']
|
||||
|
||||
# -- Extension ---------------------------------------------------------------
|
||||
|
||||
email_automode = False
|
||||
@@ -0,0 +1,9 @@
|
||||
HADevice class
|
||||
==============
|
||||
|
||||
.. doxygenclass:: HADevice
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HAMqtt class
|
||||
============
|
||||
|
||||
.. doxygenclass:: HAMqtt
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,7 @@
|
||||
Core API
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
|
||||
ha-device
|
||||
ha-mqtt
|
||||
@@ -0,0 +1,9 @@
|
||||
HABaseDeviceType class
|
||||
======================
|
||||
|
||||
.. doxygenclass:: HABaseDeviceType
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HABinarySensor class
|
||||
====================
|
||||
|
||||
.. doxygenclass:: HABinarySensor
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HAButton class
|
||||
==============
|
||||
|
||||
.. doxygenclass:: HAButton
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HACamera class
|
||||
==============
|
||||
|
||||
.. doxygenclass:: HACamera
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HACover class
|
||||
=============
|
||||
|
||||
.. doxygenclass:: HACover
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HADeviceTracker class
|
||||
=====================
|
||||
|
||||
.. doxygenclass:: HADeviceTracker
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HADeviceTrigger class
|
||||
=====================
|
||||
|
||||
.. doxygenclass:: HADeviceTrigger
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HAFan class
|
||||
===========
|
||||
|
||||
.. doxygenclass:: HAFan
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HAHVAC class
|
||||
============
|
||||
|
||||
.. doxygenclass:: HAHVAC
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HALight class
|
||||
=============
|
||||
|
||||
.. doxygenclass:: HALight
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HALock class
|
||||
============
|
||||
|
||||
.. doxygenclass:: HALock
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HANumber class
|
||||
==============
|
||||
|
||||
.. doxygenclass:: HANumber
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HAScene class
|
||||
=============
|
||||
|
||||
.. doxygenclass:: HAScene
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HASelect class
|
||||
==============
|
||||
|
||||
.. doxygenclass:: HASelect
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HASensorNumber class
|
||||
====================
|
||||
|
||||
.. doxygenclass:: HASensorNumber
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HASensor class
|
||||
==============
|
||||
|
||||
.. doxygenclass:: HASensor
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HASwitch class
|
||||
==============
|
||||
|
||||
.. doxygenclass:: HASwitch
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HATagScanner class
|
||||
==================
|
||||
|
||||
.. doxygenclass:: HATagScanner
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,23 @@
|
||||
Device types API
|
||||
================
|
||||
|
||||
.. toctree::
|
||||
|
||||
ha-base-device-type
|
||||
ha-binary-sensor
|
||||
ha-button
|
||||
ha-camera
|
||||
ha-cover
|
||||
ha-device-tracker
|
||||
ha-device-trigger
|
||||
ha-fan
|
||||
ha-hvac
|
||||
ha-light
|
||||
ha-lock
|
||||
ha-number
|
||||
ha-scene
|
||||
ha-select
|
||||
ha-sensor
|
||||
ha-sensor-number
|
||||
ha-switch
|
||||
ha-tag-scanner
|
||||
@@ -0,0 +1,8 @@
|
||||
API reference
|
||||
=============
|
||||
|
||||
.. toctree::
|
||||
|
||||
core/index
|
||||
device-types/index
|
||||
utils/index
|
||||
@@ -0,0 +1,9 @@
|
||||
HANumeric class
|
||||
===============
|
||||
|
||||
.. doxygenclass:: HANumeric
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HASerializerArray class
|
||||
=======================
|
||||
|
||||
.. doxygenclass:: HASerializerArray
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HASerializer class
|
||||
==================
|
||||
|
||||
.. doxygenclass:: HASerializer
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
HAUtils class
|
||||
=============
|
||||
|
||||
.. doxygenclass:: HAUtils
|
||||
:project: ArduinoHA
|
||||
:members:
|
||||
:protected-members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
@@ -0,0 +1,9 @@
|
||||
Utils API
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
|
||||
ha-numeric
|
||||
ha-serializer
|
||||
ha-serializer-array
|
||||
ha-utils
|
||||
@@ -0,0 +1,27 @@
|
||||
Compatible Hardware
|
||||
===================
|
||||
|
||||
The library uses the Arduino Ethernet Client API for interacting with the network hardware.
|
||||
It should work fine as long as the `Client` class is available.
|
||||
|
||||
Here is the list of devices on which the library was tested:
|
||||
|
||||
- Arduino Uno
|
||||
- Arduino Mega
|
||||
- Arduino Nano
|
||||
- Arduino Pro Mini
|
||||
- Arduino Nano 33 IoT
|
||||
- NodeMCU
|
||||
- Controllino Mega (Pure)
|
||||
- Controllino Maxi (Pure)
|
||||
- ESP-01
|
||||
- ESP32-CAM
|
||||
- Sonoff Dual R2
|
||||
- Sonoff Dual R3
|
||||
- Sonoff Basic
|
||||
- Sonoff Mini
|
||||
- Tuya Wi-Fi switch module
|
||||
- Tuya Wi-Fi curtain module
|
||||
|
||||
Please note that it's not the complete list of supported devices.
|
||||
You may try to use the library on any device that uses Arduino core.
|
||||
@@ -0,0 +1,60 @@
|
||||
Examples
|
||||
========
|
||||
|
||||
.. list-table::
|
||||
:widths: 25 75
|
||||
:header-rows: 1
|
||||
:class: examples-table
|
||||
|
||||
* - Example
|
||||
- Description
|
||||
* - :example:`Binary sensor <binary-sensor/binary-sensor.ino>`
|
||||
- Using the binary sensor as a door contact sensor.
|
||||
* - :example:`Button <button/button.ino>`
|
||||
- Adding simple buttons to the Home Assistant panel.
|
||||
* - :example:`Camera <esp32-cam/esp32-cam.ino>`
|
||||
- Publishing the preview from the ESP32-CAM module.
|
||||
* - :example:`Cover <cover/cover.ino>`
|
||||
- Controlling a window cover (open / close / stop).
|
||||
* - :example:`Device trigger <multi-state-button/multi-state-button.ino>`
|
||||
- Implementation of a simple wall switch that reports press and hold states.
|
||||
* - :example:`Fan <fan/fan.ino>`
|
||||
- Controlling a simple fan (state + speed).
|
||||
* - :example:`HVAC <hvac/hvac.ino>`
|
||||
- HVAC controller with multiple modes, power control and target temperature.
|
||||
* - :example:`Lock <lock/lock.ino>`
|
||||
- A simple door lock that's controlled by the Home Assistant.
|
||||
* - :example:`Light <light/light.ino>`
|
||||
- A simple light that allows changing brightness, color temperature and RGB color.
|
||||
* - :example:`Number <number/number.ino>`
|
||||
- Adding an interactive numeric slider in the Home Assistant panel.
|
||||
* - :example:`Scene <scene/scene.ino>`
|
||||
- Adding a custom scene in the Home Assistant panel.
|
||||
* - :example:`Select <select/select.ino>`
|
||||
- A dropdown selector that's displayed in the Home Assistant panel.
|
||||
* - :example:`Sensor <sensor/sensor.ino>`
|
||||
- A simple sensor that reports a state in a string representation (open / opening / close).
|
||||
* - :example:`Analog sensor <sensor-analog/sensor-analog.ino>`
|
||||
- Reporting the analog pin's voltage to the Home Assistant.
|
||||
* - :example:`Integer sensor <sensor-integer/sensor-integer.ino>`
|
||||
- Reporting the device's uptime to the Home Assistant.
|
||||
* - :example:`Switch <led-switch/led-switch.ino>`
|
||||
- The LED that's controlled by the Home Assistant.
|
||||
* - :example:`Multi-switch <multi-switch/multi-switch.ino>`
|
||||
- Multiple switches controlled by the Home Assistant.
|
||||
* - :example:`Tag scanner <tag-scanner/tag-scanner.ino>`
|
||||
- Scanning RFID tags using the MFRC522 module.
|
||||
* - :example:`Availability <availability/availability.ino>`
|
||||
- Reporting entities' availability (online / offline) to the Home Assistant.
|
||||
* - :example:`Advanced availability <advanced-availability/advanced-availability.ino>`
|
||||
- Advanced availability reporting with MQTT LWT (Last Will and Testament).
|
||||
* - :example:`MQTT advanced <mqtt-advanced/mqtt-advanced.ino>`
|
||||
- Subscribing to custom topics and publishing custom messages.
|
||||
* - :example:`MQTT with credentials <mqtt-with-credentials/mqtt-with-credentials.ino>`
|
||||
- Establishing connection with a MQTT broker using the credentials.
|
||||
* - :example:`NodeMCU (ESP8266) <nodemcu/nodemcu.ino>`
|
||||
- Basic example for ESP8266 devices.
|
||||
* - :example:`Arduino Nano 33 IoT <nano33iot/nano33iot.ino>`
|
||||
- Basic example for Arduino Nano 33 IoT (SAMD family).
|
||||
* - :example:`mDNS discovery <mdns/mdns.ino>`
|
||||
- Make your ESP8266 discoverable via the mDNS.
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
Getting started
|
||||
===============
|
||||
|
||||
Welcome to the ArduinoHA library!
|
||||
This documentation is a step-by-step guide that presents all features of the library.
|
||||
If you feel that there is anything unclear don't hesitate to open a new GitHub discussion.
|
||||
|
||||
Have fun!
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
prerequisites
|
||||
installation
|
||||
compatible-hardware
|
||||
examples
|
||||
@@ -0,0 +1,44 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
There are two ways to install the library depending on your project.
|
||||
If you're using Arduino IDE to compile your project you only need to install the library into the IDE.
|
||||
|
||||
The second method is meant for more advanced users that use *makeEspArduino* for building the project for ESP32/ESP8266 devices.
|
||||
|
||||
Arduino IDE
|
||||
-----------
|
||||
|
||||
To install the library into your Arduino IDE you can use Library Manager (available from IDE version 1.6.2).
|
||||
Open the IDE and click to the "Sketch" menu and then *Include Library > Manage Libraries*.
|
||||
|
||||
.. image:: images/manage-libraries.png
|
||||
:width: 500
|
||||
:align: center
|
||||
|
||||
Type "home-assistant-integration" in the search field, select the latest version of the library from the dropdown and then click *Install*.
|
||||
|
||||
.. image:: images/library-search.png
|
||||
:width: 500
|
||||
:align: center
|
||||
|
||||
For other installation methods please refer to `Arduino documentation <https://docs.arduino.cc/software/ide-v1/tutorials/installing-libraries>`_.
|
||||
|
||||
makeEspArduino
|
||||
--------------
|
||||
|
||||
The library can be installed in an environment managed by `makeEspArduino <https://github.com/plerup/makeEspArduino>`_.
|
||||
The best approach is to add the library and its dependency as submodules in the project as follows:
|
||||
|
||||
::
|
||||
|
||||
git submodule add https://github.com/dawidchyrzynski/arduino-home-assistant.git arduino-home-assistant
|
||||
cd arduino-home-assistant && git checkout tags/2.0.0 && cd ..
|
||||
git submodule add https://github.com/knolleary/pubsubclient.git pubsubclient
|
||||
cd pubsubclient && git checkout tags/v2.8
|
||||
|
||||
Then you just need to add one extra line in your `Makefile`:
|
||||
|
||||
::
|
||||
|
||||
LIBS := $(ROOT)/arduino-home-assistant $(ROOT)/pubsubclient
|
||||
@@ -0,0 +1,8 @@
|
||||
Prerequisites
|
||||
=============
|
||||
|
||||
ArduinoHA uses MQTT protocol over TCP to communicate with your Home Assistant instance.
|
||||
The MQTT broker is not installed by default so you will need to install an extra HA addon in order to get the integration working.
|
||||
|
||||
The recommended setup method is to use the `Mosquitto Brokker addon <https://github.com/home-assistant/addons/blob/master/mosquitto/DOCS.md>`_.
|
||||
After installing the addon your HA instance acts as a broker for all devices you're going to integrate with it.
|
||||
@@ -0,0 +1,100 @@
|
||||
Availability reporting
|
||||
======================
|
||||
|
||||
Home Assistant allows to track online/offline states of devices and device types.
|
||||
In this way controls available in the panel will be displayed as disabled if a device is offline.
|
||||
|
||||
The library allows to expose state of the entire device (i.e. shared availability) or specific type (sensor, switch, light, etc.).
|
||||
By default this feature is not enabled to save resources (RAM and flash) but you can easily turn it on as shown below.
|
||||
|
||||
Shared availability
|
||||
-------------------
|
||||
|
||||
I highly recommend to use shared availability feature as it allows to utilize MQTT LWT.
|
||||
Basically, shared availability allows to control availability of all types related to a specific device.
|
||||
For example: if your device has 5 switches and 2 buttons you can control their availability in the HA panel using a single method call.
|
||||
|
||||
See example below showing how to enable shared availability of the device.
|
||||
By default, the device is considered online but you can control its state manually using ``HADevice::setAvailability(bool online)`` method.
|
||||
In most cases you won't need to control availability manually as the library takes care of availability as long as the device is powered on.
|
||||
|
||||
::
|
||||
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
HADevice device("myUniqueID");
|
||||
|
||||
void setup() {
|
||||
device.enableSharedAvailability();
|
||||
// device.setAvailability(false); // changes default state to offline
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// ...
|
||||
|
||||
// device.setAvailability(true); // you can control availability manually if you want
|
||||
}
|
||||
|
||||
MQTT LWT
|
||||
--------
|
||||
|
||||
The shared availability feature is considered a complete solution only if it's used with MQTT LWT feature.
|
||||
Without LWT if the device is powered off then Home Assistant displays it as online.
|
||||
That's because availability tracking relies on MQTT messages and if you cut off power of your device then its not capable of publishing the offline message.
|
||||
|
||||
When LWT feature is enabled the device becomes offline in the HA panel even if you cut off power supply.
|
||||
This solution is implemented by MQTT broker that automatically publishes the message when the TCP connection to the device is lost.
|
||||
|
||||
::
|
||||
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
HADevice device("myUniqueID");
|
||||
|
||||
void setup() {
|
||||
device.enableSharedAvailability();
|
||||
device.enableLastWill();
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// ...
|
||||
}
|
||||
|
||||
Device type's availability
|
||||
--------------------------
|
||||
|
||||
There also a way to control availability of specific device types.
|
||||
Each type can be controlled separately as shown below.
|
||||
Please note that this solution requires shared availability to be disabled and it's not supported by LWT.
|
||||
|
||||
::
|
||||
|
||||
#include <Ethernet.h>
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A};
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
HASwitch mySwitch("mySwitchId");
|
||||
|
||||
void setup() {
|
||||
Ethernet.begin(mac);
|
||||
|
||||
// this line enables availability for your switch
|
||||
mySwitch.setAvailability(true); // you can also set it to false
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// ...
|
||||
|
||||
// you can control availability at runtime as follows:
|
||||
mySwitch.setAvailability(true); // online
|
||||
mySwitch.setAvailability(false); // offline
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
Compiler macros
|
||||
===============
|
||||
|
||||
The library supports couple of macros that are defined in the `ArduinoHADefines.h` file.
|
||||
You can uncomment them in this specific file or provide flags directly to compiler (for example: via Makefile).
|
||||
|
||||
Debug mode
|
||||
----------
|
||||
|
||||
Debug mode unlocks logging feature in the library.
|
||||
Logs may be useful for debugging the communication with the Home Assistant.
|
||||
|
||||
To enable debug mode you need to defined `ARDUINOHA_DEBUG` macro.
|
||||
|
||||
Code optimization
|
||||
-----------------
|
||||
|
||||
Defining one of the macros listed below results in truncating the corresponding device type.
|
||||
It may be useful if you want to save some flash memory occupied by virtual tables of those classes.
|
||||
|
||||
* `EX_ARDUINOHA_BINARY_SENSOR`
|
||||
* `EX_ARDUINOHA_BUTTON`
|
||||
* `EX_ARDUINOHA_CAMERA`
|
||||
* `EX_ARDUINOHA_COVER`
|
||||
* `EX_ARDUINOHA_DEVICE_TRACKER`
|
||||
* `EX_ARDUINOHA_DEVICE_TRIGGER`
|
||||
* `EX_ARDUINOHA_FAN`
|
||||
* `EX_ARDUINOHA_HVAC`
|
||||
* `EX_ARDUINOHA_LIGHT`
|
||||
* `EX_ARDUINOHA_LOCK`
|
||||
* `EX_ARDUINOHA_NUMBER`
|
||||
* `EX_ARDUINOHA_SCENE`
|
||||
* `EX_ARDUINOHA_SELECT`
|
||||
* `EX_ARDUINOHA_SENSOR`
|
||||
* `EX_ARDUINOHA_SWITCH`
|
||||
* `EX_ARDUINOHA_TAG_SCANNER`
|
||||
@@ -0,0 +1,46 @@
|
||||
Connection parameters
|
||||
=====================
|
||||
|
||||
:doc:`HAMqtt </documents/api/core/ha-mqtt>` class exposes a few variants of the ``begin`` method that allows specifying the MQTT connection parameters.
|
||||
**This method should be called only once and at the end of the setup logic.**
|
||||
The example below presents all possible variants. Pick one that meets your requirements.
|
||||
|
||||
.. NOTE::
|
||||
|
||||
Connection to the MQTT broker is established asynchronously.
|
||||
The :doc:`HAMqtt::begin </documents/api/core/ha-mqtt>` method just sets the parameters of the connection.
|
||||
The connection attempt is made during the loop cycle.
|
||||
|
||||
::
|
||||
|
||||
#include <Ethernet.h>
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A};
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
|
||||
void setup() {
|
||||
Ethernet.begin(mac);
|
||||
|
||||
// anoymous connection on default port 1883
|
||||
mqtt.begin("192.168.1.50");
|
||||
|
||||
// anoymous connection on port 8888
|
||||
mqtt.begin("192.168.1.50", 8888);
|
||||
|
||||
// connection with credentials on default port 1883
|
||||
mqtt.begin("192.168.1.50", "username", "password");
|
||||
|
||||
// connection with credentials on port 8888
|
||||
mqtt.begin("192.168.1.50", 8888, "username", "password");
|
||||
|
||||
// you can also use hostname in all variants
|
||||
mqtt.begin("mybroker.local");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Ethernet.maintain();
|
||||
mqtt.loop();
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
Device configuration
|
||||
====================
|
||||
|
||||
:doc:`HADevice </documents/api/core/ha-device>` represents the physical device where the library is installed.
|
||||
Logically it's a group of types like sensors, switches, lights and so on.
|
||||
In the Home Assistant, it's listed with properties that may be configured using the library's API.
|
||||
|
||||
Each property except the unique ID is optional.
|
||||
Setting optional properties increases flash and RAM usage so it's not recommended to set them on lower-spec MCUs.
|
||||
|
||||
The supported properties are:
|
||||
|
||||
* unique ID*
|
||||
* name
|
||||
* software version
|
||||
* manufacturer
|
||||
* model
|
||||
|
||||
Unique ID
|
||||
---------
|
||||
|
||||
The ID of a device needs to be unique in a scope of a Home Assistant instance.
|
||||
The safest solution is to use the MAC address of an Ethernet or Wi-Fi chip but you can also implement your own solution.
|
||||
|
||||
There are three different ways to set the ID of the device.
|
||||
You can pick one depending on your needs.
|
||||
|
||||
1) Providing string (const char*) to the :doc:`HADevice </documents/api/core/ha-device>` constructor
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Try to keep the ID simple (alphanumeric characters) and short.
|
||||
|
||||
::
|
||||
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
HADevice device("myUniqueID");
|
||||
|
||||
void setup() {
|
||||
// ...
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// ...
|
||||
}
|
||||
|
||||
2) Providing byte array to the :doc:`HADevice </documents/api/core/ha-device>` constructor
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
::
|
||||
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
// use your own unique bytes sequence
|
||||
byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A};
|
||||
HADevice device(mac, sizeof(mac));
|
||||
|
||||
void setup() {
|
||||
// ...
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// ...
|
||||
}
|
||||
|
||||
3) Using :doc:`HADevice::setUniqueId </documents/api/core/ha-device>` method during the setup
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
::
|
||||
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
HADevice device;
|
||||
|
||||
void setup() {
|
||||
// any length is acceptable
|
||||
byte myId[] = {0x05, 0xb4, 0xc6, 0x9f, 0xbe, 0xce, 0x8c, 0x1f, 0xc7};
|
||||
device.setUniqueId(myId, sizeof(myId)); // the input array is cloned internally
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// ...
|
||||
}
|
||||
|
||||
Device properties
|
||||
-----------------
|
||||
|
||||
Each property has its corresponding setter method in the :doc:`HADevice </documents/api/core/ha-device>` class.
|
||||
Please note that all these methods accept const char pointer whose **content is not copied**.
|
||||
|
||||
::
|
||||
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
HADevice device("myUniqueId");
|
||||
|
||||
void setup() {
|
||||
device.setName("Bedroom Light Controller");
|
||||
device.setSoftwareVersion("1.0.0");
|
||||
device.setManufacturer("Developer Corp.");
|
||||
device.setModel("ABC-123");
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// ...
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
|
||||
Device types
|
||||
============
|
||||
|
||||
Device type represents a single entity in the Home Assistant panel.
|
||||
It can be a sensor, lock, camera or anything that's listed in the table below.
|
||||
|
||||
Your physical device (for example ESP-01 board) can have multiple device types assigned.
|
||||
They will be displayed as child entities in the HA panel.
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
Registering a new device type requires some flash and RAM memory to be utilized.
|
||||
On less powerful units like Arduino Uno, you may quickly hit the limit of resources, so keeping the device simple is recommended.
|
||||
Hitting the resource limit will result in random reboots of the device.
|
||||
|
||||
By default, the maximum number of device types is 6.
|
||||
You can increase the limit using the :doc:`HAMqtt </documents/api/core/ha-mqtt>` class constructor as follows:
|
||||
|
||||
::
|
||||
|
||||
#include <Ethernet.h>
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A};
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device, 12); // <------------ 12 is a new limit of device types
|
||||
|
||||
void setup() {
|
||||
Ethernet.begin(mac);
|
||||
|
||||
// your setup logic goes here
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Ethernet.maintain();
|
||||
mqtt.loop();
|
||||
|
||||
// your loop logic goes here
|
||||
}
|
||||
|
||||
Please note that this limit cannot be changed at runtime.
|
||||
|
||||
Supported device types
|
||||
----------------------
|
||||
|
||||
.. list-table::
|
||||
:widths: 25 15 60
|
||||
:header-rows: 1
|
||||
:class: supported-device-types-table
|
||||
|
||||
* - Device type
|
||||
- Supported
|
||||
- Documentation
|
||||
* - Binary sensor
|
||||
- ✅
|
||||
- :doc:`HABinarySensor </documents/api/device-types/ha-binary-sensor>`
|
||||
* - Button
|
||||
- ✅
|
||||
- :doc:`HAButton </documents/api/device-types/ha-button>`
|
||||
* - Camera
|
||||
- ✅
|
||||
- :doc:`HACamera </documents/api/device-types/ha-camera>`
|
||||
* - Cover
|
||||
- ✅
|
||||
- :doc:`HACover </documents/api/device-types/ha-cover>`
|
||||
* - Device tracker
|
||||
- ✅
|
||||
- :doc:`HADeviceTracker </documents/api/device-types/ha-device-tracker>`
|
||||
* - Device trigger
|
||||
- ✅
|
||||
- :doc:`HADeviceTrigger </documents/api/device-types/ha-device-trigger>`
|
||||
* - Fan
|
||||
- ✅
|
||||
- :doc:`HAFan </documents/api/device-types/ha-fan>`
|
||||
* - Humidifier
|
||||
- ❌
|
||||
- --
|
||||
* - HVAC
|
||||
- ✅
|
||||
- :doc:`HAHVAC </documents/api/device-types/ha-hvac>`
|
||||
* - Light
|
||||
- ✅
|
||||
- :doc:`HALight </documents/api/device-types/ha-light>`
|
||||
* - Lock
|
||||
- ✅
|
||||
- :doc:`HALock </documents/api/device-types/ha-lock>`
|
||||
* - Number
|
||||
- ✅
|
||||
- :doc:`HANumber </documents/api/device-types/ha-number>`
|
||||
* - Scene
|
||||
- ✅
|
||||
- :doc:`HAScene </documents/api/device-types/ha-scene>`
|
||||
* - Select
|
||||
- ✅
|
||||
- :doc:`HASelect </documents/api/device-types/ha-select>`
|
||||
* - | Sensor (text)
|
||||
- ✅
|
||||
- :doc:`HASensor </documents/api/device-types/ha-sensor>`
|
||||
* - | Sensor (number)
|
||||
- ✅
|
||||
- :doc:`HASensorNumber </documents/api/device-types/ha-sensor-number>`
|
||||
* - Switch
|
||||
- ✅
|
||||
- :doc:`HASwitch </documents/api/device-types/ha-switch>`
|
||||
* - Tag scanner
|
||||
- ✅
|
||||
- :doc:`HATagScanner </documents/api/device-types/ha-tag-scanner>`
|
||||
* - Vacuum
|
||||
- ❌
|
||||
- --
|
||||
@@ -0,0 +1,44 @@
|
||||
Discovery
|
||||
=========
|
||||
|
||||
The library automatically maintains connection to the MQTT broker and takes care of the discovery process.
|
||||
Each device type that you create (sensor, switch, light, fan, etc.) is automatically registered in MQTT manager.
|
||||
Whenever connection with the MQTT broker is acquired the configuration of all device types is pushed to the Home Assistant.
|
||||
|
||||
There is one basic rule that you need to follow: device types need to be constructed after :doc:`HAMqtt </documents/api/core/ha-mqtt>` class.
|
||||
That's because device types are relying on :doc:`HAMqtt </documents/api/core/ha-mqtt>` instance internally.
|
||||
|
||||
Topics prefix
|
||||
-------------
|
||||
|
||||
In some cases you may need to change prefix of MQTT topics.
|
||||
There are two types of topics utilized by the library:
|
||||
|
||||
* **discovery topic** - used for publishing device types' configuration (default: ``homeassistant``)
|
||||
* **data topic** - used for publishing states, data, etc. (default: ``aha``)
|
||||
|
||||
The discovery topic's prefix can be changed using ``HAMqtt::setDiscoveryPrefix(const char* prefix)`` method.
|
||||
The data topic's prefix can be changed using ``HAMqtt::setDataPrefix(const char* prefix)`` method
|
||||
|
||||
::
|
||||
|
||||
#include <Ethernet.h>
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A};
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
|
||||
// register your device types here
|
||||
|
||||
void setup() {
|
||||
mqtt.setDiscoveryPrefix("myCustomPrefix");
|
||||
mqtt.setDataPrefix("myDataPrefix");
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// ...
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
Library
|
||||
=======
|
||||
|
||||
This chapter describes basic all of the library's core.
|
||||
Solid understanding of foundations will allow you to utilize full potential of the library.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
introduction
|
||||
device-configuration
|
||||
availability-reporting
|
||||
connection-params
|
||||
discovery
|
||||
device-types
|
||||
mqtt-security
|
||||
mqtt-advanced
|
||||
compiler-macros
|
||||
@@ -0,0 +1,79 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
Before implementing the business logic of your application you will need to
|
||||
add a few necessary pieces of code to your project.
|
||||
Basically, everything comes up to a few basic rules:
|
||||
|
||||
1) :doc:`HADevice </documents/api/core/ha-device>` and :doc:`HAMqtt </documents/api/core/ha-mqtt>` instances need to be initialized once globally or as a part of another global object.
|
||||
2) :doc:`HAMqtt::begin </documents/api/core/ha-mqtt>` needs to be called at the end of setup logic. It provides MQTT broker credentials that will be used for a connection.
|
||||
3) :doc:`HAMqtt::loop </documents/api/core/ha-mqtt>` method needs to be called periodically (it doesn't need to be called on each tick).
|
||||
4) Device types need to be initialized after :doc:`HAMqtt </documents/api/core/ha-mqtt>` class (it will be described later in the documentation).
|
||||
|
||||
Here are the minimal boilerplates that you can start with.
|
||||
Don't worry if you have no idea what's going on here.
|
||||
Everything will be covered in the following chapters.
|
||||
|
||||
Arduino Boilerplate
|
||||
-------------------
|
||||
|
||||
::
|
||||
|
||||
#include <Ethernet.h>
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A};
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
|
||||
void setup() {
|
||||
Ethernet.begin(mac);
|
||||
|
||||
// your setup logic goes here
|
||||
|
||||
// MQTT broker connection (use your data here)
|
||||
mqtt.begin("192.168.1.50", "username", "password");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Ethernet.maintain();
|
||||
mqtt.loop();
|
||||
|
||||
// your loop logic goes here
|
||||
}
|
||||
|
||||
ESP32/ESP8266 Boilerplate
|
||||
-------------------------
|
||||
|
||||
::
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
WiFiClient client;
|
||||
HADevice device;
|
||||
HAMqtt mqtt(client, device);
|
||||
|
||||
void setup() {
|
||||
byte mac[WL_MAC_ADDR_LENGTH];
|
||||
WiFi.macAddress(mac);
|
||||
device.setUniqueId(mac, sizeof(mac));
|
||||
|
||||
// you can skip this part if you're already maintaining the connection logic
|
||||
WiFi.begin("MyNetworkSSID", "MyPassword");
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500); // waiting for the connection
|
||||
}
|
||||
|
||||
// your setup logic goes here
|
||||
|
||||
// MQTT broker connection (use your data here)
|
||||
mqtt.begin("192.168.1.50", "username", "password");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
|
||||
// your loop logic goes here
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
MQTT advanced features
|
||||
======================
|
||||
|
||||
Callbacks
|
||||
---------
|
||||
|
||||
:doc:`HAMqtt </documents/api/core/ha-mqtt>` class exposes some useful callbacks that you can bind to.
|
||||
Please take a look at the example below.
|
||||
|
||||
::
|
||||
|
||||
#include <Ethernet.h>
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A};
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
|
||||
void onMessage(const char* topic, const uint8_t* payload, uint16_t length) {
|
||||
// this method will be called each time the device receives an MQTT message
|
||||
}
|
||||
|
||||
void onConnected() {
|
||||
// this method will be called when connection to MQTT broker is established
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Ethernet.begin(mac);
|
||||
|
||||
mqtt.onMessage(onMessage);
|
||||
mqtt.onConnected(onConnected);
|
||||
mqtt.begin("192.168.1.50", "username", "password");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Ethernet.maintain();
|
||||
mqtt.loop();
|
||||
}
|
||||
|
||||
Subscriptions
|
||||
-------------
|
||||
|
||||
You can also subscribe to a custom topic using ``HAMqtt::subscribe(const char* topic)`` method.
|
||||
The subscription needs to be made each time a connection to the MQTT broker is established.
|
||||
|
||||
::
|
||||
|
||||
#include <Ethernet.h>
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A};
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
|
||||
void onMessage(const char* topic, const uint8_t* payload, uint16_t length) {
|
||||
if (strcmp(topic, "myTopic") == 0) {
|
||||
// message on "myTopic" received
|
||||
}
|
||||
}
|
||||
|
||||
void onConnected() {
|
||||
mqtt.subscribe("myTopic");
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Ethernet.begin(mac);
|
||||
|
||||
mqtt.onMessage(onMessage);
|
||||
mqtt.onConnected(onConnected);
|
||||
mqtt.begin("192.168.1.50", "username", "password");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Ethernet.maintain();
|
||||
mqtt.loop();
|
||||
}
|
||||
|
||||
Publishing a message
|
||||
--------------------
|
||||
|
||||
HAMqtt class also exposes the method that allows to publish custom messages.
|
||||
|
||||
::
|
||||
|
||||
#include <Ethernet.h>
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A};
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
|
||||
void setup() {
|
||||
Ethernet.begin(mac);
|
||||
|
||||
mqtt.begin("192.168.1.50", "username", "password");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Ethernet.maintain();
|
||||
mqtt.loop();
|
||||
|
||||
// Publishing the non-retained message:
|
||||
// mqtt.publish("customTopic", "customPayload");
|
||||
|
||||
// Publishing the retained message:
|
||||
// mqtt.publish("customTopic", "customPayload", true);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
MQTT security
|
||||
=============
|
||||
|
||||
The library allows you to use credentials for acquiring a TCP connection with the MQTT broker.
|
||||
By default you can use the same credentials you use for login in the Home Assistant panel but you can also configure custom credentials in the Mosquitto broker.
|
||||
|
||||
.. DANGER::
|
||||
|
||||
This solution is not 100% secure because communication between Arduino and Home Assistant is not encrypted.
|
||||
Username and password can be easily discovered by analyzing your local network traffic.
|
||||
However, that's fine as long as your local network is secured against unattended access.
|
||||
On more powerful devices (like ESP), you should consider using TLS/SSL connection.
|
||||
|
||||
::
|
||||
|
||||
#include <Ethernet.h>
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A};
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
|
||||
void setup() {
|
||||
// ...
|
||||
|
||||
// replace username and password with your credentials
|
||||
mqtt.begin("192.168.1.50", "username", "password");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
SSL connection
|
||||
--------------
|
||||
|
||||
On ESP32/ESP8266 you can use ``WiFiClientSecure`` client to establish encrypted connection between your device and Home Assistant.
|
||||
Please take a look at :example:`this example <mqtt-with-ssl/mqtt-with-ssl.ino>`.
|
||||
27
lib/home-assistant-integration/docsrc/source/index.rst
Normal file
27
lib/home-assistant-integration/docsrc/source/index.rst
Normal file
@@ -0,0 +1,27 @@
|
||||
ArduinoHA documentation
|
||||
==================================================
|
||||
|
||||
ArduinoHA allows to integrate an Arduino/ESP based device with Home Assistant using MQTT.
|
||||
The library is designed to use as low resources (RAM/flash) as possible.
|
||||
Initially, it was optimized to work on Arduino Uno with Ethernet Shield,
|
||||
but I successfully use it on ESP8266/ESP8255 boards in my projects.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Two-way communication (state reporting and command execution)
|
||||
* 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
|
||||
* Doxygen documentation
|
||||
* Covered by unit tests (AUnit + EpoxyDuino + AUniter)
|
||||
|
||||
.. toctree::
|
||||
:caption: Docs
|
||||
:maxdepth: 3
|
||||
|
||||
documents/getting-started/index
|
||||
documents/library/index
|
||||
documents/api/index
|
||||
@@ -0,0 +1,2 @@
|
||||
[theme]
|
||||
inherit = press
|
||||
10
lib/home-assistant-integration/library.properties
Normal file
10
lib/home-assistant-integration/library.properties
Normal file
@@ -0,0 +1,10 @@
|
||||
name=home-assistant-integration
|
||||
version=2.0.0
|
||||
author=Dawid Chyrzynski <dev@chyrzynski.pl>
|
||||
maintainer=Dawid Chyrzynski <dev@chyrzynski.pl>
|
||||
sentence=Home Assistant MQTT integration for Arduino
|
||||
paragraph=Lightweight library that provides easy to use API for integrating your Arduino/ESP based device with Home Assistant.
|
||||
category=Communication
|
||||
url=https://github.com/dawidchyrzynski/arduino-home-assistant
|
||||
architectures=*
|
||||
depends=PubSubClient
|
||||
33
lib/home-assistant-integration/src/ArduinoHA.h
Normal file
33
lib/home-assistant-integration/src/ArduinoHA.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef AHA_ARDUINOHA_H
|
||||
#define AHA_ARDUINOHA_H
|
||||
|
||||
#include "HADevice.h"
|
||||
#include "HAMqtt.h"
|
||||
#include "device-types/HABinarySensor.h"
|
||||
#include "device-types/HAButton.h"
|
||||
//#include "device-types/HACamera.h"
|
||||
//#include "device-types/HACover.h"
|
||||
//#include "device-types/HADeviceTracker.h"
|
||||
//#include "device-types/HADeviceTrigger.h"
|
||||
//#include "device-types/HAFan.h"
|
||||
//#include "device-types/HAHVAC.h"
|
||||
#include "device-types/HALight.h"
|
||||
//#include "device-types/HALock.h"
|
||||
#include "device-types/HANumber.h"
|
||||
//#include "device-types/HAScene.h"
|
||||
#include "device-types/HASelect.h"
|
||||
#include "device-types/HASensor.h"
|
||||
#include "device-types/HASensorNumber.h"
|
||||
#include "device-types/HASwitch.h"
|
||||
//#include "device-types/HATagScanner.h"
|
||||
#include "utils/HAUtils.h"
|
||||
#include "utils/HANumeric.h"
|
||||
|
||||
#ifdef ARDUINOHA_TEST
|
||||
#include "mocks/AUnitHelpers.h"
|
||||
#include "mocks/PubSubClientMock.h"
|
||||
#include "utils/HADictionary.h"
|
||||
#include "utils/HASerializer.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
35
lib/home-assistant-integration/src/ArduinoHADefines.h
Normal file
35
lib/home-assistant-integration/src/ArduinoHADefines.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Turns on debug information of the ArduinoHA core.
|
||||
// Please note that you need to initialize serial interface manually
|
||||
// by calling Serial.begin([baudRate]) before initializing ArduinoHA.
|
||||
//#define ARDUINOHA_DEBUG
|
||||
|
||||
// These macros allow to exclude some parts of the library to save more resources.
|
||||
#define EX_ARDUINOHA_BINARY_SENSOR
|
||||
//#define EX_ARDUINOHA_BUTTON
|
||||
#define EX_ARDUINOHA_CAMERA
|
||||
#define EX_ARDUINOHA_COVER
|
||||
#define EX_ARDUINOHA_DEVICE_TRACKER
|
||||
#define EX_ARDUINOHA_DEVICE_TRIGGER
|
||||
#define EX_ARDUINOHA_FAN
|
||||
#define EX_ARDUINOHA_HVAC
|
||||
//#define EX_ARDUINOHA_LIGHT
|
||||
#define EX_ARDUINOHA_LOCK
|
||||
#define EX_ARDUINOHA_NUMBER
|
||||
#define EX_ARDUINOHA_SCENE
|
||||
//#define EX_ARDUINOHA_SELECT
|
||||
// #define EX_ARDUINOHA_SENSOR
|
||||
// #define EX_ARDUINOHA_SWITCH
|
||||
#define EX_ARDUINOHA_TAG_SCANNER
|
||||
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
#include <Arduino.h>
|
||||
#define ARDUINOHA_DEBUG_PRINTLN(x) Serial.println(x);
|
||||
#define ARDUINOHA_DEBUG_PRINT(x) Serial.print(x);
|
||||
#else
|
||||
#define ARDUINOHA_DEBUG_INIT()
|
||||
#define ARDUINOHA_DEBUG_PRINTLN(x)
|
||||
#define ARDUINOHA_DEBUG_PRINT(x)
|
||||
#endif
|
||||
|
||||
#define AHATOFSTR(x) reinterpret_cast<const __FlashStringHelper*>(x)
|
||||
#define AHAFROMFSTR(x) reinterpret_cast<const char*>(x)
|
||||
146
lib/home-assistant-integration/src/HADevice.cpp
Normal file
146
lib/home-assistant-integration/src/HADevice.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
#include "ArduinoHADefines.h"
|
||||
#include "HADevice.h"
|
||||
#include "HAMqtt.h"
|
||||
#include "utils/HAUtils.h"
|
||||
#include "utils/HASerializer.h"
|
||||
|
||||
#define HADEVICE_INIT \
|
||||
_ownsUniqueId(false), \
|
||||
_serializer(new HASerializer(nullptr, 5)), \
|
||||
_availabilityTopic(nullptr), \
|
||||
_sharedAvailability(false), \
|
||||
_available(true) // device will be available by default
|
||||
|
||||
HADevice::HADevice() :
|
||||
_uniqueId(nullptr),
|
||||
HADEVICE_INIT
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HADevice::HADevice(const char* uniqueId) :
|
||||
_uniqueId(uniqueId),
|
||||
HADEVICE_INIT
|
||||
{
|
||||
_serializer->set(AHATOFSTR(HADeviceIdentifiersProperty), _uniqueId);
|
||||
}
|
||||
|
||||
HADevice::HADevice(const byte* uniqueId, const uint16_t length) :
|
||||
_uniqueId(HAUtils::byteArrayToStr(uniqueId, length)),
|
||||
HADEVICE_INIT
|
||||
{
|
||||
_ownsUniqueId = true;
|
||||
_serializer->set(AHATOFSTR(HADeviceIdentifiersProperty), _uniqueId);
|
||||
}
|
||||
|
||||
HADevice::~HADevice()
|
||||
{
|
||||
delete _serializer;
|
||||
|
||||
if (_availabilityTopic) {
|
||||
delete _availabilityTopic;
|
||||
}
|
||||
|
||||
if (_ownsUniqueId) {
|
||||
delete[] _uniqueId;
|
||||
}
|
||||
}
|
||||
|
||||
bool HADevice::setUniqueId(const byte* uniqueId, const uint16_t length)
|
||||
{
|
||||
if (_uniqueId) {
|
||||
return false; // unique ID cannot be changed at runtime once it's set
|
||||
}
|
||||
|
||||
_uniqueId = HAUtils::byteArrayToStr(uniqueId, length);
|
||||
_ownsUniqueId = true;
|
||||
_serializer->set(AHATOFSTR(HADeviceIdentifiersProperty), _uniqueId);
|
||||
return true;
|
||||
}
|
||||
|
||||
void HADevice::setManufacturer(const char* manufacturer)
|
||||
{
|
||||
_serializer->set(AHATOFSTR(HADeviceManufacturerProperty), manufacturer);
|
||||
}
|
||||
|
||||
void HADevice::setModel(const char* model)
|
||||
{
|
||||
_serializer->set(AHATOFSTR(HADeviceModelProperty), model);
|
||||
}
|
||||
|
||||
void HADevice::setName(const char* name)
|
||||
{
|
||||
_serializer->set(AHATOFSTR(HANameProperty), name);
|
||||
}
|
||||
|
||||
void HADevice::setSoftwareVersion(const char* softwareVersion)
|
||||
{
|
||||
_serializer->set(
|
||||
AHATOFSTR(HADeviceSoftwareVersionProperty),
|
||||
softwareVersion
|
||||
);
|
||||
}
|
||||
|
||||
void HADevice::setAvailability(bool online)
|
||||
{
|
||||
_available = online;
|
||||
publishAvailability();
|
||||
}
|
||||
|
||||
bool HADevice::enableSharedAvailability()
|
||||
{
|
||||
if (_sharedAvailability) {
|
||||
return true; // already enabled
|
||||
}
|
||||
|
||||
const uint16_t topicLength = HASerializer::calculateDataTopicLength(
|
||||
nullptr,
|
||||
AHATOFSTR(HAAvailabilityTopic)
|
||||
);
|
||||
if (topicLength == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_availabilityTopic = new char[topicLength];
|
||||
|
||||
if (HASerializer::generateDataTopic(
|
||||
_availabilityTopic,
|
||||
nullptr,
|
||||
AHATOFSTR(HAAvailabilityTopic)
|
||||
) > 0) {
|
||||
_sharedAvailability = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HADevice::enableLastWill()
|
||||
{
|
||||
HAMqtt* mqtt = HAMqtt::instance();
|
||||
if (!mqtt || !_availabilityTopic) {
|
||||
return;
|
||||
}
|
||||
|
||||
mqtt->setLastWill(
|
||||
_availabilityTopic,
|
||||
"offline",
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
void HADevice::publishAvailability() const
|
||||
{
|
||||
HAMqtt* mqtt = HAMqtt::instance();
|
||||
if (!_availabilityTopic || !mqtt) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char* payload = _available ? HAOnline : HAOffline;
|
||||
const uint16_t length = strlen_P(payload);
|
||||
|
||||
if (mqtt->beginPublish(_availabilityTopic, length, true)) {
|
||||
mqtt->writePayload(AHATOFSTR(payload));
|
||||
mqtt->endPublish();
|
||||
}
|
||||
}
|
||||
160
lib/home-assistant-integration/src/HADevice.h
Normal file
160
lib/home-assistant-integration/src/HADevice.h
Normal file
@@ -0,0 +1,160 @@
|
||||
#ifndef AHA_HADEVICE_H
|
||||
#define AHA_HADEVICE_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class HASerializer;
|
||||
|
||||
/**
|
||||
* This class represents your device that's going to be registered in the Home Assistant devices registry.
|
||||
* Each entity (HABinarySensor, HASensor, etc.) that you use will be owned by this device.
|
||||
*/
|
||||
class HADevice
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructs HADevice without the unique ID.
|
||||
*
|
||||
* @note You will need to set the ID using HADevice::setUniqueId method. Otherwise none of the entities will work.
|
||||
*/
|
||||
HADevice();
|
||||
|
||||
/**
|
||||
* Constructs HADevice with the given unique ID (string).
|
||||
* Keep the unique ID short to save the memory.
|
||||
*
|
||||
* @param uniqueId String with the null terminator.
|
||||
*/
|
||||
HADevice(const char* uniqueId);
|
||||
|
||||
/**
|
||||
* Constructs HADevice using the given byte array as the unique ID.
|
||||
* It works in the same way as HADevice::setUniqueId method.
|
||||
*
|
||||
* @param uniqueId Bytes array that's going to be converted into the string.
|
||||
* @param length Number of bytes in the array.
|
||||
*/
|
||||
HADevice(const byte* uniqueId, const uint16_t length);
|
||||
|
||||
/**
|
||||
* Deletes HASerializer and the availability topic if the shared availability was enabled.
|
||||
*/
|
||||
~HADevice();
|
||||
|
||||
/**
|
||||
* Returns pointer to the unique ID. It can be nullptr if the device has no ID assigned.
|
||||
*/
|
||||
inline const char* getUniqueId() const
|
||||
{ return _uniqueId; }
|
||||
|
||||
/**
|
||||
* Returns the instance of the HASerializer used by the device.
|
||||
* This method is used by all entities to serialize device's representation.
|
||||
*/
|
||||
inline const HASerializer* getSerializer() const
|
||||
{ return _serializer; }
|
||||
|
||||
/**
|
||||
* Returns true if the shared availability is enabled for the device.
|
||||
*/
|
||||
inline bool isSharedAvailabilityEnabled() const
|
||||
{ return _sharedAvailability; }
|
||||
|
||||
/**
|
||||
* Returns availability topic generated by the HADevice::enableSharedAvailability method.
|
||||
* It can be nullptr if the shared availability is not enabled.
|
||||
*/
|
||||
inline const char* getAvailabilityTopic() const
|
||||
{ return _availabilityTopic; }
|
||||
|
||||
/**
|
||||
* Returns online/offline state of the device.
|
||||
*/
|
||||
inline bool isAvailable() const
|
||||
{ return _available; }
|
||||
|
||||
/**
|
||||
* Sets unique ID of the device based on the given byte array.
|
||||
* Each byte is converted into a hex string representation, so the final length of the unique ID will be twice as given.
|
||||
*
|
||||
* @param uniqueId Bytes array that's going to be converted into the string.
|
||||
* @param length Number of bytes in the array.
|
||||
* @note The unique ID can be set only once (via constructor or using this method).
|
||||
*/
|
||||
bool setUniqueId(const byte* uniqueId, const uint16_t length);
|
||||
|
||||
/**
|
||||
* Sets the "manufacturer" property that's going to be displayed in the Home Assistant.
|
||||
*
|
||||
* @param manufacturer Any string. Keep it short to save the memory.
|
||||
*/
|
||||
void setManufacturer(const char* manufacturer);
|
||||
|
||||
/**
|
||||
* Sets the "model" property that's going to be displayed in the Home Assistant.
|
||||
*
|
||||
* @param model Any string. Keep it short to save the memory.
|
||||
*/
|
||||
void setModel(const char* model);
|
||||
|
||||
/**
|
||||
* Sets the "name" property that's going to be displayed in the Home Assistant.
|
||||
*
|
||||
* @param name Any string. Keep it short to save the memory.
|
||||
*/
|
||||
void setName(const char* name);
|
||||
|
||||
/**
|
||||
* Sets the "software version" property that's going to be displayed in the Home Assistant.
|
||||
*
|
||||
* @param softwareVersion Any string. Keep it short to save the memory.
|
||||
*/
|
||||
void setSoftwareVersion(const char* softwareVersion);
|
||||
|
||||
/**
|
||||
* Sets device's availability and publishes MQTT message on the availability topic.
|
||||
* If the device is not connected to an MQTT broker or the shared availability is not enabled then nothing happens.
|
||||
*
|
||||
* @param online Set to true if the device should be displayed as available in the HA panel.
|
||||
*/
|
||||
void setAvailability(bool online);
|
||||
|
||||
/**
|
||||
* Enables the shared availability feature.
|
||||
*/
|
||||
bool enableSharedAvailability();
|
||||
|
||||
/**
|
||||
* Enables MQTT LWT feature.
|
||||
* Please note that the shared availability needs to be enabled first.
|
||||
*/
|
||||
void enableLastWill();
|
||||
|
||||
/**
|
||||
* Publishes current availability of the device on the availability topic.
|
||||
* If the device is not connected to an MQTT broker or the shared availability is not enabled then nothing happens.
|
||||
* This method is called by the HAMqtt when the connection to an MQTT broker is acquired.
|
||||
*/
|
||||
void publishAvailability() const;
|
||||
|
||||
private:
|
||||
/// The unique ID of the device. It can be a memory allocated by HADevice::setUniqueId method.
|
||||
const char* _uniqueId;
|
||||
|
||||
/// Specifies whether HADevice class owns the _uniqueId pointer.
|
||||
bool _ownsUniqueId;
|
||||
|
||||
/// JSON serializer of the HADevice class. It's allocated in the constructor.
|
||||
HASerializer* _serializer;
|
||||
|
||||
/// The availability topic allocated by HADevice::enableSharedAvailability method.
|
||||
char* _availabilityTopic;
|
||||
|
||||
/// Specifies whether the shared availability is enabled.
|
||||
bool _sharedAvailability;
|
||||
|
||||
/// Specifies whether the device is available (online / offline).
|
||||
bool _available;
|
||||
};
|
||||
|
||||
#endif
|
||||
318
lib/home-assistant-integration/src/HAMqtt.cpp
Normal file
318
lib/home-assistant-integration/src/HAMqtt.cpp
Normal file
@@ -0,0 +1,318 @@
|
||||
#include "HAMqtt.h"
|
||||
|
||||
#ifndef ARDUINOHA_TEST
|
||||
#include <PubSubClient.h>
|
||||
#endif
|
||||
|
||||
#include "HADevice.h"
|
||||
#include "device-types/HABaseDeviceType.h"
|
||||
#include "mocks/PubSubClientMock.h"
|
||||
|
||||
#define HAMQTT_INIT \
|
||||
_device(device), \
|
||||
_messageCallback(nullptr), \
|
||||
_connectedCallback(nullptr), \
|
||||
_initialized(false), \
|
||||
_discoveryPrefix(DefaultDiscoveryPrefix), \
|
||||
_dataPrefix(DefaultDataPrefix), \
|
||||
_username(nullptr), \
|
||||
_password(nullptr), \
|
||||
_lastConnectionAttemptAt(0), \
|
||||
_devicesTypesNb(0), \
|
||||
_maxDevicesTypesNb(maxDevicesTypesNb), \
|
||||
_devicesTypes(new HABaseDeviceType *[maxDevicesTypesNb]), \
|
||||
_lastWillTopic(nullptr), \
|
||||
_lastWillMessage(nullptr), \
|
||||
_lastWillRetain(false)
|
||||
|
||||
static const char *DefaultDiscoveryPrefix = "homeassistant";
|
||||
static const char *DefaultDataPrefix = "SHA";
|
||||
|
||||
HAMqtt *HAMqtt::_instance = nullptr;
|
||||
|
||||
void onMessageReceived(char *topic, uint8_t *payload, unsigned int length)
|
||||
{
|
||||
if (HAMqtt::instance() == nullptr || length > UINT16_MAX)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HAMqtt::instance()->processMessage(topic, payload, static_cast<uint16_t>(length));
|
||||
}
|
||||
|
||||
#ifdef ARDUINOHA_TEST
|
||||
HAMqtt::HAMqtt(
|
||||
PubSubClientMock *pubSub,
|
||||
HADevice &device,
|
||||
uint8_t maxDevicesTypesNb) : _mqtt(pubSub),
|
||||
HAMQTT_INIT
|
||||
{
|
||||
_instance = this;
|
||||
}
|
||||
#else
|
||||
HAMqtt::HAMqtt(
|
||||
Client &netClient,
|
||||
HADevice &device,
|
||||
uint8_t maxDevicesTypesNb) : _mqtt(new PubSubClient(netClient)),
|
||||
HAMQTT_INIT
|
||||
{
|
||||
_instance = this;
|
||||
}
|
||||
#endif
|
||||
|
||||
HAMqtt::~HAMqtt()
|
||||
{
|
||||
delete[] _devicesTypes;
|
||||
|
||||
if (_mqtt)
|
||||
{
|
||||
delete _mqtt;
|
||||
}
|
||||
|
||||
_instance = nullptr;
|
||||
}
|
||||
|
||||
bool HAMqtt::begin(
|
||||
const IPAddress serverIp,
|
||||
const uint16_t serverPort,
|
||||
const char *username,
|
||||
const char *password,
|
||||
const char *clientID)
|
||||
{
|
||||
ARDUINOHA_DEBUG_PRINT(F("AHA: init server "))
|
||||
ARDUINOHA_DEBUG_PRINT(serverIp)
|
||||
ARDUINOHA_DEBUG_PRINT(F(":"))
|
||||
ARDUINOHA_DEBUG_PRINTLN(serverPort)
|
||||
|
||||
if (_initialized)
|
||||
{
|
||||
ARDUINOHA_DEBUG_PRINTLN(F("AHA: already initialized"))
|
||||
return false;
|
||||
}
|
||||
|
||||
_username = username;
|
||||
_password = password;
|
||||
_initialized = true;
|
||||
_clientID = clientID;
|
||||
_mqtt->setServer(serverIp, serverPort);
|
||||
_mqtt->setCallback(onMessageReceived);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HAMqtt::begin(
|
||||
const IPAddress serverIp,
|
||||
const char *username,
|
||||
const char *password,
|
||||
const char *userid)
|
||||
{
|
||||
return begin(serverIp, HAMQTT_DEFAULT_PORT, username, password, userid);
|
||||
}
|
||||
|
||||
bool HAMqtt::begin(
|
||||
const char *serverHostname,
|
||||
const uint16_t serverPort,
|
||||
const char *username,
|
||||
const char *password,
|
||||
const char *clientID)
|
||||
{
|
||||
ARDUINOHA_DEBUG_PRINT(F("AHA: init server "))
|
||||
ARDUINOHA_DEBUG_PRINT(serverHostname)
|
||||
ARDUINOHA_DEBUG_PRINT(F(":"))
|
||||
ARDUINOHA_DEBUG_PRINTLN(serverPort)
|
||||
|
||||
if (_initialized)
|
||||
{
|
||||
ARDUINOHA_DEBUG_PRINTLN(F("AHA: already initialized"))
|
||||
return false;
|
||||
}
|
||||
|
||||
_username = username;
|
||||
_password = password;
|
||||
_initialized = true;
|
||||
_clientID = clientID;
|
||||
|
||||
_mqtt->setServer(serverHostname, serverPort);
|
||||
_mqtt->setCallback(onMessageReceived);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HAMqtt::begin(
|
||||
const char *serverHostname,
|
||||
const char *username,
|
||||
const char *password,
|
||||
const char *userid)
|
||||
{
|
||||
return begin(serverHostname, HAMQTT_DEFAULT_PORT, username, password, userid);
|
||||
}
|
||||
|
||||
bool HAMqtt::disconnect()
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ARDUINOHA_DEBUG_PRINTLN(F("AHA: disconnecting"))
|
||||
|
||||
_initialized = false;
|
||||
_lastConnectionAttemptAt = 0;
|
||||
_mqtt->disconnect();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void HAMqtt::disableHA()
|
||||
{
|
||||
noHA = true;
|
||||
}
|
||||
|
||||
void HAMqtt::loop()
|
||||
{
|
||||
if (_initialized && !_mqtt->loop())
|
||||
{
|
||||
connectToServer();
|
||||
}
|
||||
}
|
||||
|
||||
bool HAMqtt::isConnected() const
|
||||
{
|
||||
return _mqtt->connected();
|
||||
}
|
||||
|
||||
void HAMqtt::addDeviceType(HABaseDeviceType *deviceType)
|
||||
{
|
||||
if (_devicesTypesNb + 1 >= _maxDevicesTypesNb)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_devicesTypes[_devicesTypesNb++] = deviceType;
|
||||
}
|
||||
|
||||
bool HAMqtt::publish(const char *topic, const char *payload, bool retained)
|
||||
{
|
||||
if (!isConnected())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ARDUINOHA_DEBUG_PRINT(F("AHA: publishing "))
|
||||
ARDUINOHA_DEBUG_PRINT(topic)
|
||||
ARDUINOHA_DEBUG_PRINT(F(", len: "))
|
||||
ARDUINOHA_DEBUG_PRINTLN(strlen(payload))
|
||||
|
||||
_mqtt->beginPublish(topic, strlen(payload), retained);
|
||||
_mqtt->write((const uint8_t *)(payload), strlen(payload));
|
||||
return _mqtt->endPublish();
|
||||
}
|
||||
|
||||
bool HAMqtt::beginPublish(
|
||||
const char *topic,
|
||||
uint16_t payloadLength,
|
||||
bool retained)
|
||||
{
|
||||
ARDUINOHA_DEBUG_PRINT(F("AHA: begin publish "))
|
||||
ARDUINOHA_DEBUG_PRINT(topic)
|
||||
ARDUINOHA_DEBUG_PRINT(F(", len: "))
|
||||
ARDUINOHA_DEBUG_PRINTLN(payloadLength)
|
||||
|
||||
return _mqtt->beginPublish(topic, payloadLength, retained);
|
||||
}
|
||||
|
||||
void HAMqtt::writePayload(const char *data, const uint16_t length)
|
||||
{
|
||||
writePayload(reinterpret_cast<const uint8_t *>(data), length);
|
||||
}
|
||||
|
||||
void HAMqtt::writePayload(const uint8_t *data, const uint16_t length)
|
||||
{
|
||||
_mqtt->write(data, length);
|
||||
}
|
||||
|
||||
void HAMqtt::writePayload(const __FlashStringHelper *src)
|
||||
{
|
||||
_mqtt->print(src);
|
||||
}
|
||||
|
||||
bool HAMqtt::endPublish()
|
||||
{
|
||||
return _mqtt->endPublish();
|
||||
}
|
||||
|
||||
bool HAMqtt::subscribe(const char *topic)
|
||||
{
|
||||
ARDUINOHA_DEBUG_PRINT(F("AHA: subscribing "))
|
||||
ARDUINOHA_DEBUG_PRINTLN(topic)
|
||||
|
||||
return _mqtt->subscribe(topic);
|
||||
}
|
||||
|
||||
void HAMqtt::processMessage(const char *topic, const uint8_t *payload, uint16_t length)
|
||||
{
|
||||
ARDUINOHA_DEBUG_PRINT(F("AHA: received call "))
|
||||
ARDUINOHA_DEBUG_PRINT(topic)
|
||||
ARDUINOHA_DEBUG_PRINT(F(", len: "))
|
||||
ARDUINOHA_DEBUG_PRINTLN(length)
|
||||
|
||||
if (_messageCallback)
|
||||
{
|
||||
_messageCallback(topic, payload, length);
|
||||
}
|
||||
|
||||
if (!noHA)
|
||||
{
|
||||
for (uint8_t i = 0; i < _devicesTypesNb; i++)
|
||||
{
|
||||
_devicesTypes[i]->onMqttMessage(topic, payload, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HAMqtt::connectToServer()
|
||||
{
|
||||
if (_lastConnectionAttemptAt > 0 &&
|
||||
(millis() - _lastConnectionAttemptAt) < ReconnectInterval)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_lastConnectionAttemptAt = millis();
|
||||
|
||||
_mqtt->connect(
|
||||
_clientID,
|
||||
_username,
|
||||
_password,
|
||||
_lastWillTopic,
|
||||
0,
|
||||
_lastWillRetain,
|
||||
_lastWillMessage,
|
||||
true);
|
||||
|
||||
if (isConnected())
|
||||
{
|
||||
ARDUINOHA_DEBUG_PRINTLN(F("AHA: connected"))
|
||||
if (_connectedCallback)
|
||||
{
|
||||
_connectedCallback();
|
||||
}
|
||||
if (!noHA)
|
||||
onConnectedLogic();
|
||||
}
|
||||
else
|
||||
{
|
||||
ARDUINOHA_DEBUG_PRINTLN(F("AHA: failed to connect"))
|
||||
}
|
||||
}
|
||||
|
||||
void HAMqtt::onConnectedLogic()
|
||||
{
|
||||
|
||||
_device.publishAvailability();
|
||||
|
||||
for (uint8_t i = 0; i < _devicesTypesNb; i++)
|
||||
{
|
||||
_devicesTypes[i]->onMqttConnected();
|
||||
}
|
||||
}
|
||||
413
lib/home-assistant-integration/src/HAMqtt.h
Normal file
413
lib/home-assistant-integration/src/HAMqtt.h
Normal file
@@ -0,0 +1,413 @@
|
||||
#ifndef AHA_HAMQTT_H
|
||||
#define AHA_HAMQTT_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Client.h>
|
||||
#include <IPAddress.h>
|
||||
#include "ArduinoHADefines.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
|
||||
|
||||
#ifdef ARDUINOHA_TEST
|
||||
class PubSubClientMock;
|
||||
#else
|
||||
class PubSubClient;
|
||||
#endif
|
||||
|
||||
class HADevice;
|
||||
class HABaseDeviceType;
|
||||
|
||||
#if defined(ARDUINO_API_VERSION)
|
||||
using namespace arduino;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This class is a wrapper for the PubSub API.
|
||||
* It's a central point of the library where instances of all device types are stored.
|
||||
*/
|
||||
class HAMqtt
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Returns existing instance (singleton) of the HAMqtt class.
|
||||
* It may be a null pointer if the HAMqtt object was never constructed or it was destroyed.
|
||||
*/
|
||||
inline static HAMqtt *instance()
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
|
||||
#ifdef ARDUINOHA_TEST
|
||||
explicit HAMqtt(
|
||||
PubSubClientMock *pubSub,
|
||||
HADevice &device,
|
||||
const uint8_t maxDevicesTypesNb = 6);
|
||||
#else
|
||||
/**
|
||||
* Creates a new instance of the HAMqtt class.
|
||||
* Please note that only one instance of the class can be initialized at the same time.
|
||||
*
|
||||
* @param netClient The EthernetClient or WiFiClient that's going to be used for the network communication.
|
||||
* @param device An instance of the HADevice class representing your device.
|
||||
* @param maxDevicesTypesNb The maximum number of device types (sensors, switches, etc.) that you're going to implement.
|
||||
*/
|
||||
explicit HAMqtt(
|
||||
Client &netClient,
|
||||
HADevice &device,
|
||||
const uint8_t maxDevicesTypesNb = 6);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Removes singleton of the HAMqtt class.
|
||||
*/
|
||||
~HAMqtt();
|
||||
|
||||
/**
|
||||
* Sets the prefix of the Home Assistant discovery topics.
|
||||
* It needs to match the prefix set in the HA admin panel.
|
||||
* The default prefix is "homeassistant".
|
||||
*
|
||||
* @param prefix The discovery topics' prefix.
|
||||
*/
|
||||
inline void setDiscoveryPrefix(const char *prefix)
|
||||
{
|
||||
_discoveryPrefix = prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the discovery topics' prefix.
|
||||
*/
|
||||
inline const char *getDiscoveryPrefix() const
|
||||
{
|
||||
return _discoveryPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets prefix of the data topics.
|
||||
* It may be useful if you want to pass MQTT traffic through a bridge.
|
||||
* The default prefix is "aha".
|
||||
*
|
||||
* @param prefix The data topics' prefix.
|
||||
*/
|
||||
inline void setDataPrefix(const char *prefix)
|
||||
{
|
||||
_dataPrefix = prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data topics' prefix.
|
||||
*/
|
||||
inline const char *getDataPrefix() const
|
||||
{
|
||||
return _dataPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns instance of the device assigned to the HAMqtt class.
|
||||
* It's the same object (pointer) that was passed to the HAMqtt constructor.
|
||||
*/
|
||||
inline HADevice const *getDevice() const
|
||||
{
|
||||
return &_device;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new callback method that will be called when the device receives an MQTT message.
|
||||
* Please note that the callback is also fired by internal MQTT messages used by the library.
|
||||
* You should always verify the topic of the received message.
|
||||
*
|
||||
* @param callback Callback method.
|
||||
*/
|
||||
inline void onMessage(HAMQTT_MESSAGE_CALLBACK(callback))
|
||||
{
|
||||
_messageCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new callback method that will be called each time a connection to the MQTT broker is acquired.
|
||||
* The callback is also fired after reconnecting to the broker.
|
||||
* You can use this method to register topics' subscriptions.
|
||||
*
|
||||
* @param callback Callback method.
|
||||
*/
|
||||
inline void onConnected(HAMQTT_CALLBACK(callback))
|
||||
{
|
||||
_connectedCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets parameters of the MQTT connection using the IP address and port.
|
||||
* The library will try to connect to the broker in first loop cycle.
|
||||
* Please note that the library automatically reconnects to the broker if connection is lost.
|
||||
*
|
||||
* @param serverIp IP address of the MQTT broker.
|
||||
* @param serverPort Port of the MQTT broker.
|
||||
* @param username Username for authentication. It can be nullptr if the anonymous connection needs to be performed.
|
||||
* @param password Password for authentication. It can be nullptr if the anonymous connection needs to be performed.
|
||||
*/
|
||||
bool begin(
|
||||
const IPAddress serverIp,
|
||||
const uint16_t serverPort = HAMQTT_DEFAULT_PORT,
|
||||
const char *username = nullptr,
|
||||
const char *password = nullptr,
|
||||
const char *clientID = nullptr);
|
||||
|
||||
/**
|
||||
* Sets parameters of the MQTT connection using the IP address and the default port (1883).
|
||||
* The library will try to connect to the broker in first loop cycle.
|
||||
* Please note that the library automatically reconnects to the broker if connection is lost.
|
||||
*
|
||||
* @param serverIp IP address of the MQTT broker.
|
||||
* @param username Username for authentication. It can be nullptr if the anonymous connection needs to be performed.
|
||||
* @param password Password for authentication. It can be nullptr if the anonymous connection needs to be performed.
|
||||
*/
|
||||
bool begin(
|
||||
const IPAddress serverIp,
|
||||
const char *username,
|
||||
const char *password,
|
||||
const char *clientID);
|
||||
|
||||
/**
|
||||
* Sets parameters of the MQTT connection using the hostname and port.
|
||||
* The library will try to connect to the broker in first loop cycle.
|
||||
* Please note that the library automatically reconnects to the broker if connection is lost.
|
||||
*
|
||||
* @param serverHostname Hostname of the MQTT broker.
|
||||
* @param serverPort Port of the MQTT broker.
|
||||
* @param username Username for authentication. It can be nullptr if the anonymous connection needs to be performed.
|
||||
* @param password Password for authentication. It can be nullptr if the anonymous connection needs to be performed.
|
||||
*/
|
||||
bool begin(
|
||||
const char *serverHostname,
|
||||
const uint16_t serverPort = HAMQTT_DEFAULT_PORT,
|
||||
const char *username = nullptr,
|
||||
const char *password = nullptr,
|
||||
const char *clientID = nullptr);
|
||||
|
||||
/**
|
||||
* Sets parameters of the MQTT connection using the hostname and the default port (1883).
|
||||
* The library will try to connect to the broker in first loop cycle.
|
||||
* Please note that the library automatically reconnects to the broker if connection is lost.
|
||||
*
|
||||
* @param serverHostname Hostname of the MQTT broker.
|
||||
* @param username Username for authentication. It can be nullptr if the anonymous connection needs to be performed.
|
||||
* @param password Password for authentication. It can be nullptr if the anonymous connection needs to be performed.
|
||||
*/
|
||||
bool begin(
|
||||
const char *serverHostname,
|
||||
const char *username,
|
||||
const char *password,
|
||||
const char *clientID);
|
||||
|
||||
/**
|
||||
* Closes the MQTT connection.
|
||||
*/
|
||||
bool disconnect();
|
||||
void disableHA();
|
||||
/**
|
||||
* This method should be called periodically inside the main loop of the firmware.
|
||||
* It's safe to call this method in some interval (like 5ms).
|
||||
*/
|
||||
void loop();
|
||||
|
||||
/**
|
||||
* Returns true if connection to the MQTT broker is established.
|
||||
*/
|
||||
bool isConnected() const;
|
||||
|
||||
/**
|
||||
* Adds a new device's type to the MQTT.
|
||||
* Each time the connection with MQTT broker is acquired, the HAMqtt class
|
||||
* calls "onMqttConnected" method in all devices' types instances.
|
||||
*
|
||||
* @note The HAMqtt class doesn't take ownership of the given pointer.
|
||||
* @param deviceType Instance of the device's type (HASwitch, HABinarySensor, etc.).
|
||||
*/
|
||||
void addDeviceType(HABaseDeviceType *deviceType);
|
||||
|
||||
/**
|
||||
* Publishes the MQTT message with given topic and payload.
|
||||
* Message won't be published if the connection with the MQTT broker is not established.
|
||||
* In this case method returns false.
|
||||
*
|
||||
* @param topic The topic to publish.
|
||||
* @param payload The payload to publish (it may be empty const char).
|
||||
* @param retained Specifies whether message should be retained.
|
||||
*/
|
||||
bool publish(const char *topic, const char *payload, bool retained = false);
|
||||
|
||||
/**
|
||||
* Begins publishing of a message with the given properties.
|
||||
* When this method returns true the payload can be written using HAMqtt::writePayload method.
|
||||
*
|
||||
* @param topic Topic of the published message.
|
||||
* @param payloadLength Length of the payload (bytes) that's going to be published.
|
||||
* @param retained Specifies whether the published message should be retained.
|
||||
*/
|
||||
bool beginPublish(const char *topic, uint16_t payloadLength, bool retained = false);
|
||||
|
||||
/**
|
||||
* Writes given string to the TCP stream.
|
||||
* Please note that before writing any data the HAMqtt::beginPublish method
|
||||
* needs to be called.
|
||||
*
|
||||
* @param data The string to publish.
|
||||
* @param length Length of the data (bytes).
|
||||
*/
|
||||
void writePayload(const char *data, const uint16_t length);
|
||||
|
||||
/**
|
||||
* Writes given data to the TCP stream.
|
||||
* Please note that before writing any data the HAMqtt::beginPublish method
|
||||
* needs to be called.
|
||||
*
|
||||
* @param data The data to publish.
|
||||
* @param length Length of the data (bytes).
|
||||
*/
|
||||
void writePayload(const uint8_t *data, const uint16_t length);
|
||||
|
||||
/**
|
||||
* Writes given progmem data to the TCP stream.
|
||||
* Please note that before writing any data the HAMqtt::beginPublish method
|
||||
* needs to be called.
|
||||
*
|
||||
* @param data Progmem data to publish.
|
||||
*/
|
||||
void writePayload(const __FlashStringHelper *data);
|
||||
|
||||
/**
|
||||
* Finishes publishing of a message.
|
||||
* After calling this method the message will be processed by the broker.
|
||||
*/
|
||||
bool endPublish();
|
||||
|
||||
/**
|
||||
* Subscribes to the given topic.
|
||||
* Whenever a new message is received the onMqttMessage callback in all
|
||||
* devices types is called.
|
||||
*
|
||||
* Please note that you need to subscribe topic each time the connection
|
||||
* with the broker is acquired.
|
||||
*
|
||||
* @param topic Topic to subscribe.
|
||||
*/
|
||||
bool subscribe(const char *topic);
|
||||
|
||||
/**
|
||||
* Enables the last will message that will be produced when the device disconnects from the broker.
|
||||
* If you want to change availability of the device in Home Assistant panel
|
||||
* please use enableLastWill() method from the HADevice class instead.
|
||||
*
|
||||
* @param lastWillTopic The topic to publish.
|
||||
* @param lastWillMessage The message (payload) to publish.
|
||||
* @param lastWillRetain Specifies whether the published message should be retained.
|
||||
*/
|
||||
inline void setLastWill(
|
||||
const char *lastWillTopic,
|
||||
const char *lastWillMessage,
|
||||
bool lastWillRetain)
|
||||
{
|
||||
_lastWillTopic = lastWillTopic;
|
||||
_lastWillMessage = lastWillMessage;
|
||||
_lastWillRetain = lastWillRetain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes MQTT message received from the broker (subscription).
|
||||
*
|
||||
* @note Do not use this method on your own. It's only for the internal purpose.
|
||||
* @param topic Topic of the message.
|
||||
* @param payload Content of the message.
|
||||
* @param length Length of the message.
|
||||
*/
|
||||
void processMessage(const char *topic, const uint8_t *payload, uint16_t length);
|
||||
|
||||
#ifdef ARDUINOHA_TEST
|
||||
inline uint8_t getDevicesTypesNb() const
|
||||
{
|
||||
return _devicesTypesNb;
|
||||
}
|
||||
|
||||
inline HABaseDeviceType **getDevicesTypes() const
|
||||
{
|
||||
return _devicesTypes;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
/// Interval between MQTT reconnects (milliseconds).
|
||||
static const uint16_t ReconnectInterval = 5000;
|
||||
|
||||
/// Living instance of the HAMqtt class. It can be nullptr.
|
||||
static HAMqtt *_instance;
|
||||
|
||||
/**
|
||||
* Attempts to connect to the MQTT broker.
|
||||
* The method uses properties passed to the "begin" method.
|
||||
*/
|
||||
void connectToServer();
|
||||
bool noHA = false;
|
||||
/**
|
||||
* This method is called each time the connection with MQTT broker is acquired.
|
||||
*/
|
||||
void onConnectedLogic();
|
||||
|
||||
#ifdef ARDUINOHA_TEST
|
||||
PubSubClientMock *_mqtt;
|
||||
#else
|
||||
/// Instance of the PubSubClient class. It's initialized in the constructor.
|
||||
PubSubClient *_mqtt;
|
||||
#endif
|
||||
|
||||
/// Instance of the HADevice passed to the constructor.
|
||||
const HADevice &_device;
|
||||
|
||||
/// The callback method that will be called when an MQTT message is received.
|
||||
HAMQTT_MESSAGE_CALLBACK(_messageCallback);
|
||||
|
||||
/// The callback method that will be called when the MQTT connection is acquired.
|
||||
HAMQTT_CALLBACK(_connectedCallback);
|
||||
|
||||
/// Specifies whether the HAMqtt::begin method was ever called.
|
||||
bool _initialized;
|
||||
|
||||
/// Teh discovery prefix that's used for the configuration messages.
|
||||
const char *_discoveryPrefix;
|
||||
|
||||
/// The data prefix that's used for publishing data messages.
|
||||
const char *_dataPrefix;
|
||||
|
||||
/// The username used for the authentication. It's set in the HAMqtt::begin method.
|
||||
const char *_username;
|
||||
|
||||
/// The username used for the authentication. It's set in the HAMqtt::begin method.
|
||||
const char *_clientID;
|
||||
|
||||
/// The password used for the authentication. It's set in the HAMqtt::begin method.
|
||||
const char *_password;
|
||||
|
||||
/// Time of the last connection attemps (milliseconds since boot).
|
||||
uint32_t _lastConnectionAttemptAt;
|
||||
|
||||
/// The amount of registered devices types.
|
||||
uint8_t _devicesTypesNb;
|
||||
|
||||
/// The maximum amount of devices types that can be registered.
|
||||
uint8_t _maxDevicesTypesNb;
|
||||
|
||||
/// Pointers of all registered devices types (array of pointers).
|
||||
HABaseDeviceType **_devicesTypes;
|
||||
|
||||
/// The last will topic set by HAMqtt::setLastWill
|
||||
const char *_lastWillTopic;
|
||||
|
||||
/// The last will message set by HAMqtt::setLastWill
|
||||
const char *_lastWillMessage;
|
||||
|
||||
/// The last will retain set by HAMqtt::setLastWill
|
||||
bool _lastWillRetain;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,205 @@
|
||||
#include "HABaseDeviceType.h"
|
||||
#include "../HAMqtt.h"
|
||||
#include "../HADevice.h"
|
||||
#include "../utils/HAUtils.h"
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
HABaseDeviceType::HABaseDeviceType(
|
||||
const __FlashStringHelper* componentName,
|
||||
const char* uniqueId
|
||||
) :
|
||||
_componentName(componentName),
|
||||
_uniqueId(uniqueId),
|
||||
_name(nullptr),
|
||||
_serializer(nullptr),
|
||||
_availability(AvailabilityDefault)
|
||||
{
|
||||
if (mqtt()) {
|
||||
mqtt()->addDeviceType(this);
|
||||
}
|
||||
}
|
||||
|
||||
void HABaseDeviceType::setAvailability(bool online)
|
||||
{
|
||||
_availability = (online ? AvailabilityOnline : AvailabilityOffline);
|
||||
publishAvailability();
|
||||
}
|
||||
|
||||
HAMqtt* HABaseDeviceType::mqtt()
|
||||
{
|
||||
return HAMqtt::instance();
|
||||
}
|
||||
|
||||
void HABaseDeviceType::subscribeTopic(
|
||||
const char* uniqueId,
|
||||
const __FlashStringHelper* topic
|
||||
)
|
||||
{
|
||||
const uint16_t topicLength = HASerializer::calculateDataTopicLength(
|
||||
uniqueId,
|
||||
topic
|
||||
);
|
||||
if (topicLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char fullTopic[topicLength];
|
||||
if (!HASerializer::generateDataTopic(
|
||||
fullTopic,
|
||||
uniqueId,
|
||||
topic
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
|
||||
HAMqtt::instance()->subscribe(fullTopic);
|
||||
}
|
||||
|
||||
void HABaseDeviceType::onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
)
|
||||
{
|
||||
(void)topic;
|
||||
(void)payload;
|
||||
(void)length;
|
||||
}
|
||||
|
||||
void HABaseDeviceType::destroySerializer()
|
||||
{
|
||||
if (_serializer) {
|
||||
delete _serializer;
|
||||
_serializer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void HABaseDeviceType::publishConfig()
|
||||
{
|
||||
buildSerializer();
|
||||
|
||||
if (_serializer == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t topicLength = HASerializer::calculateConfigTopicLength(
|
||||
componentName(),
|
||||
uniqueId()
|
||||
);
|
||||
const uint16_t dataLength = _serializer->calculateSize();
|
||||
|
||||
if (topicLength > 0 && dataLength > 0) {
|
||||
char topic[topicLength];
|
||||
HASerializer::generateConfigTopic(
|
||||
topic,
|
||||
componentName(),
|
||||
uniqueId()
|
||||
);
|
||||
|
||||
if (mqtt()->beginPublish(topic, dataLength, true)) {
|
||||
_serializer->flush();
|
||||
mqtt()->endPublish();
|
||||
}
|
||||
}
|
||||
|
||||
destroySerializer();
|
||||
}
|
||||
|
||||
void HABaseDeviceType::publishAvailability()
|
||||
{
|
||||
const HADevice* device = mqtt()->getDevice();
|
||||
if (
|
||||
!device ||
|
||||
device->isSharedAvailabilityEnabled() ||
|
||||
!isAvailabilityConfigured()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishOnDataTopic(
|
||||
AHATOFSTR(HAAvailabilityTopic),
|
||||
_availability == AvailabilityOnline
|
||||
? AHATOFSTR(HAOnline)
|
||||
: AHATOFSTR(HAOffline),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
bool HABaseDeviceType::publishOnDataTopic(
|
||||
const __FlashStringHelper* topic,
|
||||
const __FlashStringHelper* payload,
|
||||
bool retained
|
||||
)
|
||||
{
|
||||
if (!payload) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return publishOnDataTopic(
|
||||
topic,
|
||||
reinterpret_cast<const uint8_t*>(payload),
|
||||
strlen_P(AHAFROMFSTR(payload)),
|
||||
retained,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
bool HABaseDeviceType::publishOnDataTopic(
|
||||
const __FlashStringHelper* topic,
|
||||
const char* payload,
|
||||
bool retained
|
||||
)
|
||||
{
|
||||
if (!payload) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return publishOnDataTopic(
|
||||
topic,
|
||||
reinterpret_cast<const uint8_t*>(payload),
|
||||
strlen(payload),
|
||||
retained
|
||||
);
|
||||
}
|
||||
|
||||
bool HABaseDeviceType::publishOnDataTopic(
|
||||
const __FlashStringHelper* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length,
|
||||
bool retained,
|
||||
bool isProgmemData
|
||||
)
|
||||
{
|
||||
if (!payload) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t topicLength = HASerializer::calculateDataTopicLength(
|
||||
uniqueId(),
|
||||
topic
|
||||
);
|
||||
if (topicLength == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char fullTopic[topicLength];
|
||||
if (!HASerializer::generateDataTopic(
|
||||
fullTopic,
|
||||
uniqueId(),
|
||||
topic
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mqtt()->beginPublish(fullTopic, length, retained)) {
|
||||
if (isProgmemData) {
|
||||
mqtt()->writePayload(AHATOFSTR(payload));
|
||||
} else {
|
||||
mqtt()->writePayload(payload, length);
|
||||
}
|
||||
|
||||
return mqtt()->endPublish();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
#ifndef AHA_HABASEDEVICETYPE_H
|
||||
#define AHA_HABASEDEVICETYPE_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "../ArduinoHADefines.h"
|
||||
|
||||
class HAMqtt;
|
||||
class HASerializer;
|
||||
|
||||
class HABaseDeviceType
|
||||
{
|
||||
public:
|
||||
enum NumberPrecision {
|
||||
/// No digits after the decimal point.
|
||||
PrecisionP0 = 0,
|
||||
|
||||
/// One digit after the decimal point.
|
||||
PrecisionP1,
|
||||
|
||||
/// Two digits after the decimal point.
|
||||
PrecisionP2,
|
||||
|
||||
/// Three digits after the decimal point.
|
||||
PrecisionP3
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new device type instance and registers it in the HAMqtt class.
|
||||
*
|
||||
* @param componentName The name of the Home Assistant component (e.g. `binary_sensor`).
|
||||
* You can find all available component names in the Home Assistant documentation.
|
||||
* The component name needs to be stored in the flash memory.
|
||||
* @param uniqueId The unique ID of the device type. It needs to be unique in a scope of the HADevice.
|
||||
*/
|
||||
HABaseDeviceType(
|
||||
const __FlashStringHelper* componentName,
|
||||
const char* uniqueId
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns unique ID of the device type.
|
||||
*/
|
||||
inline const char* uniqueId() const
|
||||
{ return _uniqueId; }
|
||||
|
||||
/**
|
||||
* Returns component name defined by the device type.
|
||||
* It's used for the MQTT discovery topic.
|
||||
*/
|
||||
inline const __FlashStringHelper* componentName() const
|
||||
{ return _componentName; }
|
||||
|
||||
/**
|
||||
* Returns `true` if the availability was configured for this device type.
|
||||
*/
|
||||
inline bool isAvailabilityConfigured() const
|
||||
{ return (_availability != AvailabilityDefault); }
|
||||
|
||||
/**
|
||||
* Returns online state of the device type.
|
||||
*/
|
||||
inline bool isOnline() const
|
||||
{ return (_availability == AvailabilityOnline); }
|
||||
|
||||
/**
|
||||
* Sets name of the device type that will be used as a label in the HA panel.
|
||||
* Keep the name short to save the resources.
|
||||
*
|
||||
* @param name The device type name.
|
||||
*/
|
||||
inline void setName(const char* name)
|
||||
{ _name = name; }
|
||||
|
||||
/**
|
||||
* Returns name of the deviced type that was assigned via setName method.
|
||||
* It can be nullptr if there is no name assigned.
|
||||
*/
|
||||
inline const char* getName() const
|
||||
{ return _name; }
|
||||
|
||||
/**
|
||||
* Sets availability of the device type.
|
||||
* Setting the initial availability enables availability reporting for this device type.
|
||||
* Please note that not all device types support this feature.
|
||||
* Follow HA documentation of a specific device type to get more information.
|
||||
*
|
||||
* @param online Specifies whether the device type is online.
|
||||
*/
|
||||
virtual void setAvailability(bool online);
|
||||
|
||||
#ifdef ARDUINOHA_TEST
|
||||
inline HASerializer* getSerializer() const
|
||||
{ return _serializer; }
|
||||
|
||||
inline void buildSerializerTest()
|
||||
{ buildSerializer(); }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Returns instance of the HAMqtt class.
|
||||
*/
|
||||
static HAMqtt* mqtt();
|
||||
|
||||
/**
|
||||
* Subscribes to the given data topic.
|
||||
*
|
||||
* @param uniqueId THe unique ID of the device type assigned via the constructor.
|
||||
* @param topic Topic to subscribe (progmem string).
|
||||
*/
|
||||
static void subscribeTopic(
|
||||
const char* uniqueId,
|
||||
const __FlashStringHelper* topic
|
||||
);
|
||||
|
||||
/**
|
||||
* This method should build serializer that will be used for publishing the configuration.
|
||||
* The serializer is built each time the MQTT connection is acquired.
|
||||
* Follow implementation of the existing device types to get better understanding of the logic.
|
||||
*/
|
||||
virtual void buildSerializer() { };
|
||||
|
||||
/**
|
||||
* This method is called each time the MQTT connection is acquired.
|
||||
* Each device type should publish its configuration and availability.
|
||||
* It can be also used for subscribing to MQTT topics.
|
||||
*/
|
||||
virtual void onMqttConnected() = 0;
|
||||
|
||||
/**
|
||||
* This method is called each time the device receives a MQTT message.
|
||||
* It can be any MQTT message so the method should always verify the topic.
|
||||
*
|
||||
* @param topic The topic on which the message was produced.
|
||||
* @param payload The payload of the message. It can be nullptr.
|
||||
* @param length The length of the payload.
|
||||
*/
|
||||
virtual void onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
);
|
||||
|
||||
/**
|
||||
* Destroys the existing serializer.
|
||||
*/
|
||||
void destroySerializer();
|
||||
|
||||
/**
|
||||
* Publishes configuration of this device type on the HA discovery topic.
|
||||
*/
|
||||
void publishConfig();
|
||||
|
||||
/**
|
||||
* Publishes current availability of the device type.
|
||||
* The message is only produced if the availability is configured for this device type.
|
||||
*/
|
||||
void publishAvailability();
|
||||
|
||||
/**
|
||||
* Publishes the given flash string on the data topic.
|
||||
*
|
||||
* @param topic The topic to publish on (progmem string).
|
||||
* @param payload The message's payload (progmem string).
|
||||
* @param retained Specifies whether the message should be retained.
|
||||
*/
|
||||
bool publishOnDataTopic(
|
||||
const __FlashStringHelper* topic,
|
||||
const __FlashStringHelper* payload,
|
||||
bool retained = false
|
||||
);
|
||||
|
||||
/**
|
||||
* Publishes the given string on the data topic.
|
||||
*
|
||||
* @param topic The topic to publish on (progmem string).
|
||||
* @param payload The message's payload.
|
||||
* @param retained Specifies whether the message should be retained.
|
||||
*/
|
||||
bool publishOnDataTopic(
|
||||
const __FlashStringHelper* topic,
|
||||
const char* payload,
|
||||
bool retained = false
|
||||
);
|
||||
|
||||
/**
|
||||
* Publishes the given data on the data topic.
|
||||
*
|
||||
* @param topic The topic to publish on (progmem string).
|
||||
* @param payload The message's payload.
|
||||
* @param length The length of the payload.
|
||||
* @param retained Specifies whether the message should be retained.
|
||||
* @param isProgmemData Specifies whether the given data is stored in the flash memory.
|
||||
*/
|
||||
bool publishOnDataTopic(
|
||||
const __FlashStringHelper* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length,
|
||||
bool retained = false,
|
||||
bool isProgmemData = false
|
||||
);
|
||||
|
||||
/// The component name that was assigned via the constructor.
|
||||
const __FlashStringHelper* const _componentName;
|
||||
|
||||
/// The unique ID that was assigned via the constructor.
|
||||
const char* _uniqueId;
|
||||
|
||||
/// The name that was set using setName method. It can be nullptr.
|
||||
const char* _name;
|
||||
|
||||
/// HASerializer that belongs to this device type. It can be nullptr.
|
||||
HASerializer* _serializer;
|
||||
|
||||
private:
|
||||
enum Availability {
|
||||
AvailabilityDefault = 0,
|
||||
AvailabilityOnline,
|
||||
AvailabilityOffline
|
||||
};
|
||||
|
||||
/// The current availability of this device type. AvailabilityDefault means that the initial availability was never set.
|
||||
Availability _availability;
|
||||
friend class HAMqtt;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,66 @@
|
||||
#include "HABinarySensor.h"
|
||||
#ifndef EX_ARDUINOHA_BINARY_SENSOR
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
HABinarySensor::HABinarySensor(const char* uniqueId) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentBinarySensor), uniqueId),
|
||||
_class(nullptr),
|
||||
_icon(nullptr),
|
||||
_currentState(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool HABinarySensor::setState(const bool state, const bool force)
|
||||
{
|
||||
if (!force && state == _currentState) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishState(state)) {
|
||||
_currentState = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HABinarySensor::buildSerializer()
|
||||
{
|
||||
if (_serializer || !uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 7); // 7 - max properties nb
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAUniqueIdProperty), _uniqueId);
|
||||
_serializer->set(AHATOFSTR(HADeviceClassProperty), _class);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
_serializer->set(HASerializer::WithDevice);
|
||||
_serializer->set(HASerializer::WithAvailability);
|
||||
_serializer->topic(AHATOFSTR(HAStateTopic));
|
||||
}
|
||||
|
||||
void HABinarySensor::onMqttConnected()
|
||||
{
|
||||
if (!uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishAvailability();
|
||||
publishState(_currentState);
|
||||
}
|
||||
|
||||
bool HABinarySensor::publishState(const bool state)
|
||||
{
|
||||
return publishOnDataTopic(
|
||||
AHATOFSTR(HAStateTopic),
|
||||
AHATOFSTR(state ? HAStateOn : HAStateOff),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,90 @@
|
||||
#ifndef AHA_HABINARYSENSOR_H
|
||||
#define AHA_HABINARYSENSOR_H
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
|
||||
#ifndef EX_ARDUINOHA_BINARY_SENSOR
|
||||
|
||||
/**
|
||||
* HABinarySensor represents a binary sensor that allows publishing on/off state to the Home Assistant panel.
|
||||
*
|
||||
* @note
|
||||
* You can find more information about this entity in the Home Assistant documentation:
|
||||
* https://www.home-assistant.io/integrations/binary_sensor.mqtt/
|
||||
*/
|
||||
class HABinarySensor : public HABaseDeviceType
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param uniqueId The unique ID of the button. It needs to be unique in a scope of your device.
|
||||
*/
|
||||
HABinarySensor(const char* uniqueId);
|
||||
|
||||
/**
|
||||
* Changes state of the sensor and publish MQTT message.
|
||||
* Please note that if a new value is the same as the previous one the MQTT message won't be published.
|
||||
*
|
||||
* @param state New state of the sensor (`true` - on, `false` - off).
|
||||
* @param force Forces to update the state without comparing it to a previous known state.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool setState(const bool state, const bool force = false);
|
||||
|
||||
/**
|
||||
* Sets the current state of the sensor without publishing it to Home Assistant.
|
||||
* This method may be useful if you want to change the state before the connection with the MQTT broker is acquired.
|
||||
*
|
||||
* @param state New state of the sensor.
|
||||
*/
|
||||
inline void setCurrentState(const bool state)
|
||||
{ _currentState = state; }
|
||||
|
||||
/**
|
||||
* Returns the last known state of the sensor.
|
||||
*/
|
||||
inline bool getCurrentState() const
|
||||
{ return _currentState; }
|
||||
|
||||
/**
|
||||
* Sets class of the device.
|
||||
* You can find list of available values here: https://www.home-assistant.io/integrations/binary_sensor/#device-class
|
||||
*
|
||||
* @param deviceClass The class name.
|
||||
*/
|
||||
inline void setDeviceClass(const char* deviceClass)
|
||||
{ _class = deviceClass; }
|
||||
|
||||
/**
|
||||
* Sets icon of the sensor.
|
||||
* Any icon from MaterialDesignIcons.com (for example: `mdi:home`).
|
||||
*
|
||||
* @param icon The icon name.
|
||||
*/
|
||||
inline void setIcon(const char* icon)
|
||||
{ _icon = icon; }
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
virtual void onMqttConnected() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Publishes the MQTT message with the given state.
|
||||
*
|
||||
* @param state The state to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishState(bool state);
|
||||
|
||||
/// The device class. It can be nullptr.
|
||||
const char* _class;
|
||||
|
||||
/// The icon of the sensor. It can be nullptr.
|
||||
const char* _icon;
|
||||
|
||||
/// Current state of the sensor. By default it's false.
|
||||
bool _currentState;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
72
lib/home-assistant-integration/src/device-types/HAButton.cpp
Normal file
72
lib/home-assistant-integration/src/device-types/HAButton.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "HAButton.h"
|
||||
#ifndef EX_ARDUINOHA_BUTTON
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
HAButton::HAButton(const char* uniqueId) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentButton), uniqueId),
|
||||
_class(nullptr),
|
||||
_icon(nullptr),
|
||||
_retain(false),
|
||||
_commandCallback(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void HAButton::buildSerializer()
|
||||
{
|
||||
if (_serializer || !uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 8); // 8 - max properties nb
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAUniqueIdProperty), _uniqueId);
|
||||
_serializer->set(AHATOFSTR(HADeviceClassProperty), _class);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
|
||||
// optional property
|
||||
if (_retain) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HARetainProperty),
|
||||
&_retain,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
_serializer->set(HASerializer::WithDevice);
|
||||
_serializer->set(HASerializer::WithAvailability);
|
||||
_serializer->topic(AHATOFSTR(HACommandTopic));
|
||||
}
|
||||
|
||||
void HAButton::onMqttConnected()
|
||||
{
|
||||
if (!uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishAvailability();
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HACommandTopic));
|
||||
}
|
||||
|
||||
void HAButton::onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
)
|
||||
{
|
||||
(void)payload;
|
||||
(void)length;
|
||||
|
||||
if (_commandCallback && HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HACommandTopic)
|
||||
)) {
|
||||
_commandCallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
86
lib/home-assistant-integration/src/device-types/HAButton.h
Normal file
86
lib/home-assistant-integration/src/device-types/HAButton.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef AHA_HABUTTON_H
|
||||
#define AHA_HABUTTON_H
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
|
||||
#ifndef EX_ARDUINOHA_BUTTON
|
||||
|
||||
#define HABUTTON_CALLBACK(name) void (*name)(HAButton* sender)
|
||||
|
||||
/**
|
||||
* HAButton represents a button that's displayed in the Home Assistant panel and
|
||||
* triggers some logic on your Arduino/ESP device once clicked.
|
||||
*
|
||||
* @note
|
||||
* You can find more information about this entity in the Home Assistant documentation:
|
||||
* https://www.home-assistant.io/integrations/button.mqtt/
|
||||
*/
|
||||
class HAButton : public HABaseDeviceType
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param uniqueId The unique ID of the button. It needs to be unique in a scope of your device.
|
||||
*/
|
||||
HAButton(const char* uniqueId);
|
||||
|
||||
/**
|
||||
* Sets class of the device.
|
||||
* You can find list of available values here: https://www.home-assistant.io/integrations/button/#device-class
|
||||
*
|
||||
* @param deviceClass The class name.
|
||||
*/
|
||||
inline void setDeviceClass(const char* deviceClass)
|
||||
{ _class = deviceClass; }
|
||||
|
||||
/**
|
||||
* Sets icon of the button.
|
||||
* Any icon from MaterialDesignIcons.com (for example: `mdi:home`).
|
||||
*
|
||||
* @param icon The icon name.
|
||||
*/
|
||||
inline void setIcon(const char* icon)
|
||||
{ _icon = icon; }
|
||||
|
||||
/**
|
||||
* Sets retain flag for the button's command.
|
||||
* If set to `true` the command produced by Home Assistant will be retained.
|
||||
*
|
||||
* @param retain
|
||||
*/
|
||||
inline void setRetain(const bool retain)
|
||||
{ _retain = retain; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the press command from HA is received.
|
||||
* Please note that it's not possible to register multiple callbacks for the same button.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onCommand(HABUTTON_CALLBACK(callback))
|
||||
{ _commandCallback = callback; }
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
virtual void onMqttConnected() override;
|
||||
virtual void onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
) override;
|
||||
|
||||
private:
|
||||
/// The device class. It can be nullptr.
|
||||
const char* _class;
|
||||
|
||||
/// The icon of the button. It can be nullptr.
|
||||
const char* _icon;
|
||||
|
||||
/// The retain flag for the HA commands.
|
||||
bool _retain;
|
||||
|
||||
/// The command callback that will be called once clicking the button in HA panel.
|
||||
HABUTTON_CALLBACK(_commandCallback);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
65
lib/home-assistant-integration/src/device-types/HACamera.cpp
Normal file
65
lib/home-assistant-integration/src/device-types/HACamera.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "HACamera.h"
|
||||
#ifndef EX_ARDUINOHA_CAMERA
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
HACamera::HACamera(const char* uniqueId) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentCamera), uniqueId),
|
||||
_encoding(EncodingBinary),
|
||||
_icon(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool HACamera::publishImage(const uint8_t* data, const uint16_t length)
|
||||
{
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return publishOnDataTopic(AHATOFSTR(HATopic), data, length, true);
|
||||
}
|
||||
|
||||
void HACamera::buildSerializer()
|
||||
{
|
||||
if (_serializer || !uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 7); // 7 - max properties nb
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAUniqueIdProperty), _uniqueId);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAEncodingProperty),
|
||||
getEncodingProperty(),
|
||||
HASerializer::ProgmemPropertyValue
|
||||
);
|
||||
_serializer->set(HASerializer::WithDevice);
|
||||
_serializer->set(HASerializer::WithAvailability);
|
||||
_serializer->topic(AHATOFSTR(HATopic));
|
||||
}
|
||||
|
||||
void HACamera::onMqttConnected()
|
||||
{
|
||||
if (!uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishAvailability();
|
||||
}
|
||||
|
||||
const __FlashStringHelper* HACamera::getEncodingProperty() const
|
||||
{
|
||||
switch (_encoding) {
|
||||
case EncodingBase64:
|
||||
return AHATOFSTR(HAEncodingBase64);
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
77
lib/home-assistant-integration/src/device-types/HACamera.h
Normal file
77
lib/home-assistant-integration/src/device-types/HACamera.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#ifndef AHA_HACAMERA_H
|
||||
#define AHA_HACAMERA_H
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
|
||||
#ifndef EX_ARDUINOHA_CAMERA
|
||||
|
||||
/**
|
||||
* HACamera allows to display an image in the Home Assistant panel.
|
||||
* It can be used for publishing an image from the ESP32-Cam module or any other
|
||||
* module that's equipped with a camera.
|
||||
*
|
||||
* @note
|
||||
* You can find more information about this entity in the Home Assistant documentation:
|
||||
* https://www.home-assistant.io/integrations/camera.mqtt/
|
||||
*/
|
||||
class HACamera : public HABaseDeviceType
|
||||
{
|
||||
public:
|
||||
enum ImageEncoding {
|
||||
EncodingBinary = 1,
|
||||
EncodingBase64
|
||||
};
|
||||
|
||||
/**
|
||||
* @param uniqueId The unique ID of the camera. It needs to be unique in a scope of your device.
|
||||
*/
|
||||
HACamera(const char* uniqueId);
|
||||
|
||||
/**
|
||||
* Publishes MQTT message with the given image data as a message content.
|
||||
* It updates image displayed in the Home Assistant panel.
|
||||
*
|
||||
* @param data Image data (raw binary data or base64)
|
||||
* @param length The length of the data.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishImage(const uint8_t* data, const uint16_t length);
|
||||
|
||||
/**
|
||||
* Sets encoding of the image content.
|
||||
* Bu default Home Assistant expects raw binary data (e.g. JPEG binary data).
|
||||
*
|
||||
* @param encoding The image's data encoding.
|
||||
*/
|
||||
inline void setEncoding(const ImageEncoding encoding)
|
||||
{ _encoding = encoding; }
|
||||
|
||||
/**
|
||||
* Sets icon of the camera.
|
||||
* Any icon from MaterialDesignIcons.com (for example: `mdi:home`).
|
||||
*
|
||||
* @param icon The icon name.
|
||||
*/
|
||||
inline void setIcon(const char* icon)
|
||||
{ _icon = icon; }
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
virtual void onMqttConnected() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Returns progmem string representing the encoding property.
|
||||
*/
|
||||
const __FlashStringHelper* getEncodingProperty() const;
|
||||
|
||||
/// The encoding of the image's data. By default it's `HACamera::EncodingBinary`.
|
||||
ImageEncoding _encoding;
|
||||
|
||||
/// The icon of the camera. It can be nullptr.
|
||||
const char* _icon;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
183
lib/home-assistant-integration/src/device-types/HACover.cpp
Normal file
183
lib/home-assistant-integration/src/device-types/HACover.cpp
Normal file
@@ -0,0 +1,183 @@
|
||||
#include "HACover.h"
|
||||
#ifndef EX_ARDUINOHA_COVER
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../utils/HAUtils.h"
|
||||
#include "../utils/HANumeric.h"
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
HACover::HACover(const char* uniqueId, const Features features) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentCover), uniqueId),
|
||||
_features(features),
|
||||
_currentState(StateUnknown),
|
||||
_currentPosition(DefaultPosition),
|
||||
_class(nullptr),
|
||||
_icon(nullptr),
|
||||
_retain(false),
|
||||
_optimistic(false),
|
||||
_commandCallback(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool HACover::setState(const CoverState state, const bool force)
|
||||
{
|
||||
if (!force && _currentState == state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishState(state)) {
|
||||
_currentState = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HACover::setPosition(const int16_t position, const bool force)
|
||||
{
|
||||
if (!force && _currentPosition == position) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishPosition(position)) {
|
||||
_currentPosition = position;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HACover::buildSerializer()
|
||||
{
|
||||
if (_serializer || !uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 11); // 11 - max properties nb
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAUniqueIdProperty), _uniqueId);
|
||||
_serializer->set(AHATOFSTR(HADeviceClassProperty), _class);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
|
||||
if (_retain) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HARetainProperty),
|
||||
&_retain,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_optimistic) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAOptimisticProperty),
|
||||
&_optimistic,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
_serializer->set(HASerializer::WithDevice);
|
||||
_serializer->set(HASerializer::WithAvailability);
|
||||
_serializer->topic(AHATOFSTR(HAStateTopic));
|
||||
_serializer->topic(AHATOFSTR(HACommandTopic));
|
||||
|
||||
if (_features & PositionFeature) {
|
||||
_serializer->topic(AHATOFSTR(HAPositionTopic));
|
||||
}
|
||||
}
|
||||
|
||||
void HACover::onMqttConnected()
|
||||
{
|
||||
if (!uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishAvailability();
|
||||
|
||||
if (!_retain) {
|
||||
publishState(_currentState);
|
||||
publishPosition(_currentPosition);
|
||||
}
|
||||
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HACommandTopic));
|
||||
}
|
||||
|
||||
void HACover::onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
)
|
||||
{
|
||||
if (HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HACommandTopic)
|
||||
)) {
|
||||
handleCommand(payload, length);
|
||||
}
|
||||
}
|
||||
|
||||
bool HACover::publishState(CoverState state)
|
||||
{
|
||||
if (state == StateUnknown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const __FlashStringHelper *stateStr = nullptr;
|
||||
switch (state) {
|
||||
case StateClosed:
|
||||
stateStr = AHATOFSTR(HAClosedState);
|
||||
break;
|
||||
|
||||
case StateClosing:
|
||||
stateStr = AHATOFSTR(HAClosingState);
|
||||
break;
|
||||
|
||||
case StateOpen:
|
||||
stateStr = AHATOFSTR(HAOpenState);
|
||||
break;
|
||||
|
||||
case StateOpening:
|
||||
stateStr = AHATOFSTR(HAOpeningState);
|
||||
break;
|
||||
|
||||
case StateStopped:
|
||||
stateStr = AHATOFSTR(HAStoppedState);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return publishOnDataTopic(AHATOFSTR(HAStateTopic), stateStr, true);
|
||||
}
|
||||
|
||||
bool HACover::publishPosition(int16_t position)
|
||||
{
|
||||
if (position == DefaultPosition || !(_features & PositionFeature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char str[6 + 1] = {0}; // int16_t digits with null terminator
|
||||
HANumeric(position, 0).toStr(str);
|
||||
|
||||
return publishOnDataTopic(AHATOFSTR(HAPositionTopic), str, true);
|
||||
}
|
||||
|
||||
void HACover::handleCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_commandCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp_P(cmd, HACloseCommand, length) == 0) {
|
||||
_commandCallback(CommandClose, this);
|
||||
} else if (memcmp_P(cmd, HAOpenCommand, length) == 0) {
|
||||
_commandCallback(CommandOpen, this);
|
||||
} else if (memcmp_P(cmd, HAStopCommand, length) == 0) {
|
||||
_commandCallback(CommandStop, this);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
210
lib/home-assistant-integration/src/device-types/HACover.h
Normal file
210
lib/home-assistant-integration/src/device-types/HACover.h
Normal file
@@ -0,0 +1,210 @@
|
||||
#ifndef AHA_HACOVER_H
|
||||
#define AHA_HACOVER_H
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
|
||||
#ifndef EX_ARDUINOHA_COVER
|
||||
|
||||
#define HACOVER_CALLBACK(name) void (*name)(CoverCommand cmd, HACover* sender)
|
||||
|
||||
/**
|
||||
* HACover allows to control a cover (such as blinds, a roller shutter or a garage door).
|
||||
*
|
||||
* @note
|
||||
* You can find more information about this entity in the Home Assistant documentation:
|
||||
* https://www.home-assistant.io/integrations/cover.mqtt/
|
||||
*/
|
||||
class HACover : public HABaseDeviceType
|
||||
{
|
||||
public:
|
||||
static const int16_t DefaultPosition = -32768;
|
||||
|
||||
enum CoverState {
|
||||
StateUnknown = 0,
|
||||
StateClosed,
|
||||
StateClosing,
|
||||
StateOpen,
|
||||
StateOpening,
|
||||
StateStopped
|
||||
};
|
||||
|
||||
enum CoverCommand {
|
||||
CommandOpen,
|
||||
CommandClose,
|
||||
CommandStop
|
||||
};
|
||||
|
||||
enum Features {
|
||||
DefaultFeatures = 0,
|
||||
PositionFeature = 1
|
||||
};
|
||||
|
||||
/**
|
||||
* @param uniqueId The unique ID of the cover. It needs to be unique in a scope of your device.
|
||||
* @param features Features that should be enabled for the fan.
|
||||
*/
|
||||
HACover(const char* uniqueId, const Features features = DefaultFeatures);
|
||||
|
||||
/**
|
||||
* 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(const CoverState state, const bool force = false);
|
||||
|
||||
/**
|
||||
* Changes the 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 The new position of the cover (0-100).
|
||||
* @param force Forces to update the state without comparing it to a previous known state.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setPosition(const int16_t position, const bool force = false);
|
||||
|
||||
/**
|
||||
* Sets the current state of the cover without publishing it to Home Assistant.
|
||||
* This method may be useful if you want to change the state before the connection
|
||||
* with the MQTT broker is acquired.
|
||||
*
|
||||
* @param state The new state of the cover.
|
||||
*/
|
||||
inline void setCurrentState(const CoverState state)
|
||||
{ _currentState = state; }
|
||||
|
||||
/**
|
||||
* Returns last known state of the cover.
|
||||
* By default the state is set to CoverState::StateUnknown
|
||||
*/
|
||||
inline CoverState getCurrentState() const
|
||||
{ return _currentState; }
|
||||
|
||||
/**
|
||||
* Sets the current position of the cover without pushing the value to Home Assistant.
|
||||
* This method may be useful if you want to change the position before the connection
|
||||
* with the MQTT broker is acquired.
|
||||
*
|
||||
* @param position The new position of the cover (0-100).
|
||||
*/
|
||||
inline void setCurrentPosition(const int16_t position)
|
||||
{ _currentPosition = position; }
|
||||
|
||||
/**
|
||||
* Returns the last known position of the cover.
|
||||
* By default position is set to HACover::DefaultPosition
|
||||
*/
|
||||
inline int16_t getCurrentPosition() const
|
||||
{ return _currentPosition; }
|
||||
|
||||
/**
|
||||
* Sets class of the device.
|
||||
* You can find list of available values here: https://www.home-assistant.io/integrations/cover/
|
||||
*
|
||||
* @param deviceClass The class name.
|
||||
*/
|
||||
inline void setDeviceClass(const char* deviceClass)
|
||||
{ _class = deviceClass; }
|
||||
|
||||
/**
|
||||
* Sets icon of the cover.
|
||||
* Any icon from MaterialDesignIcons.com (for example: `mdi:home`).
|
||||
*
|
||||
* @param icon The icon name.
|
||||
*/
|
||||
inline void setIcon(const char* icon)
|
||||
{ _icon = icon; }
|
||||
|
||||
/**
|
||||
* Sets retain flag for the cover's command.
|
||||
* If set to `true` the command produced by Home Assistant will be retained.
|
||||
*
|
||||
* @param retain
|
||||
*/
|
||||
inline void setRetain(const bool retain)
|
||||
{ _retain = retain; }
|
||||
|
||||
/**
|
||||
* Sets optimistic flag for the cover state.
|
||||
* In this mode the cover state doesn't need to be reported back to the HA panel when a command is received.
|
||||
* By default the optimistic mode is disabled.
|
||||
*
|
||||
* @param optimistic The optimistic mode (`true` - enabled, `false` - disabled).
|
||||
*/
|
||||
inline void setOptimistic(const bool optimistic)
|
||||
{ _optimistic = optimistic; }
|
||||
|
||||
/**
|
||||
* 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 cover.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onCommand(HACOVER_CALLBACK(callback))
|
||||
{ _commandCallback = callback; }
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
virtual void onMqttConnected() override;
|
||||
virtual void onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Publishes the MQTT message with the given state.
|
||||
*
|
||||
* @param state The state to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishState(const CoverState state);
|
||||
|
||||
/**
|
||||
* Publishes the MQTT message with the given position.
|
||||
*
|
||||
* @param position The position to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishPosition(const int16_t position);
|
||||
|
||||
/**
|
||||
* Parses the given command and executes the cover's callback with proper enum's property.
|
||||
*
|
||||
* @param cmd The data of the command.
|
||||
* @param length Length of the command.
|
||||
*/
|
||||
void handleCommand(const uint8_t* cmd, const uint16_t length);
|
||||
|
||||
/// Features enabled for the cover.
|
||||
const uint8_t _features;
|
||||
|
||||
/// The current state of the cover. By default it's `HACover::StateUnknown`.
|
||||
CoverState _currentState;
|
||||
|
||||
/// The current position of the cover. By default it's `HACover::DefaultPosition`.
|
||||
int16_t _currentPosition;
|
||||
|
||||
/// The device class. It can be nullptr.
|
||||
const char* _class;
|
||||
|
||||
/// The icon of the button. It can be nullptr.
|
||||
const char* _icon;
|
||||
|
||||
/// The retain flag for the HA commands.
|
||||
bool _retain;
|
||||
|
||||
/// The optimistic mode of the cover (`true` - enabled, `false` - disabled).
|
||||
bool _optimistic;
|
||||
|
||||
/// The command callback that will be called when clicking the cover's button in the HA panel.
|
||||
HACOVER_CALLBACK(_commandCallback);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,104 @@
|
||||
#include "HADeviceTracker.h"
|
||||
#ifndef EX_ARDUINOHA_DEVICE_TRACKER
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
HADeviceTracker::HADeviceTracker(const char* uniqueId) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentDeviceTracker), uniqueId),
|
||||
_icon(nullptr),
|
||||
_sourceType(SourceTypeUnknown),
|
||||
_currentState(StateUnknown)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool HADeviceTracker::setState(const TrackerState state, const bool force)
|
||||
{
|
||||
if (!force && state == _currentState) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishState(state)) {
|
||||
_currentState = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HADeviceTracker::buildSerializer()
|
||||
{
|
||||
if (_serializer || !uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 7); // 7 - max properties nb
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAUniqueIdProperty), _uniqueId);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
_serializer->set(
|
||||
AHATOFSTR(HASourceTypeProperty),
|
||||
getSourceTypeProperty(),
|
||||
HASerializer::ProgmemPropertyValue
|
||||
);
|
||||
_serializer->set(HASerializer::WithDevice);
|
||||
_serializer->set(HASerializer::WithAvailability);
|
||||
_serializer->topic(AHATOFSTR(HAStateTopic));
|
||||
}
|
||||
|
||||
void HADeviceTracker::onMqttConnected()
|
||||
{
|
||||
if (!uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishAvailability();
|
||||
publishState(_currentState);
|
||||
}
|
||||
|
||||
bool HADeviceTracker::publishState(const TrackerState state)
|
||||
{
|
||||
const __FlashStringHelper *stateStr = nullptr;
|
||||
switch (state) {
|
||||
case StateHome:
|
||||
stateStr = AHATOFSTR(HAHome);
|
||||
break;
|
||||
|
||||
case StateNotHome:
|
||||
stateStr = AHATOFSTR(HANotHome);
|
||||
break;
|
||||
|
||||
case StateNotAvailable:
|
||||
stateStr = AHATOFSTR(HAOffline);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return publishOnDataTopic(AHATOFSTR(HAStateTopic), stateStr, true);
|
||||
}
|
||||
|
||||
const __FlashStringHelper* HADeviceTracker::getSourceTypeProperty() const
|
||||
{
|
||||
switch (_sourceType) {
|
||||
case SourceTypeGPS:
|
||||
return AHATOFSTR(HAGPSType);
|
||||
|
||||
case SourceTypeRouter:
|
||||
return AHATOFSTR(HARouterType);
|
||||
|
||||
case SourceTypeBluetooth:
|
||||
return AHATOFSTR(HABluetoothType);
|
||||
|
||||
case SourceTypeBluetoothLE:
|
||||
return AHATOFSTR(HABluetoothLEType);
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,114 @@
|
||||
#ifndef AHA_HADEVICETRACKER_H
|
||||
#define AHA_HADEVICETRACKER_H
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
|
||||
#ifndef EX_ARDUINOHA_DEVICE_TRACKER
|
||||
|
||||
/**
|
||||
* HADeviceTracker allows to implement a custom device's tracker.
|
||||
*
|
||||
* @note
|
||||
* You can find more information about this entity in the Home Assistant documentation:
|
||||
* https://www.home-assistant.io/integrations/device_tracker.mqtt/
|
||||
*/
|
||||
class HADeviceTracker : public HABaseDeviceType
|
||||
{
|
||||
public:
|
||||
/// Available source types of the tracker.
|
||||
enum SourceType {
|
||||
SourceTypeUnknown = 0,
|
||||
SourceTypeGPS,
|
||||
SourceTypeRouter,
|
||||
SourceTypeBluetooth,
|
||||
SourceTypeBluetoothLE
|
||||
};
|
||||
|
||||
/// Available states that can be reported to the HA panel.
|
||||
enum TrackerState {
|
||||
StateUnknown = 0,
|
||||
StateHome,
|
||||
StateNotHome,
|
||||
StateNotAvailable
|
||||
};
|
||||
|
||||
/**
|
||||
* @param uniqueId The unique ID of the tracker. It needs to be unique in a scope of your device.
|
||||
*/
|
||||
HADeviceTracker(const char* uniqueId);
|
||||
|
||||
/**
|
||||
* Changes the state of the tracker 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 The new state of the tracker.
|
||||
* @param force Forces to update the state without comparing it to a previous known state.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setState(const TrackerState state, const bool force = false);
|
||||
|
||||
/**
|
||||
* Sets the current state of the tracker without publishing it to Home Assistant.
|
||||
* This method may be useful if you want to change the state before connection
|
||||
* with MQTT broker is acquired.
|
||||
*
|
||||
* @param state The new state of the tracker.
|
||||
*/
|
||||
inline void setCurrentState(const TrackerState state)
|
||||
{ _currentState = state; }
|
||||
|
||||
/**
|
||||
* Returns the last known state of the tracker.
|
||||
* If setState method wasn't called the initial value will be returned.
|
||||
*/
|
||||
inline TrackerState getState() const
|
||||
{ return _currentState; }
|
||||
|
||||
/**
|
||||
* Sets icon of the tracker.
|
||||
* Any icon from MaterialDesignIcons.com (for example: `mdi:home`).
|
||||
*
|
||||
* @param icon The icon name.
|
||||
*/
|
||||
inline void setIcon(const char* icon)
|
||||
{ _icon = icon; }
|
||||
|
||||
/**
|
||||
* Sets the source type of the tracker.
|
||||
*
|
||||
* @param type The source type (gps, router, bluetooth, bluetooth LE).
|
||||
*/
|
||||
inline void setSourceType(const SourceType type)
|
||||
{ _sourceType = type; }
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
virtual void onMqttConnected() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Publishes the MQTT message with the given state.
|
||||
*
|
||||
* @param state The state to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishState(TrackerState state);
|
||||
|
||||
/**
|
||||
* Returns progmem string representing source type of the tracker.
|
||||
*/
|
||||
const __FlashStringHelper* getSourceTypeProperty() const;
|
||||
|
||||
/// The icon of the tracker. It can be nullptr.
|
||||
const char* _icon;
|
||||
|
||||
/// The source type of the tracker. By default it's `HADeviceTracker::SourceTypeUnknown`.
|
||||
SourceType _sourceType;
|
||||
|
||||
/// The current state of the device's tracker. By default its `HADeviceTracker::StateUnknown`.
|
||||
TrackerState _currentState;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,209 @@
|
||||
#include "HADeviceTrigger.h"
|
||||
#ifndef EX_ARDUINOHA_DEVICE_TRIGGER
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
HADeviceTrigger::HADeviceTrigger(const char* type, const char* subtype) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentDeviceAutomation), nullptr),
|
||||
_type(type),
|
||||
_subtype(subtype),
|
||||
_isProgmemType(false),
|
||||
_isProgmemSubtype(false)
|
||||
{
|
||||
buildUniqueId();
|
||||
}
|
||||
|
||||
HADeviceTrigger::HADeviceTrigger(TriggerType type, const char* subtype) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentDeviceAutomation), nullptr),
|
||||
_type(determineProgmemType(type)),
|
||||
_subtype(subtype),
|
||||
_isProgmemType(true),
|
||||
_isProgmemSubtype(false)
|
||||
{
|
||||
buildUniqueId();
|
||||
}
|
||||
|
||||
HADeviceTrigger::HADeviceTrigger(const char* type, TriggerSubtype subtype) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentDeviceAutomation), nullptr),
|
||||
_type(type),
|
||||
_subtype(determineProgmemSubtype(subtype)),
|
||||
_isProgmemType(false),
|
||||
_isProgmemSubtype(true)
|
||||
{
|
||||
buildUniqueId();
|
||||
}
|
||||
|
||||
HADeviceTrigger::HADeviceTrigger(TriggerType type, TriggerSubtype subtype) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentDeviceAutomation), nullptr),
|
||||
_type(determineProgmemType(type)),
|
||||
_subtype(determineProgmemSubtype(subtype)),
|
||||
_isProgmemType(true),
|
||||
_isProgmemSubtype(true)
|
||||
{
|
||||
buildUniqueId();
|
||||
}
|
||||
|
||||
HADeviceTrigger::~HADeviceTrigger()
|
||||
{
|
||||
if (_uniqueId) {
|
||||
delete _uniqueId;
|
||||
}
|
||||
}
|
||||
|
||||
bool HADeviceTrigger::trigger()
|
||||
{
|
||||
if (!_type || !_subtype) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return publishOnDataTopic(AHATOFSTR(HATopic), "");
|
||||
}
|
||||
|
||||
void HADeviceTrigger::buildSerializer()
|
||||
{
|
||||
if (_serializer || !uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 5); // 5 - max properties nb
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAAutomationTypeProperty),
|
||||
AHATOFSTR(HATrigger),
|
||||
HASerializer::ProgmemPropertyValue
|
||||
);
|
||||
_serializer->set(
|
||||
AHATOFSTR(HATypeProperty),
|
||||
_type,
|
||||
_isProgmemType
|
||||
? HASerializer::ProgmemPropertyValue
|
||||
: HASerializer::ConstCharPropertyValue
|
||||
);
|
||||
_serializer->set(
|
||||
AHATOFSTR(HASubtypeProperty),
|
||||
_subtype,
|
||||
_isProgmemSubtype
|
||||
? HASerializer::ProgmemPropertyValue
|
||||
: HASerializer::ConstCharPropertyValue
|
||||
);
|
||||
_serializer->set(HASerializer::WithDevice);
|
||||
_serializer->topic(AHATOFSTR(HATopic));
|
||||
}
|
||||
|
||||
void HADeviceTrigger::onMqttConnected()
|
||||
{
|
||||
if (!uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
}
|
||||
|
||||
uint16_t HADeviceTrigger::calculateIdSize() const
|
||||
{
|
||||
if (!_type || !_subtype) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint16_t typeSize = _isProgmemType ? strlen_P(_type) : strlen(_type);
|
||||
const uint16_t subtypeSize = _isProgmemSubtype
|
||||
? strlen_P(_subtype)
|
||||
: strlen(_subtype);
|
||||
|
||||
// plus underscore separator and null terminator
|
||||
return typeSize + subtypeSize + 2;
|
||||
}
|
||||
|
||||
void HADeviceTrigger::buildUniqueId()
|
||||
{
|
||||
const uint16_t idSize = calculateIdSize();
|
||||
if (idSize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char* id = new char[idSize];
|
||||
|
||||
if (_isProgmemType) {
|
||||
strcpy_P(id, _type);
|
||||
} else {
|
||||
strcpy(id, _type);
|
||||
}
|
||||
|
||||
strcat_P(id, HASerializerUnderscore);
|
||||
|
||||
if (_isProgmemSubtype) {
|
||||
strcat_P(id, _subtype);
|
||||
} else {
|
||||
strcat(id, _subtype);
|
||||
}
|
||||
|
||||
_uniqueId = id;
|
||||
}
|
||||
|
||||
const char* HADeviceTrigger::determineProgmemType(TriggerType type) const
|
||||
{
|
||||
switch (type) {
|
||||
case ButtonShortPressType:
|
||||
return HAButtonShortPressType;
|
||||
|
||||
case ButtonShortReleaseType:
|
||||
return HAButtonShortReleaseType;
|
||||
|
||||
case ButtonLongPressType:
|
||||
return HAButtonLongPressType;
|
||||
|
||||
case ButtonLongReleaseType:
|
||||
return HAButtonLongReleaseType;
|
||||
|
||||
case ButtonDoublePressType:
|
||||
return HAButtonDoublePressType;
|
||||
|
||||
case ButtonTriplePressType:
|
||||
return HAButtonTriplePressType;
|
||||
|
||||
case ButtonQuadruplePressType:
|
||||
return HAButtonQuadruplePressType;
|
||||
|
||||
case ButtonQuintuplePressType:
|
||||
return HAButtonQuintuplePressType;
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const char* HADeviceTrigger::determineProgmemSubtype(
|
||||
TriggerSubtype subtype
|
||||
) const
|
||||
{
|
||||
switch (subtype) {
|
||||
case TurnOnSubtype:
|
||||
return HATurnOnSubtype;
|
||||
|
||||
case TurnOffSubtype:
|
||||
return HATurnOffSubtype;
|
||||
|
||||
case Button1Subtype:
|
||||
return HAButton1Subtype;
|
||||
|
||||
case Button2Subtype:
|
||||
return HAButton2Subtype;
|
||||
|
||||
case Button3Subtype:
|
||||
return HAButton3Subtype;
|
||||
|
||||
case Button4Subtype:
|
||||
return HAButton4Subtype;
|
||||
|
||||
case Button5Subtype:
|
||||
return HAButton5Subtype;
|
||||
|
||||
case Button6Subtype:
|
||||
return HAButton6Subtype;
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,176 @@
|
||||
#ifndef AHA_HADEVICETRIGGER_H
|
||||
#define AHA_HADEVICETRIGGER_H
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
|
||||
#ifndef EX_ARDUINOHA_DEVICE_TRIGGER
|
||||
|
||||
/**
|
||||
* HADeviceTrigger allows to a custom trigger that can be used in the Home Assistant automation.
|
||||
* For example, it can be a wall switch that produces `press` and `long_press` actions.
|
||||
*
|
||||
* @note
|
||||
* You can find more information about this entity in the Home Assistant documentation:
|
||||
* https://www.home-assistant.io/integrations/device_trigger.mqtt/
|
||||
*/
|
||||
class HADeviceTrigger : public HABaseDeviceType
|
||||
{
|
||||
public:
|
||||
/// Built-in types of the trigger.
|
||||
enum TriggerType {
|
||||
ButtonShortPressType = 1,
|
||||
ButtonShortReleaseType,
|
||||
ButtonLongPressType,
|
||||
ButtonLongReleaseType,
|
||||
ButtonDoublePressType,
|
||||
ButtonTriplePressType,
|
||||
ButtonQuadruplePressType,
|
||||
ButtonQuintuplePressType
|
||||
};
|
||||
|
||||
/// Built-in subtypes of the trigger.
|
||||
enum TriggerSubtype {
|
||||
TurnOnSubtype = 1,
|
||||
TurnOffSubtype,
|
||||
Button1Subtype,
|
||||
Button2Subtype,
|
||||
Button3Subtype,
|
||||
Button4Subtype,
|
||||
Button5Subtype,
|
||||
Button6Subtype
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the device trigger with a custom type and subtype.
|
||||
* For example, it can be `click` as the type and `btn0` as the subtype.
|
||||
* Please note that combination of the type and subtype needs to be unique in a scope of your device.
|
||||
*
|
||||
* @param type String representation of the type.
|
||||
* @param subtype String representation of the subtype.
|
||||
*/
|
||||
HADeviceTrigger(const char* type, const char* subtype);
|
||||
|
||||
/**
|
||||
* Creates the device trigger with a built-in type and a custom subtype.
|
||||
* For example, it can be `HADeviceTrigger::ButtonShortPressType` as the type and `btn0` as the subtype.
|
||||
* Please note that combination of the type and subtype needs to be unique in a scope of your device.
|
||||
*
|
||||
* @param type Built-in type of the trigger.
|
||||
* @param subtype String representation of the subtype.
|
||||
*/
|
||||
HADeviceTrigger(TriggerType type, const char* subtype);
|
||||
|
||||
/**
|
||||
* Creates the device trigger with a custom type and a built-in subtype.
|
||||
* For example, it can be `click` as the type and `HADeviceTrigger::Button1Subtype` as the subtype.
|
||||
* Please note that combination of the type and subtype needs to be unique in a scope of your device.
|
||||
*
|
||||
* @param type String representation of the subtype.
|
||||
* @param subtype Built-in subtype of the trigger.
|
||||
*/
|
||||
HADeviceTrigger(const char* type, TriggerSubtype subtype);
|
||||
|
||||
/**
|
||||
* Creates the device trigger with a built-in type and built-in subtype.
|
||||
* For example, it can be `HADeviceTrigger::ButtonShortPressType` as the type and `HADeviceTrigger::Button1Subtype` as the subtype.
|
||||
* Please note that combination of the type and subtype needs to be unique in a scope of your device.
|
||||
*
|
||||
* @param type Built-in type of the trigger.
|
||||
* @param subtype Built-in subtype of the trigger.
|
||||
*/
|
||||
HADeviceTrigger(TriggerType type, TriggerSubtype subtype);
|
||||
|
||||
/**
|
||||
* Frees memory allocated by the class.
|
||||
*/
|
||||
~HADeviceTrigger();
|
||||
|
||||
/**
|
||||
* Publishes MQTT message with the trigger event.
|
||||
* The published message is not retained.
|
||||
*
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool trigger();
|
||||
|
||||
/**
|
||||
* Returns the type of the trigger.
|
||||
* If the built-in type is used the returned value points to the flash memory.
|
||||
* Use `HADeviceTrigger::isProgmemType` to verify if the returned value is the progmem pointer.
|
||||
*
|
||||
* @returns Pointer to the type.
|
||||
*/
|
||||
inline const char* getType() const
|
||||
{ return _type; }
|
||||
|
||||
/**
|
||||
* Returns `true` if the built-in type was assigned to the trigger.
|
||||
*/
|
||||
inline bool isProgmemType() const
|
||||
{ return _isProgmemType; }
|
||||
|
||||
/**
|
||||
* Returns the subtype of the trigger.
|
||||
* If the built-in subtype is used the returned value points to the flash memory.
|
||||
* Use `HADeviceTrigger::isProgmemSubtype` to verify if the returned value is the progmem pointer.
|
||||
*
|
||||
* @returns Pointer to the subtype.
|
||||
*/
|
||||
inline const char* getSubtype() const
|
||||
{ return _subtype; }
|
||||
|
||||
/**
|
||||
* Returns `true` if the built-in subtype was assigned to the trigger.
|
||||
*/
|
||||
inline bool isProgmemSubtype() const
|
||||
{ return _isProgmemSubtype; }
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
virtual void onMqttConnected() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Calculates desired size of the unique ID based on the type and subtype that were passed to the constructor.
|
||||
*/
|
||||
uint16_t calculateIdSize() const;
|
||||
|
||||
/**
|
||||
* Builds the unique ID of the device's type based on the type and subtype that were passed to the constructor.
|
||||
*/
|
||||
void buildUniqueId();
|
||||
|
||||
/**
|
||||
* Returns pointer to the flash memory that represents the given type.
|
||||
*
|
||||
* @param subtype Built-in type enum's value.
|
||||
* @returns Pointer to the flash memory if the given type is supported.
|
||||
* For the unsupported type the nullptr is returned.
|
||||
*/
|
||||
const char* determineProgmemType(TriggerType type) const;
|
||||
|
||||
/**
|
||||
* Returns pointer to the flash memory that represents the given subtype.
|
||||
*
|
||||
* @param subtype Built-in subtype enum's value.
|
||||
* @returns Pointer to the flash memory if the given subtype is supported.
|
||||
* For the unsupported subtype the nullptr is returned.
|
||||
*/
|
||||
const char* determineProgmemSubtype(TriggerSubtype subtype) const;
|
||||
|
||||
private:
|
||||
/// Pointer to the trigger's type. It can be pointer to the flash memory.
|
||||
const char* _type;
|
||||
|
||||
/// Pointer to the trigger's subtype. It can be pointer to the flash memory.
|
||||
const char* _subtype;
|
||||
|
||||
/// Specifies whether the type points to the flash memory.
|
||||
bool _isProgmemType;
|
||||
|
||||
/// Specifies whether the subtype points to the flash memory.
|
||||
bool _isProgmemSubtype;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
193
lib/home-assistant-integration/src/device-types/HAFan.cpp
Normal file
193
lib/home-assistant-integration/src/device-types/HAFan.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
#include "HAFan.h"
|
||||
#ifndef EX_ARDUINOHA_FAN
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../utils/HAUtils.h"
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
HAFan::HAFan(const char* uniqueId, const uint8_t features) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentFan), uniqueId),
|
||||
_features(features),
|
||||
_icon(nullptr),
|
||||
_retain(false),
|
||||
_optimistic(false),
|
||||
_speedRangeMax(),
|
||||
_speedRangeMin(),
|
||||
_currentState(false),
|
||||
_currentSpeed(0),
|
||||
_stateCallback(nullptr),
|
||||
_speedCallback(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool HAFan::setState(const bool state, const bool force)
|
||||
{
|
||||
if (!force && state == _currentState) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishState(state)) {
|
||||
_currentState = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HAFan::setSpeed(const uint16_t speed, const bool force)
|
||||
{
|
||||
if (!force && speed == _currentSpeed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishSpeed(speed)) {
|
||||
_currentSpeed = speed;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HAFan::buildSerializer()
|
||||
{
|
||||
if (_serializer || !uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 13); // 13 - max properties nb
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAUniqueIdProperty), _uniqueId);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
|
||||
if (_retain) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HARetainProperty),
|
||||
&_retain,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_optimistic) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAOptimisticProperty),
|
||||
&_optimistic,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_features & SpeedsFeature) {
|
||||
_serializer->topic(AHATOFSTR(HAPercentageStateTopic));
|
||||
_serializer->topic(AHATOFSTR(HAPercentageCommandTopic));
|
||||
|
||||
if (_speedRangeMax.isSet()) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HASpeedRangeMaxProperty),
|
||||
&_speedRangeMax,
|
||||
HASerializer::NumberPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_speedRangeMin.isSet()) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HASpeedRangeMinProperty),
|
||||
&_speedRangeMin,
|
||||
HASerializer::NumberPropertyType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_serializer->set(HASerializer::WithDevice);
|
||||
_serializer->set(HASerializer::WithAvailability);
|
||||
_serializer->topic(AHATOFSTR(HAStateTopic));
|
||||
_serializer->topic(AHATOFSTR(HACommandTopic));
|
||||
}
|
||||
|
||||
void HAFan::onMqttConnected()
|
||||
{
|
||||
if (!uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishAvailability();
|
||||
|
||||
if (!_retain) {
|
||||
publishState(_currentState);
|
||||
publishSpeed(_currentSpeed);
|
||||
}
|
||||
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HACommandTopic));
|
||||
|
||||
if (_features & SpeedsFeature) {
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HAPercentageCommandTopic));
|
||||
}
|
||||
}
|
||||
|
||||
void HAFan::onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
)
|
||||
{
|
||||
if (HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HACommandTopic)
|
||||
)) {
|
||||
handleStateCommand(payload, length);
|
||||
} else if (HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HAPercentageCommandTopic)
|
||||
)) {
|
||||
handleSpeedCommand(payload, length);
|
||||
}
|
||||
}
|
||||
|
||||
bool HAFan::publishState(const bool state)
|
||||
{
|
||||
return publishOnDataTopic(
|
||||
AHATOFSTR(HAStateTopic),
|
||||
AHATOFSTR(state ? HAStateOn : HAStateOff),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
bool HAFan::publishSpeed(const uint16_t speed)
|
||||
{
|
||||
if (!(_features & SpeedsFeature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char str[5 + 1] = {0}; // uint16_t digits with null terminator
|
||||
HANumeric(speed, 0).toStr(str);
|
||||
|
||||
return publishOnDataTopic(AHATOFSTR(HAPercentageStateTopic), str, true);
|
||||
}
|
||||
|
||||
void HAFan::handleStateCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
(void)cmd;
|
||||
|
||||
if (!_stateCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool state = length == strlen_P(HAStateOn);
|
||||
_stateCallback(state, this);
|
||||
}
|
||||
|
||||
void HAFan::handleSpeedCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_speedCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
const HANumeric& number = HANumeric::fromStr(cmd, length);
|
||||
if (number.isUInt16()) {
|
||||
_speedCallback(number.toUInt16(), this);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
245
lib/home-assistant-integration/src/device-types/HAFan.h
Normal file
245
lib/home-assistant-integration/src/device-types/HAFan.h
Normal file
@@ -0,0 +1,245 @@
|
||||
#ifndef AHA_HAFAN_H
|
||||
#define AHA_HAFAN_H
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
#include "../utils/HANumeric.h"
|
||||
|
||||
#ifndef EX_ARDUINOHA_FAN
|
||||
|
||||
#define HAFAN_STATE_CALLBACK(name) void (*name)(bool state, HAFan* sender)
|
||||
#define HAFAN_SPEED_CALLBACK(name) void (*name)(uint16_t speed, HAFan* sender)
|
||||
|
||||
/**
|
||||
* HAFan allows adding a controllable fan in the Home Assistant panel.
|
||||
* The library supports only the state and speed of the fan.
|
||||
* If you want more features please open a new GitHub issue.
|
||||
*
|
||||
* @note
|
||||
* You can find more information about this entity in the Home Assistant documentation:
|
||||
* https://www.home-assistant.io/integrations/fan.mqtt/
|
||||
*/
|
||||
class HAFan : public HABaseDeviceType
|
||||
{
|
||||
public:
|
||||
enum Features {
|
||||
DefaultFeatures = 0,
|
||||
SpeedsFeature = 1
|
||||
};
|
||||
|
||||
/**
|
||||
* @param uniqueId The unique ID of the fan. It needs to be unique in a scope of your device.
|
||||
* @param features Features that should be enabled for the fan.
|
||||
*/
|
||||
HAFan(const char* uniqueId, const uint8_t features = DefaultFeatures);
|
||||
|
||||
/**
|
||||
* Changes state of the fan and publishes MQTT message.
|
||||
* Please note that if a new value is the same as previous one,
|
||||
* the MQTT message won't be published.
|
||||
*
|
||||
* @param state New state of the fan.
|
||||
* @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(const bool state, const bool force = false);
|
||||
|
||||
/**
|
||||
* Changes the speed of the fan and publishes MQTT message.
|
||||
* Please note that if a new value is the same as previous one,
|
||||
* the MQTT message won't be published.
|
||||
*
|
||||
* @param speed The new speed of the fan. It should be in range of min and max value.
|
||||
* @param force Forces to update the value without comparing it to a previous known value.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setSpeed(const uint16_t speed, const bool force = false);
|
||||
|
||||
/**
|
||||
* Alias for `setState(true)`.
|
||||
*/
|
||||
inline bool turnOn()
|
||||
{ return setState(true); }
|
||||
|
||||
/**
|
||||
* Alias for `setState(false)`.
|
||||
*/
|
||||
inline bool turnOff()
|
||||
{ return setState(false); }
|
||||
|
||||
/**
|
||||
* Sets current state of the fan without publishing it 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 fan.
|
||||
*/
|
||||
inline void setCurrentState(const bool state)
|
||||
{ _currentState = state; }
|
||||
|
||||
/**
|
||||
* Returns last known state of the fan.
|
||||
* By default it's `false`.
|
||||
*/
|
||||
inline bool getCurrentState() const
|
||||
{ return _currentState; }
|
||||
|
||||
/**
|
||||
* Sets the current speed of the fan without pushing the value to Home Assistant.
|
||||
* This method may be useful if you want to change the speed before the connection
|
||||
* with the MQTT broker is acquired.
|
||||
*
|
||||
* @param speed The new speed of the fan. It should be in range of min and max value.
|
||||
*/
|
||||
inline void setCurrentSpeed(const uint16_t speed)
|
||||
{ _currentSpeed = speed; }
|
||||
|
||||
/**
|
||||
* Returns the last known speed of the fan.
|
||||
* By default speed is set to `0`.
|
||||
*/
|
||||
inline uint16_t getCurrentSpeed() const
|
||||
{ return _currentSpeed; }
|
||||
|
||||
/**
|
||||
* Sets icon of the fan.
|
||||
* Any icon from MaterialDesignIcons.com (for example: `mdi:home`).
|
||||
*
|
||||
* @param icon The icon name.
|
||||
*/
|
||||
inline void setIcon(const char* icon)
|
||||
{ _icon = icon; }
|
||||
|
||||
/**
|
||||
* Sets retain flag for the fan's command.
|
||||
* If set to `true` the command produced by Home Assistant will be retained.
|
||||
*
|
||||
* @param retain
|
||||
*/
|
||||
inline void setRetain(const bool retain)
|
||||
{ _retain = retain; }
|
||||
|
||||
/**
|
||||
* Sets optimistic flag for the fan state.
|
||||
* In this mode the fan state doesn't need to be reported back to the HA panel when a command is received.
|
||||
* By default the optimistic mode is disabled.
|
||||
*
|
||||
* @param optimistic The optimistic mode (`true` - enabled, `false` - disabled).
|
||||
*/
|
||||
inline void setOptimistic(const bool optimistic)
|
||||
{ _optimistic = optimistic; }
|
||||
|
||||
/**
|
||||
* Sets the maximum of numeric output range (representing 100%).
|
||||
* The number of speeds within the speed_range / 100 will determine the percentage step.
|
||||
* By default the maximum range is `100`.
|
||||
*
|
||||
* @param max The maximum of numeric output range.
|
||||
*/
|
||||
inline void setSpeedRangeMax(const uint16_t max)
|
||||
{ _speedRangeMax.setBaseValue(max); }
|
||||
|
||||
/**
|
||||
* Sets the minimum of numeric output range (off is not included, so speed_range_min - 1 represents 0 %).
|
||||
* The number of speeds within the speed_range / 100 will determine the percentage step.
|
||||
* By default the minimum range is `1`.
|
||||
*
|
||||
* @param min The minimum of numeric output range.
|
||||
*/
|
||||
inline void setSpeedRangeMin(const uint16_t min)
|
||||
{ _speedRangeMin.setBaseValue(min); }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the state command from HA is received.
|
||||
* Please note that it's not possible to register multiple callbacks for the same fan.
|
||||
*
|
||||
* @param callback
|
||||
* @note In non-optimistic mode, the state must be reported back to HA using the HAFan::setState method.
|
||||
*/
|
||||
inline void onStateCommand(HAFAN_STATE_CALLBACK(callback))
|
||||
{ _stateCallback = callback; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the speed command from HA is received.
|
||||
* Please note that it's not possible to register multiple callbacks for the same fan.
|
||||
*
|
||||
* @param callback
|
||||
* @note In non-optimistic mode, the speed must be reported back to HA using the HAFan::setSpeed method.
|
||||
*/
|
||||
inline void onSpeedCommand(HAFAN_SPEED_CALLBACK(callback))
|
||||
{ _speedCallback = callback; }
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
virtual void onMqttConnected() override;
|
||||
virtual void onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Publishes the MQTT message with the given state.
|
||||
*
|
||||
* @param state The state to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishState(const bool state);
|
||||
|
||||
/**
|
||||
* Publishes the MQTT message with the given speed.
|
||||
*
|
||||
* @param speed The speed to publish. It should be in range of min and max value.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishSpeed(const uint16_t speed);
|
||||
|
||||
/**
|
||||
* Parses the given state command and executes the callback with proper value.
|
||||
*
|
||||
* @param cmd The data of the command.
|
||||
* @param length Length of the command.
|
||||
*/
|
||||
void handleStateCommand(const uint8_t* cmd, const uint16_t length);
|
||||
|
||||
/**
|
||||
* Parses the given speed command and executes the callback with proper value.
|
||||
*
|
||||
* @param cmd The data of the command.
|
||||
* @param length Length of the command.
|
||||
*/
|
||||
void handleSpeedCommand(const uint8_t* cmd, const uint16_t length);
|
||||
|
||||
/// Features enabled for the fan.
|
||||
const uint8_t _features;
|
||||
|
||||
/// The icon of the button. It can be nullptr.
|
||||
const char* _icon;
|
||||
|
||||
/// The retain flag for the HA commands.
|
||||
bool _retain;
|
||||
|
||||
/// The optimistic mode of the fan (`true` - enabled, `false` - disabled).
|
||||
bool _optimistic;
|
||||
|
||||
/// The maximum of numeric output range.
|
||||
HANumeric _speedRangeMax;
|
||||
|
||||
/// The minimum of numeric output range.
|
||||
HANumeric _speedRangeMin;
|
||||
|
||||
/// The current state of the fan. By default it's `false`.
|
||||
bool _currentState;
|
||||
|
||||
/// The current speed of the fan. By default it's `0`.
|
||||
uint16_t _currentSpeed;
|
||||
|
||||
/// The callback that will be called when the state command is received from the HA.
|
||||
HAFAN_STATE_CALLBACK(_stateCallback);
|
||||
|
||||
/// The callback that will be called when the speed command is received from the HA.
|
||||
HAFAN_SPEED_CALLBACK(_speedCallback);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
755
lib/home-assistant-integration/src/device-types/HAHVAC.cpp
Normal file
755
lib/home-assistant-integration/src/device-types/HAHVAC.cpp
Normal file
@@ -0,0 +1,755 @@
|
||||
#include "HAHVAC.h"
|
||||
#ifndef EX_ARDUINOHA_HVAC
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../utils/HAUtils.h"
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
const uint8_t HAHVAC::DefaultFanModes = AutoFanMode | LowFanMode | MediumFanMode | HighFanMode;
|
||||
const uint8_t HAHVAC::DefaultSwingModes = OnSwingMode | OffSwingMode;
|
||||
const uint8_t HAHVAC::DefaultModes = AutoMode | OffMode | CoolMode | HeatMode | DryMode | FanOnlyMode;
|
||||
|
||||
HAHVAC::HAHVAC(
|
||||
const char* uniqueId,
|
||||
const uint16_t features,
|
||||
const NumberPrecision precision
|
||||
) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentClimate), uniqueId),
|
||||
_features(features),
|
||||
_precision(precision),
|
||||
_icon(nullptr),
|
||||
_retain(false),
|
||||
_CURRENT_TEMPerature(),
|
||||
_action(UnknownAction),
|
||||
_temperatureUnit(DefaultUnit),
|
||||
_minTemp(),
|
||||
_maxTemp(),
|
||||
_tempStep(),
|
||||
_auxCallback(nullptr),
|
||||
_auxState(false),
|
||||
_powerCallback(nullptr),
|
||||
_fanMode(UnknownFanMode),
|
||||
_fanModes(DefaultFanModes),
|
||||
_fanModesSerializer(nullptr),
|
||||
_fanModeCallback(nullptr),
|
||||
_swingMode(UnknownSwingMode),
|
||||
_swingModes(DefaultSwingModes),
|
||||
_swingModesSerializer(nullptr),
|
||||
_swingModeCallback(nullptr),
|
||||
_mode(UnknownMode),
|
||||
_modes(DefaultModes),
|
||||
_modesSerializer(nullptr),
|
||||
_modeCallback(nullptr),
|
||||
_targetTemperature(),
|
||||
_targetTemperatureCallback(nullptr)
|
||||
{
|
||||
if (_features & FanFeature) {
|
||||
_fanModesSerializer = new HASerializerArray(4);
|
||||
}
|
||||
|
||||
if (_features & SwingFeature) {
|
||||
_swingModesSerializer = new HASerializerArray(2);
|
||||
}
|
||||
|
||||
if (_features & ModesFeature) {
|
||||
_modesSerializer = new HASerializerArray(6);
|
||||
}
|
||||
}
|
||||
|
||||
HAHVAC::~HAHVAC()
|
||||
{
|
||||
if (_fanModesSerializer) {
|
||||
delete _fanModesSerializer;
|
||||
}
|
||||
|
||||
if (_swingModesSerializer) {
|
||||
delete _swingModesSerializer;
|
||||
}
|
||||
|
||||
if (_modesSerializer) {
|
||||
delete _modesSerializer;
|
||||
}
|
||||
}
|
||||
|
||||
bool HAHVAC::setCurrentTemperature(const HANumeric& temperature, const bool force)
|
||||
{
|
||||
if (temperature.getPrecision() != _precision) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!force && temperature == _CURRENT_TEMPerature) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishCurrentTemperature(temperature)) {
|
||||
_CURRENT_TEMPerature = temperature;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HAHVAC::setAction(const Action action, const bool force)
|
||||
{
|
||||
if (!force && action == _action) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishAction(action)) {
|
||||
_action = action;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HAHVAC::setAuxState(const bool state, const bool force)
|
||||
{
|
||||
if (!force && state == _auxState) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishAuxState(state)) {
|
||||
_auxState = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HAHVAC::setFanMode(const FanMode mode, const bool force)
|
||||
{
|
||||
if (!force && mode == _fanMode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishFanMode(mode)) {
|
||||
_fanMode = mode;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HAHVAC::setSwingMode(const SwingMode mode, const bool force)
|
||||
{
|
||||
if (!force && mode == _swingMode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishSwingMode(mode)) {
|
||||
_swingMode = mode;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HAHVAC::setMode(const Mode mode, const bool force)
|
||||
{
|
||||
if (!force && mode == _mode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishMode(mode)) {
|
||||
_mode = mode;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HAHVAC::setTargetTemperature(const HANumeric& temperature, const bool force)
|
||||
{
|
||||
if (temperature.getPrecision() != _precision) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!force && temperature == _targetTemperature) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishTargetTemperature(temperature)) {
|
||||
_targetTemperature = temperature;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HAHVAC::buildSerializer()
|
||||
{
|
||||
if (_serializer || !uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 27); // 27 - max properties nb
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAUniqueIdProperty), _uniqueId);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
|
||||
if (_retain) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HARetainProperty),
|
||||
&_retain,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_features & ActionFeature) {
|
||||
_serializer->topic(AHATOFSTR(HAActionTopic));
|
||||
}
|
||||
|
||||
if (_features & AuxHeatingFeature) {
|
||||
_serializer->topic(AHATOFSTR(HAAuxCommandTopic));
|
||||
_serializer->topic(AHATOFSTR(HAAuxStateTopic));
|
||||
}
|
||||
|
||||
if (_features & PowerFeature) {
|
||||
_serializer->topic(AHATOFSTR(HAPowerCommandTopic));
|
||||
}
|
||||
|
||||
if (_features & FanFeature) {
|
||||
_serializer->topic(AHATOFSTR(HAFanModeCommandTopic));
|
||||
_serializer->topic(AHATOFSTR(HAFanModeStateTopic));
|
||||
|
||||
if (_fanModes != DefaultFanModes) {
|
||||
_fanModesSerializer->clear();
|
||||
|
||||
if (_fanModes & AutoFanMode) {
|
||||
_fanModesSerializer->add(HAFanModeAuto);
|
||||
}
|
||||
|
||||
if (_fanModes & LowFanMode) {
|
||||
_fanModesSerializer->add(HAFanModeLow);
|
||||
}
|
||||
|
||||
if (_fanModes & MediumFanMode) {
|
||||
_fanModesSerializer->add(HAFanModeMedium);
|
||||
}
|
||||
|
||||
if (_fanModes & HighFanMode) {
|
||||
_fanModesSerializer->add(HAFanModeHigh);
|
||||
}
|
||||
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAFanModesProperty),
|
||||
_fanModesSerializer,
|
||||
HASerializer::ArrayPropertyType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (_features & SwingFeature) {
|
||||
_serializer->topic(AHATOFSTR(HASwingModeCommandTopic));
|
||||
_serializer->topic(AHATOFSTR(HASwingModeStateTopic));
|
||||
|
||||
if (_swingModes != DefaultSwingModes) {
|
||||
_swingModesSerializer->clear();
|
||||
|
||||
if (_swingModes & OnSwingMode) {
|
||||
_swingModesSerializer->add(HASwingModeOn);
|
||||
}
|
||||
|
||||
if (_swingModes & OffSwingMode) {
|
||||
_swingModesSerializer->add(HASwingModeOff);
|
||||
}
|
||||
|
||||
_serializer->set(
|
||||
AHATOFSTR(HASwingModesProperty),
|
||||
_swingModesSerializer,
|
||||
HASerializer::ArrayPropertyType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (_features & ModesFeature) {
|
||||
_serializer->topic(AHATOFSTR(HAModeCommandTopic));
|
||||
_serializer->topic(AHATOFSTR(HAModeStateTopic));
|
||||
|
||||
if (_modes != DefaultModes) {
|
||||
_modesSerializer->clear();
|
||||
|
||||
if (_modes & AutoMode) {
|
||||
_modesSerializer->add(HAModeAuto);
|
||||
}
|
||||
|
||||
if (_modes & OffMode) {
|
||||
_modesSerializer->add(HAModeOff);
|
||||
}
|
||||
|
||||
if (_modes & CoolMode) {
|
||||
_modesSerializer->add(HAModeCool);
|
||||
}
|
||||
|
||||
if (_modes & HeatMode) {
|
||||
_modesSerializer->add(HAModeHeat);
|
||||
}
|
||||
|
||||
if (_modes & DryMode) {
|
||||
_modesSerializer->add(HAModeDry);
|
||||
}
|
||||
|
||||
if (_modes & FanOnlyMode) {
|
||||
_modesSerializer->add(HAModeFanOnly);
|
||||
}
|
||||
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAModesProperty),
|
||||
_modesSerializer,
|
||||
HASerializer::ArrayPropertyType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (_features & TargetTemperatureFeature) {
|
||||
_serializer->topic(AHATOFSTR(HATemperatureCommandTopic));
|
||||
_serializer->topic(AHATOFSTR(HATemperatureStateTopic));
|
||||
_serializer->set(
|
||||
AHATOFSTR(HATemperatureCommandTemplateProperty),
|
||||
getCommandWithFloatTemplate(),
|
||||
HASerializer::ProgmemPropertyValue
|
||||
);
|
||||
}
|
||||
|
||||
if (_temperatureUnit != DefaultUnit) {
|
||||
const __FlashStringHelper *unitStr = _temperatureUnit == CelsiusUnit
|
||||
? AHATOFSTR(HATemperatureUnitC)
|
||||
: AHATOFSTR(HATemperatureUnitF);
|
||||
|
||||
_serializer->set(
|
||||
AHATOFSTR(HATemperatureUnitProperty),
|
||||
unitStr,
|
||||
HASerializer::ProgmemPropertyValue
|
||||
);
|
||||
}
|
||||
|
||||
if (_minTemp.isSet()) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAMinTempProperty),
|
||||
&_minTemp,
|
||||
HASerializer::NumberPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_maxTemp.isSet()) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAMaxTempProperty),
|
||||
&_maxTemp,
|
||||
HASerializer::NumberPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_tempStep.isSet()) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HATempStepProperty),
|
||||
&_tempStep,
|
||||
HASerializer::NumberPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
_serializer->topic(AHATOFSTR(HACurrentTemperatureTopic));
|
||||
_serializer->set(HASerializer::WithDevice);
|
||||
_serializer->set(HASerializer::WithAvailability);
|
||||
}
|
||||
|
||||
void HAHVAC::onMqttConnected()
|
||||
{
|
||||
if (!uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishAvailability();
|
||||
|
||||
if (!_retain) {
|
||||
publishCurrentTemperature(_CURRENT_TEMPerature);
|
||||
publishAction(_action);
|
||||
publishAuxState(_auxState);
|
||||
publishFanMode(_fanMode);
|
||||
publishSwingMode(_swingMode);
|
||||
publishMode(_mode);
|
||||
publishTargetTemperature(_targetTemperature);
|
||||
}
|
||||
|
||||
if (_features & AuxHeatingFeature) {
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HAAuxCommandTopic));
|
||||
}
|
||||
|
||||
if (_features & PowerFeature) {
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HAPowerCommandTopic));
|
||||
}
|
||||
|
||||
if (_features & FanFeature) {
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HAFanModeCommandTopic));
|
||||
}
|
||||
|
||||
if (_features & SwingFeature) {
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HASwingModeCommandTopic));
|
||||
}
|
||||
|
||||
if (_features & ModesFeature) {
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HAModeCommandTopic));
|
||||
}
|
||||
|
||||
if (_features & TargetTemperatureFeature) {
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HATemperatureCommandTopic));
|
||||
}
|
||||
}
|
||||
|
||||
void HAHVAC::onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
)
|
||||
{
|
||||
if (HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HAAuxCommandTopic)
|
||||
)) {
|
||||
handleAuxStateCommand(payload, length);
|
||||
} else if (HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HAPowerCommandTopic)
|
||||
)) {
|
||||
handlePowerCommand(payload, length);
|
||||
} else if (HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HAFanModeCommandTopic)
|
||||
)) {
|
||||
handleFanModeCommand(payload, length);
|
||||
} else if (HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HASwingModeCommandTopic)
|
||||
)) {
|
||||
handleSwingModeCommand(payload, length);
|
||||
} else if (HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HAModeCommandTopic)
|
||||
)) {
|
||||
handleModeCommand(payload, length);
|
||||
} else if (HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HATemperatureCommandTopic)
|
||||
)) {
|
||||
handleTargetTemperatureCommand(payload, length);
|
||||
}
|
||||
}
|
||||
|
||||
bool HAHVAC::publishCurrentTemperature(const HANumeric& temperature)
|
||||
{
|
||||
if (!temperature.isSet()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t size = temperature.calculateSize();
|
||||
if (size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char str[size + 1]; // with null terminator
|
||||
str[size] = 0;
|
||||
temperature.toStr(str);
|
||||
|
||||
return publishOnDataTopic(
|
||||
AHATOFSTR(HACurrentTemperatureTopic),
|
||||
str,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
bool HAHVAC::publishAction(const Action action)
|
||||
{
|
||||
if (action == UnknownAction || !(_features & ActionFeature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const __FlashStringHelper *stateStr = nullptr;
|
||||
switch (action) {
|
||||
case OffAction:
|
||||
stateStr = AHATOFSTR(HAActionOff);
|
||||
break;
|
||||
|
||||
case HeatingAction:
|
||||
stateStr = AHATOFSTR(HAActionHeating);
|
||||
break;
|
||||
|
||||
case CoolingAction:
|
||||
stateStr = AHATOFSTR(HAActionCooling);
|
||||
break;
|
||||
|
||||
case DryingAction:
|
||||
stateStr = AHATOFSTR(HAActionDrying);
|
||||
break;
|
||||
|
||||
case IdleAction:
|
||||
stateStr = AHATOFSTR(HAActionIdle);
|
||||
break;
|
||||
|
||||
case FanAction:
|
||||
stateStr = AHATOFSTR(HAActionFan);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return publishOnDataTopic(
|
||||
AHATOFSTR(HAActionTopic),
|
||||
stateStr,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
bool HAHVAC::publishAuxState(const bool state)
|
||||
{
|
||||
if (!(_features & AuxHeatingFeature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return publishOnDataTopic(
|
||||
AHATOFSTR(HAAuxStateTopic),
|
||||
AHATOFSTR(state ? HAStateOn : HAStateOff),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
bool HAHVAC::publishFanMode(const FanMode mode)
|
||||
{
|
||||
if (mode == UnknownFanMode || !(_features & FanFeature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const __FlashStringHelper *stateStr = nullptr;
|
||||
switch (mode) {
|
||||
case AutoFanMode:
|
||||
stateStr = AHATOFSTR(HAFanModeAuto);
|
||||
break;
|
||||
|
||||
case LowFanMode:
|
||||
stateStr = AHATOFSTR(HAFanModeLow);
|
||||
break;
|
||||
|
||||
case MediumFanMode:
|
||||
stateStr = AHATOFSTR(HAFanModeMedium);
|
||||
break;
|
||||
|
||||
case HighFanMode:
|
||||
stateStr = AHATOFSTR(HAFanModeHigh);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return publishOnDataTopic(
|
||||
AHATOFSTR(HAFanModeStateTopic),
|
||||
stateStr,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
bool HAHVAC::publishSwingMode(const SwingMode mode)
|
||||
{
|
||||
if (mode == UnknownSwingMode || !(_features & SwingFeature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const __FlashStringHelper *stateStr = nullptr;
|
||||
switch (mode) {
|
||||
case OnSwingMode:
|
||||
stateStr = AHATOFSTR(HASwingModeOn);
|
||||
break;
|
||||
|
||||
case OffSwingMode:
|
||||
stateStr = AHATOFSTR(HASwingModeOff);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return publishOnDataTopic(
|
||||
AHATOFSTR(HASwingModeStateTopic),
|
||||
stateStr,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
bool HAHVAC::publishMode(const Mode mode)
|
||||
{
|
||||
if (mode == UnknownMode || !(_features & ModesFeature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const __FlashStringHelper *stateStr = nullptr;
|
||||
switch (mode) {
|
||||
case AutoMode:
|
||||
stateStr = AHATOFSTR(HAModeAuto);
|
||||
break;
|
||||
|
||||
case OffMode:
|
||||
stateStr = AHATOFSTR(HAModeOff);
|
||||
break;
|
||||
|
||||
case CoolMode:
|
||||
stateStr = AHATOFSTR(HAModeCool);
|
||||
break;
|
||||
|
||||
case HeatMode:
|
||||
stateStr = AHATOFSTR(HAModeHeat);
|
||||
break;
|
||||
|
||||
case DryMode:
|
||||
stateStr = AHATOFSTR(HAModeDry);
|
||||
break;
|
||||
|
||||
case FanOnlyMode:
|
||||
stateStr = AHATOFSTR(HAModeFanOnly);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return publishOnDataTopic(
|
||||
AHATOFSTR(HAModeStateTopic),
|
||||
stateStr,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
bool HAHVAC::publishTargetTemperature(const HANumeric& temperature)
|
||||
{
|
||||
if (!temperature.isSet()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t size = temperature.calculateSize();
|
||||
if (size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char str[size + 1]; // with null terminator
|
||||
str[size] = 0;
|
||||
temperature.toStr(str);
|
||||
|
||||
return publishOnDataTopic(
|
||||
AHATOFSTR(HATemperatureStateTopic),
|
||||
str,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
void HAHVAC::handleAuxStateCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
(void)cmd;
|
||||
|
||||
if (!_auxCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool state = length == strlen_P(HAStateOn);
|
||||
_auxCallback(state, this);
|
||||
}
|
||||
|
||||
void HAHVAC::handlePowerCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
(void)cmd;
|
||||
|
||||
if (!_powerCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool state = length == strlen_P(HAStateOn);
|
||||
_powerCallback(state, this);
|
||||
}
|
||||
|
||||
void HAHVAC::handleFanModeCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_fanModeCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp_P(cmd, HAFanModeAuto, length) == 0) {
|
||||
_fanModeCallback(AutoFanMode, this);
|
||||
} else if (memcmp_P(cmd, HAFanModeLow, length) == 0) {
|
||||
_fanModeCallback(LowFanMode, this);
|
||||
} else if (memcmp_P(cmd, HAFanModeMedium, length) == 0) {
|
||||
_fanModeCallback(MediumFanMode, this);
|
||||
} else if (memcmp_P(cmd, HAFanModeHigh, length) == 0) {
|
||||
_fanModeCallback(HighFanMode, this);
|
||||
}
|
||||
}
|
||||
|
||||
void HAHVAC::handleSwingModeCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_swingModeCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp_P(cmd, HASwingModeOn, length) == 0) {
|
||||
_swingModeCallback(OnSwingMode, this);
|
||||
} else if (memcmp_P(cmd, HASwingModeOff, length) == 0) {
|
||||
_swingModeCallback(OffSwingMode, this);
|
||||
}
|
||||
}
|
||||
|
||||
void HAHVAC::handleModeCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_modeCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp_P(cmd, HAModeAuto, length) == 0) {
|
||||
_modeCallback(AutoMode, this);
|
||||
} else if (memcmp_P(cmd, HAModeOff, length) == 0) {
|
||||
_modeCallback(OffMode, this);
|
||||
} else if (memcmp_P(cmd, HAModeCool, length) == 0) {
|
||||
_modeCallback(CoolMode, this);
|
||||
} else if (memcmp_P(cmd, HAModeHeat, length) == 0) {
|
||||
_modeCallback(HeatMode, this);
|
||||
} else if (memcmp_P(cmd, HAModeDry, length) == 0) {
|
||||
_modeCallback(DryMode, this);
|
||||
} else if (memcmp_P(cmd, HAModeFanOnly, length) == 0) {
|
||||
_modeCallback(FanOnlyMode, this);
|
||||
}
|
||||
}
|
||||
|
||||
void HAHVAC::handleTargetTemperatureCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_targetTemperatureCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
HANumeric number = HANumeric::fromStr(cmd, length);
|
||||
if (number.isSet()) {
|
||||
number.setPrecision(_precision);
|
||||
_targetTemperatureCallback(number, this);
|
||||
}
|
||||
}
|
||||
|
||||
const __FlashStringHelper* HAHVAC::getCommandWithFloatTemplate()
|
||||
{
|
||||
switch (_precision) {
|
||||
case PrecisionP1:
|
||||
return AHATOFSTR(HAValueTemplateFloatP1);
|
||||
|
||||
case PrecisionP2:
|
||||
return AHATOFSTR(HAValueTemplateFloatP2);
|
||||
|
||||
case PrecisionP3:
|
||||
return AHATOFSTR(HAValueTemplateFloatP3);
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
704
lib/home-assistant-integration/src/device-types/HAHVAC.h
Normal file
704
lib/home-assistant-integration/src/device-types/HAHVAC.h
Normal file
@@ -0,0 +1,704 @@
|
||||
#ifndef AHA_HAHVAC_H
|
||||
#define AHA_HAHVAC_H
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
#include "../utils/HANumeric.h"
|
||||
|
||||
#ifndef EX_ARDUINOHA_HVAC
|
||||
|
||||
#define _SET_CURRENT_TEMPERATURE_OVERLOAD(type) \
|
||||
/** @overload */ \
|
||||
inline bool setCurrentTemperature(const type temperature, const bool force = false) \
|
||||
{ return setCurrentTemperature(HANumeric(temperature, _precision), force); }
|
||||
|
||||
#define _SET_CURRENT_CURRENT_TEMPERATURE_OVERLOAD(type) \
|
||||
/** @overload */ \
|
||||
inline void setCurrentCurrentTemperature(const type temperature) \
|
||||
{ setCurrentCurrentTemperature(HANumeric(temperature, _precision)); }
|
||||
|
||||
#define _SET_TARGET_TEMPERATURE_OVERLOAD(type) \
|
||||
/** @overload */ \
|
||||
inline bool setTargetTemperature(const type temperature, const bool force = false) \
|
||||
{ return setTargetTemperature(HANumeric(temperature, _precision), force); }
|
||||
|
||||
#define _SET_CURRENT_TARGET_TEMPERATURE_OVERLOAD(type) \
|
||||
/** @overload */ \
|
||||
inline void setCurrentTargetTemperature(const type temperature) \
|
||||
{ setCurrentTargetTemperature(HANumeric(temperature, _precision)); }
|
||||
|
||||
#define HAHVAC_CALLBACK_BOOL(name) void (*name)(bool state, HAHVAC* sender)
|
||||
#define HAHVAC_CALLBACK_TARGET_TEMP(name) void (*name)(HANumeric temperature, HAHVAC* sender)
|
||||
#define HAHVAC_CALLBACK_FAN_MODE(name) void (*name)(FanMode mode, HAHVAC* sender)
|
||||
#define HAHVAC_CALLBACK_SWING_MODE(name) void (*name)(SwingMode mode, HAHVAC* sender)
|
||||
#define HAHVAC_CALLBACK_MODE(name) void (*name)(Mode mode, HAHVAC* sender)
|
||||
|
||||
class HASerializerArray;
|
||||
|
||||
/**
|
||||
* HAHVAC lets you control your HVAC devices.
|
||||
*
|
||||
* @note
|
||||
* You can find more information about this entity in the Home Assistant documentation:
|
||||
* https://www.home-assistant.io/integrations/climate.mqtt/
|
||||
*/
|
||||
class HAHVAC : public HABaseDeviceType
|
||||
{
|
||||
public:
|
||||
static const uint8_t DefaultFanModes;
|
||||
static const uint8_t DefaultSwingModes;
|
||||
static const uint8_t DefaultModes;
|
||||
|
||||
/// The list of features available in the HVAC. They're used in the constructor.
|
||||
enum Features {
|
||||
DefaultFeatures = 0,
|
||||
ActionFeature = 1,
|
||||
AuxHeatingFeature = 2,
|
||||
PowerFeature = 4,
|
||||
FanFeature = 8,
|
||||
SwingFeature = 16,
|
||||
ModesFeature = 32,
|
||||
TargetTemperatureFeature = 64
|
||||
};
|
||||
|
||||
/// The list of available actions of the HVAC.
|
||||
enum Action {
|
||||
UnknownAction = 0,
|
||||
OffAction,
|
||||
HeatingAction,
|
||||
CoolingAction,
|
||||
DryingAction,
|
||||
IdleAction,
|
||||
FanAction
|
||||
};
|
||||
|
||||
/// The list of available fan modes.
|
||||
enum FanMode {
|
||||
UnknownFanMode = 0,
|
||||
AutoFanMode = 1,
|
||||
LowFanMode = 2,
|
||||
MediumFanMode = 4,
|
||||
HighFanMode = 8
|
||||
};
|
||||
|
||||
/// The list of available swing modes.
|
||||
enum SwingMode {
|
||||
UnknownSwingMode = 0,
|
||||
OnSwingMode = 1,
|
||||
OffSwingMode = 2
|
||||
};
|
||||
|
||||
/// The list of available HVAC's modes.
|
||||
enum Mode {
|
||||
UnknownMode = 0,
|
||||
AutoMode = 1,
|
||||
OffMode = 2,
|
||||
CoolMode = 4,
|
||||
HeatMode = 8,
|
||||
DryMode = 16,
|
||||
FanOnlyMode = 32
|
||||
};
|
||||
|
||||
/// Temperature units available in the HVAC.
|
||||
enum TemperatureUnit {
|
||||
DefaultUnit = 1,
|
||||
CelsiusUnit,
|
||||
FahrenheitUnit
|
||||
};
|
||||
|
||||
/**
|
||||
* @param uniqueId The unique ID of the HVAC. It needs to be unique in a scope of your device.
|
||||
* @param features Features that should be enabled for the HVAC.
|
||||
* @param precision The precision of temperatures reported by the HVAC.
|
||||
*/
|
||||
HAHVAC(
|
||||
const char* uniqueId,
|
||||
const uint16_t features = DefaultFeatures,
|
||||
const NumberPrecision precision = PrecisionP1
|
||||
);
|
||||
|
||||
/**
|
||||
* Frees memory allocated for the arrays serialization.
|
||||
*/
|
||||
~HAHVAC();
|
||||
|
||||
/**
|
||||
* Changes current temperature of the HVAC 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 temperature New current temperature.
|
||||
* @param force Forces to update the temperature without comparing it to a previous known value.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setCurrentTemperature(const HANumeric& temperature, const bool force = false);
|
||||
|
||||
_SET_CURRENT_TEMPERATURE_OVERLOAD(int8_t)
|
||||
_SET_CURRENT_TEMPERATURE_OVERLOAD(int16_t)
|
||||
_SET_CURRENT_TEMPERATURE_OVERLOAD(int32_t)
|
||||
_SET_CURRENT_TEMPERATURE_OVERLOAD(uint8_t)
|
||||
_SET_CURRENT_TEMPERATURE_OVERLOAD(uint16_t)
|
||||
_SET_CURRENT_TEMPERATURE_OVERLOAD(uint32_t)
|
||||
_SET_CURRENT_TEMPERATURE_OVERLOAD(float)
|
||||
|
||||
#ifdef __SAMD21G18A__
|
||||
_SET_CURRENT_TEMPERATURE_OVERLOAD(int)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Changes action of the HVAC 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 action New action.
|
||||
* @param force Forces to update the action without comparing it to a previous known value.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setAction(const Action action, const bool force = false);
|
||||
|
||||
/**
|
||||
* Changes state of the aux heating 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 The new state.
|
||||
* @param force Forces to update the state without comparing it to a previous known value.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setAuxState(const bool state, const bool force = false);
|
||||
|
||||
/**
|
||||
* Changes mode of the fan of the HVAC 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 mode New fan's mode.
|
||||
* @param force Forces to update the mode without comparing it to a previous known value.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setFanMode(const FanMode mode, const bool force = false);
|
||||
|
||||
/**
|
||||
* Changes swing mode of the HVAC 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 mode New swing mode.
|
||||
* @param force Forces to update the mode without comparing it to a previous known value.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setSwingMode(const SwingMode mode, const bool force = false);
|
||||
|
||||
/**
|
||||
* Changes mode of the HVAC 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 mode New HVAC's mode.
|
||||
* @param force Forces to update the mode without comparing it to a previous known value.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setMode(const Mode mode, const bool force = false);
|
||||
|
||||
/**
|
||||
* Changes target temperature of the HVAC 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 temperature Target temperature to set.
|
||||
* @param force Forces to update the mode without comparing it to a previous known value.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setTargetTemperature(const HANumeric& temperature, const bool force = false);
|
||||
|
||||
_SET_TARGET_TEMPERATURE_OVERLOAD(int8_t)
|
||||
_SET_TARGET_TEMPERATURE_OVERLOAD(int16_t)
|
||||
_SET_TARGET_TEMPERATURE_OVERLOAD(int32_t)
|
||||
_SET_TARGET_TEMPERATURE_OVERLOAD(uint8_t)
|
||||
_SET_TARGET_TEMPERATURE_OVERLOAD(uint16_t)
|
||||
_SET_TARGET_TEMPERATURE_OVERLOAD(uint32_t)
|
||||
_SET_TARGET_TEMPERATURE_OVERLOAD(float)
|
||||
|
||||
#ifdef __SAMD21G18A__
|
||||
_SET_TARGET_TEMPERATURE_OVERLOAD(int)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Sets current temperature of the HVAC without publishing it to Home Assistant.
|
||||
* This method may be useful if you want to change temperature before connection
|
||||
* with MQTT broker is acquired.
|
||||
*
|
||||
* @param temperature New current temperature.
|
||||
*/
|
||||
inline void setCurrentCurrentTemperature(const HANumeric& temperature)
|
||||
{ if (temperature.getPrecision() == _precision) { _CURRENT_TEMPerature = temperature; } }
|
||||
|
||||
_SET_CURRENT_CURRENT_TEMPERATURE_OVERLOAD(int8_t)
|
||||
_SET_CURRENT_CURRENT_TEMPERATURE_OVERLOAD(int16_t)
|
||||
_SET_CURRENT_CURRENT_TEMPERATURE_OVERLOAD(int32_t)
|
||||
_SET_CURRENT_CURRENT_TEMPERATURE_OVERLOAD(uint8_t)
|
||||
_SET_CURRENT_CURRENT_TEMPERATURE_OVERLOAD(uint16_t)
|
||||
_SET_CURRENT_CURRENT_TEMPERATURE_OVERLOAD(uint32_t)
|
||||
_SET_CURRENT_CURRENT_TEMPERATURE_OVERLOAD(float)
|
||||
|
||||
#ifdef __SAMD21G18A__
|
||||
_SET_CURRENT_CURRENT_TEMPERATURE_OVERLOAD(int)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns last known current temperature of the HVAC.
|
||||
* If setCurrentTemperature method wasn't called the initial value will be returned.
|
||||
*/
|
||||
inline const HANumeric& getCurrentTemperature() const
|
||||
{ return _CURRENT_TEMPerature; }
|
||||
|
||||
/**
|
||||
* Sets action of the HVAC without publishing it to Home Assistant.
|
||||
* This method may be useful if you want to change the action before connection
|
||||
* with MQTT broker is acquired.
|
||||
*
|
||||
* @param action New action.
|
||||
*/
|
||||
inline void setCurrentAction(const Action action)
|
||||
{ _action = action; }
|
||||
|
||||
/**
|
||||
* Returns last known action of the HVAC.
|
||||
* If setAction method wasn't called the initial value will be returned.
|
||||
*/
|
||||
inline Action getCurrentAction() const
|
||||
{ return _action; }
|
||||
|
||||
/**
|
||||
* Sets aux heating state without publishing it to Home Assistant.
|
||||
* This method may be useful if you want to change the state before connection
|
||||
* with MQTT broker is acquired.
|
||||
*
|
||||
* @param state The new state.
|
||||
*/
|
||||
inline void setCurrentAuxState(const bool state)
|
||||
{ _auxState = state; }
|
||||
|
||||
/**
|
||||
* Returns last known state of the aux heating.
|
||||
* If setAuxState method wasn't called the initial value will be returned.
|
||||
*/
|
||||
inline bool getCurrentAuxState() const
|
||||
{ return _auxState; }
|
||||
|
||||
/**
|
||||
* Sets fan's mode of the HVAC without publishing it to Home Assistant.
|
||||
* This method may be useful if you want to change the mode before connection
|
||||
* with MQTT broker is acquired.
|
||||
*
|
||||
* @param mode New fan's mode.
|
||||
*/
|
||||
inline void setCurrentFanMode(const FanMode mode)
|
||||
{ _fanMode = mode; }
|
||||
|
||||
/**
|
||||
* Returns last known fan's mode of the HVAC.
|
||||
* If setFanMode method wasn't called the initial value will be returned.
|
||||
*/
|
||||
inline FanMode getCurrentFanMode() const
|
||||
{ return _fanMode; }
|
||||
|
||||
/**
|
||||
* Sets available fan modes.
|
||||
*
|
||||
* @param modes The modes to set (for example: `HAHVAC::AutoFanMode | HAHVAC::HighFanMode`).
|
||||
*/
|
||||
inline void setFanModes(const uint8_t modes)
|
||||
{ _fanModes = modes; }
|
||||
|
||||
/**
|
||||
* Sets swing mode of the HVAC without publishing it to Home Assistant.
|
||||
* This method may be useful if you want to change the mode before connection
|
||||
* with MQTT broker is acquired.
|
||||
*
|
||||
* @param mode New swing mode.
|
||||
*/
|
||||
inline void setCurrentSwingMode(const SwingMode mode)
|
||||
{ _swingMode = mode; }
|
||||
|
||||
/**
|
||||
* Returns last known swing mode of the HVAC.
|
||||
* If setSwingMode method wasn't called the initial value will be returned.
|
||||
*/
|
||||
inline SwingMode getCurrentSwingMode() const
|
||||
{ return _swingMode; }
|
||||
|
||||
/**
|
||||
* Sets available swing modes.
|
||||
*
|
||||
* @param modes The modes to set (for example: `HAHVAC::OnSwingMode`).
|
||||
*/
|
||||
inline void setSwingModes(const uint8_t modes)
|
||||
{ _swingModes = modes; }
|
||||
|
||||
/**
|
||||
* Sets mode of the HVAC without publishing it to Home Assistant.
|
||||
* This method may be useful if you want to change the mode before connection
|
||||
* with MQTT broker is acquired.
|
||||
*
|
||||
* @param mode New HVAC's mode.
|
||||
*/
|
||||
inline void setCurrentMode(const Mode mode)
|
||||
{ _mode = mode; }
|
||||
|
||||
/**
|
||||
* Returns last known mode of the HVAC.
|
||||
* If setMode method wasn't called the initial value will be returned.
|
||||
*/
|
||||
inline Mode getCurrentMode() const
|
||||
{ return _mode; }
|
||||
|
||||
/**
|
||||
* Sets available HVAC's modes.
|
||||
*
|
||||
* @param modes The modes to set (for example: `HAHVAC::CoolMode | HAHVAC::HeatMode`).
|
||||
*/
|
||||
inline void setModes(const uint8_t modes)
|
||||
{ _modes = modes; }
|
||||
|
||||
/**
|
||||
* Sets target temperature of the HVAC without publishing it to Home Assistant.
|
||||
* This method may be useful if you want to change the target before connection
|
||||
* with MQTT broker is acquired.
|
||||
*
|
||||
* @param temperature Target temperature to set.
|
||||
*/
|
||||
inline void setCurrentTargetTemperature(const HANumeric& temperature)
|
||||
{ if (temperature.getPrecision() == _precision) { _targetTemperature = temperature; } }
|
||||
|
||||
_SET_CURRENT_TARGET_TEMPERATURE_OVERLOAD(int8_t)
|
||||
_SET_CURRENT_TARGET_TEMPERATURE_OVERLOAD(int16_t)
|
||||
_SET_CURRENT_TARGET_TEMPERATURE_OVERLOAD(int32_t)
|
||||
_SET_CURRENT_TARGET_TEMPERATURE_OVERLOAD(uint8_t)
|
||||
_SET_CURRENT_TARGET_TEMPERATURE_OVERLOAD(uint16_t)
|
||||
_SET_CURRENT_TARGET_TEMPERATURE_OVERLOAD(uint32_t)
|
||||
_SET_CURRENT_TARGET_TEMPERATURE_OVERLOAD(float)
|
||||
|
||||
#ifdef __SAMD21G18A__
|
||||
_SET_CURRENT_TARGET_TEMPERATURE_OVERLOAD(int)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns last known target temperature of the HVAC.
|
||||
* If setTargetTemperature method wasn't called the initial value will be returned.
|
||||
*/
|
||||
inline const HANumeric& getCurrentTargetTemperature() const
|
||||
{ return _targetTemperature; }
|
||||
|
||||
/**
|
||||
* Sets icon of the HVAC.
|
||||
* Any icon from MaterialDesignIcons.com (for example: `mdi:home`).
|
||||
*
|
||||
* @param icon The icon name.
|
||||
*/
|
||||
inline void setIcon(const char* icon)
|
||||
{ _icon = icon; }
|
||||
|
||||
/**
|
||||
* Sets retain flag for the HVAC's command.
|
||||
* If set to `true` the command produced by Home Assistant will be retained.
|
||||
*
|
||||
* @param retain
|
||||
*/
|
||||
inline void setRetain(const bool retain)
|
||||
{ _retain = retain; }
|
||||
|
||||
/**
|
||||
* Changes the temperature unit.
|
||||
*
|
||||
* @param unit See the TemperatureUnit enum above.
|
||||
*/
|
||||
inline void setTemperatureUnit(TemperatureUnit unit)
|
||||
{ _temperatureUnit = unit; }
|
||||
|
||||
/**
|
||||
* Sets the minimum temperature that can be set from the Home Assistant panel.
|
||||
*
|
||||
* @param min The minimum value.
|
||||
*/
|
||||
inline void setMinTemp(const float min)
|
||||
{ _minTemp = HANumeric(min, _precision); }
|
||||
|
||||
/**
|
||||
* Sets the maximum temperature that can be set from the Home Assistant panel.
|
||||
*
|
||||
* @param min The maximum value.
|
||||
*/
|
||||
inline void setMaxTemp(const float max)
|
||||
{ _maxTemp = HANumeric(max, _precision); }
|
||||
|
||||
/**
|
||||
* Sets the step of the temperature that can be set from the Home Assistant panel.
|
||||
*
|
||||
* @param step The setp value. By default it's `1`.
|
||||
*/
|
||||
inline void setTempStep(const float step)
|
||||
{ _tempStep = HANumeric(step, _precision); }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the aux state command from HA is received.
|
||||
* Please note that it's not possible to register multiple callbacks for the same HVAC.
|
||||
*
|
||||
* @param callback
|
||||
* @note The aux state must be reported back to HA using the HAHVAC::setAuxState method.
|
||||
*/
|
||||
inline void onAuxStateCommand(HAHVAC_CALLBACK_BOOL(callback))
|
||||
{ _auxCallback = callback; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the power command from HA is received.
|
||||
* Please note that it's not possible to register multiple callbacks for the same HVAC.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onPowerCommand(HAHVAC_CALLBACK_BOOL(callback))
|
||||
{ _powerCallback = callback; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the fan mode command from HA is received.
|
||||
* Please note that it's not possible to register multiple callbacks for the same HVAC.
|
||||
*
|
||||
* @param callback
|
||||
* @note The fan mode must be reported back to HA using the HAHVAC::setFanMode method.
|
||||
*/
|
||||
inline void onFanModeCommand(HAHVAC_CALLBACK_FAN_MODE(callback))
|
||||
{ _fanModeCallback = callback; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the swing mode command from HA is received.
|
||||
* Please note that it's not possible to register multiple callbacks for the same HVAC.
|
||||
*
|
||||
* @param callback
|
||||
* @note The swing mode must be reported back to HA using the HAHVAC::setSwingMode method.
|
||||
*/
|
||||
inline void onSwingModeCommand(HAHVAC_CALLBACK_SWING_MODE(callback))
|
||||
{ _swingModeCallback = callback; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the HVAC mode command from HA is received.
|
||||
* Please note that it's not possible to register multiple callbacks for the same HVAC.
|
||||
*
|
||||
* @param callback
|
||||
* @note The mode must be reported back to HA using the HAHVAC::setMode method.
|
||||
*/
|
||||
inline void onModeCommand(HAHVAC_CALLBACK_MODE(callback))
|
||||
{ _modeCallback = callback; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the target temperature is set via HA panel.
|
||||
* Please note that it's not possible to register multiple callbacks for the same HVAC.
|
||||
*
|
||||
* @param callback
|
||||
* @note The target temperature must be reported back to HA using the HAHVAC::setTargetTemperature method.
|
||||
*/
|
||||
inline void onTargetTemperatureCommand(HAHVAC_CALLBACK_TARGET_TEMP(callback))
|
||||
{ _targetTemperatureCallback = callback; }
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
virtual void onMqttConnected() override;
|
||||
virtual void onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Publishes the MQTT message with the given current temperature.
|
||||
*
|
||||
* @param temperature The temperature to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishCurrentTemperature(const HANumeric& temperature);
|
||||
|
||||
/**
|
||||
* Publishes the MQTT message with the given action.
|
||||
*
|
||||
* @param action The action to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishAction(const Action action);
|
||||
|
||||
/**
|
||||
* Publishes the MQTT message with the given aux heating state.
|
||||
*
|
||||
* @param state The state to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishAuxState(const bool state);
|
||||
|
||||
/**
|
||||
* Publishes the MQTT message with the given fan mode.
|
||||
*
|
||||
* @param mode The mode to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishFanMode(const FanMode mode);
|
||||
|
||||
/**
|
||||
* Publishes the MQTT message with the given swing mode.
|
||||
*
|
||||
* @param mode The mode to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishSwingMode(const SwingMode mode);
|
||||
|
||||
/**
|
||||
* Publishes the MQTT message with the given mode.
|
||||
*
|
||||
* @param mode The mode to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishMode(const Mode mode);
|
||||
|
||||
/**
|
||||
* Publishes the MQTT message with the given target temperature.
|
||||
*
|
||||
* @param temperature The temperature to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishTargetTemperature(const HANumeric& temperature);
|
||||
|
||||
/**
|
||||
* Parses the given aux state command and executes the callback with proper value.
|
||||
*
|
||||
* @param cmd The data of the command.
|
||||
* @param length Length of the command.
|
||||
*/
|
||||
void handleAuxStateCommand(const uint8_t* cmd, const uint16_t length);
|
||||
|
||||
/**
|
||||
* Parses the given power command and executes the callback with proper value.
|
||||
*
|
||||
* @param cmd The data of the command.
|
||||
* @param length Length of the command.
|
||||
*/
|
||||
void handlePowerCommand(const uint8_t* cmd, const uint16_t length);
|
||||
|
||||
/**
|
||||
* Parses the given fan mode command and executes the callback with proper value.
|
||||
*
|
||||
* @param cmd The data of the command.
|
||||
* @param length Length of the command.
|
||||
*/
|
||||
void handleFanModeCommand(const uint8_t* cmd, const uint16_t length);
|
||||
|
||||
/**
|
||||
* Parses the given swing mode command and executes the callback with proper value.
|
||||
*
|
||||
* @param cmd The data of the command.
|
||||
* @param length Length of the command.
|
||||
*/
|
||||
void handleSwingModeCommand(const uint8_t* cmd, const uint16_t length);
|
||||
|
||||
/**
|
||||
* Parses the given HVAC's mode command and executes the callback with proper value.
|
||||
*
|
||||
* @param cmd The data of the command.
|
||||
* @param length Length of the command.
|
||||
*/
|
||||
void handleModeCommand(const uint8_t* cmd, const uint16_t length);
|
||||
|
||||
/**
|
||||
* Parses the given HVAC's target temperature command and executes the callback with proper value.
|
||||
*
|
||||
* @param cmd The data of the command.
|
||||
* @param length Length of the command.
|
||||
*/
|
||||
void handleTargetTemperatureCommand(const uint8_t* cmd, const uint16_t length);
|
||||
|
||||
/**
|
||||
* Returns progmem string representing value template for the command
|
||||
* that contains floating point numbers.
|
||||
*/
|
||||
const __FlashStringHelper* getCommandWithFloatTemplate();
|
||||
|
||||
/// Features enabled for the HVAC.
|
||||
const uint16_t _features;
|
||||
|
||||
/// The precision of temperatures. By default it's `HANumber::PrecisionP1`.
|
||||
const NumberPrecision _precision;
|
||||
|
||||
/// The icon of the button. It can be nullptr.
|
||||
const char* _icon;
|
||||
|
||||
/// The retain flag for the HA commands.
|
||||
bool _retain;
|
||||
|
||||
/// The current temperature of the HVAC. By default it's not set.
|
||||
HANumeric _CURRENT_TEMPerature;
|
||||
|
||||
/// The current action of the HVAC. By default it's `HAHVAC::UnknownAction`.
|
||||
Action _action;
|
||||
|
||||
/// The temperature unit for the HVAC. By default it's `HAHVAC::DefaultUnit`.
|
||||
TemperatureUnit _temperatureUnit;
|
||||
|
||||
/// The minimum temperature that can be set.
|
||||
HANumeric _minTemp;
|
||||
|
||||
/// The maximum temperature that can be set.
|
||||
HANumeric _maxTemp;
|
||||
|
||||
/// The step of the temperature that can be set.
|
||||
HANumeric _tempStep;
|
||||
|
||||
/// Callback that will be called when the aux state command is received from the HA.
|
||||
HAHVAC_CALLBACK_BOOL(_auxCallback);
|
||||
|
||||
/// The state of the aux heating. By default it's `false`.
|
||||
bool _auxState;
|
||||
|
||||
/// Callback that will be called when the power command is received from the HA.
|
||||
HAHVAC_CALLBACK_BOOL(_powerCallback);
|
||||
|
||||
/// The current mode of the fan. By default it's `HAHVAC::UnknownFanMode`.
|
||||
FanMode _fanMode;
|
||||
|
||||
/// The supported fan modes. By default it's `HAHVAC::DefaultFanModes`.
|
||||
uint8_t _fanModes;
|
||||
|
||||
/// The serializer for the fan modes. It's `nullptr` if the fan feature is disabled.
|
||||
HASerializerArray* _fanModesSerializer;
|
||||
|
||||
/// Callback that will be called when the fan mode command is received from the HA.
|
||||
HAHVAC_CALLBACK_FAN_MODE(_fanModeCallback);
|
||||
|
||||
/// The current swing mode. By default it's `HAHVAC::UnknownSwingMode`.
|
||||
SwingMode _swingMode;
|
||||
|
||||
/// The supported swing modes. By default it's `HAHVAC::DefaultSwingModes`.
|
||||
uint8_t _swingModes;
|
||||
|
||||
/// The serializer for the swing modes. It's `nullptr` if the swing feature is disabled.
|
||||
HASerializerArray* _swingModesSerializer;
|
||||
|
||||
/// Callback that will be called when the swing mode command is received from the HA.
|
||||
HAHVAC_CALLBACK_SWING_MODE(_swingModeCallback);
|
||||
|
||||
/// The current mode. By default it's `HAHVAC::UnknownMode`.
|
||||
Mode _mode;
|
||||
|
||||
/// The supported modes. By default it's `HAHVAC::DefaultModes`.
|
||||
uint8_t _modes;
|
||||
|
||||
/// The serializer for the modes. It's `nullptr` if the modes feature is disabled.
|
||||
HASerializerArray* _modesSerializer;
|
||||
|
||||
/// Callback that will be called when the mode command is received from the HA.
|
||||
HAHVAC_CALLBACK_MODE(_modeCallback);
|
||||
|
||||
/// The target temperature of the HVAC. By default it's not set.
|
||||
HANumeric _targetTemperature;
|
||||
|
||||
/// Callback that will be called when the target temperature is changed via the HA panel.
|
||||
HAHVAC_CALLBACK_TARGET_TEMP(_targetTemperatureCallback);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
372
lib/home-assistant-integration/src/device-types/HALight.cpp
Normal file
372
lib/home-assistant-integration/src/device-types/HALight.cpp
Normal file
@@ -0,0 +1,372 @@
|
||||
#include "HALight.h"
|
||||
#ifndef EX_ARDUINOHA_LIGHT
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
const uint8_t HALight::RGBStringMaxLength = 3*4; // 4 characters per color
|
||||
|
||||
void HALight::RGBColor::fromBuffer(const uint8_t* data, const uint16_t length)
|
||||
{
|
||||
if (length > RGBStringMaxLength) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t firstCommaPos = 0;
|
||||
uint8_t secondCommaPos = 0;
|
||||
|
||||
for (uint8_t i = 0; i < length; i++) {
|
||||
if (data[i] == ',') {
|
||||
if (firstCommaPos == 0) {
|
||||
firstCommaPos = i;
|
||||
} else if (secondCommaPos == 0) {
|
||||
secondCommaPos = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (firstCommaPos == 0 || secondCommaPos == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t redLen = firstCommaPos;
|
||||
const uint8_t greenLen = secondCommaPos - firstCommaPos - 1; // minus comma
|
||||
const uint8_t blueLen = length - redLen - greenLen - 2; // minus two commas
|
||||
|
||||
const HANumeric& r = HANumeric::fromStr(data, redLen);
|
||||
const HANumeric& g = HANumeric::fromStr(&data[redLen + 1], greenLen);
|
||||
const HANumeric& b = HANumeric::fromStr(&data[redLen + greenLen + 2], blueLen);
|
||||
|
||||
if (r.isUInt8() && g.isUInt8() && b.isUInt8()) {
|
||||
red = r.toUInt8();
|
||||
green = g.toUInt8();
|
||||
blue = b.toUInt8();
|
||||
isSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
HALight::HALight(const char* uniqueId, const uint8_t features) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentLight), uniqueId),
|
||||
_features(features),
|
||||
_icon(nullptr),
|
||||
_retain(false),
|
||||
_optimistic(false),
|
||||
_brightnessScale(),
|
||||
_currentState(false),
|
||||
_BRIGHTNESS(0),
|
||||
_minMireds(),
|
||||
_maxMireds(),
|
||||
_currentColorTemperature(0),
|
||||
_currentRGBColor(),
|
||||
_stateCallback(nullptr),
|
||||
_brightnessCallback(nullptr),
|
||||
_colorTemperatureCallback(nullptr),
|
||||
_rgbColorCallback(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool HALight::setState(const bool state, const bool force)
|
||||
{
|
||||
if (!force && state == _currentState) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishState(state)) {
|
||||
_currentState = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HALight::setBrightness(const uint8_t brightness, const bool force)
|
||||
{
|
||||
if (!force && brightness == _BRIGHTNESS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishBrightness(brightness)) {
|
||||
_BRIGHTNESS = brightness;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HALight::setColorTemperature(const uint16_t temperature, const bool force)
|
||||
{
|
||||
if (!force && temperature == _currentColorTemperature) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishColorTemperature(temperature)) {
|
||||
_currentColorTemperature = temperature;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HALight::setRGBColor(const RGBColor& color, const bool force)
|
||||
{
|
||||
if (!force && color == _currentRGBColor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishRGBColor(color)) {
|
||||
_currentRGBColor = color;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HALight::buildSerializer()
|
||||
{
|
||||
if (_serializer || !uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 18); // 18 - max properties nb
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAUniqueIdProperty), _uniqueId);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
|
||||
if (_retain) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HARetainProperty),
|
||||
&_retain,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_optimistic) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAOptimisticProperty),
|
||||
&_optimistic,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_features & BrightnessFeature) {
|
||||
_serializer->topic(AHATOFSTR(HABrightnessStateTopic));
|
||||
_serializer->topic(AHATOFSTR(HABrightnessCommandTopic));
|
||||
|
||||
if (_brightnessScale.isSet()) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HABrightnessScaleProperty),
|
||||
&_brightnessScale,
|
||||
HASerializer::NumberPropertyType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (_features & ColorTemperatureFeature) {
|
||||
_serializer->topic(AHATOFSTR(HAColorTemperatureStateTopic));
|
||||
_serializer->topic(AHATOFSTR(HAColorTemperatureCommandTopic));
|
||||
|
||||
if (_minMireds.isSet()) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAMinMiredsProperty),
|
||||
&_minMireds,
|
||||
HASerializer::NumberPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_maxMireds.isSet()) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAMaxMiredsProperty),
|
||||
&_maxMireds,
|
||||
HASerializer::NumberPropertyType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (_features & RGBFeature) {
|
||||
_serializer->topic(AHATOFSTR(HARGBCommandTopic));
|
||||
_serializer->topic(AHATOFSTR(HARGBStateTopic));
|
||||
}
|
||||
|
||||
_serializer->set(HASerializer::WithDevice);
|
||||
_serializer->set(HASerializer::WithAvailability);
|
||||
_serializer->topic(AHATOFSTR(HAStateTopic));
|
||||
_serializer->topic(AHATOFSTR(HACommandTopic));
|
||||
}
|
||||
|
||||
void HALight::onMqttConnected()
|
||||
{
|
||||
if (!uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishAvailability();
|
||||
|
||||
if (!_retain) {
|
||||
publishState(_currentState);
|
||||
publishBrightness(_BRIGHTNESS);
|
||||
publishColorTemperature(_currentColorTemperature);
|
||||
publishRGBColor(_currentRGBColor);
|
||||
}
|
||||
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HACommandTopic));
|
||||
|
||||
if (_features & BrightnessFeature) {
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HABrightnessCommandTopic));
|
||||
}
|
||||
|
||||
if (_features & ColorTemperatureFeature) {
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HAColorTemperatureCommandTopic));
|
||||
}
|
||||
|
||||
if (_features & RGBFeature) {
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HARGBCommandTopic));
|
||||
}
|
||||
}
|
||||
|
||||
void HALight::onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
)
|
||||
{
|
||||
if (HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HACommandTopic)
|
||||
)) {
|
||||
handleStateCommand(payload, length);
|
||||
} else if (HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HABrightnessCommandTopic)
|
||||
)) {
|
||||
handleBrightnessCommand(payload, length);
|
||||
} else if (HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HAColorTemperatureCommandTopic)
|
||||
)) {
|
||||
handleColorTemperatureCommand(payload, length);
|
||||
} else if (
|
||||
HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HARGBCommandTopic)
|
||||
)
|
||||
) {
|
||||
handleRGBCommand(payload, length);
|
||||
}
|
||||
}
|
||||
|
||||
bool HALight::publishState(const bool state)
|
||||
{
|
||||
return publishOnDataTopic(
|
||||
AHATOFSTR(HAStateTopic),
|
||||
AHATOFSTR(state ? HAStateOn : HAStateOff),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
bool HALight::publishBrightness(const uint8_t brightness)
|
||||
{
|
||||
if (!(_features & BrightnessFeature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char str[3 + 1] = {0}; // uint8_t digits with null terminator
|
||||
HANumeric(brightness, 0).toStr(str);
|
||||
|
||||
return publishOnDataTopic(AHATOFSTR(HABrightnessStateTopic), str, true);
|
||||
}
|
||||
|
||||
bool HALight::publishColorTemperature(const uint16_t temperature)
|
||||
{
|
||||
if (!(_features & ColorTemperatureFeature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char str[5 + 1] = {0}; // uint16_t digits with null terminator
|
||||
HANumeric(temperature, 0).toStr(str);
|
||||
|
||||
return publishOnDataTopic(AHATOFSTR(HAColorTemperatureStateTopic), str, true);
|
||||
}
|
||||
|
||||
bool HALight::publishRGBColor(const RGBColor& color)
|
||||
{
|
||||
if (!(_features & RGBFeature) || !color.isSet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char str[RGBStringMaxLength] = {0};
|
||||
uint16_t len = 0;
|
||||
|
||||
// append red color with comma
|
||||
len += HANumeric(color.red, 0).toStr(&str[0]);
|
||||
str[len++] = ',';
|
||||
|
||||
// append green color with comma
|
||||
len += HANumeric(color.green, 0).toStr(&str[len]);
|
||||
str[len++] = ',';
|
||||
|
||||
// append blue color
|
||||
HANumeric(color.blue, 0).toStr(&str[len]);
|
||||
|
||||
return publishOnDataTopic(AHATOFSTR(HARGBStateTopic), str, true);
|
||||
}
|
||||
|
||||
void HALight::handleStateCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
(void)cmd;
|
||||
|
||||
if (!_stateCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool state = length == strlen_P(HAStateOn);
|
||||
_stateCallback(state, this);
|
||||
}
|
||||
|
||||
void HALight::handleBrightnessCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_brightnessCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
const HANumeric& number = HANumeric::fromStr(cmd, length);
|
||||
if (number.isUInt8()) {
|
||||
_brightnessCallback(number.toUInt8(), this);
|
||||
}
|
||||
}
|
||||
|
||||
void HALight::handleColorTemperatureCommand(
|
||||
const uint8_t* cmd,
|
||||
const uint16_t length
|
||||
)
|
||||
{
|
||||
if (!_colorTemperatureCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
const HANumeric& number = HANumeric::fromStr(cmd, length);
|
||||
if (number.isUInt16()) {
|
||||
_colorTemperatureCallback(number.toUInt16(), this);
|
||||
}
|
||||
}
|
||||
|
||||
void HALight::handleRGBCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_rgbColorCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
RGBColor color;
|
||||
color.fromBuffer(cmd, length);
|
||||
|
||||
if (color.isSet) {
|
||||
_rgbColorCallback(color, this);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
421
lib/home-assistant-integration/src/device-types/HALight.h
Normal file
421
lib/home-assistant-integration/src/device-types/HALight.h
Normal file
@@ -0,0 +1,421 @@
|
||||
#ifndef AHA_HALIGHT_H
|
||||
#define AHA_HALIGHT_H
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
#include "../utils/HANumeric.h"
|
||||
|
||||
#ifndef EX_ARDUINOHA_LIGHT
|
||||
|
||||
#define HALIGHT_STATE_CALLBACK(name) void (*name)(bool state, HALight* sender)
|
||||
#define HALIGHT_BRIGHTNESS_CALLBACK(name) void (*name)(uint8_t brightness, HALight* sender)
|
||||
#define HALIGHT_COLOR_TEMP_CALLBACK(name) void (*name)(uint16_t temperature, HALight* sender)
|
||||
#define HALIGHT_RGB_COLOR_CALLBACK(name) void (*name)(HALight::RGBColor color, HALight* sender)
|
||||
|
||||
/**
|
||||
* HALight allows adding a controllable light in the Home Assistant panel.
|
||||
* The library supports only the state, brightness, color temperature and RGB color.
|
||||
* If you need more features please open a new GitHub issue.
|
||||
*
|
||||
* @note
|
||||
* You can find more information about this entity in the Home Assistant documentation:
|
||||
* https://www.home-assistant.io/integrations/light.mqtt/
|
||||
*/
|
||||
class HALight : public HABaseDeviceType
|
||||
{
|
||||
public:
|
||||
static const uint8_t RGBStringMaxLength;
|
||||
|
||||
enum Features {
|
||||
DefaultFeatures = 0,
|
||||
BrightnessFeature = 1,
|
||||
ColorTemperatureFeature = 2,
|
||||
RGBFeature = 4
|
||||
};
|
||||
|
||||
struct RGBColor {
|
||||
uint8_t red;
|
||||
uint8_t green;
|
||||
uint8_t blue;
|
||||
bool isSet;
|
||||
|
||||
RGBColor() :
|
||||
red(0), green(0), blue(0), isSet(false) { }
|
||||
|
||||
RGBColor(uint8_t r, uint8_t g, uint8_t b) :
|
||||
red(r), green(g), blue(b), isSet(true) { }
|
||||
|
||||
void operator= (const RGBColor& a) {
|
||||
red = a.red;
|
||||
green = a.green;
|
||||
blue = a.blue;
|
||||
isSet = a.isSet;
|
||||
}
|
||||
|
||||
bool operator== (const RGBColor& a) const {
|
||||
return (
|
||||
red == a.red &&
|
||||
green == a.green &&
|
||||
blue == a.blue
|
||||
);
|
||||
}
|
||||
|
||||
bool operator!= (const RGBColor& a) const {
|
||||
return (
|
||||
red != a.red ||
|
||||
green != a.green ||
|
||||
blue != a.blue
|
||||
);
|
||||
}
|
||||
|
||||
void fromBuffer(const uint8_t* data, const uint16_t length);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param uniqueId The unique ID of the light. It needs to be unique in a scope of your device.
|
||||
* @param features Features that should be enabled for the light.
|
||||
* You can enable multiple features by using OR bitwise operator, for example:
|
||||
* `HALight::BrightnessFeature | HALight::ColorTemperatureFeature`
|
||||
*/
|
||||
HALight(const char* uniqueId, const uint8_t features = DefaultFeatures);
|
||||
|
||||
/**
|
||||
* Changes state of the light 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 light.
|
||||
* @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(const bool state, const bool force = false);
|
||||
|
||||
/**
|
||||
* Changes the brightness of the light 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 brightness The new brightness of the light.
|
||||
* @param force Forces to update the value without comparing it to a previous known value.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setBrightness(const uint8_t brightness, const bool force = false);
|
||||
|
||||
/**
|
||||
* Changes the color temperature of the light 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 temperature The new color temperature of the light.
|
||||
* @param force Forces to update the value without comparing it to a previous known value.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setColorTemperature(const uint16_t temperature, const bool force = false);
|
||||
|
||||
/**
|
||||
* Changes the RGB color of the light and publishes MQTT message.
|
||||
* Please note that if a new color is the same as previous one,
|
||||
* the MQTT message won't be published.
|
||||
*
|
||||
* @param color The new RGB color of the light.
|
||||
* @param force Forces to update the value without comparing it to a previous known value.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setRGBColor(const RGBColor& color, const bool force = false);
|
||||
|
||||
/**
|
||||
* Alias for `setState(true)`.
|
||||
*/
|
||||
inline bool turnOn()
|
||||
{ return setState(true); }
|
||||
|
||||
/**
|
||||
* Alias for `setState(false)`.
|
||||
*/
|
||||
inline bool turnOff()
|
||||
{ return setState(false); }
|
||||
|
||||
/**
|
||||
* Sets current state of the light without publishing it 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 light.
|
||||
*/
|
||||
inline void setCurrentState(const bool state)
|
||||
{ _currentState = state; }
|
||||
|
||||
/**
|
||||
* Returns last known state of the light.
|
||||
* By default it's `false`.
|
||||
*/
|
||||
inline bool getCurrentState() const
|
||||
{ return _currentState; }
|
||||
|
||||
/**
|
||||
* Sets the current brightness of the light without pushing the value to Home Assistant.
|
||||
* This method may be useful if you want to change the brightness before the connection
|
||||
* with the MQTT broker is acquired.
|
||||
*
|
||||
* @param brightness The new brightness of the light.
|
||||
*/
|
||||
inline void setBRIGHTNESS(const uint8_t brightness)
|
||||
{ _BRIGHTNESS = brightness; }
|
||||
|
||||
/**
|
||||
* Returns the last known brightness of the light.
|
||||
* By default brightness is set to `0`.
|
||||
*/
|
||||
inline uint8_t getBRIGHTNESS() const
|
||||
{ return _BRIGHTNESS; }
|
||||
|
||||
/**
|
||||
* Sets the current color temperature of the light without pushing the value to Home Assistant.
|
||||
* This method may be useful if you want to change the color temperature before the connection
|
||||
* with the MQTT broker is acquired.
|
||||
*
|
||||
* @param colorTemp The new color temperature (mireds).
|
||||
*/
|
||||
inline void setCurrentColorTemperature(const uint16_t temperature)
|
||||
{ _currentColorTemperature = temperature; }
|
||||
|
||||
/**
|
||||
* Returns the last known color temperature of the light.
|
||||
* By default temperature is set to `0`.
|
||||
*/
|
||||
inline uint16_t getCurrentColorTemperature() const
|
||||
{ return _currentColorTemperature; }
|
||||
|
||||
/**
|
||||
* Sets the current RGB color of the light without pushing the value to Home Assistant.
|
||||
* This method may be useful if you want to change the color before the connection
|
||||
* with the MQTT broker is acquired.
|
||||
*
|
||||
* @param color The new RGB color.
|
||||
*/
|
||||
inline void setCurrentRGBColor(const RGBColor& color)
|
||||
{ _currentRGBColor = color; }
|
||||
|
||||
/**
|
||||
* Returns the last known RGB color of the light.
|
||||
* By default the RGB color is set to `0,0,0`.
|
||||
*/
|
||||
inline const RGBColor& getCurrentRGBColor() const
|
||||
{ return _currentRGBColor; }
|
||||
|
||||
/**
|
||||
* Sets icon of the light.
|
||||
* Any icon from MaterialDesignIcons.com (for example: `mdi:home`).
|
||||
*
|
||||
* @param icon The icon name.
|
||||
*/
|
||||
inline void setIcon(const char* icon)
|
||||
{ _icon = icon; }
|
||||
|
||||
/**
|
||||
* Sets retain flag for the light's command.
|
||||
* If set to `true` the command produced by Home Assistant will be retained.
|
||||
*
|
||||
* @param retain
|
||||
*/
|
||||
inline void setRetain(const bool retain)
|
||||
{ _retain = retain; }
|
||||
|
||||
/**
|
||||
* Sets optimistic flag for the light state.
|
||||
* In this mode the light state doesn't need to be reported back to the HA panel when a command is received.
|
||||
* By default the optimistic mode is disabled.
|
||||
*
|
||||
* @param optimistic The optimistic mode (`true` - enabled, `false` - disabled).
|
||||
*/
|
||||
inline void setOptimistic(const bool optimistic)
|
||||
{ _optimistic = optimistic; }
|
||||
|
||||
/**
|
||||
* Sets the maximum brightness value that can be set via HA panel.
|
||||
* By default it's `255`.
|
||||
*
|
||||
* @param scale The maximum value of the brightness.
|
||||
*/
|
||||
inline void setBrightnessScale(const uint8_t scale)
|
||||
{ _brightnessScale.setBaseValue(scale); }
|
||||
|
||||
/**
|
||||
* Sets the minimum color temperature (mireds) value that can be set via HA panel.
|
||||
* By default it's `153`.
|
||||
*
|
||||
* @param mireds The minimum value of the brightness.
|
||||
*/
|
||||
inline void setMinMireds(const uint16_t mireds)
|
||||
{ _minMireds.setBaseValue(mireds); }
|
||||
|
||||
/**
|
||||
* Sets the maximum color temperature (mireds) value that can be set via HA panel.
|
||||
* By default it's `500`.
|
||||
*
|
||||
* @param mireds The maximum value of the brightness.
|
||||
*/
|
||||
inline void setMaxMireds(const uint16_t mireds)
|
||||
{ _maxMireds.setBaseValue(mireds); }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the state command from HA is received.
|
||||
* Please note that it's not possible to register multiple callbacks for the same light.
|
||||
*
|
||||
* @param callback
|
||||
* @note In non-optimistic mode, the state must be reported back to HA using the HALight::setState method.
|
||||
*/
|
||||
inline void onStateCommand(HALIGHT_STATE_CALLBACK(callback))
|
||||
{ _stateCallback = callback; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the brightness command from HA is received.
|
||||
* Please note that it's not possible to register multiple callbacks for the same light.
|
||||
*
|
||||
* @param callback
|
||||
* @note In non-optimistic mode, the brightness must be reported back to HA using the HALight::setBrightness method.
|
||||
*/
|
||||
inline void onBrightnessCommand(HALIGHT_BRIGHTNESS_CALLBACK(callback))
|
||||
{ _brightnessCallback = callback; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the color temperature command from HA is received.
|
||||
* Please note that it's not possible to register multiple callbacks for the same light.
|
||||
*
|
||||
* @param callback
|
||||
* @note In non-optimistic mode, the color temperature must be reported back to HA using the HALight::setColorTemperature method.
|
||||
*/
|
||||
inline void onColorTemperatureCommand(HALIGHT_COLOR_TEMP_CALLBACK(callback))
|
||||
{ _colorTemperatureCallback = callback; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the RGB color command from HA is received.
|
||||
* Please note that it's not possible to register multiple callbacks for the same light.
|
||||
*
|
||||
* @param callback
|
||||
* @note In non-optimistic mode, the color must be reported back to HA using the HALight::setRGBColor method.
|
||||
*/
|
||||
inline void onRGBColorCommand(HALIGHT_RGB_COLOR_CALLBACK(callback))
|
||||
{ _rgbColorCallback = callback; }
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
virtual void onMqttConnected() override;
|
||||
virtual void onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Publishes the MQTT message with the given state.
|
||||
*
|
||||
* @param state The state to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishState(const bool state);
|
||||
|
||||
/**
|
||||
* Publishes the MQTT message with the given brightness.
|
||||
*
|
||||
* @param brightness The brightness to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishBrightness(const uint8_t brightness);
|
||||
|
||||
/**
|
||||
* Publishes the MQTT message with the given color temperature (mireds).
|
||||
*
|
||||
* @param temperature The color temperature to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishColorTemperature(const uint16_t temperature);
|
||||
|
||||
/**
|
||||
* Publishes the MQTT message with the given RGB color.
|
||||
*
|
||||
* @param color The color to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishRGBColor(const RGBColor& color);
|
||||
|
||||
/**
|
||||
* Parses the given state command and executes the callback with proper value.
|
||||
*
|
||||
* @param cmd The data of the command.
|
||||
* @param length Length of the command.
|
||||
*/
|
||||
void handleStateCommand(const uint8_t* cmd, const uint16_t length);
|
||||
|
||||
/**
|
||||
* Parses the given brightness command and executes the callback with proper value.
|
||||
*
|
||||
* @param cmd The data of the command.
|
||||
* @param length Length of the command.
|
||||
*/
|
||||
void handleBrightnessCommand(const uint8_t* cmd, const uint16_t length);
|
||||
|
||||
/**
|
||||
* Parses the given color temperature command and executes the callback with proper value.
|
||||
*
|
||||
* @param cmd The data of the command.
|
||||
* @param length Length of the command.
|
||||
*/
|
||||
void handleColorTemperatureCommand(const uint8_t* cmd, const uint16_t length);
|
||||
|
||||
/**
|
||||
* Parses the given RGB color command and executes the callback with proper value.
|
||||
*
|
||||
* @param cmd The data of the command.
|
||||
* @param length Length of the command.
|
||||
*/
|
||||
void handleRGBCommand(const uint8_t* cmd, const uint16_t length);
|
||||
|
||||
/// Features enabled for the light.
|
||||
const uint8_t _features;
|
||||
|
||||
/// The icon of the button. It can be nullptr.
|
||||
const char* _icon;
|
||||
|
||||
/// The retain flag for the HA commands.
|
||||
bool _retain;
|
||||
|
||||
/// The optimistic mode of the light (`true` - enabled, `false` - disabled).
|
||||
bool _optimistic;
|
||||
|
||||
/// The maximum value of the brightness. By default it's 255.
|
||||
HANumeric _brightnessScale;
|
||||
|
||||
/// The current state of the light. By default it's `false`.
|
||||
bool _currentState;
|
||||
|
||||
/// The current brightness of the light. By default it's `0`.
|
||||
uint8_t _BRIGHTNESS;
|
||||
|
||||
/// The minimum color temperature (mireds). By default the value is not set.
|
||||
HANumeric _minMireds;
|
||||
|
||||
/// The maximum color temperature (mireds). By default the value is not set.
|
||||
HANumeric _maxMireds;
|
||||
|
||||
/// The current color temperature (mireds). By default the value is not set.
|
||||
uint16_t _currentColorTemperature;
|
||||
|
||||
/// The current RBB color. By default the value is not set.
|
||||
RGBColor _currentRGBColor;
|
||||
|
||||
/// The callback that will be called when the state command is received from the HA.
|
||||
HALIGHT_STATE_CALLBACK(_stateCallback);
|
||||
|
||||
/// The callback that will be called when the brightness command is received from the HA.
|
||||
HALIGHT_BRIGHTNESS_CALLBACK(_brightnessCallback);
|
||||
|
||||
/// The callback that will be called when the color temperature command is received from the HA.
|
||||
HALIGHT_COLOR_TEMP_CALLBACK(_colorTemperatureCallback);
|
||||
|
||||
/// The callback that will be called when the RGB command is received from the HA.
|
||||
HALIGHT_RGB_COLOR_CALLBACK(_rgbColorCallback);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
124
lib/home-assistant-integration/src/device-types/HALock.cpp
Normal file
124
lib/home-assistant-integration/src/device-types/HALock.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
#include "HALock.h"
|
||||
#ifndef EX_ARDUINOHA_LOCK
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
HALock::HALock(const char* uniqueId) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentLock), uniqueId),
|
||||
_icon(nullptr),
|
||||
_retain(false),
|
||||
_optimistic(false),
|
||||
_currentState(StateUnknown),
|
||||
_commandCallback(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool HALock::setState(const LockState state, const bool force)
|
||||
{
|
||||
if (!force && state == _currentState) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishState(state)) {
|
||||
_currentState = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HALock::buildSerializer()
|
||||
{
|
||||
if (_serializer || !uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 9); // 9 - max properties nb
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAUniqueIdProperty), _uniqueId);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
|
||||
if (_retain) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HARetainProperty),
|
||||
&_retain,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_optimistic) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAOptimisticProperty),
|
||||
&_optimistic,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
_serializer->set(HASerializer::WithDevice);
|
||||
_serializer->set(HASerializer::WithAvailability);
|
||||
_serializer->topic(AHATOFSTR(HAStateTopic));
|
||||
_serializer->topic(AHATOFSTR(HACommandTopic));
|
||||
}
|
||||
|
||||
void HALock::onMqttConnected()
|
||||
{
|
||||
if (!uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishAvailability();
|
||||
|
||||
if (!_retain) {
|
||||
publishState(_currentState);
|
||||
}
|
||||
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HACommandTopic));
|
||||
}
|
||||
|
||||
void HALock::onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
)
|
||||
{
|
||||
if (HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HACommandTopic)
|
||||
)) {
|
||||
handleCommand(payload, length);
|
||||
}
|
||||
}
|
||||
|
||||
bool HALock::publishState(const LockState state)
|
||||
{
|
||||
if (state == StateUnknown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return publishOnDataTopic(
|
||||
AHATOFSTR(HAStateTopic),
|
||||
AHATOFSTR(state == StateLocked ? HAStateLocked : HAStateUnlocked),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
void HALock::handleCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_commandCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp_P(cmd, HALockCommand, length) == 0) {
|
||||
_commandCallback(CommandLock, this);
|
||||
} else if (memcmp_P(cmd, HAUnlockCommand, length) == 0) {
|
||||
_commandCallback(CommandUnlock, this);
|
||||
} else if (memcmp_P(cmd, HAOpenCommand, length) == 0) {
|
||||
_commandCallback(CommandOpen, this);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
148
lib/home-assistant-integration/src/device-types/HALock.h
Normal file
148
lib/home-assistant-integration/src/device-types/HALock.h
Normal file
@@ -0,0 +1,148 @@
|
||||
#ifndef AHA_HALOCK_H
|
||||
#define AHA_HALOCK_H
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
|
||||
#ifndef EX_ARDUINOHA_LOCK
|
||||
|
||||
#define HALOCK_CALLBACK(name) void (*name)(LockCommand command, HALock* sender)
|
||||
|
||||
/**
|
||||
* HALock allows to implement a custom lock (for example: door lock)
|
||||
* that can be controlled from the Home Assistant panel.
|
||||
*
|
||||
* @note
|
||||
* You can find more information about this entity in the Home Assistant documentation:
|
||||
* https://www.home-assistant.io/integrations/lock.mqtt/
|
||||
*/
|
||||
class HALock : public HABaseDeviceType
|
||||
{
|
||||
public:
|
||||
/// Available states of the lock that can be reported to the HA panel.
|
||||
enum LockState {
|
||||
StateUnknown = 0,
|
||||
StateLocked,
|
||||
StateUnlocked
|
||||
};
|
||||
|
||||
/// Commands that will be produced by the HA panel.
|
||||
enum LockCommand {
|
||||
CommandLock = 1,
|
||||
CommandUnlock,
|
||||
CommandOpen
|
||||
};
|
||||
|
||||
/**
|
||||
* @param uniqueId The unique ID of the lock. It needs to be unique in a scope of your device.
|
||||
*/
|
||||
HALock(const char* uniqueId);
|
||||
|
||||
/**
|
||||
* Changes state of the lock 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 lock.
|
||||
* @param force Forces to update state without comparing it to a previous known state.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setState(const LockState state, const bool force = false);
|
||||
|
||||
/**
|
||||
* Sets current state of the lock without publishing it 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 lock.
|
||||
*/
|
||||
inline void setCurrentState(const LockState state)
|
||||
{ _currentState = state; }
|
||||
|
||||
/**
|
||||
* Returns last known state of the lock.
|
||||
* If setState method wasn't called the initial value will be returned.
|
||||
*/
|
||||
inline LockState getCurrentState() const
|
||||
{ return _currentState; }
|
||||
|
||||
/**
|
||||
* Sets icon of the lock.
|
||||
* Any icon from MaterialDesignIcons.com (for example: `mdi:home`).
|
||||
*
|
||||
* @param icon The icon name.
|
||||
*/
|
||||
inline void setIcon(const char* icon)
|
||||
{ _icon = icon; }
|
||||
|
||||
/**
|
||||
* Sets retain flag for the lock's command.
|
||||
* If set to `true` the command produced by Home Assistant will be retained.
|
||||
*
|
||||
* @param retain
|
||||
*/
|
||||
inline void setRetain(const bool retain)
|
||||
{ _retain = retain; }
|
||||
|
||||
/**
|
||||
* Sets optimistic flag for the lock state.
|
||||
* In this mode the lock state doesn't need to be reported back to the HA panel when a command is received.
|
||||
* By default the optimistic mode is disabled.
|
||||
*
|
||||
* @param optimistic The optimistic mode (`true` - enabled, `false` - disabled).
|
||||
*/
|
||||
inline void setOptimistic(const bool optimistic)
|
||||
{ _optimistic = optimistic; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the lock/unlock/open command from the HA is received.
|
||||
* Please note that it's not possible to register multiple callbacks for the same lock.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onCommand(HALOCK_CALLBACK(callback))
|
||||
{ _commandCallback = callback; }
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
virtual void onMqttConnected() override;
|
||||
virtual void onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Publishes the MQTT message with the given state.
|
||||
*
|
||||
* @param state The state to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishState(const LockState state);
|
||||
|
||||
/**
|
||||
* Parses the given command and executes the lock's callback with proper enum's property.
|
||||
*
|
||||
* @param cmd The data of the command.
|
||||
* @param length Length of the command.
|
||||
*/
|
||||
void handleCommand(const uint8_t* cmd, const uint16_t length);
|
||||
|
||||
/// The icon of the lock. It can be nullptr.
|
||||
const char* _icon;
|
||||
|
||||
/// The retain flag for the HA commands.
|
||||
bool _retain;
|
||||
|
||||
/// The optimistic mode of the lock (`true` - enabled, `false` - disabled).
|
||||
bool _optimistic;
|
||||
|
||||
/// The current state of the lock. By default it's `HALock::StateUnknown`.
|
||||
LockState _currentState;
|
||||
|
||||
/// The callback that will be called when lock/unlock/open command is received from the HA.
|
||||
HALOCK_CALLBACK(_commandCallback);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
214
lib/home-assistant-integration/src/device-types/HANumber.cpp
Normal file
214
lib/home-assistant-integration/src/device-types/HANumber.cpp
Normal file
@@ -0,0 +1,214 @@
|
||||
#include "HANumber.h"
|
||||
#ifndef EX_ARDUINOHA_NUMBER
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
HANumber::HANumber(const char* uniqueId, const NumberPrecision precision) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentNumber), uniqueId),
|
||||
_precision(precision),
|
||||
_class(nullptr),
|
||||
_icon(nullptr),
|
||||
_retain(false),
|
||||
_optimistic(false),
|
||||
_mode(ModeAuto),
|
||||
_unitOfMeasurement(nullptr),
|
||||
_minValue(),
|
||||
_maxValue(),
|
||||
_step(),
|
||||
_currentState(),
|
||||
_commandCallback(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool HANumber::setState(const HANumeric& state, const bool force)
|
||||
{
|
||||
if (!force && state == _currentState) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishState(state)) {
|
||||
_currentState = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HANumber::buildSerializer()
|
||||
{
|
||||
if (_serializer || !uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 15); // 15 - max properties nb
|
||||
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAUniqueIdProperty), _uniqueId);
|
||||
_serializer->set(AHATOFSTR(HADeviceClassProperty), _class);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
_serializer->set(AHATOFSTR(HAUnitOfMeasurementProperty), _unitOfMeasurement);
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAModeProperty),
|
||||
getModeProperty(),
|
||||
HASerializer::ProgmemPropertyValue
|
||||
);
|
||||
_serializer->set(
|
||||
AHATOFSTR(HACommandTemplateProperty),
|
||||
getCommandTemplate(),
|
||||
HASerializer::ProgmemPropertyValue
|
||||
);
|
||||
|
||||
if (_minValue.isSet()) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAMinProperty),
|
||||
&_minValue,
|
||||
HASerializer::NumberPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_maxValue.isSet()) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAMaxProperty),
|
||||
&_maxValue,
|
||||
HASerializer::NumberPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_step.isSet()) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAStepProperty),
|
||||
&_step,
|
||||
HASerializer::NumberPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_retain) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HARetainProperty),
|
||||
&_retain,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_optimistic) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAOptimisticProperty),
|
||||
&_optimistic,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
_serializer->set(HASerializer::WithDevice);
|
||||
_serializer->set(HASerializer::WithAvailability);
|
||||
_serializer->topic(AHATOFSTR(HAStateTopic));
|
||||
_serializer->topic(AHATOFSTR(HACommandTopic));
|
||||
}
|
||||
|
||||
void HANumber::onMqttConnected()
|
||||
{
|
||||
if (!uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishAvailability();
|
||||
|
||||
if (!_retain) {
|
||||
publishState(_currentState);
|
||||
}
|
||||
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HACommandTopic));
|
||||
}
|
||||
|
||||
void HANumber::onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
)
|
||||
{
|
||||
if (HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HACommandTopic)
|
||||
)) {
|
||||
handleCommand(payload, length);
|
||||
}
|
||||
}
|
||||
|
||||
bool HANumber::publishState(const HANumeric& state)
|
||||
{
|
||||
if (!state.isSet()) {
|
||||
return publishOnDataTopic(
|
||||
AHATOFSTR(HAStateTopic),
|
||||
AHATOFSTR(HAStateNone),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
const uint8_t size = state.calculateSize();
|
||||
if (size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char str[size + 1]; // with null terminator
|
||||
str[size] = 0;
|
||||
state.toStr(str);
|
||||
|
||||
return publishOnDataTopic(
|
||||
AHATOFSTR(HAStateTopic),
|
||||
str,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
void HANumber::handleCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_commandCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp_P(cmd, HAStateNone, length) == 0) {
|
||||
_commandCallback(HANumeric(), this);
|
||||
} else {
|
||||
HANumeric number = HANumeric::fromStr(cmd, length);
|
||||
if (number.isSet()) {
|
||||
number.setPrecision(_precision);
|
||||
_commandCallback(number, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const __FlashStringHelper* HANumber::getModeProperty() const
|
||||
{
|
||||
switch (_mode) {
|
||||
case ModeBox:
|
||||
return AHATOFSTR(HAModeBox);
|
||||
|
||||
case ModeSlider:
|
||||
return AHATOFSTR(HAModeSlider);
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const __FlashStringHelper* HANumber::getCommandTemplate()
|
||||
{
|
||||
switch (_precision) {
|
||||
case PrecisionP1:
|
||||
return AHATOFSTR(HAValueTemplateFloatP1);
|
||||
|
||||
case PrecisionP2:
|
||||
return AHATOFSTR(HAValueTemplateFloatP2);
|
||||
|
||||
case PrecisionP3:
|
||||
return AHATOFSTR(HAValueTemplateFloatP3);
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
263
lib/home-assistant-integration/src/device-types/HANumber.h
Normal file
263
lib/home-assistant-integration/src/device-types/HANumber.h
Normal file
@@ -0,0 +1,263 @@
|
||||
#ifndef AHA_HANUMBER_H
|
||||
#define AHA_HANUMBER_H
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
#include "../utils/HANumeric.h"
|
||||
|
||||
#ifndef EX_ARDUINOHA_NUMBER
|
||||
|
||||
#define _SET_STATE_OVERLOAD(type) \
|
||||
/** @overload */ \
|
||||
inline bool setState(const type state, const bool force = false) \
|
||||
{ return setState(HANumeric(state, _precision), force); }
|
||||
|
||||
#define _SET_CURRENT_STATE_OVERLOAD(type) \
|
||||
/** @overload */ \
|
||||
inline void setCurrentState(const type state) \
|
||||
{ setCurrentState(HANumeric(state, _precision)); }
|
||||
|
||||
#define HANUMBER_CALLBACK(name) void (*name)(HANumeric number, HANumber* sender)
|
||||
|
||||
/**
|
||||
* HANumber adds a slider or a box in the Home Assistant panel
|
||||
* that controls the numeric value stored on your device.
|
||||
*
|
||||
* @note
|
||||
* You can find more information about this entity in the Home Assistant documentation:
|
||||
* https://www.home-assistant.io/integrations/number.mqtt/
|
||||
*/
|
||||
class HANumber : public HABaseDeviceType
|
||||
{
|
||||
public:
|
||||
/// Represents mode of the number.
|
||||
enum Mode {
|
||||
ModeAuto = 0,
|
||||
ModeBox,
|
||||
ModeSlider
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates instance of the HANumber entity with the given numbers precision.
|
||||
* The given precision applies to the state, min, max and step values.
|
||||
*
|
||||
* @param uniqueId The unique ID of the number. It needs to be unique in a scope of your device.
|
||||
* @param precision Precision of the floating point number that will be displayed in the HA panel.
|
||||
*/
|
||||
HANumber(const char* uniqueId, const NumberPrecision precision = PrecisionP0);
|
||||
|
||||
/**
|
||||
* Changes state of the number 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 number.
|
||||
* @param force Forces to update state without comparing it to a previous known state.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setState(const HANumeric& state, const bool force = false);
|
||||
|
||||
_SET_STATE_OVERLOAD(int8_t)
|
||||
_SET_STATE_OVERLOAD(int16_t)
|
||||
_SET_STATE_OVERLOAD(int32_t)
|
||||
_SET_STATE_OVERLOAD(uint8_t)
|
||||
_SET_STATE_OVERLOAD(uint16_t)
|
||||
_SET_STATE_OVERLOAD(uint32_t)
|
||||
_SET_STATE_OVERLOAD(float)
|
||||
|
||||
#ifdef __SAMD21G18A__
|
||||
_SET_STATE_OVERLOAD(int)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Sets current state of the number without publishing it 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 number.
|
||||
*/
|
||||
inline void setCurrentState(const HANumeric& state)
|
||||
{ if (state.getPrecision() == _precision) { _currentState = state; } }
|
||||
|
||||
_SET_CURRENT_STATE_OVERLOAD(int8_t)
|
||||
_SET_CURRENT_STATE_OVERLOAD(int16_t)
|
||||
_SET_CURRENT_STATE_OVERLOAD(int32_t)
|
||||
_SET_CURRENT_STATE_OVERLOAD(uint8_t)
|
||||
_SET_CURRENT_STATE_OVERLOAD(uint16_t)
|
||||
_SET_CURRENT_STATE_OVERLOAD(uint32_t)
|
||||
_SET_CURRENT_STATE_OVERLOAD(float)
|
||||
|
||||
#ifdef __SAMD21G18A__
|
||||
_SET_CURRENT_STATE_OVERLOAD(int)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns last known state of the number.
|
||||
* If setState method wasn't called the initial value will be returned.
|
||||
*/
|
||||
inline const HANumeric& getCurrentState() const
|
||||
{ return _currentState; }
|
||||
|
||||
/**
|
||||
* Sets class of the device.
|
||||
* You can find list of available values here: https://www.home-assistant.io/integrations/number/#device-class
|
||||
*
|
||||
* @param deviceClass The class name.
|
||||
*/
|
||||
inline void setDeviceClass(const char* deviceClass)
|
||||
{ _class = deviceClass; }
|
||||
|
||||
/**
|
||||
* Sets icon of the number.
|
||||
* Any icon from MaterialDesignIcons.com (for example: `mdi:home`).
|
||||
*
|
||||
* @param icon The icon name.
|
||||
*/
|
||||
inline void setIcon(const char* icon)
|
||||
{ _icon = icon; }
|
||||
|
||||
/**
|
||||
* Sets retain flag for the number's command.
|
||||
* If set to `true` the command produced by Home Assistant will be retained.
|
||||
*
|
||||
* @param retain
|
||||
*/
|
||||
inline void setRetain(const bool retain)
|
||||
{ _retain = retain; }
|
||||
|
||||
/**
|
||||
* Sets optimistic flag for the number state.
|
||||
* In this mode the number state doesn't need to be reported back to the HA panel when a command is received.
|
||||
* By default the optimistic mode is disabled.
|
||||
*
|
||||
* @param optimistic The optimistic mode (`true` - enabled, `false` - disabled).
|
||||
*/
|
||||
inline void setOptimistic(const bool optimistic)
|
||||
{ _optimistic = optimistic; }
|
||||
|
||||
/**
|
||||
* Sets mode of the number.
|
||||
* It controls how the number should be displayed in the UI.
|
||||
* By default it's `HANumber::ModeAuto`.
|
||||
*
|
||||
* @param mode Mode to set.
|
||||
*/
|
||||
inline void setMode(const Mode mode)
|
||||
{ _mode = mode; }
|
||||
|
||||
/**
|
||||
* Defines the units of measurement of the number, if any.
|
||||
*
|
||||
* @param units For example: °C, %
|
||||
*/
|
||||
inline void setUnitOfMeasurement(const char* unitOfMeasurement)
|
||||
{ _unitOfMeasurement = unitOfMeasurement; }
|
||||
|
||||
/**
|
||||
* Sets the minimum value that can be set from the Home Assistant panel.
|
||||
*
|
||||
* @param min The minimal value. By default the value is not set.
|
||||
*/
|
||||
inline void setMin(const float min)
|
||||
{ _minValue = HANumeric(min, _precision); }
|
||||
|
||||
/**
|
||||
* Sets the maximum value that can be set from the Home Assistant panel.
|
||||
*
|
||||
* @param min The maximum value. By default the value is not set.
|
||||
*/
|
||||
inline void setMax(const float max)
|
||||
{ _maxValue = HANumeric(max, _precision); }
|
||||
|
||||
/**
|
||||
* Sets step of the slider's movement in the Home Assistant panel.
|
||||
*
|
||||
* @param step The step value. Smallest value `0.001`. By default the value is not set.
|
||||
*/
|
||||
inline void setStep(const float step)
|
||||
{ _step = HANumeric(step, _precision); }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the number is changed in the HA panel.
|
||||
* Please note that it's not possible to register multiple callbacks for the same number.
|
||||
*
|
||||
* @param callback
|
||||
* @note In non-optimistic mode, the number must be reported back to HA using the HANumber::setState method.
|
||||
*/
|
||||
inline void onCommand(HANUMBER_CALLBACK(callback))
|
||||
{ _commandCallback = callback; }
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
virtual void onMqttConnected() override;
|
||||
virtual void onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Publishes the MQTT message with the given state.
|
||||
*
|
||||
* @param state The state to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishState(const HANumeric& state);
|
||||
|
||||
/**
|
||||
* Parses the given command and executes the number's callback with proper value.
|
||||
*
|
||||
* @param cmd The data of the command.
|
||||
* @param length Length of the command.
|
||||
*/
|
||||
void handleCommand(const uint8_t* cmd, const uint16_t length);
|
||||
|
||||
/**
|
||||
* Returns progmem string representing mode of the number
|
||||
*/
|
||||
const __FlashStringHelper* getModeProperty() const;
|
||||
|
||||
/**
|
||||
* Returns progmem string representing value template for the command.
|
||||
*/
|
||||
const __FlashStringHelper* getCommandTemplate();
|
||||
|
||||
/// The precision of the number. By default it's `HANumber::PrecisionP0`.
|
||||
const NumberPrecision _precision;
|
||||
|
||||
/// The device class. It can be nullptr.
|
||||
const char* _class;
|
||||
|
||||
/// The icon of the number. It can be nullptr.
|
||||
const char* _icon;
|
||||
|
||||
/// The retain flag for the HA commands.
|
||||
bool _retain;
|
||||
|
||||
/// The optimistic mode of the number (`true` - enabled, `false` - disabled).
|
||||
bool _optimistic;
|
||||
|
||||
/// Controls how the number should be displayed in the UI. By default it's `HANumber::ModeAuto`.
|
||||
Mode _mode;
|
||||
|
||||
/// The unit of measurement for the sensor. It can be nullptr.
|
||||
const char* _unitOfMeasurement;
|
||||
|
||||
/// The minimal value that can be set from the HA panel.
|
||||
HANumeric _minValue;
|
||||
|
||||
/// The maximum value that can be set from the HA panel.
|
||||
HANumeric _maxValue;
|
||||
|
||||
/// The step of the slider's movement.
|
||||
HANumeric _step;
|
||||
|
||||
/// The current state of the number. By default the value is not set.
|
||||
HANumeric _currentState;
|
||||
|
||||
/// The callback that will be called when the command is received from the HA.
|
||||
HANUMBER_CALLBACK(_commandCallback);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
76
lib/home-assistant-integration/src/device-types/HAScene.cpp
Normal file
76
lib/home-assistant-integration/src/device-types/HAScene.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "HAScene.h"
|
||||
#ifndef EX_ARDUINOHA_SCENE
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
HAScene::HAScene(const char* uniqueId) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentScene), uniqueId),
|
||||
_icon(nullptr),
|
||||
_retain(false),
|
||||
_commandCallback(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void HAScene::buildSerializer()
|
||||
{
|
||||
if (_serializer || !uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 7); // 7 - max properties nb
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAUniqueIdProperty), _uniqueId);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
|
||||
// optional property
|
||||
if (_retain) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HARetainProperty),
|
||||
&_retain,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
// HA 2022.10 throws an exception if this property is not set
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAPayloadOnProperty),
|
||||
AHATOFSTR(HAStateOn),
|
||||
HASerializer::ProgmemPropertyValue
|
||||
);
|
||||
|
||||
_serializer->set(HASerializer::WithAvailability);
|
||||
_serializer->topic(AHATOFSTR(HACommandTopic));
|
||||
}
|
||||
|
||||
void HAScene::onMqttConnected()
|
||||
{
|
||||
if (!uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishAvailability();
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HACommandTopic));
|
||||
}
|
||||
|
||||
void HAScene::onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
)
|
||||
{
|
||||
(void)payload;
|
||||
(void)length;
|
||||
|
||||
if (_commandCallback && HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HACommandTopic)
|
||||
)) {
|
||||
_commandCallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
73
lib/home-assistant-integration/src/device-types/HAScene.h
Normal file
73
lib/home-assistant-integration/src/device-types/HAScene.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#ifndef AHA_HASCENE_H
|
||||
#define AHA_HASCENE_H
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
|
||||
#ifndef EX_ARDUINOHA_SCENE
|
||||
|
||||
#define HASCENE_CALLBACK(name) void (*name)(HAScene* sender)
|
||||
|
||||
/**
|
||||
* HAScene adds a new scene to the Home Assistant that triggers your callback once activated.
|
||||
*
|
||||
* @note
|
||||
* You can find more information about this entity in the Home Assistant documentation:
|
||||
* https://www.home-assistant.io/integrations/scene.mqtt/
|
||||
*/
|
||||
class HAScene : public HABaseDeviceType
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param uniqueId The unique ID of the scene. It needs to be unique in a scope of your device.
|
||||
*/
|
||||
HAScene(const char* uniqueId);
|
||||
|
||||
/**
|
||||
* Sets icon of the scene.
|
||||
* Any icon from MaterialDesignIcons.com (for example: `mdi:home`).
|
||||
*
|
||||
* @param icon The icon name.
|
||||
*/
|
||||
inline void setIcon(const char* icon)
|
||||
{ _icon = icon; }
|
||||
|
||||
/**
|
||||
* Sets retain flag for the scene's command.
|
||||
* If set to `true` the command produced by Home Assistant will be retained.
|
||||
*
|
||||
* @param retain
|
||||
*/
|
||||
inline void setRetain(const bool retain)
|
||||
{ _retain = retain; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called when the scene is activated in the HA panel.
|
||||
* Please note that it's not possible to register multiple callbacks for the same scene.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onCommand(HASCENE_CALLBACK(callback))
|
||||
{ _commandCallback = callback; }
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
virtual void onMqttConnected() override;
|
||||
virtual void onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
) override;
|
||||
|
||||
private:
|
||||
/// The icon of the scene. It can be nullptr.
|
||||
const char* _icon;
|
||||
|
||||
/// The retain flag for the HA commands.
|
||||
bool _retain;
|
||||
|
||||
/// The command callback that will be called when scene is activated from the HA panel.
|
||||
HASCENE_CALLBACK(_commandCallback);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
198
lib/home-assistant-integration/src/device-types/HASelect.cpp
Normal file
198
lib/home-assistant-integration/src/device-types/HASelect.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
#include "HASelect.h"
|
||||
#ifndef EX_ARDUINOHA_SELECT
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
HASelect::HASelect(const char* uniqueId) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentSelect), uniqueId),
|
||||
_options(nullptr),
|
||||
_currentState(-1),
|
||||
_icon(nullptr),
|
||||
_retain(false),
|
||||
_optimistic(false),
|
||||
_commandCallback(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HASelect::~HASelect()
|
||||
{
|
||||
if (_options) {
|
||||
const uint8_t optionsNb = _options->getItemsNb();
|
||||
const HASerializerArray::ItemType* options = _options->getItems();
|
||||
|
||||
if (optionsNb > 1) {
|
||||
for (uint8_t i = 0; i < optionsNb; i++) {
|
||||
delete options[i];
|
||||
}
|
||||
}
|
||||
|
||||
delete _options;
|
||||
}
|
||||
}
|
||||
|
||||
void HASelect::setOptions(const char* options)
|
||||
{
|
||||
if (!options || _options) { // options can be set only once
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t optionsNb = countOptionsInString(options);
|
||||
if (optionsNb == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t optionsLen = strlen(options) + 1; // include null terminator
|
||||
_options = new HASerializerArray(optionsNb, false);
|
||||
|
||||
if (optionsNb == 1) {
|
||||
_options->add(options);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t optionLen = 0;
|
||||
for (uint16_t i = 0; i < optionsLen; i++) {
|
||||
if (options[i] == ';' || options[i] == 0) {
|
||||
if (optionLen == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
char* option = new char[optionLen + 1]; // including null terminator
|
||||
option[optionLen] = 0;
|
||||
memcpy(option, &options[i - optionLen], optionLen);
|
||||
|
||||
_options->add(option);
|
||||
optionLen = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
optionLen++;
|
||||
}
|
||||
}
|
||||
|
||||
bool HASelect::setState(const int8_t state, const bool force)
|
||||
{
|
||||
if (!force && _currentState == state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishState(state)) {
|
||||
_currentState = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HASelect::buildSerializer()
|
||||
{
|
||||
if (_serializer || !uniqueId() || !_options) {
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 10); // 10 - max properties nb
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAUniqueIdProperty), _uniqueId);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAOptionsProperty),
|
||||
_options,
|
||||
HASerializer::ArrayPropertyType
|
||||
);
|
||||
|
||||
if (_retain) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HARetainProperty),
|
||||
&_retain,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_optimistic) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAOptimisticProperty),
|
||||
&_optimistic,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
_serializer->set(HASerializer::WithDevice);
|
||||
_serializer->set(HASerializer::WithAvailability);
|
||||
_serializer->topic(AHATOFSTR(HAStateTopic));
|
||||
_serializer->topic(AHATOFSTR(HACommandTopic));
|
||||
}
|
||||
|
||||
void HASelect::onMqttConnected()
|
||||
{
|
||||
if (!uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishAvailability();
|
||||
|
||||
if (!_retain) {
|
||||
publishState(_currentState);
|
||||
}
|
||||
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HACommandTopic));
|
||||
}
|
||||
|
||||
void HASelect::onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
)
|
||||
{
|
||||
if (_commandCallback && HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HACommandTopic)
|
||||
)) {
|
||||
const uint8_t optionsNb = _options->getItemsNb();
|
||||
const HASerializerArray::ItemType* options = _options->getItems();
|
||||
|
||||
for (uint8_t i = 0; i < optionsNb; i++) {
|
||||
if (memcmp(payload, options[i], length) == 0) {
|
||||
_commandCallback(i, this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool HASelect::publishState(const int8_t state)
|
||||
{
|
||||
if (state == -1 || !_options || state >= _options->getItemsNb()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* item = _options->getItems()[state];
|
||||
if (!item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return publishOnDataTopic(AHATOFSTR(HAStateTopic), item, true);
|
||||
}
|
||||
|
||||
uint8_t HASelect::countOptionsInString(const char* options) const
|
||||
{
|
||||
// the given string is treated as a single option if there are no semicolons
|
||||
uint8_t optionsNb = 1;
|
||||
const uint16_t optionsLen = strlen(options);
|
||||
|
||||
if (optionsLen == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < optionsLen; i++) {
|
||||
if (options[i] == ';') {
|
||||
optionsNb++;
|
||||
}
|
||||
}
|
||||
|
||||
return optionsNb;
|
||||
}
|
||||
|
||||
#endif
|
||||
155
lib/home-assistant-integration/src/device-types/HASelect.h
Normal file
155
lib/home-assistant-integration/src/device-types/HASelect.h
Normal file
@@ -0,0 +1,155 @@
|
||||
#ifndef AHA_HASELECT_H
|
||||
#define AHA_HASELECT_H
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
|
||||
#ifndef EX_ARDUINOHA_SELECT
|
||||
|
||||
class HASerializerArray;
|
||||
|
||||
#define HASELECT_CALLBACK(name) void (*name)(int8_t index, HASelect* sender)
|
||||
|
||||
/**
|
||||
* HASelect adds a dropdown with options in the Home Assistant panel.
|
||||
*
|
||||
* @note
|
||||
* You can find more information about this entity in the Home Assistant documentation:
|
||||
* https://www.home-assistant.io/integrations/button.mqtt/
|
||||
*/
|
||||
class HASelect : public HABaseDeviceType
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param uniqueId The unique ID of the select. It needs to be unique in a scope of your device.
|
||||
*/
|
||||
HASelect(const char* uniqueId);
|
||||
~HASelect();
|
||||
|
||||
/**
|
||||
* Sets the list of available options that will be listed in the dropdown.
|
||||
* The input string should contain options separated using semicolons.
|
||||
* For example: `setOptions("Option A;Option B;Option C");
|
||||
*
|
||||
* @param options The list of options that are separated by semicolons.
|
||||
* @note The options list can be set only once.
|
||||
*/
|
||||
void setOptions(const char* options);
|
||||
|
||||
/**
|
||||
* Changes state of the select and publishes MQTT message.
|
||||
* State represents the index of the option that was set using the setOptions method.
|
||||
* 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 select.
|
||||
* @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(const int8_t state, const bool force = false);
|
||||
|
||||
/**
|
||||
* Sets the current state of the select without publishing it to Home Assistant.
|
||||
* State represents the index of the option that was set using the setOptions method.
|
||||
* This method may be useful if you want to change the state before the connection
|
||||
* with the MQTT broker is acquired.
|
||||
*
|
||||
* @param state The new state of the cover.
|
||||
*/
|
||||
inline void setCurrentState(const int8_t state)
|
||||
{ _currentState = state; }
|
||||
|
||||
/**
|
||||
* Returns last known state of the select.
|
||||
* State represents the index of the option that was set using the setOptions method.
|
||||
* By default the state is set to `-1`.
|
||||
*/
|
||||
inline int8_t getCurrentState() const
|
||||
{ return _currentState; }
|
||||
|
||||
/**
|
||||
* Sets icon of the select.
|
||||
* Any icon from MaterialDesignIcons.com (for example: `mdi:home`).
|
||||
*
|
||||
* @param icon The icon name.
|
||||
*/
|
||||
inline void setIcon(const char* icon)
|
||||
{ _icon = icon; }
|
||||
|
||||
/**
|
||||
* Sets retain flag for the select's command.
|
||||
* If set to `true` the command produced by Home Assistant will be retained.
|
||||
*
|
||||
* @param retain
|
||||
*/
|
||||
inline void setRetain(const bool retain)
|
||||
{ _retain = retain; }
|
||||
|
||||
/**
|
||||
* Sets optimistic flag for the select state.
|
||||
* In this mode the select state doesn't need to be reported back to the HA panel when a command is received.
|
||||
* By default the optimistic mode is disabled.
|
||||
*
|
||||
* @param optimistic The optimistic mode (`true` - enabled, `false` - disabled).
|
||||
*/
|
||||
inline void setOptimistic(const bool optimistic)
|
||||
{ _optimistic = optimistic; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the option is changed from the HA panel.
|
||||
* Please note that it's not possible to register multiple callbacks for the same select.
|
||||
*
|
||||
* @param callback
|
||||
* @note In non-optimistic mode, the selected option must be reported back to HA using the HASelect::setState method.
|
||||
*/
|
||||
inline void onCommand(HASELECT_CALLBACK(callback))
|
||||
{ _commandCallback = callback; }
|
||||
|
||||
#ifdef ARDUINOHA_TEST
|
||||
inline HASerializerArray* getOptions() const
|
||||
{ return _options; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
virtual void onMqttConnected() override;
|
||||
virtual void onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Publishes the MQTT message with the given state.
|
||||
*
|
||||
* @param state The state to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishState(const int8_t state);
|
||||
|
||||
/**
|
||||
* Counts the amount of options in the given string.
|
||||
*/
|
||||
uint8_t countOptionsInString(const char* options) const;
|
||||
|
||||
/// Array of options for the serializer.
|
||||
HASerializerArray* _options;
|
||||
|
||||
/// Stores the current state (the current option's index). By default it's `-1`.
|
||||
int8_t _currentState;
|
||||
|
||||
/// The icon of the select. It can be nullptr.
|
||||
const char* _icon;
|
||||
|
||||
/// The retain flag for the HA commands.
|
||||
bool _retain;
|
||||
|
||||
/// The optimistic mode of the select (`true` - enabled, `false` - disabled).
|
||||
bool _optimistic;
|
||||
|
||||
/// The command callback that will be called when option is changed via the HA panel.
|
||||
HASELECT_CALLBACK(_commandCallback);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
59
lib/home-assistant-integration/src/device-types/HASensor.cpp
Normal file
59
lib/home-assistant-integration/src/device-types/HASensor.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "HASensor.h"
|
||||
#ifndef EX_ARDUINOHA_SENSOR
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
HASensor::HASensor(const char* uniqueId) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentSensor), uniqueId),
|
||||
_deviceClass(nullptr),
|
||||
_forceUpdate(false),
|
||||
_icon(nullptr),
|
||||
_unitOfMeasurement(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool HASensor::setValue(const char* value)
|
||||
{
|
||||
return publishOnDataTopic(AHATOFSTR(HAStateTopic), value, true);
|
||||
}
|
||||
|
||||
void HASensor::buildSerializer()
|
||||
{
|
||||
if (_serializer || !uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 9); // 9 - max properties nb
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAUniqueIdProperty), _uniqueId);
|
||||
_serializer->set(AHATOFSTR(HADeviceClassProperty), _deviceClass);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
_serializer->set(AHATOFSTR(HAUnitOfMeasurementProperty), _unitOfMeasurement);
|
||||
|
||||
// optional property
|
||||
if (_forceUpdate) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAForceUpdateProperty),
|
||||
&_forceUpdate,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
_serializer->set(HASerializer::WithDevice);
|
||||
_serializer->set(HASerializer::WithAvailability);
|
||||
_serializer->topic(AHATOFSTR(HAStateTopic));
|
||||
}
|
||||
|
||||
void HASensor::onMqttConnected()
|
||||
{
|
||||
if (!uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishAvailability();
|
||||
}
|
||||
|
||||
#endif
|
||||
89
lib/home-assistant-integration/src/device-types/HASensor.h
Normal file
89
lib/home-assistant-integration/src/device-types/HASensor.h
Normal file
@@ -0,0 +1,89 @@
|
||||
#ifndef AHA_HASENSOR_H
|
||||
#define AHA_HASENSOR_H
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
|
||||
#ifndef EX_ARDUINOHA_SENSOR
|
||||
|
||||
/**
|
||||
* HASensor allows to publish textual sensor values that will be displayed in the HA panel.
|
||||
* If you need to publish numbers then HASensorNumber is what you're looking for.
|
||||
*
|
||||
* @note It's not possible to define a sensor that publishes mixed values (e.g. string + integer values).
|
||||
* @note
|
||||
* You can find more information about this entity in the Home Assistant documentation:
|
||||
* https://www.home-assistant.io/integrations/sensor.mqtt/
|
||||
*/
|
||||
class HASensor : public HABaseDeviceType
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param uniqueId The unique ID of the sensor. It needs to be unique in a scope of your device.
|
||||
*/
|
||||
HASensor(const char* uniqueId);
|
||||
|
||||
/**
|
||||
* Publishes the MQTT message with the given value.
|
||||
* Unlike the other device types, the HASensor doesn't store the previous value that was set.
|
||||
* It means that the MQTT message is produced each time the setValue method is called.
|
||||
*
|
||||
* @param value String representation of the sensor's value.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setValue(const char* value);
|
||||
|
||||
/**
|
||||
* Sets class of the device.
|
||||
* You can find list of available values here: https://www.home-assistant.io/integrations/sensor/#device-class
|
||||
*
|
||||
* @param deviceClass The class name.
|
||||
*/
|
||||
inline void setDeviceClass(const char* deviceClass)
|
||||
{ _deviceClass = deviceClass; }
|
||||
|
||||
/**
|
||||
* Forces HA panel to process each incoming value (MQTT message).
|
||||
* It's useful if you want to have meaningful value graphs in history.
|
||||
*
|
||||
* @param forceUpdate
|
||||
*/
|
||||
inline void setForceUpdate(bool forceUpdate)
|
||||
{ _forceUpdate = forceUpdate; }
|
||||
|
||||
/**
|
||||
* Sets icon of the sensor.
|
||||
* Any icon from MaterialDesignIcons.com (for example: `mdi:home`).
|
||||
*
|
||||
* @param class The icon name.
|
||||
*/
|
||||
inline void setIcon(const char* icon)
|
||||
{ _icon = icon; }
|
||||
|
||||
/**
|
||||
* Defines the units of measurement of the sensor, if any.
|
||||
*
|
||||
* @param units For example: °C, %
|
||||
*/
|
||||
inline void setUnitOfMeasurement(const char* unitOfMeasurement)
|
||||
{ _unitOfMeasurement = unitOfMeasurement; }
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override final;
|
||||
virtual void onMqttConnected() override;
|
||||
|
||||
private:
|
||||
/// The device class. It can be nullptr.
|
||||
const char* _deviceClass;
|
||||
|
||||
/// The force update flag for the HA panel.
|
||||
bool _forceUpdate;
|
||||
|
||||
/// The icon of the sensor. It can be nullptr.
|
||||
const char* _icon;
|
||||
|
||||
/// The unit of measurement for the sensor. It can be nullptr.
|
||||
const char* _unitOfMeasurement;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "HASensorNumber.h"
|
||||
#ifndef EX_ARDUINOHA_SENSOR
|
||||
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
HASensorNumber::HASensorNumber(
|
||||
const char* uniqueId,
|
||||
const NumberPrecision precision
|
||||
) :
|
||||
HASensor(uniqueId),
|
||||
_precision(precision),
|
||||
_currentValue()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool HASensorNumber::setValue(const HANumeric& value, const bool force)
|
||||
{
|
||||
if (value.getPrecision() != _precision) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!force && value == _currentValue) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishValue(value)) {
|
||||
_currentValue = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HASensorNumber::onMqttConnected()
|
||||
{
|
||||
if (!uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
HASensor::onMqttConnected();
|
||||
publishValue(_currentValue);
|
||||
}
|
||||
|
||||
bool HASensorNumber::publishValue(const HANumeric& value)
|
||||
{
|
||||
if (!value.isSet()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t size = value.calculateSize();
|
||||
if (size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char str[size + 1]; // with null terminator
|
||||
str[size] = 0;
|
||||
value.toStr(str);
|
||||
|
||||
return publishOnDataTopic(
|
||||
AHATOFSTR(HAStateTopic),
|
||||
str,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
||||
106
lib/home-assistant-integration/src/device-types/HASensorNumber.h
Normal file
106
lib/home-assistant-integration/src/device-types/HASensorNumber.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#ifndef AHA_HASENSORNUMBER_H
|
||||
#define AHA_HASENSORNUMBER_H
|
||||
|
||||
#include "HASensor.h"
|
||||
#include "../utils/HANumeric.h"
|
||||
|
||||
#ifndef EX_ARDUINOHA_SENSOR
|
||||
|
||||
#define _SET_VALUE_OVERLOAD(type) \
|
||||
/** @overload */ \
|
||||
inline bool setValue(const type value, const bool force = false) \
|
||||
{ return setValue(HANumeric(value, _precision), force); }
|
||||
|
||||
#define _SET_CURRENT_VALUE_OVERLOAD(type) \
|
||||
/** @overload */ \
|
||||
inline void setCurrentValue(const type value) \
|
||||
{ setCurrentValue(HANumeric(value, _precision)); }
|
||||
|
||||
/**
|
||||
* HASensorInteger allows to publish numeric values of a sensor that will be displayed in the HA panel.
|
||||
*
|
||||
* @note You can find more information about this class in HASensor documentation.
|
||||
*/
|
||||
class HASensorNumber : public HASensor
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param uniqueId The unique ID of the sensor. It needs to be unique in a scope of your device.
|
||||
* @param precision Precision of the floating point number that will be displayed in the HA panel.
|
||||
*/
|
||||
HASensorNumber(
|
||||
const char* uniqueId,
|
||||
const NumberPrecision precision = PrecisionP0
|
||||
);
|
||||
|
||||
/**
|
||||
* Changes value of the sensor and publish MQTT message.
|
||||
* Please note that if a new value is the same as the previous one the MQTT message won't be published.
|
||||
*
|
||||
* @param value New value of the sensor. THe precision of the value needs to match precision of the sensor.
|
||||
* @param force Forces to update the value without comparing it to a previous known value.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool setValue(const HANumeric& value, const bool force = false);
|
||||
|
||||
_SET_VALUE_OVERLOAD(int8_t)
|
||||
_SET_VALUE_OVERLOAD(int16_t)
|
||||
_SET_VALUE_OVERLOAD(int32_t)
|
||||
_SET_VALUE_OVERLOAD(uint8_t)
|
||||
_SET_VALUE_OVERLOAD(uint16_t)
|
||||
_SET_VALUE_OVERLOAD(uint32_t)
|
||||
_SET_VALUE_OVERLOAD(float)
|
||||
|
||||
#ifdef __SAMD21G18A__
|
||||
_SET_VALUE_OVERLOAD(int)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Sets the current value of the sensor without publishing it to Home Assistant.
|
||||
* This method may be useful if you want to change the value before the connection with the MQTT broker is acquired.
|
||||
*
|
||||
* @param value New value of the sensor.
|
||||
*/
|
||||
inline void setCurrentValue(const HANumeric& value)
|
||||
{ if (value.getPrecision() == _precision) { _currentValue = value; } }
|
||||
|
||||
_SET_CURRENT_VALUE_OVERLOAD(int8_t)
|
||||
_SET_CURRENT_VALUE_OVERLOAD(int16_t)
|
||||
_SET_CURRENT_VALUE_OVERLOAD(int32_t)
|
||||
_SET_CURRENT_VALUE_OVERLOAD(uint8_t)
|
||||
_SET_CURRENT_VALUE_OVERLOAD(uint16_t)
|
||||
_SET_CURRENT_VALUE_OVERLOAD(uint32_t)
|
||||
_SET_CURRENT_VALUE_OVERLOAD(float)
|
||||
|
||||
#ifdef __SAMD21G18A__
|
||||
_SET_CURRENT_VALUE_OVERLOAD(int)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns the last known value of the sensor.
|
||||
* By default the value is not set.
|
||||
*/
|
||||
inline const HANumeric& getCurrentValue() const
|
||||
{ return _currentValue; }
|
||||
|
||||
protected:
|
||||
virtual void onMqttConnected() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Publishes the MQTT message with the given value.
|
||||
*
|
||||
* @param state The value to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishValue(const HANumeric& value);
|
||||
|
||||
/// The precision of the sensor. By default it's `HASensorNumber::PrecisionP0`.
|
||||
const NumberPrecision _precision;
|
||||
|
||||
/// The current value of the sensor. By default the value is not set.
|
||||
HANumeric _currentValue;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
111
lib/home-assistant-integration/src/device-types/HASwitch.cpp
Normal file
111
lib/home-assistant-integration/src/device-types/HASwitch.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#include "HASwitch.h"
|
||||
#ifndef EX_ARDUINOHA_SWITCH
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
HASwitch::HASwitch(const char* uniqueId) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentSwitch), uniqueId),
|
||||
_class(nullptr),
|
||||
_icon(nullptr),
|
||||
_retain(false),
|
||||
_optimistic(false),
|
||||
_currentState(false),
|
||||
_commandCallback(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool HASwitch::setState(const bool state, const bool force)
|
||||
{
|
||||
if (!force && state == _currentState) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishState(state)) {
|
||||
_currentState = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HASwitch::buildSerializer()
|
||||
{
|
||||
if (_serializer || !uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 10); // 10 - max properties nb
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAUniqueIdProperty), _uniqueId);
|
||||
_serializer->set(AHATOFSTR(HADeviceClassProperty), _class);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
|
||||
// optional property
|
||||
if (_retain) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HARetainProperty),
|
||||
&_retain,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_optimistic) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAOptimisticProperty),
|
||||
&_optimistic,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
_serializer->set(HASerializer::WithDevice);
|
||||
_serializer->set(HASerializer::WithAvailability);
|
||||
_serializer->topic(AHATOFSTR(HAStateTopic));
|
||||
_serializer->topic(AHATOFSTR(HACommandTopic));
|
||||
}
|
||||
|
||||
void HASwitch::onMqttConnected()
|
||||
{
|
||||
if (!uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishAvailability();
|
||||
|
||||
if (!_retain) {
|
||||
publishState(_currentState);
|
||||
}
|
||||
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HACommandTopic));
|
||||
}
|
||||
|
||||
void HASwitch::onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
)
|
||||
{
|
||||
(void)payload;
|
||||
|
||||
if (_commandCallback && HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HACommandTopic)
|
||||
)) {
|
||||
bool state = length == strlen_P(HAStateOn);
|
||||
_commandCallback(state, this);
|
||||
}
|
||||
}
|
||||
|
||||
bool HASwitch::publishState(const bool state)
|
||||
{
|
||||
return publishOnDataTopic(
|
||||
AHATOFSTR(HAStateTopic),
|
||||
AHATOFSTR(state ? HAStateOn : HAStateOff),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
||||
150
lib/home-assistant-integration/src/device-types/HASwitch.h
Normal file
150
lib/home-assistant-integration/src/device-types/HASwitch.h
Normal file
@@ -0,0 +1,150 @@
|
||||
#ifndef AHA_HASWITCH_H
|
||||
#define AHA_HASWITCH_H
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
|
||||
#ifndef EX_ARDUINOHA_SWITCH
|
||||
|
||||
#define HASWITCH_CALLBACK(name) void (*name)(bool state, HASwitch* sender)
|
||||
|
||||
/**
|
||||
* HASwitch allows to display on/off switch in the HA panel and receive commands on your device.
|
||||
*
|
||||
* @note
|
||||
* You can find more information about this entity in the Home Assistant documentation:
|
||||
* https://www.home-assistant.io/integrations/switch.mqtt/
|
||||
*/
|
||||
class HASwitch : public HABaseDeviceType
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param uniqueId The unique ID of the sensor. It needs to be unique in a scope of your device.
|
||||
*/
|
||||
HASwitch(const char* uniqueId);
|
||||
|
||||
/**
|
||||
* Changes state of the switch 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 switch.
|
||||
* @param force Forces to update state without comparing it to previous known state.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setState(const bool state, const bool force = false);
|
||||
|
||||
/**
|
||||
* Alias for `setState(true)`.
|
||||
*/
|
||||
inline bool turnOn()
|
||||
{ return setState(true); }
|
||||
|
||||
/**
|
||||
* Alias for `setState(false)`.
|
||||
*/
|
||||
inline bool turnOff()
|
||||
{ return setState(false); }
|
||||
|
||||
/**
|
||||
* Sets current state of the switch without publishing it 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 switch.
|
||||
*/
|
||||
inline void setCurrentState(const bool state)
|
||||
{ _currentState = state; }
|
||||
|
||||
/**
|
||||
* Returns last known state of the switch.
|
||||
* By default it's `false`.
|
||||
*/
|
||||
inline bool getCurrentState() const
|
||||
{ return _currentState; }
|
||||
|
||||
/**
|
||||
* Sets class of the device.
|
||||
* You can find list of available values here: https://www.home-assistant.io/integrations/switch/#device-class
|
||||
*
|
||||
* @param deviceClass The class name.
|
||||
*/
|
||||
inline void setDeviceClass(const char* deviceClass)
|
||||
{ _class = deviceClass; }
|
||||
|
||||
/**
|
||||
* Sets icon of the sensor.
|
||||
* Any icon from MaterialDesignIcons.com (for example: `mdi:home`).
|
||||
*
|
||||
* @param icon The icon name.
|
||||
*/
|
||||
inline void setIcon(const char* icon)
|
||||
{ _icon = icon; }
|
||||
|
||||
/**
|
||||
* Sets retain flag for the switch command.
|
||||
* If set to `true` the command produced by Home Assistant will be retained.
|
||||
*
|
||||
* @param retain
|
||||
*/
|
||||
inline void setRetain(const bool retain)
|
||||
{ _retain = retain; }
|
||||
|
||||
/**
|
||||
* Sets optimistic flag for the switch state.
|
||||
* In this mode the switch state doesn't need to be reported back to the HA panel when a command is received.
|
||||
* By default the optimistic mode is disabled.
|
||||
*
|
||||
* @param optimistic The optimistic mode (`true` - enabled, `false` - disabled).
|
||||
*/
|
||||
inline void setOptimistic(const bool optimistic)
|
||||
{ _optimistic = optimistic; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the on/off command from HA is received.
|
||||
* Please note that it's not possible to register multiple callbacks for the same switch.
|
||||
*
|
||||
* @param callback
|
||||
* @note In non-optimistic mode, the state must be reported back to HA using the HASwitch::setState method.
|
||||
*/
|
||||
inline void onCommand(HASWITCH_CALLBACK(callback))
|
||||
{ _commandCallback = callback; }
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
virtual void onMqttConnected() override;
|
||||
virtual void onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Publishes the MQTT message with the given state.
|
||||
*
|
||||
* @param state The state to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishState(const bool state);
|
||||
|
||||
/// The device class. It can be nullptr.
|
||||
const char* _class;
|
||||
|
||||
/// The icon of the button. It can be nullptr.
|
||||
const char* _icon;
|
||||
|
||||
/// The retain flag for the HA commands.
|
||||
bool _retain;
|
||||
|
||||
/// The optimistic mode of the switch (`true` - enabled, `false` - disabled).
|
||||
bool _optimistic;
|
||||
|
||||
/// The current state of the switch. By default it's `false`.
|
||||
bool _currentState;
|
||||
|
||||
/// The callback that will be called when switch command is received from the HA.
|
||||
HASWITCH_CALLBACK(_commandCallback);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "HATagScanner.h"
|
||||
#ifndef EX_ARDUINOHA_TAG_SCANNER
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
HATagScanner::HATagScanner(const char* uniqueId) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentTag), uniqueId)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool HATagScanner::tagScanned(const char* tag)
|
||||
{
|
||||
if (!tag || strlen(tag) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return publishOnDataTopic(AHATOFSTR(HATopic), tag);
|
||||
}
|
||||
|
||||
void HATagScanner::buildSerializer()
|
||||
{
|
||||
if (_serializer || !uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 2); // 2 - max properties nb
|
||||
_serializer->set(HASerializer::WithDevice);
|
||||
_serializer->topic(AHATOFSTR(HATopic));
|
||||
}
|
||||
|
||||
void HATagScanner::onMqttConnected()
|
||||
{
|
||||
if (!uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef AHA_HATAGSCANNER_H
|
||||
#define AHA_HATAGSCANNER_H
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
|
||||
#ifndef EX_ARDUINOHA_TAG_SCANNER
|
||||
|
||||
/**
|
||||
* HATagScanner allow to produce scan events that can be used in the HA automation.
|
||||
*
|
||||
* @note
|
||||
* You can find more information about this entity in the Home Assistant documentation:
|
||||
* https://www.home-assistant.io/integrations/tag.mqtt/
|
||||
*/
|
||||
class HATagScanner : public HABaseDeviceType
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param uniqueId The unique ID of the scanner. It needs to be unique in a scope of your device.
|
||||
*/
|
||||
HATagScanner(const char* uniqueId);
|
||||
|
||||
/**
|
||||
* Sends "tag scanned" event to the MQTT (Home Assistant).
|
||||
* Based on this event HA may perform user-defined automation.
|
||||
*
|
||||
* @param tag Value of the scanned tag.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool tagScanned(const char* tag);
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
virtual void onMqttConnected() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user