diff --git a/examples/22-TLV8_Characteristics/22-TLV8_Characteristics.ino b/examples/22-TLV8_Characteristics/22-TLV8_Characteristics.ino new file mode 100644 index 0000000..5f81568 --- /dev/null +++ b/examples/22-TLV8_Characteristics/22-TLV8_Characteristics.ino @@ -0,0 +1,161 @@ +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2024 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + +//////////////////////////////////////////////////////////////// +// // +// HomeSpan: A HomeKit implementation for the ESP32 // +// ------------------------------------------------ // +// // +// Example 24: Demonstrates the use of the TLV8 Library // +// by implementing DisplayOrder, an optional // +// TLV8 Characteristic used with the TV Service // +// to sets the order in which TV Inputs are // +// displayed for selection in the Home App // +// // +//////////////////////////////////////////////////////////////// + +#include "HomeSpan.h" + +// NOTE: Please see the "Other Examples -> Television" sketch for complete details on how to implement a Television Service. The focus +// of this sketch is solely to demonstrate how to use the TLV8 Library to create TLV8 data for use with the DisplayOrder Characteristic. + +// First we define a simple Television Input Source Service with only the Identifer and Name Characteristics + +struct TVInput : Service::InputSource { + + SpanCharacteristic *inputID; + SpanCharacteristic *inputName; + + TVInput(uint32_t id, const char *name) : Service::InputSource() { + + inputID = new Characteristic::Identifier(id); + inputName = new Characteristic::ConfiguredName(name); + new Characteristic::IsConfigured(1); + } +}; + +// Next we define a very simple Television Service + +struct HomeSpanTV : Service::Television { + + SpanCharacteristic *active = new Characteristic::Active(0,true); // TV ON/OFF (set to OFF at start-up) + SpanCharacteristic *activeID = new Characteristic::ActiveIdentifier(30,true); // Set TV to input source with ID=30 + + // SpanCharacteristic *displayOrder = new Characteristic::DisplayOrder(); // <-- This is the new TLV8 Characteristic. Note the constructor has no argument + + HomeSpanTV() : Service::Television() { + + // Unlike the constructors for numerical and string-based Characteristics (such as ActiveIdentifier and ConfiguredName), + // we cannot set the initial value of TLV8 Characteristics during construction, but must instead first instantiate the + // Characteristic (as we did above), then build a TLV8 object with the information required by the TLV8 Characteristic, and + // then use setTLV() to load the completed TLV8 object into the Characteristic's value. + + // The (undocumented by Apple!) TLV8 specifications for the DisplayOrder Characteristic are as follows: + + // TAG NAME FORMAT DESCRIPTION + // ---- ------------- ------ -------------------------------------------- + // 0x01 inputSourceID uint32 ID of the Input Source to be displayed first + // 0x00 separator none Empty element to separate the inputSourceIDs + // 0x01 inputSourceID uint32 ID of the Input Source to be displayed second + // 0x00 separator none Empty element to separate the inputSourceIDs + // 0x01 inputSourceID uint32 ID of the Input Source to be displayed third + // 0x00 separator none Empty element to separate the inputSourceIDs + // etc... + + // To start, instantiate a new TLV8 object + + TLV8 orderTLV; // creates an empty TLV8 object + + // Next, fill it with TAGS and VALUES based on the above specification. The easiest, + // though not necessarily most elegant, way to do this is as follows: + + orderTLV.add(1,10); // TAG=1, VALUE=ID of first Input Source to be displayed + orderTLV.add(0); // TAG=0 (no value) + orderTLV.add(1,20); // TAG=1, VALUE=ID of the second Input Source to be displayed + orderTLV.add(0); // TAG=0 (no value) + orderTLV.add(1,50); // TAG=1, VALUE=ID of the third Input Source to be displayed + orderTLV.add(0); // TAG=0 (no value) + orderTLV.add(1,30); // TAG=1, VALUE=ID of the fourth Input Source to be displayed + orderTLV.add(0); // TAG=0 (no value) + orderTLV.add(1,40); // TAG=1, VALUE=ID of the fifth Input Source to be displayed + + // Based on the above structure, we expect the Home App to display our input sources based on their IDs + // in the following order: 100, 200, 500, 300, 400. These IDs must of course match the IDs you choose + // for your input sources when you create them at the end of this sketch in setup() + + // The final step is to load this TLV8 object into the DisplayOrder Characteristic + + // displayOrder->setTLV(orderTLV); // set the "value" of DisplayOrder to be the orderTLV object we just created + + // That's it - you've created your first TLV8 Characteristic! + } + + // Below we define the usual update() loop. There is nothing "TLV-specific" about this part of the code + + boolean update() override { + + if(active->updated()){ + Serial.printf("Set TV Power to: %s\n",active->getNewVal()?"ON":"OFF"); + } + + if(activeID->updated()){ + Serial.printf("Set Input Source to ID=%d\n",activeID->getNewVal()); + } + + return(true); + } +}; + +/////////////////////////////// + +void setup() { + + Serial.begin(115200); + + homeSpan.setLogLevel(2); + + homeSpan.begin(Category::Television,"HomeSpan Television"); + + SPAN_ACCESSORY(); + + (new HomeSpanTV()) // Define a Television Service and link in the InputSources! + ->addLink(new TVInput(10,"Xfinity")) + ->addLink(new TVInput(20,"BlueRay Disc")) + ->addLink(new TVInput(30,"Amazon Prime")) + ->addLink(new TVInput(40,"Netflix")) + ->addLink(new TVInput(50,"Hulu")) + ; + +} + +////////////////////////////////////// + +void loop(){ + homeSpan.poll(); +} + +////////////////////////////////////// diff --git a/src/Characteristics.h b/src/Characteristics.h index 679ea9b..52d43aa 100644 --- a/src/Characteristics.h +++ b/src/Characteristics.h @@ -114,6 +114,7 @@ struct HapCharacteristics { HAPCHAR( CurrentTemperature, 11, PR+EV, FLOAT, false ); HAPCHAR( CurrentTiltAngle, C1, PR+EV, INT, false ); HAPCHAR( CurrentVisibilityState, 135, PR+EV, UINT8, true ); + HAPCHAR( DisplayOrder, 136, PR+EV, TLV_ENC, true ); HAPCHAR( FilterLifeLevel, AB, PR+EV, FLOAT, false ); HAPCHAR( FilterChangeIndication, AC, PR+EV, UINT8, true ); HAPCHAR( FirmwareRevision, 52, PR+EV, STRING, true ); diff --git a/src/Span.h b/src/Span.h index e3893a7..624f0f7 100644 --- a/src/Span.h +++ b/src/Span.h @@ -429,6 +429,7 @@ namespace Service { CREATE_SERV(Television,D8) // Defines a TV. Optional Linked Services: InputSource and TelevisionSpeaker. REQ(Active); OPT(ActiveIdentifier); + OPT(DisplayOrder); OPT(RemoteKey); OPT(PowerModeSelection); OPT(ConfiguredName); @@ -513,6 +514,7 @@ namespace Characteristic { CREATE_CHAR(double,CurrentRelativeHumidity,0,0,100); // current humidity measured as a percentage CREATE_CHAR(double,CurrentTemperature,0,0,100); // current temperature measured in Celsius CREATE_CHAR(int,CurrentTiltAngle,0,-90,90); // current angle (in degrees) of slats from fully up or left (-90) to fully open (0) to fully down or right (90) + CREATE_CHAR(const char *,DisplayOrder,"",0,1); // specifies the order in which the TV inputs are displayed for selection in the Home App CREATE_CHAR(double,FilterLifeLevel,100,0,100); // measured as a percentage of remaining life CREATE_CHAR(uint8_t,FilterChangeIndication,0,0,1,NO_CHANGE_NEEDED,CHANGE_NEEDED); // indicates state of filter CREATE_CHAR(const char *,FirmwareRevision,"1.0.0",0,1); // must be in form x[.y[.z]] - informational only diff --git a/src/src.ino b/src/src.ino index bfc4942..44be4ec 100644 --- a/src/src.ino +++ b/src/src.ino @@ -27,7 +27,6 @@ #include "HomeSpan.h" -CUSTOM_CHAR_TLV8(DisplayOrder,136,PR+EV); CUSTOM_CHAR_DATA(TestData,333,PR+EV); struct HomeSpanTV : Service::Television {