From 38457c2b72afdc65983ea70603a3e4f0096896be Mon Sep 17 00:00:00 2001 From: Raal Goff Date: Tue, 3 Aug 2021 21:43:32 +0800 Subject: [PATCH 01/64] add HAP Services and Characteristics for Televisions --- examples/19-Television/19-Television.ino | 125 +++++++++++++++++++++++ src/Characteristics.h | 20 +++- src/Settings.h | 3 +- src/Span.h | 57 ++++++++++- 4 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 examples/19-Television/19-Television.ino diff --git a/examples/19-Television/19-Television.ino b/examples/19-Television/19-Television.ino new file mode 100644 index 0000000..e8bd94d --- /dev/null +++ b/examples/19-Television/19-Television.ino @@ -0,0 +1,125 @@ +#include "HomeSpan.h" + +const int OTHER = 0; +const int HOME_SCREEN = 1; +const int TUNER = 2; +const int HDMI = 3; +const int COMPOSITE_VIDEO = 4; +const int S_VIDEO = 5; +const int COMPONENT_VIDEO = 6; +const int DVI = 7; +const int AIRPLAY = 8; +const int USB = 9; +const int APPLICATION = 10; + +struct HKTV : Service::Television { + + SpanCharacteristic *active; + SpanCharacteristic *activeIdentifier; + SpanCharacteristic *configuredName; + SpanCharacteristic *remoteKey; + SpanCharacteristic *sleepDiscoveryMode; + + SpanService *speaker; + SpanCharacteristic *mute; + SpanCharacteristic *speakerActive; + SpanCharacteristic *volume; + + SpanService *hdmi1; + SpanCharacteristic *hdmi1Identifier; + SpanCharacteristic *hdmi1CurrentVisibility; + + SpanService *hdmi2; + SpanCharacteristic *hdmi2Identifier; + SpanCharacteristic *hdmi2CurrentVisibility; + + SpanService *netflix; + SpanCharacteristic *netflixIdentifier; + SpanCharacteristic *netflixCurrentVisibility; + + HKTV() : Service::Television(){ + Serial.print("Init HKTV\n"); // initialization message + + active = new Characteristic::Active(); + activeIdentifier = new Characteristic::ActiveIdentifier(1); + configuredName = new Characteristic::ConfiguredName(); + remoteKey = new Characteristic::RemoteKey(); + sleepDiscoveryMode = new Characteristic::SleepDiscoveryMode(1); + + speaker = new Service::TelevisionSpeaker(); + mute = new Characteristic::Mute(); + speakerActive = new Characteristic::Active(); + volume = new Characteristic::Volume(); + new Characteristic::VolumeControlType(3); + new Characteristic::VolumeSelector(); + + hdmi1 = new Service::InputSource(); + new Characteristic::ConfiguredName("HDMI 1"); + new Characteristic::IsConfigured(1); // configured = 1, not configured = 0 + new Characteristic::InputSourceType(HDMI); + new Characteristic::Name("HDMI 1"); + netflixIdentifier = new Characteristic::Identifier(0); + hdmi1CurrentVisibility = new Characteristic::CurrentVisibilityState(0); // 0 shown, 1 hidden + + hdmi2 = new Service::InputSource(); + new Characteristic::ConfiguredName("HDMI 2"); + new Characteristic::IsConfigured(1); // configured = 1, not configured = 0 + new Characteristic::InputSourceType(HDMI); + new Characteristic::Name("HDMI 2"); + hdmi2Identifier = new Characteristic::Identifier(1); + hdmi2CurrentVisibility = new Characteristic::CurrentVisibilityState(0); // 0 shown, 1 hidden + + netflix = new Service::InputSource(); + new Characteristic::ConfiguredName("NETFLIX"); + new Characteristic::IsConfigured(1); // configured = 1, not configured = 0 + new Characteristic::InputSourceType(APPLICATION); + new Characteristic::Name("Netflix"); + netflixIdentifier = new Characteristic::Identifier(2); + netflixCurrentVisibility = new Characteristic::CurrentVisibilityState(0); // 0 shown, 1 hidden + + addLink(hdmi1); + addLink(hdmi2); + addLink(netflix); + } + + boolean update(){ + if(active->updated()) { + if(active->getVal() != active->getNewVal()) { + Serial.printf("update(): active %d -> %d\n", active->getVal(), active->getNewVal()); + } + } + + if(activeIdentifier->updated()) { + Serial.printf("update(): activeIdentifier %d -> %d\n", activeIdentifier->getVal(), activeIdentifier->getNewVal()); + } + + return true; + } + + void loop() { + + } +}; + +void setup() { + Serial.begin(115200); + homeSpan.begin(Category::Television,"HomeSpan Television"); + + new SpanAccessory(); + new Service::AccessoryInformation(); + new Characteristic::Name("Test HKTV"); + 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"); + + new HKTV(); +} + +void loop() { + homeSpan.poll(); +} diff --git a/src/Characteristics.h b/src/Characteristics.h index 5bfa525..930b110 100644 --- a/src/Characteristics.h +++ b/src/Characteristics.h @@ -68,6 +68,7 @@ struct HapChar { struct HapCharacteristics { HAPCHAR( Active, B0, PW+PR+EV, UINT8, true ); + HAPCHAR( ActiveIdentifier, E7, PW+PR+EV, UINT32, true ); HAPCHAR( AirQuality, 95, PR+EV, UINT8, true ); HAPCHAR( BatteryLevel, 68, PR+EV, UINT8, false ); HAPCHAR( Brightness, 8, PR+PW+EV, INT, false ); @@ -78,8 +79,10 @@ struct HapCharacteristics { HAPCHAR( CarbonDioxidePeakLevel, 94, PR+EV, FLOAT, false ); HAPCHAR( CarbonMonoxideDetected, 69, PR+EV, UINT8, true ); HAPCHAR( ChargingState, 8F, PR+EV, UINT8, true ); + HAPCHAR( ClosedCaptions, DD, PW+PR+EV, UINT8, true ); HAPCHAR( CoolingThresholdTemperature, D, PR+PW+EV, FLOAT, false ); HAPCHAR( ColorTemperature, CE, PR+PW+EV, UINT32, false ); + HAPCHAR( ConfiguredName, E3, PW+PR+EV, STRING, false ); HAPCHAR( ContactSensorState, 6A, PR+EV, UINT8, true ); HAPCHAR( CurrentAmbientLightLevel, 6B, PR+EV, FLOAT, false ); HAPCHAR( CurrentHorizontalTiltAngle, 6C, PR+EV, INT, false ); @@ -92,9 +95,12 @@ struct HapCharacteristics { HAPCHAR( CurrentFanState, AF, PR+EV, UINT8, true ); HAPCHAR( CurrentHeatingCoolingState, F, PR+EV, UINT8, true ); HAPCHAR( CurrentHeaterCoolerState, B1, PR+EV, UINT8, true ); + HAPCHAR( CurrentMediaState, E0, PR+EV, UINT8, true ); HAPCHAR( CurrentRelativeHumidity, 10, PR+EV, FLOAT, false ); HAPCHAR( CurrentTemperature, 11, PR+EV, FLOAT, false ); HAPCHAR( CurrentTiltAngle, C1, PR+EV, INT, false ); + HAPCHAR( CurrentVisibilityState, 135, PR+EV, UINT8, true ); +// HAPCHAR( DisplayOrder, 136, PW+PR+EV, TLV8, false ); HAPCHAR( FilterLifeLevel, AB, PR+EV, FLOAT, false ); HAPCHAR( FilterChangeIndication, AC, PR+EV, UINT8, true ); HAPCHAR( FirmwareRevision, 52, PR, STRING, true ); @@ -103,6 +109,9 @@ struct HapCharacteristics { HAPCHAR( HoldPosition, 6F, PW, BOOL, true ); HAPCHAR( Hue, 13, PR+PW+EV, FLOAT, false ); HAPCHAR( Identify, 14, PW, BOOL, true ); + HAPCHAR( Identifier, E6, PR, UINT32, true ); + HAPCHAR( InputDeviceType, DC, PR+EV, UINT8, true ); + HAPCHAR( InputSourceType, DB, PR+EV, UINT8, true ); HAPCHAR( InUse, D2, PR+EV, UINT8, true ); HAPCHAR( IsConfigured, D6, PR+EV, UINT8, true ); HAPCHAR( LeakDetected, 70, PR+EV, UINT8, true ); @@ -122,12 +131,15 @@ struct HapCharacteristics { HAPCHAR( On, 25, PR+PW+EV, BOOL, true ); HAPCHAR( OzoneDensity, C3, PR+EV, FLOAT, false ); HAPCHAR( PM10Density, C7, PR+EV, FLOAT, false ); + HAPCHAR( PictureMode, E2, PW+PR+EV, UINT8, true ); HAPCHAR( PositionState, 72, PR+EV, UINT8, true ); + HAPCHAR( PowerModeSelection, DF, PW, UINT8, true ); HAPCHAR( ProgramMode, D1, PR+EV, UINT8, true ); HAPCHAR( ProgrammableSwitchEvent, 73, PR+EV+NV, UINT8, true ); HAPCHAR( RelativeHumidityDehumidifierThreshold, C9, PR+PW+EV, FLOAT, false ); HAPCHAR( RelativeHumidityHumidifierThreshold, CA, PR+PW+EV, FLOAT, false ); HAPCHAR( RemainingDuration, D4, PR+EV, UINT32, false ); + HAPCHAR( RemoteKey, E1, PW, UINT8, true ); HAPCHAR( ResetFilterIndication, AD, PW, UINT8, true ); HAPCHAR( RotationDirection, 28, PR+PW+EV, INT, true ); HAPCHAR( RotationSpeed, 29, PR+PW+EV, FLOAT, false ); @@ -139,6 +151,7 @@ struct HapCharacteristics { HAPCHAR( ServiceLabelIndex, CB, PR, UINT8, true ); HAPCHAR( ServiceLabelNamespace, CD, PR, UINT8, true ); HAPCHAR( SlatType, C0, PR, UINT8, true ); + HAPCHAR( SleepDiscoveryMode, E8, PR+EV, UINT8, true ); HAPCHAR( SmokeDetected, 76, PR+EV, UINT8, true ); HAPCHAR( StatusActive, 75, PR+EV, BOOL, true ); HAPCHAR( StatusFault, 77, PR+EV, UINT8, true ); @@ -157,15 +170,20 @@ struct HapCharacteristics { HAPCHAR( TargetPosition, 7C, PW+PR+EV, UINT8, false ); HAPCHAR( TargetDoorState, 32, PW+PR+EV, UINT8, true ); HAPCHAR( TargetHeatingCoolingState, 33, PW+PR+EV, UINT8, true ); + HAPCHAR( TargetMediaState, 137, PW+PR+EV, UINT8, true ); HAPCHAR( TargetRelativeHumidity, 34, PW+PR+EV, FLOAT, false ); HAPCHAR( TargetTemperature, 35, PW+PR+EV, FLOAT, false ); + HAPCHAR( TargetVisibilityState, 134, PW+PR+EV, UINT8, true ); HAPCHAR( TemperatureDisplayUnits, 36, PW+PR+EV, UINT8, true ); HAPCHAR( TargetVerticalTiltAngle, 7D, PW+PR+EV, INT, false ); HAPCHAR( ValveType, D5, PR+EV, UINT8, true ); HAPCHAR( Version, 37, PR, STRING, true ); HAPCHAR( VOCDensity, C8, PR+EV, FLOAT, false ); HAPCHAR( Volume, 119, PW+PR+EV, UINT8, false ); - HAPCHAR( WaterLevel, B5, PR+EV, FLOAT, false ); + HAPCHAR( VolumeControlType, E9, PR+EV, UINT8, true ); + HAPCHAR( VolumeSelector, EA, PR+EV, UINT8, true ); + HAPCHAR( WaterLevel, B5, PR+EV, FLOAT, false ); + }; extern HapCharacteristics hapChars; diff --git a/src/Settings.h b/src/Settings.h index ccf0323..64f256f 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -127,5 +127,6 @@ enum class Category { Dehumidifiers=23, Sprinklers=28, Faucets=29, - ShowerSystems=30 + ShowerSystems=30, + Television=31 }; diff --git a/src/Span.h b/src/Span.h index 64695af..90c07b6 100644 --- a/src/Span.h +++ b/src/Span.h @@ -199,6 +199,18 @@ namespace Service { OPT(StatusLowBattery); }}; + struct InputSource : SpanService { InputSource() : SpanService{"D9","InputSource"}{ + REQ(ConfiguredName); + REQ(InputSourceType); + REQ(IsConfigured); + REQ(Name); + REQ(CurrentVisibilityState); + + OPT(Identifier); + OPT(InputDeviceType); + OPT(TargetVisibilityState); + }}; + struct IrrigationSystem : SpanService { IrrigationSystem() : SpanService{"CF","IrrigationSystem"}{ REQ(Active); REQ(ProgramMode); @@ -319,6 +331,32 @@ namespace Service { OPT(Name); }}; + struct Television : SpanService { Television() : SpanService{"D8","Television"}{ + REQ(Active); + REQ(ActiveIdentifier); + REQ(ConfiguredName); + REQ(RemoteKey); + REQ(SleepDiscoveryMode); + + OPT(Brightness); + OPT(ClosedCaptions); + //OPT(DisplayOrder); + OPT(CurrentMediaState); + OPT(TargetMediaState); + OPT(Name); + OPT(PictureMode); + OPT(PowerModeSelection); + }}; + + struct TelevisionSpeaker : SpanService { TelevisionSpeaker() : SpanService{"113","TelevisionSpeaker"}{ + REQ(Mute); + + OPT(Active); + OPT(Volume); + OPT(VolumeControlType); + OPT(VolumeSelector); + }}; + struct TemperatureSensor : SpanService { TemperatureSensor() : SpanService{"8A","TemperatureSensor"}{ REQ(CurrentTemperature); OPT(Name); @@ -389,6 +427,7 @@ namespace Service { namespace Characteristic { CREATE_CHAR(uint8_t,Active,0,0,1); + CREATE_CHAR(uint32_t,ActiveIdentifier,0,0,100); CREATE_CHAR(uint8_t,AirQuality,0,0,5); CREATE_CHAR(uint8_t,BatteryLevel,0,0,100); CREATE_CHAR(int,Brightness,0,0,100); @@ -399,23 +438,28 @@ namespace Characteristic { CREATE_CHAR(double,CarbonDioxidePeakLevel,0,0,100000); CREATE_CHAR(uint8_t,CarbonDioxideDetected,0,0,1); CREATE_CHAR(uint8_t,ChargingState,0,0,2); + CREATE_CHAR(uint8_t,ClosedCaptions,0,0,1); CREATE_CHAR(double,CoolingThresholdTemperature,10,10,35); CREATE_CHAR(uint32_t,ColorTemperature,200,140,500); CREATE_CHAR(uint8_t,ContactSensorState,1,0,1); + CREATE_CHAR(const char *,ConfiguredName,"unnamed",0,1); CREATE_CHAR(double,CurrentAmbientLightLevel,1,0.0001,100000); CREATE_CHAR(int,CurrentHorizontalTiltAngle,0,-90,90); CREATE_CHAR(uint8_t,CurrentAirPurifierState,1,0,2); CREATE_CHAR(uint8_t,CurrentSlatState,0,0,2); CREATE_CHAR(uint8_t,CurrentPosition,0,0,100); CREATE_CHAR(int,CurrentVerticalTiltAngle,0,-90,90); + CREATE_CHAR(uint8_t,CurrentVisibilityState,0,0,1); CREATE_CHAR(uint8_t,CurrentHumidifierDehumidifierState,1,0,3); CREATE_CHAR(uint8_t,CurrentDoorState,1,0,4); CREATE_CHAR(uint8_t,CurrentFanState,1,0,2); CREATE_CHAR(uint8_t,CurrentHeatingCoolingState,0,0,2); CREATE_CHAR(uint8_t,CurrentHeaterCoolerState,1,0,3); + CREATE_CHAR(uint8_t,CurrentMediaState,0,0,5); CREATE_CHAR(double,CurrentRelativeHumidity,0,0,100); CREATE_CHAR(double,CurrentTemperature,0,0,100); CREATE_CHAR(int,CurrentTiltAngle,0,-90,90); +// CREATE_CHAR(tlv8,DisplayOrder,0,0,1); CREATE_CHAR(double,FilterLifeLevel,0,0,100); CREATE_CHAR(uint8_t,FilterChangeIndication,0,0,1); CREATE_CHAR(const char *,FirmwareRevision,"1.0.0",0,1); @@ -424,6 +468,9 @@ namespace Characteristic { CREATE_CHAR(boolean,HoldPosition,false,0,1); CREATE_CHAR(double,Hue,0,0,360); CREATE_CHAR(boolean,Identify,false,0,1); + CREATE_CHAR(uint32_t,Identifier,0,0,100); + CREATE_CHAR(uint8_t,InputDeviceType,0,0,6); + CREATE_CHAR(uint8_t,InputSourceType,0,0,10); CREATE_CHAR(uint8_t,InUse,0,0,1); CREATE_CHAR(uint8_t,IsConfigured,0,0,1); CREATE_CHAR(uint8_t,LeakDetected,0,0,1); @@ -442,13 +489,16 @@ namespace Characteristic { CREATE_CHAR(boolean,OutletInUse,false,0,1); CREATE_CHAR(boolean,On,false,0,1); CREATE_CHAR(double,OzoneDensity,0,0,1000); + CREATE_CHAR(uint8_t,PictureMode,0,0,13); CREATE_CHAR(double,PM10Density,0,0,1000); CREATE_CHAR(uint8_t,PositionState,2,0,2); + CREATE_CHAR(uint8_t,PowerModeSelection,0,0,1); CREATE_CHAR(uint8_t,ProgramMode,0,0,2); CREATE_CHAR(uint8_t,ProgrammableSwitchEvent,0,0,2); CREATE_CHAR(double,RelativeHumidityDehumidifierThreshold,50,0,100); CREATE_CHAR(double,RelativeHumidityHumidifierThreshold,50,0,100); CREATE_CHAR(uint32_t,RemainingDuration,60,0,3600); + CREATE_CHAR(uint8_t,RemoteKey,0,0,16); CREATE_CHAR(uint8_t,ResetFilterIndication,0,1,1); CREATE_CHAR(int,RotationDirection,0,0,1); CREATE_CHAR(double,RotationSpeed,0,0,100); @@ -460,6 +510,7 @@ namespace Characteristic { CREATE_CHAR(uint8_t,ServiceLabelIndex,1,1,255); CREATE_CHAR(uint8_t,ServiceLabelNamespace,1,0,1); CREATE_CHAR(uint8_t,SlatType,0,0,1); + CREATE_CHAR(uint8_t,SleepDiscoveryMode,0,0,1); CREATE_CHAR(uint8_t,SmokeDetected,0,0,1); CREATE_CHAR(boolean,StatusActive,true,0,1); CREATE_CHAR(uint8_t,StatusFault,0,0,1); @@ -478,14 +529,18 @@ namespace Characteristic { CREATE_CHAR(uint8_t,TargetPosition,0,0,100); CREATE_CHAR(uint8_t,TargetDoorState,1,0,1); CREATE_CHAR(uint8_t,TargetHeatingCoolingState,0,0,3); + CREATE_CHAR(uint8_t,TargetMediaState,0,0,2); CREATE_CHAR(double,TargetRelativeHumidity,0,0,100); CREATE_CHAR(double,TargetTemperature,16,10,38); + CREATE_CHAR(uint8_t,TargetVisibilityState,0,0,1); CREATE_CHAR(uint8_t,TemperatureDisplayUnits,0,0,1); CREATE_CHAR(int,TargetVerticalTiltAngle,0,-90,90); CREATE_CHAR(uint8_t,ValveType,0,0,3); CREATE_CHAR(const char *,Version,"1.0.0",0,1); CREATE_CHAR(double,VOCDensity,0,0,1000); CREATE_CHAR(uint8_t,Volume,0,0,100); + CREATE_CHAR(uint8_t,VolumeControlType,0,0,3); + CREATE_CHAR(uint8_t,VolumeSelector,0,0,1); CREATE_CHAR(double,WaterLevel,0,0,100); - + } From 586f690727aca509c97e34c2d46bd706bc59c210 Mon Sep 17 00:00:00 2001 From: Raal Goff Date: Sat, 7 Aug 2021 21:46:16 +0800 Subject: [PATCH 02/64] increase allowed range for Identifier characteristic --- src/Span.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Span.h b/src/Span.h index 90c07b6..e21bb6b 100644 --- a/src/Span.h +++ b/src/Span.h @@ -468,7 +468,7 @@ namespace Characteristic { CREATE_CHAR(boolean,HoldPosition,false,0,1); CREATE_CHAR(double,Hue,0,0,360); CREATE_CHAR(boolean,Identify,false,0,1); - CREATE_CHAR(uint32_t,Identifier,0,0,100); + CREATE_CHAR(uint32_t,Identifier,0,0,255); CREATE_CHAR(uint8_t,InputDeviceType,0,0,6); CREATE_CHAR(uint8_t,InputSourceType,0,0,10); CREATE_CHAR(uint8_t,InUse,0,0,1); From 86f99f696b43af72d444074e9b9d594b51b02eaf Mon Sep 17 00:00:00 2001 From: Raal Goff Date: Sun, 8 Aug 2021 09:27:25 +0800 Subject: [PATCH 03/64] allow 'value' property in characteristics updates to have whitespace --- src/HomeSpan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 4c4480d..5409393 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -1115,7 +1115,7 @@ int Span::updateCharacteristics(char *buf, SpanBuf *pObj){ pObj[nObj].iid=atoi(t3); okay|=2; } else - if(!strcmp(t2,"value") && (t3=strtok_r(t1,"}[]:, \"\t\n\r",&p2))){ + if(!strcmp(t2,"value") && (t3=strtok_r(t1,"}[]:,\"",&p2))){ pObj[nObj].val=t3; okay|=4; } else From fc9714ed953df1edfef58ba1e7653c4ad9fa0394 Mon Sep 17 00:00:00 2001 From: Raal Goff Date: Mon, 9 Aug 2021 19:41:31 +0800 Subject: [PATCH 04/64] ConfiguredName is writeable, allow writes to string characteristics when they have write perms --- src/HomeSpan.cpp | 7 +++++++ src/HomeSpan.h | 45 ++++++++++++++++++++++++++++++++------------- src/Span.h | 2 +- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 5409393..9ecf68f 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -1701,6 +1701,13 @@ StatusCode SpanCharacteristic::loadUpdate(char *val, char *ev){ return(StatusCode::InvalidValue); break; + case STRING: + newValue.STRING = (char *)realloc(newValue.STRING, strlen(val) + 1); + strncpy(newValue.STRING, val, strlen(val)); + newValue.STRING[strlen(val)] = '\0'; + + break; + default: break; diff --git a/src/HomeSpan.h b/src/HomeSpan.h index d1e5b14..aace617 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -243,7 +243,7 @@ struct SpanCharacteristic{ uint64_t UINT64; int32_t INT; double FLOAT; - const char *STRING; + char *STRING = NULL; }; int iid=0; // Instance ID (HAP Table 6-3) @@ -300,8 +300,24 @@ struct SpanCharacteristic{ } // switch } // str() - void uvSet(UVal &u, const char *val){ - u.STRING=val; + void uvSet(UVal &u, const char *val){ + u.STRING = (char *)realloc(u.STRING, strlen(val) + 1); + strncpy(u.STRING, val, strlen(val)); + u.STRING[strlen(val)] = '\0'; + } + + char *getString(){ + if(format == FORMAT::STRING) + return value.STRING; + + return NULL; + } + + char *getNewString(){ + if(format == FORMAT::STRING) + return newValue.STRING; + + return NULL; } template void uvSet(UVal &u, T val){ @@ -389,9 +405,12 @@ struct SpanCharacteristic{ uvSet(value,val); uvSet(newValue,val); - uvSet(minValue,min); - uvSet(maxValue,max); - uvSet(stepValue,0); + + if(format != FORMAT::STRING) { + uvSet(minValue,min); + uvSet(maxValue,max); + uvSet(stepValue,0); + } if(nvsStore){ nvsKey=(char *)malloc(16); @@ -413,7 +432,7 @@ struct SpanCharacteristic{ } homeSpan.configLog+="(" + uvPrint(value) + ")" + ": IID=" + String(iid) + ", UUID=0x" + String(type); - if(format!=STRING && format!=BOOL) + if(format!=FORMAT::STRING && format!=FORMAT::BOOL) homeSpan.configLog+= " Range=[" + String(uvPrint(minValue)) + "," + String(uvPrint(maxValue)) + "]"; if(nvsFlag==2) @@ -452,20 +471,20 @@ struct SpanCharacteristic{ template T getVal(){ return(uvGet(value)); - } - + } + template T getNewVal(){ return(uvGet(newValue)); - } + } template void setVal(T val){ - if(format==STRING){ - Serial.printf("\n*** WARNING: Attempt to update Characteristic::%s(\"%s\") with setVal() ignored. Can't update STRING Characteristics once they are initialized!\n\n",hapName,value.STRING); + if(format==FORMAT::STRING && perms & PW == 0){ + Serial.printf("\n*** WARNING: Attempt to update Characteristic::%s(\"%s\") with setVal() ignored. No WRITE permission on this characteristic\n\n",hapName,value.STRING); return; } - if(val < uvGet(minValue) || val > uvGet(maxValue)){ + if(format!=FORMAT::STRING && ( val < uvGet(minValue) || val > uvGet(maxValue))){ Serial.printf("\n*** WARNING: Attempt to update Characteristic::%s with setVal(%llg) is out of range [%llg,%llg]. This may cause device to become non-reponsive!\n\n", hapName,(double)val,uvGet(minValue),uvGet(maxValue)); } diff --git a/src/Span.h b/src/Span.h index e21bb6b..3bbef25 100644 --- a/src/Span.h +++ b/src/Span.h @@ -427,7 +427,7 @@ namespace Service { namespace Characteristic { CREATE_CHAR(uint8_t,Active,0,0,1); - CREATE_CHAR(uint32_t,ActiveIdentifier,0,0,100); + CREATE_CHAR(uint32_t,ActiveIdentifier,0,0,255); CREATE_CHAR(uint8_t,AirQuality,0,0,5); CREATE_CHAR(uint8_t,BatteryLevel,0,0,100); CREATE_CHAR(int,Brightness,0,0,100); From 5bced713454116234407b5c98b8adfa9b5c61040 Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 15 Oct 2021 17:42:17 -0500 Subject: [PATCH 05/64] Changed rf.add() and rf.phase() to allow for uint32_t durations Instead of limiting number of ticks to 15-bits (32767), RFControl allows for tick size to be any 32-bit number. If ticks > 32767, RFControl adds repeated LOW or HIGH phases as needed to match full duration. This provides for much more flexibility in creating pulse trains that include very long-duration "spaces" between repeats. --- src/extras/RFControl.cpp | 22 +++++++++++++--------- src/extras/RFControl.h | 4 ++-- src/extras/extras.ino | 34 ++++++++++++++++++++++------------ 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/extras/RFControl.cpp b/src/extras/RFControl.cpp index ed89819..0f4d467 100644 --- a/src/extras/RFControl.cpp +++ b/src/extras/RFControl.cpp @@ -69,11 +69,12 @@ void RFControl::start(uint32_t *data, int nData, uint8_t nCycles, uint8_t tickTi void RFControl::clear(){ data.clear(); + lowWord=true; } /////////////////// -void RFControl::add(uint16_t onTime, uint16_t offTime){ +void RFControl::add(uint32_t onTime, uint32_t offTime){ phase(onTime,HIGH); phase(offTime,LOW); @@ -81,16 +82,19 @@ void RFControl::add(uint16_t onTime, uint16_t offTime){ /////////////////// -void RFControl::phase(uint16_t nTicks, uint8_t phase){ +void RFControl::phase(uint32_t nTicks, uint8_t phase){ - uint32_t ticks=nTicks&0x7FFF; + while(nTicks>0){ // create as many repeated phases as needed to accomodate duration of nTicks + uint32_t ticks=nTicks>0x7FFF?0x7FFF:nTicks; + nTicks-=ticks; - if(lowWord) - data.push_back(ticks | (phase?(1<<15):0)); - else - data.back()|=ticks<<16 | (phase?(1<<31):0); - - lowWord=!lowWord; + if(lowWord) + data.push_back(ticks | (phase?(1<<15):0)); + else + data.back()|=ticks<<16 | (phase?(1<<31):0); + + lowWord=!lowWord; + } } /////////////////// diff --git a/src/extras/RFControl.h b/src/extras/RFControl.h index b840f37..95c23be 100644 --- a/src/extras/RFControl.h +++ b/src/extras/RFControl.h @@ -22,8 +22,8 @@ class RFControl { void start(uint8_t nCycles=1, uint8_t tickTime=1); // starts transmission of pulses from internal data structure, repeated for numCycles, where each tick in pulse is tickTime microseconds long void clear(); // clears transmitter memory - void add(uint16_t onTime, uint16_t offTime); // adds pulse of onTime ticks HIGH followed by offTime ticks LOW - void phase(uint16_t nTicks, uint8_t phase); // adds either a HIGH phase or LOW phase lasting numTicks ticks + void add(uint32_t onTime, uint32_t offTime); // adds pulse of onTime ticks HIGH followed by offTime ticks LOW + void phase(uint32_t nTicks, uint8_t phase); // adds either a HIGH phase or LOW phase lasting numTicks ticks }; // Helper macro for creating your own storage of uint32_t data array elements - used with first variation of start() above diff --git a/src/extras/extras.ino b/src/extras/extras.ino index c709135..795c6ad 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -10,23 +10,33 @@ void setup() { Serial.println("\n\nHomeSpan RF Transmitter Example"); - RFControl rf(18); // create an instance of RFControl with signal output to pin 6 + RFControl rf(17); // create an instance of RFControl with signal output to pin 17 -#define NPOINTS 3 - - uint32_t data[NPOINTS]; + rf.clear(); - for(int i=0;i Date: Fri, 15 Oct 2021 18:16:18 -0500 Subject: [PATCH 06/64] Add clock selection to RFControl (80 MHz or 1 MHz) Added second argument to RFControl(uint8_t pin, boolean refClock=true) to allow choice of Ref Tick (1 MHz) clock or APB (80 MHz) clock. Default is to use 1 MHz Ref Tick. Also fixed bug in logic that divides clock for ESP32-C3. Factor should be 79, not 80, since divider is apparently configured to divide by factor+1. --- src/extras/RFControl.cpp | 7 ++++--- src/extras/RFControl.h | 2 +- src/extras/extras.ino | 18 +++--------------- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/extras/RFControl.cpp b/src/extras/RFControl.cpp index 0f4d467..7f62c81 100644 --- a/src/extras/RFControl.cpp +++ b/src/extras/RFControl.cpp @@ -6,7 +6,7 @@ /////////////////// -RFControl::RFControl(uint8_t pin){ +RFControl::RFControl(uint8_t pin, boolean refClock){ #ifdef CONFIG_IDF_TARGET_ESP32C3 if(nChannels==RMT_CHANNEL_MAX/2){ @@ -34,10 +34,11 @@ RFControl::RFControl(uint8_t pin){ rmt_config(config); rmt_driver_install(config->channel,0,0); - // Below we set the base clock to 1 MHz so tick-units are in microseconds (before CLK_DIV) + // If specified, set the base clock to 1 MHz so tick-units are in microseconds (before any CLK_DIV is applied), otherwise default will be 80 MHz APB clock + if(refClock) #ifdef CONFIG_IDF_TARGET_ESP32C3 - REG_SET_FIELD(RMT_SYS_CONF_REG,RMT_SCLK_DIV_NUM,80); // ESP32-C3 does not have a 1 MHz REF Tick Clock, but allows the 80 MHz APB clock to be scaled by an additional RMT-specific divider + REG_SET_FIELD(RMT_SYS_CONF_REG,RMT_SCLK_DIV_NUM,79); // ESP32-C3 does not have a 1 MHz REF Tick Clock, but allows the 80 MHz APB clock to be scaled by an additional RMT-specific divider #else rmt_set_source_clk(config->channel,RMT_BASECLK_REF); // use 1 MHz REF Tick Clock for ESP32 and ESP32-S2 #endif diff --git a/src/extras/RFControl.h b/src/extras/RFControl.h index 95c23be..5ca787f 100644 --- a/src/extras/RFControl.h +++ b/src/extras/RFControl.h @@ -16,7 +16,7 @@ class RFControl { static uint8_t nChannels; public: - RFControl(uint8_t pin); // creates transmitter on pin + RFControl(uint8_t pin, boolean refClock=true); // creates transmitter on pin, using 1-MHz Ref Tick clock void start(uint32_t *data, int nData, uint8_t nCycles=1, uint8_t tickTime=1); // starts transmission of pulses from specified data pointer, repeated for numCycles, where each tick in pulse is tickTime microseconds long void start(uint8_t nCycles=1, uint8_t tickTime=1); // starts transmission of pulses from internal data structure, repeated for numCycles, where each tick in pulse is tickTime microseconds long diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 795c6ad..f104b67 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -10,32 +10,20 @@ void setup() { Serial.println("\n\nHomeSpan RF Transmitter Example"); - RFControl rf(17); // create an instance of RFControl with signal output to pin 17 + RFControl rf(10); // create an instance of RFControl with signal output to pin 17 rf.clear(); rf.add(10000,10000); rf.add(10000,10000); - rf.add(10000,40000); + rf.add(10000,30000); uint32_t t0=micros(); - rf.start(4,100); + rf.start(4,1); uint32_t t1=micros(); Serial.println("End Example"); Serial.println((t1-t0)/1000); - - rf.clear(); - - rf.add(10000,10000); - rf.add(10000,10000); - - t0=micros(); - rf.start(4,100); - t1=micros(); - - Serial.println("End Example"); - Serial.println((t1-t0)/1000); } // end of setup() From 5976fd3d0d19dc27479fadc13f7616adc63d08f7 Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 15 Oct 2021 22:32:03 -0500 Subject: [PATCH 07/64] Add enableCarrier(uint32_t frequency, float duty) and disableCarrier() to RFControl Allows you to overlay a carrier wave on the RF Signal - in practice this is only used for IR signals (not RF). Automatically scales frequency to account for 80x difference between APB Clock and Ref Tick Clock depending on which is used. Checks to ensure resulting parameters (high period and low period) are all in bounds (0,65536) and reports an error if they are not. --- src/extras/RFControl.cpp | 38 ++++++++++++++++++++++++++++++++++++++ src/extras/RFControl.h | 3 +++ src/extras/extras.ino | 17 +++++++++++++---- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/extras/RFControl.cpp b/src/extras/RFControl.cpp index 7f62c81..ce2ce88 100644 --- a/src/extras/RFControl.cpp +++ b/src/extras/RFControl.cpp @@ -36,6 +36,8 @@ RFControl::RFControl(uint8_t pin, boolean refClock){ // If specified, set the base clock to 1 MHz so tick-units are in microseconds (before any CLK_DIV is applied), otherwise default will be 80 MHz APB clock + this->refClock=refClock; + if(refClock) #ifdef CONFIG_IDF_TARGET_ESP32C3 REG_SET_FIELD(RMT_SYS_CONF_REG,RMT_SCLK_DIV_NUM,79); // ESP32-C3 does not have a 1 MHz REF Tick Clock, but allows the 80 MHz APB clock to be scaled by an additional RMT-specific divider @@ -100,4 +102,40 @@ void RFControl::phase(uint32_t nTicks, uint8_t phase){ /////////////////// +void RFControl::enableCarrier(uint32_t freq, float duty){ + + if(duty<0) + duty=0; + if(duty>1) + duty=1; + + if(freq>0){ + float period=1.0e6/freq*(refClock?1:80); + uint32_t highTime=period*duty+0.5; + uint32_t lowTime=period*(1.0-duty)+0.5; + + if(highTime>0xFFFF || lowTime>0xFFFF){ + Serial.printf("\n*** ERROR: Can't enable carrier frequency=%d Hz for RF Control pin=%d, duty=%0.2f. Frequency is too low!\n\n",freq,config->gpio_num,duty); + return; + } + + if(highTime==0){ + Serial.printf("\n*** ERROR: Can't enable carrier frequency=%d Hz for RF Control pin=%d, duty=%0.2f. Duty is too low or frequency is too high!\n\n",freq,config->gpio_num,duty); + return; + } + + if(lowTime==0){ + Serial.printf("\n*** ERROR: Can't enable carrier frequency=%d Hz for RF Control pin=%d, duty=%0.2f. Duty is too high or frequency is too high!\n\n",freq,config->gpio_num,duty); + return; + } + +// Serial.printf("%d %g %d %d\n",freq,period,highTime,lowTime); + rmt_set_tx_carrier(config->channel,true,highTime,lowTime,RMT_CARRIER_LEVEL_HIGH); + } else { + rmt_set_tx_carrier(config->channel,false,0,0,RMT_CARRIER_LEVEL_HIGH); + } +} + +/////////////////// + uint8_t RFControl::nChannels=0; diff --git a/src/extras/RFControl.h b/src/extras/RFControl.h index 5ca787f..c099935 100644 --- a/src/extras/RFControl.h +++ b/src/extras/RFControl.h @@ -13,6 +13,7 @@ class RFControl { rmt_config_t *config=NULL; vector data; boolean lowWord=true; + boolean refClock; static uint8_t nChannels; public: @@ -24,6 +25,8 @@ class RFControl { void clear(); // clears transmitter memory void add(uint32_t onTime, uint32_t offTime); // adds pulse of onTime ticks HIGH followed by offTime ticks LOW void phase(uint32_t nTicks, uint8_t phase); // adds either a HIGH phase or LOW phase lasting numTicks ticks + void enableCarrier(uint32_t freq, float duty=0.5); // enables carrier wave if freq>0, else disables carrier wave; duty is a fraction from 0-1 + void disableCarrier(){enableCarrier(0);} // disables carrier wave }; // Helper macro for creating your own storage of uint32_t data array elements - used with first variation of start() above diff --git a/src/extras/extras.ino b/src/extras/extras.ino index f104b67..4640ae7 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -14,12 +14,21 @@ void setup() { rf.clear(); - rf.add(10000,10000); - rf.add(10000,10000); - rf.add(10000,30000); +// rf.add(1000000,1000000); +// rf.add(1000000,1000000); + rf.add(100000,1); + + for(int i=100;i>=0;i-=5){ + rf.enableCarrier(38000,i/100.0); + rf.start(1,4); + } +// rf.disableCarrier(); +// rf.start(1,200); + + while(1); uint32_t t0=micros(); - rf.start(4,1); + rf.start(4,80); uint32_t t1=micros(); Serial.println("End Example"); From 760abae5ce5b454ecbb4e6154650666295ba20f4 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 16 Oct 2021 08:16:22 -0500 Subject: [PATCH 08/64] Testing pronto codes --- src/extras/extras.ino | 55 ++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 4640ae7..409aebd 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -2,38 +2,65 @@ #include "RFControl.h" // include RF Control Library +#define PRONTO_N 0.241246 + +uint16_t pronto[]={0000,0x006D,0x0000,0x0022,0x00AC,0x00AC,0x0015,0x0040,0x0015,0x0040,0x0015,0x0040,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0040,0x0015,0x0040,0x0015,0x0040,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0040,0x0015,0x0015,0x0015,0x0015,0x0015,0x0040,0x0015,0x0040,0x0015,0x0015,0x0015,0x0015,0x0015,0x0040,0x0015,0x0015,0x0015,0x0040,0x0015,0x0040,0x0015,0x0015,0x0015,0x0015,0x0015,0x0040,0x0015,0x0040,0x0015,0x0015,0x0015,0x0689}; +//uint16_t pronto[]={0000,0x006D,0x0000,0x0022,0x00AC,0x00AC,0x0017,0x003E,0x0017,0x003E,0x0017,0x003E,0x0017,0x0013,0x0017,0x0013,0x0017,0x0013,0x0017,0x0013,0x0017,0x0013,0x0017,0x003E,0x0017,0x003E,0x0017,0x003E,0x0017,0x0013,0x0017,0x0013,0x0017,0x0013,0x0017,0x0013,0x0017,0x0013,0x0017,0x0013,0x0017,0x0013,0x0017,0x0013,0x0017,0x003E,0x0017,0x003E,0x0017,0x0013,0x0017,0x0013,0x0017,0x003E,0x0017,0x003E,0x0018,0x003E,0x0017,0x003E,0x0017,0x0013,0x0017,0x0013,0x0017,0x003E,0x0017,0x003E,0x0017,0x0013,0x0017,0x0746}; +//uint16_t pronto[]={0000,0x0067,0x0000,0x000d,0x0060,0x0018,0x0018,0x0018,0x0030,0x0018,0x0030,0x0018,0x0030,0x0018,0x0018,0x0018,0x0030,0x0018,0x0018,0x0018,0x0030,0x0018,0x0018,0x0018,0x0018,0x0018,0x0018,0x0018,0x0018,0x03f6}; + +uint32_t data[100]; + void setup() { Serial.begin(115200); // start the Serial interface Serial.flush(); delay(1000); // wait for interface to flush - Serial.println("\n\nHomeSpan RF Transmitter Example"); + Serial.println("\n\nHomeSpan RF Transmitter Example\n\n"); - RFControl rf(10); // create an instance of RFControl with signal output to pin 17 + RFControl rf(10); // create an instance of RFControl with signal output to pin 10 + + uint32_t cf = 1e6/(pronto[1]*PRONTO_N); + double unit=pronto[1]*PRONTO_N; + + Serial.printf("Carrier Frequency = %d\n",cf); + Serial.printf("Unit = %g\n",unit); + + rf.enableCarrier( + + Serial.printf("CONF0: %d\n",REG_GET_FIELD(RMT_CH0CONF0_REG,RMT_DIV_CNT_CH0)); + Serial.printf("CONF1: %d\n",REG_GET_FIELD(RMT_CH0CONF1_REG,RMT_REF_ALWAYS_ON_CH0)); + Serial.printf("CHIGH: %d\n",REG_GET_FIELD(RMT_CH0CARRIER_DUTY_REG,RMT_CARRIER_HIGH_CH0)); + Serial.printf("CLOW: %d\n",REG_GET_FIELD(RMT_CH0CARRIER_DUTY_REG,RMT_CARRIER_LOW_CH0)); rf.clear(); -// rf.add(1000000,1000000); -// rf.add(1000000,1000000); - rf.add(100000,1); + uint32_t len=0; - for(int i=100;i>=0;i-=5){ - rf.enableCarrier(38000,i/100.0); - rf.start(1,4); + for(int i=0,n=4;i0){ + uint16_t data = pulse>32767?32767:pulse; + boolean phase=(i%2==0); + Serial.printf("Pulse %d: %d %d %d\n",i,pronto[n],data,phase); + pulse-=32767; + rf.phase(data,phase); + len+=data; + } + n++; } -// rf.disableCarrier(); -// rf.start(1,200); - while(1); + int repeat=20; + + Serial.printf("Total Time: %d usec\n",len*repeat); uint32_t t0=micros(); - rf.start(4,80); + rf.start(repeat,1); uint32_t t1=micros(); + Serial.printf("Actual Time: %d usec\n",t1-t0); Serial.println("End Example"); - Serial.println((t1-t0)/1000); - + } // end of setup() void loop(){ From 1566f93274bf2a32126b2253cd09753f3a58e645 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 16 Oct 2021 11:33:49 -0500 Subject: [PATCH 09/64] Update extras.ino --- src/extras/extras.ino | 52 +++++++++++++------------------------------ 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 409aebd..cd42ab9 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -18,48 +18,28 @@ void setup() { Serial.println("\n\nHomeSpan RF Transmitter Example\n\n"); - RFControl rf(10); // create an instance of RFControl with signal output to pin 10 + RFControl rf(17); + rf.enableCarrier(38000,0.5); - uint32_t cf = 1e6/(pronto[1]*PRONTO_N); - double unit=pronto[1]*PRONTO_N; +// uint32_t code = 0xE0E019E6; // OFF + uint32_t code = 0xE0E09966; // ON - Serial.printf("Carrier Frequency = %d\n",cf); - Serial.printf("Unit = %g\n",unit); + int unit=563; - rf.enableCarrier( - - Serial.printf("CONF0: %d\n",REG_GET_FIELD(RMT_CH0CONF0_REG,RMT_DIV_CNT_CH0)); - Serial.printf("CONF1: %d\n",REG_GET_FIELD(RMT_CH0CONF1_REG,RMT_REF_ALWAYS_ON_CH0)); - Serial.printf("CHIGH: %d\n",REG_GET_FIELD(RMT_CH0CARRIER_DUTY_REG,RMT_CARRIER_HIGH_CH0)); - Serial.printf("CLOW: %d\n",REG_GET_FIELD(RMT_CH0CARRIER_DUTY_REG,RMT_CARRIER_LOW_CH0)); - - rf.clear(); - - uint32_t len=0; - - for(int i=0,n=4;i0){ - uint16_t data = pulse>32767?32767:pulse; - boolean phase=(i%2==0); - Serial.printf("Pulse %d: %d %d %d\n",i,pronto[n],data,phase); - pulse-=32767; - rf.phase(data,phase); - len+=data; - } - n++; + rf.add(4500,4500); + + for(int i=31;i>=0;i--){ + rf.add(unit,unit*((code&(1< Date: Sat, 16 Oct 2021 14:49:50 -0500 Subject: [PATCH 10/64] Update extras.ino --- src/extras/extras.ino | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/extras/extras.ino b/src/extras/extras.ino index cd42ab9..6fcf935 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -21,8 +21,8 @@ void setup() { RFControl rf(17); rf.enableCarrier(38000,0.5); -// uint32_t code = 0xE0E019E6; // OFF - uint32_t code = 0xE0E09966; // ON + uint32_t code = 0xE0E019E6; // OFF +// uint32_t code = 0xE0E09966; // ON int unit=563; @@ -37,7 +37,7 @@ void setup() { rf.add(unit,45000); - rf.start(10); + rf.start(2); Serial.println("Done!"); From f07fa4e9c7f3f4a3d5ee13f25aefaf0dfa810266 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 17 Oct 2021 22:02:11 -0500 Subject: [PATCH 11/64] Testing TV functions --- examples/19-Television/19-Television.ino | 32 +++++++++++++----------- src/Span.h | 17 +++++-------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/examples/19-Television/19-Television.ino b/examples/19-Television/19-Television.ino index e8bd94d..f95828a 100644 --- a/examples/19-Television/19-Television.ino +++ b/examples/19-Television/19-Television.ino @@ -38,17 +38,22 @@ struct HKTV : Service::Television { SpanCharacteristic *netflixCurrentVisibility; HKTV() : Service::Television(){ - Serial.print("Init HKTV\n"); // initialization message + + Serial.printf("Init TV\n"); // initialization message active = new Characteristic::Active(); - activeIdentifier = new Characteristic::ActiveIdentifier(1); - configuredName = new Characteristic::ConfiguredName(); - remoteKey = new Characteristic::RemoteKey(); - sleepDiscoveryMode = new Characteristic::SleepDiscoveryMode(1); + activeIdentifier = new Characteristic::ActiveIdentifier(0); + new Characteristic::ConfiguredName("My TV"); +// new Characteristic::PowerModeSelection(); // write-only Characteristic that enables "View TV Settings" on controls screen + new Characteristic::PictureMode(1); + new Characteristic::ClosedCaptions(); + new Characteristic::Brightness(50); + new Characteristic::CurrentMediaState(1); + new Characteristic::RemoteKey(); speaker = new Service::TelevisionSpeaker(); mute = new Characteristic::Mute(); - speakerActive = new Characteristic::Active(); + speakerActive = new Characteristic::Active(1); volume = new Characteristic::Volume(); new Characteristic::VolumeControlType(3); new Characteristic::VolumeSelector(); @@ -57,7 +62,6 @@ struct HKTV : Service::Television { new Characteristic::ConfiguredName("HDMI 1"); new Characteristic::IsConfigured(1); // configured = 1, not configured = 0 new Characteristic::InputSourceType(HDMI); - new Characteristic::Name("HDMI 1"); netflixIdentifier = new Characteristic::Identifier(0); hdmi1CurrentVisibility = new Characteristic::CurrentVisibilityState(0); // 0 shown, 1 hidden @@ -65,17 +69,15 @@ struct HKTV : Service::Television { new Characteristic::ConfiguredName("HDMI 2"); new Characteristic::IsConfigured(1); // configured = 1, not configured = 0 new Characteristic::InputSourceType(HDMI); - new Characteristic::Name("HDMI 2"); - hdmi2Identifier = new Characteristic::Identifier(1); + hdmi2Identifier = new Characteristic::Identifier(10); hdmi2CurrentVisibility = new Characteristic::CurrentVisibilityState(0); // 0 shown, 1 hidden netflix = new Service::InputSource(); new Characteristic::ConfiguredName("NETFLIX"); - new Characteristic::IsConfigured(1); // configured = 1, not configured = 0 - new Characteristic::InputSourceType(APPLICATION); - new Characteristic::Name("Netflix"); - netflixIdentifier = new Characteristic::Identifier(2); +// new Characteristic::IsConfigured(0); // configured = 1, not configured = 0 + netflixIdentifier = new Characteristic::Identifier(4); netflixCurrentVisibility = new Characteristic::CurrentVisibilityState(0); // 0 shown, 1 hidden +// new Characteristic::TargetVisibilityState(0); // 0 shown, 1 hidden addLink(hdmi1); addLink(hdmi2); @@ -114,8 +116,8 @@ void setup() { new Characteristic::FirmwareRevision("0.1"); new Characteristic::Identify(); - new Service::HAPProtocolInformation(); - new Characteristic::Version("1.1.0"); + new Service::HAPProtocolInformation(); + new Characteristic::Version("1.1.0"); new HKTV(); } diff --git a/src/Span.h b/src/Span.h index 80aa80c..c492413 100644 --- a/src/Span.h +++ b/src/Span.h @@ -200,12 +200,11 @@ namespace Service { }}; struct InputSource : SpanService { InputSource() : SpanService{"D9","InputSource"}{ - REQ(ConfiguredName); - REQ(InputSourceType); + OPT(ConfiguredName); + OPT(InputSourceType); REQ(IsConfigured); - REQ(Name); REQ(CurrentVisibilityState); - + OPT(Name); OPT(Identifier); OPT(InputDeviceType); OPT(TargetVisibilityState); @@ -216,7 +215,6 @@ namespace Service { REQ(ProgramMode); REQ(InUse); OPT(RemainingDuration); - OPT(Name); OPT(StatusFault); }}; @@ -334,13 +332,11 @@ namespace Service { struct Television : SpanService { Television() : SpanService{"D8","Television"}{ REQ(Active); REQ(ActiveIdentifier); - REQ(ConfiguredName); - REQ(RemoteKey); - REQ(SleepDiscoveryMode); - + OPT(ConfiguredName); + OPT(RemoteKey); + OPT(SleepDiscoveryMode); OPT(Brightness); OPT(ClosedCaptions); - //OPT(DisplayOrder); OPT(CurrentMediaState); OPT(TargetMediaState); OPT(Name); @@ -350,7 +346,6 @@ namespace Service { struct TelevisionSpeaker : SpanService { TelevisionSpeaker() : SpanService{"113","TelevisionSpeaker"}{ REQ(Mute); - OPT(Active); OPT(Volume); OPT(VolumeControlType); From 26c15c9ca220bf017b3cd54c526ba46377a3898f Mon Sep 17 00:00:00 2001 From: Gregg Date: Mon, 18 Oct 2021 22:48:56 -0500 Subject: [PATCH 12/64] Fixed strncpy() warnings Changed strncpy() to strcpy() when copying new string values --- examples/19-Television/19-Television.ino | 19 +++++++++++++++---- src/HomeSpan.cpp | 4 +--- src/HomeSpan.h | 3 +-- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/examples/19-Television/19-Television.ino b/examples/19-Television/19-Television.ino index f95828a..37a9674 100644 --- a/examples/19-Television/19-Television.ino +++ b/examples/19-Television/19-Television.ino @@ -44,19 +44,21 @@ struct HKTV : Service::Television { active = new Characteristic::Active(); activeIdentifier = new Characteristic::ActiveIdentifier(0); new Characteristic::ConfiguredName("My TV"); -// new Characteristic::PowerModeSelection(); // write-only Characteristic that enables "View TV Settings" on controls screen + new Characteristic::PowerModeSelection(); // write-only Characteristic that enables "View TV Settings" on controls screen new Characteristic::PictureMode(1); new Characteristic::ClosedCaptions(); new Characteristic::Brightness(50); new Characteristic::CurrentMediaState(1); + new Characteristic::TargetMediaState(1); new Characteristic::RemoteKey(); + new Characteristic::SleepDiscoveryMode(1); speaker = new Service::TelevisionSpeaker(); mute = new Characteristic::Mute(); speakerActive = new Characteristic::Active(1); volume = new Characteristic::Volume(); - new Characteristic::VolumeControlType(3); - new Characteristic::VolumeSelector(); + new Characteristic::VolumeControlType(1); + new Characteristic::VolumeSelector(1); hdmi1 = new Service::InputSource(); new Characteristic::ConfiguredName("HDMI 1"); @@ -82,6 +84,7 @@ struct HKTV : Service::Television { addLink(hdmi1); addLink(hdmi2); addLink(netflix); + addLink(speaker); } boolean update(){ @@ -117,9 +120,17 @@ void setup() { new Characteristic::Identify(); new Service::HAPProtocolInformation(); - new Characteristic::Version("1.1.0"); + new Characteristic::Version("1.1.0"); new HKTV(); + + new Service::Speaker(); + new Characteristic::Mute(); +// new Characteristic::Active(1); + new Characteristic::Volume(); +// new Characteristic::VolumeControlType(1); +// new Characteristic::VolumeSelector(1); + } void loop() { diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index e743838..dd7ab1b 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -1729,9 +1729,7 @@ StatusCode SpanCharacteristic::loadUpdate(char *val, char *ev){ case STRING: newValue.STRING = (char *)realloc(newValue.STRING, strlen(val) + 1); - strncpy(newValue.STRING, val, strlen(val)); - newValue.STRING[strlen(val)] = '\0'; - + strcpy(newValue.STRING, val); break; default: diff --git a/src/HomeSpan.h b/src/HomeSpan.h index a4f5663..58936ed 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -309,8 +309,7 @@ struct SpanCharacteristic{ void uvSet(UVal &u, const char *val){ u.STRING = (char *)realloc(u.STRING, strlen(val) + 1); - strncpy(u.STRING, val, strlen(val)); - u.STRING[strlen(val)] = '\0'; + strcpy(u.STRING, val); } char *getString(){ From d4603df58d200699f10b7cec6bed56f9c8b47a2d Mon Sep 17 00:00:00 2001 From: Gregg Date: Wed, 20 Oct 2021 06:15:12 -0500 Subject: [PATCH 13/64] Creating Television Example --- Other Examples/Television/Television.ino | 184 +++++++++++++++++++++++ src/Span.h | 10 +- 2 files changed, 185 insertions(+), 9 deletions(-) create mode 100644 Other Examples/Television/Television.ino diff --git a/Other Examples/Television/Television.ino b/Other Examples/Television/Television.ino new file mode 100644 index 0000000..bbda858 --- /dev/null +++ b/Other Examples/Television/Television.ino @@ -0,0 +1,184 @@ +#include "HomeSpan.h" + +struct HKTV : Service::Television { + + SpanCharacteristic *active; + SpanCharacteristic *activeIdentifier; + SpanCharacteristic *configuredName; + SpanCharacteristic *remoteKey; + SpanCharacteristic *sleepDiscoveryMode; + + SpanService *speaker; + SpanCharacteristic *mute; + SpanCharacteristic *speakerActive; + SpanCharacteristic *volume; + + SpanService *hdmi1; + SpanCharacteristic *hdmi1Identifier; + SpanCharacteristic *hdmi1CurrentVisibility; + + SpanService *hdmi2; + SpanCharacteristic *hdmi2Identifier; + SpanCharacteristic *hdmi2CurrentVisibility; + + SpanService *netflix; + SpanCharacteristic *netflixIdentifier; + SpanCharacteristic *netflixCurrentVisibility; + + HKTV() : Service::Television(){ + + Serial.printf("Init TV\n"); // initialization message + + active = new Characteristic::Active(); + activeIdentifier = new Characteristic::ActiveIdentifier(0); + new Characteristic::ConfiguredName("My TV"); + new Characteristic::PowerModeSelection(); // write-only Characteristic that enables "View TV Settings" on controls screen + new Characteristic::PictureMode(1); + new Characteristic::ClosedCaptions(); + new Characteristic::Brightness(50); + new Characteristic::CurrentMediaState(1); + new Characteristic::TargetMediaState(1); + new Characteristic::RemoteKey(); + new Characteristic::SleepDiscoveryMode(1); + + hdmi1 = new Service::InputSource(); + new Characteristic::ConfiguredName("HDMI 1"); + new Characteristic::IsConfigured(1); // configured = 1, not configured = 0 + netflixIdentifier = new Characteristic::Identifier(0); + hdmi1CurrentVisibility = new Characteristic::CurrentVisibilityState(0); // 0 shown, 1 hidden + + hdmi2 = new Service::InputSource(); + new Characteristic::ConfiguredName("HDMI 2"); + new Characteristic::IsConfigured(1); // configured = 1, not configured = 0 + hdmi2Identifier = new Characteristic::Identifier(10); + hdmi2CurrentVisibility = new Characteristic::CurrentVisibilityState(0); // 0 shown, 1 hidden + + netflix = new Service::InputSource(); + new Characteristic::ConfiguredName("NETFLIX"); +// new Characteristic::IsConfigured(0); // configured = 1, not configured = 0 + netflixIdentifier = new Characteristic::Identifier(4); + netflixCurrentVisibility = new Characteristic::CurrentVisibilityState(0); // 0 shown, 1 hidden +// new Characteristic::TargetVisibilityState(0); // 0 shown, 1 hidden + + addLink(hdmi1); + addLink(hdmi2); + addLink(netflix); + addLink(speaker); + } + + boolean update(){ + if(active->updated()) { + if(active->getVal() != active->getNewVal()) { + Serial.printf("update(): active %d -> %d\n", active->getVal(), active->getNewVal()); + } + } + + if(activeIdentifier->updated()) { + Serial.printf("update(): activeIdentifier %d -> %d\n", activeIdentifier->getVal(), activeIdentifier->getNewVal()); + } + + return true; + } + + void loop() { + + } +}; + +void setup() { + Serial.begin(115200); + 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"); + + SpanService *hdmi1 = new Service::InputSource(); + new Characteristic::ConfiguredName("HDMI 1"); + new Characteristic::Identifier(1); + + SpanService *hdmi2 = new Service::InputSource(); + new Characteristic::ConfiguredName("HDMI 2"); + new Characteristic::Identifier(2); + new Characteristic::IsConfigured(0); + + SpanService *hdmi3 = new Service::InputSource(); + new Characteristic::ConfiguredName("HDMI 3"); + new Characteristic::Identifier(3); + new Characteristic::IsConfigured(1); + + SpanService *hdmi4 = new Service::InputSource(); + new Characteristic::ConfiguredName("HDMI 4"); + new Characteristic::Identifier(4); + new Characteristic::IsConfigured(1); + new Characteristic::TargetVisibilityState(0); + new Characteristic::CurrentVisibilityState(0); + + SpanService *hdmi5 = new Service::InputSource(); + new Characteristic::ConfiguredName("HDMI 5"); + new Characteristic::Identifier(5); + new Characteristic::IsConfigured(1); + new Characteristic::TargetVisibilityState(1); + new Characteristic::CurrentVisibilityState(1); + + SpanService *hdmi6 = new Service::InputSource(); + new Characteristic::ConfiguredName("HDMI 6"); + new Characteristic::Identifier(6); + new Characteristic::IsConfigured(0); + new Characteristic::TargetVisibilityState(0); + new Characteristic::CurrentVisibilityState(0); + + SpanService *hdmi7 = new Service::InputSource(); + new Characteristic::ConfiguredName("HDMI 7"); + new Characteristic::Identifier(0); + new Characteristic::IsConfigured(1); + new Characteristic::TargetVisibilityState(0); + new Characteristic::CurrentVisibilityState(0); + + SpanService *hdmi8 = new Service::InputSource(); + new Characteristic::ConfiguredName("HDMI 8"); + new Characteristic::Identifier(8); + new Characteristic::TargetVisibilityState(0); + new Characteristic::CurrentVisibilityState(0); + + SpanService *hdmi9 = new Service::InputSource(); + new Characteristic::ConfiguredName("HDMI 9"); + new Characteristic::Identifier(9); + new Characteristic::TargetVisibilityState(1); + new Characteristic::CurrentVisibilityState(1); + + SpanService *hdmi10 = new Service::InputSource(); + new Characteristic::ConfiguredName("HDMI 10"); + new Characteristic::IsConfigured(1); + new Characteristic::TargetVisibilityState(0); + new Characteristic::CurrentVisibilityState(0); + + (new Service::Television()) + ->addLink(hdmi1) + ->addLink(hdmi2) + ->addLink(hdmi3) + ->addLink(hdmi4) + ->addLink(hdmi5) + ->addLink(hdmi6) + ->addLink(hdmi7) + ->addLink(hdmi8) + ->addLink(hdmi9) + ->addLink(hdmi10) + ; + new Characteristic::Active(1); + new Characteristic::ConfiguredName("AdvancedTV"); + new Characteristic::ActiveIdentifier(3); + +} + +void loop() { + homeSpan.poll(); +} diff --git a/src/Span.h b/src/Span.h index c492413..8e323f1 100644 --- a/src/Span.h +++ b/src/Span.h @@ -331,8 +331,8 @@ namespace Service { struct Television : SpanService { Television() : SpanService{"D8","Television"}{ REQ(Active); - REQ(ActiveIdentifier); OPT(ConfiguredName); + OPT(ActiveIdentifier); OPT(RemoteKey); OPT(SleepDiscoveryMode); OPT(Brightness); @@ -344,14 +344,6 @@ namespace Service { OPT(PowerModeSelection); }}; - struct TelevisionSpeaker : SpanService { TelevisionSpeaker() : SpanService{"113","TelevisionSpeaker"}{ - REQ(Mute); - OPT(Active); - OPT(Volume); - OPT(VolumeControlType); - OPT(VolumeSelector); - }}; - struct TemperatureSensor : SpanService { TemperatureSensor() : SpanService{"8A","TemperatureSensor"}{ REQ(CurrentTemperature); OPT(Name); From 7c32f7a364519fe460b6d531105ecee9ff4843aa Mon Sep 17 00:00:00 2001 From: Gregg Date: Thu, 21 Oct 2021 20:32:25 -0500 Subject: [PATCH 14/64] Fix bug in PushButton() Ensure a pin value of -1 is retained so that any further calls to other methods are disabled if the pin is not defined. --- src/Utils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Utils.cpp b/src/Utils.cpp index ea34718..15de921 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -97,12 +97,12 @@ PushButton::PushButton(int pin){ void PushButton::init(int pin){ + this->pin=pin; if(pin<0) return; - + status=0; doubleCheck=false; - this->pin=pin; pinMode(pin, INPUT_PULLUP); } From 8a24819955c71d5f709541c19658f255030a559f Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 22 Oct 2021 06:35:33 -0500 Subject: [PATCH 15/64] Added Characteristic::ConfiguredNameStatic() This "custom" Characteristic is identical to ConfiguredName() but only allows paired-read and notifications. By excluding paired-write it prevents the user from editing the name of a TV input source from the Home App. This is useful when some sources are fixed, such as "netflix" instead of generic, such as "hdmi 1". --- Other Examples/Television/Television.ino | 11 ++++++++++- src/Characteristics.h | 2 +- src/Span.h | 6 ++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Other Examples/Television/Television.ino b/Other Examples/Television/Television.ino index bbda858..6beaa53 100644 --- a/Other Examples/Television/Television.ino +++ b/Other Examples/Television/Television.ino @@ -161,6 +161,13 @@ void setup() { new Characteristic::TargetVisibilityState(0); new Characteristic::CurrentVisibilityState(0); + SpanService *hdmi11 = new Service::InputSource(); + new Characteristic::ConfiguredNameStatic("HDMI 11"); + new Characteristic::Identifier(11); + new Characteristic::IsConfigured(1); + new Characteristic::TargetVisibilityState(0); + new Characteristic::CurrentVisibilityState(0); + (new Service::Television()) ->addLink(hdmi1) ->addLink(hdmi2) @@ -172,10 +179,12 @@ void setup() { ->addLink(hdmi8) ->addLink(hdmi9) ->addLink(hdmi10) + ->addLink(hdmi11) ; new Characteristic::Active(1); new Characteristic::ConfiguredName("AdvancedTV"); - new Characteristic::ActiveIdentifier(3); + new Characteristic::ActiveIdentifier(3); + new Characteristic::RemoteKey(); } diff --git a/src/Characteristics.h b/src/Characteristics.h index 930b110..cf2b44d 100644 --- a/src/Characteristics.h +++ b/src/Characteristics.h @@ -83,6 +83,7 @@ struct HapCharacteristics { HAPCHAR( CoolingThresholdTemperature, D, PR+PW+EV, FLOAT, false ); HAPCHAR( ColorTemperature, CE, PR+PW+EV, UINT32, false ); HAPCHAR( ConfiguredName, E3, PW+PR+EV, STRING, false ); + HAPCHAR( ConfiguredNameStatic, E3, PR+EV, STRING, false ); HAPCHAR( ContactSensorState, 6A, PR+EV, UINT8, true ); HAPCHAR( CurrentAmbientLightLevel, 6B, PR+EV, FLOAT, false ); HAPCHAR( CurrentHorizontalTiltAngle, 6C, PR+EV, INT, false ); @@ -100,7 +101,6 @@ 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, PW+PR+EV, TLV8, false ); HAPCHAR( FilterLifeLevel, AB, PR+EV, FLOAT, false ); HAPCHAR( FilterChangeIndication, AC, PR+EV, UINT8, true ); HAPCHAR( FirmwareRevision, 52, PR, STRING, true ); diff --git a/src/Span.h b/src/Span.h index 8e323f1..59c3326 100644 --- a/src/Span.h +++ b/src/Span.h @@ -332,8 +332,11 @@ namespace Service { struct Television : SpanService { Television() : SpanService{"D8","Television"}{ REQ(Active); OPT(ConfiguredName); + OPT(ConfiguredNameStatic); OPT(ActiveIdentifier); OPT(RemoteKey); + OPT(PowerModeSelection); + OPT(SleepDiscoveryMode); OPT(Brightness); OPT(ClosedCaptions); @@ -341,7 +344,6 @@ namespace Service { OPT(TargetMediaState); OPT(Name); OPT(PictureMode); - OPT(PowerModeSelection); }}; struct TemperatureSensor : SpanService { TemperatureSensor() : SpanService{"8A","TemperatureSensor"}{ @@ -430,6 +432,7 @@ namespace Characteristic { CREATE_CHAR(uint32_t,ColorTemperature,200,140,500); CREATE_CHAR(uint8_t,ContactSensorState,1,0,1); CREATE_CHAR(const char *,ConfiguredName,"unnamed",0,1); + CREATE_CHAR(const char *,ConfiguredNameStatic,"unnamed",0,1); CREATE_CHAR(double,CurrentAmbientLightLevel,1,0.0001,100000); CREATE_CHAR(int,CurrentHorizontalTiltAngle,0,-90,90); CREATE_CHAR(uint8_t,CurrentAirPurifierState,1,0,2); @@ -446,7 +449,6 @@ namespace Characteristic { CREATE_CHAR(double,CurrentRelativeHumidity,0,0,100); CREATE_CHAR(double,CurrentTemperature,0,0,100); CREATE_CHAR(int,CurrentTiltAngle,0,-90,90); -// CREATE_CHAR(tlv8,DisplayOrder,0,0,1); CREATE_CHAR(double,FilterLifeLevel,0,0,100); CREATE_CHAR(uint8_t,FilterChangeIndication,0,0,1); CREATE_CHAR(const char *,FirmwareRevision,"1.0.0",0,1); From dcbfbc3e15ea190b6b58e67e02a2647ff3ecc8f0 Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 22 Oct 2021 09:17:26 -0500 Subject: [PATCH 16/64] Correct setVal() logic to properly check for EV permissions, not PW permissions --- Other Examples/Television/Television.ino | 29 +++++++++++++++++++++++- src/HomeSpan.h | 4 ++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/Other Examples/Television/Television.ino b/Other Examples/Television/Television.ino index 6beaa53..9306dbc 100644 --- a/Other Examples/Television/Television.ino +++ b/Other Examples/Television/Television.ino @@ -85,6 +85,30 @@ struct HKTV : Service::Television { } }; +struct TV_Source : Service::InputSource{ + + SpanCharacteristic *currentState = new Characteristic::CurrentVisibilityState(0); + SpanCharacteristic *targetState = new Characteristic::TargetVisibilityState(0); + + TV_Source() : Service::InputSource(){ + new Characteristic::ConfiguredName("HDMI 12"); + new Characteristic::Identifier(12); + new Characteristic::IsConfigured(1); + } + + boolean update() override{ + + if(targetState->updated()){ + Serial.printf("New Target State = %d\n",targetState->getNewVal()); + currentState->setVal(targetState->getNewVal()); + } + + return(true); + } + +}; + + void setup() { Serial.begin(115200); homeSpan.begin(Category::Television,"HomeSpan Television"); @@ -162,11 +186,13 @@ void setup() { new Characteristic::CurrentVisibilityState(0); SpanService *hdmi11 = new Service::InputSource(); - new Characteristic::ConfiguredNameStatic("HDMI 11"); + new Characteristic::ConfiguredName("HDMI 11"); new Characteristic::Identifier(11); new Characteristic::IsConfigured(1); new Characteristic::TargetVisibilityState(0); new Characteristic::CurrentVisibilityState(0); + + SpanService *hdmi12 = new TV_Source(); (new Service::Television()) ->addLink(hdmi1) @@ -180,6 +206,7 @@ void setup() { ->addLink(hdmi9) ->addLink(hdmi10) ->addLink(hdmi11) + ->addLink(hdmi12) ; new Characteristic::Active(1); new Characteristic::ConfiguredName("AdvancedTV"); diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 58936ed..cde9021 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -486,8 +486,8 @@ struct SpanCharacteristic{ template void setVal(T val){ - if(format==FORMAT::STRING && perms & PW == 0){ - Serial.printf("\n*** WARNING: Attempt to update Characteristic::%s(\"%s\") with setVal() ignored. No WRITE permission on this characteristic\n\n",hapName,value.STRING); + if((perms & EV) == 0){ + Serial.printf("\n*** WARNING: Attempt to update Characteristic::%s with setVal() ignored. No NOTIFICATION permission on this characteristic\n\n",hapName); return; } From b90fc5aad5ae2ec684a1306c89632db656621578 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 23 Oct 2021 13:16:49 -0500 Subject: [PATCH 17/64] Updated logic re STRING Characteristics Added setString() as analog to setVal(). This complements getString and getNewString() which are analogs to getVal() and getNewVal(). --- Other Examples/Television/Television.ino | 19 +++- src/HomeSpan.cpp | 12 ++- src/HomeSpan.h | 126 ++++++++++++++++------- 3 files changed, 108 insertions(+), 49 deletions(-) diff --git a/Other Examples/Television/Television.ino b/Other Examples/Television/Television.ino index 9306dbc..4079c11 100644 --- a/Other Examples/Television/Television.ino +++ b/Other Examples/Television/Television.ino @@ -87,20 +87,31 @@ struct HKTV : Service::Television { struct TV_Source : Service::InputSource{ - SpanCharacteristic *currentState = new Characteristic::CurrentVisibilityState(0); - SpanCharacteristic *targetState = new Characteristic::TargetVisibilityState(0); + SpanCharacteristic *currentState = new Characteristic::CurrentVisibilityState(0,true); + SpanCharacteristic *targetState = new Characteristic::TargetVisibilityState(0,true); + SpanCharacteristic *configName = new Characteristic::ConfiguredName("HDMI 12",true); TV_Source() : Service::InputSource(){ - new Characteristic::ConfiguredName("HDMI 12"); +// new Characteristic::ConfiguredName("HDMI 12"); new Characteristic::Identifier(12); new Characteristic::IsConfigured(1); } boolean update() override{ + char c[50]; + sprintf(c,"HERE I AM "); + if(targetState->updated()){ - Serial.printf("New Target State = %d\n",targetState->getNewVal()); + Serial.printf("Old Target State = %d New Target State = %d\n",targetState->getVal(),targetState->getNewVal()); currentState->setVal(targetState->getNewVal()); + Serial.printf("Name: %s\n",configName->getString()); + configName->setString(c); + Serial.printf("Name: %s\n",configName->getString()); + } + + if(configName->updated()){ + Serial.printf("CURRENT NAME: %s NEW NAME: %s\n",configName->getString(),configName->getNewString()); } return(true); diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index dd7ab1b..b6a77dc 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -1206,14 +1206,17 @@ int Span::updateCharacteristics(char *buf, SpanBuf *pObj){ LOG1(" iid="); LOG1(pObj[j].characteristic->iid); if(status==StatusCode::OK){ // if status is okay - pObj[j].characteristic->value=pObj[j].characteristic->newValue; // update characteristic value with new value + pObj[j].characteristic->uvSet(pObj[j].characteristic->value,pObj[j].characteristic->newValue); // update characteristic value with new value if(pObj[j].characteristic->nvsKey){ // if storage key found - nvs_set_blob(charNVS,pObj[j].characteristic->nvsKey,&(pObj[j].characteristic->value),sizeof(pObj[j].characteristic->value)); // store data + if(pObj[j].characteristic->format != FORMAT::STRING) + nvs_set_blob(charNVS,pObj[j].characteristic->nvsKey,&(pObj[j].characteristic->value),sizeof(pObj[j].characteristic->value)); // store data + else + nvs_set_str(charNVS,pObj[j].characteristic->nvsKey,pObj[j].characteristic->value.STRING); // store data nvs_commit(charNVS); } LOG1(" (okay)\n"); } else { // if status not okay - pObj[j].characteristic->newValue=pObj[j].characteristic->value; // replace characteristic new value with original value + pObj[j].characteristic->uvSet(pObj[j].characteristic->newValue,pObj[j].characteristic->value); // replace characteristic new value with original value LOG1(" (failed)\n"); } pObj[j].characteristic->isUpdated=false; // reset isUpdated flag for characteristic @@ -1728,8 +1731,7 @@ StatusCode SpanCharacteristic::loadUpdate(char *val, char *ev){ break; case STRING: - newValue.STRING = (char *)realloc(newValue.STRING, strlen(val) + 1); - strcpy(newValue.STRING, val); + uvSet(newValue,(const char *)val); break; default: diff --git a/src/HomeSpan.h b/src/HomeSpan.h index cde9021..61af4ac 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -307,25 +307,20 @@ struct SpanCharacteristic{ return(String()); // included to prevent compiler warnings } + void uvSet(UVal &dest, UVal &src){ + if(format==FORMAT::STRING) + uvSet(dest,(const char *)src.STRING); + else + dest=src; + } + void uvSet(UVal &u, const char *val){ + Serial.printf("ADDRESS %d ",u.UINT32); u.STRING = (char *)realloc(u.STRING, strlen(val) + 1); + Serial.printf("-> %d \n",u.UINT32); strcpy(u.STRING, val); } - char *getString(){ - if(format == FORMAT::STRING) - return value.STRING; - - return NULL; - } - - char *getNewString(){ - if(format == FORMAT::STRING) - return newValue.STRING; - - return NULL; - } - template void uvSet(UVal &u, T val){ switch(format){ case FORMAT::BOOL: @@ -369,9 +364,6 @@ struct SpanCharacteristic{ return((T) u.UINT64); case FORMAT::FLOAT: return((T) u.FLOAT); - case FORMAT::STRING: - Serial.print("\n*** WARNING: Can't use getVal() or getNewVal() with string Characteristics.\n\n"); - return(0); } return(0); // included to prevent compiler warnings } @@ -409,9 +401,41 @@ struct SpanCharacteristic{ template void init(T val, boolean nvsStore, A min=0, B max=1){ int nvsFlag=0; - uvSet(value,val); - uvSet(newValue,val); + + if(nvsStore){ + nvsKey=(char *)malloc(16); + uint16_t t; + sscanf(type,"%x",&t); + sprintf(nvsKey,"%04X%08X%03X",t,aid,iid&0xFFF); + size_t len; + + if(format != FORMAT::STRING){ + if(!nvs_get_blob(homeSpan.charNVS,nvsKey,NULL,&len)){ + nvs_get_blob(homeSpan.charNVS,nvsKey,&value,&len); + nvsFlag=2; + } + else { + nvs_set_blob(homeSpan.charNVS,nvsKey,&value,sizeof(UVal)); // store data + nvs_commit(homeSpan.charNVS); // commit to NVS + nvsFlag=1; + } + } else { + if(!nvs_get_str(homeSpan.charNVS,nvsKey,NULL,&len)){ + char c[len]; + nvs_get_str(homeSpan.charNVS,nvsKey,c,&len); + uvSet(value,(const char *)c); + nvsFlag=2; + } + else { + nvs_set_str(homeSpan.charNVS,nvsKey,value.STRING); // store string data + nvs_commit(homeSpan.charNVS); // commit to NVS + nvsFlag=1; + } + } + } + + uvSet(newValue,value); if(format != FORMAT::STRING) { uvSet(minValue,min); @@ -419,25 +443,6 @@ struct SpanCharacteristic{ uvSet(stepValue,0); } - if(nvsStore){ - nvsKey=(char *)malloc(16); - uint16_t t; - sscanf(type,"%x",&t); - sprintf(nvsKey,"%04X%08X%03X",t,aid,iid&0xFFF); - size_t len; - - if(!nvs_get_blob(homeSpan.charNVS,nvsKey,NULL,&len)){ - nvs_get_blob(homeSpan.charNVS,nvsKey,&value,&len); - newValue=value; - nvsFlag=2; - } - else { - nvs_set_blob(homeSpan.charNVS,nvsKey,&value,sizeof(UVal)); // store data - nvs_commit(homeSpan.charNVS); // commit to NVS - nvsFlag=1; - } - } - homeSpan.configLog+="(" + uvPrint(value) + ")" + ": IID=" + String(iid) + ", UUID=0x" + String(type); if(format!=FORMAT::STRING && format!=FORMAT::BOOL) homeSpan.configLog+= " Range=[" + String(uvPrint(minValue)) + "," + String(uvPrint(maxValue)) + "]"; @@ -476,6 +481,7 @@ struct SpanCharacteristic{ } // init() + template T getVal(){ return(uvGet(value)); } @@ -484,6 +490,46 @@ struct SpanCharacteristic{ return(uvGet(newValue)); } + char *getString(){ + if(format == FORMAT::STRING) + return value.STRING; + + return NULL; + } + + char *getNewString(){ + if(format == FORMAT::STRING) + return newValue.STRING; + + return NULL; + } + + void setString(const char *val){ + + if((perms & EV) == 0){ + Serial.printf("\n*** WARNING: Attempt to update Characteristic::%s with setVal() ignored. No NOTIFICATION permission on this characteristic\n\n",hapName); + return; + } + + uvSet(value,val); + uvSet(newValue,value); + + updateTime=homeSpan.snapTime; + + SpanBuf sb; // create SpanBuf object + sb.characteristic=this; // set characteristic + sb.status=StatusCode::OK; // set status + char dummy[]=""; + sb.val=dummy; // set dummy "val" so that sprintfNotify knows to consider this "update" + homeSpan.Notifications.push_back(sb); // store SpanBuf in Notifications vector + + if(nvsKey){ + nvs_set_str(homeSpan.charNVS,nvsKey,value.STRING); // store data + nvs_commit(homeSpan.charNVS); + } + + } // setString() + template void setVal(T val){ if((perms & EV) == 0){ @@ -491,13 +537,13 @@ struct SpanCharacteristic{ return; } - if(format!=FORMAT::STRING && ( val < uvGet(minValue) || val > uvGet(maxValue))){ + if(val < uvGet(minValue) || val > uvGet(maxValue)){ Serial.printf("\n*** WARNING: Attempt to update Characteristic::%s with setVal(%llg) is out of range [%llg,%llg]. This may cause device to become non-reponsive!\n\n", hapName,(double)val,uvGet(minValue),uvGet(maxValue)); } uvSet(value,val); - uvSet(newValue,val); + uvSet(newValue,value); updateTime=homeSpan.snapTime; From 4eb1ebf80606ac9f0da61d3dee777d1f0be35a78 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 23 Oct 2021 13:21:21 -0500 Subject: [PATCH 18/64] Update HomeSpan.h --- src/HomeSpan.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 61af4ac..865dd22 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -315,9 +315,7 @@ struct SpanCharacteristic{ } void uvSet(UVal &u, const char *val){ - Serial.printf("ADDRESS %d ",u.UINT32); u.STRING = (char *)realloc(u.STRING, strlen(val) + 1); - Serial.printf("-> %d \n",u.UINT32); strcpy(u.STRING, val); } From 37e0486cf76c56ba98b8b0be699fe58b203e84e3 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 23 Oct 2021 22:58:16 -0500 Subject: [PATCH 19/64] Removed all unusable TV Characteristics from TV and Input Source Services But retained the actual Characteristics in the Characteristics lists in the event that they may be activated in the Home App in the future. --- Other Examples/Television/Television.ino | 70 +++++++++--------------- src/Span.h | 17 +----- 2 files changed, 28 insertions(+), 59 deletions(-) diff --git a/Other Examples/Television/Television.ino b/Other Examples/Television/Television.ino index 4079c11..2349019 100644 --- a/Other Examples/Television/Television.ino +++ b/Other Examples/Television/Television.ino @@ -92,7 +92,6 @@ struct TV_Source : Service::InputSource{ SpanCharacteristic *configName = new Characteristic::ConfiguredName("HDMI 12",true); TV_Source() : Service::InputSource(){ -// new Characteristic::ConfiguredName("HDMI 12"); new Characteristic::Identifier(12); new Characteristic::IsConfigured(1); } @@ -134,76 +133,59 @@ void setup() { new Characteristic::Identify(); new Service::HAPProtocolInformation(); - new Characteristic::Version("1.1.0"); + new Characteristic::Version("1.1.0"); - SpanService *hdmi1 = new Service::InputSource(); + SpanService *hdmi1 = new Service::InputSource(); // Source included in Selection List, but excluded from Settings Screen new Characteristic::ConfiguredName("HDMI 1"); new Characteristic::Identifier(1); SpanService *hdmi2 = new Service::InputSource(); new Characteristic::ConfiguredName("HDMI 2"); new Characteristic::Identifier(2); - new Characteristic::IsConfigured(0); + new Characteristic::IsConfigured(0); // Source excluded from both the Selection List and the Settings Screen SpanService *hdmi3 = new Service::InputSource(); new Characteristic::ConfiguredName("HDMI 3"); new Characteristic::Identifier(3); - new Characteristic::IsConfigured(1); + new Characteristic::IsConfigured(1); // Source included in both the Selection List and the Settings Screen SpanService *hdmi4 = new Service::InputSource(); new Characteristic::ConfiguredName("HDMI 4"); new Characteristic::Identifier(4); - new Characteristic::IsConfigured(1); - new Characteristic::TargetVisibilityState(0); - new Characteristic::CurrentVisibilityState(0); + new Characteristic::IsConfigured(1); // Source included in the Settings Screen... + new Characteristic::CurrentVisibilityState(1); // ...but excluded from the Selection List SpanService *hdmi5 = new Service::InputSource(); new Characteristic::ConfiguredName("HDMI 5"); new Characteristic::Identifier(5); - new Characteristic::IsConfigured(1); - new Characteristic::TargetVisibilityState(1); - new Characteristic::CurrentVisibilityState(1); + new Characteristic::IsConfigured(1); // Source included in the Settings Screen... + new Characteristic::CurrentVisibilityState(0); // ...and included in the Selection List SpanService *hdmi6 = new Service::InputSource(); new Characteristic::ConfiguredName("HDMI 6"); new Characteristic::Identifier(6); - new Characteristic::IsConfigured(0); - new Characteristic::TargetVisibilityState(0); - new Characteristic::CurrentVisibilityState(0); + new Characteristic::IsConfigured(0); // Source excluded from both the Selection List and the Settings Screen + new Characteristic::CurrentVisibilityState(0); // If IsConfigured(0) is specified, CurrentVisibilityState() has no effect SpanService *hdmi7 = new Service::InputSource(); new Characteristic::ConfiguredName("HDMI 7"); - new Characteristic::Identifier(0); - new Characteristic::IsConfigured(1); - new Characteristic::TargetVisibilityState(0); - new Characteristic::CurrentVisibilityState(0); + new Characteristic::Identifier(7); + new Characteristic::IsConfigured(1); // Source included in the Settings Screen... + new Characteristic::CurrentVisibilityState(0); // ...and included in the Selection List... + new Characteristic::TargetVisibilityState(0); // ...and a "checked" checkbox is provided on the Settings Screen that can be used to toggle CurrentVisibilityState() SpanService *hdmi8 = new Service::InputSource(); new Characteristic::ConfiguredName("HDMI 8"); new Characteristic::Identifier(8); - new Characteristic::TargetVisibilityState(0); - new Characteristic::CurrentVisibilityState(0); - + new Characteristic::IsConfigured(1); // Source included in the Settings Screen... + new Characteristic::CurrentVisibilityState(1); // ...but excluded from the Selection List... + new Characteristic::TargetVisibilityState(1); // ...and an "un-checked" checkbox is provided on the Settings Screen that can be used to toggle CurrentVisibilityState() + SpanService *hdmi9 = new Service::InputSource(); new Characteristic::ConfiguredName("HDMI 9"); - new Characteristic::Identifier(9); - new Characteristic::TargetVisibilityState(1); - new Characteristic::CurrentVisibilityState(1); - - SpanService *hdmi10 = new Service::InputSource(); - new Characteristic::ConfiguredName("HDMI 10"); - new Characteristic::IsConfigured(1); + new Characteristic::IsConfigured(1); // Source included in the Settings Screen... + new Characteristic::CurrentVisibilityState(0); // ...but without an Identifier() set, the Source is excluded from the Selection List regardless of CurrentVisibilityState(0) new Characteristic::TargetVisibilityState(0); - new Characteristic::CurrentVisibilityState(0); - - SpanService *hdmi11 = new Service::InputSource(); - new Characteristic::ConfiguredName("HDMI 11"); - new Characteristic::Identifier(11); - new Characteristic::IsConfigured(1); - new Characteristic::TargetVisibilityState(0); - new Characteristic::CurrentVisibilityState(0); - - SpanService *hdmi12 = new TV_Source(); (new Service::Television()) ->addLink(hdmi1) @@ -215,14 +197,12 @@ void setup() { ->addLink(hdmi7) ->addLink(hdmi8) ->addLink(hdmi9) - ->addLink(hdmi10) - ->addLink(hdmi11) - ->addLink(hdmi12) ; - new Characteristic::Active(1); - new Characteristic::ConfiguredName("AdvancedTV"); - new Characteristic::ActiveIdentifier(3); - new Characteristic::RemoteKey(); + new Characteristic::Active(0); // TV On/Off (set to Off at start-up) + new Characteristic::ConfiguredName("Test TV"); // Name of TV + new Characteristic::ActiveIdentifier(3); // Sets HDMI 3 on start-up + new Characteristic::RemoteKey(); // Used to receive button presses from the Remote Control widget + new Characteristic::PowerModeSelection(); // Adds "" option to Selection Screen } diff --git a/src/Span.h b/src/Span.h index 59c3326..29bdac9 100644 --- a/src/Span.h +++ b/src/Span.h @@ -201,12 +201,9 @@ namespace Service { struct InputSource : SpanService { InputSource() : SpanService{"D9","InputSource"}{ OPT(ConfiguredName); - OPT(InputSourceType); - REQ(IsConfigured); - REQ(CurrentVisibilityState); - OPT(Name); + OPT(IsConfigured); OPT(Identifier); - OPT(InputDeviceType); + OPT(CurrentVisibilityState); OPT(TargetVisibilityState); }}; @@ -335,15 +332,7 @@ namespace Service { OPT(ConfiguredNameStatic); OPT(ActiveIdentifier); OPT(RemoteKey); - OPT(PowerModeSelection); - - OPT(SleepDiscoveryMode); - OPT(Brightness); - OPT(ClosedCaptions); - OPT(CurrentMediaState); - OPT(TargetMediaState); - OPT(Name); - OPT(PictureMode); + OPT(PowerModeSelection); }}; struct TemperatureSensor : SpanService { TemperatureSensor() : SpanService{"8A","TemperatureSensor"}{ From 2d7ae5846d158a4b3fe0521c4e81d540b3f58823 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 24 Oct 2021 07:47:32 -0500 Subject: [PATCH 20/64] Finalized Television Example Will appear in "Other Examples" Category, NOT as a tutorial --- Other Examples/Television/Television.ino | 191 +++++++++-------------- examples/19-Television/19-Television.ino | 138 ---------------- 2 files changed, 71 insertions(+), 258 deletions(-) delete mode 100644 examples/19-Television/19-Television.ino diff --git a/Other Examples/Television/Television.ino b/Other Examples/Television/Television.ino index 2349019..ba48415 100644 --- a/Other Examples/Television/Television.ino +++ b/Other Examples/Television/Television.ino @@ -1,126 +1,66 @@ +/********************************************************************************* + * MIT License + * + * Copyright (c) 2021 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 Television Service Example + +// Covers all Characteristics of the Television Service that appear to +// be supported in the iOS 15 version of the Home App. Note these Services +// are not documented by Apple and are not officially part HAP-R2. +// +// For Service::Television(): +// +// * Characteristic::Active() +// * Characteristic::ConfiguredName() +// * Characteristic::ActiveIdentifier() +// * Characteristic::RemoteKey() +// * Characteristic::PowerModeSelection() +// +// For Service::InputSource(): +// +// * Characteristic::ConfiguredName() +// * Characteristic::ConfiguredNameStatic() // a HomeSpan-specific variation of ConfiguredName() +// * Characteristic::Identifier() +// * Characteristic::IsConfigured() +// * Characteristic::CurrentVisibilityState() +// * Characteristic::TargetVisibilityState() + +// NOTE: This example is only designed to demonstrate how Television Services and Characteristics +// appear in the Home App, and what they each control. To keep things simple, actions for the +// Characteristics have NOT been implemented in the code below. For example, the code below does not include +// any logic to update CurrentVisibilityState when the TargetVisibilityState checkboxes are clicked. + #include "HomeSpan.h" -struct HKTV : Service::Television { - - SpanCharacteristic *active; - SpanCharacteristic *activeIdentifier; - SpanCharacteristic *configuredName; - SpanCharacteristic *remoteKey; - SpanCharacteristic *sleepDiscoveryMode; - - SpanService *speaker; - SpanCharacteristic *mute; - SpanCharacteristic *speakerActive; - SpanCharacteristic *volume; - - SpanService *hdmi1; - SpanCharacteristic *hdmi1Identifier; - SpanCharacteristic *hdmi1CurrentVisibility; - - SpanService *hdmi2; - SpanCharacteristic *hdmi2Identifier; - SpanCharacteristic *hdmi2CurrentVisibility; - - SpanService *netflix; - SpanCharacteristic *netflixIdentifier; - SpanCharacteristic *netflixCurrentVisibility; - - HKTV() : Service::Television(){ - - Serial.printf("Init TV\n"); // initialization message - - active = new Characteristic::Active(); - activeIdentifier = new Characteristic::ActiveIdentifier(0); - new Characteristic::ConfiguredName("My TV"); - new Characteristic::PowerModeSelection(); // write-only Characteristic that enables "View TV Settings" on controls screen - new Characteristic::PictureMode(1); - new Characteristic::ClosedCaptions(); - new Characteristic::Brightness(50); - new Characteristic::CurrentMediaState(1); - new Characteristic::TargetMediaState(1); - new Characteristic::RemoteKey(); - new Characteristic::SleepDiscoveryMode(1); - - hdmi1 = new Service::InputSource(); - new Characteristic::ConfiguredName("HDMI 1"); - new Characteristic::IsConfigured(1); // configured = 1, not configured = 0 - netflixIdentifier = new Characteristic::Identifier(0); - hdmi1CurrentVisibility = new Characteristic::CurrentVisibilityState(0); // 0 shown, 1 hidden - - hdmi2 = new Service::InputSource(); - new Characteristic::ConfiguredName("HDMI 2"); - new Characteristic::IsConfigured(1); // configured = 1, not configured = 0 - hdmi2Identifier = new Characteristic::Identifier(10); - hdmi2CurrentVisibility = new Characteristic::CurrentVisibilityState(0); // 0 shown, 1 hidden - - netflix = new Service::InputSource(); - new Characteristic::ConfiguredName("NETFLIX"); -// new Characteristic::IsConfigured(0); // configured = 1, not configured = 0 - netflixIdentifier = new Characteristic::Identifier(4); - netflixCurrentVisibility = new Characteristic::CurrentVisibilityState(0); // 0 shown, 1 hidden -// new Characteristic::TargetVisibilityState(0); // 0 shown, 1 hidden - - addLink(hdmi1); - addLink(hdmi2); - addLink(netflix); - addLink(speaker); - } - - boolean update(){ - if(active->updated()) { - if(active->getVal() != active->getNewVal()) { - Serial.printf("update(): active %d -> %d\n", active->getVal(), active->getNewVal()); - } - } - - if(activeIdentifier->updated()) { - Serial.printf("update(): activeIdentifier %d -> %d\n", activeIdentifier->getVal(), activeIdentifier->getNewVal()); - } - - return true; - } - - void loop() { - - } -}; - -struct TV_Source : Service::InputSource{ - - SpanCharacteristic *currentState = new Characteristic::CurrentVisibilityState(0,true); - SpanCharacteristic *targetState = new Characteristic::TargetVisibilityState(0,true); - SpanCharacteristic *configName = new Characteristic::ConfiguredName("HDMI 12",true); - - TV_Source() : Service::InputSource(){ - new Characteristic::Identifier(12); - new Characteristic::IsConfigured(1); - } - - boolean update() override{ - - char c[50]; - sprintf(c,"HERE I AM "); - - if(targetState->updated()){ - Serial.printf("Old Target State = %d New Target State = %d\n",targetState->getVal(),targetState->getNewVal()); - currentState->setVal(targetState->getNewVal()); - Serial.printf("Name: %s\n",configName->getString()); - configName->setString(c); - Serial.printf("Name: %s\n",configName->getString()); - } - - if(configName->updated()){ - Serial.printf("CURRENT NAME: %s NEW NAME: %s\n",configName->getString(),configName->getNewString()); - } - - return(true); - } - -}; - - void setup() { + Serial.begin(115200); + + homeSpan.setLogLevel(2); // allows you to see HAP transmissions that occur as various Television buttons are pressed in Home App + homeSpan.begin(Category::Television,"HomeSpan Television"); new SpanAccessory(); @@ -134,6 +74,9 @@ void setup() { new Service::HAPProtocolInformation(); new Characteristic::Version("1.1.0"); + + // 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 SpanService *hdmi1 = new Service::InputSource(); // Source included in Selection List, but excluded from Settings Screen new Characteristic::ConfiguredName("HDMI 1"); @@ -187,7 +130,14 @@ void setup() { new Characteristic::CurrentVisibilityState(0); // ...but without an Identifier() set, the Source is excluded from the Selection List regardless of CurrentVisibilityState(0) new Characteristic::TargetVisibilityState(0); - (new Service::Television()) + SpanService *hdmi10 = new Service::InputSource(); + new Characteristic::ConfiguredNameStatic("HDMI 10"); // Source Name is static and cannot be edited in Settings Screen + new Characteristic::Identifier(10); + new Characteristic::IsConfigured(1); // Source included in the Settings Screen... + new Characteristic::CurrentVisibilityState(0); // ...and included in the Selection List... + new Characteristic::TargetVisibilityState(0); // ...and a "checked" checkbox is provided on the Settings Screen that can be used to toggle CurrentVisibilityState() + + (new Service::Television()) // Define a Television Service. Must link in InputSources! ->addLink(hdmi1) ->addLink(hdmi2) ->addLink(hdmi3) @@ -197,6 +147,7 @@ void setup() { ->addLink(hdmi7) ->addLink(hdmi8) ->addLink(hdmi9) + ->addLink(hdmi10) ; new Characteristic::Active(0); // TV On/Off (set to Off at start-up) new Characteristic::ConfiguredName("Test TV"); // Name of TV diff --git a/examples/19-Television/19-Television.ino b/examples/19-Television/19-Television.ino deleted file mode 100644 index 37a9674..0000000 --- a/examples/19-Television/19-Television.ino +++ /dev/null @@ -1,138 +0,0 @@ -#include "HomeSpan.h" - -const int OTHER = 0; -const int HOME_SCREEN = 1; -const int TUNER = 2; -const int HDMI = 3; -const int COMPOSITE_VIDEO = 4; -const int S_VIDEO = 5; -const int COMPONENT_VIDEO = 6; -const int DVI = 7; -const int AIRPLAY = 8; -const int USB = 9; -const int APPLICATION = 10; - -struct HKTV : Service::Television { - - SpanCharacteristic *active; - SpanCharacteristic *activeIdentifier; - SpanCharacteristic *configuredName; - SpanCharacteristic *remoteKey; - SpanCharacteristic *sleepDiscoveryMode; - - SpanService *speaker; - SpanCharacteristic *mute; - SpanCharacteristic *speakerActive; - SpanCharacteristic *volume; - - SpanService *hdmi1; - SpanCharacteristic *hdmi1Identifier; - SpanCharacteristic *hdmi1CurrentVisibility; - - SpanService *hdmi2; - SpanCharacteristic *hdmi2Identifier; - SpanCharacteristic *hdmi2CurrentVisibility; - - SpanService *netflix; - SpanCharacteristic *netflixIdentifier; - SpanCharacteristic *netflixCurrentVisibility; - - HKTV() : Service::Television(){ - - Serial.printf("Init TV\n"); // initialization message - - active = new Characteristic::Active(); - activeIdentifier = new Characteristic::ActiveIdentifier(0); - new Characteristic::ConfiguredName("My TV"); - new Characteristic::PowerModeSelection(); // write-only Characteristic that enables "View TV Settings" on controls screen - new Characteristic::PictureMode(1); - new Characteristic::ClosedCaptions(); - new Characteristic::Brightness(50); - new Characteristic::CurrentMediaState(1); - new Characteristic::TargetMediaState(1); - new Characteristic::RemoteKey(); - new Characteristic::SleepDiscoveryMode(1); - - speaker = new Service::TelevisionSpeaker(); - mute = new Characteristic::Mute(); - speakerActive = new Characteristic::Active(1); - volume = new Characteristic::Volume(); - new Characteristic::VolumeControlType(1); - new Characteristic::VolumeSelector(1); - - hdmi1 = new Service::InputSource(); - new Characteristic::ConfiguredName("HDMI 1"); - new Characteristic::IsConfigured(1); // configured = 1, not configured = 0 - new Characteristic::InputSourceType(HDMI); - netflixIdentifier = new Characteristic::Identifier(0); - hdmi1CurrentVisibility = new Characteristic::CurrentVisibilityState(0); // 0 shown, 1 hidden - - hdmi2 = new Service::InputSource(); - new Characteristic::ConfiguredName("HDMI 2"); - new Characteristic::IsConfigured(1); // configured = 1, not configured = 0 - new Characteristic::InputSourceType(HDMI); - hdmi2Identifier = new Characteristic::Identifier(10); - hdmi2CurrentVisibility = new Characteristic::CurrentVisibilityState(0); // 0 shown, 1 hidden - - netflix = new Service::InputSource(); - new Characteristic::ConfiguredName("NETFLIX"); -// new Characteristic::IsConfigured(0); // configured = 1, not configured = 0 - netflixIdentifier = new Characteristic::Identifier(4); - netflixCurrentVisibility = new Characteristic::CurrentVisibilityState(0); // 0 shown, 1 hidden -// new Characteristic::TargetVisibilityState(0); // 0 shown, 1 hidden - - addLink(hdmi1); - addLink(hdmi2); - addLink(netflix); - addLink(speaker); - } - - boolean update(){ - if(active->updated()) { - if(active->getVal() != active->getNewVal()) { - Serial.printf("update(): active %d -> %d\n", active->getVal(), active->getNewVal()); - } - } - - if(activeIdentifier->updated()) { - Serial.printf("update(): activeIdentifier %d -> %d\n", activeIdentifier->getVal(), activeIdentifier->getNewVal()); - } - - return true; - } - - void loop() { - - } -}; - -void setup() { - Serial.begin(115200); - homeSpan.begin(Category::Television,"HomeSpan Television"); - - new SpanAccessory(); - new Service::AccessoryInformation(); - new Characteristic::Name("Test HKTV"); - 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"); - - new HKTV(); - - new Service::Speaker(); - new Characteristic::Mute(); -// new Characteristic::Active(1); - new Characteristic::Volume(); -// new Characteristic::VolumeControlType(1); -// new Characteristic::VolumeSelector(1); - -} - -void loop() { - homeSpan.poll(); -} From 96d74e5920510eebb5787b7e63a702028ea53780 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 24 Oct 2021 18:45:46 -0500 Subject: [PATCH 21/64] Adding Macros to allow for creation of custom Characteristics and Service --- src/src.ino | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/src.ino b/src/src.ino index d294292..5fb03bd 100644 --- a/src/src.ino +++ b/src/src.ino @@ -4,6 +4,33 @@ #include "HomeSpan.h" +#define HAPCHAR_NEW(hapName,type,perms,format,staticRange) HapChar hapName {#type,#hapName,(PERMS)(perms),format,staticRange} + +#define CREATE_CHAR_NEW(TYPE,HAPCHAR,DEFVAL,MINVAL,MAXVAL) \ + struct HAPCHAR : SpanCharacteristic { HAPCHAR(TYPE val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&Custom_Char::HAPCHAR} { init(val,nvsStore,(TYPE)MINVAL,(TYPE)MAXVAL); } }; + +#define CUSTOM_CHAR(NAME,UUID,PERMS,FORMAT,TYPE,DEFVAL,MINVAL,MAXVAL,STATIC_RANGE) \ + namespace Custom_Char { HAPCHAR_NEW(NAME,UUID,PERMS,FORMAT,STATIC_RANGE); } \ + namespace Characteristic { using namespace Custom_Char; CREATE_CHAR_NEW(TYPE,NAME,DEFVAL,MINVAL,MAXVAL); } + + +CUSTOM_CHAR(CustomActive, 123-B0, PW+PR+EV, UINT8, uint8_t, 0, 0, 1, true); + +//HAPCHAR_NEW( CustomActive, 123-B0, PW+PR+EV, UINT8, true ); +//CREATE_CHAR_NEW(uint8_t,CustomActive,0,0,1); + +//struct CustomActive : SpanCharacteristic { +// CustomActive(uint8_t val=0, boolean nvsStore=false) : SpanCharacteristic{&CustomActive}{ +// +// } +//}; + +namespace Characteristic { + int xxx=0; +} + + + void setup() { Serial.begin(115200); @@ -50,6 +77,7 @@ void setup() { new Characteristic::On(0,true); (new Characteristic::Brightness(50,true))->setRange(10,100,5); new Characteristic::Name("Light 2"); + new Characteristic::CustomActive(); } // end of setup() From d5fa8335fb6b9fdcf62eeebbecac1018ca0b89f6 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 24 Oct 2021 20:58:45 -0500 Subject: [PATCH 22/64] Update src.ino --- src/src.ino | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/src/src.ino b/src/src.ino index 5fb03bd..76b1973 100644 --- a/src/src.ino +++ b/src/src.ino @@ -4,31 +4,11 @@ #include "HomeSpan.h" -#define HAPCHAR_NEW(hapName,type,perms,format,staticRange) HapChar hapName {#type,#hapName,(PERMS)(perms),format,staticRange} - -#define CREATE_CHAR_NEW(TYPE,HAPCHAR,DEFVAL,MINVAL,MAXVAL) \ - struct HAPCHAR : SpanCharacteristic { HAPCHAR(TYPE val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&Custom_Char::HAPCHAR} { init(val,nvsStore,(TYPE)MINVAL,(TYPE)MAXVAL); } }; - -#define CUSTOM_CHAR(NAME,UUID,PERMS,FORMAT,TYPE,DEFVAL,MINVAL,MAXVAL,STATIC_RANGE) \ - namespace Custom_Char { HAPCHAR_NEW(NAME,UUID,PERMS,FORMAT,STATIC_RANGE); } \ - namespace Characteristic { using namespace Custom_Char; CREATE_CHAR_NEW(TYPE,NAME,DEFVAL,MINVAL,MAXVAL); } - +#define CUSTOM_CHAR(NAME,UUID,PERMISISONS,FORMAT,TYPE,DEFVAL,MINVAL,MAXVAL,STATIC_RANGE) \ + HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),FORMAT,STATIC_RANGE}; \ + namespace Characteristic { struct NAME : SpanCharacteristic { NAME(TYPE val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME} { init(val,nvsStore,(TYPE)MINVAL,(TYPE)MAXVAL); } }; } CUSTOM_CHAR(CustomActive, 123-B0, PW+PR+EV, UINT8, uint8_t, 0, 0, 1, true); - -//HAPCHAR_NEW( CustomActive, 123-B0, PW+PR+EV, UINT8, true ); -//CREATE_CHAR_NEW(uint8_t,CustomActive,0,0,1); - -//struct CustomActive : SpanCharacteristic { -// CustomActive(uint8_t val=0, boolean nvsStore=false) : SpanCharacteristic{&CustomActive}{ -// -// } -//}; - -namespace Characteristic { - int xxx=0; -} - void setup() { From 4309be8f91a3af119647cd251ffe49b80c0b7506 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 24 Oct 2021 21:19:38 -0500 Subject: [PATCH 23/64] Finalized CUSTOM_CHAR() macro to add a custom Characteristic --- src/HomeSpan.h | 2 +- src/Span.h | 8 ++++++++ src/src.ino | 7 +------ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 865dd22..7494647 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -441,7 +441,7 @@ struct SpanCharacteristic{ uvSet(stepValue,0); } - homeSpan.configLog+="(" + uvPrint(value) + ")" + ": IID=" + String(iid) + ", UUID=0x" + String(type); + homeSpan.configLog+="(" + uvPrint(value) + ")" + ": IID=" + String(iid) + ", UUID=" + String(type); if(format!=FORMAT::STRING && format!=FORMAT::BOOL) homeSpan.configLog+= " Range=[" + String(uvPrint(minValue)) + "," + String(uvPrint(maxValue)) + "]"; diff --git a/src/Span.h b/src/Span.h index 29bdac9..6cecf8b 100644 --- a/src/Span.h +++ b/src/Span.h @@ -522,3 +522,11 @@ namespace Characteristic { CREATE_CHAR(double,WaterLevel,0,0,100); } + +////////////////////////////////////////// +// MACROS TO ADD CUSTOM CHARACTERISTICS // +////////////////////////////////////////// + +#define CUSTOM_CHAR(NAME,UUID,PERMISISONS,FORMAT,TYPE,DEFVAL,MINVAL,MAXVAL,STATIC_RANGE) \ + HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),FORMAT,STATIC_RANGE}; \ + namespace Characteristic { struct NAME : SpanCharacteristic { NAME(TYPE val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME} { init(val,nvsStore,(TYPE)MINVAL,(TYPE)MAXVAL); } }; } diff --git a/src/src.ino b/src/src.ino index 76b1973..8ab482e 100644 --- a/src/src.ino +++ b/src/src.ino @@ -4,12 +4,7 @@ #include "HomeSpan.h" -#define CUSTOM_CHAR(NAME,UUID,PERMISISONS,FORMAT,TYPE,DEFVAL,MINVAL,MAXVAL,STATIC_RANGE) \ - HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),FORMAT,STATIC_RANGE}; \ - namespace Characteristic { struct NAME : SpanCharacteristic { NAME(TYPE val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME} { init(val,nvsStore,(TYPE)MINVAL,(TYPE)MAXVAL); } }; } - -CUSTOM_CHAR(CustomActive, 123-B0, PW+PR+EV, UINT8, uint8_t, 0, 0, 1, true); - +CUSTOM_CHAR(CustomActive, AB-123-B0, PW+PR+EV, UINT8, uint8_t, 0, 0, 1, true); void setup() { From eeb3c334ac6ac52ff0d7e7783961af357f33e43a Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 24 Oct 2021 21:24:45 -0500 Subject: [PATCH 24/64] Update src.ino --- src/src.ino | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/src.ino b/src/src.ino index 8ab482e..f594660 100644 --- a/src/src.ino +++ b/src/src.ino @@ -4,7 +4,7 @@ #include "HomeSpan.h" -CUSTOM_CHAR(CustomActive, AB-123-B0, PW+PR+EV, UINT8, uint8_t, 0, 0, 1, true); +// CUSTOM_CHAR(CustomActive, AB-123-B0, PW+PR+EV, UINT8, uint8_t, 0, 0, 1, true); void setup() { @@ -52,7 +52,6 @@ void setup() { new Characteristic::On(0,true); (new Characteristic::Brightness(50,true))->setRange(10,100,5); new Characteristic::Name("Light 2"); - new Characteristic::CustomActive(); } // end of setup() From eb9530800e93866ccf596959463c36d0228a4bb9 Mon Sep 17 00:00:00 2001 From: Gregg Date: Mon, 25 Oct 2021 21:57:29 -0500 Subject: [PATCH 25/64] Created typedef for HAP types - used to simplify CUSTOM_CHAR No need to specify both FORMAT and TYPE. For example, specifying UINT16 automatically sets type to be uint16_t. To do: Explore if this can be used for standard Characteristics - revisit standard Characteristics definitions and structure to see if it can be simplified. --- src/Characteristics.h | 11 +++++++++++ src/HomeSpan.cpp | 2 +- src/HomeSpan.h | 26 ++++++++++++++------------ src/Span.h | 13 ++++++++++--- src/src.ino | 6 ++++-- 5 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/Characteristics.h b/src/Characteristics.h index cf2b44d..8b85fa2 100644 --- a/src/Characteristics.h +++ b/src/Characteristics.h @@ -53,6 +53,17 @@ enum FORMAT { // HAP Table 6-5 /////////////////////////////// +typedef boolean BOOL_t; +typedef uint8_t UINT8_t; +typedef uint16_t UINT16_t; +typedef uint32_t UINT32_t; +typedef uint64_t UINT64_t; +typedef int32_t INT_t; +typedef double FLOAT_t; +typedef char * STRING_t; + +/////////////////////////////// + struct HapChar { const char *type; const char *hapName; diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index b6a77dc..f0b0b15 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -1479,7 +1479,7 @@ 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=0x" + String(type); + homeSpan.configLog+=": IID=" + String(iid) + ", UUID=\"" + String(type) + "\""; if(!strcmp(this->type,"3E") && iid!=1){ homeSpan.configLog+=" *** ERROR! The AccessoryInformation Service must be defined before any other Services in an Accessory. ***"; diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 7494647..f06314d 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -239,14 +239,14 @@ struct SpanCharacteristic{ union UVal { - boolean BOOL; - uint8_t UINT8; - uint16_t UINT16; - uint32_t UINT32; - uint64_t UINT64; - int32_t INT; - double FLOAT; - char *STRING = NULL; + BOOL_t BOOL; + UINT8_t UINT8; + UINT16_t UINT16; + UINT32_t UINT32; + UINT64_t UINT64; + INT_t INT; + FLOAT_t FLOAT; + STRING_t STRING = NULL; }; int iid=0; // Instance ID (HAP Table 6-3) @@ -441,16 +441,18 @@ struct SpanCharacteristic{ uvSet(stepValue,0); } - homeSpan.configLog+="(" + uvPrint(value) + ")" + ": IID=" + String(iid) + ", UUID=" + String(type); + boolean isCustom=strchr(type,'-'); + + homeSpan.configLog+="(" + uvPrint(value) + ")" + ": IID=" + String(iid) + ", UUID=\"" + String(type) + "\""; if(format!=FORMAT::STRING && format!=FORMAT::BOOL) - homeSpan.configLog+= " Range=[" + String(uvPrint(minValue)) + "," + String(uvPrint(maxValue)) + "]"; + homeSpan.configLog+= ", Range=[" + String(uvPrint(minValue)) + "," + String(uvPrint(maxValue)) + "]"; if(nvsFlag==2) homeSpan.configLog+=" (restored)"; else if(nvsFlag==1) homeSpan.configLog+=" (storing)"; - - boolean valid=false; + + boolean valid=isCustom; for(int i=0; !valid && iServices.back()->req.size(); i++) valid=!strcmp(type,homeSpan.Accessories.back()->Services.back()->req[i]->type); diff --git a/src/Span.h b/src/Span.h index 6cecf8b..3a6ce2d 100644 --- a/src/Span.h +++ b/src/Span.h @@ -524,9 +524,16 @@ namespace Characteristic { } ////////////////////////////////////////// -// MACROS TO ADD CUSTOM CHARACTERISTICS // +// MACRO TO ADD CUSTOM CHARACTERISTICS // ////////////////////////////////////////// -#define CUSTOM_CHAR(NAME,UUID,PERMISISONS,FORMAT,TYPE,DEFVAL,MINVAL,MAXVAL,STATIC_RANGE) \ +//#define CUSTOM_CHAR(NAME,UUID,PERMISISONS,FORMAT,TYPE,DEFVAL,MINVAL,MAXVAL,STATIC_RANGE) \ +// HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),FORMAT,STATIC_RANGE}; \ +// namespace Characteristic { struct NAME : SpanCharacteristic { NAME(TYPE val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME} { init(val,nvsStore,(TYPE)MINVAL,(TYPE)MAXVAL); } }; } + + +#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(TYPE val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME} { init(val,nvsStore,(TYPE)MINVAL,(TYPE)MAXVAL); } }; } + 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); } }; } + + diff --git a/src/src.ino b/src/src.ino index f594660..9f1e5c6 100644 --- a/src/src.ino +++ b/src/src.ino @@ -4,7 +4,7 @@ #include "HomeSpan.h" -// CUSTOM_CHAR(CustomActive, AB-123-B0, PW+PR+EV, UINT8, uint8_t, 0, 0, 1, true); +CUSTOM_CHAR(CustomActive, AB-123-B0, PW+PR+EV, UINT8, 0, 0, 1, false); void setup() { @@ -45,9 +45,11 @@ void setup() { new Service::LightBulb(); new Characteristic::On(0); - new Characteristic::Brightness(); + (new Characteristic::CustomActive(2))->setRange(0,10,3); + new Characteristic::Brightness(500); new Characteristic::Name("Light 1"); new Characteristic::ColorTemperature(); + new Characteristic::Active(); new Service::LightBulb(); new Characteristic::On(0,true); (new Characteristic::Brightness(50,true))->setRange(10,100,5); From b6001f9418747e1bd6f6336237767ce1f1c0889e Mon Sep 17 00:00:00 2001 From: Gregg Date: Mon, 25 Oct 2021 21:58:51 -0500 Subject: [PATCH 26/64] Update Span.h --- src/Span.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Span.h b/src/Span.h index 3a6ce2d..787616e 100644 --- a/src/Span.h +++ b/src/Span.h @@ -527,11 +527,6 @@ namespace Characteristic { // MACRO TO ADD CUSTOM CHARACTERISTICS // ////////////////////////////////////////// -//#define CUSTOM_CHAR(NAME,UUID,PERMISISONS,FORMAT,TYPE,DEFVAL,MINVAL,MAXVAL,STATIC_RANGE) \ -// HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),FORMAT,STATIC_RANGE}; \ -// namespace Characteristic { struct NAME : SpanCharacteristic { NAME(TYPE val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME} { init(val,nvsStore,(TYPE)MINVAL,(TYPE)MAXVAL); } }; } - - #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); } }; } From 92c198d01a2e0383be70dbf89ed46c4c338c3900 Mon Sep 17 00:00:00 2001 From: Gregg Date: Mon, 25 Oct 2021 22:10:44 -0500 Subject: [PATCH 27/64] Update src.ino --- src/src.ino | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/src.ino b/src/src.ino index 9f1e5c6..eb8c2a9 100644 --- a/src/src.ino +++ b/src/src.ino @@ -55,6 +55,24 @@ void setup() { (new Characteristic::Brightness(50,true))->setRange(10,100,5); new Characteristic::Name("Light 2"); + new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments + + new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, which has 6 required Characteristics + new Characteristic::Name("HomeSpan Test"); // 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("HSL-123"); // Serial Number of the Accessory (arbitrary text string, and can be the same for every Accessory) + new Characteristic::Model("HSL Test"); // Model of the Accessory (arbitrary text string, and can be the same for every Accessory) + new Characteristic::FirmwareRevision(HOMESPAN_VERSION); // Firmware of the Accessory (arbitrary text string, and can be the same for every Accessory) + 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 Characteristic to "1.1.0" as required by HAP + + new Service::LightBulb(); + new Characteristic::On(0,true); + (new Characteristic::Brightness(50,true))->setRange(10,100,5); + new Characteristic::Name("Light 2"); + } // end of setup() ////////////////////////////////////// From 65b15b5628d5b542c0123915aa471ad55143ee4d Mon Sep 17 00:00:00 2001 From: Gregg Date: Tue, 26 Oct 2021 05:39:54 -0500 Subject: [PATCH 28/64] Added AID info to Set Range log message --- src/HomeSpan.h | 2 +- src/src.ino | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/HomeSpan.h b/src/HomeSpan.h index f06314d..19f6918 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -369,7 +369,7 @@ struct SpanCharacteristic{ template SpanCharacteristic *setRange(A min, B max, S step=0){ char c[256]; - homeSpan.configLog+=String(" \u2b0c Set Range for ") + String(hapName) + " with IID=" + String(iid); + homeSpan.configLog+=String(" \u2b0c Set Range for ") + String(hapName) + " with AID=" + String(aid) + ", IID=" + String(iid); if(customRange){ sprintf(c," *** ERROR! Range already set for this Characteristic! ***\n"); diff --git a/src/src.ino b/src/src.ino index eb8c2a9..68b3b06 100644 --- a/src/src.ino +++ b/src/src.ino @@ -45,7 +45,7 @@ void setup() { new Service::LightBulb(); new Characteristic::On(0); - (new Characteristic::CustomActive(2))->setRange(0,10,3); + SpanCharacteristic *active = new Characteristic::CustomActive(2); new Characteristic::Brightness(500); new Characteristic::Name("Light 1"); new Characteristic::ColorTemperature(); @@ -57,6 +57,7 @@ void setup() { new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments + active->setRange(0,10,3); new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, which has 6 required Characteristics new Characteristic::Name("HomeSpan Test"); // 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) @@ -71,7 +72,7 @@ void setup() { new Service::LightBulb(); new Characteristic::On(0,true); (new Characteristic::Brightness(50,true))->setRange(10,100,5); - new Characteristic::Name("Light 2"); + new Characteristic::Name("Light 3"); } // end of setup() From 61a2be533be30907685023256125db96399e2949 Mon Sep 17 00:00:00 2001 From: Gregg Date: Tue, 26 Oct 2021 22:05:44 -0500 Subject: [PATCH 29/64] Moved Range checking to a standalone routine checkRanges() Ranges are now checked for all Characteristics at the end of the configuration, instead of at the end of each Accessory definition. This is much cleaner and the output is easier to read. To do: Revisit use of REQ and OPT - what should constitute a fatal error and what should be a warning. To do: Revisit Character definitions - attempt to normalize using the methods implemented for Custom Characteristics --- src/HomeSpan.cpp | 45 +++++++++++++++++++++++++++++++++------------ src/HomeSpan.h | 1 + src/src.ino | 6 +++--- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index f0b0b15..3c2928e 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -157,6 +157,8 @@ void Span::poll() { homeSpan.Accessories.back()->validate(); } + checkRanges(); + if(nWarnings>0){ configLog+="\n*** CAUTION: There " + String((nWarnings>1?"are ":"is ")) + String(nWarnings) + " WARNING" + (nWarnings>1?"S":"") + " associated with this configuration that may lead to the device becoming non-responsive, or operating in an unexpected manner. ***\n"; } @@ -1347,6 +1349,37 @@ int Span::sprintfAttributes(char **ids, int numIDs, int flags, char *cBuf){ return(nChars); } +/////////////////////////////// + +void Span::checkRanges(){ + + boolean okay=true; + homeSpan.configLog+="\nRange Check:"; + + for(int i=0;iServices.size();j++){ + for(int k=0;kServices[j]->Characteristics.size();k++){ + SpanCharacteristic *chr=Accessories[i]->Services[j]->Characteristics[k]; + + if(chr->format!=STRING && (chr->uvGet(chr->value) < chr->uvGet(chr->minValue) || chr->uvGet(chr->value) > chr->uvGet(chr->maxValue))){ + char c[256]; + sprintf(c,"\n \u2718 Characteristic %s with AID=%d, IID=%d: Initial value of %lg is out of range [%llg,%llg]", + chr->hapName,chr->aid,chr->iid,chr->uvGet(chr->value),chr->uvGet(chr->minValue),chr->uvGet(chr->maxValue)); + if(okay) + homeSpan.configLog+="\n"; + homeSpan.configLog+=c; + homeSpan.nWarnings++; + okay=false; + } + } + } + } + + if(okay) + homeSpan.configLog+=" No Warnings"; + homeSpan.configLog+="\n\n"; +} + /////////////////////////////// // SpanAccessory // /////////////////////////////// @@ -1411,18 +1444,6 @@ void SpanAccessory::validate(){ foundProtocol=true; else if(aid==1) // this is an Accessory with aid=1, but it has more than just AccessoryInfo and HAPProtocolInformation. So... homeSpan.isBridge=false; // ...this is not a bridge device - - for(int j=0;jCharacteristics.size();j++){ // check that initial values are all in range of mix/max (which may have been modified by setRange) - SpanCharacteristic *chr=Services[i]->Characteristics[j]; - - if(chr->format!=STRING && (chr->uvGet(chr->value) < chr->uvGet(chr->minValue) || chr->uvGet(chr->value) > chr->uvGet(chr->maxValue))){ - char c[256]; - sprintf(c," \u2718 Characteristic %s with IID=%d *** WARNING: Initial value of %lg is out of range [%llg,%llg]. ***\n", - chr->hapName,chr->iid,chr->uvGet(chr->value),chr->uvGet(chr->minValue),chr->uvGet(chr->maxValue)); - homeSpan.configLog+=c; - homeSpan.nWarnings++; - } - } } if(!foundInfo){ diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 19f6918..828fab2 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -156,6 +156,7 @@ struct Span{ void checkConnect(); // check WiFi connection; connect if needed void commandMode(); // allows user to control and reset HomeSpan settings with the control button void processSerialCommand(const char *c); // process command 'c' (typically from readSerial, though can be called with any 'c') + void checkRanges(); // checks values of all Characteristics to ensure they are each within range int sprintfAttributes(char *cBuf); // prints Attributes JSON database into buf, unless buf=NULL; return number of characters printed, excluding null terminator, even if buf=NULL void prettyPrint(char *buf, int nsp=2); // print arbitrary JSON from buf to serial monitor, formatted with indentions of 'nsp' spaces diff --git a/src/src.ino b/src/src.ino index 68b3b06..b381edd 100644 --- a/src/src.ino +++ b/src/src.ino @@ -45,8 +45,8 @@ void setup() { new Service::LightBulb(); new Characteristic::On(0); - SpanCharacteristic *active = new Characteristic::CustomActive(2); - new Characteristic::Brightness(500); + SpanCharacteristic *active = new Characteristic::CustomActive(); + new Characteristic::Brightness(50); new Characteristic::Name("Light 1"); new Characteristic::ColorTemperature(); new Characteristic::Active(); @@ -57,7 +57,7 @@ void setup() { new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments - active->setRange(0,10,3); +// active->setRange(0,10,3); new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, which has 6 required Characteristics new Characteristic::Name("HomeSpan Test"); // 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) From 226548defaf3ea322038aec6463313fd121ff4b4 Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 29 Oct 2021 22:54:10 -0500 Subject: [PATCH 30/64] Added check to ensure Custom Characteristic has valid UUID And also converted ERRORS to WARNINGS when a Characteristic that is not in the REQ or OPT list is specified for a Service. This allows the user to add any Characteristic to any Service without forcing an Error (just a Warning). --- src/HomeSpan.h | 11 +++++++---- src/Span.h | 1 - src/src.ino | 9 +++++---- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 828fab2..299c0b8 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -442,9 +442,12 @@ struct SpanCharacteristic{ uvSet(stepValue,0); } - boolean isCustom=strchr(type,'-'); + 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) + ", UUID=\"" + String(type) + "\""; + 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)) + "]"; @@ -462,8 +465,8 @@ struct SpanCharacteristic{ valid=!strcmp(type,homeSpan.Accessories.back()->Services.back()->opt[i]->type); if(!valid){ - homeSpan.configLog+=" *** ERROR! Service does not support this Characteristic. ***"; - homeSpan.nFatalErrors++; + homeSpan.configLog+=" *** WARNING! Service does not support this Characteristic. ***"; + homeSpan.nWarnings++; } boolean repeated=false; diff --git a/src/Span.h b/src/Span.h index 787616e..bf23dc4 100644 --- a/src/Span.h +++ b/src/Span.h @@ -329,7 +329,6 @@ namespace Service { struct Television : SpanService { Television() : SpanService{"D8","Television"}{ REQ(Active); OPT(ConfiguredName); - OPT(ConfiguredNameStatic); OPT(ActiveIdentifier); OPT(RemoteKey); OPT(PowerModeSelection); diff --git a/src/src.ino b/src/src.ino index b381edd..cb21b6b 100644 --- a/src/src.ino +++ b/src/src.ino @@ -4,7 +4,7 @@ #include "HomeSpan.h" -CUSTOM_CHAR(CustomActive, AB-123-B0, PW+PR+EV, UINT8, 0, 0, 1, false); +CUSTOM_CHAR(CustomActive, E863F10A-079E-48FF-8F27-9C2605A29F52, PR+EV, UINT16, 0, 0, 4800, false); void setup() { @@ -45,19 +45,18 @@ void setup() { new Service::LightBulb(); new Characteristic::On(0); - SpanCharacteristic *active = new Characteristic::CustomActive(); + new Characteristic::CustomActive(1200); new Characteristic::Brightness(50); new Characteristic::Name("Light 1"); new Characteristic::ColorTemperature(); new Characteristic::Active(); new Service::LightBulb(); new Characteristic::On(0,true); - (new Characteristic::Brightness(50,true))->setRange(10,100,5); + (new Characteristic::Brightness(50,false))->setRange(10,100,5); new Characteristic::Name("Light 2"); new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments -// active->setRange(0,10,3); new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, which has 6 required Characteristics new Characteristic::Name("HomeSpan Test"); // 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) @@ -73,6 +72,8 @@ void setup() { new Characteristic::On(0,true); (new Characteristic::Brightness(50,true))->setRange(10,100,5); new Characteristic::Name("Light 3"); + new Characteristic::TargetPosition(); + new Characteristic::OzoneDensity(); } // end of setup() From 324848e330632956fdcba955331deaed58265575 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 30 Oct 2021 15:30:50 -0500 Subject: [PATCH 31/64] Update Extras.md --- docs/Extras.md | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/docs/Extras.md b/docs/Extras.md index 245c7c3..88ccd10 100644 --- a/docs/Extras.md +++ b/docs/Extras.md @@ -80,11 +80,11 @@ The ESP32 has an on-chip signal-generator peripheral designed to drive an RF or `#include "extras/RFControl.h"` -### *RFControl(int pin)* +### *RFControl(int pin, boolean refClock=true)* -Creating an instance of this **class** initializes the RF/IR signal generator and specifies the ESP32 *pin* to output the signal. You may create more than one instance of this class if driving more than one RF/IR transmitter (each connected to different *pin*), subject to the following limitations: ESP32 - 8 instances; ESP32-S2 - 4 instances; ESP32-C3 - 2 instances. +Creating an instance of this **class** initializes the RF/IR signal generator and specifies the ESP32 *pin* to output the signal. You may create more than one instance of this class if driving more than one RF/IR transmitter (each connected to different *pin*), subject to the following limitations: ESP32 - 8 instances; ESP32-S2 - 4 instances; ESP32-C3 - 2 instances. The optional parameter *refClock* is more fully described further below under the `start()` method. -Signals are defined as a sequence of HIGH and LOW phases that together form a pulse train where you specify the duration, in *ticks*, of each HIGH and LOW phase, shown respectively as H1-H4 and L1-L4 in the diagram below. +Signals are defined as a sequence of HIGH and LOW phases that together form a pulse train where you specify the duration, in *ticks*, of each HIGH and LOW phase, shown respectively as H1-H4 and L1-L4 in the following diagram: ![Pulse Train](images/pulseTrain.png) @@ -105,19 +105,33 @@ Details of each function are as follows: * clears the pulse train memory structure of a specific instance of RFControl -* `void phase(uint16_t numTicks, uint8_t phase)` +* `void phase(uint32_t numTicks, uint8_t phase)` * appends either a HIGH or LOW phase to the pulse train memory buffer for a specific instance of RFControl - * *numTicks* - the duration, in *ticks* of the pulse phase. Allowable range is 0-32767 ticks. Requests to add a pulse with *numTicks* outside this range are ignored, but raise non-fatal warning message + * *numTicks* - the duration, in *ticks* of the pulse phase. Durations of greater than 32767 ticks allowed (the system automatically creates repeated pulses of a maximum of 32767 ticks each until the specified duration of *numTicks* is reached) * *phase* - set to 0 to create a LOW phase; set to 1 (or any non-zero number) to create a HIGH phase * repeated phases of the same type (e.g. HIGH followed by another HIGH) are permitted and result in a single HIGH or LOW phase with a duration equal to the sum of the *numTicks* specified for each repeated phase (this is helpful when generating Manchester-encoded signals) -* `void add(uint16_t onTime, uint16_t offTime)` +* `void add(uint32_t onTime, uint32_t offTime)` - * appends a single HIGH/LOW pulse with duration *onTime* followed by *offTime* to the pulse train of a specific instance of RFControl. This is functionally equivalent to calling `phase(onTime,HIGH);` followed by `phase(offTime,LOW);` as defined above + * appends a single HIGH/LOW pulse with duration *onTime* followed by *offTime* to the pulse train of a specific instance of RFControl. This is functionally equivalent to calling `phase(onTime,HIGH);` followed by `phase(offTime,LOW);` as defined above + +* `void enableCarrier(uint32_t freq, float duty=0.5)` + + * enables the overlay of a "square" carrier wave on the pulse train. In practice this is only used for IR signals (not RF) + + * *freq* - the frequency, in Hz, of the carrier wave. If freq=0, carrier wave is disabled + + * *duty* - the duty cycle of the carrier wave, from 0-1. Default is 0.5 if not specified + + * RFControl will report an error if the combination of the specified frequency and duty cycle is outside the range of supported configurations + +* `void disableCarrier()` + + * disables the carrier wave. Equivalent to `enableCarrier(0);` * `void start(uint8_t _numCycles, uint8_t tickTime)` * `void start(uint32_t *data, int nData, uint8_t nCycles, uint8_t tickTime)` @@ -126,7 +140,7 @@ Details of each function are as follows: * *numCycles* - the total number of times to transmit the pulse train (i.e. a value of 3 means the pulse train will be transmitted once, followed by 2 additional re-transmissions). This is an optional argument with a default of 1 if not specified. - * *tickTime* - the duration, in **microseconds**, of a *tick*. This is an optional argument with a default of 1𝛍s if not specified. Valid range is 1-255𝛍s, or set to 0 for 256𝛍s + * *tickTime* - the duration, in ***clock units***, of a *tick*. This is an optional argument with a default of 1 *clock unit* if not specified. Valid range is 1-255 *clock units*, or set to 0 for 256 *clock units*. The duration of a *clock unit* is determined by the *refClock* parameter (the second, optional argument, in the RFControl constructor described above). If *refClock* is set to true (the default), RFControl uses the ESP32's 1 MHz Reference Clock for timing so that each *clock unit* equals 1𝛍s. If *refClock* is set to false, RFControl uses the ESP32's faster 80 MHz APB Clock so that each *clock unit* equals 0.0125𝛍s (1/80 of microsecond) * To aid in the creation of a pulse train stored in an external array of 32-bit words, RFControl includes the macro *RF_PULSE(highTicks,lowTicks)* that returns a properly-formatted 32-bit value representing a single HIGH/LOW pulse of duration *highTicks* followed by *lowTicks*. This is basically an analog to the `add()` function. For example, the following code snippet shows two ways of creating and transmitting the same 3-pulse pulse-train --- the only difference being that one uses the internal memory structure of RFControl, and the second uses an external array: From 59eb8e4cf351384388ded3adc3871a0245bbc463 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 30 Oct 2021 15:40:30 -0500 Subject: [PATCH 32/64] Update Extras.md --- docs/Extras.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Extras.md b/docs/Extras.md index 88ccd10..477a1e9 100644 --- a/docs/Extras.md +++ b/docs/Extras.md @@ -121,7 +121,7 @@ Details of each function are as follows: * `void enableCarrier(uint32_t freq, float duty=0.5)` - * enables the overlay of a "square" carrier wave on the pulse train. In practice this is only used for IR signals (not RF) + * enables modulation of the pulse train with a "square" carrier wave. In practice this is only used for IR signals (not RF) * *freq* - the frequency, in Hz, of the carrier wave. If freq=0, carrier wave is disabled From 3cdd12f7b6e004cd910e9c1daafce779d1f8c04a Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 30 Oct 2021 16:58:02 -0500 Subject: [PATCH 33/64] Update Reference.md --- docs/Reference.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/Reference.md b/docs/Reference.md index 5dfc163..03c1be2 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -273,6 +273,33 @@ If REQUIRED is defined in the main sketch prior to including the HomeSpan librar ```C++ #define REQUIRED VERISON(2,1,3) // throws a compile-time error unless HomeSpan library used is version 2.1.3 or later ``` +### *#define CUSTOM_CHAR(name,uuid,perms,format,defaultValue,minValue,maxValue,staticRange)* + +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). Parameters are as follows (note that quotes should NOT be used in any of the string parameters): + +* *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, FLOAT, and STRING. Note that the HomeSpan does not presently support the TLV8 or DATA formats +* *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()` +* *minValue* - specifies the default minimum range for a valid value, which may be able to be overriden by a call to `setRange()` +* *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 + +As an example, the following 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()`: + +```C++ +CUSTOM_CHAR(Voltage, E863F10A-079E-48FF-8F27-9C2605A29F52, PR+EV, UINT16, 120, 0, 240, false); +... +new Service::LightBulb(); + new Characteristic::Name("Low-Voltage Lamp"); + new Characteristic::On(0); + new Characteristic::Brightness(50); + new Characteristic::Voltage(12); // adds Voltage Characteristics and sets initial value to 12 volts +``` + +Note that Custom Characteristics must be created prior to calling `homeSpan.begin()` + --- #### Deprecated functions (available for backwards compatibility with older sketches): From 5d92a876ecd61f8c47e07d618ac8a9f6acc55aa2 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 30 Oct 2021 16:58:35 -0500 Subject: [PATCH 34/64] Update Reference.md --- docs/Reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Reference.md b/docs/Reference.md index 03c1be2..8d0c9b0 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -266,7 +266,7 @@ To invoke this command from the CLI, preface the single-letter name *c* with '@' 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. -## *#define REQUIRED VERSION(major,minor,patch)* +### *#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: From 2d2262d1220e8274589af2ddde23ad13515231b4 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 30 Oct 2021 17:00:18 -0500 Subject: [PATCH 35/64] Update Reference.md --- docs/Reference.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/Reference.md b/docs/Reference.md index 8d0c9b0..025c11d 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -266,6 +266,8 @@ To invoke this command from the CLI, preface the single-letter name *c* with '@' 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 + ### *#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: From 29cccd8aeea50e041b108cace6d705b848a531b4 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 30 Oct 2021 17:04:07 -0500 Subject: [PATCH 36/64] Update README.md --- docs/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/README.md b/docs/README.md index 2c0e18f..d23a09a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -87,6 +87,7 @@ HomeSpan includes the following documentation: * [HomeSpan QR Codes](https://github.com/HomeSpan/HomeSpan/blob/master/docs/QRCodes.md) - create and use QR Codes for pairing HomeSpan devices * [HomeSpan OTA](https://github.com/HomeSpan/HomeSpan/blob/master/docs/OTA.md) - update your sketches Over-the-Air directly from the Arduino IDE without a serial connection * [HomeSpan Extras](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Extras.md) - integrated access to the ESP32's on-chip LED, Servo Motor, and Remote Control peripherals! +* [HomeSpan Television Services]() - how to use HomeKit's undocumented Television Services and Characteristics * [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 From 3cb08345f3f99de507b096ba390b2c86ef8ffa79 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 30 Oct 2021 17:08:07 -0500 Subject: [PATCH 37/64] Create TVServices.md --- docs/TVServices.md | 172 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 docs/TVServices.md diff --git a/docs/TVServices.md b/docs/TVServices.md new file mode 100644 index 0000000..57949ef --- /dev/null +++ b/docs/TVServices.md @@ -0,0 +1,172 @@ +# HomeSpan Services and Characteristics + +HomeSpan implements all [HAP-R2](https://developer.apple.com/homekit/specification/) Services and Characteristics except for those that involve video or audio streaming, Apple TV, or advanced lock management (i.e. all HAP Services except those that require Characteristics with a TLV8 data type). + +HomeSpan Services and Characteristics are implemented as C++ Classes with names that exactly match the spelling and capitalization specified by Apple in Sections 8 and 9 of [HAP-R2](https://developer.apple.com/homekit/specification/), but without any spaces. HomeSpan Services are defined in HomeSpan's `Service` namespace. HomeSpan Characteristics are defined in HomeSpan's `Characteristic` namespace. For example, HomeSpan defines the *Carbon Dioxide Sensor* Service (HAP Service 8.7) as `Service::CarbonDioxideSensor`, and the *Carbon Dioxide Detected* Characteristic (HAP Characteristic 9.16) as `Characteristic::CarbonDioxideDetected`. + +HomeSpan Services and Characteristics are instantiated with a C++ `new` command. Services do not take any arguments, whereas Characteristics take a single, optional argument that is used to initialize the value of the Characteristic at startup. If this argument is not specified, HomeSpan will apply a reasonable [default value](#characteristic-types-and-defaults) based on the Characteristic's type and allowed range. + +A list of all HomeSpan Services is provided in the table below. For each Service the table also indicates which Characteristics are required and which are optional. For example, a dimmable light bulb could be configured in HomeSpan as such: + +```C++ +new Service::LightBulb(); // instantiate a Light Bulb Service + new Characteristics:On(); // instantiate the required On Characteristic without setting initial value + new Characteristic::Brightness(50); // instantiate an optional Brightness Characteristic and set initial value to 50% + new Characteristic::Name("Living Room Lamp"); // instantiate an optional Name Characteristic for this Service, and set to "Living Room Lamp" +``` + +Please see Sections 8 and 9 of [HAP-R2](https://developer.apple.com/homekit/specification/) for a complete description of all HAP Services and Characteristics. Note that HomeSpan's Service and Characteristic Classes already contain all the required HAP fields, such as the UUID, Format, and Permissions, so you don't need to specify any of these parameters. + +Additionally, when first starting up, HomeSpan begins by validating the device's configuration to ensure each Service you instantiate includes all required Characteristics, but does not include any Characteristics that are neither required nor optional. If any errors are found, HomeSpan reports them to the Arduino Serial Monitor and halts the program. + +### Service List + +| Service | Required Characteristics | Optional Characteristics | +| ------- | -------------------- | ------------------- | +| AccessoryInformation| FirmwareRevision
Identity
Manufacturer
Model
Name
SerialNumber | HardwareRevision | +| AirPurifier | Active
CurrentAirPurifierState
TargetAirPurifierState | Name
RotationSpeed
SwingMode
LockPhysicalControls | +| AirQualitySensor | AirQuality | Name
OzoneDensity
NitrogenDioxideDensity
SulphurDioxideDensity
PM25Density
PM10Density
VOCDensity
StatusActive
StatusFault
StatusTampered
StatusLowBattery | +| BatteryService | BatteryLevel
ChargingState
StatusLowBattery | Name | +| CarbonDioxideSensor | CarbonDioxideDetected | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery
CarbonDioxideLevel
CarbonDioxidePeakLevel | +| CarbonMonoxideSensor | CarbonMonoxideDetected | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery
CarbonMonoxideLevel
CarbonMonoxidePeakLevel | +| ContactSensor | ContactSensorState | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | +| Door | CurrentPosition
TargetPosition
PositionState | Name
HoldPosition
ObstructionDetected | +| Doorbell | ProgrammableSwitchEvent | Name
Volume
Brightness | +| Fan | Active | Name
CurrentFanState
TargetFanState
RotationDirection
RotationSpeed
SwingMode
LockPhysicalControls | +| Faucet | Active | StatusFault
Name | +| FilterMaintenance | FilterChangeIndication | Name
FilterLifeLevel
ResetFilterIndication | +| GarageDoorOpener | CurrentDoorState
TargetDoorState
ObstructionDetected | LockCurrentState
LockTargetState
Name | +| HAPProtocolInformation | Version | *none* | +| HeaterCooler | Active
CurrentTemperature
CurrentHeaterCoolerState
TargetHeaterCoolerState | Name
RotationSpeed
TemperatureDisplayUnits
SwingMode
CoolingThresholdTemperature
HeatingThresholdTemperature
LockPhysicalControls | +| HumidifierDehumidifier | Active
CurrentRelativeHumidity
CurrentHumidifierDehumidifierState
TargetHumidifierDehumidifierState | Name
RelativeHumidityDehumidifierThreshold
RelativeHumidityHumidifierThreshold
RotationSpeed
SwingMode
WaterLevel
LockPhysicalControls | +| HumiditySensor | CurrentRelativeHumidity | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | +| IrrigationSystem | Active
ProgramMode
InUse | RemainingDuration
Name
StatusFault | +| LeakSensor | LeakDetected | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | +| LightBulb | On | Brightness
Hue
Name
Saturation
ColorTemperature | +| LightSensor | CurrentAmbientLightLevel | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | +| LockMechanism | LockCurrentState
LockTargetState | Name | +| Microphone | Mute | Name
Volume | +| MotionSensor | MotionDetected | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | +| OccupancySensor | OccupancyDetected | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | +| Outlet | On
OutletInUse | Name | +| SecuritySystem | SecuritySystemCurrentState
SecuritySystemTargetState | Name
SecuritySystemAlarmType
StatusFault
StatusTampered | +| ServiceLabel | ServiceLabelNamespace | *none* | +| Slat | CurrentSlatState
SlatType | Name
SwingMode
CurrentTiltAngle
TargetTiltAngle | +| SmokeSensor | SmokeDetected | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | +| Speaker | Mute | Name
Volume | +| StatelessProgrammableSwitch | ProgrammableSwitchEvent | Name
ServiceLabelIndex | +| Switch | On | Name | +| TemperatureSensor | CurrentTemperature | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | +| Thermostat | CurrentHeatingCoolingState
TargetHeatingCoolingState
CurrentTemperature
TargetTemperature
TemperatureDisplayUnits | CoolingThresholdTemperature
CurrentRelativeHumidity
HeatingThresholdTemperature
Name
TargetRelativeHumidity | +| Valve | Active
InUse
ValveType | SetDuration
RemainingDuration
IsConfigured
ServiceLabelIndex
StatusFault
Name | +| Window | CurrentPosition
TargetPosition
PositionState | Name
HoldPosition
ObstructionDetected | +| WindowCovering | CurrentPosition
TargetPosition | Name
PositionState
HoldPosition
CurrentHorizontalTiltAngle
TargetHorizontalTiltAngle
CurrentVerticalTiltAngle
TargetVerticalTiltAngle
ObstructionDetected | + + +### Characteristic Types and Defaults + +|Characteristic|Type|Default +|---|---|---| +|Active|uint8_t|0| +|AirQuality|uint8_t|0| +|BatteryLevel|uint8_t|0| +|Brightness|int|0| +|CarbonMonoxideLevel|double|0| +|CarbonMonoxidePeakLevel|double|0| +|CarbonMonoxideDetected|uint8_t|0| +|CarbonDioxideLevel|double|0| +|CarbonDioxidePeakLevel|double|0| +|CarbonDioxideDetected|uint8_t|0| +|ChargingState|uint8_t|0| +|CoolingThresholdTemperature|double|10| +|ColorTemperature|uint32_t|50| +|ContactSensorState|uint8_t|1| +|CurrentAmbientLightLevel|double|1| +|CurrentHorizontalTiltAngle|int|0| +|CurrentAirPurifierState|uint8_t|1| +|CurrentSlatState|uint8_t|0| +|CurrentPosition|uint8_t|0| +|CurrentVerticalTiltAngle|int|0| +|CurrentHumidifierDehumidifierState|uint8_t|1| +|CurrentDoorState|uint8_t|1| +|CurrentFanState|uint8_t|1| +|CurrentHeatingCoolingState|uint8_t|0| +|CurrentHeaterCoolerState|uint8_t|1| +|CurrentRelativeHumidity|double|0| +|CurrentTemperature|double|0| +|CurrentTiltAngle|int|0| +|FilterLifeLevel|double|0| +|FilterChangeIndication|uint8_t|0| +|FirmwareRevision|char \*|"1.0.0"| +|HardwareRevision|char \*|"1.0.0"| +|HeatingThresholdTemperature|double|16| +|HoldPosition|boolean|false| +|Hue|double|0| +|Identify|boolean|false| +|InUse|uint8_t|0| +|IsConfigured|uint8_t|0| +|LeakDetected|uint8_t|0| +|LockCurrentState|uint8_t|0| +|LockPhysicalControls|uint8_t|0| +|LockTargetState|uint8_t|0| +|Manufacturer|char \*|"HomeSpan"| +|Model|char \*|"HomeSpan-ESP32"| +|MotionDetected|boolean|false| +|Mute|boolean|false| +|Name|char \*|"unnamed"| +|NitrogenDioxideDensity|double|0| +|ObstructionDetected|boolean|false| +|PM25Density|double|0| +|OccupancyDetected|uint8_t|0| +|OutletInUse|boolean|false| +|On|boolean|false| +|OzoneDensity|double|0| +|PM10Density|double|0| +|PositionState|uint8_t|2| +|ProgramMode|uint8_t|0| +|ProgrammableSwitchEvent|uint8_t|0| +|RelativeHumidityDehumidifierThreshold|double|50| +|RelativeHumidityHumidifierThreshold|double|50| +|RemainingDuration|uint32_t|60| +|ResetFilterIndication|uint8_t|0| +|RotationDirection|int|0| +|RotationSpeed|double|0| +|Saturation|double|0| +|SecuritySystemAlarmType|uint8_t|0| +|SecuritySystemCurrentState|uint8_t|3| +|SecuritySystemTargetState|uint8_t|3| +|SerialNumber|char \*|"HS-12345"| +|ServiceLabelIndex|uint8_t|1| +|ServiceLabelNamespace|uint8_t|1| +|SlatType|uint8_t|0| +|SmokeDetected|uint8_t|0| +|StatusActive|boolean|true| +|StatusFault|uint8_t|0| +|StatusJammed|uint8_t|0| +|StatusLowBattery|uint8_t|0| +|StatusTampered|uint8_t|0| +|SulphurDioxideDensity|double|0| +|SwingMode|uint8_t|0| +|TargetAirPurifierState|uint8_t|1| +|TargetFanState|uint8_t|1| +|TargetTiltAngle|int|0| +|SetDuration|uint32_t|60| +|TargetHorizontalTiltAngle|int|0| +|TargetHumidifierDehumidifierState|uint8_t|0| +|TargetPosition|uint8_t|0| +|TargetDoorState|uint8_t|1| +|TargetHeaterCoolerState|uint8_t|0| +|TargetHeatingCoolingState|uint8_t|0| +|TargetRelativeHumidity|double|0| +|TargetTemperature|double|16| +|TemperatureDisplayUnits|uint8_t|0| +|TargetVerticalTiltAngle|int|0| +|ValveType|uint8_t|0| +|Version|char \*|"1.0.0"| +|VOCDensity|double|0| +|Volume|uint8_t|0| +|WaterLevel|double|0| + +--- + +[↩️](README.md) Back to the Welcome page From f2e0bf6089222cfb2d8395d9f7fa4f14d61e35d4 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 30 Oct 2021 17:09:22 -0500 Subject: [PATCH 38/64] Update README.md --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index d23a09a..0a15164 100644 --- a/docs/README.md +++ b/docs/README.md @@ -87,7 +87,7 @@ HomeSpan includes the following documentation: * [HomeSpan QR Codes](https://github.com/HomeSpan/HomeSpan/blob/master/docs/QRCodes.md) - create and use QR Codes for pairing HomeSpan devices * [HomeSpan OTA](https://github.com/HomeSpan/HomeSpan/blob/master/docs/OTA.md) - update your sketches Over-the-Air directly from the Arduino IDE without a serial connection * [HomeSpan Extras](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Extras.md) - integrated access to the ESP32's on-chip LED, Servo Motor, and Remote Control peripherals! -* [HomeSpan Television Services]() - how to use HomeKit's undocumented Television Services and Characteristics +* [HomeSpan Television Services](https://github.com/HomeSpan/HomeSpan/blob/master/docs/TVServices.md) - how to use HomeKit's undocumented Television Services and Characteristics * [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 From be8dd811e887eaf0ae59ac5958dbef91297c0084 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 30 Oct 2021 17:10:44 -0500 Subject: [PATCH 39/64] Update TVServices.md --- docs/TVServices.md | 169 +-------------------------------------------- 1 file changed, 2 insertions(+), 167 deletions(-) diff --git a/docs/TVServices.md b/docs/TVServices.md index 57949ef..d2c0b90 100644 --- a/docs/TVServices.md +++ b/docs/TVServices.md @@ -1,171 +1,6 @@ -# HomeSpan Services and Characteristics +# Television Services and Characteristics -HomeSpan implements all [HAP-R2](https://developer.apple.com/homekit/specification/) Services and Characteristics except for those that involve video or audio streaming, Apple TV, or advanced lock management (i.e. all HAP Services except those that require Characteristics with a TLV8 data type). - -HomeSpan Services and Characteristics are implemented as C++ Classes with names that exactly match the spelling and capitalization specified by Apple in Sections 8 and 9 of [HAP-R2](https://developer.apple.com/homekit/specification/), but without any spaces. HomeSpan Services are defined in HomeSpan's `Service` namespace. HomeSpan Characteristics are defined in HomeSpan's `Characteristic` namespace. For example, HomeSpan defines the *Carbon Dioxide Sensor* Service (HAP Service 8.7) as `Service::CarbonDioxideSensor`, and the *Carbon Dioxide Detected* Characteristic (HAP Characteristic 9.16) as `Characteristic::CarbonDioxideDetected`. - -HomeSpan Services and Characteristics are instantiated with a C++ `new` command. Services do not take any arguments, whereas Characteristics take a single, optional argument that is used to initialize the value of the Characteristic at startup. If this argument is not specified, HomeSpan will apply a reasonable [default value](#characteristic-types-and-defaults) based on the Characteristic's type and allowed range. - -A list of all HomeSpan Services is provided in the table below. For each Service the table also indicates which Characteristics are required and which are optional. For example, a dimmable light bulb could be configured in HomeSpan as such: - -```C++ -new Service::LightBulb(); // instantiate a Light Bulb Service - new Characteristics:On(); // instantiate the required On Characteristic without setting initial value - new Characteristic::Brightness(50); // instantiate an optional Brightness Characteristic and set initial value to 50% - new Characteristic::Name("Living Room Lamp"); // instantiate an optional Name Characteristic for this Service, and set to "Living Room Lamp" -``` - -Please see Sections 8 and 9 of [HAP-R2](https://developer.apple.com/homekit/specification/) for a complete description of all HAP Services and Characteristics. Note that HomeSpan's Service and Characteristic Classes already contain all the required HAP fields, such as the UUID, Format, and Permissions, so you don't need to specify any of these parameters. - -Additionally, when first starting up, HomeSpan begins by validating the device's configuration to ensure each Service you instantiate includes all required Characteristics, but does not include any Characteristics that are neither required nor optional. If any errors are found, HomeSpan reports them to the Arduino Serial Monitor and halts the program. - -### Service List - -| Service | Required Characteristics | Optional Characteristics | -| ------- | -------------------- | ------------------- | -| AccessoryInformation| FirmwareRevision
Identity
Manufacturer
Model
Name
SerialNumber | HardwareRevision | -| AirPurifier | Active
CurrentAirPurifierState
TargetAirPurifierState | Name
RotationSpeed
SwingMode
LockPhysicalControls | -| AirQualitySensor | AirQuality | Name
OzoneDensity
NitrogenDioxideDensity
SulphurDioxideDensity
PM25Density
PM10Density
VOCDensity
StatusActive
StatusFault
StatusTampered
StatusLowBattery | -| BatteryService | BatteryLevel
ChargingState
StatusLowBattery | Name | -| CarbonDioxideSensor | CarbonDioxideDetected | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery
CarbonDioxideLevel
CarbonDioxidePeakLevel | -| CarbonMonoxideSensor | CarbonMonoxideDetected | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery
CarbonMonoxideLevel
CarbonMonoxidePeakLevel | -| ContactSensor | ContactSensorState | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | -| Door | CurrentPosition
TargetPosition
PositionState | Name
HoldPosition
ObstructionDetected | -| Doorbell | ProgrammableSwitchEvent | Name
Volume
Brightness | -| Fan | Active | Name
CurrentFanState
TargetFanState
RotationDirection
RotationSpeed
SwingMode
LockPhysicalControls | -| Faucet | Active | StatusFault
Name | -| FilterMaintenance | FilterChangeIndication | Name
FilterLifeLevel
ResetFilterIndication | -| GarageDoorOpener | CurrentDoorState
TargetDoorState
ObstructionDetected | LockCurrentState
LockTargetState
Name | -| HAPProtocolInformation | Version | *none* | -| HeaterCooler | Active
CurrentTemperature
CurrentHeaterCoolerState
TargetHeaterCoolerState | Name
RotationSpeed
TemperatureDisplayUnits
SwingMode
CoolingThresholdTemperature
HeatingThresholdTemperature
LockPhysicalControls | -| HumidifierDehumidifier | Active
CurrentRelativeHumidity
CurrentHumidifierDehumidifierState
TargetHumidifierDehumidifierState | Name
RelativeHumidityDehumidifierThreshold
RelativeHumidityHumidifierThreshold
RotationSpeed
SwingMode
WaterLevel
LockPhysicalControls | -| HumiditySensor | CurrentRelativeHumidity | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | -| IrrigationSystem | Active
ProgramMode
InUse | RemainingDuration
Name
StatusFault | -| LeakSensor | LeakDetected | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | -| LightBulb | On | Brightness
Hue
Name
Saturation
ColorTemperature | -| LightSensor | CurrentAmbientLightLevel | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | -| LockMechanism | LockCurrentState
LockTargetState | Name | -| Microphone | Mute | Name
Volume | -| MotionSensor | MotionDetected | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | -| OccupancySensor | OccupancyDetected | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | -| Outlet | On
OutletInUse | Name | -| SecuritySystem | SecuritySystemCurrentState
SecuritySystemTargetState | Name
SecuritySystemAlarmType
StatusFault
StatusTampered | -| ServiceLabel | ServiceLabelNamespace | *none* | -| Slat | CurrentSlatState
SlatType | Name
SwingMode
CurrentTiltAngle
TargetTiltAngle | -| SmokeSensor | SmokeDetected | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | -| Speaker | Mute | Name
Volume | -| StatelessProgrammableSwitch | ProgrammableSwitchEvent | Name
ServiceLabelIndex | -| Switch | On | Name | -| TemperatureSensor | CurrentTemperature | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | -| Thermostat | CurrentHeatingCoolingState
TargetHeatingCoolingState
CurrentTemperature
TargetTemperature
TemperatureDisplayUnits | CoolingThresholdTemperature
CurrentRelativeHumidity
HeatingThresholdTemperature
Name
TargetRelativeHumidity | -| Valve | Active
InUse
ValveType | SetDuration
RemainingDuration
IsConfigured
ServiceLabelIndex
StatusFault
Name | -| Window | CurrentPosition
TargetPosition
PositionState | Name
HoldPosition
ObstructionDetected | -| WindowCovering | CurrentPosition
TargetPosition | Name
PositionState
HoldPosition
CurrentHorizontalTiltAngle
TargetHorizontalTiltAngle
CurrentVerticalTiltAngle
TargetVerticalTiltAngle
ObstructionDetected | - - -### Characteristic Types and Defaults - -|Characteristic|Type|Default -|---|---|---| -|Active|uint8_t|0| -|AirQuality|uint8_t|0| -|BatteryLevel|uint8_t|0| -|Brightness|int|0| -|CarbonMonoxideLevel|double|0| -|CarbonMonoxidePeakLevel|double|0| -|CarbonMonoxideDetected|uint8_t|0| -|CarbonDioxideLevel|double|0| -|CarbonDioxidePeakLevel|double|0| -|CarbonDioxideDetected|uint8_t|0| -|ChargingState|uint8_t|0| -|CoolingThresholdTemperature|double|10| -|ColorTemperature|uint32_t|50| -|ContactSensorState|uint8_t|1| -|CurrentAmbientLightLevel|double|1| -|CurrentHorizontalTiltAngle|int|0| -|CurrentAirPurifierState|uint8_t|1| -|CurrentSlatState|uint8_t|0| -|CurrentPosition|uint8_t|0| -|CurrentVerticalTiltAngle|int|0| -|CurrentHumidifierDehumidifierState|uint8_t|1| -|CurrentDoorState|uint8_t|1| -|CurrentFanState|uint8_t|1| -|CurrentHeatingCoolingState|uint8_t|0| -|CurrentHeaterCoolerState|uint8_t|1| -|CurrentRelativeHumidity|double|0| -|CurrentTemperature|double|0| -|CurrentTiltAngle|int|0| -|FilterLifeLevel|double|0| -|FilterChangeIndication|uint8_t|0| -|FirmwareRevision|char \*|"1.0.0"| -|HardwareRevision|char \*|"1.0.0"| -|HeatingThresholdTemperature|double|16| -|HoldPosition|boolean|false| -|Hue|double|0| -|Identify|boolean|false| -|InUse|uint8_t|0| -|IsConfigured|uint8_t|0| -|LeakDetected|uint8_t|0| -|LockCurrentState|uint8_t|0| -|LockPhysicalControls|uint8_t|0| -|LockTargetState|uint8_t|0| -|Manufacturer|char \*|"HomeSpan"| -|Model|char \*|"HomeSpan-ESP32"| -|MotionDetected|boolean|false| -|Mute|boolean|false| -|Name|char \*|"unnamed"| -|NitrogenDioxideDensity|double|0| -|ObstructionDetected|boolean|false| -|PM25Density|double|0| -|OccupancyDetected|uint8_t|0| -|OutletInUse|boolean|false| -|On|boolean|false| -|OzoneDensity|double|0| -|PM10Density|double|0| -|PositionState|uint8_t|2| -|ProgramMode|uint8_t|0| -|ProgrammableSwitchEvent|uint8_t|0| -|RelativeHumidityDehumidifierThreshold|double|50| -|RelativeHumidityHumidifierThreshold|double|50| -|RemainingDuration|uint32_t|60| -|ResetFilterIndication|uint8_t|0| -|RotationDirection|int|0| -|RotationSpeed|double|0| -|Saturation|double|0| -|SecuritySystemAlarmType|uint8_t|0| -|SecuritySystemCurrentState|uint8_t|3| -|SecuritySystemTargetState|uint8_t|3| -|SerialNumber|char \*|"HS-12345"| -|ServiceLabelIndex|uint8_t|1| -|ServiceLabelNamespace|uint8_t|1| -|SlatType|uint8_t|0| -|SmokeDetected|uint8_t|0| -|StatusActive|boolean|true| -|StatusFault|uint8_t|0| -|StatusJammed|uint8_t|0| -|StatusLowBattery|uint8_t|0| -|StatusTampered|uint8_t|0| -|SulphurDioxideDensity|double|0| -|SwingMode|uint8_t|0| -|TargetAirPurifierState|uint8_t|1| -|TargetFanState|uint8_t|1| -|TargetTiltAngle|int|0| -|SetDuration|uint32_t|60| -|TargetHorizontalTiltAngle|int|0| -|TargetHumidifierDehumidifierState|uint8_t|0| -|TargetPosition|uint8_t|0| -|TargetDoorState|uint8_t|1| -|TargetHeaterCoolerState|uint8_t|0| -|TargetHeatingCoolingState|uint8_t|0| -|TargetRelativeHumidity|double|0| -|TargetTemperature|double|16| -|TemperatureDisplayUnits|uint8_t|0| -|TargetVerticalTiltAngle|int|0| -|ValveType|uint8_t|0| -|Version|char \*|"1.0.0"| -|VOCDensity|double|0| -|Volume|uint8_t|0| -|WaterLevel|double|0| +(under construction) --- From a53d98070c64a96d12e5ffd93e4e2c559ccb26f3 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 30 Oct 2021 22:06:02 -0500 Subject: [PATCH 40/64] Update version numbers to 1.4.1 --- library.properties | 2 +- src/Settings.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.properties b/library.properties index 7380088..274ff4b 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=HomeSpan -version=1.4.0 +version=1.4.1 author=Gregg maintainer=Gregg sentence=A robust and extremely easy-to-use HomeKit implementation for the Espressif ESP32 running on the Arduino IDE. diff --git a/src/Settings.h b/src/Settings.h index 51ec0d8..037c60b 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -36,7 +36,7 @@ #define HS_MAJOR 1 #define HS_MINOR 4 -#define HS_PATCH 0 +#define HS_PATCH 1 #define STRINGIFY(x) _STR(x) #define _STR(x) #x From a55a6972166a1098e869c74c1897c61422a07d3e Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 30 Oct 2021 23:01:02 -0500 Subject: [PATCH 41/64] Update TVServices.md --- docs/TVServices.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/docs/TVServices.md b/docs/TVServices.md index d2c0b90..7a61ef6 100644 --- a/docs/TVServices.md +++ b/docs/TVServices.md @@ -1,6 +1,32 @@ # Television Services and Characteristics -(under construction) +HomeSpan includes a number of undocumented Television Services and Characteristics that are not part of HAP-R2. Though the UUID and specifications for each Television Service and Characteristic have been identified by the broader HomeKit community, it is only by trial-and-error that proper usage can be determined. This page documents the results of experimenting with the different Television Services and Characteristics within HomeSpan using the Home App provided in iOS 15.1. This documentaiton should be considered reliable, but Apple can of course change the behavior of such Service and Characteristics within the Home App at any time and without notice. + +### `Category::Television` + +Use `Category::Television` as the category in `homeSpan.begin()` to specify a Television Accessory. This causes the Home App to display an TV icon in the Accessory Tile. However, this only seems to work for the first Accessory implemented. If you create a device with multiple Television Accessories, or place a Television Accessory behind a Bridge Accessory, the icon for the TV Tile converts to a generic HomeKit symbol. + +### `Service::Television()` + +Use `Service::Television()` as the Service to create a Television Accessory Tile. It support two primary Characteristics: + +* `Characteristic::Active()` - this is the standard HomeKit Active Characteristic used to turn the TV on or off. It is a required Characteristic +* `Characteristic::ConfiguredName()` - this is an optional, TV-specific version of `Characteristic::Name()` that seems to be the only way to set the default name of the TV. Unlike all other HomeKit Services, the Home App ignores any names specified with `Characeteristic::Name()` when used with `Service::Television()` + +Based on the above, the following code snippet defines a simple TV Accessory with a basic on/off switch: + +```C++ +new Service::Television(); + new Characteristic::Active(0); // set power to OFF at start-up + new Characteristic::ConfiguredName("My TV"); // optional Characteristic to set name of TV +``` + + + + + + + --- From fce9b85e2476e8ee8ab66b0502dd95ff2b9b3408 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 30 Oct 2021 23:23:24 -0500 Subject: [PATCH 42/64] Update TVServices.md --- docs/TVServices.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/TVServices.md b/docs/TVServices.md index 7a61ef6..614fd1b 100644 --- a/docs/TVServices.md +++ b/docs/TVServices.md @@ -11,6 +11,7 @@ Use `Category::Television` as the category in `homeSpan.begin()` to specify a Te Use `Service::Television()` as the Service to create a Television Accessory Tile. It support two primary Characteristics: * `Characteristic::Active()` - this is the standard HomeKit Active Characteristic used to turn the TV on or off. It is a required Characteristic + * `Characteristic::ConfiguredName()` - this is an optional, TV-specific version of `Characteristic::Name()` that seems to be the only way to set the default name of the TV. Unlike all other HomeKit Services, the Home App ignores any names specified with `Characeteristic::Name()` when used with `Service::Television()` Based on the above, the following code snippet defines a simple TV Accessory with a basic on/off switch: @@ -18,8 +19,15 @@ Based on the above, the following code snippet defines a simple TV Accessory wit ```C++ new Service::Television(); new Characteristic::Active(0); // set power to OFF at start-up - new Characteristic::ConfiguredName("My TV"); // optional Characteristic to set name of TV + new Characteristic::ConfiguredName("Sony TV"); // optional Characteristic to set name of TV ``` +More advanced control of a TV can enabled with two other optional Characteristics: + +* `Characteristic::RemoteKey()` - this write-only numerical Characteristic enables HomeSpan to read button presses from the Remote Control widget on an iPhone. This widget is normally used to control Apple TVs, but it seems any Television Accessory created per above can be selected from the widget. The layout of the widget (which cannot be modified) includes 4 arrows, a central select button, a play/pause button, a large "back" button, and an "info" button. When a key is pressed, the Home App will send an update to `Characteristic::Remote()` that can be read by HomeSpan using the usual `update()` method. Values are as follows: up=4, down=5, left=6, right=7, select=8, back=9, play/pause=11, and info=15 + +* `Characteristic::PowerModeSelection()` - this write-only Characteristic activates an option in the Home App under the Settings page for a TV Accessory named + + From 4644d254c92ba82f527cbeed6408abdb5b25e2a4 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 09:57:23 -0500 Subject: [PATCH 43/64] Update TVServices.md --- docs/TVServices.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/TVServices.md b/docs/TVServices.md index 614fd1b..f333a55 100644 --- a/docs/TVServices.md +++ b/docs/TVServices.md @@ -10,7 +10,7 @@ Use `Category::Television` as the category in `homeSpan.begin()` to specify a Te Use `Service::Television()` as the Service to create a Television Accessory Tile. It support two primary Characteristics: -* `Characteristic::Active()` - this is the standard HomeKit Active Characteristic used to turn the TV on or off. It is a required Characteristic +* `Characteristic::Active()` - this HAP-R2 standard Characteristic it used to turn the TV on or off. It is a required Characteristic * `Characteristic::ConfiguredName()` - this is an optional, TV-specific version of `Characteristic::Name()` that seems to be the only way to set the default name of the TV. Unlike all other HomeKit Services, the Home App ignores any names specified with `Characeteristic::Name()` when used with `Service::Television()` @@ -23,9 +23,18 @@ new Service::Television(); ``` More advanced control of a TV can enabled with two other optional Characteristics: -* `Characteristic::RemoteKey()` - this write-only numerical Characteristic enables HomeSpan to read button presses from the Remote Control widget on an iPhone. This widget is normally used to control Apple TVs, but it seems any Television Accessory created per above can be selected from the widget. The layout of the widget (which cannot be modified) includes 4 arrows, a central select button, a play/pause button, a large "back" button, and an "info" button. When a key is pressed, the Home App will send an update to `Characteristic::Remote()` that can be read by HomeSpan using the usual `update()` method. Values are as follows: up=4, down=5, left=6, right=7, select=8, back=9, play/pause=11, and info=15 - -* `Characteristic::PowerModeSelection()` - this write-only Characteristic activates an option in the Home App under the Settings page for a TV Accessory named +* `Characteristic::RemoteKey()` - this write-only numerical Characteristic enables HomeSpan to read button presses from the Remote Control widget on an iPhone that can be found under the Control Center. This widget is normally used to control Apple TVs, but it seems any Television Accessory created per above can also be operated from the Remote Control widget. The layout of the widget (which cannot be modified) includes 4 arrows, a central select button, a play/pause button, a large "back" button, and an "info" button. When a "key" is pressed, the Home App sends an update to `Characteristic::RemoteKey()` that can be read by HomeSpan using the usual `update()` method. Values are as follows: + + * 4 = up arrow + * 5 = down arrow + * 6 = left arrow + * 7 = right arrow + * 8 = center select button + * 9 = back button + * 11 = play/pause button + * 15 = info button + +* `Characteristic::PowerModeSelection()` - this write-only Characteristic causes the text "View TV Settings" to appear in the Home App under the Settings page for a TV Accessory. When this text is pressed, the Home App sends an update with value=0 to `Characteristic::PowerModeSelection()` that can be read by HomeSpan using the usual `update()` method From 6685768f5c91bfb661d59efa18549201a2d4f77c Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 31 Oct 2021 10:03:18 -0500 Subject: [PATCH 44/64] Update Television.ino Added `update()` logic for various key presses. --- Other Examples/Television/Television.ino | 81 ++++++++++++++++++++---- 1 file changed, 70 insertions(+), 11 deletions(-) diff --git a/Other Examples/Television/Television.ino b/Other Examples/Television/Television.ino index ba48415..e745dbc 100644 --- a/Other Examples/Television/Television.ino +++ b/Other Examples/Television/Television.ino @@ -50,17 +50,79 @@ // NOTE: This example is only designed to demonstrate how Television Services and Characteristics // appear in the Home App, and what they each control. To keep things simple, actions for the -// Characteristics have NOT been implemented in the code below. For example, the code below does not include +// Input Sources have NOT been implemented in the code below. For example, the code below does not include // any logic to update CurrentVisibilityState when the TargetVisibilityState checkboxes are clicked. #include "HomeSpan.h" +struct HomeSpanTV : Service::Television { + + SpanCharacteristic *active = new Characteristic::Active(0); // TV On/Off (set to Off at start-up) + SpanCharacteristic *activeID = new Characteristic::ActiveIdentifier(3); // Sets HDMI 3 on start-up + SpanCharacteristic *remoteKey = new Characteristic::RemoteKey(); // Used to receive button presses from the Remote Control widget + SpanCharacteristic *settingsKey = new Characteristic::PowerModeSelection(); // Adds "View TV Setting" option to Selection Screen + + HomeSpanTV(const char *name) : Service::Television() { + new Characteristic::ConfiguredName(name); // Name of TV + Serial.printf("Configured TV: %s\n",name); + } + + 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 HDMI-%d\n",activeID->getNewVal()); + } + + if(settingsKey->updated()){ + Serial.printf("Received request to \"View TV Settings\"\n"); + } + + if(remoteKey->updated()){ + Serial.printf("Remote Control key pressed: "); + switch(remoteKey->getNewVal()){ + case 4: + Serial.printf("UP ARROW\n"); + break; + case 5: + Serial.printf("DOWN ARROW\n"); + break; + case 6: + Serial.printf("LEFT ARROW\n"); + break; + case 7: + Serial.printf("RIGHT ARROW\n"); + break; + case 8: + Serial.printf("SELECT\n"); + break; + case 9: + Serial.printf("BACK\n"); + break; + case 11: + Serial.printf("PLAY/PAUSE\n"); + break; + case 15: + Serial.printf("INFO\n"); + break; + default: + Serial.print("UNKNOWN KEY\n"); + } + } + + return(true); + } +}; + +/////////////////////////////// + void setup() { Serial.begin(115200); - - homeSpan.setLogLevel(2); // allows you to see HAP transmissions that occur as various Television buttons are pressed in Home App - + homeSpan.begin(Category::Television,"HomeSpan Television"); new SpanAccessory(); @@ -137,7 +199,7 @@ void setup() { new Characteristic::CurrentVisibilityState(0); // ...and included in the Selection List... new Characteristic::TargetVisibilityState(0); // ...and a "checked" checkbox is provided on the Settings Screen that can be used to toggle CurrentVisibilityState() - (new Service::Television()) // Define a Television Service. Must link in InputSources! + (new HomeSpanTV("Test TV")) // Define a Television Service. Must link in InputSources! ->addLink(hdmi1) ->addLink(hdmi2) ->addLink(hdmi3) @@ -149,14 +211,11 @@ void setup() { ->addLink(hdmi9) ->addLink(hdmi10) ; - new Characteristic::Active(0); // TV On/Off (set to Off at start-up) - new Characteristic::ConfiguredName("Test TV"); // Name of TV - new Characteristic::ActiveIdentifier(3); // Sets HDMI 3 on start-up - new Characteristic::RemoteKey(); // Used to receive button presses from the Remote Control widget - new Characteristic::PowerModeSelection(); // Adds "" option to Selection Screen - + } +/////////////////////////////// + void loop() { homeSpan.poll(); } From 46aab531c8cb6504db71c5531a19d5b288afb4c2 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 10:21:28 -0500 Subject: [PATCH 45/64] Update TVServices.md --- docs/TVServices.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/TVServices.md b/docs/TVServices.md index f333a55..2e6ddd4 100644 --- a/docs/TVServices.md +++ b/docs/TVServices.md @@ -36,8 +36,11 @@ More advanced control of a TV can enabled with two other optional Characteristic * `Characteristic::PowerModeSelection()` - this write-only Characteristic causes the text "View TV Settings" to appear in the Home App under the Settings page for a TV Accessory. When this text is pressed, the Home App sends an update with value=0 to `Characteristic::PowerModeSelection()` that can be read by HomeSpan using the usual `update()` method +* `Characteristic::ActiveIdentifier()` - this numerical Characteristic is used to control the input source for the TV (e.g. HDMI-1, HDMI-2, Netflix, etc.). It is only used when input sources are defined and linked using `Service::InputSource()` (see below), in which case it is a *required* Characteristic +### `Service::InputSource()` +Use `Service::InputSource()` to create a new input source selection for the TV, such as HDMI-1, HDMI-2, Netflix, etc. The use of `Service::InputSource()` is optional - it is perfectly okay to create a Television Service without the ability to select different Input Sources. However, if used, each Input Source Service added should be defined in the *same* Accessory as the Television Service to which it applies, and ***must*** be linked to that Television Service using `addLink()`. The Home App behaves unexpectedly if it finds any Input Source Services that are not linked to a Television Service. `Service::InputSource()` support the following Characteristics: From 336ec1854562e303f07bcf641ebee3ee9c51a9d3 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 15:21:37 -0500 Subject: [PATCH 46/64] Update TVServices.md --- docs/TVServices.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/TVServices.md b/docs/TVServices.md index 2e6ddd4..6c93167 100644 --- a/docs/TVServices.md +++ b/docs/TVServices.md @@ -40,8 +40,21 @@ More advanced control of a TV can enabled with two other optional Characteristic ### `Service::InputSource()` -Use `Service::InputSource()` to create a new input source selection for the TV, such as HDMI-1, HDMI-2, Netflix, etc. The use of `Service::InputSource()` is optional - it is perfectly okay to create a Television Service without the ability to select different Input Sources. However, if used, each Input Source Service added should be defined in the *same* Accessory as the Television Service to which it applies, and ***must*** be linked to that Television Service using `addLink()`. The Home App behaves unexpectedly if it finds any Input Source Services that are not linked to a Television Service. `Service::InputSource()` support the following Characteristics: +Use `Service::InputSource()` to create a new input source selection for the TV, such as HDMI-1, HDMI-2, Netflix, etc. The use of `Service::InputSource()` is optional - it is perfectly okay to create a Television Service without the ability to select different Input Sources. However, if used, each Input Source Service added should be defined in the *same* Accessory as the Television Service to which it applies, and ***must*** be linked to that Television Service using `addLink()`. The Home App behaves unexpectedly if it finds any Input Source Services that are not linked to a Television Service. +Input Sources can appear in two places within the Home App. The first is in the Input Source "Selector" that appears below the On/Off power button when you open the controls for a TV (i.e. long-press the Accessory Tile). This is how you change the Input Source for thge TV. The second place Input Sources appear is on the Settings page for a TV Accessory. This list is used to (a) change the name of each source, and (b) toggle the source so that it either appears or does not appear in the Input Source Selector. + +The idea is that your sketch should implement a TV Accessory containing a full list of all potential inputs, using names that match the labels on the TV, such as "HDMI 1", "Component 1", "HDMI 2", etc. If your TV Remote has dedicated buttons for Netflix, HBO Max, Amazon Prime, etc. you can add these to the list as well. Once this generic list is created, you can then rename and enable these source directly from within the Home App. For example you might rename "HDMI 1" to "Comcast Cable", and "HDMI 2" to "Sony Blue-Ray". If you have nothing connected to the "Component 1" input source your can exclude it from the Input Source Selector. This makes it easy to configure and re-configure your TV Input Sources without ever having to change or update your HomeSpan sketch. + +All of this is accomplished by using a combination of some, or all, of the following Characteristics that are supported by `Service::InputSource()`: + +* `Characteristic::ConfiguredName()` - similar to how its used when applied to `Service::Television`, this Characteristic allows you set the default name for an Input Source. Note that if you change the name of an Input Source in the Home App, an update will be sent to HomeSpan with the new name for you to use if needed. This is very different from the usual `Characteristic::Name()` used for many other Services, and for which name changes performed in the Home App are never communicated back to the Accessory. + +* `Characteristic::Identifier()` - this numerical Characteristic sets an ID for each Input Source. Any unsigned 32-bit number can be used as an ID, provided it is *unique* and not used by other Input Sources in the same TV Service. When the use the Input Source Selector in the Home App, the ActiveIdentifier Characteristic from the Television Service (see above) will be updated to match the ID corresponding to the Input Source. Within HomeSpan you can use the `update()` method to determine when `Characteristic::ActiveIdentifer()` is updated, and which Input Source is being requested. Note that setting an Identifier for an Input Source is technically optional. However, if it not set, the Home App will not allow it to be displayed in the Input Source Selector, which defeats the purpose of creating an Input Source + +* `Characteristic::IsConfigured()` - this Characteristic determines where an Input Source appears in the Home App. Allowed values are 0 and 1. IsConfigured() is optional - if not defined, the Input Source will appear in the Input Source Selector (provided it has an Identifier), but *not* in the list of Input Sources found on the Setting page. This means you will not be able to rename the Input Source from the Home App, nor turn off its visibility in the Input Selector. If IsConfigured() is defined and set to 1, the Input Source appears in both the Input Source Selector List as well as the list of Input Sources on the Setting page. If instead IsConfigured is defined but set to 0, the Input Source will appear in the Settings page, but it will be excluded from the Input Source Selector under all circumstances + +* `Characteristic::CurrentVisibilityState()` and `Characteristic::TargetVisibilityState()` - these two Characteristics work in tandem much like any current-state/target-state pair. When these are defined for an Input Source, a checkbox toggle appears next to the name of the Input Source on the Setting page (proivided IsConfigured() has also been defined). Clicking the checkbox causes the Home App to toggle the TargetVisibilityState between 0 to 1, where 0 means the checkbox is *checked*, and 1 means it is *unchecked* (the reverse of what you might expect). If you read this update in HomeSpan you can then use `setVal()` to change the CurrentVisibiltyState() to match the TargetVisibilityState(). Setting CurrentVisibilityState() to 0 means the Input Source appears in the Input Source Selector. Setting CurrentVisibilityState() to 1 means it does not appear as a selection. Note these features only operate if an ID has been set for the Input Source with `Characteristic::Identifier()`, and IsConfigures is set to 1. From 9b8f4d2b26530299dd58d775f1d3462fb5b4fc2e Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 15:32:14 -0500 Subject: [PATCH 47/64] Update TVServices.md --- docs/TVServices.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/TVServices.md b/docs/TVServices.md index 6c93167..5cef779 100644 --- a/docs/TVServices.md +++ b/docs/TVServices.md @@ -42,11 +42,11 @@ More advanced control of a TV can enabled with two other optional Characteristic Use `Service::InputSource()` to create a new input source selection for the TV, such as HDMI-1, HDMI-2, Netflix, etc. The use of `Service::InputSource()` is optional - it is perfectly okay to create a Television Service without the ability to select different Input Sources. However, if used, each Input Source Service added should be defined in the *same* Accessory as the Television Service to which it applies, and ***must*** be linked to that Television Service using `addLink()`. The Home App behaves unexpectedly if it finds any Input Source Services that are not linked to a Television Service. -Input Sources can appear in two places within the Home App. The first is in the Input Source "Selector" that appears below the On/Off power button when you open the controls for a TV (i.e. long-press the Accessory Tile). This is how you change the Input Source for thge TV. The second place Input Sources appear is on the Settings page for a TV Accessory. This list is used to (a) change the name of each source, and (b) toggle the source so that it either appears or does not appear in the Input Source Selector. +Input Sources can appear in two places within the Home App. The first is in the Input Source "Selector" that is shown below the On/Off power button when you open the controls for a TV (i.e. long-press the Accessory Tile). This is how you change the Input Source for the TV. The second place that Input Sources appear is on the Settings page for a TV Accessory. This is where you can change the name of an Input Source, as well as configure whether to include or exclude a particular Input Source from the Input Source Selector. -The idea is that your sketch should implement a TV Accessory containing a full list of all potential inputs, using names that match the labels on the TV, such as "HDMI 1", "Component 1", "HDMI 2", etc. If your TV Remote has dedicated buttons for Netflix, HBO Max, Amazon Prime, etc. you can add these to the list as well. Once this generic list is created, you can then rename and enable these source directly from within the Home App. For example you might rename "HDMI 1" to "Comcast Cable", and "HDMI 2" to "Sony Blue-Ray". If you have nothing connected to the "Component 1" input source your can exclude it from the Input Source Selector. This makes it easy to configure and re-configure your TV Input Sources without ever having to change or update your HomeSpan sketch. +The overall idea is that your sketch should implement a TV Accessory containing a full list of all potential inputs, using names that match the labels on the TV, such as "HDMI 1", "Component 1", "HDMI 2", etc. If your TV Remote has dedicated buttons for Netflix, HBO Max, Amazon Prime, etc. you can add these to the list as well. Once this generic list is created, you can then rename and enable each Input Source directly from within the Home App. For example you might rename "HDMI 1" to "Comcast Cable", and "HDMI 2" to "Sony Blue-Ray". If you have nothing connected to the "Component 1", you can exclude it from the Input Source Selector. This makes it easy to configure and re-configure your TV Input Sources without ever having to change or update your HomeSpan sketch. -All of this is accomplished by using a combination of some, or all, of the following Characteristics that are supported by `Service::InputSource()`: +All of this is accomplished by using a combination of some, or all, of the following Characteristics: * `Characteristic::ConfiguredName()` - similar to how its used when applied to `Service::Television`, this Characteristic allows you set the default name for an Input Source. Note that if you change the name of an Input Source in the Home App, an update will be sent to HomeSpan with the new name for you to use if needed. This is very different from the usual `Characteristic::Name()` used for many other Services, and for which name changes performed in the Home App are never communicated back to the Accessory. From f4b788839fdf1e291399a31c9bf2a04c9762afa9 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 15:43:16 -0500 Subject: [PATCH 48/64] Update TVServices.md --- docs/TVServices.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/TVServices.md b/docs/TVServices.md index 5cef779..8efd50c 100644 --- a/docs/TVServices.md +++ b/docs/TVServices.md @@ -48,9 +48,9 @@ The overall idea is that your sketch should implement a TV Accessory containing All of this is accomplished by using a combination of some, or all, of the following Characteristics: -* `Characteristic::ConfiguredName()` - similar to how its used when applied to `Service::Television`, this Characteristic allows you set the default name for an Input Source. Note that if you change the name of an Input Source in the Home App, an update will be sent to HomeSpan with the new name for you to use if needed. This is very different from the usual `Characteristic::Name()` used for many other Services, and for which name changes performed in the Home App are never communicated back to the Accessory. +* `Characteristic::ConfiguredName()` - similar to how its used when applied to `Service::Television()`, this Characteristic allows you set the default name for an Input Source. Note that if you change the name of an Input Source in the Home App, an update will be sent to HomeSpan with the new name for you to use in your sketch if needed. This is very different from the usual `Characteristic::Name()` used for many other Services, and for which name changes performed in the Home App are never communicated back to the Accessory -* `Characteristic::Identifier()` - this numerical Characteristic sets an ID for each Input Source. Any unsigned 32-bit number can be used as an ID, provided it is *unique* and not used by other Input Sources in the same TV Service. When the use the Input Source Selector in the Home App, the ActiveIdentifier Characteristic from the Television Service (see above) will be updated to match the ID corresponding to the Input Source. Within HomeSpan you can use the `update()` method to determine when `Characteristic::ActiveIdentifer()` is updated, and which Input Source is being requested. Note that setting an Identifier for an Input Source is technically optional. However, if it not set, the Home App will not allow it to be displayed in the Input Source Selector, which defeats the purpose of creating an Input Source +* `Characteristic::Identifier()` - this numerical Characteristic sets an ID for each Input Source. Any unsigned 32-bit number can be used as an ID, provided it is *unique* and not used by any other Input Source in the same TV Service. When you use the Input Source Selector in the Home App to choose a particular Input Soure, the `Characteristic::ActiveIdentifier()` from the Television Service (see above) will be updated with a value that matches the ID corresponding to the chosen Input Source. Within HomeSpan you simply use the `update()` method to determine when `Characteristic::ActiveIdentifer()` is updated, and, based on its value, which Input Source was chosen. HomeKit does not seem to require `Characteristic::Identifier()` be defined for an Input Source. However, if it not set, the Home App will not allow it to be displayed as a choice in the Input Source Selector, which defeats the purpose of creating an Input Source! * `Characteristic::IsConfigured()` - this Characteristic determines where an Input Source appears in the Home App. Allowed values are 0 and 1. IsConfigured() is optional - if not defined, the Input Source will appear in the Input Source Selector (provided it has an Identifier), but *not* in the list of Input Sources found on the Setting page. This means you will not be able to rename the Input Source from the Home App, nor turn off its visibility in the Input Selector. If IsConfigured() is defined and set to 1, the Input Source appears in both the Input Source Selector List as well as the list of Input Sources on the Setting page. If instead IsConfigured is defined but set to 0, the Input Source will appear in the Settings page, but it will be excluded from the Input Source Selector under all circumstances From cd9da777a5fb2db0d4abf02c497ba8b7621b1344 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 15:50:27 -0500 Subject: [PATCH 49/64] Update TVServices.md --- docs/TVServices.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/TVServices.md b/docs/TVServices.md index 8efd50c..0679c7b 100644 --- a/docs/TVServices.md +++ b/docs/TVServices.md @@ -52,7 +52,7 @@ All of this is accomplished by using a combination of some, or all, of the follo * `Characteristic::Identifier()` - this numerical Characteristic sets an ID for each Input Source. Any unsigned 32-bit number can be used as an ID, provided it is *unique* and not used by any other Input Source in the same TV Service. When you use the Input Source Selector in the Home App to choose a particular Input Soure, the `Characteristic::ActiveIdentifier()` from the Television Service (see above) will be updated with a value that matches the ID corresponding to the chosen Input Source. Within HomeSpan you simply use the `update()` method to determine when `Characteristic::ActiveIdentifer()` is updated, and, based on its value, which Input Source was chosen. HomeKit does not seem to require `Characteristic::Identifier()` be defined for an Input Source. However, if it not set, the Home App will not allow it to be displayed as a choice in the Input Source Selector, which defeats the purpose of creating an Input Source! -* `Characteristic::IsConfigured()` - this Characteristic determines where an Input Source appears in the Home App. Allowed values are 0 and 1. IsConfigured() is optional - if not defined, the Input Source will appear in the Input Source Selector (provided it has an Identifier), but *not* in the list of Input Sources found on the Setting page. This means you will not be able to rename the Input Source from the Home App, nor turn off its visibility in the Input Selector. If IsConfigured() is defined and set to 1, the Input Source appears in both the Input Source Selector List as well as the list of Input Sources on the Setting page. If instead IsConfigured is defined but set to 0, the Input Source will appear in the Settings page, but it will be excluded from the Input Source Selector under all circumstances +* `Characteristic::IsConfigured()` - this Characteristic determines whether an Input Source is allowed to appear as a choice in the Input Source Selector of the Home App. If IsConfigured() is defined and set to 0, the Input Source will appear in the Settings page, but it will be excluded as a choice from the Input Source Selector. If IsConfigured() is defined and set to 1, he Input Source will appear in the Settings page, and will be included as a choice in the Input Source Selector. If `Characteristic::IsConfigured()` is not defined at all for an Input Source, that source will appear as a choice in the Input Source Selector, but will *not* appear in the list of Input Sources found on the Settings page. This means you will not be able to rename the Input Source from the Home App, nor toggle it as an allowable choice in the Input Selector (see below) * `Characteristic::CurrentVisibilityState()` and `Characteristic::TargetVisibilityState()` - these two Characteristics work in tandem much like any current-state/target-state pair. When these are defined for an Input Source, a checkbox toggle appears next to the name of the Input Source on the Setting page (proivided IsConfigured() has also been defined). Clicking the checkbox causes the Home App to toggle the TargetVisibilityState between 0 to 1, where 0 means the checkbox is *checked*, and 1 means it is *unchecked* (the reverse of what you might expect). If you read this update in HomeSpan you can then use `setVal()` to change the CurrentVisibiltyState() to match the TargetVisibilityState(). Setting CurrentVisibilityState() to 0 means the Input Source appears in the Input Source Selector. Setting CurrentVisibilityState() to 1 means it does not appear as a selection. Note these features only operate if an ID has been set for the Input Source with `Characteristic::Identifier()`, and IsConfigures is set to 1. From 9afb9f2cf549160623393c31e4c2ef6e46a99d65 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 15:52:58 -0500 Subject: [PATCH 50/64] Update TVServices.md --- docs/TVServices.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/TVServices.md b/docs/TVServices.md index 0679c7b..e4c4c77 100644 --- a/docs/TVServices.md +++ b/docs/TVServices.md @@ -52,7 +52,7 @@ All of this is accomplished by using a combination of some, or all, of the follo * `Characteristic::Identifier()` - this numerical Characteristic sets an ID for each Input Source. Any unsigned 32-bit number can be used as an ID, provided it is *unique* and not used by any other Input Source in the same TV Service. When you use the Input Source Selector in the Home App to choose a particular Input Soure, the `Characteristic::ActiveIdentifier()` from the Television Service (see above) will be updated with a value that matches the ID corresponding to the chosen Input Source. Within HomeSpan you simply use the `update()` method to determine when `Characteristic::ActiveIdentifer()` is updated, and, based on its value, which Input Source was chosen. HomeKit does not seem to require `Characteristic::Identifier()` be defined for an Input Source. However, if it not set, the Home App will not allow it to be displayed as a choice in the Input Source Selector, which defeats the purpose of creating an Input Source! -* `Characteristic::IsConfigured()` - this Characteristic determines whether an Input Source is allowed to appear as a choice in the Input Source Selector of the Home App. If IsConfigured() is defined and set to 0, the Input Source will appear in the Settings page, but it will be excluded as a choice from the Input Source Selector. If IsConfigured() is defined and set to 1, he Input Source will appear in the Settings page, and will be included as a choice in the Input Source Selector. If `Characteristic::IsConfigured()` is not defined at all for an Input Source, that source will appear as a choice in the Input Source Selector, but will *not* appear in the list of Input Sources found on the Settings page. This means you will not be able to rename the Input Source from the Home App, nor toggle it as an allowable choice in the Input Selector (see below) +* `Characteristic::IsConfigured()` - this Characteristic determines whether an Input Source is allowed to appear as a choice in the Input Source Selector of the Home App. If IsConfigured() is defined and set to 0, the Input Source will appear in the Settings page, but it will be excluded as a choice from the Input Source Selector. If IsConfigured() is defined and set to 1, the Input Source will appear in the Settings page, and will also be included as a choice in the Input Source Selector. If `Characteristic::IsConfigured()` is not defined for an Input Source, that source will still appear as a choice in the Input Source Selector, but it will *not* appear in the list of Input Sources found on the Settings page. This means you will not be able to rename the Input Source from the Home App, nor toggle it as an allowable choice in the Input Selector (see below) * `Characteristic::CurrentVisibilityState()` and `Characteristic::TargetVisibilityState()` - these two Characteristics work in tandem much like any current-state/target-state pair. When these are defined for an Input Source, a checkbox toggle appears next to the name of the Input Source on the Setting page (proivided IsConfigured() has also been defined). Clicking the checkbox causes the Home App to toggle the TargetVisibilityState between 0 to 1, where 0 means the checkbox is *checked*, and 1 means it is *unchecked* (the reverse of what you might expect). If you read this update in HomeSpan you can then use `setVal()` to change the CurrentVisibiltyState() to match the TargetVisibilityState(). Setting CurrentVisibilityState() to 0 means the Input Source appears in the Input Source Selector. Setting CurrentVisibilityState() to 1 means it does not appear as a selection. Note these features only operate if an ID has been set for the Input Source with `Characteristic::Identifier()`, and IsConfigures is set to 1. From f459635c753ae3d19bacb06adef612fab220ab9e Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 15:57:38 -0500 Subject: [PATCH 51/64] Update TVServices.md --- docs/TVServices.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/TVServices.md b/docs/TVServices.md index e4c4c77..753de9f 100644 --- a/docs/TVServices.md +++ b/docs/TVServices.md @@ -54,7 +54,7 @@ All of this is accomplished by using a combination of some, or all, of the follo * `Characteristic::IsConfigured()` - this Characteristic determines whether an Input Source is allowed to appear as a choice in the Input Source Selector of the Home App. If IsConfigured() is defined and set to 0, the Input Source will appear in the Settings page, but it will be excluded as a choice from the Input Source Selector. If IsConfigured() is defined and set to 1, the Input Source will appear in the Settings page, and will also be included as a choice in the Input Source Selector. If `Characteristic::IsConfigured()` is not defined for an Input Source, that source will still appear as a choice in the Input Source Selector, but it will *not* appear in the list of Input Sources found on the Settings page. This means you will not be able to rename the Input Source from the Home App, nor toggle it as an allowable choice in the Input Selector (see below) -* `Characteristic::CurrentVisibilityState()` and `Characteristic::TargetVisibilityState()` - these two Characteristics work in tandem much like any current-state/target-state pair. When these are defined for an Input Source, a checkbox toggle appears next to the name of the Input Source on the Setting page (proivided IsConfigured() has also been defined). Clicking the checkbox causes the Home App to toggle the TargetVisibilityState between 0 to 1, where 0 means the checkbox is *checked*, and 1 means it is *unchecked* (the reverse of what you might expect). If you read this update in HomeSpan you can then use `setVal()` to change the CurrentVisibiltyState() to match the TargetVisibilityState(). Setting CurrentVisibilityState() to 0 means the Input Source appears in the Input Source Selector. Setting CurrentVisibilityState() to 1 means it does not appear as a selection. Note these features only operate if an ID has been set for the Input Source with `Characteristic::Identifier()`, and IsConfigures is set to 1. +* `Characteristic::CurrentVisibilityState()` and `Characteristic::TargetVisibilityState()` - these two Characteristics work in tandem much like any current-state/target-state pair. When these are defined for an Input Source, a checkbox toggle appears next to the name of the Input Source on the Settings page, provided `Characteristic::IsConfigured()` has also been defined. Clicking the checkbox causes the Home App to toggle the TargetVisibilityState between 0 to 1, where 0 ironically means the checkbox is *checked*, and 1 means it is *unchecked* (the reverse of what you might expect!). If you read this update in HomeSpan you can then use `setVal()` to change the CurrentVisibiltyState() to match the TargetVisibilityState(). Setting CurrentVisibilityState() to 0 means the Input Source appears as a choice in the Input Source Selector. Setting CurrentVisibilityState() to 1 means it does not appear as a selection. Note these features only operate if an ID has been set for the Input Source with `Characteristic::Identifier()`, and IsConfigured() has been defined and set to 1. From 5ad030d4718221ecba67bbfbd06999e62f29c5c1 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 16:07:59 -0500 Subject: [PATCH 52/64] Update Extras.md --- docs/Extras.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Extras.md b/docs/Extras.md index 477a1e9..7ffe1ff 100644 --- a/docs/Extras.md +++ b/docs/Extras.md @@ -160,7 +160,7 @@ rf.start(pulseTrain,3,4,1000); // start transmission using the same parameters ### Example RFControl Sketch -Below is a complete sketch that produces two different pulse trains with the signal output linked to the ESP32 device's built-in LED (rather than an RF or IR transmitter). For illustrative purposes the tick duration has been set to a very long 100𝛍s, and pulse times range from of 1000-10,000 ticks, so that the individual pulses are easily discernable on the LED. Note this example sketch is also available in the Arduino IDE under [*File β†’ Examples β†’ HomeSpan β†’ Other Examples β†’ RemoteControl*](https://github.com/HomeSpan/HomeSpan/tree/dev/Other%20Examples/RemoteControl). +Below is a complete sketch that produces two different pulse trains with the signal output linked to the ESP32 device's built-in LED (rather than an RF or IR transmitter). For illustrative purposes the tick duration has been set to a very long 100𝛍s, and pulse times range from of 1000-10,000 ticks, so that the individual pulses are easily discernable on the LED. Note this example sketch is also available in the Arduino IDE under [*File β†’ Examples β†’ HomeSpan β†’ Other Examples β†’ RemoteControl*](https://github.com/HomeSpan/HomeSpan/tree/master/Other%20Examples/RemoteControl). ```C++ /* HomeSpan Remote Control Example */ From f06bb4869a0cd03ae06058f457112122351e55cf Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 16:10:40 -0500 Subject: [PATCH 53/64] Update Extras.md --- docs/Extras.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Extras.md b/docs/Extras.md index 7ffe1ff..9b0cb7d 100644 --- a/docs/Extras.md +++ b/docs/Extras.md @@ -160,7 +160,7 @@ rf.start(pulseTrain,3,4,1000); // start transmission using the same parameters ### Example RFControl Sketch -Below is a complete sketch that produces two different pulse trains with the signal output linked to the ESP32 device's built-in LED (rather than an RF or IR transmitter). For illustrative purposes the tick duration has been set to a very long 100𝛍s, and pulse times range from of 1000-10,000 ticks, so that the individual pulses are easily discernable on the LED. Note this example sketch is also available in the Arduino IDE under [*File β†’ Examples β†’ HomeSpan β†’ Other Examples β†’ RemoteControl*](https://github.com/HomeSpan/HomeSpan/tree/master/Other%20Examples/RemoteControl). +Below is a complete sketch that produces two different pulse trains with the signal output linked to the ESP32 device's built-in LED (rather than an RF or IR transmitter). For illustrative purposes the tick duration has been set to a very long 100𝛍s, and pulse times range from of 1000-10,000 ticks, so that the individual pulses are easily discernable on the LED. Note this example sketch is also available in the Arduino IDE under [*File β†’ Examples β†’ HomeSpan β†’ Other Examples β†’ RemoteControl*](../Other%20Examples/RemoteControl). ```C++ /* HomeSpan Remote Control Example */ From 5b7a38f829fe0293577187da1815d9ff6cbf1a37 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 16:15:10 -0500 Subject: [PATCH 54/64] Update TVServices.md --- docs/TVServices.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/TVServices.md b/docs/TVServices.md index 753de9f..d2741a5 100644 --- a/docs/TVServices.md +++ b/docs/TVServices.md @@ -56,7 +56,9 @@ All of this is accomplished by using a combination of some, or all, of the follo * `Characteristic::CurrentVisibilityState()` and `Characteristic::TargetVisibilityState()` - these two Characteristics work in tandem much like any current-state/target-state pair. When these are defined for an Input Source, a checkbox toggle appears next to the name of the Input Source on the Settings page, provided `Characteristic::IsConfigured()` has also been defined. Clicking the checkbox causes the Home App to toggle the TargetVisibilityState between 0 to 1, where 0 ironically means the checkbox is *checked*, and 1 means it is *unchecked* (the reverse of what you might expect!). If you read this update in HomeSpan you can then use `setVal()` to change the CurrentVisibiltyState() to match the TargetVisibilityState(). Setting CurrentVisibilityState() to 0 means the Input Source appears as a choice in the Input Source Selector. Setting CurrentVisibilityState() to 1 means it does not appear as a selection. Note these features only operate if an ID has been set for the Input Source with `Characteristic::Identifier()`, and IsConfigured() has been defined and set to 1. +### Examples +Please see [*File β†’ Examples β†’ HomeSpan β†’ Other Examples β†’ Television*](../Other%20Examples/Television) for a complete worked example demonstrating the effects of using different combinations of the above Characteristics From 07060812b50e99e8397f1cc901396b3332394958 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 16:21:43 -0500 Subject: [PATCH 55/64] Update TVServices.md --- docs/TVServices.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/TVServices.md b/docs/TVServices.md index d2741a5..fce8fb7 100644 --- a/docs/TVServices.md +++ b/docs/TVServices.md @@ -58,7 +58,9 @@ All of this is accomplished by using a combination of some, or all, of the follo ### Examples -Please see [*File β†’ Examples β†’ HomeSpan β†’ Other Examples β†’ Television*](../Other%20Examples/Television) for a complete worked example demonstrating the effects of using different combinations of the above Characteristics +Please see [*File β†’ Examples β†’ HomeSpan β†’ Other Examples β†’ Television*](../Other%20Examples/Television) for a complete worked example demonstrating the effects of using different combinations of the above Characteristics. Also, see the [HomeSpan Projects](https://github.com/topics/homespan) page for some real-world examples of Sony and Samsung TV sketches that use RFControl and an Infrared LED to control TV Power and Input Source from the Home App. + + From 31f9ec38b3f5a40c469e6bbeeaa190ca0ef436fd Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 16:35:01 -0500 Subject: [PATCH 56/64] Update TVServices.md --- docs/TVServices.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/TVServices.md b/docs/TVServices.md index fce8fb7..8e37c04 100644 --- a/docs/TVServices.md +++ b/docs/TVServices.md @@ -54,13 +54,17 @@ All of this is accomplished by using a combination of some, or all, of the follo * `Characteristic::IsConfigured()` - this Characteristic determines whether an Input Source is allowed to appear as a choice in the Input Source Selector of the Home App. If IsConfigured() is defined and set to 0, the Input Source will appear in the Settings page, but it will be excluded as a choice from the Input Source Selector. If IsConfigured() is defined and set to 1, the Input Source will appear in the Settings page, and will also be included as a choice in the Input Source Selector. If `Characteristic::IsConfigured()` is not defined for an Input Source, that source will still appear as a choice in the Input Source Selector, but it will *not* appear in the list of Input Sources found on the Settings page. This means you will not be able to rename the Input Source from the Home App, nor toggle it as an allowable choice in the Input Selector (see below) -* `Characteristic::CurrentVisibilityState()` and `Characteristic::TargetVisibilityState()` - these two Characteristics work in tandem much like any current-state/target-state pair. When these are defined for an Input Source, a checkbox toggle appears next to the name of the Input Source on the Settings page, provided `Characteristic::IsConfigured()` has also been defined. Clicking the checkbox causes the Home App to toggle the TargetVisibilityState between 0 to 1, where 0 ironically means the checkbox is *checked*, and 1 means it is *unchecked* (the reverse of what you might expect!). If you read this update in HomeSpan you can then use `setVal()` to change the CurrentVisibiltyState() to match the TargetVisibilityState(). Setting CurrentVisibilityState() to 0 means the Input Source appears as a choice in the Input Source Selector. Setting CurrentVisibilityState() to 1 means it does not appear as a selection. Note these features only operate if an ID has been set for the Input Source with `Characteristic::Identifier()`, and IsConfigured() has been defined and set to 1. +* `Characteristic::CurrentVisibilityState()` and `Characteristic::TargetVisibilityState()` - these two Characteristics work in tandem much like any current-state/target-state pair. When these are defined for an Input Source, a checkbox toggle appears next to the name of the Input Source on the Settings page, provided `Characteristic::IsConfigured()` has also been defined. Clicking the checkbox causes the Home App to toggle the TargetVisibilityState between 0 to 1, where 0 ironically means the checkbox is *checked*, and 1 means it is *unchecked* (the reverse of what you might expect!). If you read this update in HomeSpan you can then use `setVal()` to change the CurrentVisibiltyState() to match the TargetVisibilityState(). Setting CurrentVisibilityState() to 0 means the Input Source appears as a choice in the Input Source Selector. Setting CurrentVisibilityState() to 1 means it does not appear as a selection. Note these features only operate if an ID has been set for the Input Source with `Characteristic::Identifier()`, and IsConfigured() has been defined and set to 1 ### Examples -Please see [*File β†’ Examples β†’ HomeSpan β†’ Other Examples β†’ Television*](../Other%20Examples/Television) for a complete worked example demonstrating the effects of using different combinations of the above Characteristics. Also, see the [HomeSpan Projects](https://github.com/topics/homespan) page for some real-world examples of Sony and Samsung TV sketches that use RFControl and an Infrared LED to control TV Power and Input Source from the Home App. +Please see [*File β†’ Examples β†’ HomeSpan β†’ Other Examples β†’ Television*](../Other%20Examples/Television) for a complete worked example demonstrating the effects of using different combinations of the above Characteristics. Also, don't forget to check out the [HomeSpan Projects](https://github.com/topics/homespan) page for some real-world examples of TV sketches and controllers. +### Credits + +Much thanks to @unreality for the PR to include Television codes and associated functionality! + From f666f54b80cd8d34ba5751f17915722dc447e8b2 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 16:41:17 -0500 Subject: [PATCH 57/64] Update Categories.md --- docs/Categories.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Categories.md b/docs/Categories.md index af9ad96..ade5fcc 100644 --- a/docs/Categories.md +++ b/docs/Categories.md @@ -35,6 +35,7 @@ The table below provides a list of all HomeSpan Categories. * VideoDoorbells * Windows * WindowCoverings +* Television Note that the HomeKit primarily uses the Accessory Category of a device for determining the icon to show in the Home App when a device is being paired. Apart from this purely cosmetic function, the Category assigned to a device does not in any way limit which Services or Characteristics can be implemented on that device. From 7cd7aa28a08e05ab4b80aff7065035474ba02d51 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 16:46:23 -0500 Subject: [PATCH 58/64] Update ServiceList.md --- docs/ServiceList.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/ServiceList.md b/docs/ServiceList.md index 57949ef..dc470fd 100644 --- a/docs/ServiceList.md +++ b/docs/ServiceList.md @@ -56,6 +56,7 @@ Additionally, when first starting up, HomeSpan begins by validating the device's | Speaker | Mute | Name
Volume | | StatelessProgrammableSwitch | ProgrammableSwitchEvent | Name
ServiceLabelIndex | | Switch | On | Name | +| Television | Active | ConfiguredName
ActiveIdentifier
RemoteKey
PowerModeSelection | | TemperatureSensor | CurrentTemperature | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | | Thermostat | CurrentHeatingCoolingState
TargetHeatingCoolingState
CurrentTemperature
TargetTemperature
TemperatureDisplayUnits | CoolingThresholdTemperature
CurrentRelativeHumidity
HeatingThresholdTemperature
Name
TargetRelativeHumidity | | Valve | Active
InUse
ValveType | SetDuration
RemainingDuration
IsConfigured
ServiceLabelIndex
StatusFault
Name | From 9f68ffeca62885dd670676396845776c7adf1b1d Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 16:50:52 -0500 Subject: [PATCH 59/64] Update ServiceList.md --- docs/ServiceList.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/ServiceList.md b/docs/ServiceList.md index dc470fd..6c4a640 100644 --- a/docs/ServiceList.md +++ b/docs/ServiceList.md @@ -40,6 +40,7 @@ Additionally, when first starting up, HomeSpan begins by validating the device's | HeaterCooler | Active
CurrentTemperature
CurrentHeaterCoolerState
TargetHeaterCoolerState | Name
RotationSpeed
TemperatureDisplayUnits
SwingMode
CoolingThresholdTemperature
HeatingThresholdTemperature
LockPhysicalControls | | HumidifierDehumidifier | Active
CurrentRelativeHumidity
CurrentHumidifierDehumidifierState
TargetHumidifierDehumidifierState | Name
RelativeHumidityDehumidifierThreshold
RelativeHumidityHumidifierThreshold
RotationSpeed
SwingMode
WaterLevel
LockPhysicalControls | | HumiditySensor | CurrentRelativeHumidity | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | +| InputSource | Identifier | ConfiguredName
IsConfigured
CurrentVisibilityState
TargetVisibilityState | | IrrigationSystem | Active
ProgramMode
InUse | RemainingDuration
Name
StatusFault | | LeakSensor | LeakDetected | Name
StatusActive
StatusFault
StatusTampered
StatusLowBattery | | LightBulb | On | Brightness
Hue
Name
Saturation
ColorTemperature | From 23bdd1372c912f1c6a5e55ee05a4eacb4dfd80ff Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 17:04:47 -0500 Subject: [PATCH 60/64] Update ServiceList.md --- docs/ServiceList.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/ServiceList.md b/docs/ServiceList.md index 6c4a640..54c1b8e 100644 --- a/docs/ServiceList.md +++ b/docs/ServiceList.md @@ -70,6 +70,7 @@ Additionally, when first starting up, HomeSpan begins by validating the device's |Characteristic|Type|Default |---|---|---| |Active|uint8_t|0| +|ActiveIdentifier|uint32_t|0| |AirQuality|uint8_t|0| |BatteryLevel|uint8_t|0| |Brightness|int|0| @@ -82,6 +83,7 @@ Additionally, when first starting up, HomeSpan begins by validating the device's |ChargingState|uint8_t|0| |CoolingThresholdTemperature|double|10| |ColorTemperature|uint32_t|50| +|ConfiguredName|char \*|"unnamed"| |ContactSensorState|uint8_t|1| |CurrentAmbientLightLevel|double|1| |CurrentHorizontalTiltAngle|int|0| @@ -97,6 +99,7 @@ Additionally, when first starting up, HomeSpan begins by validating the device's |CurrentRelativeHumidity|double|0| |CurrentTemperature|double|0| |CurrentTiltAngle|int|0| +|CurrentVisibilityState|uint8_t|0| |FilterLifeLevel|double|0| |FilterChangeIndication|uint8_t|0| |FirmwareRevision|char \*|"1.0.0"| @@ -104,6 +107,7 @@ Additionally, when first starting up, HomeSpan begins by validating the device's |HeatingThresholdTemperature|double|16| |HoldPosition|boolean|false| |Hue|double|0| +|Identifier|uint32_t|0| |Identify|boolean|false| |InUse|uint8_t|0| |IsConfigured|uint8_t|0| @@ -125,11 +129,13 @@ Additionally, when first starting up, HomeSpan begins by validating the device's |OzoneDensity|double|0| |PM10Density|double|0| |PositionState|uint8_t|2| +|PowerModeSelection|uint8_t|0| |ProgramMode|uint8_t|0| |ProgrammableSwitchEvent|uint8_t|0| |RelativeHumidityDehumidifierThreshold|double|50| |RelativeHumidityHumidifierThreshold|double|50| |RemainingDuration|uint32_t|60| +|RemoteKey|uint8_t|0| |ResetFilterIndication|uint8_t|0| |RotationDirection|int|0| |RotationSpeed|double|0| @@ -163,6 +169,7 @@ Additionally, when first starting up, HomeSpan begins by validating the device's |TargetTemperature|double|16| |TemperatureDisplayUnits|uint8_t|0| |TargetVerticalTiltAngle|int|0| +|TargetVisibilityState|uint8_t|0| |ValveType|uint8_t|0| |Version|char \*|"1.0.0"| |VOCDensity|double|0| From 08c92f8eb0146dfbcd240ea90768bb71b6c7ffac Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 17:20:56 -0500 Subject: [PATCH 61/64] Update Span.h Make Characteristic::Identifier required for Service::InputSource --- src/Span.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Span.h b/src/Span.h index bf23dc4..8af5e6b 100644 --- a/src/Span.h +++ b/src/Span.h @@ -202,7 +202,7 @@ namespace Service { struct InputSource : SpanService { InputSource() : SpanService{"D9","InputSource"}{ OPT(ConfiguredName); OPT(IsConfigured); - OPT(Identifier); + REQ(Identifier); OPT(CurrentVisibilityState); OPT(TargetVisibilityState); }}; From 8d54be530883d89bd016bde906e6cc532ebcaf5b Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 17:35:09 -0500 Subject: [PATCH 62/64] Update FAQ.md --- docs/FAQ.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 33abe01..1b6b0cb 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -32,9 +32,9 @@ * *sketch* - the version number of the sketch, as specified with `homeSpan.setSketchVersion(const char *)`, or *n/a* if no version was specified * *ota* - either *yes* or *no* depending on whether OTA was enabled for the sketch using the method `homeSpan.enableOTA()` -#### Where are the Television Services? +#### Does HomeSpan support Television Services? -* Though HomeKit now supports Television controls, at the time of this posting Apple has not publicly released any specifications, SDKs, or ADKs, related to HAP Television Services or any HAP Television Characteristics. It appears that for now these Services are meant only for commercial use. Support for HAP Television Services and Characteristics will be added to HomeSpan as soon as Apple makes the specifications publicly available in some form or another. +* Yes. Though undocumented by Apple and not officially part of HAP-R2, HomeSpan supports HomeKit Television controls. See [Television Services](../TVServices.md) for details. #### Can you use HomeSpan with an Ethernet connection instead of a WiFi connection? From b202d5ba47ad4925d0ef4428487cda32a3502fbf Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 17:37:10 -0500 Subject: [PATCH 63/64] Update FAQ.md --- docs/FAQ.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 1b6b0cb..996e84a 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -34,7 +34,7 @@ #### Does HomeSpan support Television Services? -* Yes. Though undocumented by Apple and not officially part of HAP-R2, HomeSpan supports HomeKit Television controls. See [Television Services](../TVServices.md) for details. +* Yes. Though undocumented by Apple and not officially part of HAP-R2, HomeSpan supports HomeKit Television controls. See [Television Services](../docs/TVServices.md) for details. #### Can you use HomeSpan with an Ethernet connection instead of a WiFi connection? From 0899bb1679f801962c4dbba8498c333c72c18d20 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 31 Oct 2021 17:56:58 -0500 Subject: [PATCH 64/64] Update README.md --- docs/README.md | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/docs/README.md b/docs/README.md index 0a15164..ab8e871 100644 --- a/docs/README.md +++ b/docs/README.md @@ -41,34 +41,13 @@ 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.4.0 (10/9/2021) +## ❗Latest Update - HomeSpan 1.4.1 (10/31/2021) -**HomeSpan is now fully compatible with Version 2.0.0 of the Arduino-ESP32 Board Manager and will run on the following Espressif chips:** -* **ESP32** -* **ESP32-S2** -* **ESP32-C3** +* **Television Services and Characteristics have been added to HomeSpan!** See [HomeSpan Television Services](https://github.com/HomeSpan/HomeSpan/blob/master/docs/TVServices.md) for complete details -HomeSpan also maintains full backwards-compatability with Version 1.0.6 of the Arduino-ESP Board Manager should you need to revert to that version. This has been a complicated update! Please report any bugs or issues found when using Version 2 of the Arduino-ESP32. +* **The RFControl library has been updated to allow for the generation of a modulating carrier wave suitable for controlling an infrared LED.** This allows you to create HomeKit-enabled TV remote controls with HomeSpan. See [HomeSpan Projects](https://github.com/topics/homespan) for some real-world examples! -Also included in HomeSpan 1.4.0 are the following new features and enhancements: - -* **The PWM library has been upgraded** - * users can specify a custom PWM frequency for each instance of LedPin() (subject to chip-specific resource limitations) - * users can set the duty cycle of an LedPin() as a decimal floating number (e.g., 34.56), instead of just an integer - * the library automatically sets the duty resolution to the maximum allowable value for a chosen PWM frequency - * the library optimzizes the distribution of multiple LedPin() and ServoPin() instances to ensure all available PWM channels are used - -* **The RFControl library has been upgraded** - * the library allows for the transmisison of arbitrary-length pulse trains - * users can pre-load pulse trains into one or more arbitrary-length arrays of 32-bit words for on-demand transmission as needed - -* Users can now **limit the selection choices** of certain Characteristics (such as the Target State for a Security System) in the Home App using a new method, `setValidValues()` - -* **The Status LED and Control Button are now optional components that are ignored unless specifically enabled** - * because HomeSpan now runs on chips with many different pin configurations, HomeSpan's use of preset pin numbers for the Status LED and Control Button is likely to conflict with many devices - * the default behavior for HomeSpan has been changed to ignore all logic related to the Status LED and Control Button - * to enable the Status LED you must specify the pin to which your LED is attached using the usual method `homeSpan.setStatusPin(pin)` - * to enable the Control Button you must specify the pin to which your Control Button is attached using the usual method `homeSpan.setControlPin(pin)` +* **User-defined Custom Characteristics can be added to HomeSpan with a new macro.** See the [HomeSpan API](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Reference.md#define-custom_charnameuuidpermsformatdefaultvalueminvaluemaxvaluestaticrange) for details (for *advanced* users only) See [Releases](https://github.com/HomeSpan/HomeSpan/releases) for details on all changes included in this update.