/*********************************************************************************
 *  MIT License
 *  
 *  Copyright (c) 2020-2023 Gregg E. Berman
 *  
 *  https://github.com/HomeSpan/HomeSpan
 *  
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *  
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *  
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 *  
 ********************************************************************************/

///////////////////////////////////
// SPAN SERVICES (HAP Chapter 8) //
///////////////////////////////////

// Macros to define services, along with vectors of required and optional characteristics for each Span Service structure
// The names of the macros are picked up by external scripts to help generate documentation

#define CREATE_SERV(NAME,UUID) struct NAME : SpanService { NAME() : SpanService{#UUID,#NAME}{
#define END_SERV }};

#define REQ(HAPCHAR) req.push_back(&hapChars.HAPCHAR)
#define OPT(HAPCHAR) opt.push_back(&hapChars.HAPCHAR)

namespace Service {

  CREATE_SERV(AccessoryInformation,3E)
    REQ(Identify);
    OPT(FirmwareRevision);
    OPT(Manufacturer);
    OPT(Model);
    OPT(Name);
    OPT(SerialNumber);
    OPT(HardwareRevision);
    OPT(AccessoryFlags);    
  END_SERV

  CREATE_SERV(AirPurifier,BB)
    REQ(Active);
    REQ(CurrentAirPurifierState);
    REQ(TargetAirPurifierState);
    OPT(Name);
    OPT(RotationSpeed);
    OPT(SwingMode);
    OPT(LockPhysicalControls);
  END_SERV

  CREATE_SERV(AirQualitySensor,8D)
    REQ(AirQuality);
    OPT(Name);
    OPT(OzoneDensity);
    OPT(NitrogenDioxideDensity);
    OPT(SulphurDioxideDensity);
    OPT(PM25Density);
    OPT(PM10Density);
    OPT(VOCDensity);
    OPT(StatusActive);
    OPT(StatusFault);
    OPT(StatusTampered);
    OPT(StatusLowBattery);
  END_SERV

  CREATE_SERV(BatteryService,96)
    REQ(BatteryLevel);
    REQ(ChargingState);
    REQ(StatusLowBattery);
    OPT(Name);
  END_SERV

  CREATE_SERV(CarbonDioxideSensor,97)
    REQ(CarbonDioxideDetected);
    OPT(Name);
    OPT(StatusActive);
    OPT(StatusFault);
    OPT(StatusTampered);
    OPT(StatusLowBattery);
    OPT(CarbonDioxideLevel);
    OPT(CarbonDioxidePeakLevel);
  END_SERV

  CREATE_SERV(CarbonMonoxideSensor,7F)
    REQ(CarbonMonoxideDetected);
    OPT(Name);
    OPT(StatusActive);
    OPT(StatusFault);
    OPT(StatusTampered);
    OPT(StatusLowBattery);
    OPT(CarbonMonoxideLevel);
    OPT(CarbonMonoxidePeakLevel);
    END_SERV

  CREATE_SERV(ContactSensor,80)
    REQ(ContactSensorState);
    OPT(Name);
    OPT(StatusActive);
    OPT(StatusFault);
    OPT(StatusTampered);
    OPT(StatusLowBattery);
  END_SERV

  CREATE_SERV(Door,81)
    REQ(CurrentPosition);
    REQ(TargetPosition);
    REQ(PositionState);
    OPT(Name);
    OPT(HoldPosition);
    OPT(ObstructionDetected);
  END_SERV

  CREATE_SERV(Doorbell,121)
    REQ(ProgrammableSwitchEvent);
    OPT(Name);
    OPT(Volume);
    OPT(Brightness);
  END_SERV

  CREATE_SERV(Fan,B7)
    REQ(Active);
    OPT(Name);
    OPT(CurrentFanState);
    OPT(TargetFanState);
    OPT(RotationDirection);
    OPT(RotationSpeed);
    OPT(SwingMode);
    OPT(LockPhysicalControls);
  END_SERV

  CREATE_SERV(Faucet,D7)
    REQ(Active);
    OPT(StatusFault);
    OPT(Name);
  END_SERV

  CREATE_SERV(FilterMaintenance,BA)
    REQ(FilterChangeIndication);
    OPT(Name);
    OPT(FilterLifeLevel);
    OPT(ResetFilterIndication);
  END_SERV

  CREATE_SERV(GarageDoorOpener,41)
    REQ(CurrentDoorState);
    REQ(TargetDoorState);
    REQ(ObstructionDetected);
    OPT(LockCurrentState);
    OPT(LockTargetState);
    OPT(Name);
  END_SERV

  CREATE_SERV(HAPProtocolInformation,A2)
    REQ(Version);
  END_SERV

  CREATE_SERV(HeaterCooler,BC)
    REQ(Active);
    REQ(CurrentTemperature);
    REQ(CurrentHeaterCoolerState);
    REQ(TargetHeaterCoolerState);
    OPT(Name);
    OPT(RotationSpeed);
    OPT(TemperatureDisplayUnits);
    OPT(SwingMode);
    OPT(CoolingThresholdTemperature);
    OPT(HeatingThresholdTemperature);
    OPT(LockPhysicalControls);
  END_SERV

  CREATE_SERV(HumidifierDehumidifier,BD)
    REQ(Active);
    REQ(CurrentRelativeHumidity);
    REQ(CurrentHumidifierDehumidifierState);
    REQ(TargetHumidifierDehumidifierState);
    OPT(Name);
    OPT(RelativeHumidityDehumidifierThreshold);
    OPT(RelativeHumidityHumidifierThreshold);
    OPT(RotationSpeed);
    OPT(SwingMode);
    OPT(WaterLevel);
    OPT(LockPhysicalControls);
  END_SERV

  CREATE_SERV(HumiditySensor,82)
    REQ(CurrentRelativeHumidity);
    OPT(Name);
    OPT(StatusActive);
    OPT(StatusFault);
    OPT(StatusTampered);
    OPT(StatusLowBattery);   
  END_SERV

  CREATE_SERV(InputSource,D9)
      OPT(ConfiguredName);
      OPT(IsConfigured);
      REQ(Identifier);
      OPT(CurrentVisibilityState);
      OPT(TargetVisibilityState);
  END_SERV

  CREATE_SERV(IrrigationSystem,CF)
    REQ(Active);
    REQ(ProgramMode);
    REQ(InUse);
    OPT(RemainingDuration);
    OPT(StatusFault);
  END_SERV

  CREATE_SERV(LeakSensor,83)
    REQ(LeakDetected);
    OPT(Name);
    OPT(StatusActive);
    OPT(StatusFault);
    OPT(StatusTampered);
    OPT(StatusLowBattery);       
  END_SERV

  CREATE_SERV(LightBulb,43)
    REQ(On);
    OPT(Brightness);
    OPT(Hue);
    OPT(Name);
    OPT(Saturation);
    OPT(ColorTemperature);
  END_SERV

  CREATE_SERV(LightSensor,84)
    REQ(CurrentAmbientLightLevel);
    OPT(Name);
    OPT(StatusActive);
    OPT(StatusFault);
    OPT(StatusTampered);
    OPT(StatusLowBattery);          
  END_SERV

  CREATE_SERV(LockMechanism,45)
    REQ(LockCurrentState);
    REQ(LockTargetState);
    OPT(Name);
  END_SERV

  CREATE_SERV(Microphone,112)
    REQ(Mute);
    OPT(Name);
    OPT(Volume);
  END_SERV

  CREATE_SERV(MotionSensor,85)
    REQ(MotionDetected);
    OPT(Name);
    OPT(StatusActive);
    OPT(StatusFault);
    OPT(StatusTampered);
    OPT(StatusLowBattery);       
  END_SERV

  CREATE_SERV(OccupancySensor,86)
    REQ(OccupancyDetected);
    OPT(Name);
    OPT(StatusActive);
    OPT(StatusFault);
    OPT(StatusTampered);
    OPT(StatusLowBattery);         
  END_SERV

  CREATE_SERV(Outlet,47)
    REQ(On);
    REQ(OutletInUse);
    OPT(Name);
  END_SERV

  CREATE_SERV(SecuritySystem,7E)
    REQ(SecuritySystemCurrentState);
    REQ(SecuritySystemTargetState);
    OPT(Name);
    OPT(SecuritySystemAlarmType);
    OPT(StatusFault);
    OPT(StatusTampered);
  END_SERV  

  CREATE_SERV(ServiceLabel,CC)
    REQ(ServiceLabelNamespace);
  END_SERV  

  CREATE_SERV(Slat,B9)
    REQ(CurrentSlatState);
    REQ(SlatType);
    OPT(Name);
    OPT(SwingMode);
    OPT(CurrentTiltAngle);
    OPT(TargetTiltAngle);
  END_SERV

  CREATE_SERV(SmokeSensor,87)
    REQ(SmokeDetected);
    OPT(Name);
    OPT(StatusActive);
    OPT(StatusFault);
    OPT(StatusTampered);
    OPT(StatusLowBattery);             
  END_SERV

  CREATE_SERV(Speaker,113)
    REQ(Mute);
    OPT(Name);
    OPT(Volume);
  END_SERV

  CREATE_SERV(StatelessProgrammableSwitch,89)
    REQ(ProgrammableSwitchEvent);
    OPT(Name);
    OPT(ServiceLabelIndex);
  END_SERV

  CREATE_SERV(Switch,49)
    REQ(On);
    OPT(Name);
  END_SERV

  CREATE_SERV(Television,D8)
      REQ(Active);
      OPT(ConfiguredName);
      OPT(ActiveIdentifier);
      OPT(RemoteKey);
      OPT(PowerModeSelection);      
  END_SERV

  CREATE_SERV(TelevisionSpeaker,113)
      REQ(VolumeControlType);
      REQ(VolumeSelector);      
  END_SERV

  CREATE_SERV(TemperatureSensor,8A)
    REQ(CurrentTemperature);
    OPT(Name);
    OPT(StatusActive);
    OPT(StatusFault);
    OPT(StatusTampered);
    OPT(StatusLowBattery);
  END_SERV

  CREATE_SERV(Thermostat,4A)
    REQ(CurrentHeatingCoolingState);
    REQ(TargetHeatingCoolingState);
    REQ(CurrentTemperature);
    REQ(TargetTemperature);
    REQ(TemperatureDisplayUnits);
    OPT(CoolingThresholdTemperature);
    OPT(CurrentRelativeHumidity);
    OPT(HeatingThresholdTemperature);
    OPT(Name);
    OPT(TargetRelativeHumidity);
  END_SERV

  CREATE_SERV(Valve,D0)
    REQ(Active);
    REQ(InUse);
    REQ(ValveType);
    OPT(SetDuration);
    OPT(RemainingDuration);
    OPT(IsConfigured);
    OPT(ServiceLabelIndex);
    OPT(StatusFault);
    OPT(Name);
  END_SERV

  CREATE_SERV(Window,8B)
    REQ(CurrentPosition);
    REQ(TargetPosition);
    REQ(PositionState);
    OPT(Name);
    OPT(HoldPosition);
    OPT(ObstructionDetected);
  END_SERV

  CREATE_SERV(WindowCovering,8C)
    REQ(TargetPosition);
    REQ(CurrentPosition);
    REQ(PositionState);   
    OPT(Name);
    OPT(HoldPosition);
    OPT(CurrentHorizontalTiltAngle);
    OPT(TargetHorizontalTiltAngle);
    OPT(CurrentVerticalTiltAngle);
    OPT(TargetVerticalTiltAngle);
    OPT(ObstructionDetected);
  END_SERV

}

//////////////////////////////////////////
// SPAN CHARACTERISTICS (HAP Chapter 9) //
//////////////////////////////////////////

// Macro to define Span Characteristic structures based on name of HAP Characteristic, default value, and min/max value (not applicable for STRING or BOOL which default to min=0, max=1)

#define CREATE_CHAR(TYPE,HAPCHAR,DEFVAL,MINVAL,MAXVAL,...) \
  struct HAPCHAR : SpanCharacteristic { __VA_OPT__(enum{) __VA_ARGS__ __VA_OPT__(};) HAPCHAR(TYPE val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&hapChars.HAPCHAR} { init(val,nvsStore,(TYPE)MINVAL,(TYPE)MAXVAL); } };

namespace Characteristic {

  CREATE_CHAR(uint32_t,AccessoryFlags,1,1,1);
  CREATE_CHAR(uint8_t,Active,0,0,1,INACTIVE,ACIVE);
  CREATE_CHAR(uint32_t,ActiveIdentifier,0,0,255);
  CREATE_CHAR(uint8_t,AirQuality,0,0,5,UNKNOWN,EXCELLENT,GOOD,FAIR,INFERIOR,POOR);
  CREATE_CHAR(uint8_t,BatteryLevel,0,0,100);
  CREATE_CHAR(int,Brightness,0,0,100);
  CREATE_CHAR(double,CarbonMonoxideLevel,0,0,100);
  CREATE_CHAR(double,CarbonMonoxidePeakLevel,0,0,100);
  CREATE_CHAR(uint8_t,CarbonMonoxideDetected,0,0,1,NORMAL,ABNORMAL);
  CREATE_CHAR(double,CarbonDioxideLevel,0,0,100000);
  CREATE_CHAR(double,CarbonDioxidePeakLevel,0,0,100000);
  CREATE_CHAR(uint8_t,CarbonDioxideDetected,0,0,1,NORMAL,ABNORMAL);
  CREATE_CHAR(uint8_t,ChargingState,0,0,2,NOT_CHARGING,CHARGING,NOT_CHARGEABLE);
  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,DETECTED,NOT_DETECTED);
  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,INACTIVE,IDLE,PURIFYING);
  CREATE_CHAR(uint8_t,CurrentSlatState,0,0,2,FIXED,JAMMED,SWINGING);
  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,INACTIVE,IDLE,HUMIDIFYING,DEHUMIDIFYING);
  CREATE_CHAR(uint8_t,CurrentDoorState,1,0,4,OPEN,CLOSED,OPENING,CLOSING,STOPPED);
  CREATE_CHAR(uint8_t,CurrentFanState,1,0,2,INACTIVE,IDLE,BLOWING);
  CREATE_CHAR(uint8_t,CurrentHeatingCoolingState,0,0,2,OFF,HEATING,COOLING);
  CREATE_CHAR(uint8_t,CurrentHeaterCoolerState,1,0,3,INACTIVE,IDLE,HEATING,COOLING);
  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(double,FilterLifeLevel,0,0,100);
  CREATE_CHAR(uint8_t,FilterChangeIndication,0,0,1,NO_CHANGE_NEEDED,CHANGE_NEEDED);
  CREATE_CHAR(const char *,FirmwareRevision,"1.0.0",0,1);
  CREATE_CHAR(const char *,HardwareRevision,"1.0.0",0,1);
  CREATE_CHAR(double,HeatingThresholdTemperature,16,0,25);
  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,NOT_IN_USE,IN_USE);
  CREATE_CHAR(uint8_t,IsConfigured,0,0,1,NOT_CONFIGURED,CONFIGURED);
  CREATE_CHAR(uint8_t,LeakDetected,0,0,1,NOT_DETECTED,DETECTED);
  CREATE_CHAR(uint8_t,LockCurrentState,0,0,3,UNLOCKED,LOCKED,JAMMED,UNKNOWN);
  CREATE_CHAR(uint8_t,LockPhysicalControls,0,0,1,CONTROL_LOCK_DISABLED,CONTROL_LOCK_ENABLED);
  CREATE_CHAR(uint8_t,LockTargetState,0,0,1,UNLOCK,LOCK);
  CREATE_CHAR(const char *,Manufacturer,"HomeSpan",0,1);
  CREATE_CHAR(const char *,Model,"HomeSpan-ESP32",0,1);
  CREATE_CHAR(boolean,MotionDetected,false,0,1);
  CREATE_CHAR(boolean,Mute,false,0,1,OFF,ON);
  CREATE_CHAR(const char *,Name,"unnamed",0,1);
  CREATE_CHAR(double,NitrogenDioxideDensity,0,0,1000);
  CREATE_CHAR(boolean,ObstructionDetected,false,0,1);
  CREATE_CHAR(double,PM25Density,0,0,1000);
  CREATE_CHAR(uint8_t,OccupancyDetected,0,0,1,NOT_DETECTED,DETECTED);
  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,GOING_TO_MINIMUM,GOING_TO_MAXIMUM,STOPPED);
  CREATE_CHAR(uint8_t,PowerModeSelection,0,0,1);
  CREATE_CHAR(uint8_t,ProgramMode,0,0,2,NONE,SCHEDULED,SCHEDULE_OVERRIDEN);
  CREATE_CHAR(uint8_t,ProgrammableSwitchEvent,0,0,2,SINGLE_PRESS,DOUBLE_PRESS,LONG_PRESS);
  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,CLOCKWISE,COUNTERCLOCKWISE);
  CREATE_CHAR(double,RotationSpeed,0,0,100);
  CREATE_CHAR(double,Saturation,0,0,100);
  CREATE_CHAR(uint8_t,SecuritySystemAlarmType,0,0,1,KNOWN,UNKNOWN);
  CREATE_CHAR(uint8_t,SecuritySystemCurrentState,3,0,4,ARMED_STAY,ARMED_AWAY,ARMED_NIGHT,DISARMED,ALARM_TRIGGERED);
  CREATE_CHAR(uint8_t,SecuritySystemTargetState,3,0,3,ARM_STAY,ARM_AWAY,ARM_NIGHT,DISARM); 
  CREATE_CHAR(const char *,SerialNumber,"HS-12345",0,1);
  CREATE_CHAR(uint8_t,ServiceLabelIndex,1,1,255);
  CREATE_CHAR(uint8_t,ServiceLabelNamespace,1,0,1,DOTS,NUMERALS);
  CREATE_CHAR(uint8_t,SlatType,0,0,1,HORIZONTAL,VERTICAL);
  CREATE_CHAR(uint8_t,SleepDiscoveryMode,0,0,1);
  CREATE_CHAR(uint8_t,SmokeDetected,0,0,1,NOT_DETECTED,DETECTED);
  CREATE_CHAR(boolean,StatusActive,true,0,1);
  CREATE_CHAR(uint8_t,StatusFault,0,0,1,NO_FAULT,FAULT);
  CREATE_CHAR(uint8_t,StatusJammed,0,0,1,NOT_JAMMED,JAMMED);
  CREATE_CHAR(uint8_t,StatusLowBattery,0,0,1,NOT_LOW_BATTERY,LOW_BATTERY);
  CREATE_CHAR(uint8_t,StatusTampered,0,0,1,NOT_TAMPERED,TAMPERED);
  CREATE_CHAR(double,SulphurDioxideDensity,0,0,1000);
  CREATE_CHAR(uint8_t,SwingMode,0,0,1,SWING_DISABLED,SWING_ENABLED);
  CREATE_CHAR(uint8_t,TargetAirPurifierState,1,0,1,MANUAL,AUTO);
  CREATE_CHAR(uint8_t,TargetFanState,1,0,1,MANUAL,AUTO);
  CREATE_CHAR(int,TargetTiltAngle,0,-90,90);
  CREATE_CHAR(uint8_t,TargetHeaterCoolerState,0,0,2,AUTO,HEAT,COOL);
  CREATE_CHAR(uint32_t,SetDuration,60,0,3600);
  CREATE_CHAR(int,TargetHorizontalTiltAngle,0,-90,90);
  CREATE_CHAR(uint8_t,TargetHumidifierDehumidifierState,0,0,2,AUTO,HUMIDIFY,DEHUMIDIFY);
  CREATE_CHAR(uint8_t,TargetPosition,0,0,100);
  CREATE_CHAR(uint8_t,TargetDoorState,1,0,1,OPEN,CLOSED);
  CREATE_CHAR(uint8_t,TargetHeatingCoolingState,0,0,3,OFF,HEAT,COOL,AUTO);
  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,CELSIUS,FAHRENHEIT);
  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);

}

////////////////////////////////////////////////////////
// MACROS TO ADD CUSTOM SERVICES AND CHARACTERISTICS  //
////////////////////////////////////////////////////////

#ifndef CUSTOM_CHAR_HEADER

#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,true} { init(val,nvsStore,(FORMAT##_t)MINVAL,(FORMAT##_t)MAXVAL); } }; }

#define CUSTOM_CHAR_STRING(NAME,UUID,PERMISISONS,DEFVAL) \
  HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),STRING,true}; \
  namespace Characteristic { struct NAME : SpanCharacteristic { NAME(const char * val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init(val,nvsStore); } }; }

#define CUSTOM_CHAR_DATA(NAME,UUID,PERMISISONS) \
  HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),DATA,true}; \
  namespace Characteristic { struct NAME : SpanCharacteristic { NAME(const char * val="AA==", boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init(val,nvsStore); } }; }

#else

#define CUSTOM_CHAR(NAME,UUID,PERMISISONS,FORMAT,DEFVAL,MINVAL,MAXVAL,STATIC_RANGE) \
  extern HapChar _CUSTOM_##NAME; \
  namespace Characteristic { struct NAME : SpanCharacteristic { NAME(FORMAT##_t val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init(val,nvsStore,(FORMAT##_t)MINVAL,(FORMAT##_t)MAXVAL); } }; }

#define CUSTOM_CHAR_STRING(NAME,UUID,PERMISISONS,DEFVAL) \
  extern HapChar _CUSTOM_##NAME; \
  namespace Characteristic { struct NAME : SpanCharacteristic { NAME(const char * val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init(val,nvsStore); } }; }

#define CUSTOM_CHAR_DATA(NAME,UUID,PERMISISONS) \
  extern HapChar _CUSTOM_##NAME; \
  namespace Characteristic { struct NAME : SpanCharacteristic { NAME(const char * val="AA==", boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init(val,nvsStore); } }; }

#endif

#define CUSTOM_SERV(NAME,UUID) \
  namespace Service { struct NAME : SpanService { NAME() : SpanService{#UUID,#NAME,true}{} }; }


////////////////////////////////////////////////////////
// MACROS TO ADD A NEW ACCESSORT WITH OPTIONAL NAME   //
////////////////////////////////////////////////////////

#define SPAN_ACCESSORY(...)    new SpanAccessory();  new Service::AccessoryInformation(); new Characteristic::Identify(); __VA_OPT__(new Characteristic::Name(__VA_ARGS__));




  
