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/HomeSpan.cpp b/src/HomeSpan.cpp index 8e53c99..e743838 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -1137,7 +1137,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 @@ -1727,6 +1727,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 485fea2..a4f5663 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -246,7 +246,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) @@ -307,8 +307,24 @@ struct SpanCharacteristic{ return(String()); // included to prevent compiler warnings } - 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){ @@ -397,9 +413,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); @@ -421,7 +440,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) @@ -460,20 +479,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/Settings.h b/src/Settings.h index a19545b..51ec0d8 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -130,5 +130,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 be8b4aa..80aa80c 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,255); 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,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); 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); - + }