commit
0e26ddbd74
|
|
@ -0,0 +1,110 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 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 "HomeSpan.h"
|
||||
|
||||
// Apple's HomeKit does not provide any native services or characteristics for measuring atmospheric pressure.
|
||||
// However, Eve for HomeKit does support pressure measurements.
|
||||
|
||||
// This brief sketch demonstrates how you can use HomeSpan's Custom Service and Custom Characteristic features
|
||||
// to create a Pressure Sensor Accessory that will be recognized by the Eve for HomeKit App. Note that the
|
||||
// Apple Home App will show this as a "Not Supported" Accessory Tile indicating it cannot be used in the Home App.
|
||||
// However, this does not create any problems or errors in the Home App.
|
||||
|
||||
// Step 1:
|
||||
|
||||
// Use the CUSTOM_SERV macro to create a new service named AtmosphericPressureSensor with
|
||||
// a UUID=E863F00A-079E-48FF-8F27-9C2605A29F52. This new service will be added to HomeSpan's Service namespace
|
||||
// and can be accessed using the fully-qualified name Service::AtmosphericPressureSensor. The UUID specified
|
||||
// will not be recognized by Apple's Home App, but will be recognized by the Eve for HomeKit App. Note you
|
||||
// do NOT enclose either of the parameters in quotes!
|
||||
|
||||
CUSTOM_SERV(AtmosphericPressureSensor, E863F00A-079E-48FF-8F27-9C2605A29F52);
|
||||
|
||||
// Step 2:
|
||||
|
||||
// Use the CUSTOM_CHAR macro to create a new characteristic named AtmosphericPressure with
|
||||
// a UUID=E863F10F-079E-48FF-8F27-9C2605A29F52. This new characteristic will be added to HomeSpan's Characteristic namespace
|
||||
// and can be accessed using the fully-qualified name Characteristic::AtmosphericPressure. The UUID specified will not be
|
||||
// recognized by Apple's Home App, but will be recognized by the Eve for HomeKit App. Note you do NOT enclose any of the
|
||||
// parameters in quotes!
|
||||
//
|
||||
// The meaning of the parmameters are as follows:
|
||||
//
|
||||
// PR+EV: sets permission for "read" and "notify"
|
||||
// FLOAT: sets the format to floating-point decimal number
|
||||
// 1013: sets the default starting value to 1013, which is 1 atm in millibars
|
||||
// 700: sets the default lower range of allowed values to 700 millibars
|
||||
// 1200: sets the default upper range of allowed values to 1200 millibars
|
||||
// false: sets the "static range" flag to false, indicating that users CAN override the default range setRange() if desired
|
||||
|
||||
CUSTOM_CHAR(AtmosphericPressure, E863F10F-079E-48FF-8F27-9C2605A29F52, PR+EV, FLOAT, 1013, 700, 1200, false);
|
||||
|
||||
// Now that AtmosphericPressureSensor and AtmosphericPressure have been created, they can be used just as any other native HomeSpan
|
||||
// Service and Characteristic.
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
struct PressureSensor : Service::AtmosphericPressureSensor { // A standalone Air Pressure Sensor
|
||||
|
||||
Characteristic::AtmosphericPressure pressure; // Eve Air Pressure with range 700-1200 hPa (millibars), where 1 atm=1013 hPa
|
||||
|
||||
PressureSensor() : Service::AtmosphericPressureSensor{} {
|
||||
|
||||
Serial.print("Configuring Air Pressure Sensor"); // initialization message
|
||||
Serial.print("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
void loop(){
|
||||
|
||||
if(pressure.timeVal()>5000) // here we simulate an actual sensor by generating a random pressure reading every 5 seconds
|
||||
pressure.setVal((double)random(900,1100));
|
||||
|
||||
} // end loop
|
||||
|
||||
}; // end PressureSensor
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Sensors,"Eve Air Pressure");
|
||||
|
||||
SPAN_ACCESSORY();
|
||||
new PressureSensor();
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
}
|
||||
|
|
@ -51,14 +51,15 @@
|
|||
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
|
||||
#define NEOPIXEL_RGB_PIN 8
|
||||
#define NEOPIXEL_RGBW_PIN 2
|
||||
#define DOTSTAR_DATA_PIN 0
|
||||
#define DOTSTAR_CLOCK_PIN 1
|
||||
#define NEOPIXEL_RGB_PIN 0
|
||||
#define NEOPIXEL_RGBW_PIN 3
|
||||
#define DOTSTAR_DATA_PIN 7
|
||||
#define DOTSTAR_CLOCK_PIN 2
|
||||
|
||||
#define DEVICE_SUFFIX "-C3"
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "extras/Pixel.h" // include the HomeSpan Pixel class
|
||||
|
||||
|
|
@ -149,6 +150,7 @@ struct DotStar_RGB : Service::LightBulb { // Addressable two-wire RGB LED S
|
|||
pixel=new Dot(dataPin,clockPin); // creates Dot LED on specified pins
|
||||
this->nPixels=nPixels; // save number of Pixels in this LED Strand
|
||||
update(); // manually call update() to set pixel with restored initial values
|
||||
update(); // call second update() a second time - DotStar seems to need to be "refreshed" upon start-up
|
||||
}
|
||||
|
||||
boolean update() override {
|
||||
|
|
@ -164,8 +166,8 @@ struct DotStar_RGB : Service::LightBulb { // Addressable two-wire RGB LED S
|
|||
float hueStep=360.0/nPixels; // step size for change in hue from one pixel to the next
|
||||
|
||||
for(int i=0;i<nPixels;i++)
|
||||
color[i].HSV(h+i*hueStep*p,s*p,100,v*p); // create spectrum of all hues starting with specified Hue; use current-limiting parameter (4th argument) to control overall brightness, instead of PWM
|
||||
|
||||
color[i].HSV(h+i*hueStep,s,100,v*p); // create spectrum of all hues starting with specified Hue; use current-limiting parameter (4th argument) to control overall brightness, instead of PWM
|
||||
|
||||
pixel->set(color,nPixels); // set the colors according to the array
|
||||
|
||||
return(true);
|
||||
|
|
@ -180,56 +182,16 @@ void setup() {
|
|||
|
||||
homeSpan.begin(Category::Lighting,"Pixel LEDS" DEVICE_SUFFIX);
|
||||
|
||||
new SpanAccessory(); // create Bridge
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Name("Pixel LEDS" DEVICE_SUFFIX);
|
||||
new Characteristic::Manufacturer("HomeSpan");
|
||||
new Characteristic::SerialNumber("123-ABC");
|
||||
new Characteristic::Model("Neo/Dot Pixels");
|
||||
new Characteristic::FirmwareRevision("1.0");
|
||||
new Characteristic::Identify();
|
||||
SPAN_ACCESSORY(); // create Bridge (note this sketch uses the SPAN_ACCESSORY() macro, introduced in v1.5.1 --- see the HomeSpan API Reference for details on this convenience macro)
|
||||
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
|
||||
/////////
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Name("Neo RGB");
|
||||
new Characteristic::Manufacturer("HomeSpan");
|
||||
new Characteristic::SerialNumber("123-ABC");
|
||||
new Characteristic::Model("8-LED Strand");
|
||||
new Characteristic::FirmwareRevision("1.0");
|
||||
new Characteristic::Identify();
|
||||
|
||||
SPAN_ACCESSORY("Neo RGB");
|
||||
new NeoPixel_RGB(NEOPIXEL_RGB_PIN,8); // create 8-LED NeoPixel RGB Strand with full color control
|
||||
|
||||
/////////
|
||||
SPAN_ACCESSORY("Neo RGBW");
|
||||
new NeoPixel_RGBW(NEOPIXEL_RGBW_PIN,60); // create 60-LED NeoPixel RGBW Strand with simulated color temperature control
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Name("Neo RGBW");
|
||||
new Characteristic::Manufacturer("HomeSpan");
|
||||
new Characteristic::SerialNumber("123-ABC");
|
||||
new Characteristic::Model("60-LED Strand");
|
||||
new Characteristic::FirmwareRevision("1.0");
|
||||
new Characteristic::Identify();
|
||||
|
||||
new NeoPixel_RGBW(NEOPIXEL_RGBW_PIN,60); // create 60-LED NeoPixel RGBW Strand with simulated color temperature control
|
||||
|
||||
/////////
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Name("Dot RGB");
|
||||
new Characteristic::Manufacturer("HomeSpan");
|
||||
new Characteristic::SerialNumber("123-ABC");
|
||||
new Characteristic::Model("30-LED Strand");
|
||||
new Characteristic::FirmwareRevision("1.0");
|
||||
new Characteristic::Identify();
|
||||
|
||||
new DotStar_RGB(DOTSTAR_DATA_PIN,DOTSTAR_CLOCK_PIN,30); // create 30-LED DotStar RGB Strand displaying a spectrum of colors and using the current-limiting feature of DotStars to create flicker-free dimming
|
||||
SPAN_ACCESSORY("Dot RGB");
|
||||
new DotStar_RGB(DOTSTAR_DATA_PIN,DOTSTAR_CLOCK_PIN,30); // create 30-LED DotStar RGB Strand displaying a spectrum of colors and using the current-limiting feature of DotStars to create flicker-free dimming
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,29 @@
|
|||
/* HomeSpan Remote Control Example */
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 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 "HomeSpan.h" // include the HomeSpan library
|
||||
#include "extras/RFControl.h" // include RF Control Library
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
|
||||
//////////////////////////////////
|
||||
// DEVICE-SPECIFIC SERVICES //
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_Identify : Service::AccessoryInformation {
|
||||
|
||||
int nBlinks; // number of times to blink built-in LED in identify routine
|
||||
SpanCharacteristic *identify; // reference to the Identify Characteristic
|
||||
|
||||
DEV_Identify(const char *name, const char *manu, const char *sn, const char *model, const char *version, int nBlinks) : Service::AccessoryInformation(){
|
||||
|
||||
new Characteristic::Name(name); // create all the required Characteristics with values set based on above arguments
|
||||
new Characteristic::Manufacturer(manu);
|
||||
new Characteristic::SerialNumber(sn);
|
||||
new Characteristic::Model(model);
|
||||
new Characteristic::FirmwareRevision(version);
|
||||
identify=new Characteristic::Identify(); // store a reference to the Identify Characteristic for use below
|
||||
|
||||
this->nBlinks=nBlinks; // store the number of times to blink the LED
|
||||
|
||||
pinMode(homeSpan.getStatusPin(),OUTPUT); // make sure LED is set for output
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
for(int i=0;i<nBlinks;i++){
|
||||
digitalWrite(homeSpan.getStatusPin(),LOW);
|
||||
delay(250);
|
||||
digitalWrite(homeSpan.getStatusPin(),HIGH);
|
||||
delay(250);
|
||||
}
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Gregg E. Berman
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -31,7 +31,6 @@
|
|||
// is controlled by a Servo connected to the ESP32.
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_Identify.h"
|
||||
#include "DEV_DoorsWindows.h"
|
||||
|
||||
void setup() {
|
||||
|
|
@ -41,12 +40,13 @@ void setup() {
|
|||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Bridge #1","HomeSpan","123-ABC","HS Bridge","0.9",3);
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Window Shade","HomeSpan","123-ABC","Shade","0.9",0);
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Window Shade");
|
||||
new DEV_WindowShade(18); // Create a motorized Window Shade with a Servo attached to Pin 18 that controls the Horizontal Tilt of the Shade
|
||||
|
||||
} // end of setup()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,29 @@
|
|||
/* HomeSpan Table Lamp Example */
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 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 "HomeSpan.h" // include the HomeSpan library
|
||||
|
||||
|
|
@ -33,18 +58,10 @@ void setup() {
|
|||
|
||||
new SpanAccessory(); // Table Lamp Accessory
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, with 6 *required* Characteristics
|
||||
new Characteristic::Name("My Table Lamp"); // Name of the Accessory, which shows up on the HomeKit "tiles", and should be unique across Accessories
|
||||
new Characteristic::Manufacturer("HomeSpan"); // Manufacturer of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::SerialNumber("123-ABC"); // Serial Number of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::Model("120-Volt Lamp"); // Model of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::FirmwareRevision("0.9"); // Firmware of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::Identify(); // Provides a hook that allows a HomeKit Client to identify the device
|
||||
|
||||
new Service::HAPProtocolInformation(); // HAP requires every Accessory (except those in a bridge) to implement a Protcol Information Service
|
||||
new Characteristic::Version("1.1.0"); // Set the Version Characteristic to "1.1.0," which is required by HAP
|
||||
|
||||
new TableLamp(17); // instantiate the TableLamp Service (defined below) with lampPin set to 17
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service
|
||||
new Characteristic::Identify(); // HAP requires the Accessory Information Service to include the Identify Characteristic
|
||||
|
||||
new TableLamp(17); // instantiate the TableLamp Service (defined below) with lampPin set to 17
|
||||
|
||||
} // end of setup()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021 Gregg E. Berman
|
||||
* Copyright (c) 2021-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -125,17 +125,7 @@ void setup() {
|
|||
|
||||
homeSpan.begin(Category::Television,"HomeSpan Television");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Name("HomeSpan TV");
|
||||
new Characteristic::Manufacturer("HomeSpan");
|
||||
new Characteristic::SerialNumber("123-ABC");
|
||||
new Characteristic::Model("HomeSpan");
|
||||
new Characteristic::FirmwareRevision("0.1");
|
||||
new Characteristic::Identify();
|
||||
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
SPAN_ACCESSORY();
|
||||
|
||||
// Below we define 10 different InputSource Services using different combinations
|
||||
// of Characteristics to demonstrate how they interact and appear to the user in the Home App
|
||||
|
|
|
|||
|
|
@ -57,6 +57,10 @@
|
|||
* Yes, provided you implement your Web Server using standard ESP32-Arduino libraries, such as `WebServer.h`. See [ProgrammableHub](https://github.com/HomeSpan/ProgrammableHub) for an illustrative example of how to easily integrate a Web Server into HomeSpan. This project also covers various other advanced topics, including TCP slot management, dynamic creation of Accessories, and saving arbitrary data in the ESP32's NVS.
|
||||
* Note *ESP32AsyncWebServer* cannot be used since it requires a TCP stack that is unfortunately incompatible with HomeSpan.
|
||||
|
||||
#### Can you add *custom* Services and Characteristics to HomeSpan?
|
||||
|
||||
* Yes, HomeSpan includes two easy-to-use macros to define your own custom Services and custom Characteristics beyond those specified in HAP-R2. See the [HomeSpan API Reference](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Reference.md) for details and examples demonstrating how to do this. Note that any new Characteristics you create will be *completely ignored* by the Home App. Similarly, any new Services you create will be shown in the Home App on a tile labeled "Not Supported". Apple ***does not*** provide any mechanism to extend the functionality of the Home App itself. However, the place where custom Services and Characteristics can be used is in third-party applications designed for these extra features. For example, the *Eve for HomeKit* App properly handles all the Services and Characteristics defined in HAP-R2, *plus* a variety of additional Services and Characteristics designed explictly for use with Eve products. If you know the UUID codes for these extra Services and Characteristics you can add them to HomeKit and use them within the Eve App just as if they were HAP-R2 Services and Characteristics.
|
||||
|
||||
#### Can HomeSpan be used for commercial devices?
|
||||
|
||||
* No, a MFi license is needed to create commercial devices. HomeSpan was developed using Apple's HAP-R2 specs, which Apple provides for [non-commercial devices that won't be distributed or sold](https://developers.apple.com/homekit/faq/). Though I believe the commercial specifications are functionally the same, there is a slight, but critical, difference in the pairing protocol between HAP-R2 and MFi. Note that when you pair a HomeSpan device (or any device that is based on HAP-R2, such as Apple's HAP-R2 ADK, Espressif's non-commercial ADK, HomeBridge, etc.) the Home App on your iPhone will flag the device as uncertified and require you to grant it permission to proceed with pairing. This warning message about the device being uncertified does not appear on commercial devices, presumably because Apply provides the licensee with a custom MFi authorization code that is recognized by the iPhone.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
# Message Logging
|
||||
|
||||
HomeSpan includes a variety of message logs with different levels of verbosity, as well built-in methods to create your own log messages and web logs.
|
||||
|
||||
## HomeSpan Log Messages
|
||||
|
||||
HomeSpan log messages are typically output directly to the Arduino Serial Monitor with three possible levels of verbosity:
|
||||
|
||||
|Log Level|Output|
|
||||
|---------|------|
|
||||
|Level 0|HomeSpan configuration data and some basic status information|
|
||||
|Level 1|Eveything in Level 0 plus additional and more verbose status messages|
|
||||
|Level 2|Everything im Level 1 plus all HAP communication packages sent to and from the HomeSpan device|
|
||||
|
||||
You can set the *Log Level* in your sketch using the method `homeSpan.setLogLevel(uint8_t level)` as described in the [HomeSpan API Reference](API.md). Level 0 messages are always output; Level 1 messages are only output if the *Log Level* is set to 1 or greater; and Level 2 messages are only output if the *Log Level* is set to to 2. The *Log Level* can also be changed dynamically via the Serial Monitor at any time by typing either the 'L0', 'L1', or 'L2' as described in the [HomeSpan CLI](CLI.md).
|
||||
|
||||
## User-Defined Log Messages
|
||||
|
||||
You can add your own log messages to any sketch using HomeSpan's **LOG0()**, **LOG1()**, and **LOG2()** macros. Messages created with these macros will be output to the Arduino Serial Monitor according the *Log Level* setting described above. Each **LOGn()** macro (where n=\[0,2\]) is available in two flavors depending on the number of arguments specified:
|
||||
|
||||
* `LOGn(val)` - when only one argument is specified, HomeSpan outputs *val* using the standard Arduino `Serial.print(val)` method, which means *val* can be nearly any timvariable type. The downside is you have no control over the format. For example, `int n=255; LOG1(n);` outputs the number "255" to the Arduino Serial Monitor, provided that the *Log Level* is set to 1 or greater.
|
||||
|
||||
* `LOGn(const char *fmt, ...)` - when more than one argument is specified, HomeSpan outputs the message using the ESP32 `Serial.printf(fmt, ...)` method, which allows you to format messages with a variable number of arguments using standard C++ *printf* conventions. For example, `int n=255; LOG2("The value is 0x%X",n);` outputs the message "The value is 0xFF" to the Arduino Serial Monitor, provided that the *Log Level* is set to 2.
|
||||
|
||||
See [Example 9 - MessageLogging](Tutorials.md#example-9---messagelogging) for a tutorial sketch demonstrating these macros.
|
||||
|
||||
## Web Logging
|
||||
|
||||
In addition to logging messages to the Arduino Serial Monitor, HomeSpan can optionally serve a Web Log page at any page address you choose. Since the Web Log is hosted as part of HomeSpan's HAP Server, its base address and port will be the same as that of your device. For example, if your device name is *http<nolink>://homespan-4e8eb8504e59.local* (assuming port 80) and you choose "myLog" as the Web Log page address, it will be hosted at *http<nolink>://homespan-4e8eb8504e59.local/myLog*.
|
||||
|
||||
Also embedded in the HomeSpan's Web Log functionality is the ability to call an NTP time server to set the device clock. This optional feature allows HomeSpan to create clock-based timestamps (e.g. *Sat Apr 16 19:48:41 2022*).
|
||||
|
||||
The HomeSpan Web Log page itself comprises two parts:
|
||||
|
||||
* the top of the page provides HomeSpan-generated status information, such as the name of the device, total uptime since last reboot, and version numbers of the various software components
|
||||
|
||||
* the bottom of the page posts messages you create using the **WEBLOG()** macro. This macro comes only in the *printf*-style form `WEBLOG(const char *fmt, ...)`, similar to the second version of the LOG() macros described above.
|
||||
|
||||
Messages produced with WEBLOG() are *also* echoed to the Arduino Serial Monitor with the same priority as LOG1() messages, meaning they will be output to the Serial Monitor if the *Log Level* is set to 1 or greater. The Web Log page displays messages in reverse-chronological order, supplemented with the following additional items:
|
||||
|
||||
* *Entry Number* - HomeSpan numbers each message, starting with 1 for the first message after rebooting
|
||||
* *Up Time* - relative message time, in the form DDD:HH:MM:SS, starting at 000:00:00:00 after rebooting
|
||||
* *Log Time* - absolute message time, in standard UNIX format, provided that Web Logging has been enabled with an NTP Time Server (see below)
|
||||
* *Client* - the IP Address of the Client connected to HomeSpan at the time the WEBLOG() message was created. Only applicable for messages produced within the `update()` method of a Service. Client is otherwise set to '0.0.0.0' in all other instances
|
||||
* *Message* - the text of the formatted message. For example, `int ledNumber=5; WEBLOG("Request to turn LED %d OFF\n",ledNumber);` would produce the message "Request to turn LED 5 OFF"
|
||||
|
||||
To enable Web Logging (it's turned off by default), call the method `homeSpan.enableWebLog()`, as more fully described in the [HomeSpan API Reference](Reference.md), near the top of your sketch. This method allows you to set:
|
||||
|
||||
* 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 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"
|
||||
|
||||
Additional notes:
|
||||
|
||||
* it is okay to include WEBLOG() messages in your sketch even if Web Logging is *not* enabled. In such cases HomeSpan will not serve a Web Log page, but WEBLOG() messages will still be output to the Arduino Serial Monitor if the *Log Level* is set to 1 or greater
|
||||
* messages are **not** stored in NVS and are thus **not** saved between reboots
|
||||
|
||||
See [Example 19 - WebLog](Tutorials.md#example-19---weblog) for a tutorial sketch demonstrating the use of `homeSpan.enableWebLog()` and the WEBLOG() macro.
|
||||
|
||||
---
|
||||
|
||||
[↩️](README.md) Back to the Welcome page
|
||||
|
||||
|
||||
18
docs/OTA.md
18
docs/OTA.md
|
|
@ -12,17 +12,27 @@ You can change the password for a HomeSpan device from the [HomeSpan CLI](CLI.md
|
|||
|
||||
Note that in in order for OTA to properly operate, your sketch must be compiled with a partition scheme that includes OTA partitions. Partition schemes are found under the *Tools → Partition Scheme* menu of the Arduino IDE. Select a scheme that indicates it supports OTA. Note that schemes labeled "default" usually include OTA partitions. If unsure, try it out. HomeSpan will let you know if it does or does not.
|
||||
|
||||
This is because HomeSpan checks that a sketch has been compiled with OTA partitions if OTA has been enabled for that sketch. If OTA has been enabled but HomeSpan does not find any OTA partitioms, it will indicate it cannot start the OTA Server via a warning message sent to the Serial Monitor immediately after WiFi connectivity has been established. Otherwise it will output a confirmation message indicating the OTA Server has sucessfully started.
|
||||
This is because HomeSpan checks that a sketch has been compiled with OTA partitions if OTA has been enabled for that sketch. If OTA has been enabled but HomeSpan does not find any OTA partitions, it will indicate it cannot start the OTA Server via a warning message sent to the Serial Monitor immediately after WiFi connectivity has been established. Otherwise it will output a confirmation message indicating the OTA Server has sucessfully started.
|
||||
|
||||
### OTA Safe Load
|
||||
|
||||
HomeSpan includes two additional safety checks when using OTA to upload a sketch:
|
||||
|
||||
1. HomeSpan checks to make sure the new sketch being uploaded is also another HomeSpan sketch. If not, HomeSpan will reject the new sketch and report an OTA error back to the Arduino IDE after the new sketch is uploaded, but before the device reboots. Instead, HomeSpan will close the OTA connection and resume normal operations based on the existing sketch without rebooting. The purpose of this safety check is to prevent you from accidentally uploading a non-HomeSpan sketch onto a remote device, making it impossible for you to re-upload the correct sketch without retreiving the remote device and connecting to you computer via the serial port.
|
||||
|
||||
1. After a successful upload of a new HomeSpan sketch via OTA, HomeSpan will check that the new HomeSpan sketch just loaded *also* has OTA enabled. This check occurs after HomeSpan is rebooted with the new sketch. If HomeSpan does not find OTA enabled, it will mark the current partition as invalid and reboot the device, causing the device to "roll back" to the previous version of the sketch that had OTA enabled. The purpose of this safety check is to ensure you do not use OTA to upload a new HomeSpan sketch to a remote device, but failed to enable OTA in the new HomeSpan sketch. If you did this you would be locked out of making any further updated via OTA and would instead need to retreive the remote device and connect it to your computer via the serial port.
|
||||
|
||||
Note that these check are *only* applicable when uploading sketches via OTA. They are ignored whenever sketches are uploaded via the serial port. Also, though these safety checks are enabled by default, they can be disabled when you first enable OTA by setting the second (optional) argument to *false* as such: `homeSpan.enableOTA(..., false)`. See the API for details.
|
||||
|
||||
### OTA Tips and Tricks
|
||||
|
||||
* The name of the device HomeSpan uses for OTA is the same as the name you assigned in your call to `homeSpan.begin()`. If you have multiple devices you intend to maintain with OTA, use `homeSpan.begin()` to give them each different names so you can tell them apart when selecting which one to connect to from the Arduino IDE.
|
||||
|
||||
* Use the `homeSpan.setSketchVersion()` method to set a version for your sketch (see the [HomeSpan API](Reference.md) for details). If specified, HomeSpan will include the sketch version as part of its HAP MDNS broadcast. This allows you determine which version of a sketch is running on a remote HomeSpan device, even if you can't plug it into a serial port for use with the Arduino Serial Monitor. In addition to the sketch version, HomeSpan also includes two other fields in its MDNS broadcast: the version number of the HomeSpan *library* used to compile the sketch, and a field indicating whether or not OTA is enabled for the sketch.
|
||||
* Use the `homeSpan.setSketchVersion()` method to set a version for your sketch (see the [HomeSpan API](Reference.md) for details). If specified, HomeSpan will include the sketch version as part of its HAP MDNS broadcast. This allows you determine which version of a sketch is running on a remote HomeSpan device, even if you can't plug it into a serial port for use with the Arduino Serial Monitor. In addition to the sketch version, HomeSpan also includes other fields in its MDNS broadcast that are useful in identifying the device: the version number of the HomeSpan *library* used to compile the sketch, a field indicating whether or not OTA is enabled for the sketch, the version number of the Arduino-ESP32 library used when compiling, and the type of board (e.g. *feather_esp32*).
|
||||
|
||||
* If a sketch you've uploaded with OTA does not operate as expected, you can continue making modifications to the code and re-upload again. Or, you can upload a prior version that was working properly. However, this assumes that the sketch you uploaded does not have major problems, such as causing a kernel panic that leads to an endless cycle of device reboots. If this happens, HomeSpan won't be able to run the OTA Server code, and further OTA updates will *not* ne possible. Instead, you'll have to connect the device through a serial port to upload a new, working sketch. **For this reason you should always fully test out a new sketch on a local device connected to your computer *before* uploading it to a remote, hard-to-access device via OTA.**
|
||||
* If a sketch you've uploaded with OTA does not operate as expected, you can continue making modifications to the code and re-upload again. Or, you can upload a prior version that was working properly. However, the Safe Load features described above cannot protect against a HomeSpan sketch that has major run-time problems, such as causing a kernel panic that leads to an endless cycle of device reboots. If this happens, HomeSpan won't be able to run the OTA Server code, and further OTA updates will *not* be possible. Instead, you'll have to connect the device through a serial port to upload a new, working sketch. **For this reason you should always fully test out a new sketch on a local device connected to your computer *before* uploading it to a remote, hard-to-access device via OTA.**
|
||||
|
||||
* The ESP32 itself supports "automated" rollbacks that are designed to restore a device with a previously-working sketch if the latest sketch causes a reboot before being able to set a self-test flag verifiying the code is operating correctly. However, the version of the ESP32-IDF library (3.2.3) used by the latest version of the Arduino-ESP32 Library (1.0.4, at the time of this posting) does not support this feature.
|
||||
* Note that though the ESP IDF supports "automated" rollbacks that are designed to solve the problem of endless reboots after a bad upload, this feature is *not* enabled in the latest version of the Arudino-ESP32 library (2.0.2 at the time of this posting).
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ As you can see, you do not need to name any objects, or specify any HAP paramete
|
|||
|
||||
> :heavy_check_mark: HomeSpan has extensive error checking. At start-up, HomeSpan will validate the configuration of the HAP Accessory Attribute Database you instantiated to ensure that every Accessory has all the required Services, and that each Service has all its required Characteristics. If HomeSpan finds an Accessory is missing a required Service, a Service is missing a required Characteristic, or a Characteristic that is neither required nor optional has been added to a Service that does not support that Characteristic, HomeSpan will report these errors and halt the program.
|
||||
|
||||
In fact, if you tried to run the above sketch you would find it failed to validate. That's because each Accessory is missing two required Services - the HAP Accessory Information Service, and the HAP Protcol Information Service. See the [Tutorials](Tutorials.md) for full completed and valid configurations that include these required HAP Services, such as this complete, working example for a simple table lamp:
|
||||
In fact, if you tried to run the above sketch you would find it failed to validate. That's because each Accessory is missing a required Service and Characteristic - the HAP Accessory Information Service and the Identify Characteristic. See the [Tutorials](Tutorials.md) for a variety of complete and valid examples that include all required HAP elements, such as this sketch for a simple table lamp:
|
||||
|
||||
```C++
|
||||
/* HomeSpan Table Lamp Example */
|
||||
|
|
@ -124,17 +124,9 @@ void setup() {
|
|||
|
||||
new SpanAccessory(); // Table Lamp Accessory
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, with 6 *required* Characteristics
|
||||
new Characteristic::Name("My Table Lamp"); // Name of the Accessory, which shows up on the HomeKit "tiles", and should be unique across Accessories
|
||||
new Characteristic::Manufacturer("HomeSpan"); // Manufacturer of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::SerialNumber("123-ABC"); // Serial Number of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::Model("120-Volt Lamp"); // Model of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::FirmwareRevision("0.9"); // Firmware of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::Identify(); // Provides a hook that allows a HomeKit Client to identify the device
|
||||
|
||||
new Service::HAPProtocolInformation(); // HAP requires every Accessory (except those in a bridge) to implement a Protcol Information Service
|
||||
new Characteristic::Version("1.1.0"); // Set the Version Characteristic to "1.1.0," which is required by HAP
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service
|
||||
new Characteristic::Identify(); // HAP requires the Accessory Information Service to include the Identify Characteristic
|
||||
|
||||
new Service::LightBulb(); // Create the Light Bulb Service
|
||||
new Characteristic::On(); // Characteristic that stores that state of the light bulb: ON or OFF
|
||||
|
||||
|
|
@ -263,17 +255,9 @@ void setup() {
|
|||
|
||||
new SpanAccessory(); // Table Lamp Accessory
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, with 6 *required* Characteristics
|
||||
new Characteristic::Name("My Table Lamp"); // Name of the Accessory, which shows up on the HomeKit "tiles", and should be unique across Accessories
|
||||
new Characteristic::Manufacturer("HomeSpan"); // Manufacturer of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::SerialNumber("123-ABC"); // Serial Number of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::Model("120-Volt Lamp"); // Model of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::FirmwareRevision("0.9"); // Firmware of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::Identify(); // Provides a hook that allows a HomeKit Client to identify the device
|
||||
|
||||
new Service::HAPProtocolInformation(); // HAP requires every Accessory (except those in a bridge) to implement a Protcol Information Service
|
||||
new Characteristic::Version("1.1.0"); // Set the Version Characteristic to "1.1.0," which is required by HAP
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service
|
||||
new Characteristic::Identify(); // HAP requires the Accessory Information Service to include the Identify Characteristic
|
||||
|
||||
new TableLamp(17); // instantiate the TableLamp Service (defined below) with lampPin set to 17
|
||||
|
||||
} // end of setup()
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ HomeSpan is fully compatible with both Versions 1 and 2 of the [Arduino-ESP32 Bo
|
|||
* Integrated Push Button functionality supporting single, double, and long presses
|
||||
* Integrated access to the ESP32's on-chip Remote Control peripheral for easy generation of IR and RF signals
|
||||
* Dedicated classes to control one- and two-wire addressable RGB and RGBW LEDs and LED strips
|
||||
* Integrated Web Log for user-defined log messages
|
||||
* Extensively-commented Tutorial Sketches taking you from the very basics of HomeSpan through advanced HomeKit topics
|
||||
* Additional examples and projects showcasing real-world implementations of HomeSpan
|
||||
* A complete set of documentation explaining every aspect of the HomeSpan API
|
||||
|
|
@ -44,24 +45,34 @@ HomeSpan is fully compatible with both Versions 1 and 2 of the [Arduino-ESP32 Bo
|
|||
* Launch the WiFi Access Point
|
||||
* A standalone, detailed End-User Guide
|
||||
|
||||
## ❗Latest Update - HomeSpan 1.5.0 (2/20/2022)
|
||||
## ❗Latest Update - HomeSpan 1.5.1 (4/17/2022)
|
||||
|
||||
* **New integrated library to control one- and two-wire Addressable RGB and RGBW LEDs and LED strips!**
|
||||
* Adds two new class:
|
||||
* **Pixel()** for control of one-wire RGB and RGBW LEDs and LED strips, such as this [NeoPixel RGBW LED](https://www.adafruit.com/product/2759)
|
||||
* **Dot()** for control of two-wire RGB LEDs and LED strips, such as this [DotStar RGB Strip](https://www.adafruit.com/product/2241)
|
||||
|
||||
* See [HomeSpan Pixels](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Pixel.md) for full details, including a detailed tutorial sketch demonstrating different ways of using the **Pixel()** and **Dot()** classes, as well an advanced HomeSpan "HolidayLights" Project that shows how to develop custom special effects!
|
||||
* **New Web Logging functionality**
|
||||
* HomeSpan can now host a Web Log page for message logging
|
||||
* New WEBLOG() macro makes is easy to create user-defined log messages
|
||||
* Provides for the optional use of an NTP Time Server to set the device clock so all messages can be properly timestamped
|
||||
* See [HomeSpan Message Logging](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Logging.md) for full details
|
||||
|
||||
* **Increased the maximum number of simultaneous Controller connections from 8 to 14 (for Arduino-ESP32 version 2.0.1 and later)**
|
||||
* Added new method `reserveSocketConnection(uint8_t nSockets)` to the global homeSpan object that allows for better management of custom connections
|
||||
* Deprecated older `setMaxConnections(uint8_t nCon)` method
|
||||
* **New *printf*-style formatting for LOG() macros**
|
||||
* Adds variadic forms of the LOG0(), LOG1(), and LOG2() macros so they can be used in the same manner as a standard C printf function
|
||||
* Greatly simplifies the creation of log messages
|
||||
* See [HomeSpan Message Logging](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Logging.md) for full details
|
||||
|
||||
* **New CUSTOM_SERV() macro**
|
||||
* Allows for the creation of Custom Services
|
||||
* Can be used in conjunction with the existing CUSTOM_CHAR() macro to produce Services beyond those provided in HAP-R2
|
||||
* Includes a fully worked example of a custom [Pressure Sensor Accessory](https://github.com/HomeSpan/HomeSpan/blob/master/Other%20Examples/CustomService) that is recognized by *Eve for HomeKit*
|
||||
* See [HomeSpan API Reference](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Reference.md) for details
|
||||
|
||||
* **New "Safe-Load" mode for OTA updates**
|
||||
* HomeSpan can check to make sure the new sketch being uploaded via OTA is another HomeSpan sketch. If not, the upload fails
|
||||
* Upon rebooting after an OTA update, HomeSpan checks to ensure that OTA is enabled in the updated sketch. If not, HomeSpan rolls back to the previous version of the sketch
|
||||
* See [HomeSpan OTA](https://github.com/HomeSpan/HomeSpan/blob/master/docs/OTA.md) for full details
|
||||
|
||||
* **Additional updates include:**
|
||||
* Added new methods `setDescription(const char *desc)` and `setUnit(const char *unit)` to SpanCharacteristic. Useful when creating and working with Custom Characteristics
|
||||
* Added new method `setStatusAutoOff(uint16_t duration)` to the global homeSpan object. Causes the Status LED (if used) to automatically turn off after *duration* seconds. Very handy for devices located in bedrooms or TV rooms!
|
||||
* Added new method `setPairCallback(func)` to the global homeSpan object. Allows you to create custom actions whenever HomeSpan pairs or subsequently unpairs the device to the Home App
|
||||
* Added new method `deleteStoredValues()` to the global homeSpan object. Provides a programmatic way of deleting the value settings of all Characteristics stored in the NVS
|
||||
* a new (optional) argument to `SpanUserCommand()` that allows for passing a pointer to any arbitrary data structure
|
||||
* a new SPAN_ACCESSORY() macro that expands to a common snippet of code often used when creating Accessories
|
||||
* refreshed and streamlined example Tutorials, and fully reworked Examples 7 and 11, to best conform with Home App behavior under iOS 15.4
|
||||
|
||||
See [Releases](https://github.com/HomeSpan/HomeSpan/releases) for details on all changes and bug fixes included in this update.
|
||||
|
||||
|
|
@ -83,6 +94,7 @@ HomeSpan includes the following documentation:
|
|||
* [HomeSpan RFControl](https://github.com/HomeSpan/HomeSpan/blob/master/docs/RMT.md) - easy generation of RF and IR Remote Control signals using the ESP32's on-chip RMT peripheral
|
||||
* [HomeSpan Pixels](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Pixels.md) - integrated control of addressable one- and two-wire RGB and RGBW LEDs and LED strips
|
||||
* [HomeSpan Television Services](https://github.com/HomeSpan/HomeSpan/blob/master/docs/TVServices.md) - how to use HomeKit's undocumented Television Services and Characteristics
|
||||
* [HomeSpan Message Logging](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Logging.md) - how to generate log messages for display on the Arduino Serial Monitor as well as optionally posted to an integrated Web Log page
|
||||
* [HomeSpan Projects](https://github.com/topics/homespan) - real-world applications of the HomeSpan Library
|
||||
* [HomeSpan FAQ](https://github.com/HomeSpan/HomeSpan/blob/master/docs/FAQ.md) - answers to frequently-asked questions
|
||||
|
||||
|
|
|
|||
|
|
@ -56,10 +56,11 @@ The following **optional** `homeSpan` methods override various HomeSpan initiali
|
|||
|
||||
* `void setLogLevel(uint8_t level)`
|
||||
* sets the logging level for diagnostic messages, where:
|
||||
* 0 = top-level status messages only (default),
|
||||
* 1 = all status messages, and
|
||||
* 2 = all status messages plus all HAP communication packets to and from the HomeSpan device
|
||||
* this parameter can also be changed at runtime via the [HomeSpan CLI](CLI.md)
|
||||
* 0 = top-level HomeSpan status messages, and any messages output by the user using `Serial.print()` or `Serial.printf()` (default)
|
||||
* 1 = all HomeSpan status messages, and any `LOG1()` messages specified in the sketch by the user
|
||||
* 2 = all HomeSpan status messages plus all HAP communication packets to and from the HomeSpan device, as well as all `LOG1()` and `LOG2()` messages specified in the sketch by the user
|
||||
* note the log level can also be changed at runtime with the 'L' command via the [HomeSpan CLI](CLI.md)
|
||||
* see [Message Logging](Logging.md) for complete details
|
||||
|
||||
* `void reserveSocketConnections(uint8_t nSockets)`
|
||||
* reserves *nSockets* network sockets for uses **other than** by the HomeSpan HAP Server for HomeKit Controller Connections
|
||||
|
|
@ -88,12 +89,13 @@ The following **optional** `homeSpan` methods override various HomeSpan initiali
|
|||
|
||||
The following **optional** `homeSpan` methods enable additional features and provide for further customization of the HomeSpan environment. Unless otherwise noted, calls **should** be made before `begin()` to take effect:
|
||||
|
||||
* `void enableOTA(boolean auth=true)`
|
||||
* `void enableOTA(boolean auth=true, boolean safeLoad=true)`
|
||||
* enables [Over-the-Air (OTA) Updating](OTA.md) of a HomeSpan device, which is otherwise disabled
|
||||
* HomeSpan OTA requires an authorizing password unless *auth* is specified and set to *false*
|
||||
* the default OTA password for new HomeSpan devices is "homespan-ota"
|
||||
* this can be changed via the [HomeSpan CLI](CLI.md) using the 'O' command
|
||||
* note enabling OTA reduces the number of HAP Controller Connections by 1
|
||||
* OTA Safe Load will be enabled by default unless the second argument is specified and set to *false*. HomeSpan OTA Safe Load checks to ensure that sketches uploaded to an existing HomeSpan device are themselves HomeSpan sketches, and that they also have OTA enabled. See [HomeSpan OTA Safe Load](OTA.md#ota-safe-load) for details
|
||||
|
||||
* `void enableAutoStartAP()`
|
||||
* enables automatic start-up of WiFi Access Point if WiFi Credentials are **not** found at boot time
|
||||
|
|
@ -146,7 +148,20 @@ The following **optional** `homeSpan` methods enable additional features and pro
|
|||
* `const char *getSketchVersion()`
|
||||
* 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
|
||||
|
||||
|
||||
* `void 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:
|
||||
* *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, default 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
|
||||
* *logURL* - the URL of the log page for this device. If unspecified, defaults to "status"
|
||||
* 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 10 seconds for a response. If no response is received after the 10-second timeout period, HomeSpan assumes the server is unreachable and skips the clock-setting procedure. Use `setTimeServerTimeout()` to re-configure the 10-second timeout period to another value
|
||||
* see [Message Logging](Logging.md) for complete details
|
||||
|
||||
* `void setTimeServerTimeout(uint32_t tSec)`
|
||||
* changes the default 10-second timeout period HomeSpan uses when `enableWebLog()` tries set the device clock from an internet time server to *tSec* seconds
|
||||
|
||||
## *SpanAccessory(uint32_t aid)*
|
||||
|
||||
Creating an instance of this **class** adds a new HAP Accessory to the HomeSpan HAP Database.
|
||||
|
|
@ -175,12 +190,14 @@ This is a **base class** from which all HomeSpan Services are derived, and shoul
|
|||
The following methods are supported:
|
||||
|
||||
* `SpanService *setPrimary()`
|
||||
* specifies that this is the primary Service for the Accessory. Returns a pointer to the Service itself so that the method can be chained during instantiation.
|
||||
* specifies that this is the primary Service for the Accessory. Returns a pointer to the Service itself so that the method can be chained during instantiation
|
||||
* example: `(new Service::Fan)->setPrimary();`
|
||||
* note though this functionality is defined by Apple in HAP-R2, it seems to have been deprecated and no longer serves any purpose or has any affect on the Home App
|
||||
|
||||
* `SpanService *setHidden()`
|
||||
* specifies that this is hidden Service for the Accessory. Returns a pointer to the Service itself so that the method can be chained during instantiation.
|
||||
* note this does not seem to have any affect on the Home App. Services marked as hidden still appear as normal
|
||||
* example: `(new Service::Fan)->setHidden();`
|
||||
* note though this functionality is defined by Apple in HAP-R2, it seems to have been deprecated and no longer serves any purpose or has any affect on the Home App
|
||||
|
||||
* `SpanService *addLink(SpanService *svc)`
|
||||
* adds *svc* as a Linked Service. Returns a pointer to the calling Service itself so that the method can be chained during instantiation.
|
||||
|
|
@ -326,43 +343,57 @@ HomeSpan automatically calls the `button(int pin, int pressType)` method of a Se
|
|||
|
||||
HomeSpan will report a warning, but not an error, during initialization if the user had not overridden the virtual button() method for a Service contaning one or more Buttons; triggers of those Buttons will simply ignored.
|
||||
|
||||
## *SpanUserCommand(char c, const char \*s, void (\*f)(const char \*v))*
|
||||
### *SpanUserCommand(char c, const char \*desc, void (\*f)(const char \*buf [,void \*obj]) [,void \*userObject])*
|
||||
|
||||
Creating an instance of this **class** adds a user-defined command to the HomeSpan Command-Line Interface (CLI), where:
|
||||
|
||||
* *c* is the single-letter name of the user-defined command
|
||||
* *s* is a description of the user-defined command that is displayed when the user types '?' into the CLI
|
||||
* *f* is a pointer to a user-defined function that is called when the command is invoked. This function must be of the form `void f(const char *v)`, where *v* points to all characters typed into the CLI, beginning with the single-letter command name *c*.
|
||||
* *desc* is a description of the user-defined command that is displayed when the user types '?' into the CLI
|
||||
* *f* is a pointer to a user-defined function that is called when the command is invoked. Allowable forms for *f* are:
|
||||
1. `void f(const char *buf)`, or
|
||||
1. `void f(const char *buf, void *obj)`
|
||||
* *userObject* is a pointer to an arbitrary object HomeSpan passes to the function *f* as the second argument when the second form of *f* is used. Note it is an error to include *userObject* when the first form of *f* is used, and it is similarly an error to exclude *userObject* when the second form of *f* is used
|
||||
|
||||
To invoke this command from the CLI, preface the single-letter name *c* with '@'. This allows HomeSpan to distinguish user-defined commands from its built-in commands. For example, `new SpanUserCommand('s', "save current configuration",saveConfig)` would add a new command '@s' to the CLI with description "save current configuration" that will call the user-defined function `void saveConfig(const char *v)` when invoked. The argument *v* points to an array of all characters typed into the CLI after the '@'. This allows the user to pass arguments from the CLI to the user-defined function. For example, typing '@s123' into the CLI sets *v* to "s123" when saveConfig is called.
|
||||
To invoke your custom command from the CLI, preface the single-letter name *c* with '@'. This allows HomeSpan to distinguish user-defined commands from its built-in commands. For example,
|
||||
|
||||
```C++
|
||||
new SpanUserCommand('s', "save current configuration", saveConfig);
|
||||
...
|
||||
void saveConfig(const char *buf){ ... };
|
||||
```
|
||||
|
||||
would add a new command '@s' to the CLI with description "save current configuration" that will call the user-defined function `void saveConfig(const char *buf)` when invoked. The argument *buf* points to an array of all characters typed into the CLI after the '@'. This allows the user to pass arguments from the CLI to the user-defined function. For example, typing '@s123' into the CLI sets *buf* to "s123" when saveConfig is called.
|
||||
|
||||
In the second form of the argument, HomeSpan will pass an additional object to your function *f*. For example,
|
||||
|
||||
```C++
|
||||
struct myConfigurations[10];
|
||||
new SpanUserCommand('s', "<n> save current configuration for specified index, n", saveConfig, myConfigurations);
|
||||
...
|
||||
void saveConfig(const char *buf, void *obj){ ... do something with myConfigurations ... };
|
||||
```
|
||||
|
||||
might be used to save all the elements in *myArray* when called with just the '@s' command, and perhaps save only one element based on an index added to the command, such as '@s34' to save element 34 in *myArray*. It is up to the user to create all necessary logic within the function *f* to parse and process the full command text passed in *buf*, as well as act on whatever is being passed via *obj.
|
||||
|
||||
To create more than one user-defined command, simply create multiple instances of SpanUserCommand, each with its own single-letter name. Note that re-using the same single-letter name in an instance of SpanUserCommand over-rides any previous instances using that same letter.
|
||||
|
||||
## User Macros
|
||||
## Custom Characteristics and Custom Services Macros
|
||||
|
||||
### *#define REQUIRED VERSION(major,minor,patch)*
|
||||
### *CUSTOM_CHAR(name,uuid,perms,format,defaultValue,minValue,maxValue,staticRange)*
|
||||
### *CUSTOM_CHAR_STRING(name,uuid,perms,defaultValue)*
|
||||
|
||||
If REQUIRED is defined in the main sketch *prior* to including the HomeSpan library with `#include "HomeSpan.h"`, HomeSpan will throw a compile-time error unless the version of the library included is equal to, or later than, the version specified using the VERSION macro. Example:
|
||||
|
||||
```C++
|
||||
#define REQUIRED VERSION(1,3,0) // throws a compile-time error unless HomeSpan library used is version 1.3.0 or later
|
||||
#include "HomeSpan.h"
|
||||
```
|
||||
### *#define CUSTOM_CHAR(name,uuid,perms,format,defaultValue,minValue,maxValue,staticRange)*
|
||||
### *#define CUSTOM_CHAR_STRING(name,uuid,perms,defaultValue)*
|
||||
|
||||
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. Parameters are as follows (note that quotes should NOT be used in any of the macro parameters, except for defaultValue):
|
||||
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. 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
|
||||
* *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
|
||||
* *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 or DATA formats. Not applicable for Strings-based Characteristics
|
||||
* *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 or DATA formats). Not applicable for Strings-based Characteristics
|
||||
* *defaultValue* - specifies the default value of the Characteristic if not defined during instantiation
|
||||
* *minValue* - specifies the default minimum range for a valid value, which may be able to be overriden by a call to `setRange()`. Not applicable for Strings-based Characteristics
|
||||
* *minValue* - specifies the default minimum range for a valid value, which may be able to be overriden by a call to `setRange()`. Not applicable for Strings-based Characteristics
|
||||
* *staticRange* - set to *true* if *minValue* and *maxValue* are static and cannot be overridden with a call to `setRange()`. Set to *false* if calls to `setRange()` are allowed. Not applicable for Strings-based Characteristics
|
||||
|
||||
As an example, the first line below creates a custom Characteristic named "Voltage" with a UUID code that is recognized by Eve for HomeKit. The parameters show that the Characteristic is read-only (PR) and notifications are enabled (EV). The default range of allowed values is 0-240, with a default of 120. The range *can* be overridden by subsequent calls to `setRange()`. The second line below creates a custom read-only String-based Characteristic:
|
||||
As an example, the first line below creates a custom Characteristic named "Voltage" with a UUID code that is recognized by the *Eve for HomeKit* app. The parameters show that the Characteristic is read-only (PR) and notifications are enabled (EV). The default range of allowed values is 0-240, with a default of 120. The range *can* be overridden by subsequent calls to `setRange()`. The second line below creates a custom read-only String-based Characteristic:
|
||||
|
||||
```C++
|
||||
CUSTOM_CHAR(Voltage, E863F10A-079E-48FF-8F27-9C2605A29F52, PR+EV, UINT16, 120, 0, 240, false);
|
||||
|
|
@ -378,9 +409,44 @@ new Service::LightBulb();
|
|||
|
||||
Note that Custom Characteristics must be created prior to calling `homeSpan.begin()`
|
||||
|
||||
> Advanced Tip: When presented with an unrecognized Custom Characteristic, Eve for HomeKit helpfully displays a *generic control* allowing you to interact with any Custom Characteristic you create in HomeSpan. However, since Eve does not recognize the Characteristic, it will only render the generic control if the Characteristic includes a **description** field, which you can add to any Characteristic using the `setDescription()` method described above. You may also want to use `setUnit()` and `setRange()` so that the Eve App displays a control with appropriate ranges for your Custom Characteristic.
|
||||
> Advanced Tip: When presented with an unrecognized Custom Characteristic, *Eve for HomeKit* helpfully displays a *generic control* allowing you to interact with any Custom Characteristic you create in HomeSpan. However, since Eve does not recognize the Characteristic, it will only render the generic control if the Characteristic includes a **description** field, which you can add to any Characteristic using the `setDescription()` method described above. You may also want to use `setUnit()` and `setRange()` so that the Eve App displays a control with appropriate ranges for your Custom Characteristic.
|
||||
|
||||
---
|
||||
### *CUSTOM_SERV(name,uuid)*
|
||||
|
||||
Creates a custom Service for use with third-party applications (such as *Eve for HomeKit*). Custom Services will be displayed in the native Apple Home App with a Tile labeled "Not Supported", but otherwise the Service will be safely ignored by the Home App. Parameters are as follows (note that quotes should NOT be used in either of the macro parameters):
|
||||
|
||||
* *name* - the name of the custom Service. This will be added to the Service namespace so that it is accessed the same as any HomeSpan Service. For example, if *name*="Vent", HomeSpan would recognize `Service::Vent` as a new service class
|
||||
* *uuid* - the UUID of the Service 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
|
||||
|
||||
Custom Services may contain a mix of both Custom Characteristics and standard HAP Characteristics, though since the Service itself is custom, the Home App will ignore the entire Service even if it contains some standard HAP Characterstics. Note that Custom Services must be created prior to calling `homeSpan.begin()`
|
||||
|
||||
A fully worked example showing how to use both the ***CUSTOM_SERV()*** and ***CUSTOM_CHAR()*** macros to create a Pressure Sensor Accessory that is recognized by *Eve for HomeKit* can be found in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → CustomService*](../Other%20Examples/CustomService).
|
||||
|
||||
## Other Macros
|
||||
|
||||
### *SPAN_ACCESSORY()* and *SPAN_ACCESSORY(NAME)*
|
||||
|
||||
A "convenience" macro that implements the following very common code snippet used when creating Accessories. The last line is only included if *NAME* (a C-style string) has been included as an argument to the macro:
|
||||
|
||||
```C++
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name(NAME); // included only in the second form of the macro
|
||||
```
|
||||
|
||||
## User-Definable Macros
|
||||
|
||||
### *#define REQUIRED VERSION(major,minor,patch)*
|
||||
|
||||
If REQUIRED is defined in the main sketch *prior* to including the HomeSpan library with `#include "HomeSpan.h"`, HomeSpan will throw a compile-time error unless the version of the library included is equal to, or later than, the version specified using the VERSION macro. Example:
|
||||
|
||||
```C++
|
||||
#define REQUIRED VERSION(1,3,0) // throws a compile-time error unless HomeSpan library used is version 1.3.0 or later
|
||||
#include "HomeSpan.h"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Deprecated functions (available for backwards compatibility with older sketches):
|
||||
|
||||
|
|
|
|||
|
|
@ -176,6 +176,22 @@ Additionally, when first starting up, HomeSpan begins by validating the device's
|
|||
|Volume|uint8_t|0|
|
||||
|WaterLevel|double|0|
|
||||
|
||||
|
||||
### HAP Format Codes (HAP-R2 Table 6-5)
|
||||
|
||||
|HAP-R2 Format Code|HomeSpan C++ Type|
|
||||
|------------------|-----------------|
|
||||
|BOOL|boolean|
|
||||
|UINT8|uint8_t|
|
||||
|UINT16|uint16_t|
|
||||
|UINT32|uint32_t|
|
||||
|UINT64|uint64_t|
|
||||
|INT|int|
|
||||
|FLOAT|double|
|
||||
|STRING|char \*|
|
||||
|TLV8|(not implemented)|
|
||||
|DATA|(not implemented)|
|
||||
|
||||
---
|
||||
|
||||
[↩️](README.md) Back to the Welcome page
|
||||
|
|
|
|||
|
|
@ -39,10 +39,8 @@ Example 6 changes Example 5 so that LED #2 is now dimmable, instead of just on/o
|
|||
* setting the brightness of an LED using the LedPin `set()` method
|
||||
* storing similar derived Service classes in the same \*.h file for ease of use
|
||||
|
||||
### [Example 7 - IdentifyRoutines](../examples/07-IdentifyRoutines)
|
||||
Example 7 uses the encapsulation techniques illustrated in Examples 5 and 6 to derive an easier-to-use Identify Service from HomeSpan's AccessoryInformation Service. The example includes the implementation of an `update()` method that responds to HomeKit requests writing to the Identify Characteristic. New HomeSpan API topics covered in this example include:
|
||||
|
||||
* storing dissimilar derived Service classes in the different \*.h files for better portability
|
||||
### [Example 7 - AccessoryNames](../examples/07-AccessoryNames)
|
||||
Example 7 demonstrates how the names of two LED Accessories created in Example 6 can be changed from the defaults generated by the Home App to something more useful and customized.
|
||||
|
||||
### [Example 8 - Bridges](../examples/08-Bridges)
|
||||
Example 8 is functionally identical to Example 7, except that instead of defining two Accessories (one for the on/off LED and one for the dimmable LED), we define three Accessories, where the first acts as a HomeKit Bridge.
|
||||
|
|
@ -50,7 +48,7 @@ Example 8 is functionally identical to Example 7, except that instead of definin
|
|||
### [Example 9 - MessageLogging](../examples/09-MessageLogging)
|
||||
Example 9 illustrates how to add log messages to your HomeSpan sketch. The code is identical to Example 8 except for the inclusion of new log messages. New HomeSpan API topics covered in this example include:
|
||||
|
||||
* using the `LOG1()` and `LOG2()` macros to create log messages for different log levels
|
||||
* using the `LOG0()`, `LOG1()`, and `LOG2()` macros to create log messages for different log levels
|
||||
* setting the initial log level for a sketch with the `homeSpan.setLogLevel()` method
|
||||
|
||||
### [Example 10 - RGB_LED](../examples/10-RGB_LED)
|
||||
|
|
@ -59,10 +57,8 @@ Example 10 illustrates how to control an RGB LED to set any color and brightness
|
|||
* converting HomeKit Hue/Saturation/Brightness levels to Red/Green/Blue levels using `PwmPin::HSVtoRGB()`
|
||||
* using the optional template functionality of `getVal()`, such as `getVal<float>()`
|
||||
|
||||
### [Example 11 - ServiceOptions](../examples/11-ServiceOptions)
|
||||
This example explores how the Name Characteristic can be used to create different naming schemes for multi-Service Accessories, and how these appear in the Home App depending on how you display the Accessory tile. New HomeSpan API topics covered in this example include:
|
||||
|
||||
* setting the primary Service for an Accessory with the `setPrimary()` method
|
||||
### [Example 11 - ServiceNames](../examples/11-ServiceNames)
|
||||
Example 11 demonstrates how the names of the different Services in a multi-Service Accessory can be changed from the defaults generated by the Home App to something more useful and customized. The examples also explores how and when these names are displayed by the Home App, as well as how the Home App chooses an appropriate icon for an Accessory Tile when the device is configured as a Bridge.
|
||||
|
||||
### [Example 12 - ServiceLoops](../examples/12-ServiceLoops)
|
||||
Example 12 introduces HomeKit *Event Notifications* to implement two new accessories - a Temperature Sensor and an Air Quality Sensor. Of course we won't actually have these physical devices attached to the ESP32 for the purpose of this example, but we will simulate "reading" their properties on a periodic basis, and notify HomeKit of any changed values. New HomeSpan API topics covered in this example include:
|
||||
|
|
@ -93,10 +89,38 @@ Example 17 introduces the HAP concept of Linked Services and demonstrates how th
|
|||
* creating Linked Services using the `addLink()` method
|
||||
|
||||
### [Example 18 - SavingStatus](../examples/18-SavingStatus)
|
||||
Example 18 demonstrates, through the implementaton of two Dimmable LEDs, how the values of Characteristics can be automatically saved in the device's non-volatile storage (NVS) for restoration upon start-up if the device is loses power. New HomeSpan API topics covered in this example include:
|
||||
Example 18 demonstrates, through the implementation of two Dimmable LEDs, how the values of Characteristics can be automatically saved in the device's non-volatile storage (NVS) for restoration upon start-up if the device is loses power. New HomeSpan API topics covered in this example include:
|
||||
|
||||
* instructing HomeSpan to store the value of a Characteristic in NVS by setting the second parameter of the constuctor to `true`
|
||||
|
||||
### [Example 19 - WebLog](../examples/19-WebLog)
|
||||
Example 19 illustrates, through the implementation of two On/Off LEDs, how to add a Web Log to your HomeSpan sketch, how to syncronize the internal clock of your device using an NTP time server, and how to create your own Web Log messages. New HomeSpan API topics covered in this example include:
|
||||
|
||||
* enabling the HomeSpan Web Log and specifying an optional NTP time server with the `homeSpan.enableWebLog()` method
|
||||
* using the `WEBLOG()` macro to create Web Log messages
|
||||
|
||||
## Other Examples
|
||||
|
||||
The following examples showcase a variety of HomeSpan and HomeKit functionality as referenced in different sections of the HomeSpan documentation. The sketches can be found in the Arduino IDE under *File → Examples → HomeSpan → Other Examples*
|
||||
|
||||
### [TableLamp](../Other%20Examples/TableLamp)
|
||||
A basic implementation of a Table Lamp Accessory. Used as the tutorial in [HomeSpan API Overview](Overview.md)
|
||||
|
||||
### [RemoteControl](../Other%20Examples/RemoteControl)
|
||||
A standalone example that shows how to use HomeSpan's *RFControl* class to produce a custom pulse train. For illustrative purposes the pulse widths are very long and suitable for output to an LED so you can "see" the pulse train. See the [RF/IR Generation](RMT.md) page for full details
|
||||
|
||||
### [ServoControl](../Other%20Examples/ServoControl)
|
||||
An implementation of a Window Shade that uses HomeSpan's *ServoPin* class to control the horizontal tilt of the slats. See [ServoPin](PWM.md#servopinuint8_t-pin-double-initdegrees-uint16_t-minmicros-uint16_t-maxmicros-double-mindegrees-double-maxdegrees) for full details
|
||||
|
||||
### [Television](../Other%20Examples/Television)
|
||||
An example of HomeKit's *undocumented* Television Service showing how different Characteristics can be used to control a TV's power, input sources, and a few other functions. See the [Television Services and Characteristics](TVServices.md) page for full details
|
||||
|
||||
### [Pixel](../Other%20Examples/Pixel)
|
||||
Demonstrates how to use HomeSpan's *Pixel* and *Dot* classes to control one- and two-wire Addressable RGB and RGBW LEDs. See the [Addressable RGB LEDs](Pixels.md) page for full details
|
||||
|
||||
### [CustomService](../Other%20Examples/CustomService)
|
||||
Demonstrates how to create Custom Services and Custom Characteristics in HomeSpan to implement an Atmospheric Pressure Sensor recognized by the *Eve for HomeKit* app. See [Custom Characteristics and Custom Services Macros](Reference.md#custom-characteristics-and-custom-services-macros) for full details
|
||||
|
||||
---
|
||||
|
||||
[↩️](README.md) Back to the Welcome page
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Gregg E. Berman
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -66,39 +66,39 @@ void setup() { // Your HomeSpan code should be placed within the
|
|||
// The HomeSpan library creates a global object named "homeSpan" that encapsulates all HomeSpan functionality.
|
||||
// The begin() method is used to initialize HomeSpan and start all HomeSpan processes.
|
||||
|
||||
// The first two parameters are Category and Name, which are used by HomeKit to configure the icon and name of the device shown in your Home App
|
||||
// when initially pairing your device.
|
||||
// The first two parameters are Category and Name, which are used by HomeKit to configure the icon and name
|
||||
// of the device shown in the Home App when initially pairing a HomeSpan device with your iPhone.
|
||||
|
||||
// In addition, the Name you choose below will be used as the "default name" for all Accessory Tiles. When you first
|
||||
// pair the device, the Home App will display this default name and allow you to change it (for each Accessory Tile)
|
||||
// before pairing is complete. However, even after the device is paired you can always change the name of any
|
||||
// Accessory Tile directly from the Home App via the set-up screen for any Tile.
|
||||
|
||||
// IMPORTANT: The Name you choose below MUST BE UNIQUE across all your HomeSpan devices!
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LightBulb"); // initializes a HomeSpan device named "HomeSpan Lightbulb" with Category set to Lighting
|
||||
|
||||
// Next, we construct a simple HAP Accessory Database with a single Accessory containing 3 Services,
|
||||
// each with their own required Characteristics.
|
||||
|
||||
new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments
|
||||
new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), no arguments needed
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service
|
||||
|
||||
// The only required Characteristic for the Accessory Information Service is the special Identify Characteristic. It takes no arguments:
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, which has 6 required Characteristics:
|
||||
new Characteristic::Name("My Table Lamp"); // Name of the Accessory, which shows up on the HomeKit "tiles", and should be unique across Accessories
|
||||
new Characteristic::Identify(); // Create the required Identify Characteristic
|
||||
|
||||
// The next 4 Characteristics serve no function except for being displayed in HomeKit's setting panel for each Accessory. They are nevertheless required by HAP:
|
||||
// The Accessory Information Service also includes these four OPTIONAL Characteristics. They perform no function and are for
|
||||
// informational purposes only --- their values are displayed in HomeKit's setting panel for each Accessory. Feel free
|
||||
// to uncomment the lines and implement any combination of them, or none at all.
|
||||
|
||||
new Characteristic::Manufacturer("HomeSpan"); // Manufacturer of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::SerialNumber("123-ABC"); // Serial Number of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::Model("120-Volt Lamp"); // Model of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::FirmwareRevision("0.9"); // Firmware of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
// new Characteristic::Manufacturer("HomeSpan"); // Manufacturer of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
// new Characteristic::SerialNumber("123-ABC"); // Serial Number of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
// new Characteristic::Model("120-Volt Lamp"); // Model of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
// new Characteristic::FirmwareRevision("0.9"); // Firmware of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
|
||||
// The last required Characteristic for the Accessory Information Service is the special Identify Characteristic. We'll learn more about this
|
||||
// Characteristic in later examples. For now, you can just instantiate it without any arguments.
|
||||
|
||||
new Characteristic::Identify(); // Create the required Identify
|
||||
|
||||
// *NOTE* HAP requires that the AccessoryInformation Service always be instantiated BEFORE any other Services, which is why we created it first.
|
||||
|
||||
// HAP also requires every Accessory (with the exception of those in Bridges, as we will see later) to implement the HAP Protocol Information Service.
|
||||
// This Service supports a single required Characteristic that defines the version number of HAP used by the device.
|
||||
// HAP Release R2 requires this version to be set to "1.1.0"
|
||||
|
||||
new Service::HAPProtocolInformation(); // Create the HAP Protcol Information Service
|
||||
new Characteristic::Version("1.1.0"); // Set the Version Characteristicto "1.1.0" as required by HAP
|
||||
// *NOTE* HAP requires that the Accessory Information Service always be instantiated BEFORE any other Services, which is why we created it first.
|
||||
|
||||
// Now that the required "informational" Services have been defined, we can finally create our Light Bulb Service
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Gregg E. Berman
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -44,45 +44,39 @@ void setup() {
|
|||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LightBulbs"); // initialize HomeSpan - note the name is now "HomeSpan LightBulbs"
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LightBulb"); // initializes a HomeSpan device named "HomeSpan Lightbulb" with Category set to Lighting
|
||||
|
||||
// Here we create the first LightBulb Accessory just as in Example 1
|
||||
|
||||
new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments
|
||||
new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), no arguments needed
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, which has 6 required Characteristics
|
||||
new Characteristic::Name("My Table Lamp"); // Name of the Accessory, which shows up on the HomeKit "tiles", and should be unique across Accessories
|
||||
new Characteristic::Manufacturer("HomeSpan"); // Manufacturer of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::SerialNumber("123-ABC"); // Serial Number of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::Model("120-Volt Lamp"); // Model of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::FirmwareRevision("0.9"); // Firmware of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, with the required Identify Characteristic
|
||||
new Characteristic::Identify(); // Create the required Identify
|
||||
|
||||
new Service::HAPProtocolInformation(); // Create the HAP Protcol Information Service
|
||||
new Characteristic::Version("1.1.0"); // Set the Version Characteristicto "1.1.0" as required by HAP
|
||||
|
||||
new Service::LightBulb(); // Create the Light Bulb Service
|
||||
new Characteristic::On(); // This Service requires the "On" Characterstic to turn the light on and off
|
||||
|
||||
// Now we create a second Accessory, which is just a duplicate of Accessory 1 with the exception of changing the Name from "My Table Lamp" to "My Floor Lamp"
|
||||
// Now we create a second Accessory, which is just a duplicate of the first Accessory
|
||||
|
||||
new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments
|
||||
new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), no arguments needed
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, which has 6 required Characteristics
|
||||
new Characteristic::Name("My Floor Lamp"); // Name of the Accessory, which shows up on the HomeKit "tiles", and should be unique across Accessories
|
||||
new Characteristic::Manufacturer("HomeSpan"); // Manufacturer of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::SerialNumber("123-ABC"); // Serial Number of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::Model("120-Volt Lamp"); // Model of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::FirmwareRevision("0.9"); // Firmware of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, with the required Identify Characteristic
|
||||
new Characteristic::Identify(); // Create the required Identify
|
||||
|
||||
new Service::HAPProtocolInformation(); // Create the HAP Protcol Information Service
|
||||
new Characteristic::Version("1.1.0"); // Set the Version Characteristicto "1.1.0" as required by HAP
|
||||
|
||||
new Service::LightBulb(); // Create the Light Bulb Service
|
||||
new Characteristic::On(); // This Service requires the "On" Characterstic to turn the light on and off
|
||||
|
||||
// That's it - our device now has two Accessories!
|
||||
// That's it - our device now has two Accessories, each displayed up as a separate Tile in the Home App!
|
||||
|
||||
// Note that for a device with multiple Accessories, the Home App generates a default name for each Accessory Tile from the Name
|
||||
// specified in homeSpan.begin(). In this case, the default name for the first Accessory Tile will be "HomeSpan Lightbulb",
|
||||
// just as it was in Example 1, and the default name for the second Accessory Tile will be "HomeSpan Lightbulb 2".
|
||||
|
||||
// You can of course change the name of each Accessory Tile from these defaults when prompted by the Home App during pairing. You
|
||||
// can also change the name of any Accessory Tile, even after pairing, directly from the Home App by opening the settings page
|
||||
// for any given Tile.
|
||||
|
||||
// In Example 7 we will demonstrate how the default names can be changed from within a HomeSpan sketch.
|
||||
|
||||
// IMPORTANT: You should NOT have to re-pair your device with HomeKit when moving from Example 1 to Example 2. HomeSpan will note
|
||||
// that the Attribute Database has been updated, and will broadcast a new configuration number when the program restarts. This should
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Gregg E. Berman
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -44,33 +44,57 @@ void setup() {
|
|||
|
||||
Serial.begin(115200); // Start a serial connection - this is needed for you to type in your WiFi credentials
|
||||
|
||||
homeSpan.begin(Category::Fans,"HomeSpan Ceiling Fan"); // Begin a HomeSpan Session - note the Category has been set to "Fans"
|
||||
homeSpan.begin(Category::Fans,"HomeSpan Ceiling Fan"); // Initialize HomeSpan - note the Category has been set to "Fans"
|
||||
|
||||
// We begin by creating a Light Bulb Accessory just as in Examples 1 and 2, but with Name now set to "My Ceiling Fan"
|
||||
// We begin by creating a Light Bulb Accessory just as in Examples 1 and 2
|
||||
|
||||
new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments
|
||||
new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), no arguments needed
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, which has 6 required Characteristics
|
||||
new Characteristic::Name("My Ceiling Fan"); // Name of the Accessory, which shows up on the HomeKit "tiles", and should be unique across Accessories
|
||||
new Characteristic::Manufacturer("HomeSpan"); // Manufacturer of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::SerialNumber("123-ABC"); // Serial Number of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::Model("120-Volt Lamp"); // Model of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::FirmwareRevision("0.9"); // Firmware of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, with the required Identify Characteristic
|
||||
new Characteristic::Identify(); // Create the required Identify
|
||||
|
||||
new Service::HAPProtocolInformation(); // Create the HAP Protcol Information Service
|
||||
new Characteristic::Version("1.1.0"); // Set the Version Characteristicto "1.1.0" as required by HAP
|
||||
|
||||
new Service::LightBulb(); // Create the Light Bulb Service
|
||||
new Characteristic::On(); // This Service requires the "On" Characterstic to turn the light on and off
|
||||
|
||||
// Now we create the a Fan Service within this same Accessory
|
||||
// Now we add a Fan Service within this same Accessory
|
||||
|
||||
new Service::Fan(); // Create the Fan Service
|
||||
new Characteristic::Active(); // This Service requires the "Active" Characterstic to turn the fan on and off
|
||||
|
||||
// Similar to Example 2, we will also implement a LightBulb as a second Accessory
|
||||
|
||||
new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), no arguments needed
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, with the required Identify Characteristic
|
||||
new Characteristic::Identify(); // Create the required Identify
|
||||
|
||||
new Service::LightBulb(); // Create the Light Bulb Service
|
||||
new Characteristic::On(); // This Service requires the "On" Characterstic to turn the light on and off
|
||||
|
||||
// If everything worked correctly you should now see two Tiles in the Home App:
|
||||
//
|
||||
// * a Tile named "HomeSpan Ceiling Fan" with an icon of a Fan. Clicking this Tile should open the
|
||||
// control page showing a Fan control on the left, and a Light control on the right
|
||||
//
|
||||
// * a Tile named "HomeSpan Ceiling Fan 2" with an icon of a LightBulb. Clicking this Tile should
|
||||
// toggle the Light On/Off
|
||||
|
||||
// The reason for including the second LightBulb Accessories in this example is to illustrate the impact of the device's Category
|
||||
// on various icons. Setting Category to Fan in homeSpan.begin() serves two purposes. First, it sets the icon for the device itself,
|
||||
// as shown by the Home App during initial pairing, to a Fan. Second, it helps the Home App to determine which icon to use for an
|
||||
// Accessory Tile when there is ambiguity. The second Accessory contains nothing but a LightBulb Service, so the Home App sensibly
|
||||
// uses a LightBulb icon for the Tile. But what icon should the Home App use for the first Accessory containing both a Fan Service
|
||||
// and a LightBulb Service? Either a Fan or LightBulb icon would make sense. Setting the Category of the device to Fan causes
|
||||
// the Home App to choose a Fan icon for the first Accessory.
|
||||
|
||||
// As a test of this, unpair the device; change the Category to Lighting (as in Example 2); re-load the sketch; and re-pair the device.
|
||||
// You should now see the icon for the "HomeSpan Ceiling Fan" Tile is a LightBulb, and the control screen for the Accessory should
|
||||
// show the Light control on the left and the Fan control on the right.
|
||||
|
||||
// IMPORTANT: HomeKit Controllers often cache a lot of information. If your Controller does not update to match the above configuration,
|
||||
// simply select the Accessory in your Controller and under setting, select "Remove Accessory" and then re-pair.
|
||||
// simply select the Accessory in your Controller and under settings, select "Remove Accessory", but BEFORE re-pairing the device, type
|
||||
// 'H' into the HomeSpan CLI. This forces HomeSpan to reboot and generate a new device ID so that it will look "brand new" to the Home App
|
||||
// when you re-pair.
|
||||
|
||||
} // end of setup()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Gregg E. Berman
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
void setup() {
|
||||
|
||||
// Example 4 expands on Example 3 by adding Characteristics to set FAN SPEED, FAN DIRECTION, and LIGHT BRIGHTNESS.
|
||||
// Example 4 expands on the first Accessory in Example 3 by adding Characteristics to set FAN SPEED, FAN DIRECTION, and LIGHT BRIGHTNESS.
|
||||
// For ease of reading, all prior comments have been removed and new comments added to show explicit changes from the previous example.
|
||||
|
||||
Serial.begin(115200);
|
||||
|
|
@ -49,16 +49,8 @@ void setup() {
|
|||
|
||||
new SpanAccessory();
|
||||
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Name("My Ceiling Fan");
|
||||
new Characteristic::Manufacturer("HomeSpan");
|
||||
new Characteristic::SerialNumber("123-ABC");
|
||||
new Characteristic::Model("120-Volt Lamp");
|
||||
new Characteristic::FirmwareRevision("0.9");
|
||||
new Characteristic::Identify();
|
||||
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new Service::LightBulb();
|
||||
new Characteristic::On(true); // NEW: Providing an argument sets its initial value. In this case it means the LightBulb will be turned on at start-up
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Gregg E. Berman
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -69,20 +69,12 @@ void setup() {
|
|||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LEDs");
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LED");
|
||||
|
||||
new SpanAccessory();
|
||||
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Name("LED #1");
|
||||
new Characteristic::Manufacturer("HomeSpan");
|
||||
new Characteristic::SerialNumber("123-ABC");
|
||||
new Characteristic::Model("20mA LED");
|
||||
new Characteristic::FirmwareRevision("0.9");
|
||||
new Characteristic::Identify();
|
||||
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
new Characteristic::Identify();
|
||||
|
||||
// In Example 2 we instantiated a LightBulb Service and its "On" Characteristic here. We are now going to replace these two lines (by commenting them out)...
|
||||
|
||||
|
|
@ -102,15 +94,7 @@ void setup() {
|
|||
new SpanAccessory();
|
||||
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Name("LED #2");
|
||||
new Characteristic::Manufacturer("HomeSpan");
|
||||
new Characteristic::SerialNumber("123-ABC");
|
||||
new Characteristic::Model("20mA LED");
|
||||
new Characteristic::FirmwareRevision("0.9");
|
||||
new Characteristic::Identify();
|
||||
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
new Characteristic::Identify();
|
||||
|
||||
// new Service::LightBulb(); // Same as above, this line is deleted...
|
||||
// new Characteristic::On(); // This line is also deleted...
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Gregg E. Berman
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -59,35 +59,19 @@ void setup() {
|
|||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LEDs");
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LED");
|
||||
|
||||
new SpanAccessory();
|
||||
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Name("On/Off LED");
|
||||
new Characteristic::Manufacturer("HomeSpan");
|
||||
new Characteristic::SerialNumber("123-ABC");
|
||||
new Characteristic::Model("20mA LED");
|
||||
new Characteristic::FirmwareRevision("0.9");
|
||||
new Characteristic::Identify();
|
||||
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
new Characteristic::Identify();
|
||||
|
||||
new DEV_LED(16); // create an on/off LED attached to pin 16 (same as in Example 5)
|
||||
|
||||
new SpanAccessory();
|
||||
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Name("Dimmable LED");
|
||||
new Characteristic::Manufacturer("HomeSpan");
|
||||
new Characteristic::SerialNumber("123-ABC");
|
||||
new Characteristic::Model("20mA LED");
|
||||
new Characteristic::FirmwareRevision("0.9");
|
||||
new Characteristic::Identify();
|
||||
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
new Characteristic::Identify();
|
||||
|
||||
new DEV_DimmableLED(17); // NEW! create a dimmable (PWM-driven) LED attached to pin 17. See new code at end of DEV_LED.h
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// HomeSpan: A HomeKit implementation for the ESP32 //
|
||||
// ------------------------------------------------ //
|
||||
// //
|
||||
// Example 7: Changing an Accessory's default name //
|
||||
// to distinguish On/Off from Dimmable LEDs //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_LED.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// As discusses in previous examples, the Home App automatically generates default names for each Accessory Tile
|
||||
// based on the Name provided in the second argument of homeSpan.begin(). And though you can change these names
|
||||
// both during, and anytime after, pairing, HAP also allows you to customize the default names themselves, so
|
||||
// something more intuitive is presented to the user when the device is first paired.
|
||||
|
||||
// Changing the default name for an Accessory is done by adding an optional Name Characteristic to the
|
||||
// Accessory Information Service. This causes the Home App to use the value of that Characteristic as the default name
|
||||
// for an Accessory Tile, instead of generating one from the name used in homeSpan.begin().
|
||||
|
||||
// Howevever, there is one caveat: The Name Characteristic has no affect when used in the first Accessory of a device.
|
||||
// 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.
|
||||
|
||||
// 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.
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LED"); // Note this results in the default name of "HomeSpan LED", "HomeSpan LED 2", etc. for each Accessory Tile
|
||||
|
||||
new SpanAccessory();
|
||||
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Simple LED"); // This use of Name() will be ignored by the Home App. The default name for the Accessory will continue to be shown as "HomeSpan LED"
|
||||
|
||||
new DEV_LED(16);
|
||||
|
||||
new SpanAccessory();
|
||||
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Dimmable LED"); // This DOES change the default name for the Accessory from "HomeSpan LED 2" to "Dimmable LED"
|
||||
|
||||
new DEV_DimmableLED(17);
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
|
||||
////////////////////////////////////
|
||||
// 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
|
||||
|
||||
int ledPin; // pin number defined for this LED
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
|
||||
DEV_LED(int ledPin) : Service::LightBulb(){ // constructor() method
|
||||
|
||||
power=new Characteristic::On();
|
||||
this->ledPin=ledPin;
|
||||
pinMode(ledPin,OUTPUT);
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
digitalWrite(ledPin,power->getNewVal());
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
// Here's the new code defining DEV_DimmableLED - changes from above are noted in the comments
|
||||
|
||||
struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
|
||||
|
||||
LedPin *ledPin; // NEW! Create reference to LED Pin instantiated below
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
SpanCharacteristic *level; // NEW! Create a reference to the Brightness Characteristic instantiated below
|
||||
|
||||
DEV_DimmableLED(int pin) : Service::LightBulb(){ // constructor() method
|
||||
|
||||
power=new Characteristic::On();
|
||||
|
||||
level=new Characteristic::Brightness(50); // NEW! Instantiate the Brightness Characteristic with an initial value of 50% (same as we did in Example 4)
|
||||
level->setRange(5,100,1); // NEW! This sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1% (different from Example 4 values)
|
||||
|
||||
this->ledPin=new LedPin(pin); // NEW! Configures a PWM LED for output to the specified pin. Note pinMode() does NOT need to be called in advance
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
// Here we set the brightness of the LED by calling ledPin->set(brightness), where brightness=0-100.
|
||||
// Note HomeKit sets the on/off status of a LightBulb separately from its brightness, which means HomeKit
|
||||
// can request a LightBulb be turned off, but still retains the brightness level so that it does not need
|
||||
// to be resent once the LightBulb is turned back on.
|
||||
|
||||
// Multiplying the newValue of the On Characteristic ("power", which is a boolean) with the newValue of the
|
||||
// Brightness Characteristic ("level", which is an integer) is a short-hand way of creating the logic to
|
||||
// set the LED level to zero when the LightBulb is off, or to the current brightness level when it is on.
|
||||
|
||||
ledPin->set(power->getNewVal()*level->getNewVal());
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// HomeSpan: A HomeKit implementation for the ESP32 //
|
||||
// ------------------------------------------------ //
|
||||
// //
|
||||
// Example 7: Transforming AccessoryInformation into a //
|
||||
// derived Service that implements the //
|
||||
// Identify Characteristic //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_LED.h"
|
||||
#include "DEV_Identify.h" // NEW! This is where we store all code for the DEV_Identify Service
|
||||
|
||||
void setup() {
|
||||
|
||||
// In Example 5 we saw how to create a derived Service to encapsulate all the functionality needed to implement DEV_LED
|
||||
// in it's own DEV_LED.h file. Then, in Example 6 we extended that further by implementing DEV_DimmableLED. In this
|
||||
// example we do the same for the AccessoryInformation Service. Note how AccessoryInformation, and all of its
|
||||
// Characteristics, need to be defined for every Accessory. By deriving a new Service that implements a multi-argument
|
||||
// constructor we can avoid having to separately create each required Characteristic every time. Creating a derived Service
|
||||
// also allows us to implement device-specific code for the Identify Characteristic. We will call this derived Service
|
||||
// DEV_Identify, and store its code in "DEV_Identify.h" which has already been included above.
|
||||
|
||||
// As usual, all previous comments have been deleted and only new changes from the previous example are shown.
|
||||
|
||||
// NOTE: To see how this works in practice, you'll need to unpair your device and re-pair it once the new code is loaded.
|
||||
// This will allow oyu to activate the identify routines.
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LEDs");
|
||||
|
||||
new SpanAccessory();
|
||||
|
||||
// Rather than instantiate the AccessoryInformation Service and all of it's required Characteristics,
|
||||
// we'll delete these line (comment them out)...
|
||||
|
||||
// new Service::AccessoryInformation();
|
||||
// new Characteristic::Name("On/Off LED");
|
||||
// new Characteristic::Manufacturer("HomeSpan");
|
||||
// new Characteristic::SerialNumber("123-ABC");
|
||||
// new Characteristic::Model("20mA LED");
|
||||
// new Characteristic::FirmwareRevision("0.9");
|
||||
// new Characteristic::Identify();
|
||||
|
||||
// ...and replace them with this single line that implements everything above. See DEV_Identify.h for
|
||||
// details on how this is defined. Note there is an extra argument at the end we set to 3.
|
||||
// This optional argument will be used to run the identify routine (see code for details)
|
||||
|
||||
new DEV_Identify("On/Off LED","HomeSpan","123-ABC","20mA LED","0.9",3); // NEW! This implements all the Characteristics above
|
||||
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
|
||||
new DEV_LED(16); // create an on/off LED attached to pin 16 (same as in Example 5)
|
||||
|
||||
new SpanAccessory();
|
||||
|
||||
// Same as above, we can replace all of this...
|
||||
|
||||
// new Service::AccessoryInformation();
|
||||
// new Characteristic::Name("Dimmable LED");
|
||||
// new Characteristic::Manufacturer("HomeSpan");
|
||||
// new Characteristic::SerialNumber("123-ABC");
|
||||
// new Characteristic::Model("20mA LED");
|
||||
// new Characteristic::FirmwareRevision("0.9");
|
||||
// new Characteristic::Identify();
|
||||
|
||||
// ...with this (note we set last argument to 5 this time - see code for what this does)
|
||||
|
||||
new DEV_Identify("Dimmable LED","HomeSpan","123-ABC","20mA LED","0.9",5); // NEW! This implements all the Characteristics above
|
||||
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
|
||||
new DEV_DimmableLED(17); // create a dimmable (PWM-driven) LED attached to pin 17
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
|
||||
//////////////////////////////////
|
||||
// DEVICE-SPECIFIC SERVICES //
|
||||
//////////////////////////////////
|
||||
|
||||
// Here we define the DEV_Identify Service as derived class of AccessoryInformation
|
||||
|
||||
struct DEV_Identify : Service::AccessoryInformation {
|
||||
|
||||
int nBlinks; // number of times to blink built-in LED in identify routine
|
||||
SpanCharacteristic *identify; // reference to the Identify Characteristic
|
||||
|
||||
// Next we define the constructor using all the arguments needed to implement the required Characteristics
|
||||
// of AccessoryInformation, plus one extra argument at the end called "nBlinks" we will use to specify how many
|
||||
// times HomeSpan should blink the built-in LED when HomeKit calls this device's Identify routine during pairing.
|
||||
|
||||
DEV_Identify(const char *name, const char *manu, const char *sn, const char *model, const char *version, int nBlinks) : Service::AccessoryInformation(){
|
||||
|
||||
new Characteristic::Name(name); // create all the required Characteristics with values set based on above arguments
|
||||
new Characteristic::Manufacturer(manu);
|
||||
new Characteristic::SerialNumber(sn);
|
||||
new Characteristic::Model(model);
|
||||
new Characteristic::FirmwareRevision(version);
|
||||
identify=new Characteristic::Identify(); // store a reference to the Identify Characteristic for use below
|
||||
|
||||
this->nBlinks=nBlinks; // store the number of times to blink the LED
|
||||
|
||||
pinMode(homeSpan.getStatusPin(),OUTPUT); // make sure LED is set for output
|
||||
}
|
||||
|
||||
// How HomeKit Identifies Devices:
|
||||
//
|
||||
// When HomeKit first pairs with a new device it "calls" that device's identify routine for every defined Accessory.
|
||||
// To do so, HomeKit requests the Identify Characteristic for each defined AccessoryInformation Service to be set to "true".
|
||||
// The Identify Characteristic is write-only, so no value is ever stored, even though HomeKit is requesting its value
|
||||
// be updated. We can therefore use the same update() method as if the Identify Characteristic was the same as any
|
||||
// other boolean Characteristic.
|
||||
|
||||
// There are many ways to implement some form of identification. For an LED, you could blink it one or more times.
|
||||
// For a LightBulb, you can flash it on and off. For window shade, you could raise and lower it.
|
||||
// Most commerical devices don't do anything. Because HomeSpan can be used to control many different types of
|
||||
// device, below we implement a very generic routine that simply blinks the Status LED the number of times specified above.
|
||||
// In principle, this code could call a user-defined routine that is different for each physcially-attached device (light, shade, fan, etc),
|
||||
// but in practice this is overkill.
|
||||
|
||||
// Note that the blink routine below starts by turning off the Status LED and then leaves it on once it has blinked
|
||||
// the specified number of times. This is because when HomeSpan starts up if confirms to user that it has connected
|
||||
// to the WiFi network by turning on the Status LED. Thus we want to leave it on when blinking is completed.
|
||||
|
||||
// Also note we use the homeSpan.getStatusPin() method to find the pin number associated with the Status LED
|
||||
|
||||
boolean update(){
|
||||
|
||||
for(int i=0;i<nBlinks;i++){
|
||||
digitalWrite(homeSpan.getStatusPin(),LOW);
|
||||
delay(250);
|
||||
digitalWrite(homeSpan.getStatusPin(),HIGH);
|
||||
delay(250);
|
||||
}
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
};
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
#include "extras/PwmPin.h" // allows PWM control of LED brightness
|
||||
|
||||
struct DEV_LED : Service::LightBulb { // ON/OFF LED
|
||||
|
||||
int ledPin; // pin number defined for this LED
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
|
||||
DEV_LED(int ledPin) : Service::LightBulb(){ // constructor() method
|
||||
|
||||
power=new Characteristic::On();
|
||||
this->ledPin=ledPin;
|
||||
pinMode(ledPin,OUTPUT);
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
digitalWrite(ledPin,power->getNewVal());
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
|
||||
|
||||
LedPin *ledPin; // reference to Led Pin
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
SpanCharacteristic *level; // reference to the Brightness Characteristic
|
||||
|
||||
DEV_DimmableLED(int pin) : Service::LightBulb(){ // constructor() method
|
||||
|
||||
power=new Characteristic::On();
|
||||
|
||||
level=new Characteristic::Brightness(50); // Brightness Characteristic with an initial value of 50%
|
||||
level->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%
|
||||
|
||||
this->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
ledPin->set(power->getNewVal()*level->getNewVal());
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Gregg E. Berman
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -31,33 +31,29 @@
|
|||
// ------------------------------------------------ //
|
||||
// //
|
||||
// Example 8: HomeKit Bridges and Bridge Accessories //
|
||||
// ** the preferred method for HomeSpan ** //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_LED.h"
|
||||
#include "DEV_Identify.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// Though we've seen in prior examples that one device can support multiple Accessories, HomeKit provides a more
|
||||
// general multi-Accessory framework that is somewhat more robust and easier to use: HomeKit Bridges.
|
||||
// A Bridge is a device that includes multiple Accessories, except that the FIRST defined Accessory contains
|
||||
// nothing but the AccessoryInformation Service and the HAPProtcolInformation Service. When such a device is paired
|
||||
// to HomeKit, it is automatically recognized as a Bridge. All of the other Accessories defined in the device are
|
||||
// associated with this Bridge. If you unpair the Bridge from HomeKit, all associated Accessories are automatically
|
||||
// removed.
|
||||
//
|
||||
// Adding, editing, and deleting the other Accessories occurs in the same manner as before, but because the device
|
||||
// is paired as a Bridge, changes to the other Accessories is less likely to require you to un-pair and re-pair
|
||||
// the device. HomeKit seems to be able to better process changes when they are done within a Bridge framework.
|
||||
//
|
||||
// One added bonus is that the HAPProtcolInformation Service only needs to be defined for the Bridge Accessory, and
|
||||
// does not need to be repeated for other Accessories.
|
||||
//
|
||||
// Example 8 is functionally identical to Example 7, except that instead of defining two Accessories (one for the on/off
|
||||
// LED and one for the dimmable LED), we define three Accessories, where the first acts as the Bridge.
|
||||
// If the only Service defined in the FIRST Accessory of a mult-Accessory device is the required Accessory Information Service,
|
||||
// the device is said to be configured as a "Bridge". Historically there may have been a number of functional differences between bridge
|
||||
// devices and non-bridge devices, but since iOS 15, it's not obvious there are any differences in functionality, with two exceptions:
|
||||
|
||||
// 1. Recall from Example 7 that the use of Characteristic::Name() to change the default name of an Accessory Tile
|
||||
// does not work for the first Accessory defined. The Home App always displays the default name of the first Accessory Tile
|
||||
// as the name of the device specified in homeSpan.begin(). However, this is not an issue when implementing a device
|
||||
// as a Bridge, since the first Accessory is nothing but the Bridge itself - having the default name match the name
|
||||
// of the device in this case makes much more sense. More importantly, you can now use Characteristic::Name() to change the
|
||||
// default name of BOTH the LED Accessory Tiles.
|
||||
|
||||
// 2. Devices configured as a Bridge appear in the Home App under the main settings page that displays all Hubs and Bridges.
|
||||
|
||||
// The sketch below is functionally identical to Example 7, except that instead of defining two Accessories (one for the Simple On/Off
|
||||
// LED and one for the Dimmable LED), we define three Accessories, where the first acts as the Bridge.
|
||||
|
||||
// As usual, all previous comments have been deleted and only new changes from the previous example are shown.
|
||||
|
||||
|
|
@ -65,40 +61,27 @@ void setup() {
|
|||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge"); // CHANGED! Note that we replaced Category::Lighting with Bridges (this changes the icon when pairing)
|
||||
// Below we replace Category::Lighting with Category::Bridges. This changes the icon of the device shown when pairing
|
||||
// with the Home App, but does NOT change the icons of the Accessory Tiles. You can choose any Category you like.
|
||||
// For instance, we could have continued to use Category::Lighting, even though we are configuring the device as a Bridge.
|
||||
|
||||
// We begin by creating a Bridge Accessory, which look just like any other Accessory,
|
||||
// except that is only contains DEV_Identify (which is derived from AccessoryInformation)
|
||||
// and HAPProtcolInformation (required). Note that HomeKit will still call the identify
|
||||
// update() routine upon pairing, so we specify the number of blinks to be 3.
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Bridge #1","HomeSpan","123-ABC","HS Bridge","0.9",3);
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
new SpanAccessory(); // This first Accessory is the new "Bridge" Accessory. It contains no functional Services, just the Accessory Information Service
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory(); // This second Accessory is the same as the first Accessory in Example 7, with the exception that Characteristic::Name() now does something
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Simple LED"); // Note that unlike in Example 7, this use of Name() is now utilized by the Home App since it is not the first Accessory (the Bridge above is the first)
|
||||
new DEV_LED(16);
|
||||
|
||||
// Now we simply repeat the definitions of the previous LED Accessories, as per Example 7, with two exceptions:
|
||||
// 1) We no longer need to include the HAPProtocolInformation Service.
|
||||
// 2) We will set the number of blinks to zero, so that only the bridge accessory will cause the Built-In
|
||||
// LED to blink. This becomes especially important if you had 20 Accessories defined and needed to wait a
|
||||
// minute or more for all the blinking to finish while pairing.
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("On/Off LED","HomeSpan","123-ABC","20mA LED","0.9",0); // CHANGED! The number of blinks is now set to zero
|
||||
|
||||
// new Service::HAPProtocolInformation(); - DELETED - NO LONGER NEEDED
|
||||
// new Characteristic::Version("1.1.0"); - DELETED - NO LONGER NEEDED
|
||||
|
||||
new DEV_LED(16); // create an on/off LED attached to pin 16
|
||||
|
||||
new SpanAccessory();
|
||||
|
||||
new DEV_Identify("Dimmable LED","HomeSpan","123-ABC","20mA LED","0.9",0); // CHANGED! The number of blinks is now set to zero
|
||||
|
||||
// new Service::HAPProtocolInformation(); - DELETED - NO LONGER NEEDED
|
||||
// new Characteristic::Version("1.1.0"); - DELETED - NO LONGER NEEDED
|
||||
|
||||
new DEV_DimmableLED(17); // create a dimmable (PWM-driven) LED attached to pin 17
|
||||
new SpanAccessory(); // This third Accessory is the same as the second Accessory in Example 7
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Dimmable LED");
|
||||
new DEV_DimmableLED(17);
|
||||
|
||||
} // end of setup()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
|
||||
//////////////////////////////////
|
||||
// DEVICE-SPECIFIC SERVICES //
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_Identify : Service::AccessoryInformation {
|
||||
|
||||
int nBlinks; // number of times to blink built-in LED in identify routine
|
||||
SpanCharacteristic *identify; // reference to the Identify Characteristic
|
||||
|
||||
DEV_Identify(const char *name, const char *manu, const char *sn, const char *model, const char *version, int nBlinks) : Service::AccessoryInformation(){
|
||||
|
||||
new Characteristic::Name(name); // create all the required Characteristics with values set based on above arguments
|
||||
new Characteristic::Manufacturer(manu);
|
||||
new Characteristic::SerialNumber(sn);
|
||||
new Characteristic::Model(model);
|
||||
new Characteristic::FirmwareRevision(version);
|
||||
identify=new Characteristic::Identify(); // store a reference to the Identify Characteristic for use below
|
||||
|
||||
this->nBlinks=nBlinks; // store the number of times to blink the LED
|
||||
|
||||
pinMode(homeSpan.getStatusPin(),OUTPUT); // make sure LED is set for output
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
for(int i=0;i<nBlinks;i++){
|
||||
digitalWrite(homeSpan.getStatusPin(),LOW);
|
||||
delay(250);
|
||||
digitalWrite(homeSpan.getStatusPin(),HIGH);
|
||||
delay(250);
|
||||
}
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Gregg E. Berman
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -37,32 +37,40 @@
|
|||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_LED.h"
|
||||
#include "DEV_Identify.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// HomeSpan sends a variety of messages to the Serial Monitor of the Arduino IDE whenever the device is connected
|
||||
// to a computer. Message output is performed either by the usual Serial.print() function, or by one of two macros,
|
||||
// LOG1() and LOG2(). These two macros call Serial.print() depending on HomeSpan's Log Level setting. A setting
|
||||
// of 0 means that LOG1() and LOG2() messages are ignored. A setting of 1 causes HomeSpan to print LOG1() messages
|
||||
// to the Serial Monitor, but ignores LOG2() message. And a setting of 2 causes HomeSpan to print both LOG1() and
|
||||
// LOG2() messages.
|
||||
// to a computer. Message output can be performed either by the usual Serial.print() or Serial.printf() functions,
|
||||
// or by one of three user macros: LOG0(), LOG1() and LOG2(). These three macros output messages to the Serial Monitor
|
||||
// depending on HomeSpan's Log Level setting:
|
||||
|
||||
// at a setting of 0, only LOG0() message are output; LOG1() and LOG2() messages are ignored
|
||||
// at a setting of 1, both LOG0() and LOG1() messages are output; LOG2() messages are ignored
|
||||
// at a setting of 2, all LOG0(), LOG1(), and LOG2() messages are output
|
||||
|
||||
// Example 9 illustrates how to add such log messages. The code is identical to Example 8 (without comments), except
|
||||
// that Serial.print() and LOG1() messages have been added to DEV_LED.h. The Serial.print() messages will always be
|
||||
// that LOG0() and LOG1() messages have been added to DEV_LED.h. The LOG0() messages will always be
|
||||
// output to the Arduino Serial Monitor. The LOG1() messages will only be output if the Log Level is set to 1 or 2.
|
||||
|
||||
// The setLogLevel() method of homeSpan is used to change the log level as follows:
|
||||
// The setLogLevel() method of homeSpan can used to change the log level as follows:
|
||||
|
||||
// homeSpan.setLogLevel(0) - sets Log Level to 0
|
||||
// homeSpan.setLogLevel(1) - sets Log Level to 1
|
||||
// homeSpan.setLogLevel(2) - sets Log Level to 2
|
||||
|
||||
// The method should be called BEFORE homeSpan.begin() - see below for proper use.
|
||||
// The method should be called BEFORE homeSpan.begin() - see below for proper use. Note that the Log Level
|
||||
// can also be changed dynamically during runtime via the HomeSpan CLI by typing 'L0', 'L1', or 'L2' into the Serial Monitor
|
||||
|
||||
// There are two forms of the LOG0(), LOG1(), and LOG2() macros. The first form takes only a single argument and outputs
|
||||
// messsges using the Serial.print(var) function. This allows you to output any single variable or text message, but does not allow you
|
||||
// to control the format, or to output more than one variable at a time. The second form take multiple arguments, where the first
|
||||
// is a standard C++ formatting string, and any remaining arguments are consumed according to the format string. This form
|
||||
// utilizes the variadic Serial.printf(char *fmt [,var1, var2...]) function.
|
||||
|
||||
// RECOMMENDATION: Since a HomeSpan ESP32 is meant to be physically connected to real-world devices, you may find
|
||||
// yourself with numerous ESP32s each configured with a different set of Accessories. To aid in identification
|
||||
// you may want to add Serial.print() statements containing some sort of initialization message to the constructors for
|
||||
// you may want to add LOG0() statements containing some sort of initialization message to the constructors for
|
||||
// each derived Service, such as DEV_LED. Doing so allows HomeSpan to "report" on its configuration upon start-up. See
|
||||
// DEV_LED for examples.
|
||||
|
||||
|
|
@ -72,24 +80,21 @@ void setup() {
|
|||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
// Defines the Bridge Accessory
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Bridge #1","HomeSpan","123-ABC","HS Bridge","0.9",3);
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
|
||||
// Defines an ON/OFF LED Accessory attached to pin 16
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("On/Off LED","HomeSpan","123-ABC","20mA LED","0.9",0);
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Simple LED");
|
||||
new DEV_LED(16);
|
||||
|
||||
// Defines a Dimmable (PWM-driven) LED Accessory attached to pin 17
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Dimmable LED","HomeSpan","123-ABC","20mA LED","0.9",0);
|
||||
new DEV_DimmableLED(17);
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Dimmable LED");
|
||||
new DEV_DimmableLED(17);
|
||||
|
||||
} // end of setup()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
|
||||
//////////////////////////////////
|
||||
// DEVICE-SPECIFIC SERVICES //
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_Identify : Service::AccessoryInformation {
|
||||
|
||||
int nBlinks; // number of times to blink built-in LED in identify routine
|
||||
SpanCharacteristic *identify; // reference to the Identify Characteristic
|
||||
|
||||
DEV_Identify(const char *name, const char *manu, const char *sn, const char *model, const char *version, int nBlinks) : Service::AccessoryInformation(){
|
||||
|
||||
new Characteristic::Name(name); // create all the required Characteristics with values set based on above arguments
|
||||
new Characteristic::Manufacturer(manu);
|
||||
new Characteristic::SerialNumber(sn);
|
||||
new Characteristic::Model(model);
|
||||
new Characteristic::FirmwareRevision(version);
|
||||
identify=new Characteristic::Identify(); // store a reference to the Identify Characteristic for use below
|
||||
|
||||
this->nBlinks=nBlinks; // store the number of times to blink the LED
|
||||
|
||||
pinMode(homeSpan.getStatusPin(),OUTPUT); // make sure LED is set for output
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
for(int i=0;i<nBlinks;i++){
|
||||
digitalWrite(homeSpan.getStatusPin(),LOW);
|
||||
delay(250);
|
||||
digitalWrite(homeSpan.getStatusPin(),HIGH);
|
||||
delay(250);
|
||||
}
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
};
|
||||
|
|
@ -17,22 +17,23 @@ struct DEV_LED : Service::LightBulb { // ON/OFF LED
|
|||
pinMode(ledPin,OUTPUT);
|
||||
|
||||
// Here we output log messages when the constructor is initially called.
|
||||
// We use Serial.print() since to ensure the message is always output
|
||||
// regardless of the VERBOSITY setting.
|
||||
// We use LOG0() to ensure the message is always output regardless of the
|
||||
// LOG Level setting. Note this uses the single-argument form of LOG(), so
|
||||
// multiple calls are needed to create a complete message
|
||||
|
||||
Serial.print("Configuring On/Off LED: Pin="); // initialization message
|
||||
Serial.print(ledPin);
|
||||
Serial.print("\n");
|
||||
LOG0("Configuring On/Off LED: Pin="); // initialization message
|
||||
LOG0(ledPin);
|
||||
LOG0("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
// Here we output log messages whenever update() is called,
|
||||
// which is helpful for debugging purposes if your physical device
|
||||
// is not functioning as expected. Since it's just for debugging,
|
||||
// we use LOG1() instead of Serial.print(). Note we can output
|
||||
// both the current as well as the new power settings.
|
||||
// Here we output log messages whenever update() is called, which is helpful
|
||||
// for debugging purposes if your physical device is not functioning as expected.
|
||||
// Since it's just for debugging, we use LOG1() instead of LOG0(). Note we can
|
||||
// output both the current as well as the new power settings. We've again
|
||||
// used the single-argument form of LOG() to create this message
|
||||
|
||||
LOG1("Updating On/Off LED on pin=");
|
||||
LOG1(ledPin);
|
||||
|
|
@ -64,33 +65,18 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
|
|||
level=new Characteristic::Brightness(50); // Brightness Characteristic with an initial value of 50%
|
||||
level->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%
|
||||
|
||||
// Here we once again output log messages when the constructor is initially called.
|
||||
// However, this time we use the multi-argument form of LOG() that resembles a
|
||||
// standard printf() function, which makes for more compact code.
|
||||
|
||||
LOG0("Configuring Dimmable LED: Pin=%d\n",pin); // initialization message
|
||||
|
||||
this->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin
|
||||
|
||||
// Here we output log messages when the constructor is initially called.
|
||||
// We use Serial.print() since to ensure the message is always output
|
||||
// regardless of the VERBOSITY setting. Note that ledPin has a method getPin()
|
||||
// that retrieves the pin number so you don't need to store it separately.
|
||||
|
||||
Serial.print("Configuring Dimmable LED: Pin="); // initialization message
|
||||
Serial.print(ledPin->getPin());
|
||||
Serial.print("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
// Here we output log messages whenever update() is called,
|
||||
// which is helpful for debugging purposes if your physical device
|
||||
// is not functioning as expected. Since it's just for debugging,
|
||||
// we use LOG1() instead of Serial.print().
|
||||
|
||||
LOG1("Updating Dimmable LED on pin=");
|
||||
LOG1(ledPin->getPin());
|
||||
LOG1(": Current Power=");
|
||||
LOG1(power->getVal()?"true":"false");
|
||||
LOG1(" Current Brightness=");
|
||||
LOG1(level->getVal());
|
||||
|
||||
// Note that since Dimmable_LED has two updateable Characteristics,
|
||||
// HomeKit may be requesting either or both to be updated. We can
|
||||
// use the "isUpdated" flag of each Characteristic to output a message
|
||||
|
|
@ -98,16 +84,18 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
|
|||
// Since update() is called whenever there is an update to at least
|
||||
// one of the Characteristics in a Service, either power, level, or both
|
||||
// will have its "isUpdated" flag set.
|
||||
|
||||
if(power->updated()){
|
||||
LOG1(" New Power=");
|
||||
LOG1(power->getNewVal()?"true":"false");
|
||||
}
|
||||
|
||||
if(level->updated()){
|
||||
LOG1(" New Brightness=");
|
||||
LOG1(level->getNewVal());
|
||||
}
|
||||
// As above, we use the multi-argument form of LOG() to create the messages
|
||||
// Note that for DimmableLED, ledPin has a method getPin() that retrieves the
|
||||
// pin number so you don't need to store it separately.
|
||||
|
||||
LOG1("Updating Dimmable LED on pin=%dL Current Power=%s Current Brightness=%d",ledPin->getPin(),power->getVal()?"true":"false",level->getVal());
|
||||
|
||||
if(power->updated())
|
||||
LOG1(" New Power=%s",power->getNewVal()?"true":"false");
|
||||
|
||||
if(level->updated())
|
||||
LOG1(" New Brightness=%d",level->getNewVal());
|
||||
|
||||
LOG1("\n");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Gregg E. Berman
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -37,13 +37,12 @@
|
|||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_LED.h"
|
||||
#include "DEV_Identify.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// Example 10 illustrates how to control an RGB LED to set any color and brightness.
|
||||
// The configuration below should look familiar by now. We've created a new derived Service,
|
||||
// call RgbLED to house all the required logic. You'll find all the code in DEV_LED.h.
|
||||
// called DEV_RgbLED to house all the required logic. You'll find all the code in DEV_LED.h.
|
||||
// For completeness, this configuration also contains an on/off LED and a dimmable LED as shown
|
||||
// in prior examples.
|
||||
|
||||
|
|
@ -51,22 +50,26 @@ void setup() {
|
|||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Bridge #1","HomeSpan","123-ABC","HS Bridge","0.9",3);
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("On/Off LED","HomeSpan","123-ABC","20mA LED","0.9",0);
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Simple LED");
|
||||
new DEV_LED(16); // Create an On/Off LED attached to pin 16
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Dimmable LED","HomeSpan","123-ABC","20mA LED","0.9",0);
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Dimmable LED");
|
||||
new DEV_DimmableLED(17); // Create a Dimmable (PWM-driven) LED using attached to pin 17
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("RGB LED","HomeSpan","123-ABC","20mA LED","0.9",0);
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("RGB LED");
|
||||
new DEV_RgbLED(32,22,23); // Create an RGB LED attached to pins 32,22,23 (for R, G, and B LED anodes)
|
||||
|
||||
} // end of setup()
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
|
||||
//////////////////////////////////
|
||||
// DEVICE-SPECIFIC SERVICES //
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_Identify : Service::AccessoryInformation {
|
||||
|
||||
int nBlinks; // number of times to blink built-in LED in identify routine
|
||||
SpanCharacteristic *identify; // reference to the Identify Characteristic
|
||||
|
||||
DEV_Identify(const char *name, const char *manu, const char *sn, const char *model, const char *version, int nBlinks) : Service::AccessoryInformation(){
|
||||
|
||||
new Characteristic::Name(name); // create all the required Characteristics with values set based on above arguments
|
||||
new Characteristic::Manufacturer(manu);
|
||||
new Characteristic::SerialNumber(sn);
|
||||
new Characteristic::Model(model);
|
||||
new Characteristic::FirmwareRevision(version);
|
||||
identify=new Characteristic::Identify(); // store a reference to the Identify Characteristic for use below
|
||||
|
||||
this->nBlinks=nBlinks; // store the number of times to blink the LED
|
||||
|
||||
pinMode(homeSpan.getStatusPin(),OUTPUT); // make sure LED is set for output
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
for(int i=0;i<nBlinks;i++){
|
||||
digitalWrite(homeSpan.getStatusPin(),LOW);
|
||||
delay(250);
|
||||
digitalWrite(homeSpan.getStatusPin(),HIGH);
|
||||
delay(250);
|
||||
}
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
};
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// HomeSpan: A HomeKit implementation for the ESP32 //
|
||||
// ------------------------------------------------ //
|
||||
// //
|
||||
// Example 11: Service Names: //
|
||||
// * setting the names of individual Services //
|
||||
// * changing the icons in a bridge Accessory //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
// When pairing, the Home App will choose default names for each Service (such as Fan, Fan 2, Light, Light 2) depending on the types
|
||||
// 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. 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,
|
||||
// such as a Accessory with two 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.
|
||||
|
||||
// In the example below we create 5 different functional Accessories, each illustrating how names, as well as icons, are chosen by the Home App
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
// This device will be configured as a Bridge, with the Category set to Bridges
|
||||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
// Our first Accessory is the "Bridge" Accessory
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
// Our second Accessory is a Ceiling Fan with a single Light. There are three things to note:
|
||||
//
|
||||
// * when pairing, the Home App will generate default names of "Light" and "Fan" for the two Services.
|
||||
// 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 Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Light with Fan"); // 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 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.
|
||||
// The result is that the Home App now displays the Accessory Tile with a Fan icon intead of a Lightbulb icon.
|
||||
// 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 Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Fan with Light"); // 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 Characteristic::Active();
|
||||
new Service::LightBulb();
|
||||
new Characteristic::On();
|
||||
|
||||
// Our fourth Accessory shows what happens if we implement two identical LightBulb Services (without any Fan Service).
|
||||
// Since both Services are LightBulbs, the Home App sensibly picks a Lightbulb icon for the Accessory Tile. However,
|
||||
// when you click the Accessory Tile and open the control screen, you'll note that the Home App now does display the names
|
||||
// of the Service beneath each control. In this case the Home App uses the default names "Light 1" and "Light 2". The Home App
|
||||
// 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 Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Ceiling Lights"); // this sets the name of the Accessory Tile
|
||||
new Service::LightBulb();
|
||||
new Characteristic::On();
|
||||
new Service::LightBulb();
|
||||
new Characteristic::On();
|
||||
|
||||
// Our fifth Accessory combines a single Fan Service with two identical LightBulb Services. Since the first functional Service implemented
|
||||
// is a Fan, the Home App will pick a Fan icon for the Accessory Tile. Also, since we added Name Characteristics to two LightBulb
|
||||
// Services, their default names generated by the Home App ("Light 1" and "Light 2") will be changed to the names specified. Finally,
|
||||
// note that the Home App displays a more compact form of controls on the control screen since there are three Services. The arrangement
|
||||
// and style of the controls will depend on what combination of Characteristics are implemented for each Service.
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Fan with Lights"); // this sets the name of the Accessory Tile
|
||||
new Service::Fan();
|
||||
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 Service::LightBulb();
|
||||
new Characteristic::Name("Night Light"); // this changes the default name of this LightBulb Service from "Light 2" to "Night Light"
|
||||
new Characteristic::On();
|
||||
|
||||
// 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()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// HomeSpan: A HomeKit implementation for the ESP32 //
|
||||
// ------------------------------------------------ //
|
||||
// //
|
||||
// Example 11: Service Options: //
|
||||
// * setting the Primary Service //
|
||||
// * setting Service Names //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_LED.h"
|
||||
#include "DEV_Identify.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// Every Accessory we define has at least two Services, one of which will always be the required Accessory Information Service (and
|
||||
// which we've conveniently wrapped into a derived Service we called DEV_Identify). The second is usually a Service that performs
|
||||
// some sort of actual operation, such as the LightBulb Service or the Fan Service. It's this second operative Service that creates
|
||||
// the controls we see in the HomeKit iOS or MacOS application. These appear as tiles with a lightbulb control or fan control.
|
||||
|
||||
// We've also created Accessories with more than two operational Service, such as our definition of a ceiling fan that includes
|
||||
// BOTH a Fan Service and a LightBulb Service. The HomeKit application can display an Accessory with two or more operational Services
|
||||
// in one of two ways: one way is to display each Service as a separate tile, so that our ceiling fan would show up as one standalone
|
||||
// lightbulb tile for the LightBulb Service and one standalone fan tile for the Fan Service. The second way is for HomeKit to display
|
||||
// our ceiling fan as a single tile that you click to open a new screen showing both the fan control and the lightbulb control side by side.
|
||||
|
||||
// HomeSpan has no control over whether HomeKit displays multiple Services as separate tiles or as a single combined single tile.
|
||||
// This is determined by the user from within the HomeKit iOS or MacOS application (the default is to use the combined tile mode).
|
||||
// However, HomeSpan does have control over the which icon is used to display the ceiling fan in combined-tile mode. Should it be a
|
||||
// lightbulb or a fan?
|
||||
|
||||
// HomeKit determines which icon to show on the combined tile according to what is considered the Primary Service of the Accessory.
|
||||
// HomeKit will also list the Primary Service first when you click a combined tile to open its controls.
|
||||
|
||||
// A Service can be set to Primary with the setPrimary() method. The easiest way to do this is by "chaining" setPrimary() to the
|
||||
// end of a new Service when first instantiated. See below for examples of how to do this.
|
||||
|
||||
// To begin, we first initialize HomeSpan and define our Bridge Accessory as in the previous examples:
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Bridge #1","HomeSpan","123-ABC","HS Bridge","0.9",3);
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
|
||||
// Next, we create two Accessories each configured to control a Ceiling Fan containing a bi-directional, multi-speed fan AND a dimmable light.
|
||||
// In Ceiling Fan #1 we specify the Dimmable LED as the Primary Service. In Ceiling Fan #2 we specify the Fan as the Primary Service.
|
||||
// If you set HomeKit to display each of these Accessories as combined tiles, you'll immediately see the difference. Ceiling Fan #1 shows as
|
||||
// Lightbulb Tile with the dimmable LED listed first when you click open its controls. Ceiling Fan #2 shows as a Fan Tile with the fan listed first
|
||||
// when you click open its controls. Nice and easy.
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Ceiling Fan #1","HomeSpan","123-ABC","20mA LED","0.9",0);
|
||||
(new DEV_DimmableLED(17))->setPrimary(); // Here we specify DEV_DimmableLED as the Primary Service by "chaining" setPrimary() to the pointer return by new. Note parentheses!
|
||||
new Service::Fan();
|
||||
new Characteristic::Active();
|
||||
new Characteristic::RotationDirection();
|
||||
new Characteristic::RotationSpeed(0);
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Ceiling Fan #2","HomeSpan","123-ABC","20mA LED","0.9",0);
|
||||
new DEV_DimmableLED(17);
|
||||
(new Service::Fan())->setPrimary(); // Here we specify the Fan as the Primary Service. Again, note how we encapsulated the "new" command in parentheses, then chained setPrimary()
|
||||
new Characteristic::Active();
|
||||
new Characteristic::RotationDirection();
|
||||
new Characteristic::RotationSpeed(0);
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
// In addition to being able to specify which Service in an Accessory is the Primary Service, HomeKit also allows you to give names to each of
|
||||
// the individual Services. This is done by instantiating a Name Characteristic for a Service. We've already used this feature in creating
|
||||
// DEV_Identify --- the first argument is used to to name the Service (see DEV_Identify.h). In fact, the Name Characteristic is required
|
||||
// for the AccessoryInformation Service, so we had to instantiate as part of DEV_Identify.
|
||||
|
||||
// For all other Services the name Characteristic is optional. If not instantiated, the name will be defaulted to whatever name we specified
|
||||
// in DEV_Identify, which means that if we have more than one operational Service in an Accessory, they will all be named the same.
|
||||
// This is not necessarily a problem since names don't always come into play in the HomeKit interface. In the examples above, the only name
|
||||
// that gets displayed in combined-tile mode is "Ceiling Fan #1" or "Ceiling Fan #2", which makes sense. When you click open the controls
|
||||
// for either Accessory you see a lightbulb control and a fan control. They are not individually named, but the controls look different (one
|
||||
// is a light control, the other a fan control) so there is no confusion.
|
||||
|
||||
// If instead you set HomeKit to display the controls for these Accessories as separate tiles, you'll see that each of the light and fan controls
|
||||
// has their own name. But since in the above examples we did not provide specific names for each of these Services, they will simply inherit the
|
||||
// name "Ceiling Fan #1" or "Ceiling Fan #2". Again, there is no confusion since the light and fan controls each look different.
|
||||
|
||||
// The situation becomes more interesting when you have an Accessory with 3 or more operational Services. Sometimes HomeKit will display the names
|
||||
// of the Services on the control panel even in combined-tile mode. Consider our ceiling fan example above, but with the added feature of a night-light,
|
||||
// which we will represent as a simple On/Off LED. Let's instantiate the Name Characteristic for each Service, as shown below.
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Ceiling Fan #3","HomeSpan","123-ABC","20mA LED","0.9",0);
|
||||
new DEV_DimmableLED(17);
|
||||
new Characteristic::Name("Main Light"); // Here we create a name for the Dimmable LED
|
||||
new DEV_LED(16);
|
||||
new Characteristic::Name("Night Light"); // Here we create a name for the On/Off LED
|
||||
(new Service::Fan())->setPrimary();
|
||||
new Characteristic::Active();
|
||||
new Characteristic::RotationDirection();
|
||||
new Characteristic::RotationSpeed(0);
|
||||
new Characteristic::Name("Fan"); // Here we create a name for the Fan
|
||||
|
||||
// If you let HomeKit display this as a single, combined tile, you'll notice two things. The first is that the name of the tile is now "Fan" instead
|
||||
// of "Ceiling Fan #3". Why is that? It's because we set Fan to be the Primary Service AND gave it a name --- this is the name that shows up
|
||||
// on the combined tile. If we did not give it a name, it would have inherited the name "Ceiling Fan #3", which would have been the name of the tile
|
||||
// as in the prior example.
|
||||
|
||||
// The second thing you'll notice is that these names now appear next to each control if you click open the combined tile. It says "Fan" next to the Fan
|
||||
// control, "Main Light" next to the Dimmable LED control, and "Night Light" next to the On/Off LED control.
|
||||
|
||||
// If instead you tell HomeKit to display the controls for Ceiling Fan #3 as three separate tiles, you'll see that each tile contains the name specified
|
||||
// above for that Service. In some circumstances that can be helpful, in others it can be confusing. For instance, if you had two ceiling fans that
|
||||
// each had a main light and a night night, how would you know which "Main Light" is which? One solution is to create names like "Main Light #3" and
|
||||
// "Main Light #4". The other solution is to keep them combined in a single tile, but keep the name "Ceiling Fan #3" for the combined tile, instead of
|
||||
// having it over-ridden with the word "Fan"
|
||||
|
||||
// This is easily done by specifying DEV_Identify as the Primary Service, instead of Fan, as follows:
|
||||
|
||||
new SpanAccessory();
|
||||
(new DEV_Identify("Ceiling Fan #4","HomeSpan","123-ABC","20mA LED","0.9",0))->setPrimary(); // specify DEV_Identify as the Primary Service
|
||||
new DEV_DimmableLED(17);
|
||||
new Characteristic::Name("Main Light");
|
||||
new DEV_LED(16);
|
||||
new Characteristic::Name("Night Light");
|
||||
new Service::Fan();
|
||||
new Characteristic::Active();
|
||||
new Characteristic::RotationDirection();
|
||||
new Characteristic::RotationSpeed(0);
|
||||
new Characteristic::Name("Fan");
|
||||
|
||||
// HomeKit now shows the name "Ceiling Fan #4" for the combined tile AND it still shows the individual names for each control when you click open the tile.
|
||||
// The only downside to this configuration is that since the Fan is no longer specified as the Primary Service, the main icon on the combined tile now shows
|
||||
// as a lightbulb, instead of the fan. HomeKit documentation is not clear on how the main icon is chosen under these circumstances, but I've found
|
||||
// that changing the order of Services as they are instantiated can impact the icon. Here is the same example as above, but with the Fan
|
||||
// instantiated as the first operational Service, ahead of the Main Light and Night Night:
|
||||
|
||||
new SpanAccessory();
|
||||
(new DEV_Identify("Ceiling Fan #5","HomeSpan","123-ABC","20mA LED","0.9",0))->setPrimary(); // specify DEV_Identify as the Primary Service
|
||||
new Service::Fan();
|
||||
new Characteristic::Active();
|
||||
new Characteristic::RotationDirection();
|
||||
new Characteristic::RotationSpeed(0);
|
||||
new Characteristic::Name("Fan");
|
||||
new DEV_DimmableLED(17);
|
||||
new Characteristic::Name("Main Light");
|
||||
new DEV_LED(16);
|
||||
new Characteristic::Name("Night Light");
|
||||
|
||||
// This seems to cause HomeKit to display the Fan icon on the combined tile, as well as to list the Fan Service control first when the tile is clicked
|
||||
// open, almost as if it were the Primary Service in all ways except for its name.
|
||||
|
||||
// As you can see, there is no right or wrong way for how to name your Accessories and Services, including whether or not to even bother naming your
|
||||
// individual Services. It's purely a matter of taste - experiment and see which combinations best serve your purposes.
|
||||
|
||||
// IMPORTANT: HomeKit tries to cache as many items as possible, and although it should know when configurations change, it does not always respond
|
||||
// as expected. If you are experimenting and find that name changes are NOT being reflected in the HomeKit interface, simply unpair HomeSpan and
|
||||
// re-pair. This often causes a refresh. If not, after unpairing you can additionally reset the HAP data in HomeSpan so that its HAP ID changes (see
|
||||
// HomeSpan documentation for how to easily do this). This way when you re-pair, HomeKit should think this is a completely new device and start with
|
||||
// a clean slate. *** In some limited circumstances, HomeKit may get so confused it refuses to operate the Accessories at all. You may get a No Response
|
||||
// icon even though HomeSpan is operating correctly. Though it is possible to misconfigure HomeSpan in ways that cause this, if you do get this
|
||||
// error please try the re-pairing methods above as it often fixes the problem.
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
|
||||
//////////////////////////////////
|
||||
// DEVICE-SPECIFIC SERVICES //
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_Identify : Service::AccessoryInformation {
|
||||
|
||||
int nBlinks; // number of times to blink built-in LED in identify routine
|
||||
SpanCharacteristic *identify; // reference to the Identify Characteristic
|
||||
|
||||
DEV_Identify(const char *name, const char *manu, const char *sn, const char *model, const char *version, int nBlinks) : Service::AccessoryInformation(){
|
||||
|
||||
new Characteristic::Name(name); // create all the required Characteristics with values set based on above arguments
|
||||
new Characteristic::Manufacturer(manu);
|
||||
new Characteristic::SerialNumber(sn);
|
||||
new Characteristic::Model(model);
|
||||
new Characteristic::FirmwareRevision(version);
|
||||
identify=new Characteristic::Identify(); // store a reference to the Identify Characteristic for use below
|
||||
|
||||
this->nBlinks=nBlinks; // store the number of times to blink the LED
|
||||
|
||||
pinMode(homeSpan.getStatusPin(),OUTPUT); // make sure LED is set for output
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
for(int i=0;i<nBlinks;i++){
|
||||
digitalWrite(homeSpan.getStatusPin(),LOW);
|
||||
delay(250);
|
||||
digitalWrite(homeSpan.getStatusPin(),HIGH);
|
||||
delay(250);
|
||||
}
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
};
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
#include "extras/PwmPin.h" // library of various PWM functions
|
||||
|
||||
////////////////////////////////////
|
||||
|
||||
struct DEV_LED : Service::LightBulb { // ON/OFF LED
|
||||
|
||||
int ledPin; // pin number defined for this LED
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
|
||||
DEV_LED(int ledPin) : Service::LightBulb(){
|
||||
|
||||
power=new Characteristic::On();
|
||||
this->ledPin=ledPin;
|
||||
pinMode(ledPin,OUTPUT);
|
||||
|
||||
Serial.print("Configuring On/Off LED: Pin="); // initialization message
|
||||
Serial.print(ledPin);
|
||||
Serial.print("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
LOG1("Updating On/Off LED on pin=");
|
||||
LOG1(ledPin);
|
||||
LOG1(": Current Power=");
|
||||
LOG1(power->getVal()?"true":"false");
|
||||
LOG1(" New Power=");
|
||||
LOG1(power->getNewVal()?"true":"false");
|
||||
LOG1("\n");
|
||||
|
||||
digitalWrite(ledPin,power->getNewVal());
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
|
||||
|
||||
LedPin *ledPin; // reference to Led Pin
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
SpanCharacteristic *level; // reference to the Brightness Characteristic
|
||||
|
||||
DEV_DimmableLED(int pin) : Service::LightBulb(){ // constructor() method
|
||||
|
||||
power=new Characteristic::On();
|
||||
|
||||
level=new Characteristic::Brightness(50); // Brightness Characteristic with an initial value of 50%
|
||||
level->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%
|
||||
|
||||
this->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin
|
||||
|
||||
Serial.print("Configuring Dimmable LED: Pin="); // initialization message
|
||||
Serial.print(ledPin->getPin());
|
||||
Serial.print("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
LOG1("Updating Dimmable LED on pin=");
|
||||
LOG1(ledPin->getPin());
|
||||
LOG1(": Current Power=");
|
||||
LOG1(power->getVal()?"true":"false");
|
||||
LOG1(" Current Brightness=");
|
||||
LOG1(level->getVal());
|
||||
|
||||
if(power->updated()){
|
||||
LOG1(" New Power=");
|
||||
LOG1(power->getNewVal()?"true":"false");
|
||||
}
|
||||
|
||||
if(level->updated()){
|
||||
LOG1(" New Brightness=");
|
||||
LOG1(level->getNewVal());
|
||||
}
|
||||
|
||||
LOG1("\n");
|
||||
|
||||
ledPin->set(power->getNewVal()*level->getNewVal());
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Gregg E. Berman
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -37,7 +37,6 @@
|
|||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_Identify.h"
|
||||
#include "DEV_Sensors.h"
|
||||
|
||||
void setup() {
|
||||
|
|
@ -91,16 +90,19 @@ void setup() {
|
|||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Bridge #1","HomeSpan","123-ABC","HS Bridge","0.9",3);
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Temp Sensor","HomeSpan","123-ABC","Sensor","0.9",0);
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Temp Sensor");
|
||||
new DEV_TempSensor(); // Create a Temperature Sensor (see DEV_Sensors.h for definition)
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Air Quality","HomeSpan","123-ABC","Sensor","0.9",0);
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Air Quality");
|
||||
new DEV_AirQualitySensor(); // Create an Air Quality Sensor (see DEV_Sensors.h for definition)
|
||||
|
||||
} // end of setup()
|
||||
|
|
@ -114,34 +116,3 @@ void loop(){
|
|||
} // end of loop()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
|
||||
// Additional Technical Notes about Event Notifications in HomeKit
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
// HomeKit is designed for two-way communication: HomeSpan devices not only receive and act on operational instructions from HomeKit Controllers, but
|
||||
// HomeSpan can also send HomeKit unsolicited messages regarding changes to the state of the device. Though it may not be apparent, this has already been
|
||||
// ocurring in the background in all prior examples. This is because when a HomeKit Controller sends an operational request to any HomeKit device, it expects
|
||||
// to receive a status message back indicating whether the request was successful or not. This is the purpose of returning StatusCode:OK in custom update()
|
||||
// methods. With this information returned, HomeKit can update its own status and properly reflect a change in the device, such as by showing a light is now
|
||||
// turned on instead of off. However, HomeKit unfortunately does NOT inform any other HomeKit Controllers of this new information. So if you have two iPhones
|
||||
// and use one to turn on a light, this iPhone does not relay a message to the second iPhone that a light has been turned on. This is the case even
|
||||
// if you are using an AppleTV or HomePod as a central hub for HomeKit.
|
||||
|
||||
// Normally this does not matter much, since the second iPhone will automatically update itself as to the status of all HomeKit devices as soon as the HomeKit
|
||||
// application is launched on that iPhone. It does this by sending every HomeKit device a message asking for a status update. In this fashion the second
|
||||
// iPhone quickly synchronizes itself as soon as its HomeKit app is opened, but ONLY when it is first opened (or re-opened if you first close it). However, if you
|
||||
// have two iPhones BOTH opened to the HomeKit app (or one iPhone and one Mac opened to the HomeKit app) and you use one Controller app to turn on a light, the
|
||||
// resulting change in status of that light will NOT be reflected in the second Controller app, unless you close tha app and re-open (at which point it goes
|
||||
// through the request procedure discussed above). This can be very annoying and counterintuitive.
|
||||
|
||||
// Fortunately, HomeKit provides a solution to this in the form of an Event Notification protcol. This protcol allows a device to send unsoliciated messages
|
||||
// to all Controllers that have previously registered themselves with the device indicating the Characteristics for which they would like to receive an event
|
||||
// message from the device whenever there is a change in the status of one or more of those Characteristics.
|
||||
|
||||
// HomeSpan takes care of this automatically, and has being doing so in the background in all prior examples. To see this for yourself, use two iPhones
|
||||
// (or an iPhone and Mac) with any of the previous examples and open the HomeKit app on both. Any changes you make to the device using one of the Controllers,
|
||||
// such as turning on an LED, is immediately reflected in the other Controller. Not quite magic, but close.
|
||||
|
||||
// As described above, and fully explored in this example, HomeSpan automatically generates Event Notification messages and transmits them to all registered
|
||||
// Controllers every time you change the value of a Characteristic using the setVal() function from within a derived Service's loop() method.
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
|
||||
//////////////////////////////////
|
||||
// DEVICE-SPECIFIC SERVICES //
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_Identify : Service::AccessoryInformation {
|
||||
|
||||
int nBlinks; // number of times to blink built-in LED in identify routine
|
||||
SpanCharacteristic *identify; // reference to the Identify Characteristic
|
||||
|
||||
DEV_Identify(const char *name, const char *manu, const char *sn, const char *model, const char *version, int nBlinks) : Service::AccessoryInformation(){
|
||||
|
||||
new Characteristic::Name(name); // create all the required Characteristics with values set based on above arguments
|
||||
new Characteristic::Manufacturer(manu);
|
||||
new Characteristic::SerialNumber(sn);
|
||||
new Characteristic::Model(model);
|
||||
new Characteristic::FirmwareRevision(version);
|
||||
identify=new Characteristic::Identify(); // store a reference to the Identify Characteristic for use below
|
||||
|
||||
this->nBlinks=nBlinks; // store the number of times to blink the LED
|
||||
|
||||
pinMode(homeSpan.getStatusPin(),OUTPUT); // make sure LED is set for output
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
for(int i=0;i<nBlinks;i++){
|
||||
digitalWrite(homeSpan.getStatusPin(),LOW);
|
||||
delay(250);
|
||||
digitalWrite(homeSpan.getStatusPin(),HIGH);
|
||||
delay(250);
|
||||
}
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Gregg E. Berman
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -37,7 +37,6 @@
|
|||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_Identify.h"
|
||||
#include "DEV_DoorsWindows.h"
|
||||
|
||||
void setup() {
|
||||
|
|
@ -78,18 +77,20 @@ void setup() {
|
|||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Bridge #1","HomeSpan","123-ABC","HS Bridge","0.9",3);
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Garage Door","HomeSpan","123-ABC","Door","0.9",0);
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Garage Door");
|
||||
new DEV_GarageDoor(); // Create a Garage Door Opener (see DEV_DoorsWindows.h for definition)
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Window Shade","HomeSpan","123-ABC","Shade","0.9",0);
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Window Shade");
|
||||
new DEV_WindowShade(); // Create a motorized Window Shade (see DEV_DoorsWindows.h for definition)
|
||||
|
||||
} // end of setup()
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
|
||||
//////////////////////////////////
|
||||
// DEVICE-SPECIFIC SERVICES //
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_Identify : Service::AccessoryInformation {
|
||||
|
||||
int nBlinks; // number of times to blink built-in LED in identify routine
|
||||
SpanCharacteristic *identify; // reference to the Identify Characteristic
|
||||
|
||||
DEV_Identify(const char *name, const char *manu, const char *sn, const char *model, const char *version, int nBlinks) : Service::AccessoryInformation(){
|
||||
|
||||
new Characteristic::Name(name); // create all the required Characteristics with values set based on above arguments
|
||||
new Characteristic::Manufacturer(manu);
|
||||
new Characteristic::SerialNumber(sn);
|
||||
new Characteristic::Model(model);
|
||||
new Characteristic::FirmwareRevision(version);
|
||||
identify=new Characteristic::Identify(); // store a reference to the Identify Characteristic for use below
|
||||
|
||||
this->nBlinks=nBlinks; // store the number of times to blink the LED
|
||||
|
||||
pinMode(homeSpan.getStatusPin(),OUTPUT); // make sure LED is set for output
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
for(int i=0;i<nBlinks;i++){
|
||||
digitalWrite(homeSpan.getStatusPin(),LOW);
|
||||
delay(250);
|
||||
digitalWrite(homeSpan.getStatusPin(),HIGH);
|
||||
delay(250);
|
||||
}
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Gregg E. Berman
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -36,7 +36,6 @@
|
|||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_Blinker.h"
|
||||
#include "DEV_Identify.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
|
|
@ -82,14 +81,15 @@ void setup() {
|
|||
// Defines the Bridge Accessory
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Bridge #1","HomeSpan","123-ABC","HS Bridge","0.9",3);
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
// *** NEW *** defines an LED Blinker Accessory attached to pin 16 which blinks 3 times
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("LED Blinker","HomeSpan","123-ABC","20mA LED","0.9",0);
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("LED Blinker");
|
||||
new DEV_Blinker(16,3); // DEV_Blinker takes two arguments - pin, and number of times to blink
|
||||
|
||||
} // end of setup()
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
|
||||
//////////////////////////////////
|
||||
// DEVICE-SPECIFIC SERVICES //
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_Identify : Service::AccessoryInformation {
|
||||
|
||||
int nBlinks; // number of times to blink built-in LED in identify routine
|
||||
SpanCharacteristic *identify; // reference to the Identify Characteristic
|
||||
|
||||
DEV_Identify(const char *name, const char *manu, const char *sn, const char *model, const char *version, int nBlinks) : Service::AccessoryInformation(){
|
||||
|
||||
new Characteristic::Name(name); // create all the required Characteristics with values set based on above arguments
|
||||
new Characteristic::Manufacturer(manu);
|
||||
new Characteristic::SerialNumber(sn);
|
||||
new Characteristic::Model(model);
|
||||
new Characteristic::FirmwareRevision(version);
|
||||
identify=new Characteristic::Identify(); // store a reference to the Identify Characteristic for use below
|
||||
|
||||
this->nBlinks=nBlinks; // store the number of times to blink the LED
|
||||
|
||||
pinMode(homeSpan.getStatusPin(),OUTPUT); // make sure LED is set for output
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
for(int i=0;i<nBlinks;i++){
|
||||
digitalWrite(homeSpan.getStatusPin(),LOW);
|
||||
delay(250);
|
||||
digitalWrite(homeSpan.getStatusPin(),HIGH);
|
||||
delay(250);
|
||||
}
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Gregg E. Berman
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -38,7 +38,6 @@
|
|||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_LED.h"
|
||||
#include "DEV_Identify.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
|
|
@ -132,12 +131,13 @@ void setup() {
|
|||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Bridge #1","HomeSpan","123-ABC","HS Bridge","0.9",3);
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("PushButton LED","HomeSpan","123-ABC","20mA LED","0.9",0);
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("PushButton LED");
|
||||
|
||||
new DEV_DimmableLED(17,23,5,18); // NEW! added three extra arguments to specify the pin numbers for three SpanButtons() - see DEV_LED.h
|
||||
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
|
||||
//////////////////////////////////
|
||||
// DEVICE-SPECIFIC SERVICES //
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_Identify : Service::AccessoryInformation {
|
||||
|
||||
int nBlinks; // number of times to blink built-in LED in identify routine
|
||||
SpanCharacteristic *identify; // reference to the Identify Characteristic
|
||||
|
||||
DEV_Identify(const char *name, const char *manu, const char *sn, const char *model, const char *version, int nBlinks) : Service::AccessoryInformation(){
|
||||
|
||||
new Characteristic::Name(name); // create all the required Characteristics with values set based on above arguments
|
||||
new Characteristic::Manufacturer(manu);
|
||||
new Characteristic::SerialNumber(sn);
|
||||
new Characteristic::Model(model);
|
||||
new Characteristic::FirmwareRevision(version);
|
||||
identify=new Characteristic::Identify(); // store a reference to the Identify Characteristic for use below
|
||||
|
||||
this->nBlinks=nBlinks; // store the number of times to blink the LED
|
||||
|
||||
pinMode(homeSpan.getStatusPin(),OUTPUT); // make sure LED is set for output
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
for(int i=0;i<nBlinks;i++){
|
||||
digitalWrite(homeSpan.getStatusPin(),LOW);
|
||||
delay(250);
|
||||
digitalWrite(homeSpan.getStatusPin(),HIGH);
|
||||
delay(250);
|
||||
}
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Gregg E. Berman
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -36,7 +36,6 @@
|
|||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_ProgButton.h"
|
||||
#include "DEV_Identify.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
|
|
@ -66,12 +65,13 @@ void setup() {
|
|||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Bridge #1","HomeSpan","123-ABC","HS Bridge","0.9",3);
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("PushButton Switches","HomeSpan","123-ABC","Prog Switches","0.9",0);
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("PushButton Switches");
|
||||
|
||||
// We've written DEV_ProgButton to take two arguments. The first is a pin number that DEV_ProgButton.h uses to create a SpanButton. The second is an index number
|
||||
// that HomeKit uses as a label when you program the actions of each button in the Home App. The numbers do not have to be sequential, nor start with 1. They just need
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
|
||||
//////////////////////////////////
|
||||
// DEVICE-SPECIFIC SERVICES //
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_Identify : Service::AccessoryInformation {
|
||||
|
||||
int nBlinks; // number of times to blink built-in LED in identify routine
|
||||
SpanCharacteristic *identify; // reference to the Identify Characteristic
|
||||
|
||||
DEV_Identify(const char *name, const char *manu, const char *sn, const char *model, const char *version, int nBlinks) : Service::AccessoryInformation(){
|
||||
|
||||
new Characteristic::Name(name); // create all the required Characteristics with values set based on above arguments
|
||||
new Characteristic::Manufacturer(manu);
|
||||
new Characteristic::SerialNumber(sn);
|
||||
new Characteristic::Model(model);
|
||||
new Characteristic::FirmwareRevision(version);
|
||||
identify=new Characteristic::Identify(); // store a reference to the Identify Characteristic for use below
|
||||
|
||||
this->nBlinks=nBlinks; // store the number of times to blink the LED
|
||||
|
||||
pinMode(homeSpan.getStatusPin(),OUTPUT); // make sure LED is set for output
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
for(int i=0;i<nBlinks;i++){
|
||||
digitalWrite(homeSpan.getStatusPin(),LOW);
|
||||
delay(250);
|
||||
digitalWrite(homeSpan.getStatusPin(),HIGH);
|
||||
delay(250);
|
||||
}
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2021 Gregg E. Berman
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -35,6 +35,12 @@
|
|||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
// 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"
|
||||
|
||||
// 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
|
||||
|
|
@ -102,21 +108,21 @@
|
|||
// Note that the Shower Service only monitors the InUse Characteristics of its Linked Valves. It does not monitor the Active Characteristics of the Linked Valves. Also, turning
|
||||
// on and off the Shower Switch should NOT change the Active Characteristic of any Valve. Below is the code that implements all of this HAP-required logic:
|
||||
|
||||
struct Shower:Service::Faucet{ // this is our Shower structure, which we define as a child class of the HomeSpan Faucet structure
|
||||
struct Shower : Service::Faucet { // this is our Shower structure, which we define as a child class of the HomeSpan Faucet structure
|
||||
|
||||
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)
|
||||
for(int i=0;i<nHeads;i++) // for each spray head needed ---
|
||||
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.
|
||||
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.
|
||||
}
|
||||
|
||||
struct WaterValve:Service::Valve{ // here we define our WaterValve structure as a child class of the HomeSpan Valve Service
|
||||
SpanCharacteristic *active=new Characteristic::Active();; // the Active Characteristic is used to specify whether the Valve is Active (open) or Inactive (closed)
|
||||
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 *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
|
||||
|
||||
WaterValve(Shower *s){ // this is constructor for WaterValve. It takes a single argument that points 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
|
||||
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)
|
||||
}
|
||||
|
|
@ -145,19 +151,10 @@ void setup() {
|
|||
|
||||
homeSpan.begin(Category::ShowerSystems,"HomeSpan Shower");
|
||||
|
||||
new SpanAccessory();
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, which has 6 required Characteristics
|
||||
new Characteristic::Name("Spa Shower");
|
||||
new Characteristic::Manufacturer("HomeSpan");
|
||||
new Characteristic::SerialNumber("HSL-123");
|
||||
new Characteristic::Model("HSL Test");
|
||||
new Characteristic::FirmwareRevision(HOMESPAN_VERSION);
|
||||
new Characteristic::Identify();
|
||||
|
||||
new Service::HAPProtocolInformation(); // Create the HAP Protcol Information Service
|
||||
new Characteristic::Version("1.1.0");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new Shower(4); // Create a Spa Shower with 4 spray heads
|
||||
|
||||
} // end of setup()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021 Gregg E. Berman
|
||||
* Copyright (c) 2021-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -38,7 +38,6 @@
|
|||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_LED.h"
|
||||
#include "DEV_Identify.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
|
|
@ -76,16 +75,19 @@ void setup() {
|
|||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("Bridge #1","HomeSpan","123-ABC","HS Bridge","0.9",3);
|
||||
new Service::HAPProtocolInformation();
|
||||
new Characteristic::Version("1.1.0");
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("LED 1","HomeSpan","123-ABC","20mA LED","0.9",0);
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("LED 1");
|
||||
new DEV_DimmableLED(17,19); // The first argument specifies the LED pin; the second argument specifies the PushButton pin
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("LED 2","HomeSpan","123-ABC","20mA LED","0.9",0);
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("LED 2");
|
||||
new DEV_DimmableLED(16,18); // The first argument specifies the LED pin; the second argument specifies the PushButton pin
|
||||
|
||||
} // end of setup()
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
|
||||
//////////////////////////////////
|
||||
// DEVICE-SPECIFIC SERVICES //
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_Identify : Service::AccessoryInformation {
|
||||
|
||||
int nBlinks; // number of times to blink built-in LED in identify routine
|
||||
SpanCharacteristic *identify; // reference to the Identify Characteristic
|
||||
|
||||
DEV_Identify(const char *name, const char *manu, const char *sn, const char *model, const char *version, int nBlinks) : Service::AccessoryInformation(){
|
||||
|
||||
new Characteristic::Name(name); // create all the required Characteristics with values set based on above arguments
|
||||
new Characteristic::Manufacturer(manu);
|
||||
new Characteristic::SerialNumber(sn);
|
||||
new Characteristic::Model(model);
|
||||
new Characteristic::FirmwareRevision(version);
|
||||
identify=new Characteristic::Identify(); // store a reference to the Identify Characteristic for use below
|
||||
|
||||
this->nBlinks=nBlinks; // store the number of times to blink the LED
|
||||
|
||||
pinMode(homeSpan.getStatusPin(),OUTPUT); // make sure LED is set for output
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
for(int i=0;i<nBlinks;i++){
|
||||
digitalWrite(homeSpan.getStatusPin(),LOW);
|
||||
delay(250);
|
||||
digitalWrite(homeSpan.getStatusPin(),HIGH);
|
||||
delay(250);
|
||||
}
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
};
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// HomeSpan: A HomeKit implementation for the ESP32 //
|
||||
// ------------------------------------------------ //
|
||||
// //
|
||||
// Example 19: Web Logging with time-keeping //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_LED.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// This is a duplicate of Example 5 (Two Working LEDs) with the addition of HomeSpan Web Logging
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
// Below we enable Web Logging. The first parameter sets the maximum number of log messages to save. As the
|
||||
// log fills with messages, older ones are replaced by newer ones. The second parameter specifies a Timer Server
|
||||
// that HomeSpan calls to set the device clock. Setting the clock is optional, and you can leave this
|
||||
// argument blank (or set to NULL) if you don't care about setting the absolute time of the device. The third
|
||||
// argument defines the Time Zone used for setting the device clock. The fourth argument specifies the URL page
|
||||
// of the Web Log. See the HomeSpan API Reference for complete details, as well as additional options, related
|
||||
// to this function call.
|
||||
|
||||
homeSpan.enableWebLog(10,"pool.ntp.org","UTC","myLog"); // creates a web log on the URL /HomeSpan-[DEVICE-ID].local:[TCP-PORT]/myLog
|
||||
|
||||
// The full URL of the Web Log will be shown in the Serial Monitor at boot time for reference.
|
||||
// The Web Log output displays a variety of device parameters, plus any log messages you choose
|
||||
// to provide with the WEBLOG() macro (see DEV_LED.h)
|
||||
|
||||
// Note the rest of the sketch below is identical to Example 5. All of the Web Logging occurs in DEV_LED.h
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LEDs"); // Note we can set Category to Lighting even if device is configured as a Bridge
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("LED #1");
|
||||
new DEV_LED(16);
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("LED #2");
|
||||
new DEV_LED(17);
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
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);
|
||||
WEBLOG("Configuring LED on Pin %d",ledPin); // NEW! This creates a Web Log message announcing the configuration of the device
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){
|
||||
|
||||
digitalWrite(ledPin,power->getNewVal());
|
||||
WEBLOG("LED on Pin %d: %s",ledPin,power->getNewVal()?"ON":"OFF"); // NEW! This creates a Web Log message whenever an LED is turned ON or OFF
|
||||
return(true);
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
// SOME MORE ABOUT WEB LOGGING
|
||||
// ---------------------------
|
||||
//
|
||||
// * The WEBLOG() macro operates by calling Serial.printf(), so the first argument always needs to be a text string containing printf-like format instructions.
|
||||
// The rest of the arguments, if any, are the variables to print. For example, you cannot simply write WEBLOG(ledPin). This will cause errors at compile time,
|
||||
// though you can write LOG1(ledPin) or LOG2(ledPin) to output log messages just to the Serial Monitor.
|
||||
//
|
||||
// * You do NOT need to include a "\n" at the end of your format string since all Web Log messages are formatted into an HTML table when presented, and HTML ignores "\n".
|
||||
//
|
||||
// * Every Web Log message is recorded with TWO timestamps. The first timestamp is relative to when the device first booted, and is presented as DAYS:HH:MM:SS. This timestamp
|
||||
// is always present. The second timestamp is an absolute clock time, in standard Unix form, such as "Mon Aug 10 13:52:48 2020". This timestamp will only be present
|
||||
// if the clock time of the device was set, else it will be shown as "Unknown". Note that in the example above, the first Web Log message ("Configuring...") will
|
||||
// have a clock timestamp of "Unknown" even though we enabled Web Logging with a Time Server. This is because the Time Server cannot be configured until WiFi has
|
||||
// been established, and the first Web Log message above is created during initial configuratin of the device, BEFORE a WiFi connection is made. This is perfectly fine to do.
|
||||
//
|
||||
// * Every Web Log message also includes the IP Address of the Client that made the request, unless the Web Log message was generated independently of any Client request,
|
||||
// such as in the first message above. In these cases the IP Address will be displayed as 0.0.0.0.
|
||||
//
|
||||
// * Web Log messages are printed to the Serial Monitor whenever the HomeSpan Log Level is set to 1 or greater. Hence there is no reason to duplicate the same message
|
||||
// using WEBLOG() and LOG1() at the same time.
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
name=HomeSpan
|
||||
version=1.5.0
|
||||
version=1.5.1
|
||||
author=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.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
// For developer use and testing only - provides a common set of pin numbers mapped to the Adafruit Feather Board
|
||||
// Facilitates the testing of identical code on an ESP32, ESP32-S2, and ESP32-C3 using a common jig without rewiring
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32)
|
||||
enum {F13=13,F12=12,F27=27,F33=33,F15=15,F32=32,F14=14,F22=22,F23=23,F26=26,F25=25,F34=34,F39=39,F36=36,F4=4,F5=5,F18=18,F19=19,F16=16,F17=17,F21=21};
|
||||
#define DEVICE_SUFFIX ""
|
||||
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
enum {F13=11,F12=10,F27=7,F33=3,F15=1,F32=38,F14=33,F22=9,F23=8,F26=17,F25=18,F34=14,F39=12,F36=6,F4=5,F5=36,F18=35,F19=37,F16=44,F17=43};
|
||||
#define DEVICE_SUFFIX "-S2"
|
||||
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
enum {F27=2,F33=7,F32=3,F14=10,F22=9,F23=8,F26=0,F25=1,F4=18,F5=4,F18=6,F19=5,F16=20,F17=21,F21=19};
|
||||
#define DEVICE_SUFFIX "-C3"
|
||||
|
||||
#endif
|
||||
99
src/HAP.cpp
99
src/HAP.cpp
|
|
@ -40,16 +40,15 @@ void HAPClient::init(){
|
|||
|
||||
nvs_open("SRP",NVS_READWRITE,&srpNVS); // open SRP data namespace in NVS
|
||||
nvs_open("HAP",NVS_READWRITE,&hapNVS); // open HAP data namespace in NVS
|
||||
nvs_open("OTA",NVS_READWRITE,&otaNVS); // open OTA data namespace in NVS
|
||||
|
||||
if(!nvs_get_str(otaNVS,"OTADATA",NULL,&len)){ // if found OTA data in NVS
|
||||
nvs_get_str(otaNVS,"OTADATA",homeSpan.otaPwd,&len); // retrieve data
|
||||
if(!nvs_get_str(homeSpan.otaNVS,"OTADATA",NULL,&len)){ // if found OTA data in NVS
|
||||
nvs_get_str(homeSpan.otaNVS,"OTADATA",homeSpan.spanOTA.otaPwd,&len); // retrieve data
|
||||
} else {
|
||||
MD5Builder otaPwdHash;
|
||||
otaPwdHash.begin();
|
||||
otaPwdHash.add(DEFAULT_OTA_PASSWORD);
|
||||
otaPwdHash.calculate();
|
||||
otaPwdHash.getChars(homeSpan.otaPwd);
|
||||
otaPwdHash.getChars(homeSpan.spanOTA.otaPwd);
|
||||
}
|
||||
|
||||
if(strlen(homeSpan.pairingCodeCommand)){ // load verification setup code if provided
|
||||
|
|
@ -342,6 +341,11 @@ void HAPClient::processRequest(){
|
|||
return;
|
||||
}
|
||||
|
||||
if(homeSpan.webLog.isEnabled && !strncmp(body,homeSpan.webLog.statusURL.c_str(),homeSpan.webLog.statusURL.length())){ // GET STATUS - AN OPTIONAL, NON-HAP-R2 FEATURE
|
||||
getStatusURL();
|
||||
return;
|
||||
}
|
||||
|
||||
notFoundError();
|
||||
Serial.print("\n*** ERROR: Bad GET request - URL not found\n\n");
|
||||
return;
|
||||
|
|
@ -1249,6 +1253,91 @@ int HAPClient::putPrepareURL(char *json){
|
|||
|
||||
//////////////////////////////////////
|
||||
|
||||
int HAPClient::getStatusURL(){
|
||||
|
||||
char clocktime[33];
|
||||
|
||||
if(homeSpan.webLog.timeInit){
|
||||
struct tm timeinfo;
|
||||
getLocalTime(&timeinfo,10);
|
||||
strftime(clocktime,sizeof(clocktime),"%c",&timeinfo);
|
||||
} else {
|
||||
sprintf(clocktime,"Unknown");
|
||||
}
|
||||
|
||||
char uptime[16];
|
||||
int seconds=esp_timer_get_time()/1e6;
|
||||
int secs=seconds%60;
|
||||
int mins=(seconds/=60)%60;
|
||||
int hours=(seconds/=60)%24;
|
||||
int days=(seconds/=24);
|
||||
|
||||
sprintf(uptime,"%d:%02d:%02d:%02d",days,hours,mins,secs);
|
||||
|
||||
String response="HTTP/1.1 200 OK\r\nContent-type: text/html\r\n\r\n";
|
||||
|
||||
response+="<html><head><title>HomeSpan Status</title>\n";
|
||||
response+="<style>th, td {padding-right: 10px; padding-left: 10px; border:1px solid black;}";
|
||||
response+="</style></head>\n";
|
||||
response+="<body style=\"background-color:lightblue;\">\n";
|
||||
response+="<p><b>" + String(homeSpan.displayName) + "</b></p>\n";
|
||||
|
||||
response+="<table>\n";
|
||||
response+="<tr><td>Up Time:</td><td>" + String(uptime) + "</td></tr>\n";
|
||||
response+="<tr><td>Current Time:</td><td>" + String(clocktime) + "</td></tr>\n";
|
||||
response+="<tr><td>Boot Time:</td><td>" + String(homeSpan.webLog.bootTime) + "</td></tr>\n";
|
||||
response+="<tr><td>ESP32 Board:</td><td>" + String(ARDUINO_BOARD) + "</td></tr>\n";
|
||||
response+="<tr><td>Arduino-ESP Version:</td><td>" + String(ARDUINO_ESP_VERSION) + "</td></tr>\n";
|
||||
response+="<tr><td>ESP-IDF Version:</td><td>" + String(ESP_IDF_VERSION_MAJOR) + "." + String(ESP_IDF_VERSION_MINOR) + "." + String(ESP_IDF_VERSION_PATCH) + "</td></tr>\n";
|
||||
response+="<tr><td>HomeSpan Version:</td><td>" + String(HOMESPAN_VERSION) + "</td></tr>\n";
|
||||
response+="<tr><td>Sketch Version:</td><td>" + String(homeSpan.getSketchVersion()) + "</td></tr>\n";
|
||||
response+="<tr><td>Max Log Entries:</td><td>" + String(homeSpan.webLog.maxEntries) + "</td></tr>\n";
|
||||
response+="</table>\n";
|
||||
response+="<p></p>";
|
||||
|
||||
if(homeSpan.webLog.maxEntries>0){
|
||||
response+="<table><tr><th>Entry</th><th>Up Time</th><th>Log Time</th><th>Client</th><th>Message</th></tr>\n";
|
||||
int lastIndex=homeSpan.webLog.nEntries-homeSpan.webLog.maxEntries;
|
||||
if(lastIndex<0)
|
||||
lastIndex=0;
|
||||
|
||||
for(int i=homeSpan.webLog.nEntries-1;i>=lastIndex;i--){
|
||||
int index=i%homeSpan.webLog.maxEntries;
|
||||
seconds=homeSpan.webLog.log[index].upTime/1e6;
|
||||
secs=seconds%60;
|
||||
mins=(seconds/=60)%60;
|
||||
hours=(seconds/=60)%24;
|
||||
days=(seconds/=24);
|
||||
sprintf(uptime,"%d:%02d:%02d:%02d",days,hours,mins,secs);
|
||||
|
||||
if(homeSpan.webLog.log[index].clockTime.tm_year>0)
|
||||
strftime(clocktime,sizeof(clocktime),"%c",&homeSpan.webLog.log[index].clockTime);
|
||||
else
|
||||
sprintf(clocktime,"Unknown");
|
||||
|
||||
response+="<tr><td>" + String(i+1) + "</td><td>" + String(uptime) + "</td><td>" + String(clocktime) + "</td><td>" + homeSpan.webLog.log[index].clientIP + "</td><td>" + String(homeSpan.webLog.log[index].message) + "</td/tr>\n";
|
||||
}
|
||||
response+="</table>\n";
|
||||
}
|
||||
|
||||
response+="</body></html>";
|
||||
|
||||
LOG2("\n>>>>>>>>>> ");
|
||||
LOG2(client.remoteIP());
|
||||
LOG2(" >>>>>>>>>>\n");
|
||||
LOG2(response);
|
||||
LOG2("\n");
|
||||
client.print(response);
|
||||
LOG2("------------ SENT! --------------\n");
|
||||
|
||||
delay(1);
|
||||
client.stop();
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void HAPClient::callServiceLoops(){
|
||||
|
||||
homeSpan.snapTime=millis(); // snap the current time for use in ALL loop routines
|
||||
|
|
@ -1304,7 +1393,6 @@ void HAPClient::checkTimedWrites(){
|
|||
|
||||
//////////////////////////////////////
|
||||
|
||||
|
||||
void HAPClient::eventNotify(SpanBuf *pObj, int nObj, int ignoreClient){
|
||||
|
||||
for(int cNum=0;cNum<homeSpan.maxConnections;cNum++){ // loop over all connection slots
|
||||
|
|
@ -1660,7 +1748,6 @@ void Nonce::inc(){
|
|||
TLV<kTLVType,10> HAPClient::tlv8;
|
||||
nvs_handle HAPClient::hapNVS;
|
||||
nvs_handle HAPClient::srpNVS;
|
||||
nvs_handle HAPClient::otaNVS;
|
||||
uint8_t HAPClient::httpBuf[MAX_HTTP+1];
|
||||
HKDF HAPClient::hkdf;
|
||||
pairState HAPClient::pairStatus;
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@ struct HAPClient {
|
|||
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 srpNVS; // handle for non-volatile-storage of SRP data
|
||||
static nvs_handle otaNVS; // handle for non-volatile-storage of OTA data
|
||||
static uint8_t httpBuf[MAX_HTTP+1]; // buffer to store HTTP messages (+1 to leave room for storing an extra 'overflow' character)
|
||||
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 pairState pairStatus; // tracks pair-setup status
|
||||
|
|
@ -118,6 +117,7 @@ struct HAPClient {
|
|||
int getCharacteristicsURL(char *urlBuf); // GET /characteristics (HAP Section 6.7.4)
|
||||
int putCharacteristicsURL(char *json); // PUT /characteristics (HAP Section 6.7.2)
|
||||
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 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
|
||||
|
|
|
|||
275
src/HomeSpan.cpp
275
src/HomeSpan.cpp
|
|
@ -29,15 +29,17 @@
|
|||
#include <nvs_flash.h>
|
||||
#include <sodium.h>
|
||||
#include <WiFi.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include <esp_ota_ops.h>
|
||||
#include <driver/ledc.h>
|
||||
#include <mbedtls/version.h>
|
||||
#include <esp_task_wdt.h>
|
||||
#include <esp_sntp.h>
|
||||
#include <esp_ota_ops.h>
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "HAP.h"
|
||||
|
||||
const __attribute__((section(".rodata_custom_desc"))) SpanPartition spanPartition = {HOMESPAN_MAGIC_COOKIE};
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
HAPClient **hap; // HAP Client structure containing HTTP client connections, parsing routines, and state variables (global-scoped variable)
|
||||
|
|
@ -72,6 +74,7 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa
|
|||
nvs_flash_init(); // initialize non-volatile-storage partition in flash
|
||||
nvs_open("CHAR",NVS_READWRITE,&charNVS); // open Characteristic data namespace in NVS
|
||||
nvs_open("WIFI",NVS_READWRITE,&wifiNVS); // open WIFI data namespace in NVS
|
||||
nvs_open("OTA",NVS_READWRITE,&otaNVS); // open OTA data namespace in NVS
|
||||
|
||||
size_t len;
|
||||
|
||||
|
|
@ -134,10 +137,22 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa
|
|||
Serial.print(" ");
|
||||
Serial.print(__TIME__);
|
||||
|
||||
Serial.printf("\nPartition: %s",esp_ota_get_running_partition()->label);
|
||||
|
||||
Serial.print("\n\nDevice Name: ");
|
||||
Serial.print(displayName);
|
||||
Serial.print("\n\n");
|
||||
|
||||
|
||||
uint8_t otaRequired=0;
|
||||
nvs_get_u8(otaNVS,"OTA_REQUIRED",&otaRequired);
|
||||
nvs_set_u8(otaNVS,"OTA_REQUIRED",0);
|
||||
nvs_commit(otaNVS);
|
||||
if(otaRequired && !spanOTA.enabled){
|
||||
Serial.printf("\n\n*** OTA SAFE MODE ALERT: OTA REQUIRED BUT NOT ENABLED. ROLLING BACK TO PREVIOUS APPLICATION ***\n\n");
|
||||
delay(100);
|
||||
esp_ota_mark_app_invalid_rollback_and_reboot();
|
||||
}
|
||||
|
||||
} // begin
|
||||
|
||||
///////////////////////////////
|
||||
|
|
@ -256,8 +271,10 @@ void Span::poll() {
|
|||
|
||||
if(hap[i]->client && hap[i]->client.available()){ // if connection exists and data is available
|
||||
|
||||
HAPClient::conNum=i; // set connection number
|
||||
hap[i]->processRequest(); // process HAP request
|
||||
HAPClient::conNum=i; // set connection number
|
||||
homeSpan.lastClientIP=hap[i]->client.remoteIP().toString(); // store IP Address for web logging
|
||||
hap[i]->processRequest(); // process HAP request
|
||||
homeSpan.lastClientIP="0.0.0.0"; // reset stored IP address to show "0.0.0.0" if homeSpan.getClientIP() is used in any other context
|
||||
|
||||
if(!hap[i]->client){ // client disconnected by server
|
||||
LOG1("** Disconnecting Client #");
|
||||
|
|
@ -277,7 +294,7 @@ void Span::poll() {
|
|||
HAPClient::checkNotifications();
|
||||
HAPClient::checkTimedWrites();
|
||||
|
||||
if(otaEnabled)
|
||||
if(spanOTA.enabled)
|
||||
ArduinoOTA.handle();
|
||||
|
||||
if(controlButton.primed()){
|
||||
|
|
@ -497,11 +514,10 @@ void Span::checkConnect(){
|
|||
else
|
||||
mdns_service_txt_item_set("_hap","_tcp","sf","0"); // set Status Flag = 0
|
||||
|
||||
mdns_service_txt_item_set("_hap","_tcp","hspn",HOMESPAN_VERSION); // HomeSpan Version Number (info only - NOT used by HAP)
|
||||
mdns_service_txt_item_set("_hap","_tcp","ard-esp32",ARDUINO_ESP_VERSION); // Arduino-ESP32 Version Number (info only - NOT used by HAP)
|
||||
mdns_service_txt_item_set("_hap","_tcp","board",ARDUINO_VARIANT); // Board Name (info only - NOT used by HAP)
|
||||
mdns_service_txt_item_set("_hap","_tcp","sketch",sketchVersion); // Sketch Version (info only - NOT used by HAP)
|
||||
mdns_service_txt_item_set("_hap","_tcp","ota",otaEnabled?"yes":"no"); // OTA Enabled (info only - NOT used by HAP)
|
||||
mdns_service_txt_item_set("_hap","_tcp","hspn",HOMESPAN_VERSION); // HomeSpan Version Number (info only - NOT used by HAP)
|
||||
mdns_service_txt_item_set("_hap","_tcp","ard-esp32",ARDUINO_ESP_VERSION); // Arduino-ESP32 Version Number (info only - NOT used by HAP)
|
||||
mdns_service_txt_item_set("_hap","_tcp","board",ARDUINO_VARIANT); // Board Name (info only - NOT used by HAP)
|
||||
mdns_service_txt_item_set("_hap","_tcp","sketch",sketchVersion); // Sketch Version (info only - NOT used by HAP)
|
||||
|
||||
uint8_t hashInput[22];
|
||||
uint8_t hashOutput[64];
|
||||
|
|
@ -514,51 +530,38 @@ void Span::checkConnect(){
|
|||
mbedtls_base64_encode((uint8_t *)setupHash,9,&len,hashOutput,4); // Step 3: Encode the first 4 bytes of hashOutput in base64, which results in an 8-character, null-terminated, setupHash
|
||||
mdns_service_txt_item_set("_hap","_tcp","sh",setupHash); // Step 4: broadcast the resulting Setup Hash
|
||||
|
||||
if(otaEnabled){
|
||||
if(spanOTA.enabled){
|
||||
if(esp_ota_get_running_partition()!=esp_ota_get_next_update_partition(NULL)){
|
||||
ArduinoOTA.setHostname(hostName);
|
||||
|
||||
if(otaAuth)
|
||||
ArduinoOTA.setPasswordHash(otaPwd);
|
||||
|
||||
ArduinoOTA
|
||||
.onStart([]() {
|
||||
String type;
|
||||
if (ArduinoOTA.getCommand() == U_FLASH)
|
||||
type = "sketch";
|
||||
else // U_SPIFFS
|
||||
type = "filesystem";
|
||||
Serial.println("\n*** OTA Starting:" + type);
|
||||
homeSpan.statusLED.start(LED_OTA_STARTED);
|
||||
})
|
||||
.onEnd([]() {
|
||||
Serial.println("\n*** OTA Completed. Rebooting...");
|
||||
homeSpan.statusLED.off();
|
||||
})
|
||||
.onProgress([](unsigned int progress, unsigned int total) {
|
||||
Serial.printf("*** Progress: %u%%\r", (progress / (total / 100)));
|
||||
})
|
||||
.onError([](ota_error_t error) {
|
||||
Serial.printf("*** OTA Error[%u]: ", error);
|
||||
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed\n");
|
||||
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed\n");
|
||||
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed\n");
|
||||
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed\n");
|
||||
else if (error == OTA_END_ERROR) Serial.println("End Failed\n");
|
||||
});
|
||||
if(spanOTA.auth)
|
||||
ArduinoOTA.setPasswordHash(spanOTA.otaPwd);
|
||||
|
||||
ArduinoOTA.onStart(spanOTA.start).onEnd(spanOTA.end).onProgress(spanOTA.progress).onError(spanOTA.error);
|
||||
|
||||
ArduinoOTA.begin();
|
||||
reserveSocketConnections(1);
|
||||
Serial.print("Starting OTA Server: ");
|
||||
Serial.print(displayName);
|
||||
Serial.print(" at ");
|
||||
Serial.print(WiFi.localIP());
|
||||
Serial.print("\nAuthorization Password: ");
|
||||
Serial.print(otaAuth?"Enabled\n\n":"DISABLED!\n\n");
|
||||
Serial.print(spanOTA.auth?"Enabled\n\n":"DISABLED!\n\n");
|
||||
} else {
|
||||
Serial.print("\n*** WARNING: Can't start OTA Server - Partition table used to compile this sketch is not configured for OTA.\n\n");
|
||||
spanOTA.enabled=false;
|
||||
}
|
||||
}
|
||||
|
||||
mdns_service_txt_item_set("_hap","_tcp","ota",spanOTA.enabled?"yes":"no"); // OTA status (info only - NOT used by HAP)
|
||||
|
||||
if(webLog.isEnabled){
|
||||
mdns_service_txt_item_set("_hap","_tcp","logURL",webLog.statusURL.c_str()+4); // Web Log status (info only - NOT used by HAP)
|
||||
|
||||
Serial.printf("Web Logging enabled at http://%s.local:%d%swith max number of entries=%d\n\n",hostName,tcpPortNum,webLog.statusURL.c_str()+4,webLog.maxEntries);
|
||||
webLog.initTime();
|
||||
}
|
||||
|
||||
Serial.printf("Starting HAP Server on port %d supporting %d simultaneous HomeKit Controller Connections...\n",tcpPortNum,maxConnections);
|
||||
|
||||
hapServer->begin();
|
||||
|
|
@ -571,7 +574,7 @@ void Span::checkConnect(){
|
|||
} else {
|
||||
statusLED.on();
|
||||
}
|
||||
|
||||
|
||||
if(wifiCallback)
|
||||
wifiCallback();
|
||||
|
||||
|
|
@ -702,12 +705,12 @@ void Span::processSerialCommand(const char *c){
|
|||
otaPwdHash.begin();
|
||||
otaPwdHash.add(textPwd);
|
||||
otaPwdHash.calculate();
|
||||
otaPwdHash.getChars(otaPwd);
|
||||
nvs_set_str(HAPClient::otaNVS,"OTADATA",otaPwd); // update data
|
||||
nvs_commit(HAPClient::otaNVS);
|
||||
otaPwdHash.getChars(spanOTA.otaPwd);
|
||||
nvs_set_str(otaNVS,"OTADATA",spanOTA.otaPwd); // update data
|
||||
nvs_commit(otaNVS);
|
||||
|
||||
Serial.print("... Accepted! Password change will take effect after next restart.\n");
|
||||
if(!otaEnabled)
|
||||
if(!spanOTA.enabled)
|
||||
Serial.print("... Note: OTA has not been enabled in this sketch.\n");
|
||||
Serial.print("\n");
|
||||
}
|
||||
|
|
@ -872,7 +875,9 @@ void Span::processSerialCommand(const char *c){
|
|||
nvs_erase_all(wifiNVS);
|
||||
nvs_commit(wifiNVS);
|
||||
nvs_erase_all(charNVS);
|
||||
nvs_commit(charNVS);
|
||||
nvs_commit(charNVS);
|
||||
nvs_erase_all(otaNVS);
|
||||
nvs_commit(otaNVS);
|
||||
Serial.print("\n*** FACTORY RESET! Restarting...\n\n");
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
|
|
@ -917,12 +922,12 @@ void Span::processSerialCommand(const char *c){
|
|||
Serial.print("\n\n");
|
||||
|
||||
char d[]="------------------------------";
|
||||
Serial.printf("%-30s %s %10s %s %s %s %s %s\n","Service","UUID","AID","IID","Update","Loop","Button","Linked Services");
|
||||
Serial.printf("%.30s %.4s %.10s %.3s %.6s %.4s %.6s %.15s\n",d,d,d,d,d,d,d,d);
|
||||
Serial.printf("%-30s %8s %10s %s %s %s %s %s\n","Service","UUID","AID","IID","Update","Loop","Button","Linked Services");
|
||||
Serial.printf("%.30s %.8s %.10s %.3s %.6s %.4s %.6s %.15s\n",d,d,d,d,d,d,d,d);
|
||||
for(int i=0;i<Accessories.size();i++){ // identify all services with over-ridden loop() methods
|
||||
for(int j=0;j<Accessories[i]->Services.size();j++){
|
||||
SpanService *s=Accessories[i]->Services[j];
|
||||
Serial.printf("%-30s %4s %10u %3d %6s %4s %6s ",s->hapName,s->type,Accessories[i]->aid,s->iid,
|
||||
Serial.printf("%-30s %8.8s %10u %3d %6s %4s %6s ",s->hapName,s->type,Accessories[i]->aid,s->iid,
|
||||
(void(*)())(s->*(&SpanService::update))!=(void(*)())(&SpanService::update)?"YES":"NO",
|
||||
(void(*)())(s->*(&SpanService::loop))!=(void(*)())(&SpanService::loop)?"YES":"NO",
|
||||
(void(*)(int,boolean))(s->*(&SpanService::button))!=(void(*)(int,boolean))(&SpanService::button)?"YES":"NO"
|
||||
|
|
@ -982,7 +987,10 @@ void Span::processSerialCommand(const char *c){
|
|||
auto uCom=UserCommands.find(c[1]);
|
||||
|
||||
if(uCom!=UserCommands.end()){
|
||||
uCom->second->userFunction(c+1);
|
||||
if(uCom->second->userFunction1)
|
||||
uCom->second->userFunction1(c+1);
|
||||
else
|
||||
uCom->second->userFunction2(c+1,uCom->second->userArg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1440,14 +1448,11 @@ SpanAccessory::SpanAccessory(uint32_t aid){
|
|||
void SpanAccessory::validate(){
|
||||
|
||||
boolean foundInfo=false;
|
||||
boolean foundProtocol=false;
|
||||
|
||||
for(int i=0;i<Services.size();i++){
|
||||
if(!strcmp(Services[i]->type,"3E"))
|
||||
foundInfo=true;
|
||||
else if(!strcmp(Services[i]->type,"A2"))
|
||||
foundProtocol=true;
|
||||
else if(aid==1) // this is an Accessory with aid=1, but it has more than just AccessoryInfo and HAPProtocolInformation. So...
|
||||
else if(aid==1) // this is an Accessory with aid=1, but it has more than just AccessoryInfo. So...
|
||||
homeSpan.isBridge=false; // ...this is not a bridge device
|
||||
}
|
||||
|
||||
|
|
@ -1456,12 +1461,7 @@ void SpanAccessory::validate(){
|
|||
homeSpan.configLog+=" *** ERROR! Required Service for this Accessory not found. ***\n";
|
||||
homeSpan.nFatalErrors++;
|
||||
}
|
||||
|
||||
if(!foundProtocol && (aid==1 || !homeSpan.isBridge)){ // HAPProtocolInformation must always be present in Accessory if aid=1, and any other Accessory if the device is not a bridge)
|
||||
homeSpan.configLog+=" \u2718 Service HAPProtocolInformation";
|
||||
homeSpan.configLog+=" *** ERROR! Required Service for this Accessory not found. ***\n";
|
||||
homeSpan.nFatalErrors++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
|
@ -1486,13 +1486,14 @@ int SpanAccessory::sprintfAttributes(char *cBuf){
|
|||
// SpanService //
|
||||
///////////////////////////////
|
||||
|
||||
SpanService::SpanService(const char *type, const char *hapName){
|
||||
SpanService::SpanService(const char *type, const char *hapName, boolean isCustom){
|
||||
|
||||
if(!homeSpan.Accessories.empty() && !homeSpan.Accessories.back()->Services.empty()) // this is not the first Service to be defined for this Accessory
|
||||
homeSpan.Accessories.back()->Services.back()->validate();
|
||||
|
||||
this->type=type;
|
||||
this->hapName=hapName;
|
||||
this->isCustom=isCustom;
|
||||
|
||||
homeSpan.configLog+=" \u279f Service " + String(hapName);
|
||||
|
||||
|
|
@ -1505,7 +1506,12 @@ SpanService::SpanService(const char *type, const char *hapName){
|
|||
homeSpan.Accessories.back()->Services.push_back(this);
|
||||
iid=++(homeSpan.Accessories.back()->iidCount);
|
||||
|
||||
homeSpan.configLog+=": IID=" + String(iid) + ", UUID=\"" + String(type) + "\"";
|
||||
homeSpan.configLog+=": IID=" + String(iid) + ", " + (isCustom?"Custom-":"") + "UUID=\"" + String(type) + "\"";
|
||||
|
||||
if(Span::invalidUUID(type,isCustom)){
|
||||
homeSpan.configLog+=" *** ERROR! Format of UUID is invalid. ***";
|
||||
homeSpan.nFatalErrors++;
|
||||
}
|
||||
|
||||
if(!strcmp(this->type,"3E") && iid!=1){
|
||||
homeSpan.configLog+=" *** ERROR! The AccessoryInformation Service must be defined before any other Services in an Accessory. ***";
|
||||
|
|
@ -1597,12 +1603,13 @@ void SpanService::validate(){
|
|||
// SpanCharacteristic //
|
||||
///////////////////////////////
|
||||
|
||||
SpanCharacteristic::SpanCharacteristic(HapChar *hapChar){
|
||||
SpanCharacteristic::SpanCharacteristic(HapChar *hapChar, boolean isCustom){
|
||||
type=hapChar->type;
|
||||
perms=hapChar->perms;
|
||||
hapName=hapChar->hapName;
|
||||
format=hapChar->format;
|
||||
staticRange=hapChar->staticRange;
|
||||
this->isCustom=isCustom;
|
||||
|
||||
homeSpan.configLog+=" \u21e8 Characteristic " + String(hapName);
|
||||
|
||||
|
|
@ -1893,9 +1900,145 @@ SpanButton::SpanButton(int pin, uint16_t longTime, uint16_t singleTime, uint16_t
|
|||
// SpanUserCommand //
|
||||
///////////////////////////////
|
||||
|
||||
SpanUserCommand::SpanUserCommand(char c, const char *s, void (*f)(const char *v)){
|
||||
SpanUserCommand::SpanUserCommand(char c, const char *s, void (*f)(const char *)){
|
||||
this->s=s;
|
||||
userFunction=f;
|
||||
userFunction1=f;
|
||||
|
||||
homeSpan.UserCommands[c]=this;
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
SpanUserCommand::SpanUserCommand(char c, const char *s, void (*f)(const char *, void *), void *arg){
|
||||
this->s=s;
|
||||
userFunction2=f;
|
||||
userArg=arg;
|
||||
|
||||
homeSpan.UserCommands[c]=this;
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
// SpanWebLog //
|
||||
///////////////////////////////
|
||||
|
||||
void SpanWebLog::init(uint16_t maxEntries, const char *serv, const char *tz, const char *url){
|
||||
isEnabled=true;
|
||||
this->maxEntries=maxEntries;
|
||||
timeServer=serv;
|
||||
timeZone=tz;
|
||||
statusURL="GET /" + String(url) + " ";
|
||||
log = (log_t *)calloc(maxEntries,sizeof(log_t));
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void SpanWebLog::initTime(){
|
||||
if(!timeServer)
|
||||
return;
|
||||
|
||||
Serial.printf("Acquiring Time from %s (%s). Waiting %d second(s) for response... ",timeServer,timeZone,waitTime/1000);
|
||||
configTzTime(timeZone,timeServer);
|
||||
struct tm timeinfo;
|
||||
if(getLocalTime(&timeinfo,waitTime)){
|
||||
strftime(bootTime,sizeof(bootTime),"%c",&timeinfo);
|
||||
Serial.printf("%s\n\n",bootTime);
|
||||
homeSpan.reserveSocketConnections(1);
|
||||
timeInit=true;
|
||||
} else {
|
||||
Serial.printf("Can't access Time Server - time-keeping not initialized!\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void SpanWebLog::addLog(const char *fmt, ...){
|
||||
if(maxEntries==0)
|
||||
return;
|
||||
|
||||
int index=nEntries%maxEntries;
|
||||
|
||||
log[index].upTime=esp_timer_get_time();
|
||||
if(timeInit)
|
||||
getLocalTime(&log[index].clockTime,10);
|
||||
else
|
||||
log[index].clockTime.tm_year=0;
|
||||
|
||||
free(log[index].message);
|
||||
va_list ap;
|
||||
va_start(ap,fmt);
|
||||
vasprintf(&log[index].message,fmt,ap);
|
||||
va_end(ap);
|
||||
|
||||
log[index].clientIP=homeSpan.lastClientIP;
|
||||
nEntries++;
|
||||
|
||||
if(homeSpan.logLevel>0)
|
||||
Serial.printf("WEBLOG: %s\n",log[index].message);
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
// SpanOTA //
|
||||
///////////////////////////////
|
||||
|
||||
void SpanOTA::init(boolean _auth, boolean _safeLoad){
|
||||
enabled=true;
|
||||
safeLoad=_safeLoad;
|
||||
auth=_auth;
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void SpanOTA::start(){
|
||||
Serial.printf("\n*** Current Partition: %s\n*** New Partition: %s\n*** OTA Starting..",
|
||||
esp_ota_get_running_partition()->label,esp_ota_get_next_update_partition(NULL)->label);
|
||||
otaPercent=0;
|
||||
homeSpan.statusLED.start(LED_OTA_STARTED);
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void SpanOTA::end(){
|
||||
nvs_set_u8(homeSpan.otaNVS,"OTA_REQUIRED",safeLoad);
|
||||
nvs_commit(homeSpan.otaNVS);
|
||||
Serial.printf(" DONE! Rebooting...\n");
|
||||
homeSpan.statusLED.off();
|
||||
delay(100); // make sure commit it finished before reboot
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void SpanOTA::progress(uint32_t progress, uint32_t total){
|
||||
int percent=progress*100/total;
|
||||
if(percent/10 != otaPercent/10){
|
||||
otaPercent=percent;
|
||||
Serial.printf("%d%%..",progress*100/total);
|
||||
}
|
||||
|
||||
if(safeLoad && progress==total){
|
||||
SpanPartition newSpanPartition;
|
||||
esp_partition_read(esp_ota_get_next_update_partition(NULL), sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t), &newSpanPartition, sizeof(newSpanPartition));
|
||||
Serial.printf("Checking for HomeSpan Magic Cookie: %s..",newSpanPartition.magicCookie);
|
||||
if(strcmp(newSpanPartition.magicCookie,spanPartition.magicCookie))
|
||||
Update.abort();
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void SpanOTA::error(ota_error_t err){
|
||||
Serial.printf("*** OTA Error[%u]: ", err);
|
||||
if (err == OTA_AUTH_ERROR) Serial.println("Auth Failed\n");
|
||||
else if (err == OTA_BEGIN_ERROR) Serial.println("Begin Failed\n");
|
||||
else if (err == OTA_CONNECT_ERROR) Serial.println("Connect Failed\n");
|
||||
else if (err == OTA_RECEIVE_ERROR) Serial.println("Receive Failed\n");
|
||||
else if (err == OTA_END_ERROR) Serial.println("End Failed\n");
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
int SpanOTA::otaPercent;
|
||||
boolean SpanOTA::safeLoad;
|
||||
boolean SpanOTA::enabled=false;
|
||||
boolean SpanOTA::auth;
|
||||
|
||||
|
||||
|
|
|
|||
109
src/HomeSpan.h
109
src/HomeSpan.h
|
|
@ -37,6 +37,7 @@
|
|||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <nvs.h>
|
||||
#include <ArduinoOTA.h>
|
||||
|
||||
#include "Settings.h"
|
||||
#include "Utils.h"
|
||||
|
|
@ -76,7 +77,14 @@ extern Span homeSpan;
|
|||
|
||||
///////////////////////////////
|
||||
|
||||
struct SpanConfig {
|
||||
struct SpanPartition{
|
||||
char magicCookie[32];
|
||||
uint8_t reserved[224];
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct SpanConfig{
|
||||
int configNumber=0; // configuration number - broadcast as Bonjour "c#" (computed automatically)
|
||||
uint8_t hashCode[48]={0}; // SHA-384 hash of Span Database stored as a form of unique "signature" to know when to update the config number upon changes
|
||||
};
|
||||
|
|
@ -94,6 +102,49 @@ struct SpanBuf{ // temporary storage buffer for us
|
|||
|
||||
///////////////////////////////
|
||||
|
||||
struct SpanWebLog{ // optional web status/log data
|
||||
boolean isEnabled=false; // flag to inidicate WebLog has been enabled
|
||||
uint16_t maxEntries; // max 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 *timeZone; // optional time-zone specification
|
||||
boolean timeInit=false; // flag to indicate time has been initialized
|
||||
char bootTime[33]="Unknown"; // boot time
|
||||
String statusURL; // URL of status log
|
||||
uint32_t waitTime=10000; // number of milliseconds to wait for initial connection to time server
|
||||
|
||||
struct log_t { // log entry type
|
||||
uint64_t upTime; // number of seconds since booting
|
||||
struct tm clockTime; // clock time
|
||||
char *message; // pointers to log entries of arbitrary size
|
||||
String clientIP; // IP address of client making request (or "0.0.0.0" if not applicable)
|
||||
} *log=NULL; // array of log entries
|
||||
|
||||
void init(uint16_t maxEntries, const char *serv, const char *tz, const char *url);
|
||||
void initTime();
|
||||
void addLog(const char *fmr, ...);
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct SpanOTA{ // manages OTA process
|
||||
|
||||
char otaPwd[33]; // MD5 Hash of OTA password, represented as a string of hexidecimal characters
|
||||
|
||||
static boolean enabled; // enables OTA - default if not enabled
|
||||
static boolean auth; // indicates whether OTA password is required
|
||||
static int otaPercent;
|
||||
static boolean safeLoad; // indicates whether OTA update should reject any application update that is not another HomeSpan sketch
|
||||
|
||||
void init(boolean auth, boolean safeLoad);
|
||||
static void start();
|
||||
static void end();
|
||||
static void progress(uint32_t progress, uint32_t total);
|
||||
static void error(ota_error_t err);
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct Span{
|
||||
|
||||
const char *displayName; // display name for this device - broadcast as part of Bonjour MDNS
|
||||
|
|
@ -112,8 +163,11 @@ struct Span{
|
|||
const char *sketchVersion="n/a"; // version of the sketch
|
||||
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 otaNVS; // handle for non-volatile storaget of OTA data
|
||||
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
|
||||
boolean newCode; // flag indicating new application code has been loaded (based on keeping track of app SHA256)
|
||||
|
||||
boolean connected=false; // WiFi connection status
|
||||
unsigned long waitTime=60000; // time to wait (in milliseconds) between WiFi connection attempts
|
||||
unsigned long alarmConnect=0; // time after which WiFi connection attempt should be tried again
|
||||
|
|
@ -128,9 +182,6 @@ struct Span{
|
|||
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
|
||||
char qrID[5]=""; // Setup ID used for pairing with QR Code
|
||||
boolean otaEnabled=false; // enables Over-the-Air ("OTA") updates
|
||||
char otaPwd[33]; // MD5 Hash of OTA password, represented as a string of hexidecimal characters
|
||||
boolean otaAuth; // OTA requires password when set to true
|
||||
void (*wifiCallback)()=NULL; // optional callback function to invoke once WiFi connectivity is established
|
||||
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
|
||||
|
|
@ -140,7 +191,9 @@ struct Span{
|
|||
Blinker statusLED; // indicates HomeSpan status
|
||||
PushButton controlButton; // controls HomeSpan configuration and resets
|
||||
Network network; // configures WiFi and Setup Code via either serial monitor or temporary Access Point
|
||||
SpanWebLog webLog; // optional web status/log
|
||||
|
||||
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
|
||||
vector<SpanAccessory *> Accessories; // vector of pointers to all Accessories
|
||||
vector<SpanService *> Loops; // vector of pointer to all Services that have over-ridden loop() methods
|
||||
|
|
@ -191,18 +244,29 @@ struct Span{
|
|||
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
|
||||
void setPairCallback(void (*f)(boolean isPaired)){pairCallback=f;} // sets an optional user-defined function to call when Pairing is established (true) or lost (false)
|
||||
void setApFunction(void (*f)()){apFunction=f;} // sets an optional user-defined function to call when activating the WiFi Access Point
|
||||
|
||||
void setApFunction(void (*f)()){apFunction=f;} // sets an optional user-defined function to call when activating the WiFi Access Point
|
||||
void enableAutoStartAP(){autoStartAPEnabled=true;} // enables auto start-up of Access Point when WiFi Credentials not found
|
||||
void setWifiCredentials(const char *ssid, const char *pwd); // sets WiFi Credentials
|
||||
|
||||
void setPairingCode(const char *s){sprintf(pairingCodeCommand,"S %9s",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 enableOTA(boolean auth=true){otaEnabled=true;otaAuth=auth;reserveSocketConnections(1);} // enables Over-the-Air updates, with (auth=true) or without (auth=false) authorization password
|
||||
void enableOTA(boolean auth=true, boolean safeLoad=true){spanOTA.init(auth, safeLoad);} // enables Over-the-Air updates, with (auth=true) or without (auth=false) authorization password
|
||||
|
||||
void 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);
|
||||
}
|
||||
|
||||
void setTimeServerTimeout(uint32_t tSec){webLog.waitTime=tSec*1000;} // sets wait time (in seconds) for optional web log time server to connect
|
||||
|
||||
[[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
|
||||
|
||||
static boolean invalidUUID(const char *uuid, boolean isCustom){
|
||||
int x=0;
|
||||
sscanf(uuid,"%*8[0-9a-fA-F]-%*4[0-9a-fA-F]-%*4[0-9a-fA-F]-%*4[0-9a-fA-F]-%*12[0-9a-fA-F]%n",&x);
|
||||
return(isCustom && (strlen(uuid)!=36 || x!=36));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -233,8 +297,9 @@ struct SpanService{
|
|||
vector<HapChar *> req; // vector of pointers to all required HAP Characteristic Types for this Service
|
||||
vector<HapChar *> opt; // vector of pointers to all optional HAP Characteristic Types for this Service
|
||||
vector<SpanService *> linkedServices; // vector of pointers to any optional linked Services
|
||||
boolean isCustom; // flag to indicate this is a Custom Service
|
||||
|
||||
SpanService(const char *type, const char *hapName);
|
||||
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 *setHidden(); // sets the Service Type to be hidden and returns pointer to self
|
||||
|
|
@ -280,6 +345,7 @@ struct SpanCharacteristic{
|
|||
const char *validValues=NULL; // Optional JSON array of valid values. Applicable only to uint8 Characteristics
|
||||
boolean *ev; // Characteristic Event Notify Enable (per-connection)
|
||||
char *nvsKey=NULL; // key for NVS storage of Characteristic value
|
||||
boolean isCustom; // flag to indicate this is a Custom Characteristic
|
||||
|
||||
uint32_t aid=0; // Accessory ID - passed through from Service containing this Characteristic
|
||||
boolean isUpdated=false; // set to true when new value has been requested by PUT /characteristic
|
||||
|
|
@ -287,7 +353,7 @@ struct SpanCharacteristic{
|
|||
UVal newValue; // the updated value requested by PUT /characteristic
|
||||
SpanService *service=NULL; // pointer to Service containing this Characteristic
|
||||
|
||||
SpanCharacteristic(HapChar *hapChar); // contructor
|
||||
SpanCharacteristic(HapChar *hapChar, boolean isCustom=false); // contructor
|
||||
|
||||
int sprintfAttributes(char *cBuf, int flags); // prints Characteristic JSON records into buf, according to flags mask; return number of characters printed, excluding null terminator
|
||||
StatusCode loadUpdate(char *val, char *ev); // load updated val/ev from PUT /characteristic JSON request. Return intiial HAP status code (checks to see if characteristic is found, is writable, etc.)
|
||||
|
|
@ -457,11 +523,6 @@ struct SpanCharacteristic{
|
|||
uvSet(stepValue,0);
|
||||
}
|
||||
|
||||
int x=0;
|
||||
sscanf(type,"%*8[0-9a-fA-F]-%*4[0-9a-fA-F]-%*4[0-9a-fA-F]-%*4[0-9a-fA-F]-%*12[0-9a-fA-F]%n",&x);
|
||||
|
||||
boolean isCustom=(strlen(type)==36 && x==36);
|
||||
|
||||
homeSpan.configLog+="(" + uvPrint(value) + ")" + ": IID=" + String(iid) + ", " + (isCustom?"Custom-":"") + "UUID=\"" + String(type) + "\"";
|
||||
if(format!=FORMAT::STRING && format!=FORMAT::BOOL)
|
||||
homeSpan.configLog+= ", Range=[" + String(uvPrint(minValue)) + "," + String(uvPrint(maxValue)) + "]";
|
||||
|
|
@ -471,7 +532,12 @@ struct SpanCharacteristic{
|
|||
else if(nvsFlag==1)
|
||||
homeSpan.configLog+=" (storing)";
|
||||
|
||||
boolean valid=isCustom;
|
||||
if(Span::invalidUUID(type,isCustom)){
|
||||
homeSpan.configLog+=" *** ERROR! Format of UUID is invalid. ***";
|
||||
homeSpan.nFatalErrors++;
|
||||
}
|
||||
|
||||
boolean valid=isCustom|service->isCustom; // automatically set valid if either Characteristic or containing Service is Custom
|
||||
|
||||
for(int i=0; !valid && i<homeSpan.Accessories.back()->Services.back()->req.size(); i++)
|
||||
valid=!strcmp(type,homeSpan.Accessories.back()->Services.back()->req[i]->type);
|
||||
|
|
@ -657,10 +723,13 @@ struct SpanButton{
|
|||
///////////////////////////////
|
||||
|
||||
struct SpanUserCommand {
|
||||
const char *s; // description of command
|
||||
void (*userFunction)(const char *v); // user-defined function to call
|
||||
const char *s; // description of command
|
||||
void (*userFunction1)(const char *v)=NULL; // user-defined function to call
|
||||
void (*userFunction2)(const char *v, void *arg)=NULL; // user-defined function to call with user-defined arg
|
||||
void *userArg;
|
||||
|
||||
SpanUserCommand(char c, const char *s, void (*f)(const char *v));
|
||||
SpanUserCommand(char c, const char *s, void (*f)(const char *));
|
||||
SpanUserCommand(char c, const char *s, void (*f)(const char *, void *), void *arg);
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
#define HS_MAJOR 1
|
||||
#define HS_MINOR 5
|
||||
#define HS_PATCH 0
|
||||
#define HS_PATCH 1
|
||||
|
||||
#define STRINGIFY(x) _STR(x)
|
||||
#define _STR(x) #x
|
||||
|
|
@ -81,6 +81,13 @@
|
|||
|
||||
#define DEFAULT_TCP_PORT 80 // change with homeSpan.setPort(port);
|
||||
|
||||
#define DEFAULT_WEBLOG_URL "status" // change with optional fourth argument in homeSpan.enableWebLog()
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// OTA PARTITION INFO //
|
||||
|
||||
#define HOMESPAN_MAGIC_COOKIE "HomeSpanMagicCookie##2022"
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// STATUS LED SETTINGS //
|
||||
|
||||
|
|
@ -96,10 +103,12 @@
|
|||
// Message Log Level Control Macros //
|
||||
// 0=Minimal, 1=Informative, 2=All //
|
||||
|
||||
#define LOG1(x) if(homeSpan.logLevel>0)Serial.print(x)
|
||||
#define LOG2(x) if(homeSpan.logLevel>1)Serial.print(x)
|
||||
|
||||
#define LOG0(format,...) Serial.print ##__VA_OPT__(f)(format __VA_OPT__(,) __VA_ARGS__)
|
||||
#define LOG1(format,...) if(homeSpan.logLevel>0)Serial.print ##__VA_OPT__(f)(format __VA_OPT__(,) __VA_ARGS__)
|
||||
#define LOG2(format,...) if(homeSpan.logLevel>1)Serial.print ##__VA_OPT__(f)(format __VA_OPT__(,) __VA_ARGS__)
|
||||
|
||||
#define WEBLOG(format,...) homeSpan.webLog.addLog(format __VA_OPT__(,) __VA_ARGS__)
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Types of Accessory Categories //
|
||||
// Reference: HAP Section 13 //
|
||||
|
|
|
|||
36
src/Span.h
36
src/Span.h
|
|
@ -37,13 +37,13 @@
|
|||
namespace Service {
|
||||
|
||||
struct AccessoryInformation : SpanService { AccessoryInformation() : SpanService{"3E","AccessoryInformation"}{
|
||||
REQ(FirmwareRevision);
|
||||
REQ(Identify);
|
||||
REQ(Manufacturer);
|
||||
REQ(Model);
|
||||
REQ(Name);
|
||||
REQ(SerialNumber);
|
||||
OPT(HardwareRevision);
|
||||
OPT(FirmwareRevision);
|
||||
OPT(Manufacturer);
|
||||
OPT(Model);
|
||||
OPT(Name);
|
||||
OPT(SerialNumber);
|
||||
OPT(HardwareRevision);
|
||||
}};
|
||||
|
||||
struct AirPurifier : SpanService { AirPurifier() : SpanService{"BB","AirPurifier"}{
|
||||
|
|
@ -521,14 +521,28 @@ namespace Characteristic {
|
|||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// MACRO TO ADD CUSTOM CHARACTERISTICS //
|
||||
//////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////
|
||||
// MACROS TO ADD CUSTOM SERVICES AND CHARACTERISTICS //
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
#define CUSTOM_CHAR(NAME,UUID,PERMISISONS,FORMAT,DEFVAL,MINVAL,MAXVAL,STATIC_RANGE) \
|
||||
HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),FORMAT,STATIC_RANGE}; \
|
||||
namespace Characteristic { struct NAME : SpanCharacteristic { NAME(FORMAT##_t val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME} { init(val,nvsStore,(FORMAT##_t)MINVAL,(FORMAT##_t)MAXVAL); } }; }
|
||||
namespace Characteristic { struct NAME : SpanCharacteristic { NAME(FORMAT##_t val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init(val,nvsStore,(FORMAT##_t)MINVAL,(FORMAT##_t)MAXVAL); } }; }
|
||||
|
||||
#define CUSTOM_CHAR_STRING(NAME,UUID,PERMISISONS,DEFVAL) \
|
||||
HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),STRING,true}; \
|
||||
namespace Characteristic { struct NAME : SpanCharacteristic { NAME(const char * val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME} { init(val,nvsStore); } }; }
|
||||
namespace Characteristic { struct NAME : SpanCharacteristic { NAME(const char * val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init(val,nvsStore); } }; }
|
||||
|
||||
#define CUSTOM_SERV(NAME,UUID) \
|
||||
namespace Service { struct NAME : SpanService { NAME() : SpanService{#UUID,#NAME,true}{} }; }
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// MACROS TO ADD A NEW ACCESSORT WITH OPTIONAL NAME //
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
#define SPAN_ACCESSORY(...) new SpanAccessory(); new Service::AccessoryInformation(); new Characteristic::Identify(); __VA_OPT__(new Characteristic::Name(__VA_ARGS__));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ void setup() {
|
|||
homeSpan.setPortNum(1201);
|
||||
// homeSpan.setMaxConnections(6);
|
||||
// homeSpan.setQRID("One1");
|
||||
// homeSpan.enableOTA();
|
||||
homeSpan.setSketchVersion("Test 1.3.1");
|
||||
homeSpan.enableOTA();
|
||||
homeSpan.setSketchVersion("OTA Test 8");
|
||||
homeSpan.setWifiCallback(wifiEstablished);
|
||||
|
||||
new SpanUserCommand('d',"- My Description",userCom1);
|
||||
|
|
|
|||
Loading…
Reference in New Issue