Compare commits
233 Commits
master
...
release-1.
| Author | SHA1 | Date |
|---|---|---|
|
|
de6ec519eb | |
|
|
5e2e520f34 | |
|
|
648ebfd8c2 | |
|
|
20c2df001c | |
|
|
b36ee7468f | |
|
|
ba9115e1af | |
|
|
505184107f | |
|
|
ff850eb367 | |
|
|
3769c1fa85 | |
|
|
5c8eb95f76 | |
|
|
b8f06e3687 | |
|
|
5ad1fecac8 | |
|
|
163ed80dc6 | |
|
|
0b6eac9cac | |
|
|
35d7550b74 | |
|
|
3a2b59b2c5 | |
|
|
d043410e3b | |
|
|
c117bffe94 | |
|
|
ffb65c3252 | |
|
|
de6f54c325 | |
|
|
349270aef2 | |
|
|
be354ebed3 | |
|
|
d78abdaf6d | |
|
|
48793eff12 | |
|
|
5011733449 | |
|
|
f973a92fca | |
|
|
a0d98fad2e | |
|
|
06ed5afb9a | |
|
|
132a2eeeb1 | |
|
|
9641891e9a | |
|
|
d6d078995f | |
|
|
9eb8285f30 | |
|
|
02bf77b101 | |
|
|
d68d38f338 | |
|
|
dfc4e5f756 | |
|
|
972dda721c | |
|
|
413c35304f | |
|
|
d5936d941d | |
|
|
fea91b45ce | |
|
|
7ccb4f0f49 | |
|
|
57284ebdd6 | |
|
|
22521ab1c2 | |
|
|
1ad2a22504 | |
|
|
06c6e99a04 | |
|
|
3218c2e536 | |
|
|
f22e6113c5 | |
|
|
99308bb978 | |
|
|
2c41baece2 | |
|
|
dd44c437ec | |
|
|
d0be05f121 | |
|
|
b25a38fa3f | |
|
|
e2d954684f | |
|
|
43a9ef86a6 | |
|
|
c5a0f061ce | |
|
|
dbb0f6565f | |
|
|
27addd274e | |
|
|
eaa9cbca40 | |
|
|
c2692ba401 | |
|
|
a3846901e7 | |
|
|
66541cbef9 | |
|
|
ed7bfdaea9 | |
|
|
5fd299ede7 | |
|
|
44e07dfd5f | |
|
|
bb1b768016 | |
|
|
e421c40f4f | |
|
|
e67e49454f | |
|
|
1e65056136 | |
|
|
a8df9d9f2f | |
|
|
73f2da86e9 | |
|
|
9b6f5ef59e | |
|
|
5020fae497 | |
|
|
5b313f0c4c | |
|
|
b4bde4e61a | |
|
|
ca48fb7be7 | |
|
|
ad302e42f8 | |
|
|
e7e804112f | |
|
|
65dd9da130 | |
|
|
5d314d4fb8 | |
|
|
e5c7ba5080 | |
|
|
5568cc1ddf | |
|
|
3e9e7a1902 | |
|
|
fe0b8c1a42 | |
|
|
1a6bc93632 | |
|
|
7f2f093fff | |
|
|
fbc1ac9cbe | |
|
|
d280b5b52a | |
|
|
015625b08a | |
|
|
a5e8680452 | |
|
|
0c2f29db2f | |
|
|
f5d4806e4b | |
|
|
437771fabd | |
|
|
260be21462 | |
|
|
3c3e5c21ea | |
|
|
2d86ee4a25 | |
|
|
fe9e3d7942 | |
|
|
e42cb4e1dd | |
|
|
b5dcfbbd7d | |
|
|
ba240ea4d7 | |
|
|
d179f039d1 | |
|
|
bddffab7ad | |
|
|
fa5455d6f9 | |
|
|
73f761adc9 | |
|
|
96e6f55c72 | |
|
|
ffc4ae0cb2 | |
|
|
392eac38b9 | |
|
|
39b7b54d48 | |
|
|
0492b67b55 | |
|
|
bd474778e5 | |
|
|
4269eca982 | |
|
|
12c6cf3fe1 | |
|
|
cc7ffb4c07 | |
|
|
bef151cdb1 | |
|
|
6c3df551eb | |
|
|
00944c1f2f | |
|
|
20fe4ab6fd | |
|
|
1f13906a25 | |
|
|
f2cb880e4e | |
|
|
83924a6bdf | |
|
|
18c74e6f17 | |
|
|
117c348708 | |
|
|
fb5c9e1e29 | |
|
|
7282c2a8c9 | |
|
|
f3e98601b0 | |
|
|
ffe56e7261 | |
|
|
cd85cc1629 | |
|
|
9209cb6b41 | |
|
|
6979ab8080 | |
|
|
c529f93646 | |
|
|
b960c2fdaf | |
|
|
da55b9b6b1 | |
|
|
beef66eec0 | |
|
|
223107d8aa | |
|
|
6280c17154 | |
|
|
3c172da714 | |
|
|
76a6a2bde9 | |
|
|
0e0f1dcb1c | |
|
|
edb0cc1be0 | |
|
|
66e7fc6654 | |
|
|
fe3389da3b | |
|
|
6e323976fd | |
|
|
1587f56626 | |
|
|
aa700f94f1 | |
|
|
e715d670e3 | |
|
|
781250b362 | |
|
|
30152e61cf | |
|
|
503143c355 | |
|
|
a6a84b5e21 | |
|
|
9708678dcf | |
|
|
cf0fd7c881 | |
|
|
83ad277aa0 | |
|
|
b0243ce28e | |
|
|
37e2e0dcb1 | |
|
|
ead05d8690 | |
|
|
70126cb04f | |
|
|
f6ebc1ae22 | |
|
|
c417fe74f7 | |
|
|
841851d020 | |
|
|
c4e88d7df0 | |
|
|
9e3f6c8303 | |
|
|
b7c294d210 | |
|
|
9e0512e48b | |
|
|
2bc107032c | |
|
|
458a4fb357 | |
|
|
8268e519dd | |
|
|
77e7ff2b2e | |
|
|
a9578924c2 | |
|
|
4ab7503a72 | |
|
|
1e62a038f1 | |
|
|
98d6abeb1f | |
|
|
ae60a84855 | |
|
|
c1834fb301 | |
|
|
452558319f | |
|
|
184b658f5c | |
|
|
92a6391fe5 | |
|
|
6bfeefcded | |
|
|
6f19141a49 | |
|
|
68ccfc9858 | |
|
|
1194bd27f6 | |
|
|
713c5c65bf | |
|
|
965bdc71b7 | |
|
|
fc4b8a077b | |
|
|
4b24f850d6 | |
|
|
30ffc26666 | |
|
|
b41fc3ac7e | |
|
|
f482dc1b09 | |
|
|
8b3192e122 | |
|
|
869e768ba7 | |
|
|
e7a1bb78be | |
|
|
42f69adf49 | |
|
|
643c737634 | |
|
|
56a2f0bece | |
|
|
c835a8620f | |
|
|
fa05738ff6 | |
|
|
fe9f35aa84 | |
|
|
02d39a61c0 | |
|
|
5ce7211539 | |
|
|
a93aef1604 | |
|
|
871eba3c1d | |
|
|
eea2c44aae | |
|
|
44db046e57 | |
|
|
2421b8d1b1 | |
|
|
dee9491089 | |
|
|
81ee9e2dbc | |
|
|
b41bb653dc | |
|
|
688b51967e | |
|
|
2d84f25ac4 | |
|
|
2bda112780 | |
|
|
4de61e5914 | |
|
|
0f96d0fff6 | |
|
|
34651307db | |
|
|
b15136d2d8 | |
|
|
e0517a5964 | |
|
|
c89969cccd | |
|
|
1494c6ebda | |
|
|
3e26b9039d | |
|
|
1cd01205a3 | |
|
|
3396a5ff96 | |
|
|
5a356432b3 | |
|
|
a84429f930 | |
|
|
17410e825e | |
|
|
7325baa1a5 | |
|
|
bf057e2fad | |
|
|
131e5b1a92 | |
|
|
793f7882b1 | |
|
|
0f6e58435e | |
|
|
62f68cb33c | |
|
|
5f9458e625 | |
|
|
d6f5612f9f | |
|
|
a73b206531 | |
|
|
3f62c228af | |
|
|
604c76bc94 | |
|
|
85a6406fdf | |
|
|
4557e3866f |
95
README.md
95
README.md
|
|
@ -6,13 +6,16 @@ HomeSpan provides a microcontroller-focused implementation of Apple's HomeKit Ac
|
||||||
|
|
||||||
HomeSpan requires version 2.0.0 or later of the [Arduino-ESP32 Board Manager](https://github.com/espressif/arduino-esp32), and has been tested up through version 2.0.14 (recommended). HomeSpan can be run on the original ESP32 as well as Espressif's ESP32-S2, ESP32-C3, and ESP32-S3 chips.
|
HomeSpan requires version 2.0.0 or later of the [Arduino-ESP32 Board Manager](https://github.com/espressif/arduino-esp32), and has been tested up through version 2.0.14 (recommended). HomeSpan can be run on the original ESP32 as well as Espressif's ESP32-S2, ESP32-C3, and ESP32-S3 chips.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Apple's new HomeKit architecture [requires the use of a Home Hub](https://support.apple.com/en-us/HT207057) (either a HomePod or Apple TV) for full and proper operation of any HomeKit device, include those based on HomeSpan. Without a Home Hub, HomeSpan cannot send notifications to the Home App - things like pushbuttons and temperature sensors will not be able to transmit updates to the Home App.
|
||||||
|
|
||||||
### HomeSpan Highlights
|
### HomeSpan Highlights
|
||||||
|
|
||||||
* Provides a natural, intuitive, and **very** easy-to-use framework
|
* Provides a natural, intuitive, and **very** easy-to-use framework
|
||||||
* Utilizes a unique *Service-Centric* approach to creating HomeKit devices
|
* Utilizes a unique *Service-Centric* approach to creating HomeKit devices
|
||||||
* Takes full advantage of the widely-popular Arduino IDE
|
* Takes full advantage of the widely-popular Arduino IDE
|
||||||
* 100% HAP-R2 compliance
|
* 100% HAP-R2 compliance
|
||||||
* 41 integrated HomeKit Services
|
* 38 integrated HomeKit Services
|
||||||
* Operates in either Accessory or Bridge mode
|
* Operates in either Accessory or Bridge mode
|
||||||
* Supports pairing with Setup Codes or QR Codes
|
* Supports pairing with Setup Codes or QR Codes
|
||||||
|
|
||||||
|
|
@ -49,39 +52,81 @@ HomeSpan requires version 2.0.0 or later of the [Arduino-ESP32 Board Manager](ht
|
||||||
* Launch the WiFi Access Point
|
* Launch the WiFi Access Point
|
||||||
* A standalone, detailed End-User Guide
|
* A standalone, detailed End-User Guide
|
||||||
|
|
||||||
## ❗Latest Update - HomeSpan 1.8.0 (7/8/2023)
|
## ❗Latest Update - HomeSpan 1.9.0 (2/17/2024)
|
||||||
|
|
||||||
* **New Stepper Motor Control!**
|
* **HomeSpan has been optimized to use significantly less RAM!**
|
||||||
|
|
||||||
* adds new **StepperControl** class that allows for smooth, uninterrupted operation of one or more stepper motors running in the background while HomeSpan continues to run simultaneously in the foreground
|
* supports approximately **TWICE** the number of Accessories using the same amount of memory
|
||||||
* supports driver boards with or without PWM, including microstepping modes
|
* minimized memory use also means much more room for users to add non-HomeSpan features to their sketch without running out of memory, especially if the non-HomeSpan code consumes a lot of stack space
|
||||||
* supports automatic acceleration and deceleration for smooth starts and stops
|
* HomeSpan now automatically detects the presence of **PSRAM** (SPIRAM) and will utilize that extra memory to the largest extent possible to keep internal RAM free for certain HomeSpan functions and ESP32 functions (e.g. WiFi) that require internal RAM. Also keeps internal RAM free for use by any non-HomeSpan code that does not (or cannot) use PSRAM
|
||||||
* motors can be set to an absolute position or instructucted to move a specified number of steps
|
* increased HomeSpan's 41-Accessory limit to a 150-Accessory Limit (as specified by HAP) since it is now possible to create a device with many more than 41 Accessories without running out of memory, especially if PSRAM is used
|
||||||
* provides options to automatically enter into a "brake" state after motor stops to conserve power
|
|
||||||
* includes a fully worked example of a motorized window shade
|
|
||||||
* see [Stepper Motor Control](docs/Stepper.md) for details
|
|
||||||
|
|
||||||
* **Upgrades to HomeSpan Web Log output**
|
* **HomeSpan has been optimized to use significantly less Non-Volatile Storage (NVS)**
|
||||||
|
|
||||||
* adds new method `void homeSpan.setWebLogCSS(const char *css)` that allows you to define *Custom Style Sheets (CSS)* for the Web Log text, tables, and background
|
* allows you to use NVS to save the values of a many more Characteristics
|
||||||
* adds version numbers for the Sodium and MbedTLS libraries, HomeKit pairing status, and a text description of Reset Reason code
|
* see the newly-added [CustomNVSPartition](docs/Tutorials.md#customnvspartition) example that demonstrates how to create your own Partition Scheme to further expand the size of the NVS partition beyond the ESP32 default to support sketches with a large number of Accessories each configured to use NVS to save the values of many Characteristics
|
||||||
|
|
||||||
|
* **New features and documentation for Services and Characteristics**
|
||||||
|
* created "enumerated constants" (e.g. *SWING_ENABLED*, *HUMIDIFYING*, etc.) for every applicable Characteristic that can be used instead of integers when reading and writing values
|
||||||
|
* very helpful since Apple is no longer publishing its non-commercial HAP document that provided a list and description of the states for each Characteristic
|
||||||
|
* example: `if(target.getNewVal()==target.ARM_STAY) {...}`
|
||||||
|
* added ability to properly name individual Services within a single Accessory using new **Characteristic::ConfiguredName()**
|
||||||
|
* see revised [Example 11 - ServiceNames](docs/Tutorials.md#example-11---servicenames) for details
|
||||||
|
* new [Services and Characteristics](docs/ServiceList.md) page now provides functional descriptions and detailed specifications for every Service and Characteristic supported by HomeSpan, including a list of the enumerated constants available for every Characteristic
|
||||||
|
|
||||||
|
* **New ability to use *Inverted Buttons* and *Touch Sensors* as a Control Button**
|
||||||
|
|
||||||
|
* adds *triggerType* as a second, optional argument to `Span& setControlPin(uint8_t pin, triggerType_t triggerType)`
|
||||||
|
* supports TRIGGER_ON_LOW, TRIGGER_ON_HIGH, TRIGGER_ON_TOUCH, or any user-defined function
|
||||||
|
* see [API Reference](docs/Reference.md) for details
|
||||||
|
|
||||||
|
* **New ability to "remotely" trigger user-defined actions by repeatedly power-cycling the device**
|
||||||
|
|
||||||
|
* adds new homeSpan method `Span& setRebootCallback(void (*func)(uint8_t count), uint32_t upTime=5000)`
|
||||||
|
* the parameter *count*, which is passed by HomeSpan to *func*, indicates the number of "short" reboots that have occurred prior to the current reboot, where a "short" reboot is any that occurs before *upTime* milliseconds have elapsed
|
||||||
|
* can be use to remotely restore a device that is not easily accessible to a pre-defined state
|
||||||
|
* see [API Reference](docs/Reference.md) for details
|
||||||
|
|
||||||
|
* **Added two new Stepper Motor Drivers**
|
||||||
|
* **Stepper_UNIPOLAR**: a generic driver for any 4-wire center-tapped unipolar motor
|
||||||
|
* **Stepper_ULN2003A**: support for the ULN2003A driver board
|
||||||
|
* see [Stepper Motor Control Stepper](docs/Stepper.md) for details
|
||||||
|
|
||||||
|
* **Additional Web Log functionality**
|
||||||
|
|
||||||
|
* adds new homeSpan method `Span& setWebLogCallback(void (*func)(String &))`
|
||||||
|
* allows users to include additional data and custom HTML in the Web Log
|
||||||
|
* adds new homeSpan method `getWebLog(void (*f)(const char *, void *), void *args)`
|
||||||
|
* allows users to retrieve the underlying Web Log HTML from within sketch
|
||||||
|
* modified `enableWebLog()` so that it can be used to set the time from an NTP server without actually serving Web Log pages
|
||||||
* see [Message Logging](docs/Logging.md) for details
|
* see [Message Logging](docs/Logging.md) for details
|
||||||
|
|
||||||
* **Upgrades to Web Log Time Server initialization**
|
* **Added ability to "chain" *homeSpan* methods**
|
||||||
|
|
||||||
* the process for retrieving the time and date from an NTP server upon booting now runs in the background as a separate task
|
* converted various *homeSpan* methods that previously returned *void* to now return *Span &*
|
||||||
* HomeSpan is no longer blocked from running during the NTP query
|
* example: `homeSpan.setControlPin(21).setStatusPin(13);`
|
||||||
|
* see [API Reference](docs/Reference.md) for details
|
||||||
|
|
||||||
* **Adds new methods to disable HomeSpan's use of the USB Serial port**
|
* **Added ability to disable SpanPoint encryption**
|
||||||
|
|
||||||
* new Log Level, -1, causes HomeSpan to suppress all OUTPUT messages
|
* without encryption increases the maximum number of allowed SpanPoint devices from 7 to 20
|
||||||
* new homeSpan method `setSerialInputDisable(boolean val)` disables/re-enables HomeSpan's reading of CLI commands INPUT into the Arduino Serial Monitor
|
* see [SpanPoint](docs/NOW.md) for details
|
||||||
|
|
||||||
* **Adds ability to use a non-standard LED as the HomeSpan Status LED**
|
* **Other new *homeSpan* methods included in this release:**
|
||||||
|
|
||||||
* new homeSpan method `setStatusDevice(Blinkable *sDev)` sets the Status LED to the Blinkable object *sDev*
|
* `Span& setVerboseWifiReconnect()` - optionally suppresses "Trying to connect to..." messages
|
||||||
* allows an LED connected to a pin expander, or any other non-standard LED controller (such as an inverted LED that lights when a pin is LOW instead of HIGH) to be used as the HomeSpan Status LED
|
* `Span& setWifiCallbackAll()` - provides an optional callback every time WiFi is connected *or re-connected*
|
||||||
* see [Blinkable.md](docs/Blinkable.md) for details (including an example) on how to create Blinkable objects
|
* `TaskHandle_t getAutoPollTask()` - returns the Task Handle for the HomeSpan Auto Poll Task
|
||||||
|
|
||||||
|
* **Removed dependencies on various "extra" `#include` files**
|
||||||
|
* the following \#include files are now embedded in *HomeSpan.h* and **should not be specified in any sketch:**
|
||||||
|
* *extras/Pixel.h*
|
||||||
|
* *extras/RFControl.h*
|
||||||
|
* *extras/PwmPin.h*
|
||||||
|
* *extras/StepperControl.h*
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> At present it is okay to include the above `#include` files in your sketch (they have no effect on the compiled code), but they will be deleted at some point in the future so please remove them from your sketches now to ensure forward compatibility with subsequent releases.
|
||||||
|
|
||||||
See [Releases](https://github.com/HomeSpan/HomeSpan/releases) for details on all changes and bug fixes included in this update.
|
See [Releases](https://github.com/HomeSpan/HomeSpan/releases) for details on all changes and bug fixes included in this update.
|
||||||
|
|
||||||
|
|
@ -116,9 +161,7 @@ Note that all documentation is version-controlled and tied to each branch. The
|
||||||
|
|
||||||
# External Resources
|
# External Resources
|
||||||
|
|
||||||
In addition to HomeSpan resources, developers who are new to HomeKit programming should download Apple's HomeKit Accessory Protocol Specification, Non-Commercial Version, Release R2 (HAP-R2). This document is unfortunately no longer available from Apple (perhaps because it was last updated July, 2019, and is now somewhat out-of-date). However, you may be able find copies of this document elsewhere on the web. Note Apple has not replaced the HAP-R2 document with any other versions for non-commercial use, and Apple's open-source [HomeKit ADK](https://github.com/apple/HomeKitADK) only reflects the original HAP-R2 specs (rather than all the latest Services and Characteristics available in HomeKit for commercial devices).
|
In addition to HomeSpan resources, developers who are new to HomeKit programming may find useful Chapters 8 and 9 of Apple's HomeKit Accessory Protocol Specification, Non-Commercial Version, Release R2 (HAP-R2). This document is unfortunately no longer available from Apple (perhaps because it was last updated July, 2019, and is now somewhat out-of-date). However, you may be able find copies of this document elsewhere on the web. Note Apple has not replaced the HAP-R2 document with any other versions for non-commercial use, and Apple's open-source [HomeKit ADK](https://github.com/apple/HomeKitADK) only reflects the original HAP-R2 specs (rather than all the latest Services and Characteristics available in HomeKit for commercial devices).
|
||||||
|
|
||||||
You ***do not*** need to read the entire HAP-R2 document. The whole point of HomeSpan is that it implements all the required HAP operations under the hood so you can focus on just programming whatever logic is needed to control your real-world appliances (lights, fans, RF remote controls, etc.) with the device. However, you will find Chapters 8 and 9 of the HAP guide to be an invaluable reference as it lists and describes all of the Services and Characteristics implemented in HomeSpan, many of which you will routinely utilize in your own HomeSpan sketches.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ To enable Web Logging (it's turned off by default), call the method `homeSpan.en
|
||||||
* the total number of WEBLOG() messages to be stored - older messages are discarded in favor of newer ones once the limit you set is reached
|
* the total number of WEBLOG() messages to be stored - older messages are discarded in favor of newer ones once the limit you set is reached
|
||||||
* the URL of an NTP time server - this is optional and only needed if you want to set the clock of the device at start-up
|
* the URL of an NTP time server - this is optional and only needed if you want to set the clock of the device at start-up
|
||||||
* the time zone for the device - this is only needed if an NTP time server has been specified
|
* the time zone for the device - this is only needed if an NTP time server has been specified
|
||||||
* the URL of the Web Log page - if unspecified, HomeSpan will serve the Web Log at a page named "status"
|
* the URL of the Web Log page - if unspecified, HomeSpan will serve the Web Log at a page named "status". If set to NULL, HomeSpan will process Web Log data but will *not* serve any Web Log pages to any HTTP requests. However, Web Log data in the form of a finished HTML page can still be accessed by the user by calling `homeSpan.getWebLog()` as described further below
|
||||||
|
|
||||||
Additional notes:
|
Additional notes:
|
||||||
|
|
||||||
|
|
@ -82,6 +82,62 @@ For example, the following CSS changes the background color of the Web Log page
|
||||||
|
|
||||||
Note that HomeSpan outputs the full content of the Web Log HTML, including whatever CSS you may have specified above, to the Serial Monitor whenever the Log Level is set to 1 or greater. Reviewing this output can be helpful when creating your own CSS.
|
Note that HomeSpan outputs the full content of the Web Log HTML, including whatever CSS you may have specified above, to the Serial Monitor whenever the Log Level is set to 1 or greater. Reviewing this output can be helpful when creating your own CSS.
|
||||||
|
|
||||||
|
### Adding User-Defined Data and/or Custom HTML
|
||||||
|
|
||||||
|
Homespan provides a hook into the text used to generate the Web Log that you can extend to add your own data to the initial table as well as more generally add any custom HTML.
|
||||||
|
|
||||||
|
To access this text, set a Web Log callback using `homeSpan.setWebLogCallback(void (*func)(String &htmlText))` where
|
||||||
|
|
||||||
|
* *func* is a function of type *void* that takes a single argument of type *String*, and
|
||||||
|
* *htmlText* will be set by HomeSpan to a String reference containing all the HTML text that the Web Log has already generated to produce the initial table.
|
||||||
|
|
||||||
|
To add your own data to the table, simply extend the String *htmlText* by adding as many `<tr>` and `<td>` HTML tags as needed. If you wish to end the table and add any other HTML, simple include the `</table>` tag in *htmlText*, and then add any other custom HTML. For example, the following function could be used to extend the initial Web Log table to show free DRAM, end the table, and provide a hot link to the HomeSpan Repo:
|
||||||
|
|
||||||
|
```C++
|
||||||
|
void extraData(String &r){
|
||||||
|
r+="<tr><td>Free DRAM:</td><td>" + String(esp_get_free_internal_heap_size()) + " bytes</td></tr>\n";
|
||||||
|
r+="</table><p><a href=\"https://github.com/HomeSpan/HomeSpan\">Click Here to Access HomeSpan Repo</a></p>";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To embed this custom HTML text in the Web Log, call `homeSpan.setWebLogCallback(extraData)` in your sketch.
|
||||||
|
|
||||||
|
### Accessing Web Log HTML from within your sketch
|
||||||
|
|
||||||
|
In addition to (or as an alternative to) having HomeSpan serve HTML Web Log pages in response to HTTP requests, users can directly access the HTML text for a Web Log page from within their sketch for customized processing and handling. Since the HTML for a Web Log page can be very large, HomeSpan only generates the HTML for a Web Log page when the page has been requested, and streams the HTML in sequential chunks of 1024 bytes in response to a Web Log HTTP request. It is therefore not possible for HomeSpan to simply provide the user with a `char *` pointer to the HTML text for a complete Web Log. Instead, HomeSpan provides the user with the following *homeSpan* method to trigger the production of a Web Log page and access the resulting HTML text whenever needed:
|
||||||
|
|
||||||
|
`getWebLog(void (*f)(const char *htmlText, void *data), void *userData)`
|
||||||
|
|
||||||
|
* *f()* - a user-defined function that returns `void` and takes two arguments:
|
||||||
|
* *htmlText* - a null-terminated `const char *` pointer to a chunk of HTML text (max 1024 bytes) provided by HomeSpan
|
||||||
|
* *data* - a `void *` pointer to any user-provided data, *userData*
|
||||||
|
* *userData* - a `void *` pointer to any optional user-provided data that is passed to *f()* as its second argument, *data*
|
||||||
|
|
||||||
|
When the above method is called from a sketch, HomeSpan will repeatedly call the user-defined function *f()* and provide sequential chunks of HTML text for the Web Log page as the first argument, *htmlText*. Once all HTML chunks have been sent to the function *f()*, HomeSpan calls *f()* one final time with *htmlText* set to NULL to indicate there are no more HTML chunks to be sent.
|
||||||
|
|
||||||
|
The primary purpose of this function is for the user to provide their own method of serving an HTML Web Log page, such as through a secure HTTPS channel. Note this channel can be in addition to, or instead of, HomeSpan's normal serving of Web Log pages through HTTP requests depending on whether or not the URL argument used in the `homeSpan.enableWebLog()` method was set to NULL (disabling HomeSpan from serving Web Log pages in response to HTTP requests).
|
||||||
|
|
||||||
|
The following psuedo-code snippet shows how `getWebLog()` can be used:
|
||||||
|
|
||||||
|
```C++
|
||||||
|
...
|
||||||
|
homeSpan.enableWebLog(50,"pool.ntp.org","UTC",NULL); // this enables the Web Log for 50 entries and sets the clock, but prevents HomeSpan from responding to any HTTP requests for a Web Log page
|
||||||
|
...
|
||||||
|
IF WEBLOG NEEDED THEN{
|
||||||
|
homeSpan.getWebLog(myWebLogHandler,NULL); // this triggers HomeSpan to produce the HTML text for a Web Log page and stream the data to myWebLogHandler without any extra user data
|
||||||
|
}
|
||||||
|
...
|
||||||
|
void myWebLogHandler(const char *htmlText, void *args){ // this is the user-defined Web Log handler (note the optional *arg parameter is not used in this example)
|
||||||
|
if(htmlText!=NULL){
|
||||||
|
DO SOMETHING WITH htmlText (e.g. transmit it to the user via an HTTPS connection)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
PERFORM ANY CLEAN-UP PROCESSING (e.g. close the HTTPS connection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
10
docs/NOW.md
10
docs/NOW.md
|
|
@ -61,6 +61,16 @@ Also note that regardless of whether or not the queue if full, if the size of a
|
||||||
* if used, this method must be called *before* the instantiation of any SpanPoint objects. Example: `SpanPoint::setPassword("MyPassword");`
|
* if used, this method must be called *before* the instantiation of any SpanPoint objects. Example: `SpanPoint::setPassword("MyPassword");`
|
||||||
* the same passphrase must be used among all devices that are communicating via SpanPoint, else the receiving device will not be able to decrypt messages it receives
|
* the same passphrase must be used among all devices that are communicating via SpanPoint, else the receiving device will not be able to decrypt messages it receives
|
||||||
|
|
||||||
|
* `static void setEncryption(boolean encrypt)`
|
||||||
|
|
||||||
|
* this *optional* **class-level** method provides the ability to enable or disable encryption according to whether *encrypt* is set to *true* or *false*
|
||||||
|
* by default, encryption is normally enabled (using the password above)
|
||||||
|
* if used, this method must be called *before* the instantiation of any SpanPoint objects. Example: `SpanPoint::setEncryption(false);` disables encryption for all SpanPoint connections
|
||||||
|
* note that this is a global setting - if SpanPoint encryption is disabled on the main device, it must also be disabled on every remote device, else communication between devices will fail
|
||||||
|
* enabling/disabling encryption impacts that total number of SpanPoint connections that can be supported by the ESP32's ESP-NOW functionality:
|
||||||
|
* with encryption enabled, the ESP32 can support a maximum of 7 ESP-NOW links (i.e. 7 instances of SpanPoint)
|
||||||
|
* with encryption disabled, the ESP32 can support a maximum of 20 ESP-NOW links (i.e. 20 instances of SpanPoint)
|
||||||
|
|
||||||
* `static void setChannelMask(uint16_t mask)`
|
* `static void setChannelMask(uint16_t mask)`
|
||||||
|
|
||||||
* this *optional* **class-level** method changes the default channel bitmask from 0x3FFE (i.e. 0011 1111 1111 1110) to *mask*
|
* this *optional* **class-level** method changes the default channel bitmask from 0x3FFE (i.e. 0011 1111 1111 1110) to *mask*
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
# Pulse Width Modulation (PWM)
|
# Pulse Width Modulation (PWM)
|
||||||
|
|
||||||
The ESP32 has up to 16 PWM channels that can be used to drive a variety of devices. HomeSpan includes an integrated PWM library with dedicated classes designed for controlling **Dimmable LEDs** as well as **Servo Motors**. Both classes are provided in a standalone header file that is accessed by placing the following near the top of your sketch:
|
The ESP32 has up to 16 PWM channels that can be used to drive a variety of devices. HomeSpan includes an integrated PWM library with dedicated classes designed for controlling **Dimmable LEDs** as well as **Servo Motors**.
|
||||||
|
|
||||||
`#include "extras/PwmPin.h"`
|
|
||||||
|
|
||||||
## *LedPin(uint8_t pin [,float level [,uint16_t frequency [,boolean invert]]])*
|
## *LedPin(uint8_t pin [,float level [,uint16_t frequency [,boolean invert]]])*
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,6 @@ Both classes allow you to individually set each of the "pixels" in a multi-pixel
|
||||||
|
|
||||||
The methods for both classes are nearly identical, which allows you to readily interchange code written for single-wire devices to use with two-wire devices (and vice-versa) with only minor modifications.
|
The methods for both classes are nearly identical, which allows you to readily interchange code written for single-wire devices to use with two-wire devices (and vice-versa) with only minor modifications.
|
||||||
|
|
||||||
Both classes are provided in a standalone header file that is accessed by placing the following near the top of your sketch:
|
|
||||||
|
|
||||||
`#include "extras/Pixel.h"`
|
|
||||||
|
|
||||||
## *Pixel(uint8_t pin, [boolean isRGBW])*
|
## *Pixel(uint8_t pin, [boolean isRGBW])*
|
||||||
|
|
||||||
Creating an instance of this **class** configures the specified *pin* to output a waveform signal suitable for controlling a single-wire, addressable RGB or RGBW LED device with an arbitrary number of pixels. Such devices typically contain SK6812 or WS2812 LEDs. Arguments, along with their defaults if left unspecified, are as follows:
|
Creating an instance of this **class** configures the specified *pin* to output a waveform signal suitable for controlling a single-wire, addressable RGB or RGBW LED device with an arbitrary number of pixels. Such devices typically contain SK6812 or WS2812 LEDs. Arguments, along with their defaults if left unspecified, are as follows:
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
# Remote Control Radio Frequency / Infrared Signal Generation
|
# Remote Control Radio Frequency / Infrared Signal Generation
|
||||||
|
|
||||||
The ESP32 has an on-chip signal-generator peripheral designed to drive an RF or IR transmitter. HomeSpan includes an easy-to-use library that interfaces with this peripheral so that with a few additional electronic components you can create a HomeSpan device that controls an RF or IR appliance directly from the Home App on your iPhone, or via Siri. The library is accessed by placing the following near the top of your sketch:
|
The ESP32 has an on-chip Remote Control (RMT) signal-generator designed to drive an RF or IR transmitter. HomeSpan includes a dedicated, easy-to-use class, **RFControl()**, that interfaces with the ESP32 RMT peripheral so that with a few additional electronic components you can create a HomeSpan device that controls an RF or IR appliance directly from the Home App on your iPhone, or via Siri.
|
||||||
|
|
||||||
`#include "extras/RFControl.h"`
|
|
||||||
|
|
||||||
## *RFControl(int pin, boolean refClock=true)*
|
## *RFControl(int pin, boolean refClock=true)*
|
||||||
|
|
||||||
|
|
@ -93,7 +91,6 @@ Below is a complete sketch that produces two different pulse trains with the sig
|
||||||
/* HomeSpan Remote Control Example */
|
/* HomeSpan Remote Control Example */
|
||||||
|
|
||||||
#include "HomeSpan.h" // include the HomeSpan library
|
#include "HomeSpan.h" // include the HomeSpan library
|
||||||
#include "extras/RFControl.h" // include RF Control Library
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ The HomeSpan Library is invoked by including *HomeSpan.h* in your Arduino sketch
|
||||||
|
|
||||||
## *homeSpan*
|
## *homeSpan*
|
||||||
|
|
||||||
At runtime HomeSpan will create a global **object** named `homeSpan` that supports the following methods:
|
At runtime HomeSpan will create a global **object** named `homeSpan` (of type *class Span*) that supports the following methods:
|
||||||
|
|
||||||
* `void begin(Category catID, const char *displayName, const char *hostNameBase, const char *modelName)`
|
* `void begin(Category catID, const char *displayName, const char *hostNameBase, const char *modelName)`
|
||||||
* initializes HomeSpan
|
* initializes HomeSpan
|
||||||
|
|
@ -26,21 +26,31 @@ At runtime HomeSpan will create a global **object** named `homeSpan` that suppor
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
The following **optional** `homeSpan` methods override various HomeSpan initialization parameters used in `begin()`, and therefore **should** be called before `begin()` to take effect. If a method is *not* called, HomeSpan uses the default parameter indicated below:
|
The following **optional** `homeSpan` methods override various HomeSpan initialization parameters used in `begin()`, and therefore **should** be called before `begin()` to take effect.
|
||||||
|
Methods with a return type of `Span&` return a reference to `homeSpan` itself and can thus be chained together (e.g. `homeSpan.setControlPin(21).setStatusPin(13);`). If a method is *not* called, HomeSpan uses the default parameter indicated below:
|
||||||
|
|
||||||
* `void setControlPin(uint8_t pin)`
|
* `Span& setControlPin(uint8_t pin, triggerType=PushButton::TRIGGER_ON_LOW)`
|
||||||
* sets the ESP32 pin to use for the HomeSpan Control Button (which must connect the specified pin to **ground** when pushed). If not specified, HomeSpan will assume there is no Control Button
|
* sets the ESP32 *pin* to use for the HomeSpan Control Button
|
||||||
|
* if this method is not called, HomeSpan will assume there is no Control Button
|
||||||
|
* the optional second argument, *triggerType*, configures the Control Button as follows:
|
||||||
|
* `PushButton::TRIGGER_ON_LOW` - triggers when *pin* is driven LOW
|
||||||
|
* suitable for buttons that connect *pin* to GROUND (this is the default when *triggerType* is not specified)
|
||||||
|
* `PushButton::TRIGGER_ON_HIGH` - triggers when *pin* is driven HIGH
|
||||||
|
* suitable for buttons that connect *pin* to VCC (typically 3.3V)
|
||||||
|
* `PushButton::TRIGGER_ON_TOUCH` - uses the device's touch-sensor peripheral to trigger when *pin* has been touched
|
||||||
|
* not available on ESP32-C3
|
||||||
|
* as an alternative, you can set *triggerType* to any user-defined function of the form `boolean(int arg)` to utilize any device as a Control Button. See **SpanButton** below for details
|
||||||
|
|
||||||
* `int getControlPin()`
|
* `int getControlPin()`
|
||||||
* returns the pin number of the HomeSpan Control Button as set by `setControlPin(pin)`, or -1 if no pin has been set
|
* returns the pin number of the HomeSpan Control Button as set by `setControlPin(pin)`, or -1 if no pin has been set
|
||||||
|
|
||||||
* `void setStatusPin(uint8_t pin)`
|
* `Span& setStatusPin(uint8_t pin)`
|
||||||
* sets the ESP32 pin to use for the HomeSpan Status LED
|
* sets the ESP32 *pin* to use for the HomeSpan Status LED
|
||||||
* assumes a standard LED will be connected to *pin*
|
* assumes a standard LED will be connected to *pin*
|
||||||
* if neither this method nor any equivalent method is called, HomeSpan will assume there is no Status LED
|
* if neither this method nor any equivalent method is called, HomeSpan will assume there is no Status LED
|
||||||
|
|
||||||
* `void setStatusPixel(uint8_t pin, float h=0, float s=100, float v=100)`
|
* `Span& setStatusPixel(uint8_t pin, float h=0, float s=100, float v=100)`
|
||||||
* sets the ESP32 pin to use for the HomeSpan Status LED
|
* sets the ESP32 *pin* to use for the HomeSpan Status LED
|
||||||
* this method is an *alternative* to using `setStatusPin()` above
|
* this method is an *alternative* to using `setStatusPin()` above
|
||||||
* assumes an RGB NeoPixel (or equivalent) will be connected to *pin*
|
* assumes an RGB NeoPixel (or equivalent) will be connected to *pin*
|
||||||
* works well with ESP32 boards that have a built-in NeoPixel LED, though adding an external NeoPixel is fine
|
* works well with ESP32 boards that have a built-in NeoPixel LED, though adding an external NeoPixel is fine
|
||||||
|
|
@ -52,14 +62,14 @@ The following **optional** `homeSpan` methods override various HomeSpan initiali
|
||||||
* example: `homeSpan.setStatusPixel(8,120,100,20)` sets the Status LED to light green using a NeoPixel attached to pin 8
|
* example: `homeSpan.setStatusPixel(8,120,100,20)` sets the Status LED to light green using a NeoPixel attached to pin 8
|
||||||
* if neither this method nor any equivalent method is called, HomeSpan will assume there is no Status LED
|
* if neither this method nor any equivalent method is called, HomeSpan will assume there is no Status LED
|
||||||
|
|
||||||
* `void setStatusDevice(Blinkable *sDev)`
|
* `Span& setStatusDevice(Blinkable *sDev)`
|
||||||
* sets the Status LED to a user-specified Blinkable device, *sDev*
|
* sets the Status LED to a user-specified Blinkable device, *sDev*
|
||||||
* this method is an *alternative* to using either `setStatusPin()` or `setStatusPixel()` above
|
* this method is an *alternative* to using either `setStatusPin()` or `setStatusPixel()` above
|
||||||
* see [Blinkable](Blinkable.md) for details on how to create generic Blinkable devices
|
* see [Blinkable](Blinkable.md) for details on how to create generic Blinkable devices
|
||||||
* useful when using an LED connected to a pin expander, or other specialized driver, as the Status LED
|
* useful when using an LED connected to a pin expander, or other specialized driver, as the Status LED
|
||||||
* if neither this method nor any equivalent method is called, HomeSpan will assume there is no Status LED
|
* if neither this method nor any equivalent method is called, HomeSpan will assume there is no Status LED
|
||||||
|
|
||||||
* `void setStatusAutoOff(uint16_t duration)`
|
* `Span& setStatusAutoOff(uint16_t duration)`
|
||||||
* sets Status LED to automatically turn off after *duration* seconds
|
* sets Status LED to automatically turn off after *duration* seconds
|
||||||
* Status LED will automatically turn on, and duration timer will be reset, whenever HomeSpan activates a new blinking pattern
|
* Status LED will automatically turn on, and duration timer will be reset, whenever HomeSpan activates a new blinking pattern
|
||||||
* if *duration* is set to zero, auto-off is disabled (Status LED will remain on indefinitely)
|
* if *duration* is set to zero, auto-off is disabled (Status LED will remain on indefinitely)
|
||||||
|
|
@ -67,19 +77,19 @@ The following **optional** `homeSpan` methods override various HomeSpan initiali
|
||||||
* `int getStatusPin()`
|
* `int getStatusPin()`
|
||||||
* returns the pin number of the Status LED as set by `setStatusPin(pin)`, or -1 if no pin has been set
|
* returns the pin number of the Status LED as set by `setStatusPin(pin)`, or -1 if no pin has been set
|
||||||
|
|
||||||
* `void setApSSID(const char *ssid)`
|
* `Span& setApSSID(const char *ssid)`
|
||||||
* sets the SSID (network name) of the HomeSpan Setup Access Point (default="HomeSpan-Setup")
|
* sets the SSID (network name) of the HomeSpan Setup Access Point (default="HomeSpan-Setup")
|
||||||
|
|
||||||
* `void setApPassword(const char *pwd)`
|
* `Span& setApPassword(const char *pwd)`
|
||||||
* sets the password of the HomeSpan Setup Access Point (default="homespan")
|
* sets the password of the HomeSpan Setup Access Point (default="homespan")
|
||||||
|
|
||||||
* `void setApTimeout(uint16_t nSec)`
|
* `Span& setApTimeout(uint16_t nSec)`
|
||||||
* sets the duration (in seconds) that the HomeSpan Setup Access Point, once activated, stays alive before timing out (default=300 seconds)
|
* sets the duration (in seconds) that the HomeSpan Setup Access Point, once activated, stays alive before timing out (default=300 seconds)
|
||||||
|
|
||||||
* `void setCommandTimeout(uint16_t nSec)`
|
* `Span& setCommandTimeout(uint16_t nSec)`
|
||||||
* sets the duration (in seconds) that the HomeSpan End-User Command Mode, once activated, stays alive before timing out (default=120 seconds)
|
* sets the duration (in seconds) that the HomeSpan End-User Command Mode, once activated, stays alive before timing out (default=120 seconds)
|
||||||
|
|
||||||
* `void setLogLevel(int level)`
|
* `Span& setLogLevel(int level)`
|
||||||
* sets the logging level for diagnostic messages, where:
|
* sets the logging level for diagnostic messages, where:
|
||||||
* 0 = top-level HomeSpan status messages, and any `LOG0()` messages specified in the sketch by the user (default)
|
* 0 = top-level HomeSpan status messages, and any `LOG0()` messages specified in the sketch by the user (default)
|
||||||
* 1 = all HomeSpan status messages, and any `LOG1()` messages specified in the sketch by the user
|
* 1 = all HomeSpan status messages, and any `LOG1()` messages specified in the sketch by the user
|
||||||
|
|
@ -93,7 +103,7 @@ The following **optional** `homeSpan` methods override various HomeSpan initiali
|
||||||
* `int getLogLevel()`
|
* `int getLogLevel()`
|
||||||
* returns the current Log Level as set by `setLogLevel(level)`
|
* returns the current Log Level as set by `setLogLevel(level)`
|
||||||
|
|
||||||
* `void reserveSocketConnections(uint8_t nSockets)`
|
* `Span& reserveSocketConnections(uint8_t nSockets)`
|
||||||
* reserves *nSockets* network sockets for uses **other than** by the HomeSpan HAP Server for HomeKit Controller Connections
|
* reserves *nSockets* network sockets for uses **other than** by the HomeSpan HAP Server for HomeKit Controller Connections
|
||||||
* for sketches compiled under Arduino-ESP32 v2.0.1 or later, HomeSpan reserves 14 sockets for HAP Controller Connections
|
* for sketches compiled under Arduino-ESP32 v2.0.1 or later, HomeSpan reserves 14 sockets for HAP Controller Connections
|
||||||
* each call to `reserveSocketConnections(nSockets)` reduces this number by *nSockets*
|
* each call to `reserveSocketConnections(nSockets)` reduces this number by *nSockets*
|
||||||
|
|
@ -102,16 +112,16 @@ The following **optional** `homeSpan` methods override various HomeSpan initiali
|
||||||
* note you do not need to separately reserve sockets for built-in HomeSpan functionality
|
* note you do not need to separately reserve sockets for built-in HomeSpan functionality
|
||||||
* for example, `enableOTA()` already contains an embedded call to `reserveSocketConnections(1)` since HomeSpan knows one socket must be reserved to support OTA
|
* for example, `enableOTA()` already contains an embedded call to `reserveSocketConnections(1)` since HomeSpan knows one socket must be reserved to support OTA
|
||||||
|
|
||||||
* `void setPortNum(uint16_t port)`
|
* `Span& setPortNum(uint16_t port)`
|
||||||
* sets the TCP port number used for communication between HomeKit and HomeSpan (default=80)
|
* sets the TCP port number used for communication between HomeKit and HomeSpan (default=80)
|
||||||
|
|
||||||
* `void setHostNameSuffix(const char *suffix)`
|
* `Span& setHostNameSuffix(const char *suffix)`
|
||||||
* sets the suffix HomeSpan appends to *hostNameBase* to create the full hostName
|
* sets the suffix HomeSpan appends to *hostNameBase* to create the full hostName
|
||||||
* if not specified, the default is for HomeSpan to append a dash "-" followed the 6-byte Accessory ID of the HomeSpan device
|
* if not specified, the default is for HomeSpan to append a dash "-" followed the 6-byte Accessory ID of the HomeSpan device
|
||||||
* setting *suffix* to a null string "" is permitted
|
* setting *suffix* to a null string "" is permitted
|
||||||
* example: `homeSpan.begin(Category::Fans, "Living Room Ceiling Fan", "LivingRoomFan");` will yield a default *hostName* of the form *LivingRoomFan-A1B2C3D4E5F6.local*. Calling `homeSpan.setHostNameSuffix("v2")` prior to `homeSpan.begin()` will instead yield a *hostName* of *LivingRoomFanv2.local*
|
* example: `homeSpan.begin(Category::Fans, "Living Room Ceiling Fan", "LivingRoomFan");` will yield a default *hostName* of the form *LivingRoomFan-A1B2C3D4E5F6.local*. Calling `homeSpan.setHostNameSuffix("v2")` prior to `homeSpan.begin()` will instead yield a *hostName* of *LivingRoomFanv2.local*
|
||||||
|
|
||||||
* `void setQRID(const char *id)`
|
* `Span& setQRID(const char *id)`
|
||||||
* changes the Setup ID, which is used for pairing a device with a [QR Code](QRCodes.md), from the HomeSpan default to *id*
|
* changes the Setup ID, which is used for pairing a device with a [QR Code](QRCodes.md), from the HomeSpan default to *id*
|
||||||
* the HomeSpan default is "HSPN" unless permanently changed for the device via the [HomeSpan CLI](CLI.md) using the 'Q' command
|
* the HomeSpan default is "HSPN" unless permanently changed for the device via the [HomeSpan CLI](CLI.md) using the 'Q' command
|
||||||
* *id* must be exactly 4 alphanumeric characters (0-9, A-Z, and a-z). If not, the request to change the Setup ID is silently ignored and the default is used instead
|
* *id* must be exactly 4 alphanumeric characters (0-9, A-Z, and a-z). If not, the request to change the Setup ID is silently ignored and the default is used instead
|
||||||
|
|
@ -135,11 +145,11 @@ The following **optional** `homeSpan` methods enable additional features and pro
|
||||||
* this command causes HomeSpan to ignore, but does not otherwise alter, any password stored using the 'O' command
|
* this command causes HomeSpan to ignore, but does not otherwise alter, any password stored using the 'O' command
|
||||||
* returns 0 if enabling OTA was successful, or -1 and reports an error to the Serial Monitor if not
|
* returns 0 if enabling OTA was successful, or -1 and reports an error to the Serial Monitor if not
|
||||||
|
|
||||||
* `void enableAutoStartAP()`
|
* `Span& enableAutoStartAP()`
|
||||||
* enables automatic start-up of WiFi Access Point if WiFi Credentials are **not** found at boot time
|
* enables automatic start-up of WiFi Access Point if WiFi Credentials are **not** found at boot time
|
||||||
* methods to alter the behavior of HomeSpan's Access Point, such as `setApTimeout()`, must be called prior to `enableAutoStartAP()` to have an effect
|
* methods to alter the behavior of HomeSpan's Access Point, such as `setApTimeout()`, must be called prior to `enableAutoStartAP()` to have an effect
|
||||||
|
|
||||||
* `void setApFunction(void (*func)())`
|
* `Span& setApFunction(void (*func)())`
|
||||||
* replaces HomeSpan's built-in WiFi Access Point with user-defined function *func*
|
* replaces HomeSpan's built-in WiFi Access Point with user-defined function *func*
|
||||||
* *func* must be of type *void* and have no arguments
|
* *func* must be of type *void* and have no arguments
|
||||||
* *func* will be called instead of HomeSpan's built-in WiFi Access Point whenever the Access Point is launched:
|
* *func* will be called instead of HomeSpan's built-in WiFi Access Point whenever the Access Point is launched:
|
||||||
|
|
@ -149,22 +159,30 @@ The following **optional** `homeSpan` methods enable additional features and pro
|
||||||
* after identifying the SSID and password of the desired network, *func* must call `setWifiCredentials()` to save and use these values
|
* after identifying the SSID and password of the desired network, *func* must call `setWifiCredentials()` to save and use these values
|
||||||
* it is recommended that *func* terminates by restarting the device using `ESP.restart()`. Upon restart HomeSpan will use the SSID and password just saved
|
* it is recommended that *func* terminates by restarting the device using `ESP.restart()`. Upon restart HomeSpan will use the SSID and password just saved
|
||||||
|
|
||||||
* `void setWifiCredentials(const char *ssid, const char *pwd)`
|
* `Span& setWifiCredentials(const char *ssid, const char *pwd)`
|
||||||
* sets the SSID (*ssid*) and password (*pwd*) of the WiFi network to which HomeSpan will connect
|
* sets the SSID (*ssid*) and password (*pwd*) of the WiFi network to which HomeSpan will connect
|
||||||
* *ssid* and *pwd* are automatically saved in HomeSpan's non-volatile storage (NVS) for retrieval when the device restarts
|
* *ssid* and *pwd* are automatically saved in HomeSpan's non-volatile storage (NVS) for retrieval when the device restarts
|
||||||
* note that the saved values are truncated if they exceed the maximum allowable characters (ssid=32; pwd=64)
|
* note that the saved values are truncated if they exceed the maximum allowable characters (ssid=32; pwd=64)
|
||||||
* :warning: SECURITY WARNING: The purpose of this function is to allow advanced users to *dynamically* set the device's WiFi Credentials using a customized Access Point function specified by `setApFunction(func)`. It it NOT recommended to use this function to hardcode your WiFi SSID and password directly into your sketch. Instead, use one of the more secure methods provided by HomeSpan, such as typing 'W' from the CLI, or launching HomeSpan's Access Point, to set your WiFi credentials without hardcoding them into your sketch
|
* :warning: SECURITY WARNING: The purpose of this function is to allow advanced users to *dynamically* set the device's WiFi Credentials using a customized Access Point function specified by `setApFunction(func)`. It it NOT recommended to use this function to hardcode your WiFi SSID and password directly into your sketch. Instead, use one of the more secure methods provided by HomeSpan, such as typing 'W' from the CLI, or launching HomeSpan's Access Point, to set your WiFi credentials without hardcoding them into your sketch
|
||||||
|
|
||||||
* `void setWifiCallback(void (*func)())`
|
* `Span& setVerboseWifiReconnect(bool verbose)`
|
||||||
* sets an optional user-defined callback function, *func*, to be called by HomeSpan upon start-up just after WiFi connectivity has been established. This one-time call to *func* is provided for users that are implementing other network-related services as part of their sketch, but that cannot be started until WiFi connectivity is established. The function *func* must be of type *void* and have no arguments
|
* when trying connecting to WiFi, HomeSpan normally logs "Trying to connect to..." messages to the Serial Monitor and the Web Log
|
||||||
|
* calling this method with *verbose* set to *false* supresses these messages
|
||||||
|
* calling this method a second time with *verbose* set to *true* re-activates these messages (default behavior)
|
||||||
|
|
||||||
* `void setPairCallback(void (*func)(boolean status))`
|
* `Span& setWifiCallback(void (*func)())`
|
||||||
|
* sets an optional user-defined callback function, *func*, to be called by HomeSpan upon start-up just after WiFi connectivity has been initially established. This one-time call to *func* is provided for users that are implementing other network-related services as part of their sketch, but that cannot be started until WiFi connectivity is established. The function *func* must be of type *void* and have no arguments
|
||||||
|
|
||||||
|
* `Span& setWifiCallbackAll(void (*func)(int count))`
|
||||||
|
* similar to `setWiFiCallback()` above, but the user-defined callback function, *func*, is called by HomeSpan *every* time WiFi connectivity has been established or re-established after a disconnect. The function *func* must be of type *void* and accept a single *int* argument, *count*, into which HomeSpan passes the number of times WiFi has been established or re-established (i.e. *count*=1 on initial WiFi connection; *count*=2 if re-established after the first disconnect, etc.)
|
||||||
|
|
||||||
|
* `Span& setPairCallback(void (*func)(boolean status))`
|
||||||
* sets an optional user-defined callback function, *func*, to be called by HomeSpan upon completion of pairing to a controller (*status=true*) or unpairing from a controller (*status=false*)
|
* sets an optional user-defined callback function, *func*, to be called by HomeSpan upon completion of pairing to a controller (*status=true*) or unpairing from a controller (*status=false*)
|
||||||
* this one-time call to *func* is provided for users that would like to trigger additional actions when the device is first paired, or the device is later unpaired
|
* this one-time call to *func* is provided for users that would like to trigger additional actions when the device is first paired, or the device is later unpaired
|
||||||
* note this *func* is **not** called upon start-up and should not be used to simply check whether a device is paired or unpaired. It is only called when pairing status changes
|
* note this *func* is **not** called upon start-up and should not be used to simply check whether a device is paired or unpaired. It is only called when pairing status changes
|
||||||
* the function *func* must be of type *void* and accept one *boolean* argument
|
* the function *func* must be of type *void* and accept one *boolean* argument
|
||||||
|
|
||||||
* `void setStatusCallback(void (*func)(HS_STATUS status))`
|
* `Span& setStatusCallback(void (*func)(HS_STATUS status))`
|
||||||
* sets an optional user-defined callback function, *func*, to be called by HomeSpan whenever its running state (e.g. WiFi Connecting, Pairing Needed...) changes in way that would alter the blinking pattern of the (optional) Status LED
|
* sets an optional user-defined callback function, *func*, to be called by HomeSpan whenever its running state (e.g. WiFi Connecting, Pairing Needed...) changes in way that would alter the blinking pattern of the (optional) Status LED
|
||||||
* if *func* is set, it will be called regardless of whether or not a Status LED has actually been defined
|
* if *func* is set, it will be called regardless of whether or not a Status LED has actually been defined
|
||||||
* this allows users to reflect changes to the current state of HomeSpan using alternative methods, such as outputting messages to an embedded LCD or E-Ink display
|
* this allows users to reflect changes to the current state of HomeSpan using alternative methods, such as outputting messages to an embedded LCD or E-Ink display
|
||||||
|
|
@ -174,14 +192,14 @@ The following **optional** `homeSpan` methods enable additional features and pro
|
||||||
* returns a pre-defined character string message representing *s*, which must be of enum type [HS_STATUS](HS_STATUS.md)
|
* returns a pre-defined character string message representing *s*, which must be of enum type [HS_STATUS](HS_STATUS.md)
|
||||||
* typically used in conjunction with `setStatusCallback()` above
|
* typically used in conjunction with `setStatusCallback()` above
|
||||||
|
|
||||||
* `void setPairingCode(const char *s)`
|
* `Span& setPairingCode(const char *s)`
|
||||||
* sets the Setup Pairing Code to *s*, which **must** be exactly eight numerical digits (no dashes)
|
* sets the Setup Pairing Code to *s*, which **must** be exactly eight numerical digits (no dashes)
|
||||||
* example: `homeSpan.setPairingCode("46637726");`
|
* example: `homeSpan.setPairingCode("46637726");`
|
||||||
* a hashed version of the Pairing Code will be saved to the device's non-volatile storage, overwriting any currently-stored Pairing Code
|
* a hashed version of the Pairing Code will be saved to the device's non-volatile storage, overwriting any currently-stored Pairing Code
|
||||||
* if *s* contains an invalid code, an error will be reported and the code will *not* be saved. Instead, the currently-stored Pairing Code (or the HomeSpan default Pairing Code if no code has been stored) will be used
|
* if *s* contains an invalid code, an error will be reported and the code will *not* be saved. Instead, the currently-stored Pairing Code (or the HomeSpan default Pairing Code if no code has been stored) will be used
|
||||||
* :warning: SECURTY WARNING: Hardcoding a device's Pairing Code into your sketch is considered a security risk and is **not** recommended. Instead, use one of the more secure methods provided by HomeSpan, such as typing 'S \<code\>' from the CLI, or launching HomeSpan's Access Point, to set your Pairing Code without hardcoding it into your sketch
|
* :warning: SECURTY WARNING: Hardcoding a device's Pairing Code into your sketch is considered a security risk and is **not** recommended. Instead, use one of the more secure methods provided by HomeSpan, such as typing 'S \<code\>' from the CLI, or launching HomeSpan's Access Point, to set your Pairing Code without hardcoding it into your sketch
|
||||||
|
|
||||||
* `void setSketchVersion(const char *sVer)`
|
* `Span& setSketchVersion(const char *sVer)`
|
||||||
* sets the version of a HomeSpan sketch to *sVer*, which can be any arbitrary character string
|
* sets the version of a HomeSpan sketch to *sVer*, which can be any arbitrary character string
|
||||||
* if unspecified, HomeSpan uses "n/a" as the default version text
|
* if unspecified, HomeSpan uses "n/a" as the default version text
|
||||||
* HomeSpan displays the version of the sketch in the Arduino IDE Serial Monitor upon start-up
|
* HomeSpan displays the version of the sketch in the Arduino IDE Serial Monitor upon start-up
|
||||||
|
|
@ -191,23 +209,38 @@ The following **optional** `homeSpan` methods enable additional features and pro
|
||||||
* returns the version of a HomeSpan sketch, as set using `void setSketchVersion(const char *sVer)`, or "n/a" if not set
|
* returns the version of a HomeSpan sketch, as set using `void setSketchVersion(const char *sVer)`, or "n/a" if not set
|
||||||
* can by called from anywhere in a sketch
|
* can by called from anywhere in a sketch
|
||||||
|
|
||||||
* `void enableWebLog(uint16_t maxEntries, const char *timeServerURL, const char *timeZone, const char *logURL)`
|
* `Span& enableWebLog(uint16_t maxEntries, const char *timeServerURL, const char *timeZone, const char *logURL)`
|
||||||
* enables a rolling Web Log that displays the most recent *maxEntries* entries created by the user with the `WEBLOG()` macro. Parameters, and their default values if unspecified, are as follows:
|
* enables a rolling Web Log that displays the most recent *maxEntries* entries created by the user with the `WEBLOG()` macro. Parameters, and their default values if unspecified, are as follows:
|
||||||
* *maxEntries* - maximum number of (most recent) entries to save. If unspecified, defaults to 0, in which case the Web Log will only display status without any log entries
|
* *maxEntries* - maximum number of (most recent) entries to save. If unspecified, defaults to 0, in which case the Web Log will only display status without any log entries
|
||||||
* *timeServerURL* - the URL of a time server that HomeSpan will use to set its clock upon startup after a WiFi connection has been established. If unspecified, defaults to NULL, in which case HomeSpan skips setting the device clock
|
* *timeServerURL* - the URL of a time server that HomeSpan will use to set its clock upon startup after a WiFi connection has been established. HomeSpan will reserve one extra socket connection when a time server is specified. If unspecified, defaults to NULL, in which case HomeSpan skips setting the device clock
|
||||||
* *timeZone* - specifies the time zone to use for setting the clock. Uses standard Unix timezone formatting as interpreted by Espressif IDF. Note the IDF uses a somewhat non-intuitive convention such that a timezone of "UTC+5:00" *subtracts* 5 hours from UTC time, and "UTC-5:00" *adds* 5 hours to UTC time. If *serverURL=NULL* this field is ignored; if *serverURL!=NULL* this field is required
|
* *timeZone* - specifies the time zone to use for setting the clock. Uses POSIX.1 format only and does not support the *Time Zone Database*, or *tzdata*. As per [GNU libc documentation for TZ](https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html), *the offset specifies the time value you must **add to the local time** to get a Coordinated Universal Time value*. "UTC+5:00" means that local time + 5 hours give UTC time. See the GNU libc documentation for some examples, including how to specify North American Eastern Standard Time (EST) and Eastern Daylight Time (EDT) with start and end dates of EDT. If *serverURL=NULL* this field is ignored; if *serverURL!=NULL* this field is required
|
||||||
* *logURL* - the URL of the Web Log page for this device. If unspecified, defaults to "status"
|
* *logURL* - the URL of the Web Log page for this device. If unspecified, defaults to "status". If *logURL* is set to NULL HomeSpan will use the *timeServerURL* and *timeZone* parameters to set the clock, but it will *not* serve any Web Log pages in response to any HTTP requests. However, Web Log data is still accumulated internally and the resulting HTML can be accessed anytime by calling the `homeSpan.getWebLog()` method (see below)
|
||||||
* example: `homeSpan.enableWebLog(50,"pool.ntp.org","UTC-1:00","myLog");` creates a web log at the URL *http<nolink>://HomeSpan-\[DEVICE-ID\].local:\[TCP-PORT\]/myLog* that will display the 50 most-recent log messages produced with the WEBLOG() macro. Upon start-up (after a WiFi connection has been established) HomeSpan will attempt to set the device clock by calling the server "pool.ntp.org" and adjusting the time to be 1 hour ahead of UTC.
|
* example: `homeSpan.enableWebLog(50,"pool.ntp.org","UTC-1:00","myLog");` creates a web log at the URL *http<nolink>://HomeSpan-\[DEVICE-ID\].local:\[TCP-PORT\]/myLog* that will display the 50 most-recent log messages produced with the WEBLOG() macro. Upon start-up (after a WiFi connection has been established) HomeSpan will attempt to set the device clock by calling the server "pool.ntp.org" and adjusting the time to be 1 hour ahead of UTC.
|
||||||
* when attemping to connect to *timeServerURL*, HomeSpan waits 120 seconds for a response. This is done in the background and does not block HomeSpan from running as usual while it tries to set the time. If no response is received after the 120-second timeout period, HomeSpan assumes the server is unreachable and skips the clock-setting procedure. Use `setTimeServerTimeout()` to re-configure the 120-second timeout period to another value
|
* when attemping to connect to *timeServerURL*, HomeSpan waits 120 seconds for a response. This is done in the background and does not block HomeSpan from running as usual while it tries to set the time. If no response is received after the 120-second timeout period, HomeSpan assumes the server is unreachable and skips the clock-setting procedure. Use `setTimeServerTimeout()` to re-configure the 120-second timeout period to another value
|
||||||
* see [Message Logging](Logging.md) for complete details
|
* see [Message Logging](Logging.md) for complete details
|
||||||
|
|
||||||
* `void setTimeServerTimeout(uint32_t tSec)`
|
* `Span& setTimeServerTimeout(uint32_t tSec)`
|
||||||
* changes the default 120-second timeout period HomeSpan uses when `enableWebLog()` tries set the device clock from an internet time server to *tSec* seconds
|
* changes the default 120-second timeout period HomeSpan uses when `enableWebLog()` tries set the device clock from an internet time server to *tSec* seconds
|
||||||
|
|
||||||
* `void setWebLogCSS(const char *css)`
|
* `Span& setWebLogCSS(const char *css)`
|
||||||
* sets the format of the HomeSpan Web Log to the custom style sheet specified by *css*
|
* sets the format of the HomeSpan Web Log to the custom style sheet specified by *css*
|
||||||
* see [Message Logging](Logging.md) for details on how to construct *css*
|
* see [Message Logging](Logging.md) for details on how to construct *css*
|
||||||
|
|
||||||
|
* `Span& setWebLogCallback(void (*func)(String &htmlText))`
|
||||||
|
* sets an optional user-defined callback function, *func*, to be called by HomeSpan whenever the Web Log is produced
|
||||||
|
* allows user to add additional custom data to the initial table of the Web Log by **extending** the String *htmlText*, which is passed as a reference to *func*
|
||||||
|
* the function *func* must be of type *void* and accept one argument of type *String*
|
||||||
|
* see [Message Logging](Logging.md) for details on how to construct *htmlText*
|
||||||
|
|
||||||
|
* `void getWebLog(void (*f)(const char *htmlBuf, void *args), void *userData)`
|
||||||
|
* when called, HomeSpan *streams* the current Web Log HTML text, and any optionally-specified *userData*, directly to the user-defined function, *f()*, which should return a *void* and accept the following two arguments:
|
||||||
|
* *htmlBuf* - pointer to part of the HTML text for the Web Log page
|
||||||
|
* *args* - a pass-through of the *userData* argument
|
||||||
|
* if user-defined data is not needed, set *userData* to NULL
|
||||||
|
* to avoid creating a single large text buffer, HomeSpan splits the HTML for the Web Log into chunks of 1024 bytes and repeatedly calls *f()* until all the HTML has been streamed; HomeSpan then makes a final call to *f()* with *htmlBuf* set to NULL indicating to the user that the end of the HTML text has been reached
|
||||||
|
* this command is primarily used to redirect Web Log pages to a user-defined process for alternative handling, display, or transmission
|
||||||
|
* see [Message Logging](Logging.md) for more details
|
||||||
|
|
||||||
* `void processSerialCommand(const char *CLIcommand)`
|
* `void processSerialCommand(const char *CLIcommand)`
|
||||||
* processes the *CLIcommand* just as if were typed into the Serial Monitor
|
* processes the *CLIcommand* just as if were typed into the Serial Monitor
|
||||||
* allows for programmatic access to all CLI commands, included any custom commands defined by the user
|
* allows for programmatic access to all CLI commands, included any custom commands defined by the user
|
||||||
|
|
@ -215,7 +248,17 @@ The following **optional** `homeSpan` methods enable additional features and pro
|
||||||
* example: `homeSpan.processSerialCommand("A");` starts the HomeSpan Setup Access Point
|
* example: `homeSpan.processSerialCommand("A");` starts the HomeSpan Setup Access Point
|
||||||
* example: `homeSpan.processSerialCommand("Q HUB3");` changes the HomeKit Setup ID for QR Codes to "HUB3"
|
* example: `homeSpan.processSerialCommand("Q HUB3");` changes the HomeKit Setup ID for QR Codes to "HUB3"
|
||||||
|
|
||||||
* `void setSerialInputDisable(boolean val)`
|
* `Span& setRebootCallback(void (*func)(uint8_t count), uint32_t upTime)`
|
||||||
|
* sets an optional user-defined callback function, *func*, that is called (just once) when *upTime* milliseconds after rebooting have elapsed (default *upTime*=5000 ms if not specified)
|
||||||
|
* the function *func* must be of type *void* and accept one argument of type *uint8_t*
|
||||||
|
* the parameter *count*, which HomeSpan passes to *func*, indicates the number of "short" reboots that have occured prior to the current reboot, where a "short" reboot is any that occurs **before** *upTime* milliseconds have elapsed
|
||||||
|
* this allows the user to provide a generic form of input to a sketch by rapidly turning on/off power to the device a specified number of times, typically to provide a method of resetting some aspect of a remote device
|
||||||
|
* example using a lamba function:
|
||||||
|
* `homeSpan.setRebootCallback( [](uint8_t c) {if(c==3) homeSpan.processSerialCommand("X");} );`
|
||||||
|
* causes HomeSpan to run the 'X' Serial Command, which erases WiFi data, if the device is "short" rebooted exactly 3 times, where each reboot is for less than 5 seconds
|
||||||
|
* note that creating 3 short reboots means you actually cycle the power (or press the reset button) a total of 4 times, since the last time you allow the sketch to run without rebooting
|
||||||
|
|
||||||
|
* `Span& setSerialInputDisable(boolean val)`
|
||||||
* if *val* is true, disables HomeSpan from reading input from the Serial port
|
* if *val* is true, disables HomeSpan from reading input from the Serial port
|
||||||
* if *val* is false, re-enables HomeSpan reading input from the Serial port
|
* if *val* is false, re-enables HomeSpan reading input from the Serial port
|
||||||
* useful when the main USB Serial port is needed for reading data from an external Serial peripheral, rather than being used to read input from the Arduino Serial Monitor
|
* useful when the main USB Serial port is needed for reading data from an external Serial peripheral, rather than being used to read input from the Arduino Serial Monitor
|
||||||
|
|
@ -258,18 +301,21 @@ The following `homeSpan` methods are considered experimental, since not all use
|
||||||
|
|
||||||
* *stackSize* - size of stack, in bytes, used by the polling task. Default=8192 if unspecified
|
* *stackSize* - size of stack, in bytes, used by the polling task. Default=8192 if unspecified
|
||||||
* *priority* - priority at which task runs. Minimum is 1. Maximum is typically 24, but it depends on how the ESP32 operating system is configured. If you set it to an arbitrarily high value (e.g. 999), it will be set to the maximum priority allowed. Default=1 if unspecified
|
* *priority* - priority at which task runs. Minimum is 1. Maximum is typically 24, but it depends on how the ESP32 operating system is configured. If you set it to an arbitrarily high value (e.g. 999), it will be set to the maximum priority allowed. Default=1 if unspecified
|
||||||
* *cpu* - specifies the CPU on which the polling task will run. Valid values are 0 and 1. This paramater is ignored on single-cpu boards. Default=0 if unspecified
|
* *cpu* - specifies the CPU on which the polling task will run. Valid values are 0 and 1. This parameter is ignored on single-cpu boards. Default=0 if unspecified
|
||||||
* if used, **must** be placed in a sketch as the last line in the Arduino `setup()` method
|
* if used, **must** be placed in a sketch as the last line in the Arduino `setup()` method
|
||||||
* HomeSpan will throw and error and halt if both `poll()`and `autoPoll()` are used in the same sketch - either place `poll()` in the Arduino `loop()` method **or** place `autoPoll()` at the the end of the Arduino `setup()` method
|
* HomeSpan will throw and error and halt if both `poll()`and `autoPoll()` are used in the same sketch - either place `poll()` in the Arduino `loop()` method **or** place `autoPoll()` at the the end of the Arduino `setup()` method
|
||||||
* if this method is used, and you have no need to add your own code to the main Arduino `loop()`, you can safely skip defining a blank `void loop(){}` function in your sketch
|
* if this method is used, and you have no need to add your own code to the main Arduino `loop()`, you can safely skip defining a blank `void loop(){}` function in your sketch
|
||||||
* warning: if any code you add to the Arduino `loop()` method tries to alter any HomeSpan settings or functions running in the background `poll()` task, race conditions may yield undefined results
|
* warning: if any code you add to the Arduino `loop()` method tries to alter any HomeSpan settings or functions running in the background `poll()` task, race conditions may yield undefined results
|
||||||
|
|
||||||
|
* `TaskHandle_t getAutoPollTask()`
|
||||||
|
* returns the task handle for the Auto Poll Task, or NULL if Auto Polling has not been used
|
||||||
|
|
||||||
## *SpanAccessory(uint32_t aid)*
|
## *SpanAccessory(uint32_t aid)*
|
||||||
|
|
||||||
Creating an instance of this **class** adds a new HAP Accessory to the HomeSpan HAP Database.
|
Creating an instance of this **class** adds a new HAP Accessory to the HomeSpan HAP Database.
|
||||||
|
|
||||||
* every HomeSpan sketch requires at least one Accessory
|
* every HomeSpan sketch requires at least one Accessory
|
||||||
* a sketch can contain a maximum of 41 Accessories per sketch (if exceeded, a runtime error will the thrown and the sketch will halt)
|
* a sketch can contain a maximum of 150 Accessories per sketch (if exceeded, a runtime error will the thrown and the sketch will halt)
|
||||||
* there are no associated methods
|
* there are no associated methods
|
||||||
* the argument *aid* is optional.
|
* the argument *aid* is optional.
|
||||||
|
|
||||||
|
|
@ -496,7 +542,7 @@ Creating an instance of this **class** attaches a toggle-switch handler to the E
|
||||||
* 3=switch is closed (`SpanToggle::CLOSED`)
|
* 3=switch is closed (`SpanToggle::CLOSED`)
|
||||||
* 4=switch is open (`SpanToggle::OPEN`)
|
* 4=switch is open (`SpanToggle::OPEN`)
|
||||||
|
|
||||||
Note there are no *singleTime*, *longTime*, or *doubleTime* paramaters in the constructor since you can't single-press, double-press, or long-press a toggle switch. Instead, the constructor supports the single parameter *toggleTime* (default=5ms if left unspecified) that sets the minimum time at which the switch needs to be moved to the closed position in order to trigger a call to the `button()` method. This effectively "debounces" the toggle switch.
|
Note there are no *singleTime*, *longTime*, or *doubleTime* parameters in the constructor since you can't single-press, double-press, or long-press a toggle switch. Instead, the constructor supports the single parameter *toggleTime* (default=5ms if left unspecified) that sets the minimum time at which the switch needs to be moved to the closed position in order to trigger a call to the `button()` method. This effectively "debounces" the toggle switch.
|
||||||
|
|
||||||
SpanToggle also supports the following additional method:
|
SpanToggle also supports the following additional method:
|
||||||
|
|
||||||
|
|
@ -548,7 +594,7 @@ To create more than one user-defined command, simply create multiple instances o
|
||||||
|
|
||||||
Creates a custom Characteristic that can be added to any Service. Custom Characteristics are generally ignored by the Home App but may be used by other third-party applications (such as *Eve for HomeKit*). The first form should be used create numerical Characterstics (e.g., UINT8, BOOL...). The second form is used to STRING-based Characteristics. The third form is used for DATA-based (i.e. byte-array) Characteristics. Parameters are as follows (note that quotes should NOT be used in any of the macro parameters, except for *defaultValue* when applied to a STRING-based Characteristic):
|
Creates a custom Characteristic that can be added to any Service. Custom Characteristics are generally ignored by the Home App but may be used by other third-party applications (such as *Eve for HomeKit*). The first form should be used create numerical Characterstics (e.g., UINT8, BOOL...). The second form is used to STRING-based Characteristics. The third form is used for DATA-based (i.e. byte-array) Characteristics. Parameters are as follows (note that quotes should NOT be used in any of the macro parameters, except for *defaultValue* when applied to a STRING-based Characteristic):
|
||||||
|
|
||||||
* *name* - the name of the custom Characteristic. This will be added to the Characteristic namespace so that it is accessed the same as any HomeSpan Characteristic
|
* *name* - the name of the custom Characteristic. This will be added to the Characteristic namespace so that it is accessed the same as any HomeSpan Characteristic. Use UTF-8 coded string for non-ASCII characters.
|
||||||
* *uuid* - the UUID of the Characteristic as defined by the manufacturer. Must be *exactly* 36 characters in the form XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX, where *X* represent a valid hexidecimal digit. Leading zeros are required if needed as described more fully in HAP-R2 Section 6.6.1
|
* *uuid* - the UUID of the Characteristic as defined by the manufacturer. Must be *exactly* 36 characters in the form XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX, where *X* represent a valid hexidecimal digit. Leading zeros are required if needed as described more fully in HAP-R2 Section 6.6.1
|
||||||
* *perms* - additive list of permissions as described in HAP-R2 Table 6-4. Valid values are PR, PW, EV, AA, TW, HD, and WR
|
* *perms* - additive list of permissions as described in HAP-R2 Table 6-4. Valid values are PR, PW, EV, AA, TW, HD, and WR
|
||||||
* *format* - specifies the format of the Characteristic value, as described in HAP-R2 Table 6-5. Valid value are BOOL, UINT8, UINT16, UNIT32, UINT64, INT, and FLOAT (note that the HomeSpan does not presently support the TLV8 formats). Not applicable for the STRING or DATA Characteristic macros
|
* *format* - specifies the format of the Characteristic value, as described in HAP-R2 Table 6-5. Valid value are BOOL, UINT8, UINT16, UNIT32, UINT64, INT, and FLOAT (note that the HomeSpan does not presently support the TLV8 formats). Not applicable for the STRING or DATA Characteristic macros
|
||||||
|
|
|
||||||
|
|
@ -1,204 +1,454 @@
|
||||||
# HomeSpan Services and Characteristics
|
# HomeSpan Services and Characteristics
|
||||||
|
|
||||||
HomeSpan implements all HAP-R2 Services and Characteristics except for those that involve video or audio streaming, Apple TV, or advanced lock management (i.e. all HAP Services except those that require Characteristics with a TLV8 data type).
|
Below is a list of all Services supported by HomeSpan, along with a brief description of each Service. Click on the arrow next to the description of any Service to see a list of all Characteristics supported by the Service, as well as a description of each Characteristic.[^1]
|
||||||
|
|
||||||
|
A blue diamond (🔹) next to a Characteristic means it is **required** for that Service, otherwise it is optional. Information included for each Characteristic is as follows:
|
||||||
|
|
||||||
|
* **Format** - the native format of the Characteristic's value. Note that *string* means a standard C-string (i.e. char \*)
|
||||||
|
* **Perms**, where
|
||||||
|
* PR = Paired Read. This means HomeKit can read the value from HomeSpan[^2]
|
||||||
|
* PW = Paired Write. This means HomeKit can write the value to HomeSpan, which triggers a call to `update()`
|
||||||
|
* EV = Event Notification. This means HomeSpan can push notifications of value changes to HomeKit using `setVal()`
|
||||||
|
* **Min** / **Max** - the default minimum and maximum values allowed in HomeKit. For applicable numerical Characteristics, you can change the allowed minimum and maximum values using `setRange()`
|
||||||
|
* **Constants/Defaults**
|
||||||
|
* for enumerated Characteristics, a list of all allowed values in the form of pre-defined constant expressions and their equivalent numeric values. A check mark next to a constant indicates that it is the default value. If your Accessory does not support certain states of a Characteristic, you can change the allowed values using `setValidValues()`
|
||||||
|
* for all other Characteristics, the actual default value that is used if you do not specify one when instantiating the Characteristic
|
||||||
|
|
||||||
|
[^1]: The hexidecimal numbers in parentheses next to each Service and Characteristic represent the short-form of the Apple's UUID for that Service or Characteristic. These are provided for informational purposes only (you do not need to use them, or even know about them, to create HomeSpan sketches)
|
||||||
|
|
||||||
|
[^2]: Though rarely needed, you can change the permissions of a Characteristic using `setPerms()`, `addPerms()`, and `removePerms()`
|
||||||
|
|
||||||
HomeSpan Services and Characteristics are implemented as C++ Classes with names that exactly match the spelling and capitalization specified by Apple in Sections 8 and 9 of HAP-R2, but without any spaces. HomeSpan Services are defined in HomeSpan's `Service` namespace. HomeSpan Characteristics are defined in HomeSpan's `Characteristic` namespace. For example, HomeSpan defines the *Carbon Dioxide Sensor* Service (HAP Service 8.7) as `Service::CarbonDioxideSensor`, and the *Carbon Dioxide Detected* Characteristic (HAP Characteristic 9.16) as `Characteristic::CarbonDioxideDetected`.
|
HomeSpan Services and Characteristics are implemented as C++ Classes with names that exactly match the spelling and capitalization specified by Apple in Sections 8 and 9 of HAP-R2, but without any spaces. HomeSpan Services are defined in HomeSpan's `Service` namespace. HomeSpan Characteristics are defined in HomeSpan's `Characteristic` namespace. For example, HomeSpan defines the *Carbon Dioxide Sensor* Service (HAP Service 8.7) as `Service::CarbonDioxideSensor`, and the *Carbon Dioxide Detected* Characteristic (HAP Characteristic 9.16) as `Characteristic::CarbonDioxideDetected`.
|
||||||
|
|
||||||
HomeSpan Services and Characteristics are instantiated with a C++ `new` command. Services do not take any arguments, whereas Characteristics take a single, optional argument that is used to initialize the value of the Characteristic at startup. If this argument is not specified, HomeSpan will apply a reasonable [default value](#characteristic-types-and-defaults) based on the Characteristic's type and allowed range.
|
The pre-defined constant expressions for enumerated Characteristics are in namespaces that match the name of the Characteristic. For example, to set the *Air Quality* Characteristic of an *Air Quality Sensor* Service, you could use `setVal(Characteristic::AirQuality::GOOD)` or, equivalently, `setVal(2)`.[^3]
|
||||||
|
|
||||||
A list of all HomeSpan Services is provided in the table below. For each Service the table also indicates which Characteristics are required and which are optional. For example, a dimmable light bulb could be configured in HomeSpan as such:
|
[^3]: Note that a Characteristic's pre-defined constants are inherited by objects that you create from that Characteristic. This means that if you create a pointer to the *AirQuality* Characteristic using `Characteristic::AirQuality *air = new Characteristic::AirQuality;` then later in your code you can either say<br> `air->setVal(Characteristic::AirQuality::GOOD)` or `air->GOOD`. Both will work, but the latter is much easier to type.
|
||||||
|
|
||||||
```C++
|
<!-- AUTOGENERATED_TEXT. DO NOT EDIT THIS LINE OR ANYTHING BELOW -->
|
||||||
new Service::LightBulb(); // instantiate a Light Bulb Service
|
|
||||||
new Characteristic:On(); // instantiate the required On Characteristic without setting initial value
|
|
||||||
new Characteristic::Brightness(50); // instantiate an optional Brightness Characteristic and set initial value to 50%
|
|
||||||
new Characteristic::Name("Living Room Lamp"); // instantiate an optional Name Characteristic for this Service, and set to "Living Room Lamp"
|
|
||||||
```
|
|
||||||
|
|
||||||
Please see Sections 8 and 9 of HAP-R2 for a complete description of all HAP Services and Characteristics. Note that HomeSpan's Service and Characteristic Classes already contain all the required HAP fields, such as the UUID, Format, and Permissions, so you don't need to specify any of these parameters.
|
|
||||||
|
|
||||||
Additionally, when first starting up, HomeSpan begins by validating the device's configuration to ensure each Service you instantiate includes all required Characteristics, but does not include any Characteristics that are neither required nor optional. If any errors are found, HomeSpan reports them to the Arduino Serial Monitor.
|
## MANDATORY SERVICES
|
||||||
|
### AccessoryInformation (3E)
|
||||||
|
<i> Required Identification Information. For each Accessory in a HomeSpan device this must be included as the first Service.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>Identify (14) :small_blue_diamond:</b><ul><li> triggers an update when HomeKit wants HomeSpan to run its identification routine for an Accessory</li></ul></td><td align="center">bool</td><td align="center">PW</td><td align="center">1</td><td align="center">1</td><td><ul><li><span>RUN_ID (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>Name (23) </b><ul><li> default display name of the Accessory</li></ul></td><td align="center">string</td><td align="center">PR</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
<tr><td><b>FirmwareRevision (52) </b><ul><li> must be in form x[.y[.z]] - informational only</li></ul></td><td align="center">string</td><td align="center">PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"1.0.0"</td></tr>
|
||||||
|
<tr><td><b>Manufacturer (20) </b><ul><li> any string - informational only</li></ul></td><td align="center">string</td><td align="center">PR</td><td align="center">-</td><td align="center">-</td><td align="center">"HomeSpan"</td></tr>
|
||||||
|
<tr><td><b>Model (21) </b><ul><li> any string - informational only</li></ul></td><td align="center">string</td><td align="center">PR</td><td align="center">-</td><td align="center">-</td><td align="center">"HomeSpan-ESP32"</td></tr>
|
||||||
|
<tr><td><b>SerialNumber (30) </b><ul><li> any string - informational only</li></ul></td><td align="center">string</td><td align="center">PR</td><td align="center">-</td><td align="center">-</td><td align="center">"HS-12345"</td></tr>
|
||||||
|
<tr><td><b>HardwareRevision (53) </b><ul><li> must be in form x[.y[.z]] - informational only</li></ul></td><td align="center">string</td><td align="center">PR</td><td align="center">-</td><td align="center">-</td><td align="center">"1.0.0"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
### Service List
|
## LIGHTS, POWER, AND SWITCHES
|
||||||
|
### BatteryService (96)
|
||||||
|
<i> Defines a standalone Battery Service.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>BatteryLevel (68) :small_blue_diamond:</b><ul><li> measured as a percentage</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">100</td><td align="center">100</td></tr>
|
||||||
|
<tr><td><b>ChargingState (8F) :small_blue_diamond:</b><ul><li> indicates state of battery charging</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">2</td><td><ul><li><span>NOT_CHARGING (0) </span>:heavy_check_mark:</li><li><span>CHARGING (1) </span></li><li><span>NOT_CHARGEABLE (2) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusLowBattery (79) :small_blue_diamond:</b><ul><li> indicates state of battery</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_LOW_BATTERY (0) </span>:heavy_check_mark:</li><li><span>LOW_BATTERY (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|Service|Required Characteristics|Optional Characteristics|
|
### LightBulb (43)
|
||||||
|-|-|-|
|
<i> Defines any type of Light.</i><br><table>
|
||||||
|AccessoryInformation|Identify|FirmwareRevision<br>Manufacturer<br>Model<br>Name<br>SerialNumber<br>HardwareRevision<br>AccessoryFlags|
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|AirPurifier|Active<br>CurrentAirPurifierState<br>TargetAirPurifierState|Name<br>RotationSpeed<br>SwingMode<br>LockPhysicalControls|
|
<tr><td><b>On (25) :small_blue_diamond:</b><ul><li> indicates if the Service is active/on</li></ul></td><td align="center">bool</td><td align="center">PR+PW+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>OFF (0) </span>:heavy_check_mark:</li><li><span>ON (1) </span></li></ul></td></tr>
|
||||||
|AirQualitySensor|AirQuality|Name<br>OzoneDensity<br>NitrogenDioxideDensity<br>SulphurDioxideDensity<br>PM25Density<br>PM10Density<br>VOCDensity<br>StatusActive<br>StatusFault<br>StatusTampered<br>StatusLowBattery|
|
<tr><td><b>Brightness (8) </b><ul><li> measured as a percentage</li></ul></td><td align="center">int</td><td align="center">PR+PW+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|BatteryService|BatteryLevel<br>ChargingState<br>StatusLowBattery|Name|
|
<tr><td><b>Hue (13) </b><ul><li> color (in degrees) from red (0) to green (120) to blue (240) and back to red (360)</li></ul></td><td align="center">float</td><td align="center">PR+PW+EV</td><td align="center">0</td><td align="center">360</td><td align="center">0</td></tr>
|
||||||
|CarbonDioxideSensor|CarbonDioxideDetected|Name<br>StatusActive<br>StatusFault<br>StatusTampered<br>StatusLowBattery<br>CarbonDioxideLevel<br>CarbonDioxidePeakLevel|
|
<tr><td><b>Saturation (2F) </b><ul><li> color saturation, measured as a percentage</li></ul></td><td align="center">float</td><td align="center">PR+PW+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|CarbonMonoxideSensor|CarbonMonoxideDetected|Name<br>StatusActive<br>StatusFault<br>StatusTampered<br>StatusLowBattery<br>CarbonMonoxideLevel<br>CarbonMonoxidePeakLevel|
|
<tr><td><b>ColorTemperature (CE) </b><ul><li> measured in inverse megaKelvin (= 1,000,000 / Kelvin)</li></ul></td><td align="center">uint32</td><td align="center">PR+PW+EV</td><td align="center">140</td><td align="center">500</td><td align="center">200</td></tr>
|
||||||
|ContactSensor|ContactSensorState|Name<br>StatusActive<br>StatusFault<br>StatusTampered<br>StatusLowBattery|
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|Door|CurrentPosition<br>TargetPosition<br>PositionState|Name<br>HoldPosition<br>ObstructionDetected|
|
</table><br>
|
||||||
|Doorbell|ProgrammableSwitchEvent|Name<br>Volume<br>Brightness|
|
|
||||||
|Fan|Active|Name<br>CurrentFanState<br>TargetFanState<br>RotationDirection<br>RotationSpeed<br>SwingMode<br>LockPhysicalControls|
|
|
||||||
|Faucet|Active|StatusFault<br>Name|
|
|
||||||
|FilterMaintenance|FilterChangeIndication|Name<br>FilterLifeLevel<br>ResetFilterIndication|
|
|
||||||
|GarageDoorOpener|CurrentDoorState<br>TargetDoorState<br>ObstructionDetected|LockCurrentState<br>LockTargetState<br>Name|
|
|
||||||
|HAPProtocolInformation|Version||HeaterCooler|Active<br>CurrentTemperature<br>CurrentHeaterCoolerState<br>TargetHeaterCoolerState|Name<br>RotationSpeed<br>TemperatureDisplayUnits<br>SwingMode<br>CoolingThresholdTemperature<br>HeatingThresholdTemperature<br>LockPhysicalControls|
|
|
||||||
|HumidifierDehumidifier|Active<br>CurrentRelativeHumidity<br>CurrentHumidifierDehumidifierState<br>TargetHumidifierDehumidifierState|Name<br>RelativeHumidityDehumidifierThreshold<br>RelativeHumidityHumidifierThreshold<br>RotationSpeed<br>SwingMode<br>WaterLevel<br>LockPhysicalControls|
|
|
||||||
|HumiditySensor|CurrentRelativeHumidity|Name<br>StatusActive<br>StatusFault<br>StatusTampered<br>StatusLowBattery|
|
|
||||||
|InputSource|Identifier|ConfiguredName<br>IsConfigured<br>CurrentVisibilityState<br>TargetVisibilityState|
|
|
||||||
|IrrigationSystem|Active<br>ProgramMode<br>InUse|RemainingDuration<br>StatusFault|
|
|
||||||
|LeakSensor|LeakDetected|Name<br>StatusActive<br>StatusFault<br>StatusTampered<br>StatusLowBattery|
|
|
||||||
|LightBulb|On|Brightness<br>Hue<br>Name<br>Saturation<br>ColorTemperature|
|
|
||||||
|LightSensor|CurrentAmbientLightLevel|Name<br>StatusActive<br>StatusFault<br>StatusTampered<br>StatusLowBattery|
|
|
||||||
|LockMechanism|LockCurrentState<br>LockTargetState|Name|
|
|
||||||
|Microphone|Mute|Name<br>Volume|
|
|
||||||
|MotionSensor|MotionDetected|Name<br>StatusActive<br>StatusFault<br>StatusTampered<br>StatusLowBattery|
|
|
||||||
|OccupancySensor|OccupancyDetected|Name<br>StatusActive<br>StatusFault<br>StatusTampered<br>StatusLowBattery|
|
|
||||||
|Outlet|On<br>OutletInUse|Name|
|
|
||||||
|SecuritySystem|SecuritySystemCurrentState<br>SecuritySystemTargetState|Name<br>SecuritySystemAlarmType<br>StatusFault<br>StatusTampered|
|
|
||||||
|ServiceLabel|ServiceLabelNamespace||Slat|CurrentSlatState<br>SlatType|Name<br>SwingMode<br>CurrentTiltAngle<br>TargetTiltAngle|
|
|
||||||
|SmokeSensor|SmokeDetected|Name<br>StatusActive<br>StatusFault<br>StatusTampered<br>StatusLowBattery|
|
|
||||||
|Speaker|Mute|Name<br>Volume|
|
|
||||||
|StatelessProgrammableSwitch|ProgrammableSwitchEvent|Name<br>ServiceLabelIndex|
|
|
||||||
|Switch|On|Name|
|
|
||||||
|Television|Active|ConfiguredName<br>ActiveIdentifier<br>RemoteKey<br>PowerModeSelection|
|
|
||||||
|TelevisionSpeaker|VolumeControlType<br>VolumeSelector|
|
|
||||||
|TemperatureSensor|CurrentTemperature|Name<br>StatusActive<br>StatusFault<br>StatusTampered<br>StatusLowBattery|
|
|
||||||
|Thermostat|CurrentHeatingCoolingState<br>TargetHeatingCoolingState<br>CurrentTemperature<br>TargetTemperature<br>TemperatureDisplayUnits|CoolingThresholdTemperature<br>CurrentRelativeHumidity<br>HeatingThresholdTemperature<br>Name<br>TargetRelativeHumidity|
|
|
||||||
|Valve|Active<br>InUse<br>ValveType|SetDuration<br>RemainingDuration<br>IsConfigured<br>ServiceLabelIndex<br>StatusFault<br>Name|
|
|
||||||
|Window|CurrentPosition<br>TargetPosition<br>PositionState|Name<br>HoldPosition<br>ObstructionDetected|
|
|
||||||
|WindowCovering|TargetPosition<br>CurrentPosition<br>PositionState|Name<br>HoldPosition<br>CurrentHorizontalTiltAngle<br>TargetHorizontalTiltAngle<br>CurrentVerticalTiltAngle<br>TargetVerticalTiltAngle<br>ObstructionDetected|
|
|
||||||
|
|
||||||
### Characteristic Types and Defaults
|
### Outlet (47)
|
||||||
|
<i> Defines a controllable Outlet used to power any light or appliance.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>On (25) :small_blue_diamond:</b><ul><li> indicates if the Service is active/on</li></ul></td><td align="center">bool</td><td align="center">PR+PW+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>OFF (0) </span>:heavy_check_mark:</li><li><span>ON (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>OutletInUse (26) :small_blue_diamond:</b><ul><li> indicates if an appliance or light is plugged into the outlet, regardless of whether on or off </li></ul></td><td align="center">bool</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_IN_USE (0) </span>:heavy_check_mark:</li><li><span>IN_USE (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|Characteristic|Type|Default|Range|
|
### StatelessProgrammableSwitch (89)
|
||||||
|-|-|-|-|
|
<i> Defines a "Stateless" Programmable Switch that can be used to trigger actions in the Home App.</i><br><table>
|
||||||
AccessoryFlags|uint32_t|1|[1,1]|
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
Active|uint8_t|0|[0,1]|
|
<tr><td><b>ProgrammableSwitchEvent (73) :small_blue_diamond:</b><ul><li> specifies type of button press</li></ul></td><td align="center">uint8</td><td align="center">PR+EV+NV</td><td align="center">0</td><td align="center">2</td><td><ul><li><span>SINGLE_PRESS (0) </span>:heavy_check_mark:</li><li><span>DOUBLE_PRESS (1) </span></li><li><span>LONG_PRESS (2) </span></li></ul></td></tr>
|
||||||
ActiveIdentifier|uint32_t|0|[0,255]|
|
<tr><td><b>ServiceLabelIndex (CB) </b><ul><li> numerical index used to distinguish multiple copies of the same Service within an Accessory</li></ul></td><td align="center">uint8</td><td align="center">PR</td><td align="center">1</td><td align="center">255</td><td align="center">1</td></tr>
|
||||||
AirQuality|uint8_t|0|[0,5]|
|
</table><br>
|
||||||
BatteryLevel|uint8_t|0|[0,100]|
|
|
||||||
Brightness|int|0|[0,100]|
|
|
||||||
CarbonMonoxideLevel|double|0|[0,100]|
|
|
||||||
CarbonMonoxidePeakLevel|double|0|[0,100]|
|
|
||||||
CarbonMonoxideDetected|uint8_t|0|[0,1]|
|
|
||||||
CarbonDioxideLevel|double|0|[0,100000]|
|
|
||||||
CarbonDioxidePeakLevel|double|0|[0,100000]|
|
|
||||||
CarbonDioxideDetected|uint8_t|0|[0,1]|
|
|
||||||
ChargingState|uint8_t|0|[0,2]|
|
|
||||||
ClosedCaptions|uint8_t|0|[0,1]|
|
|
||||||
CoolingThresholdTemperature|double|10|[10,35]|
|
|
||||||
ColorTemperature|uint32_t|200|[140,500]|
|
|
||||||
ContactSensorState|uint8_t|1|[0,1]|
|
|
||||||
ConfiguredName|char \*|"unnamed"|||
|
|
||||||
CurrentAmbientLightLevel|double|1|[0.0001,100000]|
|
|
||||||
CurrentHorizontalTiltAngle|int|0|[-90,90]|
|
|
||||||
CurrentAirPurifierState|uint8_t|1|[0,2]|
|
|
||||||
CurrentSlatState|uint8_t|0|[0,2]|
|
|
||||||
CurrentPosition|uint8_t|0|[0,100]|
|
|
||||||
CurrentVerticalTiltAngle|int|0|[-90,90]|
|
|
||||||
CurrentVisibilityState|uint8_t|0|[0,1]|
|
|
||||||
CurrentHumidifierDehumidifierState|uint8_t|1|[0,3]|
|
|
||||||
CurrentDoorState|uint8_t|1|[0,4]|
|
|
||||||
CurrentFanState|uint8_t|1|[0,2]|
|
|
||||||
CurrentHeatingCoolingState|uint8_t|0|[0,2]|
|
|
||||||
CurrentHeaterCoolerState|uint8_t|1|[0,3]|
|
|
||||||
CurrentMediaState|uint8_t|0|[0,5]|
|
|
||||||
CurrentRelativeHumidity|double|0|[0,100]|
|
|
||||||
CurrentTemperature|double|0|[0,100]|
|
|
||||||
CurrentTiltAngle|int|0|[-90,90]|
|
|
||||||
FilterLifeLevel|double|0|[0,100]|
|
|
||||||
FilterChangeIndication|uint8_t|0|[0,1]|
|
|
||||||
FirmwareRevision|char \*|"1.0.0"|||
|
|
||||||
HardwareRevision|char \*|"1.0.0"|||
|
|
||||||
HeatingThresholdTemperature|double|16|[0,25]|
|
|
||||||
HoldPosition|boolean|false|[0,1]|
|
|
||||||
Hue|double|0|[0,360]|
|
|
||||||
Identify|boolean|false|[0,1]|
|
|
||||||
Identifier|uint32_t|0|[0,255]|
|
|
||||||
InputDeviceType|uint8_t|0|[0,6]|
|
|
||||||
InputSourceType|uint8_t|0|[0,10]|
|
|
||||||
InUse|uint8_t|0|[0,1]|
|
|
||||||
IsConfigured|uint8_t|0|[0,1]|
|
|
||||||
LeakDetected|uint8_t|0|[0,1]|
|
|
||||||
LockCurrentState|uint8_t|0|[0,3]|
|
|
||||||
LockPhysicalControls|uint8_t|0|[0,1]|
|
|
||||||
LockTargetState|uint8_t|0|[0,1]|
|
|
||||||
Manufacturer|char \*|"HomeSpan"|||
|
|
||||||
Model|char \*|"HomeSpan-ESP32"|||
|
|
||||||
MotionDetected|boolean|false|[0,1]|
|
|
||||||
Mute|boolean|false|[0,1]|
|
|
||||||
Name|char \*|"unnamed"|||
|
|
||||||
NitrogenDioxideDensity|double|0|[0,1000]|
|
|
||||||
ObstructionDetected|boolean|false|[0,1]|
|
|
||||||
PM25Density|double|0|[0,1000]|
|
|
||||||
OccupancyDetected|uint8_t|0|[0,1]|
|
|
||||||
OutletInUse|boolean|false|[0,1]|
|
|
||||||
On|boolean|false|[0,1]|
|
|
||||||
OzoneDensity|double|0|[0,1000]|
|
|
||||||
PictureMode|uint8_t|0|[0,13]|
|
|
||||||
PM10Density|double|0|[0,1000]|
|
|
||||||
PositionState|uint8_t|2|[0,2]|
|
|
||||||
PowerModeSelection|uint8_t|0|[0,1]|
|
|
||||||
ProgramMode|uint8_t|0|[0,2]|
|
|
||||||
ProgrammableSwitchEvent|uint8_t|0|[0,2]|
|
|
||||||
RelativeHumidityDehumidifierThreshold|double|50|[0,100]|
|
|
||||||
RelativeHumidityHumidifierThreshold|double|50|[0,100]|
|
|
||||||
RemainingDuration|uint32_t|60|[0,3600]|
|
|
||||||
RemoteKey|uint8_t|0|[0,16]|
|
|
||||||
ResetFilterIndication|uint8_t|0|[1,1]|
|
|
||||||
RotationDirection|int|0|[0,1]|
|
|
||||||
RotationSpeed|double|0|[0,100]|
|
|
||||||
Saturation|double|0|[0,100]|
|
|
||||||
SecuritySystemAlarmType|uint8_t|0|[0,1]|
|
|
||||||
SecuritySystemCurrentState|uint8_t|3|[0,4]|
|
|
||||||
SecuritySystemTargetState|uint8_t|3|[0,3]|
|
|
||||||
SerialNumber|char \*|"HS-12345"|||
|
|
||||||
ServiceLabelIndex|uint8_t|1|[1,255]|
|
|
||||||
ServiceLabelNamespace|uint8_t|1|[0,1]|
|
|
||||||
SlatType|uint8_t|0|[0,1]|
|
|
||||||
SleepDiscoveryMode|uint8_t|0|[0,1]|
|
|
||||||
SmokeDetected|uint8_t|0|[0,1]|
|
|
||||||
StatusActive|boolean|true|[0,1]|
|
|
||||||
StatusFault|uint8_t|0|[0,1]|
|
|
||||||
StatusJammed|uint8_t|0|[0,1]|
|
|
||||||
StatusLowBattery|uint8_t|0|[0,1]|
|
|
||||||
StatusTampered|uint8_t|0|[0,1]|
|
|
||||||
SulphurDioxideDensity|double|0|[0,1000]|
|
|
||||||
SwingMode|uint8_t|0|[0,1]|
|
|
||||||
TargetAirPurifierState|uint8_t|1|[0,1]|
|
|
||||||
TargetFanState|uint8_t|1|[0,1]|
|
|
||||||
TargetTiltAngle|int|0|[-90,90]|
|
|
||||||
TargetHeaterCoolerState|uint8_t|0|[0,2]|
|
|
||||||
SetDuration|uint32_t|60|[0,3600]|
|
|
||||||
TargetHorizontalTiltAngle|int|0|[-90,90]|
|
|
||||||
TargetHumidifierDehumidifierState|uint8_t|0|[0,2]|
|
|
||||||
TargetPosition|uint8_t|0|[0,100]|
|
|
||||||
TargetDoorState|uint8_t|1|[0,1]|
|
|
||||||
TargetHeatingCoolingState|uint8_t|0|[0,3]|
|
|
||||||
TargetMediaState|uint8_t|0|[0,2]|
|
|
||||||
TargetRelativeHumidity|double|0|[0,100]|
|
|
||||||
TargetTemperature|double|16|[10,38]|
|
|
||||||
TargetVisibilityState|uint8_t|0|[0,1]|
|
|
||||||
TemperatureDisplayUnits|uint8_t|0|[0,1]|
|
|
||||||
TargetVerticalTiltAngle|int|0|[-90,90]|
|
|
||||||
ValveType|uint8_t|0|[0,3]|
|
|
||||||
Version|char \*|"1.0.0"|||
|
|
||||||
VOCDensity|double|0|[0,1000]|
|
|
||||||
Volume|uint8_t|0|[0,100]|
|
|
||||||
VolumeControlType|uint8_t|0|[0,3]|
|
|
||||||
VolumeSelector|uint8_t|0|[0,1]|
|
|
||||||
WaterLevel|double|0|[0,100]|
|
|
||||||
|
|
||||||
### HAP Format Codes (HAP-R2 Table 6-5)
|
### Switch (49)
|
||||||
|
<i> Defines a generic Switch.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>On (25) :small_blue_diamond:</b><ul><li> indicates if the Service is active/on</li></ul></td><td align="center">bool</td><td align="center">PR+PW+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>OFF (0) </span>:heavy_check_mark:</li><li><span>ON (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|HAP-R2 Format Code|HomeSpan C++ Type|
|
## HEATING, VENTILATION, AND AIR CONDITIONING (HVAC)
|
||||||
|------------------|-----------------|
|
### AirPurifier (BB)
|
||||||
|BOOL|boolean|
|
<i> Defines a basic Air Purifier with an optional fan and swing mode. Optional Linked Services: <b>FilterMaintenance</b>. Combine with an <b>AirSensor</b> Service for automated operations.</i><br><table>
|
||||||
|UINT8|uint8_t|
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|UINT16|uint16_t|
|
<tr><td><b>Active (B0) :small_blue_diamond:</b><ul><li> indicates if the Service is active/on</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>INACTIVE (0) </span>:heavy_check_mark:</li><li><span>ACTIVE (1) </span></li></ul></td></tr>
|
||||||
|UINT32|uint32_t|
|
<tr><td><b>CurrentAirPurifierState (A9) :small_blue_diamond:</b><ul><li> indicates current state of air purification</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">2</td><td><ul><li><span>INACTIVE (0) </span>:heavy_check_mark:</li><li><span>IDLE (1) </span></li><li><span>PURIFYING (2) </span></li></ul></td></tr>
|
||||||
|UINT64|uint64_t|
|
<tr><td><b>TargetAirPurifierState (A8) :small_blue_diamond:</b><ul><li> indicates desired state of air purifier</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>MANUAL (0) </span></li><li><span>AUTO (1) </span>:heavy_check_mark:</li></ul></td></tr>
|
||||||
|INT|int|
|
<tr><td><b>RotationSpeed (29) </b><ul><li> measured as a percentage</li></ul></td><td align="center">float</td><td align="center">PR+PW+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|FLOAT|double|
|
<tr><td><b>SwingMode (B6) </b><ul><li> indicates whether swing-mode is enabled</li></ul></td><td align="center">uint8</td><td align="center">PR+EV+PW</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>SWING_DISABLED (0) </span>:heavy_check_mark:</li><li><span>SWING_ENABLED (1) </span></li></ul></td></tr>
|
||||||
|STRING|char \*|
|
<tr><td><b>LockPhysicalControls (A7) </b><ul><li> indicates if local control lock is enabled</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>CONTROL_LOCK_DISABLED (0) </span>:heavy_check_mark:</li><li><span>CONTROL_LOCK_ENABLED (1) </span></li></ul></td></tr>
|
||||||
|TLV8|(not implemented)|
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|DATA|uint8_t *|
|
</table><br>
|
||||||
|
|
||||||
|
### Fan (B7)
|
||||||
|
<i> Defines a Fan. Combine with a <b>LightBulb</b> Service to create a Lighted Ceiling Fan.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>Active (B0) :small_blue_diamond:</b><ul><li> indicates if the Service is active/on</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>INACTIVE (0) </span>:heavy_check_mark:</li><li><span>ACTIVE (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>CurrentFanState (AF) </b><ul><li> indicates current state of a fan</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">2</td><td><ul><li><span>INACTIVE (0) </span></li><li><span>IDLE (1) </span>:heavy_check_mark:</li><li><span>BLOWING (2) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>TargetFanState (BF) </b><ul><li> indicates desired state of fan</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>MANUAL (0) </span></li><li><span>AUTO (1) </span>:heavy_check_mark:</li></ul></td></tr>
|
||||||
|
<tr><td><b>RotationDirection (28) </b><ul><li> indicates the rotation direction of a fan</li></ul></td><td align="center">int</td><td align="center">PR+PW+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>CLOCKWISE (0) </span>:heavy_check_mark:</li><li><span>COUNTERCLOCKWISE (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>RotationSpeed (29) </b><ul><li> measured as a percentage</li></ul></td><td align="center">float</td><td align="center">PR+PW+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>SwingMode (B6) </b><ul><li> indicates whether swing-mode is enabled</li></ul></td><td align="center">uint8</td><td align="center">PR+EV+PW</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>SWING_DISABLED (0) </span>:heavy_check_mark:</li><li><span>SWING_ENABLED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>LockPhysicalControls (A7) </b><ul><li> indicates if local control lock is enabled</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>CONTROL_LOCK_DISABLED (0) </span>:heavy_check_mark:</li><li><span>CONTROL_LOCK_ENABLED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### FilterMaintenance (BA)
|
||||||
|
<i> Defines a Filter Maintainence check. Use only as a Linked Service for the <b>AirPurifier</b> Service.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>FilterChangeIndication (AC) :small_blue_diamond:</b><ul><li> indicates state of filter</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NO_CHANGE_NEEDED (0) </span>:heavy_check_mark:</li><li><span>CHANGE_NEEDED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>FilterLifeLevel (AB) </b><ul><li> measured as a percentage of remaining life</li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">100</td><td align="center">100</td></tr>
|
||||||
|
<tr><td><b>ResetFilterIndication (AD) </b><ul><li> triggers an update when the user chooses to reset the <b>FilterChangeIndication</b> (only appears in Eve App, not Home App)</li></ul></td><td align="center">uint8</td><td align="center">PW</td><td align="center">1</td><td align="center">1</td><td><ul><li><span>RESET_FILTER (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### HeaterCooler (BC)
|
||||||
|
<i> Defines a standalone Heater, Cooler, or combined Heater/Cooler.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>Active (B0) :small_blue_diamond:</b><ul><li> indicates if the Service is active/on</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>INACTIVE (0) </span>:heavy_check_mark:</li><li><span>ACTIVE (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>CurrentTemperature (11) :small_blue_diamond:</b><ul><li> current temperature measured in Celsius</li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>CurrentHeaterCoolerState (B1) :small_blue_diamond:</b><ul><li> indicates whether appliance is currently heating, cooling, idle, or off</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">3</td><td><ul><li><span>INACTIVE (0) </span></li><li><span>IDLE (1) </span>:heavy_check_mark:</li><li><span>HEATING (2) </span></li><li><span>COOLING (3) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>TargetHeaterCoolerState (B2) :small_blue_diamond:</b><ul><li> indicates desired state of heater/cooler</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">2</td><td><ul><li><span>AUTO (0) </span>:heavy_check_mark:</li><li><span>HEAT (1) </span></li><li><span>COOL (2) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>RotationSpeed (29) </b><ul><li> measured as a percentage</li></ul></td><td align="center">float</td><td align="center">PR+PW+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>TemperatureDisplayUnits (36) </b><ul><li> indicates the desired units to display the temperature on the device itself (has no effect on Home App)</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>CELSIUS (0) </span>:heavy_check_mark:</li><li><span>FAHRENHEIT (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>SwingMode (B6) </b><ul><li> indicates whether swing-mode is enabled</li></ul></td><td align="center">uint8</td><td align="center">PR+EV+PW</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>SWING_DISABLED (0) </span>:heavy_check_mark:</li><li><span>SWING_ENABLED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>CoolingThresholdTemperature (D) </b><ul><li> cooling turns on when temperature (in Celsius) rises above this threshold</li></ul></td><td align="center">float</td><td align="center">PR+PW+EV</td><td align="center">10</td><td align="center">35</td><td align="center">10</td></tr>
|
||||||
|
<tr><td><b>HeatingThresholdTemperature (12) </b><ul><li> heating turns on when temperature (in Celsius) falls below this threshold</li></ul></td><td align="center">float</td><td align="center">PR+PW+EV</td><td align="center">0</td><td align="center">25</td><td align="center">16</td></tr>
|
||||||
|
<tr><td><b>LockPhysicalControls (A7) </b><ul><li> indicates if local control lock is enabled</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>CONTROL_LOCK_DISABLED (0) </span>:heavy_check_mark:</li><li><span>CONTROL_LOCK_ENABLED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### HumidifierDehumidifier (BD)
|
||||||
|
<i> Defines a Humidifer, Dehumidifier, or combined Humidifer/Dehumidifier.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>Active (B0) :small_blue_diamond:</b><ul><li> indicates if the Service is active/on</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>INACTIVE (0) </span>:heavy_check_mark:</li><li><span>ACTIVE (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>CurrentRelativeHumidity (10) :small_blue_diamond:</b><ul><li> current humidity measured as a percentage</li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>CurrentHumidifierDehumidifierState (B3) :small_blue_diamond:</b><ul><li> indicates current state of humidifier/dehumidifer</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">3</td><td><ul><li><span>INACTIVE (0) </span></li><li><span>IDLE (1) </span>:heavy_check_mark:</li><li><span>HUMIDIFYING (2) </span></li><li><span>DEHUMIDIFYING (3) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>TargetHumidifierDehumidifierState (B4) :small_blue_diamond:</b><ul><li> indicates desired state of humidifier/dehumidifier</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">2</td><td><ul><li><span>AUTO (0) </span>:heavy_check_mark:</li><li><span>HUMIDIFY (1) </span></li><li><span>DEHUMIDIFY (2) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>RelativeHumidityDehumidifierThreshold (C9) </b><ul><li> dehumidfier turns on when humidity rises above this threshold</li></ul></td><td align="center">float</td><td align="center">PR+PW+EV</td><td align="center">0</td><td align="center">100</td><td align="center">50</td></tr>
|
||||||
|
<tr><td><b>RelativeHumidityHumidifierThreshold (CA) </b><ul><li> humidfier turns on when humidity falls below this threshold</li></ul></td><td align="center">float</td><td align="center">PR+PW+EV</td><td align="center">0</td><td align="center">100</td><td align="center">50</td></tr>
|
||||||
|
<tr><td><b>RotationSpeed (29) </b><ul><li> measured as a percentage</li></ul></td><td align="center">float</td><td align="center">PR+PW+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>SwingMode (B6) </b><ul><li> indicates whether swing-mode is enabled</li></ul></td><td align="center">uint8</td><td align="center">PR+EV+PW</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>SWING_DISABLED (0) </span>:heavy_check_mark:</li><li><span>SWING_ENABLED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>WaterLevel (B5) </b><ul><li> measured as a percentage</li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>LockPhysicalControls (A7) </b><ul><li> indicates if local control lock is enabled</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>CONTROL_LOCK_DISABLED (0) </span>:heavy_check_mark:</li><li><span>CONTROL_LOCK_ENABLED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### Slat (B9)
|
||||||
|
<i> Defines a motorized ventilation Slat(s).</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>CurrentSlatState (AA) :small_blue_diamond:</b><ul><li> indicates current state of slats</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">2</td><td><ul><li><span>FIXED (0) </span>:heavy_check_mark:</li><li><span>JAMMED (1) </span></li><li><span>SWINGING (2) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>SlatType (C0) :small_blue_diamond:</b><ul><li> indicates the direction of a slat or group of slats</li></ul></td><td align="center">uint8</td><td align="center">PR</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>HORIZONTAL (0) </span>:heavy_check_mark:</li><li><span>VERTICAL (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>SwingMode (B6) </b><ul><li> indicates whether swing-mode is enabled</li></ul></td><td align="center">uint8</td><td align="center">PR+EV+PW</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>SWING_DISABLED (0) </span>:heavy_check_mark:</li><li><span>SWING_ENABLED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>CurrentTiltAngle (C1) </b><ul><li> current angle (in degrees) of slats from fully up or left (-90) to fully open (0) to fully down or right (90)</li></ul></td><td align="center">int</td><td align="center">PR+EV</td><td align="center">-90</td><td align="center">90</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>TargetTiltAngle (C2) </b><ul><li> indicated desired angle (in degrees) of slats from fully up or left (-90) to fully open (0) to fully down or right (90) </li></ul></td><td align="center">int</td><td align="center">PW+PR+EV</td><td align="center">-90</td><td align="center">90</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### Thermostat (4A)
|
||||||
|
<i> Defines a Thermostat used to control a furnace, air conditioner, or both.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>CurrentHeatingCoolingState (F) :small_blue_diamond:</b><ul><li> indicates whether appliance is currently heating, cooling, or just idle</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">2</td><td><ul><li><span>IDLE (0) </span>:heavy_check_mark:</li><li><span>HEATING (1) </span></li><li><span>COOLING (2) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>TargetHeatingCoolingState (33) :small_blue_diamond:</b><ul><li> indicates desired state of appliance</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">3</td><td><ul><li><span>OFF (0) </span>:heavy_check_mark:</li><li><span>HEAT (1) </span></li><li><span>COOL (2) </span></li><li><span>AUTO (3) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>CurrentTemperature (11) :small_blue_diamond:</b><ul><li> current temperature measured in Celsius</li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>TargetTemperature (35) :small_blue_diamond:</b><ul><li> indicates desired temperature measures in Celsius</li></ul></td><td align="center">float</td><td align="center">PW+PR+EV</td><td align="center">10</td><td align="center">38</td><td align="center">16</td></tr>
|
||||||
|
<tr><td><b>TemperatureDisplayUnits (36) :small_blue_diamond:</b><ul><li> indicates the desired units to display the temperature on the device itself (has no effect on Home App)</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>CELSIUS (0) </span>:heavy_check_mark:</li><li><span>FAHRENHEIT (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>CoolingThresholdTemperature (D) </b><ul><li> cooling turns on when temperature (in Celsius) rises above this threshold</li></ul></td><td align="center">float</td><td align="center">PR+PW+EV</td><td align="center">10</td><td align="center">35</td><td align="center">10</td></tr>
|
||||||
|
<tr><td><b>CurrentRelativeHumidity (10) </b><ul><li> current humidity measured as a percentage</li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>HeatingThresholdTemperature (12) </b><ul><li> heating turns on when temperature (in Celsius) falls below this threshold</li></ul></td><td align="center">float</td><td align="center">PR+PW+EV</td><td align="center">0</td><td align="center">25</td><td align="center">16</td></tr>
|
||||||
|
<tr><td><b>TargetRelativeHumidity (34) </b><ul><li> indicates desired humidity measured as a percentage</li></ul></td><td align="center">float</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
## STANDALONE SENSORS
|
||||||
|
### AirQualitySensor (8D)
|
||||||
|
<i> Defines an Air Quality Sensor. </i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>AirQuality (95) :small_blue_diamond:</b><ul><li> a subjective description</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">5</td><td><ul><li><span>UNKNOWN (0) </span>:heavy_check_mark:</li><li><span>EXCELLENT (1) </span></li><li><span>GOOD (2) </span></li><li><span>FAIR (3) </span></li><li><span>INFERIOR (4) </span></li><li><span>POOR (5) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>OzoneDensity (C3) </b><ul><li> measured in µg/m<sup>3</sup></li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1000</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>NitrogenDioxideDensity (C4) </b><ul><li> measured in µg/m<sup>3</sup></li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1000</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>SulphurDioxideDensity (C5) </b><ul><li> measured in µg/m<sup>3</sup></li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1000</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>PM25Density (C6) </b><ul><li> 2.5-micron particulate density, measured in µg/m<sup>3</sup></li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1000</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>PM10Density (C7) </b><ul><li> 10-micron particulate density, measured in µg/m<sup>3</sup></li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1000</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>VOCDensity (C8) </b><ul><li> measured in µg/m<sup>3</sup></li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1000</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>StatusActive (75) </b><ul><li> indicates whether the Service is properly functioning </li></ul></td><td align="center">bool</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_FUNCTIONING (0) </span></li><li><span>FUNCTIONING (1) </span>:heavy_check_mark:</li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusFault (77) </b><ul><li> indicates whether the Service has a fault (only appears in Eve App, not Home App)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NO_FAULT (0) </span>:heavy_check_mark:</li><li><span>FAULT (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusTampered (7A) </b><ul><li> indicates whether the Service has been tampered with</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_TAMPERED (0) </span>:heavy_check_mark:</li><li><span>TAMPERED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusLowBattery (79) </b><ul><li> indicates state of battery</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_LOW_BATTERY (0) </span>:heavy_check_mark:</li><li><span>LOW_BATTERY (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### CarbonDioxideSensor (97)
|
||||||
|
<i> Defines a Carbon Dioxide Sensor.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>CarbonDioxideDetected (92) :small_blue_diamond:</b><ul><li> indicates if abnormal level is detected</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NORMAL (0) </span>:heavy_check_mark:</li><li><span>ABNORMAL (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>CarbonDioxideLevel (93) </b><ul><li> measured on parts per million (ppm)</li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">100000</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>CarbonDioxidePeakLevel (94) </b><ul><li> measured in parts per million (ppm)</li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">100000</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>StatusActive (75) </b><ul><li> indicates whether the Service is properly functioning </li></ul></td><td align="center">bool</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_FUNCTIONING (0) </span></li><li><span>FUNCTIONING (1) </span>:heavy_check_mark:</li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusFault (77) </b><ul><li> indicates whether the Service has a fault (only appears in Eve App, not Home App)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NO_FAULT (0) </span>:heavy_check_mark:</li><li><span>FAULT (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusTampered (7A) </b><ul><li> indicates whether the Service has been tampered with</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_TAMPERED (0) </span>:heavy_check_mark:</li><li><span>TAMPERED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusLowBattery (79) </b><ul><li> indicates state of battery</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_LOW_BATTERY (0) </span>:heavy_check_mark:</li><li><span>LOW_BATTERY (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### CarbonMonoxideSensor (7F)
|
||||||
|
<i> Defines a Carbon Monoxide Sensor.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>CarbonMonoxideDetected (69) :small_blue_diamond:</b><ul><li> indicates if abnormal level is detected</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NORMAL (0) </span>:heavy_check_mark:</li><li><span>ABNORMAL (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>CarbonMonoxideLevel (90) </b><ul><li> measured in parts per million (ppm)</li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>CarbonMonoxidePeakLevel (91) </b><ul><li> measured in parts per million (ppm)</li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>StatusActive (75) </b><ul><li> indicates whether the Service is properly functioning </li></ul></td><td align="center">bool</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_FUNCTIONING (0) </span></li><li><span>FUNCTIONING (1) </span>:heavy_check_mark:</li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusFault (77) </b><ul><li> indicates whether the Service has a fault (only appears in Eve App, not Home App)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NO_FAULT (0) </span>:heavy_check_mark:</li><li><span>FAULT (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusTampered (7A) </b><ul><li> indicates whether the Service has been tampered with</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_TAMPERED (0) </span>:heavy_check_mark:</li><li><span>TAMPERED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusLowBattery (79) </b><ul><li> indicates state of battery</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_LOW_BATTERY (0) </span>:heavy_check_mark:</li><li><span>LOW_BATTERY (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### ContactSensor (80)
|
||||||
|
<i> Defines a Contact Sensor.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>ContactSensorState (6A) :small_blue_diamond:</b><ul><li> indictates if contact is detected (i.e. closed)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>DETECTED (0) </span></li><li><span>NOT_DETECTED (1) </span>:heavy_check_mark:</li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusActive (75) </b><ul><li> indicates whether the Service is properly functioning </li></ul></td><td align="center">bool</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_FUNCTIONING (0) </span></li><li><span>FUNCTIONING (1) </span>:heavy_check_mark:</li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusFault (77) </b><ul><li> indicates whether the Service has a fault (only appears in Eve App, not Home App)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NO_FAULT (0) </span>:heavy_check_mark:</li><li><span>FAULT (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusTampered (7A) </b><ul><li> indicates whether the Service has been tampered with</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_TAMPERED (0) </span>:heavy_check_mark:</li><li><span>TAMPERED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusLowBattery (79) </b><ul><li> indicates state of battery</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_LOW_BATTERY (0) </span>:heavy_check_mark:</li><li><span>LOW_BATTERY (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### HumiditySensor (82)
|
||||||
|
<i> Defines a Humidity Sensor.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>CurrentRelativeHumidity (10) :small_blue_diamond:</b><ul><li> current humidity measured as a percentage</li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>StatusActive (75) </b><ul><li> indicates whether the Service is properly functioning </li></ul></td><td align="center">bool</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_FUNCTIONING (0) </span></li><li><span>FUNCTIONING (1) </span>:heavy_check_mark:</li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusFault (77) </b><ul><li> indicates whether the Service has a fault (only appears in Eve App, not Home App)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NO_FAULT (0) </span>:heavy_check_mark:</li><li><span>FAULT (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusTampered (7A) </b><ul><li> indicates whether the Service has been tampered with</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_TAMPERED (0) </span>:heavy_check_mark:</li><li><span>TAMPERED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusLowBattery (79) </b><ul><li> indicates state of battery</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_LOW_BATTERY (0) </span>:heavy_check_mark:</li><li><span>LOW_BATTERY (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### LeakSensor (83)
|
||||||
|
<i> Defines a Leak Sensor.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>LeakDetected (70) :small_blue_diamond:</b><ul><li> indictates if a leak is detected</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_DETECTED (0) </span>:heavy_check_mark:</li><li><span>DETECTED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusActive (75) </b><ul><li> indicates whether the Service is properly functioning </li></ul></td><td align="center">bool</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_FUNCTIONING (0) </span></li><li><span>FUNCTIONING (1) </span>:heavy_check_mark:</li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusFault (77) </b><ul><li> indicates whether the Service has a fault (only appears in Eve App, not Home App)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NO_FAULT (0) </span>:heavy_check_mark:</li><li><span>FAULT (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusTampered (7A) </b><ul><li> indicates whether the Service has been tampered with</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_TAMPERED (0) </span>:heavy_check_mark:</li><li><span>TAMPERED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusLowBattery (79) </b><ul><li> indicates state of battery</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_LOW_BATTERY (0) </span>:heavy_check_mark:</li><li><span>LOW_BATTERY (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### LightSensor (84)
|
||||||
|
<i> Defines a Light Sensor.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>CurrentAmbientLightLevel (6B) :small_blue_diamond:</b><ul><li> measured in Lux (lumens/m<sup>2</sup></li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0.0001</td><td align="center">100000</td><td align="center">1</td></tr>
|
||||||
|
<tr><td><b>StatusActive (75) </b><ul><li> indicates whether the Service is properly functioning </li></ul></td><td align="center">bool</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_FUNCTIONING (0) </span></li><li><span>FUNCTIONING (1) </span>:heavy_check_mark:</li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusFault (77) </b><ul><li> indicates whether the Service has a fault (only appears in Eve App, not Home App)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NO_FAULT (0) </span>:heavy_check_mark:</li><li><span>FAULT (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusTampered (7A) </b><ul><li> indicates whether the Service has been tampered with</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_TAMPERED (0) </span>:heavy_check_mark:</li><li><span>TAMPERED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusLowBattery (79) </b><ul><li> indicates state of battery</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_LOW_BATTERY (0) </span>:heavy_check_mark:</li><li><span>LOW_BATTERY (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### MotionSensor (85)
|
||||||
|
<i> Defines a Motion Sensor.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>MotionDetected (22) :small_blue_diamond:</b><ul><li> indicates if motion is detected</li></ul></td><td align="center">bool</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_DETECTED (0) </span>:heavy_check_mark:</li><li><span>DETECTED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusActive (75) </b><ul><li> indicates whether the Service is properly functioning </li></ul></td><td align="center">bool</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_FUNCTIONING (0) </span></li><li><span>FUNCTIONING (1) </span>:heavy_check_mark:</li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusFault (77) </b><ul><li> indicates whether the Service has a fault (only appears in Eve App, not Home App)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NO_FAULT (0) </span>:heavy_check_mark:</li><li><span>FAULT (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusTampered (7A) </b><ul><li> indicates whether the Service has been tampered with</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_TAMPERED (0) </span>:heavy_check_mark:</li><li><span>TAMPERED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusLowBattery (79) </b><ul><li> indicates state of battery</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_LOW_BATTERY (0) </span>:heavy_check_mark:</li><li><span>LOW_BATTERY (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### OccupancySensor (86)
|
||||||
|
<i> Defines and Occupancy Sensor.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>OccupancyDetected (71) :small_blue_diamond:</b><ul><li> indicates if occupanccy is detected</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_DETECTED (0) </span>:heavy_check_mark:</li><li><span>DETECTED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusActive (75) </b><ul><li> indicates whether the Service is properly functioning </li></ul></td><td align="center">bool</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_FUNCTIONING (0) </span></li><li><span>FUNCTIONING (1) </span>:heavy_check_mark:</li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusFault (77) </b><ul><li> indicates whether the Service has a fault (only appears in Eve App, not Home App)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NO_FAULT (0) </span>:heavy_check_mark:</li><li><span>FAULT (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusTampered (7A) </b><ul><li> indicates whether the Service has been tampered with</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_TAMPERED (0) </span>:heavy_check_mark:</li><li><span>TAMPERED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusLowBattery (79) </b><ul><li> indicates state of battery</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_LOW_BATTERY (0) </span>:heavy_check_mark:</li><li><span>LOW_BATTERY (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### SmokeSensor (87)
|
||||||
|
<i> Defines a Smoke Sensor.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>SmokeDetected (76) :small_blue_diamond:</b><ul><li> indicates if smoke is detected</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_DETECTED (0) </span>:heavy_check_mark:</li><li><span>DETECTED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusActive (75) </b><ul><li> indicates whether the Service is properly functioning </li></ul></td><td align="center">bool</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_FUNCTIONING (0) </span></li><li><span>FUNCTIONING (1) </span>:heavy_check_mark:</li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusFault (77) </b><ul><li> indicates whether the Service has a fault (only appears in Eve App, not Home App)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NO_FAULT (0) </span>:heavy_check_mark:</li><li><span>FAULT (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusTampered (7A) </b><ul><li> indicates whether the Service has been tampered with</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_TAMPERED (0) </span>:heavy_check_mark:</li><li><span>TAMPERED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusLowBattery (79) </b><ul><li> indicates state of battery</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_LOW_BATTERY (0) </span>:heavy_check_mark:</li><li><span>LOW_BATTERY (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### TemperatureSensor (8A)
|
||||||
|
<i> Defines a Temperature Sensor.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>CurrentTemperature (11) :small_blue_diamond:</b><ul><li> current temperature measured in Celsius</li></ul></td><td align="center">float</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>StatusActive (75) </b><ul><li> indicates whether the Service is properly functioning </li></ul></td><td align="center">bool</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_FUNCTIONING (0) </span></li><li><span>FUNCTIONING (1) </span>:heavy_check_mark:</li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusFault (77) </b><ul><li> indicates whether the Service has a fault (only appears in Eve App, not Home App)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NO_FAULT (0) </span>:heavy_check_mark:</li><li><span>FAULT (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusTampered (7A) </b><ul><li> indicates whether the Service has been tampered with</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_TAMPERED (0) </span>:heavy_check_mark:</li><li><span>TAMPERED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusLowBattery (79) </b><ul><li> indicates state of battery</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_LOW_BATTERY (0) </span>:heavy_check_mark:</li><li><span>LOW_BATTERY (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
## DOORS, LOCKS, AND WINDOWS
|
||||||
|
### Door (81)
|
||||||
|
<i> Defines a motorized Door.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>CurrentPosition (6D) :small_blue_diamond:</b><ul><li> current position (as a percentage) from fully closed (0) to full open (100)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>TargetPosition (7C) :small_blue_diamond:</b><ul><li> indicates target position (as a percentage) from fully closed (0) to full open (100)</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>ObstructionDetected (24) </b><ul><li> indicates if obstruction is detected</li></ul></td><td align="center">bool</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_DETECTED (0) </span>:heavy_check_mark:</li><li><span>DETECTED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### Doorbell (121)
|
||||||
|
<i> Defines a Doorbell. Can be used on a standalone basis or in conjunction with a <b>LockMechanism</b> Service.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>ProgrammableSwitchEvent (73) :small_blue_diamond:</b><ul><li> specifies type of button press</li></ul></td><td align="center">uint8</td><td align="center">PR+EV+NV</td><td align="center">0</td><td align="center">2</td><td><ul><li><span>SINGLE_PRESS (0) </span>:heavy_check_mark:</li><li><span>DOUBLE_PRESS (1) </span></li><li><span>LONG_PRESS (2) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### GarageDoorOpener (41)
|
||||||
|
<i> Defines a motorized Garage Door Opener.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>CurrentDoorState (E) :small_blue_diamond:</b><ul><li> indicates current state of a door</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">4</td><td><ul><li><span>OPEN (0) </span></li><li><span>CLOSED (1) </span>:heavy_check_mark:</li><li><span>OPENING (2) </span></li><li><span>CLOSING (3) </span></li><li><span>STOPPED (4) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>TargetDoorState (32) :small_blue_diamond:</b><ul><li> indicates desired state of door</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>OPEN (0) </span></li><li><span>CLOSED (1) </span>:heavy_check_mark:</li></ul></td></tr>
|
||||||
|
<tr><td><b>ObstructionDetected (24) :small_blue_diamond:</b><ul><li> indicates if obstruction is detected</li></ul></td><td align="center">bool</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_DETECTED (0) </span>:heavy_check_mark:</li><li><span>DETECTED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>LockCurrentState (1D) </b><ul><li> indicates state of a lock</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">3</td><td><ul><li><span>UNLOCKED (0) </span>:heavy_check_mark:</li><li><span>LOCKED (1) </span></li><li><span>JAMMED (2) </span></li><li><span>UNKNOWN (3) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>LockTargetState (1E) </b><ul><li> indicates desired state of lock</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>UNLOCK (0) </span>:heavy_check_mark:</li><li><span>LOCK (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### LockMechanism (45)
|
||||||
|
<i> Defines an electronic Lock.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>LockCurrentState (1D) :small_blue_diamond:</b><ul><li> indicates state of a lock</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">3</td><td><ul><li><span>UNLOCKED (0) </span>:heavy_check_mark:</li><li><span>LOCKED (1) </span></li><li><span>JAMMED (2) </span></li><li><span>UNKNOWN (3) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>LockTargetState (1E) :small_blue_diamond:</b><ul><li> indicates desired state of lock</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>UNLOCK (0) </span>:heavy_check_mark:</li><li><span>LOCK (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### Window (8B)
|
||||||
|
<i> Defines a motorized Window.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>CurrentPosition (6D) :small_blue_diamond:</b><ul><li> current position (as a percentage) from fully closed (0) to full open (100)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>TargetPosition (7C) :small_blue_diamond:</b><ul><li> indicates target position (as a percentage) from fully closed (0) to full open (100)</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>ObstructionDetected (24) </b><ul><li> indicates if obstruction is detected</li></ul></td><td align="center">bool</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_DETECTED (0) </span>:heavy_check_mark:</li><li><span>DETECTED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### WindowCovering (8C)
|
||||||
|
<i> Defines a motorized Window Shade, Screen, Awning, etc.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>TargetPosition (7C) :small_blue_diamond:</b><ul><li> indicates target position (as a percentage) from fully closed (0) to full open (100)</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>CurrentPosition (6D) :small_blue_diamond:</b><ul><li> current position (as a percentage) from fully closed (0) to full open (100)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">100</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>CurrentHorizontalTiltAngle (6C) </b><ul><li> current angle (in degrees) of slats from fully up (-90) to fully open (0) to fully down (90) </li></ul></td><td align="center">int</td><td align="center">PR+EV</td><td align="center">-90</td><td align="center">90</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>TargetHorizontalTiltAngle (7B) </b><ul><li> indicates desired angle (in degrees) of slats from fully up (-90) to fully open (0) to fully down (90)</li></ul></td><td align="center">int</td><td align="center">PW+PR+EV</td><td align="center">-90</td><td align="center">90</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>CurrentVerticalTiltAngle (6E) </b><ul><li> current angle (in degrees) of slats from fully left (-90) to fully open (0) to fully right (90)</li></ul></td><td align="center">int</td><td align="center">PR+EV</td><td align="center">-90</td><td align="center">90</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>TargetVerticalTiltAngle (7D) </b><ul><li> indicates desired angle (in degrees) of slats from fully left (-90) to fully open (0) to fully right (90)</li></ul></td><td align="center">int</td><td align="center">PW+PR+EV</td><td align="center">-90</td><td align="center">90</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>ObstructionDetected (24) </b><ul><li> indicates if obstruction is detected</li></ul></td><td align="center">bool</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_DETECTED (0) </span>:heavy_check_mark:</li><li><span>DETECTED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
## WATER SYSTEMS
|
||||||
|
### Faucet (D7)
|
||||||
|
<i> Defines the master control for a multi-Valve appliance. Linked Services: <b>Valve</b> (at least one required), and <b>HeaterCooler</b> (optional).</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>Active (B0) :small_blue_diamond:</b><ul><li> indicates if the Service is active/on</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>INACTIVE (0) </span>:heavy_check_mark:</li><li><span>ACTIVE (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusFault (77) </b><ul><li> indicates whether the Service has a fault (only appears in Eve App, not Home App)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NO_FAULT (0) </span>:heavy_check_mark:</li><li><span>FAULT (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### IrrigationSystem (CF)
|
||||||
|
<i> Defines an Irrigation System. Linked Services: <b>Valve</b> Service (at least one required).</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>Active (B0) :small_blue_diamond:</b><ul><li> indicates if the Service is active/on</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>INACTIVE (0) </span>:heavy_check_mark:</li><li><span>ACTIVE (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ProgramMode (D1) :small_blue_diamond:</b><ul><li> indicates if pre-scheduled program is running</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">2</td><td><ul><li><span>NONE (0) </span>:heavy_check_mark:</li><li><span>SCHEDULED (1) </span></li><li><span>SCHEDULE_OVERRIDEN (2) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>InUse (D2) :small_blue_diamond:</b><ul><li> if Service is set to active, this indictes whether it is currently in use</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_IN_USE (0) </span>:heavy_check_mark:</li><li><span>IN_USE (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>RemainingDuration (D4) </b><ul><li> duration (in seconds) remaining for Service to be active/on</li></ul></td><td align="center">uint32</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">3600</td><td align="center">60</td></tr>
|
||||||
|
<tr><td><b>StatusFault (77) </b><ul><li> indicates whether the Service has a fault (only appears in Eve App, not Home App)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NO_FAULT (0) </span>:heavy_check_mark:</li><li><span>FAULT (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### Valve (D0)
|
||||||
|
<i> Defines an electronic Valve. Can be used standalone or as a Linked Service for either a <b>Faucet</b> or <b>IrrigationSystem</b> Service.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>Active (B0) :small_blue_diamond:</b><ul><li> indicates if the Service is active/on</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>INACTIVE (0) </span>:heavy_check_mark:</li><li><span>ACTIVE (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>InUse (D2) :small_blue_diamond:</b><ul><li> if Service is set to active, this indictes whether it is currently in use</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_IN_USE (0) </span>:heavy_check_mark:</li><li><span>IN_USE (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ValveType (D5) :small_blue_diamond:</b><ul><li> indicates the type of valve</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">3</td><td><ul><li><span>GENERIC (0) </span>:heavy_check_mark:</li><li><span>IRRIGATION (1) </span></li><li><span>SHOWER_HEAD (2) </span></li><li><span>FAUCET (3) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>SetDuration (D3) </b><ul><li> specifies the duration (in seconds) for a Service to remain on once activated</li></ul></td><td align="center">uint32</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">3600</td><td align="center">60</td></tr>
|
||||||
|
<tr><td><b>RemainingDuration (D4) </b><ul><li> duration (in seconds) remaining for Service to be active/on</li></ul></td><td align="center">uint32</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">3600</td><td align="center">60</td></tr>
|
||||||
|
<tr><td><b>IsConfigured (D6) </b><ul><li> indicates if a predefined Service has been configured</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_CONFIGURED (0) </span>:heavy_check_mark:</li><li><span>CONFIGURED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ServiceLabelIndex (CB) </b><ul><li> numerical index used to distinguish multiple copies of the same Service within an Accessory</li></ul></td><td align="center">uint8</td><td align="center">PR</td><td align="center">1</td><td align="center">255</td><td align="center">1</td></tr>
|
||||||
|
<tr><td><b>StatusFault (77) </b><ul><li> indicates whether the Service has a fault (only appears in Eve App, not Home App)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NO_FAULT (0) </span>:heavy_check_mark:</li><li><span>FAULT (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
## SECURITY SYSTEMS
|
||||||
|
### SecuritySystem (7E)
|
||||||
|
<i> Defines a Security System. Often used in combination with <b>MotionSensor</b> and <b>ContactSensor</b> Services.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>SecuritySystemCurrentState (66) :small_blue_diamond:</b><ul><li> indicates current state of the security system </li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">4</td><td><ul><li><span>ARMED_STAY (0) </span></li><li><span>ARMED_AWAY (1) </span></li><li><span>ARMED_NIGHT (2) </span></li><li><span>DISARMED (3) </span>:heavy_check_mark:</li><li><span>ALARM_TRIGGERED (4) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>SecuritySystemTargetState (67) :small_blue_diamond:</b><ul><li> indicates desired state of the security system</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">3</td><td><ul><li><span>ARM_STAY (0) </span></li><li><span>ARM_AWAY (1) </span></li><li><span>ARM_NIGHT (2) </span></li><li><span>DISARM (3) </span>:heavy_check_mark:</li></ul></td></tr>
|
||||||
|
<tr><td><b>SecuritySystemAlarmType (8E) </b><ul><li> indicates whether alarm was triggered for known reason</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>KNOWN (0) </span>:heavy_check_mark:</li><li><span>UNKNOWN (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusFault (77) </b><ul><li> indicates whether the Service has a fault (only appears in Eve App, not Home App)</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NO_FAULT (0) </span>:heavy_check_mark:</li><li><span>FAULT (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>StatusTampered (7A) </b><ul><li> indicates whether the Service has been tampered with</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_TAMPERED (0) </span>:heavy_check_mark:</li><li><span>TAMPERED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
## TELEVISIONS
|
||||||
|
### InputSource (D9)
|
||||||
|
<i> Defines an Input Source for a TV. Use only as a Linked Service for the <b>Television</b> Service.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>Identifier (E6) :small_blue_diamond:</b><ul><li> numerical Identifer of the <b>InputSource</b>.</li></ul></td><td align="center">uint32</td><td align="center">PR</td><td align="center">0</td><td align="center">255</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
<tr><td><b>IsConfigured (D6) </b><ul><li> indicates if a predefined Service has been configured</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>NOT_CONFIGURED (0) </span>:heavy_check_mark:</li><li><span>CONFIGURED (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>CurrentVisibilityState (135) </b><ul><li> current visibility of the Service, as selectable on the Settings Page of the Home App</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>VISIBLE (0) </span>:heavy_check_mark:</li><li><span>NOT_VISIBLE (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>TargetVisibilityState (134) </b><ul><li> indicates desired visibility of the Service, as selectable on the Settings Page of the Home App</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>VISIBLE (0) </span>:heavy_check_mark:</li><li><span>NOT_VISIBLE (1) </span></li></ul></td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### Television (D8)
|
||||||
|
<i> Defines a TV. Optional Linked Services: <b>InputSource</b> and <b>TelevisionSpeaker</b>.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>Active (B0) :small_blue_diamond:</b><ul><li> indicates if the Service is active/on</li></ul></td><td align="center">uint8</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>INACTIVE (0) </span>:heavy_check_mark:</li><li><span>ACTIVE (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ActiveIdentifier (E7) </b><ul><li> numerical Identifier of the <b>InputSource</b> selected in the Home App.</li></ul></td><td align="center">uint32</td><td align="center">PW+PR+EV</td><td align="center">0</td><td align="center">255</td><td align="center">0</td></tr>
|
||||||
|
<tr><td><b>RemoteKey (E1) </b><ul><li> triggers an update when the corresponding key is pressed in the Remote Control widget on an iPhone </li></ul></td><td align="center">uint8</td><td align="center">PW</td><td align="center">4</td><td align="center">15</td><td><ul><li><span>UP (4) </span></li><li><span>DOWN (5) </span></li><li><span>LEFT (6) </span></li><li><span>RIGHT (7) </span></li><li><span>CENTER (8) </span></li><li><span>BACK (9) </span></li><li><span>PLAY_PAUSE (11) </span></li><li><span>INFO (15) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>PowerModeSelection (DF) </b><ul><li> when defined, creates a "View TV Settings" button in the Home App that triggers an update to this Characteristic when pressed </li></ul></td><td align="center">uint8</td><td align="center">PW</td><td align="center">0</td><td align="center">0</td><td><ul><li><span>VIEW_SETTINGS (0) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
### TelevisionSpeaker (113)
|
||||||
|
<i> Defines a Television Speaker that can be controlled via the Remote Control widget on an iPhone. Use only as a Linked Service for the <b>Television</b> Service.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>VolumeControlType (E9) :small_blue_diamond:</b><ul><li> indicates the type of volume control</li></ul></td><td align="center">uint8</td><td align="center">PR+EV</td><td align="center">0</td><td align="center">3</td><td><ul><li><span>NONE (0) </span></li><li><span>RELATIVE (1) </span></li><li><span>RELATIVE_CURRENT (2) </span></li><li><span>ABSOLUTE (3) </span>:heavy_check_mark:</li></ul></td></tr>
|
||||||
|
<tr><td><b>VolumeSelector (EA) :small_blue_diamond:</b><ul><li> triggered by presses to the iPhone's volume up/down buttons when TV is selected in the Remote Control widget</li></ul></td><td align="center">uint8</td><td align="center">PW</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>VOLUME_UP (0) </span></li><li><span>VOLUME_DOWN (1) </span></li></ul></td></tr>
|
||||||
|
<tr><td><b>ConfiguredName (E3) </b><ul><li> default display name of this Service</li></ul></td><td align="center">string</td><td align="center">PW+PR+EV</td><td align="center">-</td><td align="center">-</td><td align="center">"unnamed"</td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
|
## MISCELLANEOUS
|
||||||
|
### ServiceLabel (CC)
|
||||||
|
<i> Defines a naming scheme for un-nameable Services, such as a <b>StatelessProgrammableSwitch</b>, by Linking them to this Service. When used, those other Services must each include a <b>ServiceLabelIndex</b> Characteristic with a unique value.</i><br><table>
|
||||||
|
<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>
|
||||||
|
<tr><td><b>ServiceLabelNamespace (CD) :small_blue_diamond:</b><ul><li> indicates how un-named Services linked together with a <b>ServiceLabel</b> Service should be displayed in the Home App </li></ul></td><td align="center">uint8</td><td align="center">PR</td><td align="center">0</td><td align="center">1</td><td><ul><li><span>DOTS (0) </span></li><li><span>NUMERALS (1) </span>:heavy_check_mark:</li></ul></td></tr>
|
||||||
|
</table><br>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](../README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,6 @@
|
||||||
|
|
||||||
* *As an alternative*, instead of adding a pull-up resistor, you can simply exclude (or comment out) the `Serial.begin()` line in your sketch. This prevents the problem of HomeSpan hanging when you power it through the 5V pin, but it unfortunately means the Serial Monitor will not function when you connect the board to your computer, and you will need to add back `Serial.begin()` whenever you want to use the Serial Monitor.
|
* *As an alternative*, instead of adding a pull-up resistor, you can simply exclude (or comment out) the `Serial.begin()` line in your sketch. This prevents the problem of HomeSpan hanging when you power it through the 5V pin, but it unfortunately means the Serial Monitor will not function when you connect the board to your computer, and you will need to add back `Serial.begin()` whenever you want to use the Serial Monitor.
|
||||||
|
|
||||||
#### *Compiler Error - `core_version.h` file not found*
|
|
||||||
|
|
||||||
* HomeSpan requires the file `core_version.h`. This file is part of each Arduino-ESP32 release package and is properly installed if you use the Arduino Board Manager. If you install the Arduino-ESP32 package manually by downloading the official release zip file (listed under the "Assets" section for each release), you will also find the `core_version.h` is included.
|
|
||||||
|
|
||||||
* If you receive the above error when you try to compile a HomeSpan sketch, that means the Arduino-ESP32 package was not properly installed. This can occur if instead of using one of the two methods above, you've tried to manually install the Arduino-ESP32 package by downloading a zip file created live from a specific branch of the Arduino-ESP32 code (typically via the `<> Code` button on GitHub when browsing a particular branch). The `core_version.h` file is usually not included in any of the branches, and thus will not be part of any zip files you request directly from a specific branch.
|
|
||||||
|
|
||||||
* **Resolution:** Use the Arduino Board manager to install the Arduino-ESP32 package (recommended), or install manually using an *official release zip* file found under the "Assets" section of each release version shown on the Arduino-ESP32 GitHub site.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](../README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
|
||||||
|
|
@ -2,28 +2,28 @@
|
||||||
|
|
||||||
HomeSpan includes dedicated classes that provide for easy control of a stepper motor connected to an ESP32 via a stepper motor driver board. These classes allow one or more stepper motors to operate smoothly and asynchronously in the background while HomeSpan continues to run in the foreground. On devices with dual processors, stepper-motor control can be run either on the same or a different processor from HomeSpan.
|
HomeSpan includes dedicated classes that provide for easy control of a stepper motor connected to an ESP32 via a stepper motor driver board. These classes allow one or more stepper motors to operate smoothly and asynchronously in the background while HomeSpan continues to run in the foreground. On devices with dual processors, stepper-motor control can be run either on the same or a different processor from HomeSpan.
|
||||||
|
|
||||||
The HomeSpan class that contains all the methods to control a stepper motor is called **StepperControl**. However, this is an abstract class and cannot be instantiated directly. Instead you instantiate stepper motor objects using driver-specific child-classes (derived from **StepperControl**) that contain all the logic to configure and operate a particular driver board. Each child class supports one or more constructors allowing you to specify which output pins on your ESP32 device will be connected to the required pins on your driver board:
|
The HomeSpan class that contains all the methods to control a stepper motor is called **StepperControl**. However, this is an abstract class and cannot be instantiated directly. Instead you instantiate stepper motor objects using driver-specific child-classes (derived from **StepperControl**) that contain all the logic to configure and operate a particular driver board. Each child class supports one or more constructors allowing you to specify which output pins on your ESP32 device will be connected to the required pins on your driver board.
|
||||||
|
|
||||||
|
The following drivers are currently included in HomeSpan:
|
||||||
|
|
||||||
* **[Stepper_TB6612](StepperDrivers/Stepper_TB6612.md)**
|
* **[Stepper_TB6612](StepperDrivers/Stepper_TB6612.md)**
|
||||||
* This class is used to operate stepper motors driven by a [Toshiba TB6612](https://cdn-shop.adafruit.com/datasheets/TB6612FNG_datasheet_en_20121101.pdf) chip, either with or without the use of ESP32 PWM pins
|
* This class is used to operate stepper motors driven by a [Toshiba TB6612](https://cdn-shop.adafruit.com/datasheets/TB6612FNG_datasheet_en_20121101.pdf) (or equivalent) chip
|
||||||
|
* Can be used either with or without ESP32 PWM pins
|
||||||
* See, for example, the [Adafruit TB6612 1.2A DC/Stepper Motor Driver Breakout Board](https://www.adafruit.com/product/2448)
|
* See, for example, the [Adafruit TB6612 1.2A DC/Stepper Motor Driver Breakout Board](https://www.adafruit.com/product/2448)
|
||||||
* To use, add the following to the top of your sketch: `#include "extras/Stepper_TB6612.h"`
|
|
||||||
* Constructor 1: `Stepper_TB6612(int AIN1, int AIN2, int BIN1, int BIN2)`
|
|
||||||
* controls the driver board using only 4 digital pins from the ESP32
|
|
||||||
* does not provide ability to microstep the motor
|
|
||||||
* Constructor 2: `Stepper_TB6612(int AIN1, int AIN2, int BIN1, int BIN2, int PWMA, int PWMB)`
|
|
||||||
* controls the driver board using 4 digital pins and 2 PWM pins from the ESP32
|
|
||||||
* the addition of the PWM pins provides the ability to microstep the motor
|
|
||||||
|
|
||||||
* **[Stepper_A3967](StepperDrivers/Stepper_A3967.md)**
|
* **[Stepper_A3967](StepperDrivers/Stepper_A3967.md)**
|
||||||
* This class is used to operate stepper motors driven by an [Allegro A3967](https://cdn.sparkfun.com/datasheets/Robotics/A3967-Datasheet.pdf) chip
|
* This class is used to operate stepper motors driven by an [Allegro A3967](https://cdn.sparkfun.com/datasheets/Robotics/A3967-Datasheet.pdf) (or equivalent) chip
|
||||||
* See, for example, the [Sparkfun EasyDriver Stepper Motor Board](https://www.sparkfun.com/products/12779)
|
* See, for example, the [Sparkfun EasyDriver Stepper Motor Board](https://www.sparkfun.com/products/12779)
|
||||||
* To use, add the following to the top of your sketch: `#include "extras/Stepper_A3967.h"`
|
|
||||||
* Contructor: `Stepper_A3967(int M1, int M2, int STEP, int DIR, int ENABLE)`
|
|
||||||
* controls the driver board using 5 digital pins from the ESP32
|
|
||||||
* microstepping is built into the driver board (separate ESP32 PWM pins are not needed)
|
|
||||||
|
|
||||||
Click on either of the driver-specific classes above for complete details on how to wire and configure a particular driver board.
|
* **[Stepper_ULN2003A](StepperDrivers/Stepper_ULN2003A.md)**
|
||||||
|
* This class is used to operate stepper motors driven by a [Texas Instruments ULN2003A](https://www.ti.com/lit/ds/symlink/uln2003a.pdf) (or equivalent) chip
|
||||||
|
* See, for example, the [Opencircuit ULN2003 Stepper Motor Driver Board](https://opencircuit.shop/product/uln2003-stepper-motor-driver-module)
|
||||||
|
|
||||||
|
* **[Stepper_UNIPOLAR](StepperDrivers/Stepper_UNIPOLAR.md)**
|
||||||
|
* This class provides a generic driver for use with any center-tapped unipolar stepper motor
|
||||||
|
* Use requires a driver board that can convert the low-voltage/low-current digital signals from 4 pins on the ESP32 to higher-voltage/higher-current signals suitable for operating the stepper motor
|
||||||
|
|
||||||
|
Click on any of the driver-specific classes above for complete details on how to wire and configure a particular driver board.
|
||||||
|
|
||||||
## StepperControl Methods
|
## StepperControl Methods
|
||||||
|
|
||||||
|
|
@ -136,7 +136,7 @@ Below is a simple sketch demonstrating the above methods:
|
||||||
```C++
|
```C++
|
||||||
// StepperControl Example using TB6612-based Driver Board with HALF STEP PWM MODE
|
// StepperControl Example using TB6612-based Driver Board with HALF STEP PWM MODE
|
||||||
|
|
||||||
#include "extras/Stepper_TB6612.h" // include the driver for a TB6612 chip
|
#include "HomeSpan.h" // HomeSpan includes all the StepperControl classes
|
||||||
|
|
||||||
StepperControl *motor; // create a global pointer to StepperControl so it can be accessed in both setup() and loop()
|
StepperControl *motor; // create a global pointer to StepperControl so it can be accessed in both setup() and loop()
|
||||||
|
|
||||||
|
|
@ -186,7 +186,7 @@ A fully worked example showing how to use the *StepperControl* class within a co
|
||||||
|
|
||||||
## Creating your own **StepperControl** Driver
|
## Creating your own **StepperControl** Driver
|
||||||
|
|
||||||
If neither of the above motor driver classes works for your specific chip or driver board, it is relatively straightfoward to create a new driver to use in your sketch. This is because all the logic to operate a stepper motor in the background is already embedded in the abstract **StepperControl** class. To create your own driver, start by creating a child class derived from **StepperControl**. Next, add a constructor that defines the pins and performs any initializations if needed. Finally, define the following methods that **StepperControl** calls to operate the motor:
|
If none of the above motor driver classes works for your specific chip or driver board, it is relatively straightfoward to create a new driver to use in your sketch. This is because all the logic to operate a stepper motor in the background is already embedded in the abstract **StepperControl** class. To create your own driver, start by creating a child class derived from **StepperControl**. Next, add a constructor that defines the pins and performs any initializations if needed. Finally, define the following methods that **StepperControl** calls to operate the motor:
|
||||||
|
|
||||||
* `void onStep(boolean direction)` - contains the logic to advance the motor by a single step based on the *direction* parameter
|
* `void onStep(boolean direction)` - contains the logic to advance the motor by a single step based on the *direction* parameter
|
||||||
* `void onEnable()` - contains the logic that enables the motor driver
|
* `void onEnable()` - contains the logic that enables the motor driver
|
||||||
|
|
@ -199,7 +199,7 @@ Only the first method, `onStep()`, is required to be defined. You can leave any
|
||||||
As an example, below is the complete code for the **Stepper_A3967** class:
|
As an example, below is the complete code for the **Stepper_A3967** class:
|
||||||
|
|
||||||
```C++
|
```C++
|
||||||
#include "extras/StepperControl.h"
|
#include "HomeSpan.h"
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Stepper_A3967
|
# Stepper_A3967
|
||||||
|
|
||||||
This is a derived class of **StepperControl** designed to operate stepper motors driven by an [Allegro A3967](https://cdn.sparkfun.com/datasheets/Robotics/A3967-Datasheet.pdf) chip. To use, add `#include "extras/Stepper_A3967.h"` to the top of your sketch.
|
This is a derived class of **StepperControl** designed to operate stepper motors driven by an [Allegro A3967](https://cdn.sparkfun.com/datasheets/Robotics/A3967-Datasheet.pdf) (or equivalent) chip.
|
||||||
|
|
||||||
The Allegro A3967 is a specialized driver designed for stepper motors. It contains a built-in PWM generator and pre-programmed stepping modes. Wiring for the [Sparkfun EasyDriver Stepper Motor Board](https://learn.sparkfun.com/tutorials/easy-driver-hook-up-guide?_ga=2.152816825.1841726212.1688220137-156607829.1686369274) that uses this chip is as follows:
|
The Allegro A3967 is a specialized driver designed for stepper motors. It contains a built-in PWM generator and pre-programmed stepping modes. Wiring for the [Sparkfun EasyDriver Stepper Motor Board](https://learn.sparkfun.com/tutorials/easy-driver-hook-up-guide?_ga=2.152816825.1841726212.1688220137-156607829.1686369274) that uses this chip is as follows:
|
||||||
|
|
||||||
|
|
@ -11,14 +11,14 @@ The Allegro A3967 is a specialized driver designed for stepper motors. It contai
|
||||||
* *Motor A* - connect to the "A" coil of the stepper motor
|
* *Motor A* - connect to the "A" coil of the stepper motor
|
||||||
* *Motor B* - connect to the "B" coil of the stepper motor
|
* *Motor B* - connect to the "B" coil of the stepper motor
|
||||||
#### **Control Connections**
|
#### **Control Connections**
|
||||||
* *ENABLE* - connect to a digital pin on the ESP32 - used to enable/disable to motor driver
|
|
||||||
* *STEP, DIR* - connect to two digital pins on the ESP32 - used to step the motor and set the direction
|
|
||||||
* *MS1, MS2* - connect to two digital pins on the ESP32 - used to set the step type mode
|
* *MS1, MS2* - connect to two digital pins on the ESP32 - used to set the step type mode
|
||||||
|
* *STEP, DIR* - connect to two digital pins on the ESP32 - used to step the motor and set the direction
|
||||||
|
* *ENABLE* - connect to a digital pin on the ESP32 - used to enable/disable to motor driver
|
||||||
* *SLEEP, RESET* - already pulled high on the EasyDriver board, so no connection neeed. If using a different driver board, ensure these pins are pulled high, else connect to VCC
|
* *SLEEP, RESET* - already pulled high on the EasyDriver board, so no connection neeed. If using a different driver board, ensure these pins are pulled high, else connect to VCC
|
||||||
* *PFD* - not used
|
* *PFD* - not used
|
||||||
|
|
||||||
The **Stepper_A3967** class includes the following constructor:
|
The **Stepper_A3967** class includes the following constructor:
|
||||||
* `Stepper_A3967(int M1, int M2, int STEP, int DIR, int ENABLE)`
|
* `Stepper_A3967(int MS1, int MS2, int STEP, int DIR, int ENABLE)`
|
||||||
* controls the driver board using 5 digital pins from the ESP32, where the parameters specify the pin numbers. Supports the following step type modes:
|
* controls the driver board using 5 digital pins from the ESP32, where the parameters specify the pin numbers. Supports the following step type modes:
|
||||||
|
|
||||||
* FULL_STEP_TWO_PHASE
|
* FULL_STEP_TWO_PHASE
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Stepper_TB6612
|
# Stepper_TB6612
|
||||||
|
|
||||||
This is a derived class of **StepperControl** designed to operate stepper motors driven by a [Toshiba TB6612](https://cdn-shop.adafruit.com/datasheets/TB6612FNG_datasheet_en_20121101.pdf) chip, either with or without the use of ESP32 PWM pins. To use, add `#include "extras/Stepper_TB6612.h"` to the top of your sketch.
|
This is a derived class of **StepperControl** designed to operate stepper motors driven by a [Toshiba TB6612](https://cdn-shop.adafruit.com/datasheets/TB6612FNG_datasheet_en_20121101.pdf) (or equivalent) chip, either with or without the use of ESP32 PWM pins.
|
||||||
|
|
||||||
The Toshiba TB6612 is a generic motor driver providing direct control of two full H-bridges. Wiring for the [Adafruit TB6612 1.2A DC/Stepper Motor Driver Breakout Board](https://learn.adafruit.com/adafruit-tb6612-h-bridge-dc-stepper-motor-driver-breakout) that uses this chip is as follows:
|
The Toshiba TB6612 is a generic motor driver providing direct control of two full H-bridges. Wiring for the [Adafruit TB6612 1.2A DC/Stepper Motor Driver Breakout Board](https://learn.adafruit.com/adafruit-tb6612-h-bridge-dc-stepper-motor-driver-breakout) that uses this chip is as follows:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Stepper_ULN2003A
|
||||||
|
|
||||||
|
This is a derived class of **StepperControl** designed to operate stepper motors driven by a [Texas Instruments ULN2003A](https://www.ti.com/lit/ds/symlink/uln2003a.pdf) (or equivalent) chip.
|
||||||
|
|
||||||
|
The Texas Instruments ULN2003A chip containins an array of seven Darlington transistor pairs each capable of converting low-voltage/low-current digital signals into higher-voltage/higher-current outputs suitable for driving a stepper motor.[^1]
|
||||||
|
|
||||||
|
[^1]: Only four of the seven Darlington transistor pairs in the ULN2003A are needed to drive a typical unipolar stepper motor.
|
||||||
|
|
||||||
|
Various manufacturers have incorporated this chip into a dedicated stepper motor board designed to drive unipolar motors such the [28BYJ‑48 5‑Volt Stepper](https://opencircuit.shop/product/28byj-48-5v-stepper-motor-4-phase-5-wire) and [28BYJ‑48 12‑Volt Stepper](https://opencircuit.shop/product/28byj-48-12v-stepper-motor-4-phase-5-wire). Wiring for the [Opencircuit ULN2003 Stepper Motor Driver Board](https://opencircuit.shop/product/uln2003-stepper-motor-driver-module) that uses this chip is as follows:
|
||||||
|
|
||||||
|
#### **Power Connections**[^2]
|
||||||
|
* ➕ - connect to an external DC power supply that will drive stepper motor (5-12V)
|
||||||
|
* ➖ - connect to GND on the ESP32, and to ground of external DC power supply
|
||||||
|
#### **Motor Connections**
|
||||||
|
* plug the motor directly into the board's 5-pin connector
|
||||||
|
#### **Control Connections**
|
||||||
|
* *IN1, IN2, IN3, IN4* - connect to four digital pins on the ESP32 - used to step the motor in either direction
|
||||||
|
|
||||||
|
The **Stepper_ULN2003A** class includes the following constructors:
|
||||||
|
* `Stepper_ULN2003A(int IN1, int IN2, int IN3, int IN4)`
|
||||||
|
* controls the driver board using only 4 digital pins from the ESP32, where the parameters specify the pin numbers. Supports the following step type modes:
|
||||||
|
|
||||||
|
* FULL_STEP_ONE_PHASE
|
||||||
|
* FULL_STEP_TWO_PHASE
|
||||||
|
* HALF_STEP
|
||||||
|
|
||||||
|
❗Note: The ULN2003A chip does not support a short brake state. Calls to the `brake()` method, as well as setting the *endAction* parameter in the `move()` and `moveTo()` methods to **StepperControl::BRAKE** have no effect on the motor driver.<br><br>
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> If you set the motor to move very slowly (e.g. 500ms per step) you will be able to track how the current is turned on and off for each phase of the motor coils depending on the *step mode* selected by observing the four LEDs (labeled *A, B, C, D*) built into the driver board. See also this [Last Minute Engineers Tutorial](https://lastminuteengineers.com/28byj48-stepper-motor-arduino-tutorial) for a detailed presentation of the ULN2003A and its use to drive a 28BYJ‑48 5‑Volt Stepper Motor.
|
||||||
|
|
||||||
|
|
||||||
|
[^2]: the ULN2003A is a passive chip - there is no VCC power connection between the driver board and the ESP32
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[↩️](../Stepper.md) Back to the Stepper Motor Control page
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
# Stepper_UNIPOLAR
|
||||||
|
|
||||||
|
This class provides a generic driver for use with any center-tapped unipolar stepper motor. Requires the use of a driver board that can convert the low-voltage/low-current digital signals from 4 pins on the ESP32 to higher-voltage/higher-current outputs suitable for direct connection to the two phases (*A* and *B*) of each coil (*1* and *2*) in the stepper motor.
|
||||||
|
|
||||||
|
The **Stepper_UNIPOLAR** class includes the following constructor:
|
||||||
|
* `Stepper_UNIPOLAR(int coil1A, int coil1B, int coil2A, int coil2B)`
|
||||||
|
* controls the driver board using 4 digital pins from the ESP32, where the parameters specify the pin numbers
|
||||||
|
* the driver circuit should be connected and configured such that when any of ESP32 pins specified above are set to are HIGH, current flows through the corresponding coil/phase. Similarly, when a pin is set LOW, the driver circuit should stop the flow of current through the corresponding coil/phase.
|
||||||
|
* supported modes are as follows:
|
||||||
|
|
||||||
|
* FULL_STEP_ONE_PHASE
|
||||||
|
* FULL_STEP_TWO_PHASE
|
||||||
|
* HALF_STEP
|
||||||
|
|
||||||
|
❗Note: This class does not support a short brake state. Calls to the `brake()` method, as well as setting the *endAction* parameter in the `move()` and `moveTo()` methods to **StepperControl::BRAKE** have no effect on the motor driver.<br><br>
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> **Note the order of the constructor parameters!** The first two parameters specify the ESP32 pins that control the current flowing through phases *A* and *B* of ***Coil 1***; the second two parameters are for phases *A* and *B* of ***Coil 2***.
|
||||||
|
|
||||||
|
It does *not* matter which coil is defined as *1* or *2*, nor which side is called *A* or *B*, as long as the first two parameters are for one of the coils and the second two are for the other coil. You'll know if you mis-specified the order of the pins because the motor will vibrate back and forth instead of turning clockwise or counterclockwise.
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
The patterns by which this class sets the specified pins HIGH and LOW depend on the *step mode* chosen as follows:
|
||||||
|
|
||||||
|
#### FULL_STEP_ONE_PHASE
|
||||||
|
|
||||||
|
* 4-step cycles where in each step current flows only through **one** phase of **one** of the coils
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr><th></th><th colspan="2">Phase A</th><th colspan="2">Phase B</th></tr>
|
||||||
|
<tr><th></th><th>Coil 1</th><th>Coil 2</th><th>Coil 1</th><th>Coil 2</th></th></tr>
|
||||||
|
<tr><th>Step 1</th><td align="center">HIGH</td><td align="center">-</td><td align="center">-</td><td align="center">-</td></td></tr>
|
||||||
|
<tr><th>Step 2</th><td align="center">-</td><td align="center">HIGH</td><td align="center">-</td><td align="center">-</td></td></tr>
|
||||||
|
<tr><th>Step 3</th><td align="center">-</td><td align="center">-</td><td align="center">HIGH</td><td align="center">-</td></td></tr>
|
||||||
|
<tr><th>Step 4</th><td align="center">-</td><td align="center">-</td><td align="center">-</td><td align="center">HIGH</td></td></tr>
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
#### FULL_STEP_TWO_PHASE
|
||||||
|
|
||||||
|
* 4-step cycles where in each step current flows through **one** phase of **each** of the coils
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr><th></th><th colspan="2">Phase A</th><th colspan="2">Phase B</th></tr>
|
||||||
|
<tr><th></th><th>Coil 1</th><th>Coil 2</th><th>Coil 1</th><th>Coil 2</th></th></tr>
|
||||||
|
<tr><th>Step 1</th><td align="center">HIGH</td><td align="center">HIGH</td><td align="center">-</td><td align="center">-</td></td></tr>
|
||||||
|
<tr><th>Step 2</th><td align="center">-</td><td align="center">HIGH</td><td align="center">HIGH</td><td align="center">-</td></td></tr>
|
||||||
|
<tr><th>Step 3</th><td align="center">-</td><td align="center">-</td><td align="center">HIGH</td><td align="center">HIGH</td></td></tr>
|
||||||
|
<tr><th>Step 4</th><td align="center">HIGH</td><td align="center">-</td><td align="center">-</td><td align="center">HIGH</td></td></tr>
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
#### HALF_STEP
|
||||||
|
|
||||||
|
* 8-step cycles formed by interleaving the 4 steps of the FULL_STEP_ONE_PHASE mode with the 4 steps of the FULL_STEP_TWO_PHASE mode
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr><th></th><th colspan="2">Phase A</th><th colspan="2">Phase B</th></tr>
|
||||||
|
<tr><th></th><th>Coil 1</th><th>Coil 2</th><th>Coil 1</th><th>Coil 2</th></th></tr>
|
||||||
|
<tr><th>Step 1</th><td align="center">HIGH</td><td align="center">-</td><td align="center">-</td><td align="center">-</td></td></tr>
|
||||||
|
<tr><th>Step 2</th><td align="center">HIGH</td><td align="center">HIGH</td><td align="center">-</td><td align="center">-</td></td></tr>
|
||||||
|
<tr><th>Step 3</th><td align="center">-</td><td align="center">HIGH</td><td align="center">-</td><td align="center">-</td></td></tr>
|
||||||
|
<tr><th>Step 4</th><td align="center">-</td><td align="center">HIGH</td><td align="center">HIGH</td><td align="center">-</td></td></tr>
|
||||||
|
<tr><th>Step 5</th><td align="center">-</td><td align="center">-</td><td align="center">HIGH</td><td align="center">-</td></td></tr>
|
||||||
|
<tr><th>Step 6</th><td align="center">-</td><td align="center">-</td><td align="center">HIGH</td><td align="center">HIGH</td></td></tr>
|
||||||
|
<tr><th>Step 7</th><td align="center">-</td><td align="center">-</td><td align="center">-</td><td align="center">HIGH</td></td></tr>
|
||||||
|
<tr><th>Step 8</th><td align="center">HIGH</td><td align="center">-</td><td align="center">-</td><td align="center">HIGH</td></td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[↩️](../Stepper.md) Back to the Stepper Motor Control page
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# HomeSpan Tutorials
|
# HomeSpan Tutorials
|
||||||
|
|
||||||
The HomeSpan library includes 16 tutorial sketches of increasing complexity that take you through all the functions and features of HomeSpan. The sketches are extensively annotated, and you'll even learn a lot about HomeKit itself by working through all the examples. If you've already loaded HomeSpan into your Arduino IDE, the tutorials will be found under *File → Examples → HomeSpan*. Each sketch is ready to be compiled and uploaded to your ESP32 device so you can see them in action. Alternatively, you can explore just the code within GitHub by clicking on any of titles below. Note: you may want to first read through the [HomeSpan API Overview](Overview.md) before exploring the tutorials. They will probably make a lot more sense if you do!
|
The HomeSpan library includes many tutorial sketches of increasing complexity that take you through all the functions and features of HomeSpan. The sketches are extensively annotated, and you'll even learn a lot about HomeKit itself by working through all the examples. If you've already loaded HomeSpan into your Arduino IDE, the tutorials will be found under *File → Examples → HomeSpan*. Each sketch is ready to be compiled and uploaded to your ESP32 device so you can see them in action. Alternatively, you can explore just the code within GitHub by clicking on any of titles below. Note: you may want to first read through the [HomeSpan API Overview](Overview.md) before exploring the tutorials. They will probably make a lot more sense if you do!
|
||||||
|
|
||||||
> :heavy_check_mark: Each example is designed to be operated after pairing your ESP32 to HomeKit so you can control HomeSpan from the Home App on your iPhone, iPad, or Mac. In principle, once you configure and pair your device to HomeKit, your Home App should automatically reflect all changes in your configuration whenever you upload a different tutorial. However, in practice this is not always the case as it seems HomeKit sometimes caches information about devices, which means what you see in your Home App may not be fully in sync with your sketch. If this occurs, unpairing and then re-pairing the ESP32 device usually fixes the issue. If not, you may have to reset the ID on the ESP32 device so that HomeKit thinks it is a new device and will not use any cached data. This is very easy to do - see the [HomeSpan Command-Line Interface (CLI)](CLI.md) page for details.
|
> :heavy_check_mark: Each example is designed to be operated after pairing your ESP32 to HomeKit so you can control HomeSpan from the Home App on your iPhone, iPad, or Mac. In principle, once you configure and pair your device to HomeKit, your Home App should automatically reflect all changes in your configuration whenever you upload a different tutorial. However, in practice this is not always the case as it seems HomeKit sometimes caches information about devices, which means what you see in your Home App may not be fully in sync with your sketch. If this occurs, unpairing and then re-pairing the ESP32 device usually fixes the issue. If not, you may have to reset the ID on the ESP32 device so that HomeKit thinks it is a new device and will not use any cached data. This is very easy to do - see the [HomeSpan Command-Line Interface (CLI)](CLI.md) page for details.
|
||||||
|
|
||||||
|
|
@ -133,7 +133,7 @@ Demonstrates how to create Custom Services and Custom Characteristics in HomeSpa
|
||||||
Demonstrates how to implement a fully programmable Light Accessory Hub that allows the user to *dynamically* add/delete up to 12 Light Accessories directly through a device-hosted *web interface* or via HomeSpan's *command-line inteface*. Each light can be configured as dimmable/non-dimmable with either no color control, full RGB color control, or color-temperature control. Builds upon many of the techniques used in [Example 20](../examples/20-AdvancedTechniques)
|
Demonstrates how to implement a fully programmable Light Accessory Hub that allows the user to *dynamically* add/delete up to 12 Light Accessories directly through a device-hosted *web interface* or via HomeSpan's *command-line inteface*. Each light can be configured as dimmable/non-dimmable with either no color control, full RGB color control, or color-temperature control. Builds upon many of the techniques used in [Example 20](../examples/20-AdvancedTechniques)
|
||||||
|
|
||||||
### [RemoteSensors](../examples/Other%20Examples/RemoteSensors)
|
### [RemoteSensors](../examples/Other%20Examples/RemoteSensors)
|
||||||
Demonstrates how *SpanPoint* can be used to transmit messages from battery-powered Remote Devices running light-weight sketches that measure the local temperature, to a wall-powered Main Device running a full HomeSpan sketch implementing Temperature Sensor Accessories. See [SpanPoint: Point-to-Point Communication between ESP32 Devices](NOW.md) for full details regarding the *SpanPoint* class and all of its methods.
|
Demonstrates how *SpanPoint* can be used to transmit messages from battery-powered Remote Devices running light-weight sketches that measure the local temperature, to a wall-powered Main Device running a full HomeSpan sketch implementing Temperature Sensor Accessories. See [SpanPoint: Point-to-Point Communication between ESP32 Devices](NOW.md) for full details regarding the *SpanPoint* class and all of its methods
|
||||||
|
|
||||||
### [FadingLED](../examples/Other%20Examples/FadingLED)
|
### [FadingLED](../examples/Other%20Examples/FadingLED)
|
||||||
Demonstrates how the *LedPin* class can use the ESP32's built-in fading control to automatically fade an LED from from one level of brightness to another over a specified period of time. See the [LedPin](PWM.md#pulse-width-modulation-pwm) page for full details
|
Demonstrates how the *LedPin* class can use the ESP32's built-in fading control to automatically fade an LED from from one level of brightness to another over a specified period of time. See the [LedPin](PWM.md#pulse-width-modulation-pwm) page for full details
|
||||||
|
|
@ -141,6 +141,12 @@ Demonstrates how the *LedPin* class can use the ESP32's built-in fading control
|
||||||
### [MotorizedWindowShade](../examples/Other%20Examples/MotorizedWindowShade)
|
### [MotorizedWindowShade](../examples/Other%20Examples/MotorizedWindowShade)
|
||||||
Demonstrates how to use the *StepperControl* class to operate a stepper motor. Implements a motorized window shade based on [Example 13](../examples/13-TargetStates) above. See the [Stepper Motor Control](Stepper.md) page for full details
|
Demonstrates how to use the *StepperControl* class to operate a stepper motor. Implements a motorized window shade based on [Example 13](../examples/13-TargetStates) above. See the [Stepper Motor Control](Stepper.md) page for full details
|
||||||
|
|
||||||
|
### [CustomNVSPartition](../examples/Other%20Examples/CustomNVSPartition)
|
||||||
|
Demonstrates how to create a Custom Partition Scheme for your sketch by adding a *partitions.csv* file to your sketch folder. Can be used to expand the size of the non-volatile-storage (NVS) partition, which may be needed when creating a HomeSpan device with many Accessories whose Characteristics you want to save in NVS
|
||||||
|
|
||||||
|
### [ExternalReference](../examples/Other%20Examples/ExternalReference)
|
||||||
|
Demonstrates how to access Characteristics of Services from outside those Services, such as from within the main Arduino `loop()`. In this sketch we re-create the two LEDs in Example 5 with an added function in the main Arduino `loop()` that checks if both LEDs are on at the same time, and if so, they are automatically turned off
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](../README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
|
||||||
|
|
@ -50,12 +50,11 @@ void setup() {
|
||||||
// As usual, all previous comments have been deleted and only new changes from the previous example are shown.
|
// As usual, all previous comments have been deleted and only new changes from the previous example are shown.
|
||||||
|
|
||||||
// NOTE: The Arduino/ESP32 code base does not include the function analogWrite() which is typically used to create a PWM
|
// NOTE: The Arduino/ESP32 code base does not include the function analogWrite() which is typically used to create a PWM
|
||||||
// output to drive the brightness of an LED. The ESP32 code base itself includes a set of functions to create PWM output
|
// output to drive the brightness of an LED. Instead, the ESP32 code base itself includes a set of functions to create PWM output
|
||||||
// and the ESP32 chip has built-in PWM functionality specifically for this purpose. There are numerous libraries
|
// and the ESP32 chip has built-in PWM functionality specifically for this purpose.
|
||||||
// you can download that mimics or reproduces analogWrite() in some form or another. HomeSpan conveniently comes with
|
|
||||||
// its own version of a wrapper around the ESP32 PWM classes that make it very easy to define LED pins, and set the
|
// HomeSpan wraps all of this PWM functionality into a single integrated class called LedPin, making it very easy to define
|
||||||
// PWM level (or duty cycle) from 0-100%. These functions are encapsualted in the LedPin class, as defined in
|
// dimmable LED pins and set the PWM level (i.e. duty cycle) from 0-100%. Use of this LedPin class is shown in DEV_DimmableLED.
|
||||||
// extras/PwmPin.h. We will include this file in our updated DEV_LED.h for use with DEV_DimmableLED.
|
|
||||||
|
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
// DEVICE-SPECIFIC LED SERVICES //
|
// DEVICE-SPECIFIC LED SERVICES //
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
|
|
||||||
#include "extras/PwmPin.h" // NEW! Include this HomeSpan "extra" to create LED-compatible PWM signals on one or more pins
|
|
||||||
|
|
||||||
struct DEV_LED : Service::LightBulb { // ON/OFF LED
|
struct DEV_LED : Service::LightBulb { // ON/OFF LED
|
||||||
|
|
||||||
int ledPin; // pin number defined for this LED
|
int ledPin; // pin number defined for this LED
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,8 @@ void setup() {
|
||||||
// Rather, the default name of the first Accessory Tile will always be shown by the Home App as the name specified in
|
// Rather, the default name of the first Accessory Tile will always be shown by the Home App as the name specified in
|
||||||
// homeSpan.begin() regardless of whether or not the Name Characteristic has been added to the Accessory Information Service.
|
// homeSpan.begin() regardless of whether or not the Name Characteristic has been added to the Accessory Information Service.
|
||||||
|
|
||||||
// Below is a replay of Example 6 showing how the Name Characteristic can be used to change the default names of the second,
|
// Below is a replay of Example 6 showing how the Name Characteristic can be used to change the default names of the second
|
||||||
// but not the first, Accessory Tile.
|
// and third, but not the first, Accessory Tile.
|
||||||
|
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
|
@ -77,6 +77,14 @@ void setup() {
|
||||||
|
|
||||||
new DEV_DimmableLED(17);
|
new DEV_DimmableLED(17);
|
||||||
|
|
||||||
|
new SpanAccessory();
|
||||||
|
|
||||||
|
new Service::AccessoryInformation();
|
||||||
|
new Characteristic::Identify();
|
||||||
|
new Characteristic::Name(u8"Special chars ÄÖÜß"); // Use UTF-8 coded string for non-ASCII characters
|
||||||
|
|
||||||
|
new DEV_DimmableLED(18);
|
||||||
|
|
||||||
} // end of setup()
|
} // end of setup()
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
// DEVICE-SPECIFIC LED SERVICES //
|
// DEVICE-SPECIFIC LED SERVICES //
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
|
|
||||||
#include "extras/PwmPin.h" // NEW! Include this HomeSpan "extra" to create LED-compatible PWM signals on one or more pins
|
|
||||||
|
|
||||||
struct DEV_LED : Service::LightBulb { // ON/OFF LED
|
struct DEV_LED : Service::LightBulb { // ON/OFF LED
|
||||||
|
|
||||||
int ledPin; // pin number defined for this LED
|
int ledPin; // pin number defined for this LED
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
// DEVICE-SPECIFIC LED SERVICES //
|
// DEVICE-SPECIFIC LED SERVICES //
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
|
|
||||||
#include "extras/PwmPin.h" // allows PWM control of LED brightness
|
|
||||||
|
|
||||||
struct DEV_LED : Service::LightBulb { // ON/OFF LED
|
struct DEV_LED : Service::LightBulb { // ON/OFF LED
|
||||||
|
|
||||||
int ledPin; // pin number defined for this LED
|
int ledPin; // pin number defined for this LED
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
// DEVICE-SPECIFIC LED SERVICES //
|
// DEVICE-SPECIFIC LED SERVICES //
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
|
|
||||||
#include "extras/PwmPin.h" // allows PWM control of LED brightness
|
|
||||||
|
|
||||||
struct DEV_LED : Service::LightBulb { // ON/OFF LED
|
struct DEV_LED : Service::LightBulb { // ON/OFF LED
|
||||||
|
|
||||||
int ledPin; // pin number defined for this LED
|
int ledPin; // pin number defined for this LED
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,6 @@
|
||||||
// DEVICE-SPECIFIC LED SERVICES //
|
// DEVICE-SPECIFIC LED SERVICES //
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
|
|
||||||
#include "extras/PwmPin.h" // library of various PWM functions
|
|
||||||
|
|
||||||
////////////////////////////////////
|
|
||||||
|
|
||||||
struct DEV_LED : Service::LightBulb { // ON/OFF LED
|
struct DEV_LED : Service::LightBulb { // ON/OFF LED
|
||||||
|
|
||||||
int ledPin; // pin number defined for this LED
|
int ledPin; // pin number defined for this LED
|
||||||
|
|
|
||||||
|
|
@ -25,33 +25,47 @@
|
||||||
*
|
*
|
||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
// //
|
// //
|
||||||
// HomeSpan: A HomeKit implementation for the ESP32 //
|
// HomeSpan: A HomeKit implementation for the ESP32 //
|
||||||
// ------------------------------------------------ //
|
// ------------------------------------------------ //
|
||||||
// //
|
// //
|
||||||
// Example 11: Service Names: //
|
// Example 11: Service Names: //
|
||||||
// * setting the names of individual Services //
|
// * setting the names of individual Services //
|
||||||
// * changing the icons in a bridge Accessory //
|
// * "changing" the icons in a bridge Accessory //
|
||||||
// //
|
// //
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "HomeSpan.h"
|
#include "HomeSpan.h"
|
||||||
|
|
||||||
|
// INITIAL NOTE: Apple is constantly updating how the Home App Icons are chosen and how/if/where/when the Names for
|
||||||
|
// Accessories and Services are displayed. This example has been tested and verified as of iOS 17.2.1.
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
|
||||||
// As described in previous examples, when pairing a device the Home App will choose default names for each
|
// As described in previous examples, when pairing a device the Home App will choose default names for each
|
||||||
// Accessory Tile, unless you override those default names with your own names by adding a Name Characteristic
|
// Accessory Tile, unless you override those default names with your own names by adding a Name Characteristic
|
||||||
// to the Accessory Information Service for each Accessory (except the first, which is typically the Bridge Accessory).
|
// to the Accessory Information Service for each Accessory (except the first, which is typically the Bridge Accessory).
|
||||||
|
|
||||||
// The same process holds true for the names of the Services in an Accessory with multiple Services, such as a Ceiling Fan with a Light.
|
// The same process holds true for the names of the Services in an Accessory with multiple Services: if a Service is not named,
|
||||||
// When pairing, the Home App will choose default names for each Service (such as Fan, Fan 2, Light, Light 2) depending on the types
|
// the Home App will generate one. You can of course change the names of individual Services when prompted
|
||||||
// of Services included. Similar to the names of Accessory Tiles, you can change the names of individual Services when prompted
|
// during the pairing process, or at any time after pairing from within the appropriate settings pages in the Home App.
|
||||||
// during the pairing process, or at any time after pairing from within the appropriate settings pages in the Home App. More importantly,
|
|
||||||
// you can override the default Service names generated by the Home App by simply adding the Name Characteristic to any Service.
|
|
||||||
|
|
||||||
// However, note that Service names (whether or not overridden) only appear in the Home App if there is a chance of ambiguity,
|
// But more importantly, you can name Services in your sketch so that those name show up when pairing, saving you the need to
|
||||||
// such as a Accessory with two Services of the same type. But even if a Service name does not appear in the Home App,
|
// rename them from the settings pages in the Home App.
|
||||||
|
|
||||||
|
// Whereas we previously used the *Name* Characteristic to provide names for Accessory Tiles, we use the *ConfiguredName* Characteristic
|
||||||
|
// to provide names for individual Services within each Accessory.
|
||||||
|
|
||||||
|
// One important distinction between Name and ConfigureName is that Name is only used by the Home App during pairing. After that,
|
||||||
|
// any changes you make to the name of an Accessory Tile from within the Home App are never communicated back to HomeSpan, and any changes
|
||||||
|
// you might make to those names in your sketch will not be reflected in the Home App unless you unpair and re-pair the device. In contrast,
|
||||||
|
// ConfiguredName works like any other Characteristic: changes made to ConfiguredName from within a sketch are proporgated to the Home App,
|
||||||
|
// and any edits you make to a Service's name in the Home App trigger a corresponding call to update() in HomeSpan so HomeSpan and the Home App
|
||||||
|
// are always in sync with regard to the names of any Services that includes the ConfiguredName Characteristic.
|
||||||
|
|
||||||
|
// NOTE: Service names (whether those generated by the Home App or specified via the ConfiguredName Characteristic) are only displayed on the
|
||||||
|
// control screen of an Accessory Tile if there are two more more Services of the same type. But even if a Service name does not appear in the Home App,
|
||||||
// it will still be used by Siri to control a specific Service within an Accessory by voice.
|
// it will still be used by Siri to control a specific Service within an Accessory by voice.
|
||||||
|
|
||||||
// In the example below we create 5 different functional Accessories, each illustrating how names, as well as icons, are chosen by the Home App
|
// In the example below we create 5 different functional Accessories, each illustrating how names, as well as icons, are chosen by the Home App
|
||||||
|
|
@ -62,103 +76,92 @@ void setup() {
|
||||||
|
|
||||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||||
|
|
||||||
// Our first Accessory is the "Bridge" Accessory
|
// Our initial Accessory is therefore the "Bridge" Accessory
|
||||||
|
|
||||||
new SpanAccessory();
|
new SpanAccessory();
|
||||||
new Service::AccessoryInformation();
|
new Service::AccessoryInformation();
|
||||||
new Characteristic::Identify();
|
new Characteristic::Identify();
|
||||||
|
|
||||||
// Our second Accessory is a Ceiling Fan with a single Light. There are three things to note:
|
// Our first "functional" Accessory is a combination of a LightBulb, Outlet, and Switch. Note that when pairing, the Home App generates
|
||||||
//
|
// default names of "Light", "Outlet", and "Switch" for these three Services, though these names are NOT displayed on the control screen
|
||||||
// * when pairing, the Home App will generate default names of "Light" and "Fan" for the two Services.
|
// of the Accessory since there is only one type of each Service. Also note that the Home App selects a LightBulb icon for the Accessory Tile
|
||||||
// However, these names are not displayed on the control screen of the Accessory since there is no
|
|
||||||
// ambiguity between the Light and Fan controls - the Home App displays them differently
|
|
||||||
//
|
|
||||||
// * the icon used by the Home App for the Accessory Tile is a Lightbulb. Why does it choose this instead of a Fan icon?
|
|
||||||
// Recall from Example 3 that for Accessories with multiple Services, if there is any ambiguity of which icon to use,
|
|
||||||
// the Home App chooses based on the Category of the device. But since this device is configured as a Bridge, the
|
|
||||||
// Category provides no helpful information to the Home App. In such cases the Home App picks an icon for the
|
|
||||||
// Accessory Tile that matches the first functional Service in the Accessory, which in this instance in a LightBulb
|
|
||||||
//
|
|
||||||
// * when opening the control screen by clicking the Accessory Tile, the LightBulb control will appear on the left, and
|
|
||||||
// the Fan control will appear on the right
|
|
||||||
|
|
||||||
new SpanAccessory();
|
new SpanAccessory();
|
||||||
new Service::AccessoryInformation();
|
new Service::AccessoryInformation();
|
||||||
new Characteristic::Identify();
|
new Characteristic::Identify();
|
||||||
new Characteristic::Name("Light with Fan"); // this sets the name of the Accessory Tile
|
new Characteristic::Name("Light First"); // this sets the name of the Accessory Tile
|
||||||
new Service::LightBulb(); // the icon of the Accessory Tile will be a Lightbulb, since this is the first functional Service
|
new Service::LightBulb(); // the icon of the Accessory Tile will be a Lightbulb, since this is the first functional Service
|
||||||
|
new Characteristic::On();
|
||||||
|
new Service::Outlet();
|
||||||
|
new Characteristic::On();
|
||||||
|
new Characteristic::OutletInUse();
|
||||||
|
new Service::Switch();
|
||||||
new Characteristic::On();
|
new Characteristic::On();
|
||||||
new Service::Fan();
|
|
||||||
new Characteristic::Active();
|
|
||||||
|
|
||||||
// Our third Accessory is identical to the second, except we swapped the order of the Lightbulb and Fan Services.
|
// Our second Accessory is similar to the first, but here we define the Switch Service first. Note that the Home App now selects
|
||||||
// The result is that the Home App now displays the Accessory Tile with a Fan icon intead of a Lightbulb icon.
|
// a Switch icon for the Accessory Tile
|
||||||
// Also, when opening the control screen by clicking on the Accessory Tile, the Fan control will now appear on the
|
|
||||||
// left, and the LightBulb control on the right.
|
|
||||||
|
|
||||||
new SpanAccessory();
|
new SpanAccessory();
|
||||||
new Service::AccessoryInformation();
|
new Service::AccessoryInformation();
|
||||||
new Characteristic::Identify();
|
new Characteristic::Identify();
|
||||||
new Characteristic::Name("Fan with Light"); // this sets the name of the Accessory Tile
|
new Characteristic::Name("Switch First"); // this sets the name of the Accessory Tile
|
||||||
new Service::Fan(); // the icon of the Accessory Tile will be a Fan, since this is the first functional Service
|
new Service::Switch(); // the icon of the Accessory Tile will be a Switch, since this is the first functional Service
|
||||||
new Characteristic::Active();
|
new Characteristic::On();
|
||||||
|
new Service::Outlet();
|
||||||
|
new Characteristic::On();
|
||||||
|
new Characteristic::OutletInUse();
|
||||||
new Service::LightBulb();
|
new Service::LightBulb();
|
||||||
new Characteristic::On();
|
new Characteristic::On();
|
||||||
|
|
||||||
// Our fourth Accessory shows what happens if we implement two identical LightBulb Services (without any Fan Service).
|
// Our third Accessory is similar to the second, but here we define 2 Switches, 2 LightBulbs, but still only 1 Outlet. This time, during pairing
|
||||||
// Since both Services are LightBulbs, the Home App sensibly picks a Lightbulb icon for the Accessory Tile. However,
|
// the Home App generates default names of Switch, Switch 2, Light, Light 2, and Outlet. Importantly, note that on the control screen for
|
||||||
// when you click the Accessory Tile and open the control screen, you'll note that the Home App now does display the names
|
// this Accessory, the Home App now displays the names of the Switches ("Switch" and "Switch 2") as well as the LightBulbs ("Light" and "Light 2")
|
||||||
// of the Service beneath each control. In this case the Home App uses the default names "Light 1" and "Light 2". The Home App
|
// under each corresponding control, but it does NOT display the name "Outlet" under the Outlet control since there is only one Outlet Service
|
||||||
// presumably shows the names of each Service since the two controls are identical and there is otherwise no way of telling which
|
|
||||||
// control operates which light.
|
|
||||||
|
|
||||||
new SpanAccessory();
|
new SpanAccessory();
|
||||||
new Service::AccessoryInformation();
|
new Service::AccessoryInformation();
|
||||||
new Characteristic::Identify();
|
new Characteristic::Identify();
|
||||||
new Characteristic::Name("Ceiling Lights"); // this sets the name of the Accessory Tile
|
new Characteristic::Name("Two Switches"); // this sets the name of the Accessory Tile
|
||||||
|
new Service::Switch(); // the icon of the Accessory Tile will be a Switch, since this is the first functional Service
|
||||||
|
new Characteristic::On();
|
||||||
|
new Service::Switch();
|
||||||
|
new Characteristic::On();
|
||||||
|
new Service::Outlet();
|
||||||
|
new Characteristic::On();
|
||||||
|
new Characteristic::OutletInUse();
|
||||||
new Service::LightBulb();
|
new Service::LightBulb();
|
||||||
new Characteristic::On();
|
new Characteristic::On();
|
||||||
new Service::LightBulb();
|
new Service::LightBulb();
|
||||||
new Characteristic::On();
|
new Characteristic::On();
|
||||||
|
|
||||||
// Our fifth Accessory combines a single Fan Service with two identical LightBulb Services. Since the first functional Service implemented
|
// Our fourth and final Accessory is the same as the third, but this time we use the ConfiguredName Characteristic to define a name for each Service.
|
||||||
// is a Fan, the Home App will pick a Fan icon for the Accessory Tile. Also, since we added Name Characteristics to two LightBulb
|
// When pairing, you should see the Home App now uses the names below instead of generating default names as it did in the other examples. You
|
||||||
// Services, their default names generated by the Home App ("Light 1" and "Light 2") will be changed to the names specified. Finally,
|
// should also see these names displayed under each control on the control screen for the Accessory, with the exception of the Outlet Service.
|
||||||
// note that the Home App displays a more compact form of controls on the control screen since there are three Services. The arrangement
|
// Though we did provide a name for the Outlet, since there is only one Outlet Service in this Accessory, the Home App does not display its name.
|
||||||
// and style of the controls will depend on what combination of Characteristics are implemented for each Service.
|
// Howevever, if from the settings screen for this Accessory you further navigate to the "Accessories" page, you will indeed see the names for each
|
||||||
|
// Service exactly as specified below, including the Outlet name "Aux Power"
|
||||||
|
|
||||||
new SpanAccessory();
|
new SpanAccessory();
|
||||||
new Service::AccessoryInformation();
|
new Service::AccessoryInformation();
|
||||||
new Characteristic::Identify();
|
new Characteristic::Identify();
|
||||||
new Characteristic::Name("Fan with Lights"); // this sets the name of the Accessory Tile
|
new Characteristic::Name("Central Control"); // this sets the name of the Accessory Tile
|
||||||
new Service::Fan();
|
new Service::Switch(); // the icon of the Accessory Tile will be a Switch, since this is the first functional Service
|
||||||
new Characteristic::Active();
|
|
||||||
new Service::LightBulb();
|
|
||||||
new Characteristic::Name("Main Light"); // this changes the default name of this LightBulb Service from "Light 1" to "Main Light"
|
|
||||||
new Characteristic::On();
|
new Characteristic::On();
|
||||||
new Service::LightBulb();
|
new Characteristic::ConfiguredName("High Voltage"); // this sets the name of the first Switch Service
|
||||||
new Characteristic::Name("Night Light"); // this changes the default name of this LightBulb Service from "Light 2" to "Night Light"
|
new Service::Switch();
|
||||||
new Characteristic::On();
|
new Characteristic::On();
|
||||||
|
new Characteristic::ConfiguredName("Low Voltage"); // this sets the name of the second Switch Service
|
||||||
|
new Service::Outlet();
|
||||||
|
new Characteristic::On();
|
||||||
|
new Characteristic::OutletInUse();
|
||||||
|
new Characteristic::ConfiguredName("Aux Power"); // this sets the name of the Outlet Service
|
||||||
|
new Service::LightBulb();
|
||||||
|
new Characteristic::On();
|
||||||
|
new Characteristic::ConfiguredName("Main Lights"); // this sets the name of the first LightBulb Service
|
||||||
|
new Service::LightBulb();
|
||||||
|
new Characteristic::On();
|
||||||
|
new Characteristic::ConfiguredName("Accent Lights"); // this sets the name of the second LightBulb Service
|
||||||
|
|
||||||
// Our sixth Accessory is similar to the fifth, except we added some more features to some of the Services. Note how this changes
|
|
||||||
// the layout of the controls on the control screen.
|
|
||||||
|
|
||||||
new SpanAccessory();
|
|
||||||
new Service::AccessoryInformation();
|
|
||||||
new Characteristic::Identify();
|
|
||||||
new Characteristic::Name("Multi-Function Fan");
|
|
||||||
new Service::Fan();
|
|
||||||
new Characteristic::Active();
|
|
||||||
new Characteristic::RotationDirection(); // add a control to change the direcion of rotation
|
|
||||||
new Characteristic::RotationSpeed(0); // add a control to set the rotation speed
|
|
||||||
new Service::LightBulb();
|
|
||||||
new Characteristic::Name("Main Light");
|
|
||||||
new Characteristic::On();
|
|
||||||
new Characteristic::Brightness(100); // make this light dimmable (with intitial value set to 100%)
|
|
||||||
new Service::LightBulb();
|
|
||||||
new Characteristic::Name("Night Light"); // don't add anything new to this light
|
|
||||||
new Characteristic::On();
|
|
||||||
|
|
||||||
} // end of setup()
|
} // end of setup()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
|
|
||||||
struct DEV_GarageDoor : Service::GarageDoorOpener { // A Garage Door Opener
|
struct DEV_GarageDoor : Service::GarageDoorOpener { // A Garage Door Opener
|
||||||
|
|
||||||
SpanCharacteristic *current; // reference to the Current Door State Characteristic (specific to Garage Door Openers)
|
Characteristic::CurrentDoorState *current; // reference to the Current Door State Characteristic (specific to Garage Door Openers)
|
||||||
SpanCharacteristic *target; // reference to the Target Door State Characteristic (specific to Garage Door Openers)
|
Characteristic::TargetDoorState *target; // reference to the Target Door State Characteristic (specific to Garage Door Openers)
|
||||||
SpanCharacteristic *obstruction; // reference to the Obstruction Detected Characteristic (specific to Garage Door Openers)
|
SpanCharacteristic *obstruction; // reference to the Obstruction Detected Characteristic (specific to Garage Door Openers)
|
||||||
|
|
||||||
DEV_GarageDoor() : Service::GarageDoorOpener(){ // constructor() method
|
DEV_GarageDoor() : Service::GarageDoorOpener(){ // constructor() method
|
||||||
|
|
@ -118,8 +118,7 @@ struct DEV_WindowShade : Service::WindowCovering { // A motorized Window Sha
|
||||||
// the current state.
|
// the current state.
|
||||||
|
|
||||||
// According to HAP, the Characteristic Position State is also required. However, this seems duplicative and is NOT needed
|
// According to HAP, the Characteristic Position State is also required. However, this seems duplicative and is NOT needed
|
||||||
// at all given the way HomeKit uses current position. HomeSpan will warn you if Position State is not defined (since it
|
// at all given the way HomeKit uses current position.
|
||||||
// is technically required) but this works fine without it.
|
|
||||||
|
|
||||||
} // loop
|
} // loop
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,6 @@
|
||||||
// DEVICE-SPECIFIC LED SERVICES //
|
// DEVICE-SPECIFIC LED SERVICES //
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
|
|
||||||
#include "extras/PwmPin.h" // library of various PWM functions
|
|
||||||
|
|
||||||
////////////////////////////////////
|
|
||||||
|
|
||||||
struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
|
struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
|
||||||
|
|
||||||
// This version of the Dimmable LED Service is similar to the one last used in Example 11, but now includes support for 3 physical PushButtons
|
// This version of the Dimmable LED Service is similar to the one last used in Example 11, but now includes support for 3 physical PushButtons
|
||||||
|
|
|
||||||
|
|
@ -35,12 +35,6 @@
|
||||||
// //
|
// //
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// WARNING: THIS EXAMPLE STOPPED WORKING CORRECTLY SOMEWHERE AROUND THE IOS 15.2 OR IOS 15.3 UPDATE
|
|
||||||
// AND DOES NOT WORK AS OF IOS 15.4.1
|
|
||||||
//
|
|
||||||
// THE PROBLEM APPEARS TO BE IN THE RENDERING OF INDIVIDUAL VALVES IN THE HOME APP INTERFACE. THEY
|
|
||||||
// APPEAR IN THE EVE HOMEKIT APPLICATION, BUT NOT APPLE'S HOME APP.
|
|
||||||
|
|
||||||
#include "HomeSpan.h"
|
#include "HomeSpan.h"
|
||||||
|
|
||||||
// HAP normally treats multiple Services created within the same Accessory as independent of one another. However, certain HAP Services are designed to represent a central point
|
// HAP normally treats multiple Services created within the same Accessory as independent of one another. However, certain HAP Services are designed to represent a central point
|
||||||
|
|
@ -112,19 +106,19 @@ struct Shower : Service::Faucet { // this is our Shower structur
|
||||||
|
|
||||||
SpanCharacteristic *active=new Characteristic::Active(); // our implementation only requires the Active Characteristic
|
SpanCharacteristic *active=new Characteristic::Active(); // our implementation only requires the Active Characteristic
|
||||||
|
|
||||||
Shower(int nHeads){ // this is the constructor for Shower. It takes a single argument that specifies the number of spray heads (WaterValves)
|
Shower(int nHeads){ // this is the constructor for Shower. It takes a single argument that specifies the number of spray heads (WaterValves)
|
||||||
for(int i=0;i<nHeads;i++) // for each spray head needed ---
|
for(int i=0;i<nHeads;i++) // for each spray head needed ---
|
||||||
addLink(new WaterValve(this,i+1)); // --- instantiate a new WaterValve AND link it to the Shower. Also, pass the Shower object's pointer to WaterValve constructor. We'll see why below.
|
addLink(new WaterValve(this)); // --- instantiate a new WaterValve AND link it to the Shower. Also, pass the Shower object's pointer to WaterValve constructor. We'll see why below.
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WaterValve : Service::Valve { // here we define our WaterValve structure as a child class of the HomeSpan Valve Service
|
struct WaterValve : Service::Valve { // here we define our WaterValve structure as a child class of the HomeSpan Valve Service
|
||||||
SpanCharacteristic *active=new Characteristic::Active(1);; // the Active Characteristic is used to specify whether the Valve is Active (open) or Inactive (closed)
|
SpanCharacteristic *active=new Characteristic::Active(1);; // the Active Characteristic is used to specify whether the Valve is Active (open) or Inactive (closed)
|
||||||
SpanCharacteristic *inUse=new Characteristic::InUse(); // the InUser Characteristic is used to specify whether water is actually flowing through value
|
SpanCharacteristic *inUse=new Characteristic::InUse(); // the InUser Characteristic is used to specify whether water is actually flowing through value
|
||||||
Shower *shower; // storage for the pointer to the "controlling" Shower Service
|
Shower *shower; // storage for the pointer to the "controlling" Shower Service
|
||||||
|
|
||||||
WaterValve(Shower *s, int i){ // this is constructor for WaterValve. It takes a single argument that points to the "controlling" Shower Service
|
WaterValve(Shower *s){ // this is constructor for WaterValve. It takes a single argument that points to the "controlling" Shower Service
|
||||||
shower=s; // store the pointer to the Shower Service
|
shower=s; // store the pointer to the Shower Service
|
||||||
new Characteristic::ValveType(2); // specify the Value Type (2=Shower Head; see HAP R2 for other choices)
|
new Characteristic::ValveType(Characteristic::ValveType::SHOWER_HEAD); // specify the Valve Type as a Shower Head (note use of constant "Characteristic::ValveType::SHOWER_HEAD")
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean update() override { // HomeSpan calls this whenever the Home App requests a change in a Valve's Active Characteristic
|
boolean update() override { // HomeSpan calls this whenever the Home App requests a change in a Valve's Active Characteristic
|
||||||
|
|
@ -134,10 +128,10 @@ struct Shower : Service::Faucet { // this is our Shower structur
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() override { // Here we check if the Shower is turned on or off, and determine if that means we need to update the Valve
|
void loop() override { // Here we check if the Shower is turned on or off, and determine if that means we need to update the Valve
|
||||||
if(shower->active->getVal() && active->getVal() && !inUse->getVal()) // If the Shower is Active, and the Valve is Active, but InUse is NOT Active...
|
if(shower->active->getVal() && active->getVal() && !inUse->getVal()) // If the Shower is Active, and the Valve is Active but NOT showing InUse...
|
||||||
inUse->setVal(1); // ...set the InUse Characteristic to Active
|
inUse->setVal(1); // ...show Valve as InUse
|
||||||
else if(!shower->active->getVal() && inUse->getVal()) // Otherwise, if the Shower is NOT Active but InUse is Active...
|
else if(!shower->active->getVal() && inUse->getVal()) // Otherwise, if the Shower is NOT Active but Valve IS showing InUse...
|
||||||
inUse->setVal(0); // ...set the InUse Characteristic to NOT Active
|
inUse->setVal(0); // ...show Valve as NOT InUse
|
||||||
}
|
}
|
||||||
|
|
||||||
}; // WaterValve
|
}; // WaterValve
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,6 @@
|
||||||
// DEVICE-SPECIFIC LED SERVICES //
|
// DEVICE-SPECIFIC LED SERVICES //
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
|
|
||||||
#include "extras/PwmPin.h" // library of various PWM functions
|
|
||||||
|
|
||||||
////////////////////////////////////
|
|
||||||
|
|
||||||
struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
|
struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
|
||||||
|
|
||||||
// This version of the Dimmable LED Service includes a PushButton that can be used to turn on/off the LED. Status of both the
|
// This version of the Dimmable LED Service includes a PushButton that can be used to turn on/off the LED. Status of both the
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
/*********************************************************************************
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||||
|
*
|
||||||
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
// This example demonstrates the use of a custom Partition Scheme file: "partitions.csv"
|
||||||
|
|
||||||
|
// During compilation, if a file with this EXACT name is placed in the sketch folder,
|
||||||
|
// the esptool performing the compilation will use the partition scheme found
|
||||||
|
// in "partitions.csv" regardless of what partition scheme you selected in the Arduino IDE.
|
||||||
|
|
||||||
|
// Note if you change the partition scheme it is highly recommended that you fully erase the flash
|
||||||
|
// upon your next compile/upload by enabling the "Erase All Flash" option from the Arduino IDE menu.
|
||||||
|
// NOTE: remember to turn OFF this option after you've successully uploaded a sketch with the new
|
||||||
|
// partition scheme, else you will continue to erase everything saved in the NVS every time you upload
|
||||||
|
// a new sketch (which is likely NOT what you want to occur).
|
||||||
|
|
||||||
|
// The main reason for wanting to create your own partition scheme is to expand the NVS space.
|
||||||
|
// All of the pre-configured partition scheme you can select from the Arduino IDE provide
|
||||||
|
// for 504 records of NVS space. This is usuall sufficient for most HomeSpan projects, but if
|
||||||
|
// you have a LOT of Accessories (as per below) AND you are saving their states in NVS, you can
|
||||||
|
// use up all the NVS space. If this occurs, HomeSpan will warn you of low NVS space upon boot-up.
|
||||||
|
|
||||||
|
// The custom partition scheme included in this sketch folder solves this problem by eliminating
|
||||||
|
// the SPIFFs partition (which is generally not used by HomeSpan) and using this portion of the flash
|
||||||
|
// to provide an NVS space with 3906 records --- more than enough for even the largest projects.
|
||||||
|
|
||||||
|
// For reference, in addition to HomeSpan's internal use of NVS (about 32 records), saving a
|
||||||
|
// numerical Characteristic consumes one additional NVS record, and saving a string Characteristic (of
|
||||||
|
// less than 32 characters) consumes two NVS records. Also, the ESP32 WiFi stack consumes about 130
|
||||||
|
// additional NVS records once initialized. As such, the sketch below requires:
|
||||||
|
|
||||||
|
// 32 records (internal HomeSpan use)
|
||||||
|
// + 320 records (80 Accessories * 4 saved numerical Characterstics)
|
||||||
|
// + 160 records (80 Accessories * 2 records per saved string Characterstic)
|
||||||
|
// + 130 records (with WiFi initialized)
|
||||||
|
// ----------------------------------------
|
||||||
|
// = 642 NVS records needed (which exceeds the normal 504 limit, unless a custom partition scheme is used)
|
||||||
|
|
||||||
|
// Note that once HomeSpan is paired with HomeKit, additional NVS records will be consumed to store the
|
||||||
|
// pairing information for each verified HomeKit Controller.
|
||||||
|
|
||||||
|
// Note also that when compiling under the Arduino IDE, the IDE reports the size of partition based on the
|
||||||
|
// Partition Scheme you selected in the IDE menu, even though that scheme is not actually used if you have your
|
||||||
|
// own "partition.csv" file, as in this example. This may lead the IDE to report an incorrect partition size.
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "HomeSpan.h"
|
||||||
|
|
||||||
|
#define MAX_LIGHTS 80 // configure for 80 Light Accessories
|
||||||
|
|
||||||
|
struct RGB_Light : Service::LightBulb {
|
||||||
|
|
||||||
|
Characteristic::On power{0,true}; // save these 4 numerical Characteristics (4*80 = 320 NVS records)
|
||||||
|
Characteristic::Hue H{0,true};
|
||||||
|
Characteristic::Saturation S{0,true};
|
||||||
|
Characteristic::Brightness V{0,true};
|
||||||
|
|
||||||
|
int lightNumber;
|
||||||
|
|
||||||
|
RGB_Light(int n) : Service::LightBulb(){
|
||||||
|
|
||||||
|
lightNumber=n;
|
||||||
|
LOG0("Configured RGB Light-%0d\n",lightNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean update(){
|
||||||
|
|
||||||
|
if(power.updated())
|
||||||
|
LOG0("Light-%d: Power=%s",lightNumber,power.getNewVal()?"ON":"OFF");
|
||||||
|
|
||||||
|
if(H.updated())
|
||||||
|
LOG0("Light-%d: Hue=%d",lightNumber,H.getNewVal());
|
||||||
|
|
||||||
|
if(S.updated())
|
||||||
|
LOG0("Light-%d: Saturation=%d",lightNumber,S.getNewVal());
|
||||||
|
|
||||||
|
if(V.updated())
|
||||||
|
LOG0("Light-%d: Brightness=%d",lightNumber,V.getNewVal());
|
||||||
|
|
||||||
|
return(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
homeSpan.begin(Category::Lighting,"HomeSpan Max");
|
||||||
|
|
||||||
|
new SpanAccessory();
|
||||||
|
new Service::AccessoryInformation();
|
||||||
|
new Characteristic::Identify();
|
||||||
|
|
||||||
|
for(int i=1;i<=MAX_LIGHTS;i++){
|
||||||
|
char c[60];
|
||||||
|
sprintf(c,"Light-%02d",i);
|
||||||
|
|
||||||
|
new SpanAccessory();
|
||||||
|
new Service::AccessoryInformation();
|
||||||
|
new Characteristic::Identify();
|
||||||
|
new Characteristic::Name(c,true); // save this string Characteristic (2*80 = 160 NVS records)
|
||||||
|
|
||||||
|
new RGB_Light(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
void loop(){
|
||||||
|
|
||||||
|
homeSpan.poll();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
# --- Custom Partition Table for HomeSpan ---
|
||||||
|
#
|
||||||
|
# Similar to min_spiffs, except that the 128K SPIFF block at the end
|
||||||
|
# is replaced by a 128K NVS block, and the initial 20K NVS block
|
||||||
|
# is no longer used. Note this table is designed for use with 4MB Flash.
|
||||||
|
# To use with 8MB Flash, increase app0 and app1 by 2048K to become 3968K each.
|
||||||
|
# To use with 16MB Flash, increase app0 and app1 by 6144K to become 8064K each
|
||||||
|
#
|
||||||
|
unused_nvs,data,nvs,,20K,
|
||||||
|
otadata,data,ota,,8K,
|
||||||
|
app0,app,ota_0,,1920K,
|
||||||
|
app1,app,ota_1,,1920K,
|
||||||
|
nvs,data,nvs,,128K,
|
||||||
|
coredump,data,coredump,,64K,
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
/*********************************************************************************
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
|
*
|
||||||
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
// Sometimes you need to access Characteristics from outside of the Service structure
|
||||||
|
// in which they were created so you can read and/or modify them in other parts of
|
||||||
|
// a sketch, such as from within the main Arduino loop().
|
||||||
|
|
||||||
|
// This sketch is basically the same as Tutorial Example 5, in which we created
|
||||||
|
// two working LEDs attached to pins 16, and 17. However, in this sketch we will
|
||||||
|
// create global pointers to the LED Services that we can then use in the main loop() to
|
||||||
|
// do something unique.
|
||||||
|
|
||||||
|
#include "HomeSpan.h"
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
// First we define our DEV_LED Service exactly the same as in Tutorial Example 5.
|
||||||
|
// This Service contains a single Characteristic named "power" of type Chacracteristic::On
|
||||||
|
|
||||||
|
struct DEV_LED : Service::LightBulb {
|
||||||
|
|
||||||
|
int ledPin;
|
||||||
|
SpanCharacteristic *power;
|
||||||
|
|
||||||
|
DEV_LED(int ledPin) : Service::LightBulb(){
|
||||||
|
|
||||||
|
power=new Characteristic::On();
|
||||||
|
this->ledPin=ledPin;
|
||||||
|
pinMode(ledPin,OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean update(){
|
||||||
|
|
||||||
|
digitalWrite(ledPin,power->getNewVal());
|
||||||
|
return(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
// Next we create two pointers to the DEV_LED Service. These are created
|
||||||
|
// outside of any class or function so they are globally-scoped and can be
|
||||||
|
// accessed from anywhere else in this sketch.
|
||||||
|
|
||||||
|
// Note that there are just POINTERS to DEV_LED objects. The objects themselves
|
||||||
|
// are not yet created.
|
||||||
|
|
||||||
|
DEV_LED *led16; // pointer to a DEV_LED structure to be used below to reference a DEV_LED object assigned to pin 16
|
||||||
|
DEV_LED *led17; // pointer to a DEV_LED structure to be used below to reference a DEV_LED object assigned to pin 17
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
homeSpan.begin(Category::Lighting,"HomeSpan LED");
|
||||||
|
|
||||||
|
new SpanAccessory();
|
||||||
|
new Service::AccessoryInformation();
|
||||||
|
new Characteristic::Identify();
|
||||||
|
led16=new DEV_LED(16); // this is the key step - we SAVE the pointer returned by 'new DEV_LED(16)' in the global variable led16 created above
|
||||||
|
|
||||||
|
|
||||||
|
new SpanAccessory();
|
||||||
|
new Service::AccessoryInformation();
|
||||||
|
new Characteristic::Identify();
|
||||||
|
led17=new DEV_LED(17); // also save the pointer to the second LED object using the global variable led17 created above
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
void loop(){
|
||||||
|
|
||||||
|
homeSpan.poll();
|
||||||
|
|
||||||
|
// Because the pointers led16 and led17 were created in the global scope, they will still exist even after setup() is completed.
|
||||||
|
// This means we can use them to access the Characteristics within each of those Services.
|
||||||
|
|
||||||
|
// Here we access the power Characteristic of both Services and check to see if they are BOTH on, and if so,
|
||||||
|
// we turn them both off and print a "power overload" message.
|
||||||
|
|
||||||
|
// Note how you can use all the same methods, such as getVal() and setVal(), just as you would do in the Service itself.
|
||||||
|
// Caution: always use getVal(), not getNewVal(), which is only formally defined from within the Service update() method.
|
||||||
|
|
||||||
|
if(led16->power->getVal() && led17->power->getVal()){
|
||||||
|
Serial.printf("Power overload! Can't have both LED's on at the same time. Turn off both LEDs...\n");
|
||||||
|
led16->power->setVal(false);
|
||||||
|
led17->power->setVal(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
|
@ -40,7 +40,6 @@
|
||||||
// off.
|
// off.
|
||||||
|
|
||||||
#include "HomeSpan.h"
|
#include "HomeSpan.h"
|
||||||
#include "extras/PwmPin.h" // library of various PWM functions
|
|
||||||
|
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,6 @@
|
||||||
// as well as for instructions on how you can easily extend StepperControl to create a custom driver for any board.
|
// as well as for instructions on how you can easily extend StepperControl to create a custom driver for any board.
|
||||||
|
|
||||||
#include "HomeSpan.h"
|
#include "HomeSpan.h"
|
||||||
#include "extras/Stepper_TB6612.h" // this contains HomeSpan's StepperControl Class for the Adafruit TB6612 driver board
|
|
||||||
#include "extras/Stepper_A3967.h" // this contains HomeSpan's StepperControl Class for the Sparkfun A3967 driver board
|
|
||||||
|
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "HomeSpan.h"
|
#include "HomeSpan.h"
|
||||||
#include "extras/Pixel.h" // include the HomeSpan Pixel class
|
|
||||||
|
|
||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
|
|
||||||
|
|
@ -72,9 +71,9 @@ struct NeoPixel_RGB : Service::LightBulb { // Addressable single-wire RGB L
|
||||||
Characteristic::Saturation S{0,true};
|
Characteristic::Saturation S{0,true};
|
||||||
Characteristic::Brightness V{100,true};
|
Characteristic::Brightness V{100,true};
|
||||||
Pixel *pixel;
|
Pixel *pixel;
|
||||||
uint8_t nPixels;
|
int nPixels;
|
||||||
|
|
||||||
NeoPixel_RGB(uint8_t pin, uint8_t nPixels) : Service::LightBulb(){
|
NeoPixel_RGB(uint8_t pin, int nPixels) : Service::LightBulb(){
|
||||||
|
|
||||||
V.setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1%
|
V.setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1%
|
||||||
pixel=new Pixel(pin); // creates Pixel LED on specified pin
|
pixel=new Pixel(pin); // creates Pixel LED on specified pin
|
||||||
|
|
@ -106,9 +105,9 @@ struct NeoPixel_RGBW : Service::LightBulb { // Addressable single-wire RGBW
|
||||||
Characteristic::Brightness V{100,true};
|
Characteristic::Brightness V{100,true};
|
||||||
Characteristic::ColorTemperature T{140,true};
|
Characteristic::ColorTemperature T{140,true};
|
||||||
Pixel *pixel;
|
Pixel *pixel;
|
||||||
uint8_t nPixels;
|
int nPixels;
|
||||||
|
|
||||||
NeoPixel_RGBW(uint8_t pin, uint8_t nPixels) : Service::LightBulb(){
|
NeoPixel_RGBW(uint8_t pin, int nPixels) : Service::LightBulb(){
|
||||||
|
|
||||||
V.setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1%
|
V.setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1%
|
||||||
pixel=new Pixel(pin,true); // creates Pixel RGBW LED (second parameter set to true for RGBW) on specified pin
|
pixel=new Pixel(pin,true); // creates Pixel RGBW LED (second parameter set to true for RGBW) on specified pin
|
||||||
|
|
@ -142,9 +141,9 @@ struct DotStar_RGB : Service::LightBulb { // Addressable two-wire RGB LED S
|
||||||
Characteristic::Saturation S{0,true};
|
Characteristic::Saturation S{0,true};
|
||||||
Characteristic::Brightness V{100,true};
|
Characteristic::Brightness V{100,true};
|
||||||
Dot *pixel;
|
Dot *pixel;
|
||||||
uint8_t nPixels;
|
int nPixels;
|
||||||
|
|
||||||
DotStar_RGB(uint8_t dataPin, uint8_t clockPin, uint8_t nPixels) : Service::LightBulb(){
|
DotStar_RGB(uint8_t dataPin, uint8_t clockPin, int nPixels) : Service::LightBulb(){
|
||||||
|
|
||||||
V.setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1%
|
V.setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1%
|
||||||
pixel=new Dot(dataPin,clockPin); // creates Dot LED on specified pins
|
pixel=new Dot(dataPin,clockPin); // creates Dot LED on specified pins
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@
|
||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
|
|
||||||
#include "HomeSpan.h" // include the HomeSpan library
|
#include "HomeSpan.h" // include the HomeSpan library
|
||||||
#include "extras/RFControl.h" // include RF Control Library
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,8 @@ void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
delay(1000);
|
delay(1000);
|
||||||
|
|
||||||
Serial.printf("Starting\n\n");
|
Serial.printf("\n\nThis is a REMOTE Device with MAC Address = %s\n",WiFi.macAddress().c_str());
|
||||||
|
Serial.printf("NOTE: This MAC Address must be entered into the corresponding SpanPoint() call of the MAIN Device.\n\n");
|
||||||
|
|
||||||
// In the line below, replace the MAC Address with that of your MAIN HOMESPAN DEVICE
|
// In the line below, replace the MAC Address with that of your MAIN HOMESPAN DEVICE
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,6 @@
|
||||||
// DEVICE-SPECIFIC LED SERVICES //
|
// DEVICE-SPECIFIC LED SERVICES //
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
|
|
||||||
#include <extras/PwmPin.h>
|
|
||||||
|
|
||||||
////////////////////////////////////
|
|
||||||
|
|
||||||
struct DEV_WindowShade : Service::WindowCovering { // A motorized Window Shade with Hold Feature
|
struct DEV_WindowShade : Service::WindowCovering { // A motorized Window Shade with Hold Feature
|
||||||
|
|
||||||
SpanCharacteristic *current; // reference to a "generic" Current Position Characteristic (used by a variety of different Service)
|
SpanCharacteristic *current; // reference to a "generic" Current Position Characteristic (used by a variety of different Service)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
name=HomeSpan
|
name=HomeSpan
|
||||||
version=1.8.0
|
version=1.9.0
|
||||||
author=Gregg <homespan@icloud.com>
|
author=Gregg <homespan@icloud.com>
|
||||||
maintainer=Gregg <homespan@icloud.com>
|
maintainer=Gregg <homespan@icloud.com>
|
||||||
sentence=A robust and extremely easy-to-use HomeKit implementation for the Espressif ESP32 running on the Arduino IDE.
|
sentence=A robust and extremely easy-to-use HomeKit implementation for the Espressif ESP32 running on the Arduino IDE.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
@ -61,7 +61,7 @@
|
||||||
|
|
||||||
#elif defined(ARDUINO_ESP32S3_DEV)
|
#elif defined(ARDUINO_ESP32S3_DEV)
|
||||||
enum {
|
enum {
|
||||||
F13=5,F12=6,F27=7,F15=16,F32=17,F14=18,F16=37,F17=36,F21=35, // Digital Only (9 pins)
|
F13=5,F12=6,F27=7,F15=16,F32=17,F14=18,F16=37,F17=36,F21=38, // Digital Only (9 pins)
|
||||||
F26=1,F25=2,F34=20,F39=19,F36=15,F4=4, // A0-A5
|
F26=1,F25=2,F34=20,F39=19,F36=15,F4=4, // A0-A5
|
||||||
F22=9,F23=8, // I2C SCL/SDA
|
F22=9,F23=8, // I2C SCL/SDA
|
||||||
F5=12,F18=11,F19=13,F33=10, // SPI SCK/SDO/SDI/CS
|
F5=12,F18=11,F19=13,F33=10, // SPI SCK/SDO/SDI/CS
|
||||||
|
|
|
||||||
1759
src/HAP.cpp
1759
src/HAP.cpp
File diff suppressed because it is too large
Load Diff
172
src/HAP.h
172
src/HAP.h
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
@ -27,13 +27,31 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
|
||||||
#include "HomeSpan.h"
|
#include "HomeSpan.h"
|
||||||
#include "TLV.h"
|
|
||||||
#include "HAPConstants.h"
|
#include "HAPConstants.h"
|
||||||
#include "HKDF.h"
|
#include "HKDF.h"
|
||||||
#include "SRP.h"
|
#include "SRP.h"
|
||||||
|
#include "TLV8.h"
|
||||||
|
|
||||||
|
const TLV8_names HAP_Names[] = {
|
||||||
|
{kTLVType_Separator,"SEPARATOR"},
|
||||||
|
{kTLVType_State,"STATE"},
|
||||||
|
{kTLVType_PublicKey,"PUBKEY"},
|
||||||
|
{kTLVType_Method,"METHOD"},
|
||||||
|
{kTLVType_Salt,"SALT"},
|
||||||
|
{kTLVType_Error,"ERROR"},
|
||||||
|
{kTLVType_Proof,"PROOF"},
|
||||||
|
{kTLVType_EncryptedData,"ENC.DATA"},
|
||||||
|
{kTLVType_Signature,"SIGNATURE"},
|
||||||
|
{kTLVType_Identifier,"IDENTIFIER"},
|
||||||
|
{kTLVType_Permissions,"PERMISSION"}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define hap_controller_IDBYTES 36
|
||||||
|
#define hap_accessory_IDBYTES 17
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
// NONCE Structure (HAP used last 64 of 96 bits)
|
// NONCE Structure (HAP used last 64 of 96 bits)
|
||||||
|
|
@ -50,19 +68,29 @@ struct Nonce {
|
||||||
// Paired Controller Structure for Permanently-Stored Data
|
// Paired Controller Structure for Permanently-Stored Data
|
||||||
|
|
||||||
struct Controller {
|
struct Controller {
|
||||||
boolean allocated=false; // slot is allocated with Controller data
|
boolean allocated=false; // DEPRECATED (but needed for backwards compatability with original NVS storage of Controller info)
|
||||||
boolean admin; // Controller has admin privileges
|
boolean admin; // Controller has admin privileges
|
||||||
uint8_t ID[36]; // Pairing ID
|
uint8_t ID[hap_controller_IDBYTES]; // Pairing ID
|
||||||
uint8_t LTPK[32]; // Long Term Ed2519 Public Key
|
uint8_t LTPK[crypto_sign_PUBLICKEYBYTES]; // Long Term Ed2519 Public Key
|
||||||
|
|
||||||
|
Controller(){}
|
||||||
|
|
||||||
|
Controller(uint8_t *id, uint8_t *ltpk, boolean ad){
|
||||||
|
allocated=true;
|
||||||
|
admin=ad;
|
||||||
|
memcpy(ID,id,hap_controller_IDBYTES);
|
||||||
|
memcpy(LTPK,ltpk,crypto_sign_PUBLICKEYBYTES);
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
// Accessory Structure for Permanently-Stored Data
|
// Accessory Structure for Permanently-Stored Data
|
||||||
|
|
||||||
struct Accessory {
|
struct Accessory {
|
||||||
uint8_t ID[17]; // Pairing ID in form "XX:XX:XX:XX:XX:XX"
|
uint8_t ID[hap_accessory_IDBYTES]; // Pairing ID in form "XX:XX:XX:XX:XX:XX" (no null terminator)
|
||||||
uint8_t LTSK[64]; // secret key for Ed25519 signatures
|
uint8_t LTSK[crypto_sign_SECRETKEYBYTES]; // Long Term Ed2519 Secret Key
|
||||||
uint8_t LTPK[32]; // public key for Ed25519 signatures
|
uint8_t LTPK[crypto_sign_PUBLICKEYBYTES]; // Long Term Ed2519 Public Key
|
||||||
};
|
};
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
|
|
@ -73,32 +101,30 @@ struct HAPClient {
|
||||||
|
|
||||||
// common structures and data shared across all HAP Clients
|
// common structures and data shared across all HAP Clients
|
||||||
|
|
||||||
static const int MAX_HTTP=8095; // max number of bytes in HTTP message buffer
|
static const int MAX_HTTP=8096; // max number of bytes allowed for HTTP message
|
||||||
static const int MAX_CONTROLLERS=16; // maximum number of paired controllers (HAP requires at least 16)
|
static const int MAX_CONTROLLERS=16; // maximum number of paired controllers (HAP requires at least 16)
|
||||||
static const int MAX_ACCESSORIES=41; // maximum number of allowed Acessories (HAP limit=150, but not enough memory in ESP32 to run that many)
|
static const int MAX_ACCESSORIES=150; // maximum number of allowed Accessories (HAP limit=150)
|
||||||
|
|
||||||
static TLV<kTLVType,10> tlv8; // TLV8 structure (HAP Section 14.1) with space for 10 TLV records of type kTLVType (HAP Table 5-6)
|
static nvs_handle hapNVS; // handle for non-volatile-storage of HAP data
|
||||||
static nvs_handle hapNVS; // handle for non-volatile-storage of HAP data
|
static nvs_handle srpNVS; // handle for non-volatile-storage of SRP data
|
||||||
static nvs_handle srpNVS; // handle for non-volatile-storage of SRP data
|
static HKDF hkdf; // generates (and stores) HKDF-SHA-512 32-byte keys derived from an inputKey of arbitrary length, a salt string, and an info string
|
||||||
static uint8_t httpBuf[MAX_HTTP+1]; // buffer to store HTTP messages (+1 to leave room for storing an extra 'overflow' character)
|
static pairState pairStatus; // tracks pair-setup status
|
||||||
static HKDF hkdf; // generates (and stores) HKDF-SHA-512 32-byte keys derived from an inputKey of arbitrary length, a salt string, and an info string
|
static SRP6A *srp; // stores all SRP-6A keys used for Pair-Setup (must persist through multiple calls to Pair-Setup)
|
||||||
static pairState pairStatus; // tracks pair-setup status
|
static Accessory accessory; // Accessory ID and Ed25519 public and secret keys- permanently stored
|
||||||
static SRP6A srp; // stores all SRP-6A keys used for Pair-Setup
|
static list<Controller, Mallocator<Controller>> controllerList; // linked-list of Paired Controller IDs and ED25519 long-term public keys - permanently stored
|
||||||
static Accessory accessory; // Accessory ID and Ed25519 public and secret keys- permanently stored
|
static int conNum; // connection number - used to keep track of per-connection EV notifications
|
||||||
static Controller controllers[MAX_CONTROLLERS]; // Paired Controller IDs and ED25519 long-term public keys - permanently stored
|
|
||||||
static int conNum; // connection number - used to keep track of per-connection EV notifications
|
|
||||||
|
|
||||||
// individual structures and data defined for each Hap Client connection
|
// individual structures and data defined for each Hap Client connection
|
||||||
|
|
||||||
WiFiClient client; // handle to client
|
WiFiClient client; // handle to client
|
||||||
Controller *cPair; // pointer to info on current, session-verified Paired Controller (NULL=un-verified, and therefore un-encrypted, connection)
|
Controller *cPair=NULL; // pointer to info on current, session-verified Paired Controller (NULL=un-verified, and therefore un-encrypted, connection)
|
||||||
|
|
||||||
// These keys are generated in the first call to pair-verify and used in the second call to pair-verify so must persist for a short period
|
// These temporary Curve25519 keys are generated in the first call to pair-verify and used in the second call to pair-verify so must persist for a short period
|
||||||
|
|
||||||
uint8_t publicCurveKey[32]; // public key for Curve25519 encryption
|
uint8_t *publicCurveKey; // Accessory's Curve25519 Public Key
|
||||||
uint8_t sharedCurveKey[32]; // Pair-Verfied Shared Secret key derived from Accessory's epehmeral secretCurveKey and Controller's iosCurveKey
|
uint8_t *sharedCurveKey; // Shared-Secret Curve25519 Key derived from Accessory's Secret Key and Controller's Public Key
|
||||||
uint8_t sessionKey[32]; // shared Session Key (derived with various HKDF calls)
|
uint8_t *sessionKey; // Session Key Curve25519 (derived with various HKDF calls)
|
||||||
uint8_t iosCurveKey[32]; // Curve25519 public key for associated paired controller
|
uint8_t *iosCurveKey; // Controller's Curve25519 Public Key
|
||||||
|
|
||||||
// CurveKey and CurveKey Nonces are created once each new session is verified in /pair-verify. Keys persist for as long as connection is open
|
// CurveKey and CurveKey Nonces are created once each new session is verified in /pair-verify. Keys persist for as long as connection is open
|
||||||
|
|
||||||
|
|
@ -109,19 +135,17 @@ struct HAPClient {
|
||||||
|
|
||||||
// define member methods
|
// define member methods
|
||||||
|
|
||||||
void processRequest(); // process HAP request
|
void processRequest(); // process HAP request
|
||||||
int postPairSetupURL(); // POST /pair-setup (HAP Section 5.6)
|
int postPairSetupURL(uint8_t *content, size_t len); // POST /pair-setup (HAP Section 5.6)
|
||||||
int postPairVerifyURL(); // POST /pair-verify (HAP Section 5.7)
|
int postPairVerifyURL(uint8_t *content, size_t len); // POST /pair-verify (HAP Section 5.7)
|
||||||
int getAccessoriesURL(); // GET /accessories (HAP Section 6.6)
|
int postPairingsURL(uint8_t *content, size_t len); // POST /pairings (HAP Sections 5.10-5.12)
|
||||||
int postPairingsURL(); // POST /pairings (HAP Sections 5.10-5.12)
|
int getAccessoriesURL(); // GET /accessories (HAP Section 6.6)
|
||||||
int getCharacteristicsURL(char *urlBuf); // GET /characteristics (HAP Section 6.7.4)
|
int getCharacteristicsURL(char *urlBuf); // GET /characteristics (HAP Section 6.7.4)
|
||||||
int putCharacteristicsURL(char *json); // PUT /characteristics (HAP Section 6.7.2)
|
int putCharacteristicsURL(char *json); // PUT /characteristics (HAP Section 6.7.2)
|
||||||
int putPrepareURL(char *json); // PUT /prepare (HAP Section 6.7.2.4)
|
int putPrepareURL(char *json); // PUT /prepare (HAP Section 6.7.2.4)
|
||||||
int getStatusURL(); // GET / status (an optional, non-HAP feature)
|
|
||||||
|
|
||||||
void tlvRespond(); // respond to client with HTTP OK header and all defined TLV data records (those with length>0)
|
void tlvRespond(TLV8 &tlv8); // respond to client with HTTP OK header and all defined TLV data records
|
||||||
void sendEncrypted(char *body, uint8_t *dataBuf, int dataLen); // send client complete ChaCha20-Poly1305 encrypted HTTP mesage comprising a null-terminated 'body' and 'dataBuf' with 'dataLen' bytes
|
int receiveEncrypted(uint8_t *httpBuf, int messageSize); // decrypt HTTP request (HAP Section 6.5)
|
||||||
int receiveEncrypted(); // decrypt HTTP request (HAP Section 6.5)
|
|
||||||
|
|
||||||
int notFoundError(); // return 404 error
|
int notFoundError(); // return 404 error
|
||||||
int badRequestError(); // return 400 error
|
int badRequestError(); // return 400 error
|
||||||
|
|
@ -129,25 +153,83 @@ struct HAPClient {
|
||||||
|
|
||||||
// define static methods
|
// define static methods
|
||||||
|
|
||||||
static void init(); // initialize HAP after start-up
|
static void init(); // initialize HAP after start-up
|
||||||
|
|
||||||
static void hexPrintColumn(uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as HEX, one byte per row, subject to specified minimum log level
|
static void hexPrintColumn(uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as HEX, one byte per row, subject to specified minimum log level
|
||||||
static void hexPrintRow(uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as HEX, all on one row, subject to specified minimum log level
|
static void hexPrintRow(uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as HEX, all on one row, subject to specified minimum log level
|
||||||
static void charPrintRow(uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as CHAR, all on one row, subject to specified minimum log level
|
static void charPrintRow(uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as CHAR, all on one row, subject to specified minimum log level
|
||||||
|
|
||||||
static Controller *findController(uint8_t *id); // returns pointer to controller with mathching ID (or NULL if no match)
|
static Controller *findController(uint8_t *id); // returns pointer to controller with matching ID (or NULL if no match)
|
||||||
static Controller *getFreeController(); // return pointer to next free controller slot (or NULL if no free slots)
|
static tagError addController(uint8_t *id, uint8_t *ltpk, boolean admin); // stores data for new Controller with specified data. Returns tagError (if any)
|
||||||
static Controller *addController(uint8_t *id, uint8_t *ltpk, boolean admin); // stores data for new Controller with specified data. Returns pointer to Controller slot on success, else NULL
|
|
||||||
static int nAdminControllers(); // returns number of admin Controllers stored
|
|
||||||
static void removeControllers(); // removes all Controllers (sets allocated flags to false for all slots)
|
|
||||||
static void removeController(uint8_t *id); // removes specific Controller. If no remaining admin Controllers, remove all others (if any) as per HAP requirements.
|
static void removeController(uint8_t *id); // removes specific Controller. If no remaining admin Controllers, remove all others (if any) as per HAP requirements.
|
||||||
static void printControllers(int minLogLevel=0); // prints IDs of all allocated (paired) Controller, subject to specified minimum log level
|
static void printControllers(int minLogLevel=0); // prints IDs of all allocated (paired) Controller, subject to specified minimum log level
|
||||||
|
static void saveControllers(); // saves Controller list in NVS
|
||||||
|
static int nAdminControllers(); // returns number of admin Controller
|
||||||
|
static void tearDown(uint8_t *id); // tears down connections using Controller with ID=id; tears down all connections if id=NULL
|
||||||
static void checkNotifications(); // checks for Event Notifications and reports to controllers as needed (HAP Section 6.8)
|
static void checkNotifications(); // checks for Event Notifications and reports to controllers as needed (HAP Section 6.8)
|
||||||
static void checkTimedWrites(); // checks for expired Timed Write PIDs, and clears any found (HAP Section 6.7.2.4)
|
static void checkTimedWrites(); // checks for expired Timed Write PIDs, and clears any found (HAP Section 6.7.2.4)
|
||||||
static void eventNotify(SpanBuf *pObj, int nObj, int ignoreClient=-1); // transmits EVENT Notifications for nObj SpanBuf objects, pObj, with optional flag to ignore a specific client
|
static void eventNotify(SpanBuf *pObj, int nObj, int ignoreClient=-1); // transmits EVENT Notifications for nObj SpanBuf objects, pObj, with optional flag to ignore a specific client
|
||||||
|
|
||||||
|
static void getStatusURL(HAPClient *, void (*)(const char *, void *), void *); // GET / status (an optional, non-HAP feature)
|
||||||
|
|
||||||
|
class HAPTLV : public TLV8 { // dedicated class for HAP TLV8 records
|
||||||
|
public:
|
||||||
|
HAPTLV() : TLV8(HAP_Names,11){}
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
// HapOut Structure
|
||||||
|
|
||||||
|
class HapOut : public std::ostream {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
struct HapStreamBuffer : public std::streambuf {
|
||||||
|
|
||||||
|
const size_t bufSize=1024; // max allowed for HAP encrypted records
|
||||||
|
char *buffer;
|
||||||
|
uint8_t *encBuf;
|
||||||
|
HAPClient *hapClient=NULL;
|
||||||
|
int logLevel=255; // default is NOT to print anything
|
||||||
|
boolean enablePrettyPrint=false;
|
||||||
|
size_t byteCount=0;
|
||||||
|
size_t indent=0;
|
||||||
|
uint8_t *hash;
|
||||||
|
mbedtls_sha512_context *ctx;
|
||||||
|
void (*callBack)(const char *, void *)=NULL;
|
||||||
|
void *callBackUserData = NULL;
|
||||||
|
|
||||||
|
void flushBuffer();
|
||||||
|
int_type overflow(int_type c) override;
|
||||||
|
int sync() override;
|
||||||
|
size_t getSize(){return(byteCount+pptr()-pbase());}
|
||||||
|
void printFormatted(char *buf, size_t nChars, size_t nsp);
|
||||||
|
|
||||||
|
HapStreamBuffer();
|
||||||
|
~HapStreamBuffer();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
HapStreamBuffer hapBuffer;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
HapOut() : std::ostream(&hapBuffer){}
|
||||||
|
|
||||||
|
HapOut& setHapClient(HAPClient *hapClient){hapBuffer.hapClient=hapClient;return(*this);}
|
||||||
|
HapOut& setLogLevel(int logLevel){hapBuffer.logLevel=logLevel;return(*this);}
|
||||||
|
HapOut& prettyPrint(){hapBuffer.enablePrettyPrint=true;hapBuffer.logLevel=0;return(*this);}
|
||||||
|
HapOut& setCallback(void(*f)(const char *, void *)){hapBuffer.callBack=f;return(*this);}
|
||||||
|
HapOut& setCallbackUserData(void *userData){hapBuffer.callBackUserData=userData;return(*this);}
|
||||||
|
|
||||||
|
uint8_t *getHash(){return(hapBuffer.hash);}
|
||||||
|
size_t getSize(){return(hapBuffer.getSize());}
|
||||||
};
|
};
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
// Extern Variables
|
// Extern Variables
|
||||||
|
|
||||||
extern HAPClient **hap;
|
extern HAPClient **hap;
|
||||||
|
extern HapOut hapOut;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
@ -52,6 +52,7 @@ typedef enum {
|
||||||
// HAP Error Codes (HAP Table 5-5)
|
// HAP Error Codes (HAP Table 5-5)
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
tagError_None=0x00,
|
||||||
tagError_Unknown=0x01,
|
tagError_Unknown=0x01,
|
||||||
tagError_Authentication=0x02,
|
tagError_Authentication=0x02,
|
||||||
tagError_Backoff=0x03,
|
tagError_Backoff=0x03,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
|
||||||
570
src/HomeSpan.cpp
570
src/HomeSpan.cpp
File diff suppressed because it is too large
Load Diff
223
src/HomeSpan.h
223
src/HomeSpan.h
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
@ -37,14 +37,18 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <unordered_set>
|
#include <list>
|
||||||
#include <nvs.h>
|
#include <nvs.h>
|
||||||
#include <ArduinoOTA.h>
|
#include <ArduinoOTA.h>
|
||||||
#include <esp_now.h>
|
#include <esp_now.h>
|
||||||
#include <mbedtls/base64.h>
|
#include <mbedtls/base64.h>
|
||||||
|
|
||||||
#include "extras/Blinker.h"
|
#include "src/extras/Blinker.h"
|
||||||
#include "extras/Pixel.h"
|
#include "src/extras/Pixel.h"
|
||||||
|
#include "src/extras/RFControl.h"
|
||||||
|
#include "src/extras/PwmPin.h"
|
||||||
|
#include "src/extras/StepperControl.h"
|
||||||
|
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "Network.h"
|
#include "Network.h"
|
||||||
|
|
@ -54,7 +58,7 @@
|
||||||
|
|
||||||
using std::vector;
|
using std::vector;
|
||||||
using std::unordered_map;
|
using std::unordered_map;
|
||||||
using std::unordered_set;
|
using std::list;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
GET_AID=1,
|
GET_AID=1,
|
||||||
|
|
@ -64,7 +68,8 @@ enum {
|
||||||
GET_EV=16,
|
GET_EV=16,
|
||||||
GET_DESC=32,
|
GET_DESC=32,
|
||||||
GET_NV=64,
|
GET_NV=64,
|
||||||
GET_VALUE=128
|
GET_VALUE=128,
|
||||||
|
GET_STATUS=256
|
||||||
};
|
};
|
||||||
|
|
||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
|
|
@ -143,7 +148,7 @@ struct SpanWebLog{ // optional web status/log data
|
||||||
boolean isEnabled=false; // flag to inidicate WebLog has been enabled
|
boolean isEnabled=false; // flag to inidicate WebLog has been enabled
|
||||||
uint16_t maxEntries=0; // max number of log entries;
|
uint16_t maxEntries=0; // max number of log entries;
|
||||||
int nEntries=0; // total cumulative number of log entries
|
int nEntries=0; // total cumulative number of log entries
|
||||||
const char *timeServer; // optional time server to use for acquiring clock time
|
const char *timeServer=NULL; // optional time server to use for acquiring clock time
|
||||||
const char *timeZone; // optional time-zone specification
|
const char *timeZone; // optional time-zone specification
|
||||||
boolean timeInit=false; // flag to indicate time has been initialized
|
boolean timeInit=false; // flag to indicate time has been initialized
|
||||||
char bootTime[33]="Unknown"; // boot time
|
char bootTime[33]="Unknown"; // boot time
|
||||||
|
|
@ -202,7 +207,7 @@ class Span{
|
||||||
const char *displayName; // display name for this device - broadcast as part of Bonjour MDNS
|
const char *displayName; // display name for this device - broadcast as part of Bonjour MDNS
|
||||||
const char *hostNameBase; // base of hostName of this device - full host name broadcast by Bonjour MDNS will have 6-byte accessoryID as well as '.local' automatically appended
|
const char *hostNameBase; // base of hostName of this device - full host name broadcast by Bonjour MDNS will have 6-byte accessoryID as well as '.local' automatically appended
|
||||||
const char *hostNameSuffix=NULL; // optional "suffix" of hostName of this device. If specified, will be used as the hostName suffix instead of the 6-byte accessoryID
|
const char *hostNameSuffix=NULL; // optional "suffix" of hostName of this device. If specified, will be used as the hostName suffix instead of the 6-byte accessoryID
|
||||||
char *hostName; // full host name of this device - constructed from hostNameBase and 6-byte AccessoryID
|
char *hostName=NULL; // derived full hostname
|
||||||
const char *modelName; // model name of this device - broadcast as Bonjour field "md"
|
const char *modelName; // model name of this device - broadcast as Bonjour field "md"
|
||||||
char category[3]=""; // category ID of primary accessory - broadcast as Bonjour field "ci" (HAP Section 13)
|
char category[3]=""; // category ID of primary accessory - broadcast as Bonjour field "ci" (HAP Section 13)
|
||||||
unsigned long snapTime; // current time (in millis) snapped before entering Service loops() or updates()
|
unsigned long snapTime; // current time (in millis) snapped before entering Service loops() or updates()
|
||||||
|
|
@ -212,11 +217,13 @@ class Span{
|
||||||
const char *sketchVersion="n/a"; // version of the sketch
|
const char *sketchVersion="n/a"; // version of the sketch
|
||||||
nvs_handle charNVS; // handle for non-volatile-storage of Characteristics data
|
nvs_handle charNVS; // handle for non-volatile-storage of Characteristics data
|
||||||
nvs_handle wifiNVS=0; // handle for non-volatile-storage of WiFi data
|
nvs_handle wifiNVS=0; // handle for non-volatile-storage of WiFi data
|
||||||
nvs_handle otaNVS; // handle for non-volatile storaget of OTA data
|
nvs_handle otaNVS; // handle for non-volatile storage of OTA data
|
||||||
char pairingCodeCommand[12]=""; // user-specified Pairing Code - only needed if Pairing Setup Code is specified in sketch using setPairingCode()
|
char pairingCodeCommand[12]=""; // user-specified Pairing Code - only needed if Pairing Setup Code is specified in sketch using setPairingCode()
|
||||||
String lastClientIP="0.0.0.0"; // IP address of last client accessing device through encrypted channel
|
String lastClientIP="0.0.0.0"; // IP address of last client accessing device through encrypted channel
|
||||||
boolean newCode; // flag indicating new application code has been loaded (based on keeping track of app SHA256)
|
boolean newCode; // flag indicating new application code has been loaded (based on keeping track of app SHA256)
|
||||||
boolean serialInputDisabled=false; // flag indiating that serial input is disabled
|
boolean serialInputDisabled=false; // flag indiating that serial input is disabled
|
||||||
|
uint8_t rebootCount=0; // counts number of times device was rebooted (used in optional Reboot callback)
|
||||||
|
uint32_t rebootCallbackTime; // length of time to wait (in milliseconds) before calling optional Reboot callback
|
||||||
|
|
||||||
int connected=0; // WiFi connection status (increments upon each connect and disconnect)
|
int connected=0; // WiFi connection status (increments upon each connect and disconnect)
|
||||||
unsigned long waitTime=60000; // time to wait (in milliseconds) between WiFi connection attempts
|
unsigned long waitTime=60000; // time to wait (in milliseconds) between WiFi connection attempts
|
||||||
|
|
@ -230,11 +237,14 @@ class Span{
|
||||||
unsigned long comModeLife=DEFAULT_COMMAND_TIMEOUT*1000; // length of time (in milliseconds) to keep Command Mode alive before resuming normal operations
|
unsigned long comModeLife=DEFAULT_COMMAND_TIMEOUT*1000; // length of time (in milliseconds) to keep Command Mode alive before resuming normal operations
|
||||||
uint16_t tcpPortNum=DEFAULT_TCP_PORT; // port for TCP communications between HomeKit and HomeSpan
|
uint16_t tcpPortNum=DEFAULT_TCP_PORT; // port for TCP communications between HomeKit and HomeSpan
|
||||||
char qrID[5]=""; // Setup ID used for pairing with QR Code
|
char qrID[5]=""; // Setup ID used for pairing with QR Code
|
||||||
void (*wifiCallback)()=NULL; // optional callback function to invoke once WiFi connectivity is established
|
void (*wifiCallback)()=NULL; // optional callback function to invoke once WiFi connectivity is initially established
|
||||||
|
void (*wifiCallbackAll)(int)=NULL; // optional callback function to invoke every time WiFi connectivity is established or re-established
|
||||||
|
void (*weblogCallback)(String &)=NULL; // optional callback function to invoke after header table in Web Log is produced
|
||||||
void (*pairCallback)(boolean isPaired)=NULL; // optional callback function to invoke when pairing is established (true) or lost (false)
|
void (*pairCallback)(boolean isPaired)=NULL; // optional callback function to invoke when pairing is established (true) or lost (false)
|
||||||
boolean autoStartAPEnabled=false; // enables auto start-up of Access Point when WiFi Credentials not found
|
boolean autoStartAPEnabled=false; // enables auto start-up of Access Point when WiFi Credentials not found
|
||||||
void (*apFunction)()=NULL; // optional function to invoke when starting Access Point
|
void (*apFunction)()=NULL; // optional function to invoke when starting Access Point
|
||||||
void (*statusCallback)(HS_STATUS status)=NULL; // optional callback when HomeSpan status changes
|
void (*statusCallback)(HS_STATUS status)=NULL; // optional callback when HomeSpan status changes
|
||||||
|
void (*rebootCallback)(uint8_t)=NULL; // optional callback when device reboots
|
||||||
|
|
||||||
WiFiServer *hapServer; // pointer to the HAP Server connection
|
WiFiServer *hapServer; // pointer to the HAP Server connection
|
||||||
Blinker *statusLED; // indicates HomeSpan status
|
Blinker *statusLED; // indicates HomeSpan status
|
||||||
|
|
@ -243,13 +253,15 @@ class Span{
|
||||||
Network network; // configures WiFi and Setup Code via either serial monitor or temporary Access Point
|
Network network; // configures WiFi and Setup Code via either serial monitor or temporary Access Point
|
||||||
SpanWebLog webLog; // optional web status/log
|
SpanWebLog webLog; // optional web status/log
|
||||||
TaskHandle_t pollTaskHandle = NULL; // optional task handle to use for poll() function
|
TaskHandle_t pollTaskHandle = NULL; // optional task handle to use for poll() function
|
||||||
|
TaskHandle_t loopTaskHandle; // Arduino Loop Task handle
|
||||||
|
boolean verboseWifiReconnect = true; // set to false to not print WiFi reconnect attempts messages
|
||||||
|
|
||||||
SpanOTA spanOTA; // manages OTA process
|
SpanOTA spanOTA; // manages OTA process
|
||||||
SpanConfig hapConfig; // track configuration changes to the HAP Accessory database; used to increment the configuration number (c#) when changes found
|
SpanConfig hapConfig; // track configuration changes to the HAP Accessory database; used to increment the configuration number (c#) when changes found
|
||||||
vector<SpanAccessory *> Accessories; // vector of pointers to all Accessories
|
vector<SpanAccessory *, Mallocator<SpanAccessory *>> Accessories; // vector of pointers to all Accessories
|
||||||
vector<SpanService *> Loops; // vector of pointer to all Services that have over-ridden loop() methods
|
vector<SpanService *, Mallocator<SpanService *>> Loops; // vector of pointer to all Services that have over-ridden loop() methods
|
||||||
vector<SpanBuf> Notifications; // vector of SpanBuf objects that store info for Characteristics that are updated with setVal() and require a Notification Event
|
vector<SpanBuf, Mallocator<SpanBuf>> Notifications; // vector of SpanBuf objects that store info for Characteristics that are updated with setVal() and require a Notification Event
|
||||||
vector<SpanButton *> PushButtons; // vector of pointer to all PushButtons
|
vector<SpanButton *, Mallocator<SpanButton *>> PushButtons; // vector of pointer to all PushButtons
|
||||||
unordered_map<uint64_t, uint32_t> TimedWrites; // map of timed-write PIDs and Alarm Times (based on TTLs)
|
unordered_map<uint64_t, uint32_t> TimedWrites; // map of timed-write PIDs and Alarm Times (based on TTLs)
|
||||||
|
|
||||||
unordered_map<char, SpanUserCommand *> UserCommands; // map of pointers to all UserCommands
|
unordered_map<char, SpanUserCommand *> UserCommands; // map of pointers to all UserCommands
|
||||||
|
|
@ -261,16 +273,15 @@ class Span{
|
||||||
void resetStatus(); // resets statusLED and calls statusCallback based on current HomeSpan status
|
void resetStatus(); // resets statusLED and calls statusCallback based on current HomeSpan status
|
||||||
void reboot(); // reboots device
|
void reboot(); // reboots device
|
||||||
|
|
||||||
int sprintfAttributes(char *cBuf, int flags=GET_VALUE|GET_META|GET_PERMS|GET_TYPE|GET_DESC); // prints Attributes JSON database into buf, unless buf=NULL; return number of characters printed, excluding null terminator
|
void printfAttributes(int flags=GET_VALUE|GET_META|GET_PERMS|GET_TYPE|GET_DESC); // writes Attributes JSON database to hapOut stream
|
||||||
|
|
||||||
void prettyPrint(char *buf, int nsp=2, int minLogLevel=0); // print arbitrary JSON from buf to serial monitor, formatted with indentions of 'nsp' spaces, subject to specified minimum log level
|
|
||||||
SpanCharacteristic *find(uint32_t aid, int iid); // return Characteristic with matching aid and iid (else NULL if not found)
|
SpanCharacteristic *find(uint32_t aid, int iid); // return Characteristic with matching aid and iid (else NULL if not found)
|
||||||
int countCharacteristics(char *buf); // return number of characteristic objects referenced in PUT /characteristics JSON request
|
int countCharacteristics(char *buf); // return number of characteristic objects referenced in PUT /characteristics JSON request
|
||||||
int updateCharacteristics(char *buf, SpanBuf *pObj); // parses PUT /characteristics JSON request 'buf into 'pObj' and updates referenced characteristics; returns 1 on success, 0 on fail
|
int updateCharacteristics(char *buf, SpanBuf *pObj); // parses PUT /characteristics JSON request 'buf into 'pObj' and updates referenced characteristics; returns 1 on success, 0 on fail
|
||||||
int sprintfAttributes(SpanBuf *pObj, int nObj, char *cBuf); // prints SpanBuf object into buf, unless buf=NULL; return number of characters printed, excluding null terminator, even if buf=NULL
|
void printfAttributes(SpanBuf *pObj, int nObj); // writes SpanBuf objects to hapOut stream
|
||||||
int sprintfAttributes(char **ids, int numIDs, int flags, char *cBuf); // prints accessory.characteristic ids into buf, unless buf=NULL; return number of characters printed, excluding null terminator, even if buf=NULL
|
boolean printfAttributes(char **ids, int numIDs, int flags); // writes accessory requested characteristic ids to hapOut stream - returns true if all characteristics are found and readable, else returns false
|
||||||
void clearNotify(int slotNum); // set ev notification flags for connection 'slotNum' to false across all characteristics
|
void clearNotify(int slotNum); // set ev notification flags for connection 'slotNum' to false across all characteristics
|
||||||
int sprintfNotify(SpanBuf *pObj, int nObj, char *cBuf, int conNum); // prints notification JSON into buf based on SpanBuf objects and specified connection number
|
void printfNotify(SpanBuf *pObj, int nObj, int conNum); // writes notification JSON to hapOut stream based on SpanBuf objects and specified connection number
|
||||||
|
|
||||||
static boolean invalidUUID(const char *uuid, boolean isCustom){
|
static boolean invalidUUID(const char *uuid, boolean isCustom){
|
||||||
int x=0;
|
int x=0;
|
||||||
|
|
@ -291,49 +302,55 @@ class Span{
|
||||||
boolean updateDatabase(boolean updateMDNS=true); // updates HAP Configuration Number and Loop vector; if updateMDNS=true and config number has changed, re-broadcasts MDNS 'c#' record; returns true if config number changed
|
boolean updateDatabase(boolean updateMDNS=true); // updates HAP Configuration Number and Loop vector; if updateMDNS=true and config number has changed, re-broadcasts MDNS 'c#' record; returns true if config number changed
|
||||||
boolean deleteAccessory(uint32_t aid); // deletes Accessory with matching aid; returns true if found, else returns false
|
boolean deleteAccessory(uint32_t aid); // deletes Accessory with matching aid; returns true if found, else returns false
|
||||||
|
|
||||||
void setControlPin(uint8_t pin){controlButton=new PushButton(pin);} // sets Control Pin
|
Span& setControlPin(uint8_t pin, PushButton::triggerType_t triggerType=PushButton::TRIGGER_ON_LOW){ // sets Control Pin, with optional trigger type
|
||||||
void setStatusPin(uint8_t pin){statusDevice=new GenericLED(pin);} // sets Status Device to a simple LED on specified pin
|
controlButton=new PushButton(pin, triggerType);
|
||||||
void setStatusAutoOff(uint16_t duration){autoOffLED=duration;} // sets Status LED auto off (seconds)
|
return(*this);
|
||||||
int getStatusPin(){return(statusLED->getPin());} // get Status Pin (getPin will return -1 if underlying statusDevice is undefined)
|
}
|
||||||
int getControlPin(){return(controlButton?controlButton->getPin():-1);} // get Control Pin (returns -1 if undefined)
|
|
||||||
|
|
||||||
void setStatusPixel(uint8_t pin,float h=0,float s=100,float v=100){ // sets Status Device to an RGB Pixel on specified pin
|
int getControlPin(){return(controlButton?controlButton->getPin():-1);} // get Control Pin (returns -1 if undefined)
|
||||||
|
|
||||||
|
Span& setStatusPin(uint8_t pin){statusDevice=new GenericLED(pin);return(*this);} // sets Status Device to a simple LED on specified pin
|
||||||
|
Span& setStatusPixel(uint8_t pin,float h=0,float s=100,float v=100){ // sets Status Device to an RGB Pixel on specified pin
|
||||||
statusDevice=((new Pixel(pin))->setOnColor(Pixel::HSV(h,s,v)));
|
statusDevice=((new Pixel(pin))->setOnColor(Pixel::HSV(h,s,v)));
|
||||||
|
return(*this);
|
||||||
}
|
}
|
||||||
|
Span& setStatusDevice(Blinkable *sDev){statusDevice=sDev;return(*this);} // sets Status Device to a generic Blinkable object
|
||||||
|
|
||||||
void setStatusDevice(Blinkable *sDev){statusDevice=sDev;}
|
Span& setStatusAutoOff(uint16_t duration){autoOffLED=duration;return(*this);} // sets Status LED auto off (seconds)
|
||||||
void refreshStatusDevice(){if(statusLED)statusLED->refresh();}
|
int getStatusPin(){return(statusLED->getPin());} // get Status Pin (returns -1 if undefined)
|
||||||
|
void refreshStatusDevice(){if(statusLED)statusLED->refresh();} // refreshes state of Status LED
|
||||||
|
|
||||||
void setApSSID(const char *ssid){network.apSSID=ssid;} // sets Access Point SSID
|
Span& setApSSID(const char *ssid){network.apSSID=ssid;return(*this);} // sets Access Point SSID
|
||||||
void setApPassword(const char *pwd){network.apPassword=pwd;} // sets Access Point Password
|
Span& setApPassword(const char *pwd){network.apPassword=pwd;return(*this);} // sets Access Point Password
|
||||||
void setApTimeout(uint16_t nSec){network.lifetime=nSec*1000;} // sets Access Point Timeout (seconds)
|
Span& setApTimeout(uint16_t nSec){network.lifetime=nSec*1000;return(*this);} // sets Access Point Timeout (seconds)
|
||||||
void setCommandTimeout(uint16_t nSec){comModeLife=nSec*1000;} // sets Command Mode Timeout (seconds)
|
Span& setCommandTimeout(uint16_t nSec){comModeLife=nSec*1000;return(*this);} // sets Command Mode Timeout (seconds)
|
||||||
void setLogLevel(int level){logLevel=level;} // sets Log Level for log messages (0=baseline, 1=intermediate, 2=all, -1=disable all serial input/output)
|
Span& setLogLevel(int level){logLevel=level;return(*this);} // sets Log Level for log messages (0=baseline, 1=intermediate, 2=all, -1=disable all serial input/output)
|
||||||
int getLogLevel(){return(logLevel);} // get Log Level
|
int getLogLevel(){return(logLevel);} // get Log Level
|
||||||
void setSerialInputDisable(boolean val){serialInputDisabled=val;} // sets whether serial input is disabled (true) or enabled (false)
|
Span& setSerialInputDisable(boolean val){serialInputDisabled=val;return(*this);} // sets whether serial input is disabled (true) or enabled (false)
|
||||||
boolean getSerialInputDisable(){return(serialInputDisabled);} // returns true if serial input is disabled, or false if serial input in enabled
|
boolean getSerialInputDisable(){return(serialInputDisabled);} // returns true if serial input is disabled, or false if serial input in enabled
|
||||||
void reserveSocketConnections(uint8_t n){maxConnections-=n;} // reserves n socket connections *not* to be used for HAP
|
Span& reserveSocketConnections(uint8_t n){maxConnections-=n;return(*this);} // reserves n socket connections *not* to be used for HAP
|
||||||
void setHostNameSuffix(const char *suffix){hostNameSuffix=suffix;} // sets the hostName suffix to be used instead of the 6-byte AccessoryID
|
Span& setHostNameSuffix(const char *suffix){hostNameSuffix=suffix;return(*this);} // sets the hostName suffix to be used instead of the 6-byte AccessoryID
|
||||||
void setPortNum(uint16_t port){tcpPortNum=port;} // sets the TCP port number to use for communications between HomeKit and HomeSpan
|
Span& setPortNum(uint16_t port){tcpPortNum=port;return(*this);} // sets the TCP port number to use for communications between HomeKit and HomeSpan
|
||||||
void setQRID(const char *id); // sets the Setup ID for optional pairing with a QR Code
|
Span& setQRID(const char *id); // sets the Setup ID for optional pairing with a QR Code
|
||||||
void setSketchVersion(const char *sVer){sketchVersion=sVer;} // set optional sketch version number
|
Span& setSketchVersion(const char *sVer){sketchVersion=sVer;return(*this);} // set optional sketch version number
|
||||||
const char *getSketchVersion(){return sketchVersion;} // get sketch version number
|
const char *getSketchVersion(){return sketchVersion;} // get sketch version number
|
||||||
void setWifiCallback(void (*f)()){wifiCallback=f;} // sets an optional user-defined function to call once WiFi connectivity is established
|
Span& setWifiCallback(void (*f)()){wifiCallback=f;return(*this);} // sets an optional user-defined function to call once WiFi connectivity is initially established
|
||||||
void setPairCallback(void (*f)(boolean isPaired)){pairCallback=f;} // sets an optional user-defined function to call when Pairing is established (true) or lost (false)
|
Span& setWifiCallbackAll(void (*f)(int)){wifiCallbackAll=f;return(*this);} // sets an optional user-defined function to call every time WiFi connectivity is established or re-established
|
||||||
void setApFunction(void (*f)()){apFunction=f;} // sets an optional user-defined function to call when activating the WiFi Access Point
|
Span& setPairCallback(void (*f)(boolean isPaired)){pairCallback=f;return(*this);} // sets an optional user-defined function to call when Pairing is established (true) or lost (false)
|
||||||
void enableAutoStartAP(){autoStartAPEnabled=true;} // enables auto start-up of Access Point when WiFi Credentials not found
|
Span& setApFunction(void (*f)()){apFunction=f;return(*this);} // sets an optional user-defined function to call when activating the WiFi Access Point
|
||||||
void setWifiCredentials(const char *ssid, const char *pwd); // sets WiFi Credentials
|
Span& enableAutoStartAP(){autoStartAPEnabled=true;return(*this);} // enables auto start-up of Access Point when WiFi Credentials not found
|
||||||
void setStatusCallback(void (*f)(HS_STATUS status)){statusCallback=f;} // sets an optional user-defined function to call when HomeSpan status changes
|
Span& setWifiCredentials(const char *ssid, const char *pwd); // sets WiFi Credentials
|
||||||
const char* statusString(HS_STATUS s); // returns char string for HomeSpan status change messages
|
Span& setStatusCallback(void (*f)(HS_STATUS status)){statusCallback=f;return(*this);} // sets an optional user-defined function to call when HomeSpan status changes
|
||||||
|
const char* statusString(HS_STATUS s); // returns char string for HomeSpan status change messages
|
||||||
void setPairingCode(const char *s){sprintf(pairingCodeCommand,"S %9s",s);} // sets the Pairing Code - use is NOT recommended. Use 'S' from CLI instead
|
Span& setPairingCode(const char *s); // sets the Pairing Code - use is NOT recommended. Use 'S' from CLI instead
|
||||||
void deleteStoredValues(){processSerialCommand("V");} // deletes stored Characteristic values from NVS
|
void deleteStoredValues(){processSerialCommand("V");} // deletes stored Characteristic values from NVS
|
||||||
|
|
||||||
int enableOTA(boolean auth=true, boolean safeLoad=true){return(spanOTA.init(auth, safeLoad, NULL));} // enables Over-the-Air updates, with (auth=true) or without (auth=false) authorization password
|
int enableOTA(boolean auth=true, boolean safeLoad=true){return(spanOTA.init(auth, safeLoad, NULL));} // enables Over-the-Air updates, with (auth=true) or without (auth=false) authorization password
|
||||||
int enableOTA(const char *pwd, boolean safeLoad=true){return(spanOTA.init(true, safeLoad, pwd));} // enables Over-the-Air updates, with custom authorization password (overrides any password stored with the 'O' command)
|
int enableOTA(const char *pwd, boolean safeLoad=true){return(spanOTA.init(true, safeLoad, pwd));} // enables Over-the-Air updates, with custom authorization password (overrides any password stored with the 'O' command)
|
||||||
|
|
||||||
void enableWebLog(uint16_t maxEntries=0, const char *serv=NULL, const char *tz="UTC", const char *url=DEFAULT_WEBLOG_URL){ // enable Web Logging
|
Span& enableWebLog(uint16_t maxEntries=0, const char *serv=NULL, const char *tz="UTC", const char *url=DEFAULT_WEBLOG_URL){ // enable Web Logging
|
||||||
webLog.init(maxEntries, serv, tz, url);
|
webLog.init(maxEntries, serv, tz, url);
|
||||||
|
return(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addWebLog(boolean sysMsg, const char *fmt, ...){ // add Web Log entry
|
void addWebLog(boolean sysMsg, const char *fmt, ...){ // add Web Log entry
|
||||||
|
|
@ -343,14 +360,28 @@ class Span{
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setWebLogCSS(const char *css){webLog.css="\n" + String(css) + "\n";}
|
Span& setWebLogCSS(const char *css){webLog.css="\n" + String(css) + "\n";return(*this);}
|
||||||
|
Span& setWebLogCallback(void (*f)(String &)){weblogCallback=f;return(*this);}
|
||||||
|
void getWebLog(void (*f)(const char *, void *), void *);
|
||||||
|
|
||||||
|
Span& setVerboseWifiReconnect(bool verbose=true){verboseWifiReconnect=verbose;return(*this);}
|
||||||
|
|
||||||
|
Span& setRebootCallback(void (*f)(uint8_t),uint32_t t=DEFAULT_REBOOT_CALLBACK_TIME){rebootCallback=f;rebootCallbackTime=t;return(*this);}
|
||||||
|
|
||||||
void autoPoll(uint32_t stackSize=8192, uint32_t priority=1, uint32_t cpu=0){ // start pollTask()
|
void autoPoll(uint32_t stackSize=8192, uint32_t priority=1, uint32_t cpu=0){ // start pollTask()
|
||||||
xTaskCreateUniversal([](void *parms){for(;;)homeSpan.pollTask();}, "pollTask", stackSize, NULL, priority, &pollTaskHandle, cpu);
|
xTaskCreateUniversal([](void *parms){
|
||||||
|
for(;;){
|
||||||
|
homeSpan.pollTask();
|
||||||
|
vTaskDelay(5);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pollTask", stackSize, NULL, priority, &pollTaskHandle, cpu);
|
||||||
LOG0("\n*** AutoPolling Task started with priority=%d\n\n",uxTaskPriorityGet(pollTaskHandle));
|
LOG0("\n*** AutoPolling Task started with priority=%d\n\n",uxTaskPriorityGet(pollTaskHandle));
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTimeServerTimeout(uint32_t tSec){webLog.waitTime=tSec*1000;} // sets wait time (in seconds) for optional web log time server to connect
|
TaskHandle_t getAutoPollTask(){return(pollTaskHandle);}
|
||||||
|
|
||||||
|
Span& setTimeServerTimeout(uint32_t tSec){webLog.waitTime=tSec*1000;return(*this);} // sets wait time (in seconds) for optional web log time server to connect
|
||||||
|
|
||||||
[[deprecated("Please use reserveSocketConnections(n) method instead.")]]
|
[[deprecated("Please use reserveSocketConnections(n) method instead.")]]
|
||||||
void setMaxConnections(uint8_t n){requestedMaxCon=n;} // sets maximum number of simultaneous HAP connections
|
void setMaxConnections(uint8_t n){requestedMaxCon=n;} // sets maximum number of simultaneous HAP connections
|
||||||
|
|
@ -368,9 +399,9 @@ class SpanAccessory{
|
||||||
|
|
||||||
uint32_t aid=0; // Accessory Instance ID (HAP Table 6-1)
|
uint32_t aid=0; // Accessory Instance ID (HAP Table 6-1)
|
||||||
int iidCount=0; // running count of iid to use for Services and Characteristics associated with this Accessory
|
int iidCount=0; // running count of iid to use for Services and Characteristics associated with this Accessory
|
||||||
vector<SpanService *> Services; // vector of pointers to all Services in this Accessory
|
vector<SpanService *, Mallocator<SpanService*>> Services; // vector of pointers to all Services in this Accessory
|
||||||
|
|
||||||
int sprintfAttributes(char *cBuf, int flags); // prints Accessory JSON database into buf, unless buf=NULL; return number of characters printed, excluding null terminator, even if buf=NULL
|
void printfAttributes(int flags); // writes Accessory JSON to hapOut stream
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|
@ -378,7 +409,8 @@ class SpanAccessory{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
SpanAccessory(uint32_t aid=0); // constructor
|
void *operator new(size_t size){return(HS_MALLOC(size));} // override new operator to use PSRAM when available
|
||||||
|
SpanAccessory(uint32_t aid=0); // constructor
|
||||||
};
|
};
|
||||||
|
|
||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
|
|
@ -395,26 +427,27 @@ class SpanService{
|
||||||
const char *hapName; // HAP Name
|
const char *hapName; // HAP Name
|
||||||
boolean hidden=false; // optional property indicating service is hidden
|
boolean hidden=false; // optional property indicating service is hidden
|
||||||
boolean primary=false; // optional property indicating service is primary
|
boolean primary=false; // optional property indicating service is primary
|
||||||
vector<SpanCharacteristic *> Characteristics; // vector of pointers to all Characteristics in this Service
|
vector<SpanCharacteristic *, Mallocator<SpanCharacteristic*>> Characteristics; // vector of pointers to all Characteristics in this Service
|
||||||
vector<SpanService *> linkedServices; // vector of pointers to any optional linked Services
|
vector<SpanService *, Mallocator<SpanService *>> linkedServices; // vector of pointers to any optional linked Services
|
||||||
boolean isCustom; // flag to indicate this is a Custom Service
|
boolean isCustom; // flag to indicate this is a Custom Service
|
||||||
SpanAccessory *accessory=NULL; // pointer to Accessory containing this Service
|
SpanAccessory *accessory=NULL; // pointer to Accessory containing this Service
|
||||||
|
|
||||||
int sprintfAttributes(char *cBuf, int flags); // prints Service JSON records into buf; return number of characters printed, excluding null terminator
|
void printfAttributes(int flags); // writes Service JSON to hapOut stream
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
virtual ~SpanService(); // destructor
|
virtual ~SpanService(); // destructor
|
||||||
unordered_set<HapChar *> req; // unordered set of pointers to all required HAP Characteristic Types for this Service
|
vector<HapChar *, Mallocator<HapChar*>> req; // vector of pointers to all required HAP Characteristic Types for this Service
|
||||||
unordered_set<HapChar *> opt; // unordered set of pointers to all optional HAP Characteristic Types for this Service
|
vector<HapChar *, Mallocator<HapChar*>> opt; // vector of pointers to all optional HAP Characteristic Types for this Service
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
void *operator new(size_t size){return(HS_MALLOC(size));} // override new operator to use PSRAM when available
|
||||||
SpanService(const char *type, const char *hapName, boolean isCustom=false); // constructor
|
SpanService(const char *type, const char *hapName, boolean isCustom=false); // constructor
|
||||||
SpanService *setPrimary(); // sets the Service Type to be primary and returns pointer to self
|
SpanService *setPrimary(); // sets the Service Type to be primary and returns pointer to self
|
||||||
SpanService *setHidden(); // sets the Service Type to be hidden and returns pointer to self
|
SpanService *setHidden(); // sets the Service Type to be hidden and returns pointer to self
|
||||||
SpanService *addLink(SpanService *svc); // adds svc as a Linked Service and returns pointer to self
|
SpanService *addLink(SpanService *svc); // adds svc as a Linked Service and returns pointer to self
|
||||||
vector<SpanService *> getLinks(){return(linkedServices);} // returns linkedServices vector for use as range in "for-each" loops
|
vector<SpanService *, Mallocator<SpanService *>> getLinks(){return(linkedServices);} // returns linkedServices vector for use as range in "for-each" loops
|
||||||
|
|
||||||
virtual boolean update() {return(true);} // placeholder for code that is called when a Service is updated via a Controller. Must return true/false depending on success of update
|
virtual boolean update() {return(true);} // placeholder for code that is called when a Service is updated via a Controller. Must return true/false depending on success of update
|
||||||
virtual void loop(){} // loops for each Service - called every cycle if over-ridden with user-defined code
|
virtual void loop(){} // loops for each Service - called every cycle if over-ridden with user-defined code
|
||||||
|
|
@ -466,11 +499,11 @@ class SpanCharacteristic{
|
||||||
UVal newValue; // the updated value requested by PUT /characteristic
|
UVal newValue; // the updated value requested by PUT /characteristic
|
||||||
SpanService *service=NULL; // pointer to Service containing this Characteristic
|
SpanService *service=NULL; // pointer to Service containing this Characteristic
|
||||||
|
|
||||||
int sprintfAttributes(char *cBuf, int flags); // prints Characteristic JSON records into buf, according to flags mask; return number of characters printed, excluding null terminator
|
void printfAttributes(int flags); // writes Characteristic JSON to hapOut stream
|
||||||
StatusCode loadUpdate(char *val, char *ev); // load updated val/ev from PUT /characteristic JSON request. Return intitial HAP status code (checks to see if characteristic is found, is writable, etc.)
|
StatusCode loadUpdate(char *val, char *ev); // load updated val/ev from PUT /characteristic JSON request. Return intitial HAP status code (checks to see if characteristic is found, is writable, etc.)
|
||||||
|
|
||||||
String uvPrint(UVal &u){
|
String uvPrint(UVal &u){
|
||||||
char c[64];
|
char c[67]; // space for 64 characters + surrounding quotes + terminating null
|
||||||
switch(format){
|
switch(format){
|
||||||
case FORMAT::BOOL:
|
case FORMAT::BOOL:
|
||||||
return(String(u.BOOL));
|
return(String(u.BOOL));
|
||||||
|
|
@ -490,7 +523,7 @@ class SpanCharacteristic{
|
||||||
return(String(c));
|
return(String(c));
|
||||||
case FORMAT::STRING:
|
case FORMAT::STRING:
|
||||||
case FORMAT::DATA:
|
case FORMAT::DATA:
|
||||||
sprintf(c,"\"%s\"",u.STRING);
|
sprintf(c,"\"%.64s\"",u.STRING); // Truncating string to 64 chars
|
||||||
return(String(c));
|
return(String(c));
|
||||||
} // switch
|
} // switch
|
||||||
return(String()); // included to prevent compiler warnings
|
return(String()); // included to prevent compiler warnings
|
||||||
|
|
@ -504,7 +537,7 @@ class SpanCharacteristic{
|
||||||
}
|
}
|
||||||
|
|
||||||
void uvSet(UVal &u, const char *val){
|
void uvSet(UVal &u, const char *val){
|
||||||
u.STRING = (char *)realloc(u.STRING, strlen(val) + 1);
|
u.STRING = (char *)HS_REALLOC(u.STRING, strlen(val) + 1);
|
||||||
strcpy(u.STRING, val);
|
strcpy(u.STRING, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -558,7 +591,7 @@ class SpanCharacteristic{
|
||||||
case FORMAT::DATA:
|
case FORMAT::DATA:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return(0); // included to prevent compiler warnings
|
return((T)0); // included to prevent compiler warnings
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
@ -570,28 +603,24 @@ class SpanCharacteristic{
|
||||||
uvSet(value,val);
|
uvSet(value,val);
|
||||||
|
|
||||||
if(nvsStore){
|
if(nvsStore){
|
||||||
nvsKey=(char *)malloc(16);
|
nvsKey=(char *)HS_MALLOC(16);
|
||||||
uint16_t t;
|
uint16_t t;
|
||||||
sscanf(type,"%hx",&t);
|
sscanf(type,"%hx",&t);
|
||||||
sprintf(nvsKey,"%04X%08X%03X",t,aid,iid&0xFFF);
|
sprintf(nvsKey,"%04X%08X%03X",t,aid,iid&0xFFF);
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
if(format!=FORMAT::STRING && format!=FORMAT::DATA){
|
if(format!=FORMAT::STRING && format!=FORMAT::DATA){
|
||||||
if(!nvs_get_blob(homeSpan.charNVS,nvsKey,NULL,&len)){
|
if(nvs_get_u64(homeSpan.charNVS,nvsKey,&(value.UINT64))!=ESP_OK) {
|
||||||
nvs_get_blob(homeSpan.charNVS,nvsKey,&value,&len);
|
nvs_set_u64(homeSpan.charNVS,nvsKey,value.UINT64); // store data as uint64_t regardless of actual type (it will be read correctly when access through uvGet())
|
||||||
}
|
nvs_commit(homeSpan.charNVS); // commit to NVS
|
||||||
else {
|
|
||||||
nvs_set_blob(homeSpan.charNVS,nvsKey,&value,sizeof(UVal)); // store data
|
|
||||||
nvs_commit(homeSpan.charNVS); // commit to NVS
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(!nvs_get_str(homeSpan.charNVS,nvsKey,NULL,&len)){
|
if(!nvs_get_str(homeSpan.charNVS,nvsKey,NULL,&len)){
|
||||||
char c[len];
|
value.STRING = (char *)HS_REALLOC(value.STRING,len);
|
||||||
nvs_get_str(homeSpan.charNVS,nvsKey,c,&len);
|
nvs_get_str(homeSpan.charNVS,nvsKey,value.STRING,&len);
|
||||||
uvSet(value,(const char *)c);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
nvs_set_str(homeSpan.charNVS,nvsKey,value.STRING); // store string data
|
nvs_set_str(homeSpan.charNVS,nvsKey,value.STRING); // store string data
|
||||||
nvs_commit(homeSpan.charNVS); // commit to NVS
|
nvs_commit(homeSpan.charNVS); // commit to NVS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -609,6 +638,7 @@ class SpanCharacteristic{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
void *operator new(size_t size){return(HS_MALLOC(size));} // override new operator to use PSRAM when available
|
||||||
SpanCharacteristic(HapChar *hapChar, boolean isCustom=false); // constructor
|
SpanCharacteristic(HapChar *hapChar, boolean isCustom=false); // constructor
|
||||||
|
|
||||||
template <class T=int> T getVal(){
|
template <class T=int> T getVal(){
|
||||||
|
|
@ -649,7 +679,7 @@ class SpanCharacteristic{
|
||||||
sb.characteristic=this; // set characteristic
|
sb.characteristic=this; // set characteristic
|
||||||
sb.status=StatusCode::OK; // set status
|
sb.status=StatusCode::OK; // set status
|
||||||
char dummy[]="";
|
char dummy[]="";
|
||||||
sb.val=dummy; // set dummy "val" so that sprintfNotify knows to consider this "update"
|
sb.val=dummy; // set dummy "val" so that printfNotify knows to consider this "update"
|
||||||
homeSpan.Notifications.push_back(sb); // store SpanBuf in Notifications vector
|
homeSpan.Notifications.push_back(sb); // store SpanBuf in Notifications vector
|
||||||
|
|
||||||
if(nvsKey){
|
if(nvsKey){
|
||||||
|
|
@ -710,8 +740,8 @@ class SpanCharacteristic{
|
||||||
size_t olen;
|
size_t olen;
|
||||||
mbedtls_base64_encode(NULL,0,&olen,data,len); // get length of string buffer needed (mbedtls includes the trailing null in this size)
|
mbedtls_base64_encode(NULL,0,&olen,data,len); // get length of string buffer needed (mbedtls includes the trailing null in this size)
|
||||||
TempBuffer<char> tBuf(olen); // create temporary string buffer, with room for trailing null
|
TempBuffer<char> tBuf(olen); // create temporary string buffer, with room for trailing null
|
||||||
mbedtls_base64_encode((uint8_t*)tBuf.buf,olen,&olen,data,len ); // encode data into string buf
|
mbedtls_base64_encode((uint8_t*)tBuf.get(),olen,&olen,data,len ); // encode data into string buf
|
||||||
setString(tBuf.buf); // call setString to continue processing as if characteristic was a string
|
setString(tBuf); // call setString to continue processing as if characteristic was a string
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> void setVal(T val, boolean notify=true){
|
template <typename T> void setVal(T val, boolean notify=true){
|
||||||
|
|
@ -721,8 +751,8 @@ class SpanCharacteristic{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(val < uvGet<T>(minValue) || val > uvGet<T>(maxValue)){
|
if(!((val >= uvGet<T>(minValue)) && (val <= uvGet<T>(maxValue)))){
|
||||||
LOG0("\n*** WARNING: Attempt to update Characteristic::%s with setVal(%g) is out of range [%g,%g]. This may cause device to become non-reponsive!\n\n",
|
LOG0("\n*** WARNING: Attempt to update Characteristic::%s with setVal(%g) is out of range [%g,%g]. This may cause device to become non-responsive!\n\n",
|
||||||
hapName,(double)val,uvGet<double>(minValue),uvGet<double>(maxValue));
|
hapName,(double)val,uvGet<double>(minValue),uvGet<double>(maxValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -736,11 +766,11 @@ class SpanCharacteristic{
|
||||||
sb.characteristic=this; // set characteristic
|
sb.characteristic=this; // set characteristic
|
||||||
sb.status=StatusCode::OK; // set status
|
sb.status=StatusCode::OK; // set status
|
||||||
char dummy[]="";
|
char dummy[]="";
|
||||||
sb.val=dummy; // set dummy "val" so that sprintfNotify knows to consider this "update"
|
sb.val=dummy; // set dummy "val" so that printfNotify knows to consider this "update"
|
||||||
homeSpan.Notifications.push_back(sb); // store SpanBuf in Notifications vector
|
homeSpan.Notifications.push_back(sb); // store SpanBuf in Notifications vector
|
||||||
|
|
||||||
if(nvsKey){
|
if(nvsKey){
|
||||||
nvs_set_blob(homeSpan.charNVS,nvsKey,&value,sizeof(UVal)); // store data
|
nvs_set_u64(homeSpan.charNVS,nvsKey,value.UINT64); // store data as uint64_t regardless of actual type (it will be read correctly when access through uvGet())
|
||||||
nvs_commit(homeSpan.charNVS);
|
nvs_commit(homeSpan.charNVS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -782,13 +812,13 @@ class SpanCharacteristic{
|
||||||
}
|
}
|
||||||
|
|
||||||
SpanCharacteristic *setDescription(const char *c){
|
SpanCharacteristic *setDescription(const char *c){
|
||||||
desc = (char *)realloc(desc, strlen(c) + 1);
|
desc = (char *)HS_REALLOC(desc, strlen(c) + 1);
|
||||||
strcpy(desc, c);
|
strcpy(desc, c);
|
||||||
return(this);
|
return(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
SpanCharacteristic *setUnit(const char *c){
|
SpanCharacteristic *setUnit(const char *c){
|
||||||
unit = (char *)realloc(unit, strlen(c) + 1);
|
unit = (char *)HS_REALLOC(unit, strlen(c) + 1);
|
||||||
strcpy(unit, c);
|
strcpy(unit, c);
|
||||||
return(this);
|
return(this);
|
||||||
}
|
}
|
||||||
|
|
@ -826,15 +856,6 @@ class SpanButton : public PushButton {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
static constexpr triggerType_t TRIGGER_ON_LOW=PushButton::TRIGGER_ON_LOW;
|
|
||||||
static constexpr triggerType_t TRIGGER_ON_HIGH=PushButton::TRIGGER_ON_HIGH;
|
|
||||||
|
|
||||||
#if SOC_TOUCH_SENSOR_NUM > 0
|
|
||||||
static constexpr triggerType_t TRIGGER_ON_TOUCH=PushButton::TRIGGER_ON_TOUCH;
|
|
||||||
static void setTouchCycles(uint16_t measureTime, uint16_t sleepTime){PushButton::setTouchCycles(measureTime,sleepTime);}
|
|
||||||
static void setTouchThreshold(touch_value_t thresh){PushButton::setTouchThreshold(thresh);}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SpanButton(int pin, uint16_t longTime=2000, uint16_t singleTime=5, uint16_t doubleTime=200, triggerType_t triggerType=TRIGGER_ON_LOW);
|
SpanButton(int pin, uint16_t longTime=2000, uint16_t singleTime=5, uint16_t doubleTime=200, triggerType_t triggerType=TRIGGER_ON_LOW);
|
||||||
SpanButton(int pin, triggerType_t triggerType, uint16_t longTime=2000, uint16_t singleTime=5, uint16_t doubleTime=200) : SpanButton(pin,longTime,singleTime,doubleTime,triggerType){};
|
SpanButton(int pin, triggerType_t triggerType, uint16_t longTime=2000, uint16_t singleTime=5, uint16_t doubleTime=200) : SpanButton(pin,longTime,singleTime,doubleTime,triggerType){};
|
||||||
|
|
||||||
|
|
@ -882,7 +903,8 @@ class SpanPoint {
|
||||||
static uint8_t lmk[16];
|
static uint8_t lmk[16];
|
||||||
static boolean initialized;
|
static boolean initialized;
|
||||||
static boolean isHub;
|
static boolean isHub;
|
||||||
static vector<SpanPoint *> SpanPoints;
|
static boolean useEncryption;
|
||||||
|
static vector<SpanPoint *, Mallocator<SpanPoint *>> SpanPoints;
|
||||||
static uint16_t channelMask; // channel mask (only used for remote devices)
|
static uint16_t channelMask; // channel mask (only used for remote devices)
|
||||||
static QueueHandle_t statusQueue; // queue for communication between SpanPoint::dataSend and SpanPoint::send
|
static QueueHandle_t statusQueue; // queue for communication between SpanPoint::dataSend and SpanPoint::send
|
||||||
static nvs_handle pointNVS; // NVS storage for channel number (only used for remote devices)
|
static nvs_handle pointNVS; // NVS storage for channel number (only used for remote devices)
|
||||||
|
|
@ -899,8 +921,9 @@ class SpanPoint {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
SpanPoint(const char *macAddress, int sendSize, int receiveSize, int queueDepth=1, boolean useAPaddress=false);
|
SpanPoint(const char *macAddress, int sendSize, int receiveSize, int queueDepth=1, boolean useAPaddress=false);
|
||||||
static void setPassword(const char *pwd){init(pwd);};
|
static void setPassword(const char *pwd){init(pwd);}
|
||||||
static void setChannelMask(uint16_t mask);
|
static void setChannelMask(uint16_t mask);
|
||||||
|
static void setEncryption(boolean encrypt){useEncryption=encrypt;}
|
||||||
boolean get(void *dataBuf);
|
boolean get(void *dataBuf);
|
||||||
boolean send(const void *data);
|
boolean send(const void *data);
|
||||||
uint32_t time(){return(millis()-receiveTime);}
|
uint32_t time(){return(millis()-receiveTime);}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
@ -40,7 +40,7 @@ void Network::scan(){
|
||||||
int n=WiFi.scanNetworks();
|
int n=WiFi.scanNetworks();
|
||||||
|
|
||||||
free(ssidList);
|
free(ssidList);
|
||||||
ssidList=(char **)calloc(n,sizeof(char *));
|
ssidList=(char **)HS_CALLOC(n,sizeof(char *));
|
||||||
numSSID=0;
|
numSSID=0;
|
||||||
|
|
||||||
for(int i=0;i<n;i++){
|
for(int i=0;i<n;i++){
|
||||||
|
|
@ -50,7 +50,7 @@ void Network::scan(){
|
||||||
found=true;
|
found=true;
|
||||||
}
|
}
|
||||||
if(!found){
|
if(!found){
|
||||||
ssidList[numSSID]=(char *)calloc(WiFi.SSID(i).length()+1,sizeof(char));
|
ssidList[numSSID]=(char *)HS_CALLOC(WiFi.SSID(i).length()+1,sizeof(char));
|
||||||
sprintf(ssidList[numSSID],"%s",WiFi.SSID(i).c_str());
|
sprintf(ssidList[numSSID],"%s",WiFi.SSID(i).c_str());
|
||||||
numSSID++;
|
numSSID++;
|
||||||
}
|
}
|
||||||
|
|
@ -117,9 +117,6 @@ void Network::apConfigure(){
|
||||||
WiFiServer apServer(80);
|
WiFiServer apServer(80);
|
||||||
client=0;
|
client=0;
|
||||||
|
|
||||||
TempBuffer <uint8_t> tempBuffer(MAX_HTTP+1);
|
|
||||||
uint8_t *httpBuf=tempBuffer.buf;
|
|
||||||
|
|
||||||
const byte DNS_PORT = 53;
|
const byte DNS_PORT = 53;
|
||||||
DNSServer dnsServer;
|
DNSServer dnsServer;
|
||||||
IPAddress apIP(192, 168, 4, 1);
|
IPAddress apIP(192, 168, 4, 1);
|
||||||
|
|
@ -179,19 +176,29 @@ void Network::apConfigure(){
|
||||||
LOG2(client.remoteIP());
|
LOG2(client.remoteIP());
|
||||||
LOG2(" <<<<<<<<<\n");
|
LOG2(" <<<<<<<<<\n");
|
||||||
|
|
||||||
int nBytes=client.read(httpBuf,MAX_HTTP+1); // read all available bytes up to maximum allowed+1
|
int messageSize=client.available();
|
||||||
|
|
||||||
if(nBytes>MAX_HTTP){ // exceeded maximum number of bytes allowed
|
if(messageSize>MAX_HTTP){ // exceeded maximum number of bytes allowed
|
||||||
badRequestError();
|
badRequestError();
|
||||||
LOG0("\n*** ERROR: Exceeded maximum HTTP message length\n\n");
|
LOG0("\n*** ERROR: HTTP message of %d bytes exceeds maximum allowed (%d)\n\n",messageSize,MAX_HTTP);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
httpBuf[nBytes]='\0'; // add null character to enable string functions
|
TempBuffer<uint8_t> httpBuf(messageSize+1); // leave room for null character added below
|
||||||
char *body=(char *)httpBuf; // char pointer to start of HTTP Body
|
|
||||||
|
int nBytes=client.read(httpBuf,messageSize); // read all available bytes up to maximum allowed+1
|
||||||
|
|
||||||
|
if(nBytes!=messageSize || client.available()!=0){
|
||||||
|
badRequestError();
|
||||||
|
LOG0("\n*** ERROR: HTTP message not read correctly. Expected %d bytes, read %d bytes, %d bytes remaining\n\n",messageSize,nBytes,client.available());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
httpBuf[nBytes]='\0'; // add null character to enable string functions
|
||||||
|
char *body=(char *)httpBuf.get(); // char pointer to start of HTTP Body
|
||||||
char *p; // char pointer used for searches
|
char *p; // char pointer used for searches
|
||||||
|
|
||||||
if(!(p=strstr((char *)httpBuf,"\r\n\r\n"))){
|
if(!(p=strstr((char *)httpBuf.get(),"\r\n\r\n"))){
|
||||||
badRequestError();
|
badRequestError();
|
||||||
LOG0("\n*** ERROR: Malformed HTTP request (can't find blank line indicating end of BODY)\n\n");
|
LOG0("\n*** ERROR: Malformed HTTP request (can't find blank line indicating end of BODY)\n\n");
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -230,7 +237,7 @@ void Network::processRequest(char *body, char *formData){
|
||||||
|
|
||||||
String responseHead="HTTP/1.1 200 OK\r\nContent-type: text/html\r\n";
|
String responseHead="HTTP/1.1 200 OK\r\nContent-type: text/html\r\n";
|
||||||
|
|
||||||
String responseBody="<html><head><style>"
|
String responseBody="<html><meta charset=\"utf-8\"><head><style>"
|
||||||
"p{font-size:300%; margin:1em}"
|
"p{font-size:300%; margin:1em}"
|
||||||
"label{font-size:300%; margin:1em}"
|
"label{font-size:300%; margin:1em}"
|
||||||
"input{font-size:250%; margin:1em}"
|
"input{font-size:250%; margin:1em}"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
@ -28,11 +28,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <unordered_set>
|
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
|
||||||
using std::unordered_set;
|
|
||||||
|
|
||||||
const int MAX_SSID=32; // max number of characters in WiFi SSID
|
const int MAX_SSID=32; // max number of characters in WiFi SSID
|
||||||
const int MAX_PWD=64; // max number of characters in WiFi Password
|
const int MAX_PWD=64; // max number of characters in WiFi Password
|
||||||
|
|
||||||
|
|
@ -43,7 +40,7 @@ struct Network {
|
||||||
const int MAX_HTTP=4095; // max number of bytes in HTTP message
|
const int MAX_HTTP=4095; // max number of bytes in HTTP message
|
||||||
|
|
||||||
const char *apSSID=DEFAULT_AP_SSID; // Access Point SSID
|
const char *apSSID=DEFAULT_AP_SSID; // Access Point SSID
|
||||||
const char *apPassword=DEFAULT_AP_PASSWORD; // Access Point password (does not need to be secret - only used to ensure excrypted WiFi connection)
|
const char *apPassword=DEFAULT_AP_PASSWORD; // Access Point password (does not need to be secret - only used to ensure encrypted WiFi connection)
|
||||||
unsigned long lifetime=DEFAULT_AP_TIMEOUT*1000; // length of time (in milliseconds) to keep Access Point alive before shutting down and restarting
|
unsigned long lifetime=DEFAULT_AP_TIMEOUT*1000; // length of time (in milliseconds) to keep Access Point alive before shutting down and restarting
|
||||||
|
|
||||||
char **ssidList=NULL;
|
char **ssidList=NULL;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*********************************************************************************
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
|
*
|
||||||
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef HS_MALLOC
|
||||||
|
|
||||||
|
#if defined(BOARD_HAS_PSRAM)
|
||||||
|
#define HS_MALLOC ps_malloc
|
||||||
|
#define HS_CALLOC ps_calloc
|
||||||
|
#define HS_REALLOC ps_realloc
|
||||||
|
#define ps_new(X) new(ps_malloc(sizeof(X)))X
|
||||||
|
#else
|
||||||
|
#define HS_MALLOC malloc
|
||||||
|
#define HS_CALLOC calloc
|
||||||
|
#define HS_REALLOC realloc
|
||||||
|
#define ps_new(X) new X
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct Mallocator {
|
||||||
|
typedef T value_type;
|
||||||
|
Mallocator() = default;
|
||||||
|
template <class U> constexpr Mallocator(const Mallocator<U>&) noexcept {}
|
||||||
|
[[nodiscard]] T* allocate(std::size_t n) {
|
||||||
|
if(n > std::size_t(-1) / sizeof(T)) throw std::bad_alloc();
|
||||||
|
if(auto p = static_cast<T*>(HS_MALLOC(n*sizeof(T)))) return p;
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
void deallocate(T* p, std::size_t) noexcept { std::free(p); }
|
||||||
|
};
|
||||||
|
template <class T, class U>
|
||||||
|
bool operator==(const Mallocator<T>&, const Mallocator<U>&) { return true; }
|
||||||
|
template <class T, class U>
|
||||||
|
bool operator!=(const Mallocator<T>&, const Mallocator<U>&) { return false; }
|
||||||
|
|
||||||
|
#endif
|
||||||
270
src/SRP.cpp
270
src/SRP.cpp
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
@ -29,29 +29,12 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
#include "SRP.h"
|
#include "SRP.h"
|
||||||
#include "HAP.h"
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
/////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
SRP6A::SRP6A(){
|
SRP6A::SRP6A(){
|
||||||
|
|
||||||
uint8_t tBuf[768]; // temporary buffer for staging
|
|
||||||
uint8_t tHash[64]; // temporary buffer for storing SHA-512 results
|
|
||||||
|
|
||||||
char N3072[]="FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74"
|
|
||||||
"020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437"
|
|
||||||
"4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
|
|
||||||
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05"
|
|
||||||
"98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB"
|
|
||||||
"9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
|
|
||||||
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
|
|
||||||
"3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33"
|
|
||||||
"A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7"
|
|
||||||
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864"
|
|
||||||
"D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2"
|
|
||||||
"08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF";
|
|
||||||
|
|
||||||
// initialize MPI structures
|
// initialize MPI structures
|
||||||
|
|
||||||
mbedtls_mpi_init(&N);
|
mbedtls_mpi_init(&N);
|
||||||
|
|
@ -65,134 +48,156 @@ SRP6A::SRP6A(){
|
||||||
mbedtls_mpi_init(&S);
|
mbedtls_mpi_init(&S);
|
||||||
mbedtls_mpi_init(&k);
|
mbedtls_mpi_init(&k);
|
||||||
mbedtls_mpi_init(&u);
|
mbedtls_mpi_init(&u);
|
||||||
mbedtls_mpi_init(&K);
|
|
||||||
mbedtls_mpi_init(&M1);
|
|
||||||
mbedtls_mpi_init(&M1V);
|
|
||||||
mbedtls_mpi_init(&M2);
|
|
||||||
mbedtls_mpi_init(&_rr);
|
mbedtls_mpi_init(&_rr);
|
||||||
mbedtls_mpi_init(&t1);
|
mbedtls_mpi_init(&t1);
|
||||||
mbedtls_mpi_init(&t2);
|
mbedtls_mpi_init(&t2);
|
||||||
mbedtls_mpi_init(&t3);
|
mbedtls_mpi_init(&t3);
|
||||||
|
|
||||||
// load N and g into mpi structures
|
// load N and g into MPI structures
|
||||||
|
|
||||||
mbedtls_mpi_read_string(&N,16,N3072);
|
mbedtls_mpi_read_string(&N,16,N3072);
|
||||||
mbedtls_mpi_lset(&g,5);
|
mbedtls_mpi_lset(&g,g3072);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
SRP6A::~SRP6A(){
|
||||||
|
|
||||||
|
mbedtls_mpi_free(&N);
|
||||||
|
mbedtls_mpi_free(&g);
|
||||||
|
mbedtls_mpi_free(&s);
|
||||||
|
mbedtls_mpi_free(&x);
|
||||||
|
mbedtls_mpi_free(&v);
|
||||||
|
mbedtls_mpi_free(&A);
|
||||||
|
mbedtls_mpi_free(&b);
|
||||||
|
mbedtls_mpi_free(&B);
|
||||||
|
mbedtls_mpi_free(&S);
|
||||||
|
mbedtls_mpi_free(&k);
|
||||||
|
mbedtls_mpi_free(&u);
|
||||||
|
mbedtls_mpi_free(&_rr);
|
||||||
|
mbedtls_mpi_free(&t1);
|
||||||
|
mbedtls_mpi_free(&t2);
|
||||||
|
mbedtls_mpi_free(&t3);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
void SRP6A::createVerifyCode(const char *setupCode, Verification *vData){
|
||||||
|
|
||||||
|
TempBuffer<uint8_t> tBuf(80); // temporary buffer for staging
|
||||||
|
TempBuffer<uint8_t> tHash(64); // temporary buffer for storing SHA-512 results
|
||||||
|
char *icp; // storage for I:P
|
||||||
|
|
||||||
|
// generate random salt, s
|
||||||
|
|
||||||
|
randombytes_buf(vData->salt,16); // generate 16 random bytes for salt
|
||||||
|
|
||||||
|
// create I:P
|
||||||
|
|
||||||
|
asprintf(&icp,"%s:%.3s-%.2s-%.3s",I,setupCode,setupCode+3,setupCode+5);
|
||||||
|
|
||||||
|
// compute x = SHA512( s | SHA512( I | ":" | P ) )
|
||||||
|
|
||||||
|
memcpy(tBuf,vData->salt,16); // write salt into first 16 bytes of staging buffer
|
||||||
|
mbedtls_sha512_ret((uint8_t *)icp,strlen(icp),tBuf+16,0); // create hash of username:password and write into last 64 bytes of staging buffer
|
||||||
|
mbedtls_sha512_ret(tBuf,80,tHash,0); // create second hash of salted, hashed username:password
|
||||||
|
mbedtls_mpi_read_binary(&x,tHash,64); // load hash result into x
|
||||||
|
|
||||||
|
// compute v = g^x %N
|
||||||
|
|
||||||
|
mbedtls_mpi_exp_mod(&v,&g,&x,&N,&_rr); // create verifier, v (_rr is an internal "helper" structure that mbedtls uses to speed up subsequent exponential calculations)
|
||||||
|
mbedtls_mpi_write_binary(&v,vData->verifyCode,384); // write v into verifyCode (padding with initial zeros is less than 384 bytes)
|
||||||
|
|
||||||
|
free(icp);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
void SRP6A::createPublicKey(const Verification *vData, uint8_t *publicKey){
|
||||||
|
|
||||||
|
TempBuffer<uint8_t> tBuf(768); // temporary buffer for staging
|
||||||
|
TempBuffer<uint8_t> tHash(64); // temporary buffer for storing SHA-512 results
|
||||||
|
TempBuffer<uint8_t> privateKey(32); // temporary buffer for generating private key random numbers
|
||||||
|
|
||||||
|
// load stored salt, s, and verification code, v
|
||||||
|
|
||||||
|
mbedtls_mpi_read_binary(&s,vData->salt,16); // load salt into s for use in later steps
|
||||||
|
mbedtls_mpi_read_binary(&v,vData->verifyCode,384); // load verifyCode into v for use below
|
||||||
|
|
||||||
|
// generate random private key, b
|
||||||
|
|
||||||
|
randombytes_buf(privateKey,32); // generate 32 random bytes for private key
|
||||||
|
mbedtls_mpi_read_binary(&b,privateKey,32); // load private key into b
|
||||||
|
|
||||||
// compute k = SHA512( N | PAD(g) )
|
// compute k = SHA512( N | PAD(g) )
|
||||||
|
|
||||||
mbedtls_mpi_write_binary(&N,tBuf,384); // write N into first half of staging buffer
|
mbedtls_mpi_write_binary(&N,tBuf,384); // write N into first half of staging buffer
|
||||||
mbedtls_mpi_write_binary(&g,tBuf+384,384); // write g into second half of staging buffer (fully padded with leading zeros)
|
mbedtls_mpi_write_binary(&g,tBuf+384,384); // write g into second half of staging buffer (fully padded with leading zeros)
|
||||||
mbedtls_sha512_ret(tBuf,768,tHash,0); // create hash of data
|
mbedtls_sha512_ret(tBuf,768,tHash,0); // create hash of data
|
||||||
mbedtls_mpi_read_binary(&k,tHash,64); // load hash result into mpi structure k
|
mbedtls_mpi_read_binary(&k,tHash,64); // load hash result into k
|
||||||
|
|
||||||
|
// compute B = (k*v + g^b) %N
|
||||||
|
|
||||||
|
mbedtls_mpi_mul_mpi(&t1,&k,&v); // t1 = k*v
|
||||||
|
mbedtls_mpi_exp_mod(&t2,&g,&b,&N,&_rr); // t2 = g^b %N
|
||||||
|
mbedtls_mpi_add_mpi(&t3,&t1,&t2); // t3 = t1 + t2
|
||||||
|
mbedtls_mpi_mod_mpi(&B,&t3,&N); // B = t3 %N = ACCESSORY PUBLIC KEY
|
||||||
|
|
||||||
|
mbedtls_mpi_write_binary(&B,publicKey,384); // write B into publicKey (padding with initial zeros is less than 384 bytes)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
|
|
||||||
void SRP6A::createVerifyCode(const char *setupCode, uint8_t *verifyCode, uint8_t *salt){
|
void SRP6A::createSessionKey(const uint8_t *publicKey, size_t len){
|
||||||
|
|
||||||
uint8_t tBuf[80]; // temporary buffer for staging
|
TempBuffer<uint8_t> tBuf(768); // temporary buffer for staging
|
||||||
uint8_t tHash[64]; // temporary buffer for storing SHA-512 results
|
TempBuffer<uint8_t> tHash(64); // temporary buffer for storing SHA-512 results
|
||||||
char icp[22]; // storage for I:P
|
|
||||||
|
|
||||||
randombytes_buf(salt,16); // generate 16 random bytes using libsodium (which uses the ESP32 hardware-based random number generator)
|
mbedtls_mpi_read_binary(&A,publicKey,len); // load client PublicKey into A
|
||||||
mbedtls_mpi_read_binary(&s,salt,16);
|
|
||||||
|
|
||||||
sprintf(icp,"Pair-Setup:%.3s-%.2s-%.3s",setupCode,setupCode+3,setupCode+5);
|
|
||||||
|
|
||||||
// compute x = SHA512( s | SHA512( I | ":" | P ) )
|
|
||||||
|
|
||||||
mbedtls_mpi_write_binary(&s,tBuf,16); // write s into first 16 bytes of staging buffer
|
|
||||||
mbedtls_sha512_ret((uint8_t *)icp,strlen(icp),tBuf+16,0); // create hash of username:password and write into last 64 bytes of staging buffer
|
|
||||||
mbedtls_sha512_ret(tBuf,80,tHash,0); // create second hash of salted, hashed username:password
|
|
||||||
mbedtls_mpi_read_binary(&x,tHash,64); // load hash result into mpi structure x
|
|
||||||
|
|
||||||
// compute v = g^x % N
|
|
||||||
|
|
||||||
mbedtls_mpi_exp_mod(&v,&g,&x,&N,&_rr); // create verifier, v (_rr is an internal "helper" structure that mbedtls uses to speed up subsequent exponential calculations)
|
|
||||||
mbedtls_mpi_write_binary(&v,verifyCode,384); // write v into verifyCode
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
|
|
||||||
void SRP6A::loadVerifyCode(uint8_t *verifyCode, uint8_t *salt){
|
|
||||||
|
|
||||||
mbedtls_mpi_read_binary(&s,salt,16);
|
|
||||||
mbedtls_mpi_read_binary(&v,verifyCode,384);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
|
|
||||||
void SRP6A::createPublicKey(){
|
|
||||||
|
|
||||||
getPrivateKey(); // create and load b (random 32 bytes)
|
|
||||||
|
|
||||||
// compute B = kv + g^b %N
|
|
||||||
|
|
||||||
mbedtls_mpi_mul_mpi(&t1,&k,&v); // t1 = k*v
|
|
||||||
mbedtls_mpi_exp_mod(&t2,&g,&b,&N,&_rr); // t2 = g^b %N
|
|
||||||
mbedtls_mpi_add_mpi(&t3,&t1,&t2); // t3 = t1 + t2
|
|
||||||
mbedtls_mpi_mod_mpi(&B,&t3,&N); // B = t3 %N = ACCESSORY PUBLIC KEY
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
|
|
||||||
void SRP6A::getPrivateKey(){
|
|
||||||
|
|
||||||
uint8_t privateKey[32];
|
|
||||||
|
|
||||||
randombytes_buf(privateKey,32); // generate 32 random bytes using libsodium (which uses the ESP32 hardware-based random number generator)
|
|
||||||
mbedtls_mpi_read_binary(&b,privateKey,32);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
|
|
||||||
void SRP6A::createSessionKey(){
|
|
||||||
|
|
||||||
uint8_t tBuf[768]; // temporary buffer for staging
|
|
||||||
uint8_t tHash[64]; // temporary buffer for storing SHA-512 results
|
|
||||||
|
|
||||||
// compute u = SHA512( PAD(A) | PAD(B) )
|
// compute u = SHA512( PAD(A) | PAD(B) )
|
||||||
|
|
||||||
mbedtls_mpi_write_binary(&A,tBuf,384); // write A into first half of staging buffer
|
mbedtls_mpi_write_binary(&A,tBuf,384); // write A into first half of staging buffer (padding with initial zeros is less than 384 bytes)
|
||||||
mbedtls_mpi_write_binary(&B,tBuf+384,384); // write B into second half of staging buffer
|
mbedtls_mpi_write_binary(&B,tBuf+384,384); // write B into second half of staging buffer (padding with initial zeros is less than 384 bytes)
|
||||||
mbedtls_sha512_ret(tBuf,768,tHash,0); // create hash of data
|
mbedtls_sha512_ret(tBuf,768,tHash,0); // create hash of data
|
||||||
mbedtls_mpi_read_binary(&u,tHash,64); // load hash result into mpi structure u
|
mbedtls_mpi_read_binary(&u,tHash,64); // load hash result into mpi structure u
|
||||||
|
|
||||||
// compute S = (Av^u)^b %N
|
// compute S = (A * v^u)^b %N
|
||||||
|
|
||||||
mbedtls_mpi_exp_mod(&t1,&v,&u,&N,&_rr); // t1 = v^u %N
|
mbedtls_mpi_exp_mod(&t1,&v,&u,&N,&_rr); // t1 = v^u %N
|
||||||
mbedtls_mpi_mul_mpi(&t2,&A,&t1); // t2 = A*t1
|
mbedtls_mpi_mul_mpi(&t2,&A,&t1); // t2 = A*t1
|
||||||
mbedtls_mpi_mod_mpi(&t1,&t2,&N); // t1 = t2 %N (this is needed to reduce size of t2 before next calculation)
|
mbedtls_mpi_mod_mpi(&t1,&t2,&N); // t1 = t2 %N (this is needed to reduce size of t2 before next calculation)
|
||||||
mbedtls_mpi_exp_mod(&S,&t1,&b,&N,&_rr); // S = t1^b %N
|
mbedtls_mpi_exp_mod(&S,&t1,&b,&N,&_rr); // S = t1^b %N
|
||||||
|
|
||||||
// compute K = SHA512( S )
|
// compute K = SHA512( PAD(S) )
|
||||||
|
|
||||||
mbedtls_mpi_write_binary(&S,tBuf,384); // write S into staging buffer (only first half of buffer will be used)
|
mbedtls_mpi_write_binary(&S,tBuf,384); // write S into staging buffer (only first half of buffer will be used)
|
||||||
mbedtls_sha512_ret(tBuf,384,tHash,0); // create hash of data
|
mbedtls_sha512_ret(tBuf,384,K,0); // create hash of data - this is the SRP SHARED SESSION KEY, K
|
||||||
mbedtls_mpi_read_binary(&K,tHash,64); // load hash result into mpi structure K. This is the SRP SHARED SECRET KEY
|
|
||||||
|
|
||||||
mbedtls_mpi_write_binary(&K,sharedSecret,64); // store SHARED SECRET in easy-to-use binary (uint8_t) format
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
|
|
||||||
int SRP6A::verifyProof(){
|
|
||||||
|
|
||||||
uint8_t tBuf[976]; // temporary buffer for staging
|
int SRP6A::verifyClientProof(const uint8_t *proof){
|
||||||
uint8_t tHash[64]; // temporary buffer for storing SHA-512 results
|
|
||||||
|
|
||||||
size_t count=0; // total number of bytes for final hash
|
TempBuffer<uint8_t> tBuf(976); // temporary buffer for staging
|
||||||
|
TempBuffer<uint8_t> tHash(64); // temporary buffer for storing SHA-512 results
|
||||||
|
|
||||||
|
memcpy(M1,proof,64); // load client Proof into M1
|
||||||
|
|
||||||
|
size_t count=0; // total number of bytes for final hash
|
||||||
size_t sLen;
|
size_t sLen;
|
||||||
|
|
||||||
|
// compute M1V = SHA512( SHA512(N) xor SHA512(g) | SHA512(I) | s | A | B | K )
|
||||||
|
|
||||||
mbedtls_mpi_write_binary(&N,tBuf,384); // write N into staging buffer
|
mbedtls_mpi_write_binary(&N,tBuf,384); // write N into staging buffer
|
||||||
mbedtls_sha512_ret(tBuf,384,tHash,0); // create hash of data
|
mbedtls_sha512_ret(tBuf,384,tHash,0); // create hash of data
|
||||||
mbedtls_sha512_ret((uint8_t *)g3072,1,tBuf,0); // create hash of g, but place output directly into staging buffer
|
mbedtls_sha512_ret(&g3072,1,tBuf,0); // create hash of g, but place output directly into staging buffer
|
||||||
|
|
||||||
for(int i=0;i<64;i++) // H(g) -> H(g) XOR H(N), with results in first 64 bytes of staging buffer
|
for(int i=0;i<64;i++) // H(g) -> H(g) XOR H(N), with results in first 64 bytes of staging buffer
|
||||||
tBuf[i]^=tHash[i];
|
tBuf[i]^=tHash[i];
|
||||||
|
|
@ -209,13 +214,12 @@ int SRP6A::verifyProof(){
|
||||||
mbedtls_mpi_write_binary(&B,tBuf+count,sLen); // concatenate B to staging buffer. Note B is NOT padded with leading zeros (so may be less than 384 bytes)
|
mbedtls_mpi_write_binary(&B,tBuf+count,sLen); // concatenate B to staging buffer. Note B is NOT padded with leading zeros (so may be less than 384 bytes)
|
||||||
count+=sLen; // increment total bytes written to staging buffer
|
count+=sLen; // increment total bytes written to staging buffer
|
||||||
|
|
||||||
mbedtls_mpi_write_binary(&K,tBuf+count,64); // concatenate K to staging buffer (should always be 64 bytes since it is a hashed value)
|
memcpy(tBuf+count,K,64); // concatenate K to staging buffer (should always be 64 bytes since it is a hashed value)
|
||||||
count+=64; // final total of bytes written to staging buffer
|
count+=64; // final total of bytes written to staging buffer
|
||||||
|
|
||||||
mbedtls_sha512_ret(tBuf,count,tHash,0); // create hash of data
|
mbedtls_sha512_ret(tBuf,count,tHash,0); // create hash of data - this is M1V
|
||||||
mbedtls_mpi_read_binary(&M1V,tHash,64); // load hash result into mpi structure M1V
|
|
||||||
|
|
||||||
if(!mbedtls_mpi_cmp_mpi(&M1,&M1V)) // cmp_mpi uses same logic as strcmp: returns 0 if EQUAL, otherwise +/- 1
|
if(!memcmp(M1,tHash,64)) // check that client Proof M1 matches M1V
|
||||||
return(1); // success - proof from HAP Client is verified
|
return(1); // success - proof from HAP Client is verified
|
||||||
|
|
||||||
return(0);
|
return(0);
|
||||||
|
|
@ -223,60 +227,34 @@ int SRP6A::verifyProof(){
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
|
|
||||||
void SRP6A::createProof(){
|
void SRP6A::createAccProof(uint8_t *proof){
|
||||||
|
|
||||||
uint8_t tBuf[512]; // temporary buffer for staging
|
TempBuffer<uint8_t> tBuf(512); // temporary buffer for staging
|
||||||
|
|
||||||
// compute M2 = H( A | M1 | K )
|
// compute M2 = SHA512( A | M1 | K )
|
||||||
|
|
||||||
mbedtls_mpi_write_binary(&A,tBuf,384); // write A into staging buffer
|
mbedtls_mpi_write_binary(&A,tBuf,384); // write A into staging buffer
|
||||||
mbedtls_mpi_write_binary(&M1,tBuf+384,64); // concatenate M1 (now verified) to staging buffer
|
memcpy(tBuf+384,M1,64); // concatenate M1 (now verified) to staging buffer
|
||||||
mbedtls_mpi_write_binary(&K,tBuf+448,64); // concatenate K to staging buffer
|
memcpy(tBuf+448,K,64); // concatenate K to staging buffer
|
||||||
mbedtls_sha512_ret(tBuf,512,tBuf,0); // create hash of data
|
mbedtls_sha512_ret(tBuf,512,proof,0); // create hash of data writing directly to proof - this is M2
|
||||||
mbedtls_mpi_read_binary(&M2,tBuf,64); // load hash results into mpi structure M2
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
|
|
||||||
int SRP6A::loadTLV(kTLVType tag, mbedtls_mpi *mpi, int nBytes){
|
void SRP6A::print(mbedtls_mpi *mpi){
|
||||||
|
|
||||||
uint8_t *buf=HAPClient::tlv8.buf(tag,nBytes);
|
|
||||||
|
|
||||||
if(!buf)
|
|
||||||
return(0);
|
|
||||||
|
|
||||||
mbedtls_mpi_write_binary(mpi,buf,nBytes);
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
|
|
||||||
int SRP6A::writeTLV(kTLVType tag, mbedtls_mpi *mpi){
|
|
||||||
|
|
||||||
int nBytes=HAPClient::tlv8.len(tag);
|
|
||||||
|
|
||||||
if(nBytes>0){
|
|
||||||
mbedtls_mpi_read_binary(mpi,HAPClient::tlv8.buf(tag),nBytes);
|
|
||||||
return(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
|
|
||||||
void SRP6A::print(mbedtls_mpi *mpi, int minLogLevel){
|
|
||||||
|
|
||||||
if(homeSpan.getLogLevel()<minLogLevel)
|
|
||||||
return;
|
|
||||||
|
|
||||||
char sBuf[2000];
|
|
||||||
size_t sLen;
|
size_t sLen;
|
||||||
|
|
||||||
mbedtls_mpi_write_string(mpi,16,sBuf,2000,&sLen);
|
mbedtls_mpi_write_string(mpi,16,NULL,0,&sLen);
|
||||||
|
TempBuffer<char> sBuf(sLen);
|
||||||
|
mbedtls_mpi_write_string(mpi,16,sBuf,sLen,&sLen);
|
||||||
|
|
||||||
Serial.printf("%d %s\n",(sLen-1)/2,sBuf); // subtract 1 for null-terminator, and then divide by 2 to get number of bytes (e.g. 4F = 2 characters, but represents just one mpi byte)
|
Serial.printf("%d %s\n",(sLen-1)/2,sBuf.get()); // subtract 1 for null-terminator, and then divide by 2 to get number of bytes (e.g. 4F = 2 characters, but represents just one mpi byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
constexpr char SRP6A::N3072[];
|
||||||
|
constexpr char SRP6A::I[];
|
||||||
|
const uint8_t SRP6A::g3072;
|
||||||
|
|
|
||||||
70
src/SRP.h
70
src/SRP.h
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
@ -31,11 +31,19 @@
|
||||||
#include <mbedtls/bignum.h>
|
#include <mbedtls/bignum.h>
|
||||||
#include <mbedtls/base64.h>
|
#include <mbedtls/base64.h>
|
||||||
|
|
||||||
#include "HAPConstants.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
// Pair-Setup Code Verification Data and Salt
|
||||||
|
|
||||||
|
struct Verification {
|
||||||
|
uint8_t salt[16];
|
||||||
|
uint8_t verifyCode[384];
|
||||||
|
};
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
// SRP-6A Structure from RFC 5054 (Nov 2007)
|
// SRP-6A Structure from RFC 5054 (Nov 2007)
|
||||||
// ** HAP uses N=3072-bit Group specified in RFC 5054
|
// ** HAP uses N=3072-bit Group specified in RFC 5054 with Generator g=5
|
||||||
// ** HAP replaces H=SHA-1 with H=SHA-512 (HAP Section 5.5)
|
// ** HAP replaces H=SHA-1 with H=SHA-512 (HAP Section 5.5)
|
||||||
//
|
//
|
||||||
// I = SRP-6A username, defined by HAP to be the word "Pair-Setup"
|
// I = SRP-6A username, defined by HAP to be the word "Pair-Setup"
|
||||||
|
|
@ -43,6 +51,22 @@
|
||||||
|
|
||||||
struct SRP6A {
|
struct SRP6A {
|
||||||
|
|
||||||
|
static constexpr char N3072[]="FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74"
|
||||||
|
"020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437"
|
||||||
|
"4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
|
||||||
|
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05"
|
||||||
|
"98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB"
|
||||||
|
"9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
|
||||||
|
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
|
||||||
|
"3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33"
|
||||||
|
"A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7"
|
||||||
|
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864"
|
||||||
|
"D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2"
|
||||||
|
"08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF";
|
||||||
|
|
||||||
|
static const uint8_t g3072=5;
|
||||||
|
static constexpr char I[]="Pair-Setup";
|
||||||
|
|
||||||
mbedtls_mpi N; // N - 3072-bit Group pre-defined prime used for all SRP-6A calculations (384 bytes)
|
mbedtls_mpi N; // N - 3072-bit Group pre-defined prime used for all SRP-6A calculations (384 bytes)
|
||||||
mbedtls_mpi g; // g - pre-defined generator for the specified 3072-bit Group (g=5)
|
mbedtls_mpi g; // g - pre-defined generator for the specified 3072-bit Group (g=5)
|
||||||
mbedtls_mpi k; // k = H(N | PAD(g)) - SRP-6A multiplier (which is different from versions SRP-6 or SRP-3)
|
mbedtls_mpi k; // k = H(N | PAD(g)) - SRP-6A multiplier (which is different from versions SRP-6 or SRP-3)
|
||||||
|
|
@ -54,39 +78,25 @@ struct SRP6A {
|
||||||
mbedtls_mpi A; // A - public key RECEIVED from HAP Client (max 384 bytes)
|
mbedtls_mpi A; // A - public key RECEIVED from HAP Client (max 384 bytes)
|
||||||
mbedtls_mpi u; // u = H(PAD(A) | PAB(B)) - "u-factor" (64 bytes)
|
mbedtls_mpi u; // u = H(PAD(A) | PAB(B)) - "u-factor" (64 bytes)
|
||||||
mbedtls_mpi S; // S = (A*v^u)^b %N - SRP shared "premaster" key, based on accessory private key and client public key (max 384 bytes)
|
mbedtls_mpi S; // S = (A*v^u)^b %N - SRP shared "premaster" key, based on accessory private key and client public key (max 384 bytes)
|
||||||
mbedtls_mpi K; // K = H( S ) - SRP SHARED SECRET KEY (64 bytes)
|
uint8_t K[64]; // K = H(S) - SRP SHARED SECRET KEY (64 bytes)
|
||||||
mbedtls_mpi M1; // M1 - proof RECEIVED from HAP Client (64 bytes)
|
uint8_t M1[64]; // M1 - proof RECEIVED from HAP Client (64 bytes)
|
||||||
mbedtls_mpi M1V; // M1V - accessory's independent computation of M1 to verify proof (see code for details of computation)
|
mbedtls_mpi t1; // temp1 - temporary mpi structures for intermediate results
|
||||||
mbedtls_mpi M2; // M2 - accessory's counter-proof to send to HAP Client after M1=M1V has been verified (64 bytes)
|
mbedtls_mpi t2; // temp2 - temporary mpi structures for intermediate results
|
||||||
|
mbedtls_mpi t3; // temp3 - temporary mpi structures for intermediate results
|
||||||
mbedtls_mpi t1; // temporary mpi structures for intermediate results
|
|
||||||
mbedtls_mpi t2;
|
|
||||||
mbedtls_mpi t3;
|
|
||||||
|
|
||||||
mbedtls_mpi _rr; // _rr - temporary "helper" for large exponential modulus calculations
|
mbedtls_mpi _rr; // _rr - temporary "helper" for large exponential modulus calculations
|
||||||
|
|
||||||
char I[11]="Pair-Setup"; // I - userName pre-defined by HAP pairing setup protocol
|
|
||||||
char g3072[2]="\x05"; // g - 3072-bit Group generator
|
|
||||||
|
|
||||||
uint8_t sharedSecret[64]; // permanent storage for binary version of SHARED SECRET KEY for ease of use upstream
|
|
||||||
|
|
||||||
SRP6A(); // initializes N, G, and computes k
|
SRP6A(); // initializes N, G, and computes k
|
||||||
|
~SRP6A();
|
||||||
|
|
||||||
void createVerifyCode(const char *setupCode, uint8_t *verifyCode, uint8_t *salt);
|
void *operator new(size_t size){return(HS_MALLOC(size));} // override new operator to use PSRAM when available
|
||||||
void loadVerifyCode(uint8_t *verifyCode, uint8_t *salt);
|
|
||||||
|
|
||||||
void getSalt(); // generates and stores random 16-byte salt, s
|
void createVerifyCode(const char *setupCode, Verification *vData); // generates random s and computes v; writes back resulting Verification Data
|
||||||
void getPrivateKey(); // generates and stores random 32-byte private key, b
|
void createPublicKey(const Verification *vData, uint8_t *publicKey); // generates random b and computes k and B; writes back resulting Accessory Public Key
|
||||||
void getSetupCode(char *c); // generates and displays random 8-digit Pair-Setup code, P, in format XXX-XX-XXX
|
void createSessionKey(const uint8_t *publicKey, size_t len); // computes u, S, and K from Client Public Key, A (of variable length)
|
||||||
void createPublicKey(); // computes x, v, and B from random s, P, and b
|
int verifyClientProof(const uint8_t *proof); // verifies Client Proof, M1, received from HAP client (return 1 on success, 0 on failure)
|
||||||
void createSessionKey(); // computes u from A and B, and then S from A, v, u, and b
|
void createAccProof(uint8_t *proof); // computes M2; write back resulting Accessory Proof
|
||||||
|
|
||||||
int loadTLV(kTLVType tag, mbedtls_mpi *mpi, int nBytes); // load binary contents of mpi into a TLV record and set its length
|
void print(mbedtls_mpi *mpi); // prints size of mpi (in bytes), followed by the mpi itself (as a hex character string)
|
||||||
int writeTLV(kTLVType tag, mbedtls_mpi *mpi); // write binary contents of a TLV record into an mpi
|
|
||||||
|
|
||||||
int verifyProof(); // verify M1 SRP6A Proof received from HAP client (return 1 on success, 0 on failure)
|
|
||||||
void createProof(); // create M2 server-side SRP6A Proof based on M1 as received from HAP Client
|
|
||||||
|
|
||||||
void print(mbedtls_mpi *mpi, int minLogLevel=0); // prints size of mpi (in bytes), followed by the mpi itself (as a hex charcter string), subject to specified minimum log level
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
|
|
||||||
// USER-DEFINED SETTINGS AND REFERENCE ENUMERATION CLASSES
|
// USER-DEFINED SETTINGS AND REFERENCE ENUMERATION CLASSES
|
||||||
|
|
||||||
#include <core_version.h>
|
#include <esp_arduino_version.h>
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
// HomeSpan Version //
|
// HomeSpan Version //
|
||||||
|
|
||||||
#define HS_MAJOR 1
|
#define HS_MAJOR 1
|
||||||
#define HS_MINOR 8
|
#define HS_MINOR 9
|
||||||
#define HS_PATCH 0
|
#define HS_PATCH 0
|
||||||
|
|
||||||
#define STRINGIFY(x) _STR(x)
|
#define STRINGIFY(x) _STR(x)
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
#error THIS SKETCH REQUIRES A LATER VERSION OF THE HOMESPAN LIBRARY
|
#error THIS SKETCH REQUIRES A LATER VERSION OF THE HOMESPAN LIBRARY
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define ARDUINO_ESP_VERSION STRINGIFY(ARDUINO_ESP32_GIT_DESC)
|
#define ARDUINO_ESP_VERSION STRINGIFY(ESP_ARDUINO_VERSION_MAJOR) "." STRINGIFY(ESP_ARDUINO_VERSION_MINOR) "." STRINGIFY(ESP_ARDUINO_VERSION_PATCH)
|
||||||
|
|
||||||
#if ESP_ARDUINO_VERSION_MAJOR<2
|
#if ESP_ARDUINO_VERSION_MAJOR<2
|
||||||
#error HOMESPAN REQUIRES VERSION 2 OF THE ARDUINO ESP32 LIBRARY
|
#error HOMESPAN REQUIRES VERSION 2 OF THE ARDUINO ESP32 LIBRARY
|
||||||
|
|
@ -84,6 +84,10 @@
|
||||||
|
|
||||||
#define DEFAULT_WEBLOG_URL "status" // change with optional fourth argument in homeSpan.enableWebLog()
|
#define DEFAULT_WEBLOG_URL "status" // change with optional fourth argument in homeSpan.enableWebLog()
|
||||||
|
|
||||||
|
#define DEFAULT_LOW_MEM_THRESHOLD 80000 // default low watermark memory (for internal RAM) threshold that triggers warning
|
||||||
|
|
||||||
|
#define DEFAULT_REBOOT_CALLBACK_TIME 5000 // default time (in milliseconds) to check for reboot callback
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
// OTA PARTITION INFO //
|
// OTA PARTITION INFO //
|
||||||
|
|
||||||
|
|
|
||||||
856
src/Span.h
856
src/Span.h
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
@ -32,38 +32,168 @@
|
||||||
// Macros to define services, along with vectors of required and optional characteristics for each Span Service structure
|
// Macros to define services, along with vectors of required and optional characteristics for each Span Service structure
|
||||||
// The names of the macros are picked up by external scripts to help generate documentation
|
// The names of the macros are picked up by external scripts to help generate documentation
|
||||||
|
|
||||||
|
// Note: These macros below are also parsed by an external awk script to auto-generate Services and Characteristics documentation.
|
||||||
|
//
|
||||||
|
// The CREATE_SERV_DEP() macro is the same as the CREATE_SERV() macro, except that it is used for deprecated Services that will not
|
||||||
|
// be included in documentation. The OPT_DEP() macro is that same as the OPT() macro, except that it is used for deprecated Characteristics
|
||||||
|
// that will not be included in documentation.
|
||||||
|
|
||||||
#define CREATE_SERV(NAME,UUID) struct NAME : SpanService { NAME() : SpanService{#UUID,#NAME}{
|
#define CREATE_SERV(NAME,UUID) struct NAME : SpanService { NAME() : SpanService{#UUID,#NAME}{
|
||||||
|
#define CREATE_SERV_DEP(NAME,UUID) struct NAME : SpanService { NAME() : SpanService{#UUID,#NAME}{
|
||||||
#define END_SERV }};
|
#define END_SERV }};
|
||||||
|
|
||||||
#define REQ(HAPCHAR) req.insert(&hapChars.HAPCHAR)
|
#define REQ(HAPCHAR) req.push_back(&hapChars.HAPCHAR)
|
||||||
#define OPT(HAPCHAR) opt.insert(&hapChars.HAPCHAR)
|
#define REQ_DEP(HAPCHAR) req.push_back(&hapChars.HAPCHAR)
|
||||||
|
#define OPT(HAPCHAR) opt.push_back(&hapChars.HAPCHAR)
|
||||||
|
#define OPT_DEP(HAPCHAR) opt.push_back(&hapChars.HAPCHAR)
|
||||||
|
|
||||||
|
#define SERVICES_GROUP
|
||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
|
|
||||||
CREATE_SERV(AccessoryInformation,3E)
|
SERVICES_GROUP // Mandatory Services
|
||||||
|
|
||||||
|
CREATE_SERV(AccessoryInformation,3E) // Required Identification Information. For each Accessory in a HomeSpan device this must be included as the first Service.
|
||||||
REQ(Identify);
|
REQ(Identify);
|
||||||
|
OPT(Name);
|
||||||
OPT(FirmwareRevision);
|
OPT(FirmwareRevision);
|
||||||
OPT(Manufacturer);
|
OPT(Manufacturer);
|
||||||
OPT(Model);
|
OPT(Model);
|
||||||
OPT(Name);
|
|
||||||
OPT(SerialNumber);
|
OPT(SerialNumber);
|
||||||
OPT(HardwareRevision);
|
OPT(HardwareRevision);
|
||||||
OPT(AccessoryFlags);
|
OPT_DEP(AccessoryFlags);
|
||||||
END_SERV
|
END_SERV
|
||||||
|
|
||||||
CREATE_SERV(AirPurifier,BB)
|
SERVICES_GROUP // Lights, Power, and Switches
|
||||||
|
|
||||||
|
CREATE_SERV(BatteryService,96) // Defines a standalone Battery Service.
|
||||||
|
REQ(BatteryLevel);
|
||||||
|
REQ(ChargingState);
|
||||||
|
REQ(StatusLowBattery);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(LightBulb,43) // Defines any type of Light.
|
||||||
|
REQ(On);
|
||||||
|
OPT(Brightness);
|
||||||
|
OPT(Hue);
|
||||||
|
OPT(Saturation);
|
||||||
|
OPT(ColorTemperature);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(Outlet,47) // Defines a controllable Outlet used to power any light or appliance.
|
||||||
|
REQ(On);
|
||||||
|
REQ(OutletInUse);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(StatelessProgrammableSwitch,89) // Defines a "Stateless" Programmable Switch that can be used to trigger actions in the Home App.
|
||||||
|
REQ(ProgrammableSwitchEvent);
|
||||||
|
OPT(ServiceLabelIndex);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(Switch,49) // Defines a generic Switch.
|
||||||
|
REQ(On);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
SERVICES_GROUP // Heating, Ventilation, and Air Conditioning (HVAC)
|
||||||
|
|
||||||
|
CREATE_SERV(AirPurifier,BB) // Defines a basic Air Purifier with an optional fan and swing mode. Optional Linked Services: <b>FilterMaintenance</b>. Combine with an <b>AirSensor</b> Service for automated operations.
|
||||||
REQ(Active);
|
REQ(Active);
|
||||||
REQ(CurrentAirPurifierState);
|
REQ(CurrentAirPurifierState);
|
||||||
REQ(TargetAirPurifierState);
|
REQ(TargetAirPurifierState);
|
||||||
OPT(Name);
|
|
||||||
OPT(RotationSpeed);
|
OPT(RotationSpeed);
|
||||||
OPT(SwingMode);
|
OPT(SwingMode);
|
||||||
OPT(LockPhysicalControls);
|
OPT(LockPhysicalControls);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
END_SERV
|
END_SERV
|
||||||
|
|
||||||
CREATE_SERV(AirQualitySensor,8D)
|
CREATE_SERV(Fan,B7) // Defines a Fan. Combine with a <b>LightBulb</b> Service to create a Lighted Ceiling Fan.
|
||||||
|
REQ(Active);
|
||||||
|
OPT(CurrentFanState);
|
||||||
|
OPT(TargetFanState);
|
||||||
|
OPT(RotationDirection);
|
||||||
|
OPT(RotationSpeed);
|
||||||
|
OPT(SwingMode);
|
||||||
|
OPT(LockPhysicalControls);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(FilterMaintenance,BA) // Defines a Filter Maintainence check. Use only as a Linked Service for the <b>AirPurifier</b> Service.
|
||||||
|
REQ(FilterChangeIndication);
|
||||||
|
OPT(FilterLifeLevel);
|
||||||
|
OPT(ResetFilterIndication);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(HeaterCooler,BC) // Defines a standalone Heater, Cooler, or combined Heater/Cooler.
|
||||||
|
REQ(Active);
|
||||||
|
REQ(CurrentTemperature);
|
||||||
|
REQ(CurrentHeaterCoolerState);
|
||||||
|
REQ(TargetHeaterCoolerState);
|
||||||
|
OPT(RotationSpeed);
|
||||||
|
OPT(TemperatureDisplayUnits);
|
||||||
|
OPT(SwingMode);
|
||||||
|
OPT(CoolingThresholdTemperature);
|
||||||
|
OPT(HeatingThresholdTemperature);
|
||||||
|
OPT(LockPhysicalControls);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(HumidifierDehumidifier,BD) // Defines a Humidifer, Dehumidifier, or combined Humidifer/Dehumidifier.
|
||||||
|
REQ(Active);
|
||||||
|
REQ(CurrentRelativeHumidity);
|
||||||
|
REQ(CurrentHumidifierDehumidifierState);
|
||||||
|
REQ(TargetHumidifierDehumidifierState);
|
||||||
|
OPT(RelativeHumidityDehumidifierThreshold);
|
||||||
|
OPT(RelativeHumidityHumidifierThreshold);
|
||||||
|
OPT(RotationSpeed);
|
||||||
|
OPT(SwingMode);
|
||||||
|
OPT(WaterLevel);
|
||||||
|
OPT(LockPhysicalControls);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(Slat,B9) // Defines a motorized ventilation Slat(s).
|
||||||
|
REQ(CurrentSlatState);
|
||||||
|
REQ(SlatType);
|
||||||
|
OPT(SwingMode);
|
||||||
|
OPT(CurrentTiltAngle);
|
||||||
|
OPT(TargetTiltAngle);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(Thermostat,4A) // Defines a Thermostat used to control a furnace, air conditioner, or both.
|
||||||
|
REQ(CurrentHeatingCoolingState);
|
||||||
|
REQ(TargetHeatingCoolingState);
|
||||||
|
REQ(CurrentTemperature);
|
||||||
|
REQ(TargetTemperature);
|
||||||
|
REQ(TemperatureDisplayUnits);
|
||||||
|
OPT(CoolingThresholdTemperature);
|
||||||
|
OPT(CurrentRelativeHumidity);
|
||||||
|
OPT(HeatingThresholdTemperature);
|
||||||
|
OPT(TargetRelativeHumidity);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
SERVICES_GROUP // Standalone Sensors
|
||||||
|
|
||||||
|
CREATE_SERV(AirQualitySensor,8D) // Defines an Air Quality Sensor.
|
||||||
REQ(AirQuality);
|
REQ(AirQuality);
|
||||||
OPT(Name);
|
|
||||||
OPT(OzoneDensity);
|
OPT(OzoneDensity);
|
||||||
OPT(NitrogenDioxideDensity);
|
OPT(NitrogenDioxideDensity);
|
||||||
OPT(SulphurDioxideDensity);
|
OPT(SulphurDioxideDensity);
|
||||||
|
|
@ -74,299 +204,194 @@ namespace Service {
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
OPT(StatusTampered);
|
OPT(StatusTampered);
|
||||||
OPT(StatusLowBattery);
|
OPT(StatusLowBattery);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
END_SERV
|
END_SERV
|
||||||
|
|
||||||
CREATE_SERV(BatteryService,96)
|
CREATE_SERV(CarbonDioxideSensor,97) // Defines a Carbon Dioxide Sensor.
|
||||||
REQ(BatteryLevel);
|
|
||||||
REQ(ChargingState);
|
|
||||||
REQ(StatusLowBattery);
|
|
||||||
OPT(Name);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(CarbonDioxideSensor,97)
|
|
||||||
REQ(CarbonDioxideDetected);
|
REQ(CarbonDioxideDetected);
|
||||||
OPT(Name);
|
|
||||||
OPT(StatusActive);
|
|
||||||
OPT(StatusFault);
|
|
||||||
OPT(StatusTampered);
|
|
||||||
OPT(StatusLowBattery);
|
|
||||||
OPT(CarbonDioxideLevel);
|
OPT(CarbonDioxideLevel);
|
||||||
OPT(CarbonDioxidePeakLevel);
|
OPT(CarbonDioxidePeakLevel);
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(CarbonMonoxideSensor,7F)
|
|
||||||
REQ(CarbonMonoxideDetected);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(StatusActive);
|
OPT(StatusActive);
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
OPT(StatusTampered);
|
OPT(StatusTampered);
|
||||||
OPT(StatusLowBattery);
|
OPT(StatusLowBattery);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(CarbonMonoxideSensor,7F) // Defines a Carbon Monoxide Sensor.
|
||||||
|
REQ(CarbonMonoxideDetected);
|
||||||
OPT(CarbonMonoxideLevel);
|
OPT(CarbonMonoxideLevel);
|
||||||
OPT(CarbonMonoxidePeakLevel);
|
OPT(CarbonMonoxidePeakLevel);
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(ContactSensor,80)
|
|
||||||
REQ(ContactSensorState);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(StatusActive);
|
OPT(StatusActive);
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
OPT(StatusTampered);
|
OPT(StatusTampered);
|
||||||
OPT(StatusLowBattery);
|
OPT(StatusLowBattery);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(ContactSensor,80) // Defines a Contact Sensor.
|
||||||
|
REQ(ContactSensorState);
|
||||||
|
OPT(StatusActive);
|
||||||
|
OPT(StatusFault);
|
||||||
|
OPT(StatusTampered);
|
||||||
|
OPT(StatusLowBattery);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
END_SERV
|
END_SERV
|
||||||
|
|
||||||
CREATE_SERV(Door,81)
|
CREATE_SERV(HumiditySensor,82) // Defines a Humidity Sensor.
|
||||||
|
REQ(CurrentRelativeHumidity);
|
||||||
|
OPT(StatusActive);
|
||||||
|
OPT(StatusFault);
|
||||||
|
OPT(StatusTampered);
|
||||||
|
OPT(StatusLowBattery);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(LeakSensor,83) // Defines a Leak Sensor.
|
||||||
|
REQ(LeakDetected);
|
||||||
|
OPT(StatusActive);
|
||||||
|
OPT(StatusFault);
|
||||||
|
OPT(StatusTampered);
|
||||||
|
OPT(StatusLowBattery);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(LightSensor,84) // Defines a Light Sensor.
|
||||||
|
REQ(CurrentAmbientLightLevel);
|
||||||
|
OPT(StatusActive);
|
||||||
|
OPT(StatusFault);
|
||||||
|
OPT(StatusTampered);
|
||||||
|
OPT(StatusLowBattery);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(MotionSensor,85) // Defines a Motion Sensor.
|
||||||
|
REQ(MotionDetected);
|
||||||
|
OPT(StatusActive);
|
||||||
|
OPT(StatusFault);
|
||||||
|
OPT(StatusTampered);
|
||||||
|
OPT(StatusLowBattery);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(OccupancySensor,86) // Defines and Occupancy Sensor.
|
||||||
|
REQ(OccupancyDetected);
|
||||||
|
OPT(StatusActive);
|
||||||
|
OPT(StatusFault);
|
||||||
|
OPT(StatusTampered);
|
||||||
|
OPT(StatusLowBattery);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(SmokeSensor,87) // Defines a Smoke Sensor.
|
||||||
|
REQ(SmokeDetected);
|
||||||
|
OPT(StatusActive);
|
||||||
|
OPT(StatusFault);
|
||||||
|
OPT(StatusTampered);
|
||||||
|
OPT(StatusLowBattery);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(TemperatureSensor,8A) // Defines a Temperature Sensor.
|
||||||
|
REQ(CurrentTemperature);
|
||||||
|
OPT(StatusActive);
|
||||||
|
OPT(StatusFault);
|
||||||
|
OPT(StatusTampered);
|
||||||
|
OPT(StatusLowBattery);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
SERVICES_GROUP // Doors, Locks, and Windows
|
||||||
|
|
||||||
|
CREATE_SERV(Door,81) // Defines a motorized Door.
|
||||||
REQ(CurrentPosition);
|
REQ(CurrentPosition);
|
||||||
REQ(TargetPosition);
|
REQ(TargetPosition);
|
||||||
REQ(PositionState);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(HoldPosition);
|
|
||||||
OPT(ObstructionDetected);
|
OPT(ObstructionDetected);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
OPT_DEP(PositionState);
|
||||||
|
OPT_DEP(HoldPosition);
|
||||||
END_SERV
|
END_SERV
|
||||||
|
|
||||||
CREATE_SERV(Doorbell,121)
|
CREATE_SERV(Doorbell,121) // Defines a Doorbell. Can be used on a standalone basis or in conjunction with a <b>LockMechanism</b> Service.
|
||||||
REQ(ProgrammableSwitchEvent);
|
REQ(ProgrammableSwitchEvent);
|
||||||
OPT(Name);
|
OPT_DEP(Volume);
|
||||||
OPT(Volume);
|
OPT_DEP(Brightness);
|
||||||
OPT(Brightness);
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
END_SERV
|
END_SERV
|
||||||
|
|
||||||
CREATE_SERV(Fan,B7)
|
CREATE_SERV(GarageDoorOpener,41) // Defines a motorized Garage Door Opener.
|
||||||
REQ(Active);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(CurrentFanState);
|
|
||||||
OPT(TargetFanState);
|
|
||||||
OPT(RotationDirection);
|
|
||||||
OPT(RotationSpeed);
|
|
||||||
OPT(SwingMode);
|
|
||||||
OPT(LockPhysicalControls);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(Faucet,D7)
|
|
||||||
REQ(Active);
|
|
||||||
OPT(StatusFault);
|
|
||||||
OPT(Name);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(FilterMaintenance,BA)
|
|
||||||
REQ(FilterChangeIndication);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(FilterLifeLevel);
|
|
||||||
OPT(ResetFilterIndication);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(GarageDoorOpener,41)
|
|
||||||
REQ(CurrentDoorState);
|
REQ(CurrentDoorState);
|
||||||
REQ(TargetDoorState);
|
REQ(TargetDoorState);
|
||||||
REQ(ObstructionDetected);
|
REQ(ObstructionDetected);
|
||||||
OPT(LockCurrentState);
|
OPT(LockCurrentState);
|
||||||
OPT(LockTargetState);
|
OPT(LockTargetState);
|
||||||
OPT(Name);
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
END_SERV
|
END_SERV
|
||||||
|
|
||||||
CREATE_SERV(HAPProtocolInformation,A2)
|
CREATE_SERV(LockMechanism,45) // Defines an electronic Lock.
|
||||||
REQ(Version);
|
REQ(LockCurrentState);
|
||||||
|
REQ(LockTargetState);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
END_SERV
|
END_SERV
|
||||||
|
|
||||||
CREATE_SERV(HeaterCooler,BC)
|
CREATE_SERV(Window,8B) // Defines a motorized Window.
|
||||||
|
REQ(CurrentPosition);
|
||||||
|
REQ(TargetPosition);
|
||||||
|
OPT(ObstructionDetected);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
OPT_DEP(PositionState);
|
||||||
|
OPT_DEP(HoldPosition);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(WindowCovering,8C) // Defines a motorized Window Shade, Screen, Awning, etc.
|
||||||
|
REQ(TargetPosition);
|
||||||
|
REQ(CurrentPosition);
|
||||||
|
OPT(CurrentHorizontalTiltAngle);
|
||||||
|
OPT(TargetHorizontalTiltAngle);
|
||||||
|
OPT(CurrentVerticalTiltAngle);
|
||||||
|
OPT(TargetVerticalTiltAngle);
|
||||||
|
OPT(ObstructionDetected);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
OPT_DEP(PositionState);
|
||||||
|
OPT_DEP(HoldPosition);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
SERVICES_GROUP // Water Systems
|
||||||
|
|
||||||
|
CREATE_SERV(Faucet,D7) // Defines the master control for a multi-Valve appliance. Linked Services: <b>Valve</b> (at least one required), and <b>HeaterCooler</b> (optional).
|
||||||
REQ(Active);
|
REQ(Active);
|
||||||
REQ(CurrentTemperature);
|
|
||||||
REQ(CurrentHeaterCoolerState);
|
|
||||||
REQ(TargetHeaterCoolerState);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(RotationSpeed);
|
|
||||||
OPT(TemperatureDisplayUnits);
|
|
||||||
OPT(SwingMode);
|
|
||||||
OPT(CoolingThresholdTemperature);
|
|
||||||
OPT(HeatingThresholdTemperature);
|
|
||||||
OPT(LockPhysicalControls);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(HumidifierDehumidifier,BD)
|
|
||||||
REQ(Active);
|
|
||||||
REQ(CurrentRelativeHumidity);
|
|
||||||
REQ(CurrentHumidifierDehumidifierState);
|
|
||||||
REQ(TargetHumidifierDehumidifierState);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(RelativeHumidityDehumidifierThreshold);
|
|
||||||
OPT(RelativeHumidityHumidifierThreshold);
|
|
||||||
OPT(RotationSpeed);
|
|
||||||
OPT(SwingMode);
|
|
||||||
OPT(WaterLevel);
|
|
||||||
OPT(LockPhysicalControls);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(HumiditySensor,82)
|
|
||||||
REQ(CurrentRelativeHumidity);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(StatusActive);
|
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
OPT(StatusTampered);
|
OPT(ConfiguredName);
|
||||||
OPT(StatusLowBattery);
|
OPT_DEP(Name);
|
||||||
END_SERV
|
END_SERV
|
||||||
|
|
||||||
CREATE_SERV(InputSource,D9)
|
CREATE_SERV(IrrigationSystem,CF) // Defines an Irrigation System. Linked Services: <b>Valve</b> Service (at least one required).
|
||||||
OPT(ConfiguredName);
|
|
||||||
OPT(IsConfigured);
|
|
||||||
REQ(Identifier);
|
|
||||||
OPT(CurrentVisibilityState);
|
|
||||||
OPT(TargetVisibilityState);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(IrrigationSystem,CF)
|
|
||||||
REQ(Active);
|
REQ(Active);
|
||||||
REQ(ProgramMode);
|
REQ(ProgramMode);
|
||||||
REQ(InUse);
|
REQ(InUse);
|
||||||
OPT(RemainingDuration);
|
OPT(RemainingDuration);
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
END_SERV
|
END_SERV
|
||||||
|
|
||||||
CREATE_SERV(LeakSensor,83)
|
CREATE_SERV(Valve,D0) // Defines an electronic Valve. Can be used standalone or as a Linked Service for either a <b>Faucet</b> or <b>IrrigationSystem</b> Service.
|
||||||
REQ(LeakDetected);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(StatusActive);
|
|
||||||
OPT(StatusFault);
|
|
||||||
OPT(StatusTampered);
|
|
||||||
OPT(StatusLowBattery);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(LightBulb,43)
|
|
||||||
REQ(On);
|
|
||||||
OPT(Brightness);
|
|
||||||
OPT(Hue);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(Saturation);
|
|
||||||
OPT(ColorTemperature);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(LightSensor,84)
|
|
||||||
REQ(CurrentAmbientLightLevel);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(StatusActive);
|
|
||||||
OPT(StatusFault);
|
|
||||||
OPT(StatusTampered);
|
|
||||||
OPT(StatusLowBattery);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(LockMechanism,45)
|
|
||||||
REQ(LockCurrentState);
|
|
||||||
REQ(LockTargetState);
|
|
||||||
OPT(Name);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(Microphone,112)
|
|
||||||
REQ(Mute);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(Volume);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(MotionSensor,85)
|
|
||||||
REQ(MotionDetected);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(StatusActive);
|
|
||||||
OPT(StatusFault);
|
|
||||||
OPT(StatusTampered);
|
|
||||||
OPT(StatusLowBattery);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(OccupancySensor,86)
|
|
||||||
REQ(OccupancyDetected);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(StatusActive);
|
|
||||||
OPT(StatusFault);
|
|
||||||
OPT(StatusTampered);
|
|
||||||
OPT(StatusLowBattery);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(Outlet,47)
|
|
||||||
REQ(On);
|
|
||||||
REQ(OutletInUse);
|
|
||||||
OPT(Name);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(SecuritySystem,7E)
|
|
||||||
REQ(SecuritySystemCurrentState);
|
|
||||||
REQ(SecuritySystemTargetState);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(SecuritySystemAlarmType);
|
|
||||||
OPT(StatusFault);
|
|
||||||
OPT(StatusTampered);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(ServiceLabel,CC)
|
|
||||||
REQ(ServiceLabelNamespace);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(Slat,B9)
|
|
||||||
REQ(CurrentSlatState);
|
|
||||||
REQ(SlatType);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(SwingMode);
|
|
||||||
OPT(CurrentTiltAngle);
|
|
||||||
OPT(TargetTiltAngle);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(SmokeSensor,87)
|
|
||||||
REQ(SmokeDetected);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(StatusActive);
|
|
||||||
OPT(StatusFault);
|
|
||||||
OPT(StatusTampered);
|
|
||||||
OPT(StatusLowBattery);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(Speaker,113)
|
|
||||||
REQ(Mute);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(Volume);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(StatelessProgrammableSwitch,89)
|
|
||||||
REQ(ProgrammableSwitchEvent);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(ServiceLabelIndex);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(Switch,49)
|
|
||||||
REQ(On);
|
|
||||||
OPT(Name);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(Television,D8)
|
|
||||||
REQ(Active);
|
|
||||||
OPT(ConfiguredName);
|
|
||||||
OPT(ActiveIdentifier);
|
|
||||||
OPT(RemoteKey);
|
|
||||||
OPT(PowerModeSelection);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(TelevisionSpeaker,113)
|
|
||||||
REQ(VolumeControlType);
|
|
||||||
REQ(VolumeSelector);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(TemperatureSensor,8A)
|
|
||||||
REQ(CurrentTemperature);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(StatusActive);
|
|
||||||
OPT(StatusFault);
|
|
||||||
OPT(StatusTampered);
|
|
||||||
OPT(StatusLowBattery);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(Thermostat,4A)
|
|
||||||
REQ(CurrentHeatingCoolingState);
|
|
||||||
REQ(TargetHeatingCoolingState);
|
|
||||||
REQ(CurrentTemperature);
|
|
||||||
REQ(TargetTemperature);
|
|
||||||
REQ(TemperatureDisplayUnits);
|
|
||||||
OPT(CoolingThresholdTemperature);
|
|
||||||
OPT(CurrentRelativeHumidity);
|
|
||||||
OPT(HeatingThresholdTemperature);
|
|
||||||
OPT(Name);
|
|
||||||
OPT(TargetRelativeHumidity);
|
|
||||||
END_SERV
|
|
||||||
|
|
||||||
CREATE_SERV(Valve,D0)
|
|
||||||
REQ(Active);
|
REQ(Active);
|
||||||
REQ(InUse);
|
REQ(InUse);
|
||||||
REQ(ValveType);
|
REQ(ValveType);
|
||||||
|
|
@ -375,29 +400,70 @@ namespace Service {
|
||||||
OPT(IsConfigured);
|
OPT(IsConfigured);
|
||||||
OPT(ServiceLabelIndex);
|
OPT(ServiceLabelIndex);
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
OPT(Name);
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
END_SERV
|
END_SERV
|
||||||
|
|
||||||
CREATE_SERV(Window,8B)
|
SERVICES_GROUP // Security Systems
|
||||||
REQ(CurrentPosition);
|
|
||||||
REQ(TargetPosition);
|
CREATE_SERV(SecuritySystem,7E) // Defines a Security System. Often used in combination with <b>MotionSensor</b> and <b>ContactSensor</b> Services.
|
||||||
REQ(PositionState);
|
REQ(SecuritySystemCurrentState);
|
||||||
OPT(Name);
|
REQ(SecuritySystemTargetState);
|
||||||
OPT(HoldPosition);
|
OPT(SecuritySystemAlarmType);
|
||||||
OPT(ObstructionDetected);
|
OPT(StatusFault);
|
||||||
|
OPT(StatusTampered);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
END_SERV
|
END_SERV
|
||||||
|
|
||||||
CREATE_SERV(WindowCovering,8C)
|
SERVICES_GROUP // Televisions
|
||||||
REQ(TargetPosition);
|
|
||||||
REQ(CurrentPosition);
|
CREATE_SERV(InputSource,D9) // Defines an Input Source for a TV. Use only as a Linked Service for the <b>Television</b> Service.
|
||||||
REQ(PositionState);
|
REQ(Identifier);
|
||||||
OPT(Name);
|
OPT(ConfiguredName);
|
||||||
OPT(HoldPosition);
|
OPT(IsConfigured);
|
||||||
OPT(CurrentHorizontalTiltAngle);
|
OPT(CurrentVisibilityState);
|
||||||
OPT(TargetHorizontalTiltAngle);
|
OPT(TargetVisibilityState);
|
||||||
OPT(CurrentVerticalTiltAngle);
|
END_SERV
|
||||||
OPT(TargetVerticalTiltAngle);
|
|
||||||
OPT(ObstructionDetected);
|
CREATE_SERV(Television,D8) // Defines a TV. Optional Linked Services: <b>InputSource</b> and <b>TelevisionSpeaker</b>.
|
||||||
|
REQ(Active);
|
||||||
|
OPT(ActiveIdentifier);
|
||||||
|
OPT(RemoteKey);
|
||||||
|
OPT(PowerModeSelection);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV(TelevisionSpeaker,113) // Defines a Television Speaker that can be controlled via the Remote Control widget on an iPhone. Use only as a Linked Service for the <b>Television</b> Service.
|
||||||
|
REQ(VolumeControlType);
|
||||||
|
REQ(VolumeSelector);
|
||||||
|
OPT(ConfiguredName);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
SERVICES_GROUP // Miscellaneous
|
||||||
|
|
||||||
|
CREATE_SERV(ServiceLabel,CC) // Defines a naming scheme for un-nameable Services, such as a <b>StatelessProgrammableSwitch</b>, by Linking them to this Service. When used, those other Services must each include a <b>ServiceLabelIndex</b> Characteristic with a unique value.
|
||||||
|
REQ(ServiceLabelNamespace);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
// Deprecated or unsupported Services
|
||||||
|
|
||||||
|
CREATE_SERV_DEP(HAPProtocolInformation,A2)
|
||||||
|
REQ_DEP(Version);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV_DEP(Microphone,112)
|
||||||
|
REQ_DEP(Mute);
|
||||||
|
OPT_DEP(Volume);
|
||||||
|
OPT_DEP(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
|
END_SERV
|
||||||
|
|
||||||
|
CREATE_SERV_DEP(Speaker,113)
|
||||||
|
REQ_DEP(Mute);
|
||||||
|
OPT_DEP(Volume);
|
||||||
|
OPT_DEP(ConfiguredName);
|
||||||
|
OPT_DEP(Name);
|
||||||
END_SERV
|
END_SERV
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -408,127 +474,127 @@ namespace Service {
|
||||||
|
|
||||||
// Macro to define Span Characteristic structures based on name of HAP Characteristic, default value, and min/max value (not applicable for STRING or BOOL which default to min=0, max=1)
|
// Macro to define Span Characteristic structures based on name of HAP Characteristic, default value, and min/max value (not applicable for STRING or BOOL which default to min=0, max=1)
|
||||||
|
|
||||||
#define CREATE_CHAR(TYPE,HAPCHAR,DEFVAL,MINVAL,MAXVAL) \
|
#define CREATE_CHAR(TYPE,HAPCHAR,DEFVAL,MINVAL,MAXVAL,...) \
|
||||||
struct HAPCHAR : SpanCharacteristic { HAPCHAR(TYPE val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&hapChars.HAPCHAR} { init(val,nvsStore,(TYPE)MINVAL,(TYPE)MAXVAL); } };
|
struct HAPCHAR : SpanCharacteristic { __VA_OPT__(enum{) __VA_ARGS__ __VA_OPT__(};) HAPCHAR(TYPE val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&hapChars.HAPCHAR} { init(val,nvsStore,(TYPE)MINVAL,(TYPE)MAXVAL); } };
|
||||||
|
|
||||||
namespace Characteristic {
|
namespace Characteristic {
|
||||||
|
|
||||||
CREATE_CHAR(uint32_t,AccessoryFlags,1,1,1);
|
CREATE_CHAR(uint32_t,AccessoryFlags,1,1,1); // not applicable for HomeSpan
|
||||||
CREATE_CHAR(uint8_t,Active,0,0,1);
|
CREATE_CHAR(uint8_t,Active,0,0,1,INACTIVE,ACTIVE); // indicates if the Service is active/on
|
||||||
CREATE_CHAR(uint32_t,ActiveIdentifier,0,0,255);
|
CREATE_CHAR(uint32_t,ActiveIdentifier,0,0,255); // numerical Identifier of the <b>InputSource</b> selected in the Home App.
|
||||||
CREATE_CHAR(uint8_t,AirQuality,0,0,5);
|
CREATE_CHAR(uint8_t,AirQuality,0,0,5,UNKNOWN,EXCELLENT,GOOD,FAIR,INFERIOR,POOR); // a subjective description
|
||||||
CREATE_CHAR(uint8_t,BatteryLevel,0,0,100);
|
CREATE_CHAR(uint8_t,BatteryLevel,100,0,100); // measured as a percentage
|
||||||
CREATE_CHAR(int,Brightness,0,0,100);
|
CREATE_CHAR(int,Brightness,0,0,100); // measured as a percentage
|
||||||
CREATE_CHAR(double,CarbonMonoxideLevel,0,0,100);
|
CREATE_CHAR(double,CarbonMonoxideLevel,0,0,100); // measured in parts per million (ppm)
|
||||||
CREATE_CHAR(double,CarbonMonoxidePeakLevel,0,0,100);
|
CREATE_CHAR(double,CarbonMonoxidePeakLevel,0,0,100); // measured in parts per million (ppm)
|
||||||
CREATE_CHAR(uint8_t,CarbonMonoxideDetected,0,0,1);
|
CREATE_CHAR(uint8_t,CarbonMonoxideDetected,0,0,1,NORMAL,ABNORMAL); // indicates if abnormal level is detected
|
||||||
CREATE_CHAR(double,CarbonDioxideLevel,0,0,100000);
|
CREATE_CHAR(double,CarbonDioxideLevel,0,0,100000); // measured on parts per million (ppm)
|
||||||
CREATE_CHAR(double,CarbonDioxidePeakLevel,0,0,100000);
|
CREATE_CHAR(double,CarbonDioxidePeakLevel,0,0,100000); // measured in parts per million (ppm)
|
||||||
CREATE_CHAR(uint8_t,CarbonDioxideDetected,0,0,1);
|
CREATE_CHAR(uint8_t,CarbonDioxideDetected,0,0,1,NORMAL,ABNORMAL); // indicates if abnormal level is detected
|
||||||
CREATE_CHAR(uint8_t,ChargingState,0,0,2);
|
CREATE_CHAR(uint8_t,ChargingState,0,0,2,NOT_CHARGING,CHARGING,NOT_CHARGEABLE); // indicates state of battery charging
|
||||||
CREATE_CHAR(uint8_t,ClosedCaptions,0,0,1);
|
CREATE_CHAR(uint8_t,ClosedCaptions,0,0,1); // unused by any Service
|
||||||
CREATE_CHAR(double,CoolingThresholdTemperature,10,10,35);
|
CREATE_CHAR(double,CoolingThresholdTemperature,10,10,35); // cooling turns on when temperature (in Celsius) rises above this threshold
|
||||||
CREATE_CHAR(uint32_t,ColorTemperature,200,140,500);
|
CREATE_CHAR(uint32_t,ColorTemperature,200,140,500); // measured in inverse megaKelvin (= 1,000,000 / Kelvin)
|
||||||
CREATE_CHAR(uint8_t,ContactSensorState,1,0,1);
|
CREATE_CHAR(uint8_t,ContactSensorState,1,0,1,DETECTED,NOT_DETECTED); // indictates if contact is detected (i.e. closed)
|
||||||
CREATE_CHAR(const char *,ConfiguredName,"unnamed",0,1);
|
CREATE_CHAR(const char *,ConfiguredName,"unnamed",0,1); // default display name of this Service
|
||||||
CREATE_CHAR(double,CurrentAmbientLightLevel,1,0.0001,100000);
|
CREATE_CHAR(double,CurrentAmbientLightLevel,1,0.0001,100000); // measured in Lux (lumens/m<sup>2</sup>
|
||||||
CREATE_CHAR(int,CurrentHorizontalTiltAngle,0,-90,90);
|
CREATE_CHAR(int,CurrentHorizontalTiltAngle,0,-90,90); // current angle (in degrees) of slats from fully up (-90) to fully open (0) to fully down (90)
|
||||||
CREATE_CHAR(uint8_t,CurrentAirPurifierState,1,0,2);
|
CREATE_CHAR(uint8_t,CurrentAirPurifierState,0,0,2,INACTIVE,IDLE,PURIFYING); // indicates current state of air purification
|
||||||
CREATE_CHAR(uint8_t,CurrentSlatState,0,0,2);
|
CREATE_CHAR(uint8_t,CurrentSlatState,0,0,2,FIXED,JAMMED,SWINGING); // indicates current state of slats
|
||||||
CREATE_CHAR(uint8_t,CurrentPosition,0,0,100);
|
CREATE_CHAR(uint8_t,CurrentPosition,0,0,100); // current position (as a percentage) from fully closed (0) to full open (100)
|
||||||
CREATE_CHAR(int,CurrentVerticalTiltAngle,0,-90,90);
|
CREATE_CHAR(int,CurrentVerticalTiltAngle,0,-90,90); // current angle (in degrees) of slats from fully left (-90) to fully open (0) to fully right (90)
|
||||||
CREATE_CHAR(uint8_t,CurrentVisibilityState,0,0,1);
|
CREATE_CHAR(uint8_t,CurrentVisibilityState,0,0,1,VISIBLE,NOT_VISIBLE); // current visibility of the Service, as selectable on the Settings Page of the Home App
|
||||||
CREATE_CHAR(uint8_t,CurrentHumidifierDehumidifierState,1,0,3);
|
CREATE_CHAR(uint8_t,CurrentHumidifierDehumidifierState,1,0,3,INACTIVE,IDLE,HUMIDIFYING,DEHUMIDIFYING); // indicates current state of humidifier/dehumidifer
|
||||||
CREATE_CHAR(uint8_t,CurrentDoorState,1,0,4);
|
CREATE_CHAR(uint8_t,CurrentDoorState,1,0,4,OPEN,CLOSED,OPENING,CLOSING,STOPPED); // indicates current state of a door
|
||||||
CREATE_CHAR(uint8_t,CurrentFanState,1,0,2);
|
CREATE_CHAR(uint8_t,CurrentFanState,1,0,2,INACTIVE,IDLE,BLOWING); // indicates current state of a fan
|
||||||
CREATE_CHAR(uint8_t,CurrentHeatingCoolingState,0,0,2);
|
CREATE_CHAR(uint8_t,CurrentHeatingCoolingState,0,0,2,IDLE,HEATING,COOLING); // indicates whether appliance is currently heating, cooling, or just idle
|
||||||
CREATE_CHAR(uint8_t,CurrentHeaterCoolerState,1,0,3);
|
CREATE_CHAR(uint8_t,CurrentHeaterCoolerState,1,0,3,INACTIVE,IDLE,HEATING,COOLING); // indicates whether appliance is currently heating, cooling, idle, or off
|
||||||
CREATE_CHAR(uint8_t,CurrentMediaState,0,0,5);
|
CREATE_CHAR(uint8_t,CurrentMediaState,0,0,5); // not used
|
||||||
CREATE_CHAR(double,CurrentRelativeHumidity,0,0,100);
|
CREATE_CHAR(double,CurrentRelativeHumidity,0,0,100); // current humidity measured as a percentage
|
||||||
CREATE_CHAR(double,CurrentTemperature,0,0,100);
|
CREATE_CHAR(double,CurrentTemperature,0,0,100); // current temperature measured in Celsius
|
||||||
CREATE_CHAR(int,CurrentTiltAngle,0,-90,90);
|
CREATE_CHAR(int,CurrentTiltAngle,0,-90,90); // current angle (in degrees) of slats from fully up or left (-90) to fully open (0) to fully down or right (90)
|
||||||
CREATE_CHAR(double,FilterLifeLevel,0,0,100);
|
CREATE_CHAR(double,FilterLifeLevel,100,0,100); // measured as a percentage of remaining life
|
||||||
CREATE_CHAR(uint8_t,FilterChangeIndication,0,0,1);
|
CREATE_CHAR(uint8_t,FilterChangeIndication,0,0,1,NO_CHANGE_NEEDED,CHANGE_NEEDED); // indicates state of filter
|
||||||
CREATE_CHAR(const char *,FirmwareRevision,"1.0.0",0,1);
|
CREATE_CHAR(const char *,FirmwareRevision,"1.0.0",0,1); // must be in form x[.y[.z]] - informational only
|
||||||
CREATE_CHAR(const char *,HardwareRevision,"1.0.0",0,1);
|
CREATE_CHAR(const char *,HardwareRevision,"1.0.0",0,1); // must be in form x[.y[.z]] - informational only
|
||||||
CREATE_CHAR(double,HeatingThresholdTemperature,16,0,25);
|
CREATE_CHAR(double,HeatingThresholdTemperature,16,0,25); // heating turns on when temperature (in Celsius) falls below this threshold
|
||||||
CREATE_CHAR(boolean,HoldPosition,false,0,1);
|
CREATE_CHAR(boolean,HoldPosition,false,0,1); // deprecated
|
||||||
CREATE_CHAR(double,Hue,0,0,360);
|
CREATE_CHAR(double,Hue,0,0,360); // color (in degrees) from red (0) to green (120) to blue (240) and back to red (360)
|
||||||
CREATE_CHAR(boolean,Identify,false,0,1);
|
CREATE_CHAR(boolean,Identify,1,1,1,RUN_ID=1); // triggers an update when HomeKit wants HomeSpan to run its identification routine for an Accessory
|
||||||
CREATE_CHAR(uint32_t,Identifier,0,0,255);
|
CREATE_CHAR(uint32_t,Identifier,0,0,255); // numerical Identifer of the <b>InputSource</b>.
|
||||||
CREATE_CHAR(uint8_t,InputDeviceType,0,0,6);
|
CREATE_CHAR(uint8_t,InputDeviceType,0,0,6); // not used
|
||||||
CREATE_CHAR(uint8_t,InputSourceType,0,0,10);
|
CREATE_CHAR(uint8_t,InputSourceType,0,0,10); // not used
|
||||||
CREATE_CHAR(uint8_t,InUse,0,0,1);
|
CREATE_CHAR(uint8_t,InUse,0,0,1,NOT_IN_USE,IN_USE); // if Service is set to active, this indictes whether it is currently in use
|
||||||
CREATE_CHAR(uint8_t,IsConfigured,0,0,1);
|
CREATE_CHAR(uint8_t,IsConfigured,0,0,1,NOT_CONFIGURED,CONFIGURED); // indicates if a predefined Service has been configured
|
||||||
CREATE_CHAR(uint8_t,LeakDetected,0,0,1);
|
CREATE_CHAR(uint8_t,LeakDetected,0,0,1,NOT_DETECTED,DETECTED); // indictates if a leak is detected
|
||||||
CREATE_CHAR(uint8_t,LockCurrentState,0,0,3);
|
CREATE_CHAR(uint8_t,LockCurrentState,0,0,3,UNLOCKED,LOCKED,JAMMED,UNKNOWN); // indicates state of a lock
|
||||||
CREATE_CHAR(uint8_t,LockPhysicalControls,0,0,1);
|
CREATE_CHAR(uint8_t,LockPhysicalControls,0,0,1,CONTROL_LOCK_DISABLED,CONTROL_LOCK_ENABLED); // indicates if local control lock is enabled
|
||||||
CREATE_CHAR(uint8_t,LockTargetState,0,0,1);
|
CREATE_CHAR(uint8_t,LockTargetState,0,0,1,UNLOCK,LOCK); // indicates desired state of lock
|
||||||
CREATE_CHAR(const char *,Manufacturer,"HomeSpan",0,1);
|
CREATE_CHAR(const char *,Manufacturer,"HomeSpan",0,1); // any string - informational only
|
||||||
CREATE_CHAR(const char *,Model,"HomeSpan-ESP32",0,1);
|
CREATE_CHAR(const char *,Model,"HomeSpan-ESP32",0,1); // any string - informational only
|
||||||
CREATE_CHAR(boolean,MotionDetected,false,0,1);
|
CREATE_CHAR(boolean,MotionDetected,0,0,1,NOT_DETECTED,DETECTED); // indicates if motion is detected
|
||||||
CREATE_CHAR(boolean,Mute,false,0,1);
|
CREATE_CHAR(boolean,Mute,0,0,1,OFF,ON); // not used
|
||||||
CREATE_CHAR(const char *,Name,"unnamed",0,1);
|
CREATE_CHAR(const char *,Name,"unnamed",0,1); // default display name of the Accessory
|
||||||
CREATE_CHAR(double,NitrogenDioxideDensity,0,0,1000);
|
CREATE_CHAR(double,NitrogenDioxideDensity,0,0,1000); // measured in µg/m<sup>3</sup>
|
||||||
CREATE_CHAR(boolean,ObstructionDetected,false,0,1);
|
CREATE_CHAR(boolean,ObstructionDetected,0,0,1,NOT_DETECTED,DETECTED); // indicates if obstruction is detected
|
||||||
CREATE_CHAR(double,PM25Density,0,0,1000);
|
CREATE_CHAR(double,PM25Density,0,0,1000); // 2.5-micron particulate density, measured in µg/m<sup>3</sup>
|
||||||
CREATE_CHAR(uint8_t,OccupancyDetected,0,0,1);
|
CREATE_CHAR(uint8_t,OccupancyDetected,0,0,1,NOT_DETECTED,DETECTED); // indicates if occupanccy is detected
|
||||||
CREATE_CHAR(boolean,OutletInUse,false,0,1);
|
CREATE_CHAR(boolean,OutletInUse,0,0,1,NOT_IN_USE,IN_USE); // indicates if an appliance or light is plugged into the outlet, regardless of whether on or off
|
||||||
CREATE_CHAR(boolean,On,false,0,1);
|
CREATE_CHAR(boolean,On,0,0,1,OFF,ON); // indicates if the Service is active/on
|
||||||
CREATE_CHAR(double,OzoneDensity,0,0,1000);
|
CREATE_CHAR(double,OzoneDensity,0,0,1000); // measured in µg/m<sup>3</sup>
|
||||||
CREATE_CHAR(uint8_t,PictureMode,0,0,13);
|
CREATE_CHAR(uint8_t,PictureMode,0,0,13); // not used
|
||||||
CREATE_CHAR(double,PM10Density,0,0,1000);
|
CREATE_CHAR(double,PM10Density,0,0,1000); // 10-micron particulate density, measured in µg/m<sup>3</sup>
|
||||||
CREATE_CHAR(uint8_t,PositionState,2,0,2);
|
CREATE_CHAR(uint8_t,PositionState,2,0,2,GOING_TO_MINIMUM,GOING_TO_MAXIMUM,STOPPED); // deprecated
|
||||||
CREATE_CHAR(uint8_t,PowerModeSelection,0,0,1);
|
CREATE_CHAR(uint8_t,PowerModeSelection,0,0,0,VIEW_SETTINGS); // when defined, creates a "View TV Settings" button in the Home App that triggers an update to this Characteristic when pressed
|
||||||
CREATE_CHAR(uint8_t,ProgramMode,0,0,2);
|
CREATE_CHAR(uint8_t,ProgramMode,0,0,2,NONE,SCHEDULED,SCHEDULE_OVERRIDEN); // indicates if pre-scheduled program is running
|
||||||
CREATE_CHAR(uint8_t,ProgrammableSwitchEvent,0,0,2);
|
CREATE_CHAR(uint8_t,ProgrammableSwitchEvent,0,0,2,SINGLE_PRESS,DOUBLE_PRESS,LONG_PRESS); // specifies type of button press
|
||||||
CREATE_CHAR(double,RelativeHumidityDehumidifierThreshold,50,0,100);
|
CREATE_CHAR(double,RelativeHumidityDehumidifierThreshold,50,0,100); // dehumidfier turns on when humidity rises above this threshold
|
||||||
CREATE_CHAR(double,RelativeHumidityHumidifierThreshold,50,0,100);
|
CREATE_CHAR(double,RelativeHumidityHumidifierThreshold,50,0,100); // humidfier turns on when humidity falls below this threshold
|
||||||
CREATE_CHAR(uint32_t,RemainingDuration,60,0,3600);
|
CREATE_CHAR(uint32_t,RemainingDuration,60,0,3600); // duration (in seconds) remaining for Service to be active/on
|
||||||
CREATE_CHAR(uint8_t,RemoteKey,0,0,16);
|
CREATE_CHAR(uint8_t,RemoteKey,4,4,15,UP=4,DOWN,LEFT,RIGHT,CENTER,BACK,PLAY_PAUSE=11,INFO=15); // triggers an update when the corresponding key is pressed in the Remote Control widget on an iPhone
|
||||||
CREATE_CHAR(uint8_t,ResetFilterIndication,0,1,1);
|
CREATE_CHAR(uint8_t,ResetFilterIndication,1,1,1,RESET_FILTER=1); // triggers an update when the user chooses to reset the <b>FilterChangeIndication</b> (only appears in Eve App, not Home App)
|
||||||
CREATE_CHAR(int,RotationDirection,0,0,1);
|
CREATE_CHAR(int,RotationDirection,0,0,1,CLOCKWISE,COUNTERCLOCKWISE); // indicates the rotation direction of a fan
|
||||||
CREATE_CHAR(double,RotationSpeed,0,0,100);
|
CREATE_CHAR(double,RotationSpeed,0,0,100); // measured as a percentage
|
||||||
CREATE_CHAR(double,Saturation,0,0,100);
|
CREATE_CHAR(double,Saturation,0,0,100); // color saturation, measured as a percentage
|
||||||
CREATE_CHAR(uint8_t,SecuritySystemAlarmType,0,0,1);
|
CREATE_CHAR(uint8_t,SecuritySystemAlarmType,0,0,1,KNOWN,UNKNOWN); // indicates whether alarm was triggered for known reason
|
||||||
CREATE_CHAR(uint8_t,SecuritySystemCurrentState,3,0,4);
|
CREATE_CHAR(uint8_t,SecuritySystemCurrentState,3,0,4,ARMED_STAY,ARMED_AWAY,ARMED_NIGHT,DISARMED,ALARM_TRIGGERED); // indicates current state of the security system
|
||||||
CREATE_CHAR(uint8_t,SecuritySystemTargetState,3,0,3);
|
CREATE_CHAR(uint8_t,SecuritySystemTargetState,3,0,3,ARM_STAY,ARM_AWAY,ARM_NIGHT,DISARM); // indicates desired state of the security system
|
||||||
CREATE_CHAR(const char *,SerialNumber,"HS-12345",0,1);
|
CREATE_CHAR(const char *,SerialNumber,"HS-12345",0,1); // any string - informational only
|
||||||
CREATE_CHAR(uint8_t,ServiceLabelIndex,1,1,255);
|
CREATE_CHAR(uint8_t,ServiceLabelIndex,1,1,255); // numerical index used to distinguish multiple copies of the same Service within an Accessory
|
||||||
CREATE_CHAR(uint8_t,ServiceLabelNamespace,1,0,1);
|
CREATE_CHAR(uint8_t,ServiceLabelNamespace,1,0,1,DOTS,NUMERALS); // indicates how un-named Services linked together with a <b>ServiceLabel</b> Service should be displayed in the Home App
|
||||||
CREATE_CHAR(uint8_t,SlatType,0,0,1);
|
CREATE_CHAR(uint8_t,SlatType,0,0,1,HORIZONTAL,VERTICAL); // indicates the direction of a slat or group of slats
|
||||||
CREATE_CHAR(uint8_t,SleepDiscoveryMode,0,0,1);
|
CREATE_CHAR(uint8_t,SleepDiscoveryMode,0,0,1); // not used
|
||||||
CREATE_CHAR(uint8_t,SmokeDetected,0,0,1);
|
CREATE_CHAR(uint8_t,SmokeDetected,0,0,1,NOT_DETECTED,DETECTED); // indicates if smoke is detected
|
||||||
CREATE_CHAR(boolean,StatusActive,true,0,1);
|
CREATE_CHAR(boolean,StatusActive,1,0,1,NOT_FUNCTIONING,FUNCTIONING); // indicates whether the Service is properly functioning
|
||||||
CREATE_CHAR(uint8_t,StatusFault,0,0,1);
|
CREATE_CHAR(uint8_t,StatusFault,0,0,1,NO_FAULT,FAULT); // indicates whether the Service has a fault (only appears in Eve App, not Home App)
|
||||||
CREATE_CHAR(uint8_t,StatusJammed,0,0,1);
|
CREATE_CHAR(uint8_t,StatusJammed,0,0,1,NOT_JAMMED,JAMMED); // indicates whether the Service has been "jammed"
|
||||||
CREATE_CHAR(uint8_t,StatusLowBattery,0,0,1);
|
CREATE_CHAR(uint8_t,StatusLowBattery,0,0,1,NOT_LOW_BATTERY,LOW_BATTERY); // indicates state of battery
|
||||||
CREATE_CHAR(uint8_t,StatusTampered,0,0,1);
|
CREATE_CHAR(uint8_t,StatusTampered,0,0,1,NOT_TAMPERED,TAMPERED); // indicates whether the Service has been tampered with
|
||||||
CREATE_CHAR(double,SulphurDioxideDensity,0,0,1000);
|
CREATE_CHAR(double,SulphurDioxideDensity,0,0,1000); // measured in µg/m<sup>3</sup>
|
||||||
CREATE_CHAR(uint8_t,SwingMode,0,0,1);
|
CREATE_CHAR(uint8_t,SwingMode,0,0,1,SWING_DISABLED,SWING_ENABLED); // indicates whether swing-mode is enabled
|
||||||
CREATE_CHAR(uint8_t,TargetAirPurifierState,1,0,1);
|
CREATE_CHAR(uint8_t,TargetAirPurifierState,1,0,1,MANUAL,AUTO); // indicates desired state of air purifier
|
||||||
CREATE_CHAR(uint8_t,TargetFanState,1,0,1);
|
CREATE_CHAR(uint8_t,TargetFanState,1,0,1,MANUAL,AUTO); // indicates desired state of fan
|
||||||
CREATE_CHAR(int,TargetTiltAngle,0,-90,90);
|
CREATE_CHAR(int,TargetTiltAngle,0,-90,90); // indicated desired angle (in degrees) of slats from fully up or left (-90) to fully open (0) to fully down or right (90)
|
||||||
CREATE_CHAR(uint8_t,TargetHeaterCoolerState,0,0,2);
|
CREATE_CHAR(uint8_t,TargetHeaterCoolerState,0,0,2,AUTO,HEAT,COOL); // indicates desired state of heater/cooler
|
||||||
CREATE_CHAR(uint32_t,SetDuration,60,0,3600);
|
CREATE_CHAR(uint32_t,SetDuration,60,0,3600); // specifies the duration (in seconds) for a Service to remain on once activated
|
||||||
CREATE_CHAR(int,TargetHorizontalTiltAngle,0,-90,90);
|
CREATE_CHAR(int,TargetHorizontalTiltAngle,0,-90,90); // indicates desired angle (in degrees) of slats from fully up (-90) to fully open (0) to fully down (90)
|
||||||
CREATE_CHAR(uint8_t,TargetHumidifierDehumidifierState,0,0,2);
|
CREATE_CHAR(uint8_t,TargetHumidifierDehumidifierState,0,0,2,AUTO,HUMIDIFY,DEHUMIDIFY); // indicates desired state of humidifier/dehumidifier
|
||||||
CREATE_CHAR(uint8_t,TargetPosition,0,0,100);
|
CREATE_CHAR(uint8_t,TargetPosition,0,0,100); // indicates target position (as a percentage) from fully closed (0) to full open (100)
|
||||||
CREATE_CHAR(uint8_t,TargetDoorState,1,0,1);
|
CREATE_CHAR(uint8_t,TargetDoorState,1,0,1,OPEN,CLOSED); // indicates desired state of door
|
||||||
CREATE_CHAR(uint8_t,TargetHeatingCoolingState,0,0,3);
|
CREATE_CHAR(uint8_t,TargetHeatingCoolingState,0,0,3,OFF,HEAT,COOL,AUTO); // indicates desired state of appliance
|
||||||
CREATE_CHAR(uint8_t,TargetMediaState,0,0,2);
|
CREATE_CHAR(uint8_t,TargetMediaState,0,0,2); // unused
|
||||||
CREATE_CHAR(double,TargetRelativeHumidity,0,0,100);
|
CREATE_CHAR(double,TargetRelativeHumidity,0,0,100); // indicates desired humidity measured as a percentage
|
||||||
CREATE_CHAR(double,TargetTemperature,16,10,38);
|
CREATE_CHAR(double,TargetTemperature,16,10,38); // indicates desired temperature measures in Celsius
|
||||||
CREATE_CHAR(uint8_t,TargetVisibilityState,0,0,1);
|
CREATE_CHAR(uint8_t,TargetVisibilityState,0,0,1,VISIBLE,NOT_VISIBLE); // indicates desired visibility of the Service, as selectable on the Settings Page of the Home App
|
||||||
CREATE_CHAR(uint8_t,TemperatureDisplayUnits,0,0,1);
|
CREATE_CHAR(uint8_t,TemperatureDisplayUnits,0,0,1,CELSIUS,FAHRENHEIT); // indicates the desired units to display the temperature on the device itself (has no effect on Home App)
|
||||||
CREATE_CHAR(int,TargetVerticalTiltAngle,0,-90,90);
|
CREATE_CHAR(int,TargetVerticalTiltAngle,0,-90,90); // indicates desired angle (in degrees) of slats from fully left (-90) to fully open (0) to fully right (90)
|
||||||
CREATE_CHAR(uint8_t,ValveType,0,0,3);
|
CREATE_CHAR(uint8_t,ValveType,0,0,3,GENERIC,IRRIGATION,SHOWER_HEAD,FAUCET); // indicates the type of valve
|
||||||
CREATE_CHAR(const char *,Version,"1.0.0",0,1);
|
CREATE_CHAR(const char *,Version,"1.0.0",0,1); // unused
|
||||||
CREATE_CHAR(double,VOCDensity,0,0,1000);
|
CREATE_CHAR(double,VOCDensity,0,0,1000); // measured in µg/m<sup>3</sup>
|
||||||
CREATE_CHAR(uint8_t,Volume,0,0,100);
|
CREATE_CHAR(uint8_t,Volume,0,0,100); // unused
|
||||||
CREATE_CHAR(uint8_t,VolumeControlType,0,0,3);
|
CREATE_CHAR(uint8_t,VolumeControlType,3,0,3,NONE,RELATIVE,RELATIVE_CURRENT,ABSOLUTE); // indicates the type of volume control
|
||||||
CREATE_CHAR(uint8_t,VolumeSelector,0,0,1);
|
CREATE_CHAR(uint8_t,VolumeSelector,0,0,1,VOLUME_UP,VOLUME_DOWN); // triggered by presses to the iPhone's volume up/down buttons when TV is selected in the Remote Control widget
|
||||||
CREATE_CHAR(double,WaterLevel,0,0,100);
|
CREATE_CHAR(double,WaterLevel,0,0,100); // measured as a percentage
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
314
src/TLV.h
314
src/TLV.h
|
|
@ -1,314 +0,0 @@
|
||||||
/*********************************************************************************
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
|
||||||
*
|
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
********************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
template <class tagType, int maxTags>
|
|
||||||
class TLV {
|
|
||||||
|
|
||||||
int cLen; // total number of bytes in all defined TLV records, including TAG andf LEN (suitable for use as Content-Length in HTTP Body)
|
|
||||||
int numTags; // actual number of tags defined
|
|
||||||
|
|
||||||
struct tlv_t {
|
|
||||||
tagType tag; // TAG
|
|
||||||
int len; // LENGTH
|
|
||||||
uint8_t *val; // VALUE buffer
|
|
||||||
int maxLen; // maximum length of VALUE buffer
|
|
||||||
const char *name; // abbreviated name of this TAG
|
|
||||||
};
|
|
||||||
|
|
||||||
tlv_t tlv[maxTags]; // pointer to array of TLV record structures
|
|
||||||
tlv_t *find(tagType tag); // returns pointer to TLV record with matching TAG (or NULL if no match)
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
TLV();
|
|
||||||
|
|
||||||
int create(tagType tag, int maxLen, const char *name); // creates a new TLV record of type 'tag' with 'maxLen' bytes and display 'name'
|
|
||||||
|
|
||||||
void clear(); // clear all TLV structures
|
|
||||||
int val(tagType tag); // returns VAL for TLV with matching TAG (or -1 if no match)
|
|
||||||
int val(tagType tag, uint8_t val); // sets and returns VAL for TLV with matching TAG (or -1 if no match)
|
|
||||||
uint8_t *buf(tagType tag); // returns VAL Buffer for TLV with matching TAG (or NULL if no match)
|
|
||||||
uint8_t *buf(tagType tag, int len); // set length and returns VAL Buffer for TLV with matching TAG (or NULL if no match or if LEN>MAX)
|
|
||||||
int len(tagType tag); // returns LEN for TLV matching TAG (or 0 if TAG is found but LEN not yet set; -1 if no match at all)
|
|
||||||
void print(int minLogLevel=0); // prints all defined TLVs (those with length>0), subject to specified minimum log level
|
|
||||||
int unpack(uint8_t *tlvBuf, int nBytes); // unpacks nBytes of TLV content from single byte buffer into individual TLV records (return 1 on success, 0 if fail)
|
|
||||||
int pack(uint8_t *tlvBuf); // if tlvBuf!=NULL, packs all defined TLV records (LEN>0) into a single byte buffer, spitting large TLVs into separate 255-byte chunks. Returns number of bytes (that would be) stored in buffer
|
|
||||||
int pack_old(uint8_t *buf); // packs all defined TLV records (LEN>0) into a single byte buffer, spitting large TLVs into separate 255-byte records. Returns number of bytes stored in buffer
|
|
||||||
|
|
||||||
}; // TLV
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
// TLV contructor()
|
|
||||||
|
|
||||||
template<class tagType, int maxTags>
|
|
||||||
TLV<tagType, maxTags>::TLV(){
|
|
||||||
numTags=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
// TLV create(tag, maxLen, name)
|
|
||||||
|
|
||||||
template<class tagType, int maxTags>
|
|
||||||
int TLV<tagType, maxTags>::create(tagType tag, int maxLen, const char *name){
|
|
||||||
|
|
||||||
if(numTags==maxTags){
|
|
||||||
Serial.print("\n*** ERROR: Can't create new TLC tag type with name='");
|
|
||||||
Serial.print(name);
|
|
||||||
Serial.print("' - exceeded number of records reserved\n\n");
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
tlv[numTags].tag=tag;
|
|
||||||
tlv[numTags].maxLen=maxLen;
|
|
||||||
tlv[numTags].name=name;
|
|
||||||
tlv[numTags].len=-1;
|
|
||||||
tlv[numTags].val=(uint8_t *)malloc(maxLen);
|
|
||||||
numTags++;
|
|
||||||
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
// TLV find(tag)
|
|
||||||
|
|
||||||
template<class tagType, int maxTags>
|
|
||||||
typename TLV<tagType, maxTags>::tlv_t *TLV<tagType, maxTags>::find(tagType tag){
|
|
||||||
|
|
||||||
for(int i=0;i<numTags;i++){
|
|
||||||
if(tlv[i].tag==tag)
|
|
||||||
return(tlv+i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
// TLV clear()
|
|
||||||
|
|
||||||
template<class tagType, int maxTags>
|
|
||||||
void TLV<tagType, maxTags>::clear(){
|
|
||||||
|
|
||||||
cLen=0;
|
|
||||||
|
|
||||||
for(int i=0;i<numTags;i++)
|
|
||||||
tlv[i].len=-1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
// TLV val(tag)
|
|
||||||
|
|
||||||
template<class tagType, int maxTags>
|
|
||||||
int TLV<tagType, maxTags>::val(tagType tag){
|
|
||||||
|
|
||||||
tlv_t *tlv=find(tag);
|
|
||||||
|
|
||||||
if(tlv && tlv->len>0)
|
|
||||||
return(tlv->val[0]);
|
|
||||||
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
// TLV val(tag, val)
|
|
||||||
|
|
||||||
template<class tagType, int maxTags>
|
|
||||||
int TLV<tagType, maxTags>::val(tagType tag, uint8_t val){
|
|
||||||
|
|
||||||
tlv_t *tlv=find(tag);
|
|
||||||
|
|
||||||
if(tlv){
|
|
||||||
tlv->val[0]=val;
|
|
||||||
tlv->len=1;
|
|
||||||
cLen+=tlv->len+2;
|
|
||||||
return(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
// TLV buf(tag)
|
|
||||||
|
|
||||||
template<class tagType, int maxTags>
|
|
||||||
uint8_t *TLV<tagType, maxTags>::buf(tagType tag){
|
|
||||||
|
|
||||||
tlv_t *tlv=find(tag);
|
|
||||||
|
|
||||||
if(tlv)
|
|
||||||
return(tlv->val);
|
|
||||||
|
|
||||||
return(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
// TLV buf(tag, len)
|
|
||||||
|
|
||||||
template<class tagType, int maxTags>
|
|
||||||
uint8_t *TLV<tagType, maxTags>::buf(tagType tag, int len){
|
|
||||||
|
|
||||||
tlv_t *tlv=find(tag);
|
|
||||||
|
|
||||||
if(tlv && len<=tlv->maxLen){
|
|
||||||
tlv->len=len;
|
|
||||||
cLen+=tlv->len;
|
|
||||||
|
|
||||||
for(int i=0;i<tlv->len;i+=255)
|
|
||||||
cLen+=2;
|
|
||||||
|
|
||||||
return(tlv->val);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
// TLV print()
|
|
||||||
|
|
||||||
template<class tagType, int maxTags>
|
|
||||||
void TLV<tagType, maxTags>::print(int minLogLevel){
|
|
||||||
|
|
||||||
if(homeSpan.getLogLevel()<minLogLevel)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for(int i=0;i<numTags;i++){
|
|
||||||
|
|
||||||
if(tlv[i].len>0){
|
|
||||||
Serial.printf("%s(%d) ",tlv[i].name,tlv[i].len);
|
|
||||||
|
|
||||||
for(int j=0;j<tlv[i].len;j++)
|
|
||||||
Serial.printf("%02X",tlv[i].val[j]);
|
|
||||||
|
|
||||||
Serial.printf("\n");
|
|
||||||
|
|
||||||
} // len>0
|
|
||||||
} // loop over all TLVs
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
// TLV pack(tlvBuf)
|
|
||||||
|
|
||||||
template<class tagType, int maxTags>
|
|
||||||
int TLV<tagType, maxTags>::pack(uint8_t *tlvBuf){
|
|
||||||
|
|
||||||
int n=0;
|
|
||||||
int nBytes;
|
|
||||||
|
|
||||||
for(int i=0;i<numTags;i++){
|
|
||||||
|
|
||||||
if((nBytes=tlv[i].len)>0){
|
|
||||||
for(int j=0;j<tlv[i].len;j+=255,nBytes-=255){
|
|
||||||
if(tlvBuf!=NULL){
|
|
||||||
*tlvBuf++=tlv[i].tag;
|
|
||||||
*tlvBuf++=nBytes>255?255:nBytes;
|
|
||||||
memcpy(tlvBuf,tlv[i].val+j,nBytes>255?255:nBytes);
|
|
||||||
tlvBuf+=nBytes>255?255:nBytes;
|
|
||||||
}
|
|
||||||
n+=(nBytes>255?255:nBytes)+2;
|
|
||||||
} // j-loop
|
|
||||||
} // len>0
|
|
||||||
|
|
||||||
} // loop over all TLVs
|
|
||||||
|
|
||||||
return(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
// TLV len(tag)
|
|
||||||
|
|
||||||
template<class tagType, int maxTags>
|
|
||||||
int TLV<tagType, maxTags>::len(tagType tag){
|
|
||||||
|
|
||||||
tlv_t *tlv=find(tag);
|
|
||||||
|
|
||||||
if(tlv)
|
|
||||||
return(tlv->len>0?tlv->len:0);
|
|
||||||
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
// TLV unpack(tlvBuf, nBytes)
|
|
||||||
|
|
||||||
template<class tagType, int maxTags>
|
|
||||||
int TLV<tagType, maxTags>::unpack(uint8_t *tlvBuf, int nBytes){
|
|
||||||
|
|
||||||
clear();
|
|
||||||
|
|
||||||
tagType tag;
|
|
||||||
int tagLen;
|
|
||||||
uint8_t *val;
|
|
||||||
int currentLen;
|
|
||||||
int state=0;
|
|
||||||
|
|
||||||
for(int i=0;i<nBytes;i++){
|
|
||||||
|
|
||||||
switch(state){
|
|
||||||
|
|
||||||
case 0: // ready to read next tag
|
|
||||||
if((tag=(tagType)tlvBuf[i])==-1){ // read TAG; return with error if not found
|
|
||||||
clear();
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
state=1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1: // ready to read tag length
|
|
||||||
tagLen=tlvBuf[i]; // read LEN
|
|
||||||
currentLen=len(tag); // get current length of existing tag
|
|
||||||
if(!(val=buf(tag,tagLen+currentLen))){ // get VAL Buffer for TAG and set LEN (returns NULL if LEN > maxLen)
|
|
||||||
clear();
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
val+=currentLen; // move val to end of current length (tag repeats to load more than 255 bytes)
|
|
||||||
|
|
||||||
if(tagLen==0) // no bytes to read
|
|
||||||
state=0;
|
|
||||||
else // move to next state
|
|
||||||
state=2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: // ready to read another byte into VAL
|
|
||||||
*val=tlvBuf[i]; // copy byte into VAL buffer
|
|
||||||
val++; // increment VAL buffer (already checked for sufficient length above)
|
|
||||||
tagLen--; // decrement number of bytes to continue copying
|
|
||||||
if(tagLen==0) // no more bytes to copy
|
|
||||||
state=0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
} // switch
|
|
||||||
} // for-loop
|
|
||||||
|
|
||||||
if(state==0) // should always end back in state=0
|
|
||||||
return(1); // return success
|
|
||||||
|
|
||||||
clear();
|
|
||||||
return(0); // return fail
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,243 @@
|
||||||
|
/*********************************************************************************
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
|
*
|
||||||
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
#include "TLV8.h"
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
tlv8_t::tlv8_t(uint8_t tag, size_t len, const uint8_t* val) : tag{tag}, len{len} {
|
||||||
|
if(len>0){
|
||||||
|
this->val=std::unique_ptr<uint8_t>((uint8_t *)HS_MALLOC(len));
|
||||||
|
if(val!=NULL)
|
||||||
|
memcpy((this->val).get(),val,len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
void tlv8_t::update(size_t addLen, const uint8_t *addVal){
|
||||||
|
if(addLen>0){
|
||||||
|
uint8_t *p=val.release();
|
||||||
|
p=(uint8_t *)HS_REALLOC(p,len+addLen);
|
||||||
|
val=std::unique_ptr<uint8_t>(p);
|
||||||
|
if(addVal!=NULL)
|
||||||
|
memcpy(p+len,addVal,addLen);
|
||||||
|
len+=addLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
|
||||||
|
void tlv8_t::osprint(std::ostream& os){
|
||||||
|
|
||||||
|
uint8_t *p=val.get(); // starting pointer
|
||||||
|
uint8_t *pend=p+len; // ending pointer (may equal starting if len=0)
|
||||||
|
|
||||||
|
do{
|
||||||
|
uint8_t nBytes=(pend-p)>255?255:(pend-p); // max is 255 bytes per TLV record
|
||||||
|
os.write((char *)&tag,1);
|
||||||
|
os.write((char *)&nBytes,1);
|
||||||
|
os.write((char *)p,nBytes);
|
||||||
|
p+=nBytes;
|
||||||
|
} while(p<pend);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
|
||||||
|
TLV8_it TLV8::add(uint8_t tag, size_t len, const uint8_t* val){
|
||||||
|
|
||||||
|
if(!empty() && front().tag==tag)
|
||||||
|
front().update(len,val);
|
||||||
|
else
|
||||||
|
emplace_front(tag,len,val);
|
||||||
|
|
||||||
|
return(begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
|
||||||
|
TLV8_it TLV8::find(uint8_t tag, TLV8_it it1, TLV8_it it2){
|
||||||
|
|
||||||
|
auto it=it1;
|
||||||
|
while(it!=it2 && (*it).tag!=tag)
|
||||||
|
it++;
|
||||||
|
return(it==it2?end():it);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
|
||||||
|
size_t TLV8::pack_size(TLV8_it it1, TLV8_it it2){
|
||||||
|
|
||||||
|
size_t nBytes=0;
|
||||||
|
|
||||||
|
while(it1!=it2){
|
||||||
|
nBytes+=2+(*it1).len;
|
||||||
|
if((*it1).len>255)
|
||||||
|
nBytes+=2*(((*it1).len-1)/255);
|
||||||
|
it1++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return(nBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
|
||||||
|
size_t TLV8::pack(uint8_t *buf, size_t bufSize){
|
||||||
|
|
||||||
|
size_t nBytes=0;
|
||||||
|
|
||||||
|
while(nBytes<bufSize && currentPackIt!=endPackIt){
|
||||||
|
switch(currentPackPhase){
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
currentPackBuf=(*currentPackIt).val.get();
|
||||||
|
endPackBuf=(*currentPackIt).val.get()+(*currentPackIt).len;
|
||||||
|
currentPackPhase=1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
*buf++=(*currentPackIt).tag;
|
||||||
|
nBytes++;
|
||||||
|
currentPackPhase=2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
currentPackLen=endPackBuf-currentPackBuf;
|
||||||
|
if(currentPackLen>255)
|
||||||
|
currentPackLen=255;
|
||||||
|
*buf++=currentPackLen;
|
||||||
|
nBytes++;
|
||||||
|
currentPackPhase=3;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
if(currentPackLen==0){
|
||||||
|
if(endPackBuf==currentPackBuf){
|
||||||
|
currentPackIt++;
|
||||||
|
currentPackPhase=0;
|
||||||
|
} else {
|
||||||
|
currentPackPhase=1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t copyBytes=(currentPackLen<(bufSize-nBytes)) ? currentPackLen : (bufSize-nBytes);
|
||||||
|
memcpy(buf,currentPackBuf,copyBytes);
|
||||||
|
buf+=copyBytes;
|
||||||
|
currentPackBuf+=copyBytes;
|
||||||
|
currentPackLen-=copyBytes;
|
||||||
|
nBytes+=copyBytes;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return(nBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
|
||||||
|
void TLV8::unpack(uint8_t *buf, size_t bufSize){
|
||||||
|
|
||||||
|
if(empty())
|
||||||
|
unpackPhase=0;
|
||||||
|
|
||||||
|
while(bufSize>0){
|
||||||
|
switch(unpackPhase){
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
unpackTag=*buf++;
|
||||||
|
bufSize--;
|
||||||
|
unpackPhase=1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
unpackBytes=*buf++;
|
||||||
|
bufSize--;
|
||||||
|
if(unpackBytes==0){
|
||||||
|
add(unpackTag);
|
||||||
|
unpackPhase=0;
|
||||||
|
} else {
|
||||||
|
unpackPhase=2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
size_t copyBytes=unpackBytes<bufSize ? unpackBytes : bufSize;
|
||||||
|
add(unpackTag,copyBytes,buf);
|
||||||
|
buf+=copyBytes;
|
||||||
|
unpackBytes-=copyBytes;
|
||||||
|
bufSize-=copyBytes;
|
||||||
|
if(unpackBytes==0)
|
||||||
|
unpackPhase=0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
|
||||||
|
const char *TLV8::getName(uint8_t tag){
|
||||||
|
|
||||||
|
if(names==NULL)
|
||||||
|
return(NULL);
|
||||||
|
|
||||||
|
for(int i=0;i<nNames;i++){
|
||||||
|
if(names[i].tag==tag)
|
||||||
|
return(names[i].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
|
||||||
|
void TLV8::print(TLV8_it it1, TLV8_it it2){
|
||||||
|
|
||||||
|
while(it1!=it2){
|
||||||
|
const char *name=getName((*it1).tag);
|
||||||
|
if(name)
|
||||||
|
Serial.printf("%s",name);
|
||||||
|
else
|
||||||
|
Serial.printf("%d",(*it1).tag);
|
||||||
|
Serial.printf("(%d) ",(*it1).len);
|
||||||
|
for(int i=0;i<(*it1).len;i++)
|
||||||
|
Serial.printf("%02X",(*it1).val.get()[i]);
|
||||||
|
Serial.printf("\n");
|
||||||
|
it1++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
void TLV8::osprint(std::ostream& os, TLV8_it it1, TLV8_it it2){
|
||||||
|
|
||||||
|
for(auto it=it1;it!=it2;it++)
|
||||||
|
(*it).osprint(os);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*********************************************************************************
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
|
*
|
||||||
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <sstream>
|
||||||
|
#include <forward_list>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "PSRAM.h"
|
||||||
|
|
||||||
|
struct tlv8_t {
|
||||||
|
uint8_t tag;
|
||||||
|
size_t len;
|
||||||
|
std::unique_ptr<uint8_t> val;
|
||||||
|
|
||||||
|
tlv8_t(uint8_t tag, size_t len, const uint8_t* val);
|
||||||
|
void update(size_t addLen, const uint8_t *addVal);
|
||||||
|
void osprint(std::ostream& os);
|
||||||
|
|
||||||
|
operator uint8_t*() const{
|
||||||
|
return(val.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
|
||||||
|
typedef std::forward_list<tlv8_t, Mallocator<tlv8_t>>::iterator TLV8_it;
|
||||||
|
typedef struct { const uint8_t tag; const char *name; } TLV8_names;
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
|
||||||
|
class TLV8 : public std::forward_list<tlv8_t, Mallocator<tlv8_t>> {
|
||||||
|
|
||||||
|
TLV8_it currentPackIt;
|
||||||
|
TLV8_it endPackIt;
|
||||||
|
uint8_t *currentPackBuf;
|
||||||
|
uint8_t *endPackBuf;
|
||||||
|
int currentPackPhase;
|
||||||
|
size_t currentPackLen;
|
||||||
|
|
||||||
|
uint8_t unpackTag;
|
||||||
|
size_t unpackBytes;
|
||||||
|
int unpackPhase;
|
||||||
|
|
||||||
|
const TLV8_names *names=NULL;
|
||||||
|
int nNames=0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
TLV8(){};
|
||||||
|
TLV8(const TLV8_names *names, int nNames) : names{names}, nNames{nNames} {};
|
||||||
|
|
||||||
|
TLV8_it add(uint8_t tag, size_t len, const uint8_t *val);
|
||||||
|
TLV8_it add(uint8_t tag, uint8_t val){return(add(tag, 1, &val));}
|
||||||
|
TLV8_it add(uint8_t tag){return(add(tag, 0, NULL));}
|
||||||
|
|
||||||
|
TLV8_it find(uint8_t tag, TLV8_it it1, TLV8_it it2);
|
||||||
|
TLV8_it find(uint8_t tag, TLV8_it it1){return(find(tag, it1, end()));}
|
||||||
|
TLV8_it find(uint8_t tag){return(find(tag, begin(), end()));}
|
||||||
|
|
||||||
|
int len(TLV8_it it){return(it==end()?-1:(*it).len);}
|
||||||
|
|
||||||
|
size_t pack_size(TLV8_it it1, TLV8_it it2);
|
||||||
|
size_t pack_size(){return(pack_size(begin(), end()));}
|
||||||
|
|
||||||
|
void pack_init(TLV8_it it1, TLV8_it it2){currentPackIt=it1; endPackIt=it2; currentPackPhase=0;}
|
||||||
|
void pack_init(TLV8_it it1){pack_init(it1, it1++);}
|
||||||
|
void pack_init(){pack_init(begin(),end());}
|
||||||
|
|
||||||
|
size_t pack(uint8_t *buf, size_t bufSize);
|
||||||
|
size_t pack(uint8_t *buf){pack_init(); return(pack(buf,pack_size()));}
|
||||||
|
|
||||||
|
const char *getName(uint8_t tag);
|
||||||
|
|
||||||
|
void print(TLV8_it it1, TLV8_it it2);
|
||||||
|
void print(TLV8_it it1){print(it1, it1++);}
|
||||||
|
void print(){print(begin(), end());}
|
||||||
|
|
||||||
|
void osprint(std::ostream& os, TLV8_it it1, TLV8_it it2);
|
||||||
|
void osprint(std::ostream& os, TLV8_it it1){osprint(os, it1, it1++);}
|
||||||
|
void osprint(std::ostream& os){osprint(os, begin(), end());}
|
||||||
|
|
||||||
|
void unpack(uint8_t *buf, size_t bufSize);
|
||||||
|
|
||||||
|
void wipe(){std::forward_list<tlv8_t, Mallocator<tlv8_t>>().swap(*this);}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
@ -278,4 +278,4 @@ void PushButton::reset(){
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
|
|
||||||
touch_value_t PushButton::threshold=0;
|
PushButton::touch_value_t PushButton::threshold=0;
|
||||||
|
|
|
||||||
76
src/Utils.h
76
src/Utils.h
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
@ -28,7 +28,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <driver/timer.h>
|
|
||||||
|
#include "PSRAM.h"
|
||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
|
|
||||||
|
|
@ -42,27 +43,58 @@ String mask(char *c, int n); // simply utility that creates a String fr
|
||||||
// going out of scope
|
// going out of scope
|
||||||
|
|
||||||
template <class bufType>
|
template <class bufType>
|
||||||
struct TempBuffer {
|
class TempBuffer {
|
||||||
bufType *buf;
|
|
||||||
int nBytes;
|
|
||||||
|
|
||||||
TempBuffer(size_t len){
|
private:
|
||||||
nBytes=len*sizeof(bufType);
|
|
||||||
buf=(bufType *)heap_caps_malloc(nBytes,MALLOC_CAP_8BIT);
|
bufType *buf=NULL;
|
||||||
|
size_t nElements;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
TempBuffer(size_t _nElements=1) : nElements(_nElements) {
|
||||||
|
buf=(bufType *)HS_MALLOC(nElements*sizeof(bufType));
|
||||||
if(buf==NULL){
|
if(buf==NULL){
|
||||||
Serial.print("\n\n*** FATAL ERROR: Requested allocation of ");
|
Serial.printf("\n\n*** FATAL ERROR: Requested allocation of %d bytes failed. Program Halting.\n\n",nElements*sizeof(bufType));
|
||||||
Serial.print(nBytes);
|
|
||||||
Serial.print(" bytes failed. Program Halting.\n\n");
|
|
||||||
while(1);
|
while(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TempBuffer(bufType *addBuf...) : nElements(0) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args,addBuf);
|
||||||
|
while(addBuf!=NULL){
|
||||||
|
size_t addElements=va_arg(args,size_t);
|
||||||
|
buf=(bufType *)HS_REALLOC(buf,(nElements+addElements)*sizeof(bufType));
|
||||||
|
if(buf==NULL){
|
||||||
|
Serial.printf("\n\n*** FATAL ERROR: Requested allocation of %d bytes failed. Program Halting.\n\n",nElements*sizeof(bufType));
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
memcpy(buf+nElements,addBuf,addElements*sizeof(bufType));
|
||||||
|
nElements+=addElements;
|
||||||
|
addBuf=va_arg(args,bufType *);
|
||||||
|
}
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
~TempBuffer(){
|
~TempBuffer(){
|
||||||
heap_caps_free(buf);
|
free(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
int len(){
|
int len(){
|
||||||
return(nBytes);
|
return(nElements*sizeof(bufType));
|
||||||
|
}
|
||||||
|
|
||||||
|
int size(){
|
||||||
|
return(nElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
bufType *get(){
|
||||||
|
return(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bufType*() const{
|
||||||
|
return(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
@ -71,12 +103,6 @@ struct TempBuffer {
|
||||||
// PushButton //
|
// PushButton //
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
|
||||||
#if SOC_TOUCH_VERSION_2
|
|
||||||
typedef uint32_t touch_value_t;
|
|
||||||
#else
|
|
||||||
typedef uint16_t touch_value_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class PushButton{
|
class PushButton{
|
||||||
|
|
||||||
int status;
|
int status;
|
||||||
|
|
@ -86,14 +112,22 @@ class PushButton{
|
||||||
uint32_t doubleAlarm;
|
uint32_t doubleAlarm;
|
||||||
uint32_t longAlarm;
|
uint32_t longAlarm;
|
||||||
|
|
||||||
|
#if SOC_TOUCH_VERSION_2
|
||||||
|
typedef uint32_t touch_value_t;
|
||||||
|
#else
|
||||||
|
typedef uint16_t touch_value_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
static touch_value_t threshold;
|
static touch_value_t threshold;
|
||||||
static const int calibCount=20;
|
static const int calibCount=20;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef boolean (*triggerType_t)(int pin);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
int pressType;
|
int pressType;
|
||||||
typedef boolean (*triggerType_t)(int pin);
|
|
||||||
|
|
||||||
int pin;
|
int pin;
|
||||||
triggerType_t triggerType;
|
triggerType_t triggerType;
|
||||||
|
|
||||||
|
|
|
||||||
86
src/src.ino
86
src/src.ino
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
@ -27,66 +27,60 @@
|
||||||
|
|
||||||
#include "HomeSpan.h"
|
#include "HomeSpan.h"
|
||||||
|
|
||||||
struct LED_Service : Service::LightBulb {
|
#define MAX_LIGHTS 2
|
||||||
|
|
||||||
int ledPin;
|
|
||||||
SpanCharacteristic *power;
|
|
||||||
|
|
||||||
LED_Service(int ledPin) : Service::LightBulb(){
|
|
||||||
power=new Characteristic::On();
|
|
||||||
this->ledPin=ledPin;
|
|
||||||
pinMode(ledPin,OUTPUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean update(){
|
|
||||||
digitalWrite(ledPin,power->getNewVal());
|
|
||||||
return(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
|
|
||||||
struct invertedLED : Blinkable { // create a child class derived from Blinkable
|
|
||||||
|
|
||||||
int pin; // variable to store the pin number
|
|
||||||
|
|
||||||
invertedLED(int pin) : pin{pin} { // constructor that initializes the pin parameter
|
|
||||||
pinMode(pin,OUTPUT); // set the pin to OUTPUT
|
|
||||||
digitalWrite(pin,HIGH); // set pin HIGH (which is off for an inverted LED)
|
|
||||||
}
|
|
||||||
|
|
||||||
void on() override { digitalWrite(pin,LOW); } // required function on() - sets pin LOW
|
|
||||||
void off() override { digitalWrite(pin,HIGH); } // required function off() - sets pin HIGH
|
|
||||||
int getPin() override { return(pin); } // required function getPin() - returns pin number
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
|
||||||
// homeSpan.setLogLevel(-1);
|
homeSpan.setLogLevel(1);
|
||||||
// homeSpan.setSerialInputDisable(true);
|
homeSpan.enableWebLog(50,"pool.ntp.org","UTC",NULL);
|
||||||
homeSpan.enableOTA();
|
// homeSpan.enableWebLog(50,"pool.ntp.org","UTC","myStatus");
|
||||||
|
// homeSpan.enableWebLog(50,NULL,NULL,NULL);
|
||||||
|
|
||||||
homeSpan.setStatusDevice(new invertedLED(13)); // set Status LED to be a new Blinkable device attached to pin 13
|
homeSpan.begin(Category::Lighting,"HomeSpan Max");
|
||||||
homeSpan.setStatusAutoOff(30);
|
|
||||||
|
|
||||||
homeSpan.begin(Category::Lighting,"HomeSpan LED");
|
new SpanAccessory();
|
||||||
|
|
||||||
new SpanAccessory();
|
|
||||||
new Service::AccessoryInformation();
|
new Service::AccessoryInformation();
|
||||||
new Characteristic::Identify();
|
new Characteristic::Identify();
|
||||||
new LED_Service(13);
|
|
||||||
|
for(int i=0;i<MAX_LIGHTS;i++){
|
||||||
|
new SpanAccessory();
|
||||||
|
new Service::AccessoryInformation();
|
||||||
|
new Characteristic::Identify();
|
||||||
|
char c[30];
|
||||||
|
sprintf(c,"Light-%d",i);
|
||||||
|
new Characteristic::Name(c);
|
||||||
|
new Service::LightBulb();
|
||||||
|
new Characteristic::On(0,false);
|
||||||
|
WEBLOG("Configuring %s",c);
|
||||||
|
}
|
||||||
|
|
||||||
|
new SpanUserCommand('w', " - get web log test",webLogTest); // simulate getting an HTTPS request for weblog
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
|
|
||||||
void loop(){
|
void loop(){
|
||||||
|
|
||||||
homeSpan.poll();
|
homeSpan.poll();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
void webLogTest(const char *dummy){
|
||||||
|
Serial.printf("\n*** In Web Log Test. Starting Custom Web Log Handler\n"); // here is where you would perform any HTTPS initializations
|
||||||
|
homeSpan.getWebLog(webLogHandler,NULL); // this starts the normal weblog with output redirected to the specified handler (below)
|
||||||
|
}
|
||||||
|
|
||||||
|
void webLogHandler(const char *buf, void *args){
|
||||||
|
if(buf!=NULL){
|
||||||
|
Serial.printf("--------\n");
|
||||||
|
Serial.printf("%s",buf); // here is where you would transmit data to the HTTPS connection
|
||||||
|
Serial.printf("********\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Serial.print("*** DONE!\n\n"); // here is where you would close the HTTPS connection
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2023 Gregg E. Berman
|
* Copyright (c) 2023-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2023 Gregg E. Berman
|
* Copyright (c) 2023-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include "PwmPin.h"
|
||||||
|
|
||||||
[[maybe_unused]] static const char* STEPPER_TAG = "StepperControl";
|
[[maybe_unused]] static const char* STEPPER_TAG = "StepperControl";
|
||||||
|
|
||||||
|
|
@ -103,3 +104,12 @@ class StepperControl {
|
||||||
};
|
};
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
|
||||||
|
#include "Stepper_UNIPOLAR.h"
|
||||||
|
#include "Stepper_TB6612.h" // https://www.adafruit.com/product/2448
|
||||||
|
#include "Stepper_A3967.h" // https://www.sparkfun.com/products/12779
|
||||||
|
|
||||||
|
|
||||||
|
struct Stepper_ULN2003A : Stepper_UNIPOLAR {
|
||||||
|
Stepper_ULN2003A(int IN1, int IN2, int IN3, int IN4, std::pair<uint32_t, uint32_t> taskParams = {1,0}) : Stepper_UNIPOLAR(IN1,IN3,IN2,IN4,taskParams){}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2023 Gregg E. Berman
|
* Copyright (c) 2023-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
@ -25,6 +25,8 @@
|
||||||
*
|
*
|
||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
// Implementation of StepperControl for a Sparkfun A3967 EasyDriver Stepper Motor Driver
|
// Implementation of StepperControl for a Sparkfun A3967 EasyDriver Stepper Motor Driver
|
||||||
// Breakout Board (https://www.sparkfun.com/products/12779)
|
// Breakout Board (https://www.sparkfun.com/products/12779)
|
||||||
|
|
||||||
|
|
@ -37,10 +39,6 @@
|
||||||
// disabled (no current / high impedence). The EasyDriver board does NOT support
|
// disabled (no current / high impedence). The EasyDriver board does NOT support
|
||||||
// the short brake mode.
|
// the short brake mode.
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "StepperControl.h"
|
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
|
||||||
struct Stepper_A3967 : StepperControl {
|
struct Stepper_A3967 : StepperControl {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2023 Gregg E. Berman
|
* Copyright (c) 2023-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
@ -25,6 +25,8 @@
|
||||||
*
|
*
|
||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
// Implementation of StepperControl for an Adafruit TB6612 1.2A DC/Stepper Motor Driver
|
// Implementation of StepperControl for an Adafruit TB6612 1.2A DC/Stepper Motor Driver
|
||||||
// Breakout Board (https://www.adafruit.com/product/2448)
|
// Breakout Board (https://www.adafruit.com/product/2448)
|
||||||
|
|
||||||
|
|
@ -44,11 +46,6 @@
|
||||||
// In either configuration the motor outputs can be enabled (current running through the coils)
|
// In either configuration the motor outputs can be enabled (current running through the coils)
|
||||||
// disabled (no current / high impedence) or set to a short brake.
|
// disabled (no current / high impedence) or set to a short brake.
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "StepperControl.h"
|
|
||||||
#include "PwmPin.h"
|
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
|
||||||
struct Stepper_TB6612 : StepperControl {
|
struct Stepper_TB6612 : StepperControl {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,169 @@
|
||||||
|
/*********************************************************************************
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024 Gregg E. Berman
|
||||||
|
*
|
||||||
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Implementation of a basic 4-wire controller for a center-tapped Unipolar Stepper Motor
|
||||||
|
// with two coils (Coil 1 and Coil 2) each having two driving inputs (A and B).
|
||||||
|
//
|
||||||
|
// Requires 4 pins on the ESP32 connected to an appropriate driver circuit, such as a set
|
||||||
|
// of Darlington transitors, that are in turn connected to each of the 4 Stepper Motor wires:
|
||||||
|
//
|
||||||
|
// * Coil 1, Input A (1A)
|
||||||
|
// * Coil 1, Input B (1B)
|
||||||
|
// * Coil 2, Input A (2A)
|
||||||
|
// * Coil 2, Input B (2B)
|
||||||
|
//
|
||||||
|
// When any of the pins are HIGH, the driver circuit should cause current to flow in the corresponding
|
||||||
|
// half of the coil. When the pin is set LOW, the driver circuit should stop the flow of current through
|
||||||
|
// that half of the coil. Supported modes and driving logic are as follows:
|
||||||
|
//
|
||||||
|
// FULL_STEP_ONE_PHASE (4 steps, where current flows only through ONE of the phases of ONE of the coils at each step):
|
||||||
|
//
|
||||||
|
// 1A 2A 1B 2B
|
||||||
|
// Step 1: HI -- -- --
|
||||||
|
// Step 2: -- HI -- --
|
||||||
|
// Step 3: -- -- HI --
|
||||||
|
// Step 4: -- -- -- HI
|
||||||
|
//
|
||||||
|
// FULL_STEP_TWO_PHASE (4 steps, where current flows through ONE of the phases of EACH of the coils at each step):
|
||||||
|
//
|
||||||
|
// 1A 2A 1B 2B
|
||||||
|
// Step 1: HI HI -- --
|
||||||
|
// Step 2: -- HI HI --
|
||||||
|
// Step 3: -- -- HI HI
|
||||||
|
// Step 4: HI -- -- HI
|
||||||
|
//
|
||||||
|
// HALF_STEP (8 steps that alternate between the 4 steps of the FULL_STEP modes above):
|
||||||
|
//
|
||||||
|
// 1A 2A 1B 2B
|
||||||
|
// Step 1: HI -- -- --
|
||||||
|
// Step 2: HI HI -- --
|
||||||
|
// Step 3: -- HI -- --
|
||||||
|
// Step 4: -- HI HI --
|
||||||
|
// Step 5: -- -- HI --
|
||||||
|
// Step 6: -- -- HI HI
|
||||||
|
// Step 7: -- -- -- HI
|
||||||
|
// Step 8: HI -- -- HI
|
||||||
|
|
||||||
|
// NOTE ORDER OF CONSTRUCTOR PARAMETERS: First the two pins that drive the A and B side of Coil 1,
|
||||||
|
// followed by the two pints that drive the A and B side of Coil 2.
|
||||||
|
|
||||||
|
// It does not matter which coil is defined as 1 or 2, nor which side is called A or B, as long as
|
||||||
|
// the first two parameters are for one of the coils and the second two are for the other coil.
|
||||||
|
|
||||||
|
// Note: This driver supports enabling and disabling all current flow, but does NOT support a short brake.
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
struct Stepper_UNIPOLAR : StepperControl {
|
||||||
|
|
||||||
|
int c1A, c1B, c2A, c2B;
|
||||||
|
uint8_t phase, nPhases;
|
||||||
|
double offset;
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
Stepper_UNIPOLAR(int coil1A, int coil1B, int coil2A, int coil2B, std::pair<uint32_t, uint32_t> taskParams = {1,0}) : StepperControl(taskParams.first,taskParams.second) {
|
||||||
|
|
||||||
|
c1A=coil1A;
|
||||||
|
c1B=coil1B;
|
||||||
|
c2A=coil2A;
|
||||||
|
c2B=coil2B;
|
||||||
|
|
||||||
|
pinMode(c1A,OUTPUT);
|
||||||
|
pinMode(c1B,OUTPUT);
|
||||||
|
pinMode(c2A,OUTPUT);
|
||||||
|
pinMode(c2B,OUTPUT);
|
||||||
|
|
||||||
|
setStepType(FULL_STEP_ONE_PHASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
void onEnable() override {
|
||||||
|
setPins();
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
void onDisable() override {
|
||||||
|
digitalWrite(c1A,0);
|
||||||
|
digitalWrite(c1B,0);
|
||||||
|
digitalWrite(c2A,0);
|
||||||
|
digitalWrite(c2B,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
void onStep(boolean direction) override {
|
||||||
|
if(direction)
|
||||||
|
phase=(phase+1)%nPhases;
|
||||||
|
else
|
||||||
|
phase=(phase+nPhases-1)%nPhases;
|
||||||
|
|
||||||
|
setPins();
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
void setPins(){
|
||||||
|
float levelA=cos(phase*TWO_PI/nPhases+offset)*100.0;
|
||||||
|
float levelB=sin(phase*TWO_PI/nPhases+offset)*100.0;
|
||||||
|
digitalWrite(c1A,levelA>0.01);
|
||||||
|
digitalWrite(c1B,levelA<-0.01);
|
||||||
|
digitalWrite(c2A,levelB>0.01);
|
||||||
|
digitalWrite(c2B,levelB<-0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
StepperControl *setStepType(int mode) override {
|
||||||
|
|
||||||
|
switch(mode){
|
||||||
|
case FULL_STEP_ONE_PHASE:
|
||||||
|
phase=0;
|
||||||
|
nPhases=4;
|
||||||
|
offset=0;
|
||||||
|
break;
|
||||||
|
case FULL_STEP_TWO_PHASE:
|
||||||
|
phase=0;
|
||||||
|
nPhases=4;
|
||||||
|
offset=TWO_PI/8.0;
|
||||||
|
break;
|
||||||
|
case HALF_STEP:
|
||||||
|
phase=0;
|
||||||
|
nPhases=8;
|
||||||
|
offset=0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_LOGE(STEPPER_TAG,"Unknown StepType=%d",mode);
|
||||||
|
}
|
||||||
|
return(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||||
*
|
*
|
||||||
* https://github.com/HomeSpan/HomeSpan
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
*
|
*
|
||||||
|
|
@ -28,8 +28,7 @@
|
||||||
// This is a placeholder .ino file that allows you to easily edit the contents of this files using the Arduino IDE,
|
// This is a placeholder .ino file that allows you to easily edit the contents of this files using the Arduino IDE,
|
||||||
// as well as compile and test from this point. This file is ignored when the library is included in other sketches.
|
// as well as compile and test from this point. This file is ignored when the library is included in other sketches.
|
||||||
|
|
||||||
#include "Stepper_TB6612.h" // include the driver for a TB6612 chip
|
#include "StepperControl.h"
|
||||||
#include "Stepper_A3967.h"
|
|
||||||
|
|
||||||
StepperControl *bigMotor;
|
StepperControl *bigMotor;
|
||||||
StepperControl *smallMotor;
|
StepperControl *smallMotor;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/zsh
|
||||||
|
|
||||||
|
grep -B 1000 "AUTOGENERATED_TEXT" ../docs/ServiceList.md > ServiceList.md
|
||||||
|
./makeServices ../src/Characteristics.h ../src/Span.h >> ServiceList.md
|
||||||
|
mv ServiceList.md ../docs/ServiceList.md
|
||||||
|
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
#!/usr/bin/awk -f
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
|
||||||
|
ws="[ \t,();]+" # regexp of separators
|
||||||
|
ltws="^" ws "|" ws "$" # regexp of leading and trailing separators
|
||||||
|
|
||||||
|
nServs=0 # number of Services found
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
split($0,line,"//") # separate line into program and (optional) comment
|
||||||
|
gsub(ltws,"",line[1]) # strip out leading or trailing separators
|
||||||
|
gsub("[ \t]+","",line[1]) # strip out any other spaces
|
||||||
|
|
||||||
|
n=split(line[1],x,ws) # split program portion according to separators
|
||||||
|
|
||||||
|
if(x[1]=="CREATE_SERV"){
|
||||||
|
currentService=x[2]
|
||||||
|
services[nServs++]=currentService
|
||||||
|
uuid[currentService]=x[3]
|
||||||
|
desc[currentService]=line[2] # save optional comment as description of Service
|
||||||
|
nChars[currentService]=0
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(x[1]=="REQ" || x[1]=="OPT"){
|
||||||
|
servChars[currentService,nChars[currentService]]=x[2]
|
||||||
|
servReq[currentService,nChars[currentService]]=(x[1]=="REQ")
|
||||||
|
nChars[currentService]++
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(x[1]=="CREATE_CHAR"){
|
||||||
|
char=x[3]
|
||||||
|
default[char]=x[4]
|
||||||
|
min[char]=x[5]
|
||||||
|
max[char]=x[6]
|
||||||
|
nConstants[char]=n-6 # parse any pre-defined constants
|
||||||
|
value=0 # default starting value of constants
|
||||||
|
for(i=0;i<nConstants[char];i++){
|
||||||
|
v=split(x[i+7],y,"=") # split name of constant from (optional) value
|
||||||
|
constantName[char,i]=y[1]
|
||||||
|
if(v==2) # override value if provided
|
||||||
|
value=y[2]
|
||||||
|
constantValue[char,i]=value
|
||||||
|
if(index(perms[char],"PR") && x[4]==value)
|
||||||
|
defaultMark[char,i]=":heavy_check_mark:"
|
||||||
|
value++
|
||||||
|
}
|
||||||
|
notes[char]=line[2] # save optional comment as notes for Characteristic
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(x[1]=="HAPCHAR"){
|
||||||
|
char=x[2]
|
||||||
|
uuid[char]=x[3]
|
||||||
|
perms[char]=x[4]
|
||||||
|
format[char]=tolower(x[5])
|
||||||
|
static[char]=x[6]
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(x[1]=="SERVICES_GROUP"){
|
||||||
|
group[nServs]=line[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
END {
|
||||||
|
|
||||||
|
printf("\n\n");
|
||||||
|
|
||||||
|
for(i=0;i<nServs;i++){
|
||||||
|
if(group[i]!="")
|
||||||
|
printf("## %s\n",toupper(group[i]))
|
||||||
|
s=services[i]
|
||||||
|
printf("### %s (%s)\n",s,uuid[s])
|
||||||
|
printf("<i>%s</i><br><table>\n",desc[s])
|
||||||
|
printf("<tr><th>Characteristic</th><th>Format</th><th>Perms</th><th>Min</th><th>Max</th><th>Constants/Defaults</th></tr>\n")
|
||||||
|
|
||||||
|
for(j=0;j<nChars[s];j++){
|
||||||
|
char=servChars[s,j]
|
||||||
|
printf("<tr>")
|
||||||
|
printf("<td><b>%s (%s) %s</b><ul><li>%s</li></ul></td>",char,uuid[char],servReq[s,j]?":small_blue_diamond:":"",notes[char])
|
||||||
|
printf("<td align=\"center\">%s</td>",format[char])
|
||||||
|
printf("<td align=\"center\">%s</td>",perms[char])
|
||||||
|
|
||||||
|
if(format[char]!="string")
|
||||||
|
printf("<td align=\"center\">%s</td><td align=\"center\">%s</td>",min[char],max[char])
|
||||||
|
else
|
||||||
|
printf("<td align=\"center\">-</td><td align=\"center\">-</td>")
|
||||||
|
|
||||||
|
if(nConstants[char]>0){
|
||||||
|
printf("<td><ul>")
|
||||||
|
for(k=0;k<nConstants[char];k++)
|
||||||
|
printf("<li><span>%s (%d) </span>%s</li>",constantName[char,k],constantValue[char,k],defaultMark[char,k])
|
||||||
|
printf("</ul></td>")
|
||||||
|
} else {
|
||||||
|
printf("<td align=\"center\">%s</td>",default[char])
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("</tr>\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("</table><br>\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("---\n\n")
|
||||||
|
printf("[↩️](../README.md) Back to the Welcome page\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Loading…
Reference in New Issue