From 9aa77d559a2e7a5d4ed48b410503612041a732bb Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sun, 15 Mar 2020 23:48:38 +0100 Subject: [PATCH 001/138] major refactoring to support optional attributes --- pom.xml | 6 +- .../hapjava/accessories/BatteryAccessory.java | 58 ++++++- .../accessories/BatteryStatusAccessory.java | 31 ---- .../{impl => }/accessories/Bridge.java | 4 +- ...java => CarbonDioxideSensorAccessory.java} | 40 ++--- ...ava => CarbonMonoxideSensorAccessory.java} | 25 ++- ...ensor.java => ContactSensorAccessory.java} | 25 ++- .../hapjava/accessories/DoorAccessory.java | 82 +++++++++ .../io/github/hapjava/accessories/Fan.java | 99 ----------- .../hapjava/accessories/FanAccessory.java | 48 ++++++ ...or.java => GarageDoorOpenerAccessory.java} | 18 +- .../{ => accessories}/HomekitAccessory.java | 27 ++- ...nsor.java => HumiditySensorAccessory.java} | 17 +- ...akSensor.java => LeakSensorAccessory.java} | 24 +-- ...tSensor.java => LightSensorAccessory.java} | 19 +-- ...Lightbulb.java => LightbulbAccessory.java} | 17 +- .../hapjava/accessories/LockMechanism.java | 44 ----- .../accessories/LockMechanismAccessory.java | 64 +++++++ .../accessories/LockableLockMechanism.java | 38 ----- ...Sensor.java => MotionSensorAccessory.java} | 21 ++- ...sor.java => OccupancySensorAccessory.java} | 24 +-- .../{Outlet.java => OutletAccessory.java} | 17 +- ...stem.java => SecuritySystemAccessory.java} | 39 ++--- ...eSensor.java => SmokeSensorAccessory.java} | 25 ++- .../{Switch.java => SwitchAccessory.java} | 17 +- .../accessories/TemperatureSensor.java | 83 --------- .../TemperatureSensorAccessory.java | 38 +++++ .../hapjava/accessories/Thermostat.java | 13 -- .../accessories/ThermostatAccessory.java | 138 +++++++++++++++ .../{Valve.java => ValveAccessory.java} | 31 ++-- .../hapjava/accessories/WindowAccessory.java | 82 +++++++++ .../hapjava/accessories/WindowCovering.java | 16 -- ...ring.java => WindowCoveringAccessory.java} | 15 +- .../AccessoryWithBrightness.java} | 8 +- .../AccessoryWithCarbonDioxideLevel.java | 46 +++++ .../AccessoryWithCarbonMonoxideLevel.java | 46 +++++ .../AccessoryWithColor.java} | 9 +- ...essoryWithCoolingThresholdTemprature.java} | 6 +- .../AccessoryWithDuration.java} | 28 +--- .../AccessoryWithFanState.java | 158 ++++++++++++++++++ .../AccessoryWithHardwareRevision.java | 18 ++ ...essoryWithHearingThresholdTemprature.java} | 6 +- .../AccessoryWithHoldPosition.java} | 5 +- .../AccessoryWithHorizontalTilting.java} | 8 +- .../AccessoryWithObstructionDetection.java} | 9 +- .../AccessoryWithRemainingDuration.java | 30 ++++ .../AccessoryWithVerticalTilting.java} | 13 +- .../CarbonDioxideDetectedState.java | 32 ---- .../CarbonMonoxideDetectedState.java | 32 ---- .../accessories/properties/ContactState.java | 32 ---- .../CurrentSecuritySystemState.java | 48 ------ .../accessories/properties/DoorState.java | 33 ---- .../properties/LockMechanismState.java | 40 ----- .../properties/RotationDirection.java | 32 ---- .../properties/SecuritySystemAlarmType.java | 41 ----- .../properties/SmokeDetectedState.java | 32 ---- .../properties/TargetSecuritySystemState.java | 45 ----- .../properties/TemperatureUnit.java | 37 ---- .../properties/ThermostatMode.java | 39 ----- .../accessories/properties/ValveType.java | 38 ----- .../WindowCoveringPositionState.java | 39 ----- .../accessories/properties/package-info.java | 2 - .../thermostat/BasicThermostat.java | 88 ---------- .../BooleanCharacteristic.java | 42 ----- .../characteristics/CharacteristicEnum.java | 5 + .../EventableCharacteristic.java | 2 - .../ExceptionalConsumer.java | 2 +- .../HomekitCharacteristicChangeCallback.java | 4 +- .../WriteOnlyBooleanCharacteristic.java | 33 ---- .../FirmwareRevisionCharacteristic.java | 19 +++ .../HardwareRevisionCharacteristic.java | 19 +++ .../IdentifyCharacteristic.java | 19 +++ .../ManufacturerCharacteristic.java | 22 +++ .../ModelCharacteristic.java | 22 +++ .../NameCharacteristic.java | 19 +++ .../SerialNumberCharacteristic.java | 22 +++ .../{ => impl/base}/BaseCharacteristic.java | 60 +++++-- .../impl/base/BooleanCharacteristic.java | 73 ++++++++ .../{ => impl/base}/EnumCharacteristic.java | 44 ++++- .../{ => impl/base}/FloatCharacteristic.java | 51 ++++-- .../base}/IntegerCharacteristic.java | 39 ++++- .../base}/StaticStringCharacteristic.java | 24 ++- .../battery/BatteryLevelCharacteristic.java | 30 ++++ .../battery/ChargingStateCharacteristic.java | 26 +++ .../impl/battery/ChargingStateEnum.java | 36 ++++ .../StatusLowBatteryCharacteristic.java | 30 ++++ .../impl/battery/StatusLowBatteryEnum.java | 35 ++++ .../CarbonDioxideDetectedCharacteristic.java | 31 ++++ .../CarbonDioxideDetectedEnum.java | 35 ++++ .../CarbonDioxideLevelCharacteristic.java | 31 ++++ .../CarbonDioxidePeakLevelCharacteristic.java | 32 ++++ .../CarbonMonoxideDetectedCharacteristic.java | 31 ++++ .../CarbonMonoxideDetectedEnum.java | 35 ++++ .../CarbonMonoxideLevelCharacteristic.java | 29 ++++ ...CarbonMonoxidePeakLevelCharacteristic.java | 32 ++++ .../impl/common/ActiveCharacteristic.java | 33 ++++ .../impl/common/ActiveEnum.java | 34 ++++ .../impl/common/InUseCharacteristic.java | 28 ++++ .../impl/common/InUseEnum.java | 34 ++++ .../common/IsConfiguredCharacteristic.java | 37 ++++ .../impl/common/IsConfiguredEnum.java | 35 ++++ .../ObstructionDetectedCharacteristic.java | 27 +++ .../impl/common/OnCharacteristic.java | 27 +++ .../ServiceLabelIndexCharacteristic.java | 28 ++++ .../ServiceLabelNamespaceCharacteristic.java | 26 +++ .../common/ServiceLabelNamespaceEnum.java | 37 ++++ .../common/StatusActiveCharacteristic.java | 27 +++ .../common/StatusFaultCharacteristic.java | 30 ++++ .../impl/common/StatusFaultEnum.java | 35 ++++ .../common/StatusTamperedCharacteristic.java | 30 ++++ .../impl/common/StatusTamperedEnum.java | 35 ++++ .../ContactSensorStateCharacteristic.java | 31 ++++ .../impl/contactsensor/ContactStateEnum.java | 37 ++++ .../fan/CurrentFanStateCharacteristic.java | 28 ++++ .../impl/fan/CurrentFanStateEnum.java | 35 ++++ .../LockPhysicalControlsCharacteristic.java | 41 +++++ .../impl/fan/LockPhysicalControlsEnum.java | 35 ++++ .../fan/RotationDirectionCharacteristic.java | 38 +++++ .../impl/fan/RotationDirectionEnum.java | 35 ++++ .../impl/fan/RotationSpeedCharacteristic.java | 32 ++++ .../impl/fan/SwingModeCharacteristic.java | 38 +++++ .../impl/fan/SwingModeEnum.java | 35 ++++ .../fan/TargetFanStateCharacteristic.java | 38 +++++ .../impl/fan/TargetFanStateEnum.java | 35 ++++ .../CurrentDoorStateCharacteristic.java | 28 ++++ .../impl/garagedoor/CurrentDoorStateEnum.java | 42 +++++ .../TargetDoorStateCharacteristic.java | 38 +++++ .../impl/garagedoor/TargetDoorStateEnum.java | 35 ++++ ...CurrentRelativeHumidityCharacteristic.java | 32 ++++ .../TargetRelativeHumidityCharacteristic.java | 34 ++++ .../LeakDetectedStateCharacteristic.java | 31 ++++ .../leaksensor/LeakDetectedStateEnum.java | 35 ++++ .../lightbulb/BrightnessCharacteristic.java | 36 ++++ .../ColorTemperatureCharacteristic.java | 36 ++++ .../impl/lightbulb/HueCharacteristic.java | 30 ++++ .../lightbulb/SaturationCharacteristic.java | 33 ++++ ...urrentAmbientLightLevelCharacteristic.java | 32 ++++ .../lock/LockCurrentStateCharacteristic.java | 26 +++ .../impl/lock/LockCurrentStateEnum.java | 37 ++++ .../lock/LockTargetStateCharacteristic.java | 36 ++++ .../impl/lock/LockTargetStateEnum.java | 35 ++++ .../MotionDetectedCharacteristic.java | 25 +++ .../OccupancyDetectedCharacteristic.java | 26 +++ .../OccupancyDetectedEnum.java | 35 ++++ .../outlet/OutletInUseCharacteristic.java | 28 ++++ ...rentSecuritySystemStateCharacteristic.java | 27 +++ .../CurrentSecuritySystemStateEnum.java | 52 ++++++ ...SecuritySystemAlarmTypeCharacteristic.java | 30 ++++ .../SecuritySystemAlarmTypeEnum.java | 45 +++++ ...rgetSecuritySystemStateCharacteristic.java | 37 ++++ .../TargetSecuritySystemStateEnum.java | 50 ++++++ .../SmokeDetectedCharacteristic.java | 29 ++++ .../smokesensor/SmokeDetectedStateEnum.java | 34 ++++ ...ingThresholdTemperatureCharacteristic.java | 35 ++++ ...rentHeatingCoolingStateCharacteristic.java | 30 ++++ .../CurrentHeatingCoolingStateEnum.java | 37 ++++ .../CurrentTemperatureCharacteristic.java | 29 ++++ ...ingThresholdTemperatureCharacteristic.java | 35 ++++ ...rgetHeatingCoolingStateCharacteristic.java | 40 +++++ .../TargetHeatingCoolingStateEnum.java | 43 +++++ .../TargetTemperatureCharacteristic.java | 35 ++++ .../TemperatureDisplayUnitCharacteristic.java | 40 +++++ .../TemperatureDisplayUnitEnum.java | 41 +++++ .../RemainingDurationCharacteristic.java | 38 +++++ .../impl/valve/SetDurationCharacteristic.java | 36 ++++ .../impl/valve/ValveTypeCharacteristic.java | 26 +++ .../impl/valve/ValveTypeEnum.java | 42 +++++ ...rentHorizontalTiltAngleCharacteristic.java | 36 ++++ .../CurrentPositionCharacteristic.java | 36 ++++ ...urrentVerticalTiltAngleCharacteristic.java | 36 ++++ .../HoldPositionCharacteristic.java | 23 +++ .../PositionStateCharacteristic.java | 29 ++++ .../windowcovering/PositionStateEnum.java | 39 +++++ ...rgetHorizontalTiltAngleCharacteristic.java | 35 ++++ .../TargetPositionCharacteristic.java | 38 +++++ ...TargetVerticalTiltAngleCharacteristic.java | 35 ++++ .../io/github/hapjava/impl/HomekitBridge.java | 51 ------ .../CarbonDioxideDetectedCharacteristic.java | 41 ----- .../CarbonDioxideLevelCharacteristic.java | 54 ------ .../CarbonMonoxideDetectedCharacteristic.java | 41 ----- .../common/ActiveCharacteristic.java | 49 ------ .../common/BatteryLevelCharacteristic.java | 50 ------ .../common/InUseCharacteristic.java | 45 ----- .../LowBatteryStatusCharacteristic.java | 46 ----- .../impl/characteristics/common/Name.java | 10 -- .../ObstructionDetectedCharacteristic.java | 46 ----- .../common/PowerStateCharacteristic.java | 50 ------ .../RemainingDurationCharacteristic.java | 53 ------ .../ContactSensorStateCharacteristic.java | 39 ----- .../fan/RotationDirectionCharacteristic.java | 39 ----- .../fan/RotationSpeedCharacteristic.java | 38 ----- .../CurrentDoorStateCharacteristic.java | 38 ----- .../garage/TargetDoorStateCharacteristic.java | 39 ----- ...CurrentRelativeHumidityCharacteristic.java | 46 ----- .../characteristics/information/Identify.java | 23 --- .../information/Manufacturer.java | 14 -- .../characteristics/information/Model.java | 11 -- .../information/SerialNumber.java | 14 -- .../LeakDetectedStateCharacteristic.java | 38 ----- .../AmbientLightLevelCharacteristic.java | 46 ----- .../lightbulb/BrightnessCharacteristic.java | 45 ----- .../lightbulb/HueCharacteristic.java | 45 ----- .../lightbulb/SaturationCharacteristic.java | 46 ----- ...rrentLockMechanismStateCharacteristic.java | 38 ----- ...argetLockMechanismStateCharacteristic.java | 39 ----- .../MotionDetectedStateCharacteristic.java | 38 ----- .../OccupancyDetectedStateCharacteristic.java | 38 ----- .../outlet/OutletInUseCharacteristic.java | 38 ----- ...rentSecuritySystemStateCharacteristic.java | 41 ----- ...SecuritySystemAlarmTypeCharacteristic.java | 39 ----- ...rgetSecuritySystemStateCharacteristic.java | 41 ----- .../SmokeDetectedCharacteristic.java | 39 ----- ...tractHeatingCoolingModeCharacteristic.java | 29 ---- .../AbstractTemperatureCharacteristic.java | 22 --- ...ingThresholdTemperatureCharacteristic.java | 39 ----- ...rrentHeatingCoolingModeCharacteristic.java | 37 ---- .../CurrentTemperatureCharacteristic.java | 35 ---- ...ingThresholdTemperatureCharacteristic.java | 39 ----- ...argetHeatingCoolingModeCharacteristic.java | 37 ---- .../TargetTemperatureCharacteristic.java | 35 ---- .../TemperatureUnitsCharacteristic.java | 40 ----- .../valve/SetDurationCharacteristic.java | 37 ---- .../valve/ValveTypeCharacteristic.java | 37 ---- ...rentHorizontalTiltAngleCharacteristic.java | 45 ----- .../CurrentPositionCharacteristic.java | 38 ----- ...urrentVerticalTiltAngleCharacteristic.java | 45 ----- .../HoldPositionCharacteristic.java | 26 --- .../PositionStateCharacteristic.java | 38 ----- ...rgetHorizontalTiltAngleCharacteristic.java | 45 ----- .../TargetPositionCharacteristic.java | 38 ----- ...TargetVerticalTiltAngleCharacteristic.java | 45 ----- .../io/github/hapjava/impl/pairing/Stage.java | 7 - .../impl/services/AbstractServiceImpl.java | 89 ---------- .../services/AccessoryInformationService.java | 23 --- .../services/CarbonDioxideSensorService.java | 18 -- .../services/CarbonMonoxideSensorService.java | 17 -- .../impl/services/ContactSensorService.java | 16 -- .../hapjava/impl/services/FanService.java | 25 --- .../impl/services/GarageDoorService.java | 24 --- .../impl/services/HumiditySensorService.java | 16 -- .../impl/services/LeakSensorService.java | 16 -- .../impl/services/LightSensorService.java | 16 -- .../impl/services/LightbulbService.java | 35 ---- .../impl/services/LockMechanismService.java | 22 --- .../impl/services/MotionSensorService.java | 16 -- .../impl/services/OccupancySensorService.java | 16 -- .../hapjava/impl/services/OutletService.java | 23 --- .../impl/services/SecuritySystemService.java | 20 --- .../impl/services/SmokeSensorService.java | 16 -- .../hapjava/impl/services/SwitchService.java | 21 --- .../services/TemperatureSensorService.java | 16 -- .../impl/services/ThermostatService.java | 30 ---- .../hapjava/impl/services/ValveService.java | 40 ----- .../impl/services/WindowCoveringService.java | 59 ------- .../java/io/github/hapjava/package-info.java | 5 - .../hapjava/{ => server}/HomekitAuthInfo.java | 3 +- .../{impl => server}/HomekitWebHandler.java | 4 +- .../hapjava/server/impl/HomekitBridge.java | 67 ++++++++ .../{ => server}/impl/HomekitRegistry.java | 10 +- .../{ => server/impl}/HomekitRoot.java | 23 +-- .../{ => server/impl}/HomekitServer.java | 24 ++- .../HomekitStandaloneAccessoryServer.java | 11 +- .../{ => server}/impl/HomekitUtils.java | 2 +- .../impl/connections/ConnectionImpl.java | 18 +- .../HomekitClientConnectionFactoryImpl.java | 14 +- .../impl/connections/HttpSession.java | 32 ++-- .../LengthPrefixedByteArrayProcessor.java | 2 +- .../impl/connections/PendingNotification.java | 2 +- .../impl/connections/SubscriptionManager.java | 8 +- .../impl/crypto/ChachaDecoder.java | 2 +- .../impl/crypto/ChachaEncoder.java | 2 +- .../{ => server}/impl/crypto/EdsaSigner.java | 2 +- .../impl/crypto/EdsaVerifier.java | 2 +- .../impl/crypto/PolyKeyCreator.java | 2 +- .../impl/http/HomekitClientConnection.java | 2 +- .../http/HomekitClientConnectionFactory.java | 2 +- .../{ => server}/impl/http/HttpMethod.java | 2 +- .../{ => server}/impl/http/HttpRequest.java | 2 +- .../{ => server}/impl/http/HttpResponse.java | 2 +- .../impl/http/impl/AccessoryHandler.java | 8 +- .../impl/http/impl/BinaryHandler.java | 4 +- .../http/impl/DefaultHttpRequestImpl.java | 6 +- .../http/impl/FullRequestHttpRequestImpl.java | 2 +- .../impl/http/impl/HomekitHttpServer.java | 6 +- .../impl/HttpResponseEncoderAggregate.java | 2 +- .../impl/http/impl/LoggingHandler.java | 2 +- .../http/impl/NettyHomekitHttpService.java | 4 +- .../impl/http/impl/NettyResponseUtil.java | 4 +- .../impl/http/impl/ServerInitializer.java | 4 +- .../impl/jmdns/JmdnsHomekitAdvertiser.java | 2 +- .../impl/json/AccessoryController.java | 10 +- .../impl/json/CharacteristicsController.java | 14 +- .../impl/json/EventController.java | 6 +- .../{ => server}/impl/json/EventResponse.java | 2 +- .../impl/json/HapJsonNoContentResponse.java | 2 +- .../impl/json/HapJsonResponse.java | 4 +- .../{ => server}/impl/pairing/ByteUtils.java | 2 +- .../pairing/ClientEvidenceRoutineImpl.java | 2 +- .../impl/pairing/FinalPairHandler.java | 19 ++- .../impl/pairing/HomekitSRP6Routines.java | 2 +- .../pairing/HomekitSRP6ServerSession.java | 2 +- .../impl/pairing/MessageType.java | 2 +- .../impl/pairing/PairSetupRequest.java | 4 +- .../impl/pairing/PairVerificationManager.java | 27 +-- .../impl/pairing/PairVerificationRequest.java | 4 +- .../impl/pairing/PairingManager.java | 14 +- .../impl/pairing/PairingResponse.java | 4 +- .../impl/pairing/PairingUpdateController.java | 12 +- .../pairing/ServerEvidenceRoutineImpl.java | 2 +- .../{ => server}/impl/pairing/SrpHandler.java | 14 +- .../hapjava/server/impl/pairing/Stage.java | 7 + .../impl/pairing/TypeLengthValueUtils.java | 2 +- .../impl/pairing/UpgradeResponse.java | 2 +- .../impl/responses/ConflictResponse.java | 4 +- .../InternalServerErrorResponse.java | 4 +- .../impl/responses/NotFoundResponse.java | 4 +- .../impl/responses/OkResponse.java | 4 +- .../impl/responses/UnauthorizedResponse.java | 4 +- .../hapjava/{ => services}/Service.java | 2 +- .../services/impl/AbstractServiceImpl.java | 37 ++++ .../impl/AccessoryInformationService.java | 56 +++++++ .../hapjava/services/impl/BatteryService.java | 36 ++++ .../impl/CarbonDioxideSensorService.java | 66 ++++++++ .../impl/CarbonMonoxideSensorService.java | 52 ++++++ .../services/impl/ContactSensorService.java | 41 +++++ .../hapjava/services/impl/DoorService.java | 46 +++++ .../hapjava/services/impl/FanService.java | 53 ++++++ .../impl/GarageDoorOpenerService.java | 50 ++++++ .../services/impl/HumiditySensorService.java | 42 +++++ .../services/impl/LeakSensorService.java | 41 +++++ .../services/impl/LightSensorService.java | 46 +++++ .../services/impl/LightbulbService.java | 47 ++++++ .../services/impl/LockMechanismService.java | 38 +++++ .../services/impl/MotionSensorService.java | 46 +++++ .../services/impl/OccupancySensorService.java | 46 +++++ .../hapjava/services/impl/OutletService.java | 31 ++++ .../services/impl/SecuritySystemService.java | 50 ++++++ .../services/impl/SmokeSensorService.java | 46 +++++ .../hapjava/services/impl/SwitchService.java | 27 +++ .../impl/TemperatureSensorService.java | 47 ++++++ .../services/impl/ThermostatService.java | 78 +++++++++ .../hapjava/services/impl/ValveService.java | 62 +++++++ .../services/impl/WindowCoveringService.java | 80 +++++++++ .../hapjava/services/impl/WindowService.java | 53 ++++++ .../{ => server/impl}/HomekitRootTest.java | 10 +- 345 files changed, 6100 insertions(+), 4241 deletions(-) delete mode 100644 src/main/java/io/github/hapjava/accessories/BatteryStatusAccessory.java rename src/main/java/io/github/hapjava/{impl => }/accessories/Bridge.java (52%) rename src/main/java/io/github/hapjava/accessories/{CarbonDioxideSensor.java => CarbonDioxideSensorAccessory.java} (50%) rename src/main/java/io/github/hapjava/accessories/{CarbonMonoxideSensor.java => CarbonMonoxideSensorAccessory.java} (68%) rename src/main/java/io/github/hapjava/accessories/{ContactSensor.java => ContactSensorAccessory.java} (73%) create mode 100644 src/main/java/io/github/hapjava/accessories/DoorAccessory.java delete mode 100644 src/main/java/io/github/hapjava/accessories/Fan.java create mode 100644 src/main/java/io/github/hapjava/accessories/FanAccessory.java rename src/main/java/io/github/hapjava/accessories/{GarageDoor.java => GarageDoorOpenerAccessory.java} (73%) rename src/main/java/io/github/hapjava/{ => accessories}/HomekitAccessory.java (70%) rename src/main/java/io/github/hapjava/accessories/{HumiditySensor.java => HumiditySensorAccessory.java} (78%) rename src/main/java/io/github/hapjava/accessories/{LeakSensor.java => LeakSensorAccessory.java} (69%) rename src/main/java/io/github/hapjava/accessories/{LightSensor.java => LightSensorAccessory.java} (78%) rename src/main/java/io/github/hapjava/accessories/{Lightbulb.java => LightbulbAccessory.java} (83%) delete mode 100644 src/main/java/io/github/hapjava/accessories/LockMechanism.java create mode 100644 src/main/java/io/github/hapjava/accessories/LockMechanismAccessory.java delete mode 100644 src/main/java/io/github/hapjava/accessories/LockableLockMechanism.java rename src/main/java/io/github/hapjava/accessories/{MotionSensor.java => MotionSensorAccessory.java} (77%) rename src/main/java/io/github/hapjava/accessories/{OccupancySensor.java => OccupancySensorAccessory.java} (68%) rename src/main/java/io/github/hapjava/accessories/{Outlet.java => OutletAccessory.java} (88%) rename src/main/java/io/github/hapjava/accessories/{SecuritySystem.java => SecuritySystemAccessory.java} (59%) rename src/main/java/io/github/hapjava/accessories/{SmokeSensor.java => SmokeSensorAccessory.java} (69%) rename src/main/java/io/github/hapjava/accessories/{Switch.java => SwitchAccessory.java} (82%) delete mode 100644 src/main/java/io/github/hapjava/accessories/TemperatureSensor.java create mode 100644 src/main/java/io/github/hapjava/accessories/TemperatureSensorAccessory.java delete mode 100644 src/main/java/io/github/hapjava/accessories/Thermostat.java create mode 100644 src/main/java/io/github/hapjava/accessories/ThermostatAccessory.java rename src/main/java/io/github/hapjava/accessories/{Valve.java => ValveAccessory.java} (78%) create mode 100644 src/main/java/io/github/hapjava/accessories/WindowAccessory.java delete mode 100644 src/main/java/io/github/hapjava/accessories/WindowCovering.java rename src/main/java/io/github/hapjava/accessories/{BasicWindowCovering.java => WindowCoveringAccessory.java} (81%) rename src/main/java/io/github/hapjava/accessories/{DimmableLightbulb.java => optionalcharacteristic/AccessoryWithBrightness.java} (81%) create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonDioxideLevel.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonMonoxideLevel.java rename src/main/java/io/github/hapjava/accessories/{ColorfulLightbulb.java => optionalcharacteristic/AccessoryWithColor.java} (85%) rename src/main/java/io/github/hapjava/accessories/{thermostat/CoolingThermostat.java => optionalcharacteristic/AccessoryWithCoolingThresholdTemprature.java} (82%) rename src/main/java/io/github/hapjava/accessories/{ValveWithTimer.java => optionalcharacteristic/AccessoryWithDuration.java} (55%) create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFanState.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHardwareRevision.java rename src/main/java/io/github/hapjava/accessories/{thermostat/HeatingThermostat.java => optionalcharacteristic/AccessoryWithHearingThresholdTemprature.java} (82%) rename src/main/java/io/github/hapjava/accessories/{HoldPositionWindowCovering.java => optionalcharacteristic/AccessoryWithHoldPosition.java} (71%) rename src/main/java/io/github/hapjava/accessories/{HorizontalTiltingWindowCovering.java => optionalcharacteristic/AccessoryWithHorizontalTilting.java} (86%) rename src/main/java/io/github/hapjava/accessories/{ObstructionDetectedWindowCovering.java => optionalcharacteristic/AccessoryWithObstructionDetection.java} (66%) create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRemainingDuration.java rename src/main/java/io/github/hapjava/accessories/{VerticalTiltingWindowCovering.java => optionalcharacteristic/AccessoryWithVerticalTilting.java} (83%) delete mode 100644 src/main/java/io/github/hapjava/accessories/properties/CarbonDioxideDetectedState.java delete mode 100644 src/main/java/io/github/hapjava/accessories/properties/CarbonMonoxideDetectedState.java delete mode 100644 src/main/java/io/github/hapjava/accessories/properties/ContactState.java delete mode 100644 src/main/java/io/github/hapjava/accessories/properties/CurrentSecuritySystemState.java delete mode 100644 src/main/java/io/github/hapjava/accessories/properties/DoorState.java delete mode 100644 src/main/java/io/github/hapjava/accessories/properties/LockMechanismState.java delete mode 100644 src/main/java/io/github/hapjava/accessories/properties/RotationDirection.java delete mode 100644 src/main/java/io/github/hapjava/accessories/properties/SecuritySystemAlarmType.java delete mode 100644 src/main/java/io/github/hapjava/accessories/properties/SmokeDetectedState.java delete mode 100644 src/main/java/io/github/hapjava/accessories/properties/TargetSecuritySystemState.java delete mode 100644 src/main/java/io/github/hapjava/accessories/properties/TemperatureUnit.java delete mode 100644 src/main/java/io/github/hapjava/accessories/properties/ThermostatMode.java delete mode 100644 src/main/java/io/github/hapjava/accessories/properties/ValveType.java delete mode 100644 src/main/java/io/github/hapjava/accessories/properties/WindowCoveringPositionState.java delete mode 100644 src/main/java/io/github/hapjava/accessories/properties/package-info.java delete mode 100644 src/main/java/io/github/hapjava/accessories/thermostat/BasicThermostat.java delete mode 100644 src/main/java/io/github/hapjava/characteristics/BooleanCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/CharacteristicEnum.java rename src/main/java/io/github/hapjava/{impl => characteristics}/ExceptionalConsumer.java (65%) rename src/main/java/io/github/hapjava/{ => characteristics}/HomekitCharacteristicChangeCallback.java (86%) delete mode 100644 src/main/java/io/github/hapjava/characteristics/WriteOnlyBooleanCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/FirmwareRevisionCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/HardwareRevisionCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/IdentifyCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ManufacturerCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ModelCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/NameCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/SerialNumberCharacteristic.java rename src/main/java/io/github/hapjava/characteristics/{ => impl/base}/BaseCharacteristic.java (80%) create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java rename src/main/java/io/github/hapjava/characteristics/{ => impl/base}/EnumCharacteristic.java (56%) rename src/main/java/io/github/hapjava/characteristics/{ => impl/base}/FloatCharacteristic.java (74%) rename src/main/java/io/github/hapjava/characteristics/{ => impl/base}/IntegerCharacteristic.java (56%) rename src/main/java/io/github/hapjava/characteristics/{ => impl/base}/StaticStringCharacteristic.java (62%) create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideLevelCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxidePeakLevelCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideLevelCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxidePeakLevelCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/ActiveCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/ActiveEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/InUseCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/InUseEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/ObstructionDetectedCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/OnCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelIndexCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/StatusActiveCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactSensorStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/fan/RotationDirectionCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/fan/RotationDirectionEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/fan/RotationSpeedCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/fan/SwingModeCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/fan/SwingModeEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/fan/TargetFanStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/fan/TargetFanStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/garagedoor/TargetDoorStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/garagedoor/TargetDoorStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/lightbulb/ColorTemperatureCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/lightsensor/CurrentAmbientLightLevelCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/lock/LockCurrentStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/lock/LockCurrentStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/lock/LockTargetStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/lock/LockTargetStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/motionsensor/MotionDetectedCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/occupancysensor/OccupancyDetectedCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/occupancysensor/OccupancyDetectedEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/outlet/OutletInUseCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/securitysystem/CurrentSecuritySystemStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/securitysystem/CurrentSecuritySystemStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/securitysystem/TargetSecuritySystemStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/securitysystem/TargetSecuritySystemStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/valve/RemainingDurationCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/valve/SetDurationCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/valve/ValveTypeCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/valve/ValveTypeEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/windowcovering/HoldPositionCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/HomekitBridge.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/carbondioxide/CarbonDioxideDetectedCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/carbondioxide/CarbonDioxideLevelCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/carbonmonoxide/CarbonMonoxideDetectedCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/common/ActiveCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/common/BatteryLevelCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/common/InUseCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/common/LowBatteryStatusCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/common/Name.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/common/ObstructionDetectedCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/common/PowerStateCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/common/RemainingDurationCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/contactsensor/ContactSensorStateCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/fan/RotationDirectionCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/fan/RotationSpeedCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/garage/CurrentDoorStateCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/garage/TargetDoorStateCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/humiditysensor/CurrentRelativeHumidityCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/information/Identify.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/information/Manufacturer.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/information/Model.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/information/SerialNumber.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/leaksensor/LeakDetectedStateCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/light/AmbientLightLevelCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/lightbulb/BrightnessCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/lightbulb/HueCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/lightbulb/SaturationCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/lock/mechanism/CurrentLockMechanismStateCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/lock/mechanism/TargetLockMechanismStateCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/motionsensor/MotionDetectedStateCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/occupancysensor/OccupancyDetectedStateCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/outlet/OutletInUseCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/securitysystem/CurrentSecuritySystemStateCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/securitysystem/SecuritySystemAlarmTypeCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/securitysystem/TargetSecuritySystemStateCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/smokesensor/SmokeDetectedCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/thermostat/AbstractHeatingCoolingModeCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/thermostat/AbstractTemperatureCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/thermostat/CoolingThresholdTemperatureCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/thermostat/CurrentHeatingCoolingModeCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/thermostat/CurrentTemperatureCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/thermostat/HeatingThresholdTemperatureCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/thermostat/TargetHeatingCoolingModeCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/thermostat/TargetTemperatureCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/thermostat/TemperatureUnitsCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/valve/SetDurationCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/valve/ValveTypeCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/windowcovering/CurrentPositionCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/windowcovering/CurrentVerticalTiltAngleCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/windowcovering/HoldPositionCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/windowcovering/PositionStateCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/windowcovering/TargetHorizontalTiltAngleCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/windowcovering/TargetPositionCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/characteristics/windowcovering/TargetVerticalTiltAngleCharacteristic.java delete mode 100644 src/main/java/io/github/hapjava/impl/pairing/Stage.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/AbstractServiceImpl.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/AccessoryInformationService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/CarbonDioxideSensorService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/CarbonMonoxideSensorService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/ContactSensorService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/FanService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/GarageDoorService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/HumiditySensorService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/LeakSensorService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/LightSensorService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/LightbulbService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/LockMechanismService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/MotionSensorService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/OccupancySensorService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/OutletService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/SecuritySystemService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/SmokeSensorService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/SwitchService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/TemperatureSensorService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/ThermostatService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/ValveService.java delete mode 100644 src/main/java/io/github/hapjava/impl/services/WindowCoveringService.java delete mode 100644 src/main/java/io/github/hapjava/package-info.java rename src/main/java/io/github/hapjava/{ => server}/HomekitAuthInfo.java (97%) rename src/main/java/io/github/hapjava/{impl => server}/HomekitWebHandler.java (67%) create mode 100644 src/main/java/io/github/hapjava/server/impl/HomekitBridge.java rename src/main/java/io/github/hapjava/{ => server}/impl/HomekitRegistry.java (92%) rename src/main/java/io/github/hapjava/{ => server/impl}/HomekitRoot.java (89%) rename src/main/java/io/github/hapjava/{ => server/impl}/HomekitServer.java (88%) rename src/main/java/io/github/hapjava/{ => server/impl}/HomekitStandaloneAccessoryServer.java (67%) rename src/main/java/io/github/hapjava/{ => server}/impl/HomekitUtils.java (98%) rename src/main/java/io/github/hapjava/{ => server}/impl/connections/ConnectionImpl.java (88%) rename src/main/java/io/github/hapjava/{ => server}/impl/connections/HomekitClientConnectionFactoryImpl.java (67%) rename src/main/java/io/github/hapjava/{ => server}/impl/connections/HttpSession.java (84%) rename src/main/java/io/github/hapjava/{ => server}/impl/connections/LengthPrefixedByteArrayProcessor.java (98%) rename src/main/java/io/github/hapjava/{ => server}/impl/connections/PendingNotification.java (87%) rename src/main/java/io/github/hapjava/{ => server}/impl/connections/SubscriptionManager.java (95%) rename src/main/java/io/github/hapjava/{ => server}/impl/crypto/ChachaDecoder.java (97%) rename src/main/java/io/github/hapjava/{ => server}/impl/crypto/ChachaEncoder.java (97%) rename src/main/java/io/github/hapjava/{ => server}/impl/crypto/EdsaSigner.java (96%) rename src/main/java/io/github/hapjava/{ => server}/impl/crypto/EdsaVerifier.java (95%) rename src/main/java/io/github/hapjava/{ => server}/impl/crypto/PolyKeyCreator.java (96%) rename src/main/java/io/github/hapjava/{ => server}/impl/http/HomekitClientConnection.java (87%) rename src/main/java/io/github/hapjava/{ => server}/impl/http/HomekitClientConnectionFactory.java (80%) rename src/main/java/io/github/hapjava/{ => server}/impl/http/HttpMethod.java (52%) rename src/main/java/io/github/hapjava/{ => server}/impl/http/HttpRequest.java (69%) rename src/main/java/io/github/hapjava/{ => server}/impl/http/HttpResponse.java (91%) rename src/main/java/io/github/hapjava/{ => server}/impl/http/impl/AccessoryHandler.java (93%) rename src/main/java/io/github/hapjava/{ => server}/impl/http/impl/BinaryHandler.java (94%) rename src/main/java/io/github/hapjava/{ => server}/impl/http/impl/DefaultHttpRequestImpl.java (85%) rename src/main/java/io/github/hapjava/{ => server}/impl/http/impl/FullRequestHttpRequestImpl.java (90%) rename src/main/java/io/github/hapjava/{ => server}/impl/http/impl/HomekitHttpServer.java (85%) rename src/main/java/io/github/hapjava/{ => server}/impl/http/impl/HttpResponseEncoderAggregate.java (92%) rename src/main/java/io/github/hapjava/{ => server}/impl/http/impl/LoggingHandler.java (97%) rename src/main/java/io/github/hapjava/{ => server}/impl/http/impl/NettyHomekitHttpService.java (96%) rename src/main/java/io/github/hapjava/{ => server}/impl/http/impl/NettyResponseUtil.java (92%) rename src/main/java/io/github/hapjava/{ => server}/impl/http/impl/ServerInitializer.java (92%) rename src/main/java/io/github/hapjava/{ => server}/impl/jmdns/JmdnsHomekitAdvertiser.java (98%) rename src/main/java/io/github/hapjava/{ => server}/impl/json/AccessoryController.java (93%) rename src/main/java/io/github/hapjava/{ => server}/impl/json/CharacteristicsController.java (90%) rename src/main/java/io/github/hapjava/{ => server}/impl/json/EventController.java (91%) rename src/main/java/io/github/hapjava/{ => server}/impl/json/EventResponse.java (82%) rename src/main/java/io/github/hapjava/{ => server}/impl/json/HapJsonNoContentResponse.java (81%) rename src/main/java/io/github/hapjava/{ => server}/impl/json/HapJsonResponse.java (84%) rename src/main/java/io/github/hapjava/{ => server}/impl/pairing/ByteUtils.java (95%) rename src/main/java/io/github/hapjava/{ => server}/impl/pairing/ClientEvidenceRoutineImpl.java (97%) rename src/main/java/io/github/hapjava/{ => server}/impl/pairing/FinalPairHandler.java (85%) rename src/main/java/io/github/hapjava/{ => server}/impl/pairing/HomekitSRP6Routines.java (97%) rename src/main/java/io/github/hapjava/{ => server}/impl/pairing/HomekitSRP6ServerSession.java (99%) rename src/main/java/io/github/hapjava/{ => server}/impl/pairing/MessageType.java (87%) rename src/main/java/io/github/hapjava/{ => server}/impl/pairing/PairSetupRequest.java (94%) rename src/main/java/io/github/hapjava/{ => server}/impl/pairing/PairVerificationManager.java (84%) rename src/main/java/io/github/hapjava/{ => server}/impl/pairing/PairVerificationRequest.java (92%) rename src/main/java/io/github/hapjava/{ => server}/impl/pairing/PairingManager.java (83%) rename src/main/java/io/github/hapjava/{ => server}/impl/pairing/PairingResponse.java (84%) rename src/main/java/io/github/hapjava/{ => server}/impl/pairing/PairingUpdateController.java (77%) rename src/main/java/io/github/hapjava/{ => server}/impl/pairing/ServerEvidenceRoutineImpl.java (95%) rename src/main/java/io/github/hapjava/{ => server}/impl/pairing/SrpHandler.java (89%) create mode 100644 src/main/java/io/github/hapjava/server/impl/pairing/Stage.java rename src/main/java/io/github/hapjava/{ => server}/impl/pairing/TypeLengthValueUtils.java (98%) rename src/main/java/io/github/hapjava/{ => server}/impl/pairing/UpgradeResponse.java (91%) rename src/main/java/io/github/hapjava/{ => server}/impl/responses/ConflictResponse.java (54%) rename src/main/java/io/github/hapjava/{ => server}/impl/responses/InternalServerErrorResponse.java (82%) rename src/main/java/io/github/hapjava/{ => server}/impl/responses/NotFoundResponse.java (54%) rename src/main/java/io/github/hapjava/{ => server}/impl/responses/OkResponse.java (75%) rename src/main/java/io/github/hapjava/{ => server}/impl/responses/UnauthorizedResponse.java (54%) rename src/main/java/io/github/hapjava/{ => services}/Service.java (96%) create mode 100644 src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java create mode 100644 src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/BatteryService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/CarbonMonoxideSensorService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/ContactSensorService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/DoorService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/FanService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/GarageDoorOpenerService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/HumiditySensorService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/LeakSensorService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/LightSensorService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/LightbulbService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/LockMechanismService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/MotionSensorService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/OccupancySensorService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/OutletService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/SecuritySystemService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/SmokeSensorService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/SwitchService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/TemperatureSensorService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/ThermostatService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/ValveService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/WindowCoveringService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/WindowService.java rename src/test/java/io/github/hapjava/{ => server/impl}/HomekitRootTest.java (89%) diff --git a/pom.xml b/pom.xml index d5709647c..bc523c756 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 1.2.0-snapshot + 2.0.0-snapshot jar https://github.com/hap-java/HAP-Java @@ -165,7 +165,7 @@ maven-javadoc-plugin 3.0.1 - io.github.hapjava.impl + io.github.hapjava.server.impl @@ -272,7 +272,7 @@ maven-javadoc-plugin 3.0.1 - io.github.hapjava.impl + io.github.hapjava.server.impl diff --git a/src/main/java/io/github/hapjava/accessories/BatteryAccessory.java b/src/main/java/io/github/hapjava/accessories/BatteryAccessory.java index 88c424a32..d3724bc50 100644 --- a/src/main/java/io/github/hapjava/accessories/BatteryAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/BatteryAccessory.java @@ -1,30 +1,74 @@ package io.github.hapjava.accessories; -import io.github.hapjava.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.battery.ChargingStateEnum; +import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.BatteryService; +import java.util.Collection; +import java.util.Collections; import java.util.concurrent.CompletableFuture; /** - * Do not use. Devices that have battery levels should implement LowBatteryStatusAccessory. + * Devices with battery . * * @author Gaston Dombiak */ -@Deprecated -public interface BatteryAccessory { +public interface BatteryAccessory extends HomekitAccessory { /** * Retrieves the battery level of the accessory. * * @return a future that will contain the accessory's battery state */ - CompletableFuture getBatteryLevelState(); + CompletableFuture getBatteryLevel(); + + /** + * Queries if the device battery level is low; returning a value of true will cause a low-battery + * status to appear in Home for the device. + * + * @return a future that will contain the accessory's low battery state + */ + CompletableFuture getLowBatteryState(); + + /** + * Retriece the battery charging state. + * + * @return a future that will contain the battery charging state + */ + CompletableFuture getChargingState(); /** * Subscribes to changes in the battery level. * * @param callback the function to call when battery level changes. */ - void subscribeBatteryLevelState(HomekitCharacteristicChangeCallback callback); + void subscribeBatteryLevel(HomekitCharacteristicChangeCallback callback); + /** + * Subscribes to changes in the battery level. + * + * @param callback the function to call when low battery state changes. + */ + void subscribeLowBatteryState(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the battery level. + * + * @param callback the function to call when low battery state changes. + */ + void subscribeBatteryChargingState(HomekitCharacteristicChangeCallback callback); /** Unsubscribes from changes in the battery level. */ - void unsubscribeBatteryLevelState(); + void unsubscribeBatteryLevel(); + + /** Unsubscribes from changes in the low battery state. */ + void unsubscribeLowBatteryState(); + + /** Unsubscribes from changes in the low battery state. */ + void unsubscribeBatteryChargingState(); + + @Override + default Collection getServices() { + return Collections.singleton(new BatteryService(this)); + } } diff --git a/src/main/java/io/github/hapjava/accessories/BatteryStatusAccessory.java b/src/main/java/io/github/hapjava/accessories/BatteryStatusAccessory.java deleted file mode 100644 index b8c6ba201..000000000 --- a/src/main/java/io/github/hapjava/accessories/BatteryStatusAccessory.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.hapjava.accessories; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import java.util.concurrent.CompletableFuture; - -/** - * An accessory that runs on batteries. Accessories that run on batteries are able to report battery - * level. - * - * @author Tim Harper - */ -public interface BatteryStatusAccessory { - - /** - * Queries if the device battery level is low; returning a value of true will cause a low-battery - * status to appear in Home for the device. - * - * @return a future that will contain the accessory's low battery state - */ - CompletableFuture getLowBatteryState(); - - /** - * Subscribes to changes in the battery level. - * - * @param callback the function to call when low battery state changes. - */ - void subscribeLowBatteryState(HomekitCharacteristicChangeCallback callback); - - /** Unsubscribes from changes in the low battery state. */ - void unsubscribeLowBatteryState(); -} diff --git a/src/main/java/io/github/hapjava/impl/accessories/Bridge.java b/src/main/java/io/github/hapjava/accessories/Bridge.java similarity index 52% rename from src/main/java/io/github/hapjava/impl/accessories/Bridge.java rename to src/main/java/io/github/hapjava/accessories/Bridge.java index 024693823..2c71c09df 100644 --- a/src/main/java/io/github/hapjava/impl/accessories/Bridge.java +++ b/src/main/java/io/github/hapjava/accessories/Bridge.java @@ -1,6 +1,4 @@ -package io.github.hapjava.impl.accessories; - -import io.github.hapjava.HomekitAccessory; +package io.github.hapjava.accessories; public interface Bridge extends HomekitAccessory { diff --git a/src/main/java/io/github/hapjava/accessories/CarbonDioxideSensor.java b/src/main/java/io/github/hapjava/accessories/CarbonDioxideSensorAccessory.java similarity index 50% rename from src/main/java/io/github/hapjava/accessories/CarbonDioxideSensor.java rename to src/main/java/io/github/hapjava/accessories/CarbonDioxideSensorAccessory.java index 27c332471..0bd56c4ee 100644 --- a/src/main/java/io/github/hapjava/accessories/CarbonDioxideSensor.java +++ b/src/main/java/io/github/hapjava/accessories/CarbonDioxideSensorAccessory.java @@ -1,10 +1,9 @@ package io.github.hapjava.accessories; -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.Service; -import io.github.hapjava.accessories.properties.CarbonDioxideDetectedState; -import io.github.hapjava.impl.services.CarbonDioxideSensorService; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.carbondioxidesensor.CarbonDioxideDetectedEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.CarbonDioxideSensorService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; @@ -13,23 +12,18 @@ * A carbon dioxide sensor reports whether carbon dioxide has been detected or not. * *

Carbon dioxide sensors that run on batteries will need to implement this interface and also - * implement {@link BatteryStatusAccessory}. + * implement {@link BatteryAccessory}. * * @author Eugen Freiter */ -public interface CarbonDioxideSensor extends HomekitAccessory { +public interface CarbonDioxideSensorAccessory extends HomekitAccessory { /** * Retrieves the state of the sensor that indicates if carbon dioxide has been detected. * * @return a future that will contain the carbon dioxide sensor's state */ - CompletableFuture getCarbonDioxideDetectedState(); - - @Override - default Collection getServices() { - return Collections.singleton(new CarbonDioxideSensorService(this)); - } + CompletableFuture getCarbonDioxideDetectedState(); /** * Subscribes to changes in the carbon dioxide's state. @@ -38,23 +32,11 @@ default Collection getServices() { */ void subscribeCarbonDioxideDetectedState(HomekitCharacteristicChangeCallback callback); - /** - * Retrieves the carbon dioxide level - * - * @return a future that will contain the carbon dioxide level as a value between 0 and 100000 - */ - CompletableFuture getCarbonDioxideLevel(); - /** Unsubscribes from changes in the carbon dioxide's state. */ void unsubscribeCarbonDioxideDetectedState(); - /** - * Subscribes to changes in the carbon dioxide level. - * - * @param callback the function to call when the state changes. - */ - void subscribeCarbonDioxideLevel(HomekitCharacteristicChangeCallback callback); - - /** Unsubscribes from changes in the carbon dioxide level. */ - void unsubscribeCarbonDioxideLevel(); + @Override + default Collection getServices() { + return Collections.singleton(new CarbonDioxideSensorService(this)); + } } diff --git a/src/main/java/io/github/hapjava/accessories/CarbonMonoxideSensor.java b/src/main/java/io/github/hapjava/accessories/CarbonMonoxideSensorAccessory.java similarity index 68% rename from src/main/java/io/github/hapjava/accessories/CarbonMonoxideSensor.java rename to src/main/java/io/github/hapjava/accessories/CarbonMonoxideSensorAccessory.java index fd9ed9fef..9bb23f727 100644 --- a/src/main/java/io/github/hapjava/accessories/CarbonMonoxideSensor.java +++ b/src/main/java/io/github/hapjava/accessories/CarbonMonoxideSensorAccessory.java @@ -1,10 +1,9 @@ package io.github.hapjava.accessories; -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.Service; -import io.github.hapjava.accessories.properties.CarbonMonoxideDetectedState; -import io.github.hapjava.impl.services.CarbonMonoxideSensorService; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.carbonmonoxidesensor.CarbonMonoxideDetectedEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.CarbonMonoxideSensorService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; @@ -13,23 +12,18 @@ * A carbon monoxide sensor reports whether carbon monoxide has been detected or not. * *

Carbon monoxide sensors that run on batteries will need to implement this interface and also - * implement {@link BatteryStatusAccessory}. + * implement {@link BatteryAccessory}. * * @author Gaston Dombiak */ -public interface CarbonMonoxideSensor extends HomekitAccessory { +public interface CarbonMonoxideSensorAccessory extends HomekitAccessory { /** * Retrieves the state of the sensor that indicates if carbon monoxide has been detected. * * @return a future that will contain the carbon monoxide sensor's state */ - CompletableFuture getCarbonMonoxideDetectedState(); - - @Override - default Collection getServices() { - return Collections.singleton(new CarbonMonoxideSensorService(this)); - } + CompletableFuture getCarbonMonoxideDetectedState(); /** * Subscribes to changes in the carbon monoxide's state. @@ -40,4 +34,9 @@ default Collection getServices() { /** Unsubscribes from changes in the carbon monoxide's state. */ void unsubscribeCarbonMonoxideDetectedState(); + + @Override + default Collection getServices() { + return Collections.singleton(new CarbonMonoxideSensorService(this)); + } } diff --git a/src/main/java/io/github/hapjava/accessories/ContactSensor.java b/src/main/java/io/github/hapjava/accessories/ContactSensorAccessory.java similarity index 73% rename from src/main/java/io/github/hapjava/accessories/ContactSensor.java rename to src/main/java/io/github/hapjava/accessories/ContactSensorAccessory.java index af85760db..61db18768 100644 --- a/src/main/java/io/github/hapjava/accessories/ContactSensor.java +++ b/src/main/java/io/github/hapjava/accessories/ContactSensorAccessory.java @@ -1,10 +1,9 @@ package io.github.hapjava.accessories; -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.Service; -import io.github.hapjava.accessories.properties.ContactState; -import io.github.hapjava.impl.services.ContactSensorService; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.contactsensor.ContactStateEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.ContactSensorService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; @@ -14,11 +13,11 @@ * window/door sensors. When contact is detected it means that the door/window is closed. * *

Contact sensors that run on batteries will need to implement this interface and also implement - * {@link BatteryStatusAccessory}. + * {@link BatteryAccessory}. * * @author Gaston Dombiak */ -public interface ContactSensor extends HomekitAccessory { +public interface ContactSensorAccessory extends HomekitAccessory { /** * Retrieves the state of the contact. This is whether the contact is detected or not. Detected @@ -26,12 +25,7 @@ public interface ContactSensor extends HomekitAccessory { * * @return a future that will contain the contact's state */ - CompletableFuture getCurrentState(); - - @Override - default Collection getServices() { - return Collections.singleton(new ContactSensorService(this)); - } + CompletableFuture getCurrentState(); /** * Subscribes to changes in the contact state. @@ -42,4 +36,9 @@ default Collection getServices() { /** Unsubscribes from changes in the contact state. */ void unsubscribeContactState(); + + @Override + default Collection getServices() { + return Collections.singleton(new ContactSensorService(this)); + } } diff --git a/src/main/java/io/github/hapjava/accessories/DoorAccessory.java b/src/main/java/io/github/hapjava/accessories/DoorAccessory.java new file mode 100644 index 000000000..dbd7b3b7e --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/DoorAccessory.java @@ -0,0 +1,82 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.windowcovering.PositionStateEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.DoorService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** + * A door with status + * + * @author Eugen Freiter + */ +public interface DoorAccessory extends HomekitAccessory { + + /** + * Retrieves the position state of the door + * + * @return a future which will contain the door's position state + */ + CompletableFuture getPositionState(); + + /** + * Retrieves the current position of the door + * + * @return a future which will contain the door's current position + */ + CompletableFuture getCurrentPosition(); + + /** + * Retrieves the target position of the door + * + * @return a future which will contain the door's target position + */ + CompletableFuture getTargetPosition(); + + /** + * Sets the targeted state of the door. + * + * @param position the targeted position + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setTargetPosition(Integer position) throws Exception; + + /** + * Subscribes to changes in the door's position state + * + * @param callback the function to call when the state changes + */ + void subscribePositionState(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the door's current position + * + * @param callback the function to call when the current position changes + */ + void subscribeCurrentPosition(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the door's target position + * + * @param callback the function to call when the target position changes + */ + void subscribeTargetPosition(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the door's state */ + void unsubscribePositionState(); + + /** Unsubscribes from changes in the door's current position */ + void unsubscribeCurrentPosition(); + + /** Unsubscribes from changes in the door's target position */ + void unsubscribeTargetPosition(); + + @Override + default Collection getServices() { + return Collections.singleton(new DoorService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/Fan.java b/src/main/java/io/github/hapjava/accessories/Fan.java deleted file mode 100644 index 9d5ac4276..000000000 --- a/src/main/java/io/github/hapjava/accessories/Fan.java +++ /dev/null @@ -1,99 +0,0 @@ -package io.github.hapjava.accessories; - -import io.github.hapjava.*; -import io.github.hapjava.accessories.properties.RotationDirection; -import io.github.hapjava.impl.services.FanService; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.CompletableFuture; - -/** - * A fan, with power and rotational characteristics. - * - * @author Andy Lintner - */ -public interface Fan extends HomekitAccessory { - - /** - * Retrieves the current binary state of the fan's power. - * - * @return a future that will contain the binary state - */ - CompletableFuture getFanPower(); - - /** - * Retrieves the current rotation direction of the fan. - * - * @return a future that will contain the direction - */ - CompletableFuture getRotationDirection(); - - /** - * Retrieves the current speed of the fan's rotation - * - * @return a future that will contain the speed, expressed as an integer between 0 and 100. - */ - CompletableFuture getRotationSpeed(); - - /** - * Sets the binary state of the fan's power - * - * @param state the binary state to set - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setFanPower(boolean state) throws Exception; - - /** - * Sets the rotation direction of the fan - * - * @param direction the direction to set - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setRotationDirection(RotationDirection direction) throws Exception; - - /** - * Sets the speed of the fan's rotation - * - * @param speed the speed to set, expressed as an integer between 0 and 100. - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setRotationSpeed(Integer speed) throws Exception; - - @Override - default Collection getServices() { - return Collections.singleton(new FanService(this)); - } - - /** - * Subscribes to changes in the binary state of the fan's power. - * - * @param callback the function to call when the state changes. - */ - void subscribeFanPower(HomekitCharacteristicChangeCallback callback); - - /** - * Subscribes to changes in the rotation direction of the fan. - * - * @param callback the function to call when the direction changes. - */ - void subscribeRotationDirection(HomekitCharacteristicChangeCallback callback); - - /** - * Subscribes to changes in the rotation speed of the fan. - * - * @param callback the function to call when the speed changes. - */ - void subscribeRotationSpeed(HomekitCharacteristicChangeCallback callback); - - /** Unsubscribes from changes in the binary state of the fan's power. */ - void unsubscribeFanPower(); - - /** Unsubscribes from changes in the rotation direction of the fan. */ - void unsubscribeRotationDirection(); - - /** Unsubscribes from changes in the fan's rotation speed. */ - void unsubscribeRotationSpeed(); -} diff --git a/src/main/java/io/github/hapjava/accessories/FanAccessory.java b/src/main/java/io/github/hapjava/accessories/FanAccessory.java new file mode 100644 index 000000000..5b1bb3ac6 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/FanAccessory.java @@ -0,0 +1,48 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithFanState; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.FanService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** + * A fan with basic characteristics. User {@link AccessoryWithFanState} for Fan with more + * characteristics. + * + * @author Andy Lintner + */ +public interface FanAccessory extends HomekitAccessory { + /** + * Mandatory: Retrieves the current active state of the fan'. + * + * @return a future that will contain the binary state + */ + CompletableFuture isActive(); + + /** + * Sets the active state of the fan + * + * @param state the binary state to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setActive(boolean state) throws Exception; + + /** + * Subscribes to changes in the active state of the fan. + * + * @param callback the function to call when the direction changes. + */ + void subscribeActive(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the active state of the fan. */ + void unsubscribeActive(); + + @Override + default Collection getServices() { + return Collections.singleton(new FanService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/GarageDoor.java b/src/main/java/io/github/hapjava/accessories/GarageDoorOpenerAccessory.java similarity index 73% rename from src/main/java/io/github/hapjava/accessories/GarageDoor.java rename to src/main/java/io/github/hapjava/accessories/GarageDoorOpenerAccessory.java index d94c1d071..f012d4ca6 100644 --- a/src/main/java/io/github/hapjava/accessories/GarageDoor.java +++ b/src/main/java/io/github/hapjava/accessories/GarageDoorOpenerAccessory.java @@ -1,8 +1,10 @@ package io.github.hapjava.accessories; -import io.github.hapjava.*; -import io.github.hapjava.accessories.properties.DoorState; -import io.github.hapjava.impl.services.GarageDoorService; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.garagedoor.CurrentDoorStateEnum; +import io.github.hapjava.characteristics.impl.garagedoor.TargetDoorStateEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.GarageDoorOpenerService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; @@ -12,21 +14,21 @@ * * @author Andy Lintner */ -public interface GarageDoor extends HomekitAccessory { +public interface GarageDoorOpenerAccessory extends HomekitAccessory { /** * Retrieves the current state of the door * * @return a future which will contain the door's state */ - CompletableFuture getCurrentDoorState(); + CompletableFuture getCurrentDoorState(); /** * Retrieves the targeted state of the door * * @return a future which will contain the door's targeted state */ - CompletableFuture getTargetDoorState(); + CompletableFuture getTargetDoorState(); /** * Retrieves an indicator of an obstruction detected by the door @@ -42,7 +44,7 @@ public interface GarageDoor extends HomekitAccessory { * @return a future that completes when the change is made * @throws Exception when the change cannot be made */ - CompletableFuture setTargetDoorState(DoorState state) throws Exception; + CompletableFuture setTargetDoorState(TargetDoorStateEnum state) throws Exception; /** * Subscribes to changes in the door's state @@ -76,6 +78,6 @@ public interface GarageDoor extends HomekitAccessory { @Override default Collection getServices() { - return Collections.singleton(new GarageDoorService(this)); + return Collections.singleton(new GarageDoorOpenerService(this)); } } diff --git a/src/main/java/io/github/hapjava/HomekitAccessory.java b/src/main/java/io/github/hapjava/accessories/HomekitAccessory.java similarity index 70% rename from src/main/java/io/github/hapjava/HomekitAccessory.java rename to src/main/java/io/github/hapjava/accessories/HomekitAccessory.java index beb08eac3..adb56a150 100644 --- a/src/main/java/io/github/hapjava/HomekitAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/HomekitAccessory.java @@ -1,6 +1,10 @@ -package io.github.hapjava; +package io.github.hapjava.accessories; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.AccessoryInformationService; import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; /** * Base interface for all Homekit Accessories. You can implement this interface directly, but most @@ -21,11 +25,11 @@ public interface HomekitAccessory { int getId(); /** - * Returns a label to display in iOS. + * Returns a name to display in iOS. * * @return the label. */ - String getLabel(); + CompletableFuture getName(); /** * Performs an operation that can be used to identify the accessory. This action can be performed @@ -38,21 +42,28 @@ public interface HomekitAccessory { * * @return the serial number, or null. */ - String getSerialNumber(); + CompletableFuture getSerialNumber(); /** * Returns a model name to display in iOS. * * @return the model name, or null. */ - String getModel(); + CompletableFuture getModel(); /** * Returns a manufacturer to display in iOS. * * @return the manufacturer, or null. */ - String getManufacturer(); + CompletableFuture getManufacturer(); + + /** + * Returns a firmware revision to display in iOS. + * + * @return the firmware revision, or null. + */ + CompletableFuture getFirmwareRevision(); /** * The collection of Services this accessory supports. Services are the primary way to interact @@ -65,5 +76,7 @@ public interface HomekitAccessory { * * @return the collection of services. */ - Collection getServices(); + default Collection getServices() { + return Collections.singleton(new AccessoryInformationService(this)); + }; } diff --git a/src/main/java/io/github/hapjava/accessories/HumiditySensor.java b/src/main/java/io/github/hapjava/accessories/HumiditySensorAccessory.java similarity index 78% rename from src/main/java/io/github/hapjava/accessories/HumiditySensor.java rename to src/main/java/io/github/hapjava/accessories/HumiditySensorAccessory.java index dd7335635..07c24be58 100644 --- a/src/main/java/io/github/hapjava/accessories/HumiditySensor.java +++ b/src/main/java/io/github/hapjava/accessories/HumiditySensorAccessory.java @@ -1,7 +1,8 @@ package io.github.hapjava.accessories; -import io.github.hapjava.*; -import io.github.hapjava.impl.services.HumiditySensorService; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.HumiditySensorService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; @@ -11,7 +12,7 @@ * * @author Andy Lintner */ -public interface HumiditySensor extends HomekitAccessory { +public interface HumiditySensorAccessory extends HomekitAccessory { /** * Retrieves the current relative humidity. @@ -20,11 +21,6 @@ public interface HumiditySensor extends HomekitAccessory { */ CompletableFuture getCurrentRelativeHumidity(); - @Override - default Collection getServices() { - return Collections.singleton(new HumiditySensorService(this)); - } - /** * Subscribes to changes in the current relative humidity. * @@ -34,4 +30,9 @@ default Collection getServices() { /** Unsubscribes from changes in the current relative humidity. */ void unsubscribeCurrentRelativeHumidity(); + + @Override + default Collection getServices() { + return Collections.singleton(new HumiditySensorService(this)); + } } diff --git a/src/main/java/io/github/hapjava/accessories/LeakSensor.java b/src/main/java/io/github/hapjava/accessories/LeakSensorAccessory.java similarity index 69% rename from src/main/java/io/github/hapjava/accessories/LeakSensor.java rename to src/main/java/io/github/hapjava/accessories/LeakSensorAccessory.java index aad2266e9..04049d8d2 100644 --- a/src/main/java/io/github/hapjava/accessories/LeakSensor.java +++ b/src/main/java/io/github/hapjava/accessories/LeakSensorAccessory.java @@ -1,9 +1,9 @@ package io.github.hapjava.accessories; -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.Service; -import io.github.hapjava.impl.services.LeakSensorService; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.leaksensor.LeakDetectedStateEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.LeakSensorService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; @@ -12,23 +12,18 @@ * A leak sensor that reports whether a leak has been detected. * *

Leak sensors that run on batteries will need to implement this interface and also implement - * {@link BatteryStatusAccessory}. + * {@link BatteryAccessory}. * * @author Tim Harper */ -public interface LeakSensor extends HomekitAccessory { +public interface LeakSensorAccessory extends HomekitAccessory { /** * Retrieves the state of the leak sensor. If true then leak has been detected. * * @return a future that will contain the leak sensor's state */ - CompletableFuture getLeakDetected(); - - @Override - default Collection getServices() { - return Collections.singleton(new LeakSensorService(this)); - } + CompletableFuture getLeakDetected(); /** * Subscribes to changes in the leak sensor. @@ -39,4 +34,9 @@ default Collection getServices() { /** Unsubscribes from changes in the leak sensor. */ void unsubscribeLeakDetected(); + + @Override + default Collection getServices() { + return Collections.singleton(new LeakSensorService(this)); + } } diff --git a/src/main/java/io/github/hapjava/accessories/LightSensor.java b/src/main/java/io/github/hapjava/accessories/LightSensorAccessory.java similarity index 78% rename from src/main/java/io/github/hapjava/accessories/LightSensor.java rename to src/main/java/io/github/hapjava/accessories/LightSensorAccessory.java index 12b2e05d3..bcdab09c4 100644 --- a/src/main/java/io/github/hapjava/accessories/LightSensor.java +++ b/src/main/java/io/github/hapjava/accessories/LightSensorAccessory.java @@ -1,9 +1,8 @@ package io.github.hapjava.accessories; -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.Service; -import io.github.hapjava.impl.services.LightSensorService; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.LightSensorService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; @@ -13,7 +12,7 @@ * * @author Gaston Dombiak */ -public interface LightSensor extends HomekitAccessory { +public interface LightSensorAccessory extends HomekitAccessory { /** * Retrieves the current ambient light level. @@ -22,11 +21,6 @@ public interface LightSensor extends HomekitAccessory { */ CompletableFuture getCurrentAmbientLightLevel(); - @Override - default Collection getServices() { - return Collections.singleton(new LightSensorService(this)); - } - /** * Subscribes to changes in the current ambient light level. * @@ -36,4 +30,9 @@ default Collection getServices() { /** Unsubscribes from changes in the current ambient light level. */ void unsubscribeCurrentAmbientLightLevel(); + + @Override + default Collection getServices() { + return Collections.singleton(new LightSensorService(this)); + } } diff --git a/src/main/java/io/github/hapjava/accessories/Lightbulb.java b/src/main/java/io/github/hapjava/accessories/LightbulbAccessory.java similarity index 83% rename from src/main/java/io/github/hapjava/accessories/Lightbulb.java rename to src/main/java/io/github/hapjava/accessories/LightbulbAccessory.java index c39b659eb..8a6caaed8 100644 --- a/src/main/java/io/github/hapjava/accessories/Lightbulb.java +++ b/src/main/java/io/github/hapjava/accessories/LightbulbAccessory.java @@ -1,7 +1,8 @@ package io.github.hapjava.accessories; -import io.github.hapjava.*; -import io.github.hapjava.impl.services.LightbulbService; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.LightbulbService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; @@ -11,7 +12,7 @@ * * @author Andy Lintner */ -public interface Lightbulb extends HomekitAccessory { +public interface LightbulbAccessory extends HomekitAccessory { /** * Retrieves the current binary state of the light. @@ -29,11 +30,6 @@ public interface Lightbulb extends HomekitAccessory { */ CompletableFuture setLightbulbPowerState(boolean powerState) throws Exception; - @Override - default Collection getServices() { - return Collections.singleton(new LightbulbService(this)); - } - /** * Subscribes to changes in the binary state of the light. * @@ -43,4 +39,9 @@ default Collection getServices() { /** Unsubscribes from changes in the binary state of the light. */ void unsubscribeLightbulbPowerState(); + + @Override + default Collection getServices() { + return Collections.singleton(new LightbulbService(this)); + } } diff --git a/src/main/java/io/github/hapjava/accessories/LockMechanism.java b/src/main/java/io/github/hapjava/accessories/LockMechanism.java deleted file mode 100644 index f1eb051e7..000000000 --- a/src/main/java/io/github/hapjava/accessories/LockMechanism.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.hapjava.accessories; - -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.Service; -import io.github.hapjava.accessories.properties.LockMechanismState; -import io.github.hapjava.impl.services.LockMechanismService; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.CompletableFuture; - -/** - * A lock capable of exposing its binary locked state. For a lock that can be locked/unlocked, use - * {@link LockableLockMechanism}. - * - *

Locks that run on batteries will need to implement this interface and also implement {@link - * BatteryStatusAccessory}. - * - * @author Andy Lintner - */ -public interface LockMechanism extends HomekitAccessory { - - /** - * Retrieves the current binary state of the lock. - * - * @return a future that will contain the binary state. - */ - CompletableFuture getCurrentMechanismState(); - - /** - * Subscribes to changes in the binary state of the lock. - * - * @param callback the function to call when the state changes. - */ - void subscribeCurrentMechanismState(HomekitCharacteristicChangeCallback callback); - - /** Unsubscribes from changes in the binary state of the lock. */ - void unsubscribeCurrentMechanismState(); - - @Override - default Collection getServices() { - return Collections.singleton(new LockMechanismService(this)); - } -} diff --git a/src/main/java/io/github/hapjava/accessories/LockMechanismAccessory.java b/src/main/java/io/github/hapjava/accessories/LockMechanismAccessory.java new file mode 100644 index 000000000..d38838558 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/LockMechanismAccessory.java @@ -0,0 +1,64 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.lock.LockCurrentStateEnum; +import io.github.hapjava.characteristics.impl.lock.LockTargetStateEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.LockMechanismService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** + * Lock mechanism + * + * @author Eugen Freiter + */ +public interface LockMechanismAccessory extends HomekitAccessory { + + /** + * Retrieves the lock current state. + * + * @return a future that will contain the lock current state . + */ + CompletableFuture getLockCurrentState(); + + /** + * Retrieves the lock trget state. + * + * @return a future that will contain the lock target state . + */ + CompletableFuture getLockTargetState(); + + /** + * set lock target state the lock trget state. + * + * @param state lock target state + */ + CompletableFuture setLockTargetState(LockTargetStateEnum state); + + /** + * Subscribes to changes in the lock current state. + * + * @param callback the function to call when the state changes. + */ + void subscribeLockCurrentState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in lock current state. */ + void unsubscribeLockCurrentState(); + + /** + * Subscribes to changes in the lock target state. + * + * @param callback the function to call when the state changes. + */ + void subscribeLockTargetState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in lock target state. */ + void unsubscribeLockTargetState(); + + @Override + default Collection getServices() { + return Collections.singleton(new LockMechanismService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/LockableLockMechanism.java b/src/main/java/io/github/hapjava/accessories/LockableLockMechanism.java deleted file mode 100644 index 474c8abd6..000000000 --- a/src/main/java/io/github/hapjava/accessories/LockableLockMechanism.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.hapjava.accessories; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.properties.LockMechanismState; -import java.util.concurrent.CompletableFuture; - -/** - * Extends {@link LockMechanism} with the ability to lock and unlock the mechanism. - * - * @author Andy Lintner - */ -public interface LockableLockMechanism extends LockMechanism { - - /** - * Sets the binary state of the lock mechanism. - * - * @param state true for a locked mechanism, false for unlocked. - * @throws Exception when the change cannot be made. - */ - void setTargetMechanismState(LockMechanismState state) throws Exception; - - /** - * Retrieves the pending, but not yet completed, state of the lock mechanism. - * - * @return the binary state - */ - CompletableFuture getTargetMechanismState(); - - /** - * Subscribes to changes in the pending, but not yet completed, binary state. - * - * @param callback the function to call when the state changes. - */ - void subscribeTargetMechanismState(HomekitCharacteristicChangeCallback callback); - - /** Unsubscribes from changes in the pending, but not yet completed, binary state. */ - void unsubscribeTargetMechanismState(); -} diff --git a/src/main/java/io/github/hapjava/accessories/MotionSensor.java b/src/main/java/io/github/hapjava/accessories/MotionSensorAccessory.java similarity index 77% rename from src/main/java/io/github/hapjava/accessories/MotionSensor.java rename to src/main/java/io/github/hapjava/accessories/MotionSensorAccessory.java index dc424625b..b8fdedab8 100644 --- a/src/main/java/io/github/hapjava/accessories/MotionSensor.java +++ b/src/main/java/io/github/hapjava/accessories/MotionSensorAccessory.java @@ -1,9 +1,8 @@ package io.github.hapjava.accessories; -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.Service; -import io.github.hapjava.impl.services.MotionSensorService; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.MotionSensorService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; @@ -12,11 +11,11 @@ * A motion sensor that reports whether motion has been detected. * *

Motion sensors that run on batteries will need to implement this interface and also implement - * {@link BatteryStatusAccessory}. + * {@link BatteryAccessory}. * * @author Gaston Dombiak */ -public interface MotionSensor extends HomekitAccessory { +public interface MotionSensorAccessory extends HomekitAccessory { /** * Retrieves the state of the motion sensor. If true then motion has been detected. @@ -25,11 +24,6 @@ public interface MotionSensor extends HomekitAccessory { */ CompletableFuture getMotionDetected(); - @Override - default Collection getServices() { - return Collections.singleton(new MotionSensorService(this)); - } - /** * Subscribes to changes in the motion sensor. * @@ -39,4 +33,9 @@ default Collection getServices() { /** Unsubscribes from changes in the motion sensor. */ void unsubscribeMotionDetected(); + + @Override + default Collection getServices() { + return Collections.singleton(new MotionSensorService(this)); + } } diff --git a/src/main/java/io/github/hapjava/accessories/OccupancySensor.java b/src/main/java/io/github/hapjava/accessories/OccupancySensorAccessory.java similarity index 68% rename from src/main/java/io/github/hapjava/accessories/OccupancySensor.java rename to src/main/java/io/github/hapjava/accessories/OccupancySensorAccessory.java index 0148a6f9f..df2f59709 100644 --- a/src/main/java/io/github/hapjava/accessories/OccupancySensor.java +++ b/src/main/java/io/github/hapjava/accessories/OccupancySensorAccessory.java @@ -1,9 +1,9 @@ package io.github.hapjava.accessories; -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.Service; -import io.github.hapjava.impl.services.OccupancySensorService; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.occupancysensor.OccupancyDetectedEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.OccupancySensorService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; @@ -12,22 +12,17 @@ * An occupancy sensor that reports whether occupancy has been detected. * *

Occupancy sensors that run on batteries will need to implement this interface and also - * implement {@link BatteryStatusAccessory}. + * implement {@link BatteryAccessory}. * * @author Tim Harper */ -public interface OccupancySensor extends HomekitAccessory { +public interface OccupancySensorAccessory extends HomekitAccessory { /** * Retrieves the state of the occupancy sensor. If true then occupancy has been detected. * * @return a future that will contain the occupancy sensor's state */ - CompletableFuture getOccupancyDetected(); - - @Override - default Collection getServices() { - return Collections.singleton(new OccupancySensorService(this)); - } + CompletableFuture getOccupancyDetected(); /** * Subscribes to changes in the occupancy sensor. @@ -38,4 +33,9 @@ default Collection getServices() { /** Unsubscribes from changes in the occupancy sensor. */ void unsubscribeOccupancyDetected(); + + @Override + default Collection getServices() { + return Collections.singleton(new OccupancySensorService(this)); + } } diff --git a/src/main/java/io/github/hapjava/accessories/Outlet.java b/src/main/java/io/github/hapjava/accessories/OutletAccessory.java similarity index 88% rename from src/main/java/io/github/hapjava/accessories/Outlet.java rename to src/main/java/io/github/hapjava/accessories/OutletAccessory.java index 4578bfea4..213f673b7 100644 --- a/src/main/java/io/github/hapjava/accessories/Outlet.java +++ b/src/main/java/io/github/hapjava/accessories/OutletAccessory.java @@ -1,7 +1,8 @@ package io.github.hapjava.accessories; -import io.github.hapjava.*; -import io.github.hapjava.impl.services.OutletService; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.OutletService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; @@ -11,12 +12,7 @@ * * @author Andy Lintner */ -public interface Outlet extends HomekitAccessory { - - @Override - default Collection getServices() { - return Collections.singleton(new OutletService(this)); - } +public interface OutletAccessory extends HomekitAccessory { /** * Retrieves the current binary state of the outlet's power. @@ -60,4 +56,9 @@ default Collection getServices() { /** Unsubscribes from changes in the binary state indicating whether hte outlet is in use. */ void unsubscribeOutletInUse(); + + @Override + default Collection getServices() { + return Collections.singleton(new OutletService(this)); + } } diff --git a/src/main/java/io/github/hapjava/accessories/SecuritySystem.java b/src/main/java/io/github/hapjava/accessories/SecuritySystemAccessory.java similarity index 59% rename from src/main/java/io/github/hapjava/accessories/SecuritySystem.java rename to src/main/java/io/github/hapjava/accessories/SecuritySystemAccessory.java index fcf1d1f6f..93f59fa1b 100644 --- a/src/main/java/io/github/hapjava/accessories/SecuritySystem.java +++ b/src/main/java/io/github/hapjava/accessories/SecuritySystemAccessory.java @@ -1,12 +1,10 @@ package io.github.hapjava.accessories; -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.Service; -import io.github.hapjava.accessories.properties.CurrentSecuritySystemState; -import io.github.hapjava.accessories.properties.SecuritySystemAlarmType; -import io.github.hapjava.accessories.properties.TargetSecuritySystemState; -import io.github.hapjava.impl.services.SecuritySystemService; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.securitysystem.CurrentSecuritySystemStateEnum; +import io.github.hapjava.characteristics.impl.securitysystem.TargetSecuritySystemStateEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.SecuritySystemService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; @@ -14,11 +12,11 @@ /** * A security system that can be armed so that when a contact sensor is opened or a motion sensor * detects movement, then a siren could be fired off. There are different modes for arming the - * system. See {@link TargetSecuritySystemState} for more information. + * system. See {@link TargetSecuritySystemStateEnum} for more information. * * @author Gaston Dombiak */ -public interface SecuritySystem extends HomekitAccessory { +public interface SecuritySystemAccessory extends HomekitAccessory { /** * Retrieves the current state of the security system. The state describes if the system is armed @@ -26,7 +24,7 @@ public interface SecuritySystem extends HomekitAccessory { * * @return current state of the security system. */ - CompletableFuture getCurrentSecuritySystemState(); + CompletableFuture getCurrentSecuritySystemState(); /** * Subscribes to changes to the state of the security system. @@ -45,14 +43,14 @@ public interface SecuritySystem extends HomekitAccessory { * @param state target state of the security system. * @throws Exception when the change cannot be made. */ - void setTargetSecuritySystemState(TargetSecuritySystemState state) throws Exception; + void setTargetSecuritySystemState(TargetSecuritySystemStateEnum state) throws Exception; /** * Retrieves the pending, but not yet completed, state of the security system. * * @return target state of the security system. */ - CompletableFuture getTargetSecuritySystemState(); + CompletableFuture getTargetSecuritySystemState(); /** * Subscribes to changes in the pending, but not yet completed, state of the security system. @@ -66,23 +64,6 @@ public interface SecuritySystem extends HomekitAccessory { */ void unsubscribeTargetSecuritySystemState(); - /** - * Retrieves the alarm type of the security system. - * - * @return alarm type of the security system. - */ - CompletableFuture getAlarmTypeState(); - - /** - * Subscribes to changes to the alarm type of the security system. - * - * @param callback the function to call when the alarm type changes. - */ - void subscribeAlarmTypeState(HomekitCharacteristicChangeCallback callback); - - /** Unsubscribes from changes in the alarm type of the security system. */ - void unsubscribeAlarmTypeState(); - @Override default Collection getServices() { return Collections.singleton(new SecuritySystemService(this)); diff --git a/src/main/java/io/github/hapjava/accessories/SmokeSensor.java b/src/main/java/io/github/hapjava/accessories/SmokeSensorAccessory.java similarity index 69% rename from src/main/java/io/github/hapjava/accessories/SmokeSensor.java rename to src/main/java/io/github/hapjava/accessories/SmokeSensorAccessory.java index 68f43d1a7..ca1945351 100644 --- a/src/main/java/io/github/hapjava/accessories/SmokeSensor.java +++ b/src/main/java/io/github/hapjava/accessories/SmokeSensorAccessory.java @@ -1,10 +1,9 @@ package io.github.hapjava.accessories; -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.Service; -import io.github.hapjava.accessories.properties.SmokeDetectedState; -import io.github.hapjava.impl.services.SmokeSensorService; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.smokesensor.SmokeDetectedStateEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.SmokeSensorService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; @@ -13,23 +12,18 @@ * A smoke sensor reports whether smoke has been detected or not. * *

Smoke sensors that run on batteries will need to implement this interface and also implement - * {@link BatteryStatusAccessory}. + * {@link BatteryAccessory}. * * @author Gaston Dombiak */ -public interface SmokeSensor extends HomekitAccessory { +public interface SmokeSensorAccessory extends HomekitAccessory { /** * Retrieves the state of the smoke sensor. This is whether smoke has been detected or not. * * @return a future that will contain the smoke sensor's state */ - CompletableFuture getSmokeDetectedState(); - - @Override - default Collection getServices() { - return Collections.singleton(new SmokeSensorService(this)); - } + CompletableFuture getSmokeDetectedState(); /** * Subscribes to changes in the smoke sensor's state. @@ -40,4 +34,9 @@ default Collection getServices() { /** Unsubscribes from changes in the smoke sensor's state. */ void unsubscribeSmokeDetectedState(); + + @Override + default Collection getServices() { + return Collections.singleton(new SmokeSensorService(this)); + } } diff --git a/src/main/java/io/github/hapjava/accessories/Switch.java b/src/main/java/io/github/hapjava/accessories/SwitchAccessory.java similarity index 82% rename from src/main/java/io/github/hapjava/accessories/Switch.java rename to src/main/java/io/github/hapjava/accessories/SwitchAccessory.java index 682d758c2..aae442f90 100644 --- a/src/main/java/io/github/hapjava/accessories/Switch.java +++ b/src/main/java/io/github/hapjava/accessories/SwitchAccessory.java @@ -1,7 +1,8 @@ package io.github.hapjava.accessories; -import io.github.hapjava.*; -import io.github.hapjava.impl.services.SwitchService; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.SwitchService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; @@ -11,7 +12,7 @@ * * @author Andy Lintner */ -public interface Switch extends HomekitAccessory { +public interface SwitchAccessory extends HomekitAccessory { /** * Retrieves the current binary state of the switch. @@ -29,11 +30,6 @@ public interface Switch extends HomekitAccessory { */ CompletableFuture setSwitchState(boolean state) throws Exception; - @Override - default Collection getServices() { - return Collections.singleton(new SwitchService(this)); - } - /** * Subscribes to changes in the binary state of the switch. * @@ -43,4 +39,9 @@ default Collection getServices() { /** Unsubscribes from changes in the binary state of the switch. */ void unsubscribeSwitchState(); + + @Override + default Collection getServices() { + return Collections.singleton(new SwitchService(this)); + } } diff --git a/src/main/java/io/github/hapjava/accessories/TemperatureSensor.java b/src/main/java/io/github/hapjava/accessories/TemperatureSensor.java deleted file mode 100644 index a53b40a00..000000000 --- a/src/main/java/io/github/hapjava/accessories/TemperatureSensor.java +++ /dev/null @@ -1,83 +0,0 @@ -package io.github.hapjava.accessories; - -import io.github.hapjava.*; -import io.github.hapjava.accessories.properties.TemperatureUnit; -import io.github.hapjava.impl.services.TemperatureSensorService; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.CompletableFuture; - -/** - * A temperature sensor that reports the current temperature - * - * @author Andy Lintner - */ -public interface TemperatureSensor extends HomekitAccessory { - - /** - * Retrieves the current temperature, in celsius degrees. - * - * @return a future that will contain the temperature. - */ - CompletableFuture getCurrentTemperature(); - - @Override - default Collection getServices() { - return Collections.singleton(new TemperatureSensorService(this)); - } - - /** - * Subscribes to changes in the current temperature. - * - * @param callback the function to call when the state changes. - */ - void subscribeCurrentTemperature(HomekitCharacteristicChangeCallback callback); - - /** Unsubscribes from changes in the current temperature. */ - void unsubscribeCurrentTemperature(); - - /** - * Retrieves the minimum temperature, in celsius degrees, the thermostat can be set to. - * - * @return the minimum temperature. - */ - double getMinimumTemperature(); - - /** - * Retrieves the maximum temperature, in celsius degrees, the thermostat can be set to. - * - * @return the maximum temperature. - */ - double getMaximumTemperature(); - - /** - * Retrieves the temperature unit of the thermostat. The impact of this is unclear, as the actual - * temperature is always communicated in celsius degrees, and the iOS device uses the user's - * locale to determine the unit to convert to. - * - * @return the temperature unit of the thermostat. - */ - default TemperatureUnit getTemperatureUnit() { - return TemperatureUnit.CELSIUS; - } - - /** - * set default temperature unit of the thermostat. this is the unit thermostat use to display - * temprature. the homekit interface uses celsius. - * - * @param unit the temperature unit of the thermostat. - */ - default void setTemperatureUnit(TemperatureUnit unit) { - // override depending on the thermostat if required. - } - - /** - * subscribe to unit changes. - * - * @param callback callback - */ - default void subscribeTemperatureUnit(final HomekitCharacteristicChangeCallback callback) {} - - /** unsubscribe from unit changes. */ - default void unsubscribeTemperatureUnit() {} -} diff --git a/src/main/java/io/github/hapjava/accessories/TemperatureSensorAccessory.java b/src/main/java/io/github/hapjava/accessories/TemperatureSensorAccessory.java new file mode 100644 index 000000000..f2941f957 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/TemperatureSensorAccessory.java @@ -0,0 +1,38 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.TemperatureSensorService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** + * A temperature sensor that reports the current temperature + * + * @author Andy Lintner + */ +public interface TemperatureSensorAccessory extends HomekitAccessory { + + /** + * Retrieves the current temperature, in celsius degrees. + * + * @return a future that will contain the temperature. + */ + CompletableFuture getCurrentTemperature(); + + /** + * Subscribes to changes in the current temperature. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentTemperature(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current temperature. */ + void unsubscribeCurrentTemperature(); + + @Override + default Collection getServices() { + return Collections.singleton(new TemperatureSensorService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/Thermostat.java b/src/main/java/io/github/hapjava/accessories/Thermostat.java deleted file mode 100644 index 54b1ca625..000000000 --- a/src/main/java/io/github/hapjava/accessories/Thermostat.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.hapjava.accessories; - -import io.github.hapjava.accessories.thermostat.*; - -/** - * A thermostat with heating and cooling controls. - * - * @author Andy Lintner - * @deprecated Use {@link BasicThermostat}, {@link HeatingThermostat}, and {@link CoolingThermostat} - * instead - */ -@Deprecated -public interface Thermostat extends BasicThermostat, HeatingThermostat, CoolingThermostat {} diff --git a/src/main/java/io/github/hapjava/accessories/ThermostatAccessory.java b/src/main/java/io/github/hapjava/accessories/ThermostatAccessory.java new file mode 100644 index 000000000..43bca6926 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/ThermostatAccessory.java @@ -0,0 +1,138 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.thermostat.CurrentHeatingCoolingStateEnum; +import io.github.hapjava.characteristics.impl.thermostat.TargetHeatingCoolingStateEnum; +import io.github.hapjava.characteristics.impl.thermostat.TemperatureDisplayUnitEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.ThermostatService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** + * A thermostat with heating and cooling controls. + * + * @author Andy Lintner + */ +public interface ThermostatAccessory extends HomekitAccessory { + + /** + * Retrieves the current temperature, in celsius degrees. + * + * @return a future that will contain the temperature. + */ + CompletableFuture getCurrentTemperature(); + + /** + * Retrieves the target temperature, in celsius degrees. + * + * @return a future that will contain the target temperature. + */ + CompletableFuture getTargetTemperature(); + + /** + * Sets the target temperature. + * + * @param value the target temperature, in celsius degrees. + * @throws Exception when the temperature cannot be changed. + */ + void setTargetTemperature(Double value) throws Exception; + + /** + * Retrieves the current {@link CurrentHeatingCoolingStateEnum} of the thermostat. + * + * @return a future that will contain the mode. + */ + CompletableFuture getCurrentState(); + + /** + * Sets the {@link TargetHeatingCoolingStateEnum} of the thermostat. + * + * @param mode The {@link TargetHeatingCoolingStateEnum} to set. + * @throws Exception when the change cannot be made. + */ + void setTargetState(TargetHeatingCoolingStateEnum mode) throws Exception; + + /** + * Retrieves the pending, but not yet complete, {@link TargetHeatingCoolingStateEnum} of the + * thermostat. + * + * @return a future that will contain the target mode. + */ + CompletableFuture getTargetState(); + + /** + * Retrieves the temperture display units, e.g. C or F. + * + * @return a future that will contain the temperature unit. + */ + CompletableFuture getTemperatureDisplayUnit(); + + /** + * Sets the temperature unit. + * + * @param value the temperature display unit. + * @throws Exception when the temperature cannot be changed. + */ + void setTemperatureDisplayUnit(TemperatureDisplayUnitEnum value) throws Exception; + + /** + * Subscribes to changes in the pending, but not yet complete, {@link + * TargetHeatingCoolingStateEnum} of the thermostat. + * + * @param callback the function to call when the state changes. + */ + void subscribeTargetState(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the target temperature. + * + * @param callback the function to call when the state changes. + */ + void subscribeTargetTemperature(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the temperature display unit. + * + * @param callback the function to call when the uni changes. + */ + void subscribeTemperatureDisplayUnit(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the current temperature. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentTemperature(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the {@link CurrentHeatingCoolingStateEnum} of the thermostat. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the mode of the thermostat. */ + void unsubscribeCurrentState(); + + /** + * Unsubscribes from changes in the pending, but not yet complete, {@link + * TargetHeatingCoolingStateEnum} of the thermostat. + */ + void unsubscribeTargetState(); + + /** Unsubscribes from changes in the current temperature. */ + void unsubscribeCurrentTemperature(); + + /** Unsubscribes from changes in temperature display unit */ + void unsubscribeTemperatureDisplayUnit(); + + /** Unsubscribes from changes in the target temperature. */ + void unsubscribeTargetTemperature(); + + @Override + default Collection getServices() { + return Collections.singleton(new ThermostatService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/Valve.java b/src/main/java/io/github/hapjava/accessories/ValveAccessory.java similarity index 78% rename from src/main/java/io/github/hapjava/accessories/Valve.java rename to src/main/java/io/github/hapjava/accessories/ValveAccessory.java index 8a62f0772..a99613b3b 100644 --- a/src/main/java/io/github/hapjava/accessories/Valve.java +++ b/src/main/java/io/github/hapjava/accessories/ValveAccessory.java @@ -1,10 +1,11 @@ package io.github.hapjava.accessories; -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.Service; -import io.github.hapjava.accessories.properties.ValveType; -import io.github.hapjava.impl.services.ValveService; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.common.ActiveEnum; +import io.github.hapjava.characteristics.impl.common.InUseEnum; +import io.github.hapjava.characteristics.impl.valve.ValveTypeEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.ValveService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; @@ -14,12 +15,7 @@ * * @author Tim Harper */ -public interface Valve extends HomekitAccessory { - - @Override - default Collection getServices() { - return Collections.singleton(new ValveService(this)); - } +public interface ValveAccessory extends HomekitAccessory { /** * Retrieves the current active state of the valve; Active could mean the valve is open (but not @@ -30,7 +26,7 @@ default Collection getServices() { * * @return a future that will contain the binary state */ - CompletableFuture getValveActive(); + CompletableFuture getValveActive(); /** * Sets the valve active state @@ -39,7 +35,7 @@ default Collection getServices() { * @return a future that completes when the change is made * @throws Exception when the change cannot be made */ - CompletableFuture setValveActive(boolean active) throws Exception; + CompletableFuture setValveActive(ActiveEnum active) throws Exception; /** * Subscribes to changes in the active state of the valve. @@ -59,7 +55,7 @@ default Collection getServices() { * * @return a future that will contain the binary state */ - CompletableFuture getValveInUse(); + CompletableFuture getValveInUse(); /** * Subscribes to changes in the inUse state of the valve. @@ -78,7 +74,7 @@ default Collection getServices() { * * @return a future that will contain the binary state */ - CompletableFuture getValveType(); + CompletableFuture getValveType(); /** * Subscribes to changes in the valveType state of the valve. @@ -89,4 +85,9 @@ default Collection getServices() { /** Unsubscribes from changes in the valveType state light. */ void unsubscribeValveType(); + + @Override + default Collection getServices() { + return Collections.singleton(new ValveService(this)); + } } diff --git a/src/main/java/io/github/hapjava/accessories/WindowAccessory.java b/src/main/java/io/github/hapjava/accessories/WindowAccessory.java new file mode 100644 index 000000000..f5a7559b3 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/WindowAccessory.java @@ -0,0 +1,82 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.windowcovering.PositionStateEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.WindowService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** + * A Window with status of a door + * + * @author Eugen Freiter + */ +public interface WindowAccessory extends HomekitAccessory { + + /** + * Retrieves the position state of the door + * + * @return a future which will contain the door's position state + */ + CompletableFuture getPositionState(); + + /** + * Retrieves the current position of the door + * + * @return a future which will contain the door's current position + */ + CompletableFuture getCurrentPosition(); + + /** + * Retrieves the target position of the door + * + * @return a future which will contain the door's target position + */ + CompletableFuture getTargetPosition(); + + /** + * Sets the targeted state of the door. + * + * @param position the targeted position + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setTargetPosition(Integer position) throws Exception; + + /** + * Subscribes to changes in the door's position state + * + * @param callback the function to call when the state changes + */ + void subscribePositionState(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the door's current position + * + * @param callback the function to call when the current position changes + */ + void subscribeCurrentPosition(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the door's target position + * + * @param callback the function to call when the target position changes + */ + void subscribeTargetPosition(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the door's state */ + void unsubscribePositionState(); + + /** Unsubscribes from changes in the door's current position */ + void unsubscribeCurrentPosition(); + + /** Unsubscribes from changes in the door's target position */ + void unsubscribeTargetPosition(); + + @Override + default Collection getServices() { + return Collections.singleton(new WindowService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/WindowCovering.java b/src/main/java/io/github/hapjava/accessories/WindowCovering.java deleted file mode 100644 index afabbf097..000000000 --- a/src/main/java/io/github/hapjava/accessories/WindowCovering.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.hapjava.accessories; - -/** - * A window covering, like blinds, which can be remotely controlled. - * - * @author Andy Lintner - * @deprecated In 1.2.x, this interface will become replaced with BasicWindowCovering. Update your - * code to use that interface for now, and include the HoldPositionWindowCovering and - * ObstructionDetectedWindowCovering interfaces respectively - */ -@Deprecated -public interface WindowCovering - extends BasicWindowCovering, HoldPositionWindowCovering, ObstructionDetectedWindowCovering { - /* - * TODO - 1.2.x: Replace this interface with BasicWindowCovering */ -} diff --git a/src/main/java/io/github/hapjava/accessories/BasicWindowCovering.java b/src/main/java/io/github/hapjava/accessories/WindowCoveringAccessory.java similarity index 81% rename from src/main/java/io/github/hapjava/accessories/BasicWindowCovering.java rename to src/main/java/io/github/hapjava/accessories/WindowCoveringAccessory.java index 7d58b2b2d..1b618d66f 100644 --- a/src/main/java/io/github/hapjava/accessories/BasicWindowCovering.java +++ b/src/main/java/io/github/hapjava/accessories/WindowCoveringAccessory.java @@ -1,16 +1,15 @@ package io.github.hapjava.accessories; -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.Service; -import io.github.hapjava.accessories.properties.WindowCoveringPositionState; -import io.github.hapjava.impl.services.WindowCoveringService; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.windowcovering.PositionStateEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.WindowCoveringService; import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; -public interface BasicWindowCovering extends HomekitAccessory { - +/** A window covering, like blinds, which can be remotely controlled. */ +public interface WindowCoveringAccessory extends HomekitAccessory { /** * Retrieves the current position * @@ -30,7 +29,7 @@ public interface BasicWindowCovering extends HomekitAccessory { * * @return a future that will contain the current state */ - CompletableFuture getPositionState(); + CompletableFuture getPositionState(); /** * Sets the target position diff --git a/src/main/java/io/github/hapjava/accessories/DimmableLightbulb.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithBrightness.java similarity index 81% rename from src/main/java/io/github/hapjava/accessories/DimmableLightbulb.java rename to src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithBrightness.java index 2eea5abdb..4fa4557c2 100644 --- a/src/main/java/io/github/hapjava/accessories/DimmableLightbulb.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithBrightness.java @@ -1,14 +1,14 @@ -package io.github.hapjava.accessories; +package io.github.hapjava.accessories.optionalcharacteristic; -import io.github.hapjava.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; /** - * Extends {@link Lightbulb} with brightness values. + * Accessory with brightness values. * * @author Andy Lintner */ -public interface DimmableLightbulb extends Lightbulb { +public interface AccessoryWithBrightness { /** * Retrieves the current brightness of the light diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonDioxideLevel.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonDioxideLevel.java new file mode 100644 index 000000000..16260bda2 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonDioxideLevel.java @@ -0,0 +1,46 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with carbon dioxide level and peak level characteristic. + * + * @author Eugen Freiter + */ +public interface AccessoryWithCarbonDioxideLevel { + + /** + * Retrieves the carbon dioxide peak level + * + * @return a future that will contain the carbon dioxide level as a value between 0 and 100000 + */ + CompletableFuture getCarbonDioxidePeakLevel(); + + /** + * Retrieves the carbon dioxide level + * + * @return a future that will contain the carbon dioxide level as a value between 0 and 100000 + */ + CompletableFuture getCarbonDioxideLevel(); + + /** + * Subscribes to changes in the carbon dioxide level. + * + * @param callback the function to call when the state changes. + */ + void subscribeCarbonDioxideLevel(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the carbon dioxide level. */ + void unsubscribeCarbonDioxideLevel(); + + /** + * Subscribes to changes in the carbon dioxide level. + * + * @param callback the function to call when the state changes. + */ + void subscribeCarbonDioxidePeakLevel(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the carbon dioxide level. */ + void unsubscribeCarbonDioxidePeakLevel(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonMonoxideLevel.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonMonoxideLevel.java new file mode 100644 index 000000000..befb4d5ac --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonMonoxideLevel.java @@ -0,0 +1,46 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with carbon dioxide level and peak level characteristic. + * + * @author Eugen Freiter + */ +public interface AccessoryWithCarbonMonoxideLevel { + + /** + * Retrieves the carbon monoxide peak level + * + * @return a future that will contain the carbon monoxide level as a value between 0 and 100000 + */ + CompletableFuture getCarbonMonoxidePeakLevel(); + + /** + * Retrieves the carbon monoxide level + * + * @return a future that will contain the carbon monoxide level as a value between 0 and 100000 + */ + CompletableFuture getCarbonMonoxideLevel(); + + /** + * Subscribes to changes in the carbon monoxide level. + * + * @param callback the function to call when the state changes. + */ + void subscribeCarbonMonoxideLevel(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the carbon monoxide level. */ + void unsubscribeCarbonMonoxideLevel(); + + /** + * Subscribes to changes in the carbon monoxide level. + * + * @param callback the function to call when the state changes. + */ + void subscribeCarbonMonoxidePeakLevel(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the carbon monoxide level. */ + void unsubscribeCarbonMonoxidePeakLevel(); +} diff --git a/src/main/java/io/github/hapjava/accessories/ColorfulLightbulb.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithColor.java similarity index 85% rename from src/main/java/io/github/hapjava/accessories/ColorfulLightbulb.java rename to src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithColor.java index 32ed8d294..e8f2dd66a 100644 --- a/src/main/java/io/github/hapjava/accessories/ColorfulLightbulb.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithColor.java @@ -1,15 +1,14 @@ -package io.github.hapjava.accessories; +package io.github.hapjava.accessories.optionalcharacteristic; -import io.github.hapjava.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; /** - * Extends {@link Lightbulb} with color settings. This will usually be implemented along with {@link - * DimmableLightbulb}, but not necessarily so. + * Accessory with Color * * @author Andy Lintner */ -public interface ColorfulLightbulb extends Lightbulb { +public interface AccessoryWithColor { /** * Retrieves the current hue of the light. diff --git a/src/main/java/io/github/hapjava/accessories/thermostat/CoolingThermostat.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCoolingThresholdTemprature.java similarity index 82% rename from src/main/java/io/github/hapjava/accessories/thermostat/CoolingThermostat.java rename to src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCoolingThresholdTemprature.java index 3229146b5..170c921d9 100644 --- a/src/main/java/io/github/hapjava/accessories/thermostat/CoolingThermostat.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCoolingThresholdTemprature.java @@ -1,9 +1,9 @@ -package io.github.hapjava.accessories.thermostat; +package io.github.hapjava.accessories.optionalcharacteristic; -import io.github.hapjava.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -public interface CoolingThermostat extends BasicThermostat { +public interface AccessoryWithCoolingThresholdTemprature { /** * Retrieves the temperature above which the thermostat should begin cooling. diff --git a/src/main/java/io/github/hapjava/accessories/ValveWithTimer.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithDuration.java similarity index 55% rename from src/main/java/io/github/hapjava/accessories/ValveWithTimer.java rename to src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithDuration.java index 5ed2ee048..bf8da5f44 100644 --- a/src/main/java/io/github/hapjava/accessories/ValveWithTimer.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithDuration.java @@ -1,32 +1,14 @@ -package io.github.hapjava.accessories; +package io.github.hapjava.accessories.optionalcharacteristic; -import io.github.hapjava.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; /** - * Extends {@link Valve} with timer values. + * Accessory with duration characteristic. * - * @author Tim Harper + * @author Eugen Freiter */ -public interface ValveWithTimer extends Valve { - - /** - * Retrieves the current duration for which the valve will run - * - * @return a future with the value - */ - CompletableFuture getRemainingDuration(); - - /** - * Subscribes to changes in the duration; note it is not necessary to emit a change every second, - * homekit infers the countdown progress clientside. - * - * @param callback the function when the existing duration has been replaced with a new one. - */ - void subscribeRemainingDuration(HomekitCharacteristicChangeCallback callback); - - /** Unsubscribes from changes */ - void unsubscribeRemainingDuration(); +public interface AccessoryWithDuration { /** * Retrieves the current set duration for which the valve will be scheduled to run; this is diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFanState.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFanState.java new file mode 100644 index 000000000..2c0488d26 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFanState.java @@ -0,0 +1,158 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.fan.CurrentFanStateEnum; +import io.github.hapjava.characteristics.impl.fan.LockPhysicalControlsEnum; +import io.github.hapjava.characteristics.impl.fan.RotationDirectionEnum; +import io.github.hapjava.characteristics.impl.fan.SwingModeEnum; +import io.github.hapjava.characteristics.impl.fan.TargetFanStateEnum; +import java.util.concurrent.CompletableFuture; + +/** + * A fan, with power and rotational characteristics. + * + * @author Andy Lintner + */ +public interface AccessoryWithFanState { + + /** + * OPTIONAL: Retrieves the current state of the fan (INACTIVE, IDLE, BLOWING AIR). + * + * @return a future that will contain the state + */ + CompletableFuture getCurrentFanState(); + + /** + * OPTIONAL: Retrieves the target state of the fan (MANUAL, AUTO). + * + * @return a future that will contain the state + */ + CompletableFuture getTargetFanState(); + + /** + * OPTIONAL: Set the target state of the fan (MANUAL, AUTO). + * + * @param targetState target state + */ + CompletableFuture setTargetFanState(TargetFanStateEnum targetState); + + /** + * OPTIONAL: Retrieves the swing mode of the fan. + * + * @return a future that will contain the swing mode + */ + CompletableFuture getSwingMode(); + + /** + * OPTIONAL: Set the swing mode of the fan (DISABLED, ENABLED). + * + * @param swingMode swing mode + */ + CompletableFuture setSwingMode(SwingModeEnum swingMode); + + /** + * OPTIONAL: Retrieves the lock controls of the fan. + * + * @return a future that will contain the lock controls + */ + CompletableFuture getLockControls(); + + /** + * OPTIONAL: Set the lock controls of the fan (DISABLED, ENABLED). + * + * @param lockControls lock controls mode + */ + CompletableFuture setLockControls(LockPhysicalControlsEnum lockControls); + + /** + * OPTIONAL: Retrieves the current rotation direction of the fan. + * + * @return a future that will contain the direction + */ + CompletableFuture getRotationDirection(); + + /** + * OPTIONAL: Retrieves the current speed of the fan's rotation + * + * @return a future that will contain the speed, expressed as an integer between 0 and 100. + */ + CompletableFuture getRotationSpeed(); + + /** + * OPTIONAL: Sets the rotation direction of the fan + * + * @param direction the direction to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setRotationDirection(RotationDirectionEnum direction) throws Exception; + + /** + * OPTIONAL: Sets the speed of the fan's rotation + * + * @param speed the speed to set, expressed as an integer between 0 and 100. + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setRotationSpeed(Integer speed) throws Exception; + + /** + * OPTIONAL: Subscribes to changes in the rotation direction of the fan. + * + * @param callback the function to call when the direction changes. + */ + void subscribeRotationDirection(HomekitCharacteristicChangeCallback callback); + + /** + * OPTIONAL: Subscribes to changes in the current fan state. + * + * @param callback the function to call when the direction changes. + */ + void subscribeCurrentFanState(HomekitCharacteristicChangeCallback callback); + + /** + * OPTIONAL: Subscribes to changes in the target fan state. + * + * @param callback the function to call when the direction changes. + */ + void subscribeTargetFanState(HomekitCharacteristicChangeCallback callback); + + /** + * OPTIONAL: Subscribes to changes in the rotation speed of the fan. + * + * @param callback the function to call when the speed changes. + */ + void subscribeRotationSpeed(HomekitCharacteristicChangeCallback callback); + + /** + * OPTIONAL: Subscribes to changes in the swing mode of the fan. + * + * @param callback the function to call when the swing mode changes. + */ + void subscribeSwingMode(HomekitCharacteristicChangeCallback callback); + + /** + * OPTIONAL: Subscribes to changes in the lock controls of the fan. + * + * @param callback the function to call when the lock controls changes. + */ + void subscribeLockControls(HomekitCharacteristicChangeCallback callback); + + /** OPTIONAL: Unsubscribes from changes in the rotation direction of the fan. */ + void unsubscribeRotationDirection(); + + /** OPTIONAL: Unsubscribes from changes in the fan's rotation speed. */ + void unsubscribeRotationSpeed(); + + /** OPTIONAL: Unsubscribes from changes in the current state of the fan. */ + void unsubscribeCurrentFanState(); + + /** OPTIONAL: Unsubscribes from changes in the target state of the fan. */ + void unsubscribeTargetFanState(); + + /** OPTIONAL: Unsubscribes from changes in the swing mode of the fan. */ + void unsubscribeSwingMode(); + + /** OPTIONAL: Unsubscribes from changes in the lock controls of the fan. */ + void unsubscribeLockControls(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHardwareRevision.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHardwareRevision.java new file mode 100644 index 000000000..6f1259ef4 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHardwareRevision.java @@ -0,0 +1,18 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with name . + * + * @author Eugen Freiter + */ +public interface AccessoryWithHardwareRevision { + + /** + * Returns a hardware revision to display in iOS. + * + * @return the hardware revision, or null. + */ + CompletableFuture getHardwareRevision(); +} diff --git a/src/main/java/io/github/hapjava/accessories/thermostat/HeatingThermostat.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHearingThresholdTemprature.java similarity index 82% rename from src/main/java/io/github/hapjava/accessories/thermostat/HeatingThermostat.java rename to src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHearingThresholdTemprature.java index 5a672adb9..af18baca3 100644 --- a/src/main/java/io/github/hapjava/accessories/thermostat/HeatingThermostat.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHearingThresholdTemprature.java @@ -1,9 +1,9 @@ -package io.github.hapjava.accessories.thermostat; +package io.github.hapjava.accessories.optionalcharacteristic; -import io.github.hapjava.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -public interface HeatingThermostat extends BasicThermostat { +public interface AccessoryWithHearingThresholdTemprature { /** * Retrieves the temperature below which the thermostat should begin heating. diff --git a/src/main/java/io/github/hapjava/accessories/HoldPositionWindowCovering.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHoldPosition.java similarity index 71% rename from src/main/java/io/github/hapjava/accessories/HoldPositionWindowCovering.java rename to src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHoldPosition.java index f5cfe0c80..b1d0d65fa 100644 --- a/src/main/java/io/github/hapjava/accessories/HoldPositionWindowCovering.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHoldPosition.java @@ -1,8 +1,9 @@ -package io.github.hapjava.accessories; +package io.github.hapjava.accessories.optionalcharacteristic; import java.util.concurrent.CompletableFuture; -public interface HoldPositionWindowCovering { +/** Accessory with hold position */ +public interface AccessoryWithHoldPosition { /** * Sets the hold position state diff --git a/src/main/java/io/github/hapjava/accessories/HorizontalTiltingWindowCovering.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHorizontalTilting.java similarity index 86% rename from src/main/java/io/github/hapjava/accessories/HorizontalTiltingWindowCovering.java rename to src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHorizontalTilting.java index 623291f02..58556c9d0 100644 --- a/src/main/java/io/github/hapjava/accessories/HorizontalTiltingWindowCovering.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHorizontalTilting.java @@ -1,14 +1,14 @@ -package io.github.hapjava.accessories; +package io.github.hapjava.accessories.optionalcharacteristic; -import io.github.hapjava.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; /** - * Extends WindowCovering with the ability to control horizontal tilt angles + * Accessory with horizontal tilting characteristic. * * @author Andy Lintner */ -public interface HorizontalTiltingWindowCovering extends WindowCovering { +public interface AccessoryWithHorizontalTilting { /** * Retrieves the current horizontal tilt angle diff --git a/src/main/java/io/github/hapjava/accessories/ObstructionDetectedWindowCovering.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithObstructionDetection.java similarity index 66% rename from src/main/java/io/github/hapjava/accessories/ObstructionDetectedWindowCovering.java rename to src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithObstructionDetection.java index 6dea4ec63..859379cd0 100644 --- a/src/main/java/io/github/hapjava/accessories/ObstructionDetectedWindowCovering.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithObstructionDetection.java @@ -1,12 +1,13 @@ -package io.github.hapjava.accessories; +package io.github.hapjava.accessories.optionalcharacteristic; -import io.github.hapjava.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -public interface ObstructionDetectedWindowCovering { +/** Accessory with hold position */ +public interface AccessoryWithObstructionDetection { /** - * Retrieves an indication that the window covering is obstructed from moving + * Retrieves an indication obstructed is detected * * @return a future that will contain a boolean indicating whether an obstruction is present */ diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRemainingDuration.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRemainingDuration.java new file mode 100644 index 000000000..d5071bbe6 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRemainingDuration.java @@ -0,0 +1,30 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with remaining duration characteristic. + * + * @author Tim Harper + */ +public interface AccessoryWithRemainingDuration { + + /** + * Retrieves the current duration for which the valve will run + * + * @return a future with the value + */ + CompletableFuture getRemainingDuration(); + + /** + * Subscribes to changes in the duration; note it is not necessary to emit a change every second, + * homekit infers the countdown progress clientside. + * + * @param callback the function when the existing duration has been replaced with a new one. + */ + void subscribeRemainingDuration(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes */ + void unsubscribeRemainingDuration(); +} diff --git a/src/main/java/io/github/hapjava/accessories/VerticalTiltingWindowCovering.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVerticalTilting.java similarity index 83% rename from src/main/java/io/github/hapjava/accessories/VerticalTiltingWindowCovering.java rename to src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVerticalTilting.java index e85a7e9c0..3e849e5bb 100644 --- a/src/main/java/io/github/hapjava/accessories/VerticalTiltingWindowCovering.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVerticalTilting.java @@ -1,15 +1,10 @@ -package io.github.hapjava.accessories; +package io.github.hapjava.accessories.optionalcharacteristic; -import io.github.hapjava.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Extends WindowCovering with the ability to control vertical tilt angles - * - * @author Andy Lintner - */ -public interface VerticalTiltingWindowCovering extends WindowCovering { - +/** Accessory with vertical tilting characteristic. */ +public interface AccessoryWithVerticalTilting { /** * Retrieves the current vertical tilt angle * diff --git a/src/main/java/io/github/hapjava/accessories/properties/CarbonDioxideDetectedState.java b/src/main/java/io/github/hapjava/accessories/properties/CarbonDioxideDetectedState.java deleted file mode 100644 index 66dabf5b6..000000000 --- a/src/main/java/io/github/hapjava/accessories/properties/CarbonDioxideDetectedState.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.hapjava.accessories.properties; - -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Collectors; - -public enum CarbonDioxideDetectedState { - NORMAL(0), - ABNORMAL(1); - - private static final Map reverse; - - static { - reverse = - Arrays.stream(CarbonDioxideDetectedState.values()) - .collect(Collectors.toMap(CarbonDioxideDetectedState::getCode, t -> t)); - } - - public static CarbonDioxideDetectedState fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - CarbonDioxideDetectedState(int code) { - this.code = code; - } - - public int getCode() { - return code; - } -} diff --git a/src/main/java/io/github/hapjava/accessories/properties/CarbonMonoxideDetectedState.java b/src/main/java/io/github/hapjava/accessories/properties/CarbonMonoxideDetectedState.java deleted file mode 100644 index 3a713094d..000000000 --- a/src/main/java/io/github/hapjava/accessories/properties/CarbonMonoxideDetectedState.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.hapjava.accessories.properties; - -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Collectors; - -public enum CarbonMonoxideDetectedState { - NORMAL(0), - ABNORMAL(1); - - private static final Map reverse; - - static { - reverse = - Arrays.stream(CarbonMonoxideDetectedState.values()) - .collect(Collectors.toMap(CarbonMonoxideDetectedState::getCode, t -> t)); - } - - public static CarbonMonoxideDetectedState fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - CarbonMonoxideDetectedState(int code) { - this.code = code; - } - - public int getCode() { - return code; - } -} diff --git a/src/main/java/io/github/hapjava/accessories/properties/ContactState.java b/src/main/java/io/github/hapjava/accessories/properties/ContactState.java deleted file mode 100644 index 958e0b689..000000000 --- a/src/main/java/io/github/hapjava/accessories/properties/ContactState.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.hapjava.accessories.properties; - -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Collectors; - -public enum ContactState { - DETECTED(0), - NOT_DETECTED(1); - - private static final Map reverse; - - static { - reverse = - Arrays.stream(ContactState.values()) - .collect(Collectors.toMap(ContactState::getCode, t -> t)); - } - - public static ContactState fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - ContactState(int code) { - this.code = code; - } - - public int getCode() { - return code; - } -} diff --git a/src/main/java/io/github/hapjava/accessories/properties/CurrentSecuritySystemState.java b/src/main/java/io/github/hapjava/accessories/properties/CurrentSecuritySystemState.java deleted file mode 100644 index f444a38cc..000000000 --- a/src/main/java/io/github/hapjava/accessories/properties/CurrentSecuritySystemState.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.github.hapjava.accessories.properties; - -import io.github.hapjava.accessories.SecuritySystem; -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * The current state of a {@link SecuritySystem}. Unlike {@link TargetSecuritySystemState}, this - * enum includes a triggered state. - * - * @author Gaston Dombiak - */ -public enum CurrentSecuritySystemState { - - /** The home is occupied and residents are active. */ - STAY_ARM(0), - /** The home is unoccupied. */ - AWAY_ARM(1), - /** The home is occupied and residents are sleeping. */ - NIGHT_ARM(2), - /** The security system is disarmed. */ - DISARMED(3), - /** The security system is triggered. */ - TRIGGERED(4); - - private static final Map reverse; - - static { - reverse = - Arrays.stream(CurrentSecuritySystemState.values()) - .collect(Collectors.toMap(CurrentSecuritySystemState::getCode, t -> t)); - } - - public static CurrentSecuritySystemState fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - CurrentSecuritySystemState(int code) { - this.code = code; - } - - public int getCode() { - return code; - } -} diff --git a/src/main/java/io/github/hapjava/accessories/properties/DoorState.java b/src/main/java/io/github/hapjava/accessories/properties/DoorState.java deleted file mode 100644 index c47bc4ccf..000000000 --- a/src/main/java/io/github/hapjava/accessories/properties/DoorState.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.hapjava.accessories.properties; - -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Collectors; - -public enum DoorState { - OPEN(0), - CLOSED(1), - OPENING(2), - CLOSING(3), - STOPPED(4); - - private static final Map reverse; - - static { - reverse = Arrays.stream(DoorState.values()).collect(Collectors.toMap(t -> t.getCode(), t -> t)); - } - - public static DoorState fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - private DoorState(int code) { - this.code = code; - } - - public int getCode() { - return code; - } -} diff --git a/src/main/java/io/github/hapjava/accessories/properties/LockMechanismState.java b/src/main/java/io/github/hapjava/accessories/properties/LockMechanismState.java deleted file mode 100644 index 7dceff8c7..000000000 --- a/src/main/java/io/github/hapjava/accessories/properties/LockMechanismState.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.hapjava.accessories.properties; - -import io.github.hapjava.accessories.LockMechanism; -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * The state of a {@link LockMechanism}. - * - * @author Andy Lintner - */ -public enum LockMechanismState { - UNSECURED(0), - SECURED(1), - JAMMED(2), - UNKNOWN(3); - - private static final Map reverse; - - static { - reverse = - Arrays.stream(LockMechanismState.values()) - .collect(Collectors.toMap(t -> t.getCode(), t -> t)); - } - - public static LockMechanismState fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - private LockMechanismState(int code) { - this.code = code; - } - - public int getCode() { - return code; - } -} diff --git a/src/main/java/io/github/hapjava/accessories/properties/RotationDirection.java b/src/main/java/io/github/hapjava/accessories/properties/RotationDirection.java deleted file mode 100644 index 96ee54749..000000000 --- a/src/main/java/io/github/hapjava/accessories/properties/RotationDirection.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.hapjava.accessories.properties; - -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Collectors; - -public enum RotationDirection { - CLOCKWISE(0), - COUNTER_CLOCKWISE(1); - - private static final Map reverse; - - static { - reverse = - Arrays.stream(RotationDirection.values()) - .collect(Collectors.toMap(t -> t.getCode(), t -> t)); - } - - public static RotationDirection fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - private RotationDirection(int code) { - this.code = code; - } - - public int getCode() { - return code; - } -} diff --git a/src/main/java/io/github/hapjava/accessories/properties/SecuritySystemAlarmType.java b/src/main/java/io/github/hapjava/accessories/properties/SecuritySystemAlarmType.java deleted file mode 100644 index d8a96de63..000000000 --- a/src/main/java/io/github/hapjava/accessories/properties/SecuritySystemAlarmType.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.hapjava.accessories.properties; - -import io.github.hapjava.accessories.SecuritySystem; -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * Type of alarm of a {@link SecuritySystem}. - * - * @author Gaston Dombiak - */ -public enum SecuritySystemAlarmType { - - /** Alarm conditions are cleared */ - NO_ALARM(0), - /** Alarm type is not known */ - UNKNOWN(1); - - private static final Map reverse; - - static { - reverse = - Arrays.stream(SecuritySystemAlarmType.values()) - .collect(Collectors.toMap(SecuritySystemAlarmType::getCode, t -> t)); - } - - public static SecuritySystemAlarmType fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - SecuritySystemAlarmType(int code) { - this.code = code; - } - - public int getCode() { - return code; - } -} diff --git a/src/main/java/io/github/hapjava/accessories/properties/SmokeDetectedState.java b/src/main/java/io/github/hapjava/accessories/properties/SmokeDetectedState.java deleted file mode 100644 index a7f71a30a..000000000 --- a/src/main/java/io/github/hapjava/accessories/properties/SmokeDetectedState.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.hapjava.accessories.properties; - -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Collectors; - -public enum SmokeDetectedState { - NOT_DETECTED(0), - DETECTED(1); - - private static final Map reverse; - - static { - reverse = - Arrays.stream(SmokeDetectedState.values()) - .collect(Collectors.toMap(SmokeDetectedState::getCode, t -> t)); - } - - public static SmokeDetectedState fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - SmokeDetectedState(int code) { - this.code = code; - } - - public int getCode() { - return code; - } -} diff --git a/src/main/java/io/github/hapjava/accessories/properties/TargetSecuritySystemState.java b/src/main/java/io/github/hapjava/accessories/properties/TargetSecuritySystemState.java deleted file mode 100644 index a08a2987b..000000000 --- a/src/main/java/io/github/hapjava/accessories/properties/TargetSecuritySystemState.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.hapjava.accessories.properties; - -import io.github.hapjava.accessories.SecuritySystem; -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * The target state of a {@link SecuritySystem}. - * - * @author Gaston Dombiak - */ -public enum TargetSecuritySystemState { - - /** Arm the system when the home is occupied and residents are active. */ - STAY_ARM(0), - /** Arm the system when the home is unoccupied. */ - AWAY_ARM(1), - /** Arm the system when the home is occupied and residents are sleeping. */ - NIGHT_ARM(2), - /** Disarm the system. */ - DISARM(3); - - private static final Map reverse; - - static { - reverse = - Arrays.stream(TargetSecuritySystemState.values()) - .collect(Collectors.toMap(TargetSecuritySystemState::getCode, t -> t)); - } - - public static TargetSecuritySystemState fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - TargetSecuritySystemState(int code) { - this.code = code; - } - - public int getCode() { - return code; - } -} diff --git a/src/main/java/io/github/hapjava/accessories/properties/TemperatureUnit.java b/src/main/java/io/github/hapjava/accessories/properties/TemperatureUnit.java deleted file mode 100644 index 228366cec..000000000 --- a/src/main/java/io/github/hapjava/accessories/properties/TemperatureUnit.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.hapjava.accessories.properties; - -import io.github.hapjava.accessories.TemperatureSensor; -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * The temperature unit used by a {@link TemperatureSensor}. - * - * @author Andy Lintner - */ -public enum TemperatureUnit { - CELSIUS(0), - FAHRENHEIT(1); - - private static final Map reverse; - - static { - reverse = - Arrays.stream(TemperatureUnit.values()).collect(Collectors.toMap(t -> t.getCode(), t -> t)); - } - - static TemperatureUnit fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - private TemperatureUnit(int code) { - this.code = code; - } - - public int getCode() { - return code; - } -} diff --git a/src/main/java/io/github/hapjava/accessories/properties/ThermostatMode.java b/src/main/java/io/github/hapjava/accessories/properties/ThermostatMode.java deleted file mode 100644 index 4eb586584..000000000 --- a/src/main/java/io/github/hapjava/accessories/properties/ThermostatMode.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.hapjava.accessories.properties; - -import io.github.hapjava.accessories.thermostat.BasicThermostat; -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * The mode used by a {@link BasicThermostat} - * - * @author Andy Lintner - */ -public enum ThermostatMode { - OFF(0), - HEAT(1), - COOL(2), - AUTO(3); - - private static final Map reverse; - - static { - reverse = - Arrays.stream(ThermostatMode.values()).collect(Collectors.toMap(t -> t.getCode(), t -> t)); - } - - public static ThermostatMode fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - private ThermostatMode(int code) { - this.code = code; - } - - public int getCode() { - return code; - } -} diff --git a/src/main/java/io/github/hapjava/accessories/properties/ValveType.java b/src/main/java/io/github/hapjava/accessories/properties/ValveType.java deleted file mode 100644 index 95f564869..000000000 --- a/src/main/java/io/github/hapjava/accessories/properties/ValveType.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.hapjava.accessories.properties; - -import io.github.hapjava.accessories.Valve; -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * The mode used by a {@link Valve} - * - * @author Tim Harper - */ -public enum ValveType { - GENERIC(0), - IRRIGATION(1), - SHOWER(2), - WATER_FAUCET(3); - - private static final Map reverse; - - static { - reverse = Arrays.stream(ValveType.values()).collect(Collectors.toMap(t -> t.getCode(), t -> t)); - } - - public static ValveType fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - private ValveType(int code) { - this.code = code; - } - - public int getCode() { - return code; - } -} diff --git a/src/main/java/io/github/hapjava/accessories/properties/WindowCoveringPositionState.java b/src/main/java/io/github/hapjava/accessories/properties/WindowCoveringPositionState.java deleted file mode 100644 index 1686fd2fb..000000000 --- a/src/main/java/io/github/hapjava/accessories/properties/WindowCoveringPositionState.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.hapjava.accessories.properties; - -import io.github.hapjava.accessories.WindowCovering; -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * The position state used by a {@link WindowCovering} - * - * @author Andy Lintner - */ -public enum WindowCoveringPositionState { - DECREASING(0), - INCREASING(1), - STOPPED(2); - - private static final Map reverse; - - static { - reverse = - Arrays.stream(WindowCoveringPositionState.values()) - .collect(Collectors.toMap(t -> t.getCode(), t -> t)); - } - - public static WindowCoveringPositionState fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - private WindowCoveringPositionState(int code) { - this.code = code; - } - - public int getCode() { - return code; - } -} diff --git a/src/main/java/io/github/hapjava/accessories/properties/package-info.java b/src/main/java/io/github/hapjava/accessories/properties/package-info.java deleted file mode 100644 index f2827b56b..000000000 --- a/src/main/java/io/github/hapjava/accessories/properties/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Contains properties used by {@link io.github.hapjava.accessories}. */ -package io.github.hapjava.accessories.properties; diff --git a/src/main/java/io/github/hapjava/accessories/thermostat/BasicThermostat.java b/src/main/java/io/github/hapjava/accessories/thermostat/BasicThermostat.java deleted file mode 100644 index 8747ab01a..000000000 --- a/src/main/java/io/github/hapjava/accessories/thermostat/BasicThermostat.java +++ /dev/null @@ -1,88 +0,0 @@ -package io.github.hapjava.accessories.thermostat; - -import io.github.hapjava.*; -import io.github.hapjava.accessories.TemperatureSensor; -import io.github.hapjava.accessories.properties.ThermostatMode; -import io.github.hapjava.impl.services.ThermostatService; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.CompletableFuture; - -public interface BasicThermostat extends HomekitAccessory, TemperatureSensor { - - /** - * Retrieves the current {@link ThermostatMode} of the thermostat. - * - * @return a future that will contain the mode. - */ - CompletableFuture getCurrentMode(); - - /** - * Subscribes to changes in the {@link ThermostatMode} of the thermostat. - * - * @param callback the function to call when the state changes. - */ - void subscribeCurrentMode(HomekitCharacteristicChangeCallback callback); - - /** Unsubscribes from changes in the mode of the thermostat. */ - void unsubscribeCurrentMode(); - - /** - * Sets the {@link ThermostatMode} of the thermostat. - * - * @param mode The {@link ThermostatMode} to set. - * @throws Exception when the change cannot be made. - */ - void setTargetMode(ThermostatMode mode) throws Exception; - - /** - * Retrieves the pending, but not yet complete, {@link ThermostatMode} of the thermostat. - * - * @return a future that will contain the target mode. - */ - CompletableFuture getTargetMode(); - - /** - * Subscribes to changes in the pending, but not yet complete, {@link ThermostatMode} of the - * thermostat. - * - * @param callback the function to call when the state changes. - */ - void subscribeTargetMode(HomekitCharacteristicChangeCallback callback); - - /** - * Unsubscribes from changes in the pending, but not yet complete, {@link ThermostatMode} of the - * thermostat. - */ - void unsubscribeTargetMode(); - - /** - * Retrieves the target temperature, in celsius degrees. - * - * @return a future that will contain the target temperature. - */ - CompletableFuture getTargetTemperature(); - - /** - * Sets the target temperature. - * - * @param value the target temperature, in celsius degrees. - * @throws Exception when the temperature cannot be changed. - */ - void setTargetTemperature(Double value) throws Exception; - - /** - * Subscribes to changes in the target temperature. - * - * @param callback the function to call when the state changes. - */ - void subscribeTargetTemperature(HomekitCharacteristicChangeCallback callback); - - /** Unsubscribes from changes in the target temperature. */ - void unsubscribeTargetTemperature(); - - @Override - default Collection getServices() { - return Collections.singleton(new ThermostatService(this)); - } -} diff --git a/src/main/java/io/github/hapjava/characteristics/BooleanCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/BooleanCharacteristic.java deleted file mode 100644 index f5f064bfb..000000000 --- a/src/main/java/io/github/hapjava/characteristics/BooleanCharacteristic.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.hapjava.characteristics; - -import javax.json.JsonNumber; -import javax.json.JsonValue; -import javax.json.JsonValue.ValueType; - -/** - * Characteristic that exposes a Boolean value. - * - * @author Andy Lintner - */ -public abstract class BooleanCharacteristic extends BaseCharacteristic { - - /** - * Default constructor - * - * @param type a string containing a UUID that indicates the type of characteristic. Apple defines - * a set of these, however implementors can create their own as well. - * @param isWritable indicates whether the value can be changed. - * @param isReadable indicates whether the value can be retrieved. - * @param description a description of the characteristic to be passed to the consuming device. - */ - public BooleanCharacteristic( - String type, boolean isWritable, boolean isReadable, String description) { - super(type, "bool", isWritable, isReadable, description); - } - - /** {@inheritDoc} */ - @Override - protected Boolean convert(JsonValue jsonValue) { - if (jsonValue.getValueType().equals(ValueType.NUMBER)) { - return ((JsonNumber) jsonValue).intValue() > 0; - } - return jsonValue.equals(JsonValue.TRUE); - } - - /** {@inheritDoc} */ - @Override - protected Boolean getDefault() { - return false; - } -} diff --git a/src/main/java/io/github/hapjava/characteristics/CharacteristicEnum.java b/src/main/java/io/github/hapjava/characteristics/CharacteristicEnum.java new file mode 100644 index 000000000..bc8dbc9c2 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/CharacteristicEnum.java @@ -0,0 +1,5 @@ +package io.github.hapjava.characteristics; + +public interface CharacteristicEnum { + int getCode(); +} diff --git a/src/main/java/io/github/hapjava/characteristics/EventableCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/EventableCharacteristic.java index ddb5c5558..1da8f228b 100644 --- a/src/main/java/io/github/hapjava/characteristics/EventableCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/EventableCharacteristic.java @@ -1,7 +1,5 @@ package io.github.hapjava.characteristics; -import io.github.hapjava.HomekitCharacteristicChangeCallback; - /** * A characteristic that can be listened to by the connected iOS device. * diff --git a/src/main/java/io/github/hapjava/impl/ExceptionalConsumer.java b/src/main/java/io/github/hapjava/characteristics/ExceptionalConsumer.java similarity index 65% rename from src/main/java/io/github/hapjava/impl/ExceptionalConsumer.java rename to src/main/java/io/github/hapjava/characteristics/ExceptionalConsumer.java index 84ab5fd0c..653d73c5f 100644 --- a/src/main/java/io/github/hapjava/impl/ExceptionalConsumer.java +++ b/src/main/java/io/github/hapjava/characteristics/ExceptionalConsumer.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl; +package io.github.hapjava.characteristics; public interface ExceptionalConsumer { void accept(T t) throws Exception; diff --git a/src/main/java/io/github/hapjava/HomekitCharacteristicChangeCallback.java b/src/main/java/io/github/hapjava/characteristics/HomekitCharacteristicChangeCallback.java similarity index 86% rename from src/main/java/io/github/hapjava/HomekitCharacteristicChangeCallback.java rename to src/main/java/io/github/hapjava/characteristics/HomekitCharacteristicChangeCallback.java index 6008b9d04..e2d5bc5f2 100644 --- a/src/main/java/io/github/hapjava/HomekitCharacteristicChangeCallback.java +++ b/src/main/java/io/github/hapjava/characteristics/HomekitCharacteristicChangeCallback.java @@ -1,6 +1,4 @@ -package io.github.hapjava; - -import io.github.hapjava.characteristics.EventableCharacteristic; +package io.github.hapjava.characteristics; /** * A callback interface for notifying subscribers that a characteristic value has changed. diff --git a/src/main/java/io/github/hapjava/characteristics/WriteOnlyBooleanCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/WriteOnlyBooleanCharacteristic.java deleted file mode 100644 index d055a0872..000000000 --- a/src/main/java/io/github/hapjava/characteristics/WriteOnlyBooleanCharacteristic.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.hapjava.characteristics; - -import java.util.concurrent.CompletableFuture; -import javax.json.JsonObjectBuilder; - -/** - * A characteristic that provides a boolean that can only be written to, not read. - * - * @author Andy Lintner - */ -public abstract class WriteOnlyBooleanCharacteristic extends BooleanCharacteristic { - - /** - * Default constructor - * - * @param type a string containing a UUID that indicates the type of characteristic. Apple defines - * a set of these, however implementors can create their own as well. - * @param description a description of the characteristic to be passed to the consuming device. - */ - public WriteOnlyBooleanCharacteristic(String type, String description) { - super(type, true, false, description); - } - - @Override - protected final CompletableFuture getValue() { - return CompletableFuture.completedFuture(false); - } - - @Override - protected final void setJsonValue(JsonObjectBuilder builder, Boolean value) { - // Do nothing - non-readable characteristics cannot have a value key set - } -} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/FirmwareRevisionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/FirmwareRevisionCharacteristic.java new file mode 100644 index 000000000..542cb805b --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/FirmwareRevisionCharacteristic.java @@ -0,0 +1,19 @@ +package io.github.hapjava.characteristics.impl.accessoryinformation; + +import io.github.hapjava.characteristics.impl.base.StaticStringCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +/** This characteristic describes a firmware revision string */ +public class FirmwareRevisionCharacteristic extends StaticStringCharacteristic { + + public FirmwareRevisionCharacteristic(Supplier> getter) { + super( + "00000052-0000-1000-8000-0026BB765291", + "firmware revision", + Optional.of(getter), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/HardwareRevisionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/HardwareRevisionCharacteristic.java new file mode 100644 index 000000000..91fa21335 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/HardwareRevisionCharacteristic.java @@ -0,0 +1,19 @@ +package io.github.hapjava.characteristics.impl.accessoryinformation; + +import io.github.hapjava.characteristics.impl.base.StaticStringCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +/** This characteristic describes a hardware revision string x[.y[.z]] (e.g. ”100.1.1”) */ +public class HardwareRevisionCharacteristic extends StaticStringCharacteristic { + + public HardwareRevisionCharacteristic(Supplier> getter) { + super( + "00000053-0000-1000-8000-0026BB765291", + "hardware revision", + Optional.of(getter), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/IdentifyCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/IdentifyCharacteristic.java new file mode 100644 index 000000000..49e3e1fa2 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/IdentifyCharacteristic.java @@ -0,0 +1,19 @@ +package io.github.hapjava.characteristics.impl.accessoryinformation; + +import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import java.util.Optional; + +/** This characteristic enables accessory to run its identify routine. */ +public class IdentifyCharacteristic extends BooleanCharacteristic { + + public IdentifyCharacteristic(ExceptionalConsumer setter) { + super( + "00000014-0000-1000-8000-0026BB765291", + "identifies the accessory via a physical action on the accessory", + Optional.empty(), + Optional.of(setter), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ManufacturerCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ManufacturerCharacteristic.java new file mode 100644 index 000000000..0610b3b9b --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ManufacturerCharacteristic.java @@ -0,0 +1,22 @@ +package io.github.hapjava.characteristics.impl.accessoryinformation; + +import io.github.hapjava.characteristics.impl.base.StaticStringCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +/** + * This characteristic contains the name of the company whose brand will appear on the accessory, + * e.g., ”Acme”. + */ +public class ManufacturerCharacteristic extends StaticStringCharacteristic { + + public ManufacturerCharacteristic(Supplier> getter) { + super( + "00000020-0000-1000-8000-0026BB765291", + "ame of the manufacturer", + Optional.of(getter), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ModelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ModelCharacteristic.java new file mode 100644 index 000000000..d448fd984 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ModelCharacteristic.java @@ -0,0 +1,22 @@ +package io.github.hapjava.characteristics.impl.accessoryinformation; + +import io.github.hapjava.characteristics.impl.base.StaticStringCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +/** + * This characteristic contains the manufacturer-specific model of the accessory, e.g. ”A1234”. The + * minimum length of this characteristic must be 1. + */ +public class ModelCharacteristic extends StaticStringCharacteristic { + + public ModelCharacteristic(Supplier> getter) { + super( + "00000021-0000-1000-8000-0026BB765291", + "name of the model", + Optional.of(getter), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/NameCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/NameCharacteristic.java new file mode 100644 index 000000000..ddafc57ab --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/NameCharacteristic.java @@ -0,0 +1,19 @@ +package io.github.hapjava.characteristics.impl.accessoryinformation; + +import io.github.hapjava.characteristics.impl.base.StaticStringCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +/** This characteristic describes a name and must not be a null value. */ +public class NameCharacteristic extends StaticStringCharacteristic { + + public NameCharacteristic(Supplier> getter) { + super( + "00000023-0000-1000-8000-0026BB765291", + "name of the accessory", + Optional.of(getter), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/SerialNumberCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/SerialNumberCharacteristic.java new file mode 100644 index 000000000..596c2be77 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/SerialNumberCharacteristic.java @@ -0,0 +1,22 @@ +package io.github.hapjava.characteristics.impl.accessoryinformation; + +import io.github.hapjava.characteristics.impl.base.StaticStringCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +/** + * This characteristic contains the manufacturer-specific serial number of the accessory, e.g. + * ”1A2B3C4D5E6F”. The length must be greater than 1. + */ +public class SerialNumberCharacteristic extends StaticStringCharacteristic { + + public SerialNumberCharacteristic(Supplier> getter) { + super( + "00000030-0000-1000-8000-0026BB765291", + "serial number of the accessory", + Optional.of(getter), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/BaseCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java similarity index 80% rename from src/main/java/io/github/hapjava/characteristics/BaseCharacteristic.java rename to src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java index f345c9d8b..5260a19a4 100644 --- a/src/main/java/io/github/hapjava/characteristics/BaseCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java @@ -1,9 +1,11 @@ -package io.github.hapjava.characteristics; +package io.github.hapjava.characteristics.impl.base; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; import javax.json.Json; import javax.json.JsonArrayBuilder; import javax.json.JsonObject; @@ -11,23 +13,27 @@ import javax.json.JsonValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.github.hapjava.characteristics.Characteristic; +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; /** * Base class for implementing {@link Characteristic}. * * @author Andy Lintner */ -public abstract class BaseCharacteristic implements Characteristic { +public abstract class BaseCharacteristic implements Characteristic, EventableCharacteristic { private final Logger logger = LoggerFactory.getLogger(BaseCharacteristic.class); private final String type; private final String shortType; private final String format; - private final boolean isWritable; - private final boolean isReadable; - private final boolean isEventable; private final String description; + private final boolean isReadable; + private final boolean isWritable; + private final Optional> subscriber; + private final Optional unsubscriber; /** * Default constructor @@ -39,9 +45,17 @@ public abstract class BaseCharacteristic implements Characteristic { * @param isWritable indicates whether the value can be changed. * @param isReadable indicates whether the value can be retrieved. * @param description a description of the characteristic to be passed to the consuming device. + * @param subscriber subscribes to changes + * @param unsubscriber unsubscribes to changes */ public BaseCharacteristic( - String type, String format, boolean isWritable, boolean isReadable, String description) { + String type, + String format, + String description, + boolean isReadable, + boolean isWritable, + Optional> subscriber, + Optional unsubscriber) { if (type == null || format == null || description == null) { throw new NullPointerException(); } @@ -49,10 +63,11 @@ public BaseCharacteristic( this.type = type; this.shortType = this.type.replaceAll("^0*([0-9a-fA-F]+)-0000-1000-8000-0026BB765291$", "$1"); this.format = format; - this.isWritable = isWritable; - this.isReadable = isReadable; - this.isEventable = this instanceof EventableCharacteristic; this.description = description; + this.isReadable = isReadable; + this.isWritable = isWritable; + this.subscriber = subscriber; + this.unsubscriber = unsubscriber; } @Override @@ -71,8 +86,7 @@ protected CompletableFuture makeBuilder(int instanceId) { CompletableFuture futureValue = getValue(); if (futureValue == null) { - logger.error("Could not retrieve value " + this.getClass().getName()); - return null; + futureValue = CompletableFuture.completedFuture(getDefault()); } return futureValue @@ -84,13 +98,13 @@ protected CompletableFuture makeBuilder(int instanceId) { .thenApply( value -> { JsonArrayBuilder perms = Json.createArrayBuilder(); - if (isWritable) { - perms.add("pw"); - } if (isReadable) { perms.add("pr"); } - if (isEventable) { + if (isWritable) { + perms.add("pw"); + } + if (subscriber.isPresent()) { perms.add("ev"); } JsonObjectBuilder builder = @@ -101,7 +115,7 @@ protected CompletableFuture makeBuilder(int instanceId) { .add("format", format) .add("ev", false) .add("description", description); - setJsonValue(builder, value); + if (isReadable) setJsonValue(builder, value); return builder; }); } @@ -110,7 +124,7 @@ protected CompletableFuture makeBuilder(int instanceId) { @Override public final void setValue(JsonValue jsonValue) { try { - this.setValue(convert(jsonValue)); + setValue(convert(jsonValue)); } catch (Exception e) { logger.error("Error while setting JSON value", e); } @@ -127,6 +141,18 @@ public void supplyValue(JsonObjectBuilder builder) { } } + /** {@inheritDoc} */ + @Override + public void subscribe(HomekitCharacteristicChangeCallback callback) { + subscriber.get().accept(callback); + } + + /** {@inheritDoc} */ + @Override + public void unsubscribe() { + unsubscriber.get().run(); + } + /** * Converts from the JSON value to a Java object of the type T * diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java new file mode 100644 index 000000000..29d3b0bd9 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java @@ -0,0 +1,73 @@ +package io.github.hapjava.characteristics.impl.base; + +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; +import javax.json.JsonNumber; +import javax.json.JsonValue; +import javax.json.JsonValue.ValueType; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; + +/** + * Characteristic that exposes a Boolean value. + * + * @author Andy Lintner + */ +public abstract class BooleanCharacteristic extends BaseCharacteristic { + + private final Optional>> getter; + private final Optional> setter; + + /** + * Default constructor + * + * @param type a string containing a UUID that indicates the type of characteristic. Apple defines + * a set of these, however implementors can create their own as well. + * @param description a description of the characteristic to be passed to the consuming device. + */ + public BooleanCharacteristic( + String type, + String description, + Optional>> getter, + Optional> setter, + Optional> subscriber, + Optional unsubscriber) { + super( + type, + "bool", + description, + getter.isPresent(), + setter.isPresent(), + subscriber, + unsubscriber); + this.getter = getter; + this.setter = setter; + } + + /** {@inheritDoc} */ + @Override + protected Boolean convert(JsonValue jsonValue) { + if (jsonValue.getValueType().equals(ValueType.NUMBER)) { + return ((JsonNumber) jsonValue).intValue() > 0; + } + return jsonValue.equals(JsonValue.TRUE); + } + + @Override + protected CompletableFuture getValue() { + return getter.isPresent() ? getter.map(booleanGetter -> booleanGetter.get()).get() : null; + } + + @Override + protected void setValue(Boolean value) throws Exception { + if (setter.isPresent()) setter.get().accept(value); + } + + /** {@inheritDoc} */ + @Override + protected Boolean getDefault() { + return false; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/EnumCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java similarity index 56% rename from src/main/java/io/github/hapjava/characteristics/EnumCharacteristic.java rename to src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java index 84000fbb2..000d53d14 100644 --- a/src/main/java/io/github/hapjava/characteristics/EnumCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java @@ -1,10 +1,17 @@ -package io.github.hapjava.characteristics; +package io.github.hapjava.characteristics.impl.base; +import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; import javax.json.JsonNumber; import javax.json.JsonObject; import javax.json.JsonObjectBuilder; import javax.json.JsonValue; +import io.github.hapjava.characteristics.CharacteristicEnum; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.BaseCharacteristic; /** * Characteristic that exposes an Enum value. Enums are represented as an Integer value in the @@ -13,23 +20,31 @@ * * @author Andy Lintner */ -public abstract class EnumCharacteristic extends BaseCharacteristic { +public abstract class EnumCharacteristic + extends BaseCharacteristic { private final int maxValue; + Optional>> getter; + protected Optional> setter; /** * Default constructor * * @param type a string containing a UUID that indicates the type of characteristic. Apple defines * a set of these, however implementors can create their own as well. - * @param isWritable indicates whether the value can be changed. - * @param isReadable indicates whether the value can be retrieved. * @param description a description of the characteristic to be passed to the consuming device. * @param maxValue the number of enum items. */ public EnumCharacteristic( - String type, boolean isWritable, boolean isReadable, String description, int maxValue) { - super(type, "int", isWritable, isReadable, description); + String type, + String description, + int maxValue, + Optional>> getter, + Optional> setter, + Optional> subscriber, + Optional unsubscriber) { + super( + type, "int", description, getter.isPresent(), setter.isPresent(), subscriber, unsubscriber); this.maxValue = maxValue; } @@ -57,6 +72,23 @@ protected Integer convert(JsonValue jsonValue) { } } + @Override + protected CompletableFuture getValue() { + if (!getter.isPresent()) { + return null; + } + return getter.get().get().thenApply(T::getCode); + } + + @Override + protected void setValue(Integer value) throws Exception { + if (!setter.isPresent()) { + return; + } + + // TODO implement setter here? + } + /** {@inheritDoc} */ @Override protected Integer getDefault() { diff --git a/src/main/java/io/github/hapjava/characteristics/FloatCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java similarity index 74% rename from src/main/java/io/github/hapjava/characteristics/FloatCharacteristic.java rename to src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java index b3e086cfc..c50e46ba7 100644 --- a/src/main/java/io/github/hapjava/characteristics/FloatCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java @@ -1,11 +1,16 @@ -package io.github.hapjava.characteristics; +package io.github.hapjava.characteristics.impl.base; +import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; import javax.json.JsonNumber; import javax.json.JsonObjectBuilder; import javax.json.JsonValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; /** * A characteristic that provides a Float value type. @@ -20,14 +25,14 @@ public abstract class FloatCharacteristic extends BaseCharacteristic { private final double maxValue; private final double minStep; private final String unit; + private final Optional>> getter; + private final Optional> setter; /** * Default constructor * * @param type a string containing a UUID that indicates the type of characteristic. Apple defines * a set of these, however implementors can create their own as well. - * @param isWritable indicates whether the value can be changed. - * @param isReadable indicates whether the value can be retrieved. * @param description a description of the characteristic to be passed to the consuming device. * @param minValue the minimum supported value. * @param maxValue the maximum supported value @@ -36,18 +41,29 @@ public abstract class FloatCharacteristic extends BaseCharacteristic { */ public FloatCharacteristic( String type, - boolean isWritable, - boolean isReadable, String description, double minValue, double maxValue, double minStep, - String unit) { - super(type, "float", isWritable, isReadable, description); + String unit, + Optional>> getter, + Optional> setter, + Optional> subscriber, + Optional unsubscriber) { + super( + type, + "float", + description, + getter.isPresent(), + setter.isPresent(), + subscriber, + unsubscriber); this.minValue = minValue; this.maxValue = maxValue; - this.minStep = minStep; this.unit = unit; + this.getter = getter; + this.setter = setter; + this.minStep = minStep; } /** {@inheritDoc} */ @@ -75,8 +91,13 @@ protected Double convert(JsonValue jsonValue) { */ @Override protected final CompletableFuture getValue() { + if (!getter.isPresent()) { + return null; + } double rounder = 1 / this.minStep; - return getDoubleValue() + return getter + .get() + .get() .thenApply(d -> d == null ? null : Math.round(d * rounder) / rounder) .thenApply( d -> { @@ -103,16 +124,14 @@ protected final CompletableFuture getValue() { }); } + @Override + protected void setValue(Double value) throws Exception { + setter.get().accept(value); + } + /** {@inheritDoc} */ @Override protected Double getDefault() { return minValue; } - - /** - * Supplies the value of this characteristic as a double. - * - * @return a future that will contain the value. - */ - protected abstract CompletableFuture getDoubleValue(); } diff --git a/src/main/java/io/github/hapjava/characteristics/IntegerCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java similarity index 56% rename from src/main/java/io/github/hapjava/characteristics/IntegerCharacteristic.java rename to src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java index f83000a79..ecaa60475 100644 --- a/src/main/java/io/github/hapjava/characteristics/IntegerCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java @@ -1,9 +1,15 @@ -package io.github.hapjava.characteristics; +package io.github.hapjava.characteristics.impl.base; +import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; import javax.json.JsonNumber; import javax.json.JsonObjectBuilder; import javax.json.JsonValue; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.BaseCharacteristic; /** * A characteristic that provides an Integer data type. @@ -15,31 +21,40 @@ public abstract class IntegerCharacteristic extends BaseCharacteristic private final int minValue; private final int maxValue; private final String unit; + private final Optional>> getter; + private final Optional> setter; /** * Default constructor * * @param type a string containing a UUID that indicates the type of characteristic. Apple defines * a set of these, however implementors can create their own as well. - * @param isWritable indicates whether the value can be changed. - * @param isReadable indicates whether the value can be retrieved. * @param description a description of the characteristic to be passed to the consuming device. * @param minValue the minimum supported value. * @param maxValue the maximum supported value * @param unit a description of the unit this characteristic supports. + * @param getter getter for value + * @param setter setter for value + * @param subscriber subscribers to changes + * @param unsubscriber unsubscribers to changes */ public IntegerCharacteristic( String type, - boolean isWritable, - boolean isReadable, String description, int minValue, int maxValue, - String unit) { - super(type, "int", isWritable, isReadable, description); + String unit, + Optional>> getter, + Optional> setter, + Optional> subscriber, + Optional unsubscriber) { + super( + type, "int", description, getter.isPresent(), setter.isPresent(), subscriber, unsubscriber); this.minValue = minValue; this.maxValue = maxValue; this.unit = unit; + this.getter = getter; + this.setter = setter; } /** {@inheritDoc} */ @@ -56,6 +71,16 @@ protected CompletableFuture makeBuilder(int iid) { }); } + @Override + protected CompletableFuture getValue() { + return getter.map(integerGetter -> integerGetter.get()).get(); + } + + @Override + protected void setValue(Integer value) throws Exception { + setter.get().accept(value); + } + /** {@inheritDoc} */ @Override protected Integer getDefault() { diff --git a/src/main/java/io/github/hapjava/characteristics/StaticStringCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java similarity index 62% rename from src/main/java/io/github/hapjava/characteristics/StaticStringCharacteristic.java rename to src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java index 0665d8359..fc73b6192 100644 --- a/src/main/java/io/github/hapjava/characteristics/StaticStringCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java @@ -1,9 +1,14 @@ -package io.github.hapjava.characteristics; +package io.github.hapjava.characteristics.impl.base; +import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; import javax.json.JsonObjectBuilder; import javax.json.JsonString; import javax.json.JsonValue; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.BaseCharacteristic; /** * A characteristic that provides an immutable String value. @@ -14,19 +19,22 @@ public class StaticStringCharacteristic extends BaseCharacteristic { private static final int MAX_LEN = 255; - private final String value; - + private final Optional>> getter; /** * Default constructor * * @param type a string containing a UUID that indicates the type of characteristic. Apple defines * a set of these, however implementors can create their own as well. * @param description a description of the characteristic to be passed to the consuming device. - * @param value the value of the static string. */ - public StaticStringCharacteristic(String type, String description, String value) { - super(type, "string", false, true, description); - this.value = value; + public StaticStringCharacteristic( + String type, + String description, + Optional>> getter, + Optional> subscriber, + Optional unsubscriber) { + super(type, "string", description, getter.isPresent(), false, subscriber, unsubscriber); + this.getter = getter; } /** {@inheritDoc} */ @@ -50,7 +58,7 @@ public void setValue(String value) throws Exception { /** {@inheritDoc} */ @Override protected CompletableFuture getValue() { - return CompletableFuture.completedFuture(value).thenApply(s -> s != null ? s : "Unavailable"); + return getter.map(stringGetter -> stringGetter.get()).get(); } /** {@inheritDoc} */ diff --git a/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java new file mode 100644 index 000000000..0fa56ff40 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java @@ -0,0 +1,30 @@ +package io.github.hapjava.characteristics.impl.battery; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes the current level of the battery. */ +public class BatteryLevelCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + public BatteryLevelCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000068-0000-1000-8000-0026BB765291", + "battery level", + 0, + 100, + "%", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateCharacteristic.java new file mode 100644 index 000000000..1005fbcf9 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateCharacteristic.java @@ -0,0 +1,26 @@ +package io.github.hapjava.characteristics.impl.battery; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes the charging state of a battery or an accessory. */ +public class ChargingStateCharacteristic extends EnumCharacteristic { + + public ChargingStateCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000008F-0000-1000-8000-0026BB765291", + "Charging state", + 2, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateEnum.java new file mode 100644 index 000000000..d790ff2f7 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateEnum.java @@ -0,0 +1,36 @@ +package io.github.hapjava.characteristics.impl.battery; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Not Charging” 1 ”Charging” 2 ”Not Chargeable” */ +public enum ChargingStateEnum implements CharacteristicEnum { + NOT_CHARGING(0), + CHARGING(1), + NOT_CHARABLE(2); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(ChargingStateEnum.values()) + .collect(Collectors.toMap(ChargingStateEnum::getCode, t -> t)); + } + + public static ChargingStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + ChargingStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryCharacteristic.java new file mode 100644 index 000000000..24f706662 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryCharacteristic.java @@ -0,0 +1,30 @@ +package io.github.hapjava.characteristics.impl.battery; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes an accessoryʼs battery status. A status of 1 indicates that the + * battery level of the accessory is low. Value should return to 0 when the battery charges to a + * level thats above the low threshold. + */ +public class StatusLowBatteryCharacteristic extends EnumCharacteristic { + + public StatusLowBatteryCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000079-0000-1000-8000-0026BB765291", + "Status Low Battery", + 1, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryEnum.java new file mode 100644 index 000000000..92981247d --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.battery; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Battery level is normal” 1 ”Battery level is low” */ +public enum StatusLowBatteryEnum implements CharacteristicEnum { + NORMAL(0), + LOW(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(StatusLowBatteryEnum.values()) + .collect(Collectors.toMap(StatusLowBatteryEnum::getCode, t -> t)); + } + + public static StatusLowBatteryEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + StatusLowBatteryEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedCharacteristic.java new file mode 100644 index 000000000..8d84898bb --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedCharacteristic.java @@ -0,0 +1,31 @@ +package io.github.hapjava.characteristics.impl.carbondioxidesensor; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic indicates if a sensor detects abnormal levels of Carbon Dioxide. Value should + * revert to 0 after the Carbon Dioxide levels drop to normal levels. + */ +public class CarbonDioxideDetectedCharacteristic + extends EnumCharacteristic implements EventableCharacteristic { + + public CarbonDioxideDetectedCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000092-0000-1000-8000-0026BB765291", + "Carbon Dioxide Detected", + 1, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedEnum.java new file mode 100644 index 000000000..a6fe144b0 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.carbondioxidesensor; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Carbon Dioxide levels are normal” 1 ”Carbon Dioxide levels are abnormal” */ +public enum CarbonDioxideDetectedEnum implements CharacteristicEnum { + NORMAL(0), + ABNORMAL(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(CarbonDioxideDetectedEnum.values()) + .collect(Collectors.toMap(CarbonDioxideDetectedEnum::getCode, t -> t)); + } + + public static CarbonDioxideDetectedEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + CarbonDioxideDetectedEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideLevelCharacteristic.java new file mode 100644 index 000000000..9b3ca5447 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideLevelCharacteristic.java @@ -0,0 +1,31 @@ +package io.github.hapjava.characteristics.impl.carbondioxidesensor; + +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic indicates the detected level of Carbon Dioxide in parts per million (ppm). + */ +public class CarbonDioxideLevelCharacteristic extends FloatCharacteristic { + + public CarbonDioxideLevelCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000093-0000-1000-8000-0026BB765291", + "Carbon Dioxide Level", + 0, + 100000, + 1, + "ppm", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxidePeakLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxidePeakLevelCharacteristic.java new file mode 100644 index 000000000..5a9201fc2 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxidePeakLevelCharacteristic.java @@ -0,0 +1,32 @@ +package io.github.hapjava.characteristics.impl.carbondioxidesensor; + +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic indicates the highest detected level (ppm) of carbon dioxide detected by a + * sensor. + */ +public class CarbonDioxidePeakLevelCharacteristic extends FloatCharacteristic { + + public CarbonDioxidePeakLevelCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000094-0000-1000-8000-0026BB765291", + "Carbon Dioxide Level", + 0, + 100000, + 1, + "ppm", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedCharacteristic.java new file mode 100644 index 000000000..ddca27549 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedCharacteristic.java @@ -0,0 +1,31 @@ +package io.github.hapjava.characteristics.impl.carbonmonoxidesensor; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic indicates if a sensor detects abnormal levels of Carbon Monoxide. Value + * should revert to 0 after the Carbon Monoxide levels drop to normal levels + */ +public class CarbonMonoxideDetectedCharacteristic + extends EnumCharacteristic implements EventableCharacteristic { + + public CarbonMonoxideDetectedCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000069-0000-1000-8000-0026BB765291", + "Carbon Monoxide Detected", + 1, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedEnum.java new file mode 100644 index 000000000..2d1adf7f0 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.carbonmonoxidesensor; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Carbon Monoxide levels are normal” 1 ”Carbon Monoxide levels are abnormal” */ +public enum CarbonMonoxideDetectedEnum implements CharacteristicEnum { + NORMAL(0), + ABNORMAL(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(CarbonMonoxideDetectedEnum.values()) + .collect(Collectors.toMap(CarbonMonoxideDetectedEnum::getCode, t -> t)); + } + + public static CarbonMonoxideDetectedEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + CarbonMonoxideDetectedEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideLevelCharacteristic.java new file mode 100644 index 000000000..175cb497e --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideLevelCharacteristic.java @@ -0,0 +1,29 @@ +package io.github.hapjava.characteristics.impl.carbonmonoxidesensor; + +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic indicates the Carbon Monoxide levels detected in parts per million (ppm). */ +public class CarbonMonoxideLevelCharacteristic extends FloatCharacteristic { + + public CarbonMonoxideLevelCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000090-0000-1000-8000-0026BB765291", + "Carbon Monoxide Level", + 0, + 100, + 1, + "ppm", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxidePeakLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxidePeakLevelCharacteristic.java new file mode 100644 index 000000000..744e53858 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxidePeakLevelCharacteristic.java @@ -0,0 +1,32 @@ +package io.github.hapjava.characteristics.impl.carbonmonoxidesensor; + +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic indicates the highest detected level (ppm) of Carbon Monoxide detected by a + * sensor. + */ +public class CarbonMonoxidePeakLevelCharacteristic extends FloatCharacteristic { + + public CarbonMonoxidePeakLevelCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000091-0000-1000-8000-0026BB765291", + "Carbon Monoxide Peak Level", + 0, + 100000, + 1, + "ppm", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveCharacteristic.java new file mode 100644 index 000000000..741c6f5b1 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveCharacteristic.java @@ -0,0 +1,33 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** The Active characteristic indicates whether the service is currently active. */ +public class ActiveCharacteristic extends EnumCharacteristic { + + public ActiveCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000B0-0000-1000-8000-0026BB765291", + "Active", + 1, + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } + + @Override + protected void setValue(Integer value) throws Exception { + if (setter.isPresent()) setter.get().accept(ActiveEnum.fromCode(value)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveEnum.java new file mode 100644 index 000000000..7fd0318c4 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveEnum.java @@ -0,0 +1,34 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Inactive” 1 ”Active” 2-255 ”Reserved” */ +public enum ActiveEnum implements CharacteristicEnum { + INACTIVE(0), + ACTIVE(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(ActiveEnum.values()).collect(Collectors.toMap(ActiveEnum::getCode, t -> t)); + } + + public static ActiveEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + ActiveEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/InUseCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/InUseCharacteristic.java new file mode 100644 index 000000000..e8acb12c5 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/InUseCharacteristic.java @@ -0,0 +1,28 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes if the service is in use. The service must be Active before the + * value of this characteristic can be set to in use. + */ +public class InUseCharacteristic extends EnumCharacteristic { + public InUseCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000D2-0000-1000-8000-0026BB765291", + "In Use", + 1, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/InUseEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/common/InUseEnum.java new file mode 100644 index 000000000..0b368b128 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/InUseEnum.java @@ -0,0 +1,34 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Not in use” 1 ”In use” 2-255 ”Reserved” */ +public enum InUseEnum implements CharacteristicEnum { + NOT_IN_USE(0), + IN_USE(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(InUseEnum.values()).collect(Collectors.toMap(InUseEnum::getCode, t -> t)); + } + + public static InUseEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + InUseEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredCharacteristic.java new file mode 100644 index 000000000..f9a0539ee --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredCharacteristic.java @@ -0,0 +1,37 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes if the service is configured for use. For example, all of the + * valves in an irrigation system may not be configured depending on physical wire connection. If + * the accessory supports updating through HAP, then it must also advertise Paired Write in the + * permissions. + */ +public class IsConfiguredCharacteristic extends EnumCharacteristic { + public IsConfiguredCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000D6-0000-1000-8000-0026BB765291", + "Is Configured", + 1, + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } + + @Override + protected void setValue(Integer value) throws Exception { + if (setter.isPresent()) setter.get().accept(IsConfiguredEnum.fromCode(value)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredEnum.java new file mode 100644 index 000000000..1d20f6b3b --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Not Configured” 1 ”Configured” 2-255 ”Reserved” */ +public enum IsConfiguredEnum implements CharacteristicEnum { + NOT_CONFIGURED(0), + CONFIGURED(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(IsConfiguredEnum.values()) + .collect(Collectors.toMap(IsConfiguredEnum::getCode, t -> t)); + } + + public static IsConfiguredEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + IsConfiguredEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ObstructionDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ObstructionDetectedCharacteristic.java new file mode 100644 index 000000000..7de444190 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ObstructionDetectedCharacteristic.java @@ -0,0 +1,27 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the current state of an obstruction sensor, such as one that is + * used in a garage door. If the state is true then there is an obstruction detected. + */ +public class ObstructionDetectedCharacteristic extends BooleanCharacteristic { + public ObstructionDetectedCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000024-0000-1000-8000-0026BB765291", + "Current state of an obstruction sensor", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/OnCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/OnCharacteristic.java new file mode 100644 index 000000000..21f0999f9 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/OnCharacteristic.java @@ -0,0 +1,27 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic represents the states for “on” and “off”. */ +public class OnCharacteristic extends BooleanCharacteristic { + + public OnCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000025-0000-1000-8000-0026BB765291", + "On / Off state", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelIndexCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelIndexCharacteristic.java new file mode 100644 index 000000000..c5fa2c05b --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelIndexCharacteristic.java @@ -0,0 +1,28 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +/** + * This characteristic should be used identify the index of the label that maps to "Service Label + * Namespace” used by the accessory. + */ +public class ServiceLabelIndexCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + public ServiceLabelIndexCharacteristic(Supplier> getter) { + super( + "000000CB-0000-1000-8000-0026BB765291", + "service label index", + 0, + 100, + "%", + Optional.of(getter), + Optional.empty(), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceCharacteristic.java new file mode 100644 index 000000000..d7444e9a2 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceCharacteristic.java @@ -0,0 +1,26 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +/** + * This characteristic describes the naming schema for an accessory. For example, this + * characteristic can be used to describe the type of labels used to identify individual services of + * an accessory. + */ +public class ServiceLabelNamespaceCharacteristic + extends EnumCharacteristic { + public ServiceLabelNamespaceCharacteristic( + Supplier> getter) { + super( + "000000CD-0000-1000-8000-0026BB765291", + "service label namespace", + 1, + Optional.of(getter), + Optional.empty(), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceEnum.java new file mode 100644 index 000000000..0523a276a --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceEnum.java @@ -0,0 +1,37 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 0 ”Dots. For e.g ”.” ”..” ”...” ”....”” 1 ”Arabic numerals. For e.g. 0,1,2,3” 2-255 ”Reserved” + */ +public enum ServiceLabelNamespaceEnum implements CharacteristicEnum { + DOTS(0), + ARABIC_NUMERALS(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(ServiceLabelNamespaceEnum.values()) + .collect(Collectors.toMap(ServiceLabelNamespaceEnum::getCode, t -> t)); + } + + public static ServiceLabelNamespaceEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + ServiceLabelNamespaceEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusActiveCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusActiveCharacteristic.java new file mode 100644 index 000000000..d46a994d0 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusActiveCharacteristic.java @@ -0,0 +1,27 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes an accessoryʼs current working status. A value of true indicates + * that the accessory is active and is functioning without any errors. + */ +public class StatusActiveCharacteristic extends BooleanCharacteristic { + public StatusActiveCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000075-0000-1000-8000-0026BB765291", + "Accessory active and functioning", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultCharacteristic.java new file mode 100644 index 000000000..d972b76a3 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultCharacteristic.java @@ -0,0 +1,30 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes an accessory which has a fault. A non-zero value indicates that the + * accessory has experienced a fault that may be interfering with its intended functionality. A + * value of 0 indicates that there is no fault. + */ +public class StatusFaultCharacteristic extends EnumCharacteristic { + + public StatusFaultCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000077-0000-1000-8000-0026BB765291", + "Status Fault", + 1, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultEnum.java new file mode 100644 index 000000000..4fd866847 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”No Fault” 1 ”General Fault” */ +public enum StatusFaultEnum implements CharacteristicEnum { + NO_FAULT(0), + GENERAL_FAULT(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(StatusFaultEnum.values()) + .collect(Collectors.toMap(StatusFaultEnum::getCode, t -> t)); + } + + public static StatusFaultEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + StatusFaultEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedCharacteristic.java new file mode 100644 index 000000000..52a1c3f99 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedCharacteristic.java @@ -0,0 +1,30 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes an accessory which has been tampered with. A status of 1 indicates + * that the accessory has been tampered with. Value should return to 0 when the accessory has been + * reset to a non-tampered state. + */ +public class StatusTamperedCharacteristic extends EnumCharacteristic { + + public StatusTamperedCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000007A-0000-1000-8000-0026BB765291", + "Status Tampered", + 1, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedEnum.java new file mode 100644 index 000000000..2d725c132 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Accessory is not tampered” 1 ”Accessory is tampered with” */ +public enum StatusTamperedEnum implements CharacteristicEnum { + NOT_TAMPERED(0), + TAMPERED(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(StatusTamperedEnum.values()) + .collect(Collectors.toMap(StatusTamperedEnum::getCode, t -> t)); + } + + public static StatusTamperedEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + StatusTamperedEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactSensorStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactSensorStateCharacteristic.java new file mode 100644 index 000000000..81a2ea8f4 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactSensorStateCharacteristic.java @@ -0,0 +1,31 @@ +package io.github.hapjava.characteristics.impl.contactsensor; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the state of a door/window contact sensor. A value of 0 indicates + * that the contact is detected. A value of 1 indicates that the contact is not detected. + */ +public class ContactSensorStateCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { + + public ContactSensorStateCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000006A-0000-1000-8000-0026BB765291", + "Contact Sensor State", + 1, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactStateEnum.java new file mode 100644 index 000000000..275ed9734 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactStateEnum.java @@ -0,0 +1,37 @@ +package io.github.hapjava.characteristics.impl.contactsensor; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/* + * 0 ”Contact is detected” + * 1 ”Contact is not detected” + */ +public enum ContactStateEnum implements CharacteristicEnum { + DETECTED(0), + NOT_DETECTED(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(ContactStateEnum.values()) + .collect(Collectors.toMap(ContactStateEnum::getCode, t -> t)); + } + + public static ContactStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + ContactStateEnum(int code) { + this.code = code; + } + + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateCharacteristic.java new file mode 100644 index 000000000..bd0aa8a44 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateCharacteristic.java @@ -0,0 +1,28 @@ +package io.github.hapjava.characteristics.impl.fan; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes the current state of the fan. */ +public class CurrentFanStateCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { + + public CurrentFanStateCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000AF-0000-1000-8000-0026BB765291", + "Current Fan State", + 2, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateEnum.java new file mode 100644 index 000000000..674924e9a --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.fan; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +public enum CurrentFanStateEnum implements CharacteristicEnum { + INACTIVE(0), + IDLE(1), + BLOWING_AIR(2); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(CurrentFanStateEnum.values()) + .collect(Collectors.toMap(CurrentFanStateEnum::getCode, t -> t)); + } + + public static CurrentFanStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + CurrentFanStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsCharacteristic.java new file mode 100644 index 000000000..5d1f7a29d --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsCharacteristic.java @@ -0,0 +1,41 @@ +package io.github.hapjava.characteristics.impl.fan; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes a way to lock a set of physical controls on an accessory (eg. child + * lock). + */ +public class LockPhysicalControlsCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { + + public LockPhysicalControlsCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000A7-0000-1000-8000-0026BB765291", + "Lock Physical Locks", + 1, + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } + + @Override + protected void setValue(Integer value) throws Exception { + if (!setter.isPresent()) { + return; + } + setter.get().accept(LockPhysicalControlsEnum.fromCode(value)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsEnum.java new file mode 100644 index 000000000..87503ed96 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.fan; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Control lock disabled” 1 ”Control lock enabled” */ +public enum LockPhysicalControlsEnum implements CharacteristicEnum { + CONTROL_LOCK_DISABLED(0), + CONTROL_LOCK_ENABLED(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(LockPhysicalControlsEnum.values()) + .collect(Collectors.toMap(LockPhysicalControlsEnum::getCode, t -> t)); + } + + public static LockPhysicalControlsEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + LockPhysicalControlsEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationDirectionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationDirectionCharacteristic.java new file mode 100644 index 000000000..ce689c1dd --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationDirectionCharacteristic.java @@ -0,0 +1,38 @@ +package io.github.hapjava.characteristics.impl.fan; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes the direction of rotation of a fan. */ +public class RotationDirectionCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { + + public RotationDirectionCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000028-0000-1000-8000-0026BB765291", + "Rotation direction", + 1, + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } + + @Override + protected void setValue(Integer value) throws Exception { + if (!setter.isPresent()) { + return; + } + setter.get().accept(RotationDirectionEnum.fromCode(value)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationDirectionEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationDirectionEnum.java new file mode 100644 index 000000000..20651fd1c --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationDirectionEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.fan; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Clockwise” 1 ”Counter-clockwise” 2-255 ”Reserved” */ +public enum RotationDirectionEnum implements CharacteristicEnum { + CLOCKWISE(0), + COUNTER_CLOCKWISE(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(RotationDirectionEnum.values()) + .collect(Collectors.toMap(RotationDirectionEnum::getCode, t -> t)); + } + + public static RotationDirectionEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + RotationDirectionEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationSpeedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationSpeedCharacteristic.java new file mode 100644 index 000000000..27c715530 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationSpeedCharacteristic.java @@ -0,0 +1,32 @@ +package io.github.hapjava.characteristics.impl.fan; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes the rotation speed of a fan. */ +public class RotationSpeedCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + public RotationSpeedCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000029-0000-1000-8000-0026BB765291", + "Rotation Speed", + 0, + 100, + "%", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/SwingModeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/SwingModeCharacteristic.java new file mode 100644 index 000000000..c1bd55bda --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/SwingModeCharacteristic.java @@ -0,0 +1,38 @@ +package io.github.hapjava.characteristics.impl.fan; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes if swing mode is enabled. */ +public class SwingModeCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { + + public SwingModeCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000B6-0000-1000-8000-0026BB765291", + "Swing Mode", + 1, + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } + + @Override + protected void setValue(Integer value) throws Exception { + if (!setter.isPresent()) { + return; + } + setter.get().accept(SwingModeEnum.fromCode(value)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/SwingModeEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/SwingModeEnum.java new file mode 100644 index 000000000..928bafd27 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/SwingModeEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.fan; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Swing disabled” 1 ”Swing enabled” */ +public enum SwingModeEnum implements CharacteristicEnum { + SWING_DISABLED(0), + SWING_ENABLED(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(SwingModeEnum.values()) + .collect(Collectors.toMap(SwingModeEnum::getCode, t -> t)); + } + + public static SwingModeEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + SwingModeEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/TargetFanStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/TargetFanStateCharacteristic.java new file mode 100644 index 000000000..057c95b7a --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/TargetFanStateCharacteristic.java @@ -0,0 +1,38 @@ +package io.github.hapjava.characteristics.impl.fan; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes the target state of the fan. */ +public class TargetFanStateCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { + + public TargetFanStateCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000BF-0000-1000-8000-0026BB765291", + "Target Fan State", + 1, + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } + + @Override + protected void setValue(Integer value) throws Exception { + if (!setter.isPresent()) { + return; + } + setter.get().accept(TargetFanStateEnum.fromCode(value)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/TargetFanStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/TargetFanStateEnum.java new file mode 100644 index 000000000..600f07401 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/TargetFanStateEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.fan; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Manual” 1 ”Auto” */ +public enum TargetFanStateEnum implements CharacteristicEnum { + MANUAL(0), + AUTO(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(TargetFanStateEnum.values()) + .collect(Collectors.toMap(TargetFanStateEnum::getCode, t -> t)); + } + + public static TargetFanStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + TargetFanStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateCharacteristic.java new file mode 100644 index 000000000..77a9e9d91 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateCharacteristic.java @@ -0,0 +1,28 @@ +package io.github.hapjava.characteristics.impl.garagedoor; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes the current state of a door. */ +public class CurrentDoorStateCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { + + public CurrentDoorStateCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000000E-0000-1000-8000-0026BB765291", + "Current Door State", + 4, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateEnum.java new file mode 100644 index 000000000..145046a7e --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateEnum.java @@ -0,0 +1,42 @@ +package io.github.hapjava.characteristics.impl.garagedoor; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 0 ”Open. The door is fully open.” 1 ”Closed. The door is fully closed.” 2 ”Opening. The door is + * actively opening.” 3 ”Closing. The door is actively closing.” 4 ”Stopped. The door is not moving, + * and it is not fully open nor fully closed.” 5-255 ”Reserved” + */ +public enum CurrentDoorStateEnum implements CharacteristicEnum { + OPEN(0), + CLOSED(1), + OPENING(2), + CLOSING(3), + SOPPED(4); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(CurrentDoorStateEnum.values()) + .collect(Collectors.toMap(CurrentDoorStateEnum::getCode, t -> t)); + } + + public static CurrentDoorStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + CurrentDoorStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/TargetDoorStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/TargetDoorStateCharacteristic.java new file mode 100644 index 000000000..c3d5735db --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/TargetDoorStateCharacteristic.java @@ -0,0 +1,38 @@ +package io.github.hapjava.characteristics.impl.garagedoor; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes the target state of a door. */ +public class TargetDoorStateCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { + + public TargetDoorStateCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000032-0000-1000-8000-0026BB765291", + "Target Door State", + 1, + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } + + @Override + protected void setValue(Integer value) throws Exception { + if (!setter.isPresent()) { + return; + } + setter.get().accept(TargetDoorStateEnum.fromCode(value)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/TargetDoorStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/TargetDoorStateEnum.java new file mode 100644 index 000000000..ca282fdc3 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/TargetDoorStateEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.garagedoor; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Open” 1 ”Closed” 2-255 ”Reserved” */ +public enum TargetDoorStateEnum implements CharacteristicEnum { + OPEN(0), + CLOSED(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(TargetDoorStateEnum.values()) + .collect(Collectors.toMap(TargetDoorStateEnum::getCode, t -> t)); + } + + public static TargetDoorStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + TargetDoorStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java new file mode 100644 index 000000000..95b56d831 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java @@ -0,0 +1,32 @@ +package io.github.hapjava.characteristics.impl.humiditysensor; + +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the current relative humidity of the accessoryʼs environment. The + * value is expressed as a percentage (%). + */ +public class CurrentRelativeHumidityCharacteristic extends FloatCharacteristic { + + public CurrentRelativeHumidityCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000010-0000-1000-8000-0026BB765291", + "Current Relative Humidity", + 0, + 100, + 1, + "%", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java new file mode 100644 index 000000000..d9c7bec52 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java @@ -0,0 +1,34 @@ +package io.github.hapjava.characteristics.impl.humiditysensor; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the target relative humidity that the accessory is actively + * attempting to reach. The value is expressed as a percentage (%). + */ +public class TargetRelativeHumidityCharacteristic extends FloatCharacteristic { + + public TargetRelativeHumidityCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000034-0000-1000-8000-0026BB765291", + "Target Relative Humidity", + 0, + 100, + 1, + "%", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateCharacteristic.java new file mode 100644 index 000000000..72237a537 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateCharacteristic.java @@ -0,0 +1,31 @@ +package io.github.hapjava.characteristics.impl.leaksensor; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic indicates if a sensor detected a leak (e.g. water leak, gas leak). A value of + * 1 indicates that a leak is detected. Value should return to 0 when leak stops. + */ +public class LeakDetectedStateCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { + + public LeakDetectedStateCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000070-0000-1000-8000-0026BB765291", + "Leak Detected State", + 3, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateEnum.java new file mode 100644 index 000000000..1708230c8 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.leaksensor; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Leak is not detected” 1 ”Leak is detected” */ +public enum LeakDetectedStateEnum implements CharacteristicEnum { + LEAK_NOT_DETECTED(0), + LEAK_DETECTED(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(LeakDetectedStateEnum.values()) + .collect(Collectors.toMap(LeakDetectedStateEnum::getCode, t -> t)); + } + + public static LeakDetectedStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + LeakDetectedStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java new file mode 100644 index 000000000..d47be8cde --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java @@ -0,0 +1,36 @@ +package io.github.hapjava.characteristics.impl.lightbulb; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes a perceived level of brightness, e.g. for lighting, and can be used + * for backlights or color. The value is expressed as a percentage (%) of the maximum level of + * supported brightness. + */ +public class BrightnessCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + public BrightnessCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000008-0000-1000-8000-0026BB765291", + "level of brightness", + 0, + 100, + "%", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/ColorTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/ColorTemperatureCharacteristic.java new file mode 100644 index 000000000..722782140 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/ColorTemperatureCharacteristic.java @@ -0,0 +1,36 @@ +package io.github.hapjava.characteristics.impl.lightbulb; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes color temperature which is represented in reciprocal megaKelvin + * (MK-1) or mirek scale. (M = 1,000,000 / K where M is the desired mirek value and K is temperature + * in Kelvin) + */ +public class ColorTemperatureCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + public ColorTemperatureCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000CE-0000-1000-8000-0026BB765291", + "color temperature", + 50, + 400, + "K", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java new file mode 100644 index 000000000..8d8823529 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java @@ -0,0 +1,30 @@ +package io.github.hapjava.characteristics.impl.lightbulb; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes hue or color. */ +public class HueCharacteristic extends FloatCharacteristic { + public HueCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000013-0000-1000-8000-0026BB765291", + "hue or color", + 0, + 360, + 1, + "arcdegrees", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java new file mode 100644 index 000000000..8a07caca5 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java @@ -0,0 +1,33 @@ +package io.github.hapjava.characteristics.impl.lightbulb; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes color saturation. */ +public class SaturationCharacteristic extends FloatCharacteristic + implements EventableCharacteristic { + + public SaturationCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000002F-0000-1000-8000-0026BB765291", + "color saturation", + 0, + 100, + 1, + "%", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightsensor/CurrentAmbientLightLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightsensor/CurrentAmbientLightLevelCharacteristic.java new file mode 100644 index 000000000..d2dd850cc --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightsensor/CurrentAmbientLightLevelCharacteristic.java @@ -0,0 +1,32 @@ +package io.github.hapjava.characteristics.impl.lightsensor; + +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic indicates the current light level. The value is expressed in Lux units + * (lumens/m2) + */ +public class CurrentAmbientLightLevelCharacteristic extends FloatCharacteristic { + + public CurrentAmbientLightLevelCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000006B-0000-1000-8000-0026BB765291", + "ambient light level", + 0.0001, + 100000, + 0.0001, + "lux", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lock/LockCurrentStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lock/LockCurrentStateCharacteristic.java new file mode 100644 index 000000000..df2f27592 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/lock/LockCurrentStateCharacteristic.java @@ -0,0 +1,26 @@ +package io.github.hapjava.characteristics.impl.lock; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** The current state of the physical security mechanism (e.g. deadbolt). */ +public class LockCurrentStateCharacteristic extends EnumCharacteristic { + + public LockCurrentStateCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000001D-0000-1000-8000-0026BB765291", + "Current Lock State", + 3, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lock/LockCurrentStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/lock/LockCurrentStateEnum.java new file mode 100644 index 000000000..66eb23afe --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/lock/LockCurrentStateEnum.java @@ -0,0 +1,37 @@ +package io.github.hapjava.characteristics.impl.lock; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Unsecured” 1 ”Secured” 2 ”Jammed” 3 ”Unknown” 4-255 ”Reserved” */ +public enum LockCurrentStateEnum implements CharacteristicEnum { + UNSECURED(0), + SECURED(1), + JAMMED(2), + UNKNOWN(3); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(LockCurrentStateEnum.values()) + .collect(Collectors.toMap(LockCurrentStateEnum::getCode, t -> t)); + } + + public static LockCurrentStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + LockCurrentStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lock/LockTargetStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lock/LockTargetStateCharacteristic.java new file mode 100644 index 000000000..4c7821dc6 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/lock/LockTargetStateCharacteristic.java @@ -0,0 +1,36 @@ +package io.github.hapjava.characteristics.impl.lock; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** The target state of the physical security mechanism (e.g. deadbolt). */ +public class LockTargetStateCharacteristic extends EnumCharacteristic { + + public LockTargetStateCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000001E-0000-1000-8000-0026BB765291", + "Target Lock State", + 1, + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } + + @Override + protected void setValue(Integer value) throws Exception { + if (!setter.isPresent()) { + return; + } + setter.get().accept(LockTargetStateEnum.fromCode(value)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lock/LockTargetStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/lock/LockTargetStateEnum.java new file mode 100644 index 000000000..d5aa0d536 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/lock/LockTargetStateEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.lock; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Unsecured” 1 ”Secured” 4-255 ”Reserved” */ +public enum LockTargetStateEnum implements CharacteristicEnum { + UNSECURED(0), + SECURED(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(LockTargetStateEnum.values()) + .collect(Collectors.toMap(LockTargetStateEnum::getCode, t -> t)); + } + + public static LockTargetStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + LockTargetStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/motionsensor/MotionDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/motionsensor/MotionDetectedCharacteristic.java new file mode 100644 index 000000000..239e004de --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/motionsensor/MotionDetectedCharacteristic.java @@ -0,0 +1,25 @@ +package io.github.hapjava.characteristics.impl.motionsensor; + +import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic indicates if motion (e.g. a person moving) was detected. */ +public class MotionDetectedCharacteristic extends BooleanCharacteristic { + + public MotionDetectedCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000022-0000-1000-8000-0026BB765291", + "Motion detection", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/occupancysensor/OccupancyDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/occupancysensor/OccupancyDetectedCharacteristic.java new file mode 100644 index 000000000..995cbcf21 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/occupancysensor/OccupancyDetectedCharacteristic.java @@ -0,0 +1,26 @@ +package io.github.hapjava.characteristics.impl.occupancysensor; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic indicates if occupancy was detected (e.g. a person present). */ +public class OccupancyDetectedCharacteristic extends EnumCharacteristic { + + public OccupancyDetectedCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000071-0000-1000-8000-0026BB765291", + "Occupancy Detected", + 1, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/occupancysensor/OccupancyDetectedEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/occupancysensor/OccupancyDetectedEnum.java new file mode 100644 index 000000000..688fa9d24 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/occupancysensor/OccupancyDetectedEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.occupancysensor; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Occupancy is not detected” 1 ”Occupancy is detected” */ +public enum OccupancyDetectedEnum implements CharacteristicEnum { + NOT_DETECTED(0), + DETECTED(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(OccupancyDetectedEnum.values()) + .collect(Collectors.toMap(OccupancyDetectedEnum::getCode, t -> t)); + } + + public static OccupancyDetectedEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + OccupancyDetectedEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/outlet/OutletInUseCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/outlet/OutletInUseCharacteristic.java new file mode 100644 index 000000000..7158f4b96 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/outlet/OutletInUseCharacteristic.java @@ -0,0 +1,28 @@ +package io.github.hapjava.characteristics.impl.outlet; + +import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes if the power outlet has an appliance e.g., a floor lamp, physically + * plugged in. This characteristic is set to True even if the plugged-in appliance is off. + */ +public class OutletInUseCharacteristic extends BooleanCharacteristic { + + public OutletInUseCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000026-0000-1000-8000-0026BB765291", + "Outlet in Use", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/CurrentSecuritySystemStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/CurrentSecuritySystemStateCharacteristic.java new file mode 100644 index 000000000..0324a5554 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/CurrentSecuritySystemStateCharacteristic.java @@ -0,0 +1,27 @@ +package io.github.hapjava.characteristics.impl.securitysystem; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes the state of a security system */ +public class CurrentSecuritySystemStateCharacteristic + extends EnumCharacteristic { + + public CurrentSecuritySystemStateCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000066-0000-1000-8000-0026BB765291", + "Current Security System State", + 4, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/CurrentSecuritySystemStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/CurrentSecuritySystemStateEnum.java new file mode 100644 index 000000000..90a66cf7e --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/CurrentSecuritySystemStateEnum.java @@ -0,0 +1,52 @@ +package io.github.hapjava.characteristics.impl.securitysystem; + +import io.github.hapjava.accessories.SecuritySystemAccessory; +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * The current state of a {@link SecuritySystemAccessory}. Unlike {@link + * TargetSecuritySystemStateEnum}, this enum includes a triggered state. 0 ”Stay Arm. The home is + * occupied and the residents are active. e.g. morning or evenings” 1 ”Away Arm. The home is + * unoccupied” 2 ”Night Arm. The home is occupied and the residents are sleeping” 3 ”Disarmed” 4 + * ”Alarm Triggered” 5-255 ”Reserved” + * + * @author Gaston Dombiak + */ +public enum CurrentSecuritySystemStateEnum implements CharacteristicEnum { + + /** The home is occupied and residents are active. */ + STAY_ARM(0), + /** The home is unoccupied. */ + AWAY_ARM(1), + /** The home is occupied and residents are sleeping. */ + NIGHT_ARM(2), + /** The security system is disarmed. */ + DISARMED(3), + /** The security system is triggered. */ + TRIGGERED(4); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(CurrentSecuritySystemStateEnum.values()) + .collect(Collectors.toMap(CurrentSecuritySystemStateEnum::getCode, t -> t)); + } + + public static CurrentSecuritySystemStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + CurrentSecuritySystemStateEnum(int code) { + this.code = code; + } + + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java new file mode 100644 index 000000000..2c92b9eec --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java @@ -0,0 +1,30 @@ +package io.github.hapjava.characteristics.impl.securitysystem; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the type of alarm triggered by a security system. A value of 1 + * indicates an ʼunknownʼ cause. Value should revert to 0 when the alarm conditions are cleared. + */ +public class SecuritySystemAlarmTypeCharacteristic + extends EnumCharacteristic { + + public SecuritySystemAlarmTypeCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000008E-0000-1000-8000-0026BB765291", + "Alert Type", + 1, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeEnum.java new file mode 100644 index 000000000..00bc0f24e --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeEnum.java @@ -0,0 +1,45 @@ +package io.github.hapjava.characteristics.impl.securitysystem; + +import io.github.hapjava.accessories.SecuritySystemAccessory; +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Type of alarm of a {@link SecuritySystemAccessory}. + * + *

This characteristic describes the type of alarm triggered by a security system. A value of 1 + * indicates an ʼunknownʼ cause. Value should revert to 0 when the alarm conditions are cleared. + * + * @author Gaston Dombiak + */ +public enum SecuritySystemAlarmTypeEnum implements CharacteristicEnum { + + /** Alarm conditions are cleared */ + NO_ALARM(0), + /** Alarm type is not known */ + UNKNOWN(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(SecuritySystemAlarmTypeEnum.values()) + .collect(Collectors.toMap(SecuritySystemAlarmTypeEnum::getCode, t -> t)); + } + + public static SecuritySystemAlarmTypeEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + SecuritySystemAlarmTypeEnum(int code) { + this.code = code; + } + + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/TargetSecuritySystemStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/TargetSecuritySystemStateCharacteristic.java new file mode 100644 index 000000000..849ffa80a --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/TargetSecuritySystemStateCharacteristic.java @@ -0,0 +1,37 @@ +package io.github.hapjava.characteristics.impl.securitysystem; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes the target state of the security system. */ +public class TargetSecuritySystemStateCharacteristic + extends EnumCharacteristic { + + public TargetSecuritySystemStateCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000067-0000-1000-8000-0026BB765291", + "Target Security System State", + 3, + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } + + @Override + protected void setValue(Integer value) throws Exception { + if (!setter.isPresent()) { + return; + } + setter.get().accept(TargetSecuritySystemStateEnum.fromCode(value)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/TargetSecuritySystemStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/TargetSecuritySystemStateEnum.java new file mode 100644 index 000000000..81b0adc22 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/TargetSecuritySystemStateEnum.java @@ -0,0 +1,50 @@ +package io.github.hapjava.characteristics.impl.securitysystem; + +import io.github.hapjava.accessories.SecuritySystemAccessory; +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * The target state of a {@link SecuritySystemAccessory}. + * + *

0 ”Stay Arm. The home is occupied and the residents are active. e.g. morning or evenings” 1 + * ”Away Arm. The home is unoccupied” 2 ”Night Arm. The home is occupied and the residents are + * sleeping” 3 ”Disarm” 4-255 ”Reserved” + * + * @author Gaston Dombiak + */ +public enum TargetSecuritySystemStateEnum implements CharacteristicEnum { + + /** Arm the system when the home is occupied and residents are active. */ + STAY_ARM(0), + /** Arm the system when the home is unoccupied. */ + AWAY_ARM(1), + /** Arm the system when the home is occupied and residents are sleeping. */ + NIGHT_ARM(2), + /** Disarm the system. */ + DISARM(3); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(TargetSecuritySystemStateEnum.values()) + .collect(Collectors.toMap(TargetSecuritySystemStateEnum::getCode, t -> t)); + } + + public static TargetSecuritySystemStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + TargetSecuritySystemStateEnum(int code) { + this.code = code; + } + + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedCharacteristic.java new file mode 100644 index 000000000..55d415c49 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedCharacteristic.java @@ -0,0 +1,29 @@ +package io.github.hapjava.characteristics.impl.smokesensor; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic indicates if a sensor detects abnormal levels of smoke. A value of 1 + * indicates that smoke levels are abnormal. Value should return to 0 when smoke levels are normal. + */ +public class SmokeDetectedCharacteristic extends EnumCharacteristic { + + public SmokeDetectedCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000076-0000-1000-8000-0026BB765291", + "Smoke Detected", + 1, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedStateEnum.java new file mode 100644 index 000000000..c6e3b6d5d --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedStateEnum.java @@ -0,0 +1,34 @@ +package io.github.hapjava.characteristics.impl.smokesensor; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Smoke is not detected” 1 ”Smoke is detected” */ +public enum SmokeDetectedStateEnum implements CharacteristicEnum { + NOT_DETECTED(0), + DETECTED(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(SmokeDetectedStateEnum.values()) + .collect(Collectors.toMap(SmokeDetectedStateEnum::getCode, t -> t)); + } + + public static SmokeDetectedStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + SmokeDetectedStateEnum(int code) { + this.code = code; + } + + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java new file mode 100644 index 000000000..93f4e17fe --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.thermostat; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the cooling threshold in Celsius for accessories that support + * simultaneous heating and cooling. The value of this characteristic represents the maximum + * temperature that must be reached before cooling is turned on. + */ +public class CoolingThresholdTemperatureCharacteristic extends FloatCharacteristic { + + public CoolingThresholdTemperatureCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000000D-0000-1000-8000-0026BB765291", + "cooling threshold", + 10, + 35, + 0.1, + "C", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateCharacteristic.java new file mode 100644 index 000000000..e58f42105 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateCharacteristic.java @@ -0,0 +1,30 @@ +package io.github.hapjava.characteristics.impl.thermostat; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the current mode of an accessory that supports cooling or heating + * its environment, e.g. a thermostat is ”heating” a room to 75 degrees Fahrenheit. + */ +public class CurrentHeatingCoolingStateCharacteristic + extends EnumCharacteristic { + + public CurrentHeatingCoolingStateCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000000F-0000-1000-8000-0026BB765291", + "Current heating cooling mode", + 2, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateEnum.java new file mode 100644 index 000000000..9f2c01f85 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateEnum.java @@ -0,0 +1,37 @@ +package io.github.hapjava.characteristics.impl.thermostat; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 0 ”Off.” 1 ”Heat. The Heater is currently on.” 2 ”Cool. Cooler is currently on.” 3-255 ”Reserved” + */ +public enum CurrentHeatingCoolingStateEnum implements CharacteristicEnum { + OFF(0), + HEAT(1), + COOL(2); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(CurrentHeatingCoolingStateEnum.values()) + .collect(Collectors.toMap(CurrentHeatingCoolingStateEnum::getCode, t -> t)); + } + + public static CurrentHeatingCoolingStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + CurrentHeatingCoolingStateEnum(int code) { + this.code = code; + } + + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java new file mode 100644 index 000000000..f80d760fc --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java @@ -0,0 +1,29 @@ +package io.github.hapjava.characteristics.impl.thermostat; + +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** characteristic describes the current temperature of the environment in Celsius */ +public class CurrentTemperatureCharacteristic extends FloatCharacteristic { + + public CurrentTemperatureCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000011-0000-1000-8000-0026BB765291", + "current temperature", + 0, + 100, + 0.1, + "C", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java new file mode 100644 index 000000000..0693b737c --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.thermostat; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the heating threshold in Celsius for accessories that support + * simultaneous heating and cooling. The value of this characteristic represents the minimum + * temperature that must be reached before heating is turned on. + */ +public class HeatingThresholdTemperatureCharacteristic extends FloatCharacteristic { + + public HeatingThresholdTemperatureCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000012-0000-1000-8000-0026BB765291", + "heating threshold", + 0, + 25, + 0.1, + "C", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateCharacteristic.java new file mode 100644 index 000000000..93bdd4e2e --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateCharacteristic.java @@ -0,0 +1,40 @@ +package io.github.hapjava.characteristics.impl.thermostat; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the target mode of an accessory that supports heating/cooling, e.g. + * a thermostat. + */ +public class TargetHeatingCoolingStateCharacteristic + extends EnumCharacteristic { + + public TargetHeatingCoolingStateCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000033-0000-1000-8000-0026BB765291", + "Target heating cooling mode", + 3, + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } + + @Override + protected void setValue(Integer value) throws Exception { + if (!setter.isPresent()) { + return; + } + setter.get().accept(TargetHeatingCoolingStateEnum.fromCode(value)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateEnum.java new file mode 100644 index 000000000..62f2a3d81 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateEnum.java @@ -0,0 +1,43 @@ +package io.github.hapjava.characteristics.impl.thermostat; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 0 ”Off” 1 ”Heat. If the current temperature is below the target temperature then turn on + * heating.” 2 ”Cool. If the current temperature is above the target temperature then turn on + * cooling.” 3 ”Auto. Turn on heating or cooling to maintain temperature within the heating and + * cooling threshold of the target temperature.” 4-255 ”Reserved” + * + * @author Andy Lintner + */ +public enum TargetHeatingCoolingStateEnum implements CharacteristicEnum { + OFF(0), + HEAT(1), + COOL(2), + AUTO(3); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(TargetHeatingCoolingStateEnum.values()) + .collect(Collectors.toMap(t -> t.getCode(), t -> t)); + } + + public static TargetHeatingCoolingStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + private TargetHeatingCoolingStateEnum(int code) { + this.code = code; + } + + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java new file mode 100644 index 000000000..9e2e504dd --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.thermostat; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the target temperature in Celsius that the accessory is actively + * attempting to reach. For example, a thermostat cooling a room to 75 degrees Fahrenheit would set + * the target temperature value to 23.9 degrees Celsius. + */ +public class TargetTemperatureCharacteristic extends FloatCharacteristic { + + public TargetTemperatureCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000035-0000-1000-8000-0026BB765291", + "target temperature", + 10, + 38, + 0.1, + "C", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitCharacteristic.java new file mode 100644 index 000000000..0492ce51b --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitCharacteristic.java @@ -0,0 +1,40 @@ +package io.github.hapjava.characteristics.impl.thermostat; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes units of temperature used for presentation purposes (e.g. the units + * of temperature displayed on the screen). + */ +public class TemperatureDisplayUnitCharacteristic + extends EnumCharacteristic { + + public TemperatureDisplayUnitCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000036-0000-1000-8000-0026BB765291", + "Temperature units", + 1, + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } + + @Override + protected void setValue(Integer value) throws Exception { + if (!setter.isPresent()) { + return; + } + setter.get().accept(TemperatureDisplayUnitEnum.fromCode(value)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitEnum.java new file mode 100644 index 000000000..8440f5de9 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitEnum.java @@ -0,0 +1,41 @@ +package io.github.hapjava.characteristics.impl.thermostat; + +import io.github.hapjava.accessories.TemperatureSensorAccessory; +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * The temperature unit used by a {@link TemperatureSensorAccessory}. + * + *

0 ”Celsius” 1 ”Fahrenheit” 2-255 ”Reserved” + * + * @author Andy Lintner + */ +public enum TemperatureDisplayUnitEnum implements CharacteristicEnum { + CELSIUS(0), + FAHRENHEIT(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(TemperatureDisplayUnitEnum.values()) + .collect(Collectors.toMap(t -> t.getCode(), t -> t)); + } + + static TemperatureDisplayUnitEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + private TemperatureDisplayUnitEnum(int code) { + this.code = code; + } + + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/valve/RemainingDurationCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/valve/RemainingDurationCharacteristic.java new file mode 100644 index 000000000..5f16e396a --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/valve/RemainingDurationCharacteristic.java @@ -0,0 +1,38 @@ +package io.github.hapjava.characteristics.impl.valve; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the remaining duration on the accessory. Notifications on this + * characteristic must only be used if the remaining duration increases/decreases from the + * accessoryʼs usual countdown of remaining duration and when the duration reaches 0. e.g. It must + * not send notifications when the remaining duration is ticking down from 100,99,98... if 100 was + * the initial Set duration. However, if the remaining duration changes to 95 from 92 (increase) or + * 85 from 92 (decrease which is not part of the usual duration countdown), it must send a + * notification. This duration is defined in seconds. + */ +public class RemainingDurationCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + public RemainingDurationCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000D4-0000-1000-8000-0026BB765291", + "remaining duration", + 0, + 3600, + "s", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/valve/SetDurationCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/valve/SetDurationCharacteristic.java new file mode 100644 index 000000000..22be62b64 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/valve/SetDurationCharacteristic.java @@ -0,0 +1,36 @@ +package io.github.hapjava.characteristics.impl.valve; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the set duration. For a Valve” this duration defines how long a + * valve should be set to ʼIn Useʼ. Once the valve is ʼIn Useʼ, any changes to this characteristic + * take affect in the next operation when the Valve is Active. This duration is defined in seconds. + */ +public class SetDurationCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + public SetDurationCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000D3-0000-1000-8000-0026BB765291", + "set duration", + 0, + 3600, + "s", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/valve/ValveTypeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/valve/ValveTypeCharacteristic.java new file mode 100644 index 000000000..b3a55278c --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/valve/ValveTypeCharacteristic.java @@ -0,0 +1,26 @@ +package io.github.hapjava.characteristics.impl.valve; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes the type of valve. */ +public class ValveTypeCharacteristic extends EnumCharacteristic { + + public ValveTypeCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000D5-0000-1000-8000-0026BB765291", + "Valve type", + 3, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/valve/ValveTypeEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/valve/ValveTypeEnum.java new file mode 100644 index 000000000..d18d923e2 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/valve/ValveTypeEnum.java @@ -0,0 +1,42 @@ +package io.github.hapjava.characteristics.impl.valve; + +import io.github.hapjava.accessories.ValveAccessory; +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * The mode used by a {@link ValveAccessory} + * + *

0 ”Generic valve” 1 ”Irrigation” 2 ”Shower head” 3 ”Water faucet” 4-255 ”Reserved” + * + * @author Tim Harper + */ +public enum ValveTypeEnum implements CharacteristicEnum { + GENERIC(0), + IRRIGATION(1), + SHOWER(2), + WATER_FAUCET(3); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(ValveTypeEnum.values()).collect(Collectors.toMap(t -> t.getCode(), t -> t)); + } + + public static ValveTypeEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + private ValveTypeEnum(int code) { + this.code = code; + } + + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java new file mode 100644 index 000000000..8295d1db9 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java @@ -0,0 +1,36 @@ +package io.github.hapjava.characteristics.impl.windowcovering; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the current angle of horizontal slats for accessories such as + * windows, fans, portable heater/coolers etc. This characteristic takes values between -90 and 90. + * A value of 0 indicates that the slats are rotated to a fully open position. A value of -90 + * indicates that the slats are rotated all the way in a direction where the user-facing edge is + * higher than the window-facing edge. + */ +public class CurrentHorizontalTiltAngleCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + public CurrentHorizontalTiltAngleCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000006C-0000-1000-8000-0026BB765291", + "current horizontal tilt angel", + -90, + 90, + "arcdegrees", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java new file mode 100644 index 000000000..aaf19ec2e --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java @@ -0,0 +1,36 @@ +package io.github.hapjava.characteristics.impl.windowcovering; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the current position of accessories. This characteristic can be + * used with doors, windows, awnings or window coverings. For windows and doors, a value of 0 + * indicates that a window (or door) is fully closed while a value of 100 indicates a fully open + * position. For blinds/shades/awnings, a value of 0 indicates a position that permits the least + * light and a value of 100 indicates a position that allows most light. + */ +public class CurrentPositionCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + public CurrentPositionCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000006D-0000-1000-8000-0026BB765291", + "current position", + 0, + 100, + "%", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java new file mode 100644 index 000000000..7504b4302 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java @@ -0,0 +1,36 @@ +package io.github.hapjava.characteristics.impl.windowcovering; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the current angle of vertical slats for accessories such as + * windows, fans, portable heater/coolers etc. This characteristic takes values between -90 and 90. + * A value of 0 indicates that the slats are rotated to be fully open. A value of -90 indicates that + * the slats are rotated all the way in a direction where the user-facing edge is to the left of the + * window-facing edge. + */ +public class CurrentVerticalTiltAngleCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + public CurrentVerticalTiltAngleCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000006E-0000-1000-8000-0026BB765291", + "current vertical tilt angel", + -90, + 90, + "arcdegrees", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/HoldPositionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/HoldPositionCharacteristic.java new file mode 100644 index 000000000..4d9892c73 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/HoldPositionCharacteristic.java @@ -0,0 +1,23 @@ +package io.github.hapjava.characteristics.impl.windowcovering; + +import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import java.util.Optional; + +/** + * This characteristic causes the service such as door or window covering to stop at its current + * position. A value of 1 must hold the state of the accessory. For e.g, the window must stop moving + * when this characteristic is written a value of 1. A value of 0 should be ignored. + */ +public class HoldPositionCharacteristic extends BooleanCharacteristic { + + public HoldPositionCharacteristic(ExceptionalConsumer setter) { + super( + "0000006F-0000-1000-8000-0026BB765291", + "Hold Position", + Optional.empty(), + Optional.of(setter), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateCharacteristic.java new file mode 100644 index 000000000..163ff8a2e --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateCharacteristic.java @@ -0,0 +1,29 @@ +package io.github.hapjava.characteristics.impl.windowcovering; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the state of the position of accessories. This characteristic can + * be used with doors, windows, awnings or window coverings for presentation purposes. + */ +public class PositionStateCharacteristic extends EnumCharacteristic { + + public PositionStateCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000072-0000-1000-8000-0026BB765291", + "Position state", + 2, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateEnum.java new file mode 100644 index 000000000..b82b56a27 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateEnum.java @@ -0,0 +1,39 @@ +package io.github.hapjava.characteristics.impl.windowcovering; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 0 ”Going to the minimum value specified in metadata” 1 ”Going to the maximum value specified in + * metadata” 2 ”Stopped” 3-255 ”Reserved” + */ +public enum PositionStateEnum implements CharacteristicEnum { + DECREASING(0), + INCREASING(1), + STOPPED(2); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(PositionStateEnum.values()) + .collect(Collectors.toMap(PositionStateEnum::getCode, t -> t)); + } + + public static PositionStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + PositionStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java new file mode 100644 index 000000000..34e425b3e --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.windowcovering; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the target angle of horizontal slats for accessories such as + * windows, fans, portable heater/coolers etc. + */ +public class TargetHorizontalTiltAngleCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + public TargetHorizontalTiltAngleCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000B2-0000-1000-8000-0026BB765291", + "target horizontal tilt angel", + -90, + 90, + "arcdegrees", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java new file mode 100644 index 000000000..02b3125a6 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java @@ -0,0 +1,38 @@ +package io.github.hapjava.characteristics.impl.windowcovering; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the target position of accessories. This characteristic can be used + * with doors, windows, awnings or window coverings. For windows and doors, a value of 0 indicates + * that a window (or door) is fully closed while a value of 100 indicates a fully open position. For + * blinds/shades/awnings, a value of 0 indicates a position that permits the least light and a value + * of 100 indicates a position that allows most light. + */ +public class TargetPositionCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + public TargetPositionCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000007C-0000-1000-8000-0026BB765291", + "target position", + 0, + 100, + "%", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java new file mode 100644 index 000000000..a3edbcad3 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.windowcovering; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the target angle of vertical slats for accessories such as windows, + * fans, portable heater/coolers etc. + */ +public class TargetVerticalTiltAngleCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + public TargetVerticalTiltAngleCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000007D-0000-1000-8000-0026BB765291", + "target vertical tilt angel", + -90, + 90, + "arcdegrees", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/impl/HomekitBridge.java b/src/main/java/io/github/hapjava/impl/HomekitBridge.java deleted file mode 100644 index acce783ce..000000000 --- a/src/main/java/io/github/hapjava/impl/HomekitBridge.java +++ /dev/null @@ -1,51 +0,0 @@ -package io.github.hapjava.impl; - -import io.github.hapjava.Service; -import io.github.hapjava.impl.accessories.Bridge; -import java.util.Collection; -import java.util.Collections; - -public class HomekitBridge implements Bridge { - - private final String label; - private final String serialNumber; - private final String model; - private final String manufacturer; - - public HomekitBridge(String label, String serialNumber, String model, String manufacturer) { - this.label = label; - this.serialNumber = serialNumber; - this.model = model; - this.manufacturer = manufacturer; - } - - @Override - public String getLabel() { - return label; - } - - @Override - public String getSerialNumber() { - return serialNumber; - } - - @Override - public String getModel() { - return model; - } - - @Override - public String getManufacturer() { - return manufacturer; - } - - @Override - public Collection getServices() { - return Collections.emptyList(); - } - - @Override - public int getId() { - return 1; - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/carbondioxide/CarbonDioxideDetectedCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/carbondioxide/CarbonDioxideDetectedCharacteristic.java deleted file mode 100644 index 5ce03856a..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/carbondioxide/CarbonDioxideDetectedCharacteristic.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.hapjava.impl.characteristics.carbondioxide; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.CarbonDioxideSensor; -import io.github.hapjava.accessories.properties.CarbonDioxideDetectedState; -import io.github.hapjava.characteristics.EnumCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class CarbonDioxideDetectedCharacteristic extends EnumCharacteristic - implements EventableCharacteristic { - - private final CarbonDioxideSensor carbonDioxideSensor; - - public CarbonDioxideDetectedCharacteristic(CarbonDioxideSensor carbonDioxideSensor) { - super("00000092-0000-1000-8000-0026BB765291", false, true, "Carbon Dioxide Detected", 1); - this.carbonDioxideSensor = carbonDioxideSensor; - } - - @Override - protected CompletableFuture getValue() { - return carbonDioxideSensor - .getCarbonDioxideDetectedState() - .thenApply(CarbonDioxideDetectedState::getCode); - } - - @Override - protected void setValue(Integer value) throws Exception { - // Read Only - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - carbonDioxideSensor.subscribeCarbonDioxideDetectedState(callback); - } - - @Override - public void unsubscribe() { - carbonDioxideSensor.unsubscribeCarbonDioxideDetectedState(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/carbondioxide/CarbonDioxideLevelCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/carbondioxide/CarbonDioxideLevelCharacteristic.java deleted file mode 100644 index f6f6ecc9e..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/carbondioxide/CarbonDioxideLevelCharacteristic.java +++ /dev/null @@ -1,54 +0,0 @@ -package io.github.hapjava.impl.characteristics.carbondioxide; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.CarbonDioxideSensor; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.FloatCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class CarbonDioxideLevelCharacteristic extends FloatCharacteristic - implements EventableCharacteristic { - - private final CarbonDioxideSensor sensor; - - public CarbonDioxideLevelCharacteristic(CarbonDioxideSensor sensor) { - super( - "00000093-0000-1000-8000-0026BB765291", - false, - true, - "Carbon Dioxide level", - 0, - 100000, - 0.1, - "%"); - this.sensor = sensor; - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - sensor.subscribeCarbonDioxideLevel(callback); - } - - @Override - public void unsubscribe() { - sensor.unsubscribeCarbonDioxideLevel(); - } - - @Override - protected void setValue(Double value) throws Exception { - // Read Only - } - - @Override - protected CompletableFuture getDoubleValue() { - return sensor.getCarbonDioxideLevel(); - } - - @Override - public String toString() { - return "CarbonDioxideLevelCharacteristic{" - + "sensor level =" - + sensor.getCarbonDioxideLevel() - + '}'; - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/carbonmonoxide/CarbonMonoxideDetectedCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/carbonmonoxide/CarbonMonoxideDetectedCharacteristic.java deleted file mode 100644 index cf38cc837..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/carbonmonoxide/CarbonMonoxideDetectedCharacteristic.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.hapjava.impl.characteristics.carbonmonoxide; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.CarbonMonoxideSensor; -import io.github.hapjava.accessories.properties.CarbonMonoxideDetectedState; -import io.github.hapjava.characteristics.EnumCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class CarbonMonoxideDetectedCharacteristic extends EnumCharacteristic - implements EventableCharacteristic { - - private final CarbonMonoxideSensor carbonMonoxideSensor; - - public CarbonMonoxideDetectedCharacteristic(CarbonMonoxideSensor carbonMonoxideSensor) { - super("00000069-0000-1000-8000-0026BB765291", false, true, "Carbon Monoxide Detected", 1); - this.carbonMonoxideSensor = carbonMonoxideSensor; - } - - @Override - protected CompletableFuture getValue() { - return carbonMonoxideSensor - .getCarbonMonoxideDetectedState() - .thenApply(CarbonMonoxideDetectedState::getCode); - } - - @Override - protected void setValue(Integer value) throws Exception { - // Read Only - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - carbonMonoxideSensor.subscribeCarbonMonoxideDetectedState(callback); - } - - @Override - public void unsubscribe() { - carbonMonoxideSensor.unsubscribeCarbonMonoxideDetectedState(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/common/ActiveCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/common/ActiveCharacteristic.java deleted file mode 100644 index bdb0f9866..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/common/ActiveCharacteristic.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.github.hapjava.impl.characteristics.common; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.characteristics.BooleanCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.impl.ExceptionalConsumer; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class ActiveCharacteristic extends BooleanCharacteristic implements EventableCharacteristic { - - private final Supplier> getter; - private final ExceptionalConsumer setter; - private final Consumer subscriber; - private final Runnable unsubscriber; - - public ActiveCharacteristic( - Supplier> getter, - ExceptionalConsumer setter, - Consumer subscriber, - Runnable unsubscriber) { - super("000000B0-0000-1000-8000-0026BB765291", true, true, "Active"); - this.getter = getter; - this.setter = setter; - this.subscriber = subscriber; - this.unsubscriber = unsubscriber; - } - - @Override - protected CompletableFuture getValue() { - return getter.get(); - } - - @Override - protected void setValue(Boolean value) throws Exception { - setter.accept(value); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - subscriber.accept(callback); - } - - @Override - public void unsubscribe() { - unsubscriber.run(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/common/BatteryLevelCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/common/BatteryLevelCharacteristic.java deleted file mode 100644 index d3afac86a..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/common/BatteryLevelCharacteristic.java +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.hapjava.impl.characteristics.common; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.IntegerCharacteristic; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; - -/** - * This characteristic is used by a stand-alone BatteryService, which describes a stand-alone - * battery device, not the battery status of a battery operated device such as a motion sensor. - */ -public class BatteryLevelCharacteristic extends IntegerCharacteristic - implements EventableCharacteristic { - - private final Supplier> getter; - private final Consumer subscriber; - private final Runnable unsubscriber; - - public BatteryLevelCharacteristic( - Supplier> getter, - Consumer subscriber, - Runnable unsubscriber) { - super("00000068-0000-1000-8000-0026BB765291", false, true, "Battery Level", 0, 100, "%"); - this.getter = getter; - this.subscriber = subscriber; - this.unsubscriber = unsubscriber; - } - - @Override - protected CompletableFuture getValue() { - return getter.get(); - } - - @Override - protected void setValue(Integer value) throws Exception { - // Read Only - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - subscriber.accept(callback); - } - - @Override - public void unsubscribe() { - unsubscriber.run(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/common/InUseCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/common/InUseCharacteristic.java deleted file mode 100644 index b58b2eea3..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/common/InUseCharacteristic.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.hapjava.impl.characteristics.common; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.characteristics.BooleanCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class InUseCharacteristic extends BooleanCharacteristic implements EventableCharacteristic { - - private final Supplier> getter; - private final Consumer subscriber; - private final Runnable unsubscriber; - - public InUseCharacteristic( - Supplier> getter, - Consumer subscriber, - Runnable unsubscriber) { - super("000000D2-0000-1000-8000-0026BB765291", false, true, "InUse"); - this.getter = getter; - this.subscriber = subscriber; - this.unsubscriber = unsubscriber; - } - - @Override - protected CompletableFuture getValue() { - return getter.get(); - } - - @Override - protected void setValue(Boolean value) throws Exception { - // Read Only - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - subscriber.accept(callback); - } - - @Override - public void unsubscribe() { - unsubscriber.run(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/common/LowBatteryStatusCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/common/LowBatteryStatusCharacteristic.java deleted file mode 100644 index 28869e3cd..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/common/LowBatteryStatusCharacteristic.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.hapjava.impl.characteristics.common; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.characteristics.BooleanCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class LowBatteryStatusCharacteristic extends BooleanCharacteristic - implements EventableCharacteristic { - - private final Supplier> getter; - private final Consumer subscriber; - private final Runnable unsubscriber; - - public LowBatteryStatusCharacteristic( - Supplier> getter, - Consumer subscriber, - Runnable unsubscriber) { - super("00000079-0000-1000-8000-0026BB765291", false, true, "Status Low Battery"); - this.getter = getter; - this.subscriber = subscriber; - this.unsubscriber = unsubscriber; - } - - @Override - protected CompletableFuture getValue() { - return getter.get(); - } - - @Override - protected void setValue(Boolean value) throws Exception { - // Read Only - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - subscriber.accept(callback); - } - - @Override - public void unsubscribe() { - unsubscriber.run(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/common/Name.java b/src/main/java/io/github/hapjava/impl/characteristics/common/Name.java deleted file mode 100644 index 35bebcacc..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/common/Name.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.hapjava.impl.characteristics.common; - -import io.github.hapjava.characteristics.StaticStringCharacteristic; - -public class Name extends StaticStringCharacteristic { - - public Name(String label) { - super("00000023-0000-1000-8000-0026BB765291", "Name of the accessory", label); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/common/ObstructionDetectedCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/common/ObstructionDetectedCharacteristic.java deleted file mode 100644 index e4d242a8e..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/common/ObstructionDetectedCharacteristic.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.hapjava.impl.characteristics.common; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.characteristics.BooleanCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class ObstructionDetectedCharacteristic extends BooleanCharacteristic - implements EventableCharacteristic { - - private final Supplier> getter; - private final Consumer subscriber; - private final Runnable unsubscriber; - - public ObstructionDetectedCharacteristic( - Supplier> getter, - Consumer subscriber, - Runnable unsubscriber) { - super("00000024-0000-1000-8000-0026BB765291", false, true, "An obstruction has been detected"); - this.getter = getter; - this.subscriber = subscriber; - this.unsubscriber = unsubscriber; - } - - @Override - protected void setValue(Boolean value) throws Exception { - // Read Only - } - - @Override - protected CompletableFuture getValue() { - return getter.get(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - subscriber.accept(callback); - } - - @Override - public void unsubscribe() { - unsubscriber.run(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/common/PowerStateCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/common/PowerStateCharacteristic.java deleted file mode 100644 index 0b045247c..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/common/PowerStateCharacteristic.java +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.hapjava.impl.characteristics.common; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.characteristics.BooleanCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.impl.ExceptionalConsumer; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class PowerStateCharacteristic extends BooleanCharacteristic - implements EventableCharacteristic { - - private final Supplier> getter; - private final ExceptionalConsumer setter; - private final Consumer subscriber; - private final Runnable unsubscriber; - - public PowerStateCharacteristic( - Supplier> getter, - ExceptionalConsumer setter, - Consumer subscriber, - Runnable unsubscriber) { - super("00000025-0000-1000-8000-0026BB765291", true, true, "Turn on and off"); - this.getter = getter; - this.setter = setter; - this.subscriber = subscriber; - this.unsubscriber = unsubscriber; - } - - @Override - public void setValue(Boolean value) throws Exception { - setter.accept(value); - } - - @Override - protected CompletableFuture getValue() { - return getter.get(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - subscriber.accept(callback); - } - - @Override - public void unsubscribe() { - unsubscriber.run(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/common/RemainingDurationCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/common/RemainingDurationCharacteristic.java deleted file mode 100644 index b5d9f3e4f..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/common/RemainingDurationCharacteristic.java +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.hapjava.impl.characteristics.common; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.IntegerCharacteristic; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class RemainingDurationCharacteristic extends IntegerCharacteristic - implements EventableCharacteristic { - - private final Supplier> getter; - private final Consumer subscriber; - private final Runnable unsubscriber; - - public RemainingDurationCharacteristic( - Supplier> getter, - Consumer subscriber, - Runnable unsubscriber) { - super( - "000000D4-0000-1000-8000-0026BB765291", - false, - true, - "Remaining Duration", - 0, - 3600, - "seconds"); - this.getter = getter; - this.subscriber = subscriber; - this.unsubscriber = unsubscriber; - } - - @Override - protected CompletableFuture getValue() { - return getter.get(); - } - - @Override - protected void setValue(Integer value) throws Exception { - // Read Only - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - subscriber.accept(callback); - } - - @Override - public void unsubscribe() { - unsubscriber.run(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/contactsensor/ContactSensorStateCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/contactsensor/ContactSensorStateCharacteristic.java deleted file mode 100644 index e246fa5e7..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/contactsensor/ContactSensorStateCharacteristic.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.hapjava.impl.characteristics.contactsensor; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.ContactSensor; -import io.github.hapjava.accessories.properties.ContactState; -import io.github.hapjava.characteristics.EnumCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class ContactSensorStateCharacteristic extends EnumCharacteristic - implements EventableCharacteristic { - - private final ContactSensor contactSensor; - - public ContactSensorStateCharacteristic(ContactSensor contactSensor) { - super("0000006A-0000-1000-8000-0026BB765291", false, true, "Contact State", 1); - this.contactSensor = contactSensor; - } - - @Override - protected CompletableFuture getValue() { - return contactSensor.getCurrentState().thenApply(ContactState::getCode); - } - - @Override - protected void setValue(Integer value) throws Exception { - // Read Only - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - contactSensor.subscribeContactState(callback); - } - - @Override - public void unsubscribe() { - contactSensor.unsubscribeContactState(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/fan/RotationDirectionCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/fan/RotationDirectionCharacteristic.java deleted file mode 100644 index b545f8210..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/fan/RotationDirectionCharacteristic.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.hapjava.impl.characteristics.fan; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.Fan; -import io.github.hapjava.accessories.properties.RotationDirection; -import io.github.hapjava.characteristics.EnumCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class RotationDirectionCharacteristic extends EnumCharacteristic - implements EventableCharacteristic { - - private final Fan fan; - - public RotationDirectionCharacteristic(Fan fan) { - super("00000028-0000-1000-8000-0026BB765291", true, true, "Rotation Direction", 1); - this.fan = fan; - } - - @Override - protected void setValue(Integer value) throws Exception { - fan.setRotationDirection(RotationDirection.fromCode(value)); - } - - @Override - protected CompletableFuture getValue() { - return fan.getRotationDirection().thenApply(s -> s.getCode()); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - fan.subscribeRotationDirection(callback); - } - - @Override - public void unsubscribe() { - fan.unsubscribeRotationDirection(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/fan/RotationSpeedCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/fan/RotationSpeedCharacteristic.java deleted file mode 100644 index 43c905802..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/fan/RotationSpeedCharacteristic.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.hapjava.impl.characteristics.fan; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.Fan; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.IntegerCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class RotationSpeedCharacteristic extends IntegerCharacteristic - implements EventableCharacteristic { - - private final Fan fan; - - public RotationSpeedCharacteristic(Fan fan) { - super("00000029-0000-1000-8000-0026BB765291", true, true, "Rotation speed", 0, 100, "%"); - this.fan = fan; - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - fan.subscribeRotationSpeed(callback); - } - - @Override - public void unsubscribe() { - fan.unsubscribeRotationSpeed(); - } - - @Override - protected void setValue(Integer value) throws Exception { - fan.setRotationSpeed(value); - } - - @Override - protected CompletableFuture getValue() { - return fan.getRotationSpeed(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/garage/CurrentDoorStateCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/garage/CurrentDoorStateCharacteristic.java deleted file mode 100644 index 7d6944b96..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/garage/CurrentDoorStateCharacteristic.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.hapjava.impl.characteristics.garage; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.GarageDoor; -import io.github.hapjava.characteristics.EnumCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class CurrentDoorStateCharacteristic extends EnumCharacteristic - implements EventableCharacteristic { - - private final GarageDoor door; - - public CurrentDoorStateCharacteristic(GarageDoor door) { - super("0000000E-0000-1000-8000-0026BB765291", false, true, "Current Door State", 4); - this.door = door; - } - - @Override - protected void setValue(Integer value) throws Exception { - // Read Only - } - - @Override - protected CompletableFuture getValue() { - return door.getCurrentDoorState().thenApply(s -> s.getCode()); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - door.subscribeCurrentDoorState(callback); - } - - @Override - public void unsubscribe() { - door.unsubscribeCurrentDoorState(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/garage/TargetDoorStateCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/garage/TargetDoorStateCharacteristic.java deleted file mode 100644 index 20e8c30a5..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/garage/TargetDoorStateCharacteristic.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.hapjava.impl.characteristics.garage; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.GarageDoor; -import io.github.hapjava.accessories.properties.DoorState; -import io.github.hapjava.characteristics.EnumCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class TargetDoorStateCharacteristic extends EnumCharacteristic - implements EventableCharacteristic { - - private final GarageDoor door; - - public TargetDoorStateCharacteristic(GarageDoor door) { - super("00000032-0000-1000-8000-0026BB765291", true, true, "Target Door State", 1); - this.door = door; - } - - @Override - protected void setValue(Integer value) throws Exception { - door.setTargetDoorState(DoorState.fromCode(value)); - } - - @Override - protected CompletableFuture getValue() { - return door.getTargetDoorState().thenApply(s -> s.getCode()); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - door.subscribeTargetDoorState(callback); - } - - @Override - public void unsubscribe() { - door.unsubscribeTargetDoorState(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/humiditysensor/CurrentRelativeHumidityCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/humiditysensor/CurrentRelativeHumidityCharacteristic.java deleted file mode 100644 index 5dda83acf..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/humiditysensor/CurrentRelativeHumidityCharacteristic.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.hapjava.impl.characteristics.humiditysensor; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.HumiditySensor; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.FloatCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class CurrentRelativeHumidityCharacteristic extends FloatCharacteristic - implements EventableCharacteristic { - - private final HumiditySensor sensor; - - public CurrentRelativeHumidityCharacteristic(HumiditySensor sensor) { - super( - "00000010-0000-1000-8000-0026BB765291", - false, - true, - "Current relative humidity", - 0, - 100, - 0.1, - "%"); - this.sensor = sensor; - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - sensor.subscribeCurrentRelativeHumidity(callback); - } - - @Override - public void unsubscribe() { - sensor.unsubscribeCurrentRelativeHumidity(); - } - - @Override - protected void setValue(Double value) throws Exception { - // Read Only - } - - @Override - protected CompletableFuture getDoubleValue() { - return sensor.getCurrentRelativeHumidity(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/information/Identify.java b/src/main/java/io/github/hapjava/impl/characteristics/information/Identify.java deleted file mode 100644 index 097cda764..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/information/Identify.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.hapjava.impl.characteristics.information; - -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.characteristics.WriteOnlyBooleanCharacteristic; - -public class Identify extends WriteOnlyBooleanCharacteristic { - - private HomekitAccessory accessory; - - public Identify(HomekitAccessory accessory) throws Exception { - super( - "00000014-0000-1000-8000-0026BB765291", - "Identifies the accessory via a physical action on the accessory"); - this.accessory = accessory; - } - - @Override - public void setValue(Boolean value) throws Exception { - if (value) { - accessory.identify(); - } - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/information/Manufacturer.java b/src/main/java/io/github/hapjava/impl/characteristics/information/Manufacturer.java deleted file mode 100644 index 8a43fce08..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/information/Manufacturer.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.hapjava.impl.characteristics.information; - -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.characteristics.StaticStringCharacteristic; - -public class Manufacturer extends StaticStringCharacteristic { - - public Manufacturer(HomekitAccessory accessory) throws Exception { - super( - "00000020-0000-1000-8000-0026BB765291", - "The name of the manufacturer", - accessory.getManufacturer()); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/information/Model.java b/src/main/java/io/github/hapjava/impl/characteristics/information/Model.java deleted file mode 100644 index aa4235900..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/information/Model.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.hapjava.impl.characteristics.information; - -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.characteristics.StaticStringCharacteristic; - -public class Model extends StaticStringCharacteristic { - - public Model(HomekitAccessory accessory) throws Exception { - super("00000021-0000-1000-8000-0026BB765291", "The name of the model", accessory.getModel()); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/information/SerialNumber.java b/src/main/java/io/github/hapjava/impl/characteristics/information/SerialNumber.java deleted file mode 100644 index a4f90564b..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/information/SerialNumber.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.hapjava.impl.characteristics.information; - -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.characteristics.StaticStringCharacteristic; - -public class SerialNumber extends StaticStringCharacteristic { - - public SerialNumber(HomekitAccessory accessory) throws Exception { - super( - "00000030-0000-1000-8000-0026BB765291", - "The serial number of the accessory", - accessory.getSerialNumber()); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/leaksensor/LeakDetectedStateCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/leaksensor/LeakDetectedStateCharacteristic.java deleted file mode 100644 index ba29c1dbc..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/leaksensor/LeakDetectedStateCharacteristic.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.hapjava.impl.characteristics.leaksensor; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.LeakSensor; -import io.github.hapjava.characteristics.BooleanCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class LeakDetectedStateCharacteristic extends BooleanCharacteristic - implements EventableCharacteristic { - - private final LeakSensor leakSensor; - - public LeakDetectedStateCharacteristic(LeakSensor leakSensor) { - super("00000070-0000-1000-8000-0026BB765291", false, true, "Leak Detected"); - this.leakSensor = leakSensor; - } - - @Override - protected CompletableFuture getValue() { - return leakSensor.getLeakDetected(); - } - - @Override - protected void setValue(Boolean value) throws Exception { - // Read Only - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - leakSensor.subscribeLeakDetected(callback); - } - - @Override - public void unsubscribe() { - leakSensor.unsubscribeLeakDetected(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/light/AmbientLightLevelCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/light/AmbientLightLevelCharacteristic.java deleted file mode 100644 index b280ceb05..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/light/AmbientLightLevelCharacteristic.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.hapjava.impl.characteristics.light; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.LightSensor; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.FloatCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class AmbientLightLevelCharacteristic extends FloatCharacteristic - implements EventableCharacteristic { - - private final LightSensor lightSensor; - - public AmbientLightLevelCharacteristic(LightSensor lightSensor) { - super( - "0000006B-0000-1000-8000-0026BB765291", - false, - true, - "Current ambient light level", - 0.0001, - 100000, - 0.0001, - "lux"); - this.lightSensor = lightSensor; - } - - @Override - protected void setValue(Double value) throws Exception { - // Read Only - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - lightSensor.subscribeCurrentAmbientLightLevel(callback); - } - - @Override - public void unsubscribe() { - lightSensor.unsubscribeCurrentAmbientLightLevel(); - } - - @Override - protected CompletableFuture getDoubleValue() { - return lightSensor.getCurrentAmbientLightLevel(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/lightbulb/BrightnessCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/lightbulb/BrightnessCharacteristic.java deleted file mode 100644 index 7dc85c619..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/lightbulb/BrightnessCharacteristic.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.hapjava.impl.characteristics.lightbulb; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.DimmableLightbulb; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.IntegerCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class BrightnessCharacteristic extends IntegerCharacteristic - implements EventableCharacteristic { - - private final DimmableLightbulb lightbulb; - - public BrightnessCharacteristic(DimmableLightbulb lightbulb) { - super( - "00000008-0000-1000-8000-0026BB765291", - true, - true, - "Adjust brightness of the light", - 0, - 100, - "%"); - this.lightbulb = lightbulb; - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - lightbulb.subscribeBrightness(callback); - } - - @Override - public void unsubscribe() { - lightbulb.unsubscribeBrightness(); - } - - @Override - protected void setValue(Integer value) throws Exception { - lightbulb.setBrightness(value); - } - - @Override - protected CompletableFuture getValue() { - return lightbulb.getBrightness(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/lightbulb/HueCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/lightbulb/HueCharacteristic.java deleted file mode 100644 index ea9df9457..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/lightbulb/HueCharacteristic.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.hapjava.impl.characteristics.lightbulb; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.ColorfulLightbulb; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.FloatCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class HueCharacteristic extends FloatCharacteristic implements EventableCharacteristic { - - private final ColorfulLightbulb lightbulb; - - public HueCharacteristic(ColorfulLightbulb lightbulb) { - super( - "00000013-0000-1000-8000-0026BB765291", - true, - true, - "Adjust hue of the light", - 0, - 360, - 1, - "arcdegrees"); - this.lightbulb = lightbulb; - } - - @Override - protected void setValue(Double value) throws Exception { - lightbulb.setHue(value); - } - - @Override - protected CompletableFuture getDoubleValue() { - return lightbulb.getHue(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - lightbulb.subscribeHue(callback); - } - - @Override - public void unsubscribe() { - lightbulb.unsubscribeHue(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/lightbulb/SaturationCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/lightbulb/SaturationCharacteristic.java deleted file mode 100644 index ca48a31f0..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/lightbulb/SaturationCharacteristic.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.hapjava.impl.characteristics.lightbulb; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.ColorfulLightbulb; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.FloatCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class SaturationCharacteristic extends FloatCharacteristic - implements EventableCharacteristic { - - private final ColorfulLightbulb lightbulb; - - public SaturationCharacteristic(ColorfulLightbulb lightbulb) { - super( - "0000002F-0000-1000-8000-0026BB765291", - true, - true, - "Adjust saturation of the light", - 0, - 100, - 1, - "%"); - this.lightbulb = lightbulb; - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - lightbulb.subscribeSaturation(callback); - } - - @Override - public void unsubscribe() { - lightbulb.unsubscribeSaturation(); - } - - @Override - protected void setValue(Double value) throws Exception { - lightbulb.setSaturation(value); - } - - @Override - protected CompletableFuture getDoubleValue() { - return lightbulb.getSaturation(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/lock/mechanism/CurrentLockMechanismStateCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/lock/mechanism/CurrentLockMechanismStateCharacteristic.java deleted file mode 100644 index 1182c43e9..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/lock/mechanism/CurrentLockMechanismStateCharacteristic.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.hapjava.impl.characteristics.lock.mechanism; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.LockMechanism; -import io.github.hapjava.characteristics.EnumCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class CurrentLockMechanismStateCharacteristic extends EnumCharacteristic - implements EventableCharacteristic { - - private final LockMechanism lock; - - public CurrentLockMechanismStateCharacteristic(LockMechanism lock) { - super("0000001D-0000-1000-8000-0026BB765291", false, true, "Current lock state", 3); - this.lock = lock; - } - - @Override - protected void setValue(Integer value) throws Exception { - // Not writable - } - - @Override - protected CompletableFuture getValue() { - return lock.getCurrentMechanismState().thenApply(s -> s.getCode()); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - lock.subscribeCurrentMechanismState(callback); - } - - @Override - public void unsubscribe() { - lock.unsubscribeCurrentMechanismState(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/lock/mechanism/TargetLockMechanismStateCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/lock/mechanism/TargetLockMechanismStateCharacteristic.java deleted file mode 100644 index e351c4c0b..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/lock/mechanism/TargetLockMechanismStateCharacteristic.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.hapjava.impl.characteristics.lock.mechanism; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.LockableLockMechanism; -import io.github.hapjava.accessories.properties.LockMechanismState; -import io.github.hapjava.characteristics.EnumCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class TargetLockMechanismStateCharacteristic extends EnumCharacteristic - implements EventableCharacteristic { - - private final LockableLockMechanism lock; - - public TargetLockMechanismStateCharacteristic(LockableLockMechanism lock) { - super("0000001E-0000-1000-8000-0026BB765291", true, true, "Current lock state", 3); - this.lock = lock; - } - - @Override - protected void setValue(Integer value) throws Exception { - lock.setTargetMechanismState(LockMechanismState.fromCode(value)); - } - - @Override - protected CompletableFuture getValue() { - return lock.getTargetMechanismState().thenApply(s -> s.getCode()); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - lock.subscribeTargetMechanismState(callback); - } - - @Override - public void unsubscribe() { - lock.unsubscribeTargetMechanismState(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/motionsensor/MotionDetectedStateCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/motionsensor/MotionDetectedStateCharacteristic.java deleted file mode 100644 index 1c451fe32..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/motionsensor/MotionDetectedStateCharacteristic.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.hapjava.impl.characteristics.motionsensor; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.MotionSensor; -import io.github.hapjava.characteristics.BooleanCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class MotionDetectedStateCharacteristic extends BooleanCharacteristic - implements EventableCharacteristic { - - private final MotionSensor motionSensor; - - public MotionDetectedStateCharacteristic(MotionSensor motionSensor) { - super("00000022-0000-1000-8000-0026BB765291", false, true, "Motion Detected"); - this.motionSensor = motionSensor; - } - - @Override - protected CompletableFuture getValue() { - return motionSensor.getMotionDetected(); - } - - @Override - protected void setValue(Boolean value) throws Exception { - // Read Only - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - motionSensor.subscribeMotionDetected(callback); - } - - @Override - public void unsubscribe() { - motionSensor.unsubscribeMotionDetected(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/occupancysensor/OccupancyDetectedStateCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/occupancysensor/OccupancyDetectedStateCharacteristic.java deleted file mode 100644 index 813101039..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/occupancysensor/OccupancyDetectedStateCharacteristic.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.hapjava.impl.characteristics.occupancysensor; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.OccupancySensor; -import io.github.hapjava.characteristics.BooleanCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class OccupancyDetectedStateCharacteristic extends BooleanCharacteristic - implements EventableCharacteristic { - - private final OccupancySensor occupancySensor; - - public OccupancyDetectedStateCharacteristic(OccupancySensor occupancySensor) { - super("00000071-0000-1000-8000-0026BB765291", false, true, "Occupancy Detected"); - this.occupancySensor = occupancySensor; - } - - @Override - protected CompletableFuture getValue() { - return occupancySensor.getOccupancyDetected(); - } - - @Override - protected void setValue(Boolean value) throws Exception { - // Read Only - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - occupancySensor.subscribeOccupancyDetected(callback); - } - - @Override - public void unsubscribe() { - occupancySensor.unsubscribeOccupancyDetected(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/outlet/OutletInUseCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/outlet/OutletInUseCharacteristic.java deleted file mode 100644 index 98bd2108b..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/outlet/OutletInUseCharacteristic.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.hapjava.impl.characteristics.outlet; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.Outlet; -import io.github.hapjava.characteristics.BooleanCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class OutletInUseCharacteristic extends BooleanCharacteristic - implements EventableCharacteristic { - - private final Outlet outlet; - - public OutletInUseCharacteristic(Outlet outlet) { - super("00000026-0000-1000-8000-0026BB765291", false, true, "The outlet is in use"); - this.outlet = outlet; - } - - @Override - protected void setValue(Boolean value) throws Exception { - // Read Only - } - - @Override - protected CompletableFuture getValue() { - return outlet.getOutletInUse(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - outlet.subscribeOutletInUse(callback); - } - - @Override - public void unsubscribe() { - outlet.unsubscribeOutletInUse(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/securitysystem/CurrentSecuritySystemStateCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/securitysystem/CurrentSecuritySystemStateCharacteristic.java deleted file mode 100644 index 0d0b1b23e..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/securitysystem/CurrentSecuritySystemStateCharacteristic.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.hapjava.impl.characteristics.securitysystem; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.SecuritySystem; -import io.github.hapjava.accessories.properties.CurrentSecuritySystemState; -import io.github.hapjava.characteristics.EnumCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class CurrentSecuritySystemStateCharacteristic extends EnumCharacteristic - implements EventableCharacteristic { - - private final SecuritySystem securitySystem; - - public CurrentSecuritySystemStateCharacteristic(SecuritySystem securitySystem) { - super("00000066-0000-1000-8000-0026BB765291", false, true, "Current security system state", 4); - this.securitySystem = securitySystem; - } - - @Override - protected CompletableFuture getValue() { - return securitySystem - .getCurrentSecuritySystemState() - .thenApply(CurrentSecuritySystemState::getCode); - } - - @Override - protected void setValue(Integer value) throws Exception { - // Not writable - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - securitySystem.subscribeCurrentSecuritySystemState(callback); - } - - @Override - public void unsubscribe() { - securitySystem.unsubscribeCurrentSecuritySystemState(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/securitysystem/SecuritySystemAlarmTypeCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/securitysystem/SecuritySystemAlarmTypeCharacteristic.java deleted file mode 100644 index 772943ba9..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/securitysystem/SecuritySystemAlarmTypeCharacteristic.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.hapjava.impl.characteristics.securitysystem; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.SecuritySystem; -import io.github.hapjava.accessories.properties.SecuritySystemAlarmType; -import io.github.hapjava.characteristics.EnumCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class SecuritySystemAlarmTypeCharacteristic extends EnumCharacteristic - implements EventableCharacteristic { - - private final SecuritySystem securitySystem; - - public SecuritySystemAlarmTypeCharacteristic(SecuritySystem securitySystem) { - super("0000008E-0000-1000-8000-0026BB765291", false, true, "Security system alarm type", 1); - this.securitySystem = securitySystem; - } - - @Override - protected CompletableFuture getValue() { - return securitySystem.getAlarmTypeState().thenApply(SecuritySystemAlarmType::getCode); - } - - @Override - protected void setValue(Integer value) throws Exception { - // Not writable - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - securitySystem.subscribeAlarmTypeState(callback); - } - - @Override - public void unsubscribe() { - securitySystem.unsubscribeAlarmTypeState(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/securitysystem/TargetSecuritySystemStateCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/securitysystem/TargetSecuritySystemStateCharacteristic.java deleted file mode 100644 index 7357b43cd..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/securitysystem/TargetSecuritySystemStateCharacteristic.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.hapjava.impl.characteristics.securitysystem; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.SecuritySystem; -import io.github.hapjava.accessories.properties.TargetSecuritySystemState; -import io.github.hapjava.characteristics.EnumCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class TargetSecuritySystemStateCharacteristic extends EnumCharacteristic - implements EventableCharacteristic { - - private final SecuritySystem securitySystem; - - public TargetSecuritySystemStateCharacteristic(SecuritySystem securitySystem) { - super("00000067-0000-1000-8000-0026BB765291", true, true, "Target security system state", 3); - this.securitySystem = securitySystem; - } - - @Override - protected CompletableFuture getValue() { - return securitySystem - .getTargetSecuritySystemState() - .thenApply(TargetSecuritySystemState::getCode); - } - - @Override - protected void setValue(Integer value) throws Exception { - securitySystem.setTargetSecuritySystemState(TargetSecuritySystemState.fromCode(value)); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - securitySystem.subscribeTargetSecuritySystemState(callback); - } - - @Override - public void unsubscribe() { - securitySystem.unsubscribeTargetSecuritySystemState(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/smokesensor/SmokeDetectedCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/smokesensor/SmokeDetectedCharacteristic.java deleted file mode 100644 index e9823292d..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/smokesensor/SmokeDetectedCharacteristic.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.hapjava.impl.characteristics.smokesensor; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.SmokeSensor; -import io.github.hapjava.accessories.properties.SmokeDetectedState; -import io.github.hapjava.characteristics.EnumCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class SmokeDetectedCharacteristic extends EnumCharacteristic - implements EventableCharacteristic { - - private final SmokeSensor smokeSensor; - - public SmokeDetectedCharacteristic(SmokeSensor smokeSensor) { - super("00000076-0000-1000-8000-0026BB765291", false, true, "Smoke Detected", 1); - this.smokeSensor = smokeSensor; - } - - @Override - protected CompletableFuture getValue() { - return smokeSensor.getSmokeDetectedState().thenApply(SmokeDetectedState::getCode); - } - - @Override - protected void setValue(Integer value) throws Exception { - // Read Only - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - smokeSensor.subscribeSmokeDetectedState(callback); - } - - @Override - public void unsubscribe() { - smokeSensor.unsubscribeSmokeDetectedState(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/thermostat/AbstractHeatingCoolingModeCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/thermostat/AbstractHeatingCoolingModeCharacteristic.java deleted file mode 100644 index e28ffddbe..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/thermostat/AbstractHeatingCoolingModeCharacteristic.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.hapjava.impl.characteristics.thermostat; - -import io.github.hapjava.accessories.properties.ThermostatMode; -import io.github.hapjava.characteristics.EnumCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -abstract class AbstractHeatingCoolingModeCharacteristic extends EnumCharacteristic - implements EventableCharacteristic { - - public AbstractHeatingCoolingModeCharacteristic( - String type, boolean isWritable, String description) { - super(type, isWritable, true, description, 3); - } - - @Override - protected final void setValue(Integer value) throws Exception { - setModeValue(ThermostatMode.fromCode(value)); - } - - @Override - protected final CompletableFuture getValue() { - return getModeValue().thenApply(t -> t.getCode()); - } - - protected abstract void setModeValue(ThermostatMode mode) throws Exception; - - protected abstract CompletableFuture getModeValue(); -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/thermostat/AbstractTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/thermostat/AbstractTemperatureCharacteristic.java deleted file mode 100644 index 5f1dc7f1c..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/thermostat/AbstractTemperatureCharacteristic.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.hapjava.impl.characteristics.thermostat; - -import io.github.hapjava.accessories.TemperatureSensor; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.FloatCharacteristic; - -public abstract class AbstractTemperatureCharacteristic extends FloatCharacteristic - implements EventableCharacteristic { - - public AbstractTemperatureCharacteristic( - String type, boolean isWritable, String description, TemperatureSensor sensor) { - super( - type, - isWritable, - true, - description, - sensor.getMinimumTemperature(), - sensor.getMaximumTemperature(), - 0.1, - "celsius"); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/thermostat/CoolingThresholdTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/thermostat/CoolingThresholdTemperatureCharacteristic.java deleted file mode 100644 index 0b2d1b4f8..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/thermostat/CoolingThresholdTemperatureCharacteristic.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.hapjava.impl.characteristics.thermostat; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.thermostat.CoolingThermostat; -import java.util.concurrent.CompletableFuture; - -public class CoolingThresholdTemperatureCharacteristic extends AbstractTemperatureCharacteristic { - - private final CoolingThermostat thermostat; - - public CoolingThresholdTemperatureCharacteristic(CoolingThermostat thermostat) { - super( - "0000000D-0000-1000-8000-0026BB765291", - true, - "Temperature above which cooling will be active", - thermostat); - this.thermostat = thermostat; - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - thermostat.subscribeCoolingThresholdTemperature(callback); - } - - @Override - public void unsubscribe() { - thermostat.unsubscribeCoolingThresholdTemperature(); - } - - @Override - protected CompletableFuture getDoubleValue() { - return thermostat.getCoolingThresholdTemperature(); - } - - @Override - protected void setValue(Double value) throws Exception { - thermostat.setCoolingThresholdTemperature(value); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/thermostat/CurrentHeatingCoolingModeCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/thermostat/CurrentHeatingCoolingModeCharacteristic.java deleted file mode 100644 index 26c4ac987..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/thermostat/CurrentHeatingCoolingModeCharacteristic.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.hapjava.impl.characteristics.thermostat; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.properties.ThermostatMode; -import io.github.hapjava.accessories.thermostat.BasicThermostat; -import java.util.concurrent.CompletableFuture; - -public class CurrentHeatingCoolingModeCharacteristic - extends AbstractHeatingCoolingModeCharacteristic { - - private final BasicThermostat thermostat; - - public CurrentHeatingCoolingModeCharacteristic(BasicThermostat thermostat) { - super("0000000F-0000-1000-8000-0026BB765291", false, "Current Mode"); - this.thermostat = thermostat; - } - - @Override - protected void setModeValue(ThermostatMode mode) throws Exception { - // Not writable - } - - @Override - protected CompletableFuture getModeValue() { - return thermostat.getCurrentMode(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - thermostat.subscribeCurrentMode(callback); - } - - @Override - public void unsubscribe() { - thermostat.unsubscribeCurrentMode(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/thermostat/CurrentTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/thermostat/CurrentTemperatureCharacteristic.java deleted file mode 100644 index 7745c56cc..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/thermostat/CurrentTemperatureCharacteristic.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.hapjava.impl.characteristics.thermostat; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.TemperatureSensor; -import java.util.concurrent.CompletableFuture; - -public class CurrentTemperatureCharacteristic extends AbstractTemperatureCharacteristic { - - private final TemperatureSensor sensor; - - public CurrentTemperatureCharacteristic(TemperatureSensor thermostat) { - super("00000011-0000-1000-8000-0026BB765291", false, "Current Temperature", thermostat); - this.sensor = thermostat; - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - sensor.subscribeCurrentTemperature(callback); - } - - @Override - public void unsubscribe() { - sensor.unsubscribeCurrentTemperature(); - } - - @Override - protected CompletableFuture getDoubleValue() { - return sensor.getCurrentTemperature(); - } - - @Override - protected void setValue(Double value) throws Exception { - // Not writable - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/thermostat/HeatingThresholdTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/thermostat/HeatingThresholdTemperatureCharacteristic.java deleted file mode 100644 index dba7af9a2..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/thermostat/HeatingThresholdTemperatureCharacteristic.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.hapjava.impl.characteristics.thermostat; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.thermostat.HeatingThermostat; -import java.util.concurrent.CompletableFuture; - -public class HeatingThresholdTemperatureCharacteristic extends AbstractTemperatureCharacteristic { - - private final HeatingThermostat thermostat; - - public HeatingThresholdTemperatureCharacteristic(HeatingThermostat thermostat) { - super( - "00000012-0000-1000-8000-0026BB765291", - true, - "Temperature below which heating will be active", - thermostat); - this.thermostat = thermostat; - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - thermostat.subscribeHeatingThresholdTemperature(callback); - } - - @Override - public void unsubscribe() { - thermostat.unsubscribeHeatingThresholdTemperature(); - } - - @Override - protected CompletableFuture getDoubleValue() { - return thermostat.getHeatingThresholdTemperature(); - } - - @Override - protected void setValue(Double value) throws Exception { - thermostat.setHeatingThresholdTemperature(value); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/thermostat/TargetHeatingCoolingModeCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/thermostat/TargetHeatingCoolingModeCharacteristic.java deleted file mode 100644 index 238af49ab..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/thermostat/TargetHeatingCoolingModeCharacteristic.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.hapjava.impl.characteristics.thermostat; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.properties.ThermostatMode; -import io.github.hapjava.accessories.thermostat.BasicThermostat; -import java.util.concurrent.CompletableFuture; - -public class TargetHeatingCoolingModeCharacteristic - extends AbstractHeatingCoolingModeCharacteristic { - - private final BasicThermostat thermostat; - - public TargetHeatingCoolingModeCharacteristic(BasicThermostat thermostat) { - super("00000033-0000-1000-8000-0026BB765291", true, "Target Mode"); - this.thermostat = thermostat; - } - - @Override - protected void setModeValue(ThermostatMode mode) throws Exception { - thermostat.setTargetMode(mode); - } - - @Override - protected CompletableFuture getModeValue() { - return thermostat.getTargetMode(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - thermostat.subscribeTargetMode(callback); - } - - @Override - public void unsubscribe() { - thermostat.unsubscribeTargetMode(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/thermostat/TargetTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/thermostat/TargetTemperatureCharacteristic.java deleted file mode 100644 index 1c7f9274e..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/thermostat/TargetTemperatureCharacteristic.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.hapjava.impl.characteristics.thermostat; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.thermostat.BasicThermostat; -import java.util.concurrent.CompletableFuture; - -public class TargetTemperatureCharacteristic extends AbstractTemperatureCharacteristic { - - private final BasicThermostat thermostat; - - public TargetTemperatureCharacteristic(BasicThermostat thermostat) { - super("00000035-0000-1000-8000-0026BB765291", true, "Target Temperature", thermostat); - this.thermostat = thermostat; - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - thermostat.subscribeTargetTemperature(callback); - } - - @Override - public void unsubscribe() { - thermostat.unsubscribeTargetTemperature(); - } - - @Override - protected CompletableFuture getDoubleValue() { - return thermostat.getTargetTemperature(); - } - - @Override - protected void setValue(Double value) throws Exception { - thermostat.setTargetTemperature(value); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/thermostat/TemperatureUnitsCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/thermostat/TemperatureUnitsCharacteristic.java deleted file mode 100644 index a24a5fc74..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/thermostat/TemperatureUnitsCharacteristic.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.hapjava.impl.characteristics.thermostat; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.properties.TemperatureUnit; -import io.github.hapjava.accessories.thermostat.BasicThermostat; -import io.github.hapjava.characteristics.EnumCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class TemperatureUnitsCharacteristic extends EnumCharacteristic - implements EventableCharacteristic { - - private final BasicThermostat thermostat; - - public TemperatureUnitsCharacteristic(BasicThermostat thermostat) { - super("00000036-0000-1000-8000-0026BB765291", true, true, "The temperature unit", 1); - this.thermostat = thermostat; - } - - @Override - protected void setValue(Integer value) throws Exception { - thermostat.setTemperatureUnit( - value == 1 ? TemperatureUnit.FAHRENHEIT : TemperatureUnit.CELSIUS); - } - - @Override - protected CompletableFuture getValue() { - return CompletableFuture.completedFuture(thermostat.getTemperatureUnit().getCode()); - } - - @Override - public void subscribe(final HomekitCharacteristicChangeCallback callback) { - thermostat.subscribeTemperatureUnit(callback); - } - - @Override - public void unsubscribe() { - thermostat.unsubscribeTemperatureUnit(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/valve/SetDurationCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/valve/SetDurationCharacteristic.java deleted file mode 100644 index 75feae8c2..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/valve/SetDurationCharacteristic.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.hapjava.impl.characteristics.valve; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.ValveWithTimer; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.IntegerCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class SetDurationCharacteristic extends IntegerCharacteristic - implements EventableCharacteristic { - private final ValveWithTimer valve; - - public SetDurationCharacteristic(ValveWithTimer valve) { - super("000000D3-0000-1000-8000-0026BB765291", true, true, "Set Duration", 0, 3600, "seconds"); - this.valve = valve; - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - valve.subscribeSetDuration(callback); - } - - @Override - public void unsubscribe() { - valve.unsubscribeSetDuration(); - } - - @Override - protected void setValue(Integer value) throws Exception { - valve.setSetDuration(value); - } - - @Override - protected CompletableFuture getValue() { - return valve.getSetDuration(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/valve/ValveTypeCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/valve/ValveTypeCharacteristic.java deleted file mode 100644 index 3fe6b0aeb..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/valve/ValveTypeCharacteristic.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.hapjava.impl.characteristics.valve; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.Valve; -import io.github.hapjava.characteristics.EnumCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class ValveTypeCharacteristic extends EnumCharacteristic implements EventableCharacteristic { - - private final Valve valve; - - public ValveTypeCharacteristic(Valve valve) { - super("000000D5-0000-1000-8000-0026BB765291", false, true, "Valve Type", 3); - this.valve = valve; - } - - @Override - protected void setValue(Integer value) throws Exception { - // Read only - } - - @Override - protected CompletableFuture getValue() { - return valve.getValveType().thenApply(v -> v.getCode()); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - valve.subscribeValveType(callback); - } - - @Override - public void unsubscribe() { - valve.unsubscribeValveType(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java deleted file mode 100644 index d4b57acb0..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.hapjava.impl.characteristics.windowcovering; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.HorizontalTiltingWindowCovering; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.IntegerCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class CurrentHorizontalTiltAngleCharacteristic extends IntegerCharacteristic - implements EventableCharacteristic { - - private final HorizontalTiltingWindowCovering windowCovering; - - public CurrentHorizontalTiltAngleCharacteristic(HorizontalTiltingWindowCovering windowCovering) { - super( - "0000006C-0000-1000-8000-0026BB765291", - false, - true, - "The current horizontal tilt angle", - -90, - 90, - "Arc Degree"); - this.windowCovering = windowCovering; - } - - @Override - protected void setValue(Integer value) throws Exception { - // Read Only - } - - @Override - protected CompletableFuture getValue() { - return windowCovering.getCurrentHorizontalTiltAngle(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - windowCovering.subscribeCurrentHorizontalTiltAngle(callback); - } - - @Override - public void unsubscribe() { - windowCovering.unsubscribeCurrentHorizontalTiltAngle(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/CurrentPositionCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/CurrentPositionCharacteristic.java deleted file mode 100644 index 22fbfd171..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/CurrentPositionCharacteristic.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.hapjava.impl.characteristics.windowcovering; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.BasicWindowCovering; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.IntegerCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class CurrentPositionCharacteristic extends IntegerCharacteristic - implements EventableCharacteristic { - - private final BasicWindowCovering windowCovering; - - public CurrentPositionCharacteristic(BasicWindowCovering windowCovering) { - super("0000006D-0000-1000-8000-0026BB765291", false, true, "The current position", 0, 100, "%"); - this.windowCovering = windowCovering; - } - - @Override - protected void setValue(Integer value) throws Exception { - // Read Only - } - - @Override - protected CompletableFuture getValue() { - return windowCovering.getCurrentPosition(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - windowCovering.subscribeCurrentPosition(callback); - } - - @Override - public void unsubscribe() { - windowCovering.unsubscribeCurrentPosition(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/CurrentVerticalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/CurrentVerticalTiltAngleCharacteristic.java deleted file mode 100644 index 371e5939b..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/CurrentVerticalTiltAngleCharacteristic.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.hapjava.impl.characteristics.windowcovering; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.VerticalTiltingWindowCovering; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.IntegerCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class CurrentVerticalTiltAngleCharacteristic extends IntegerCharacteristic - implements EventableCharacteristic { - - private final VerticalTiltingWindowCovering windowCovering; - - public CurrentVerticalTiltAngleCharacteristic(VerticalTiltingWindowCovering windowCovering) { - super( - "0000006E-0000-1000-8000-0026BB765291", - false, - true, - "The current vertical tilt angle", - -90, - 90, - "Arc Degree"); - this.windowCovering = windowCovering; - } - - @Override - protected void setValue(Integer value) throws Exception { - // Read Only - } - - @Override - protected CompletableFuture getValue() { - return windowCovering.getCurrentVerticalTiltAngle(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - windowCovering.subscribeCurrentVerticalTiltAngle(callback); - } - - @Override - public void unsubscribe() { - windowCovering.unsubscribeCurrentVerticalTiltAngle(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/HoldPositionCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/HoldPositionCharacteristic.java deleted file mode 100644 index 09edf8b11..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/HoldPositionCharacteristic.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.hapjava.impl.characteristics.windowcovering; - -import io.github.hapjava.accessories.HoldPositionWindowCovering; -import io.github.hapjava.characteristics.BooleanCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class HoldPositionCharacteristic extends BooleanCharacteristic { - - private final HoldPositionWindowCovering windowCovering; - - public HoldPositionCharacteristic(HoldPositionWindowCovering windowCovering) { - super("0000006F-0000-1000-8000-0026BB765291", true, false, "Whether or not to hold position"); - this.windowCovering = windowCovering; - } - - @Override - protected void setValue(Boolean value) throws Exception { - this.windowCovering.setHoldPosition(value); - } - - @Override - protected CompletableFuture getValue() { - // Write only - return CompletableFuture.completedFuture(null); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/PositionStateCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/PositionStateCharacteristic.java deleted file mode 100644 index 80e87ad01..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/PositionStateCharacteristic.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.hapjava.impl.characteristics.windowcovering; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.BasicWindowCovering; -import io.github.hapjava.characteristics.EnumCharacteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class PositionStateCharacteristic extends EnumCharacteristic - implements EventableCharacteristic { - - private final BasicWindowCovering windowCovering; - - public PositionStateCharacteristic(BasicWindowCovering windowCovering) { - super("00000072-0000-1000-8000-0026BB765291", false, true, "The position state", 2); - this.windowCovering = windowCovering; - } - - @Override - protected void setValue(Integer value) throws Exception { - // Read only - } - - @Override - protected CompletableFuture getValue() { - return windowCovering.getPositionState().thenApply(v -> v.getCode()); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - windowCovering.subscribePositionState(callback); - } - - @Override - public void unsubscribe() { - windowCovering.unsubscribePositionState(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/TargetHorizontalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/TargetHorizontalTiltAngleCharacteristic.java deleted file mode 100644 index 835ec6449..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/TargetHorizontalTiltAngleCharacteristic.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.hapjava.impl.characteristics.windowcovering; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.HorizontalTiltingWindowCovering; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.IntegerCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class TargetHorizontalTiltAngleCharacteristic extends IntegerCharacteristic - implements EventableCharacteristic { - - private final HorizontalTiltingWindowCovering windowCovering; - - public TargetHorizontalTiltAngleCharacteristic(HorizontalTiltingWindowCovering windowCovering) { - super( - "0000007B-0000-1000-8000-0026BB765291", - true, - true, - "The target horizontal tilt angle", - -90, - 90, - "Arc Degree"); - this.windowCovering = windowCovering; - } - - @Override - protected void setValue(Integer value) throws Exception { - windowCovering.setTargetHorizontalTiltAngle(value); - } - - @Override - protected CompletableFuture getValue() { - return windowCovering.getTargetHorizontalTiltAngle(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - windowCovering.subscribeTargetHorizontalTiltAngle(callback); - } - - @Override - public void unsubscribe() { - windowCovering.unsubscribeTargetHorizontalTiltAngle(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/TargetPositionCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/TargetPositionCharacteristic.java deleted file mode 100644 index 0db8fe6df..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/TargetPositionCharacteristic.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.hapjava.impl.characteristics.windowcovering; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.BasicWindowCovering; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.IntegerCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class TargetPositionCharacteristic extends IntegerCharacteristic - implements EventableCharacteristic { - - private final BasicWindowCovering windowCovering; - - public TargetPositionCharacteristic(BasicWindowCovering windowCovering) { - super("0000007C-0000-1000-8000-0026BB765291", true, true, "The target position", 0, 100, "%"); - this.windowCovering = windowCovering; - } - - @Override - protected void setValue(Integer value) throws Exception { - windowCovering.setTargetPosition(value); - } - - @Override - protected CompletableFuture getValue() { - return windowCovering.getTargetPosition(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - windowCovering.subscribeTargetPosition(callback); - } - - @Override - public void unsubscribe() { - windowCovering.unsubscribeTargetPosition(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/TargetVerticalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/TargetVerticalTiltAngleCharacteristic.java deleted file mode 100644 index 20472689e..000000000 --- a/src/main/java/io/github/hapjava/impl/characteristics/windowcovering/TargetVerticalTiltAngleCharacteristic.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.hapjava.impl.characteristics.windowcovering; - -import io.github.hapjava.HomekitCharacteristicChangeCallback; -import io.github.hapjava.accessories.VerticalTiltingWindowCovering; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.IntegerCharacteristic; -import java.util.concurrent.CompletableFuture; - -public class TargetVerticalTiltAngleCharacteristic extends IntegerCharacteristic - implements EventableCharacteristic { - - private final VerticalTiltingWindowCovering windowCovering; - - public TargetVerticalTiltAngleCharacteristic(VerticalTiltingWindowCovering windowCovering) { - super( - "0000007D-0000-1000-8000-0026BB765291", - true, - true, - "The target vertical tilt angle", - -90, - 90, - "Arc Degree"); - this.windowCovering = windowCovering; - } - - @Override - protected void setValue(Integer value) throws Exception { - windowCovering.setTargetVerticalTiltAngle(value); - } - - @Override - protected CompletableFuture getValue() { - return windowCovering.getTargetVerticalTiltAngle(); - } - - @Override - public void subscribe(HomekitCharacteristicChangeCallback callback) { - windowCovering.subscribeTargetVerticalTiltAngle(callback); - } - - @Override - public void unsubscribe() { - windowCovering.unsubscribeTargetVerticalTiltAngle(); - } -} diff --git a/src/main/java/io/github/hapjava/impl/pairing/Stage.java b/src/main/java/io/github/hapjava/impl/pairing/Stage.java deleted file mode 100644 index 1d5d3ef7d..000000000 --- a/src/main/java/io/github/hapjava/impl/pairing/Stage.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.hapjava.impl.pairing; - -public enum Stage { - ONE, - TWO, - THREE -} diff --git a/src/main/java/io/github/hapjava/impl/services/AbstractServiceImpl.java b/src/main/java/io/github/hapjava/impl/services/AbstractServiceImpl.java deleted file mode 100644 index 6898a5a0d..000000000 --- a/src/main/java/io/github/hapjava/impl/services/AbstractServiceImpl.java +++ /dev/null @@ -1,89 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.Service; -import io.github.hapjava.accessories.BatteryAccessory; -import io.github.hapjava.accessories.BatteryStatusAccessory; -import io.github.hapjava.characteristics.Characteristic; -import io.github.hapjava.impl.characteristics.common.BatteryLevelCharacteristic; -import io.github.hapjava.impl.characteristics.common.LowBatteryStatusCharacteristic; -import io.github.hapjava.impl.characteristics.common.Name; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -abstract class AbstractServiceImpl implements Service { - private final Logger logger = LoggerFactory.getLogger(this.getClass()); - private final String type; - private final List characteristics = new LinkedList<>(); - - /** - * This constructor has been deprecated and replaced with {@link #AbstractServiceImpl(String, - * HomekitAccessory, String)}. Usages of this constructor will need to manually configure {@link - * Name} characteristic and {@link BatteryLevelCharacteristic} if needed. - * - * @param type unique UUID of the service. This information can be obtained from HomeKit Accessory - * Simulator. - */ - @Deprecated - public AbstractServiceImpl(String type) { - this(type, null, null); - } - - /** - * Creates a new instance of this class with the specified UUID and {@link HomekitAccessory}. - * Download and install HomeKit Accessory Simulator to discover the corresponding UUID for - * the specific service. - * - *

The new service will automatically add {@link Name} characteristic. If the accessory is - * battery operated then it must implement {@link BatteryAccessory} and {@link - * BatteryLevelCharacteristic} will be added too. - * - * @param type unique UUID of the service. This information can be obtained from HomeKit Accessory - * Simulator. - * @param accessory HomeKit accessory exposed as a service. - * @param serviceName name of the service. This information is usually the name of the accessory. - */ - public AbstractServiceImpl(String type, HomekitAccessory accessory, String serviceName) { - this.type = type; - - if (accessory != null) { - // Add name characteristic - addCharacteristic(new Name(serviceName)); - - // If battery operated accessory then add BatteryLevelCharacteristic - if (accessory instanceof BatteryAccessory) { - logger.warn( - "Accessory {} implements BatteryAccessory, which was incorrectly used to advertise battery state and is not recognized by HomeKit. " - + "Battery-powered devices should report their battery status using LowBatteryStatusAccessory", - accessory.getClass()); - } - - // If battery operated accessory then add LowBatteryStatusAccessory - if (accessory instanceof BatteryStatusAccessory) { - BatteryStatusAccessory batteryStatusAccessory = (BatteryStatusAccessory) accessory; - addCharacteristic( - new LowBatteryStatusCharacteristic( - batteryStatusAccessory::getLowBatteryState, - batteryStatusAccessory::subscribeLowBatteryState, - batteryStatusAccessory::unsubscribeLowBatteryState)); - } - } - } - - @Override - public List getCharacteristics() { - return Collections.unmodifiableList(characteristics); - } - - @Override - public String getType() { - return type; - } - - protected void addCharacteristic(Characteristic characteristic) { - this.characteristics.add(characteristic); - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/AccessoryInformationService.java b/src/main/java/io/github/hapjava/impl/services/AccessoryInformationService.java deleted file mode 100644 index eb464b4d0..000000000 --- a/src/main/java/io/github/hapjava/impl/services/AccessoryInformationService.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.impl.characteristics.information.Identify; -import io.github.hapjava.impl.characteristics.information.Manufacturer; -import io.github.hapjava.impl.characteristics.information.Model; -import io.github.hapjava.impl.characteristics.information.SerialNumber; - -public class AccessoryInformationService extends AbstractServiceImpl { - - public AccessoryInformationService(HomekitAccessory accessory) throws Exception { - this(accessory, accessory.getLabel()); - } - - public AccessoryInformationService(HomekitAccessory accessory, String serviceName) - throws Exception { - super("0000003E-0000-1000-8000-0026BB765291", accessory, serviceName); - addCharacteristic(new Manufacturer(accessory)); - addCharacteristic(new Model(accessory)); - addCharacteristic(new SerialNumber(accessory)); - addCharacteristic(new Identify(accessory)); - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/CarbonDioxideSensorService.java b/src/main/java/io/github/hapjava/impl/services/CarbonDioxideSensorService.java deleted file mode 100644 index f00f04996..000000000 --- a/src/main/java/io/github/hapjava/impl/services/CarbonDioxideSensorService.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.CarbonDioxideSensor; -import io.github.hapjava.impl.characteristics.carbondioxide.CarbonDioxideDetectedCharacteristic; -import io.github.hapjava.impl.characteristics.carbondioxide.CarbonDioxideLevelCharacteristic; - -public class CarbonDioxideSensorService extends AbstractServiceImpl { - - public CarbonDioxideSensorService(CarbonDioxideSensor carbonDioxideSensor) { - this(carbonDioxideSensor, carbonDioxideSensor.getLabel()); - } - - public CarbonDioxideSensorService(CarbonDioxideSensor carbonDioxideSensor, String serviceName) { - super("00000097-0000-1000-8000-0026BB765291", carbonDioxideSensor, serviceName); - addCharacteristic(new CarbonDioxideDetectedCharacteristic(carbonDioxideSensor)); - addCharacteristic(new CarbonDioxideLevelCharacteristic(carbonDioxideSensor)); - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/CarbonMonoxideSensorService.java b/src/main/java/io/github/hapjava/impl/services/CarbonMonoxideSensorService.java deleted file mode 100644 index 0225ab0e4..000000000 --- a/src/main/java/io/github/hapjava/impl/services/CarbonMonoxideSensorService.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.CarbonMonoxideSensor; -import io.github.hapjava.impl.characteristics.carbonmonoxide.CarbonMonoxideDetectedCharacteristic; - -public class CarbonMonoxideSensorService extends AbstractServiceImpl { - - public CarbonMonoxideSensorService(CarbonMonoxideSensor carbonMonoxideSensor) { - this(carbonMonoxideSensor, carbonMonoxideSensor.getLabel()); - } - - public CarbonMonoxideSensorService( - CarbonMonoxideSensor carbonMonoxideSensor, String serviceName) { - super("0000007F-0000-1000-8000-0026BB765291", carbonMonoxideSensor, serviceName); - addCharacteristic(new CarbonMonoxideDetectedCharacteristic(carbonMonoxideSensor)); - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/ContactSensorService.java b/src/main/java/io/github/hapjava/impl/services/ContactSensorService.java deleted file mode 100644 index b4df03fc2..000000000 --- a/src/main/java/io/github/hapjava/impl/services/ContactSensorService.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.ContactSensor; -import io.github.hapjava.impl.characteristics.contactsensor.ContactSensorStateCharacteristic; - -public class ContactSensorService extends AbstractServiceImpl { - - public ContactSensorService(ContactSensor contactSensor) { - this(contactSensor, contactSensor.getLabel()); - } - - public ContactSensorService(ContactSensor contactSensor, String serviceName) { - super("00000080-0000-1000-8000-0026BB765291", contactSensor, serviceName); - addCharacteristic(new ContactSensorStateCharacteristic(contactSensor)); - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/FanService.java b/src/main/java/io/github/hapjava/impl/services/FanService.java deleted file mode 100644 index 86bbb701c..000000000 --- a/src/main/java/io/github/hapjava/impl/services/FanService.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.Fan; -import io.github.hapjava.impl.characteristics.common.PowerStateCharacteristic; -import io.github.hapjava.impl.characteristics.fan.RotationDirectionCharacteristic; -import io.github.hapjava.impl.characteristics.fan.RotationSpeedCharacteristic; - -public class FanService extends AbstractServiceImpl { - - public FanService(Fan fan) { - this(fan, fan.getLabel()); - } - - public FanService(Fan fan, String serviceName) { - super("00000040-0000-1000-8000-0026BB765291", fan, serviceName); - addCharacteristic( - new PowerStateCharacteristic( - () -> fan.getFanPower(), - v -> fan.setFanPower(v), - c -> fan.subscribeFanPower(c), - () -> fan.unsubscribeFanPower())); - addCharacteristic(new RotationDirectionCharacteristic(fan)); - addCharacteristic(new RotationSpeedCharacteristic(fan)); - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/GarageDoorService.java b/src/main/java/io/github/hapjava/impl/services/GarageDoorService.java deleted file mode 100644 index 1296a9b7d..000000000 --- a/src/main/java/io/github/hapjava/impl/services/GarageDoorService.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.GarageDoor; -import io.github.hapjava.impl.characteristics.common.ObstructionDetectedCharacteristic; -import io.github.hapjava.impl.characteristics.garage.CurrentDoorStateCharacteristic; -import io.github.hapjava.impl.characteristics.garage.TargetDoorStateCharacteristic; - -public class GarageDoorService extends AbstractServiceImpl { - - public GarageDoorService(GarageDoor door) { - this(door, door.getLabel()); - } - - public GarageDoorService(GarageDoor door, String serviceName) { - super("00000041-0000-1000-8000-0026BB765291", door, serviceName); - addCharacteristic(new CurrentDoorStateCharacteristic(door)); - addCharacteristic(new TargetDoorStateCharacteristic(door)); - addCharacteristic( - new ObstructionDetectedCharacteristic( - () -> door.getObstructionDetected(), - c -> door.subscribeObstructionDetected(c), - () -> door.unsubscribeObstructionDetected())); - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/HumiditySensorService.java b/src/main/java/io/github/hapjava/impl/services/HumiditySensorService.java deleted file mode 100644 index 99b1c716f..000000000 --- a/src/main/java/io/github/hapjava/impl/services/HumiditySensorService.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.HumiditySensor; -import io.github.hapjava.impl.characteristics.humiditysensor.CurrentRelativeHumidityCharacteristic; - -public class HumiditySensorService extends AbstractServiceImpl { - - public HumiditySensorService(HumiditySensor sensor) { - this(sensor, sensor.getLabel()); - } - - public HumiditySensorService(HumiditySensor sensor, String serviceName) { - super("00000082-0000-1000-8000-0026BB765291", sensor, serviceName); - addCharacteristic(new CurrentRelativeHumidityCharacteristic(sensor)); - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/LeakSensorService.java b/src/main/java/io/github/hapjava/impl/services/LeakSensorService.java deleted file mode 100644 index 89eab8bf6..000000000 --- a/src/main/java/io/github/hapjava/impl/services/LeakSensorService.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.LeakSensor; -import io.github.hapjava.impl.characteristics.leaksensor.LeakDetectedStateCharacteristic; - -public class LeakSensorService extends AbstractServiceImpl { - - public LeakSensorService(LeakSensor leakSensor) { - this(leakSensor, leakSensor.getLabel()); - } - - public LeakSensorService(LeakSensor leakSensor, String serviceName) { - super("00000083-0000-1000-8000-0026BB765291", leakSensor, serviceName); - addCharacteristic(new LeakDetectedStateCharacteristic(leakSensor)); - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/LightSensorService.java b/src/main/java/io/github/hapjava/impl/services/LightSensorService.java deleted file mode 100644 index 3b41e4d9b..000000000 --- a/src/main/java/io/github/hapjava/impl/services/LightSensorService.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.LightSensor; -import io.github.hapjava.impl.characteristics.light.AmbientLightLevelCharacteristic; - -public class LightSensorService extends AbstractServiceImpl { - - public LightSensorService(LightSensor lightSensor) { - this(lightSensor, lightSensor.getLabel()); - } - - public LightSensorService(LightSensor lightSensor, String serviceName) { - super("00000084-0000-1000-8000-0026BB765291", lightSensor, serviceName); - addCharacteristic(new AmbientLightLevelCharacteristic(lightSensor)); - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/LightbulbService.java b/src/main/java/io/github/hapjava/impl/services/LightbulbService.java deleted file mode 100644 index a3b55a141..000000000 --- a/src/main/java/io/github/hapjava/impl/services/LightbulbService.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.ColorfulLightbulb; -import io.github.hapjava.accessories.DimmableLightbulb; -import io.github.hapjava.accessories.Lightbulb; -import io.github.hapjava.impl.characteristics.common.PowerStateCharacteristic; -import io.github.hapjava.impl.characteristics.lightbulb.BrightnessCharacteristic; -import io.github.hapjava.impl.characteristics.lightbulb.HueCharacteristic; -import io.github.hapjava.impl.characteristics.lightbulb.SaturationCharacteristic; - -public class LightbulbService extends AbstractServiceImpl { - - public LightbulbService(Lightbulb lightbulb) { - this(lightbulb, lightbulb.getLabel()); - } - - public LightbulbService(Lightbulb lightbulb, String serviceName) { - super("00000043-0000-1000-8000-0026BB765291", lightbulb, serviceName); - addCharacteristic( - new PowerStateCharacteristic( - () -> lightbulb.getLightbulbPowerState(), - v -> lightbulb.setLightbulbPowerState(v), - c -> lightbulb.subscribeLightbulbPowerState(c), - () -> lightbulb.unsubscribeLightbulbPowerState())); - - if (lightbulb instanceof DimmableLightbulb) { - addCharacteristic(new BrightnessCharacteristic((DimmableLightbulb) lightbulb)); - } - - if (lightbulb instanceof ColorfulLightbulb) { - addCharacteristic(new HueCharacteristic((ColorfulLightbulb) lightbulb)); - addCharacteristic(new SaturationCharacteristic((ColorfulLightbulb) lightbulb)); - } - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/LockMechanismService.java b/src/main/java/io/github/hapjava/impl/services/LockMechanismService.java deleted file mode 100644 index a0b1346f4..000000000 --- a/src/main/java/io/github/hapjava/impl/services/LockMechanismService.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.LockMechanism; -import io.github.hapjava.accessories.LockableLockMechanism; -import io.github.hapjava.impl.characteristics.lock.mechanism.CurrentLockMechanismStateCharacteristic; -import io.github.hapjava.impl.characteristics.lock.mechanism.TargetLockMechanismStateCharacteristic; - -public class LockMechanismService extends AbstractServiceImpl { - - public LockMechanismService(LockMechanism lock) { - this(lock, lock.getLabel()); - } - - public LockMechanismService(LockMechanism lock, String serviceName) { - super("00000045-0000-1000-8000-0026BB765291", lock, serviceName); - addCharacteristic(new CurrentLockMechanismStateCharacteristic(lock)); - - if (lock instanceof LockableLockMechanism) { - addCharacteristic(new TargetLockMechanismStateCharacteristic((LockableLockMechanism) lock)); - } - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/MotionSensorService.java b/src/main/java/io/github/hapjava/impl/services/MotionSensorService.java deleted file mode 100644 index 354650973..000000000 --- a/src/main/java/io/github/hapjava/impl/services/MotionSensorService.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.MotionSensor; -import io.github.hapjava.impl.characteristics.motionsensor.MotionDetectedStateCharacteristic; - -public class MotionSensorService extends AbstractServiceImpl { - - public MotionSensorService(MotionSensor motionSensor) { - this(motionSensor, motionSensor.getLabel()); - } - - public MotionSensorService(MotionSensor motionSensor, String serviceName) { - super("00000085-0000-1000-8000-0026BB765291", motionSensor, serviceName); - addCharacteristic(new MotionDetectedStateCharacteristic(motionSensor)); - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/OccupancySensorService.java b/src/main/java/io/github/hapjava/impl/services/OccupancySensorService.java deleted file mode 100644 index 35965a9d2..000000000 --- a/src/main/java/io/github/hapjava/impl/services/OccupancySensorService.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.OccupancySensor; -import io.github.hapjava.impl.characteristics.occupancysensor.OccupancyDetectedStateCharacteristic; - -public class OccupancySensorService extends AbstractServiceImpl { - - public OccupancySensorService(OccupancySensor occupancySensor) { - this(occupancySensor, occupancySensor.getLabel()); - } - - public OccupancySensorService(OccupancySensor occupancySensor, String serviceName) { - super("00000086-0000-1000-8000-0026BB765291", occupancySensor, serviceName); - addCharacteristic(new OccupancyDetectedStateCharacteristic(occupancySensor)); - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/OutletService.java b/src/main/java/io/github/hapjava/impl/services/OutletService.java deleted file mode 100644 index 3d03a7331..000000000 --- a/src/main/java/io/github/hapjava/impl/services/OutletService.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.Outlet; -import io.github.hapjava.impl.characteristics.common.PowerStateCharacteristic; -import io.github.hapjava.impl.characteristics.outlet.OutletInUseCharacteristic; - -public class OutletService extends AbstractServiceImpl { - - public OutletService(Outlet outlet) { - this(outlet, outlet.getLabel()); - } - - public OutletService(Outlet outlet, String serviceName) { - super("00000047-0000-1000-8000-0026BB765291", outlet, serviceName); - addCharacteristic( - new PowerStateCharacteristic( - () -> outlet.getPowerState(), - v -> outlet.setPowerState(v), - c -> outlet.subscribePowerState(c), - () -> outlet.unsubscribePowerState())); - addCharacteristic(new OutletInUseCharacteristic(outlet)); - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/SecuritySystemService.java b/src/main/java/io/github/hapjava/impl/services/SecuritySystemService.java deleted file mode 100644 index 855f027e7..000000000 --- a/src/main/java/io/github/hapjava/impl/services/SecuritySystemService.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.SecuritySystem; -import io.github.hapjava.impl.characteristics.securitysystem.CurrentSecuritySystemStateCharacteristic; -import io.github.hapjava.impl.characteristics.securitysystem.SecuritySystemAlarmTypeCharacteristic; -import io.github.hapjava.impl.characteristics.securitysystem.TargetSecuritySystemStateCharacteristic; - -public class SecuritySystemService extends AbstractServiceImpl { - - public SecuritySystemService(SecuritySystem securitySystem) { - this(securitySystem, securitySystem.getLabel()); - } - - public SecuritySystemService(SecuritySystem securitySystem, String serviceName) { - super("0000007E-0000-1000-8000-0026BB765291", securitySystem, serviceName); - addCharacteristic(new CurrentSecuritySystemStateCharacteristic(securitySystem)); - addCharacteristic(new TargetSecuritySystemStateCharacteristic(securitySystem)); - addCharacteristic(new SecuritySystemAlarmTypeCharacteristic(securitySystem)); - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/SmokeSensorService.java b/src/main/java/io/github/hapjava/impl/services/SmokeSensorService.java deleted file mode 100644 index ad846c8b8..000000000 --- a/src/main/java/io/github/hapjava/impl/services/SmokeSensorService.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.SmokeSensor; -import io.github.hapjava.impl.characteristics.smokesensor.SmokeDetectedCharacteristic; - -public class SmokeSensorService extends AbstractServiceImpl { - - public SmokeSensorService(SmokeSensor smokeSensor) { - this(smokeSensor, smokeSensor.getLabel()); - } - - public SmokeSensorService(SmokeSensor smokeSensor, String serviceName) { - super("00000087-0000-1000-8000-0026BB765291", smokeSensor, serviceName); - addCharacteristic(new SmokeDetectedCharacteristic(smokeSensor)); - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/SwitchService.java b/src/main/java/io/github/hapjava/impl/services/SwitchService.java deleted file mode 100644 index a297a2cec..000000000 --- a/src/main/java/io/github/hapjava/impl/services/SwitchService.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.Switch; -import io.github.hapjava.impl.characteristics.common.PowerStateCharacteristic; - -public class SwitchService extends AbstractServiceImpl { - - public SwitchService(Switch switchAccessory) { - this(switchAccessory, switchAccessory.getLabel()); - } - - public SwitchService(Switch switchAccessory, String serviceName) { - super("00000049-0000-1000-8000-0026BB765291", switchAccessory, serviceName); - addCharacteristic( - new PowerStateCharacteristic( - () -> switchAccessory.getSwitchState(), - v -> switchAccessory.setSwitchState(v), - c -> switchAccessory.subscribeSwitchState(c), - () -> switchAccessory.unsubscribeSwitchState())); - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/TemperatureSensorService.java b/src/main/java/io/github/hapjava/impl/services/TemperatureSensorService.java deleted file mode 100644 index 5a59513aa..000000000 --- a/src/main/java/io/github/hapjava/impl/services/TemperatureSensorService.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.TemperatureSensor; -import io.github.hapjava.impl.characteristics.thermostat.CurrentTemperatureCharacteristic; - -public class TemperatureSensorService extends AbstractServiceImpl { - - public TemperatureSensorService(TemperatureSensor sensor) { - this(sensor, sensor.getLabel()); - } - - public TemperatureSensorService(TemperatureSensor sensor, String serviceName) { - super("0000008A-0000-1000-8000-0026BB765291", sensor, serviceName); - addCharacteristic(new CurrentTemperatureCharacteristic(sensor)); - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/ThermostatService.java b/src/main/java/io/github/hapjava/impl/services/ThermostatService.java deleted file mode 100644 index e679680ae..000000000 --- a/src/main/java/io/github/hapjava/impl/services/ThermostatService.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.thermostat.BasicThermostat; -import io.github.hapjava.accessories.thermostat.CoolingThermostat; -import io.github.hapjava.accessories.thermostat.HeatingThermostat; -import io.github.hapjava.impl.characteristics.thermostat.*; - -public class ThermostatService extends AbstractServiceImpl { - - public ThermostatService(BasicThermostat thermostat) { - this(thermostat, thermostat.getLabel()); - } - - public ThermostatService(BasicThermostat thermostat, String serviceName) { - super("0000004A-0000-1000-8000-0026BB765291", thermostat, serviceName); - addCharacteristic(new CurrentHeatingCoolingModeCharacteristic(thermostat)); - addCharacteristic(new CurrentTemperatureCharacteristic(thermostat)); - addCharacteristic(new TargetHeatingCoolingModeCharacteristic(thermostat)); - addCharacteristic(new TargetTemperatureCharacteristic(thermostat)); - addCharacteristic(new TemperatureUnitsCharacteristic(thermostat)); - if (thermostat instanceof HeatingThermostat) { - addCharacteristic( - new HeatingThresholdTemperatureCharacteristic((HeatingThermostat) thermostat)); - } - if (thermostat instanceof CoolingThermostat) { - addCharacteristic( - new CoolingThresholdTemperatureCharacteristic((CoolingThermostat) thermostat)); - } - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/ValveService.java b/src/main/java/io/github/hapjava/impl/services/ValveService.java deleted file mode 100644 index c1c14ff88..000000000 --- a/src/main/java/io/github/hapjava/impl/services/ValveService.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.Valve; -import io.github.hapjava.accessories.ValveWithTimer; -import io.github.hapjava.impl.characteristics.common.ActiveCharacteristic; -import io.github.hapjava.impl.characteristics.common.InUseCharacteristic; -import io.github.hapjava.impl.characteristics.common.RemainingDurationCharacteristic; -import io.github.hapjava.impl.characteristics.valve.SetDurationCharacteristic; -import io.github.hapjava.impl.characteristics.valve.ValveTypeCharacteristic; - -public class ValveService extends AbstractServiceImpl { - - public ValveService(Valve valve) { - this(valve, valve.getLabel()); - } - - public ValveService(Valve valve, String serviceName) { - super("000000D0-0000-1000-8000-0026BB765291", valve, serviceName); - addCharacteristic( - new ActiveCharacteristic( - valve::getValveActive, - a -> valve.setValveActive(a), - valve::subscribeValveActive, - valve::unsubscribeValveActive)); - addCharacteristic(new ValveTypeCharacteristic(valve)); - addCharacteristic( - new InUseCharacteristic( - valve::getValveInUse, valve::subscribeValveInUse, valve::unsubscribeValveInUse)); - - if (valve instanceof ValveWithTimer) { - ValveWithTimer vwt = (ValveWithTimer) valve; - addCharacteristic(new SetDurationCharacteristic(vwt)); - addCharacteristic( - new RemainingDurationCharacteristic( - vwt::getRemainingDuration, - vwt::subscribeRemainingDuration, - vwt::unsubscribeRemainingDuration)); - } - } -} diff --git a/src/main/java/io/github/hapjava/impl/services/WindowCoveringService.java b/src/main/java/io/github/hapjava/impl/services/WindowCoveringService.java deleted file mode 100644 index 001863da1..000000000 --- a/src/main/java/io/github/hapjava/impl/services/WindowCoveringService.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.github.hapjava.impl.services; - -import io.github.hapjava.accessories.BasicWindowCovering; -import io.github.hapjava.accessories.HoldPositionWindowCovering; -import io.github.hapjava.accessories.HorizontalTiltingWindowCovering; -import io.github.hapjava.accessories.ObstructionDetectedWindowCovering; -import io.github.hapjava.accessories.VerticalTiltingWindowCovering; -import io.github.hapjava.impl.characteristics.common.ObstructionDetectedCharacteristic; -import io.github.hapjava.impl.characteristics.windowcovering.CurrentHorizontalTiltAngleCharacteristic; -import io.github.hapjava.impl.characteristics.windowcovering.CurrentPositionCharacteristic; -import io.github.hapjava.impl.characteristics.windowcovering.CurrentVerticalTiltAngleCharacteristic; -import io.github.hapjava.impl.characteristics.windowcovering.HoldPositionCharacteristic; -import io.github.hapjava.impl.characteristics.windowcovering.PositionStateCharacteristic; -import io.github.hapjava.impl.characteristics.windowcovering.TargetHorizontalTiltAngleCharacteristic; -import io.github.hapjava.impl.characteristics.windowcovering.TargetPositionCharacteristic; -import io.github.hapjava.impl.characteristics.windowcovering.TargetVerticalTiltAngleCharacteristic; - -public class WindowCoveringService extends AbstractServiceImpl { - - public WindowCoveringService(BasicWindowCovering windowCovering) { - this(windowCovering, windowCovering.getLabel()); - } - - public WindowCoveringService(BasicWindowCovering windowCovering, String serviceName) { - super("0000008C-0000-1000-8000-0026BB765291", windowCovering, serviceName); - addCharacteristic(new CurrentPositionCharacteristic(windowCovering)); - addCharacteristic(new PositionStateCharacteristic(windowCovering)); - addCharacteristic(new TargetPositionCharacteristic(windowCovering)); - - if (windowCovering instanceof HorizontalTiltingWindowCovering) { - addCharacteristic( - new CurrentHorizontalTiltAngleCharacteristic( - (HorizontalTiltingWindowCovering) windowCovering)); - addCharacteristic( - new TargetHorizontalTiltAngleCharacteristic( - (HorizontalTiltingWindowCovering) windowCovering)); - } - if (windowCovering instanceof VerticalTiltingWindowCovering) { - addCharacteristic( - new CurrentVerticalTiltAngleCharacteristic( - (VerticalTiltingWindowCovering) windowCovering)); - addCharacteristic( - new TargetVerticalTiltAngleCharacteristic( - (VerticalTiltingWindowCovering) windowCovering)); - } - if (windowCovering instanceof HoldPositionWindowCovering) { - HoldPositionWindowCovering hpwc = (HoldPositionWindowCovering) windowCovering; - addCharacteristic(new HoldPositionCharacteristic(hpwc)); - } - if (windowCovering instanceof ObstructionDetectedWindowCovering) { - ObstructionDetectedWindowCovering wc = (ObstructionDetectedWindowCovering) windowCovering; - addCharacteristic( - new ObstructionDetectedCharacteristic( - () -> wc.getObstructionDetected(), - c -> wc.subscribeObstructionDetected(c), - () -> wc.unsubscribeObstructionDetected())); - } - } -} diff --git a/src/main/java/io/github/hapjava/package-info.java b/src/main/java/io/github/hapjava/package-info.java deleted file mode 100644 index b4b98ad13..000000000 --- a/src/main/java/io/github/hapjava/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Base package for implementing the Homekit Accessory Protocol. Start with {@link - * io.github.hapjava.HomekitServer}. - */ -package io.github.hapjava; diff --git a/src/main/java/io/github/hapjava/HomekitAuthInfo.java b/src/main/java/io/github/hapjava/server/HomekitAuthInfo.java similarity index 97% rename from src/main/java/io/github/hapjava/HomekitAuthInfo.java rename to src/main/java/io/github/hapjava/server/HomekitAuthInfo.java index 3326c8bae..9eeae8fe5 100644 --- a/src/main/java/io/github/hapjava/HomekitAuthInfo.java +++ b/src/main/java/io/github/hapjava/server/HomekitAuthInfo.java @@ -1,5 +1,6 @@ -package io.github.hapjava; +package io.github.hapjava.server; +import io.github.hapjava.server.impl.HomekitServer; import java.math.BigInteger; /** diff --git a/src/main/java/io/github/hapjava/impl/HomekitWebHandler.java b/src/main/java/io/github/hapjava/server/HomekitWebHandler.java similarity index 67% rename from src/main/java/io/github/hapjava/impl/HomekitWebHandler.java rename to src/main/java/io/github/hapjava/server/HomekitWebHandler.java index 5c7693a29..d66a843c4 100644 --- a/src/main/java/io/github/hapjava/impl/HomekitWebHandler.java +++ b/src/main/java/io/github/hapjava/server/HomekitWebHandler.java @@ -1,6 +1,6 @@ -package io.github.hapjava.impl; +package io.github.hapjava.server; -import io.github.hapjava.impl.http.HomekitClientConnectionFactory; +import io.github.hapjava.server.impl.http.HomekitClientConnectionFactory; import java.util.concurrent.CompletableFuture; public interface HomekitWebHandler { diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitBridge.java b/src/main/java/io/github/hapjava/server/impl/HomekitBridge.java new file mode 100644 index 000000000..11efb0d37 --- /dev/null +++ b/src/main/java/io/github/hapjava/server/impl/HomekitBridge.java @@ -0,0 +1,67 @@ +package io.github.hapjava.server.impl; + +import io.github.hapjava.accessories.Bridge; +import io.github.hapjava.services.Service; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +public class HomekitBridge implements Bridge { + + private final String label; + private final String serialNumber; + private final String model; + private final String manufacturer; + private final String firmwareRevision; + private final String hardwareRevision; + + public HomekitBridge( + String label, + String serialNumber, + String model, + String manufacturer, + String firmwareRevision, + String hardwareRevision) { + this.label = label; + this.serialNumber = serialNumber; + this.model = model; + this.manufacturer = manufacturer; + this.firmwareRevision = firmwareRevision; + this.hardwareRevision = hardwareRevision; + } + + @Override + public CompletableFuture getName() { + return CompletableFuture.completedFuture(label); + } + + @Override + public CompletableFuture getSerialNumber() { + return CompletableFuture.completedFuture(serialNumber); + } + + @Override + public CompletableFuture getModel() { + return CompletableFuture.completedFuture(model); + } + + @Override + public CompletableFuture getManufacturer() { + return CompletableFuture.completedFuture(manufacturer); + } + + @Override + public CompletableFuture getFirmwareRevision() { + return CompletableFuture.completedFuture(firmwareRevision); + } + + @Override + public Collection getServices() { + return Collections.emptyList(); + } + + @Override + public int getId() { + return 1; + } +} diff --git a/src/main/java/io/github/hapjava/impl/HomekitRegistry.java b/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java similarity index 92% rename from src/main/java/io/github/hapjava/impl/HomekitRegistry.java rename to src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java index e3107d44e..8c7b3d0cd 100644 --- a/src/main/java/io/github/hapjava/impl/HomekitRegistry.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java @@ -1,9 +1,9 @@ -package io.github.hapjava.impl; +package io.github.hapjava.server.impl; -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.Service; +import io.github.hapjava.accessories.HomekitAccessory; import io.github.hapjava.characteristics.Characteristic; -import io.github.hapjava.impl.services.AccessoryInformationService; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.AccessoryInformationService; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; @@ -37,7 +37,7 @@ public synchronized void reset() { newServices.add(new AccessoryInformationService(accessory)); newServices.addAll(accessory.getServices()); } catch (Exception e) { - logger.error("Could not instantiate services for accessory " + accessory.getLabel(), e); + logger.error("Could not instantiate services for accessory " + accessory.getName(), e); services.put(accessory, Collections.emptyList()); continue; } diff --git a/src/main/java/io/github/hapjava/HomekitRoot.java b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java similarity index 89% rename from src/main/java/io/github/hapjava/HomekitRoot.java rename to src/main/java/io/github/hapjava/server/impl/HomekitRoot.java index 29ed0113c..bc7a2686f 100644 --- a/src/main/java/io/github/hapjava/HomekitRoot.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java @@ -1,11 +1,12 @@ -package io.github.hapjava; +package io.github.hapjava.server.impl; -import io.github.hapjava.impl.HomekitRegistry; -import io.github.hapjava.impl.HomekitWebHandler; -import io.github.hapjava.impl.accessories.Bridge; -import io.github.hapjava.impl.connections.HomekitClientConnectionFactoryImpl; -import io.github.hapjava.impl.connections.SubscriptionManager; -import io.github.hapjava.impl.jmdns.JmdnsHomekitAdvertiser; +import io.github.hapjava.accessories.Bridge; +import io.github.hapjava.accessories.HomekitAccessory; +import io.github.hapjava.server.HomekitAuthInfo; +import io.github.hapjava.server.HomekitWebHandler; +import io.github.hapjava.server.impl.connections.HomekitClientConnectionFactoryImpl; +import io.github.hapjava.server.impl.connections.SubscriptionManager; +import io.github.hapjava.server.impl.jmdns.JmdnsHomekitAdvertiser; import java.io.IOException; import java.net.InetAddress; import org.slf4j.Logger; @@ -15,8 +16,8 @@ * Provides advertising and handling for Homekit accessories. This class handles the advertising of * Homekit accessories and contains one or more accessories. When implementing a bridge accessory, * you will interact with this class directly. Instantiate it via {@link - * HomekitServer#createBridge(HomekitAuthInfo, String, String, String, String)}. For single - * accessories, this is composed by {@link HomekitStandaloneAccessoryServer}. + * HomekitServer#createBridge(HomekitAuthInfo, String, String, String, String, String, String)}. For + * single accessories, this is composed by {@link HomekitStandaloneAccessoryServer}. * * @author Andy Lintner */ @@ -76,7 +77,7 @@ public void addAccessory(HomekitAccessory accessory) { */ void addAccessorySkipRangeCheck(HomekitAccessory accessory) { this.registry.add(accessory); - logger.info("Added accessory " + accessory.getLabel()); + logger.info("Added accessory " + accessory.getName()); if (started) { registry.reset(); webHandler.resetConnections(); @@ -92,7 +93,7 @@ void addAccessorySkipRangeCheck(HomekitAccessory accessory) { */ public void removeAccessory(HomekitAccessory accessory) { this.registry.remove(accessory); - logger.info("Removed accessory " + accessory.getLabel()); + logger.info("Removed accessory " + accessory.getName()); if (started) { registry.reset(); webHandler.resetConnections(); diff --git a/src/main/java/io/github/hapjava/HomekitServer.java b/src/main/java/io/github/hapjava/server/impl/HomekitServer.java similarity index 88% rename from src/main/java/io/github/hapjava/HomekitServer.java rename to src/main/java/io/github/hapjava/server/impl/HomekitServer.java index 96b64fbdc..ea57f8da4 100644 --- a/src/main/java/io/github/hapjava/HomekitServer.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitServer.java @@ -1,18 +1,19 @@ -package io.github.hapjava; +package io.github.hapjava.server.impl; -import io.github.hapjava.impl.HomekitBridge; -import io.github.hapjava.impl.HomekitUtils; -import io.github.hapjava.impl.http.impl.HomekitHttpServer; +import io.github.hapjava.accessories.HomekitAccessory; +import io.github.hapjava.server.HomekitAuthInfo; +import io.github.hapjava.server.impl.http.impl.HomekitHttpServer; import java.io.IOException; import java.math.BigInteger; import java.net.InetAddress; import java.security.InvalidAlgorithmParameterException; +import java.util.concurrent.ExecutionException; /** * The main entry point for hap-java. Creating an instance of this class will listen for Homekit * connections on the supplied port. Only a single root accessory can be added for each unique * instance and port, however, that accessory may be a {@link #createBridge(HomekitAuthInfo, String, - * String, String, String) bridge accessory} containing child accessories. + * String, String, String, String, String) bridge accessory} containing child accessories. * *

The {@link HomekitAuthInfo HomekitAuthInfo} argument when creating accessories should be an * implementation supplied by your application. Several of the values needed for your implementation @@ -81,7 +82,8 @@ public void stop() { * @throws IOException when mDNS cannot connect to the network */ public HomekitStandaloneAccessoryServer createStandaloneAccessory( - HomekitAuthInfo authInfo, HomekitAccessory accessory) throws IOException { + HomekitAuthInfo authInfo, HomekitAccessory accessory) + throws IOException, ExecutionException, InterruptedException { return new HomekitStandaloneAccessoryServer(accessory, http, localAddress, authInfo); } @@ -97,6 +99,8 @@ public HomekitStandaloneAccessoryServer createStandaloneAccessory( * purposes. * @param model model of the bridge. This is also exposed to iOS for unknown purposes. * @param serialNumber serial number of the bridge. Also exposed. Purposes also unknown. + * @param firmwareRevision firmware revision of the bridge. + * @param hardwareRevision hardware revision of the brigde. * @return the bridge, from which you can {@link HomekitRoot#addAccessory add accessories} and * then {@link HomekitRoot#start start} handling requests. * @throws IOException when mDNS cannot connect to the network @@ -106,10 +110,14 @@ public HomekitRoot createBridge( String label, String manufacturer, String model, - String serialNumber) + String serialNumber, + String firmwareRevision, + String hardwareRevision) throws IOException { HomekitRoot root = new HomekitRoot(label, http, localAddress, authInfo); - root.addAccessory(new HomekitBridge(label, serialNumber, model, manufacturer)); + root.addAccessory( + new HomekitBridge( + label, serialNumber, model, manufacturer, firmwareRevision, hardwareRevision)); return root; } diff --git a/src/main/java/io/github/hapjava/HomekitStandaloneAccessoryServer.java b/src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java similarity index 67% rename from src/main/java/io/github/hapjava/HomekitStandaloneAccessoryServer.java rename to src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java index 6df6fb603..9ab4530ea 100644 --- a/src/main/java/io/github/hapjava/HomekitStandaloneAccessoryServer.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java @@ -1,9 +1,12 @@ -package io.github.hapjava; +package io.github.hapjava.server.impl; -import io.github.hapjava.impl.HomekitWebHandler; +import io.github.hapjava.accessories.HomekitAccessory; +import io.github.hapjava.server.HomekitAuthInfo; +import io.github.hapjava.server.HomekitWebHandler; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; +import java.util.concurrent.ExecutionException; /** * A server for exposing standalone Homekit accessory (as opposed to a Bridge accessory which @@ -22,8 +25,8 @@ public class HomekitStandaloneAccessoryServer { HomekitWebHandler webHandler, InetAddress localhost, HomekitAuthInfo authInfo) - throws UnknownHostException, IOException { - root = new HomekitRoot(accessory.getLabel(), webHandler, localhost, authInfo); + throws UnknownHostException, IOException, ExecutionException, InterruptedException { + root = new HomekitRoot(accessory.getName().get(), webHandler, localhost, authInfo); root.addAccessory(accessory); } diff --git a/src/main/java/io/github/hapjava/impl/HomekitUtils.java b/src/main/java/io/github/hapjava/server/impl/HomekitUtils.java similarity index 98% rename from src/main/java/io/github/hapjava/impl/HomekitUtils.java rename to src/main/java/io/github/hapjava/server/impl/HomekitUtils.java index 614515993..9b5b2b8ee 100644 --- a/src/main/java/io/github/hapjava/impl/HomekitUtils.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitUtils.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl; +package io.github.hapjava.server.impl; import com.nimbusds.srp6.SRP6Routines; import java.math.BigInteger; diff --git a/src/main/java/io/github/hapjava/impl/connections/ConnectionImpl.java b/src/main/java/io/github/hapjava/server/impl/connections/ConnectionImpl.java similarity index 88% rename from src/main/java/io/github/hapjava/impl/connections/ConnectionImpl.java rename to src/main/java/io/github/hapjava/server/impl/connections/ConnectionImpl.java index 8751a9652..c1853855d 100644 --- a/src/main/java/io/github/hapjava/impl/connections/ConnectionImpl.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/ConnectionImpl.java @@ -1,12 +1,14 @@ -package io.github.hapjava.impl.connections; +package io.github.hapjava.server.impl.connections; -import io.github.hapjava.HomekitAuthInfo; -import io.github.hapjava.impl.HomekitRegistry; -import io.github.hapjava.impl.crypto.ChachaDecoder; -import io.github.hapjava.impl.crypto.ChachaEncoder; -import io.github.hapjava.impl.http.*; -import io.github.hapjava.impl.jmdns.JmdnsHomekitAdvertiser; -import io.github.hapjava.impl.pairing.UpgradeResponse; +import io.github.hapjava.server.HomekitAuthInfo; +import io.github.hapjava.server.impl.HomekitRegistry; +import io.github.hapjava.server.impl.crypto.ChachaDecoder; +import io.github.hapjava.server.impl.crypto.ChachaEncoder; +import io.github.hapjava.server.impl.http.HomekitClientConnection; +import io.github.hapjava.server.impl.http.HttpRequest; +import io.github.hapjava.server.impl.http.HttpResponse; +import io.github.hapjava.server.impl.jmdns.JmdnsHomekitAdvertiser; +import io.github.hapjava.server.impl.pairing.UpgradeResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/src/main/java/io/github/hapjava/impl/connections/HomekitClientConnectionFactoryImpl.java b/src/main/java/io/github/hapjava/server/impl/connections/HomekitClientConnectionFactoryImpl.java similarity index 67% rename from src/main/java/io/github/hapjava/impl/connections/HomekitClientConnectionFactoryImpl.java rename to src/main/java/io/github/hapjava/server/impl/connections/HomekitClientConnectionFactoryImpl.java index b4b304b1a..9c8ad9fc0 100644 --- a/src/main/java/io/github/hapjava/impl/connections/HomekitClientConnectionFactoryImpl.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/HomekitClientConnectionFactoryImpl.java @@ -1,11 +1,11 @@ -package io.github.hapjava.impl.connections; +package io.github.hapjava.server.impl.connections; -import io.github.hapjava.HomekitAuthInfo; -import io.github.hapjava.impl.HomekitRegistry; -import io.github.hapjava.impl.http.HomekitClientConnection; -import io.github.hapjava.impl.http.HomekitClientConnectionFactory; -import io.github.hapjava.impl.http.HttpResponse; -import io.github.hapjava.impl.jmdns.JmdnsHomekitAdvertiser; +import io.github.hapjava.server.HomekitAuthInfo; +import io.github.hapjava.server.impl.HomekitRegistry; +import io.github.hapjava.server.impl.http.HomekitClientConnection; +import io.github.hapjava.server.impl.http.HomekitClientConnectionFactory; +import io.github.hapjava.server.impl.http.HttpResponse; +import io.github.hapjava.server.impl.jmdns.JmdnsHomekitAdvertiser; import java.util.function.Consumer; public class HomekitClientConnectionFactoryImpl implements HomekitClientConnectionFactory { diff --git a/src/main/java/io/github/hapjava/impl/connections/HttpSession.java b/src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java similarity index 84% rename from src/main/java/io/github/hapjava/impl/connections/HttpSession.java rename to src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java index 4a7efe7d9..3d7d8c817 100644 --- a/src/main/java/io/github/hapjava/impl/connections/HttpSession.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java @@ -1,19 +1,19 @@ -package io.github.hapjava.impl.connections; - -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.HomekitAuthInfo; -import io.github.hapjava.impl.HomekitRegistry; -import io.github.hapjava.impl.http.HomekitClientConnection; -import io.github.hapjava.impl.http.HttpRequest; -import io.github.hapjava.impl.http.HttpResponse; -import io.github.hapjava.impl.jmdns.JmdnsHomekitAdvertiser; -import io.github.hapjava.impl.json.AccessoryController; -import io.github.hapjava.impl.json.CharacteristicsController; -import io.github.hapjava.impl.pairing.PairVerificationManager; -import io.github.hapjava.impl.pairing.PairingManager; -import io.github.hapjava.impl.pairing.PairingUpdateController; -import io.github.hapjava.impl.responses.InternalServerErrorResponse; -import io.github.hapjava.impl.responses.NotFoundResponse; +package io.github.hapjava.server.impl.connections; + +import io.github.hapjava.accessories.HomekitAccessory; +import io.github.hapjava.server.HomekitAuthInfo; +import io.github.hapjava.server.impl.HomekitRegistry; +import io.github.hapjava.server.impl.http.HomekitClientConnection; +import io.github.hapjava.server.impl.http.HttpRequest; +import io.github.hapjava.server.impl.http.HttpResponse; +import io.github.hapjava.server.impl.jmdns.JmdnsHomekitAdvertiser; +import io.github.hapjava.server.impl.json.AccessoryController; +import io.github.hapjava.server.impl.json.CharacteristicsController; +import io.github.hapjava.server.impl.pairing.PairVerificationManager; +import io.github.hapjava.server.impl.pairing.PairingManager; +import io.github.hapjava.server.impl.pairing.PairingUpdateController; +import io.github.hapjava.server.impl.responses.InternalServerErrorResponse; +import io.github.hapjava.server.impl.responses.NotFoundResponse; import java.io.IOException; import java.net.InetAddress; import org.slf4j.Logger; diff --git a/src/main/java/io/github/hapjava/impl/connections/LengthPrefixedByteArrayProcessor.java b/src/main/java/io/github/hapjava/server/impl/connections/LengthPrefixedByteArrayProcessor.java similarity index 98% rename from src/main/java/io/github/hapjava/impl/connections/LengthPrefixedByteArrayProcessor.java rename to src/main/java/io/github/hapjava/server/impl/connections/LengthPrefixedByteArrayProcessor.java index d84194685..124acbb51 100644 --- a/src/main/java/io/github/hapjava/impl/connections/LengthPrefixedByteArrayProcessor.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/LengthPrefixedByteArrayProcessor.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.connections; +package io.github.hapjava.server.impl.connections; import java.io.ByteArrayOutputStream; import java.util.Collection; diff --git a/src/main/java/io/github/hapjava/impl/connections/PendingNotification.java b/src/main/java/io/github/hapjava/server/impl/connections/PendingNotification.java similarity index 87% rename from src/main/java/io/github/hapjava/impl/connections/PendingNotification.java rename to src/main/java/io/github/hapjava/server/impl/connections/PendingNotification.java index 8cd65a4b9..ea9c69305 100644 --- a/src/main/java/io/github/hapjava/impl/connections/PendingNotification.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/PendingNotification.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.connections; +package io.github.hapjava.server.impl.connections; import io.github.hapjava.characteristics.EventableCharacteristic; diff --git a/src/main/java/io/github/hapjava/impl/connections/SubscriptionManager.java b/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java similarity index 95% rename from src/main/java/io/github/hapjava/impl/connections/SubscriptionManager.java rename to src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java index 2a2fc693d..5a26a41b4 100644 --- a/src/main/java/io/github/hapjava/impl/connections/SubscriptionManager.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java @@ -1,9 +1,9 @@ -package io.github.hapjava.impl.connections; +package io.github.hapjava.server.impl.connections; import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.impl.http.HomekitClientConnection; -import io.github.hapjava.impl.http.HttpResponse; -import io.github.hapjava.impl.json.EventController; +import io.github.hapjava.server.impl.http.HomekitClientConnection; +import io.github.hapjava.server.impl.http.HttpResponse; +import io.github.hapjava.server.impl.json.EventController; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; diff --git a/src/main/java/io/github/hapjava/impl/crypto/ChachaDecoder.java b/src/main/java/io/github/hapjava/server/impl/crypto/ChachaDecoder.java similarity index 97% rename from src/main/java/io/github/hapjava/impl/crypto/ChachaDecoder.java rename to src/main/java/io/github/hapjava/server/impl/crypto/ChachaDecoder.java index 73a09b945..4c74a469d 100644 --- a/src/main/java/io/github/hapjava/impl/crypto/ChachaDecoder.java +++ b/src/main/java/io/github/hapjava/server/impl/crypto/ChachaDecoder.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.crypto; +package io.github.hapjava.server.impl.crypto; import java.io.IOException; import org.bouncycastle.crypto.engines.ChaChaEngine; diff --git a/src/main/java/io/github/hapjava/impl/crypto/ChachaEncoder.java b/src/main/java/io/github/hapjava/server/impl/crypto/ChachaEncoder.java similarity index 97% rename from src/main/java/io/github/hapjava/impl/crypto/ChachaEncoder.java rename to src/main/java/io/github/hapjava/server/impl/crypto/ChachaEncoder.java index aab841877..3304e7d9a 100644 --- a/src/main/java/io/github/hapjava/impl/crypto/ChachaEncoder.java +++ b/src/main/java/io/github/hapjava/server/impl/crypto/ChachaEncoder.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.crypto; +package io.github.hapjava.server.impl.crypto; import java.io.IOException; import org.bouncycastle.crypto.engines.ChaChaEngine; diff --git a/src/main/java/io/github/hapjava/impl/crypto/EdsaSigner.java b/src/main/java/io/github/hapjava/server/impl/crypto/EdsaSigner.java similarity index 96% rename from src/main/java/io/github/hapjava/impl/crypto/EdsaSigner.java rename to src/main/java/io/github/hapjava/server/impl/crypto/EdsaSigner.java index 3c522438b..a1985d186 100644 --- a/src/main/java/io/github/hapjava/impl/crypto/EdsaSigner.java +++ b/src/main/java/io/github/hapjava/server/impl/crypto/EdsaSigner.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.crypto; +package io.github.hapjava.server.impl.crypto; import java.security.InvalidKeyException; import java.security.MessageDigest; diff --git a/src/main/java/io/github/hapjava/impl/crypto/EdsaVerifier.java b/src/main/java/io/github/hapjava/server/impl/crypto/EdsaVerifier.java similarity index 95% rename from src/main/java/io/github/hapjava/impl/crypto/EdsaVerifier.java rename to src/main/java/io/github/hapjava/server/impl/crypto/EdsaVerifier.java index 4ed210a3a..e5ca8e90a 100644 --- a/src/main/java/io/github/hapjava/impl/crypto/EdsaVerifier.java +++ b/src/main/java/io/github/hapjava/server/impl/crypto/EdsaVerifier.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.crypto; +package io.github.hapjava.server.impl.crypto; import java.security.MessageDigest; import java.security.PublicKey; diff --git a/src/main/java/io/github/hapjava/impl/crypto/PolyKeyCreator.java b/src/main/java/io/github/hapjava/server/impl/crypto/PolyKeyCreator.java similarity index 96% rename from src/main/java/io/github/hapjava/impl/crypto/PolyKeyCreator.java rename to src/main/java/io/github/hapjava/server/impl/crypto/PolyKeyCreator.java index 9d314aa5c..ed66342c8 100644 --- a/src/main/java/io/github/hapjava/impl/crypto/PolyKeyCreator.java +++ b/src/main/java/io/github/hapjava/server/impl/crypto/PolyKeyCreator.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.crypto; +package io.github.hapjava.server.impl.crypto; import org.bouncycastle.crypto.macs.Poly1305; import org.bouncycastle.crypto.params.KeyParameter; diff --git a/src/main/java/io/github/hapjava/impl/http/HomekitClientConnection.java b/src/main/java/io/github/hapjava/server/impl/http/HomekitClientConnection.java similarity index 87% rename from src/main/java/io/github/hapjava/impl/http/HomekitClientConnection.java rename to src/main/java/io/github/hapjava/server/impl/http/HomekitClientConnection.java index b91cad70b..58ef23c6e 100644 --- a/src/main/java/io/github/hapjava/impl/http/HomekitClientConnection.java +++ b/src/main/java/io/github/hapjava/server/impl/http/HomekitClientConnection.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.http; +package io.github.hapjava.server.impl.http; import java.io.IOException; diff --git a/src/main/java/io/github/hapjava/impl/http/HomekitClientConnectionFactory.java b/src/main/java/io/github/hapjava/server/impl/http/HomekitClientConnectionFactory.java similarity index 80% rename from src/main/java/io/github/hapjava/impl/http/HomekitClientConnectionFactory.java rename to src/main/java/io/github/hapjava/server/impl/http/HomekitClientConnectionFactory.java index 435d592a6..18c814306 100644 --- a/src/main/java/io/github/hapjava/impl/http/HomekitClientConnectionFactory.java +++ b/src/main/java/io/github/hapjava/server/impl/http/HomekitClientConnectionFactory.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.http; +package io.github.hapjava.server.impl.http; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/impl/http/HttpMethod.java b/src/main/java/io/github/hapjava/server/impl/http/HttpMethod.java similarity index 52% rename from src/main/java/io/github/hapjava/impl/http/HttpMethod.java rename to src/main/java/io/github/hapjava/server/impl/http/HttpMethod.java index 014bf024a..f4844b667 100644 --- a/src/main/java/io/github/hapjava/impl/http/HttpMethod.java +++ b/src/main/java/io/github/hapjava/server/impl/http/HttpMethod.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.http; +package io.github.hapjava.server.impl.http; public enum HttpMethod { GET, diff --git a/src/main/java/io/github/hapjava/impl/http/HttpRequest.java b/src/main/java/io/github/hapjava/server/impl/http/HttpRequest.java similarity index 69% rename from src/main/java/io/github/hapjava/impl/http/HttpRequest.java rename to src/main/java/io/github/hapjava/server/impl/http/HttpRequest.java index 088050ad9..9921af341 100644 --- a/src/main/java/io/github/hapjava/impl/http/HttpRequest.java +++ b/src/main/java/io/github/hapjava/server/impl/http/HttpRequest.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.http; +package io.github.hapjava.server.impl.http; public interface HttpRequest { diff --git a/src/main/java/io/github/hapjava/impl/http/HttpResponse.java b/src/main/java/io/github/hapjava/server/impl/http/HttpResponse.java similarity index 91% rename from src/main/java/io/github/hapjava/impl/http/HttpResponse.java rename to src/main/java/io/github/hapjava/server/impl/http/HttpResponse.java index 205ff5e56..4871dba99 100644 --- a/src/main/java/io/github/hapjava/impl/http/HttpResponse.java +++ b/src/main/java/io/github/hapjava/server/impl/http/HttpResponse.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.http; +package io.github.hapjava.server.impl.http; import java.nio.ByteBuffer; import java.util.Collections; diff --git a/src/main/java/io/github/hapjava/impl/http/impl/AccessoryHandler.java b/src/main/java/io/github/hapjava/server/impl/http/impl/AccessoryHandler.java similarity index 93% rename from src/main/java/io/github/hapjava/impl/http/impl/AccessoryHandler.java rename to src/main/java/io/github/hapjava/server/impl/http/impl/AccessoryHandler.java index 20d4246ac..35ac055cf 100644 --- a/src/main/java/io/github/hapjava/impl/http/impl/AccessoryHandler.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/AccessoryHandler.java @@ -1,8 +1,8 @@ -package io.github.hapjava.impl.http.impl; +package io.github.hapjava.server.impl.http.impl; -import io.github.hapjava.impl.http.HomekitClientConnection; -import io.github.hapjava.impl.http.HomekitClientConnectionFactory; -import io.github.hapjava.impl.http.HttpResponse; +import io.github.hapjava.server.impl.http.HomekitClientConnection; +import io.github.hapjava.server.impl.http.HomekitClientConnectionFactory; +import io.github.hapjava.server.impl.http.HttpResponse; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; diff --git a/src/main/java/io/github/hapjava/impl/http/impl/BinaryHandler.java b/src/main/java/io/github/hapjava/server/impl/http/impl/BinaryHandler.java similarity index 94% rename from src/main/java/io/github/hapjava/impl/http/impl/BinaryHandler.java rename to src/main/java/io/github/hapjava/server/impl/http/impl/BinaryHandler.java index cb5335ee0..5a2337707 100644 --- a/src/main/java/io/github/hapjava/impl/http/impl/BinaryHandler.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/BinaryHandler.java @@ -1,6 +1,6 @@ -package io.github.hapjava.impl.http.impl; +package io.github.hapjava.server.impl.http.impl; -import io.github.hapjava.impl.http.HomekitClientConnection; +import io.github.hapjava.server.impl.http.HomekitClientConnection; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; diff --git a/src/main/java/io/github/hapjava/impl/http/impl/DefaultHttpRequestImpl.java b/src/main/java/io/github/hapjava/server/impl/http/impl/DefaultHttpRequestImpl.java similarity index 85% rename from src/main/java/io/github/hapjava/impl/http/impl/DefaultHttpRequestImpl.java rename to src/main/java/io/github/hapjava/server/impl/http/impl/DefaultHttpRequestImpl.java index 1fe33c48f..48a3ceb35 100644 --- a/src/main/java/io/github/hapjava/impl/http/impl/DefaultHttpRequestImpl.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/DefaultHttpRequestImpl.java @@ -1,7 +1,7 @@ -package io.github.hapjava.impl.http.impl; +package io.github.hapjava.server.impl.http.impl; -import io.github.hapjava.impl.http.HttpMethod; -import io.github.hapjava.impl.http.HttpRequest; +import io.github.hapjava.server.impl.http.HttpMethod; +import io.github.hapjava.server.impl.http.HttpRequest; class DefaultHttpRequestImpl implements HttpRequest { diff --git a/src/main/java/io/github/hapjava/impl/http/impl/FullRequestHttpRequestImpl.java b/src/main/java/io/github/hapjava/server/impl/http/impl/FullRequestHttpRequestImpl.java similarity index 90% rename from src/main/java/io/github/hapjava/impl/http/impl/FullRequestHttpRequestImpl.java rename to src/main/java/io/github/hapjava/server/impl/http/impl/FullRequestHttpRequestImpl.java index ee82d15f4..088ec9832 100644 --- a/src/main/java/io/github/hapjava/impl/http/impl/FullRequestHttpRequestImpl.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/FullRequestHttpRequestImpl.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.http.impl; +package io.github.hapjava.server.impl.http.impl; import io.netty.handler.codec.http.FullHttpRequest; diff --git a/src/main/java/io/github/hapjava/impl/http/impl/HomekitHttpServer.java b/src/main/java/io/github/hapjava/server/impl/http/impl/HomekitHttpServer.java similarity index 85% rename from src/main/java/io/github/hapjava/impl/http/impl/HomekitHttpServer.java rename to src/main/java/io/github/hapjava/server/impl/http/impl/HomekitHttpServer.java index 3a8773132..085d632cf 100644 --- a/src/main/java/io/github/hapjava/impl/http/impl/HomekitHttpServer.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/HomekitHttpServer.java @@ -1,7 +1,7 @@ -package io.github.hapjava.impl.http.impl; +package io.github.hapjava.server.impl.http.impl; -import io.github.hapjava.impl.HomekitWebHandler; -import io.github.hapjava.impl.http.HomekitClientConnectionFactory; +import io.github.hapjava.server.HomekitWebHandler; +import io.github.hapjava.server.impl.http.HomekitClientConnectionFactory; import java.net.InetAddress; import java.util.concurrent.CompletableFuture; diff --git a/src/main/java/io/github/hapjava/impl/http/impl/HttpResponseEncoderAggregate.java b/src/main/java/io/github/hapjava/server/impl/http/impl/HttpResponseEncoderAggregate.java similarity index 92% rename from src/main/java/io/github/hapjava/impl/http/impl/HttpResponseEncoderAggregate.java rename to src/main/java/io/github/hapjava/server/impl/http/impl/HttpResponseEncoderAggregate.java index 23ac740fa..96aee2fe7 100644 --- a/src/main/java/io/github/hapjava/impl/http/impl/HttpResponseEncoderAggregate.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/HttpResponseEncoderAggregate.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.http.impl; +package io.github.hapjava.server.impl.http.impl; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; diff --git a/src/main/java/io/github/hapjava/impl/http/impl/LoggingHandler.java b/src/main/java/io/github/hapjava/server/impl/http/impl/LoggingHandler.java similarity index 97% rename from src/main/java/io/github/hapjava/impl/http/impl/LoggingHandler.java rename to src/main/java/io/github/hapjava/server/impl/http/impl/LoggingHandler.java index 1bae4fa9f..5b8e52368 100644 --- a/src/main/java/io/github/hapjava/impl/http/impl/LoggingHandler.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/LoggingHandler.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.http.impl; +package io.github.hapjava.server.impl.http.impl; import io.netty.buffer.ByteBuf; import io.netty.channel.*; diff --git a/src/main/java/io/github/hapjava/impl/http/impl/NettyHomekitHttpService.java b/src/main/java/io/github/hapjava/server/impl/http/impl/NettyHomekitHttpService.java similarity index 96% rename from src/main/java/io/github/hapjava/impl/http/impl/NettyHomekitHttpService.java rename to src/main/java/io/github/hapjava/server/impl/http/impl/NettyHomekitHttpService.java index 2bc427d54..329b803dc 100644 --- a/src/main/java/io/github/hapjava/impl/http/impl/NettyHomekitHttpService.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/NettyHomekitHttpService.java @@ -1,6 +1,6 @@ -package io.github.hapjava.impl.http.impl; +package io.github.hapjava.server.impl.http.impl; -import io.github.hapjava.impl.http.HomekitClientConnectionFactory; +import io.github.hapjava.server.impl.http.HomekitClientConnectionFactory; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.group.ChannelGroup; diff --git a/src/main/java/io/github/hapjava/impl/http/impl/NettyResponseUtil.java b/src/main/java/io/github/hapjava/server/impl/http/impl/NettyResponseUtil.java similarity index 92% rename from src/main/java/io/github/hapjava/impl/http/impl/NettyResponseUtil.java rename to src/main/java/io/github/hapjava/server/impl/http/impl/NettyResponseUtil.java index c9b141771..fba52eef5 100644 --- a/src/main/java/io/github/hapjava/impl/http/impl/NettyResponseUtil.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/NettyResponseUtil.java @@ -1,6 +1,6 @@ -package io.github.hapjava.impl.http.impl; +package io.github.hapjava.server.impl.http.impl; -import io.github.hapjava.impl.http.HttpResponse; +import io.github.hapjava.server.impl.http.HttpResponse; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse; diff --git a/src/main/java/io/github/hapjava/impl/http/impl/ServerInitializer.java b/src/main/java/io/github/hapjava/server/impl/http/impl/ServerInitializer.java similarity index 92% rename from src/main/java/io/github/hapjava/impl/http/impl/ServerInitializer.java rename to src/main/java/io/github/hapjava/server/impl/http/impl/ServerInitializer.java index ed447a1af..3c914ffe7 100644 --- a/src/main/java/io/github/hapjava/impl/http/impl/ServerInitializer.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/ServerInitializer.java @@ -1,6 +1,6 @@ -package io.github.hapjava.impl.http.impl; +package io.github.hapjava.server.impl.http.impl; -import io.github.hapjava.impl.http.HomekitClientConnectionFactory; +import io.github.hapjava.server.impl.http.HomekitClientConnectionFactory; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.group.ChannelGroup; diff --git a/src/main/java/io/github/hapjava/impl/jmdns/JmdnsHomekitAdvertiser.java b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java similarity index 98% rename from src/main/java/io/github/hapjava/impl/jmdns/JmdnsHomekitAdvertiser.java rename to src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java index 062536708..8ba452906 100644 --- a/src/main/java/io/github/hapjava/impl/jmdns/JmdnsHomekitAdvertiser.java +++ b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.jmdns; +package io.github.hapjava.server.impl.jmdns; import java.io.IOException; import java.net.InetAddress; diff --git a/src/main/java/io/github/hapjava/impl/json/AccessoryController.java b/src/main/java/io/github/hapjava/server/impl/json/AccessoryController.java similarity index 93% rename from src/main/java/io/github/hapjava/impl/json/AccessoryController.java rename to src/main/java/io/github/hapjava/server/impl/json/AccessoryController.java index 66c46bdcf..fb53c6315 100644 --- a/src/main/java/io/github/hapjava/impl/json/AccessoryController.java +++ b/src/main/java/io/github/hapjava/server/impl/json/AccessoryController.java @@ -1,10 +1,10 @@ -package io.github.hapjava.impl.json; +package io.github.hapjava.server.impl.json; -import io.github.hapjava.HomekitAccessory; -import io.github.hapjava.Service; +import io.github.hapjava.accessories.HomekitAccessory; import io.github.hapjava.characteristics.Characteristic; -import io.github.hapjava.impl.HomekitRegistry; -import io.github.hapjava.impl.http.HttpResponse; +import io.github.hapjava.server.impl.HomekitRegistry; +import io.github.hapjava.server.impl.http.HttpResponse; +import io.github.hapjava.services.Service; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Collection; diff --git a/src/main/java/io/github/hapjava/impl/json/CharacteristicsController.java b/src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java similarity index 90% rename from src/main/java/io/github/hapjava/impl/json/CharacteristicsController.java rename to src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java index 8a85956e7..484a4ea30 100644 --- a/src/main/java/io/github/hapjava/impl/json/CharacteristicsController.java +++ b/src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java @@ -1,13 +1,13 @@ -package io.github.hapjava.impl.json; +package io.github.hapjava.server.impl.json; import io.github.hapjava.characteristics.Characteristic; import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.impl.HomekitRegistry; -import io.github.hapjava.impl.connections.SubscriptionManager; -import io.github.hapjava.impl.http.HomekitClientConnection; -import io.github.hapjava.impl.http.HttpRequest; -import io.github.hapjava.impl.http.HttpResponse; -import io.github.hapjava.impl.responses.NotFoundResponse; +import io.github.hapjava.server.impl.HomekitRegistry; +import io.github.hapjava.server.impl.connections.SubscriptionManager; +import io.github.hapjava.server.impl.http.HomekitClientConnection; +import io.github.hapjava.server.impl.http.HttpRequest; +import io.github.hapjava.server.impl.http.HttpResponse; +import io.github.hapjava.server.impl.responses.NotFoundResponse; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Map; diff --git a/src/main/java/io/github/hapjava/impl/json/EventController.java b/src/main/java/io/github/hapjava/server/impl/json/EventController.java similarity index 91% rename from src/main/java/io/github/hapjava/impl/json/EventController.java rename to src/main/java/io/github/hapjava/server/impl/json/EventController.java index 9322f2507..80be265ae 100644 --- a/src/main/java/io/github/hapjava/impl/json/EventController.java +++ b/src/main/java/io/github/hapjava/server/impl/json/EventController.java @@ -1,8 +1,8 @@ -package io.github.hapjava.impl.json; +package io.github.hapjava.server.impl.json; import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.impl.connections.PendingNotification; -import io.github.hapjava.impl.http.HttpResponse; +import io.github.hapjava.server.impl.connections.PendingNotification; +import io.github.hapjava.server.impl.http.HttpResponse; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import javax.json.Json; diff --git a/src/main/java/io/github/hapjava/impl/json/EventResponse.java b/src/main/java/io/github/hapjava/server/impl/json/EventResponse.java similarity index 82% rename from src/main/java/io/github/hapjava/impl/json/EventResponse.java rename to src/main/java/io/github/hapjava/server/impl/json/EventResponse.java index faa4478b3..113582c5b 100644 --- a/src/main/java/io/github/hapjava/impl/json/EventResponse.java +++ b/src/main/java/io/github/hapjava/server/impl/json/EventResponse.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.json; +package io.github.hapjava.server.impl.json; public class EventResponse extends HapJsonResponse { diff --git a/src/main/java/io/github/hapjava/impl/json/HapJsonNoContentResponse.java b/src/main/java/io/github/hapjava/server/impl/json/HapJsonNoContentResponse.java similarity index 81% rename from src/main/java/io/github/hapjava/impl/json/HapJsonNoContentResponse.java rename to src/main/java/io/github/hapjava/server/impl/json/HapJsonNoContentResponse.java index fbb5066bb..be01ebbe3 100644 --- a/src/main/java/io/github/hapjava/impl/json/HapJsonNoContentResponse.java +++ b/src/main/java/io/github/hapjava/server/impl/json/HapJsonNoContentResponse.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.json; +package io.github.hapjava.server.impl.json; class HapJsonNoContentResponse extends HapJsonResponse { diff --git a/src/main/java/io/github/hapjava/impl/json/HapJsonResponse.java b/src/main/java/io/github/hapjava/server/impl/json/HapJsonResponse.java similarity index 84% rename from src/main/java/io/github/hapjava/impl/json/HapJsonResponse.java rename to src/main/java/io/github/hapjava/server/impl/json/HapJsonResponse.java index 1696a7f0a..dbbe7cada 100644 --- a/src/main/java/io/github/hapjava/impl/json/HapJsonResponse.java +++ b/src/main/java/io/github/hapjava/server/impl/json/HapJsonResponse.java @@ -1,6 +1,6 @@ -package io.github.hapjava.impl.json; +package io.github.hapjava.server.impl.json; -import io.github.hapjava.impl.responses.OkResponse; +import io.github.hapjava.server.impl.responses.OkResponse; import java.util.Collections; import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/io/github/hapjava/impl/pairing/ByteUtils.java b/src/main/java/io/github/hapjava/server/impl/pairing/ByteUtils.java similarity index 95% rename from src/main/java/io/github/hapjava/impl/pairing/ByteUtils.java rename to src/main/java/io/github/hapjava/server/impl/pairing/ByteUtils.java index 8f903811b..6516e2e29 100644 --- a/src/main/java/io/github/hapjava/impl/pairing/ByteUtils.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/ByteUtils.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.pairing; +package io.github.hapjava.server.impl.pairing; import java.io.IOException; import java.io.InputStream; diff --git a/src/main/java/io/github/hapjava/impl/pairing/ClientEvidenceRoutineImpl.java b/src/main/java/io/github/hapjava/server/impl/pairing/ClientEvidenceRoutineImpl.java similarity index 97% rename from src/main/java/io/github/hapjava/impl/pairing/ClientEvidenceRoutineImpl.java rename to src/main/java/io/github/hapjava/server/impl/pairing/ClientEvidenceRoutineImpl.java index db464b8fb..16c514707 100644 --- a/src/main/java/io/github/hapjava/impl/pairing/ClientEvidenceRoutineImpl.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/ClientEvidenceRoutineImpl.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.pairing; +package io.github.hapjava.server.impl.pairing; import com.nimbusds.srp6.*; import java.math.BigInteger; diff --git a/src/main/java/io/github/hapjava/impl/pairing/FinalPairHandler.java b/src/main/java/io/github/hapjava/server/impl/pairing/FinalPairHandler.java similarity index 85% rename from src/main/java/io/github/hapjava/impl/pairing/FinalPairHandler.java rename to src/main/java/io/github/hapjava/server/impl/pairing/FinalPairHandler.java index 29b8b88a3..e5d9e371c 100644 --- a/src/main/java/io/github/hapjava/impl/pairing/FinalPairHandler.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/FinalPairHandler.java @@ -1,11 +1,14 @@ -package io.github.hapjava.impl.pairing; - -import io.github.hapjava.HomekitAuthInfo; -import io.github.hapjava.impl.crypto.*; -import io.github.hapjava.impl.http.HttpResponse; -import io.github.hapjava.impl.pairing.PairSetupRequest.Stage3Request; -import io.github.hapjava.impl.pairing.TypeLengthValueUtils.DecodeResult; -import io.github.hapjava.impl.pairing.TypeLengthValueUtils.Encoder; +package io.github.hapjava.server.impl.pairing; + +import io.github.hapjava.server.HomekitAuthInfo; +import io.github.hapjava.server.impl.crypto.ChachaDecoder; +import io.github.hapjava.server.impl.crypto.ChachaEncoder; +import io.github.hapjava.server.impl.crypto.EdsaSigner; +import io.github.hapjava.server.impl.crypto.EdsaVerifier; +import io.github.hapjava.server.impl.http.HttpResponse; +import io.github.hapjava.server.impl.pairing.PairSetupRequest.Stage3Request; +import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.DecodeResult; +import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.Encoder; import java.nio.charset.StandardCharsets; import org.bouncycastle.crypto.digests.SHA512Digest; import org.bouncycastle.crypto.generators.HKDFBytesGenerator; diff --git a/src/main/java/io/github/hapjava/impl/pairing/HomekitSRP6Routines.java b/src/main/java/io/github/hapjava/server/impl/pairing/HomekitSRP6Routines.java similarity index 97% rename from src/main/java/io/github/hapjava/impl/pairing/HomekitSRP6Routines.java rename to src/main/java/io/github/hapjava/server/impl/pairing/HomekitSRP6Routines.java index d21adfe8b..5e44117f3 100644 --- a/src/main/java/io/github/hapjava/impl/pairing/HomekitSRP6Routines.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/HomekitSRP6Routines.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.pairing; +package io.github.hapjava.server.impl.pairing; import java.math.BigInteger; import java.security.SecureRandom; diff --git a/src/main/java/io/github/hapjava/impl/pairing/HomekitSRP6ServerSession.java b/src/main/java/io/github/hapjava/server/impl/pairing/HomekitSRP6ServerSession.java similarity index 99% rename from src/main/java/io/github/hapjava/impl/pairing/HomekitSRP6ServerSession.java rename to src/main/java/io/github/hapjava/server/impl/pairing/HomekitSRP6ServerSession.java index a459a5c54..0ec311048 100644 --- a/src/main/java/io/github/hapjava/impl/pairing/HomekitSRP6ServerSession.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/HomekitSRP6ServerSession.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.pairing; +package io.github.hapjava.server.impl.pairing; import com.nimbusds.srp6.SRP6ClientEvidenceContext; import com.nimbusds.srp6.SRP6CryptoParams; diff --git a/src/main/java/io/github/hapjava/impl/pairing/MessageType.java b/src/main/java/io/github/hapjava/server/impl/pairing/MessageType.java similarity index 87% rename from src/main/java/io/github/hapjava/impl/pairing/MessageType.java rename to src/main/java/io/github/hapjava/server/impl/pairing/MessageType.java index 2750264a3..ffdd0ba33 100644 --- a/src/main/java/io/github/hapjava/impl/pairing/MessageType.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/MessageType.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.pairing; +package io.github.hapjava.server.impl.pairing; public enum MessageType { METHOD(0), diff --git a/src/main/java/io/github/hapjava/impl/pairing/PairSetupRequest.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairSetupRequest.java similarity index 94% rename from src/main/java/io/github/hapjava/impl/pairing/PairSetupRequest.java rename to src/main/java/io/github/hapjava/server/impl/pairing/PairSetupRequest.java index a7d7a0445..bd0da4c81 100644 --- a/src/main/java/io/github/hapjava/impl/pairing/PairSetupRequest.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairSetupRequest.java @@ -1,6 +1,6 @@ -package io.github.hapjava.impl.pairing; +package io.github.hapjava.server.impl.pairing; -import io.github.hapjava.impl.pairing.TypeLengthValueUtils.DecodeResult; +import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.DecodeResult; import java.math.BigInteger; abstract class PairSetupRequest { diff --git a/src/main/java/io/github/hapjava/impl/pairing/PairVerificationManager.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairVerificationManager.java similarity index 84% rename from src/main/java/io/github/hapjava/impl/pairing/PairVerificationManager.java rename to src/main/java/io/github/hapjava/server/impl/pairing/PairVerificationManager.java index 4c5546745..6630c82e8 100644 --- a/src/main/java/io/github/hapjava/impl/pairing/PairVerificationManager.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairVerificationManager.java @@ -1,17 +1,20 @@ -package io.github.hapjava.impl.pairing; +package io.github.hapjava.server.impl.pairing; import djb.Curve25519; -import io.github.hapjava.HomekitAuthInfo; -import io.github.hapjava.impl.HomekitRegistry; -import io.github.hapjava.impl.crypto.*; -import io.github.hapjava.impl.http.HttpRequest; -import io.github.hapjava.impl.http.HttpResponse; -import io.github.hapjava.impl.pairing.PairVerificationRequest.Stage1Request; -import io.github.hapjava.impl.pairing.PairVerificationRequest.Stage2Request; -import io.github.hapjava.impl.pairing.TypeLengthValueUtils.DecodeResult; -import io.github.hapjava.impl.pairing.TypeLengthValueUtils.Encoder; -import io.github.hapjava.impl.responses.NotFoundResponse; -import io.github.hapjava.impl.responses.OkResponse; +import io.github.hapjava.server.HomekitAuthInfo; +import io.github.hapjava.server.impl.HomekitRegistry; +import io.github.hapjava.server.impl.crypto.ChachaDecoder; +import io.github.hapjava.server.impl.crypto.ChachaEncoder; +import io.github.hapjava.server.impl.crypto.EdsaSigner; +import io.github.hapjava.server.impl.crypto.EdsaVerifier; +import io.github.hapjava.server.impl.http.HttpRequest; +import io.github.hapjava.server.impl.http.HttpResponse; +import io.github.hapjava.server.impl.pairing.PairVerificationRequest.Stage1Request; +import io.github.hapjava.server.impl.pairing.PairVerificationRequest.Stage2Request; +import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.DecodeResult; +import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.Encoder; +import io.github.hapjava.server.impl.responses.NotFoundResponse; +import io.github.hapjava.server.impl.responses.OkResponse; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import org.bouncycastle.crypto.digests.SHA512Digest; diff --git a/src/main/java/io/github/hapjava/impl/pairing/PairVerificationRequest.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairVerificationRequest.java similarity index 92% rename from src/main/java/io/github/hapjava/impl/pairing/PairVerificationRequest.java rename to src/main/java/io/github/hapjava/server/impl/pairing/PairVerificationRequest.java index 1d4f8db4b..3bb6627d9 100644 --- a/src/main/java/io/github/hapjava/impl/pairing/PairVerificationRequest.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairVerificationRequest.java @@ -1,6 +1,6 @@ -package io.github.hapjava.impl.pairing; +package io.github.hapjava.server.impl.pairing; -import io.github.hapjava.impl.pairing.TypeLengthValueUtils.DecodeResult; +import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.DecodeResult; abstract class PairVerificationRequest { diff --git a/src/main/java/io/github/hapjava/impl/pairing/PairingManager.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairingManager.java similarity index 83% rename from src/main/java/io/github/hapjava/impl/pairing/PairingManager.java rename to src/main/java/io/github/hapjava/server/impl/pairing/PairingManager.java index 934181309..d4265a587 100644 --- a/src/main/java/io/github/hapjava/impl/pairing/PairingManager.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairingManager.java @@ -1,11 +1,11 @@ -package io.github.hapjava.impl.pairing; +package io.github.hapjava.server.impl.pairing; -import io.github.hapjava.HomekitAuthInfo; -import io.github.hapjava.impl.HomekitRegistry; -import io.github.hapjava.impl.http.HttpRequest; -import io.github.hapjava.impl.http.HttpResponse; -import io.github.hapjava.impl.responses.NotFoundResponse; -import io.github.hapjava.impl.responses.UnauthorizedResponse; +import io.github.hapjava.server.HomekitAuthInfo; +import io.github.hapjava.server.impl.HomekitRegistry; +import io.github.hapjava.server.impl.http.HttpRequest; +import io.github.hapjava.server.impl.http.HttpResponse; +import io.github.hapjava.server.impl.responses.NotFoundResponse; +import io.github.hapjava.server.impl.responses.UnauthorizedResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/io/github/hapjava/impl/pairing/PairingResponse.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairingResponse.java similarity index 84% rename from src/main/java/io/github/hapjava/impl/pairing/PairingResponse.java rename to src/main/java/io/github/hapjava/server/impl/pairing/PairingResponse.java index 1291f66ee..af678a86c 100644 --- a/src/main/java/io/github/hapjava/impl/pairing/PairingResponse.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairingResponse.java @@ -1,6 +1,6 @@ -package io.github.hapjava.impl.pairing; +package io.github.hapjava.server.impl.pairing; -import io.github.hapjava.impl.responses.OkResponse; +import io.github.hapjava.server.impl.responses.OkResponse; import java.util.Collections; import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/io/github/hapjava/impl/pairing/PairingUpdateController.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairingUpdateController.java similarity index 77% rename from src/main/java/io/github/hapjava/impl/pairing/PairingUpdateController.java rename to src/main/java/io/github/hapjava/server/impl/pairing/PairingUpdateController.java index 6eabd6992..3d52fe907 100644 --- a/src/main/java/io/github/hapjava/impl/pairing/PairingUpdateController.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairingUpdateController.java @@ -1,10 +1,10 @@ -package io.github.hapjava.impl.pairing; +package io.github.hapjava.server.impl.pairing; -import io.github.hapjava.HomekitAuthInfo; -import io.github.hapjava.impl.http.HttpRequest; -import io.github.hapjava.impl.http.HttpResponse; -import io.github.hapjava.impl.jmdns.JmdnsHomekitAdvertiser; -import io.github.hapjava.impl.pairing.TypeLengthValueUtils.DecodeResult; +import io.github.hapjava.server.HomekitAuthInfo; +import io.github.hapjava.server.impl.http.HttpRequest; +import io.github.hapjava.server.impl.http.HttpResponse; +import io.github.hapjava.server.impl.jmdns.JmdnsHomekitAdvertiser; +import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.DecodeResult; import java.io.IOException; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/io/github/hapjava/impl/pairing/ServerEvidenceRoutineImpl.java b/src/main/java/io/github/hapjava/server/impl/pairing/ServerEvidenceRoutineImpl.java similarity index 95% rename from src/main/java/io/github/hapjava/impl/pairing/ServerEvidenceRoutineImpl.java rename to src/main/java/io/github/hapjava/server/impl/pairing/ServerEvidenceRoutineImpl.java index ca61f8ed4..7cf7b3164 100644 --- a/src/main/java/io/github/hapjava/impl/pairing/ServerEvidenceRoutineImpl.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/ServerEvidenceRoutineImpl.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.pairing; +package io.github.hapjava.server.impl.pairing; import com.nimbusds.srp6.SRP6CryptoParams; import com.nimbusds.srp6.SRP6ServerEvidenceContext; diff --git a/src/main/java/io/github/hapjava/impl/pairing/SrpHandler.java b/src/main/java/io/github/hapjava/server/impl/pairing/SrpHandler.java similarity index 89% rename from src/main/java/io/github/hapjava/impl/pairing/SrpHandler.java rename to src/main/java/io/github/hapjava/server/impl/pairing/SrpHandler.java index 0e70f6f86..2743ea4f1 100644 --- a/src/main/java/io/github/hapjava/impl/pairing/SrpHandler.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/SrpHandler.java @@ -1,12 +1,12 @@ -package io.github.hapjava.impl.pairing; +package io.github.hapjava.server.impl.pairing; import com.nimbusds.srp6.*; -import io.github.hapjava.impl.http.HttpResponse; -import io.github.hapjava.impl.pairing.HomekitSRP6ServerSession.State; -import io.github.hapjava.impl.pairing.PairSetupRequest.Stage2Request; -import io.github.hapjava.impl.pairing.TypeLengthValueUtils.Encoder; -import io.github.hapjava.impl.responses.ConflictResponse; -import io.github.hapjava.impl.responses.NotFoundResponse; +import io.github.hapjava.server.impl.http.HttpResponse; +import io.github.hapjava.server.impl.pairing.HomekitSRP6ServerSession.State; +import io.github.hapjava.server.impl.pairing.PairSetupRequest.Stage2Request; +import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.Encoder; +import io.github.hapjava.server.impl.responses.ConflictResponse; +import io.github.hapjava.server.impl.responses.NotFoundResponse; import java.math.BigInteger; import java.security.MessageDigest; import java.util.Arrays; diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/Stage.java b/src/main/java/io/github/hapjava/server/impl/pairing/Stage.java new file mode 100644 index 000000000..19bac9a14 --- /dev/null +++ b/src/main/java/io/github/hapjava/server/impl/pairing/Stage.java @@ -0,0 +1,7 @@ +package io.github.hapjava.server.impl.pairing; + +public enum Stage { + ONE, + TWO, + THREE +} diff --git a/src/main/java/io/github/hapjava/impl/pairing/TypeLengthValueUtils.java b/src/main/java/io/github/hapjava/server/impl/pairing/TypeLengthValueUtils.java similarity index 98% rename from src/main/java/io/github/hapjava/impl/pairing/TypeLengthValueUtils.java rename to src/main/java/io/github/hapjava/server/impl/pairing/TypeLengthValueUtils.java index 2f03d0a0b..396829d34 100644 --- a/src/main/java/io/github/hapjava/impl/pairing/TypeLengthValueUtils.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/TypeLengthValueUtils.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.pairing; +package io.github.hapjava.server.impl.pairing; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; diff --git a/src/main/java/io/github/hapjava/impl/pairing/UpgradeResponse.java b/src/main/java/io/github/hapjava/server/impl/pairing/UpgradeResponse.java similarity index 91% rename from src/main/java/io/github/hapjava/impl/pairing/UpgradeResponse.java rename to src/main/java/io/github/hapjava/server/impl/pairing/UpgradeResponse.java index 9d4003729..87168aed1 100644 --- a/src/main/java/io/github/hapjava/impl/pairing/UpgradeResponse.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/UpgradeResponse.java @@ -1,4 +1,4 @@ -package io.github.hapjava.impl.pairing; +package io.github.hapjava.server.impl.pairing; import java.nio.ByteBuffer; diff --git a/src/main/java/io/github/hapjava/impl/responses/ConflictResponse.java b/src/main/java/io/github/hapjava/server/impl/responses/ConflictResponse.java similarity index 54% rename from src/main/java/io/github/hapjava/impl/responses/ConflictResponse.java rename to src/main/java/io/github/hapjava/server/impl/responses/ConflictResponse.java index 203b1a676..017fee857 100644 --- a/src/main/java/io/github/hapjava/impl/responses/ConflictResponse.java +++ b/src/main/java/io/github/hapjava/server/impl/responses/ConflictResponse.java @@ -1,6 +1,6 @@ -package io.github.hapjava.impl.responses; +package io.github.hapjava.server.impl.responses; -import io.github.hapjava.impl.http.HttpResponse; +import io.github.hapjava.server.impl.http.HttpResponse; public class ConflictResponse implements HttpResponse { diff --git a/src/main/java/io/github/hapjava/impl/responses/InternalServerErrorResponse.java b/src/main/java/io/github/hapjava/server/impl/responses/InternalServerErrorResponse.java similarity index 82% rename from src/main/java/io/github/hapjava/impl/responses/InternalServerErrorResponse.java rename to src/main/java/io/github/hapjava/server/impl/responses/InternalServerErrorResponse.java index 66fe5ec14..6be78e75a 100644 --- a/src/main/java/io/github/hapjava/impl/responses/InternalServerErrorResponse.java +++ b/src/main/java/io/github/hapjava/server/impl/responses/InternalServerErrorResponse.java @@ -1,6 +1,6 @@ -package io.github.hapjava.impl.responses; +package io.github.hapjava.server.impl.responses; -import io.github.hapjava.impl.http.HttpResponse; +import io.github.hapjava.server.impl.http.HttpResponse; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/io/github/hapjava/impl/responses/NotFoundResponse.java b/src/main/java/io/github/hapjava/server/impl/responses/NotFoundResponse.java similarity index 54% rename from src/main/java/io/github/hapjava/impl/responses/NotFoundResponse.java rename to src/main/java/io/github/hapjava/server/impl/responses/NotFoundResponse.java index 18ab53d74..aca74ad9d 100644 --- a/src/main/java/io/github/hapjava/impl/responses/NotFoundResponse.java +++ b/src/main/java/io/github/hapjava/server/impl/responses/NotFoundResponse.java @@ -1,6 +1,6 @@ -package io.github.hapjava.impl.responses; +package io.github.hapjava.server.impl.responses; -import io.github.hapjava.impl.http.HttpResponse; +import io.github.hapjava.server.impl.http.HttpResponse; public class NotFoundResponse implements HttpResponse { diff --git a/src/main/java/io/github/hapjava/impl/responses/OkResponse.java b/src/main/java/io/github/hapjava/server/impl/responses/OkResponse.java similarity index 75% rename from src/main/java/io/github/hapjava/impl/responses/OkResponse.java rename to src/main/java/io/github/hapjava/server/impl/responses/OkResponse.java index a48e5cee8..b2e62fe76 100644 --- a/src/main/java/io/github/hapjava/impl/responses/OkResponse.java +++ b/src/main/java/io/github/hapjava/server/impl/responses/OkResponse.java @@ -1,6 +1,6 @@ -package io.github.hapjava.impl.responses; +package io.github.hapjava.server.impl.responses; -import io.github.hapjava.impl.http.HttpResponse; +import io.github.hapjava.server.impl.http.HttpResponse; import java.nio.ByteBuffer; public class OkResponse implements HttpResponse { diff --git a/src/main/java/io/github/hapjava/impl/responses/UnauthorizedResponse.java b/src/main/java/io/github/hapjava/server/impl/responses/UnauthorizedResponse.java similarity index 54% rename from src/main/java/io/github/hapjava/impl/responses/UnauthorizedResponse.java rename to src/main/java/io/github/hapjava/server/impl/responses/UnauthorizedResponse.java index 19656ee8d..c40a03f08 100644 --- a/src/main/java/io/github/hapjava/impl/responses/UnauthorizedResponse.java +++ b/src/main/java/io/github/hapjava/server/impl/responses/UnauthorizedResponse.java @@ -1,6 +1,6 @@ -package io.github.hapjava.impl.responses; +package io.github.hapjava.server.impl.responses; -import io.github.hapjava.impl.http.HttpResponse; +import io.github.hapjava.server.impl.http.HttpResponse; public class UnauthorizedResponse implements HttpResponse { diff --git a/src/main/java/io/github/hapjava/Service.java b/src/main/java/io/github/hapjava/services/Service.java similarity index 96% rename from src/main/java/io/github/hapjava/Service.java rename to src/main/java/io/github/hapjava/services/Service.java index 45be2a1c6..c4ab23a82 100644 --- a/src/main/java/io/github/hapjava/Service.java +++ b/src/main/java/io/github/hapjava/services/Service.java @@ -1,4 +1,4 @@ -package io.github.hapjava; +package io.github.hapjava.services; import io.github.hapjava.characteristics.Characteristic; import java.util.List; diff --git a/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java b/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java new file mode 100644 index 000000000..76a00030d --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java @@ -0,0 +1,37 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.characteristics.Characteristic; +import io.github.hapjava.services.Service; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +abstract class AbstractServiceImpl implements Service { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + private final String type; + private final List characteristics = new LinkedList<>(); + + /** + * @param type unique UUID of the service. This information can be obtained from HomeKit Accessory + * Simulator. + */ + public AbstractServiceImpl(String type) { + this.type = type; + } + + @Override + public List getCharacteristics() { + return Collections.unmodifiableList(characteristics); + } + + @Override + public String getType() { + return type; + } + + protected void addCharacteristic(Characteristic characteristic) { + this.characteristics.add(characteristic); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java b/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java new file mode 100644 index 000000000..d5e71cd5c --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java @@ -0,0 +1,56 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.HomekitAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithHardwareRevision; +import io.github.hapjava.characteristics.impl.accessoryinformation.FirmwareRevisionCharacteristic; +import io.github.hapjava.characteristics.impl.accessoryinformation.HardwareRevisionCharacteristic; +import io.github.hapjava.characteristics.impl.accessoryinformation.IdentifyCharacteristic; +import io.github.hapjava.characteristics.impl.accessoryinformation.ManufacturerCharacteristic; +import io.github.hapjava.characteristics.impl.accessoryinformation.ModelCharacteristic; +import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.characteristics.impl.accessoryinformation.SerialNumberCharacteristic; + +/** Every accessory must expose a single instance of the Accessory Information service. */ +public class AccessoryInformationService extends AbstractServiceImpl { + + public AccessoryInformationService( + IdentifyCharacteristic identify, + ManufacturerCharacteristic manufacturer, + ModelCharacteristic model, + NameCharacteristic name, + SerialNumberCharacteristic serialNumber, + FirmwareRevisionCharacteristic firmwareRevision) { + super("0000003E-0000-1000-8000-0026BB765291"); + addCharacteristic(identify); + addCharacteristic(manufacturer); + addCharacteristic(model); + addCharacteristic(name); + addCharacteristic(serialNumber); + addCharacteristic(firmwareRevision); + } + + public AccessoryInformationService(HomekitAccessory accessory) { + this( + new IdentifyCharacteristic( + value -> { + if (value) { + accessory.identify(); + } + }), + new ManufacturerCharacteristic(accessory::getManufacturer), + new ModelCharacteristic(accessory::getModel), + new NameCharacteristic(accessory::getName), + new SerialNumberCharacteristic(accessory::getSerialNumber), + new FirmwareRevisionCharacteristic(accessory::getFirmwareRevision)); + + if (accessory instanceof AccessoryWithHardwareRevision) { + addOptionalCharacteristic( + new HardwareRevisionCharacteristic( + ((AccessoryWithHardwareRevision) accessory)::getHardwareRevision)); + } + } + + public void addOptionalCharacteristic(HardwareRevisionCharacteristic hardwareRevision) { + addCharacteristic(hardwareRevision); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/BatteryService.java b/src/main/java/io/github/hapjava/services/impl/BatteryService.java new file mode 100644 index 000000000..312853058 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/BatteryService.java @@ -0,0 +1,36 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.BatteryAccessory; +import io.github.hapjava.characteristics.impl.battery.BatteryLevelCharacteristic; +import io.github.hapjava.characteristics.impl.battery.ChargingStateCharacteristic; +import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; + +/** This service describes a battery service. */ +public class BatteryService extends AbstractServiceImpl { + + public BatteryService( + BatteryLevelCharacteristic batteryLevelCharacteristic, + ChargingStateCharacteristic chargingStateCharacteristic, + StatusLowBatteryCharacteristic statusLowBatteryCharacteristic) { + super("00000096-0000-1000-8000-0026BB765291"); + addCharacteristic(batteryLevelCharacteristic); + addCharacteristic(chargingStateCharacteristic); + addCharacteristic(statusLowBatteryCharacteristic); + } + + public BatteryService(BatteryAccessory batteryAccessory) { + this( + new BatteryLevelCharacteristic( + batteryAccessory::getBatteryLevel, + batteryAccessory::subscribeBatteryLevel, + batteryAccessory::unsubscribeBatteryLevel), + new ChargingStateCharacteristic( + batteryAccessory::getChargingState, + batteryAccessory::subscribeBatteryChargingState, + batteryAccessory::unsubscribeBatteryChargingState), + new StatusLowBatteryCharacteristic( + batteryAccessory::getLowBatteryState, + batteryAccessory::subscribeLowBatteryState, + batteryAccessory::unsubscribeLowBatteryState)); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java b/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java new file mode 100644 index 000000000..f0ef2823f --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java @@ -0,0 +1,66 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.CarbonDioxideSensorAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithCarbonDioxideLevel; +import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.carbondioxidesensor.CarbonDioxideDetectedCharacteristic; +import io.github.hapjava.characteristics.impl.carbondioxidesensor.CarbonDioxideLevelCharacteristic; +import io.github.hapjava.characteristics.impl.carbondioxidesensor.CarbonDioxidePeakLevelCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; + +/** This service describes a carbon dioxide sensor. */ +public class CarbonDioxideSensorService extends AbstractServiceImpl { + + public CarbonDioxideSensorService(CarbonDioxideDetectedCharacteristic carbonDioxideDetected) { + super("00000097-0000-1000-8000-0026BB765291"); + addCharacteristic(carbonDioxideDetected); + } + + public CarbonDioxideSensorService(CarbonDioxideSensorAccessory accessory) { + this( + new CarbonDioxideDetectedCharacteristic( + accessory::getCarbonDioxideDetectedState, + accessory::subscribeCarbonDioxideDetectedState, + accessory::unsubscribeCarbonDioxideDetectedState)); + + if (accessory instanceof AccessoryWithCarbonDioxideLevel) { + addOptionalCharacteristic( + new CarbonDioxideLevelCharacteristic( + ((AccessoryWithCarbonDioxideLevel) accessory)::getCarbonDioxideLevel, + ((AccessoryWithCarbonDioxideLevel) accessory)::subscribeCarbonDioxideLevel, + ((AccessoryWithCarbonDioxideLevel) accessory)::unsubscribeCarbonDioxideLevel)); + addOptionalCharacteristic( + new CarbonDioxidePeakLevelCharacteristic( + ((AccessoryWithCarbonDioxideLevel) accessory)::getCarbonDioxidePeakLevel, + ((AccessoryWithCarbonDioxideLevel) accessory)::subscribeCarbonDioxidePeakLevel, + ((AccessoryWithCarbonDioxideLevel) accessory)::unsubscribeCarbonDioxidePeakLevel)); + } + } + + public void addOptionalCharacteristic(StatusActiveCharacteristic statusActive) { + addCharacteristic(statusActive); + } + + public void addOptionalCharacteristic(StatusFaultCharacteristic statusFault) { + addCharacteristic(statusFault); + } + + public void addOptionalCharacteristic(StatusTamperedCharacteristic statusTampered) { + addCharacteristic(statusTampered); + } + + public void addOptionalCharacteristic(StatusLowBatteryCharacteristic statusLowBattery) { + addCharacteristic(statusLowBattery); + } + + public void addOptionalCharacteristic(CarbonDioxideLevelCharacteristic carbonMonoxideLevel) { + addCharacteristic(carbonMonoxideLevel); + } + + public void addOptionalCharacteristic( + CarbonDioxidePeakLevelCharacteristic carbonDioxidePeakLevel) { + addCharacteristic(carbonDioxidePeakLevel); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/CarbonMonoxideSensorService.java b/src/main/java/io/github/hapjava/services/impl/CarbonMonoxideSensorService.java new file mode 100644 index 000000000..1df903b4d --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/CarbonMonoxideSensorService.java @@ -0,0 +1,52 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.CarbonMonoxideSensorAccessory; +import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.carbonmonoxidesensor.CarbonMonoxideDetectedCharacteristic; +import io.github.hapjava.characteristics.impl.carbonmonoxidesensor.CarbonMonoxideLevelCharacteristic; +import io.github.hapjava.characteristics.impl.carbonmonoxidesensor.CarbonMonoxidePeakLevelCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; + +/** This service describes a carbon monoxide sensor. */ +public class CarbonMonoxideSensorService extends AbstractServiceImpl { + + public CarbonMonoxideSensorService(CarbonMonoxideDetectedCharacteristic carbonMonoxideDetected) { + super("0000007F-0000-1000-8000-0026BB765291"); + addCharacteristic(carbonMonoxideDetected); + } + + public CarbonMonoxideSensorService(CarbonMonoxideSensorAccessory carbonMonoxideSensor) { + this( + new CarbonMonoxideDetectedCharacteristic( + carbonMonoxideSensor::getCarbonMonoxideDetectedState, + carbonMonoxideSensor::subscribeCarbonMonoxideDetectedState, + carbonMonoxideSensor::unsubscribeCarbonMonoxideDetectedState)); + } + + public void addOptionalCharacteristic(StatusActiveCharacteristic statusActive) { + addCharacteristic(statusActive); + } + + public void addOptionalCharacteristic(StatusFaultCharacteristic statusFault) { + addCharacteristic(statusFault); + } + + public void addOptionalCharacteristic(StatusTamperedCharacteristic statusTampered) { + addCharacteristic(statusTampered); + } + + public void addOptionalCharacteristic(StatusLowBatteryCharacteristic statusLowBattery) { + addCharacteristic(statusLowBattery); + } + + public void addOptionalCharacteristic(CarbonMonoxideLevelCharacteristic carbonMonoxideLevel) { + addCharacteristic(carbonMonoxideLevel); + } + + public void addOptionalCharacteristic( + CarbonMonoxidePeakLevelCharacteristic carbonMonoxidePeakLevel) { + addCharacteristic(carbonMonoxidePeakLevel); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/ContactSensorService.java b/src/main/java/io/github/hapjava/services/impl/ContactSensorService.java new file mode 100644 index 000000000..47bcbfbcf --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/ContactSensorService.java @@ -0,0 +1,41 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.ContactSensorAccessory; +import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; +import io.github.hapjava.characteristics.impl.contactsensor.ContactSensorStateCharacteristic; + +/** This service describes a Contact Sensor. */ +public class ContactSensorService extends AbstractServiceImpl { + + public ContactSensorService(ContactSensorStateCharacteristic contactSensorState) { + super("00000080-0000-1000-8000-0026BB765291"); + addCharacteristic(contactSensorState); + } + + public ContactSensorService(ContactSensorAccessory contactSensor) { + this( + new ContactSensorStateCharacteristic( + contactSensor::getCurrentState, + contactSensor::subscribeContactState, + contactSensor::unsubscribeContactState)); + } + + public void addOptionalCharacteristic(StatusActiveCharacteristic statusActive) { + addCharacteristic(statusActive); + } + + public void addOptionalCharacteristic(StatusFaultCharacteristic statusFault) { + addCharacteristic(statusFault); + } + + public void addOptionalCharacteristic(StatusTamperedCharacteristic statusTampered) { + addCharacteristic(statusTampered); + } + + public void addOptionalCharacteristic(StatusLowBatteryCharacteristic statusLowBattery) { + addCharacteristic(statusLowBattery); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/DoorService.java b/src/main/java/io/github/hapjava/services/impl/DoorService.java new file mode 100644 index 000000000..8dcb224a6 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/DoorService.java @@ -0,0 +1,46 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.DoorAccessory; +import io.github.hapjava.characteristics.impl.common.ObstructionDetectedCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.CurrentPositionCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.HoldPositionCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.PositionStateCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.TargetPositionCharacteristic; + +/** This service describes a motorized door. */ +public class DoorService extends AbstractServiceImpl { + + public DoorService( + CurrentPositionCharacteristic currentPositionCharacteristic, + TargetPositionCharacteristic targetPositionCharacteristic, + PositionStateCharacteristic positionStateCharacteristic) { + super("00000081-0000-1000-8000-0026BB765291"); + addCharacteristic(currentPositionCharacteristic); + addCharacteristic(targetPositionCharacteristic); + addCharacteristic(positionStateCharacteristic); + } + + public DoorService(DoorAccessory door) { + this( + new CurrentPositionCharacteristic( + door::getCurrentPosition, + door::subscribeCurrentPosition, + door::unsubscribeCurrentPosition), + new TargetPositionCharacteristic( + door::getTargetPosition, + door::setTargetPosition, + door::subscribeTargetPosition, + door::unsubscribeTargetPosition), + new PositionStateCharacteristic( + door::getPositionState, door::subscribePositionState, door::unsubscribePositionState)); + } + + public void addOptionalCharacteristic(HoldPositionCharacteristic holdPositionCharacteristic) { + addCharacteristic(holdPositionCharacteristic); + } + + public void addOptionalCharacteristic( + ObstructionDetectedCharacteristic obstructionDetectedCharacteristic) { + addCharacteristic(obstructionDetectedCharacteristic); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/FanService.java b/src/main/java/io/github/hapjava/services/impl/FanService.java new file mode 100644 index 000000000..4fc8d8660 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/FanService.java @@ -0,0 +1,53 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.FanAccessory; +import io.github.hapjava.characteristics.impl.common.ActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.ActiveEnum; +import io.github.hapjava.characteristics.impl.fan.CurrentFanStateCharacteristic; +import io.github.hapjava.characteristics.impl.fan.LockPhysicalControlsCharacteristic; +import io.github.hapjava.characteristics.impl.fan.RotationDirectionCharacteristic; +import io.github.hapjava.characteristics.impl.fan.RotationSpeedCharacteristic; +import io.github.hapjava.characteristics.impl.fan.SwingModeCharacteristic; +import io.github.hapjava.characteristics.impl.fan.TargetFanStateCharacteristic; + +/** This service describes a fan. */ +public class FanService extends AbstractServiceImpl { + + public FanService(ActiveCharacteristic active) { + super("000000B7-0000-1000-8000-0026BB765291"); + addCharacteristic(active); + } + + public FanService(FanAccessory fan) { + this( + new ActiveCharacteristic( + () -> fan.isActive().thenApply(s -> s ? ActiveEnum.ACTIVE : ActiveEnum.INACTIVE), + (v) -> fan.setActive(v == ActiveEnum.ACTIVE), + fan::subscribeActive, + fan::unsubscribeActive)); + } + + public void addOptionalCharacteristic(CurrentFanStateCharacteristic state) { + addCharacteristic(state); + } + + public void addOptionalCharacteristic(TargetFanStateCharacteristic state) { + addCharacteristic(state); + } + + public void addOptionalCharacteristic(RotationDirectionCharacteristic direction) { + addCharacteristic(direction); + } + + public void addOptionalCharacteristic(RotationSpeedCharacteristic speed) { + addCharacteristic(speed); + } + + public void addOptionalCharacteristic(SwingModeCharacteristic mode) { + addCharacteristic(mode); + } + + public void addOptionalCharacteristic(LockPhysicalControlsCharacteristic lock) { + addCharacteristic(lock); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/GarageDoorOpenerService.java b/src/main/java/io/github/hapjava/services/impl/GarageDoorOpenerService.java new file mode 100644 index 000000000..329494a27 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/GarageDoorOpenerService.java @@ -0,0 +1,50 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.GarageDoorOpenerAccessory; +import io.github.hapjava.characteristics.impl.common.ObstructionDetectedCharacteristic; +import io.github.hapjava.characteristics.impl.garagedoor.CurrentDoorStateCharacteristic; +import io.github.hapjava.characteristics.impl.garagedoor.TargetDoorStateCharacteristic; +import io.github.hapjava.characteristics.impl.lock.LockCurrentStateCharacteristic; +import io.github.hapjava.characteristics.impl.lock.LockTargetStateCharacteristic; + +/** + * This service describes a garage door opener that controls a single door. If a garage has more + * than one door, then each door should have its own Garage Door Opener Service. + */ +public class GarageDoorOpenerService extends AbstractServiceImpl { + + public GarageDoorOpenerService( + CurrentDoorStateCharacteristic currentState, + TargetDoorStateCharacteristic targetState, + ObstructionDetectedCharacteristic obstruction) { + super("00000041-0000-1000-8000-0026BB765291"); + addCharacteristic(currentState); + addCharacteristic(targetState); + addCharacteristic(obstruction); + } + + public GarageDoorOpenerService(GarageDoorOpenerAccessory door) { + this( + new CurrentDoorStateCharacteristic( + door::getCurrentDoorState, + door::subscribeCurrentDoorState, + door::unsubscribeCurrentDoorState), + new TargetDoorStateCharacteristic( + door::getTargetDoorState, + door::setTargetDoorState, + door::subscribeTargetDoorState, + door::unsubscribeTargetDoorState), + new ObstructionDetectedCharacteristic( + door::getObstructionDetected, + door::subscribeObstructionDetected, + door::unsubscribeObstructionDetected)); + } + + public void addOptionalCharacteristic(LockCurrentStateCharacteristic lockState) { + addCharacteristic(lockState); + } + + public void addOptionalCharacteristic(LockTargetStateCharacteristic lockState) { + addCharacteristic(lockState); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/HumiditySensorService.java b/src/main/java/io/github/hapjava/services/impl/HumiditySensorService.java new file mode 100644 index 000000000..1e0de26a8 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/HumiditySensorService.java @@ -0,0 +1,42 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.HumiditySensorAccessory; +import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; +import io.github.hapjava.characteristics.impl.humiditysensor.CurrentRelativeHumidityCharacteristic; + +/** This service describes a humidity sensor. */ +public class HumiditySensorService extends AbstractServiceImpl { + + public HumiditySensorService( + CurrentRelativeHumidityCharacteristic currentRelativeHumidityCharacteristic) { + super("00000082-0000-1000-8000-0026BB765291"); + addCharacteristic(currentRelativeHumidityCharacteristic); + } + + public HumiditySensorService(HumiditySensorAccessory sensor) { + this( + new CurrentRelativeHumidityCharacteristic( + sensor::getCurrentRelativeHumidity, + sensor::subscribeCurrentRelativeHumidity, + sensor::unsubscribeCurrentRelativeHumidity)); + } + + public void addOptionalCharacteristic(StatusActiveCharacteristic statusActive) { + addCharacteristic(statusActive); + } + + public void addOptionalCharacteristic(StatusFaultCharacteristic statusFault) { + addCharacteristic(statusFault); + } + + public void addOptionalCharacteristic(StatusTamperedCharacteristic statusTampered) { + addCharacteristic(statusTampered); + } + + public void addOptionalCharacteristic(StatusLowBatteryCharacteristic statusLowBattery) { + addCharacteristic(statusLowBattery); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/LeakSensorService.java b/src/main/java/io/github/hapjava/services/impl/LeakSensorService.java new file mode 100644 index 000000000..be95d5e61 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/LeakSensorService.java @@ -0,0 +1,41 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.LeakSensorAccessory; +import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; +import io.github.hapjava.characteristics.impl.leaksensor.LeakDetectedStateCharacteristic; + +/** This service describes a leak sensor. */ +public class LeakSensorService extends AbstractServiceImpl { + + public LeakSensorService(LeakDetectedStateCharacteristic leakDetectedState) { + super("00000083-0000-1000-8000-0026BB765291"); + addCharacteristic(leakDetectedState); + } + + public LeakSensorService(LeakSensorAccessory leakSensor) { + this( + new LeakDetectedStateCharacteristic( + leakSensor::getLeakDetected, + leakSensor::subscribeLeakDetected, + leakSensor::unsubscribeLeakDetected)); + } + + public void addOptionalCharacteristic(StatusActiveCharacteristic statusActive) { + addCharacteristic(statusActive); + } + + public void addOptionalCharacteristic(StatusFaultCharacteristic statusFault) { + addCharacteristic(statusFault); + } + + public void addOptionalCharacteristic(StatusTamperedCharacteristic statusTampered) { + addCharacteristic(statusTampered); + } + + public void addOptionalCharacteristic(StatusLowBatteryCharacteristic statusLowBattery) { + addCharacteristic(statusLowBattery); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/LightSensorService.java b/src/main/java/io/github/hapjava/services/impl/LightSensorService.java new file mode 100644 index 000000000..04e0a8e9d --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/LightSensorService.java @@ -0,0 +1,46 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.LightSensorAccessory; +import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; +import io.github.hapjava.characteristics.impl.lightsensor.CurrentAmbientLightLevelCharacteristic; + +/** This service describes a light sensor. */ +public class LightSensorService extends AbstractServiceImpl { + + public LightSensorService(CurrentAmbientLightLevelCharacteristic lightLevel) { + super("00000084-0000-1000-8000-0026BB765291"); + addCharacteristic(lightLevel); + } + + public LightSensorService(LightSensorAccessory lightSensor) { + this( + new CurrentAmbientLightLevelCharacteristic( + lightSensor::getCurrentAmbientLightLevel, + lightSensor::subscribeCurrentAmbientLightLevel, + lightSensor::unsubscribeCurrentAmbientLightLevel)); + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(StatusActiveCharacteristic statusActive) { + addCharacteristic(statusActive); + } + + public void addOptionalCharacteristic(StatusFaultCharacteristic statusFault) { + addCharacteristic(statusFault); + } + + public void addOptionalCharacteristic(StatusTamperedCharacteristic statusTampered) { + addCharacteristic(statusTampered); + } + + public void addOptionalCharacteristic(StatusLowBatteryCharacteristic statusLowBattery) { + addCharacteristic(statusLowBattery); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/LightbulbService.java b/src/main/java/io/github/hapjava/services/impl/LightbulbService.java new file mode 100644 index 000000000..d2394826a --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/LightbulbService.java @@ -0,0 +1,47 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.LightbulbAccessory; +import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.characteristics.impl.common.OnCharacteristic; +import io.github.hapjava.characteristics.impl.lightbulb.BrightnessCharacteristic; +import io.github.hapjava.characteristics.impl.lightbulb.ColorTemperatureCharacteristic; +import io.github.hapjava.characteristics.impl.lightbulb.HueCharacteristic; +import io.github.hapjava.characteristics.impl.lightbulb.SaturationCharacteristic; + +/** This service describes a light bulb. */ +public class LightbulbService extends AbstractServiceImpl { + + public LightbulbService(OnCharacteristic onState) { + super("00000043-0000-1000-8000-0026BB765291"); + addCharacteristic(onState); + } + + public LightbulbService(LightbulbAccessory lightbulb) { + this( + new OnCharacteristic( + lightbulb::getLightbulbPowerState, + lightbulb::setLightbulbPowerState, + lightbulb::subscribeLightbulbPowerState, + lightbulb::unsubscribeLightbulbPowerState)); + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(HueCharacteristic hue) { + addCharacteristic(hue); + } + + public void addOptionalCharacteristic(BrightnessCharacteristic brightness) { + addCharacteristic(brightness); + } + + public void addOptionalCharacteristic(SaturationCharacteristic saturation) { + addCharacteristic(saturation); + } + + public void addOptionalCharacteristic(ColorTemperatureCharacteristic color) { + addCharacteristic(color); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/LockMechanismService.java b/src/main/java/io/github/hapjava/services/impl/LockMechanismService.java new file mode 100644 index 000000000..c319b7192 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/LockMechanismService.java @@ -0,0 +1,38 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.LockMechanismAccessory; +import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.characteristics.impl.lock.LockCurrentStateCharacteristic; +import io.github.hapjava.characteristics.impl.lock.LockTargetStateCharacteristic; + +/** + * The HomeKit Lock Mechanism Service is designed to expose and control the physical lock mechanism + * on a device. + */ +public class LockMechanismService extends AbstractServiceImpl { + + public LockMechanismService( + LockCurrentStateCharacteristic lockCurrentState, + LockTargetStateCharacteristic lockTargetState) { + super("00000045-0000-1000-8000-0026BB765291"); + addCharacteristic(lockCurrentState); + addCharacteristic(lockTargetState); + } + + public LockMechanismService(LockMechanismAccessory lockMechanism) { + this( + new LockCurrentStateCharacteristic( + lockMechanism::getLockCurrentState, + lockMechanism::subscribeLockCurrentState, + lockMechanism::unsubscribeLockCurrentState), + new LockTargetStateCharacteristic( + lockMechanism::getLockTargetState, + lockMechanism::setLockTargetState, + lockMechanism::subscribeLockTargetState, + lockMechanism::unsubscribeLockTargetState)); + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/MotionSensorService.java b/src/main/java/io/github/hapjava/services/impl/MotionSensorService.java new file mode 100644 index 000000000..5d5cd5460 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/MotionSensorService.java @@ -0,0 +1,46 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.MotionSensorAccessory; +import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; +import io.github.hapjava.characteristics.impl.motionsensor.MotionDetectedCharacteristic; + +/** This service describes a motion sensor. */ +public class MotionSensorService extends AbstractServiceImpl { + + public MotionSensorService(MotionDetectedCharacteristic motionDetectedCharacteristic) { + super("00000085-0000-1000-8000-0026BB765291"); + addCharacteristic(motionDetectedCharacteristic); + } + + public MotionSensorService(MotionSensorAccessory motionSensor) { + this( + new MotionDetectedCharacteristic( + motionSensor::getMotionDetected, + motionSensor::subscribeMotionDetected, + motionSensor::unsubscribeMotionDetected)); + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(StatusActiveCharacteristic statusActive) { + addCharacteristic(statusActive); + } + + public void addOptionalCharacteristic(StatusFaultCharacteristic statusFault) { + addCharacteristic(statusFault); + } + + public void addOptionalCharacteristic(StatusTamperedCharacteristic statusTampered) { + addCharacteristic(statusTampered); + } + + public void addOptionalCharacteristic(StatusLowBatteryCharacteristic statusLowBattery) { + addCharacteristic(statusLowBattery); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/OccupancySensorService.java b/src/main/java/io/github/hapjava/services/impl/OccupancySensorService.java new file mode 100644 index 000000000..22b832edc --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/OccupancySensorService.java @@ -0,0 +1,46 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.OccupancySensorAccessory; +import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; +import io.github.hapjava.characteristics.impl.occupancysensor.OccupancyDetectedCharacteristic; + +/** This service describes an occupancy sensor. */ +public class OccupancySensorService extends AbstractServiceImpl { + + public OccupancySensorService(OccupancyDetectedCharacteristic occupancyDetectedCharacteristic) { + super("00000086-0000-1000-8000-0026BB765291"); + addCharacteristic(occupancyDetectedCharacteristic); + } + + public OccupancySensorService(OccupancySensorAccessory occupancySensor) { + this( + new OccupancyDetectedCharacteristic( + occupancySensor::getOccupancyDetected, + occupancySensor::subscribeOccupancyDetected, + occupancySensor::unsubscribeOccupancyDetected)); + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(StatusActiveCharacteristic statusActive) { + addCharacteristic(statusActive); + } + + public void addOptionalCharacteristic(StatusFaultCharacteristic statusFault) { + addCharacteristic(statusFault); + } + + public void addOptionalCharacteristic(StatusTamperedCharacteristic statusTampered) { + addCharacteristic(statusTampered); + } + + public void addOptionalCharacteristic(StatusLowBatteryCharacteristic statusLowBattery) { + addCharacteristic(statusLowBattery); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/OutletService.java b/src/main/java/io/github/hapjava/services/impl/OutletService.java new file mode 100644 index 000000000..9367d8c84 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/OutletService.java @@ -0,0 +1,31 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.OutletAccessory; +import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.characteristics.impl.common.OnCharacteristic; +import io.github.hapjava.characteristics.impl.outlet.OutletInUseCharacteristic; + +/** This service describes a power outlet. */ +public class OutletService extends AbstractServiceImpl { + + public OutletService(OnCharacteristic onState, OutletInUseCharacteristic outletInUse) { + super("00000047-0000-1000-8000-0026BB765291"); + addCharacteristic(onState); + addCharacteristic(outletInUse); + } + + public OutletService(OutletAccessory outlet) { + this( + new OnCharacteristic( + outlet::getPowerState, + outlet::setPowerState, + outlet::subscribePowerState, + outlet::unsubscribePowerState), + new OutletInUseCharacteristic( + outlet::getOutletInUse, outlet::subscribeOutletInUse, outlet::unsubscribeOutletInUse)); + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/SecuritySystemService.java b/src/main/java/io/github/hapjava/services/impl/SecuritySystemService.java new file mode 100644 index 000000000..ed1e1cb05 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/SecuritySystemService.java @@ -0,0 +1,50 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.SecuritySystemAccessory; +import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; +import io.github.hapjava.characteristics.impl.securitysystem.CurrentSecuritySystemStateCharacteristic; +import io.github.hapjava.characteristics.impl.securitysystem.SecuritySystemAlarmTypeCharacteristic; +import io.github.hapjava.characteristics.impl.securitysystem.TargetSecuritySystemStateCharacteristic; + +/** This service describes a security system service. */ +public class SecuritySystemService extends AbstractServiceImpl { + + public SecuritySystemService( + CurrentSecuritySystemStateCharacteristic currentState, + TargetSecuritySystemStateCharacteristic targetState) { + super("0000007E-0000-1000-8000-0026BB765291"); + addCharacteristic(currentState); + addCharacteristic(targetState); + } + + public SecuritySystemService(SecuritySystemAccessory securitySystem) { + this( + new CurrentSecuritySystemStateCharacteristic( + securitySystem::getCurrentSecuritySystemState, + securitySystem::subscribeCurrentSecuritySystemState, + securitySystem::unsubscribeCurrentSecuritySystemState), + new TargetSecuritySystemStateCharacteristic( + securitySystem::getTargetSecuritySystemState, + securitySystem::setTargetSecuritySystemState, + securitySystem::subscribeTargetSecuritySystemState, + securitySystem::unsubscribeTargetSecuritySystemState)); + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(SecuritySystemAlarmTypeCharacteristic alermType) { + addCharacteristic(alermType); + } + + public void addOptionalCharacteristic(StatusFaultCharacteristic statusFault) { + addCharacteristic(statusFault); + } + + public void addOptionalCharacteristic(StatusTamperedCharacteristic statusTampered) { + addCharacteristic(statusTampered); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/SmokeSensorService.java b/src/main/java/io/github/hapjava/services/impl/SmokeSensorService.java new file mode 100644 index 000000000..fef1daf54 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/SmokeSensorService.java @@ -0,0 +1,46 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.SmokeSensorAccessory; +import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; +import io.github.hapjava.characteristics.impl.smokesensor.SmokeDetectedCharacteristic; + +/** This service describes a Smoke detector Sensor. */ +public class SmokeSensorService extends AbstractServiceImpl { + + public SmokeSensorService(SmokeDetectedCharacteristic smokeDetectedCharacteristic) { + super("00000087-0000-1000-8000-0026BB765291"); + addCharacteristic(smokeDetectedCharacteristic); + } + + public SmokeSensorService(SmokeSensorAccessory smokeSensor) { + this( + new SmokeDetectedCharacteristic( + smokeSensor::getSmokeDetectedState, + smokeSensor::subscribeSmokeDetectedState, + smokeSensor::unsubscribeSmokeDetectedState)); + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(StatusActiveCharacteristic statusActive) { + addCharacteristic(statusActive); + } + + public void addOptionalCharacteristic(StatusFaultCharacteristic statusFault) { + addCharacteristic(statusFault); + } + + public void addOptionalCharacteristic(StatusTamperedCharacteristic statusTampered) { + addCharacteristic(statusTampered); + } + + public void addOptionalCharacteristic(StatusLowBatteryCharacteristic statusLowBattery) { + addCharacteristic(statusLowBattery); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/SwitchService.java b/src/main/java/io/github/hapjava/services/impl/SwitchService.java new file mode 100644 index 000000000..a50f5d49e --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/SwitchService.java @@ -0,0 +1,27 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.SwitchAccessory; +import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.characteristics.impl.common.OnCharacteristic; + +/** This service describes a binary switch. */ +public class SwitchService extends AbstractServiceImpl { + + public SwitchService(OnCharacteristic onState) { + super("00000049-0000-1000-8000-0026BB765291"); + addCharacteristic(onState); + } + + public SwitchService(SwitchAccessory accessory) { + this( + new OnCharacteristic( + accessory::getSwitchState, + accessory::setSwitchState, + accessory::subscribeSwitchState, + accessory::unsubscribeSwitchState)); + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/TemperatureSensorService.java b/src/main/java/io/github/hapjava/services/impl/TemperatureSensorService.java new file mode 100644 index 000000000..6aef2e4ed --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/TemperatureSensorService.java @@ -0,0 +1,47 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.TemperatureSensorAccessory; +import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; +import io.github.hapjava.characteristics.impl.thermostat.CurrentTemperatureCharacteristic; + +/** This service describes a Temperature Sensor. */ +public class TemperatureSensorService extends AbstractServiceImpl { + + public TemperatureSensorService( + CurrentTemperatureCharacteristic currentTemperatureCharacteristic) { + super("0000008A-0000-1000-8000-0026BB765291"); + addCharacteristic(currentTemperatureCharacteristic); + } + + public TemperatureSensorService(TemperatureSensorAccessory temperatureSensor) { + this( + new CurrentTemperatureCharacteristic( + temperatureSensor::getCurrentTemperature, + temperatureSensor::subscribeCurrentTemperature, + temperatureSensor::unsubscribeCurrentTemperature)); + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(StatusActiveCharacteristic statusActive) { + addCharacteristic(statusActive); + } + + public void addOptionalCharacteristic(StatusFaultCharacteristic statusFault) { + addCharacteristic(statusFault); + } + + public void addOptionalCharacteristic(StatusTamperedCharacteristic statusTampered) { + addCharacteristic(statusTampered); + } + + public void addOptionalCharacteristic(StatusLowBatteryCharacteristic statusLowBattery) { + addCharacteristic(statusLowBattery); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/ThermostatService.java b/src/main/java/io/github/hapjava/services/impl/ThermostatService.java new file mode 100644 index 000000000..d82975a51 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/ThermostatService.java @@ -0,0 +1,78 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.ThermostatAccessory; +import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.characteristics.impl.humiditysensor.CurrentRelativeHumidityCharacteristic; +import io.github.hapjava.characteristics.impl.humiditysensor.TargetRelativeHumidityCharacteristic; +import io.github.hapjava.characteristics.impl.thermostat.CoolingThresholdTemperatureCharacteristic; +import io.github.hapjava.characteristics.impl.thermostat.CurrentHeatingCoolingStateCharacteristic; +import io.github.hapjava.characteristics.impl.thermostat.CurrentTemperatureCharacteristic; +import io.github.hapjava.characteristics.impl.thermostat.HeatingThresholdTemperatureCharacteristic; +import io.github.hapjava.characteristics.impl.thermostat.TargetHeatingCoolingStateCharacteristic; +import io.github.hapjava.characteristics.impl.thermostat.TargetTemperatureCharacteristic; +import io.github.hapjava.characteristics.impl.thermostat.TemperatureDisplayUnitCharacteristic; + +/** This service describes a thermostat. */ +public class ThermostatService extends AbstractServiceImpl { + + public ThermostatService( + CurrentHeatingCoolingStateCharacteristic currentHeatingCoolingStateCharacteristic, + TargetHeatingCoolingStateCharacteristic targetHeatingCoolingStateCharacteristic, + CurrentTemperatureCharacteristic currentTemperatureCharacteristic, + TargetTemperatureCharacteristic targetTemperatureCharacteristic, + TemperatureDisplayUnitCharacteristic temperatureUnitsCharacteristic) { + super("0000004A-0000-1000-8000-0026BB765291"); + addCharacteristic(currentHeatingCoolingStateCharacteristic); + addCharacteristic(targetHeatingCoolingStateCharacteristic); + addCharacteristic(currentTemperatureCharacteristic); + addCharacteristic(targetTemperatureCharacteristic); + addCharacteristic(temperatureUnitsCharacteristic); + } + + public ThermostatService(ThermostatAccessory thermostat) { + this( + new CurrentHeatingCoolingStateCharacteristic( + thermostat::getCurrentState, + thermostat::subscribeCurrentState, + thermostat::unsubscribeCurrentState), + new TargetHeatingCoolingStateCharacteristic( + thermostat::getTargetState, + thermostat::setTargetState, + thermostat::subscribeTargetState, + thermostat::unsubscribeTargetState), + new CurrentTemperatureCharacteristic( + thermostat::getCurrentTemperature, + thermostat::subscribeCurrentTemperature, + thermostat::unsubscribeCurrentTemperature), + new TargetTemperatureCharacteristic( + thermostat::getTargetTemperature, + thermostat::setTargetTemperature, + thermostat::subscribeTargetTemperature, + thermostat::unsubscribeTargetTemperature), + new TemperatureDisplayUnitCharacteristic( + thermostat::getTemperatureDisplayUnit, + thermostat::setTemperatureDisplayUnit, + thermostat::subscribeTemperatureDisplayUnit, + thermostat::unsubscribeTemperatureDisplayUnit)); + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(CoolingThresholdTemperatureCharacteristic threshold) { + addCharacteristic(threshold); + } + + public void addOptionalCharacteristic(HeatingThresholdTemperatureCharacteristic threshold) { + addCharacteristic(threshold); + } + + public void addOptionalCharacteristic(CurrentRelativeHumidityCharacteristic humidity) { + addCharacteristic(humidity); + } + + public void addOptionalCharacteristic(TargetRelativeHumidityCharacteristic humidity) { + addCharacteristic(humidity); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/ValveService.java b/src/main/java/io/github/hapjava/services/impl/ValveService.java new file mode 100644 index 000000000..3b906261b --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/ValveService.java @@ -0,0 +1,62 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.ValveAccessory; +import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.characteristics.impl.common.ActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.InUseCharacteristic; +import io.github.hapjava.characteristics.impl.common.IsConfiguredCharacteristic; +import io.github.hapjava.characteristics.impl.common.ServiceLabelIndexCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; +import io.github.hapjava.characteristics.impl.valve.SetDurationCharacteristic; +import io.github.hapjava.characteristics.impl.valve.ValveTypeCharacteristic; + +/** + * This service describes accessories like irrigation valves or water outlets. A valve is set to In + * Use when there are fluid flowing through the valve. + */ +public class ValveService extends AbstractServiceImpl { + + public ValveService( + ActiveCharacteristic activeCharacteristic, + InUseCharacteristic inUseCharacteristic, + ValveTypeCharacteristic valveTypeCharacteristic) { + super("000000D0-0000-1000-8000-0026BB765291"); + addCharacteristic(activeCharacteristic); + addCharacteristic(inUseCharacteristic); + addCharacteristic(valveTypeCharacteristic); + } + + public ValveService(ValveAccessory valve) { + this( + new ActiveCharacteristic( + valve::getValveActive, + valve::setValveActive, + valve::subscribeValveActive, + valve::unsubscribeValveActive), + new InUseCharacteristic( + valve::getValveInUse, valve::subscribeValveInUse, valve::unsubscribeValveInUse), + new ValveTypeCharacteristic( + valve::getValveType, valve::subscribeValveType, valve::unsubscribeValveType)); + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(SetDurationCharacteristic setDurationCharacteristic) { + addCharacteristic(setDurationCharacteristic); + } + + public void addOptionalCharacteristic(IsConfiguredCharacteristic isConfiguredCharacteristic) { + addCharacteristic(isConfiguredCharacteristic); + } + + public void addOptionalCharacteristic( + ServiceLabelIndexCharacteristic serviceLabelIndexCharacteristic) { + addCharacteristic(serviceLabelIndexCharacteristic); + } + + public void addOptionalCharacteristic(StatusFaultCharacteristic statusFaultCharacteristic) { + addCharacteristic(statusFaultCharacteristic); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/WindowCoveringService.java b/src/main/java/io/github/hapjava/services/impl/WindowCoveringService.java new file mode 100644 index 000000000..2adfd8ab6 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/WindowCoveringService.java @@ -0,0 +1,80 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.WindowCoveringAccessory; +import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.characteristics.impl.common.ObstructionDetectedCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.CurrentHorizontalTiltAngleCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.CurrentPositionCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.CurrentVerticalTiltAngleCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.HoldPositionCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.PositionStateCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.TargetHorizontalTiltAngleCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.TargetPositionCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.TargetVerticalTiltAngleCharacteristic; + +/** + * This service describes motorized window coverings or shades - examples include shutters, blinds, + * awnings etc. + */ +public class WindowCoveringService extends AbstractServiceImpl { + + public WindowCoveringService( + TargetPositionCharacteristic targetPositionCharacteristic, + CurrentPositionCharacteristic currentPositionCharacteristic, + PositionStateCharacteristic positionStateCharacteristic) { + super("0000008C-0000-1000-8000-0026BB765291"); + addCharacteristic(targetPositionCharacteristic); + addCharacteristic(currentPositionCharacteristic); + addCharacteristic(positionStateCharacteristic); + } + + public WindowCoveringService(WindowCoveringAccessory windowCovering) { + this( + new TargetPositionCharacteristic( + windowCovering::getTargetPosition, + windowCovering::setTargetPosition, + windowCovering::subscribeTargetPosition, + windowCovering::unsubscribeTargetPosition), + new CurrentPositionCharacteristic( + windowCovering::getCurrentPosition, + windowCovering::subscribeCurrentPosition, + windowCovering::unsubscribeCurrentPosition), + new PositionStateCharacteristic( + windowCovering::getPositionState, + windowCovering::subscribePositionState, + windowCovering::unsubscribePositionState)); + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(HoldPositionCharacteristic holdPositionCharacteristic) { + addCharacteristic(holdPositionCharacteristic); + } + + public void addOptionalCharacteristic( + CurrentHorizontalTiltAngleCharacteristic currentHorizontalTiltAngleCharacteristic) { + addCharacteristic(currentHorizontalTiltAngleCharacteristic); + } + + public void addOptionalCharacteristic( + TargetHorizontalTiltAngleCharacteristic targetHorizontalTiltAngleCharacteristic) { + addCharacteristic(targetHorizontalTiltAngleCharacteristic); + } + + public void addOptionalCharacteristic( + CurrentVerticalTiltAngleCharacteristic currentVerticalTiltAngleCharacteristic) { + addCharacteristic(currentVerticalTiltAngleCharacteristic); + } + + public void addOptionalCharacteristic( + TargetVerticalTiltAngleCharacteristic targetVerticalTiltAngleCharacteristic) { + addCharacteristic(targetVerticalTiltAngleCharacteristic); + } + + public void addOptionalCharacteristic( + ObstructionDetectedCharacteristic obstructionDetectedCharacteristic) { + addCharacteristic(obstructionDetectedCharacteristic); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/WindowService.java b/src/main/java/io/github/hapjava/services/impl/WindowService.java new file mode 100644 index 000000000..2255afbd5 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/WindowService.java @@ -0,0 +1,53 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.WindowAccessory; +import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.characteristics.impl.common.ObstructionDetectedCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.CurrentPositionCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.HoldPositionCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.PositionStateCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.TargetPositionCharacteristic; + +/** This service describes a motorized window. */ +public class WindowService extends AbstractServiceImpl { + + public WindowService( + CurrentPositionCharacteristic currentPositionCharacteristic, + TargetPositionCharacteristic targetPositionCharacteristic, + PositionStateCharacteristic positionStateCharacteristic) { + super("0000008B-0000-1000-8000-0026BB765291"); + addCharacteristic(currentPositionCharacteristic); + addCharacteristic(targetPositionCharacteristic); + addCharacteristic(positionStateCharacteristic); + } + + public WindowService(WindowAccessory window) { + this( + new CurrentPositionCharacteristic( + window::getCurrentPosition, + window::subscribeCurrentPosition, + window::unsubscribeCurrentPosition), + new TargetPositionCharacteristic( + window::getTargetPosition, + window::setTargetPosition, + window::subscribeTargetPosition, + window::unsubscribeTargetPosition), + new PositionStateCharacteristic( + window::getPositionState, + window::subscribePositionState, + window::unsubscribePositionState)); + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(HoldPositionCharacteristic holdPositionCharacteristic) { + addCharacteristic(holdPositionCharacteristic); + } + + public void addOptionalCharacteristic( + ObstructionDetectedCharacteristic obstructionDetectedCharacteristic) { + addCharacteristic(obstructionDetectedCharacteristic); + } +} diff --git a/src/test/java/io/github/hapjava/HomekitRootTest.java b/src/test/java/io/github/hapjava/server/impl/HomekitRootTest.java similarity index 89% rename from src/test/java/io/github/hapjava/HomekitRootTest.java rename to src/test/java/io/github/hapjava/server/impl/HomekitRootTest.java index 33eb51b10..aa9969a93 100644 --- a/src/test/java/io/github/hapjava/HomekitRootTest.java +++ b/src/test/java/io/github/hapjava/server/impl/HomekitRootTest.java @@ -1,4 +1,4 @@ -package io.github.hapjava; +package io.github.hapjava.server.impl; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; @@ -7,9 +7,11 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import io.github.hapjava.impl.HomekitWebHandler; -import io.github.hapjava.impl.http.HomekitClientConnectionFactory; -import io.github.hapjava.impl.jmdns.JmdnsHomekitAdvertiser; +import io.github.hapjava.accessories.HomekitAccessory; +import io.github.hapjava.server.HomekitAuthInfo; +import io.github.hapjava.server.HomekitWebHandler; +import io.github.hapjava.server.impl.http.HomekitClientConnectionFactory; +import io.github.hapjava.server.impl.jmdns.JmdnsHomekitAdvertiser; import java.util.concurrent.CompletableFuture; import org.junit.Assert; import org.junit.Before; From 68f09a78a10273e553078fa34ce87187b6814e20 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Mon, 16 Mar 2020 00:56:37 +0100 Subject: [PATCH 002/138] add name, active and fault interfaces --- .../accessories/LockMechanismAccessory.java | 1 + .../AccessoryWithDuration.java | 14 ++---- .../AccessoryWithFanState.java | 45 +++++++++-------- .../AccessoryWithName.java | 18 +++++++ .../AccessoryWithRemainingDuration.java | 6 +-- .../AccessoryWithStatusActive.java | 30 ++++++++++++ .../AccessoryWithStatusFault.java | 34 +++++++++++++ .../IdentifyCharacteristic.java | 2 +- .../impl/base/BaseCharacteristic.java | 6 +-- .../impl/base/BooleanCharacteristic.java | 8 +++- .../impl/base/EnumCharacteristic.java | 11 +++-- .../impl/base/FloatCharacteristic.java | 8 +++- .../impl/base/IntegerCharacteristic.java | 5 +- .../impl/base/StaticStringCharacteristic.java | 6 ++- .../battery/ChargingStateCharacteristic.java | 2 +- .../StatusLowBatteryCharacteristic.java | 2 +- .../CarbonDioxideDetectedCharacteristic.java | 2 +- .../CarbonDioxideLevelCharacteristic.java | 2 +- .../CarbonDioxidePeakLevelCharacteristic.java | 2 +- .../CarbonMonoxideDetectedCharacteristic.java | 2 +- .../CarbonMonoxideLevelCharacteristic.java | 2 +- ...CarbonMonoxidePeakLevelCharacteristic.java | 2 +- .../impl/common/ActiveCharacteristic.java | 2 +- .../impl/common/InUseCharacteristic.java | 2 +- .../common/IsConfiguredCharacteristic.java | 2 +- .../NameCharacteristic.java | 2 +- .../ObstructionDetectedCharacteristic.java | 2 +- .../impl/common/OnCharacteristic.java | 2 +- .../common/StatusActiveCharacteristic.java | 2 +- .../common/StatusFaultCharacteristic.java | 2 +- .../common/StatusTamperedCharacteristic.java | 2 +- .../ContactSensorStateCharacteristic.java | 2 +- .../fan/CurrentFanStateCharacteristic.java | 2 +- .../LockPhysicalControlsCharacteristic.java | 2 +- .../fan/RotationDirectionCharacteristic.java | 2 +- .../impl/fan/SwingModeCharacteristic.java | 2 +- .../fan/TargetFanStateCharacteristic.java | 2 +- .../CurrentDoorStateCharacteristic.java | 2 +- .../TargetDoorStateCharacteristic.java | 2 +- ...CurrentRelativeHumidityCharacteristic.java | 2 +- .../TargetRelativeHumidityCharacteristic.java | 2 +- .../LeakDetectedStateCharacteristic.java | 2 +- .../impl/lightbulb/HueCharacteristic.java | 2 +- .../lightbulb/SaturationCharacteristic.java | 2 +- ...urrentAmbientLightLevelCharacteristic.java | 2 +- .../lock/LockCurrentStateCharacteristic.java | 2 +- .../lock/LockTargetStateCharacteristic.java | 2 +- .../MotionDetectedCharacteristic.java | 2 +- .../OccupancyDetectedCharacteristic.java | 2 +- .../outlet/OutletInUseCharacteristic.java | 2 +- ...rentSecuritySystemStateCharacteristic.java | 2 +- ...SecuritySystemAlarmTypeCharacteristic.java | 2 +- ...rgetSecuritySystemStateCharacteristic.java | 2 +- .../SmokeDetectedCharacteristic.java | 2 +- ...ingThresholdTemperatureCharacteristic.java | 2 +- ...rentHeatingCoolingStateCharacteristic.java | 2 +- .../CurrentTemperatureCharacteristic.java | 2 +- ...ingThresholdTemperatureCharacteristic.java | 2 +- ...rgetHeatingCoolingStateCharacteristic.java | 2 +- .../TargetTemperatureCharacteristic.java | 2 +- .../TemperatureDisplayUnitCharacteristic.java | 2 +- .../impl/valve/ValveTypeCharacteristic.java | 2 +- .../HoldPositionCharacteristic.java | 2 +- .../PositionStateCharacteristic.java | 2 +- .../services/impl/AbstractServiceImpl.java | 2 +- .../impl/AccessoryInformationService.java | 2 +- .../hapjava/services/impl/BatteryService.java | 29 +++++++---- .../impl/CarbonDioxideSensorService.java | 26 ++++++++++ .../impl/CarbonMonoxideSensorService.java | 48 +++++++++++++++++-- .../services/impl/ContactSensorService.java | 34 +++++++++++-- .../hapjava/services/impl/DoorService.java | 37 ++++++++++---- .../hapjava/services/impl/FanService.java | 19 ++++++-- .../impl/GarageDoorOpenerService.java | 31 +++++++----- .../services/impl/HumiditySensorService.java | 34 +++++++++++-- .../services/impl/LeakSensorService.java | 34 +++++++++++-- .../services/impl/LightSensorService.java | 31 ++++++++++-- .../services/impl/LightbulbService.java | 16 ++++--- .../services/impl/LockMechanismService.java | 22 +++++---- .../services/impl/MotionSensorService.java | 30 ++++++++++-- .../services/impl/OccupancySensorService.java | 30 ++++++++++-- .../hapjava/services/impl/OutletService.java | 20 +++++--- .../services/impl/SecuritySystemService.java | 30 ++++++++---- .../services/impl/SmokeSensorService.java | 30 ++++++++++-- .../hapjava/services/impl/SwitchService.java | 6 ++- .../impl/TemperatureSensorService.java | 30 ++++++++++-- .../services/impl/ThermostatService.java | 44 +++++++++-------- .../hapjava/services/impl/ValveService.java | 32 +++++++++---- .../services/impl/WindowCoveringService.java | 36 +++++++++----- .../hapjava/services/impl/WindowService.java | 36 +++++++++----- 89 files changed, 721 insertions(+), 262 deletions(-) create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithName.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusActive.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusFault.java rename src/main/java/io/github/hapjava/characteristics/impl/{accessoryinformation => common}/NameCharacteristic.java (89%) diff --git a/src/main/java/io/github/hapjava/accessories/LockMechanismAccessory.java b/src/main/java/io/github/hapjava/accessories/LockMechanismAccessory.java index d38838558..30d2e460f 100644 --- a/src/main/java/io/github/hapjava/accessories/LockMechanismAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/LockMechanismAccessory.java @@ -34,6 +34,7 @@ public interface LockMechanismAccessory extends HomekitAccessory { * set lock target state the lock trget state. * * @param state lock target state + * @return a future that completes when the change is made */ CompletableFuture setLockTargetState(LockTargetStateEnum state); diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithDuration.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithDuration.java index bf8da5f44..7304dc3e0 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithDuration.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithDuration.java @@ -11,28 +11,24 @@ public interface AccessoryWithDuration { /** - * Retrieves the current set duration for which the valve will be scheduled to run; this is - * usually used as the duration to use when the valve is set to active. + * Retrieves the current set duration; * * @return a future with the value */ CompletableFuture getSetDuration(); /** - * Sets the duration for which the valve will be scheduled to run; this is usually used as the - * duration to use when the valve is set to active. + * Sets the duration for which the service should run. * - *

If the valve is currently running, then Homekit assumes that changing this value affects the - * current remaining duration. - * - * @return a future with the value + * @param value duration in seconds + * @return a future that completes when the change is made */ CompletableFuture setSetDuration(int value); /** * Subscribes to changes in the set duration * - * @param callback the function when the value has changed + * @param callback the function when the duration has changed */ void subscribeSetDuration(HomekitCharacteristicChangeCallback callback); diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFanState.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFanState.java index 2c0488d26..548ee34e5 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFanState.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFanState.java @@ -16,37 +16,39 @@ public interface AccessoryWithFanState { /** - * OPTIONAL: Retrieves the current state of the fan (INACTIVE, IDLE, BLOWING AIR). + * Retrieves the current state of the fan (INACTIVE, IDLE, BLOWING AIR). * * @return a future that will contain the state */ CompletableFuture getCurrentFanState(); /** - * OPTIONAL: Retrieves the target state of the fan (MANUAL, AUTO). + * Retrieves the target state of the fan (MANUAL, AUTO). * * @return a future that will contain the state */ CompletableFuture getTargetFanState(); /** - * OPTIONAL: Set the target state of the fan (MANUAL, AUTO). + * Set the target state of the fan (MANUAL, AUTO). * * @param targetState target state + * @return a future that completes when the change is made */ CompletableFuture setTargetFanState(TargetFanStateEnum targetState); /** - * OPTIONAL: Retrieves the swing mode of the fan. + * Retrieves the swing mode of the fan. * * @return a future that will contain the swing mode */ CompletableFuture getSwingMode(); /** - * OPTIONAL: Set the swing mode of the fan (DISABLED, ENABLED). + * Set the swing mode of the fan (DISABLED, ENABLED). * * @param swingMode swing mode + * @return a future that completes when the change is made */ CompletableFuture setSwingMode(SwingModeEnum swingMode); @@ -58,28 +60,29 @@ public interface AccessoryWithFanState { CompletableFuture getLockControls(); /** - * OPTIONAL: Set the lock controls of the fan (DISABLED, ENABLED). + * Set the lock controls of the fan (DISABLED, ENABLED). * * @param lockControls lock controls mode + * @return a future that completes when the change is made */ CompletableFuture setLockControls(LockPhysicalControlsEnum lockControls); /** - * OPTIONAL: Retrieves the current rotation direction of the fan. + * Retrieves the current rotation direction of the fan. * * @return a future that will contain the direction */ CompletableFuture getRotationDirection(); /** - * OPTIONAL: Retrieves the current speed of the fan's rotation + * Retrieves the current speed of the fan's rotation * * @return a future that will contain the speed, expressed as an integer between 0 and 100. */ CompletableFuture getRotationSpeed(); /** - * OPTIONAL: Sets the rotation direction of the fan + * Sets the rotation direction of the fan * * @param direction the direction to set * @return a future that completes when the change is made @@ -97,62 +100,62 @@ public interface AccessoryWithFanState { CompletableFuture setRotationSpeed(Integer speed) throws Exception; /** - * OPTIONAL: Subscribes to changes in the rotation direction of the fan. + * Subscribes to changes in the rotation direction of the fan. * * @param callback the function to call when the direction changes. */ void subscribeRotationDirection(HomekitCharacteristicChangeCallback callback); /** - * OPTIONAL: Subscribes to changes in the current fan state. + * Subscribes to changes in the current fan state. * * @param callback the function to call when the direction changes. */ void subscribeCurrentFanState(HomekitCharacteristicChangeCallback callback); /** - * OPTIONAL: Subscribes to changes in the target fan state. + * Subscribes to changes in the target fan state. * * @param callback the function to call when the direction changes. */ void subscribeTargetFanState(HomekitCharacteristicChangeCallback callback); /** - * OPTIONAL: Subscribes to changes in the rotation speed of the fan. + * Subscribes to changes in the rotation speed of the fan. * * @param callback the function to call when the speed changes. */ void subscribeRotationSpeed(HomekitCharacteristicChangeCallback callback); /** - * OPTIONAL: Subscribes to changes in the swing mode of the fan. + * Subscribes to changes in the swing mode of the fan. * * @param callback the function to call when the swing mode changes. */ void subscribeSwingMode(HomekitCharacteristicChangeCallback callback); /** - * OPTIONAL: Subscribes to changes in the lock controls of the fan. + * Subscribes to changes in the lock controls of the fan. * * @param callback the function to call when the lock controls changes. */ void subscribeLockControls(HomekitCharacteristicChangeCallback callback); - /** OPTIONAL: Unsubscribes from changes in the rotation direction of the fan. */ + /** Unsubscribes from changes in the rotation direction of the fan. */ void unsubscribeRotationDirection(); - /** OPTIONAL: Unsubscribes from changes in the fan's rotation speed. */ + /** Unsubscribes from changes in the fan's rotation speed. */ void unsubscribeRotationSpeed(); - /** OPTIONAL: Unsubscribes from changes in the current state of the fan. */ + /** Unsubscribes from changes in the current state of the fan. */ void unsubscribeCurrentFanState(); - /** OPTIONAL: Unsubscribes from changes in the target state of the fan. */ + /** Unsubscribes from changes in the target state of the fan. */ void unsubscribeTargetFanState(); - /** OPTIONAL: Unsubscribes from changes in the swing mode of the fan. */ + /** Unsubscribes from changes in the swing mode of the fan. */ void unsubscribeSwingMode(); - /** OPTIONAL: Unsubscribes from changes in the lock controls of the fan. */ + /** Unsubscribes from changes in the lock controls of the fan. */ void unsubscribeLockControls(); } diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithName.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithName.java new file mode 100644 index 000000000..4d6a22cbb --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithName.java @@ -0,0 +1,18 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with name. + * + * @author Eugen Freiter + */ +public interface AccessoryWithName { + + /** + * Retrieves the name of service. + * + * @return a future with the name + */ + CompletableFuture getName(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRemainingDuration.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRemainingDuration.java index d5071bbe6..b949b413e 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRemainingDuration.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRemainingDuration.java @@ -11,15 +11,15 @@ public interface AccessoryWithRemainingDuration { /** - * Retrieves the current duration for which the valve will run + * Retrieves the remaining duration * - * @return a future with the value + * @return a future with the duration in seconds */ CompletableFuture getRemainingDuration(); /** * Subscribes to changes in the duration; note it is not necessary to emit a change every second, - * homekit infers the countdown progress clientside. + * homekit infers the countdown progress client side. * * @param callback the function when the existing duration has been replaced with a new one. */ diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusActive.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusActive.java new file mode 100644 index 000000000..7fb9aa5f3 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusActive.java @@ -0,0 +1,30 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with status active. + * + * @author Eugen Freiter + */ +public interface AccessoryWithStatusActive { + + /** + * Retrieves the status active. A value of true indicates that the accessory is active and is + * functioning without any errors. + * + * @return a future with the value + */ + CompletableFuture getStatusActive(); + + /** + * Subscribes to changes in status active. + * + * @param callback the function when the status active changes + */ + void subscribeStatusActive(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes */ + void unsubscribeStatusActive(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusFault.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusFault.java new file mode 100644 index 000000000..ad2351a26 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusFault.java @@ -0,0 +1,34 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.common.StatusFaultEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with characteristic that describes an accessory which has a fault. A non-zero value + * indicates that the accessory has experienced a fault that may be interfering with its intended + * functionality. A value of 0 indicates that there is no fault. + * + * @author Eugen Freiter + */ +public interface AccessoryWithStatusFault { + + /** + * Retrieves the status fault. A non-zero value indicates that the accessory has experienced a + * fault that may be interfering with its intended functionality. A value of 0 indicates that + * there is no fault. + * + * @return a future with the value + */ + CompletableFuture getStatusFault(); + + /** + * Subscribes to changes in status fault. + * + * @param callback the function when the status fault changes + */ + void subscribeStatusFault(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes */ + void unsubscribeStatusFault(); +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/IdentifyCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/IdentifyCharacteristic.java index 49e3e1fa2..cacb34b71 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/IdentifyCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/IdentifyCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.accessoryinformation; -import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; import java.util.Optional; /** This characteristic enables accessory to run its identify routine. */ diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java index 5260a19a4..6a3390d46 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java @@ -1,5 +1,8 @@ package io.github.hapjava.characteristics.impl.base; +import io.github.hapjava.characteristics.Characteristic; +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Optional; @@ -13,9 +16,6 @@ import javax.json.JsonValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.github.hapjava.characteristics.Characteristic; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; /** * Base class for implementing {@link Characteristic}. diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java index 29d3b0bd9..83d7f765a 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java @@ -1,5 +1,7 @@ package io.github.hapjava.characteristics.impl.base; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @@ -7,8 +9,6 @@ import javax.json.JsonNumber; import javax.json.JsonValue; import javax.json.JsonValue.ValueType; -import io.github.hapjava.characteristics.ExceptionalConsumer; -import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; /** * Characteristic that exposes a Boolean value. @@ -26,6 +26,10 @@ public abstract class BooleanCharacteristic extends BaseCharacteristic * @param type a string containing a UUID that indicates the type of characteristic. Apple defines * a set of these, however implementors can create their own as well. * @param description a description of the characteristic to be passed to the consuming device. + * @param getter getter to retrieve the value + * @param setter setter to set value + * @param subscriber subscriber to subscribe to changes + * @param unsubscriber unsubscriber to unsubscribe from chnages */ public BooleanCharacteristic( String type, diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java index 000d53d14..93f4770de 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java @@ -1,5 +1,8 @@ package io.github.hapjava.characteristics.impl.base; +import io.github.hapjava.characteristics.CharacteristicEnum; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @@ -8,10 +11,6 @@ import javax.json.JsonObject; import javax.json.JsonObjectBuilder; import javax.json.JsonValue; -import io.github.hapjava.characteristics.CharacteristicEnum; -import io.github.hapjava.characteristics.ExceptionalConsumer; -import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; -import io.github.hapjava.characteristics.impl.base.BaseCharacteristic; /** * Characteristic that exposes an Enum value. Enums are represented as an Integer value in the @@ -34,6 +33,10 @@ public abstract class EnumCharacteristic * a set of these, however implementors can create their own as well. * @param description a description of the characteristic to be passed to the consuming device. * @param maxValue the number of enum items. + * @param getter getter to retrieve the value + * @param setter setter to set value + * @param subscriber subscriber to subscribe to changes + * @param unsubscriber unsubscriber to unsubscribe from chnages */ public EnumCharacteristic( String type, diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java index c50e46ba7..e15da4fde 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java @@ -1,5 +1,7 @@ package io.github.hapjava.characteristics.impl.base; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @@ -9,8 +11,6 @@ import javax.json.JsonValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.github.hapjava.characteristics.ExceptionalConsumer; -import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; /** * A characteristic that provides a Float value type. @@ -38,6 +38,10 @@ public abstract class FloatCharacteristic extends BaseCharacteristic { * @param maxValue the maximum supported value * @param minStep the smallest supported step. Values will be rounded to a multiple of this. * @param unit a description of the unit this characteristic supports. + * @param getter getter to retrieve the value + * @param setter setter to set value + * @param subscriber subscriber to subscribe to changes + * @param unsubscriber unsubscriber to unsubscribe from chnages */ public FloatCharacteristic( String type, diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java index ecaa60475..e7a6d480b 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java @@ -1,5 +1,7 @@ package io.github.hapjava.characteristics.impl.base; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @@ -7,9 +9,6 @@ import javax.json.JsonNumber; import javax.json.JsonObjectBuilder; import javax.json.JsonValue; -import io.github.hapjava.characteristics.ExceptionalConsumer; -import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; -import io.github.hapjava.characteristics.impl.base.BaseCharacteristic; /** * A characteristic that provides an Integer data type. diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java index fc73b6192..d5de2f9ce 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java @@ -1,5 +1,6 @@ package io.github.hapjava.characteristics.impl.base; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @@ -7,8 +8,6 @@ import javax.json.JsonObjectBuilder; import javax.json.JsonString; import javax.json.JsonValue; -import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; -import io.github.hapjava.characteristics.impl.base.BaseCharacteristic; /** * A characteristic that provides an immutable String value. @@ -26,6 +25,9 @@ public class StaticStringCharacteristic extends BaseCharacteristic { * @param type a string containing a UUID that indicates the type of characteristic. Apple defines * a set of these, however implementors can create their own as well. * @param description a description of the characteristic to be passed to the consuming device. + * @param getter getter to retrieve the value + * @param subscriber subscriber to subscribe to changes + * @param unsubscriber unsubscriber to unsubscribe from chnages */ public StaticStringCharacteristic( String type, diff --git a/src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateCharacteristic.java index 1005fbcf9..c6f54e029 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.battery; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryCharacteristic.java index 24f706662..300e69dfa 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.battery; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedCharacteristic.java index 8d84898bb..27b15edd3 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedCharacteristic.java @@ -1,8 +1,8 @@ package io.github.hapjava.characteristics.impl.carbondioxidesensor; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.EventableCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideLevelCharacteristic.java index 9b3ca5447..7d1d8d440 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideLevelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideLevelCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.carbondioxidesensor; -import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxidePeakLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxidePeakLevelCharacteristic.java index 5a9201fc2..da5905fb2 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxidePeakLevelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxidePeakLevelCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.carbondioxidesensor; -import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedCharacteristic.java index ddca27549..09397f864 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedCharacteristic.java @@ -1,8 +1,8 @@ package io.github.hapjava.characteristics.impl.carbonmonoxidesensor; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.EventableCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideLevelCharacteristic.java index 175cb497e..b151842c4 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideLevelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideLevelCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.carbonmonoxidesensor; -import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxidePeakLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxidePeakLevelCharacteristic.java index 744e53858..118f2e597 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxidePeakLevelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxidePeakLevelCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.carbonmonoxidesensor; -import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveCharacteristic.java index 741c6f5b1..269bd8615 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveCharacteristic.java @@ -1,8 +1,8 @@ package io.github.hapjava.characteristics.impl.common; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.ExceptionalConsumer; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/InUseCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/InUseCharacteristic.java index e8acb12c5..543c8cf7c 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/InUseCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/InUseCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.common; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredCharacteristic.java index f9a0539ee..dd192333f 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredCharacteristic.java @@ -1,8 +1,8 @@ package io.github.hapjava.characteristics.impl.common; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.ExceptionalConsumer; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/NameCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/NameCharacteristic.java similarity index 89% rename from src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/NameCharacteristic.java rename to src/main/java/io/github/hapjava/characteristics/impl/common/NameCharacteristic.java index ddafc57ab..e565fb0c5 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/NameCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/NameCharacteristic.java @@ -1,4 +1,4 @@ -package io.github.hapjava.characteristics.impl.accessoryinformation; +package io.github.hapjava.characteristics.impl.common; import io.github.hapjava.characteristics.impl.base.StaticStringCharacteristic; import java.util.Optional; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ObstructionDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ObstructionDetectedCharacteristic.java index 7de444190..6f152ae9c 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/ObstructionDetectedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ObstructionDetectedCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.common; -import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/OnCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/OnCharacteristic.java index 21f0999f9..99da018b8 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/OnCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/OnCharacteristic.java @@ -1,8 +1,8 @@ package io.github.hapjava.characteristics.impl.common; -import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; import io.github.hapjava.characteristics.ExceptionalConsumer; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusActiveCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusActiveCharacteristic.java index d46a994d0..aa3590124 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusActiveCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusActiveCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.common; -import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultCharacteristic.java index d972b76a3..51175f22e 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.common; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedCharacteristic.java index 52a1c3f99..dda3f8af7 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.common; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactSensorStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactSensorStateCharacteristic.java index 81a2ea8f4..e3a252665 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactSensorStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactSensorStateCharacteristic.java @@ -1,8 +1,8 @@ package io.github.hapjava.characteristics.impl.contactsensor; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.EventableCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateCharacteristic.java index bd0aa8a44..fb1eb52ef 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateCharacteristic.java @@ -1,8 +1,8 @@ package io.github.hapjava.characteristics.impl.fan; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.EventableCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsCharacteristic.java index 5d1f7a29d..7d97213ef 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsCharacteristic.java @@ -1,9 +1,9 @@ package io.github.hapjava.characteristics.impl.fan; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.EventableCharacteristic; import io.github.hapjava.characteristics.ExceptionalConsumer; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationDirectionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationDirectionCharacteristic.java index ce689c1dd..1a2ed8d1d 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationDirectionCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationDirectionCharacteristic.java @@ -1,9 +1,9 @@ package io.github.hapjava.characteristics.impl.fan; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.EventableCharacteristic; import io.github.hapjava.characteristics.ExceptionalConsumer; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/SwingModeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/SwingModeCharacteristic.java index c1bd55bda..821a4a487 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/fan/SwingModeCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/SwingModeCharacteristic.java @@ -1,9 +1,9 @@ package io.github.hapjava.characteristics.impl.fan; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.EventableCharacteristic; import io.github.hapjava.characteristics.ExceptionalConsumer; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/TargetFanStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/TargetFanStateCharacteristic.java index 057c95b7a..e91b09732 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/fan/TargetFanStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/TargetFanStateCharacteristic.java @@ -1,9 +1,9 @@ package io.github.hapjava.characteristics.impl.fan; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.EventableCharacteristic; import io.github.hapjava.characteristics.ExceptionalConsumer; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateCharacteristic.java index 77a9e9d91..9743c0a2a 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateCharacteristic.java @@ -1,8 +1,8 @@ package io.github.hapjava.characteristics.impl.garagedoor; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.EventableCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/TargetDoorStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/TargetDoorStateCharacteristic.java index c3d5735db..3e6080eae 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/TargetDoorStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/TargetDoorStateCharacteristic.java @@ -1,9 +1,9 @@ package io.github.hapjava.characteristics.impl.garagedoor; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.EventableCharacteristic; import io.github.hapjava.characteristics.ExceptionalConsumer; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java index 95b56d831..e404e4152 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.humiditysensor; -import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java index d9c7bec52..ae7b539db 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java @@ -1,8 +1,8 @@ package io.github.hapjava.characteristics.impl.humiditysensor; import io.github.hapjava.characteristics.ExceptionalConsumer; -import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateCharacteristic.java index 72237a537..f4e1287be 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateCharacteristic.java @@ -1,8 +1,8 @@ package io.github.hapjava.characteristics.impl.leaksensor; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.EventableCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java index 8d8823529..7a00816bc 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java @@ -1,8 +1,8 @@ package io.github.hapjava.characteristics.impl.lightbulb; import io.github.hapjava.characteristics.ExceptionalConsumer; -import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java index 8a07caca5..90694ef98 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java @@ -2,8 +2,8 @@ import io.github.hapjava.characteristics.EventableCharacteristic; import io.github.hapjava.characteristics.ExceptionalConsumer; -import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightsensor/CurrentAmbientLightLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightsensor/CurrentAmbientLightLevelCharacteristic.java index d2dd850cc..d3736db84 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lightsensor/CurrentAmbientLightLevelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightsensor/CurrentAmbientLightLevelCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.lightsensor; -import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lock/LockCurrentStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lock/LockCurrentStateCharacteristic.java index df2f27592..ad674779b 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lock/LockCurrentStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lock/LockCurrentStateCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.lock; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lock/LockTargetStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lock/LockTargetStateCharacteristic.java index 4c7821dc6..f2efc26e0 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lock/LockTargetStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lock/LockTargetStateCharacteristic.java @@ -1,8 +1,8 @@ package io.github.hapjava.characteristics.impl.lock; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.ExceptionalConsumer; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/motionsensor/MotionDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/motionsensor/MotionDetectedCharacteristic.java index 239e004de..4be591bec 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/motionsensor/MotionDetectedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/motionsensor/MotionDetectedCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.motionsensor; -import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/occupancysensor/OccupancyDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/occupancysensor/OccupancyDetectedCharacteristic.java index 995cbcf21..97c7c70f5 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/occupancysensor/OccupancyDetectedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/occupancysensor/OccupancyDetectedCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.occupancysensor; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/outlet/OutletInUseCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/outlet/OutletInUseCharacteristic.java index 7158f4b96..6f5cfdfcd 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/outlet/OutletInUseCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/outlet/OutletInUseCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.outlet; -import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/CurrentSecuritySystemStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/CurrentSecuritySystemStateCharacteristic.java index 0324a5554..779ab51c4 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/CurrentSecuritySystemStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/CurrentSecuritySystemStateCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.securitysystem; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java index 2c92b9eec..a73155d1b 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.securitysystem; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/TargetSecuritySystemStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/TargetSecuritySystemStateCharacteristic.java index 849ffa80a..5284d4f6e 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/TargetSecuritySystemStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/TargetSecuritySystemStateCharacteristic.java @@ -1,8 +1,8 @@ package io.github.hapjava.characteristics.impl.securitysystem; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.ExceptionalConsumer; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedCharacteristic.java index 55d415c49..5a33fea6d 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.smokesensor; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java index 93f4e17fe..2a23edc95 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java @@ -1,8 +1,8 @@ package io.github.hapjava.characteristics.impl.thermostat; import io.github.hapjava.characteristics.ExceptionalConsumer; -import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateCharacteristic.java index e58f42105..bf0c56637 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.thermostat; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java index f80d760fc..96ea27735 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.thermostat; -import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java index 0693b737c..fb61aeeb7 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java @@ -1,8 +1,8 @@ package io.github.hapjava.characteristics.impl.thermostat; import io.github.hapjava.characteristics.ExceptionalConsumer; -import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateCharacteristic.java index 93bdd4e2e..5f74c1543 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateCharacteristic.java @@ -1,8 +1,8 @@ package io.github.hapjava.characteristics.impl.thermostat; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.ExceptionalConsumer; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java index 9e2e504dd..580e3f9f5 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java @@ -1,8 +1,8 @@ package io.github.hapjava.characteristics.impl.thermostat; import io.github.hapjava.characteristics.ExceptionalConsumer; -import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitCharacteristic.java index 0492ce51b..43d3d8c5d 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitCharacteristic.java @@ -1,8 +1,8 @@ package io.github.hapjava.characteristics.impl.thermostat; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.ExceptionalConsumer; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/valve/ValveTypeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/valve/ValveTypeCharacteristic.java index b3a55278c..4c25ce867 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/valve/ValveTypeCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/valve/ValveTypeCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.valve; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/HoldPositionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/HoldPositionCharacteristic.java index 4d9892c73..e9c768867 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/HoldPositionCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/HoldPositionCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.windowcovering; -import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; import java.util.Optional; /** diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateCharacteristic.java index 163ff8a2e..6f4427cfb 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateCharacteristic.java @@ -1,7 +1,7 @@ package io.github.hapjava.characteristics.impl.windowcovering; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; diff --git a/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java b/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java index 76a00030d..e0f47b56b 100644 --- a/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java +++ b/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java @@ -31,7 +31,7 @@ public String getType() { return type; } - protected void addCharacteristic(Characteristic characteristic) { + public void addCharacteristic(Characteristic characteristic) { this.characteristics.add(characteristic); } } diff --git a/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java b/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java index d5e71cd5c..127bb0179 100644 --- a/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java +++ b/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java @@ -7,8 +7,8 @@ import io.github.hapjava.characteristics.impl.accessoryinformation.IdentifyCharacteristic; import io.github.hapjava.characteristics.impl.accessoryinformation.ManufacturerCharacteristic; import io.github.hapjava.characteristics.impl.accessoryinformation.ModelCharacteristic; -import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; import io.github.hapjava.characteristics.impl.accessoryinformation.SerialNumberCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; /** Every accessory must expose a single instance of the Accessory Information service. */ public class AccessoryInformationService extends AbstractServiceImpl { diff --git a/src/main/java/io/github/hapjava/services/impl/BatteryService.java b/src/main/java/io/github/hapjava/services/impl/BatteryService.java index 312853058..46259a6e8 100644 --- a/src/main/java/io/github/hapjava/services/impl/BatteryService.java +++ b/src/main/java/io/github/hapjava/services/impl/BatteryService.java @@ -1,9 +1,11 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.BatteryAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.characteristics.impl.battery.BatteryLevelCharacteristic; import io.github.hapjava.characteristics.impl.battery.ChargingStateCharacteristic; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; /** This service describes a battery service. */ public class BatteryService extends AbstractServiceImpl { @@ -18,19 +20,26 @@ public BatteryService( addCharacteristic(statusLowBatteryCharacteristic); } - public BatteryService(BatteryAccessory batteryAccessory) { + public BatteryService(BatteryAccessory accessory) { this( new BatteryLevelCharacteristic( - batteryAccessory::getBatteryLevel, - batteryAccessory::subscribeBatteryLevel, - batteryAccessory::unsubscribeBatteryLevel), + accessory::getBatteryLevel, + accessory::subscribeBatteryLevel, + accessory::unsubscribeBatteryLevel), new ChargingStateCharacteristic( - batteryAccessory::getChargingState, - batteryAccessory::subscribeBatteryChargingState, - batteryAccessory::unsubscribeBatteryChargingState), + accessory::getChargingState, + accessory::subscribeBatteryChargingState, + accessory::unsubscribeBatteryChargingState), new StatusLowBatteryCharacteristic( - batteryAccessory::getLowBatteryState, - batteryAccessory::subscribeLowBatteryState, - batteryAccessory::unsubscribeLowBatteryState)); + accessory::getLowBatteryState, + accessory::subscribeLowBatteryState, + accessory::unsubscribeLowBatteryState)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); } } diff --git a/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java b/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java index f0ef2823f..bcd009dfd 100644 --- a/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java @@ -2,10 +2,14 @@ import io.github.hapjava.accessories.CarbonDioxideSensorAccessory; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithCarbonDioxideLevel; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; import io.github.hapjava.characteristics.impl.carbondioxidesensor.CarbonDioxideDetectedCharacteristic; import io.github.hapjava.characteristics.impl.carbondioxidesensor.CarbonDioxideLevelCharacteristic; import io.github.hapjava.characteristics.impl.carbondioxidesensor.CarbonDioxidePeakLevelCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; @@ -37,6 +41,28 @@ public CarbonDioxideSensorService(CarbonDioxideSensorAccessory accessory) { ((AccessoryWithCarbonDioxideLevel) accessory)::subscribeCarbonDioxidePeakLevel, ((AccessoryWithCarbonDioxideLevel) accessory)::unsubscribeCarbonDioxidePeakLevel)); } + + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithStatusActive) { + addOptionalCharacteristic( + new StatusActiveCharacteristic( + ((AccessoryWithStatusActive) accessory)::getStatusActive, + ((AccessoryWithStatusActive) accessory)::subscribeStatusActive, + ((AccessoryWithStatusActive) accessory)::unsubscribeStatusActive)); + } + if (accessory instanceof AccessoryWithStatusFault) { + addOptionalCharacteristic( + new StatusFaultCharacteristic( + ((AccessoryWithStatusFault) accessory)::getStatusFault, + ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, + ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); } public void addOptionalCharacteristic(StatusActiveCharacteristic statusActive) { diff --git a/src/main/java/io/github/hapjava/services/impl/CarbonMonoxideSensorService.java b/src/main/java/io/github/hapjava/services/impl/CarbonMonoxideSensorService.java index 1df903b4d..86c6d7b7d 100644 --- a/src/main/java/io/github/hapjava/services/impl/CarbonMonoxideSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/CarbonMonoxideSensorService.java @@ -1,10 +1,15 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.CarbonMonoxideSensorAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithCarbonMonoxideLevel; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; import io.github.hapjava.characteristics.impl.carbonmonoxidesensor.CarbonMonoxideDetectedCharacteristic; import io.github.hapjava.characteristics.impl.carbonmonoxidesensor.CarbonMonoxideLevelCharacteristic; import io.github.hapjava.characteristics.impl.carbonmonoxidesensor.CarbonMonoxidePeakLevelCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; @@ -17,12 +22,47 @@ public CarbonMonoxideSensorService(CarbonMonoxideDetectedCharacteristic carbonMo addCharacteristic(carbonMonoxideDetected); } - public CarbonMonoxideSensorService(CarbonMonoxideSensorAccessory carbonMonoxideSensor) { + public CarbonMonoxideSensorService(CarbonMonoxideSensorAccessory accessory) { this( new CarbonMonoxideDetectedCharacteristic( - carbonMonoxideSensor::getCarbonMonoxideDetectedState, - carbonMonoxideSensor::subscribeCarbonMonoxideDetectedState, - carbonMonoxideSensor::unsubscribeCarbonMonoxideDetectedState)); + accessory::getCarbonMonoxideDetectedState, + accessory::subscribeCarbonMonoxideDetectedState, + accessory::unsubscribeCarbonMonoxideDetectedState)); + + if (accessory instanceof AccessoryWithCarbonMonoxideLevel) { + addOptionalCharacteristic( + new CarbonMonoxideLevelCharacteristic( + ((AccessoryWithCarbonMonoxideLevel) accessory)::getCarbonMonoxideLevel, + ((AccessoryWithCarbonMonoxideLevel) accessory)::subscribeCarbonMonoxideLevel, + ((AccessoryWithCarbonMonoxideLevel) accessory)::unsubscribeCarbonMonoxideLevel)); + addOptionalCharacteristic( + new CarbonMonoxidePeakLevelCharacteristic( + ((AccessoryWithCarbonMonoxideLevel) accessory)::getCarbonMonoxidePeakLevel, + ((AccessoryWithCarbonMonoxideLevel) accessory)::subscribeCarbonMonoxidePeakLevel, + ((AccessoryWithCarbonMonoxideLevel) accessory)::unsubscribeCarbonMonoxidePeakLevel)); + } + + if (accessory instanceof AccessoryWithStatusActive) { + addOptionalCharacteristic( + new StatusActiveCharacteristic( + ((AccessoryWithStatusActive) accessory)::getStatusActive, + ((AccessoryWithStatusActive) accessory)::subscribeStatusActive, + ((AccessoryWithStatusActive) accessory)::unsubscribeStatusActive)); + } + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithStatusFault) { + addOptionalCharacteristic( + new StatusFaultCharacteristic( + ((AccessoryWithStatusFault) accessory)::getStatusFault, + ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, + ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); } public void addOptionalCharacteristic(StatusActiveCharacteristic statusActive) { diff --git a/src/main/java/io/github/hapjava/services/impl/ContactSensorService.java b/src/main/java/io/github/hapjava/services/impl/ContactSensorService.java index 47bcbfbcf..aa300955f 100644 --- a/src/main/java/io/github/hapjava/services/impl/ContactSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/ContactSensorService.java @@ -1,7 +1,11 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.ContactSensorAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; @@ -15,12 +19,34 @@ public ContactSensorService(ContactSensorStateCharacteristic contactSensorState) addCharacteristic(contactSensorState); } - public ContactSensorService(ContactSensorAccessory contactSensor) { + public ContactSensorService(ContactSensorAccessory accessory) { this( new ContactSensorStateCharacteristic( - contactSensor::getCurrentState, - contactSensor::subscribeContactState, - contactSensor::unsubscribeContactState)); + accessory::getCurrentState, + accessory::subscribeContactState, + accessory::unsubscribeContactState)); + + if (accessory instanceof AccessoryWithStatusActive) { + addOptionalCharacteristic( + new StatusActiveCharacteristic( + ((AccessoryWithStatusActive) accessory)::getStatusActive, + ((AccessoryWithStatusActive) accessory)::subscribeStatusActive, + ((AccessoryWithStatusActive) accessory)::unsubscribeStatusActive)); + } + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithStatusFault) { + addOptionalCharacteristic( + new StatusFaultCharacteristic( + ((AccessoryWithStatusFault) accessory)::getStatusFault, + ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, + ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); } public void addOptionalCharacteristic(StatusActiveCharacteristic statusActive) { diff --git a/src/main/java/io/github/hapjava/services/impl/DoorService.java b/src/main/java/io/github/hapjava/services/impl/DoorService.java index 8dcb224a6..62a191c8c 100644 --- a/src/main/java/io/github/hapjava/services/impl/DoorService.java +++ b/src/main/java/io/github/hapjava/services/impl/DoorService.java @@ -1,6 +1,9 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.DoorAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithObstructionDetection; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.ObstructionDetectedCharacteristic; import io.github.hapjava.characteristics.impl.windowcovering.CurrentPositionCharacteristic; import io.github.hapjava.characteristics.impl.windowcovering.HoldPositionCharacteristic; @@ -20,19 +23,35 @@ public DoorService( addCharacteristic(positionStateCharacteristic); } - public DoorService(DoorAccessory door) { + public DoorService(DoorAccessory accessory) { this( new CurrentPositionCharacteristic( - door::getCurrentPosition, - door::subscribeCurrentPosition, - door::unsubscribeCurrentPosition), + accessory::getCurrentPosition, + accessory::subscribeCurrentPosition, + accessory::unsubscribeCurrentPosition), new TargetPositionCharacteristic( - door::getTargetPosition, - door::setTargetPosition, - door::subscribeTargetPosition, - door::unsubscribeTargetPosition), + accessory::getTargetPosition, + accessory::setTargetPosition, + accessory::subscribeTargetPosition, + accessory::unsubscribeTargetPosition), new PositionStateCharacteristic( - door::getPositionState, door::subscribePositionState, door::unsubscribePositionState)); + accessory::getPositionState, + accessory::subscribePositionState, + accessory::unsubscribePositionState)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithObstructionDetection) { + addOptionalCharacteristic( + new ObstructionDetectedCharacteristic( + ((AccessoryWithObstructionDetection) accessory)::getObstructionDetected, + ((AccessoryWithObstructionDetection) accessory)::subscribeObstructionDetected, + ((AccessoryWithObstructionDetection) accessory)::unsubscribeObstructionDetected)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); } public void addOptionalCharacteristic(HoldPositionCharacteristic holdPositionCharacteristic) { diff --git a/src/main/java/io/github/hapjava/services/impl/FanService.java b/src/main/java/io/github/hapjava/services/impl/FanService.java index 4fc8d8660..a09c5b61f 100644 --- a/src/main/java/io/github/hapjava/services/impl/FanService.java +++ b/src/main/java/io/github/hapjava/services/impl/FanService.java @@ -1,8 +1,10 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.FanAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.characteristics.impl.common.ActiveCharacteristic; import io.github.hapjava.characteristics.impl.common.ActiveEnum; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.fan.CurrentFanStateCharacteristic; import io.github.hapjava.characteristics.impl.fan.LockPhysicalControlsCharacteristic; import io.github.hapjava.characteristics.impl.fan.RotationDirectionCharacteristic; @@ -18,13 +20,20 @@ public FanService(ActiveCharacteristic active) { addCharacteristic(active); } - public FanService(FanAccessory fan) { + public FanService(FanAccessory accessory) { this( new ActiveCharacteristic( - () -> fan.isActive().thenApply(s -> s ? ActiveEnum.ACTIVE : ActiveEnum.INACTIVE), - (v) -> fan.setActive(v == ActiveEnum.ACTIVE), - fan::subscribeActive, - fan::unsubscribeActive)); + () -> accessory.isActive().thenApply(s -> s ? ActiveEnum.ACTIVE : ActiveEnum.INACTIVE), + (v) -> accessory.setActive(v == ActiveEnum.ACTIVE), + accessory::subscribeActive, + accessory::unsubscribeActive)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); } public void addOptionalCharacteristic(CurrentFanStateCharacteristic state) { diff --git a/src/main/java/io/github/hapjava/services/impl/GarageDoorOpenerService.java b/src/main/java/io/github/hapjava/services/impl/GarageDoorOpenerService.java index 329494a27..c1c7346f5 100644 --- a/src/main/java/io/github/hapjava/services/impl/GarageDoorOpenerService.java +++ b/src/main/java/io/github/hapjava/services/impl/GarageDoorOpenerService.java @@ -1,6 +1,8 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.GarageDoorOpenerAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.ObstructionDetectedCharacteristic; import io.github.hapjava.characteristics.impl.garagedoor.CurrentDoorStateCharacteristic; import io.github.hapjava.characteristics.impl.garagedoor.TargetDoorStateCharacteristic; @@ -23,21 +25,28 @@ public GarageDoorOpenerService( addCharacteristic(obstruction); } - public GarageDoorOpenerService(GarageDoorOpenerAccessory door) { + public GarageDoorOpenerService(GarageDoorOpenerAccessory accessory) { this( new CurrentDoorStateCharacteristic( - door::getCurrentDoorState, - door::subscribeCurrentDoorState, - door::unsubscribeCurrentDoorState), + accessory::getCurrentDoorState, + accessory::subscribeCurrentDoorState, + accessory::unsubscribeCurrentDoorState), new TargetDoorStateCharacteristic( - door::getTargetDoorState, - door::setTargetDoorState, - door::subscribeTargetDoorState, - door::unsubscribeTargetDoorState), + accessory::getTargetDoorState, + accessory::setTargetDoorState, + accessory::subscribeTargetDoorState, + accessory::unsubscribeTargetDoorState), new ObstructionDetectedCharacteristic( - door::getObstructionDetected, - door::subscribeObstructionDetected, - door::unsubscribeObstructionDetected)); + accessory::getObstructionDetected, + accessory::subscribeObstructionDetected, + accessory::unsubscribeObstructionDetected)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); } public void addOptionalCharacteristic(LockCurrentStateCharacteristic lockState) { diff --git a/src/main/java/io/github/hapjava/services/impl/HumiditySensorService.java b/src/main/java/io/github/hapjava/services/impl/HumiditySensorService.java index 1e0de26a8..b5a21a76e 100644 --- a/src/main/java/io/github/hapjava/services/impl/HumiditySensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/HumiditySensorService.java @@ -1,7 +1,11 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.HumiditySensorAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; @@ -16,12 +20,34 @@ public HumiditySensorService( addCharacteristic(currentRelativeHumidityCharacteristic); } - public HumiditySensorService(HumiditySensorAccessory sensor) { + public HumiditySensorService(HumiditySensorAccessory accessory) { this( new CurrentRelativeHumidityCharacteristic( - sensor::getCurrentRelativeHumidity, - sensor::subscribeCurrentRelativeHumidity, - sensor::unsubscribeCurrentRelativeHumidity)); + accessory::getCurrentRelativeHumidity, + accessory::subscribeCurrentRelativeHumidity, + accessory::unsubscribeCurrentRelativeHumidity)); + + if (accessory instanceof AccessoryWithStatusActive) { + addOptionalCharacteristic( + new StatusActiveCharacteristic( + ((AccessoryWithStatusActive) accessory)::getStatusActive, + ((AccessoryWithStatusActive) accessory)::subscribeStatusActive, + ((AccessoryWithStatusActive) accessory)::unsubscribeStatusActive)); + } + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithStatusFault) { + addOptionalCharacteristic( + new StatusFaultCharacteristic( + ((AccessoryWithStatusFault) accessory)::getStatusFault, + ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, + ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); } public void addOptionalCharacteristic(StatusActiveCharacteristic statusActive) { diff --git a/src/main/java/io/github/hapjava/services/impl/LeakSensorService.java b/src/main/java/io/github/hapjava/services/impl/LeakSensorService.java index be95d5e61..e0a493ed0 100644 --- a/src/main/java/io/github/hapjava/services/impl/LeakSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/LeakSensorService.java @@ -1,7 +1,11 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.LeakSensorAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; @@ -15,12 +19,34 @@ public LeakSensorService(LeakDetectedStateCharacteristic leakDetectedState) { addCharacteristic(leakDetectedState); } - public LeakSensorService(LeakSensorAccessory leakSensor) { + public LeakSensorService(LeakSensorAccessory accessory) { this( new LeakDetectedStateCharacteristic( - leakSensor::getLeakDetected, - leakSensor::subscribeLeakDetected, - leakSensor::unsubscribeLeakDetected)); + accessory::getLeakDetected, + accessory::subscribeLeakDetected, + accessory::unsubscribeLeakDetected)); + + if (accessory instanceof AccessoryWithStatusActive) { + addOptionalCharacteristic( + new StatusActiveCharacteristic( + ((AccessoryWithStatusActive) accessory)::getStatusActive, + ((AccessoryWithStatusActive) accessory)::subscribeStatusActive, + ((AccessoryWithStatusActive) accessory)::unsubscribeStatusActive)); + } + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithStatusFault) { + addOptionalCharacteristic( + new StatusFaultCharacteristic( + ((AccessoryWithStatusFault) accessory)::getStatusFault, + ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, + ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); } public void addOptionalCharacteristic(StatusActiveCharacteristic statusActive) { diff --git a/src/main/java/io/github/hapjava/services/impl/LightSensorService.java b/src/main/java/io/github/hapjava/services/impl/LightSensorService.java index 04e0a8e9d..07a15a0fc 100644 --- a/src/main/java/io/github/hapjava/services/impl/LightSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/LightSensorService.java @@ -1,8 +1,11 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.LightSensorAccessory; -import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; @@ -16,12 +19,30 @@ public LightSensorService(CurrentAmbientLightLevelCharacteristic lightLevel) { addCharacteristic(lightLevel); } - public LightSensorService(LightSensorAccessory lightSensor) { + public LightSensorService(LightSensorAccessory accessory) { this( new CurrentAmbientLightLevelCharacteristic( - lightSensor::getCurrentAmbientLightLevel, - lightSensor::subscribeCurrentAmbientLightLevel, - lightSensor::unsubscribeCurrentAmbientLightLevel)); + accessory::getCurrentAmbientLightLevel, + accessory::subscribeCurrentAmbientLightLevel, + accessory::unsubscribeCurrentAmbientLightLevel)); + + if (accessory instanceof AccessoryWithStatusActive) { + addOptionalCharacteristic( + new StatusActiveCharacteristic( + ((AccessoryWithStatusActive) accessory)::getStatusActive, + ((AccessoryWithStatusActive) accessory)::subscribeStatusActive, + ((AccessoryWithStatusActive) accessory)::unsubscribeStatusActive)); + } + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithStatusFault) { + addOptionalCharacteristic( + new StatusFaultCharacteristic( + ((AccessoryWithStatusFault) accessory)::getStatusFault, + ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, + ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/LightbulbService.java b/src/main/java/io/github/hapjava/services/impl/LightbulbService.java index d2394826a..eff2c2b47 100644 --- a/src/main/java/io/github/hapjava/services/impl/LightbulbService.java +++ b/src/main/java/io/github/hapjava/services/impl/LightbulbService.java @@ -1,7 +1,8 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.LightbulbAccessory; -import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.OnCharacteristic; import io.github.hapjava.characteristics.impl.lightbulb.BrightnessCharacteristic; import io.github.hapjava.characteristics.impl.lightbulb.ColorTemperatureCharacteristic; @@ -16,13 +17,16 @@ public LightbulbService(OnCharacteristic onState) { addCharacteristic(onState); } - public LightbulbService(LightbulbAccessory lightbulb) { + public LightbulbService(LightbulbAccessory accessory) { this( new OnCharacteristic( - lightbulb::getLightbulbPowerState, - lightbulb::setLightbulbPowerState, - lightbulb::subscribeLightbulbPowerState, - lightbulb::unsubscribeLightbulbPowerState)); + accessory::getLightbulbPowerState, + accessory::setLightbulbPowerState, + accessory::subscribeLightbulbPowerState, + accessory::unsubscribeLightbulbPowerState)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/LockMechanismService.java b/src/main/java/io/github/hapjava/services/impl/LockMechanismService.java index c319b7192..c82f107c8 100644 --- a/src/main/java/io/github/hapjava/services/impl/LockMechanismService.java +++ b/src/main/java/io/github/hapjava/services/impl/LockMechanismService.java @@ -1,7 +1,8 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.LockMechanismAccessory; -import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.lock.LockCurrentStateCharacteristic; import io.github.hapjava.characteristics.impl.lock.LockTargetStateCharacteristic; @@ -19,17 +20,20 @@ public LockMechanismService( addCharacteristic(lockTargetState); } - public LockMechanismService(LockMechanismAccessory lockMechanism) { + public LockMechanismService(LockMechanismAccessory accessory) { this( new LockCurrentStateCharacteristic( - lockMechanism::getLockCurrentState, - lockMechanism::subscribeLockCurrentState, - lockMechanism::unsubscribeLockCurrentState), + accessory::getLockCurrentState, + accessory::subscribeLockCurrentState, + accessory::unsubscribeLockCurrentState), new LockTargetStateCharacteristic( - lockMechanism::getLockTargetState, - lockMechanism::setLockTargetState, - lockMechanism::subscribeLockTargetState, - lockMechanism::unsubscribeLockTargetState)); + accessory::getLockTargetState, + accessory::setLockTargetState, + accessory::subscribeLockTargetState, + accessory::unsubscribeLockTargetState)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/MotionSensorService.java b/src/main/java/io/github/hapjava/services/impl/MotionSensorService.java index 5d5cd5460..7b4216f32 100644 --- a/src/main/java/io/github/hapjava/services/impl/MotionSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/MotionSensorService.java @@ -1,8 +1,11 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.MotionSensorAccessory; -import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; @@ -16,12 +19,29 @@ public MotionSensorService(MotionDetectedCharacteristic motionDetectedCharacteri addCharacteristic(motionDetectedCharacteristic); } - public MotionSensorService(MotionSensorAccessory motionSensor) { + public MotionSensorService(MotionSensorAccessory accessory) { this( new MotionDetectedCharacteristic( - motionSensor::getMotionDetected, - motionSensor::subscribeMotionDetected, - motionSensor::unsubscribeMotionDetected)); + accessory::getMotionDetected, + accessory::subscribeMotionDetected, + accessory::unsubscribeMotionDetected)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithStatusActive) { + addOptionalCharacteristic( + new StatusActiveCharacteristic( + ((AccessoryWithStatusActive) accessory)::getStatusActive, + ((AccessoryWithStatusActive) accessory)::subscribeStatusActive, + ((AccessoryWithStatusActive) accessory)::unsubscribeStatusActive)); + } + if (accessory instanceof AccessoryWithStatusFault) { + addOptionalCharacteristic( + new StatusFaultCharacteristic( + ((AccessoryWithStatusFault) accessory)::getStatusFault, + ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, + ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/OccupancySensorService.java b/src/main/java/io/github/hapjava/services/impl/OccupancySensorService.java index 22b832edc..7a6ff235a 100644 --- a/src/main/java/io/github/hapjava/services/impl/OccupancySensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/OccupancySensorService.java @@ -1,8 +1,11 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.OccupancySensorAccessory; -import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; @@ -16,12 +19,29 @@ public OccupancySensorService(OccupancyDetectedCharacteristic occupancyDetectedC addCharacteristic(occupancyDetectedCharacteristic); } - public OccupancySensorService(OccupancySensorAccessory occupancySensor) { + public OccupancySensorService(OccupancySensorAccessory accessory) { this( new OccupancyDetectedCharacteristic( - occupancySensor::getOccupancyDetected, - occupancySensor::subscribeOccupancyDetected, - occupancySensor::unsubscribeOccupancyDetected)); + accessory::getOccupancyDetected, + accessory::subscribeOccupancyDetected, + accessory::unsubscribeOccupancyDetected)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithStatusActive) { + addOptionalCharacteristic( + new StatusActiveCharacteristic( + ((AccessoryWithStatusActive) accessory)::getStatusActive, + ((AccessoryWithStatusActive) accessory)::subscribeStatusActive, + ((AccessoryWithStatusActive) accessory)::unsubscribeStatusActive)); + } + if (accessory instanceof AccessoryWithStatusFault) { + addOptionalCharacteristic( + new StatusFaultCharacteristic( + ((AccessoryWithStatusFault) accessory)::getStatusFault, + ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, + ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/OutletService.java b/src/main/java/io/github/hapjava/services/impl/OutletService.java index 9367d8c84..780024f1f 100644 --- a/src/main/java/io/github/hapjava/services/impl/OutletService.java +++ b/src/main/java/io/github/hapjava/services/impl/OutletService.java @@ -1,7 +1,8 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.OutletAccessory; -import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.OnCharacteristic; import io.github.hapjava.characteristics.impl.outlet.OutletInUseCharacteristic; @@ -14,15 +15,20 @@ public OutletService(OnCharacteristic onState, OutletInUseCharacteristic outletI addCharacteristic(outletInUse); } - public OutletService(OutletAccessory outlet) { + public OutletService(OutletAccessory accessory) { this( new OnCharacteristic( - outlet::getPowerState, - outlet::setPowerState, - outlet::subscribePowerState, - outlet::unsubscribePowerState), + accessory::getPowerState, + accessory::setPowerState, + accessory::subscribePowerState, + accessory::unsubscribePowerState), new OutletInUseCharacteristic( - outlet::getOutletInUse, outlet::subscribeOutletInUse, outlet::unsubscribeOutletInUse)); + accessory::getOutletInUse, + accessory::subscribeOutletInUse, + accessory::unsubscribeOutletInUse)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/SecuritySystemService.java b/src/main/java/io/github/hapjava/services/impl/SecuritySystemService.java index ed1e1cb05..a4f1de059 100644 --- a/src/main/java/io/github/hapjava/services/impl/SecuritySystemService.java +++ b/src/main/java/io/github/hapjava/services/impl/SecuritySystemService.java @@ -1,7 +1,9 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.SecuritySystemAccessory; -import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; import io.github.hapjava.characteristics.impl.securitysystem.CurrentSecuritySystemStateCharacteristic; @@ -19,17 +21,27 @@ public SecuritySystemService( addCharacteristic(targetState); } - public SecuritySystemService(SecuritySystemAccessory securitySystem) { + public SecuritySystemService(SecuritySystemAccessory accessory) { this( new CurrentSecuritySystemStateCharacteristic( - securitySystem::getCurrentSecuritySystemState, - securitySystem::subscribeCurrentSecuritySystemState, - securitySystem::unsubscribeCurrentSecuritySystemState), + accessory::getCurrentSecuritySystemState, + accessory::subscribeCurrentSecuritySystemState, + accessory::unsubscribeCurrentSecuritySystemState), new TargetSecuritySystemStateCharacteristic( - securitySystem::getTargetSecuritySystemState, - securitySystem::setTargetSecuritySystemState, - securitySystem::subscribeTargetSecuritySystemState, - securitySystem::unsubscribeTargetSecuritySystemState)); + accessory::getTargetSecuritySystemState, + accessory::setTargetSecuritySystemState, + accessory::subscribeTargetSecuritySystemState, + accessory::unsubscribeTargetSecuritySystemState)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithStatusFault) { + addOptionalCharacteristic( + new StatusFaultCharacteristic( + ((AccessoryWithStatusFault) accessory)::getStatusFault, + ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, + ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/SmokeSensorService.java b/src/main/java/io/github/hapjava/services/impl/SmokeSensorService.java index fef1daf54..df361f9cd 100644 --- a/src/main/java/io/github/hapjava/services/impl/SmokeSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/SmokeSensorService.java @@ -1,8 +1,11 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.SmokeSensorAccessory; -import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; @@ -16,12 +19,29 @@ public SmokeSensorService(SmokeDetectedCharacteristic smokeDetectedCharacteristi addCharacteristic(smokeDetectedCharacteristic); } - public SmokeSensorService(SmokeSensorAccessory smokeSensor) { + public SmokeSensorService(SmokeSensorAccessory accessory) { this( new SmokeDetectedCharacteristic( - smokeSensor::getSmokeDetectedState, - smokeSensor::subscribeSmokeDetectedState, - smokeSensor::unsubscribeSmokeDetectedState)); + accessory::getSmokeDetectedState, + accessory::subscribeSmokeDetectedState, + accessory::unsubscribeSmokeDetectedState)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithStatusActive) { + addOptionalCharacteristic( + new StatusActiveCharacteristic( + ((AccessoryWithStatusActive) accessory)::getStatusActive, + ((AccessoryWithStatusActive) accessory)::subscribeStatusActive, + ((AccessoryWithStatusActive) accessory)::unsubscribeStatusActive)); + } + if (accessory instanceof AccessoryWithStatusFault) { + addOptionalCharacteristic( + new StatusFaultCharacteristic( + ((AccessoryWithStatusFault) accessory)::getStatusFault, + ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, + ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/SwitchService.java b/src/main/java/io/github/hapjava/services/impl/SwitchService.java index a50f5d49e..65a8f2124 100644 --- a/src/main/java/io/github/hapjava/services/impl/SwitchService.java +++ b/src/main/java/io/github/hapjava/services/impl/SwitchService.java @@ -1,7 +1,8 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.SwitchAccessory; -import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.OnCharacteristic; /** This service describes a binary switch. */ @@ -19,6 +20,9 @@ public SwitchService(SwitchAccessory accessory) { accessory::setSwitchState, accessory::subscribeSwitchState, accessory::unsubscribeSwitchState)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/TemperatureSensorService.java b/src/main/java/io/github/hapjava/services/impl/TemperatureSensorService.java index 6aef2e4ed..d1c6f2e62 100644 --- a/src/main/java/io/github/hapjava/services/impl/TemperatureSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/TemperatureSensorService.java @@ -1,8 +1,11 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.TemperatureSensorAccessory; -import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; @@ -17,12 +20,29 @@ public TemperatureSensorService( addCharacteristic(currentTemperatureCharacteristic); } - public TemperatureSensorService(TemperatureSensorAccessory temperatureSensor) { + public TemperatureSensorService(TemperatureSensorAccessory accessory) { this( new CurrentTemperatureCharacteristic( - temperatureSensor::getCurrentTemperature, - temperatureSensor::subscribeCurrentTemperature, - temperatureSensor::unsubscribeCurrentTemperature)); + accessory::getCurrentTemperature, + accessory::subscribeCurrentTemperature, + accessory::unsubscribeCurrentTemperature)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithStatusActive) { + addOptionalCharacteristic( + new StatusActiveCharacteristic( + ((AccessoryWithStatusActive) accessory)::getStatusActive, + ((AccessoryWithStatusActive) accessory)::subscribeStatusActive, + ((AccessoryWithStatusActive) accessory)::unsubscribeStatusActive)); + } + if (accessory instanceof AccessoryWithStatusFault) { + addOptionalCharacteristic( + new StatusFaultCharacteristic( + ((AccessoryWithStatusFault) accessory)::getStatusFault, + ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, + ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/ThermostatService.java b/src/main/java/io/github/hapjava/services/impl/ThermostatService.java index d82975a51..75b01e3a2 100644 --- a/src/main/java/io/github/hapjava/services/impl/ThermostatService.java +++ b/src/main/java/io/github/hapjava/services/impl/ThermostatService.java @@ -1,7 +1,8 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.ThermostatAccessory; -import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.humiditysensor.CurrentRelativeHumidityCharacteristic; import io.github.hapjava.characteristics.impl.humiditysensor.TargetRelativeHumidityCharacteristic; import io.github.hapjava.characteristics.impl.thermostat.CoolingThresholdTemperatureCharacteristic; @@ -29,31 +30,34 @@ public ThermostatService( addCharacteristic(temperatureUnitsCharacteristic); } - public ThermostatService(ThermostatAccessory thermostat) { + public ThermostatService(ThermostatAccessory accessory) { this( new CurrentHeatingCoolingStateCharacteristic( - thermostat::getCurrentState, - thermostat::subscribeCurrentState, - thermostat::unsubscribeCurrentState), + accessory::getCurrentState, + accessory::subscribeCurrentState, + accessory::unsubscribeCurrentState), new TargetHeatingCoolingStateCharacteristic( - thermostat::getTargetState, - thermostat::setTargetState, - thermostat::subscribeTargetState, - thermostat::unsubscribeTargetState), + accessory::getTargetState, + accessory::setTargetState, + accessory::subscribeTargetState, + accessory::unsubscribeTargetState), new CurrentTemperatureCharacteristic( - thermostat::getCurrentTemperature, - thermostat::subscribeCurrentTemperature, - thermostat::unsubscribeCurrentTemperature), + accessory::getCurrentTemperature, + accessory::subscribeCurrentTemperature, + accessory::unsubscribeCurrentTemperature), new TargetTemperatureCharacteristic( - thermostat::getTargetTemperature, - thermostat::setTargetTemperature, - thermostat::subscribeTargetTemperature, - thermostat::unsubscribeTargetTemperature), + accessory::getTargetTemperature, + accessory::setTargetTemperature, + accessory::subscribeTargetTemperature, + accessory::unsubscribeTargetTemperature), new TemperatureDisplayUnitCharacteristic( - thermostat::getTemperatureDisplayUnit, - thermostat::setTemperatureDisplayUnit, - thermostat::subscribeTemperatureDisplayUnit, - thermostat::unsubscribeTemperatureDisplayUnit)); + accessory::getTemperatureDisplayUnit, + accessory::setTemperatureDisplayUnit, + accessory::subscribeTemperatureDisplayUnit, + accessory::unsubscribeTemperatureDisplayUnit)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/ValveService.java b/src/main/java/io/github/hapjava/services/impl/ValveService.java index 3b906261b..88270fc13 100644 --- a/src/main/java/io/github/hapjava/services/impl/ValveService.java +++ b/src/main/java/io/github/hapjava/services/impl/ValveService.java @@ -1,10 +1,12 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.ValveAccessory; -import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; import io.github.hapjava.characteristics.impl.common.ActiveCharacteristic; import io.github.hapjava.characteristics.impl.common.InUseCharacteristic; import io.github.hapjava.characteristics.impl.common.IsConfiguredCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.ServiceLabelIndexCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; import io.github.hapjava.characteristics.impl.valve.SetDurationCharacteristic; @@ -26,17 +28,31 @@ public ValveService( addCharacteristic(valveTypeCharacteristic); } - public ValveService(ValveAccessory valve) { + public ValveService(ValveAccessory accessory) { this( new ActiveCharacteristic( - valve::getValveActive, - valve::setValveActive, - valve::subscribeValveActive, - valve::unsubscribeValveActive), + accessory::getValveActive, + accessory::setValveActive, + accessory::subscribeValveActive, + accessory::unsubscribeValveActive), new InUseCharacteristic( - valve::getValveInUse, valve::subscribeValveInUse, valve::unsubscribeValveInUse), + accessory::getValveInUse, + accessory::subscribeValveInUse, + accessory::unsubscribeValveInUse), new ValveTypeCharacteristic( - valve::getValveType, valve::subscribeValveType, valve::unsubscribeValveType)); + accessory::getValveType, + accessory::subscribeValveType, + accessory::unsubscribeValveType)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithStatusFault) { + addOptionalCharacteristic( + new StatusFaultCharacteristic( + ((AccessoryWithStatusFault) accessory)::getStatusFault, + ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, + ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/WindowCoveringService.java b/src/main/java/io/github/hapjava/services/impl/WindowCoveringService.java index 2adfd8ab6..bffaa3b53 100644 --- a/src/main/java/io/github/hapjava/services/impl/WindowCoveringService.java +++ b/src/main/java/io/github/hapjava/services/impl/WindowCoveringService.java @@ -1,7 +1,9 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.WindowCoveringAccessory; -import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithObstructionDetection; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.ObstructionDetectedCharacteristic; import io.github.hapjava.characteristics.impl.windowcovering.CurrentHorizontalTiltAngleCharacteristic; import io.github.hapjava.characteristics.impl.windowcovering.CurrentPositionCharacteristic; @@ -28,21 +30,31 @@ public WindowCoveringService( addCharacteristic(positionStateCharacteristic); } - public WindowCoveringService(WindowCoveringAccessory windowCovering) { + public WindowCoveringService(WindowCoveringAccessory accessory) { this( new TargetPositionCharacteristic( - windowCovering::getTargetPosition, - windowCovering::setTargetPosition, - windowCovering::subscribeTargetPosition, - windowCovering::unsubscribeTargetPosition), + accessory::getTargetPosition, + accessory::setTargetPosition, + accessory::subscribeTargetPosition, + accessory::unsubscribeTargetPosition), new CurrentPositionCharacteristic( - windowCovering::getCurrentPosition, - windowCovering::subscribeCurrentPosition, - windowCovering::unsubscribeCurrentPosition), + accessory::getCurrentPosition, + accessory::subscribeCurrentPosition, + accessory::unsubscribeCurrentPosition), new PositionStateCharacteristic( - windowCovering::getPositionState, - windowCovering::subscribePositionState, - windowCovering::unsubscribePositionState)); + accessory::getPositionState, + accessory::subscribePositionState, + accessory::unsubscribePositionState)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithObstructionDetection) { + addOptionalCharacteristic( + new ObstructionDetectedCharacteristic( + ((AccessoryWithObstructionDetection) accessory)::getObstructionDetected, + ((AccessoryWithObstructionDetection) accessory)::subscribeObstructionDetected, + ((AccessoryWithObstructionDetection) accessory)::unsubscribeObstructionDetected)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/WindowService.java b/src/main/java/io/github/hapjava/services/impl/WindowService.java index 2255afbd5..b984f536c 100644 --- a/src/main/java/io/github/hapjava/services/impl/WindowService.java +++ b/src/main/java/io/github/hapjava/services/impl/WindowService.java @@ -1,7 +1,9 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.WindowAccessory; -import io.github.hapjava.characteristics.impl.accessoryinformation.NameCharacteristic; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithObstructionDetection; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.ObstructionDetectedCharacteristic; import io.github.hapjava.characteristics.impl.windowcovering.CurrentPositionCharacteristic; import io.github.hapjava.characteristics.impl.windowcovering.HoldPositionCharacteristic; @@ -21,21 +23,31 @@ public WindowService( addCharacteristic(positionStateCharacteristic); } - public WindowService(WindowAccessory window) { + public WindowService(WindowAccessory accessory) { this( new CurrentPositionCharacteristic( - window::getCurrentPosition, - window::subscribeCurrentPosition, - window::unsubscribeCurrentPosition), + accessory::getCurrentPosition, + accessory::subscribeCurrentPosition, + accessory::unsubscribeCurrentPosition), new TargetPositionCharacteristic( - window::getTargetPosition, - window::setTargetPosition, - window::subscribeTargetPosition, - window::unsubscribeTargetPosition), + accessory::getTargetPosition, + accessory::setTargetPosition, + accessory::subscribeTargetPosition, + accessory::unsubscribeTargetPosition), new PositionStateCharacteristic( - window::getPositionState, - window::subscribePositionState, - window::unsubscribePositionState)); + accessory::getPositionState, + accessory::subscribePositionState, + accessory::unsubscribePositionState)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithObstructionDetection) { + addOptionalCharacteristic( + new ObstructionDetectedCharacteristic( + ((AccessoryWithObstructionDetection) accessory)::getObstructionDetected, + ((AccessoryWithObstructionDetection) accessory)::subscribeObstructionDetected, + ((AccessoryWithObstructionDetection) accessory)::unsubscribeObstructionDetected)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { From fc078d1c86a930eb8cd99947bf5e62e01328c7e4 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Mon, 16 Mar 2020 13:55:46 +0100 Subject: [PATCH 003/138] make service characteristics complete --- .../AccessoryWithAccessoryFlags.java | 30 +++++ .../AccessoryWithColorTemperature.java | 38 +++++++ ...ssoryWithCoolingThresholdTemperature.java} | 7 +- ...AccessoryWithCurrentHorizontalTilting.java | 29 +++++ .../AccessoryWithCurrentRelativeHumidity.java | 25 +++++ .../AccessoryWithCurrentVerticalTilting.java | 24 ++++ .../AccessoryWithFanState.java | 105 ------------------ ...essoryWithHeatingThresholdTemprature.java} | 2 +- .../AccessoryWithIsConfigured.java | 40 +++++++ .../AccessoryWithLockCurrentState.java | 30 +++++ .../AccessoryWithLockTargetState.java | 39 +++++++ .../AccessoryWithPhysicalControlsLock.java | 38 +++++++ .../AccessoryWithRotationDirection.java | 39 +++++++ .../AccessoryWithRotationSpeed.java | 38 +++++++ .../AccessoryWithSecurityAlarmType.java | 32 ++++++ .../AccessoryWithServiceLabelIndex.java | 19 ++++ .../AccessoryWithStatusLowBattery.java | 33 ++++++ .../AccessoryWithStatusTampered.java | 33 ++++++ .../AccessoryWithSwingMode.java | 38 +++++++ ...AccessoryWithTargetHorizontalTilting.java} | 21 +--- .../AccessoryWithTargetRelativeHumidity.java | 33 ++++++ .../AccessoryWithTargetVerticalTilting.java | 34 ++++++ .../AccessoryFlagsCharacteristic.java | 26 +++++ .../AccessoryFlagsEnum.java | 35 ++++++ ...SecuritySystemAlarmTypeCharacteristic.java | 4 +- .../impl/AccessoryInformationService.java | 13 +++ .../impl/CarbonDioxideSensorService.java | 16 +++ .../impl/CarbonMonoxideSensorService.java | 16 +++ .../services/impl/ContactSensorService.java | 16 +++ .../hapjava/services/impl/DoorService.java | 5 + .../hapjava/services/impl/FanService.java | 51 +++++++++ .../impl/GarageDoorOpenerService.java | 17 +++ .../services/impl/HumiditySensorService.java | 16 +++ .../services/impl/LeakSensorService.java | 16 +++ .../services/impl/LightSensorService.java | 22 +++- .../services/impl/LightbulbService.java | 33 ++++++ .../services/impl/MotionSensorService.java | 16 +++ .../services/impl/OccupancySensorService.java | 16 +++ .../services/impl/SecuritySystemService.java | 20 +++- .../services/impl/SmokeSensorService.java | 16 +++ .../impl/TemperatureSensorService.java | 16 +++ .../services/impl/ThermostatService.java | 35 ++++++ .../hapjava/services/impl/ValveService.java | 46 +++++++- .../services/impl/WindowCoveringService.java | 56 +++++++++- .../hapjava/services/impl/WindowService.java | 6 + 45 files changed, 1098 insertions(+), 142 deletions(-) create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithAccessoryFlags.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithColorTemperature.java rename src/main/java/io/github/hapjava/accessories/optionalcharacteristic/{AccessoryWithCoolingThresholdTemprature.java => AccessoryWithCoolingThresholdTemperature.java} (88%) create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentHorizontalTilting.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentRelativeHumidity.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentVerticalTilting.java rename src/main/java/io/github/hapjava/accessories/optionalcharacteristic/{AccessoryWithHearingThresholdTemprature.java => AccessoryWithHeatingThresholdTemprature.java} (94%) create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithIsConfigured.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithLockCurrentState.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithLockTargetState.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPhysicalControlsLock.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationDirection.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationSpeed.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSecurityAlarmType.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithServiceLabelIndex.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusLowBattery.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusTampered.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSwingMode.java rename src/main/java/io/github/hapjava/accessories/optionalcharacteristic/{AccessoryWithHorizontalTilting.java => AccessoryWithTargetHorizontalTilting.java} (62%) create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetRelativeHumidity.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetVerticalTilting.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/AccessoryFlagsCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/AccessoryFlagsEnum.java diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithAccessoryFlags.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithAccessoryFlags.java new file mode 100644 index 000000000..05634a3e8 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithAccessoryFlags.java @@ -0,0 +1,30 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.accessoryinformation.AccessoryFlagsEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with accessory flags, which indicates that additional setup is required + * + * @author Eugen Freiter + */ +public interface AccessoryWithAccessoryFlags { + + /** + * When set indicates accessory requires additional setup. + * + * @return a future that will contain the accessory flags + */ + CompletableFuture getAccessoryFlags(); + + /** + * Subscribes to changes in accessory flags + * + * @param callback the function to call when the accessory flags changes. + */ + void subscribeAccessoryFlags(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the accessory flags . */ + void unsubscribeAccessoryFlags(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithColorTemperature.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithColorTemperature.java new file mode 100644 index 000000000..c6bde73da --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithColorTemperature.java @@ -0,0 +1,38 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with color temperature. + * + * @author Eugen Freiter + */ +public interface AccessoryWithColorTemperature { + + /** + * Retrieves the color temperature + * + * @return a future that will contain the brightness, expressed as an integer between 0 and 100. + */ + CompletableFuture getColorTemperature(); + + /** + * Sets the color temperature + * + * @param value the brightness, on a scale of 0 to 100, to set + * @return a future that completes when the brightness is changed + * @throws Exception when the brightness cannot be set + */ + CompletableFuture setColorTemperature(Integer value) throws Exception; + + /** + * Subscribes to changes in color temperature. + * + * @param callback the function to call when the color temperature changes. + */ + void subscribeColorTemperature(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the color temperature. */ + void unsubscribeColorTemperature(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCoolingThresholdTemprature.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCoolingThresholdTemperature.java similarity index 88% rename from src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCoolingThresholdTemprature.java rename to src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCoolingThresholdTemperature.java index 170c921d9..6e529bd53 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCoolingThresholdTemprature.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCoolingThresholdTemperature.java @@ -3,7 +3,12 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -public interface AccessoryWithCoolingThresholdTemprature { +/** + * Accessory with cooling threshold temperature. + * + * @author Eugen Freiter + */ +public interface AccessoryWithCoolingThresholdTemperature { /** * Retrieves the temperature above which the thermostat should begin cooling. diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentHorizontalTilting.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentHorizontalTilting.java new file mode 100644 index 000000000..b5ce298bf --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentHorizontalTilting.java @@ -0,0 +1,29 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with current horizontal tilting characteristic. + * + * @author Andy Lintner + */ +public interface AccessoryWithCurrentHorizontalTilting { + + /** + * Retrieves the current horizontal tilt angle + * + * @return a future that will contain the position as a value between -90 and 90 + */ + CompletableFuture getCurrentHorizontalTiltAngle(); + + /** + * Subscribes to changes in the current horizontal tilt angle. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentHorizontalTiltAngle(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current horizontal tilt angle */ + void unsubscribeCurrentHorizontalTiltAngle(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentRelativeHumidity.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentRelativeHumidity.java new file mode 100644 index 000000000..562e42649 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentRelativeHumidity.java @@ -0,0 +1,25 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** accessory with current relative humidity. */ +public interface AccessoryWithCurrentRelativeHumidity { + + /** + * Retrieves the current relative humidity. + * + * @return a future that will contain the current relative humidity. + */ + CompletableFuture getCurrentRelativeHumidity(); + + /** + * Subscribes to changes in current relative humidity. + * + * @param callback the function to call when the current relative humidity changes. + */ + void subscribeCurrentRelativeHumidity(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current relative humidity. */ + void unsubscribeCurrentRelativeHumidity(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentVerticalTilting.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentVerticalTilting.java new file mode 100644 index 000000000..3b996c925 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentVerticalTilting.java @@ -0,0 +1,24 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** Accessory with current vertical tilting characteristic. */ +public interface AccessoryWithCurrentVerticalTilting { + /** + * Retrieves the current vertical tilt angle + * + * @return a future that will contain the position as a value between -90 and 90 + */ + CompletableFuture getCurrentVerticalTiltAngle(); + + /** + * Subscribes to changes in the current vertical tilt angle. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentVerticalTiltAngle(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current vertical tilt angle */ + void unsubscribeCurrentVerticalTiltAngle(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFanState.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFanState.java index 548ee34e5..e2359f85c 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFanState.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFanState.java @@ -2,9 +2,6 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import io.github.hapjava.characteristics.impl.fan.CurrentFanStateEnum; -import io.github.hapjava.characteristics.impl.fan.LockPhysicalControlsEnum; -import io.github.hapjava.characteristics.impl.fan.RotationDirectionEnum; -import io.github.hapjava.characteristics.impl.fan.SwingModeEnum; import io.github.hapjava.characteristics.impl.fan.TargetFanStateEnum; import java.util.concurrent.CompletableFuture; @@ -37,75 +34,6 @@ public interface AccessoryWithFanState { */ CompletableFuture setTargetFanState(TargetFanStateEnum targetState); - /** - * Retrieves the swing mode of the fan. - * - * @return a future that will contain the swing mode - */ - CompletableFuture getSwingMode(); - - /** - * Set the swing mode of the fan (DISABLED, ENABLED). - * - * @param swingMode swing mode - * @return a future that completes when the change is made - */ - CompletableFuture setSwingMode(SwingModeEnum swingMode); - - /** - * OPTIONAL: Retrieves the lock controls of the fan. - * - * @return a future that will contain the lock controls - */ - CompletableFuture getLockControls(); - - /** - * Set the lock controls of the fan (DISABLED, ENABLED). - * - * @param lockControls lock controls mode - * @return a future that completes when the change is made - */ - CompletableFuture setLockControls(LockPhysicalControlsEnum lockControls); - - /** - * Retrieves the current rotation direction of the fan. - * - * @return a future that will contain the direction - */ - CompletableFuture getRotationDirection(); - - /** - * Retrieves the current speed of the fan's rotation - * - * @return a future that will contain the speed, expressed as an integer between 0 and 100. - */ - CompletableFuture getRotationSpeed(); - - /** - * Sets the rotation direction of the fan - * - * @param direction the direction to set - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setRotationDirection(RotationDirectionEnum direction) throws Exception; - - /** - * OPTIONAL: Sets the speed of the fan's rotation - * - * @param speed the speed to set, expressed as an integer between 0 and 100. - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setRotationSpeed(Integer speed) throws Exception; - - /** - * Subscribes to changes in the rotation direction of the fan. - * - * @param callback the function to call when the direction changes. - */ - void subscribeRotationDirection(HomekitCharacteristicChangeCallback callback); - /** * Subscribes to changes in the current fan state. * @@ -120,42 +48,9 @@ public interface AccessoryWithFanState { */ void subscribeTargetFanState(HomekitCharacteristicChangeCallback callback); - /** - * Subscribes to changes in the rotation speed of the fan. - * - * @param callback the function to call when the speed changes. - */ - void subscribeRotationSpeed(HomekitCharacteristicChangeCallback callback); - - /** - * Subscribes to changes in the swing mode of the fan. - * - * @param callback the function to call when the swing mode changes. - */ - void subscribeSwingMode(HomekitCharacteristicChangeCallback callback); - - /** - * Subscribes to changes in the lock controls of the fan. - * - * @param callback the function to call when the lock controls changes. - */ - void subscribeLockControls(HomekitCharacteristicChangeCallback callback); - - /** Unsubscribes from changes in the rotation direction of the fan. */ - void unsubscribeRotationDirection(); - - /** Unsubscribes from changes in the fan's rotation speed. */ - void unsubscribeRotationSpeed(); - /** Unsubscribes from changes in the current state of the fan. */ void unsubscribeCurrentFanState(); /** Unsubscribes from changes in the target state of the fan. */ void unsubscribeTargetFanState(); - - /** Unsubscribes from changes in the swing mode of the fan. */ - void unsubscribeSwingMode(); - - /** Unsubscribes from changes in the lock controls of the fan. */ - void unsubscribeLockControls(); } diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHearingThresholdTemprature.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHeatingThresholdTemprature.java similarity index 94% rename from src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHearingThresholdTemprature.java rename to src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHeatingThresholdTemprature.java index af18baca3..536a18075 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHearingThresholdTemprature.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHeatingThresholdTemprature.java @@ -3,7 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -public interface AccessoryWithHearingThresholdTemprature { +public interface AccessoryWithHeatingThresholdTemprature { /** * Retrieves the temperature below which the thermostat should begin heating. diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithIsConfigured.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithIsConfigured.java new file mode 100644 index 000000000..b80f1ad65 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithIsConfigured.java @@ -0,0 +1,40 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.common.IsConfiguredEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with isConfigured characteristics. This characteristic describes if the service is + * configured for use. For example, all of the valves in an irrigation system may not be configured + * depending on physical wire connection. + * + * @author Eugen Freiter + */ +public interface AccessoryWithIsConfigured { + + /** + * isConfigured state + * + * @return a future that will contain the isConfigured state + */ + CompletableFuture getIsConfigured(); + + /** + * Set the isConfigured (NOT_CONFIGURED, CONFIGURED). + * + * @param isConfigured isConfigured state + * @return a future that completes when the change is made + */ + CompletableFuture setIsConfigured(IsConfiguredEnum isConfigured); + + /** + * Subscribes to changes in isConfigured state + * + * @param callback the function to call when the isConfigured state changes. + */ + void subscribeIsConfigured(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the isConfigured state. */ + void unsubscribeIsConfigured(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithLockCurrentState.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithLockCurrentState.java new file mode 100644 index 000000000..2a63b64f5 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithLockCurrentState.java @@ -0,0 +1,30 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.lock.LockCurrentStateEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with current lock state. + * + * @author Eugen Freiter + */ +public interface AccessoryWithLockCurrentState { + + /** + * Retrieves the lock states. The current state of the physical security mechanism (e.g. deadbolt) + * + * @return a future with the value + */ + CompletableFuture getLockCurrentState(); + + /** + * Subscribes to changes in lock current state. + * + * @param callback the function when the lock current state changes + */ + void subscribeLockCurrentState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes */ + void unsubscribeLockCurrentState(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithLockTargetState.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithLockTargetState.java new file mode 100644 index 000000000..b35ca0542 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithLockTargetState.java @@ -0,0 +1,39 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.lock.LockTargetStateEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with lock target state. + * + * @author Eugen Freiter + */ +public interface AccessoryWithLockTargetState { + + /** + * Retrieves the lock states. The target state of the physical security mechanism (e.g. deadbolt) + * + * @return a future with the value + */ + CompletableFuture getLockTargetState(); + + /** + * Sets the lock target state + * + * @param state the target state to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setLockTargetState(LockTargetStateEnum state) throws Exception; + + /** + * Subscribes to changes in lock target state. + * + * @param callback the function when the lock target state changes + */ + void subscribeLockTargetState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes */ + void unsubscribeLockTargetState(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPhysicalControlsLock.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPhysicalControlsLock.java new file mode 100644 index 000000000..903abe229 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPhysicalControlsLock.java @@ -0,0 +1,38 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.fan.LockPhysicalControlsEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with physical controls which can be locked, e.g. child lock + * + * @author Eugen Freiter + */ +public interface AccessoryWithPhysicalControlsLock { + + /** + * OPTIONAL: Retrieves the lock controls. + * + * @return a future that will contain the lock controls + */ + CompletableFuture getLockControls(); + + /** + * Set the lock controls (DISABLED, ENABLED). + * + * @param lockControls lock controls mode + * @return a future that completes when the change is made + */ + CompletableFuture setLockControls(LockPhysicalControlsEnum lockControls); + + /** + * Subscribes to changes in the lock controls. + * + * @param callback the function to call when the lock controls changes. + */ + void subscribeLockControls(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the lock controls. */ + void unsubscribeLockControls(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationDirection.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationDirection.java new file mode 100644 index 000000000..8c77e0ba8 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationDirection.java @@ -0,0 +1,39 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.fan.RotationDirectionEnum; +import java.util.concurrent.CompletableFuture; + +/** + * accessoory with rotation direction characteristics. + * + * @author Eugen Freiter + */ +public interface AccessoryWithRotationDirection { + + /** + * Retrieves the current rotation direction. + * + * @return a future that will contain the direction + */ + CompletableFuture getRotationDirection(); + + /** + * Sets the rotation direction + * + * @param direction the direction to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setRotationDirection(RotationDirectionEnum direction) throws Exception; + + /** + * Subscribes to changes in the rotation direction. + * + * @param callback the function to call when the direction changes. + */ + void subscribeRotationDirection(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the rotation direction. */ + void unsubscribeRotationDirection(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationSpeed.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationSpeed.java new file mode 100644 index 000000000..fcc27f32f --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationSpeed.java @@ -0,0 +1,38 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * accessory with rotation speed characteristics. + * + * @author Eugen Freiter + */ +public interface AccessoryWithRotationSpeed { + + /** + * Retrieves the current speed of the rotation + * + * @return a future that will contain the speed, expressed as an integer between 0 and 100. + */ + CompletableFuture getRotationSpeed(); + + /** + * Sets the speed of the rotation + * + * @param speed the speed to set, expressed as an integer between 0 and 100. + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setRotationSpeed(Integer speed) throws Exception; + + /** + * Subscribes to changes in the rotation speed. + * + * @param callback the function to call when the speed changes. + */ + void subscribeRotationSpeed(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the rotation speed. */ + void unsubscribeRotationSpeed(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSecurityAlarmType.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSecurityAlarmType.java new file mode 100644 index 000000000..ca707f8df --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSecurityAlarmType.java @@ -0,0 +1,32 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.securitysystem.SecuritySystemAlarmTypeEnum; +import java.util.concurrent.CompletableFuture; + +/** + * This characteristic describes the type of alarm triggered by a security system. A value of 1 + * indicates an ʼunknownʼ cause. Value should revert to 0 when the alarm conditions are cleared. + * + * @author Eugen Freiter + */ +public interface AccessoryWithSecurityAlarmType { + + /** + * A value of 1 indicates an ʼunknownʼ cause. Value should revert to 0 when the alarm conditions + * are cleared. + * + * @return a future with the value + */ + CompletableFuture getSecurityAlarmType(); + + /** + * Subscribes to changes in status alarm type + * + * @param callback the function when the alarm type changes + */ + void subscribeSecurityAlarmType(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes */ + void unsubscribeSecurityAlarmType(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithServiceLabelIndex.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithServiceLabelIndex.java new file mode 100644 index 000000000..b350654c2 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithServiceLabelIndex.java @@ -0,0 +1,19 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with service label index. This characteristic should be used identify the index of the + * label that maps to ”Service Label Namespace” used by the accessory. + * + * @author Eugen Freiter + */ +public interface AccessoryWithServiceLabelIndex { + + /** + * Retrieves the service label index. + * + * @return a future with the service label index + */ + CompletableFuture getServiceLabelIndex(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusLowBattery.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusLowBattery.java new file mode 100644 index 000000000..964d016ab --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusLowBattery.java @@ -0,0 +1,33 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryEnum; +import java.util.concurrent.CompletableFuture; + +/** + * This characteristic describes an accessoryʼs battery status. A status of 1 indicates that the + * battery level of the accessory is low. Value should return to 0 when the battery charges to a + * level thats above the low threshold. + * + * @author Eugen Freiter + */ +public interface AccessoryWithStatusLowBattery { + + /** + * A status of 1 indicates that the battery level of the accessory is low. Value should return to + * 0 when the battery charges to a level thats above the low threshold. + * + * @return a future with the value + */ + CompletableFuture getStatusLowBattery(); + + /** + * Subscribes to changes in status low battery. + * + * @param callback the function when the status low batter changes + */ + void subscribeStatusLowBattery(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes */ + void unsubscribeStatusLowBattery(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusTampered.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusTampered.java new file mode 100644 index 000000000..5637cb7ee --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusTampered.java @@ -0,0 +1,33 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.common.StatusTamperedEnum; +import java.util.concurrent.CompletableFuture; + +/** + * This characteristic describes an accessory which has been tampered with. A status of 1 indicates + * that the accessory has been tampered with. Value should return to 0 when the accessory has been + * reset to a non-tampered state, + * + * @author Eugen Freiter + */ +public interface AccessoryWithStatusTampered { + + /** + * Retrieves the status tampered. A status of 1 indicates that the accessory has been tampered + * with. Value should return to 0 when the accessory has been reset to a non-tampered state, + * + * @return a future with the value + */ + CompletableFuture getStatusTampered(); + + /** + * Subscribes to changes in status tampered. + * + * @param callback the function when the status tampered changes + */ + void subscribeStatusTampered(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes */ + void unsubscribeStatusTampered(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSwingMode.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSwingMode.java new file mode 100644 index 000000000..92b11058f --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSwingMode.java @@ -0,0 +1,38 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.fan.SwingModeEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with Swing Mode characteristics. + * + * @author Eugen Freiter + */ +public interface AccessoryWithSwingMode { + + /** + * Retrieves the swing mode. + * + * @return a future that will contain the swing mode + */ + CompletableFuture getSwingMode(); + + /** + * Set the swing mode (DISABLED, ENABLED). + * + * @param swingMode swing mode + * @return a future that completes when the change is made + */ + CompletableFuture setSwingMode(SwingModeEnum swingMode); + + /** + * Subscribes to changes in the swing mode. + * + * @param callback the function to call when the swing mode changes. + */ + void subscribeSwingMode(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the swing mode. */ + void unsubscribeSwingMode(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHorizontalTilting.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetHorizontalTilting.java similarity index 62% rename from src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHorizontalTilting.java rename to src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetHorizontalTilting.java index 58556c9d0..cc154520d 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHorizontalTilting.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetHorizontalTilting.java @@ -4,18 +4,11 @@ import java.util.concurrent.CompletableFuture; /** - * Accessory with horizontal tilting characteristic. + * Accessory with target horizontal tilting characteristic. * * @author Andy Lintner */ -public interface AccessoryWithHorizontalTilting { - - /** - * Retrieves the current horizontal tilt angle - * - * @return a future that will contain the position as a value between -90 and 90 - */ - CompletableFuture getCurrentHorizontalTiltAngle(); +public interface AccessoryWithTargetHorizontalTilting { /** * Retrieves the target horizontal tilt angle @@ -33,13 +26,6 @@ public interface AccessoryWithHorizontalTilting { */ CompletableFuture setTargetHorizontalTiltAngle(int angle) throws Exception; - /** - * Subscribes to changes in the current horizontal tilt angle. - * - * @param callback the function to call when the state changes. - */ - void subscribeCurrentHorizontalTiltAngle(HomekitCharacteristicChangeCallback callback); - /** * Subscribes to changes in the target horizontal tilt angle. * @@ -47,9 +33,6 @@ public interface AccessoryWithHorizontalTilting { */ void subscribeTargetHorizontalTiltAngle(HomekitCharacteristicChangeCallback callback); - /** Unsubscribes from changes in the current horizontal tilt angle */ - void unsubscribeCurrentHorizontalTiltAngle(); - /** Unsubscribes from changes in the target horizontal tilt angle */ void unsubscribeTargetHorizontalTiltAngle(); } diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetRelativeHumidity.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetRelativeHumidity.java new file mode 100644 index 000000000..23ac5fd97 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetRelativeHumidity.java @@ -0,0 +1,33 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** accessory with target relative humidity. */ +public interface AccessoryWithTargetRelativeHumidity { + + /** + * Retrieves the target relative humidity. + * + * @return a future that will contain the target relative humidity. + */ + CompletableFuture getTargetRelativeHumidity(); + + /** + * Sets the target relative humidity. + * + * @param value the target relative humidity. + * @throws Exception when the target relative humidity cannot be changed. + */ + void setTargetRelativeHumidity(Double value) throws Exception; + + /** + * Subscribes to changes in the target relative humidity. + * + * @param callback the function to call when the target relative humidity changes. + */ + void subscribeTargetRelativeHumidity(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the target relative humidity. */ + void unsubscribeTargetRelativeHumidity(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetVerticalTilting.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetVerticalTilting.java new file mode 100644 index 000000000..e87607204 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetVerticalTilting.java @@ -0,0 +1,34 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** Accessory with target vertical tilting characteristic. */ +public interface AccessoryWithTargetVerticalTilting { + + /** + * Retrieves the target vertical tilt angle + * + * @return a future that will contain the target position as a value between -90 and 90 + */ + CompletableFuture getTargetVerticalTiltAngle(); + + /** + * Sets the target position + * + * @param angle the target angle to set, as a value between -90 and 90 + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setTargetVerticalTiltAngle(int angle) throws Exception; + + /** + * Subscribes to changes in the target vertical tilt angle. + * + * @param callback the function to call when the state changes. + */ + void subscribeTargetVerticalTiltAngle(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the target vertical tilt angle */ + void unsubscribeTargetVerticalTiltAngle(); +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/AccessoryFlagsCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/AccessoryFlagsCharacteristic.java new file mode 100644 index 000000000..3dece74b7 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/AccessoryFlagsCharacteristic.java @@ -0,0 +1,26 @@ +package io.github.hapjava.characteristics.impl.accessoryinformation; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** When set indicates accessory requires additional setup. */ +public class AccessoryFlagsCharacteristic extends EnumCharacteristic { + + public AccessoryFlagsCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000A6-0000-1000-8000-0026BB765291", + "accessory flags", + 1, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/AccessoryFlagsEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/AccessoryFlagsEnum.java new file mode 100644 index 000000000..a849383e8 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/AccessoryFlagsEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.accessoryinformation; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0x0001 (bit0) ”Requires additional setup” 0x0002 - 0xFFFF ”Reserved” */ +public enum AccessoryFlagsEnum implements CharacteristicEnum { + NO_FLAGS(0), + REQUIRES_ADDITIONAL_SETUP(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(AccessoryFlagsEnum.values()) + .collect(Collectors.toMap(AccessoryFlagsEnum::getCode, t -> t)); + } + + public static AccessoryFlagsEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + AccessoryFlagsEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java index a73155d1b..b5b2b04a7 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java @@ -12,10 +12,10 @@ * indicates an ʼunknownʼ cause. Value should revert to 0 when the alarm conditions are cleared. */ public class SecuritySystemAlarmTypeCharacteristic - extends EnumCharacteristic { + extends EnumCharacteristic { public SecuritySystemAlarmTypeCharacteristic( - Supplier> getter, + Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( diff --git a/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java b/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java index 127bb0179..2746118bf 100644 --- a/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java +++ b/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java @@ -1,7 +1,9 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.HomekitAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithAccessoryFlags; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithHardwareRevision; +import io.github.hapjava.characteristics.impl.accessoryinformation.AccessoryFlagsCharacteristic; import io.github.hapjava.characteristics.impl.accessoryinformation.FirmwareRevisionCharacteristic; import io.github.hapjava.characteristics.impl.accessoryinformation.HardwareRevisionCharacteristic; import io.github.hapjava.characteristics.impl.accessoryinformation.IdentifyCharacteristic; @@ -48,9 +50,20 @@ public AccessoryInformationService(HomekitAccessory accessory) { new HardwareRevisionCharacteristic( ((AccessoryWithHardwareRevision) accessory)::getHardwareRevision)); } + if (accessory instanceof AccessoryWithAccessoryFlags) { + addOptionalCharacteristic( + new AccessoryFlagsCharacteristic( + ((AccessoryWithAccessoryFlags) accessory)::getAccessoryFlags, + ((AccessoryWithAccessoryFlags) accessory)::subscribeAccessoryFlags, + ((AccessoryWithAccessoryFlags) accessory)::unsubscribeAccessoryFlags)); + } } public void addOptionalCharacteristic(HardwareRevisionCharacteristic hardwareRevision) { addCharacteristic(hardwareRevision); } + + public void addOptionalCharacteristic(AccessoryFlagsCharacteristic accessoryFlagsCharacteristic) { + addCharacteristic(accessoryFlagsCharacteristic); + } } diff --git a/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java b/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java index bcd009dfd..eaf4e44d6 100644 --- a/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java @@ -5,6 +5,8 @@ import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusLowBattery; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusTampered; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; import io.github.hapjava.characteristics.impl.carbondioxidesensor.CarbonDioxideDetectedCharacteristic; import io.github.hapjava.characteristics.impl.carbondioxidesensor.CarbonDioxideLevelCharacteristic; @@ -59,6 +61,20 @@ public CarbonDioxideSensorService(CarbonDioxideSensorAccessory accessory) { ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); } + if (accessory instanceof AccessoryWithStatusTampered) { + addOptionalCharacteristic( + new StatusTamperedCharacteristic( + ((AccessoryWithStatusTampered) accessory)::getStatusTampered, + ((AccessoryWithStatusTampered) accessory)::subscribeStatusTampered, + ((AccessoryWithStatusTampered) accessory)::unsubscribeStatusTampered)); + } + if (accessory instanceof AccessoryWithStatusLowBattery) { + addOptionalCharacteristic( + new StatusLowBatteryCharacteristic( + ((AccessoryWithStatusLowBattery) accessory)::getStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::subscribeStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::unsubscribeStatusLowBattery)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/CarbonMonoxideSensorService.java b/src/main/java/io/github/hapjava/services/impl/CarbonMonoxideSensorService.java index 86c6d7b7d..11b66c5c4 100644 --- a/src/main/java/io/github/hapjava/services/impl/CarbonMonoxideSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/CarbonMonoxideSensorService.java @@ -5,6 +5,8 @@ import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusLowBattery; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusTampered; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; import io.github.hapjava.characteristics.impl.carbonmonoxidesensor.CarbonMonoxideDetectedCharacteristic; import io.github.hapjava.characteristics.impl.carbonmonoxidesensor.CarbonMonoxideLevelCharacteristic; @@ -59,6 +61,20 @@ public CarbonMonoxideSensorService(CarbonMonoxideSensorAccessory accessory) { ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); } + if (accessory instanceof AccessoryWithStatusTampered) { + addOptionalCharacteristic( + new StatusTamperedCharacteristic( + ((AccessoryWithStatusTampered) accessory)::getStatusTampered, + ((AccessoryWithStatusTampered) accessory)::subscribeStatusTampered, + ((AccessoryWithStatusTampered) accessory)::unsubscribeStatusTampered)); + } + if (accessory instanceof AccessoryWithStatusLowBattery) { + addOptionalCharacteristic( + new StatusLowBatteryCharacteristic( + ((AccessoryWithStatusLowBattery) accessory)::getStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::subscribeStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::unsubscribeStatusLowBattery)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/ContactSensorService.java b/src/main/java/io/github/hapjava/services/impl/ContactSensorService.java index aa300955f..675553697 100644 --- a/src/main/java/io/github/hapjava/services/impl/ContactSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/ContactSensorService.java @@ -4,6 +4,8 @@ import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusLowBattery; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusTampered; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; @@ -43,6 +45,20 @@ public ContactSensorService(ContactSensorAccessory accessory) { ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); } + if (accessory instanceof AccessoryWithStatusTampered) { + addOptionalCharacteristic( + new StatusTamperedCharacteristic( + ((AccessoryWithStatusTampered) accessory)::getStatusTampered, + ((AccessoryWithStatusTampered) accessory)::subscribeStatusTampered, + ((AccessoryWithStatusTampered) accessory)::unsubscribeStatusTampered)); + } + if (accessory instanceof AccessoryWithStatusLowBattery) { + addOptionalCharacteristic( + new StatusLowBatteryCharacteristic( + ((AccessoryWithStatusLowBattery) accessory)::getStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::subscribeStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::unsubscribeStatusLowBattery)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/DoorService.java b/src/main/java/io/github/hapjava/services/impl/DoorService.java index 62a191c8c..c4c3b40b3 100644 --- a/src/main/java/io/github/hapjava/services/impl/DoorService.java +++ b/src/main/java/io/github/hapjava/services/impl/DoorService.java @@ -1,6 +1,7 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.DoorAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithHoldPosition; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithObstructionDetection; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; @@ -41,6 +42,10 @@ public DoorService(DoorAccessory accessory) { if (accessory instanceof AccessoryWithName) { addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); } + if (accessory instanceof AccessoryWithHoldPosition) { + addOptionalCharacteristic( + new HoldPositionCharacteristic(((AccessoryWithHoldPosition) accessory)::setHoldPosition)); + } if (accessory instanceof AccessoryWithObstructionDetection) { addOptionalCharacteristic( new ObstructionDetectedCharacteristic( diff --git a/src/main/java/io/github/hapjava/services/impl/FanService.java b/src/main/java/io/github/hapjava/services/impl/FanService.java index a09c5b61f..24d2bc052 100644 --- a/src/main/java/io/github/hapjava/services/impl/FanService.java +++ b/src/main/java/io/github/hapjava/services/impl/FanService.java @@ -1,7 +1,12 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.FanAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithFanState; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithPhysicalControlsLock; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithRotationDirection; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithRotationSpeed; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithSwingMode; import io.github.hapjava.characteristics.impl.common.ActiveCharacteristic; import io.github.hapjava.characteristics.impl.common.ActiveEnum; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; @@ -30,6 +35,52 @@ public FanService(FanAccessory accessory) { if (accessory instanceof AccessoryWithName) { addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); } + + if (accessory instanceof AccessoryWithFanState) { + addOptionalCharacteristic( + new CurrentFanStateCharacteristic( + ((AccessoryWithFanState) accessory)::getCurrentFanState, + ((AccessoryWithFanState) accessory)::subscribeCurrentFanState, + ((AccessoryWithFanState) accessory)::unsubscribeCurrentFanState)); + addOptionalCharacteristic( + new TargetFanStateCharacteristic( + ((AccessoryWithFanState) accessory)::getTargetFanState, + ((AccessoryWithFanState) accessory)::setTargetFanState, + ((AccessoryWithFanState) accessory)::subscribeTargetFanState, + ((AccessoryWithFanState) accessory)::unsubscribeTargetFanState)); + } + if (accessory instanceof AccessoryWithRotationDirection) { + addOptionalCharacteristic( + new RotationDirectionCharacteristic( + ((AccessoryWithRotationDirection) accessory)::getRotationDirection, + ((AccessoryWithRotationDirection) accessory)::setRotationDirection, + ((AccessoryWithRotationDirection) accessory)::subscribeRotationDirection, + ((AccessoryWithRotationDirection) accessory)::unsubscribeRotationDirection)); + } + if (accessory instanceof AccessoryWithRotationSpeed) { + addOptionalCharacteristic( + new RotationSpeedCharacteristic( + ((AccessoryWithRotationSpeed) accessory)::getRotationSpeed, + ((AccessoryWithRotationSpeed) accessory)::setRotationSpeed, + ((AccessoryWithRotationSpeed) accessory)::subscribeRotationSpeed, + ((AccessoryWithRotationSpeed) accessory)::unsubscribeRotationSpeed)); + } + if (accessory instanceof AccessoryWithSwingMode) { + addOptionalCharacteristic( + new SwingModeCharacteristic( + ((AccessoryWithSwingMode) accessory)::getSwingMode, + ((AccessoryWithSwingMode) accessory)::setSwingMode, + ((AccessoryWithSwingMode) accessory)::subscribeSwingMode, + ((AccessoryWithSwingMode) accessory)::unsubscribeSwingMode)); + } + if (accessory instanceof AccessoryWithPhysicalControlsLock) { + addOptionalCharacteristic( + new LockPhysicalControlsCharacteristic( + ((AccessoryWithPhysicalControlsLock) accessory)::getLockControls, + ((AccessoryWithPhysicalControlsLock) accessory)::setLockControls, + ((AccessoryWithPhysicalControlsLock) accessory)::subscribeLockControls, + ((AccessoryWithPhysicalControlsLock) accessory)::unsubscribeLockControls)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/GarageDoorOpenerService.java b/src/main/java/io/github/hapjava/services/impl/GarageDoorOpenerService.java index c1c7346f5..86078ee05 100644 --- a/src/main/java/io/github/hapjava/services/impl/GarageDoorOpenerService.java +++ b/src/main/java/io/github/hapjava/services/impl/GarageDoorOpenerService.java @@ -1,6 +1,8 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.GarageDoorOpenerAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithLockCurrentState; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithLockTargetState; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.ObstructionDetectedCharacteristic; @@ -43,6 +45,21 @@ public GarageDoorOpenerService(GarageDoorOpenerAccessory accessory) { if (accessory instanceof AccessoryWithName) { addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); } + if (accessory instanceof AccessoryWithLockCurrentState) { + addOptionalCharacteristic( + new LockCurrentStateCharacteristic( + ((AccessoryWithLockCurrentState) accessory)::getLockCurrentState, + ((AccessoryWithLockCurrentState) accessory)::subscribeLockCurrentState, + ((AccessoryWithLockCurrentState) accessory)::unsubscribeLockCurrentState)); + } + if (accessory instanceof AccessoryWithLockTargetState) { + addOptionalCharacteristic( + new LockTargetStateCharacteristic( + ((AccessoryWithLockTargetState) accessory)::getLockTargetState, + ((AccessoryWithLockTargetState) accessory)::setLockTargetState, + ((AccessoryWithLockTargetState) accessory)::subscribeLockTargetState, + ((AccessoryWithLockTargetState) accessory)::unsubscribeLockTargetState)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/HumiditySensorService.java b/src/main/java/io/github/hapjava/services/impl/HumiditySensorService.java index b5a21a76e..4153510a3 100644 --- a/src/main/java/io/github/hapjava/services/impl/HumiditySensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/HumiditySensorService.java @@ -4,6 +4,8 @@ import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusLowBattery; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusTampered; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; @@ -44,6 +46,20 @@ public HumiditySensorService(HumiditySensorAccessory accessory) { ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); } + if (accessory instanceof AccessoryWithStatusTampered) { + addOptionalCharacteristic( + new StatusTamperedCharacteristic( + ((AccessoryWithStatusTampered) accessory)::getStatusTampered, + ((AccessoryWithStatusTampered) accessory)::subscribeStatusTampered, + ((AccessoryWithStatusTampered) accessory)::unsubscribeStatusTampered)); + } + if (accessory instanceof AccessoryWithStatusLowBattery) { + addOptionalCharacteristic( + new StatusLowBatteryCharacteristic( + ((AccessoryWithStatusLowBattery) accessory)::getStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::subscribeStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::unsubscribeStatusLowBattery)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/LeakSensorService.java b/src/main/java/io/github/hapjava/services/impl/LeakSensorService.java index e0a493ed0..b8470371a 100644 --- a/src/main/java/io/github/hapjava/services/impl/LeakSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/LeakSensorService.java @@ -4,6 +4,8 @@ import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusLowBattery; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusTampered; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; @@ -43,6 +45,20 @@ public LeakSensorService(LeakSensorAccessory accessory) { ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); } + if (accessory instanceof AccessoryWithStatusTampered) { + addOptionalCharacteristic( + new StatusTamperedCharacteristic( + ((AccessoryWithStatusTampered) accessory)::getStatusTampered, + ((AccessoryWithStatusTampered) accessory)::subscribeStatusTampered, + ((AccessoryWithStatusTampered) accessory)::unsubscribeStatusTampered)); + } + if (accessory instanceof AccessoryWithStatusLowBattery) { + addOptionalCharacteristic( + new StatusLowBatteryCharacteristic( + ((AccessoryWithStatusLowBattery) accessory)::getStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::subscribeStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::unsubscribeStatusLowBattery)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/LightSensorService.java b/src/main/java/io/github/hapjava/services/impl/LightSensorService.java index 07a15a0fc..b40b4a43e 100644 --- a/src/main/java/io/github/hapjava/services/impl/LightSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/LightSensorService.java @@ -4,6 +4,8 @@ import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusLowBattery; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusTampered; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; @@ -26,6 +28,9 @@ public LightSensorService(LightSensorAccessory accessory) { accessory::subscribeCurrentAmbientLightLevel, accessory::unsubscribeCurrentAmbientLightLevel)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } if (accessory instanceof AccessoryWithStatusActive) { addOptionalCharacteristic( new StatusActiveCharacteristic( @@ -33,9 +38,6 @@ public LightSensorService(LightSensorAccessory accessory) { ((AccessoryWithStatusActive) accessory)::subscribeStatusActive, ((AccessoryWithStatusActive) accessory)::unsubscribeStatusActive)); } - if (accessory instanceof AccessoryWithName) { - addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); - } if (accessory instanceof AccessoryWithStatusFault) { addOptionalCharacteristic( new StatusFaultCharacteristic( @@ -43,6 +45,20 @@ public LightSensorService(LightSensorAccessory accessory) { ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); } + if (accessory instanceof AccessoryWithStatusTampered) { + addOptionalCharacteristic( + new StatusTamperedCharacteristic( + ((AccessoryWithStatusTampered) accessory)::getStatusTampered, + ((AccessoryWithStatusTampered) accessory)::subscribeStatusTampered, + ((AccessoryWithStatusTampered) accessory)::unsubscribeStatusTampered)); + } + if (accessory instanceof AccessoryWithStatusLowBattery) { + addOptionalCharacteristic( + new StatusLowBatteryCharacteristic( + ((AccessoryWithStatusLowBattery) accessory)::getStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::subscribeStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::unsubscribeStatusLowBattery)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/LightbulbService.java b/src/main/java/io/github/hapjava/services/impl/LightbulbService.java index eff2c2b47..387a2b092 100644 --- a/src/main/java/io/github/hapjava/services/impl/LightbulbService.java +++ b/src/main/java/io/github/hapjava/services/impl/LightbulbService.java @@ -1,6 +1,9 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.LightbulbAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithBrightness; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithColor; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithColorTemperature; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.OnCharacteristic; @@ -27,6 +30,36 @@ public LightbulbService(LightbulbAccessory accessory) { if (accessory instanceof AccessoryWithName) { addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); } + if (accessory instanceof AccessoryWithColor) { + addOptionalCharacteristic( + new HueCharacteristic( + ((AccessoryWithColor) accessory)::getHue, + ((AccessoryWithColor) accessory)::setHue, + ((AccessoryWithColor) accessory)::subscribeHue, + ((AccessoryWithColor) accessory)::unsubscribeHue)); + addOptionalCharacteristic( + new SaturationCharacteristic( + ((AccessoryWithColor) accessory)::getSaturation, + ((AccessoryWithColor) accessory)::setSaturation, + ((AccessoryWithColor) accessory)::subscribeSaturation, + ((AccessoryWithColor) accessory)::unsubscribeSaturation)); + } + if (accessory instanceof AccessoryWithBrightness) { + addOptionalCharacteristic( + new BrightnessCharacteristic( + ((AccessoryWithBrightness) accessory)::getBrightness, + ((AccessoryWithBrightness) accessory)::setBrightness, + ((AccessoryWithBrightness) accessory)::subscribeBrightness, + ((AccessoryWithBrightness) accessory)::unsubscribeBrightness)); + } + if (accessory instanceof AccessoryWithColorTemperature) { + addOptionalCharacteristic( + new ColorTemperatureCharacteristic( + ((AccessoryWithColorTemperature) accessory)::getColorTemperature, + ((AccessoryWithColorTemperature) accessory)::setColorTemperature, + ((AccessoryWithColorTemperature) accessory)::subscribeColorTemperature, + ((AccessoryWithColorTemperature) accessory)::unsubscribeColorTemperature)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/MotionSensorService.java b/src/main/java/io/github/hapjava/services/impl/MotionSensorService.java index 7b4216f32..388448b35 100644 --- a/src/main/java/io/github/hapjava/services/impl/MotionSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/MotionSensorService.java @@ -4,6 +4,8 @@ import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusLowBattery; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusTampered; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; @@ -42,6 +44,20 @@ public MotionSensorService(MotionSensorAccessory accessory) { ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); } + if (accessory instanceof AccessoryWithStatusTampered) { + addOptionalCharacteristic( + new StatusTamperedCharacteristic( + ((AccessoryWithStatusTampered) accessory)::getStatusTampered, + ((AccessoryWithStatusTampered) accessory)::subscribeStatusTampered, + ((AccessoryWithStatusTampered) accessory)::unsubscribeStatusTampered)); + } + if (accessory instanceof AccessoryWithStatusLowBattery) { + addOptionalCharacteristic( + new StatusLowBatteryCharacteristic( + ((AccessoryWithStatusLowBattery) accessory)::getStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::subscribeStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::unsubscribeStatusLowBattery)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/OccupancySensorService.java b/src/main/java/io/github/hapjava/services/impl/OccupancySensorService.java index 7a6ff235a..12771e60e 100644 --- a/src/main/java/io/github/hapjava/services/impl/OccupancySensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/OccupancySensorService.java @@ -4,6 +4,8 @@ import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusLowBattery; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusTampered; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; @@ -42,6 +44,20 @@ public OccupancySensorService(OccupancySensorAccessory accessory) { ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); } + if (accessory instanceof AccessoryWithStatusTampered) { + addOptionalCharacteristic( + new StatusTamperedCharacteristic( + ((AccessoryWithStatusTampered) accessory)::getStatusTampered, + ((AccessoryWithStatusTampered) accessory)::subscribeStatusTampered, + ((AccessoryWithStatusTampered) accessory)::unsubscribeStatusTampered)); + } + if (accessory instanceof AccessoryWithStatusLowBattery) { + addOptionalCharacteristic( + new StatusLowBatteryCharacteristic( + ((AccessoryWithStatusLowBattery) accessory)::getStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::subscribeStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::unsubscribeStatusLowBattery)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/SecuritySystemService.java b/src/main/java/io/github/hapjava/services/impl/SecuritySystemService.java index a4f1de059..f4cf60e00 100644 --- a/src/main/java/io/github/hapjava/services/impl/SecuritySystemService.java +++ b/src/main/java/io/github/hapjava/services/impl/SecuritySystemService.java @@ -2,7 +2,9 @@ import io.github.hapjava.accessories.SecuritySystemAccessory; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithSecurityAlarmType; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusTampered; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; @@ -42,14 +44,28 @@ public SecuritySystemService(SecuritySystemAccessory accessory) { ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); } + if (accessory instanceof AccessoryWithStatusTampered) { + addOptionalCharacteristic( + new StatusTamperedCharacteristic( + ((AccessoryWithStatusTampered) accessory)::getStatusTampered, + ((AccessoryWithStatusTampered) accessory)::subscribeStatusTampered, + ((AccessoryWithStatusTampered) accessory)::unsubscribeStatusTampered)); + } + if (accessory instanceof AccessoryWithSecurityAlarmType) { + addOptionalCharacteristic( + new SecuritySystemAlarmTypeCharacteristic( + ((AccessoryWithSecurityAlarmType) accessory)::getSecurityAlarmType, + ((AccessoryWithSecurityAlarmType) accessory)::subscribeSecurityAlarmType, + ((AccessoryWithSecurityAlarmType) accessory)::unsubscribeSecurityAlarmType)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { addCharacteristic(name); } - public void addOptionalCharacteristic(SecuritySystemAlarmTypeCharacteristic alermType) { - addCharacteristic(alermType); + public void addOptionalCharacteristic(SecuritySystemAlarmTypeCharacteristic alarmType) { + addCharacteristic(alarmType); } public void addOptionalCharacteristic(StatusFaultCharacteristic statusFault) { diff --git a/src/main/java/io/github/hapjava/services/impl/SmokeSensorService.java b/src/main/java/io/github/hapjava/services/impl/SmokeSensorService.java index df361f9cd..b6bf2f8d6 100644 --- a/src/main/java/io/github/hapjava/services/impl/SmokeSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/SmokeSensorService.java @@ -4,6 +4,8 @@ import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusLowBattery; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusTampered; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; @@ -42,6 +44,20 @@ public SmokeSensorService(SmokeSensorAccessory accessory) { ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); } + if (accessory instanceof AccessoryWithStatusTampered) { + addOptionalCharacteristic( + new StatusTamperedCharacteristic( + ((AccessoryWithStatusTampered) accessory)::getStatusTampered, + ((AccessoryWithStatusTampered) accessory)::subscribeStatusTampered, + ((AccessoryWithStatusTampered) accessory)::unsubscribeStatusTampered)); + } + if (accessory instanceof AccessoryWithStatusLowBattery) { + addOptionalCharacteristic( + new StatusLowBatteryCharacteristic( + ((AccessoryWithStatusLowBattery) accessory)::getStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::subscribeStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::unsubscribeStatusLowBattery)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/TemperatureSensorService.java b/src/main/java/io/github/hapjava/services/impl/TemperatureSensorService.java index d1c6f2e62..5fbb5a4ab 100644 --- a/src/main/java/io/github/hapjava/services/impl/TemperatureSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/TemperatureSensorService.java @@ -4,6 +4,8 @@ import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusLowBattery; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusTampered; import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; @@ -43,6 +45,20 @@ public TemperatureSensorService(TemperatureSensorAccessory accessory) { ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); } + if (accessory instanceof AccessoryWithStatusTampered) { + addOptionalCharacteristic( + new StatusTamperedCharacteristic( + ((AccessoryWithStatusTampered) accessory)::getStatusTampered, + ((AccessoryWithStatusTampered) accessory)::subscribeStatusTampered, + ((AccessoryWithStatusTampered) accessory)::unsubscribeStatusTampered)); + } + if (accessory instanceof AccessoryWithStatusLowBattery) { + addOptionalCharacteristic( + new StatusLowBatteryCharacteristic( + ((AccessoryWithStatusLowBattery) accessory)::getStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::subscribeStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::unsubscribeStatusLowBattery)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/ThermostatService.java b/src/main/java/io/github/hapjava/services/impl/ThermostatService.java index 75b01e3a2..ad575125a 100644 --- a/src/main/java/io/github/hapjava/services/impl/ThermostatService.java +++ b/src/main/java/io/github/hapjava/services/impl/ThermostatService.java @@ -1,7 +1,11 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.ThermostatAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithCoolingThresholdTemperature; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithCurrentRelativeHumidity; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithHeatingThresholdTemprature; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithTargetRelativeHumidity; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.humiditysensor.CurrentRelativeHumidityCharacteristic; import io.github.hapjava.characteristics.impl.humiditysensor.TargetRelativeHumidityCharacteristic; @@ -58,6 +62,37 @@ public ThermostatService(ThermostatAccessory accessory) { if (accessory instanceof AccessoryWithName) { addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); } + if (accessory instanceof AccessoryWithCoolingThresholdTemperature) { + new CoolingThresholdTemperatureCharacteristic( + ((AccessoryWithCoolingThresholdTemperature) accessory)::getCoolingThresholdTemperature, + ((AccessoryWithCoolingThresholdTemperature) accessory)::setCoolingThresholdTemperature, + ((AccessoryWithCoolingThresholdTemperature) accessory) + ::subscribeCoolingThresholdTemperature, + ((AccessoryWithCoolingThresholdTemperature) accessory) + ::unsubscribeCoolingThresholdTemperature); + } + if (accessory instanceof AccessoryWithHeatingThresholdTemprature) { + new HeatingThresholdTemperatureCharacteristic( + ((AccessoryWithHeatingThresholdTemprature) accessory)::getHeatingThresholdTemperature, + ((AccessoryWithHeatingThresholdTemprature) accessory)::setHeatingThresholdTemperature, + ((AccessoryWithHeatingThresholdTemprature) accessory) + ::subscribeHeatingThresholdTemperature, + ((AccessoryWithHeatingThresholdTemprature) accessory) + ::unsubscribeHeatingThresholdTemperature); + } + if (accessory instanceof AccessoryWithCurrentRelativeHumidity) { + new CurrentRelativeHumidityCharacteristic( + ((AccessoryWithCurrentRelativeHumidity) accessory)::getCurrentRelativeHumidity, + ((AccessoryWithCurrentRelativeHumidity) accessory)::subscribeCurrentRelativeHumidity, + ((AccessoryWithCurrentRelativeHumidity) accessory)::unsubscribeCurrentRelativeHumidity); + } + if (accessory instanceof AccessoryWithTargetRelativeHumidity) { + new TargetRelativeHumidityCharacteristic( + ((AccessoryWithTargetRelativeHumidity) accessory)::getTargetRelativeHumidity, + ((AccessoryWithTargetRelativeHumidity) accessory)::setTargetRelativeHumidity, + ((AccessoryWithTargetRelativeHumidity) accessory)::subscribeTargetRelativeHumidity, + ((AccessoryWithTargetRelativeHumidity) accessory)::unsubscribeTargetRelativeHumidity); + } } public void addOptionalCharacteristic(NameCharacteristic name) { diff --git a/src/main/java/io/github/hapjava/services/impl/ValveService.java b/src/main/java/io/github/hapjava/services/impl/ValveService.java index 88270fc13..ae8d5b1c4 100644 --- a/src/main/java/io/github/hapjava/services/impl/ValveService.java +++ b/src/main/java/io/github/hapjava/services/impl/ValveService.java @@ -1,7 +1,11 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.ValveAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithDuration; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithIsConfigured; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithRemainingDuration; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithServiceLabelIndex; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; import io.github.hapjava.characteristics.impl.common.ActiveCharacteristic; import io.github.hapjava.characteristics.impl.common.InUseCharacteristic; @@ -9,6 +13,7 @@ import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.ServiceLabelIndexCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; +import io.github.hapjava.characteristics.impl.valve.RemainingDurationCharacteristic; import io.github.hapjava.characteristics.impl.valve.SetDurationCharacteristic; import io.github.hapjava.characteristics.impl.valve.ValveTypeCharacteristic; @@ -53,16 +58,53 @@ public ValveService(ValveAccessory accessory) { ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); } + if (accessory instanceof AccessoryWithDuration) { + addOptionalCharacteristic( + new SetDurationCharacteristic( + ((AccessoryWithDuration) accessory)::getSetDuration, + ((AccessoryWithDuration) accessory)::setSetDuration, + ((AccessoryWithDuration) accessory)::subscribeSetDuration, + ((AccessoryWithDuration) accessory)::unsubscribeSetDuration)); + } + if (accessory instanceof AccessoryWithRemainingDuration) { + addOptionalCharacteristic( + new RemainingDurationCharacteristic( + ((AccessoryWithRemainingDuration) accessory)::getRemainingDuration, + ((AccessoryWithRemainingDuration) accessory)::subscribeRemainingDuration, + ((AccessoryWithRemainingDuration) accessory)::unsubscribeRemainingDuration)); + } + if (accessory instanceof AccessoryWithIsConfigured) { + addOptionalCharacteristic( + new IsConfiguredCharacteristic( + ((AccessoryWithIsConfigured) accessory)::getIsConfigured, + ((AccessoryWithIsConfigured) accessory)::setIsConfigured, + ((AccessoryWithIsConfigured) accessory)::subscribeIsConfigured, + ((AccessoryWithIsConfigured) accessory)::unsubscribeIsConfigured)); + } + if (accessory instanceof AccessoryWithServiceLabelIndex) { + addOptionalCharacteristic( + new ServiceLabelIndexCharacteristic( + ((AccessoryWithServiceLabelIndex) accessory)::getServiceLabelIndex)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { addCharacteristic(name); } + public void addOptionalCharacteristic(StatusFaultCharacteristic statusFaultCharacteristic) { + addCharacteristic(statusFaultCharacteristic); + } + public void addOptionalCharacteristic(SetDurationCharacteristic setDurationCharacteristic) { addCharacteristic(setDurationCharacteristic); } + public void addOptionalCharacteristic( + RemainingDurationCharacteristic remainingDurationCharacteristic) { + addCharacteristic(remainingDurationCharacteristic); + } + public void addOptionalCharacteristic(IsConfiguredCharacteristic isConfiguredCharacteristic) { addCharacteristic(isConfiguredCharacteristic); } @@ -71,8 +113,4 @@ public void addOptionalCharacteristic( ServiceLabelIndexCharacteristic serviceLabelIndexCharacteristic) { addCharacteristic(serviceLabelIndexCharacteristic); } - - public void addOptionalCharacteristic(StatusFaultCharacteristic statusFaultCharacteristic) { - addCharacteristic(statusFaultCharacteristic); - } } diff --git a/src/main/java/io/github/hapjava/services/impl/WindowCoveringService.java b/src/main/java/io/github/hapjava/services/impl/WindowCoveringService.java index bffaa3b53..173751333 100644 --- a/src/main/java/io/github/hapjava/services/impl/WindowCoveringService.java +++ b/src/main/java/io/github/hapjava/services/impl/WindowCoveringService.java @@ -1,8 +1,13 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.WindowCoveringAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithCurrentHorizontalTilting; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithCurrentVerticalTilting; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithHoldPosition; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithObstructionDetection; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithTargetHorizontalTilting; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithTargetVerticalTilting; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.ObstructionDetectedCharacteristic; import io.github.hapjava.characteristics.impl.windowcovering.CurrentHorizontalTiltAngleCharacteristic; @@ -48,6 +53,11 @@ public WindowCoveringService(WindowCoveringAccessory accessory) { if (accessory instanceof AccessoryWithName) { addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); } + if (accessory instanceof AccessoryWithHoldPosition) { + addOptionalCharacteristic( + new HoldPositionCharacteristic(((AccessoryWithHoldPosition) accessory)::setHoldPosition)); + } + if (accessory instanceof AccessoryWithObstructionDetection) { addOptionalCharacteristic( new ObstructionDetectedCharacteristic( @@ -55,6 +65,42 @@ public WindowCoveringService(WindowCoveringAccessory accessory) { ((AccessoryWithObstructionDetection) accessory)::subscribeObstructionDetected, ((AccessoryWithObstructionDetection) accessory)::unsubscribeObstructionDetected)); } + if (accessory instanceof AccessoryWithCurrentHorizontalTilting) { + addOptionalCharacteristic( + new CurrentHorizontalTiltAngleCharacteristic( + ((AccessoryWithCurrentHorizontalTilting) accessory)::getCurrentHorizontalTiltAngle, + ((AccessoryWithCurrentHorizontalTilting) accessory) + ::subscribeCurrentHorizontalTiltAngle, + ((AccessoryWithCurrentHorizontalTilting) accessory) + ::unsubscribeCurrentHorizontalTiltAngle)); + } + if (accessory instanceof AccessoryWithTargetHorizontalTilting) { + addOptionalCharacteristic( + new TargetHorizontalTiltAngleCharacteristic( + ((AccessoryWithTargetHorizontalTilting) accessory)::getTargetHorizontalTiltAngle, + ((AccessoryWithTargetHorizontalTilting) accessory)::setTargetHorizontalTiltAngle, + ((AccessoryWithTargetHorizontalTilting) accessory) + ::subscribeTargetHorizontalTiltAngle, + ((AccessoryWithTargetHorizontalTilting) accessory) + ::unsubscribeTargetHorizontalTiltAngle)); + } + if (accessory instanceof AccessoryWithCurrentVerticalTilting) { + addOptionalCharacteristic( + new CurrentVerticalTiltAngleCharacteristic( + ((AccessoryWithCurrentVerticalTilting) accessory)::getCurrentVerticalTiltAngle, + ((AccessoryWithCurrentVerticalTilting) accessory)::subscribeCurrentVerticalTiltAngle, + ((AccessoryWithCurrentVerticalTilting) accessory) + ::unsubscribeCurrentVerticalTiltAngle)); + } + if (accessory instanceof AccessoryWithTargetVerticalTilting) { + addOptionalCharacteristic( + new TargetVerticalTiltAngleCharacteristic( + ((AccessoryWithTargetVerticalTilting) accessory)::getTargetVerticalTiltAngle, + ((AccessoryWithTargetVerticalTilting) accessory)::setTargetVerticalTiltAngle, + ((AccessoryWithTargetVerticalTilting) accessory)::subscribeTargetVerticalTiltAngle, + ((AccessoryWithTargetVerticalTilting) accessory) + ::unsubscribeTargetVerticalTiltAngle)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { @@ -65,6 +111,11 @@ public void addOptionalCharacteristic(HoldPositionCharacteristic holdPositionCha addCharacteristic(holdPositionCharacteristic); } + public void addOptionalCharacteristic( + ObstructionDetectedCharacteristic obstructionDetectedCharacteristic) { + addCharacteristic(obstructionDetectedCharacteristic); + } + public void addOptionalCharacteristic( CurrentHorizontalTiltAngleCharacteristic currentHorizontalTiltAngleCharacteristic) { addCharacteristic(currentHorizontalTiltAngleCharacteristic); @@ -84,9 +135,4 @@ public void addOptionalCharacteristic( TargetVerticalTiltAngleCharacteristic targetVerticalTiltAngleCharacteristic) { addCharacteristic(targetVerticalTiltAngleCharacteristic); } - - public void addOptionalCharacteristic( - ObstructionDetectedCharacteristic obstructionDetectedCharacteristic) { - addCharacteristic(obstructionDetectedCharacteristic); - } } diff --git a/src/main/java/io/github/hapjava/services/impl/WindowService.java b/src/main/java/io/github/hapjava/services/impl/WindowService.java index b984f536c..e7f2e775d 100644 --- a/src/main/java/io/github/hapjava/services/impl/WindowService.java +++ b/src/main/java/io/github/hapjava/services/impl/WindowService.java @@ -1,6 +1,7 @@ package io.github.hapjava.services.impl; import io.github.hapjava.accessories.WindowAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithHoldPosition; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithObstructionDetection; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; @@ -41,6 +42,11 @@ public WindowService(WindowAccessory accessory) { if (accessory instanceof AccessoryWithName) { addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); } + if (accessory instanceof AccessoryWithHoldPosition) { + addOptionalCharacteristic( + new HoldPositionCharacteristic(((AccessoryWithHoldPosition) accessory)::setHoldPosition)); + } + if (accessory instanceof AccessoryWithObstructionDetection) { addOptionalCharacteristic( new ObstructionDetectedCharacteristic( From 428ea3de14aef1981014d876ae2cf0cc713f55d7 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Mon, 16 Mar 2020 23:52:28 +0100 Subject: [PATCH 004/138] update CHANGES.md and README.md --- CHANGES.md | 10 ++++++++++ README.md | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 143958172..aae6d6c50 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,13 @@ +# HAP-Java 2.0.0 +* major refactoring to support optional characteristics +* structure and names are adapted to HAP spec structure and naming. +* structure is following + * `accessory` package include basis accessory as the listed in HAP spec, plus interfaces for optional characteristics. clients should extend the accessory classes. e.g. `WindowCoveringAccessory` or `AccessoryWithBrightness` + * `characteristics` package consists of all characteristics, optional and mandatory. e.g. `TargetHorizontalTiltAngleCharacteristic`. The naming is done in accordance to HAP spec. + * `services` package consists of services, which grouping characteristics. e.g. `WindowCoveringService` defines mandatory and optional characteristics for a window covering service as it is defined in HAP spec. + * `server` package consists classes to run homekit server and handle communication +* the process is following: client, e.g. Openhab bindings, extends accessory classes, e.g. `WindowCoveringAccessory` and implements all required methods. WindowCoveringAccessory is linked already to WindowCoveringService, that in turn is link to single characteristics. + # HAP-Java 1.1.5 ## Fixes diff --git a/README.md b/README.md index 2d3a5ba53..4a2d3f319 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Include HAP-Java in your project using maven: io.github.hap-java hap - 1.2.0-SNAPSHOT + 2.0.0-SNAPSHOT ``` From 9c91d828145740bdde2429c4d7874a5b01909a20 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Tue, 17 Mar 2020 23:52:57 +0100 Subject: [PATCH 005/138] Apply J-N-K patches for Openhab. update dependencies to be aligned with OH --- pom.xml | 52 +++++++++++++++---- .../server/impl/http/impl/LoggingHandler.java | 40 ++++++++------ 2 files changed, 65 insertions(+), 27 deletions(-) diff --git a/pom.xml b/pom.xml index bc523c756..b8c2cbac4 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,9 @@ UTF-8 - + 4.1.42.Final + + @@ -56,8 +58,44 @@ io.netty - netty-all - 4.0.32.Final + netty-common + ${netty.version} + + + + io.netty + netty-buffer + ${netty.version} + + + + io.netty + netty-transport + ${netty.version} + + + + io.netty + netty-handler + ${netty.version} + + + + io.netty + netty-codec + ${netty.version} + + + + io.netty + netty-codec-http + ${netty.version} + + + + io.netty + netty-resolver + ${netty.version} @@ -102,12 +140,6 @@ 3.4.1 - - commons-io - commons-io - 2.4 - - junit junit @@ -178,7 +210,7 @@ maven-assembly-plugin - 3.1.0 + 3.1.1 deploy/distribution.xml diff --git a/src/main/java/io/github/hapjava/server/impl/http/impl/LoggingHandler.java b/src/main/java/io/github/hapjava/server/impl/http/impl/LoggingHandler.java index 5b8e52368..5b271dc24 100644 --- a/src/main/java/io/github/hapjava/server/impl/http/impl/LoggingHandler.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/LoggingHandler.java @@ -2,16 +2,14 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.*; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; -import org.apache.commons.io.HexDump; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LoggingHandler extends ChannelDuplexHandler { - private static final Logger logger = LoggerFactory.getLogger(NettyHomekitHttpService.class); + private static final Logger logger = LoggerFactory.getLogger(LoggingHandler.class); + private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { @@ -32,19 +30,27 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) private void logBytes(String type, ByteBuf buf, ChannelHandlerContext ctx) throws IOException { if (buf.readableBytes() > 0) { - try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { - byte[] bytes = new byte[buf.readableBytes()]; - buf.getBytes(0, bytes, 0, bytes.length); - HexDump.dump(bytes, 0, stream, 0); - stream.flush(); - logger.trace( - String.format( - "%s %s [%s]:%n%s%n", - type, - buf, - ctx.channel().remoteAddress().toString(), - stream.toString(StandardCharsets.UTF_8.name()))); - } + byte[] bytes = new byte[buf.readableBytes()]; + buf.getBytes(0, bytes, 0, bytes.length); + logger.trace( + String.format( + "%s %s [%s]:%n%s%n", + type, buf, ctx.channel().remoteAddress().toString(), bytesToHex(bytes))); } } + + /* + This method is licensed under CC-BY-SA and published on Stackoverflow + https://stackoverflow.com/questions/9655181/how-to-convert-a-byte-array-to-a-hex-string-in-java + from the answer by Melquiades https://stackoverflow.com/users/2964945/melquiades + */ + private String bytesToHex(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = HEX_ARRAY[v >>> 4]; + hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; + } + return new String(hexChars); + } } From ad9ab2c71253667d8385fa6d692609f6077937ae Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Tue, 31 Mar 2020 18:31:34 +0200 Subject: [PATCH 006/138] fix some typos in the characteristics descriptions --- .../io/github/hapjava/accessories/HomekitAccessory.java | 9 +++++++++ .../accessoryinformation/IdentifyCharacteristic.java | 2 +- .../accessoryinformation/ManufacturerCharacteristic.java | 2 +- .../impl/accessoryinformation/ModelCharacteristic.java | 2 +- .../accessoryinformation/SerialNumberCharacteristic.java | 2 +- .../characteristics/impl/base/EnumCharacteristic.java | 2 ++ .../CurrentHorizontalTiltAngleCharacteristic.java | 2 +- .../CurrentVerticalTiltAngleCharacteristic.java | 2 +- .../TargetHorizontalTiltAngleCharacteristic.java | 2 +- .../TargetVerticalTiltAngleCharacteristic.java | 2 +- 10 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/github/hapjava/accessories/HomekitAccessory.java b/src/main/java/io/github/hapjava/accessories/HomekitAccessory.java index adb56a150..3bbde9538 100644 --- a/src/main/java/io/github/hapjava/accessories/HomekitAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/HomekitAccessory.java @@ -79,4 +79,13 @@ public interface HomekitAccessory { default Collection getServices() { return Collections.singleton(new AccessoryInformationService(this)); }; + + /** + * default implementation which is sufficient if accessory has only one services + * + * @return primary service + */ + default Service getPrimaryService() { + return getServices().iterator().next(); + }; } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/IdentifyCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/IdentifyCharacteristic.java index cacb34b71..8936ba77d 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/IdentifyCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/IdentifyCharacteristic.java @@ -10,7 +10,7 @@ public class IdentifyCharacteristic extends BooleanCharacteristic { public IdentifyCharacteristic(ExceptionalConsumer setter) { super( "00000014-0000-1000-8000-0026BB765291", - "identifies the accessory via a physical action on the accessory", + "identifies the accessory", Optional.empty(), Optional.of(setter), Optional.empty(), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ManufacturerCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ManufacturerCharacteristic.java index 0610b3b9b..0aa4e2189 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ManufacturerCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ManufacturerCharacteristic.java @@ -14,7 +14,7 @@ public class ManufacturerCharacteristic extends StaticStringCharacteristic { public ManufacturerCharacteristic(Supplier> getter) { super( "00000020-0000-1000-8000-0026BB765291", - "ame of the manufacturer", + "manufacturer", Optional.of(getter), Optional.empty(), Optional.empty()); diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ModelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ModelCharacteristic.java index d448fd984..ce87e62e3 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ModelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ModelCharacteristic.java @@ -14,7 +14,7 @@ public class ModelCharacteristic extends StaticStringCharacteristic { public ModelCharacteristic(Supplier> getter) { super( "00000021-0000-1000-8000-0026BB765291", - "name of the model", + "model", Optional.of(getter), Optional.empty(), Optional.empty()); diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/SerialNumberCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/SerialNumberCharacteristic.java index 596c2be77..018d35ca5 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/SerialNumberCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/SerialNumberCharacteristic.java @@ -14,7 +14,7 @@ public class SerialNumberCharacteristic extends StaticStringCharacteristic { public SerialNumberCharacteristic(Supplier> getter) { super( "00000030-0000-1000-8000-0026BB765291", - "serial number of the accessory", + "serial number", Optional.of(getter), Optional.empty(), Optional.empty()); diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java index 93f4770de..395d8ed53 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java @@ -49,6 +49,8 @@ public EnumCharacteristic( super( type, "int", description, getter.isPresent(), setter.isPresent(), subscriber, unsubscriber); this.maxValue = maxValue; + this.getter = getter; + this.setter = setter; } /** {@inheritDoc} */ diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java index 8295d1db9..37583d00e 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java @@ -24,7 +24,7 @@ public CurrentHorizontalTiltAngleCharacteristic( Runnable unsubscriber) { super( "0000006C-0000-1000-8000-0026BB765291", - "current horizontal tilt angel", + "current horizontal tilt angle", -90, 90, "arcdegrees", diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java index 7504b4302..590712958 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java @@ -24,7 +24,7 @@ public CurrentVerticalTiltAngleCharacteristic( Runnable unsubscriber) { super( "0000006E-0000-1000-8000-0026BB765291", - "current vertical tilt angel", + "current vertical tilt angle", -90, 90, "arcdegrees", diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java index 34e425b3e..a12a1da80 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java @@ -23,7 +23,7 @@ public TargetHorizontalTiltAngleCharacteristic( Runnable unsubscriber) { super( "000000B2-0000-1000-8000-0026BB765291", - "target horizontal tilt angel", + "target horizontal tilt angle", -90, 90, "arcdegrees", diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java index a3edbcad3..adfb8a554 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java @@ -23,7 +23,7 @@ public TargetVerticalTiltAngleCharacteristic( Runnable unsubscriber) { super( "0000007D-0000-1000-8000-0026BB765291", - "target vertical tilt angel", + "target vertical tilt angle", -90, 90, "arcdegrees", From 3cfad1d015bd2e19da19bb0bf16cf5095396d1f1 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sun, 26 Apr 2020 13:23:29 +0200 Subject: [PATCH 007/138] fix for javadoc bug --- pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b8c2cbac4..bd88630bc 100644 --- a/pom.xml +++ b/pom.xml @@ -195,9 +195,10 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.0.1 + 3.2.0 io.github.hapjava.server.impl + 8 From 4b8ba89b5e46923790fed3e530ca5de697ed06b8 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sun, 26 Apr 2020 13:24:51 +0200 Subject: [PATCH 008/138] fix potential NPE. adapt log level Signed-off-by: Eugen Freiter --- .../server/impl/connections/SubscriptionManager.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java b/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java index 5a26a41b4..7f3cbbab0 100644 --- a/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java @@ -97,7 +97,7 @@ public synchronized void batchUpdate() { public synchronized void completeUpdateBatch() { if (--this.nestedBatches == 0 && !pendingNotifications.isEmpty()) { - LOGGER.info("Publishing batched changes"); + LOGGER.debug("Publishing batched changes"); for (ConcurrentMap.Entry> entry : pendingNotifications.entrySet()) { try { @@ -112,10 +112,12 @@ public synchronized void completeUpdateBatch() { } public synchronized void publish(int accessoryId, int iid, EventableCharacteristic changed) { + final Set subscribers = subscriptions.get(changed); + if ((subscribers == null) || (subscribers.isEmpty())) return; // no subscribers if (nestedBatches != 0) { - LOGGER.info("Batching change for " + accessoryId); + LOGGER.debug("Batching change for " + accessoryId); PendingNotification notification = new PendingNotification(accessoryId, iid, changed); - for (HomekitClientConnection connection : subscriptions.get(changed)) { + for (HomekitClientConnection connection : subscribers) { if (!pendingNotifications.containsKey(connection)) { pendingNotifications.put(connection, new ArrayList()); } @@ -126,8 +128,8 @@ public synchronized void publish(int accessoryId, int iid, EventableCharacterist try { HttpResponse message = new EventController().getMessage(accessoryId, iid, changed); - LOGGER.info("Publishing change for " + accessoryId); - for (HomekitClientConnection connection : subscriptions.get(changed)) { + LOGGER.debug("Publishing change for " + accessoryId); + for (HomekitClientConnection connection : subscribers) { connection.outOfBand(message); } } catch (Exception e) { From 5f404f803939cae5d725c34141868e4d807e1db4 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sun, 26 Apr 2020 21:00:12 +0200 Subject: [PATCH 009/138] fix typo and add some logging Signed-off-by: Eugen Freiter --- .../server/impl/connections/SubscriptionManager.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java b/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java index 7f3cbbab0..0c1b2dd47 100644 --- a/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java @@ -104,7 +104,7 @@ public synchronized void completeUpdateBatch() { HttpResponse message = new EventController().getMessage(entry.getValue()); entry.getKey().outOfBand(message); } catch (Exception e) { - LOGGER.error("Faled to create new event message", e); + LOGGER.error("Failed to create new event message", e); } } pendingNotifications.clear(); @@ -113,7 +113,10 @@ public synchronized void completeUpdateBatch() { public synchronized void publish(int accessoryId, int iid, EventableCharacteristic changed) { final Set subscribers = subscriptions.get(changed); - if ((subscribers == null) || (subscribers.isEmpty())) return; // no subscribers + if ((subscribers == null) || (subscribers.isEmpty())) { + LOGGER.debug("No subscribers to characteristic {} at accessory {} ", changed, accessoryId); + return; // no subscribers + } if (nestedBatches != 0) { LOGGER.debug("Batching change for " + accessoryId); PendingNotification notification = new PendingNotification(accessoryId, iid, changed); From c640cdb4add213f40446ca7a25e195bb6d029fff Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Wed, 29 Apr 2020 12:58:21 +0200 Subject: [PATCH 010/138] Add Doorbell, Microphone, Slat, Speaker, StatelessProgrammableSwitch accessories Signed-off-by: Eugen Freiter --- .../accessories/DoorbellAccessory.java | 40 +++++++++ .../accessories/MicrophoneAccessory.java | 47 +++++++++++ .../hapjava/accessories/SlatAccessory.java | 47 +++++++++++ .../hapjava/accessories/SpeakerAccessory.java | 47 +++++++++++ .../StatelessProgrammableSwitchAccessory.java | 40 +++++++++ .../hapjava/accessories/ValveAccessory.java | 4 +- .../AccessoryWithCurrentTilting.java | 29 +++++++ .../AccessoryWithTargetTilting.java | 38 +++++++++ .../AccessoryWithVolume.java | 38 +++++++++ .../impl/audio/MuteCharacteristic.java | 27 +++++++ .../characteristics/impl/audio/MuteEnum.java | 33 ++++++++ .../impl/audio/VolumeCharacteristic.java | 37 +++++++++ .../impl/common/ProgrammableSwitchEnum.java | 36 +++++++++ ...ProgrammableSwitchEventCharacteristic.java | 35 ++++++++ .../slat/CurrentSlatStateCharacteristic.java | 26 ++++++ .../impl/slat/CurrentSlatStateEnum.java | 36 +++++++++ .../slat/CurrentTiltAngleCharacteristic.java | 34 ++++++++ .../impl/slat/SlatTypeCharacteristic.java | 25 ++++++ .../impl/slat/SlatTypeEnum.java | 35 ++++++++ .../slat/TargetTiltAngleCharacteristic.java | 35 ++++++++ .../services/impl/DoorbellService.java | 63 +++++++++++++++ .../services/impl/MicrophoneService.java | 49 +++++++++++ .../hapjava/services/impl/SlatService.java | 81 +++++++++++++++++++ .../hapjava/services/impl/SpeakerService.java | 46 +++++++++++ .../StatelessProgrammableSwitchService.java | 46 +++++++++++ 25 files changed, 971 insertions(+), 3 deletions(-) create mode 100644 src/main/java/io/github/hapjava/accessories/DoorbellAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/MicrophoneAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/SlatAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/SpeakerAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/StatelessProgrammableSwitchAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentTilting.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetTilting.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolume.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/audio/MuteCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/audio/MuteEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEventCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentSlatStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentSlatStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/slat/SlatTypeCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/slat/SlatTypeEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/services/impl/DoorbellService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/MicrophoneService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/SlatService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/SpeakerService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/StatelessProgrammableSwitchService.java diff --git a/src/main/java/io/github/hapjava/accessories/DoorbellAccessory.java b/src/main/java/io/github/hapjava/accessories/DoorbellAccessory.java new file mode 100644 index 000000000..6bcdeb17a --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/DoorbellAccessory.java @@ -0,0 +1,40 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.common.ProgrammableSwitchEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.DoorbellService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** + * The Doorbell accessory describes a doorbell. + * + * @author Eugen Freiter + */ +public interface DoorbellAccessory extends HomekitAccessory { + + /** + * Retrieves the last states of the doorbell. Bluetooth device should return the last event, the + * IP device should always return null + * + * @return state of the door bell event + */ + CompletableFuture getSwitchEvent(); + + /** + * Subscribes to changes in doorbell switch event, i.e. pressing on the door bell button. + * + * @param callback the function to call when the state changes. + */ + void subscribeSwitchEvent(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in doorbell switch event. */ + void unsubscribeSwitchEvent(); + + @Override + default Collection getServices() { + return Collections.singleton(new DoorbellService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/MicrophoneAccessory.java b/src/main/java/io/github/hapjava/accessories/MicrophoneAccessory.java new file mode 100644 index 000000000..480f1b067 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/MicrophoneAccessory.java @@ -0,0 +1,47 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.MicrophoneService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** + * Microphone accessory. + * + * @author Eugen Freiter + */ +public interface MicrophoneAccessory extends HomekitAccessory { + + /** + * Retrieves mute status. + * + * @return true if accessory is muted + */ + CompletableFuture isMuted(); + + /** + * Sets the mute status + * + * @param mute true if accessory should be muted + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setMute(boolean mute) throws Exception; + + /** + * Subscribes to changes in mute state. + * + * @param callback the function to call when the state changes. + */ + void subscribeMuteState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the mute state. */ + void unsubscribeMuteState(); + + @Override + default Collection getServices() { + return Collections.singleton(new MicrophoneService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/SlatAccessory.java b/src/main/java/io/github/hapjava/accessories/SlatAccessory.java new file mode 100644 index 000000000..89266a400 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/SlatAccessory.java @@ -0,0 +1,47 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.slat.CurrentSlatStateEnum; +import io.github.hapjava.characteristics.impl.slat.SlatTypeEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.SlatService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** + * A slat which tilts on a vertical or a horizontal axis. + * + * @author Eugen Freiter + */ +public interface SlatAccessory extends HomekitAccessory { + + /** + * Retrieves the current state of the slat + * + * @return a future that will contain the state + */ + CompletableFuture getSlatState(); + + /** + * Subscribes to changes in the state of the slat. + * + * @param callback the function to call when the state changes. + */ + void subscribeSlatState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the state of the slat. */ + void unsubscribeSlatState(); + + /** + * Retrieves the slat type. + * + * @return a future that will slat type. + */ + CompletableFuture getSlatType(); + + @Override + default Collection getServices() { + return Collections.singleton(new SlatService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/SpeakerAccessory.java b/src/main/java/io/github/hapjava/accessories/SpeakerAccessory.java new file mode 100644 index 000000000..f0c44e228 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/SpeakerAccessory.java @@ -0,0 +1,47 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.SpeakerService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** + * Speaker accessory. + * + * @author Eugen Freiter + */ +public interface SpeakerAccessory extends HomekitAccessory { + + /** + * Retrieves mute status. + * + * @return true if accessory is muted + */ + CompletableFuture isMuted(); + + /** + * Sets the mute status + * + * @param mute true if accessory should be muted + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setMute(boolean mute) throws Exception; + + /** + * Subscribes to changes in mute state. + * + * @param callback the function to call when the state changes. + */ + void subscribeMuteState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the mute state. */ + void unsubscribeMuteState(); + + @Override + default Collection getServices() { + return Collections.singleton(new SpeakerService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/StatelessProgrammableSwitchAccessory.java b/src/main/java/io/github/hapjava/accessories/StatelessProgrammableSwitchAccessory.java new file mode 100644 index 000000000..fa36713bd --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/StatelessProgrammableSwitchAccessory.java @@ -0,0 +1,40 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.common.ProgrammableSwitchEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.StatelessProgrammableSwitchService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** + * The Stateless Programmable Switch accessory describes a stateless programmable switch.. + * + * @author Eugen Freiter + */ +public interface StatelessProgrammableSwitchAccessory extends HomekitAccessory { + + /** + * Retrieves the last states of the switch. Bluetooth device should return the last event, the IP + * device should always return null + * + * @return state of the switch event + */ + CompletableFuture getSwitchEvent(); + + /** + * Subscribes to changes in switch event, i.e. pressing on the button. + * + * @param callback the function to call when the state changes. + */ + void subscribeSwitchEvent(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in switch event. */ + void unsubscribeSwitchEvent(); + + @Override + default Collection getServices() { + return Collections.singleton(new StatelessProgrammableSwitchService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/ValveAccessory.java b/src/main/java/io/github/hapjava/accessories/ValveAccessory.java index a99613b3b..4348e163a 100644 --- a/src/main/java/io/github/hapjava/accessories/ValveAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/ValveAccessory.java @@ -70,9 +70,7 @@ public interface ValveAccessory extends HomekitAccessory { /** * Retrieves the valve type. * - *

To communicate water is flowing through a valve, inUse should be used. - * - * @return a future that will contain the binary state + * @return a future that will contain the valve type. */ CompletableFuture getValveType(); diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentTilting.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentTilting.java new file mode 100644 index 000000000..c8b3125ec --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentTilting.java @@ -0,0 +1,29 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with current tilting characteristic. + * + * @author Eugen Freiter + */ +public interface AccessoryWithCurrentTilting { + + /** + * Retrieves the current tilt angle + * + * @return a future that will contain the position as a value between -90 and 90 + */ + CompletableFuture getCurrentTiltAngle(); + + /** + * Subscribes to changes in the current tilt angle. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentTiltAngle(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current tilt angle */ + void unsubscribeCurrentTiltAngle(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetTilting.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetTilting.java new file mode 100644 index 000000000..76cebc4e3 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetTilting.java @@ -0,0 +1,38 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with target tilting characteristic. + * + * @author Eugen Freiter + */ +public interface AccessoryWithTargetTilting { + + /** + * Retrieves the target tilt angle + * + * @return a future that will contain the target position as a value between -90 and 90 + */ + CompletableFuture getTargetTiltAngle(); + + /** + * Sets the target tilt angle + * + * @param angle the target angle to set, as a value between -90 and 90 + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setTargetTiltAngle(int angle) throws Exception; + + /** + * Subscribes to changes in the target tilt angle. + * + * @param callback the function to call when the state changes. + */ + void subscribeTargetTiltAngle(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the target tilt angle */ + void unsubscribeTargetTiltAngle(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolume.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolume.java new file mode 100644 index 000000000..693751fc4 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolume.java @@ -0,0 +1,38 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with volume values. + * + * @author Eugen Freiter + */ +public interface AccessoryWithVolume { + + /** + * Retrieves the current volume + * + * @return a future that will contain the volume, expressed as an integer between 0 and 100. + */ + CompletableFuture getVolume(); + + /** + * Sets the current volume + * + * @param value the volume, on a scale of 0 to 100, to set + * @return a future that completes when the volume is changed + * @throws Exception when the volume cannot be set + */ + CompletableFuture setVolume(Integer value) throws Exception; + + /** + * Subscribes to changes in the volume. + * + * @param callback the function to call when the state changes. + */ + void subscribeVolume(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the volume. */ + void unsubscribeVolume(); +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/audio/MuteCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/audio/MuteCharacteristic.java new file mode 100644 index 000000000..bdfe65f1b --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/audio/MuteCharacteristic.java @@ -0,0 +1,27 @@ +package io.github.hapjava.characteristics.impl.audio; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** A Mute characteristic allows the control of audio input or output accessory respectively. */ +public class MuteCharacteristic extends EnumCharacteristic { + public MuteCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000011A-0000-1000-8000-0026BB765291", + "Mute", + 1, + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/audio/MuteEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/audio/MuteEnum.java new file mode 100644 index 000000000..a06f1407a --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/audio/MuteEnum.java @@ -0,0 +1,33 @@ +package io.github.hapjava.characteristics.impl.audio; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Mute is Off / Audio is On” 1 ”Mute is On / There is no Audio” */ +public enum MuteEnum implements CharacteristicEnum { + OFF(0), + ON(1); + + private static final Map reverse; + + static { + reverse = Arrays.stream(MuteEnum.values()).collect(Collectors.toMap(MuteEnum::getCode, t -> t)); + } + + public static MuteEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + MuteEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java new file mode 100644 index 000000000..9358f4d0c --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java @@ -0,0 +1,37 @@ +package io.github.hapjava.characteristics.impl.audio; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * A Volume characteristic allows the control of input or output volume of an audio input or output + * accessory respectively. The value of this characteristic indicates the percentage of the maximum + * volume supported by the service. + * + * @author Eugen Freiter + */ +public class VolumeCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { + + public VolumeCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000119-0000-1000-8000-0026BB765291", + "volume", + 0, + 100, + "percentage", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEnum.java new file mode 100644 index 000000000..12f600f92 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEnum.java @@ -0,0 +1,36 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Single Press” 1 ”Double Press” 2 ”Long Press” 3-255 ”Reserved” */ +public enum ProgrammableSwitchEnum implements CharacteristicEnum { + SINGLE_PRESS(0), + DOUBLE_PRESS(1), + LONG_PRESS(3); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(ProgrammableSwitchEnum.values()) + .collect(Collectors.toMap(ProgrammableSwitchEnum::getCode, t -> t)); + } + + public static ProgrammableSwitchEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + ProgrammableSwitchEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEventCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEventCharacteristic.java new file mode 100644 index 000000000..cce06ab07 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEventCharacteristic.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes an event generated by a programmable switch. Reading this + * characteristic must return the last event triggered for BLE. For IP accessories, the accessory + * must set the value of Paired Read to null(i.e. ”value” : null) in the attribute database. A read + * of this characteristic must always return a null value for IP accessories. The value must only be + * reported in the events (”ev”) property.. + * + * @author Eugen Freiter + */ +public class ProgrammableSwitchEventCharacteristic + extends EnumCharacteristic { + + public ProgrammableSwitchEventCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000073-0000-1000-8000-0026BB765291", + "Switch Event", + 2, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentSlatStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentSlatStateCharacteristic.java new file mode 100644 index 000000000..32e897d8b --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentSlatStateCharacteristic.java @@ -0,0 +1,26 @@ +package io.github.hapjava.characteristics.impl.slat; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes the current state of the slats. */ +public class CurrentSlatStateCharacteristic extends EnumCharacteristic { + + public CurrentSlatStateCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000AA-0000-1000-8000-0026BB765291", + "current slat state", + 2, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentSlatStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentSlatStateEnum.java new file mode 100644 index 000000000..a94fd4d8e --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentSlatStateEnum.java @@ -0,0 +1,36 @@ +package io.github.hapjava.characteristics.impl.slat; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Fixed” 1 ”Jammed” 2 ”Swinging” */ +public enum CurrentSlatStateEnum implements CharacteristicEnum { + FIXED(0), + JAMMED(1), + SWINGING(2); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(CurrentSlatStateEnum.values()) + .collect(Collectors.toMap(CurrentSlatStateEnum::getCode, t -> t)); + } + + public static CurrentSlatStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + CurrentSlatStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java new file mode 100644 index 000000000..13dbf93c3 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java @@ -0,0 +1,34 @@ +package io.github.hapjava.characteristics.impl.slat; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * etc. This characteristic takes values between -90 and 90. A value of 0 indicates that the slats + * are rotated to be fully open. At value 0 the user-facing edge and the window-facing edge are + * perpendicular to the window. + */ +public class CurrentTiltAngleCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + public CurrentTiltAngleCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000C1-0000-1000-8000-0026BB765291", + "current tilt angle", + -90, + 90, + "arcdegrees", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/SlatTypeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/SlatTypeCharacteristic.java new file mode 100644 index 000000000..b995406a6 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/SlatTypeCharacteristic.java @@ -0,0 +1,25 @@ +package io.github.hapjava.characteristics.impl.slat; + +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +/** + * TThis characteristic describes the type of the slats. If the slats can tilt on a horizontal axis, + * the value of this characteristic must be set to Horizontal. If the slats can tilt on a vertical + * axis, the value of this characteristic must be set to Vertical. + */ +public class SlatTypeCharacteristic extends EnumCharacteristic { + + public SlatTypeCharacteristic(Supplier> getter) { + super( + "000000C0-0000-1000-8000-0026BB765291", + "slat type", + 1, + Optional.of(getter), + Optional.empty(), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/SlatTypeEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/SlatTypeEnum.java new file mode 100644 index 000000000..2634b0ef5 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/SlatTypeEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.slat; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Horizontal” 1 ”Vertical” */ +public enum SlatTypeEnum implements CharacteristicEnum { + HORIZONTAL(0), + VERTICAL(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(SlatTypeEnum.values()) + .collect(Collectors.toMap(SlatTypeEnum::getCode, t -> t)); + } + + public static SlatTypeEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + SlatTypeEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java new file mode 100644 index 000000000..40b5e7566 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.slat; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the target angle of slats for accessories such as windows, fans, + * portable heater/coolers etc. + */ +public class TargetTiltAngleCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + public TargetTiltAngleCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000C2-0000-1000-8000-0026BB765291", + "target tilt angle", + -90, + 90, + "arcdegrees", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/DoorbellService.java b/src/main/java/io/github/hapjava/services/impl/DoorbellService.java new file mode 100644 index 000000000..9e0591ba7 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/DoorbellService.java @@ -0,0 +1,63 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.DoorbellAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithBrightness; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithVolume; +import io.github.hapjava.characteristics.impl.audio.VolumeCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; +import io.github.hapjava.characteristics.impl.common.ProgrammableSwitchEventCharacteristic; +import io.github.hapjava.characteristics.impl.lightbulb.BrightnessCharacteristic; + +/** + * The Doorbell service describes a doorbell and is the primary service of the Video Doorbell + * Profile. + * + * @author Eugen Freiter + */ +public class DoorbellService extends AbstractServiceImpl { + + public DoorbellService(ProgrammableSwitchEventCharacteristic switchEvent) { + super("00000121-0000-1000-8000-0026BB765291"); + addCharacteristic(switchEvent); + } + + public DoorbellService(DoorbellAccessory accessory) { + this( + new ProgrammableSwitchEventCharacteristic( + accessory::getSwitchEvent, + accessory::subscribeSwitchEvent, + accessory::unsubscribeSwitchEvent)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithBrightness) { + addOptionalCharacteristic( + new BrightnessCharacteristic( + ((AccessoryWithBrightness) accessory)::getBrightness, + ((AccessoryWithBrightness) accessory)::setBrightness, + ((AccessoryWithBrightness) accessory)::subscribeBrightness, + ((AccessoryWithBrightness) accessory)::unsubscribeBrightness)); + } + if (accessory instanceof AccessoryWithVolume) { + addOptionalCharacteristic( + new VolumeCharacteristic( + ((AccessoryWithVolume) accessory)::getVolume, + ((AccessoryWithVolume) accessory)::setVolume, + ((AccessoryWithVolume) accessory)::subscribeVolume, + ((AccessoryWithVolume) accessory)::unsubscribeVolume)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(BrightnessCharacteristic brightness) { + addCharacteristic(brightness); + } + + public void addOptionalCharacteristic(VolumeCharacteristic volume) { + addCharacteristic(volume); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/MicrophoneService.java b/src/main/java/io/github/hapjava/services/impl/MicrophoneService.java new file mode 100644 index 000000000..741af556f --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/MicrophoneService.java @@ -0,0 +1,49 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.MicrophoneAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithVolume; +import io.github.hapjava.characteristics.impl.audio.MuteCharacteristic; +import io.github.hapjava.characteristics.impl.audio.MuteEnum; +import io.github.hapjava.characteristics.impl.audio.VolumeCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; + +/** + * A Microphone service is used to control the sourcing of the input audio – primarily through a + * microphone. + */ +public class MicrophoneService extends AbstractServiceImpl { + + public MicrophoneService(MuteCharacteristic muteCharacteristic) { + super("00000112-0000-1000-8000-0026BB765291"); + addCharacteristic(muteCharacteristic); + } + + public MicrophoneService(MicrophoneAccessory accessory) { + this( + new MuteCharacteristic( + () -> accessory.isMuted().thenApply(s -> s ? MuteEnum.ON : MuteEnum.OFF), + (v) -> accessory.setMute(v == MuteEnum.ON), + accessory::subscribeMuteState, + accessory::unsubscribeMuteState)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithVolume) { + addOptionalCharacteristic( + new VolumeCharacteristic( + ((AccessoryWithVolume) accessory)::getVolume, + ((AccessoryWithVolume) accessory)::setVolume, + ((AccessoryWithVolume) accessory)::subscribeVolume, + ((AccessoryWithVolume) accessory)::unsubscribeVolume)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(VolumeCharacteristic volume) { + addCharacteristic(volume); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/SlatService.java b/src/main/java/io/github/hapjava/services/impl/SlatService.java new file mode 100644 index 000000000..486029d00 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/SlatService.java @@ -0,0 +1,81 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.SlatAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithCurrentTilting; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithSwingMode; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithTargetTilting; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; +import io.github.hapjava.characteristics.impl.fan.SwingModeCharacteristic; +import io.github.hapjava.characteristics.impl.slat.CurrentSlatStateCharacteristic; +import io.github.hapjava.characteristics.impl.slat.CurrentTiltAngleCharacteristic; +import io.github.hapjava.characteristics.impl.slat.SlatTypeCharacteristic; +import io.github.hapjava.characteristics.impl.slat.TargetTiltAngleCharacteristic; + +/** + * This service describes a slat which tilts on a vertical or a horizontal axis. ”Current Tilt + * Angle” and ”Target Tilt Angle”may be included in this service if the user can set the slats to a + * particular tilt angle. + */ +public class SlatService extends AbstractServiceImpl { + + public SlatService( + CurrentSlatStateCharacteristic state, SlatTypeCharacteristic slatTypeCharacteristic) { + super("000000B9-0000-1000-8000-0026BB765291"); + addCharacteristic(state); + addCharacteristic(slatTypeCharacteristic); + } + + public SlatService(SlatAccessory accessory) { + this( + new CurrentSlatStateCharacteristic( + accessory::getSlatState, + accessory::subscribeSlatState, + accessory::unsubscribeSlatState), + new SlatTypeCharacteristic(accessory::getSlatType)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithSwingMode) { + addOptionalCharacteristic( + new SwingModeCharacteristic( + ((AccessoryWithSwingMode) accessory)::getSwingMode, + ((AccessoryWithSwingMode) accessory)::setSwingMode, + ((AccessoryWithSwingMode) accessory)::subscribeSwingMode, + ((AccessoryWithSwingMode) accessory)::unsubscribeSwingMode)); + } + if (accessory instanceof AccessoryWithCurrentTilting) { + addOptionalCharacteristic( + new CurrentTiltAngleCharacteristic( + ((AccessoryWithCurrentTilting) accessory)::getCurrentTiltAngle, + ((AccessoryWithCurrentTilting) accessory)::subscribeCurrentTiltAngle, + ((AccessoryWithCurrentTilting) accessory)::unsubscribeCurrentTiltAngle)); + } + if (accessory instanceof AccessoryWithTargetTilting) { + addOptionalCharacteristic( + new TargetTiltAngleCharacteristic( + ((AccessoryWithTargetTilting) accessory)::getTargetTiltAngle, + ((AccessoryWithTargetTilting) accessory)::setTargetTiltAngle, + ((AccessoryWithTargetTilting) accessory)::subscribeTargetTiltAngle, + ((AccessoryWithTargetTilting) accessory)::unsubscribeTargetTiltAngle)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(SwingModeCharacteristic mode) { + addCharacteristic(mode); + } + + public void addOptionalCharacteristic( + TargetTiltAngleCharacteristic targetTiltAngleCharacteristic) { + addCharacteristic(targetTiltAngleCharacteristic); + } + + public void addOptionalCharacteristic( + CurrentTiltAngleCharacteristic currentTiltAngleCharacteristic) { + addCharacteristic(currentTiltAngleCharacteristic); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/SpeakerService.java b/src/main/java/io/github/hapjava/services/impl/SpeakerService.java new file mode 100644 index 000000000..b92eb4d4b --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/SpeakerService.java @@ -0,0 +1,46 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.SpeakerAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithVolume; +import io.github.hapjava.characteristics.impl.audio.MuteCharacteristic; +import io.github.hapjava.characteristics.impl.audio.MuteEnum; +import io.github.hapjava.characteristics.impl.audio.VolumeCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; + +/** A Speaker service is to use to control the audio output settings on a speaker device. */ +public class SpeakerService extends AbstractServiceImpl { + + public SpeakerService(MuteCharacteristic muteCharacteristic) { + super("00000113-0000-1000-8000-0026BB765291"); + addCharacteristic(muteCharacteristic); + } + + public SpeakerService(SpeakerAccessory accessory) { + this( + new MuteCharacteristic( + () -> accessory.isMuted().thenApply(s -> s ? MuteEnum.ON : MuteEnum.OFF), + (v) -> accessory.setMute(v == MuteEnum.ON), + accessory::subscribeMuteState, + accessory::unsubscribeMuteState)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithVolume) { + addOptionalCharacteristic( + new VolumeCharacteristic( + ((AccessoryWithVolume) accessory)::getVolume, + ((AccessoryWithVolume) accessory)::setVolume, + ((AccessoryWithVolume) accessory)::subscribeVolume, + ((AccessoryWithVolume) accessory)::unsubscribeVolume)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(VolumeCharacteristic volume) { + addCharacteristic(volume); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/StatelessProgrammableSwitchService.java b/src/main/java/io/github/hapjava/services/impl/StatelessProgrammableSwitchService.java new file mode 100644 index 000000000..d2ef1e337 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/StatelessProgrammableSwitchService.java @@ -0,0 +1,46 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.StatelessProgrammableSwitchAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithServiceLabelIndex; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; +import io.github.hapjava.characteristics.impl.common.ProgrammableSwitchEventCharacteristic; +import io.github.hapjava.characteristics.impl.common.ServiceLabelIndexCharacteristic; + +/** + * This service describes a stateless programmable switch. + * + * @author Eugen Freiter + */ +public class StatelessProgrammableSwitchService extends AbstractServiceImpl { + + public StatelessProgrammableSwitchService(ProgrammableSwitchEventCharacteristic switchEvent) { + super("00000089-0000-1000-8000-0026BB765291"); + addCharacteristic(switchEvent); + } + + public StatelessProgrammableSwitchService(StatelessProgrammableSwitchAccessory accessory) { + this( + new ProgrammableSwitchEventCharacteristic( + accessory::getSwitchEvent, + accessory::subscribeSwitchEvent, + accessory::unsubscribeSwitchEvent)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithServiceLabelIndex) { + addOptionalCharacteristic( + new ServiceLabelIndexCharacteristic( + ((AccessoryWithServiceLabelIndex) accessory)::getServiceLabelIndex)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic( + ServiceLabelIndexCharacteristic serviceLabelIndexCharacteristic) { + addCharacteristic(serviceLabelIndexCharacteristic); + } +} From 8aa99f1c5f5556dac3bbfa1f28d0c063d9808326 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Wed, 29 Apr 2020 14:08:09 +0200 Subject: [PATCH 011/138] update readme Signed-off-by: Eugen Freiter --- README.md | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4a2d3f319..05fc09fad 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,6 @@ HAP-Java is a Java implementation of the Homekit Accessory Protocol. Using this library, you can create your own Homekit Accessory or Homekit Accessory Bridge. -Because the MFi Specification is closed to individual developers, and this implementation was made without access to that specification, it may not be complete. iOS devices do recognize and are able to interact with accessories exposed via this library however. - This library would not have been possible without [Tian Zhang](https://github.com/KhaosT) who did a lot of the hard work of figuring out how the protocol works in his NodeJS implementation. Usage @@ -19,4 +17,58 @@ Include HAP-Java in your project using maven: ``` -After that, read the [Javadoc](http://beowulfe.github.io/HAP-Java/apidocs/) and check out the [Sample](https://github.com/beowulfe/HAP-Java/tree/sample). \ No newline at end of file +After that, read the [Javadoc](http://beowulfe.github.io/HAP-Java/apidocs/) and check out the [Sample](https://github.com/beowulfe/HAP-Java/tree/sample). + +Supported HomeKit Accessories +========= + +Current implementation is based on HAP specification Release R2 (published 2019-07-26) +fully supports 29 out of 48 HomeKit accessories defined there. + +| HomeKit Accessory | Supported by Java-HAP | +|--------------------|--------------------| +| Accessory Information | :white_check_mark: yes | +| Air Purifier | :x: no | +| Air Quality Sensor | :x: no | +| Audio Stream Management | :x: no | +| Battery Service | :white_check_mark: yes | +| Camera RTP Stream Management | :x: no | +| Carbon Dioxide Sensor | :white_check_mark: yes | +| Carbon Monoxide Sensor | :white_check_mark: yes | +| Contact Sensor | :white_check_mark: yes | +| Data Stream Transport Management | :x: no | +| Door | :white_check_mark: yes | +| Doorbell | :white_check_mark: yes | +| Fan | :white_check_mark: yes | +| Faucet | :x: no | +| Filter Maintenance | :x: no | +| Garage Door Opener | :white_check_mark: yes | +| HAP Protocol Information | :x: no | +| Heater Cooler | :x: no | +| Humidifier Dehumidifier | :x: no | +| Humidity Sensor | :white_check_mark: yes | +| Irrigation System | :x: no | +| Leak Sensor | :white_check_mark: yes | +| Light Bulb | :white_check_mark: yes | +| Light Sensor | :white_check_mark: yes | +| Lock Management | :x: no | +| Lock Mechanism | :white_check_mark: yes | +| Microphone | :white_check_mark: yes | +| Motion Sensor | :white_check_mark: yes | +| Occupancy Sensor | :white_check_mark: yes | +| Outlet | :white_check_mark: yes | +| Security System | :white_check_mark: yes | +| Service Label | :x: no | +| Siri | :x: no | +| Slat | :white_check_mark: yes | +| Smoke Sensor | :white_check_mark: yes | +| Speaker | :white_check_mark: yes | +| Stateless Programmable Switch | :white_check_mark: yes | +| Switch | :white_check_mark: yes | +| Target Control | :x: no | +| Target Control Management | :x: no | +| Temperature Sensor | :white_check_mark: yes | +| Thermostat | :white_check_mark: yes | +| Valve | :white_check_mark: yes | +| Window | :white_check_mark: yes | +| Window Covering | :white_check_mark: yes | \ No newline at end of file From ee4fb0c567701e038ada8fa42f7879087cd4b372 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Wed, 29 Apr 2020 15:32:50 +0200 Subject: [PATCH 012/138] add air purifier and air quality Signed-off-by: Eugen Freiter --- README.md | 94 +++++----- .../accessories/AirPurifierAccessory.java | 90 ++++++++++ .../accessories/AirQualityAccessory.java | 39 +++++ .../accessories/LockMechanismAccessory.java | 4 +- .../AccessoryWithNitrogenDioxideDensity.java | 29 ++++ .../AccessoryWithOzoneDensity.java | 29 ++++ .../AccessoryWithPM10Density.java | 29 ++++ .../AccessoryWithPM25Density.java | 29 ++++ .../AccessoryWithSulphurDioxideDensity.java | 29 ++++ .../AccessoryWithVOCDensity.java | 29 ++++ .../CurrentAirPurifierCharacteristic.java | 27 +++ .../CurrentAirPurifierStateEnum.java | 36 ++++ .../TargetAirPurifierStateCharacteristic.java | 37 ++++ .../TargetAirPurifierStateEnum.java | 33 ++++ .../airquality/AirQualityCharacteristic.java | 26 +++ .../impl/airquality/AirQualityEnum.java | 39 +++++ .../NitrogenDioxideDensityCharacteristic.java | 29 ++++ .../OzoneDensityCharacteristic.java | 29 ++++ .../airquality/PM10DensityCharacteristic.java | 31 ++++ .../airquality/PM25DensityCharacteristic.java | 31 ++++ .../SulphurDioxideDensityCharacteristic.java | 29 ++++ .../airquality/VOCDensityCharacteristic.java | 29 ++++ .../services/impl/AirPurifierService.java | 89 ++++++++++ .../services/impl/AirQualityService.java | 163 ++++++++++++++++++ 24 files changed, 980 insertions(+), 49 deletions(-) create mode 100644 src/main/java/io/github/hapjava/accessories/AirPurifierAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/AirQualityAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithNitrogenDioxideDensity.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithOzoneDensity.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM10Density.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM25Density.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSulphurDioxideDensity.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVOCDensity.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/airpurifier/CurrentAirPurifierCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/airpurifier/CurrentAirPurifierStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/airpurifier/TargetAirPurifierStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/airpurifier/TargetAirPurifierStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/airquality/NitrogenDioxideDensityCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/airquality/OzoneDensityCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/airquality/PM10DensityCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/airquality/PM25DensityCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/airquality/SulphurDioxideDensityCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/airquality/VOCDensityCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/services/impl/AirPurifierService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/AirQualityService.java diff --git a/README.md b/README.md index 05fc09fad..8f516a4ac 100644 --- a/README.md +++ b/README.md @@ -22,53 +22,53 @@ After that, read the [Javadoc](http://beowulfe.github.io/HAP-Java/apidocs/) and Supported HomeKit Accessories ========= -Current implementation is based on HAP specification Release R2 (published 2019-07-26) -fully supports 29 out of 48 HomeKit accessories defined there. +Current implementation is based on HAP specification Release R2 (published 2019-07-26) and +fully supports 31 out of 48 HomeKit accessories defined there. | HomeKit Accessory | Supported by Java-HAP | |--------------------|--------------------| -| Accessory Information | :white_check_mark: yes | -| Air Purifier | :x: no | -| Air Quality Sensor | :x: no | -| Audio Stream Management | :x: no | -| Battery Service | :white_check_mark: yes | -| Camera RTP Stream Management | :x: no | -| Carbon Dioxide Sensor | :white_check_mark: yes | -| Carbon Monoxide Sensor | :white_check_mark: yes | -| Contact Sensor | :white_check_mark: yes | -| Data Stream Transport Management | :x: no | -| Door | :white_check_mark: yes | -| Doorbell | :white_check_mark: yes | -| Fan | :white_check_mark: yes | -| Faucet | :x: no | -| Filter Maintenance | :x: no | -| Garage Door Opener | :white_check_mark: yes | -| HAP Protocol Information | :x: no | -| Heater Cooler | :x: no | -| Humidifier Dehumidifier | :x: no | -| Humidity Sensor | :white_check_mark: yes | -| Irrigation System | :x: no | -| Leak Sensor | :white_check_mark: yes | -| Light Bulb | :white_check_mark: yes | -| Light Sensor | :white_check_mark: yes | -| Lock Management | :x: no | -| Lock Mechanism | :white_check_mark: yes | -| Microphone | :white_check_mark: yes | -| Motion Sensor | :white_check_mark: yes | -| Occupancy Sensor | :white_check_mark: yes | -| Outlet | :white_check_mark: yes | -| Security System | :white_check_mark: yes | -| Service Label | :x: no | -| Siri | :x: no | -| Slat | :white_check_mark: yes | -| Smoke Sensor | :white_check_mark: yes | -| Speaker | :white_check_mark: yes | -| Stateless Programmable Switch | :white_check_mark: yes | -| Switch | :white_check_mark: yes | -| Target Control | :x: no | -| Target Control Management | :x: no | -| Temperature Sensor | :white_check_mark: yes | -| Thermostat | :white_check_mark: yes | -| Valve | :white_check_mark: yes | -| Window | :white_check_mark: yes | -| Window Covering | :white_check_mark: yes | \ No newline at end of file +| Accessory Information | :white_check_mark: | +| Air Purifier | :white_check_mark: | +| Air Quality Sensor | :white_check_mark: | +| Audio Stream Management | :x: | +| Battery Service | :white_check_mark: | +| Camera RTP Stream Management | :x: | +| Carbon Dioxide Sensor | :white_check_mark: | +| Carbon Monoxide Sensor | :white_check_mark: | +| Contact Sensor | :white_check_mark: | +| Data Stream Transport Management | :x: | +| Door | :white_check_mark: | +| Doorbell | :white_check_mark: | +| Fan | :white_check_mark: | +| Faucet | :x: | +| Filter Maintenance | :x: | +| Garage Door Opener | :white_check_mark: | +| HAP Protocol Information | :x: | +| Heater Cooler | :x: | +| Humidifier Dehumidifier | :x: | +| Humidity Sensor | :white_check_mark: | +| Irrigation System | :x: | +| Leak Sensor | :white_check_mark: | +| Light Bulb | :white_check_mark: | +| Light Sensor | :white_check_mark: | +| Lock Management | :x: | +| Lock Mechanism | :white_check_mark: | +| Microphone | :white_check_mark: | +| Motion Sensor | :white_check_mark: | +| Occupancy Sensor | :white_check_mark: | +| Outlet | :white_check_mark: | +| Security System | :white_check_mark: | +| Service Label | :x: | +| Siri | :x: | +| Slat | :white_check_mark: | +| Smoke Sensor | :white_check_mark: | +| Speaker | :white_check_mark: | +| Stateless Programmable Switch | :white_check_mark: | +| Switch | :white_check_mark: | +| Target Control | :x: | +| Target Control Management | :x: | +| Temperature Sensor | :white_check_mark: | +| Thermostat | :white_check_mark: | +| Valve | :white_check_mark: | +| Window | :white_check_mark: | +| Window Covering | :white_check_mark: | \ No newline at end of file diff --git a/src/main/java/io/github/hapjava/accessories/AirPurifierAccessory.java b/src/main/java/io/github/hapjava/accessories/AirPurifierAccessory.java new file mode 100644 index 000000000..a96db6e33 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/AirPurifierAccessory.java @@ -0,0 +1,90 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.airpurifier.CurrentAirPurifierStateEnum; +import io.github.hapjava.characteristics.impl.airpurifier.TargetAirPurifierStateEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.AirPurifierService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** + * An air purifier. + * + * @author Eugen Freiter + */ +public interface AirPurifierAccessory extends HomekitAccessory { + /** + * Mandatory: Retrieves the current active state of the fan'. + * + * @return a future that will contain the binary state + */ + CompletableFuture isActive(); + + /** + * Sets the active state of the fan + * + * @param state the binary state to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setActive(boolean state) throws Exception; + + /** + * Subscribes to changes in the active state of the fan. + * + * @param callback the function to call when the direction changes. + */ + void subscribeActive(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the active state of the fan. */ + void unsubscribeActive(); + + /** + * Retrieves the current state of the air purifier + * + * @return a future that will contain the state + */ + CompletableFuture getCurrentState(); + + /** + * Subscribes to changes in the state of the air purifier. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the state of the air purifier. */ + void unsubscribeCurrentState(); + + /** + * Retrieves the air purifier target state. + * + * @return a future that will contain the air purifier target state . + */ + CompletableFuture getTargetState(); + + /** + * set target state the air purifier target state. + * + * @param state air purifier target state + * @return a future that completes when the change is made + */ + CompletableFuture setTargetState(TargetAirPurifierStateEnum state); + + /** + * Subscribes to changes in the target state of the air purifier. + * + * @param callback the function to call when the target state changes. + */ + void subscribeTargetState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the target state of the air purifier. */ + void unsubscribeTargetState(); + + @Override + default Collection getServices() { + return Collections.singleton(new AirPurifierService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/AirQualityAccessory.java b/src/main/java/io/github/hapjava/accessories/AirQualityAccessory.java new file mode 100644 index 000000000..fca769272 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/AirQualityAccessory.java @@ -0,0 +1,39 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.airquality.AirQualityEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.AirQualityService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** + * An air quality accessory which can include several sensors. + * + * @author Eugen Freiter + */ +public interface AirQualityAccessory extends HomekitAccessory { + + /** + * Retrieves the state of the air quality + * + * @return a future that will contain the state + */ + CompletableFuture getAirQuality(); + + /** + * Subscribes to changes in the air quality + * + * @param callback the function to call when the air quality changes. + */ + void subscribeAirQuality(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the air quality. */ + void unsubscribeAirQuality(); + + @Override + default Collection getServices() { + return Collections.singleton(new AirQualityService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/LockMechanismAccessory.java b/src/main/java/io/github/hapjava/accessories/LockMechanismAccessory.java index 30d2e460f..fb3dcf9aa 100644 --- a/src/main/java/io/github/hapjava/accessories/LockMechanismAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/LockMechanismAccessory.java @@ -24,14 +24,14 @@ public interface LockMechanismAccessory extends HomekitAccessory { CompletableFuture getLockCurrentState(); /** - * Retrieves the lock trget state. + * Retrieves the lock target state. * * @return a future that will contain the lock target state . */ CompletableFuture getLockTargetState(); /** - * set lock target state the lock trget state. + * set lock target state the lock target state. * * @param state lock target state * @return a future that completes when the change is made diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithNitrogenDioxideDensity.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithNitrogenDioxideDensity.java new file mode 100644 index 000000000..f90969d2d --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithNitrogenDioxideDensity.java @@ -0,0 +1,29 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with nitrogen dioxide density characteristic. + * + * @author Eugen Freiter + */ +public interface AccessoryWithNitrogenDioxideDensity { + + /** + * Retrieves the nitrogen dioxide density. + * + * @return a future with the nitrogen dioxide density + */ + CompletableFuture getNitrogenDioxideDensity(); + + /** + * Subscribes to changes in nitrogen dioxide density. + * + * @param callback the function when nitrogen dioxide density changes + */ + void subscribeNitrogenDioxideDensity(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes */ + void unsubscribeNitrogenDioxideDensity(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithOzoneDensity.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithOzoneDensity.java new file mode 100644 index 000000000..149547b27 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithOzoneDensity.java @@ -0,0 +1,29 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with Ozone Density characteristic. + * + * @author Eugen Freiter + */ +public interface AccessoryWithOzoneDensity { + + /** + * Retrieves the ozone density. + * + * @return a future with the ozone density + */ + CompletableFuture getOzoneDensity(); + + /** + * Subscribes to changes in ozone density. + * + * @param callback the function when ozone density changes + */ + void subscribeOzoneDensity(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes */ + void unsubscribeOzoneDensity(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM10Density.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM10Density.java new file mode 100644 index 000000000..380251371 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM10Density.java @@ -0,0 +1,29 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with PM10 Density characteristic. + * + * @author Eugen Freiter + */ +public interface AccessoryWithPM10Density { + + /** + * Retrieves the PM10 density. + * + * @return a future with the PM10 density + */ + CompletableFuture getPM10Density(); + + /** + * Subscribes to changes in PM10 density. + * + * @param callback the function when PM10 density changes + */ + void subscribePM10Density(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes */ + void unsubscribePM10Density(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM25Density.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM25Density.java new file mode 100644 index 000000000..1203e0bfc --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM25Density.java @@ -0,0 +1,29 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with PM25 Density characteristic. + * + * @author Eugen Freiter + */ +public interface AccessoryWithPM25Density { + + /** + * Retrieves the PM25 density. + * + * @return a future with the PM25 density + */ + CompletableFuture getPM25Density(); + + /** + * Subscribes to changes in PM25 density. + * + * @param callback the function when PM25 density changes + */ + void subscribePM25Density(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes */ + void unsubscribePM25Density(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSulphurDioxideDensity.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSulphurDioxideDensity.java new file mode 100644 index 000000000..7d9473bfd --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSulphurDioxideDensity.java @@ -0,0 +1,29 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with sulphur dioxide density characteristic. + * + * @author Eugen Freiter + */ +public interface AccessoryWithSulphurDioxideDensity { + + /** + * Retrieves the sulphur dioxide density. + * + * @return a future with the sulphur dioxide density + */ + CompletableFuture getSulphurDioxideDensity(); + + /** + * Subscribes to changes in sulphur dioxide density. + * + * @param callback the function when sulphur dioxide density changes + */ + void subscribeSulphurDioxideDensity(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes */ + void unsubscribeSulphurDioxideDensity(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVOCDensity.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVOCDensity.java new file mode 100644 index 000000000..fde95da7a --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVOCDensity.java @@ -0,0 +1,29 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with VOC Density characteristic. + * + * @author Eugen Freiter + */ +public interface AccessoryWithVOCDensity { + + /** + * Retrieves the VOC density. + * + * @return a future with the VOC density + */ + CompletableFuture getVOCDensity(); + + /** + * Subscribes to changes in VOC density. + * + * @param callback the function when VOC density changes + */ + void subscribeVOCDensity(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes */ + void unsubscribeVOCDensity(); +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/CurrentAirPurifierCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/CurrentAirPurifierCharacteristic.java new file mode 100644 index 000000000..b6d4cf393 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/CurrentAirPurifierCharacteristic.java @@ -0,0 +1,27 @@ +package io.github.hapjava.characteristics.impl.airpurifier; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes the current state of the air purifier. */ +public class CurrentAirPurifierCharacteristic + extends EnumCharacteristic { + + public CurrentAirPurifierCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000A9-0000-1000-8000-0026BB765291", + "current air purifier state", + 2, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/CurrentAirPurifierStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/CurrentAirPurifierStateEnum.java new file mode 100644 index 000000000..ce4d8cc0d --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/CurrentAirPurifierStateEnum.java @@ -0,0 +1,36 @@ +package io.github.hapjava.characteristics.impl.airpurifier; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Inactive” 1 ”Idle” 2 ”Purifying Air” */ +public enum CurrentAirPurifierStateEnum implements CharacteristicEnum { + INACTIVE(0), + IDLE(1), + PURIFYING_AIR(2); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(CurrentAirPurifierStateEnum.values()) + .collect(Collectors.toMap(CurrentAirPurifierStateEnum::getCode, t -> t)); + } + + public static CurrentAirPurifierStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + CurrentAirPurifierStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/TargetAirPurifierStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/TargetAirPurifierStateCharacteristic.java new file mode 100644 index 000000000..680c933bc --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/TargetAirPurifierStateCharacteristic.java @@ -0,0 +1,37 @@ +package io.github.hapjava.characteristics.impl.airpurifier; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes the target state of the air purifier. */ +public class TargetAirPurifierStateCharacteristic + extends EnumCharacteristic { + + public TargetAirPurifierStateCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000A8-0000-1000-8000-0026BB765291", + "Air purifier state", + 1, + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } + + @Override + protected void setValue(Integer value) throws Exception { + if (!setter.isPresent()) { + return; + } + setter.get().accept(TargetAirPurifierStateEnum.fromCode(value)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/TargetAirPurifierStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/TargetAirPurifierStateEnum.java new file mode 100644 index 000000000..a20a73e9f --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/TargetAirPurifierStateEnum.java @@ -0,0 +1,33 @@ +package io.github.hapjava.characteristics.impl.airpurifier; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Manual” 1 ”Auto” */ +public enum TargetAirPurifierStateEnum implements CharacteristicEnum { + MANUAL(0), + AUTO(1); + private static final Map reverse; + + static { + reverse = + Arrays.stream(TargetAirPurifierStateEnum.values()) + .collect(Collectors.toMap(t -> t.getCode(), t -> t)); + } + + public static TargetAirPurifierStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + private TargetAirPurifierStateEnum(int code) { + this.code = code; + } + + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityCharacteristic.java new file mode 100644 index 000000000..b701e7346 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityCharacteristic.java @@ -0,0 +1,26 @@ +package io.github.hapjava.characteristics.impl.airquality; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes the subject assessment of air quality by an accessory. */ +public class AirQualityCharacteristic extends EnumCharacteristic { + + public AirQualityCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000095-0000-1000-8000-0026BB765291", + "air quality", + 5, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityEnum.java new file mode 100644 index 000000000..c688065af --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityEnum.java @@ -0,0 +1,39 @@ +package io.github.hapjava.characteristics.impl.airquality; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Unknown” 1 ”Excellent” 2 ”Good” 3 ”Fair” 4 ”Inferior” 5 ”Poor” */ +public enum AirQualityEnum implements CharacteristicEnum { + UNKNOWN(0), + EXCELLENT(1), + GOOD(0), + FAIR(1), + INFERIOR(1), + POOR(5); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(AirQualityEnum.values()) + .collect(Collectors.toMap(AirQualityEnum::getCode, t -> t)); + } + + public static AirQualityEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + AirQualityEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/NitrogenDioxideDensityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/NitrogenDioxideDensityCharacteristic.java new file mode 100644 index 000000000..657fa7fbf --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/NitrogenDioxideDensityCharacteristic.java @@ -0,0 +1,29 @@ +package io.github.hapjava.characteristics.impl.airquality; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic indicates the current NO2 density in micrograms/m3. */ +public class NitrogenDioxideDensityCharacteristic extends FloatCharacteristic { + + public NitrogenDioxideDensityCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000C4-0000-1000-8000-0026BB765291", + "nitrogen dioxide density", + 0, + 1000, + 1, + "micrograms", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/OzoneDensityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/OzoneDensityCharacteristic.java new file mode 100644 index 000000000..fd8372708 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/OzoneDensityCharacteristic.java @@ -0,0 +1,29 @@ +package io.github.hapjava.characteristics.impl.airquality; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic indicates the current ozone density in micrograms/m3. */ +public class OzoneDensityCharacteristic extends FloatCharacteristic { + + public OzoneDensityCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000C3-0000-1000-8000-0026BB765291", + "ozone density", + 0, + 1000, + 1, + "micrograms", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM10DensityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM10DensityCharacteristic.java new file mode 100644 index 000000000..0ff36d0e2 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM10DensityCharacteristic.java @@ -0,0 +1,31 @@ +package io.github.hapjava.characteristics.impl.airquality; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic indicates the current PM10 micrometer particulate density in micrograms/m3. + */ +public class PM10DensityCharacteristic extends FloatCharacteristic { + + public PM10DensityCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000C7-0000-1000-8000-0026BB765291", + "PM10 density", + 0, + 1000, + 1, + "micrograms", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM25DensityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM25DensityCharacteristic.java new file mode 100644 index 000000000..fc5c1d181 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM25DensityCharacteristic.java @@ -0,0 +1,31 @@ +package io.github.hapjava.characteristics.impl.airquality; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic indicates the current PM2.5 micrometer particulate density in micrograms/m3. + */ +public class PM25DensityCharacteristic extends FloatCharacteristic { + + public PM25DensityCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000C6-0000-1000-8000-0026BB765291", + "PM2.5 density", + 0, + 1000, + 1, + "micrograms", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/SulphurDioxideDensityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/SulphurDioxideDensityCharacteristic.java new file mode 100644 index 000000000..0c46dbb99 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/SulphurDioxideDensityCharacteristic.java @@ -0,0 +1,29 @@ +package io.github.hapjava.characteristics.impl.airquality; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic indicates the current SO2 density in micrograms/m3. */ +public class SulphurDioxideDensityCharacteristic extends FloatCharacteristic { + + public SulphurDioxideDensityCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000C5-0000-1000-8000-0026BB765291", + "sulphur dioxide density", + 0, + 1000, + 1, + "micrograms", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/VOCDensityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/VOCDensityCharacteristic.java new file mode 100644 index 000000000..7e92ed82a --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/VOCDensityCharacteristic.java @@ -0,0 +1,29 @@ +package io.github.hapjava.characteristics.impl.airquality; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic indicates the current volatile organic compound density in micrograms/m3. */ +public class VOCDensityCharacteristic extends FloatCharacteristic { + + public VOCDensityCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000C8-0000-1000-8000-0026BB765291", + "VOC density", + 0, + 1000, + 1, + "micrograms", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/AirPurifierService.java b/src/main/java/io/github/hapjava/services/impl/AirPurifierService.java new file mode 100644 index 000000000..2346cdfc9 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/AirPurifierService.java @@ -0,0 +1,89 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.AirPurifierAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithPhysicalControlsLock; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithSwingMode; +import io.github.hapjava.characteristics.impl.airpurifier.CurrentAirPurifierCharacteristic; +import io.github.hapjava.characteristics.impl.airpurifier.TargetAirPurifierStateCharacteristic; +import io.github.hapjava.characteristics.impl.common.ActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.ActiveEnum; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; +import io.github.hapjava.characteristics.impl.fan.LockPhysicalControlsCharacteristic; +import io.github.hapjava.characteristics.impl.fan.RotationSpeedCharacteristic; +import io.github.hapjava.characteristics.impl.fan.SwingModeCharacteristic; + +/** This service describes an air purifier. */ +public class AirPurifierService extends AbstractServiceImpl { + + public AirPurifierService( + ActiveCharacteristic active, + CurrentAirPurifierCharacteristic currentState, + TargetAirPurifierStateCharacteristic targetState) { + super("000000BB-0000-1000-8000-0026BB765291"); + addCharacteristic(active); + addCharacteristic(currentState); + addCharacteristic(targetState); + } + + public AirPurifierService(AirPurifierAccessory accessory) { + this( + new ActiveCharacteristic( + () -> accessory.isActive().thenApply(s -> s ? ActiveEnum.ACTIVE : ActiveEnum.INACTIVE), + (v) -> accessory.setActive(v == ActiveEnum.ACTIVE), + accessory::subscribeActive, + accessory::unsubscribeActive), + new CurrentAirPurifierCharacteristic( + accessory::getCurrentState, + accessory::subscribeCurrentState, + accessory::unsubscribeCurrentState), + new TargetAirPurifierStateCharacteristic( + accessory::getTargetState, + accessory::setTargetState, + accessory::subscribeCurrentState, + accessory::unsubscribeCurrentState)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithSwingMode) { + addOptionalCharacteristic( + new SwingModeCharacteristic( + ((AccessoryWithSwingMode) accessory)::getSwingMode, + ((AccessoryWithSwingMode) accessory)::setSwingMode, + ((AccessoryWithSwingMode) accessory)::subscribeSwingMode, + ((AccessoryWithSwingMode) accessory)::unsubscribeSwingMode)); + } + if (accessory instanceof AccessoryWithSwingMode) { + addOptionalCharacteristic( + new SwingModeCharacteristic( + ((AccessoryWithSwingMode) accessory)::getSwingMode, + ((AccessoryWithSwingMode) accessory)::setSwingMode, + ((AccessoryWithSwingMode) accessory)::subscribeSwingMode, + ((AccessoryWithSwingMode) accessory)::unsubscribeSwingMode)); + } + if (accessory instanceof AccessoryWithPhysicalControlsLock) { + addOptionalCharacteristic( + new LockPhysicalControlsCharacteristic( + ((AccessoryWithPhysicalControlsLock) accessory)::getLockControls, + ((AccessoryWithPhysicalControlsLock) accessory)::setLockControls, + ((AccessoryWithPhysicalControlsLock) accessory)::subscribeLockControls, + ((AccessoryWithPhysicalControlsLock) accessory)::unsubscribeLockControls)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(SwingModeCharacteristic mode) { + addCharacteristic(mode); + } + + public void addOptionalCharacteristic(RotationSpeedCharacteristic speed) { + addCharacteristic(speed); + } + + public void addOptionalCharacteristic(LockPhysicalControlsCharacteristic lock) { + addCharacteristic(lock); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/AirQualityService.java b/src/main/java/io/github/hapjava/services/impl/AirQualityService.java new file mode 100644 index 000000000..cf4a79952 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/AirQualityService.java @@ -0,0 +1,163 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.AirQualityAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithNitrogenDioxideDensity; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithOzoneDensity; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithPM10Density; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithPM25Density; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusActive; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusLowBattery; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusTampered; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithSulphurDioxideDensity; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithVOCDensity; +import io.github.hapjava.characteristics.impl.airquality.AirQualityCharacteristic; +import io.github.hapjava.characteristics.impl.airquality.NitrogenDioxideDensityCharacteristic; +import io.github.hapjava.characteristics.impl.airquality.OzoneDensityCharacteristic; +import io.github.hapjava.characteristics.impl.airquality.PM10DensityCharacteristic; +import io.github.hapjava.characteristics.impl.airquality.PM25DensityCharacteristic; +import io.github.hapjava.characteristics.impl.airquality.SulphurDioxideDensityCharacteristic; +import io.github.hapjava.characteristics.impl.airquality.VOCDensityCharacteristic; +import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; + +/** This characteristic describes the subject assessment of air quality by an accessory. */ +public class AirQualityService extends AbstractServiceImpl { + + public AirQualityService(AirQualityCharacteristic airQuality) { + super("00000096-0000-1000-8000-0026BB765291"); + addCharacteristic(airQuality); + } + + public AirQualityService(AirQualityAccessory accessory) { + this( + new AirQualityCharacteristic( + accessory::getAirQuality, + accessory::subscribeAirQuality, + accessory::unsubscribeAirQuality)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + + if (accessory instanceof AccessoryWithOzoneDensity) { + addOptionalCharacteristic( + new OzoneDensityCharacteristic( + ((AccessoryWithOzoneDensity) accessory)::getOzoneDensity, + ((AccessoryWithOzoneDensity) accessory)::subscribeOzoneDensity, + ((AccessoryWithOzoneDensity) accessory)::unsubscribeOzoneDensity)); + } + if (accessory instanceof AccessoryWithNitrogenDioxideDensity) { + addOptionalCharacteristic( + new NitrogenDioxideDensityCharacteristic( + ((AccessoryWithNitrogenDioxideDensity) accessory)::getNitrogenDioxideDensity, + ((AccessoryWithNitrogenDioxideDensity) accessory)::subscribeNitrogenDioxideDensity, + ((AccessoryWithNitrogenDioxideDensity) accessory) + ::unsubscribeNitrogenDioxideDensity)); + } + + if (accessory instanceof AccessoryWithSulphurDioxideDensity) { + addOptionalCharacteristic( + new SulphurDioxideDensityCharacteristic( + ((AccessoryWithSulphurDioxideDensity) accessory)::getSulphurDioxideDensity, + ((AccessoryWithSulphurDioxideDensity) accessory)::subscribeSulphurDioxideDensity, + ((AccessoryWithSulphurDioxideDensity) accessory)::unsubscribeSulphurDioxideDensity)); + } + if (accessory instanceof AccessoryWithPM25Density) { + addOptionalCharacteristic( + new PM25DensityCharacteristic( + ((AccessoryWithPM25Density) accessory)::getPM25Density, + ((AccessoryWithPM25Density) accessory)::subscribePM25Density, + ((AccessoryWithPM25Density) accessory)::unsubscribePM25Density)); + } + if (accessory instanceof AccessoryWithPM10Density) { + addOptionalCharacteristic( + new PM10DensityCharacteristic( + ((AccessoryWithPM10Density) accessory)::getPM10Density, + ((AccessoryWithPM10Density) accessory)::subscribePM10Density, + ((AccessoryWithPM10Density) accessory)::unsubscribePM10Density)); + } + if (accessory instanceof AccessoryWithVOCDensity) { + addOptionalCharacteristic( + new VOCDensityCharacteristic( + ((AccessoryWithVOCDensity) accessory)::getVOCDensity, + ((AccessoryWithVOCDensity) accessory)::subscribeVOCDensity, + ((AccessoryWithVOCDensity) accessory)::unsubscribeVOCDensity)); + } + if (accessory instanceof AccessoryWithStatusActive) { + addOptionalCharacteristic( + new StatusActiveCharacteristic( + ((AccessoryWithStatusActive) accessory)::getStatusActive, + ((AccessoryWithStatusActive) accessory)::subscribeStatusActive, + ((AccessoryWithStatusActive) accessory)::unsubscribeStatusActive)); + } + if (accessory instanceof AccessoryWithStatusFault) { + addOptionalCharacteristic( + new StatusFaultCharacteristic( + ((AccessoryWithStatusFault) accessory)::getStatusFault, + ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, + ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); + } + if (accessory instanceof AccessoryWithStatusTampered) { + addOptionalCharacteristic( + new StatusTamperedCharacteristic( + ((AccessoryWithStatusTampered) accessory)::getStatusTampered, + ((AccessoryWithStatusTampered) accessory)::subscribeStatusTampered, + ((AccessoryWithStatusTampered) accessory)::unsubscribeStatusTampered)); + } + if (accessory instanceof AccessoryWithStatusLowBattery) { + addOptionalCharacteristic( + new StatusLowBatteryCharacteristic( + ((AccessoryWithStatusLowBattery) accessory)::getStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::subscribeStatusLowBattery, + ((AccessoryWithStatusLowBattery) accessory)::unsubscribeStatusLowBattery)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(OzoneDensityCharacteristic ozoneDensity) { + addCharacteristic(ozoneDensity); + } + + public void addOptionalCharacteristic(NitrogenDioxideDensityCharacteristic nitrogenDensity) { + addCharacteristic(nitrogenDensity); + } + + public void addOptionalCharacteristic(SulphurDioxideDensityCharacteristic sulphuhrDensity) { + addCharacteristic(sulphuhrDensity); + } + + public void addOptionalCharacteristic(PM25DensityCharacteristic pm25Density) { + addCharacteristic(pm25Density); + } + + public void addOptionalCharacteristic(PM10DensityCharacteristic pm10Density) { + addCharacteristic(pm10Density); + } + + public void addOptionalCharacteristic(VOCDensityCharacteristic vocDensity) { + addCharacteristic(vocDensity); + } + + public void addOptionalCharacteristic(StatusActiveCharacteristic statusActive) { + addCharacteristic(statusActive); + } + + public void addOptionalCharacteristic(StatusFaultCharacteristic statusFault) { + addCharacteristic(statusFault); + } + + public void addOptionalCharacteristic(StatusTamperedCharacteristic statusTampered) { + addCharacteristic(statusTampered); + } + + public void addOptionalCharacteristic(StatusLowBatteryCharacteristic statusLowBattery) { + addCharacteristic(statusLowBattery); + } +} From 110a3590f3fdb43e5d878284003fe63b3a59fc35 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sun, 3 May 2020 12:37:55 +0200 Subject: [PATCH 013/138] reduce log leve. align more with OH guidance on logging Signed-off-by: Eugen Freiter --- .../hapjava/accessories/HomekitAccessory.java | 4 ++-- .../characteristics/Characteristic.java | 4 ++-- .../impl/base/BaseCharacteristic.java | 8 +++---- .../impl/base/EnumCharacteristic.java | 2 +- .../hapjava/characteristics/package-info.java | 2 +- .../hapjava/server/HomekitAuthInfo.java | 2 +- .../hapjava/server/impl/HomekitRegistry.java | 2 +- .../hapjava/server/impl/HomekitRoot.java | 18 +++++++------- .../hapjava/server/impl/HomekitServer.java | 6 ++--- .../HomekitStandaloneAccessoryServer.java | 2 +- .../impl/connections/ConnectionImpl.java | 2 +- .../server/impl/connections/HttpSession.java | 12 +++++----- .../impl/connections/SubscriptionManager.java | 24 +++++++++---------- .../impl/http/impl/AccessoryHandler.java | 8 +++---- .../server/impl/http/impl/BinaryHandler.java | 6 ++--- .../http/impl/NettyHomekitHttpService.java | 4 ++-- .../impl/jmdns/JmdnsHomekitAdvertiser.java | 10 ++++---- .../impl/json/CharacteristicsController.java | 2 +- .../impl/pairing/HomekitSRP6Routines.java | 2 +- .../pairing/HomekitSRP6ServerSession.java | 2 +- .../impl/pairing/PairVerificationManager.java | 4 ++-- .../server/impl/pairing/PairingManager.java | 10 ++++---- .../server/impl/pairing/SrpHandler.java | 4 ++-- .../io/github/hapjava/services/Service.java | 2 +- 24 files changed, 71 insertions(+), 71 deletions(-) diff --git a/src/main/java/io/github/hapjava/accessories/HomekitAccessory.java b/src/main/java/io/github/hapjava/accessories/HomekitAccessory.java index 3bbde9538..f61fd223d 100644 --- a/src/main/java/io/github/hapjava/accessories/HomekitAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/HomekitAccessory.java @@ -7,7 +7,7 @@ import java.util.concurrent.CompletableFuture; /** - * Base interface for all Homekit Accessories. You can implement this interface directly, but most + * Base interface for all HomeKit Accessories. You can implement this interface directly, but most * users will prefer to use the more full featured interfaces in {@link * io.github.hapjava.accessories} which include a default implementation of {@link #getServices()}. * @@ -67,7 +67,7 @@ public interface HomekitAccessory { /** * The collection of Services this accessory supports. Services are the primary way to interact - * with the accessory via Homekit. Besides the Services offered here, the accessory will + * with the accessory via HomeKit. Besides the Services offered here, the accessory will * automatically include the required information service. * *

This method will only be useful if you're implementing your own accessory type. For the diff --git a/src/main/java/io/github/hapjava/characteristics/Characteristic.java b/src/main/java/io/github/hapjava/characteristics/Characteristic.java index ec85f8d65..f73bf345b 100644 --- a/src/main/java/io/github/hapjava/characteristics/Characteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/Characteristic.java @@ -8,7 +8,7 @@ /** * Interface for the characteristics provided by a Service. * - *

Characteristics are the lowest level building block of the Homekit Accessory Protocol. They + *

Characteristics are the lowest level building block of the HomeKit Accessory Protocol. They * define variables that can be retrieved or set by the remote client. Most consumers of this * library will be better served by using one of the characteristic classes in {@link * io.github.hapjava.characteristics} when creating custom accessory types (the standard accessories @@ -28,7 +28,7 @@ public interface Characteristic { void supplyValue(JsonObjectBuilder characteristicBuilder); /** - * Creates the JSON representation of the characteristic, in accordance with the Homekit Accessory + * Creates the JSON representation of the characteristic, in accordance with the HomeKit Accessory * Protocol. * * @param iid The instance ID of the characteristic to be included in the serialization. diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java index 6a3390d46..73ce32df4 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java @@ -77,7 +77,7 @@ public final CompletableFuture toJson(int iid) { } /** - * Creates the JSON serialized form of the accessory for use over the Homekit Accessory Protocol. + * Creates the JSON serialized form of the accessory for use over the HomeKit Accessory Protocol. * * @param instanceId the static id of the accessory. * @return a future that will complete with the JSON builder for the object. @@ -92,7 +92,7 @@ protected CompletableFuture makeBuilder(int instanceId) { return futureValue .exceptionally( t -> { - logger.error("Could not retrieve value " + this.getClass().getName(), t); + logger.warn("Could not retrieve value " + this.getClass().getName(), t); return null; }) .thenApply( @@ -126,7 +126,7 @@ public final void setValue(JsonValue jsonValue) { try { setValue(convert(jsonValue)); } catch (Exception e) { - logger.error("Error while setting JSON value", e); + logger.warn("Error while setting JSON value", e); } } @@ -136,7 +136,7 @@ public void supplyValue(JsonObjectBuilder builder) { try { setJsonValue(builder, getValue().get()); } catch (InterruptedException | ExecutionException e) { - logger.error("Error retrieving value", e); + logger.warn("Error retrieving value", e); setJsonValue(builder, getDefault()); } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java index 395d8ed53..52eb1ccf9 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java @@ -14,7 +14,7 @@ /** * Characteristic that exposes an Enum value. Enums are represented as an Integer value in the - * Homekit protocol, and classes extending this one must handle the static mapping to an Integer + * HomeKit protocol, and classes extending this one must handle the static mapping to an Integer * value. * * @author Andy Lintner diff --git a/src/main/java/io/github/hapjava/characteristics/package-info.java b/src/main/java/io/github/hapjava/characteristics/package-info.java index a0bbf1a3f..955ab73a7 100644 --- a/src/main/java/io/github/hapjava/characteristics/package-info.java +++ b/src/main/java/io/github/hapjava/characteristics/package-info.java @@ -1,4 +1,4 @@ /** - * Contains the basic characteristic types that can be supplied over the Homekit Accessory Protocol. + * Contains the basic characteristic types that can be supplied over the HomeKit Accessory Protocol. */ package io.github.hapjava.characteristics; diff --git a/src/main/java/io/github/hapjava/server/HomekitAuthInfo.java b/src/main/java/io/github/hapjava/server/HomekitAuthInfo.java index 9eeae8fe5..a62b4e416 100644 --- a/src/main/java/io/github/hapjava/server/HomekitAuthInfo.java +++ b/src/main/java/io/github/hapjava/server/HomekitAuthInfo.java @@ -22,7 +22,7 @@ public interface HomekitAuthInfo { String getPin(); /** - * A unique MAC address to be advertised with the Homekit information. This does not have to be + * A unique MAC address to be advertised with the HomeKit information. This does not have to be * the MAC address of the network interface. You can generate this using {@link * HomekitServer#generateMac()}. * diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java b/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java index 8c7b3d0cd..f655c1879 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java @@ -37,7 +37,7 @@ public synchronized void reset() { newServices.add(new AccessoryInformationService(accessory)); newServices.addAll(accessory.getServices()); } catch (Exception e) { - logger.error("Could not instantiate services for accessory " + accessory.getName(), e); + logger.warn("Could not instantiate services for accessory " + accessory.getName(), e); services.put(accessory, Collections.emptyList()); continue; } diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java index bc7a2686f..c9bb97a8d 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java @@ -13,8 +13,8 @@ import org.slf4j.LoggerFactory; /** - * Provides advertising and handling for Homekit accessories. This class handles the advertising of - * Homekit accessories and contains one or more accessories. When implementing a bridge accessory, + * Provides advertising and handling for HomeKit accessories. This class handles the advertising of + * HomeKit accessories and contains one or more accessories. When implementing a bridge accessory, * you will interact with this class directly. Instantiate it via {@link * HomekitServer#createBridge(HomekitAuthInfo, String, String, String, String, String, String)}. For * single accessories, this is composed by {@link HomekitStandaloneAccessoryServer}. @@ -54,7 +54,7 @@ public class HomekitRoot { } /** - * Add an accessory to be handled and advertised by this root. Any existing Homekit connections + * Add an accessory to be handled and advertised by this root. Any existing HomeKit connections * will be terminated to allow the clients to reconnect and see the updated accessory list. When * using this for a bridge, the ID of the accessory must be greater than 1, as that ID is reserved * for the Bridge itself. @@ -77,7 +77,7 @@ public void addAccessory(HomekitAccessory accessory) { */ void addAccessorySkipRangeCheck(HomekitAccessory accessory) { this.registry.add(accessory); - logger.info("Added accessory " + accessory.getName()); + logger.trace("Added accessory " + accessory.getName()); if (started) { registry.reset(); webHandler.resetConnections(); @@ -85,7 +85,7 @@ void addAccessorySkipRangeCheck(HomekitAccessory accessory) { } /** - * Removes an accessory from being handled or advertised by this root. Any existing Homekit + * Removes an accessory from being handled or advertised by this root. Any existing HomeKit * connections will be terminated to allow the clients to reconnect and see the updated accessory * list. * @@ -93,7 +93,7 @@ void addAccessorySkipRangeCheck(HomekitAccessory accessory) { */ public void removeAccessory(HomekitAccessory accessory) { this.registry.remove(accessory); - logger.info("Removed accessory " + accessory.getName()); + logger.trace("Removed accessory " + accessory.getName()); if (started) { registry.reset(); webHandler.resetConnections(); @@ -101,9 +101,9 @@ public void removeAccessory(HomekitAccessory accessory) { } /** - * Starts advertising and handling the previously added Homekit accessories. You should try to + * Starts advertising and handling the previously added HomeKit accessories. You should try to * call this after you have used the {@link #addAccessory(HomekitAccessory)} method to add all the - * initial accessories you plan on advertising, as any later additions will cause the Homekit + * initial accessories you plan on advertising, as any later additions will cause the HomeKit * clients to reconnect. */ public void start() { @@ -123,7 +123,7 @@ public void start() { }); } - /** Stops advertising and handling the Homekit accessories. */ + /** Stops advertising and handling the HomeKit accessories. */ public void stop() { advertiser.stop(); webHandler.stop(); diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitServer.java b/src/main/java/io/github/hapjava/server/impl/HomekitServer.java index ea57f8da4..f323fa60b 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitServer.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitServer.java @@ -10,7 +10,7 @@ import java.util.concurrent.ExecutionException; /** - * The main entry point for hap-java. Creating an instance of this class will listen for Homekit + * The main entry point for hap-java. Creating an instance of this class will listen for HomeKit * connections on the supplied port. Only a single root accessory can be added for each unique * instance and port, however, that accessory may be a {@link #createBridge(HomekitAuthInfo, String, * String, String, String, String, String) bridge accessory} containing child accessories. @@ -19,7 +19,7 @@ * implementation supplied by your application. Several of the values needed for your implementation * are provided by this class, specifically {@link #generateKey() generateKey}, {@link * #generateMac() generateMac}, and {@link #generateSalt()}. It is important that you provide these - * same values on each start of your application or Homekit will fail to recognize your device as + * same values on each start of your application or HomeKit will fail to recognize your device as * being the same. * * @author Andy Lintner @@ -160,7 +160,7 @@ public static String generateMac() { /** * Generates a value to supply in {@link HomekitAuthInfo#getPin() HomekitAuthInfo.getPin()}. This - * is used as the Pin a user enters into their Homekit device in order to confirm pairing. + * is used as the Pin a user enters into their HomeKit device in order to confirm pairing. * * @return the generated Pin */ diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java b/src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java index 9ab4530ea..e9b09145b 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java @@ -9,7 +9,7 @@ import java.util.concurrent.ExecutionException; /** - * A server for exposing standalone Homekit accessory (as opposed to a Bridge accessory which + * A server for exposing standalone HomeKit accessory (as opposed to a Bridge accessory which * contains multiple accessories). Each standalone accessory will have its own pairing information, * port, and pin. Instantiate this class via {@link * HomekitServer#createStandaloneAccessory(HomekitAuthInfo, HomekitAccessory)}. diff --git a/src/main/java/io/github/hapjava/server/impl/connections/ConnectionImpl.java b/src/main/java/io/github/hapjava/server/impl/connections/ConnectionImpl.java index c1853855d..ab2ff4444 100644 --- a/src/main/java/io/github/hapjava/server/impl/connections/ConnectionImpl.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/ConnectionImpl.java @@ -59,7 +59,7 @@ private HttpResponse doHandleRequest(HttpRequest request) throws IOException { readKey = ((UpgradeResponse) response).getReadKey().array(); writeKey = ((UpgradeResponse) response).getWriteKey().array(); } - LOGGER.info(response.getStatusCode() + " " + request.getUri()); + LOGGER.trace(response.getStatusCode() + " " + request.getUri()); return response; } diff --git a/src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java b/src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java index 3d7d8c817..ce567b3de 100644 --- a/src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java @@ -59,7 +59,7 @@ public HttpResponse handleRequest(HttpRequest request) throws IOException { if (registry.isAllowUnauthenticatedRequests()) { return handleAuthenticatedRequest(request); } else { - logger.info("Unrecognized request for " + request.getUri()); + logger.warn("Unrecognized request for " + request.getUri()); return new NotFoundResponse(); } } @@ -79,7 +79,7 @@ public HttpResponse handleAuthenticatedRequest(HttpRequest request) throws IOExc return getCharacteristicsController().put(request, connection); default: - logger.info("Unrecognized method for " + request.getUri()); + logger.warn("Unrecognized method for " + request.getUri()); return new NotFoundResponse(); } @@ -90,11 +90,11 @@ public HttpResponse handleAuthenticatedRequest(HttpRequest request) throws IOExc if (request.getUri().startsWith("/characteristics?")) { return getCharacteristicsController().get(request); } - logger.info("Unrecognized request for " + request.getUri()); + logger.warn("Unrecognized request for " + request.getUri()); return new NotFoundResponse(); } } catch (Exception e) { - logger.error("Could not handle request", e); + logger.warn("Could not handle request", e); return new InternalServerErrorResponse(e); } } @@ -110,7 +110,7 @@ private HttpResponse handlePairSetup(HttpRequest request) { try { return pairingManager.handle(request); } catch (Exception e) { - logger.error("Exception encountered during pairing", e); + logger.warn("Exception encountered during pairing", e); return new InternalServerErrorResponse(e); } } @@ -126,7 +126,7 @@ private HttpResponse handlePairVerify(HttpRequest request) { try { return pairVerificationManager.handle(request); } catch (Exception e) { - logger.error("Excepton encountered while verifying pairing", e); + logger.warn("Excepton encountered while verifying pairing", e); return new InternalServerErrorResponse(e); } } diff --git a/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java b/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java index 0c1b2dd47..25f68526c 100644 --- a/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java @@ -46,7 +46,7 @@ public synchronized void addSubscription( reverse.putIfAbsent(connection, newSet()); } reverse.get(connection).add(characteristic); - LOGGER.info( + LOGGER.trace( "Added subscription to " + characteristic.getClass() + " for " + connection.hashCode()); } } @@ -65,7 +65,7 @@ public synchronized void removeSubscription( if (reverse != null) { reverse.remove(characteristic); } - LOGGER.info( + LOGGER.trace( "Removed subscription to " + characteristic.getClass() + " for " + connection.hashCode()); } @@ -78,13 +78,13 @@ public synchronized void removeConnection(HomekitClientConnection connection) { subscriptions.get(characteristic); characteristicSubscriptions.remove(connection); if (characteristicSubscriptions.isEmpty()) { - LOGGER.debug("Unsubscribing from characteristic as all subscriptions are closed"); + LOGGER.trace("Unsubscribing from characteristic as all subscriptions are closed"); characteristic.unsubscribe(); subscriptions.remove(characteristic); } } } - LOGGER.debug("Removed connection {}", connection.hashCode()); + LOGGER.trace("Removed connection {}", connection.hashCode()); } private Set newSet() { @@ -97,14 +97,14 @@ public synchronized void batchUpdate() { public synchronized void completeUpdateBatch() { if (--this.nestedBatches == 0 && !pendingNotifications.isEmpty()) { - LOGGER.debug("Publishing batched changes"); + LOGGER.trace("Publishing batched changes"); for (ConcurrentMap.Entry> entry : pendingNotifications.entrySet()) { try { HttpResponse message = new EventController().getMessage(entry.getValue()); entry.getKey().outOfBand(message); } catch (Exception e) { - LOGGER.error("Failed to create new event message", e); + LOGGER.warn("Failed to create new event message", e); } } pendingNotifications.clear(); @@ -118,7 +118,7 @@ public synchronized void publish(int accessoryId, int iid, EventableCharacterist return; // no subscribers } if (nestedBatches != 0) { - LOGGER.debug("Batching change for " + accessoryId); + LOGGER.trace("Batching change for accessory {} and characteristic {} " + accessoryId, iid); PendingNotification notification = new PendingNotification(accessoryId, iid, changed); for (HomekitClientConnection connection : subscribers) { if (!pendingNotifications.containsKey(connection)) { @@ -131,24 +131,24 @@ public synchronized void publish(int accessoryId, int iid, EventableCharacterist try { HttpResponse message = new EventController().getMessage(accessoryId, iid, changed); - LOGGER.debug("Publishing change for " + accessoryId); + LOGGER.trace("Publishing change for " + accessoryId); for (HomekitClientConnection connection : subscribers) { connection.outOfBand(message); } } catch (Exception e) { - LOGGER.error("Failed to create new event message", e); + LOGGER.warn("Failed to create new event message", e); } } /** Remove all existing subscriptions */ public void removeAll() { - LOGGER.debug("Removing {} reverse connections from subscription manager", reverse.size()); + LOGGER.trace("Removing {} reverse connections from subscription manager", reverse.size()); Iterator i = reverse.keySet().iterator(); while (i.hasNext()) { HomekitClientConnection connection = i.next(); - LOGGER.debug("Removing connection {}", connection.hashCode()); + LOGGER.trace("Removing connection {}", connection.hashCode()); removeConnection(connection); } - LOGGER.debug("Subscription sizes are {} and {}", reverse.size(), subscriptions.size()); + LOGGER.trace("Subscription sizes are {} and {}", reverse.size(), subscriptions.size()); } } diff --git a/src/main/java/io/github/hapjava/server/impl/http/impl/AccessoryHandler.java b/src/main/java/io/github/hapjava/server/impl/http/impl/AccessoryHandler.java index 35ac055cf..0e1bf1146 100644 --- a/src/main/java/io/github/hapjava/server/impl/http/impl/AccessoryHandler.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/AccessoryHandler.java @@ -36,13 +36,13 @@ public void channelActive(ChannelHandlerContext ctx) throws Exception { } channel.writeAndFlush(NettyResponseUtil.createResponse(response)); }); - LOGGER.info("New homekit connection from " + ctx.channel().remoteAddress().toString()); + LOGGER.trace("New HomeKit connection from " + ctx.channel().remoteAddress().toString()); super.channelActive(ctx); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { - LOGGER.info("Terminated homekit connection from " + ctx.channel().remoteAddress().toString()); + LOGGER.trace("Terminated HomeKit connection from " + ctx.channel().remoteAddress().toString()); super.channelInactive(ctx); } @@ -57,7 +57,7 @@ public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) throws } sendResponse(response, ctx); } catch (Exception e) { - LOGGER.error("Error handling homekit http request", e); + LOGGER.warn("Error handling homekit http request", e); sendResponse(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Error: " + e.getMessage(), ctx); } } @@ -95,7 +95,7 @@ public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { boolean errorLevel = !(cause instanceof IOException); if (errorLevel) { - LOGGER.error("Exception caught in web handler", cause); + LOGGER.warn("Exception caught in web handler", cause); } else { LOGGER.debug("Exception caught in web handler", cause); } diff --git a/src/main/java/io/github/hapjava/server/impl/http/impl/BinaryHandler.java b/src/main/java/io/github/hapjava/server/impl/http/impl/BinaryHandler.java index 5a2337707..9bb218c17 100644 --- a/src/main/java/io/github/hapjava/server/impl/http/impl/BinaryHandler.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/BinaryHandler.java @@ -49,7 +49,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) t public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { boolean errorLevel = !(cause instanceof IOException); if (errorLevel) { - logger.error("Exception in binary handler", cause); + logger.warn("Exception in binary handler", cause); } else { logger.debug("Exception in binary handler", cause); } @@ -57,8 +57,8 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E } private void debugData(String msg, ByteBuf b, ChannelHandlerContext ctx) throws Exception { - if (logger.isDebugEnabled()) { - logger.debug( + if (logger.isTraceEnabled()) { + logger.trace( String.format( "%s [%s]:%n%s", msg, ctx.channel().remoteAddress().toString(), b.toString(StandardCharsets.UTF_8))); diff --git a/src/main/java/io/github/hapjava/server/impl/http/impl/NettyHomekitHttpService.java b/src/main/java/io/github/hapjava/server/impl/http/impl/NettyHomekitHttpService.java index 329b803dc..aad5d5a13 100644 --- a/src/main/java/io/github/hapjava/server/impl/http/impl/NettyHomekitHttpService.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/NettyHomekitHttpService.java @@ -59,7 +59,7 @@ public void operationComplete(Future future) throws Exception { future.get(); SocketAddress socketAddress = bindFuture.channel().localAddress(); if (socketAddress instanceof InetSocketAddress) { - logger.info("Bound homekit listener to " + socketAddress.toString()); + logger.trace("Bound homekit listener to " + socketAddress.toString()); portFuture.complete(((InetSocketAddress) socketAddress).getPort()); } else { throw new RuntimeException( @@ -79,7 +79,7 @@ public void shutdown() { } public void resetConnections() { - logger.info("Resetting connections"); + logger.trace("Resetting connections"); allChannels.close(); } } diff --git a/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java index 8ba452906..81c4d4fed 100644 --- a/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java +++ b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java @@ -31,14 +31,14 @@ public JmdnsHomekitAdvertiser(InetAddress localAddress) throws UnknownHostExcept public synchronized void advertise(String label, String mac, int port, int configurationIndex) throws Exception { if (isAdvertising) { - throw new IllegalStateException("Homekit advertiser is already running"); + throw new IllegalStateException("HomeKit advertiser is already running"); } this.label = label; this.mac = mac; this.port = port; this.configurationIndex = configurationIndex; - logger.info("Advertising accessory " + label); + logger.trace("Advertising accessory " + label); registerService(); @@ -46,7 +46,7 @@ public synchronized void advertise(String label, String mac, int port, int confi .addShutdownHook( new Thread( () -> { - logger.info("Stopping advertising in response to shutdown."); + logger.trace("Stopping advertising in response to shutdown."); jmdns.unregisterAllServices(); })); isAdvertising = true; @@ -60,7 +60,7 @@ public synchronized void setDiscoverable(boolean discoverable) throws IOExceptio if (this.discoverable != discoverable) { this.discoverable = discoverable; if (isAdvertising) { - logger.info("Re-creating service due to change in discoverability to " + discoverable); + logger.trace("Re-creating service due to change in discoverability to " + discoverable); jmdns.unregisterAllServices(); registerService(); } @@ -71,7 +71,7 @@ public synchronized void setConfigurationIndex(int revision) throws IOException if (this.configurationIndex != revision) { this.configurationIndex = revision; if (isAdvertising) { - logger.info("Re-creating service due to change in configuration index to " + revision); + logger.trace("Re-creating service due to change in configuration index to " + revision); jmdns.unregisterAllServices(); registerService(); } diff --git a/src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java b/src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java index 484a4ea30..df5509dda 100644 --- a/src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java +++ b/src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java @@ -36,7 +36,7 @@ public HttpResponse get(HttpRequest request) throws Exception { for (String id : ids) { String[] parts = id.split("\\."); if (parts.length != 2) { - logger.error("Unexpected characteristics request: " + uri); + logger.warn("Unexpected characteristics request: " + uri); return new NotFoundResponse(); } int aid = Integer.parseInt(parts[0]); diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/HomekitSRP6Routines.java b/src/main/java/io/github/hapjava/server/impl/pairing/HomekitSRP6Routines.java index 5e44117f3..44417a91f 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/HomekitSRP6Routines.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/HomekitSRP6Routines.java @@ -5,7 +5,7 @@ /** * This class is modified from the nimbus SRP library to provide methods that are compatible with - * some pecularities of Homekit. Namely, the need for a 3072 bit private value + * some pecularities of HomeKit. Namely, the need for a 3072 bit private value * * @author Vladimir Dzhuvinov */ diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/HomekitSRP6ServerSession.java b/src/main/java/io/github/hapjava/server/impl/pairing/HomekitSRP6ServerSession.java index 0ec311048..3841d9fb7 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/HomekitSRP6ServerSession.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/HomekitSRP6ServerSession.java @@ -11,7 +11,7 @@ /** * This is a slightly modified version of the SRP6ServerSession class included with nimbus. The only - * change made for homekit compatability is a change to the size of the b key. Homekit pairing fails + * change made for homekit compatability is a change to the size of the b key. HomeKit pairing fails * if b is not 3072 bytes. * *

Stateful server-side Secure Remote Password (SRP-6a) authentication session. Handles the diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/PairVerificationManager.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairVerificationManager.java index 6630c82e8..d43b2d210 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/PairVerificationManager.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairVerificationManager.java @@ -56,7 +56,7 @@ public HttpResponse handle(HttpRequest rawRequest) throws Exception { } private HttpResponse stage1(Stage1Request request) throws Exception { - logger.debug("Starting pair verification for " + registry.getLabel()); + logger.trace("Starting pair verification for " + registry.getLabel()); clientPublicKey = request.getClientPublicKey(); publicKey = new byte[32]; byte[] privateKey = new byte[32]; @@ -116,7 +116,7 @@ private HttpResponse stage2(Stage2Request request) throws Exception { Encoder encoder = TypeLengthValueUtils.getEncoder(); if (new EdsaVerifier(clientLtpk).verify(material, clientSignature)) { encoder.add(MessageType.STATE, (short) 4); - logger.debug("Completed pair verification for " + registry.getLabel()); + logger.trace("Completed pair verification for " + registry.getLabel()); return new UpgradeResponse( encoder.toByteArray(), createKey("Control-Write-Encryption-Key"), diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/PairingManager.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairingManager.java index d4265a587..af6a6e01f 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/PairingManager.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairingManager.java @@ -27,11 +27,11 @@ public HttpResponse handle(HttpRequest httpRequest) throws Exception { PairSetupRequest req = PairSetupRequest.of(httpRequest.getBody()); if (req.getStage() == Stage.ONE) { - logger.info("Starting pair for " + registry.getLabel()); + logger.trace("Starting pair for " + registry.getLabel()); srpHandler = new SrpHandler(authInfo.getPin(), authInfo.getSalt()); return srpHandler.handle(req); } else if (req.getStage() == Stage.TWO) { - logger.debug("Entering second stage of pair for " + registry.getLabel()); + logger.trace("Entering second stage of pair for " + registry.getLabel()); if (srpHandler == null) { logger.warn("Received unexpected stage 2 request for " + registry.getLabel()); return new UnauthorizedResponse(); @@ -40,12 +40,12 @@ public HttpResponse handle(HttpRequest httpRequest) throws Exception { return srpHandler.handle(req); } catch (Exception e) { srpHandler = null; // You don't get to try again - need a new key - logger.error("Exception encountered while processing pairing request", e); + logger.warn("Exception encountered while processing pairing request", e); return new UnauthorizedResponse(); } } } else if (req.getStage() == Stage.THREE) { - logger.debug("Entering third stage of pair for " + registry.getLabel()); + logger.trace("Entering third stage of pair for " + registry.getLabel()); if (srpHandler == null) { logger.warn("Received unexpected stage 3 request for " + registry.getLabel()); return new UnauthorizedResponse(); @@ -54,7 +54,7 @@ public HttpResponse handle(HttpRequest httpRequest) throws Exception { try { return handler.handle(req); } catch (Exception e) { - logger.error("Exception while finalizing pairing", e); + logger.warn("Exception while finalizing pairing", e); return new UnauthorizedResponse(); } } diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/SrpHandler.java b/src/main/java/io/github/hapjava/server/impl/pairing/SrpHandler.java index 2743ea4f1..e02ccdb90 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/SrpHandler.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/SrpHandler.java @@ -53,7 +53,7 @@ public HttpResponse handle(PairSetupRequest request) throws Exception { private HttpResponse step1() throws Exception { if (session.getState() != State.INIT) { - logger.error("Session is not in state INIT when receiving step1"); + logger.warn("Session is not in state INIT when receiving step1"); return new ConflictResponse(); } @@ -70,7 +70,7 @@ private HttpResponse step1() throws Exception { private HttpResponse step2(Stage2Request request) throws Exception { if (session.getState() != State.STEP_1) { - logger.error("Session is not in state Stage 1 when receiving step2"); + logger.warn("Session is not in state Stage 1 when receiving step2"); return new ConflictResponse(); } BigInteger m2 = session.step2(request.getA(), request.getM1()); diff --git a/src/main/java/io/github/hapjava/services/Service.java b/src/main/java/io/github/hapjava/services/Service.java index c4ab23a82..aab8ef7e4 100644 --- a/src/main/java/io/github/hapjava/services/Service.java +++ b/src/main/java/io/github/hapjava/services/Service.java @@ -12,7 +12,7 @@ public interface Service { /** * Characteristics are the variables offered for reading, updating, and eventing by the Service - * over the Homekit protocol. + * over the HomeKit protocol. * *

It is important to maintain the order of this list and not change its contents between * invocations, or a pairing error will result. From 0db27e87349f709f4e563c79d412a99715e881c6 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Tue, 5 May 2020 11:41:31 +0200 Subject: [PATCH 014/138] add service label service and hap protocol version service. fix Mute characteristic Signed-off-by: Eugen Freiter --- README.md | 15 +++++---- .../impl/audio/MuteCharacteristic.java | 9 +++-- .../characteristics/impl/audio/MuteEnum.java | 33 ------------------- .../impl/common/VersionCharacteristic.java | 19 +++++++++++ .../server/impl/http/impl/BinaryHandler.java | 12 ------- .../impl/HAPProtocolInformationService.java | 12 +++++++ .../services/impl/MicrophoneService.java | 5 ++- .../services/impl/ServiceLabelService.java | 13 ++++++++ .../hapjava/services/impl/SpeakerService.java | 5 ++- 9 files changed, 60 insertions(+), 63 deletions(-) delete mode 100644 src/main/java/io/github/hapjava/characteristics/impl/audio/MuteEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/VersionCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/services/impl/HAPProtocolInformationService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/ServiceLabelService.java diff --git a/README.md b/README.md index 8f516a4ac..9694fec1c 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ This library would not have been possible without [Tian Zhang](https://github.co Usage ========= Include HAP-Java in your project using maven: + ``` io.github.hap-java @@ -23,9 +24,9 @@ Supported HomeKit Accessories ========= Current implementation is based on HAP specification Release R2 (published 2019-07-26) and -fully supports 31 out of 48 HomeKit accessories defined there. +fully supports 33 out of 45 HomeKit accessory services defined there. -| HomeKit Accessory | Supported by Java-HAP | +| HomeKit Accessory & Service type | Supported by Java-HAP | |--------------------|--------------------| | Accessory Information | :white_check_mark: | | Air Purifier | :white_check_mark: | @@ -43,7 +44,7 @@ fully supports 31 out of 48 HomeKit accessories defined there. | Faucet | :x: | | Filter Maintenance | :x: | | Garage Door Opener | :white_check_mark: | -| HAP Protocol Information | :x: | +| HAP Protocol Information | :white_check_mark: | | Heater Cooler | :x: | | Humidifier Dehumidifier | :x: | | Humidity Sensor | :white_check_mark: | @@ -58,7 +59,7 @@ fully supports 31 out of 48 HomeKit accessories defined there. | Occupancy Sensor | :white_check_mark: | | Outlet | :white_check_mark: | | Security System | :white_check_mark: | -| Service Label | :x: | +| Service Label | :white_check_mark: | | Siri | :x: | | Slat | :white_check_mark: | | Smoke Sensor | :white_check_mark: | @@ -69,6 +70,6 @@ fully supports 31 out of 48 HomeKit accessories defined there. | Target Control Management | :x: | | Temperature Sensor | :white_check_mark: | | Thermostat | :white_check_mark: | -| Valve | :white_check_mark: | -| Window | :white_check_mark: | -| Window Covering | :white_check_mark: | \ No newline at end of file +| Valve | :white_check_mark: | +| Window | :white_check_mark: | +| Window Covering | :white_check_mark: | \ No newline at end of file diff --git a/src/main/java/io/github/hapjava/characteristics/impl/audio/MuteCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/audio/MuteCharacteristic.java index bdfe65f1b..f56b2aab5 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/audio/MuteCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/audio/MuteCharacteristic.java @@ -2,23 +2,22 @@ import io.github.hapjava.characteristics.ExceptionalConsumer; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.function.Supplier; /** A Mute characteristic allows the control of audio input or output accessory respectively. */ -public class MuteCharacteristic extends EnumCharacteristic { +public class MuteCharacteristic extends BooleanCharacteristic { public MuteCharacteristic( - Supplier> getter, - ExceptionalConsumer setter, + Supplier> getter, + ExceptionalConsumer setter, Consumer subscriber, Runnable unsubscriber) { super( "0000011A-0000-1000-8000-0026BB765291", "Mute", - 1, Optional.of(getter), Optional.of(setter), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/audio/MuteEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/audio/MuteEnum.java deleted file mode 100644 index a06f1407a..000000000 --- a/src/main/java/io/github/hapjava/characteristics/impl/audio/MuteEnum.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.hapjava.characteristics.impl.audio; - -import io.github.hapjava.characteristics.CharacteristicEnum; -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Collectors; - -/** 0 ”Mute is Off / Audio is On” 1 ”Mute is On / There is no Audio” */ -public enum MuteEnum implements CharacteristicEnum { - OFF(0), - ON(1); - - private static final Map reverse; - - static { - reverse = Arrays.stream(MuteEnum.values()).collect(Collectors.toMap(MuteEnum::getCode, t -> t)); - } - - public static MuteEnum fromCode(Integer code) { - return reverse.get(code); - } - - private final int code; - - MuteEnum(int code) { - this.code = code; - } - - @Override - public int getCode() { - return code; - } -} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/VersionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/VersionCharacteristic.java new file mode 100644 index 000000000..f3964f78c --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/VersionCharacteristic.java @@ -0,0 +1,19 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.impl.base.StaticStringCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +/** This characteristic describes a name and must not be a null value. */ +public class VersionCharacteristic extends StaticStringCharacteristic { + + public VersionCharacteristic(Supplier> getter) { + super( + "00000023-0000-1000-8000-0026BB765291", + "HAP version", + Optional.of(getter), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/server/impl/http/impl/BinaryHandler.java b/src/main/java/io/github/hapjava/server/impl/http/impl/BinaryHandler.java index 9bb218c17..6bd5892f8 100644 --- a/src/main/java/io/github/hapjava/server/impl/http/impl/BinaryHandler.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/BinaryHandler.java @@ -6,7 +6,6 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageCodec; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,7 +24,6 @@ public BinaryHandler(HomekitClientConnection connection) { @Override protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception { if (started) { - debugData("Sending data", msg, ctx); byte[] b = new byte[msg.readableBytes()]; msg.readBytes(b); out.writeBytes(connection.encryptResponse(b)); @@ -40,7 +38,6 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) t in.readBytes(b); byte[] decrypted = connection.decryptRequest(b); ByteBuf outBuf = Unpooled.copiedBuffer(decrypted); - debugData("Received data", outBuf, ctx); out.add(outBuf); started = true; } @@ -55,13 +52,4 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E } super.exceptionCaught(ctx, cause); } - - private void debugData(String msg, ByteBuf b, ChannelHandlerContext ctx) throws Exception { - if (logger.isTraceEnabled()) { - logger.trace( - String.format( - "%s [%s]:%n%s", - msg, ctx.channel().remoteAddress().toString(), b.toString(StandardCharsets.UTF_8))); - } - } } diff --git a/src/main/java/io/github/hapjava/services/impl/HAPProtocolInformationService.java b/src/main/java/io/github/hapjava/services/impl/HAPProtocolInformationService.java new file mode 100644 index 000000000..8363320cf --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/HAPProtocolInformationService.java @@ -0,0 +1,12 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.characteristics.impl.common.VersionCharacteristic; + +/** This service describes label scheme. */ +public class HAPProtocolInformationService extends AbstractServiceImpl { + + public HAPProtocolInformationService(VersionCharacteristic versionCharacteristic) { + super("000000A2-0000-1000-8000-0026BB765291"); + addCharacteristic(versionCharacteristic); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/MicrophoneService.java b/src/main/java/io/github/hapjava/services/impl/MicrophoneService.java index 741af556f..3f72bd4f7 100644 --- a/src/main/java/io/github/hapjava/services/impl/MicrophoneService.java +++ b/src/main/java/io/github/hapjava/services/impl/MicrophoneService.java @@ -4,7 +4,6 @@ import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithVolume; import io.github.hapjava.characteristics.impl.audio.MuteCharacteristic; -import io.github.hapjava.characteristics.impl.audio.MuteEnum; import io.github.hapjava.characteristics.impl.audio.VolumeCharacteristic; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; @@ -22,8 +21,8 @@ public MicrophoneService(MuteCharacteristic muteCharacteristic) { public MicrophoneService(MicrophoneAccessory accessory) { this( new MuteCharacteristic( - () -> accessory.isMuted().thenApply(s -> s ? MuteEnum.ON : MuteEnum.OFF), - (v) -> accessory.setMute(v == MuteEnum.ON), + accessory::isMuted, + accessory::setMute, accessory::subscribeMuteState, accessory::unsubscribeMuteState)); if (accessory instanceof AccessoryWithName) { diff --git a/src/main/java/io/github/hapjava/services/impl/ServiceLabelService.java b/src/main/java/io/github/hapjava/services/impl/ServiceLabelService.java new file mode 100644 index 000000000..bee40f094 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/ServiceLabelService.java @@ -0,0 +1,13 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.characteristics.impl.common.ServiceLabelNamespaceCharacteristic; + +/** This service describes label scheme. */ +public class ServiceLabelService extends AbstractServiceImpl { + + public ServiceLabelService( + ServiceLabelNamespaceCharacteristic serviceLabelNamespaceCharacteristic) { + super("000000CC-0000-1000-8000-0026BB765291"); + addCharacteristic(serviceLabelNamespaceCharacteristic); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/SpeakerService.java b/src/main/java/io/github/hapjava/services/impl/SpeakerService.java index b92eb4d4b..0c2a073d8 100644 --- a/src/main/java/io/github/hapjava/services/impl/SpeakerService.java +++ b/src/main/java/io/github/hapjava/services/impl/SpeakerService.java @@ -4,7 +4,6 @@ import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithVolume; import io.github.hapjava.characteristics.impl.audio.MuteCharacteristic; -import io.github.hapjava.characteristics.impl.audio.MuteEnum; import io.github.hapjava.characteristics.impl.audio.VolumeCharacteristic; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; @@ -19,8 +18,8 @@ public SpeakerService(MuteCharacteristic muteCharacteristic) { public SpeakerService(SpeakerAccessory accessory) { this( new MuteCharacteristic( - () -> accessory.isMuted().thenApply(s -> s ? MuteEnum.ON : MuteEnum.OFF), - (v) -> accessory.setMute(v == MuteEnum.ON), + accessory::isMuted, + accessory::setMute, accessory::subscribeMuteState, accessory::unsubscribeMuteState)); if (accessory instanceof AccessoryWithName) { From f9502d236dcdc955a56ba6b472f82ab81cbb1e4f Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Thu, 7 May 2020 22:45:22 +0200 Subject: [PATCH 015/138] fix the Id for TargetHorizontalTiltAngleCharacteristic Signed-off-by: Eugen Freiter --- .../windowcovering/TargetHorizontalTiltAngleCharacteristic.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java index a12a1da80..33d31d0f0 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java @@ -22,7 +22,7 @@ public TargetHorizontalTiltAngleCharacteristic( Consumer subscriber, Runnable unsubscriber) { super( - "000000B2-0000-1000-8000-0026BB765291", + "0000007B-0000-1000-8000-0026BB765291", "target horizontal tilt angle", -90, 90, From 12e3b45e7b64fcb5ff1f53767560621a5f15cacc Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Tue, 12 May 2020 12:23:49 +0200 Subject: [PATCH 016/138] add Faucet and HeaterCooler Signed-off-by: Eugen Freiter --- CHANGES.md | 8 +- README.md | 6 +- .../hapjava/accessories/FanAccessory.java | 2 +- .../hapjava/accessories/FaucetAccessory.java | 48 ++++++ .../accessories/HeaterCoolerAccessory.java | 108 +++++++++++++ .../AccessoryWithTemperatureDisplayUnits.java | 37 +++++ .../impl/airquality/AirQualityEnum.java | 6 +- ...urrentHeaterCoolerStateCharacteristic.java | 31 ++++ .../CurrentHeaterCoolerStateEnum.java | 40 +++++ ...TargetHeaterCoolerStateCharacteristic.java | 41 +++++ .../TargetHeaterCoolerStateEnum.java | 39 +++++ .../CurrentTemperatureCharacteristic.java | 2 +- .../services/impl/AirQualityService.java | 2 +- .../hapjava/services/impl/FaucetService.java | 49 ++++++ .../services/impl/HeaterCoolerService.java | 151 ++++++++++++++++++ 15 files changed, 557 insertions(+), 13 deletions(-) create mode 100644 src/main/java/io/github/hapjava/accessories/FaucetAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/HeaterCoolerAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTemperatureDisplayUnits.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateEnum.java create mode 100644 src/main/java/io/github/hapjava/services/impl/FaucetService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java diff --git a/CHANGES.md b/CHANGES.md index aae6d6c50..7680cec66 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,12 +1,12 @@ # HAP-Java 2.0.0 * major refactoring to support optional characteristics -* structure and names are adapted to HAP spec structure and naming. +* structure and names adapted to HAP spec structure and naming. * structure is following * `accessory` package include basis accessory as the listed in HAP spec, plus interfaces for optional characteristics. clients should extend the accessory classes. e.g. `WindowCoveringAccessory` or `AccessoryWithBrightness` * `characteristics` package consists of all characteristics, optional and mandatory. e.g. `TargetHorizontalTiltAngleCharacteristic`. The naming is done in accordance to HAP spec. - * `services` package consists of services, which grouping characteristics. e.g. `WindowCoveringService` defines mandatory and optional characteristics for a window covering service as it is defined in HAP spec. - * `server` package consists classes to run homekit server and handle communication -* the process is following: client, e.g. Openhab bindings, extends accessory classes, e.g. `WindowCoveringAccessory` and implements all required methods. WindowCoveringAccessory is linked already to WindowCoveringService, that in turn is link to single characteristics. + * `services` package consists of services, which grouping characteristics. e.g. `WindowCoveringService` defines mandatory and optional characteristics for a window covering service as it is defined in HAP spec. + * `server` package consists classes to run HomeKit server and handle communication +* the process is following: client, e.g. openHAB bindings, extends accessory classes, e.g. `WindowCoveringAccessory` and implements all required methods. WindowCoveringAccessory is linked already to WindowCoveringService, that in turn is link to single characteristics. # HAP-Java 1.1.5 diff --git a/README.md b/README.md index 9694fec1c..16992be14 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Supported HomeKit Accessories ========= Current implementation is based on HAP specification Release R2 (published 2019-07-26) and -fully supports 33 out of 45 HomeKit accessory services defined there. +fully supports 35 out of 45 HomeKit accessory services defined there. | HomeKit Accessory & Service type | Supported by Java-HAP | |--------------------|--------------------| @@ -41,11 +41,11 @@ fully supports 33 out of 45 HomeKit accessory services defined there. | Door | :white_check_mark: | | Doorbell | :white_check_mark: | | Fan | :white_check_mark: | -| Faucet | :x: | +| Faucet | :white_check_mark: | | Filter Maintenance | :x: | | Garage Door Opener | :white_check_mark: | | HAP Protocol Information | :white_check_mark: | -| Heater Cooler | :x: | +| Heater Cooler | :white_check_mark: | | Humidifier Dehumidifier | :x: | | Humidity Sensor | :white_check_mark: | | Irrigation System | :x: | diff --git a/src/main/java/io/github/hapjava/accessories/FanAccessory.java b/src/main/java/io/github/hapjava/accessories/FanAccessory.java index 5b1bb3ac6..fad72fc42 100644 --- a/src/main/java/io/github/hapjava/accessories/FanAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/FanAccessory.java @@ -34,7 +34,7 @@ public interface FanAccessory extends HomekitAccessory { /** * Subscribes to changes in the active state of the fan. * - * @param callback the function to call when the direction changes. + * @param callback the function to call when the active state changes. */ void subscribeActive(HomekitCharacteristicChangeCallback callback); diff --git a/src/main/java/io/github/hapjava/accessories/FaucetAccessory.java b/src/main/java/io/github/hapjava/accessories/FaucetAccessory.java new file mode 100644 index 000000000..907d7bb38 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/FaucetAccessory.java @@ -0,0 +1,48 @@ +package io.github.hapjava.accessories; + +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithFanState; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.FanService; +import io.github.hapjava.services.impl.FaucetService; + +/** + * This service describes accessories like faucets or shower heads. + * + * @author Eugen Freiter + */ +public interface FaucetAccessory extends HomekitAccessory { + /** + * Mandatory: Retrieves the current active state of the faucet. + * + * @return a future that will contain the binary state + */ + CompletableFuture isActive(); + + /** + * Sets the active state of the faucet + * + * @param state the binary state to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setActive(boolean state) throws Exception; + + /** + * Subscribes to changes in the active state of the faucet. + * + * @param callback the function to call when the active state changes. + */ + void subscribeActive(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the active state of the faucet. */ + void unsubscribeActive(); + + @Override + default Collection getServices() { + return Collections.singleton(new FaucetService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/HeaterCoolerAccessory.java b/src/main/java/io/github/hapjava/accessories/HeaterCoolerAccessory.java new file mode 100644 index 000000000..c8ef7a090 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/HeaterCoolerAccessory.java @@ -0,0 +1,108 @@ +package io.github.hapjava.accessories; + +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.heatercooler.CurrentHeaterCoolerStateEnum; +import io.github.hapjava.characteristics.impl.heatercooler.TargetHeaterCoolerStateEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.HeaterCoolerService; + +/** + * Heater Cooler accessory + * + * @author Eugen Freiter + */ +public interface HeaterCoolerAccessory extends HomekitAccessory { + /** + * Retrieves the current temperature, in celsius degrees. + * + * @return a future that will contain the temperature. + */ + CompletableFuture getCurrentTemperature(); + + + /** + * Mandatory: Retrieves the current active state of the fan'. + * + * @return a future that will contain the binary state + */ + CompletableFuture isActive(); + + /** + * Sets the active state of the fan + * + * @param state the binary state to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setActive(boolean state) throws Exception; + + /** + * Retrieves the heater /cooler current state. + * + * @return a future that will contain the heater cooler current state . + */ + CompletableFuture getCurrentHeaterCoolerState(); + + /** + * Retrieves the heater cooler target state. + * + * @return a future that will contain the heater cooler target state . + */ + CompletableFuture getTargetHeaterCoolerState(); + + /** + * set heater cooler target state the lock target state. + * + * @param state heater cooler target state + * @return a future that completes when the change is made + */ + CompletableFuture setTargetHeaterCoolerState(TargetHeaterCoolerStateEnum state); + + /** + * Subscribes to changes in the heater cooler current state. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentHeaterCoolerState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in heater cooler current state. */ + void unsubscribeCurrentHeaterCoolerState(); + + /** + * Subscribes to changes in the heater cooler target state. + * + * @param callback the function to call when the state changes. + */ + void subscribeTargetHeaterCoolerState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in heater cooler target state. */ + void unsubscribeTargetHeaterCoolerState(); + + /** + * Subscribes to changes in the active state of the heater cooler . + * + * @param callback the function to call when the active state changes. + */ + void subscribeActive(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the active state of the heater cooler . */ + void unsubscribeActive(); + + /** + * Subscribes to changes in the current temperature. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentTemperature(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current temperature. */ + void unsubscribeCurrentTemperature(); + + @Override + default Collection getServices() { + return Collections.singleton(new HeaterCoolerService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTemperatureDisplayUnits.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTemperatureDisplayUnits.java new file mode 100644 index 000000000..495c3c8bd --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTemperatureDisplayUnits.java @@ -0,0 +1,37 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import java.util.concurrent.CompletableFuture; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.thermostat.TemperatureDisplayUnitEnum; + +/** Accessory with characteristic that describes units of temperature used for presentation purposes (e.g. the units of temperature displayed on the + screen). */ +public interface AccessoryWithTemperatureDisplayUnits { + + /** + * Retrieves temperature display units + * + * @return a future that will contain temperature display units + */ + CompletableFuture getTemperatureDisplayUnits(); + + + /** + * Sets the temperature display units + * + * @param units the target temperature display units + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setTemperatureDisplayUnits(TemperatureDisplayUnitEnum units) throws Exception; + + /** + * Subscribes to changes in the temperature display units + * + * @param callback the function to call when temperature display units changes. + */ + void subscribeTemperatureDisplayUnits(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the temperature display units */ + void unsubscribeTemperatureDisplayUnits(); +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityEnum.java index c688065af..390b51dfe 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityEnum.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityEnum.java @@ -9,9 +9,9 @@ public enum AirQualityEnum implements CharacteristicEnum { UNKNOWN(0), EXCELLENT(1), - GOOD(0), - FAIR(1), - INFERIOR(1), + GOOD(2), + FAIR(3), + INFERIOR(4), POOR(5); private static final Map reverse; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateCharacteristic.java new file mode 100644 index 000000000..73d75cd13 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateCharacteristic.java @@ -0,0 +1,31 @@ +package io.github.hapjava.characteristics.impl.heatercooler; + +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; + +/** + * This characteristic describes the current state of a heater cooler. + */ +public class CurrentHeaterCoolerStateCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { + + public CurrentHeaterCoolerStateCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000B1-0000-1000-8000-0026BB765291", + "current heater cooler state", + 3, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateEnum.java new file mode 100644 index 000000000..befc53284 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateEnum.java @@ -0,0 +1,40 @@ +package io.github.hapjava.characteristics.impl.heatercooler; + +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; +import io.github.hapjava.characteristics.CharacteristicEnum; + +/** 0 ”Inactive” + 1 ”Idle” + 2 ”Heating” + 3 ”Cooling” */ +public enum CurrentHeaterCoolerStateEnum implements CharacteristicEnum { + INACTIVE(0), + IDLE(1), + HEATING(2), + COOLING(3); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(CurrentHeaterCoolerStateEnum.values()) + .collect(Collectors.toMap(CurrentHeaterCoolerStateEnum::getCode, t -> t)); + } + + public static CurrentHeaterCoolerStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + CurrentHeaterCoolerStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateCharacteristic.java new file mode 100644 index 000000000..31cda96ec --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateCharacteristic.java @@ -0,0 +1,41 @@ +package io.github.hapjava.characteristics.impl.heatercooler; + +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import io.github.hapjava.characteristics.impl.thermostat.TargetHeatingCoolingStateEnum; + +/** + * This characteristic describes the target state of heater cooler. + */ +public class TargetHeaterCoolerStateCharacteristic extends EnumCharacteristic + implements EventableCharacteristic { + + public TargetHeaterCoolerStateCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000B2-0000-1000-8000-0026BB765291", + "target heater cooler state", + 2, + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } + + @Override + protected void setValue(Integer value) throws Exception { + if (!setter.isPresent()) { + return; + } + setter.get().accept(TargetHeaterCoolerStateEnum.fromCode(value)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateEnum.java new file mode 100644 index 000000000..80914273e --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateEnum.java @@ -0,0 +1,39 @@ +package io.github.hapjava.characteristics.impl.heatercooler; + +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; +import io.github.hapjava.characteristics.CharacteristicEnum; + +/** 0 AUTO - ”Heat or Cool” + 1 ”Heat” + 2 ”Cool” + */ +public enum TargetHeaterCoolerStateEnum implements CharacteristicEnum { + AUTO(0), + HEAT(1), + COOL(2); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(TargetHeaterCoolerStateEnum.values()) + .collect(Collectors.toMap(TargetHeaterCoolerStateEnum::getCode, t -> t)); + } + + public static TargetHeaterCoolerStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + TargetHeaterCoolerStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java index 96ea27735..4227ceaab 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java @@ -15,7 +15,7 @@ public CurrentTemperatureCharacteristic( Consumer subscriber, Runnable unsubscriber) { super( - "0000011-0000-1000-8000-0026BB765291", + "00000011-0000-1000-8000-0026BB765291", "current temperature", 0, 100, diff --git a/src/main/java/io/github/hapjava/services/impl/AirQualityService.java b/src/main/java/io/github/hapjava/services/impl/AirQualityService.java index cf4a79952..bc66b3270 100644 --- a/src/main/java/io/github/hapjava/services/impl/AirQualityService.java +++ b/src/main/java/io/github/hapjava/services/impl/AirQualityService.java @@ -29,7 +29,7 @@ public class AirQualityService extends AbstractServiceImpl { public AirQualityService(AirQualityCharacteristic airQuality) { - super("00000096-0000-1000-8000-0026BB765291"); + super("0000008D-0000-1000-8000-0026BB765291"); addCharacteristic(airQuality); } diff --git a/src/main/java/io/github/hapjava/services/impl/FaucetService.java b/src/main/java/io/github/hapjava/services/impl/FaucetService.java new file mode 100644 index 000000000..774d083d5 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/FaucetService.java @@ -0,0 +1,49 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.FaucetAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; +import io.github.hapjava.characteristics.impl.common.ActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.ActiveEnum; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; + +/** This service describes accessories like faucets or shower heads. This service must only be included when an accessory + has either a linked {@link HeaterCoolerService} ith single linked {@link ValveService} or multiple + linked {@link ValveService} (with/without {@link HeaterCoolerService} to describe water + outlets. This service serves as a top level service for such accessories. */ + +public class FaucetService extends AbstractServiceImpl { + + public FaucetService(ActiveCharacteristic active) { + super("000000D7-0000-1000-8000-0026BB765291"); + addCharacteristic(active); + } + + public FaucetService(FaucetAccessory accessory) { + this( + new ActiveCharacteristic( + () -> accessory.isActive().thenApply(s -> s ? ActiveEnum.ACTIVE : ActiveEnum.INACTIVE), + (v) -> accessory.setActive(v == ActiveEnum.ACTIVE), + accessory::subscribeActive, + accessory::unsubscribeActive)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithStatusFault) { + addOptionalCharacteristic( + new StatusFaultCharacteristic( + ((AccessoryWithStatusFault) accessory)::getStatusFault, + ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, + ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + public void addOptionalCharacteristic(StatusFaultCharacteristic statusFault) { + addCharacteristic(statusFault); + } + +} diff --git a/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java b/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java new file mode 100644 index 000000000..af36433a5 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java @@ -0,0 +1,151 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.HeaterCoolerAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithCoolingThresholdTemperature; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithHeatingThresholdTemprature; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithPhysicalControlsLock; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithRotationSpeed; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithSwingMode; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithTemperatureDisplayUnits; +import io.github.hapjava.characteristics.impl.common.ActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.ActiveEnum; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; +import io.github.hapjava.characteristics.impl.fan.LockPhysicalControlsCharacteristic; +import io.github.hapjava.characteristics.impl.fan.RotationSpeedCharacteristic; +import io.github.hapjava.characteristics.impl.fan.SwingModeCharacteristic; +import io.github.hapjava.characteristics.impl.heatercooler.CurrentHeaterCoolerStateCharacteristic; +import io.github.hapjava.characteristics.impl.heatercooler.TargetHeaterCoolerStateCharacteristic; +import io.github.hapjava.characteristics.impl.thermostat.CoolingThresholdTemperatureCharacteristic; +import io.github.hapjava.characteristics.impl.thermostat.CurrentTemperatureCharacteristic; +import io.github.hapjava.characteristics.impl.thermostat.HeatingThresholdTemperatureCharacteristic; +import io.github.hapjava.characteristics.impl.thermostat.TemperatureDisplayUnitCharacteristic; + +/** + * This service can be used to describe either of the following: + * • a heater + * • a cooler + * • a heater and a cooler + * A heater/cooler accessory may have additional: + * • {@link FanService} service to describe a fan which can be independently controlled + * • {@link SlatService} service to control vents + * A heater must include {@link HeatingThresholdTemperatureCharacteristic}. A cooler must include {@link CoolingThresholdTemperatureCharacteristic}. + * A heater/cooler accessory service may include {@link RotationSpeedCharacteristic} to control fan speed if the fan cannot + * be independently controlled. + */ +public class HeaterCoolerService extends AbstractServiceImpl { + + public HeaterCoolerService( + ActiveCharacteristic activeCharacteristic, + CurrentTemperatureCharacteristic currentTemperatureCharacteristic, + CurrentHeaterCoolerStateCharacteristic currentHeaterCoolerStateCharacteristic, + TargetHeaterCoolerStateCharacteristic targetHeaterCoolerStateCharacteristic) { + super("000000BC-0000-1000-8000-0026BB765291"); + addCharacteristic(activeCharacteristic); + addCharacteristic(currentTemperatureCharacteristic); + addCharacteristic(currentHeaterCoolerStateCharacteristic); + addCharacteristic(targetHeaterCoolerStateCharacteristic); + } + + public HeaterCoolerService(HeaterCoolerAccessory accessory) { + this( + new ActiveCharacteristic( + () -> accessory.isActive().thenApply(s -> s ? ActiveEnum.ACTIVE : ActiveEnum.INACTIVE), + (v) -> accessory.setActive(v == ActiveEnum.ACTIVE), + accessory::subscribeActive, + accessory::unsubscribeActive), + new CurrentTemperatureCharacteristic( + accessory::getCurrentTemperature, + accessory::subscribeCurrentTemperature, + accessory::unsubscribeCurrentTemperature + ), + new CurrentHeaterCoolerStateCharacteristic( + accessory::getCurrentHeaterCoolerState, + accessory::subscribeCurrentHeaterCoolerState, + accessory::unsubscribeCurrentHeaterCoolerState + ), + new TargetHeaterCoolerStateCharacteristic( + accessory::getTargetHeaterCoolerState, + accessory::setTargetHeaterCoolerState, + accessory::subscribeTargetHeaterCoolerState, + accessory::unsubscribeTargetHeaterCoolerState + ) + ); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithRotationSpeed) { + addOptionalCharacteristic( + new RotationSpeedCharacteristic( + ((AccessoryWithRotationSpeed) accessory)::getRotationSpeed, + ((AccessoryWithRotationSpeed) accessory)::setRotationSpeed, + ((AccessoryWithRotationSpeed) accessory)::subscribeRotationSpeed, + ((AccessoryWithRotationSpeed) accessory)::unsubscribeRotationSpeed)); + } + if (accessory instanceof AccessoryWithSwingMode) { + addOptionalCharacteristic( + new SwingModeCharacteristic( + ((AccessoryWithSwingMode) accessory)::getSwingMode, + ((AccessoryWithSwingMode) accessory)::setSwingMode, + ((AccessoryWithSwingMode) accessory)::subscribeSwingMode, + ((AccessoryWithSwingMode) accessory)::unsubscribeSwingMode)); + } + if (accessory instanceof AccessoryWithPhysicalControlsLock) { + addOptionalCharacteristic( + new LockPhysicalControlsCharacteristic( + ((AccessoryWithPhysicalControlsLock) accessory)::getLockControls, + ((AccessoryWithPhysicalControlsLock) accessory)::setLockControls, + ((AccessoryWithPhysicalControlsLock) accessory)::subscribeLockControls, + ((AccessoryWithPhysicalControlsLock) accessory)::unsubscribeLockControls)); + } + if (accessory instanceof AccessoryWithCoolingThresholdTemperature) { + new CoolingThresholdTemperatureCharacteristic( + ((AccessoryWithCoolingThresholdTemperature) accessory)::getCoolingThresholdTemperature, + ((AccessoryWithCoolingThresholdTemperature) accessory)::setCoolingThresholdTemperature, + ((AccessoryWithCoolingThresholdTemperature) accessory) + ::subscribeCoolingThresholdTemperature, + ((AccessoryWithCoolingThresholdTemperature) accessory) + ::unsubscribeCoolingThresholdTemperature); + } + if (accessory instanceof AccessoryWithHeatingThresholdTemprature) { + new HeatingThresholdTemperatureCharacteristic( + ((AccessoryWithHeatingThresholdTemprature) accessory)::getHeatingThresholdTemperature, + ((AccessoryWithHeatingThresholdTemprature) accessory)::setHeatingThresholdTemperature, + ((AccessoryWithHeatingThresholdTemprature) accessory) + ::subscribeHeatingThresholdTemperature, + ((AccessoryWithHeatingThresholdTemprature) accessory) + ::unsubscribeHeatingThresholdTemperature); + } + if (accessory instanceof AccessoryWithTemperatureDisplayUnits) { + addOptionalCharacteristic( + new TemperatureDisplayUnitCharacteristic( + ((AccessoryWithTemperatureDisplayUnits) accessory)::getTemperatureDisplayUnits, + ((AccessoryWithTemperatureDisplayUnits) accessory)::setTemperatureDisplayUnits, + ((AccessoryWithTemperatureDisplayUnits) accessory)::subscribeTemperatureDisplayUnits, + ((AccessoryWithTemperatureDisplayUnits) accessory)::unsubscribeTemperatureDisplayUnits)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + public void addOptionalCharacteristic(RotationSpeedCharacteristic speed) { + addCharacteristic(speed); + } + public void addOptionalCharacteristic(SwingModeCharacteristic mode) { + addCharacteristic(mode); + } + public void addOptionalCharacteristic(LockPhysicalControlsCharacteristic lock) { + addCharacteristic(lock); + } + public void addOptionalCharacteristic(CoolingThresholdTemperatureCharacteristic threshold) { + addCharacteristic(threshold); + } + public void addOptionalCharacteristic(HeatingThresholdTemperatureCharacteristic threshold) { + addCharacteristic(threshold); + } + public void addOptionalCharacteristic(TemperatureDisplayUnitCharacteristic displayUnits) { + addCharacteristic(displayUnits); + } + +} From 8d692a1509e7e09c225a230e85cd8f52f2a0ecc7 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Tue, 12 May 2020 19:00:45 +0200 Subject: [PATCH 017/138] add humidifier dehumidifier accessory Signed-off-by: Eugen Freiter --- README.md | 10 +- .../hapjava/accessories/FaucetAccessory.java | 8 +- .../accessories/HeaterCoolerAccessory.java | 11 +- .../HumidifierDehumidifierAccessory.java | 108 +++++++++++++ ...soryWithHumidityDehumidifierThreshold.java | 37 +++++ ...essoryWithHumidityHumidifierThreshold.java | 37 +++++ .../AccessoryWithTemperatureDisplayUnits.java | 12 +- .../AccessoryWithWaterLevel.java | 29 ++++ .../impl/common/WaterLavelCharacteristic.java | 31 ++++ ...urrentHeaterCoolerStateCharacteristic.java | 15 +- .../CurrentHeaterCoolerStateEnum.java | 7 +- ...TargetHeaterCoolerStateCharacteristic.java | 17 +- .../TargetHeaterCoolerStateEnum.java | 7 +- ...difierDehumidifierStateCharacteristic.java | 29 ++++ ...urrentHumidifierDehumidifierStateEnum.java | 37 +++++ ...tyDehumidifierThresholdCharacteristic.java | 35 ++++ ...dityHumidifierThresholdCharacteristic.java | 35 ++++ ...difierDehumidifierStateCharacteristic.java | 39 +++++ ...TargetHumidifierDehumidifierStateEnum.java | 36 +++++ .../hapjava/services/impl/FaucetService.java | 14 +- .../services/impl/HeaterCoolerService.java | 37 +++-- .../impl/HumidifierDehumidifierService.java | 151 ++++++++++++++++++ 22 files changed, 667 insertions(+), 75 deletions(-) create mode 100644 src/main/java/io/github/hapjava/accessories/HumidifierDehumidifierAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHumidityDehumidifierThreshold.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHumidityHumidifierThreshold.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithWaterLevel.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/humidifier/CurrentHumidifierDehumidifierStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/humidifier/CurrentHumidifierDehumidifierStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/humidifier/TargetHumidifierDehumidifierStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/humidifier/TargetHumidifierDehumidifierStateEnum.java create mode 100644 src/main/java/io/github/hapjava/services/impl/HumidifierDehumidifierService.java diff --git a/README.md b/README.md index 16992be14..0b85001d5 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ HAP-Java ========= -HAP-Java is a Java implementation of the Homekit Accessory Protocol. +HAP-Java is a Java implementation of the HomeKit Accessory Protocol. -Using this library, you can create your own Homekit Accessory or Homekit Accessory Bridge. +Using this library, you can create your own HomeKit Accessory or HomeKit Accessory Bridge. This library would not have been possible without [Tian Zhang](https://github.com/KhaosT) who did a lot of the hard work of figuring out how the protocol works in his NodeJS implementation. @@ -18,13 +18,13 @@ Include HAP-Java in your project using maven: ``` -After that, read the [Javadoc](http://beowulfe.github.io/HAP-Java/apidocs/) and check out the [Sample](https://github.com/beowulfe/HAP-Java/tree/sample). +After that, check out the [Sample](https://github.com/hap-java/HAP-Java/tree/sample). Supported HomeKit Accessories ========= Current implementation is based on HAP specification Release R2 (published 2019-07-26) and -fully supports 35 out of 45 HomeKit accessory services defined there. +fully supports 36 out of 45 HomeKit accessory services defined there. | HomeKit Accessory & Service type | Supported by Java-HAP | |--------------------|--------------------| @@ -46,7 +46,7 @@ fully supports 35 out of 45 HomeKit accessory services defined there. | Garage Door Opener | :white_check_mark: | | HAP Protocol Information | :white_check_mark: | | Heater Cooler | :white_check_mark: | -| Humidifier Dehumidifier | :x: | +| Humidifier Dehumidifier | :white_check_mark: | | Humidity Sensor | :white_check_mark: | | Irrigation System | :x: | | Leak Sensor | :white_check_mark: | diff --git a/src/main/java/io/github/hapjava/accessories/FaucetAccessory.java b/src/main/java/io/github/hapjava/accessories/FaucetAccessory.java index 907d7bb38..7e09a927c 100644 --- a/src/main/java/io/github/hapjava/accessories/FaucetAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/FaucetAccessory.java @@ -1,13 +1,11 @@ package io.github.hapjava.accessories; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.CompletableFuture; -import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithFanState; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import io.github.hapjava.services.Service; -import io.github.hapjava.services.impl.FanService; import io.github.hapjava.services.impl.FaucetService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; /** * This service describes accessories like faucets or shower heads. diff --git a/src/main/java/io/github/hapjava/accessories/HeaterCoolerAccessory.java b/src/main/java/io/github/hapjava/accessories/HeaterCoolerAccessory.java index c8ef7a090..0c43ac7fc 100644 --- a/src/main/java/io/github/hapjava/accessories/HeaterCoolerAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/HeaterCoolerAccessory.java @@ -1,13 +1,13 @@ package io.github.hapjava.accessories; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.CompletableFuture; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import io.github.hapjava.characteristics.impl.heatercooler.CurrentHeaterCoolerStateEnum; import io.github.hapjava.characteristics.impl.heatercooler.TargetHeaterCoolerStateEnum; import io.github.hapjava.services.Service; import io.github.hapjava.services.impl.HeaterCoolerService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; /** * Heater Cooler accessory @@ -22,16 +22,15 @@ public interface HeaterCoolerAccessory extends HomekitAccessory { */ CompletableFuture getCurrentTemperature(); - /** - * Mandatory: Retrieves the current active state of the fan'. + * Mandatory: Retrieves the current active state of the Heater Cooler. * * @return a future that will contain the binary state */ CompletableFuture isActive(); /** - * Sets the active state of the fan + * Sets the active state of the Heater Cooler * * @param state the binary state to set * @return a future that completes when the change is made diff --git a/src/main/java/io/github/hapjava/accessories/HumidifierDehumidifierAccessory.java b/src/main/java/io/github/hapjava/accessories/HumidifierDehumidifierAccessory.java new file mode 100644 index 000000000..124cc31bf --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/HumidifierDehumidifierAccessory.java @@ -0,0 +1,108 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.humidifier.CurrentHumidifierDehumidifierStateEnum; +import io.github.hapjava.characteristics.impl.humidifier.TargetHumidifierDehumidifierStateEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.HumidifierDehumidifierService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** + * Humidifier/Dehumidifier accessory + * + * @author Eugen Freiter + */ +public interface HumidifierDehumidifierAccessory extends HomekitAccessory { + /** + * Retrieves the current relative humidity. + * + * @return a future that will contain the humidity as a value between 0 and 100. + */ + CompletableFuture getCurrentHumidity(); + + /** + * Mandatory: Retrieves the current active state of the humidifier/dehumidifier. + * + * @return a future that will contain the binary state + */ + CompletableFuture isActive(); + + /** + * Sets the active state of the humidifier/dehumidifier + * + * @param state the binary state to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setActive(boolean state) throws Exception; + + /** + * Retrieves the humidifier/dehumidifier current state. + * + * @return a future that will contain the humidifier/dehumidifier current state . + */ + CompletableFuture getCurrentHumidifierDehumidifierState(); + + /** + * Retrieves the humidifier/dehumidifier target state. + * + * @return a future that will contain the humidifier/dehumidifier target state . + */ + CompletableFuture getTargetHumidifierDehumidifierState(); + + /** + * set humidifier/dehumidifier target state the lock target state. + * + * @param state humidifier/dehumidifier target state + * @return a future that completes when the change is made + */ + CompletableFuture setTargetHumidifierDehumidifierState( + TargetHumidifierDehumidifierStateEnum state); + + /** + * Subscribes to changes in the humidifier/dehumidifier current state. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentHumidifierDehumidifierState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in humidifier/dehumidifier current state. */ + void unsubscribeCurrentHumidifierDehumidifierState(); + + /** + * Subscribes to changes in the humidifier/dehumidifier target state. + * + * @param callback the function to call when the state changes. + */ + void subscribeTargetHumidifierDehumidifierState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in humidifier/dehumidifier target state. */ + void unsubscribeTargetHumidifierDehumidifierState(); + + /** + * Subscribes to changes in the active state of the humidifier/dehumidifier . + * + * @param callback the function to call when the active state changes. + */ + void subscribeActive(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the active state of the humidifier/dehumidifier . */ + void unsubscribeActive(); + + /** + * Subscribes to changes in the current humidity. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentHumidity(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current humidity. */ + void unsubscribeCurrentHumidity(); + + @Override + default Collection getServices() { + return Collections.singleton(new HumidifierDehumidifierService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHumidityDehumidifierThreshold.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHumidityDehumidifierThreshold.java new file mode 100644 index 000000000..90cdeb790 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHumidityDehumidifierThreshold.java @@ -0,0 +1,37 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Dehumidifier with humidity threshold. + * + * @author Eugen Freiter + */ +public interface AccessoryWithHumidityDehumidifierThreshold { + + /** + * Retrieves the humidity threshold. + * + * @return a future that will contain the humidity threshold. + */ + CompletableFuture getHumidityThreshold(); + + /** + * Sets the humidity threshold above which the dehumidifier should be turned on. + * + * @param value the humidity threshold, in celsius degrees. + * @throws Exception when the threshold cannot be changed. + */ + void setHumidityThreshold(Double value) throws Exception; + + /** + * Subscribes to changes in the humidity threshold. + * + * @param callback the function to call when the state changes. + */ + void subscribeHumidityThreshold(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the humidity threshold. */ + void unsubscribeHumidityThreshold(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHumidityHumidifierThreshold.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHumidityHumidifierThreshold.java new file mode 100644 index 000000000..89158d913 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHumidityHumidifierThreshold.java @@ -0,0 +1,37 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Humidifier with humidity threshold. + * + * @author Eugen Freiter + */ +public interface AccessoryWithHumidityHumidifierThreshold { + + /** + * Retrieves the humidity threshold. + * + * @return a future that will contain the humidity threshold. + */ + CompletableFuture getHumidityThreshold(); + + /** + * Sets the humidity threshold above which the humidifier should be turned on. + * + * @param value the humidity threshold, in celsius degrees. + * @throws Exception when the threshold cannot be changed. + */ + void setHumidityThreshold(Double value) throws Exception; + + /** + * Subscribes to changes in the humidity threshold. + * + * @param callback the function to call when the state changes. + */ + void subscribeHumidityThreshold(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the humidity threshold. */ + void unsubscribeHumidityThreshold(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTemperatureDisplayUnits.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTemperatureDisplayUnits.java index 495c3c8bd..b9407b34b 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTemperatureDisplayUnits.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTemperatureDisplayUnits.java @@ -1,11 +1,13 @@ package io.github.hapjava.accessories.optionalcharacteristic; -import java.util.concurrent.CompletableFuture; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import io.github.hapjava.characteristics.impl.thermostat.TemperatureDisplayUnitEnum; +import java.util.concurrent.CompletableFuture; -/** Accessory with characteristic that describes units of temperature used for presentation purposes (e.g. the units of temperature displayed on the - screen). */ +/** + * Accessory with characteristic that describes units of temperature used for presentation purposes + * (e.g. the units of temperature displayed on the screen). + */ public interface AccessoryWithTemperatureDisplayUnits { /** @@ -15,7 +17,6 @@ public interface AccessoryWithTemperatureDisplayUnits { */ CompletableFuture getTemperatureDisplayUnits(); - /** * Sets the temperature display units * @@ -23,7 +24,8 @@ public interface AccessoryWithTemperatureDisplayUnits { * @return a future that completes when the change is made * @throws Exception when the change cannot be made */ - CompletableFuture setTemperatureDisplayUnits(TemperatureDisplayUnitEnum units) throws Exception; + CompletableFuture setTemperatureDisplayUnits(TemperatureDisplayUnitEnum units) + throws Exception; /** * Subscribes to changes in the temperature display units diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithWaterLevel.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithWaterLevel.java new file mode 100644 index 000000000..45c296645 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithWaterLevel.java @@ -0,0 +1,29 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with water level. + * + * @author Eugen Freiter + */ +public interface AccessoryWithWaterLevel { + + /** + * Retrieves the water level in percent. + * + * @return a future that will contain the water level, expressed as an double between 0 and 100. + */ + CompletableFuture getWaterLevel(); + + /** + * Subscribes to changes in the water level. + * + * @param callback the function to call when the state changes. + */ + void subscribeWaterLevel(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the water level. */ + void unsubscribeWaterLevel(); +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java new file mode 100644 index 000000000..2a1b60751 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java @@ -0,0 +1,31 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes the current water level. */ +public class WaterLavelCharacteristic extends FloatCharacteristic + implements EventableCharacteristic { + + public WaterLavelCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000B5-0000-1000-8000-0026BB765291", + "water level", + 0, + 100, + 1, + "%", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateCharacteristic.java index 73d75cd13..644e2ca2a 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateCharacteristic.java @@ -1,19 +1,16 @@ package io.github.hapjava.characteristics.impl.heatercooler; +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.function.Supplier; -import io.github.hapjava.characteristics.EventableCharacteristic; -import io.github.hapjava.characteristics.ExceptionalConsumer; -import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; -import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; -/** - * This characteristic describes the current state of a heater cooler. - */ -public class CurrentHeaterCoolerStateCharacteristic extends EnumCharacteristic - implements EventableCharacteristic { +/** This characteristic describes the current state of a heater cooler. */ +public class CurrentHeaterCoolerStateCharacteristic + extends EnumCharacteristic implements EventableCharacteristic { public CurrentHeaterCoolerStateCharacteristic( Supplier> getter, diff --git a/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateEnum.java index befc53284..9864acb12 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateEnum.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateEnum.java @@ -1,14 +1,11 @@ package io.github.hapjava.characteristics.impl.heatercooler; +import io.github.hapjava.characteristics.CharacteristicEnum; import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; -import io.github.hapjava.characteristics.CharacteristicEnum; -/** 0 ”Inactive” - 1 ”Idle” - 2 ”Heating” - 3 ”Cooling” */ +/** 0 ”Inactive” 1 ”Idle” 2 ”Heating” 3 ”Cooling” */ public enum CurrentHeaterCoolerStateEnum implements CharacteristicEnum { INACTIVE(0), IDLE(1), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateCharacteristic.java index 31cda96ec..0e360c735 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateCharacteristic.java @@ -1,20 +1,17 @@ package io.github.hapjava.characteristics.impl.heatercooler; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; import io.github.hapjava.characteristics.EventableCharacteristic; import io.github.hapjava.characteristics.ExceptionalConsumer; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; -import io.github.hapjava.characteristics.impl.thermostat.TargetHeatingCoolingStateEnum; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; -/** - * This characteristic describes the target state of heater cooler. - */ -public class TargetHeaterCoolerStateCharacteristic extends EnumCharacteristic - implements EventableCharacteristic { +/** This characteristic describes the target state of heater cooler. */ +public class TargetHeaterCoolerStateCharacteristic + extends EnumCharacteristic implements EventableCharacteristic { public TargetHeaterCoolerStateCharacteristic( Supplier> getter, diff --git a/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateEnum.java index 80914273e..a16d3cd6a 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateEnum.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateEnum.java @@ -1,14 +1,11 @@ package io.github.hapjava.characteristics.impl.heatercooler; +import io.github.hapjava.characteristics.CharacteristicEnum; import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; -import io.github.hapjava.characteristics.CharacteristicEnum; -/** 0 AUTO - ”Heat or Cool” - 1 ”Heat” - 2 ”Cool” - */ +/** 0 AUTO - ”Heat or Cool” 1 ”Heat” 2 ”Cool” */ public enum TargetHeaterCoolerStateEnum implements CharacteristicEnum { AUTO(0), HEAT(1), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/CurrentHumidifierDehumidifierStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/CurrentHumidifierDehumidifierStateCharacteristic.java new file mode 100644 index 000000000..ba72a856b --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/CurrentHumidifierDehumidifierStateCharacteristic.java @@ -0,0 +1,29 @@ +package io.github.hapjava.characteristics.impl.humidifier; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes the current state of a humidifier or/and a dehumidifier. */ +public class CurrentHumidifierDehumidifierStateCharacteristic + extends EnumCharacteristic + implements EventableCharacteristic { + + public CurrentHumidifierDehumidifierStateCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000B3-0000-1000-8000-0026BB765291", + "current humidifier state", + 3, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/CurrentHumidifierDehumidifierStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/CurrentHumidifierDehumidifierStateEnum.java new file mode 100644 index 000000000..d6ec775de --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/CurrentHumidifierDehumidifierStateEnum.java @@ -0,0 +1,37 @@ +package io.github.hapjava.characteristics.impl.humidifier; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Inactive” 1 ”Idle” 2 ”Humidifying” 3 ”Dehumidifying” */ +public enum CurrentHumidifierDehumidifierStateEnum implements CharacteristicEnum { + INACTIVE(0), + IDLE(1), + HUMIDIFYING(2), + DEHUMIDIFYING(3); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(CurrentHumidifierDehumidifierStateEnum.values()) + .collect(Collectors.toMap(CurrentHumidifierDehumidifierStateEnum::getCode, t -> t)); + } + + public static CurrentHumidifierDehumidifierStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + CurrentHumidifierDehumidifierStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java new file mode 100644 index 000000000..4375dca54 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.humidifier; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the relative humidity dehumidifier threshold. The value of this + * characteristic represents the ʼmaximum relative humidityʼ that must be reached before + * dehumidifier is turned on. + */ +public class HumidityDehumidifierThresholdCharacteristic extends FloatCharacteristic { + + public HumidityDehumidifierThresholdCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000C9-0000-1000-8000-0026BB765291", + "humidity threshold dehumidifier", + 0, + 100, + 1, + "%", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java new file mode 100644 index 000000000..8a7a1abcb --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.humidifier; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes the relative humidity humidifier threshold. The value of this + * characteristic represents the ʼminimum relative humidityʼ that must be reached before humidifier + * is turned on. + */ +public class HumidityHumidifierThresholdCharacteristic extends FloatCharacteristic { + + public HumidityHumidifierThresholdCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000CA-0000-1000-8000-0026BB765291", + "humidity threshold humidifier", + 0, + 100, + 1, + "%", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/TargetHumidifierDehumidifierStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/TargetHumidifierDehumidifierStateCharacteristic.java new file mode 100644 index 000000000..63de111d8 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/TargetHumidifierDehumidifierStateCharacteristic.java @@ -0,0 +1,39 @@ +package io.github.hapjava.characteristics.impl.humidifier; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes the target state of a humidifier or/and a dehumidifier.. */ +public class TargetHumidifierDehumidifierStateCharacteristic + extends EnumCharacteristic + implements EventableCharacteristic { + + public TargetHumidifierDehumidifierStateCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000B4-0000-1000-8000-0026BB765291", + "target humidifier state", + 2, + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } + + @Override + protected void setValue(Integer value) throws Exception { + if (!setter.isPresent()) { + return; + } + setter.get().accept(TargetHumidifierDehumidifierStateEnum.fromCode(value)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/TargetHumidifierDehumidifierStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/TargetHumidifierDehumidifierStateEnum.java new file mode 100644 index 000000000..3d4fee0a5 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/TargetHumidifierDehumidifierStateEnum.java @@ -0,0 +1,36 @@ +package io.github.hapjava.characteristics.impl.humidifier; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 - Auto - ”Humidifier or Dehumidifier” 1 ”Humidifier” 2 ”Dehumidifier” */ +public enum TargetHumidifierDehumidifierStateEnum implements CharacteristicEnum { + AUTO(0), + HUMIDIFIER(1), + DEHUMIDIFIER(2); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(TargetHumidifierDehumidifierStateEnum.values()) + .collect(Collectors.toMap(TargetHumidifierDehumidifierStateEnum::getCode, t -> t)); + } + + public static TargetHumidifierDehumidifierStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + TargetHumidifierDehumidifierStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/FaucetService.java b/src/main/java/io/github/hapjava/services/impl/FaucetService.java index 774d083d5..e641f24c7 100644 --- a/src/main/java/io/github/hapjava/services/impl/FaucetService.java +++ b/src/main/java/io/github/hapjava/services/impl/FaucetService.java @@ -8,11 +8,13 @@ import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; -/** This service describes accessories like faucets or shower heads. This service must only be included when an accessory - has either a linked {@link HeaterCoolerService} ith single linked {@link ValveService} or multiple - linked {@link ValveService} (with/without {@link HeaterCoolerService} to describe water - outlets. This service serves as a top level service for such accessories. */ - +/** + * This service describes accessories like faucets or shower heads. This service must only be + * included when an accessory has either a linked {@link HeaterCoolerService} ith single linked + * {@link ValveService} or multiple linked {@link ValveService} (with/without {@link + * HeaterCoolerService} to describe water outlets. This service serves as a top level service for + * such accessories. + */ public class FaucetService extends AbstractServiceImpl { public FaucetService(ActiveCharacteristic active) { @@ -42,8 +44,8 @@ public FaucetService(FaucetAccessory accessory) { public void addOptionalCharacteristic(NameCharacteristic name) { addCharacteristic(name); } + public void addOptionalCharacteristic(StatusFaultCharacteristic statusFault) { addCharacteristic(statusFault); } - } diff --git a/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java b/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java index af36433a5..8c53c5b2b 100644 --- a/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java +++ b/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java @@ -22,16 +22,13 @@ import io.github.hapjava.characteristics.impl.thermostat.TemperatureDisplayUnitCharacteristic; /** - * This service can be used to describe either of the following: - * • a heater - * • a cooler - * • a heater and a cooler - * A heater/cooler accessory may have additional: - * • {@link FanService} service to describe a fan which can be independently controlled - * • {@link SlatService} service to control vents - * A heater must include {@link HeatingThresholdTemperatureCharacteristic}. A cooler must include {@link CoolingThresholdTemperatureCharacteristic}. - * A heater/cooler accessory service may include {@link RotationSpeedCharacteristic} to control fan speed if the fan cannot - * be independently controlled. + * This service can be used to describe either of the following: • a heater • a cooler • a heater + * and a cooler A heater/cooler accessory may have additional: • {@link FanService} service to + * describe a fan which can be independently controlled • {@link SlatService} service to control + * vents A heater must include {@link HeatingThresholdTemperatureCharacteristic}. A cooler must + * include {@link CoolingThresholdTemperatureCharacteristic}. A heater/cooler accessory service may + * include {@link RotationSpeedCharacteristic} to control fan speed if the fan cannot be + * independently controlled. */ public class HeaterCoolerService extends AbstractServiceImpl { @@ -57,20 +54,16 @@ public HeaterCoolerService(HeaterCoolerAccessory accessory) { new CurrentTemperatureCharacteristic( accessory::getCurrentTemperature, accessory::subscribeCurrentTemperature, - accessory::unsubscribeCurrentTemperature - ), + accessory::unsubscribeCurrentTemperature), new CurrentHeaterCoolerStateCharacteristic( accessory::getCurrentHeaterCoolerState, accessory::subscribeCurrentHeaterCoolerState, - accessory::unsubscribeCurrentHeaterCoolerState - ), + accessory::unsubscribeCurrentHeaterCoolerState), new TargetHeaterCoolerStateCharacteristic( accessory::getTargetHeaterCoolerState, accessory::setTargetHeaterCoolerState, accessory::subscribeTargetHeaterCoolerState, - accessory::unsubscribeTargetHeaterCoolerState - ) - ); + accessory::unsubscribeTargetHeaterCoolerState)); if (accessory instanceof AccessoryWithName) { addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); } @@ -122,30 +115,36 @@ public HeaterCoolerService(HeaterCoolerAccessory accessory) { ((AccessoryWithTemperatureDisplayUnits) accessory)::getTemperatureDisplayUnits, ((AccessoryWithTemperatureDisplayUnits) accessory)::setTemperatureDisplayUnits, ((AccessoryWithTemperatureDisplayUnits) accessory)::subscribeTemperatureDisplayUnits, - ((AccessoryWithTemperatureDisplayUnits) accessory)::unsubscribeTemperatureDisplayUnits)); + ((AccessoryWithTemperatureDisplayUnits) accessory) + ::unsubscribeTemperatureDisplayUnits)); } } public void addOptionalCharacteristic(NameCharacteristic name) { addCharacteristic(name); } + public void addOptionalCharacteristic(RotationSpeedCharacteristic speed) { addCharacteristic(speed); } + public void addOptionalCharacteristic(SwingModeCharacteristic mode) { addCharacteristic(mode); } + public void addOptionalCharacteristic(LockPhysicalControlsCharacteristic lock) { addCharacteristic(lock); } + public void addOptionalCharacteristic(CoolingThresholdTemperatureCharacteristic threshold) { addCharacteristic(threshold); } + public void addOptionalCharacteristic(HeatingThresholdTemperatureCharacteristic threshold) { addCharacteristic(threshold); } + public void addOptionalCharacteristic(TemperatureDisplayUnitCharacteristic displayUnits) { addCharacteristic(displayUnits); } - } diff --git a/src/main/java/io/github/hapjava/services/impl/HumidifierDehumidifierService.java b/src/main/java/io/github/hapjava/services/impl/HumidifierDehumidifierService.java new file mode 100644 index 000000000..600b877ea --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/HumidifierDehumidifierService.java @@ -0,0 +1,151 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.HumidifierDehumidifierAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithHumidityDehumidifierThreshold; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithHumidityHumidifierThreshold; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithPhysicalControlsLock; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithRotationSpeed; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithSwingMode; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithWaterLevel; +import io.github.hapjava.characteristics.impl.common.ActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.ActiveEnum; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; +import io.github.hapjava.characteristics.impl.common.WaterLavelCharacteristic; +import io.github.hapjava.characteristics.impl.fan.LockPhysicalControlsCharacteristic; +import io.github.hapjava.characteristics.impl.fan.RotationSpeedCharacteristic; +import io.github.hapjava.characteristics.impl.fan.SwingModeCharacteristic; +import io.github.hapjava.characteristics.impl.humidifier.CurrentHumidifierDehumidifierStateCharacteristic; +import io.github.hapjava.characteristics.impl.humidifier.HumidityDehumidifierThresholdCharacteristic; +import io.github.hapjava.characteristics.impl.humidifier.HumidityHumidifierThresholdCharacteristic; +import io.github.hapjava.characteristics.impl.humidifier.TargetHumidifierDehumidifierStateCharacteristic; +import io.github.hapjava.characteristics.impl.humiditysensor.CurrentRelativeHumidityCharacteristic; + +/** + * This service can be used to describe either of the following: • an air humidifier • an air + * dehumidifier • an air humidifier and an air dehumidifier An air humidifier/dehumidifier accessory + * may have additional: • {@link FanService} service to describe a fan which can be independently + * controlled • {@link SlatService}service to control vents A dehumidifier must include {@link + * HumidityDehumidifierThresholdCharacteristic}. A humidifier must include {@link + * HumidityHumidifierThresholdCharacteristic}. A humidifier/dehumidifier accessory service may + * include {@link RotationSpeedCharacteristic} to control fan speed if the fan cannot be + * independently controlled. + */ +public class HumidifierDehumidifierService extends AbstractServiceImpl { + + public HumidifierDehumidifierService( + ActiveCharacteristic activeCharacteristic, + CurrentRelativeHumidityCharacteristic currentRelativeHumidityCharacteristic, + CurrentHumidifierDehumidifierStateCharacteristic + currentHumidifierDehumidifierStateCharacteristic, + TargetHumidifierDehumidifierStateCharacteristic + targetHumidifierDehumidifierStateCharacteristic) { + super("000000BD-0000-1000-8000-0026BB765291"); + addCharacteristic(activeCharacteristic); + addCharacteristic(currentRelativeHumidityCharacteristic); + addCharacteristic(currentHumidifierDehumidifierStateCharacteristic); + addCharacteristic(targetHumidifierDehumidifierStateCharacteristic); + } + + public HumidifierDehumidifierService(HumidifierDehumidifierAccessory accessory) { + this( + new ActiveCharacteristic( + () -> accessory.isActive().thenApply(s -> s ? ActiveEnum.ACTIVE : ActiveEnum.INACTIVE), + (v) -> accessory.setActive(v == ActiveEnum.ACTIVE), + accessory::subscribeActive, + accessory::unsubscribeActive), + new CurrentRelativeHumidityCharacteristic( + accessory::getCurrentHumidity, + accessory::subscribeCurrentHumidity, + accessory::unsubscribeCurrentHumidity), + new CurrentHumidifierDehumidifierStateCharacteristic( + accessory::getCurrentHumidifierDehumidifierState, + accessory::subscribeCurrentHumidifierDehumidifierState, + accessory::unsubscribeCurrentHumidifierDehumidifierState), + new TargetHumidifierDehumidifierStateCharacteristic( + accessory::getTargetHumidifierDehumidifierState, + accessory::setTargetHumidifierDehumidifierState, + accessory::subscribeTargetHumidifierDehumidifierState, + accessory::unsubscribeTargetHumidifierDehumidifierState)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithRotationSpeed) { + addOptionalCharacteristic( + new RotationSpeedCharacteristic( + ((AccessoryWithRotationSpeed) accessory)::getRotationSpeed, + ((AccessoryWithRotationSpeed) accessory)::setRotationSpeed, + ((AccessoryWithRotationSpeed) accessory)::subscribeRotationSpeed, + ((AccessoryWithRotationSpeed) accessory)::unsubscribeRotationSpeed)); + } + if (accessory instanceof AccessoryWithSwingMode) { + addOptionalCharacteristic( + new SwingModeCharacteristic( + ((AccessoryWithSwingMode) accessory)::getSwingMode, + ((AccessoryWithSwingMode) accessory)::setSwingMode, + ((AccessoryWithSwingMode) accessory)::subscribeSwingMode, + ((AccessoryWithSwingMode) accessory)::unsubscribeSwingMode)); + } + if (accessory instanceof AccessoryWithPhysicalControlsLock) { + addOptionalCharacteristic( + new LockPhysicalControlsCharacteristic( + ((AccessoryWithPhysicalControlsLock) accessory)::getLockControls, + ((AccessoryWithPhysicalControlsLock) accessory)::setLockControls, + ((AccessoryWithPhysicalControlsLock) accessory)::subscribeLockControls, + ((AccessoryWithPhysicalControlsLock) accessory)::unsubscribeLockControls)); + } + if (accessory instanceof AccessoryWithHumidityDehumidifierThreshold) { + addOptionalCharacteristic( + new HumidityDehumidifierThresholdCharacteristic( + ((AccessoryWithHumidityDehumidifierThreshold) accessory)::getHumidityThreshold, + ((AccessoryWithHumidityDehumidifierThreshold) accessory)::setHumidityThreshold, + ((AccessoryWithHumidityDehumidifierThreshold) accessory)::subscribeHumidityThreshold, + ((AccessoryWithHumidityDehumidifierThreshold) accessory) + ::unsubscribeHumidityThreshold)); + } + if (accessory instanceof AccessoryWithHumidityHumidifierThreshold) { + addOptionalCharacteristic( + new HumidityHumidifierThresholdCharacteristic( + ((AccessoryWithHumidityHumidifierThreshold) accessory)::getHumidityThreshold, + ((AccessoryWithHumidityHumidifierThreshold) accessory)::setHumidityThreshold, + ((AccessoryWithHumidityHumidifierThreshold) accessory)::subscribeHumidityThreshold, + ((AccessoryWithHumidityHumidifierThreshold) accessory) + ::unsubscribeHumidityThreshold)); + } + if (accessory instanceof AccessoryWithWaterLevel) { + addOptionalCharacteristic( + new WaterLavelCharacteristic( + ((AccessoryWithWaterLevel) accessory)::getWaterLevel, + ((AccessoryWithWaterLevel) accessory)::subscribeWaterLevel, + ((AccessoryWithWaterLevel) accessory)::unsubscribeWaterLevel)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(RotationSpeedCharacteristic speed) { + addCharacteristic(speed); + } + + public void addOptionalCharacteristic(SwingModeCharacteristic mode) { + addCharacteristic(mode); + } + + public void addOptionalCharacteristic(LockPhysicalControlsCharacteristic lock) { + addCharacteristic(lock); + } + + public void addOptionalCharacteristic(HumidityDehumidifierThresholdCharacteristic threshold) { + addCharacteristic(threshold); + } + + public void addOptionalCharacteristic(HumidityHumidifierThresholdCharacteristic threshold) { + addCharacteristic(threshold); + } + + public void addOptionalCharacteristic(WaterLavelCharacteristic level) { + addCharacteristic(level); + } +} From 9e4985bc88ae0ac653195339192a7a319d6616f0 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Tue, 12 May 2020 23:10:55 +0200 Subject: [PATCH 018/138] add irrigation system, clean up javadocs Signed-off-by: Eugen Freiter --- README.md | 4 +- .../accessories/AirPurifierAccessory.java | 6 +- .../accessories/AirQualityAccessory.java | 6 +- .../io/github/hapjava/accessories/Bridge.java | 1 + .../CarbonDioxideSensorAccessory.java | 9 +- .../CarbonMonoxideSensorAccessory.java | 3 - .../accessories/ContactSensorAccessory.java | 3 - .../hapjava/accessories/DoorAccessory.java | 6 +- .../accessories/DoorbellAccessory.java | 6 +- .../hapjava/accessories/FanAccessory.java | 4 +- .../hapjava/accessories/FaucetAccessory.java | 6 +- .../GarageDoorOpenerAccessory.java | 2 +- .../accessories/HeaterCoolerAccessory.java | 6 +- .../HumidifierDehumidifierAccessory.java | 6 +- .../IrrigationSystemAccessory.java | 82 +++++++++++++++++++ .../accessories/LeakSensorAccessory.java | 3 - .../accessories/LockMechanismAccessory.java | 6 +- .../accessories/MicrophoneAccessory.java | 6 +- .../accessories/MotionSensorAccessory.java | 3 - .../accessories/OccupancySensorAccessory.java | 3 - .../hapjava/accessories/SlatAccessory.java | 6 +- .../accessories/SmokeSensorAccessory.java | 3 - .../hapjava/accessories/SpeakerAccessory.java | 6 +- .../StatelessProgrammableSwitchAccessory.java | 6 +- .../hapjava/accessories/ValveAccessory.java | 2 +- .../hapjava/accessories/WindowAccessory.java | 6 +- .../AccessoryWithAccessoryFlags.java | 6 +- .../AccessoryWithCarbonDioxideLevel.java | 6 +- .../AccessoryWithCarbonMonoxideLevel.java | 6 +- .../AccessoryWithColorTemperature.java | 6 +- ...essoryWithCoolingThresholdTemperature.java | 6 +- .../AccessoryWithCurrentTilting.java | 6 +- .../AccessoryWithDuration.java | 6 +- .../AccessoryWithFanState.java | 2 +- .../AccessoryWithHardwareRevision.java | 6 +- ...soryWithHumidityDehumidifierThreshold.java | 6 +- ...essoryWithHumidityHumidifierThreshold.java | 6 +- .../AccessoryWithIsConfigured.java | 2 - .../AccessoryWithLockCurrentState.java | 6 +- .../AccessoryWithLockTargetState.java | 6 +- .../AccessoryWithName.java | 6 +- .../AccessoryWithNitrogenDioxideDensity.java | 6 +- .../AccessoryWithOzoneDensity.java | 6 +- .../AccessoryWithPM10Density.java | 6 +- .../AccessoryWithPM25Density.java | 6 +- .../AccessoryWithPhysicalControlsLock.java | 6 +- .../AccessoryWithRotationDirection.java | 6 +- .../AccessoryWithRotationSpeed.java | 6 +- .../AccessoryWithSecurityAlarmType.java | 10 +-- .../AccessoryWithServiceLabelIndex.java | 7 +- .../AccessoryWithStatusActive.java | 6 +- .../AccessoryWithStatusFault.java | 8 +- .../AccessoryWithStatusLowBattery.java | 8 +- .../AccessoryWithStatusTampered.java | 11 +-- .../AccessoryWithSulphurDioxideDensity.java | 6 +- .../AccessoryWithSwingMode.java | 6 +- .../AccessoryWithTargetTilting.java | 6 +- .../AccessoryWithVOCDensity.java | 6 +- .../AccessoryWithVolume.java | 6 +- .../AccessoryWithWaterLevel.java | 6 +- .../AccessoryFlagsCharacteristic.java | 5 +- .../HardwareRevisionCharacteristic.java | 2 +- .../ManufacturerCharacteristic.java | 5 +- .../ModelCharacteristic.java | 5 +- .../SerialNumberCharacteristic.java | 5 +- .../CurrentAirPurifierCharacteristic.java | 5 +- .../TargetAirPurifierStateCharacteristic.java | 5 +- .../airquality/AirQualityCharacteristic.java | 4 +- .../NitrogenDioxideDensityCharacteristic.java | 2 +- .../OzoneDensityCharacteristic.java | 2 +- .../airquality/PM10DensityCharacteristic.java | 2 +- .../airquality/PM25DensityCharacteristic.java | 2 +- .../SulphurDioxideDensityCharacteristic.java | 2 +- .../airquality/VOCDensityCharacteristic.java | 5 +- .../impl/audio/MuteCharacteristic.java | 2 +- .../impl/audio/VolumeCharacteristic.java | 8 +- .../battery/ChargingStateCharacteristic.java | 5 +- .../StatusLowBatteryCharacteristic.java | 5 +- .../CarbonDioxideDetectedCharacteristic.java | 4 +- .../CarbonDioxidePeakLevelCharacteristic.java | 5 +- .../CarbonMonoxideDetectedCharacteristic.java | 4 +- .../CarbonMonoxideLevelCharacteristic.java | 2 +- ...CarbonMonoxidePeakLevelCharacteristic.java | 5 +- .../impl/common/ActiveCharacteristic.java | 5 +- .../impl/common/InUseCharacteristic.java | 4 +- .../common/IsConfiguredCharacteristic.java | 6 +- .../impl/common/NameCharacteristic.java | 2 +- .../ObstructionDetectedCharacteristic.java | 7 +- .../common/ProgramModeCharacteristic.java | 28 +++++++ .../impl/common/ProgramModeEnum.java | 39 +++++++++ ...ProgrammableSwitchEventCharacteristic.java | 10 +-- .../ServiceLabelIndexCharacteristic.java | 4 +- .../ServiceLabelNamespaceCharacteristic.java | 5 +- .../common/ServiceLabelNamespaceEnum.java | 4 +- .../common/StatusActiveCharacteristic.java | 7 +- .../common/StatusFaultCharacteristic.java | 6 +- .../common/StatusTamperedCharacteristic.java | 5 +- .../impl/common/VersionCharacteristic.java | 2 +- .../impl/common/WaterLavelCharacteristic.java | 2 +- .../ContactSensorStateCharacteristic.java | 6 +- .../fan/CurrentFanStateCharacteristic.java | 5 +- .../LockPhysicalControlsCharacteristic.java | 7 +- .../impl/garagedoor/CurrentDoorStateEnum.java | 2 +- ...tyDehumidifierThresholdCharacteristic.java | 5 +- ...dityHumidifierThresholdCharacteristic.java | 5 +- ...CurrentRelativeHumidityCharacteristic.java | 5 +- .../TargetRelativeHumidityCharacteristic.java | 5 +- .../LeakDetectedStateCharacteristic.java | 4 +- .../lightbulb/BrightnessCharacteristic.java | 5 +- .../ColorTemperatureCharacteristic.java | 6 +- ...urrentAmbientLightLevelCharacteristic.java | 5 +- .../outlet/OutletInUseCharacteristic.java | 4 +- ...SecuritySystemAlarmTypeCharacteristic.java | 4 +- .../slat/CurrentTiltAngleCharacteristic.java | 6 +- .../impl/slat/SlatTypeCharacteristic.java | 4 +- .../slat/TargetTiltAngleCharacteristic.java | 5 +- .../SmokeDetectedCharacteristic.java | 5 +- ...ingThresholdTemperatureCharacteristic.java | 4 +- ...rentHeatingCoolingStateCharacteristic.java | 1 - .../CurrentHeatingCoolingStateEnum.java | 4 +- ...ingThresholdTemperatureCharacteristic.java | 6 +- ...rgetHeatingCoolingStateCharacteristic.java | 5 +- .../TargetHeatingCoolingStateEnum.java | 6 +- .../TargetTemperatureCharacteristic.java | 3 +- .../TemperatureDisplayUnitEnum.java | 2 +- .../RemainingDurationCharacteristic.java | 10 +-- .../impl/valve/SetDurationCharacteristic.java | 6 +- ...rentHorizontalTiltAngleCharacteristic.java | 8 +- .../CurrentPositionCharacteristic.java | 8 +- ...urrentVerticalTiltAngleCharacteristic.java | 8 +- .../HoldPositionCharacteristic.java | 6 +- .../PositionStateCharacteristic.java | 5 +- .../windowcovering/PositionStateEnum.java | 2 +- ...rgetHorizontalTiltAngleCharacteristic.java | 5 +- .../TargetPositionCharacteristic.java | 8 +- ...TargetVerticalTiltAngleCharacteristic.java | 5 +- .../services/impl/AbstractServiceImpl.java | 5 +- .../impl/AccessoryInformationService.java | 2 +- .../services/impl/AirQualityService.java | 2 +- .../services/impl/DoorbellService.java | 7 +- .../hapjava/services/impl/FaucetService.java | 9 +- .../impl/GarageDoorOpenerService.java | 5 +- .../impl/HAPProtocolInformationService.java | 2 +- .../services/impl/HeaterCoolerService.java | 10 +-- .../impl/HumidifierDehumidifierService.java | 11 +-- .../impl/IrrigationSystemService.java | 71 ++++++++++++++++ .../services/impl/LockMechanismService.java | 5 +- .../hapjava/services/impl/SlatService.java | 6 +- .../hapjava/services/impl/SpeakerService.java | 2 +- .../StatelessProgrammableSwitchService.java | 6 +- .../hapjava/services/impl/ValveService.java | 5 +- .../services/impl/WindowCoveringService.java | 5 +- 152 files changed, 414 insertions(+), 566 deletions(-) create mode 100644 src/main/java/io/github/hapjava/accessories/IrrigationSystemAccessory.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/ProgramModeCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/ProgramModeEnum.java create mode 100644 src/main/java/io/github/hapjava/services/impl/IrrigationSystemService.java diff --git a/README.md b/README.md index 0b85001d5..d27b04e8c 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Supported HomeKit Accessories ========= Current implementation is based on HAP specification Release R2 (published 2019-07-26) and -fully supports 36 out of 45 HomeKit accessory services defined there. +fully supports 37 out of 45 HomeKit accessory services defined there. | HomeKit Accessory & Service type | Supported by Java-HAP | |--------------------|--------------------| @@ -48,7 +48,7 @@ fully supports 36 out of 45 HomeKit accessory services defined there. | Heater Cooler | :white_check_mark: | | Humidifier Dehumidifier | :white_check_mark: | | Humidity Sensor | :white_check_mark: | -| Irrigation System | :x: | +| Irrigation System | :white_check_mark: | | Leak Sensor | :white_check_mark: | | Light Bulb | :white_check_mark: | | Light Sensor | :white_check_mark: | diff --git a/src/main/java/io/github/hapjava/accessories/AirPurifierAccessory.java b/src/main/java/io/github/hapjava/accessories/AirPurifierAccessory.java index a96db6e33..3766f75a1 100644 --- a/src/main/java/io/github/hapjava/accessories/AirPurifierAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/AirPurifierAccessory.java @@ -9,11 +9,7 @@ import java.util.Collections; import java.util.concurrent.CompletableFuture; -/** - * An air purifier. - * - * @author Eugen Freiter - */ +/** An air purifier. */ public interface AirPurifierAccessory extends HomekitAccessory { /** * Mandatory: Retrieves the current active state of the fan'. diff --git a/src/main/java/io/github/hapjava/accessories/AirQualityAccessory.java b/src/main/java/io/github/hapjava/accessories/AirQualityAccessory.java index fca769272..309fb486e 100644 --- a/src/main/java/io/github/hapjava/accessories/AirQualityAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/AirQualityAccessory.java @@ -8,11 +8,7 @@ import java.util.Collections; import java.util.concurrent.CompletableFuture; -/** - * An air quality accessory which can include several sensors. - * - * @author Eugen Freiter - */ +/** An air quality accessory which can include several sensors. */ public interface AirQualityAccessory extends HomekitAccessory { /** diff --git a/src/main/java/io/github/hapjava/accessories/Bridge.java b/src/main/java/io/github/hapjava/accessories/Bridge.java index 2c71c09df..e53c427f2 100644 --- a/src/main/java/io/github/hapjava/accessories/Bridge.java +++ b/src/main/java/io/github/hapjava/accessories/Bridge.java @@ -1,5 +1,6 @@ package io.github.hapjava.accessories; +/** HomeKit bridge service. */ public interface Bridge extends HomekitAccessory { @Override diff --git a/src/main/java/io/github/hapjava/accessories/CarbonDioxideSensorAccessory.java b/src/main/java/io/github/hapjava/accessories/CarbonDioxideSensorAccessory.java index 0bd56c4ee..185aedd13 100644 --- a/src/main/java/io/github/hapjava/accessories/CarbonDioxideSensorAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/CarbonDioxideSensorAccessory.java @@ -8,14 +8,7 @@ import java.util.Collections; import java.util.concurrent.CompletableFuture; -/** - * A carbon dioxide sensor reports whether carbon dioxide has been detected or not. - * - *

Carbon dioxide sensors that run on batteries will need to implement this interface and also - * implement {@link BatteryAccessory}. - * - * @author Eugen Freiter - */ +/** A carbon dioxide sensor reports carbon dioxide level. */ public interface CarbonDioxideSensorAccessory extends HomekitAccessory { /** diff --git a/src/main/java/io/github/hapjava/accessories/CarbonMonoxideSensorAccessory.java b/src/main/java/io/github/hapjava/accessories/CarbonMonoxideSensorAccessory.java index 9bb23f727..93cfebbd6 100644 --- a/src/main/java/io/github/hapjava/accessories/CarbonMonoxideSensorAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/CarbonMonoxideSensorAccessory.java @@ -11,9 +11,6 @@ /** * A carbon monoxide sensor reports whether carbon monoxide has been detected or not. * - *

Carbon monoxide sensors that run on batteries will need to implement this interface and also - * implement {@link BatteryAccessory}. - * * @author Gaston Dombiak */ public interface CarbonMonoxideSensorAccessory extends HomekitAccessory { diff --git a/src/main/java/io/github/hapjava/accessories/ContactSensorAccessory.java b/src/main/java/io/github/hapjava/accessories/ContactSensorAccessory.java index 61db18768..754f6981b 100644 --- a/src/main/java/io/github/hapjava/accessories/ContactSensorAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/ContactSensorAccessory.java @@ -12,9 +12,6 @@ * A contact sensor that reports whether contact is detected or not. Typical contact sensors are * window/door sensors. When contact is detected it means that the door/window is closed. * - *

Contact sensors that run on batteries will need to implement this interface and also implement - * {@link BatteryAccessory}. - * * @author Gaston Dombiak */ public interface ContactSensorAccessory extends HomekitAccessory { diff --git a/src/main/java/io/github/hapjava/accessories/DoorAccessory.java b/src/main/java/io/github/hapjava/accessories/DoorAccessory.java index dbd7b3b7e..1049c7686 100644 --- a/src/main/java/io/github/hapjava/accessories/DoorAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/DoorAccessory.java @@ -8,11 +8,7 @@ import java.util.Collections; import java.util.concurrent.CompletableFuture; -/** - * A door with status - * - * @author Eugen Freiter - */ +/** A motorized door with current and target position. */ public interface DoorAccessory extends HomekitAccessory { /** diff --git a/src/main/java/io/github/hapjava/accessories/DoorbellAccessory.java b/src/main/java/io/github/hapjava/accessories/DoorbellAccessory.java index 6bcdeb17a..d46d4a00e 100644 --- a/src/main/java/io/github/hapjava/accessories/DoorbellAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/DoorbellAccessory.java @@ -8,11 +8,7 @@ import java.util.Collections; import java.util.concurrent.CompletableFuture; -/** - * The Doorbell accessory describes a doorbell. - * - * @author Eugen Freiter - */ +/** doorbell accessory with a switch (button push) event. */ public interface DoorbellAccessory extends HomekitAccessory { /** diff --git a/src/main/java/io/github/hapjava/accessories/FanAccessory.java b/src/main/java/io/github/hapjava/accessories/FanAccessory.java index fad72fc42..641214e76 100644 --- a/src/main/java/io/github/hapjava/accessories/FanAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/FanAccessory.java @@ -1,6 +1,5 @@ package io.github.hapjava.accessories; -import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithFanState; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import io.github.hapjava.services.Service; import io.github.hapjava.services.impl.FanService; @@ -9,8 +8,7 @@ import java.util.concurrent.CompletableFuture; /** - * A fan with basic characteristics. User {@link AccessoryWithFanState} for Fan with more - * characteristics. + * A fan with mandatory characteristics. * * @author Andy Lintner */ diff --git a/src/main/java/io/github/hapjava/accessories/FaucetAccessory.java b/src/main/java/io/github/hapjava/accessories/FaucetAccessory.java index 7e09a927c..7a000fcec 100644 --- a/src/main/java/io/github/hapjava/accessories/FaucetAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/FaucetAccessory.java @@ -7,11 +7,7 @@ import java.util.Collections; import java.util.concurrent.CompletableFuture; -/** - * This service describes accessories like faucets or shower heads. - * - * @author Eugen Freiter - */ +/** Faucets or shower heads accessory. */ public interface FaucetAccessory extends HomekitAccessory { /** * Mandatory: Retrieves the current active state of the faucet. diff --git a/src/main/java/io/github/hapjava/accessories/GarageDoorOpenerAccessory.java b/src/main/java/io/github/hapjava/accessories/GarageDoorOpenerAccessory.java index f012d4ca6..37bd8a063 100644 --- a/src/main/java/io/github/hapjava/accessories/GarageDoorOpenerAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/GarageDoorOpenerAccessory.java @@ -10,7 +10,7 @@ import java.util.concurrent.CompletableFuture; /** - * A garage door opener, with control and status of a garage door + * garage door opener accessory with control and status of a garage door * * @author Andy Lintner */ diff --git a/src/main/java/io/github/hapjava/accessories/HeaterCoolerAccessory.java b/src/main/java/io/github/hapjava/accessories/HeaterCoolerAccessory.java index 0c43ac7fc..d403b3839 100644 --- a/src/main/java/io/github/hapjava/accessories/HeaterCoolerAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/HeaterCoolerAccessory.java @@ -9,11 +9,7 @@ import java.util.Collections; import java.util.concurrent.CompletableFuture; -/** - * Heater Cooler accessory - * - * @author Eugen Freiter - */ +/** Heater Cooler accessory */ public interface HeaterCoolerAccessory extends HomekitAccessory { /** * Retrieves the current temperature, in celsius degrees. diff --git a/src/main/java/io/github/hapjava/accessories/HumidifierDehumidifierAccessory.java b/src/main/java/io/github/hapjava/accessories/HumidifierDehumidifierAccessory.java index 124cc31bf..bc723ce27 100644 --- a/src/main/java/io/github/hapjava/accessories/HumidifierDehumidifierAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/HumidifierDehumidifierAccessory.java @@ -9,11 +9,7 @@ import java.util.Collections; import java.util.concurrent.CompletableFuture; -/** - * Humidifier/Dehumidifier accessory - * - * @author Eugen Freiter - */ +/** Humidifier/Dehumidifier accessory */ public interface HumidifierDehumidifierAccessory extends HomekitAccessory { /** * Retrieves the current relative humidity. diff --git a/src/main/java/io/github/hapjava/accessories/IrrigationSystemAccessory.java b/src/main/java/io/github/hapjava/accessories/IrrigationSystemAccessory.java new file mode 100644 index 000000000..dffa2e00b --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/IrrigationSystemAccessory.java @@ -0,0 +1,82 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.common.ActiveEnum; +import io.github.hapjava.characteristics.impl.common.InUseEnum; +import io.github.hapjava.characteristics.impl.common.ProgramModeEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.IrrigationSystemService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** irrigation system accessory that controls several valves. */ +public interface IrrigationSystemAccessory extends HomekitAccessory { + + /** + * An irrigation system is set to Active when the system is enabled. When one of the valves is set + * to In Use, the irrigation system must be set to in use. + * + * @return a future that will contain the binary state + */ + CompletableFuture getActive(); + + /** + * Sets the irrigation active state + * + * @param active the binary state to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setActive(ActiveEnum active) throws Exception; + + /** + * Retrieves the program mode status + * + * @return a future that will contain the valve type. + */ + CompletableFuture getProgramMode(); + + /** + * Retrieves the current inUse state of the irrigation; InUse means one of the valves is set to In + * Use. + * + * @return a future that will contain the binary state + */ + CompletableFuture getInUse(); + + /** + * Subscribes to changes in the inUse state. + * + * @param callback the function to call when the state changes. + */ + void subscribeInUse(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the inUse state. */ + void unsubscribeInUse(); + + /** + * Subscribes to changes in the active state of the irrigation. + * + * @param callback the function to call when the state changes. + */ + void subscribeActive(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the irrigation active state. */ + void unsubscribeActive(); + + /** + * Subscribes to changes in the program mode of the irrigation system. + * + * @param callback the function to call when the state changes. + */ + void subscribeProgramMode(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the program mode. */ + void unsubscribeProgramMode(); + + @Override + default Collection getServices() { + return Collections.singleton(new IrrigationSystemService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/LeakSensorAccessory.java b/src/main/java/io/github/hapjava/accessories/LeakSensorAccessory.java index 04049d8d2..c36ce278c 100644 --- a/src/main/java/io/github/hapjava/accessories/LeakSensorAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/LeakSensorAccessory.java @@ -11,9 +11,6 @@ /** * A leak sensor that reports whether a leak has been detected. * - *

Leak sensors that run on batteries will need to implement this interface and also implement - * {@link BatteryAccessory}. - * * @author Tim Harper */ public interface LeakSensorAccessory extends HomekitAccessory { diff --git a/src/main/java/io/github/hapjava/accessories/LockMechanismAccessory.java b/src/main/java/io/github/hapjava/accessories/LockMechanismAccessory.java index fb3dcf9aa..f12f3e286 100644 --- a/src/main/java/io/github/hapjava/accessories/LockMechanismAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/LockMechanismAccessory.java @@ -9,11 +9,7 @@ import java.util.Collections; import java.util.concurrent.CompletableFuture; -/** - * Lock mechanism - * - * @author Eugen Freiter - */ +/** Lock mechanism with current and target state. */ public interface LockMechanismAccessory extends HomekitAccessory { /** diff --git a/src/main/java/io/github/hapjava/accessories/MicrophoneAccessory.java b/src/main/java/io/github/hapjava/accessories/MicrophoneAccessory.java index 480f1b067..f63f8b70c 100644 --- a/src/main/java/io/github/hapjava/accessories/MicrophoneAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/MicrophoneAccessory.java @@ -7,11 +7,7 @@ import java.util.Collections; import java.util.concurrent.CompletableFuture; -/** - * Microphone accessory. - * - * @author Eugen Freiter - */ +/** Microphone accessory with mute state. */ public interface MicrophoneAccessory extends HomekitAccessory { /** diff --git a/src/main/java/io/github/hapjava/accessories/MotionSensorAccessory.java b/src/main/java/io/github/hapjava/accessories/MotionSensorAccessory.java index b8fdedab8..e843068b1 100644 --- a/src/main/java/io/github/hapjava/accessories/MotionSensorAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/MotionSensorAccessory.java @@ -10,9 +10,6 @@ /** * A motion sensor that reports whether motion has been detected. * - *

Motion sensors that run on batteries will need to implement this interface and also implement - * {@link BatteryAccessory}. - * * @author Gaston Dombiak */ public interface MotionSensorAccessory extends HomekitAccessory { diff --git a/src/main/java/io/github/hapjava/accessories/OccupancySensorAccessory.java b/src/main/java/io/github/hapjava/accessories/OccupancySensorAccessory.java index df2f59709..5fc3cff1e 100644 --- a/src/main/java/io/github/hapjava/accessories/OccupancySensorAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/OccupancySensorAccessory.java @@ -11,9 +11,6 @@ /** * An occupancy sensor that reports whether occupancy has been detected. * - *

Occupancy sensors that run on batteries will need to implement this interface and also - * implement {@link BatteryAccessory}. - * * @author Tim Harper */ public interface OccupancySensorAccessory extends HomekitAccessory { diff --git a/src/main/java/io/github/hapjava/accessories/SlatAccessory.java b/src/main/java/io/github/hapjava/accessories/SlatAccessory.java index 89266a400..62888ab60 100644 --- a/src/main/java/io/github/hapjava/accessories/SlatAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/SlatAccessory.java @@ -9,11 +9,7 @@ import java.util.Collections; import java.util.concurrent.CompletableFuture; -/** - * A slat which tilts on a vertical or a horizontal axis. - * - * @author Eugen Freiter - */ +/** A slat accessory which tilts on a vertical or a horizontal axis. */ public interface SlatAccessory extends HomekitAccessory { /** diff --git a/src/main/java/io/github/hapjava/accessories/SmokeSensorAccessory.java b/src/main/java/io/github/hapjava/accessories/SmokeSensorAccessory.java index ca1945351..b6a8b1054 100644 --- a/src/main/java/io/github/hapjava/accessories/SmokeSensorAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/SmokeSensorAccessory.java @@ -11,9 +11,6 @@ /** * A smoke sensor reports whether smoke has been detected or not. * - *

Smoke sensors that run on batteries will need to implement this interface and also implement - * {@link BatteryAccessory}. - * * @author Gaston Dombiak */ public interface SmokeSensorAccessory extends HomekitAccessory { diff --git a/src/main/java/io/github/hapjava/accessories/SpeakerAccessory.java b/src/main/java/io/github/hapjava/accessories/SpeakerAccessory.java index f0c44e228..661607a30 100644 --- a/src/main/java/io/github/hapjava/accessories/SpeakerAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/SpeakerAccessory.java @@ -7,11 +7,7 @@ import java.util.Collections; import java.util.concurrent.CompletableFuture; -/** - * Speaker accessory. - * - * @author Eugen Freiter - */ +/** Speaker accessory. */ public interface SpeakerAccessory extends HomekitAccessory { /** diff --git a/src/main/java/io/github/hapjava/accessories/StatelessProgrammableSwitchAccessory.java b/src/main/java/io/github/hapjava/accessories/StatelessProgrammableSwitchAccessory.java index fa36713bd..354c36587 100644 --- a/src/main/java/io/github/hapjava/accessories/StatelessProgrammableSwitchAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/StatelessProgrammableSwitchAccessory.java @@ -8,11 +8,7 @@ import java.util.Collections; import java.util.concurrent.CompletableFuture; -/** - * The Stateless Programmable Switch accessory describes a stateless programmable switch.. - * - * @author Eugen Freiter - */ +/** The Stateless Programmable Switch accessory */ public interface StatelessProgrammableSwitchAccessory extends HomekitAccessory { /** diff --git a/src/main/java/io/github/hapjava/accessories/ValveAccessory.java b/src/main/java/io/github/hapjava/accessories/ValveAccessory.java index 4348e163a..3db17e41f 100644 --- a/src/main/java/io/github/hapjava/accessories/ValveAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/ValveAccessory.java @@ -81,7 +81,7 @@ public interface ValveAccessory extends HomekitAccessory { */ void subscribeValveType(HomekitCharacteristicChangeCallback callback); - /** Unsubscribes from changes in the valveType state light. */ + /** Unsubscribes from changes in the valveType state. */ void unsubscribeValveType(); @Override diff --git a/src/main/java/io/github/hapjava/accessories/WindowAccessory.java b/src/main/java/io/github/hapjava/accessories/WindowAccessory.java index f5a7559b3..6cc52322b 100644 --- a/src/main/java/io/github/hapjava/accessories/WindowAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/WindowAccessory.java @@ -8,11 +8,7 @@ import java.util.Collections; import java.util.concurrent.CompletableFuture; -/** - * A Window with status of a door - * - * @author Eugen Freiter - */ +/** motorized window accessory. */ public interface WindowAccessory extends HomekitAccessory { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithAccessoryFlags.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithAccessoryFlags.java index 05634a3e8..27d9c2e18 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithAccessoryFlags.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithAccessoryFlags.java @@ -4,11 +4,7 @@ import io.github.hapjava.characteristics.impl.accessoryinformation.AccessoryFlagsEnum; import java.util.concurrent.CompletableFuture; -/** - * Accessory with accessory flags, which indicates that additional setup is required - * - * @author Eugen Freiter - */ +/** Accessory with accessory flags, which indicates that additional setup is required */ public interface AccessoryWithAccessoryFlags { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonDioxideLevel.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonDioxideLevel.java index 16260bda2..bd4bb3afc 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonDioxideLevel.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonDioxideLevel.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Accessory with carbon dioxide level and peak level characteristic. - * - * @author Eugen Freiter - */ +/** Accessory with carbon dioxide level and peak level characteristic. */ public interface AccessoryWithCarbonDioxideLevel { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonMonoxideLevel.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonMonoxideLevel.java index befb4d5ac..a3946c40b 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonMonoxideLevel.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonMonoxideLevel.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Accessory with carbon dioxide level and peak level characteristic. - * - * @author Eugen Freiter - */ +/** Accessory with carbon dioxide level and peak level characteristic. */ public interface AccessoryWithCarbonMonoxideLevel { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithColorTemperature.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithColorTemperature.java index c6bde73da..fee36884d 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithColorTemperature.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithColorTemperature.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Accessory with color temperature. - * - * @author Eugen Freiter - */ +/** Accessory with color temperature. */ public interface AccessoryWithColorTemperature { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCoolingThresholdTemperature.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCoolingThresholdTemperature.java index 6e529bd53..b96be7b6e 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCoolingThresholdTemperature.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCoolingThresholdTemperature.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Accessory with cooling threshold temperature. - * - * @author Eugen Freiter - */ +/** Accessory with cooling threshold temperature. */ public interface AccessoryWithCoolingThresholdTemperature { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentTilting.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentTilting.java index c8b3125ec..54a558491 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentTilting.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentTilting.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Accessory with current tilting characteristic. - * - * @author Eugen Freiter - */ +/** Accessory with current tilting characteristic. */ public interface AccessoryWithCurrentTilting { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithDuration.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithDuration.java index 7304dc3e0..e3d310dfd 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithDuration.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithDuration.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Accessory with duration characteristic. - * - * @author Eugen Freiter - */ +/** Accessory with duration characteristic. */ public interface AccessoryWithDuration { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFanState.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFanState.java index e2359f85c..75be61e8c 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFanState.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFanState.java @@ -6,7 +6,7 @@ import java.util.concurrent.CompletableFuture; /** - * A fan, with power and rotational characteristics. + * An accessory with current and target fan states. * * @author Andy Lintner */ diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHardwareRevision.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHardwareRevision.java index 6f1259ef4..866921817 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHardwareRevision.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHardwareRevision.java @@ -2,11 +2,7 @@ import java.util.concurrent.CompletableFuture; -/** - * Accessory with name . - * - * @author Eugen Freiter - */ +/** Accessory with hardware revision. */ public interface AccessoryWithHardwareRevision { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHumidityDehumidifierThreshold.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHumidityDehumidifierThreshold.java index 90cdeb790..4dff49352 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHumidityDehumidifierThreshold.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHumidityDehumidifierThreshold.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Dehumidifier with humidity threshold. - * - * @author Eugen Freiter - */ +/** Dehumidifier with humidity threshold. */ public interface AccessoryWithHumidityDehumidifierThreshold { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHumidityHumidifierThreshold.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHumidityHumidifierThreshold.java index 89158d913..f783a3039 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHumidityHumidifierThreshold.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHumidityHumidifierThreshold.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Humidifier with humidity threshold. - * - * @author Eugen Freiter - */ +/** Humidifier with humidity threshold. */ public interface AccessoryWithHumidityHumidifierThreshold { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithIsConfigured.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithIsConfigured.java index b80f1ad65..017fdce5f 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithIsConfigured.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithIsConfigured.java @@ -8,8 +8,6 @@ * Accessory with isConfigured characteristics. This characteristic describes if the service is * configured for use. For example, all of the valves in an irrigation system may not be configured * depending on physical wire connection. - * - * @author Eugen Freiter */ public interface AccessoryWithIsConfigured { diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithLockCurrentState.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithLockCurrentState.java index 2a63b64f5..f3f9782ca 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithLockCurrentState.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithLockCurrentState.java @@ -4,11 +4,7 @@ import io.github.hapjava.characteristics.impl.lock.LockCurrentStateEnum; import java.util.concurrent.CompletableFuture; -/** - * Accessory with current lock state. - * - * @author Eugen Freiter - */ +/** Accessory with current lock state. */ public interface AccessoryWithLockCurrentState { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithLockTargetState.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithLockTargetState.java index b35ca0542..41ef0d3ed 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithLockTargetState.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithLockTargetState.java @@ -4,11 +4,7 @@ import io.github.hapjava.characteristics.impl.lock.LockTargetStateEnum; import java.util.concurrent.CompletableFuture; -/** - * Accessory with lock target state. - * - * @author Eugen Freiter - */ +/** Accessory with lock target state. */ public interface AccessoryWithLockTargetState { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithName.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithName.java index 4d6a22cbb..626429124 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithName.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithName.java @@ -2,11 +2,7 @@ import java.util.concurrent.CompletableFuture; -/** - * Accessory with name. - * - * @author Eugen Freiter - */ +/** Accessory with name. */ public interface AccessoryWithName { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithNitrogenDioxideDensity.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithNitrogenDioxideDensity.java index f90969d2d..685edfb5c 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithNitrogenDioxideDensity.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithNitrogenDioxideDensity.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Accessory with nitrogen dioxide density characteristic. - * - * @author Eugen Freiter - */ +/** Accessory with nitrogen dioxide density characteristic. */ public interface AccessoryWithNitrogenDioxideDensity { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithOzoneDensity.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithOzoneDensity.java index 149547b27..87bb8fa74 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithOzoneDensity.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithOzoneDensity.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Accessory with Ozone Density characteristic. - * - * @author Eugen Freiter - */ +/** Accessory with Ozone Density characteristic. */ public interface AccessoryWithOzoneDensity { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM10Density.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM10Density.java index 380251371..2bfad14ac 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM10Density.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM10Density.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Accessory with PM10 Density characteristic. - * - * @author Eugen Freiter - */ +/** Accessory with PM10 Density characteristic. */ public interface AccessoryWithPM10Density { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM25Density.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM25Density.java index 1203e0bfc..11ad2a12e 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM25Density.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM25Density.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Accessory with PM25 Density characteristic. - * - * @author Eugen Freiter - */ +/** Accessory with PM25 Density characteristic. */ public interface AccessoryWithPM25Density { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPhysicalControlsLock.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPhysicalControlsLock.java index 903abe229..2e1e3cd87 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPhysicalControlsLock.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPhysicalControlsLock.java @@ -4,11 +4,7 @@ import io.github.hapjava.characteristics.impl.fan.LockPhysicalControlsEnum; import java.util.concurrent.CompletableFuture; -/** - * Accessory with physical controls which can be locked, e.g. child lock - * - * @author Eugen Freiter - */ +/** Accessory with physical controls which can be locked, e.g. child lock */ public interface AccessoryWithPhysicalControlsLock { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationDirection.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationDirection.java index 8c77e0ba8..91acf9afa 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationDirection.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationDirection.java @@ -4,11 +4,7 @@ import io.github.hapjava.characteristics.impl.fan.RotationDirectionEnum; import java.util.concurrent.CompletableFuture; -/** - * accessoory with rotation direction characteristics. - * - * @author Eugen Freiter - */ +/** accessory with rotation direction characteristics. */ public interface AccessoryWithRotationDirection { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationSpeed.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationSpeed.java index fcc27f32f..8b00e3350 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationSpeed.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationSpeed.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * accessory with rotation speed characteristics. - * - * @author Eugen Freiter - */ +/** accessory with rotation speed characteristics. */ public interface AccessoryWithRotationSpeed { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSecurityAlarmType.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSecurityAlarmType.java index ca707f8df..036fd35b3 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSecurityAlarmType.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSecurityAlarmType.java @@ -4,17 +4,11 @@ import io.github.hapjava.characteristics.impl.securitysystem.SecuritySystemAlarmTypeEnum; import java.util.concurrent.CompletableFuture; -/** - * This characteristic describes the type of alarm triggered by a security system. A value of 1 - * indicates an ʼunknownʼ cause. Value should revert to 0 when the alarm conditions are cleared. - * - * @author Eugen Freiter - */ +/** This characteristic describes the type of alarm triggered by a security system. */ public interface AccessoryWithSecurityAlarmType { /** - * A value of 1 indicates an ʼunknownʼ cause. Value should revert to 0 when the alarm conditions - * are cleared. + * return alarm type See {@link SecuritySystemAlarmTypeEnum} for possible values * * @return a future with the value */ diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithServiceLabelIndex.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithServiceLabelIndex.java index b350654c2..07cb17fc7 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithServiceLabelIndex.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithServiceLabelIndex.java @@ -2,12 +2,7 @@ import java.util.concurrent.CompletableFuture; -/** - * Accessory with service label index. This characteristic should be used identify the index of the - * label that maps to ”Service Label Namespace” used by the accessory. - * - * @author Eugen Freiter - */ +/** Accessory with service label index. T */ public interface AccessoryWithServiceLabelIndex { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusActive.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusActive.java index 7fb9aa5f3..6bd7a18b5 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusActive.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusActive.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Accessory with status active. - * - * @author Eugen Freiter - */ +/** Accessory with status active. */ public interface AccessoryWithStatusActive { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusFault.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusFault.java index ad2351a26..b6b0aebdf 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusFault.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusFault.java @@ -4,13 +4,7 @@ import io.github.hapjava.characteristics.impl.common.StatusFaultEnum; import java.util.concurrent.CompletableFuture; -/** - * Accessory with characteristic that describes an accessory which has a fault. A non-zero value - * indicates that the accessory has experienced a fault that may be interfering with its intended - * functionality. A value of 0 indicates that there is no fault. - * - * @author Eugen Freiter - */ +/** Accessory with characteristic that describes an accessory which has a fault. */ public interface AccessoryWithStatusFault { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusLowBattery.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusLowBattery.java index 964d016ab..def16df5d 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusLowBattery.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusLowBattery.java @@ -4,13 +4,7 @@ import io.github.hapjava.characteristics.impl.battery.StatusLowBatteryEnum; import java.util.concurrent.CompletableFuture; -/** - * This characteristic describes an accessoryʼs battery status. A status of 1 indicates that the - * battery level of the accessory is low. Value should return to 0 when the battery charges to a - * level thats above the low threshold. - * - * @author Eugen Freiter - */ +/** This characteristic describes an accessoryʼs battery status. */ public interface AccessoryWithStatusLowBattery { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusTampered.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusTampered.java index 5637cb7ee..fe25d2f44 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusTampered.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithStatusTampered.java @@ -4,18 +4,11 @@ import io.github.hapjava.characteristics.impl.common.StatusTamperedEnum; import java.util.concurrent.CompletableFuture; -/** - * This characteristic describes an accessory which has been tampered with. A status of 1 indicates - * that the accessory has been tampered with. Value should return to 0 when the accessory has been - * reset to a non-tampered state, - * - * @author Eugen Freiter - */ +/** This characteristic describes an accessory which has been tampered with. */ public interface AccessoryWithStatusTampered { /** - * Retrieves the status tampered. A status of 1 indicates that the accessory has been tampered - * with. Value should return to 0 when the accessory has been reset to a non-tampered state, + * Retrieves the status tampered. * * @return a future with the value */ diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSulphurDioxideDensity.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSulphurDioxideDensity.java index 7d9473bfd..94fad0be4 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSulphurDioxideDensity.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSulphurDioxideDensity.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Accessory with sulphur dioxide density characteristic. - * - * @author Eugen Freiter - */ +/** Accessory with sulphur dioxide density characteristic. */ public interface AccessoryWithSulphurDioxideDensity { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSwingMode.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSwingMode.java index 92b11058f..ae49ffee6 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSwingMode.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSwingMode.java @@ -4,11 +4,7 @@ import io.github.hapjava.characteristics.impl.fan.SwingModeEnum; import java.util.concurrent.CompletableFuture; -/** - * Accessory with Swing Mode characteristics. - * - * @author Eugen Freiter - */ +/** Accessory with Swing Mode characteristics. */ public interface AccessoryWithSwingMode { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetTilting.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetTilting.java index 76cebc4e3..9c2bc8009 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetTilting.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetTilting.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Accessory with target tilting characteristic. - * - * @author Eugen Freiter - */ +/** Accessory with target tilting characteristic. */ public interface AccessoryWithTargetTilting { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVOCDensity.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVOCDensity.java index fde95da7a..b41cc43b7 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVOCDensity.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVOCDensity.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Accessory with VOC Density characteristic. - * - * @author Eugen Freiter - */ +/** Accessory with VOC Density characteristic. */ public interface AccessoryWithVOCDensity { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolume.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolume.java index 693751fc4..b6dda376f 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolume.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolume.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Accessory with volume values. - * - * @author Eugen Freiter - */ +/** Accessory with volume values. */ public interface AccessoryWithVolume { /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithWaterLevel.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithWaterLevel.java index 45c296645..591a1b5ac 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithWaterLevel.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithWaterLevel.java @@ -3,11 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -/** - * Accessory with water level. - * - * @author Eugen Freiter - */ +/** Accessory with water level. */ public interface AccessoryWithWaterLevel { /** diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/AccessoryFlagsCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/AccessoryFlagsCharacteristic.java index 3dece74b7..1e7885382 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/AccessoryFlagsCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/AccessoryFlagsCharacteristic.java @@ -7,7 +7,10 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** When set indicates accessory requires additional setup. */ +/** + * This characteristic indicates whether accessory requires additional setup. See {@link + * AccessoryFlagsEnum} for possible values. + */ public class AccessoryFlagsCharacteristic extends EnumCharacteristic { public AccessoryFlagsCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/HardwareRevisionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/HardwareRevisionCharacteristic.java index 91fa21335..f323de0d8 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/HardwareRevisionCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/HardwareRevisionCharacteristic.java @@ -5,7 +5,7 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; -/** This characteristic describes a hardware revision string x[.y[.z]] (e.g. ”100.1.1”) */ +/** This characteristic describes a hardware revision in a form x[.y[.z]] (e.g. ”100.1.1”) */ public class HardwareRevisionCharacteristic extends StaticStringCharacteristic { public HardwareRevisionCharacteristic(Supplier> getter) { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ManufacturerCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ManufacturerCharacteristic.java index 0aa4e2189..777b571c2 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ManufacturerCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ManufacturerCharacteristic.java @@ -5,10 +5,7 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; -/** - * This characteristic contains the name of the company whose brand will appear on the accessory, - * e.g., ”Acme”. - */ +/** This characteristic contains the name of the company whose brand will appear on the accessory */ public class ManufacturerCharacteristic extends StaticStringCharacteristic { public ManufacturerCharacteristic(Supplier> getter) { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ModelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ModelCharacteristic.java index ce87e62e3..4df7975af 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ModelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/ModelCharacteristic.java @@ -5,10 +5,7 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; -/** - * This characteristic contains the manufacturer-specific model of the accessory, e.g. ”A1234”. The - * minimum length of this characteristic must be 1. - */ +/** This characteristic contains the manufacturer-specific model of the accessory */ public class ModelCharacteristic extends StaticStringCharacteristic { public ModelCharacteristic(Supplier> getter) { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/SerialNumberCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/SerialNumberCharacteristic.java index 018d35ca5..0c9860d73 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/SerialNumberCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/SerialNumberCharacteristic.java @@ -5,10 +5,7 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; -/** - * This characteristic contains the manufacturer-specific serial number of the accessory, e.g. - * ”1A2B3C4D5E6F”. The length must be greater than 1. - */ +/** This characteristic contains the manufacturer-specific serial number of the accessory. */ public class SerialNumberCharacteristic extends StaticStringCharacteristic { public SerialNumberCharacteristic(Supplier> getter) { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/CurrentAirPurifierCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/CurrentAirPurifierCharacteristic.java index b6d4cf393..051f1b7ea 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/CurrentAirPurifierCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/CurrentAirPurifierCharacteristic.java @@ -7,7 +7,10 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** This characteristic describes the current state of the air purifier. */ +/** + * This characteristic describes the current state of the air purifier. See {@link + * CurrentAirPurifierStateEnum} for possible values. + */ public class CurrentAirPurifierCharacteristic extends EnumCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/TargetAirPurifierStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/TargetAirPurifierStateCharacteristic.java index 680c933bc..ecc762c6b 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/TargetAirPurifierStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/TargetAirPurifierStateCharacteristic.java @@ -8,7 +8,10 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** This characteristic describes the target state of the air purifier. */ +/** + * This characteristic describes the target state of the air purifier. See {@link + * TargetAirPurifierStateEnum} for possible values. + */ public class TargetAirPurifierStateCharacteristic extends EnumCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityCharacteristic.java index b701e7346..ab9feec85 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityCharacteristic.java @@ -7,7 +7,9 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** This characteristic describes the subject assessment of air quality by an accessory. */ +/** + * This characteristic describes the air quality. See {@link AirQualityEnum} for possible values. + */ public class AirQualityCharacteristic extends EnumCharacteristic { public AirQualityCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/NitrogenDioxideDensityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/NitrogenDioxideDensityCharacteristic.java index 657fa7fbf..432a85029 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airquality/NitrogenDioxideDensityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/NitrogenDioxideDensityCharacteristic.java @@ -7,7 +7,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** This characteristic indicates the current NO2 density in micrograms/m3. */ +/** This characteristic contains the current NO2 density in micrograms/m3. */ public class NitrogenDioxideDensityCharacteristic extends FloatCharacteristic { public NitrogenDioxideDensityCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/OzoneDensityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/OzoneDensityCharacteristic.java index fd8372708..542dbce0b 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airquality/OzoneDensityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/OzoneDensityCharacteristic.java @@ -7,7 +7,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** This characteristic indicates the current ozone density in micrograms/m3. */ +/** This characteristic contains the current ozone density in micrograms/m3. */ public class OzoneDensityCharacteristic extends FloatCharacteristic { public OzoneDensityCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM10DensityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM10DensityCharacteristic.java index 0ff36d0e2..fdb0b9b49 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM10DensityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM10DensityCharacteristic.java @@ -8,7 +8,7 @@ import java.util.function.Supplier; /** - * This characteristic indicates the current PM10 micrometer particulate density in micrograms/m3. + * This characteristic contains the current PM10 micrometer particulate density in micrograms/m3. */ public class PM10DensityCharacteristic extends FloatCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM25DensityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM25DensityCharacteristic.java index fc5c1d181..c54831553 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM25DensityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM25DensityCharacteristic.java @@ -8,7 +8,7 @@ import java.util.function.Supplier; /** - * This characteristic indicates the current PM2.5 micrometer particulate density in micrograms/m3. + * This characteristic contains the current PM2.5 micrometer particulate density in micrograms/m3. */ public class PM25DensityCharacteristic extends FloatCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/SulphurDioxideDensityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/SulphurDioxideDensityCharacteristic.java index 0c46dbb99..03f668757 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airquality/SulphurDioxideDensityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/SulphurDioxideDensityCharacteristic.java @@ -7,7 +7,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** This characteristic indicates the current SO2 density in micrograms/m3. */ +/** This characteristic contains the current SO2 density in micrograms/m3. */ public class SulphurDioxideDensityCharacteristic extends FloatCharacteristic { public SulphurDioxideDensityCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/VOCDensityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/VOCDensityCharacteristic.java index 7e92ed82a..76e062f45 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airquality/VOCDensityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/VOCDensityCharacteristic.java @@ -7,7 +7,10 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** This characteristic indicates the current volatile organic compound density in micrograms/m3. */ +/** + * This characteristic indicates the current volatile organic compound (VOC) density in + * micrograms/m3. + */ public class VOCDensityCharacteristic extends FloatCharacteristic { public VOCDensityCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/audio/MuteCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/audio/MuteCharacteristic.java index f56b2aab5..105ca0977 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/audio/MuteCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/audio/MuteCharacteristic.java @@ -8,7 +8,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** A Mute characteristic allows the control of audio input or output accessory respectively. */ +/** Mute characteristic to control audio input or output accessory. */ public class MuteCharacteristic extends BooleanCharacteristic { public MuteCharacteristic( Supplier> getter, diff --git a/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java index 9358f4d0c..2d1caedd7 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java @@ -9,13 +9,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * A Volume characteristic allows the control of input or output volume of an audio input or output - * accessory respectively. The value of this characteristic indicates the percentage of the maximum - * volume supported by the service. - * - * @author Eugen Freiter - */ +/** Volume characteristic to control audio volume. */ public class VolumeCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { public VolumeCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateCharacteristic.java index c6f54e029..7f662a651 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateCharacteristic.java @@ -7,7 +7,10 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** This characteristic describes the charging state of a battery or an accessory. */ +/** + * This characteristic describes the charging state of a battery or an accessory. See {@link + * ChargingStateEnum} for possible values. + */ public class ChargingStateCharacteristic extends EnumCharacteristic { public ChargingStateCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryCharacteristic.java index 300e69dfa..8fa55bbf1 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryCharacteristic.java @@ -8,9 +8,8 @@ import java.util.function.Supplier; /** - * This characteristic describes an accessoryʼs battery status. A status of 1 indicates that the - * battery level of the accessory is low. Value should return to 0 when the battery charges to a - * level thats above the low threshold. + * This characteristic describes an accessory battery status. See {@link StatusLowBatteryEnum} for + * possible values */ public class StatusLowBatteryCharacteristic extends EnumCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedCharacteristic.java index 27b15edd3..443d1d457 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedCharacteristic.java @@ -9,8 +9,8 @@ import java.util.function.Supplier; /** - * This characteristic indicates if a sensor detects abnormal levels of Carbon Dioxide. Value should - * revert to 0 after the Carbon Dioxide levels drop to normal levels. + * This characteristic indicates the levels of Carbon Dioxide. See {@link CarbonDioxideDetectedEnum} + * for possible values. */ public class CarbonDioxideDetectedCharacteristic extends EnumCharacteristic implements EventableCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxidePeakLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxidePeakLevelCharacteristic.java index da5905fb2..a2ad994fc 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxidePeakLevelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxidePeakLevelCharacteristic.java @@ -7,10 +7,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic indicates the highest detected level (ppm) of carbon dioxide detected by a - * sensor. - */ +/** This characteristic indicates the highest detected level (ppm) of carbon dioxide. */ public class CarbonDioxidePeakLevelCharacteristic extends FloatCharacteristic { public CarbonDioxidePeakLevelCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedCharacteristic.java index 09397f864..d9aa32a22 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedCharacteristic.java @@ -9,8 +9,8 @@ import java.util.function.Supplier; /** - * This characteristic indicates if a sensor detects abnormal levels of Carbon Monoxide. Value - * should revert to 0 after the Carbon Monoxide levels drop to normal levels + * This characteristic indicates the levels of Carbon Monoxide. See {@link + * CarbonMonoxideDetectedEnum} for possible values */ public class CarbonMonoxideDetectedCharacteristic extends EnumCharacteristic implements EventableCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideLevelCharacteristic.java index b151842c4..554638a22 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideLevelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideLevelCharacteristic.java @@ -7,7 +7,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** This characteristic indicates the Carbon Monoxide levels detected in parts per million (ppm). */ +/** This characteristic contains the Carbon Monoxide levels in parts per million (ppm). */ public class CarbonMonoxideLevelCharacteristic extends FloatCharacteristic { public CarbonMonoxideLevelCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxidePeakLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxidePeakLevelCharacteristic.java index 118f2e597..3a714674a 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxidePeakLevelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxidePeakLevelCharacteristic.java @@ -7,10 +7,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic indicates the highest detected level (ppm) of Carbon Monoxide detected by a - * sensor. - */ +/** This characteristic indicates the highest detected level (ppm) of Carbon Monoxide. */ public class CarbonMonoxidePeakLevelCharacteristic extends FloatCharacteristic { public CarbonMonoxidePeakLevelCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveCharacteristic.java index 269bd8615..fdc932218 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveCharacteristic.java @@ -8,7 +8,10 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** The Active characteristic indicates whether the service is currently active. */ +/** + * Active characteristic indicates whether the service is currently active. See {@link ActiveEnum} + * for possible values. + */ public class ActiveCharacteristic extends EnumCharacteristic { public ActiveCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/InUseCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/InUseCharacteristic.java index 543c8cf7c..967d69f13 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/InUseCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/InUseCharacteristic.java @@ -8,8 +8,8 @@ import java.util.function.Supplier; /** - * This characteristic describes if the service is in use. The service must be Active before the - * value of this characteristic can be set to in use. + * This characteristic indicates whether the service is in use. See {@link InUseEnum} for possible + * values. */ public class InUseCharacteristic extends EnumCharacteristic { public InUseCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredCharacteristic.java index dd192333f..c63c032c0 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredCharacteristic.java @@ -9,10 +9,8 @@ import java.util.function.Supplier; /** - * This characteristic describes if the service is configured for use. For example, all of the - * valves in an irrigation system may not be configured depending on physical wire connection. If - * the accessory supports updating through HAP, then it must also advertise Paired Write in the - * permissions. + * This characteristic describes whether the service is configured for use. See {@link + * IsConfiguredEnum} for possible values. */ public class IsConfiguredCharacteristic extends EnumCharacteristic { public IsConfiguredCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/NameCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/NameCharacteristic.java index e565fb0c5..49c3a824a 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/NameCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/NameCharacteristic.java @@ -11,7 +11,7 @@ public class NameCharacteristic extends StaticStringCharacteristic { public NameCharacteristic(Supplier> getter) { super( "00000023-0000-1000-8000-0026BB765291", - "name of the accessory", + "name", Optional.of(getter), Optional.empty(), Optional.empty()); diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ObstructionDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ObstructionDetectedCharacteristic.java index 6f152ae9c..2644232e3 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/ObstructionDetectedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ObstructionDetectedCharacteristic.java @@ -7,10 +7,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic describes the current state of an obstruction sensor, such as one that is - * used in a garage door. If the state is true then there is an obstruction detected. - */ +/** This characteristic describes the current state of an obstruction sensor. */ public class ObstructionDetectedCharacteristic extends BooleanCharacteristic { public ObstructionDetectedCharacteristic( Supplier> getter, @@ -18,7 +15,7 @@ public ObstructionDetectedCharacteristic( Runnable unsubscriber) { super( "00000024-0000-1000-8000-0026BB765291", - "Current state of an obstruction sensor", + "Obstruction", Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ProgramModeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ProgramModeCharacteristic.java new file mode 100644 index 000000000..6504049e7 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ProgramModeCharacteristic.java @@ -0,0 +1,28 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic describes if there are programs scheduled on the accessory. See {@link + * ProgramModeEnum} for possible values. + */ +public class ProgramModeCharacteristic extends EnumCharacteristic { + public ProgramModeCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000D1-0000-1000-8000-0026BB765291", + "Program Mode", + 2, + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ProgramModeEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ProgramModeEnum.java new file mode 100644 index 000000000..7924f370b --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ProgramModeEnum.java @@ -0,0 +1,39 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 0 ”No Programs Scheduled” 1 ”Program Scheduled” 2 ”Program Scheduled, currently overriden to + * manual mode” + */ +public enum ProgramModeEnum implements CharacteristicEnum { + NO_SCHEDULED(0), + SCHEDULED(1), + SCHEDULED_MANUAL(2); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(ProgramModeEnum.values()) + .collect(Collectors.toMap(ProgramModeEnum::getCode, t -> t)); + } + + public static ProgramModeEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + ProgramModeEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEventCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEventCharacteristic.java index cce06ab07..de32f0475 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEventCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEventCharacteristic.java @@ -8,13 +8,11 @@ import java.util.function.Supplier; /** - * This characteristic describes an event generated by a programmable switch. Reading this - * characteristic must return the last event triggered for BLE. For IP accessories, the accessory - * must set the value of Paired Read to null(i.e. ”value” : null) in the attribute database. A read - * of this characteristic must always return a null value for IP accessories. The value must only be - * reported in the events (”ev”) property.. + * This characteristic describes an event generated by a programmable switch. For BLE accessories, + * read request should return the last event. For IP accessory, read request must return "null". The + * changes are reported via events * - * @author Eugen Freiter + *

See {@link ProgrammableSwitchEnum} for possible values. */ public class ProgrammableSwitchEventCharacteristic extends EnumCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelIndexCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelIndexCharacteristic.java index c5fa2c05b..522cb04fe 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelIndexCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelIndexCharacteristic.java @@ -7,8 +7,8 @@ import java.util.function.Supplier; /** - * This characteristic should be used identify the index of the label that maps to "Service Label - * Namespace” used by the accessory. + * This characteristic is used to identify the index of the label according to {@link + * ServiceLabelNamespaceCharacteristic} */ public class ServiceLabelIndexCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceCharacteristic.java index d7444e9a2..4d6f1e24d 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceCharacteristic.java @@ -6,9 +6,8 @@ import java.util.function.Supplier; /** - * This characteristic describes the naming schema for an accessory. For example, this - * characteristic can be used to describe the type of labels used to identify individual services of - * an accessory. + * This characteristic describes the naming schema for an accessory. See {@link + * ServiceLabelNamespaceEnum} for possible values. */ public class ServiceLabelNamespaceCharacteristic extends EnumCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceEnum.java index 0523a276a..a9b2df513 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceEnum.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceEnum.java @@ -5,9 +5,7 @@ import java.util.Map; import java.util.stream.Collectors; -/** - * 0 ”Dots. For e.g ”.” ”..” ”...” ”....”” 1 ”Arabic numerals. For e.g. 0,1,2,3” 2-255 ”Reserved” - */ +/** 0 ”Dots. For e.g ”.” ”..” ”...” ”....”” 1 ”Arabic numerals. For e.g. 0,1,2,3” */ public enum ServiceLabelNamespaceEnum implements CharacteristicEnum { DOTS(0), ARABIC_NUMERALS(1); diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusActiveCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusActiveCharacteristic.java index aa3590124..a242ae6a1 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusActiveCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusActiveCharacteristic.java @@ -7,10 +7,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic describes an accessoryʼs current working status. A value of true indicates - * that the accessory is active and is functioning without any errors. - */ +/** This characteristic describes current working (active and functional) status of accessory. */ public class StatusActiveCharacteristic extends BooleanCharacteristic { public StatusActiveCharacteristic( Supplier> getter, @@ -18,7 +15,7 @@ public StatusActiveCharacteristic( Runnable unsubscriber) { super( "00000075-0000-1000-8000-0026BB765291", - "Accessory active and functioning", + "Active status", Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultCharacteristic.java index 51175f22e..773d1c04d 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultCharacteristic.java @@ -7,11 +7,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic describes an accessory which has a fault. A non-zero value indicates that the - * accessory has experienced a fault that may be interfering with its intended functionality. A - * value of 0 indicates that there is no fault. - */ +/** This characteristic describes fault status. See {@link StatusFaultEnum} for possible values. */ public class StatusFaultCharacteristic extends EnumCharacteristic { public StatusFaultCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedCharacteristic.java index dda3f8af7..be0c4ae6b 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedCharacteristic.java @@ -8,9 +8,8 @@ import java.util.function.Supplier; /** - * This characteristic describes an accessory which has been tampered with. A status of 1 indicates - * that the accessory has been tampered with. Value should return to 0 when the accessory has been - * reset to a non-tampered state. + * This characteristic describes tampered status. See {@link StatusTamperedEnum} for possible + * values. */ public class StatusTamperedCharacteristic extends EnumCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/VersionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/VersionCharacteristic.java index f3964f78c..038c46c5c 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/VersionCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/VersionCharacteristic.java @@ -5,7 +5,7 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; -/** This characteristic describes a name and must not be a null value. */ +/** This characteristic describes HAP version. */ public class VersionCharacteristic extends StaticStringCharacteristic { public VersionCharacteristic(Supplier> getter) { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java index 2a1b60751..dda11b087 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java @@ -8,7 +8,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** This characteristic describes the current water level. */ +/** This characteristic describes the water level. */ public class WaterLavelCharacteristic extends FloatCharacteristic implements EventableCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactSensorStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactSensorStateCharacteristic.java index e3a252665..0dfb7ad9c 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactSensorStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactSensorStateCharacteristic.java @@ -9,8 +9,8 @@ import java.util.function.Supplier; /** - * This characteristic describes the state of a door/window contact sensor. A value of 0 indicates - * that the contact is detected. A value of 1 indicates that the contact is not detected. + * This characteristic describes the state of a door/window contact sensor. See {@link + * ContactStateEnum} for possible values. */ public class ContactSensorStateCharacteristic extends EnumCharacteristic implements EventableCharacteristic { @@ -21,7 +21,7 @@ public ContactSensorStateCharacteristic( Runnable unsubscriber) { super( "0000006A-0000-1000-8000-0026BB765291", - "Contact Sensor State", + "Contact Sensor", 1, Optional.of(getter), Optional.empty(), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateCharacteristic.java index fb1eb52ef..ec9e5c9dd 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateCharacteristic.java @@ -8,7 +8,10 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** This characteristic describes the current state of the fan. */ +/** + * This characteristic describes the current state of the fan. See {@link CurrentFanStateEnum} for + * possible values. + */ public class CurrentFanStateCharacteristic extends EnumCharacteristic implements EventableCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsCharacteristic.java index 7d97213ef..810aa8769 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsCharacteristic.java @@ -9,10 +9,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic describes a way to lock a set of physical controls on an accessory (eg. child - * lock). - */ +/** This characteristic describes physical controls on an accessory (eg. child lock). */ public class LockPhysicalControlsCharacteristic extends EnumCharacteristic implements EventableCharacteristic { @@ -23,7 +20,7 @@ public LockPhysicalControlsCharacteristic( Runnable unsubscriber) { super( "000000A7-0000-1000-8000-0026BB765291", - "Lock Physical Locks", + "Physical Locks", 1, Optional.of(getter), Optional.of(setter), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateEnum.java index 145046a7e..9d2b145ab 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateEnum.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateEnum.java @@ -8,7 +8,7 @@ /** * 0 ”Open. The door is fully open.” 1 ”Closed. The door is fully closed.” 2 ”Opening. The door is * actively opening.” 3 ”Closing. The door is actively closing.” 4 ”Stopped. The door is not moving, - * and it is not fully open nor fully closed.” 5-255 ”Reserved” + * and it is not fully open nor fully closed.” */ public enum CurrentDoorStateEnum implements CharacteristicEnum { OPEN(0), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java index 4375dca54..b8a4ca569 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java @@ -9,9 +9,8 @@ import java.util.function.Supplier; /** - * This characteristic describes the relative humidity dehumidifier threshold. The value of this - * characteristic represents the ʼmaximum relative humidityʼ that must be reached before - * dehumidifier is turned on. + * This characteristic describes the relative humidity dehumidifier threshold on which dehumidifier + * turns on. */ public class HumidityDehumidifierThresholdCharacteristic extends FloatCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java index 8a7a1abcb..2ee8adbf2 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java @@ -9,9 +9,8 @@ import java.util.function.Supplier; /** - * This characteristic describes the relative humidity humidifier threshold. The value of this - * characteristic represents the ʼminimum relative humidityʼ that must be reached before humidifier - * is turned on. + * This characteristic describes the relative humidity humidifier threshold on which humidifier + * turns on. */ public class HumidityHumidifierThresholdCharacteristic extends FloatCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java index e404e4152..be77125b6 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java @@ -7,10 +7,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic describes the current relative humidity of the accessoryʼs environment. The - * value is expressed as a percentage (%). - */ +/** This characteristic describes the current relative humidity. */ public class CurrentRelativeHumidityCharacteristic extends FloatCharacteristic { public CurrentRelativeHumidityCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java index ae7b539db..ab8ad7e29 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java @@ -8,10 +8,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic describes the target relative humidity that the accessory is actively - * attempting to reach. The value is expressed as a percentage (%). - */ +/** This characteristic describes the target relative humidity. */ public class TargetRelativeHumidityCharacteristic extends FloatCharacteristic { public TargetRelativeHumidityCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateCharacteristic.java index f4e1287be..ce9bc8120 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateCharacteristic.java @@ -9,8 +9,8 @@ import java.util.function.Supplier; /** - * This characteristic indicates if a sensor detected a leak (e.g. water leak, gas leak). A value of - * 1 indicates that a leak is detected. Value should return to 0 when leak stops. + * This characteristic indicates if a sensor detected a leak (e.g. water leak, gas leak). See {@link + * LeakDetectedStateEnum} for possible values */ public class LeakDetectedStateCharacteristic extends EnumCharacteristic implements EventableCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java index d47be8cde..693e42efc 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java @@ -10,9 +10,8 @@ import java.util.function.Supplier; /** - * This characteristic describes a perceived level of brightness, e.g. for lighting, and can be used - * for backlights or color. The value is expressed as a percentage (%) of the maximum level of - * supported brightness. + * This characteristic describes a perceived level of brightness, The value is a percentage (%) of + * the maximum level of supported brightness. */ public class BrightnessCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/ColorTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/ColorTemperatureCharacteristic.java index 722782140..c7779233a 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/ColorTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/ColorTemperatureCharacteristic.java @@ -9,11 +9,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic describes color temperature which is represented in reciprocal megaKelvin - * (MK-1) or mirek scale. (M = 1,000,000 / K where M is the desired mirek value and K is temperature - * in Kelvin) - */ +/** This characteristic describes color temperature in Kelvin */ public class ColorTemperatureCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightsensor/CurrentAmbientLightLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightsensor/CurrentAmbientLightLevelCharacteristic.java index d3736db84..aee5e24d5 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lightsensor/CurrentAmbientLightLevelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightsensor/CurrentAmbientLightLevelCharacteristic.java @@ -7,10 +7,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic indicates the current light level. The value is expressed in Lux units - * (lumens/m2) - */ +/** This characteristic indicates the current light level in Lux */ public class CurrentAmbientLightLevelCharacteristic extends FloatCharacteristic { public CurrentAmbientLightLevelCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/outlet/OutletInUseCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/outlet/OutletInUseCharacteristic.java index 6f5cfdfcd..bd70f46f7 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/outlet/OutletInUseCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/outlet/OutletInUseCharacteristic.java @@ -8,8 +8,8 @@ import java.util.function.Supplier; /** - * This characteristic describes if the power outlet has an appliance e.g., a floor lamp, physically - * plugged in. This characteristic is set to True even if the plugged-in appliance is off. + * This characteristic describes if the power outlet has an appliance physically plugged in. This + * characteristic is set to True even if the plugged-in appliance is off. */ public class OutletInUseCharacteristic extends BooleanCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java index b5b2b04a7..fa768f4fd 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java @@ -8,8 +8,8 @@ import java.util.function.Supplier; /** - * This characteristic describes the type of alarm triggered by a security system. A value of 1 - * indicates an ʼunknownʼ cause. Value should revert to 0 when the alarm conditions are cleared. + * This characteristic describes the type of alarm triggered by a security system. See {@link + * SecuritySystemAlarmTypeEnum} for possible values. */ public class SecuritySystemAlarmTypeCharacteristic extends EnumCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java index 13dbf93c3..3a4868b2c 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java @@ -8,11 +8,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * etc. This characteristic takes values between -90 and 90. A value of 0 indicates that the slats - * are rotated to be fully open. At value 0 the user-facing edge and the window-facing edge are - * perpendicular to the window. - */ +/** This characteristic describe current tilt angle (between -90 and 90) */ public class CurrentTiltAngleCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/SlatTypeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/SlatTypeCharacteristic.java index b995406a6..b65dbdf2c 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/slat/SlatTypeCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/SlatTypeCharacteristic.java @@ -6,9 +6,7 @@ import java.util.function.Supplier; /** - * TThis characteristic describes the type of the slats. If the slats can tilt on a horizontal axis, - * the value of this characteristic must be set to Horizontal. If the slats can tilt on a vertical - * axis, the value of this characteristic must be set to Vertical. + * This characteristic describes the type of the slats. See {@link SlatTypeEnum} for possible values */ public class SlatTypeCharacteristic extends EnumCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java index 40b5e7566..2a327522d 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java @@ -9,10 +9,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic describes the target angle of slats for accessories such as windows, fans, - * portable heater/coolers etc. - */ +/** This characteristic describes the target angle of slats. */ public class TargetTiltAngleCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedCharacteristic.java index 5a33fea6d..76bf58ea9 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedCharacteristic.java @@ -7,10 +7,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic indicates if a sensor detects abnormal levels of smoke. A value of 1 - * indicates that smoke levels are abnormal. Value should return to 0 when smoke levels are normal. - */ +/** This characteristic indicates if a sensor detects smoke. */ public class SmokeDetectedCharacteristic extends EnumCharacteristic { public SmokeDetectedCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java index 2a23edc95..4c5d9907f 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java @@ -9,9 +9,7 @@ import java.util.function.Supplier; /** - * This characteristic describes the cooling threshold in Celsius for accessories that support - * simultaneous heating and cooling. The value of this characteristic represents the maximum - * temperature that must be reached before cooling is turned on. + * This characteristic describes the cooling threshold on which accessories turns on the cooling. */ public class CoolingThresholdTemperatureCharacteristic extends FloatCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateCharacteristic.java index bf0c56637..87cf113a9 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateCharacteristic.java @@ -9,7 +9,6 @@ /** * This characteristic describes the current mode of an accessory that supports cooling or heating - * its environment, e.g. a thermostat is ”heating” a room to 75 degrees Fahrenheit. */ public class CurrentHeatingCoolingStateCharacteristic extends EnumCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateEnum.java index 9f2c01f85..487262975 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateEnum.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateEnum.java @@ -5,9 +5,7 @@ import java.util.Map; import java.util.stream.Collectors; -/** - * 0 ”Off.” 1 ”Heat. The Heater is currently on.” 2 ”Cool. Cooler is currently on.” 3-255 ”Reserved” - */ +/** 0 ”Off.” 1 ”Heat. The Heater is currently on.” 2 ”Cool. Cooler is currently on.” */ public enum CurrentHeatingCoolingStateEnum implements CharacteristicEnum { OFF(0), HEAT(1), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java index fb61aeeb7..0ffd3093f 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java @@ -8,11 +8,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic describes the heating threshold in Celsius for accessories that support - * simultaneous heating and cooling. The value of this characteristic represents the minimum - * temperature that must be reached before heating is turned on. - */ +/** This characteristic describes the heating threshold on which heating get turned on. */ public class HeatingThresholdTemperatureCharacteristic extends FloatCharacteristic { public HeatingThresholdTemperatureCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateCharacteristic.java index 5f74c1543..6c5091451 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateCharacteristic.java @@ -8,10 +8,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic describes the target mode of an accessory that supports heating/cooling, e.g. - * a thermostat. - */ +/** This characteristic describes the target mode of an accessory that supports heating/cooling, */ public class TargetHeatingCoolingStateCharacteristic extends EnumCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateEnum.java index 62f2a3d81..6120422e5 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateEnum.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateEnum.java @@ -6,10 +6,8 @@ import java.util.stream.Collectors; /** - * 0 ”Off” 1 ”Heat. If the current temperature is below the target temperature then turn on - * heating.” 2 ”Cool. If the current temperature is above the target temperature then turn on - * cooling.” 3 ”Auto. Turn on heating or cooling to maintain temperature within the heating and - * cooling threshold of the target temperature.” 4-255 ”Reserved” + * 0 ”Off” 1 ”Heat.” 2 ”Cool." 3 ”Auto. Turn on heating or cooling to maintain depending on the + * temperature” * * @author Andy Lintner */ diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java index 580e3f9f5..f96483d22 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java @@ -10,8 +10,7 @@ /** * This characteristic describes the target temperature in Celsius that the accessory is actively - * attempting to reach. For example, a thermostat cooling a room to 75 degrees Fahrenheit would set - * the target temperature value to 23.9 degrees Celsius. + * attempting to reach. */ public class TargetTemperatureCharacteristic extends FloatCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitEnum.java index 8440f5de9..dbecc60f0 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitEnum.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitEnum.java @@ -9,7 +9,7 @@ /** * The temperature unit used by a {@link TemperatureSensorAccessory}. * - *

0 ”Celsius” 1 ”Fahrenheit” 2-255 ”Reserved” + *

0 ”Celsius” 1 ”Fahrenheit” * * @author Andy Lintner */ diff --git a/src/main/java/io/github/hapjava/characteristics/impl/valve/RemainingDurationCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/valve/RemainingDurationCharacteristic.java index 5f16e396a..4866be230 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/valve/RemainingDurationCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/valve/RemainingDurationCharacteristic.java @@ -9,13 +9,9 @@ import java.util.function.Supplier; /** - * This characteristic describes the remaining duration on the accessory. Notifications on this - * characteristic must only be used if the remaining duration increases/decreases from the - * accessoryʼs usual countdown of remaining duration and when the duration reaches 0. e.g. It must - * not send notifications when the remaining duration is ticking down from 100,99,98... if 100 was - * the initial Set duration. However, if the remaining duration changes to 95 from 92 (increase) or - * 85 from 92 (decrease which is not part of the usual duration countdown), it must send a - * notification. This duration is defined in seconds. + * This characteristic describes the remaining duration of the active status of an accessory, e.g. + * {@link io.github.hapjava.accessories.ValveAccessory}. This characteristic should only notify + * about changes if it defers from normal countdown. */ public class RemainingDurationCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/valve/SetDurationCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/valve/SetDurationCharacteristic.java index 22be62b64..a372d0515 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/valve/SetDurationCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/valve/SetDurationCharacteristic.java @@ -9,11 +9,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic describes the set duration. For a Valve” this duration defines how long a - * valve should be set to ʼIn Useʼ. Once the valve is ʼIn Useʼ, any changes to this characteristic - * take affect in the next operation when the Valve is Active. This duration is defined in seconds. - */ +/** This characteristic describes the duration, how long an accessory should be set to "InUse". */ public class SetDurationCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java index 37583d00e..1255d7eff 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java @@ -8,13 +8,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic describes the current angle of horizontal slats for accessories such as - * windows, fans, portable heater/coolers etc. This characteristic takes values between -90 and 90. - * A value of 0 indicates that the slats are rotated to a fully open position. A value of -90 - * indicates that the slats are rotated all the way in a direction where the user-facing edge is - * higher than the window-facing edge. - */ +/** This characteristic describes the current angle of horizontal slats. */ public class CurrentHorizontalTiltAngleCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java index aaf19ec2e..d51614615 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java @@ -8,13 +8,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic describes the current position of accessories. This characteristic can be - * used with doors, windows, awnings or window coverings. For windows and doors, a value of 0 - * indicates that a window (or door) is fully closed while a value of 100 indicates a fully open - * position. For blinds/shades/awnings, a value of 0 indicates a position that permits the least - * light and a value of 100 indicates a position that allows most light. - */ +/** This characteristic describes the current position of accessories. */ public class CurrentPositionCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java index 590712958..c6258a2db 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java @@ -8,13 +8,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic describes the current angle of vertical slats for accessories such as - * windows, fans, portable heater/coolers etc. This characteristic takes values between -90 and 90. - * A value of 0 indicates that the slats are rotated to be fully open. A value of -90 indicates that - * the slats are rotated all the way in a direction where the user-facing edge is to the left of the - * window-facing edge. - */ +/** This characteristic describes the current angle of vertical slats for accessory. */ public class CurrentVerticalTiltAngleCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/HoldPositionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/HoldPositionCharacteristic.java index e9c768867..7a6455919 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/HoldPositionCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/HoldPositionCharacteristic.java @@ -4,11 +4,7 @@ import io.github.hapjava.characteristics.impl.base.BooleanCharacteristic; import java.util.Optional; -/** - * This characteristic causes the service such as door or window covering to stop at its current - * position. A value of 1 must hold the state of the accessory. For e.g, the window must stop moving - * when this characteristic is written a value of 1. A value of 0 should be ignored. - */ +/** This characteristic causes the accessories like window covering to stop at its current */ public class HoldPositionCharacteristic extends BooleanCharacteristic { public HoldPositionCharacteristic(ExceptionalConsumer setter) { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateCharacteristic.java index 6f4427cfb..73141cb24 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateCharacteristic.java @@ -7,10 +7,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic describes the state of the position of accessories. This characteristic can - * be used with doors, windows, awnings or window coverings for presentation purposes. - */ +/** This characteristic describes the state of the position of accessories. */ public class PositionStateCharacteristic extends EnumCharacteristic { public PositionStateCharacteristic( diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateEnum.java index b82b56a27..86b9174c8 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateEnum.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateEnum.java @@ -7,7 +7,7 @@ /** * 0 ”Going to the minimum value specified in metadata” 1 ”Going to the maximum value specified in - * metadata” 2 ”Stopped” 3-255 ”Reserved” + * metadata” 2 ”Stopped” */ public enum PositionStateEnum implements CharacteristicEnum { DECREASING(0), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java index 33d31d0f0..38c075760 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java @@ -9,10 +9,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic describes the target angle of horizontal slats for accessories such as - * windows, fans, portable heater/coolers etc. - */ +/** This characteristic describes the target angle of horizontal slats for accessories. */ public class TargetHorizontalTiltAngleCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java index 02b3125a6..6398a0b37 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java @@ -9,13 +9,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic describes the target position of accessories. This characteristic can be used - * with doors, windows, awnings or window coverings. For windows and doors, a value of 0 indicates - * that a window (or door) is fully closed while a value of 100 indicates a fully open position. For - * blinds/shades/awnings, a value of 0 indicates a position that permits the least light and a value - * of 100 indicates a position that allows most light. - */ +/** This characteristic describes the target position of accessories like door, window, blinds. */ public class TargetPositionCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java index adfb8a554..4d7c3ad71 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java @@ -9,10 +9,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** - * This characteristic describes the target angle of vertical slats for accessories such as windows, - * fans, portable heater/coolers etc. - */ +/** This characteristic describes the target angle of vertical slats. */ public class TargetVerticalTiltAngleCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { diff --git a/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java b/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java index e0f47b56b..97f99906d 100644 --- a/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java +++ b/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java @@ -13,10 +13,7 @@ abstract class AbstractServiceImpl implements Service { private final String type; private final List characteristics = new LinkedList<>(); - /** - * @param type unique UUID of the service. This information can be obtained from HomeKit Accessory - * Simulator. - */ + /** @param type unique UUID of the service according to HAP specification. */ public AbstractServiceImpl(String type) { this.type = type; } diff --git a/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java b/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java index 2746118bf..8aa7f3635 100644 --- a/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java +++ b/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java @@ -12,7 +12,7 @@ import io.github.hapjava.characteristics.impl.accessoryinformation.SerialNumberCharacteristic; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; -/** Every accessory must expose a single instance of the Accessory Information service. */ +/** Accessory Information service. */ public class AccessoryInformationService extends AbstractServiceImpl { public AccessoryInformationService( diff --git a/src/main/java/io/github/hapjava/services/impl/AirQualityService.java b/src/main/java/io/github/hapjava/services/impl/AirQualityService.java index bc66b3270..c9ccd6d62 100644 --- a/src/main/java/io/github/hapjava/services/impl/AirQualityService.java +++ b/src/main/java/io/github/hapjava/services/impl/AirQualityService.java @@ -25,7 +25,7 @@ import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; import io.github.hapjava.characteristics.impl.common.StatusTamperedCharacteristic; -/** This characteristic describes the subject assessment of air quality by an accessory. */ +/** This service describes air quality sensor. */ public class AirQualityService extends AbstractServiceImpl { public AirQualityService(AirQualityCharacteristic airQuality) { diff --git a/src/main/java/io/github/hapjava/services/impl/DoorbellService.java b/src/main/java/io/github/hapjava/services/impl/DoorbellService.java index 9e0591ba7..fd8fc7004 100644 --- a/src/main/java/io/github/hapjava/services/impl/DoorbellService.java +++ b/src/main/java/io/github/hapjava/services/impl/DoorbellService.java @@ -9,12 +9,7 @@ import io.github.hapjava.characteristics.impl.common.ProgrammableSwitchEventCharacteristic; import io.github.hapjava.characteristics.impl.lightbulb.BrightnessCharacteristic; -/** - * The Doorbell service describes a doorbell and is the primary service of the Video Doorbell - * Profile. - * - * @author Eugen Freiter - */ +/** The Doorbell service describes a doorbell. */ public class DoorbellService extends AbstractServiceImpl { public DoorbellService(ProgrammableSwitchEventCharacteristic switchEvent) { diff --git a/src/main/java/io/github/hapjava/services/impl/FaucetService.java b/src/main/java/io/github/hapjava/services/impl/FaucetService.java index e641f24c7..1028fa9b1 100644 --- a/src/main/java/io/github/hapjava/services/impl/FaucetService.java +++ b/src/main/java/io/github/hapjava/services/impl/FaucetService.java @@ -9,11 +9,10 @@ import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; /** - * This service describes accessories like faucets or shower heads. This service must only be - * included when an accessory has either a linked {@link HeaterCoolerService} ith single linked - * {@link ValveService} or multiple linked {@link ValveService} (with/without {@link - * HeaterCoolerService} to describe water outlets. This service serves as a top level service for - * such accessories. + * This service describes accessories like faucets or shower heads and must only be included when an + * accessory has either a linked {@link HeaterCoolerService}, single linked {@link ValveService} or + * multiple linked {@link ValveService} (with/without {@link HeaterCoolerService} to describe water + * outlets. This service serves as a top level service for such accessories. */ public class FaucetService extends AbstractServiceImpl { diff --git a/src/main/java/io/github/hapjava/services/impl/GarageDoorOpenerService.java b/src/main/java/io/github/hapjava/services/impl/GarageDoorOpenerService.java index 86078ee05..b90a7e5ee 100644 --- a/src/main/java/io/github/hapjava/services/impl/GarageDoorOpenerService.java +++ b/src/main/java/io/github/hapjava/services/impl/GarageDoorOpenerService.java @@ -11,10 +11,7 @@ import io.github.hapjava.characteristics.impl.lock.LockCurrentStateCharacteristic; import io.github.hapjava.characteristics.impl.lock.LockTargetStateCharacteristic; -/** - * This service describes a garage door opener that controls a single door. If a garage has more - * than one door, then each door should have its own Garage Door Opener Service. - */ +/** This service describes a garage door opener that controls a single door. */ public class GarageDoorOpenerService extends AbstractServiceImpl { public GarageDoorOpenerService( diff --git a/src/main/java/io/github/hapjava/services/impl/HAPProtocolInformationService.java b/src/main/java/io/github/hapjava/services/impl/HAPProtocolInformationService.java index 8363320cf..bc49852f5 100644 --- a/src/main/java/io/github/hapjava/services/impl/HAPProtocolInformationService.java +++ b/src/main/java/io/github/hapjava/services/impl/HAPProtocolInformationService.java @@ -2,7 +2,7 @@ import io.github.hapjava.characteristics.impl.common.VersionCharacteristic; -/** This service describes label scheme. */ +/** This service HAP version. */ public class HAPProtocolInformationService extends AbstractServiceImpl { public HAPProtocolInformationService(VersionCharacteristic versionCharacteristic) { diff --git a/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java b/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java index 8c53c5b2b..a28cec8fa 100644 --- a/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java +++ b/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java @@ -21,15 +21,7 @@ import io.github.hapjava.characteristics.impl.thermostat.HeatingThresholdTemperatureCharacteristic; import io.github.hapjava.characteristics.impl.thermostat.TemperatureDisplayUnitCharacteristic; -/** - * This service can be used to describe either of the following: • a heater • a cooler • a heater - * and a cooler A heater/cooler accessory may have additional: • {@link FanService} service to - * describe a fan which can be independently controlled • {@link SlatService} service to control - * vents A heater must include {@link HeatingThresholdTemperatureCharacteristic}. A cooler must - * include {@link CoolingThresholdTemperatureCharacteristic}. A heater/cooler accessory service may - * include {@link RotationSpeedCharacteristic} to control fan speed if the fan cannot be - * independently controlled. - */ +/** This service can be used to describe a heater and/or a cooler */ public class HeaterCoolerService extends AbstractServiceImpl { public HeaterCoolerService( diff --git a/src/main/java/io/github/hapjava/services/impl/HumidifierDehumidifierService.java b/src/main/java/io/github/hapjava/services/impl/HumidifierDehumidifierService.java index 600b877ea..be57b7eb5 100644 --- a/src/main/java/io/github/hapjava/services/impl/HumidifierDehumidifierService.java +++ b/src/main/java/io/github/hapjava/services/impl/HumidifierDehumidifierService.java @@ -21,16 +21,7 @@ import io.github.hapjava.characteristics.impl.humidifier.TargetHumidifierDehumidifierStateCharacteristic; import io.github.hapjava.characteristics.impl.humiditysensor.CurrentRelativeHumidityCharacteristic; -/** - * This service can be used to describe either of the following: • an air humidifier • an air - * dehumidifier • an air humidifier and an air dehumidifier An air humidifier/dehumidifier accessory - * may have additional: • {@link FanService} service to describe a fan which can be independently - * controlled • {@link SlatService}service to control vents A dehumidifier must include {@link - * HumidityDehumidifierThresholdCharacteristic}. A humidifier must include {@link - * HumidityHumidifierThresholdCharacteristic}. A humidifier/dehumidifier accessory service may - * include {@link RotationSpeedCharacteristic} to control fan speed if the fan cannot be - * independently controlled. - */ +/** This service can be used to describe an air humidifier or/and an air dehumidifier. */ public class HumidifierDehumidifierService extends AbstractServiceImpl { public HumidifierDehumidifierService( diff --git a/src/main/java/io/github/hapjava/services/impl/IrrigationSystemService.java b/src/main/java/io/github/hapjava/services/impl/IrrigationSystemService.java new file mode 100644 index 000000000..3501ba723 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/IrrigationSystemService.java @@ -0,0 +1,71 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.IrrigationSystemAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithRemainingDuration; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithStatusFault; +import io.github.hapjava.characteristics.impl.common.ActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.InUseCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; +import io.github.hapjava.characteristics.impl.common.ProgramModeCharacteristic; +import io.github.hapjava.characteristics.impl.common.StatusFaultCharacteristic; +import io.github.hapjava.characteristics.impl.valve.RemainingDurationCharacteristic; + +/** An irrigation system service */ +public class IrrigationSystemService extends AbstractServiceImpl { + + public IrrigationSystemService( + ActiveCharacteristic activeCharacteristic, + InUseCharacteristic inUseCharacteristic, + ProgramModeCharacteristic programModeCharacteristic) { + super("000000CF-0000-1000-8000-0026BB765291"); + addCharacteristic(activeCharacteristic); + addCharacteristic(inUseCharacteristic); + addCharacteristic(programModeCharacteristic); + } + + public IrrigationSystemService(IrrigationSystemAccessory accessory) { + this( + new ActiveCharacteristic( + accessory::getActive, + accessory::setActive, + accessory::subscribeActive, + accessory::unsubscribeActive), + new InUseCharacteristic( + accessory::getInUse, accessory::subscribeInUse, accessory::unsubscribeInUse), + new ProgramModeCharacteristic( + accessory::getProgramMode, + accessory::subscribeProgramMode, + accessory::unsubscribeProgramMode)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithStatusFault) { + addOptionalCharacteristic( + new StatusFaultCharacteristic( + ((AccessoryWithStatusFault) accessory)::getStatusFault, + ((AccessoryWithStatusFault) accessory)::subscribeStatusFault, + ((AccessoryWithStatusFault) accessory)::unsubscribeStatusFault)); + } + if (accessory instanceof AccessoryWithRemainingDuration) { + addOptionalCharacteristic( + new RemainingDurationCharacteristic( + ((AccessoryWithRemainingDuration) accessory)::getRemainingDuration, + ((AccessoryWithRemainingDuration) accessory)::subscribeRemainingDuration, + ((AccessoryWithRemainingDuration) accessory)::unsubscribeRemainingDuration)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(StatusFaultCharacteristic statusFaultCharacteristic) { + addCharacteristic(statusFaultCharacteristic); + } + + public void addOptionalCharacteristic( + RemainingDurationCharacteristic remainingDurationCharacteristic) { + addCharacteristic(remainingDurationCharacteristic); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/LockMechanismService.java b/src/main/java/io/github/hapjava/services/impl/LockMechanismService.java index c82f107c8..de59ca516 100644 --- a/src/main/java/io/github/hapjava/services/impl/LockMechanismService.java +++ b/src/main/java/io/github/hapjava/services/impl/LockMechanismService.java @@ -6,10 +6,7 @@ import io.github.hapjava.characteristics.impl.lock.LockCurrentStateCharacteristic; import io.github.hapjava.characteristics.impl.lock.LockTargetStateCharacteristic; -/** - * The HomeKit Lock Mechanism Service is designed to expose and control the physical lock mechanism - * on a device. - */ +/** The Lock Mechanism Service describes the physical lock mechanism on a device. */ public class LockMechanismService extends AbstractServiceImpl { public LockMechanismService( diff --git a/src/main/java/io/github/hapjava/services/impl/SlatService.java b/src/main/java/io/github/hapjava/services/impl/SlatService.java index 486029d00..5993983fa 100644 --- a/src/main/java/io/github/hapjava/services/impl/SlatService.java +++ b/src/main/java/io/github/hapjava/services/impl/SlatService.java @@ -12,11 +12,7 @@ import io.github.hapjava.characteristics.impl.slat.SlatTypeCharacteristic; import io.github.hapjava.characteristics.impl.slat.TargetTiltAngleCharacteristic; -/** - * This service describes a slat which tilts on a vertical or a horizontal axis. ”Current Tilt - * Angle” and ”Target Tilt Angle”may be included in this service if the user can set the slats to a - * particular tilt angle. - */ +/** This service describes a slat */ public class SlatService extends AbstractServiceImpl { public SlatService( diff --git a/src/main/java/io/github/hapjava/services/impl/SpeakerService.java b/src/main/java/io/github/hapjava/services/impl/SpeakerService.java index 0c2a073d8..8619d5a50 100644 --- a/src/main/java/io/github/hapjava/services/impl/SpeakerService.java +++ b/src/main/java/io/github/hapjava/services/impl/SpeakerService.java @@ -7,7 +7,7 @@ import io.github.hapjava.characteristics.impl.audio.VolumeCharacteristic; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; -/** A Speaker service is to use to control the audio output settings on a speaker device. */ +/** A Speaker service can be used to control the audio output settings on a speaker device. */ public class SpeakerService extends AbstractServiceImpl { public SpeakerService(MuteCharacteristic muteCharacteristic) { diff --git a/src/main/java/io/github/hapjava/services/impl/StatelessProgrammableSwitchService.java b/src/main/java/io/github/hapjava/services/impl/StatelessProgrammableSwitchService.java index d2ef1e337..bded30f47 100644 --- a/src/main/java/io/github/hapjava/services/impl/StatelessProgrammableSwitchService.java +++ b/src/main/java/io/github/hapjava/services/impl/StatelessProgrammableSwitchService.java @@ -7,11 +7,7 @@ import io.github.hapjava.characteristics.impl.common.ProgrammableSwitchEventCharacteristic; import io.github.hapjava.characteristics.impl.common.ServiceLabelIndexCharacteristic; -/** - * This service describes a stateless programmable switch. - * - * @author Eugen Freiter - */ +/** This service describes a stateless programmable switch. */ public class StatelessProgrammableSwitchService extends AbstractServiceImpl { public StatelessProgrammableSwitchService(ProgrammableSwitchEventCharacteristic switchEvent) { diff --git a/src/main/java/io/github/hapjava/services/impl/ValveService.java b/src/main/java/io/github/hapjava/services/impl/ValveService.java index ae8d5b1c4..b45c48ef4 100644 --- a/src/main/java/io/github/hapjava/services/impl/ValveService.java +++ b/src/main/java/io/github/hapjava/services/impl/ValveService.java @@ -17,10 +17,7 @@ import io.github.hapjava.characteristics.impl.valve.SetDurationCharacteristic; import io.github.hapjava.characteristics.impl.valve.ValveTypeCharacteristic; -/** - * This service describes accessories like irrigation valves or water outlets. A valve is set to In - * Use when there are fluid flowing through the valve. - */ +/** This service describes accessories valves. */ public class ValveService extends AbstractServiceImpl { public ValveService( diff --git a/src/main/java/io/github/hapjava/services/impl/WindowCoveringService.java b/src/main/java/io/github/hapjava/services/impl/WindowCoveringService.java index 173751333..fe483e4c0 100644 --- a/src/main/java/io/github/hapjava/services/impl/WindowCoveringService.java +++ b/src/main/java/io/github/hapjava/services/impl/WindowCoveringService.java @@ -19,10 +19,7 @@ import io.github.hapjava.characteristics.impl.windowcovering.TargetPositionCharacteristic; import io.github.hapjava.characteristics.impl.windowcovering.TargetVerticalTiltAngleCharacteristic; -/** - * This service describes motorized window coverings or shades - examples include shutters, blinds, - * awnings etc. - */ +/** This service describes motorized window coverings or shades */ public class WindowCoveringService extends AbstractServiceImpl { public WindowCoveringService( From 2b7e9d75e7d95bba134702a5a18d68c9cdedcdbe Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sun, 17 May 2020 00:23:06 +0200 Subject: [PATCH 019/138] remove reference to spec Signed-off-by: Eugen Freiter --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index d27b04e8c..f67ee2fd2 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,7 @@ After that, check out the [Sample](https://github.com/hap-java/HAP-Java/tree/sam Supported HomeKit Accessories ========= -Current implementation is based on HAP specification Release R2 (published 2019-07-26) and -fully supports 37 out of 45 HomeKit accessory services defined there. +Current implementation fully supports 37 HomeKit accessory/services. | HomeKit Accessory & Service type | Supported by Java-HAP | |--------------------|--------------------| From 1372385d33c8f264056677f654492beccd230e97 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sun, 24 May 2020 10:56:37 +0200 Subject: [PATCH 020/138] add support for custom min/max values for temperatures Signed-off-by: Eugen Freiter --- .../TemperatureSensorAccessory.java | 30 ++++++++++ .../accessories/ThermostatAccessory.java | 58 +++++++++++++++++++ .../CurrentTemperatureCharacteristic.java | 16 ++++- .../TargetTemperatureCharacteristic.java | 17 +++++- .../impl/TemperatureSensorService.java | 3 + .../services/impl/ThermostatService.java | 6 ++ 6 files changed, 124 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/hapjava/accessories/TemperatureSensorAccessory.java b/src/main/java/io/github/hapjava/accessories/TemperatureSensorAccessory.java index f2941f957..4f9184926 100644 --- a/src/main/java/io/github/hapjava/accessories/TemperatureSensorAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/TemperatureSensorAccessory.java @@ -21,6 +21,36 @@ public interface TemperatureSensorAccessory extends HomekitAccessory { */ CompletableFuture getCurrentTemperature(); + /** + * return the min value for current temperature. overwrite if you want to change the default + * value. + * + * @return min current temperature + */ + default double getMinCurrentTemperature() { + return 0; + } + + /** + * return the max value for current temperature. overwrite if you want to change the default + * value. + * + * @return max current temperature + */ + default double getMaxCurrentTemperature() { + return 100; + } + + /** + * return the min step value for current temperature. overwrite if you want to change the default + * value. + * + * @return min step current temperature + */ + default double getMinStepCurrentTemperature() { + return 0.1; + } + /** * Subscribes to changes in the current temperature. * diff --git a/src/main/java/io/github/hapjava/accessories/ThermostatAccessory.java b/src/main/java/io/github/hapjava/accessories/ThermostatAccessory.java index 43bca6926..f55ed7a63 100644 --- a/src/main/java/io/github/hapjava/accessories/ThermostatAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/ThermostatAccessory.java @@ -24,6 +24,36 @@ public interface ThermostatAccessory extends HomekitAccessory { */ CompletableFuture getCurrentTemperature(); + /** + * return the min value for current temperature. overwrite if you want to change the default + * value. + * + * @return min current temperature + */ + default double getMinCurrentTemperature() { + return 0; + } + + /** + * return the max value for current temperature. overwrite if you want to change the default + * value. + * + * @return max current temperature + */ + default double getMaxCurrentTemperature() { + return 100; + } + + /** + * return the min step value for current temperature. overwrite if you want to change the default + * value. + * + * @return min step current temperature + */ + default double getMinStepCurrentTemperature() { + return 0.1; + } + /** * Retrieves the target temperature, in celsius degrees. * @@ -39,6 +69,34 @@ public interface ThermostatAccessory extends HomekitAccessory { */ void setTargetTemperature(Double value) throws Exception; + /** + * return the min value for target temperature. overwrite if you want to change the default value. + * + * @return min target temperature + */ + default double getMinTargetTemperature() { + return 10; + } + + /** + * return the max value for target temperature. overwrite if you want to change the default value. + * + * @return max target temperature + */ + default double getMaxTargetTemperature() { + return 38; + } + + /** + * return the min step value for target temperature. overwrite if you want to change the default + * value. + * + * @return min step target temperature + */ + default double getMinStepTargetTemperature() { + return 0.1; + } + /** * Retrieves the current {@link CurrentHeatingCoolingStateEnum} of the thermostat. * diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java index 4227ceaab..864d31d29 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java @@ -11,19 +11,29 @@ public class CurrentTemperatureCharacteristic extends FloatCharacteristic { public CurrentTemperatureCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "00000011-0000-1000-8000-0026BB765291", "current temperature", - 0, - 100, - 0.1, + minValue, + maxValue, + minStep, "C", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public CurrentTemperatureCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(0, 100, 0.1, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java index f96483d22..ff03bfbff 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java @@ -15,6 +15,9 @@ public class TargetTemperatureCharacteristic extends FloatCharacteristic { public TargetTemperatureCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -22,13 +25,21 @@ public TargetTemperatureCharacteristic( super( "00000035-0000-1000-8000-0026BB765291", "target temperature", - 10, - 38, - 0.1, + minValue, + maxValue, + minStep, "C", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public TargetTemperatureCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + this(10, 38, 0.1, getter, setter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/services/impl/TemperatureSensorService.java b/src/main/java/io/github/hapjava/services/impl/TemperatureSensorService.java index 5fbb5a4ab..bef9fff60 100644 --- a/src/main/java/io/github/hapjava/services/impl/TemperatureSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/TemperatureSensorService.java @@ -25,6 +25,9 @@ public TemperatureSensorService( public TemperatureSensorService(TemperatureSensorAccessory accessory) { this( new CurrentTemperatureCharacteristic( + accessory.getMinCurrentTemperature(), + accessory.getMaxCurrentTemperature(), + accessory.getMinStepCurrentTemperature(), accessory::getCurrentTemperature, accessory::subscribeCurrentTemperature, accessory::unsubscribeCurrentTemperature)); diff --git a/src/main/java/io/github/hapjava/services/impl/ThermostatService.java b/src/main/java/io/github/hapjava/services/impl/ThermostatService.java index ad575125a..c3cc5d8f9 100644 --- a/src/main/java/io/github/hapjava/services/impl/ThermostatService.java +++ b/src/main/java/io/github/hapjava/services/impl/ThermostatService.java @@ -46,10 +46,16 @@ public ThermostatService(ThermostatAccessory accessory) { accessory::subscribeTargetState, accessory::unsubscribeTargetState), new CurrentTemperatureCharacteristic( + accessory.getMinCurrentTemperature(), + accessory.getMaxCurrentTemperature(), + accessory.getMinStepCurrentTemperature(), accessory::getCurrentTemperature, accessory::subscribeCurrentTemperature, accessory::unsubscribeCurrentTemperature), new TargetTemperatureCharacteristic( + accessory.getMinTargetTemperature(), + accessory.getMaxTargetTemperature(), + accessory.getMinStepTargetTemperature(), accessory::getTargetTemperature, accessory::setTargetTemperature, accessory::subscribeTargetTemperature, From 7fcc492c2c1cbbfa0e28bf32dcd803d89c338513 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sun, 24 May 2020 12:48:59 +0200 Subject: [PATCH 021/138] fix typos. add support for custom min/max values for threshold temperatures Signed-off-by: Eugen Freiter --- ...essoryWithCoolingThresholdTemperature.java | 30 +++++++++++++++++ ...ssoryWithHeatingThresholdTemperature.java} | 32 ++++++++++++++++++- ...ingThresholdTemperatureCharacteristic.java | 17 ++++++++-- ...ingThresholdTemperatureCharacteristic.java | 17 ++++++++-- .../server/impl/connections/HttpSession.java | 2 +- .../services/impl/HeaterCoolerService.java | 24 ++++++++++---- .../services/impl/ThermostatService.java | 12 +++---- 7 files changed, 114 insertions(+), 20 deletions(-) rename src/main/java/io/github/hapjava/accessories/optionalcharacteristic/{AccessoryWithHeatingThresholdTemprature.java => AccessoryWithHeatingThresholdTemperature.java} (56%) diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCoolingThresholdTemperature.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCoolingThresholdTemperature.java index b96be7b6e..c42398a10 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCoolingThresholdTemperature.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCoolingThresholdTemperature.java @@ -21,6 +21,36 @@ public interface AccessoryWithCoolingThresholdTemperature { */ void setCoolingThresholdTemperature(Double value) throws Exception; + /** + * return the min value for cooling threshold temperature. overwrite if you want to change the + * default value. + * + * @return min threshold temperature + */ + default double getMinCoolingThresholdTemperature() { + return 10; + } + + /** + * return the max value for cooling threshold temperature. overwrite if you want to change the + * default value. + * + * @return max threshold temperature + */ + default double getMaxCoolingThresholdTemperature() { + return 35; + } + + /** + * return the min step value for cooling threshold temperature. overwrite if you want to change + * the default value. + * + * @return step for threshold temperature + */ + default double getStepCoolingThresholdTemperature() { + return 0.1; + } + /** * Subscribes to changes in the cooling threshold. * diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHeatingThresholdTemprature.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHeatingThresholdTemperature.java similarity index 56% rename from src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHeatingThresholdTemprature.java rename to src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHeatingThresholdTemperature.java index 536a18075..02cb3f819 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHeatingThresholdTemprature.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithHeatingThresholdTemperature.java @@ -3,7 +3,7 @@ import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; import java.util.concurrent.CompletableFuture; -public interface AccessoryWithHeatingThresholdTemprature { +public interface AccessoryWithHeatingThresholdTemperature { /** * Retrieves the temperature below which the thermostat should begin heating. @@ -20,6 +20,36 @@ public interface AccessoryWithHeatingThresholdTemprature { */ void setHeatingThresholdTemperature(Double value) throws Exception; + /** + * return the min value for heating threshold temperature. overwrite if you want to change the + * default value. + * + * @return min threshold temperature + */ + default double getMinHeatingThresholdTemperature() { + return 0; + } + + /** + * return the max value for heating threshold temperature. overwrite if you want to change the + * default value. + * + * @return max threshold temperature + */ + default double getMaxHeatingThresholdTemperature() { + return 25; + } + + /** + * return the min step value for heating threshold temperature. overwrite if you want to change + * the default value. + * + * @return step for threshold temperature + */ + default double getStepHeatingThresholdTemperature() { + return 0.1; + } + /** * Subscribes to changes in the heating threshold. * diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java index 4c5d9907f..68dbaccf3 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java @@ -14,6 +14,9 @@ public class CoolingThresholdTemperatureCharacteristic extends FloatCharacteristic { public CoolingThresholdTemperatureCharacteristic( + double minValue, + double maxValue, + double step, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -21,13 +24,21 @@ public CoolingThresholdTemperatureCharacteristic( super( "0000000D-0000-1000-8000-0026BB765291", "cooling threshold", - 10, - 35, - 0.1, + minValue, + maxValue, + step, "C", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public CoolingThresholdTemperatureCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + this(10, 35, 0.1, getter, setter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java index 0ffd3093f..05e1ef647 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java @@ -12,6 +12,9 @@ public class HeatingThresholdTemperatureCharacteristic extends FloatCharacteristic { public HeatingThresholdTemperatureCharacteristic( + double minValue, + double maxValue, + double step, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -19,13 +22,21 @@ public HeatingThresholdTemperatureCharacteristic( super( "00000012-0000-1000-8000-0026BB765291", "heating threshold", - 0, - 25, - 0.1, + minValue, + maxValue, + step, "C", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public HeatingThresholdTemperatureCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + this(0, 25, 0.1, getter, setter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java b/src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java index ce567b3de..36ac04085 100644 --- a/src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java @@ -126,7 +126,7 @@ private HttpResponse handlePairVerify(HttpRequest request) { try { return pairVerificationManager.handle(request); } catch (Exception e) { - logger.warn("Excepton encountered while verifying pairing", e); + logger.warn("Exception encountered while verifying pairing", e); return new InternalServerErrorResponse(e); } } diff --git a/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java b/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java index a28cec8fa..299161ed8 100644 --- a/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java +++ b/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java @@ -2,7 +2,7 @@ import io.github.hapjava.accessories.HeaterCoolerAccessory; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithCoolingThresholdTemperature; -import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithHeatingThresholdTemprature; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithHeatingThresholdTemperature; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithPhysicalControlsLock; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithRotationSpeed; @@ -85,6 +85,12 @@ public HeaterCoolerService(HeaterCoolerAccessory accessory) { } if (accessory instanceof AccessoryWithCoolingThresholdTemperature) { new CoolingThresholdTemperatureCharacteristic( + ((AccessoryWithCoolingThresholdTemperature) accessory) + .getMinCoolingThresholdTemperature(), + ((AccessoryWithCoolingThresholdTemperature) accessory) + .getMaxCoolingThresholdTemperature(), + ((AccessoryWithCoolingThresholdTemperature) accessory) + .getStepCoolingThresholdTemperature(), ((AccessoryWithCoolingThresholdTemperature) accessory)::getCoolingThresholdTemperature, ((AccessoryWithCoolingThresholdTemperature) accessory)::setCoolingThresholdTemperature, ((AccessoryWithCoolingThresholdTemperature) accessory) @@ -92,13 +98,19 @@ public HeaterCoolerService(HeaterCoolerAccessory accessory) { ((AccessoryWithCoolingThresholdTemperature) accessory) ::unsubscribeCoolingThresholdTemperature); } - if (accessory instanceof AccessoryWithHeatingThresholdTemprature) { + if (accessory instanceof AccessoryWithHeatingThresholdTemperature) { new HeatingThresholdTemperatureCharacteristic( - ((AccessoryWithHeatingThresholdTemprature) accessory)::getHeatingThresholdTemperature, - ((AccessoryWithHeatingThresholdTemprature) accessory)::setHeatingThresholdTemperature, - ((AccessoryWithHeatingThresholdTemprature) accessory) + ((AccessoryWithHeatingThresholdTemperature) accessory) + .getMinHeatingThresholdTemperature(), + ((AccessoryWithHeatingThresholdTemperature) accessory) + .getMaxHeatingThresholdTemperature(), + ((AccessoryWithHeatingThresholdTemperature) accessory) + .getStepHeatingThresholdTemperature(), + ((AccessoryWithHeatingThresholdTemperature) accessory)::getHeatingThresholdTemperature, + ((AccessoryWithHeatingThresholdTemperature) accessory)::setHeatingThresholdTemperature, + ((AccessoryWithHeatingThresholdTemperature) accessory) ::subscribeHeatingThresholdTemperature, - ((AccessoryWithHeatingThresholdTemprature) accessory) + ((AccessoryWithHeatingThresholdTemperature) accessory) ::unsubscribeHeatingThresholdTemperature); } if (accessory instanceof AccessoryWithTemperatureDisplayUnits) { diff --git a/src/main/java/io/github/hapjava/services/impl/ThermostatService.java b/src/main/java/io/github/hapjava/services/impl/ThermostatService.java index c3cc5d8f9..0806bb9b3 100644 --- a/src/main/java/io/github/hapjava/services/impl/ThermostatService.java +++ b/src/main/java/io/github/hapjava/services/impl/ThermostatService.java @@ -3,7 +3,7 @@ import io.github.hapjava.accessories.ThermostatAccessory; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithCoolingThresholdTemperature; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithCurrentRelativeHumidity; -import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithHeatingThresholdTemprature; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithHeatingThresholdTemperature; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithTargetRelativeHumidity; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; @@ -77,13 +77,13 @@ public ThermostatService(ThermostatAccessory accessory) { ((AccessoryWithCoolingThresholdTemperature) accessory) ::unsubscribeCoolingThresholdTemperature); } - if (accessory instanceof AccessoryWithHeatingThresholdTemprature) { + if (accessory instanceof AccessoryWithHeatingThresholdTemperature) { new HeatingThresholdTemperatureCharacteristic( - ((AccessoryWithHeatingThresholdTemprature) accessory)::getHeatingThresholdTemperature, - ((AccessoryWithHeatingThresholdTemprature) accessory)::setHeatingThresholdTemperature, - ((AccessoryWithHeatingThresholdTemprature) accessory) + ((AccessoryWithHeatingThresholdTemperature) accessory)::getHeatingThresholdTemperature, + ((AccessoryWithHeatingThresholdTemperature) accessory)::setHeatingThresholdTemperature, + ((AccessoryWithHeatingThresholdTemperature) accessory) ::subscribeHeatingThresholdTemperature, - ((AccessoryWithHeatingThresholdTemprature) accessory) + ((AccessoryWithHeatingThresholdTemperature) accessory) ::unsubscribeHeatingThresholdTemperature); } if (accessory instanceof AccessoryWithCurrentRelativeHumidity) { From 1720a25e1b619796638d84d66bcd39ea353c1923 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sun, 24 May 2020 17:11:24 +0200 Subject: [PATCH 022/138] add constants for default min/max values Signed-off-by: Eugen Freiter --- .../CoolingThresholdTemperatureCharacteristic.java | 12 +++++++++++- .../thermostat/CurrentTemperatureCharacteristic.java | 5 ++++- .../HeatingThresholdTemperatureCharacteristic.java | 12 +++++++++++- .../thermostat/TargetTemperatureCharacteristic.java | 12 +++++++++++- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java index 68dbaccf3..d855f70d6 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CoolingThresholdTemperatureCharacteristic.java @@ -12,6 +12,9 @@ * This characteristic describes the cooling threshold on which accessories turns on the cooling. */ public class CoolingThresholdTemperatureCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 10; + public static final double DEFAULT_MAX_VALUE = 35; + public static final double DEFAULT_STEP = 0.1; public CoolingThresholdTemperatureCharacteristic( double minValue, @@ -39,6 +42,13 @@ public CoolingThresholdTemperatureCharacteristic( ExceptionalConsumer setter, Consumer subscriber, Runnable unsubscriber) { - this(10, 35, 0.1, getter, setter, subscriber, unsubscriber); + this( + DEFAULT_MIN_VALUE, + DEFAULT_MAX_VALUE, + DEFAULT_STEP, + getter, + setter, + subscriber, + unsubscriber); } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java index 864d31d29..f3c2b74ff 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentTemperatureCharacteristic.java @@ -9,6 +9,9 @@ /** characteristic describes the current temperature of the environment in Celsius */ public class CurrentTemperatureCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 100; + public static final double DEFAULT_STEP = 0.1; public CurrentTemperatureCharacteristic( double minValue, @@ -34,6 +37,6 @@ public CurrentTemperatureCharacteristic( Supplier> getter, Consumer subscriber, Runnable unsubscriber) { - this(0, 100, 0.1, getter, subscriber, unsubscriber); + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber); } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java index 05e1ef647..8d7624f8f 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/HeatingThresholdTemperatureCharacteristic.java @@ -10,6 +10,9 @@ /** This characteristic describes the heating threshold on which heating get turned on. */ public class HeatingThresholdTemperatureCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 25; + public static final double DEFAULT_STEP = 0.1; public HeatingThresholdTemperatureCharacteristic( double minValue, @@ -37,6 +40,13 @@ public HeatingThresholdTemperatureCharacteristic( ExceptionalConsumer setter, Consumer subscriber, Runnable unsubscriber) { - this(0, 25, 0.1, getter, setter, subscriber, unsubscriber); + this( + DEFAULT_MIN_VALUE, + DEFAULT_MAX_VALUE, + DEFAULT_STEP, + getter, + setter, + subscriber, + unsubscriber); } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java index ff03bfbff..9a329d383 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetTemperatureCharacteristic.java @@ -13,6 +13,9 @@ * attempting to reach. */ public class TargetTemperatureCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 10; + public static final double DEFAULT_MAX_VALUE = 38; + public static final double DEFAULT_STEP = 0.1; public TargetTemperatureCharacteristic( double minValue, @@ -40,6 +43,13 @@ public TargetTemperatureCharacteristic( ExceptionalConsumer setter, Consumer subscriber, Runnable unsubscriber) { - this(10, 38, 0.1, getter, setter, subscriber, unsubscriber); + this( + DEFAULT_MIN_VALUE, + DEFAULT_MAX_VALUE, + DEFAULT_STEP, + getter, + setter, + subscriber, + unsubscriber); } } From 6d13db3cb4681d874097dd5f8237d1f2c4682e4b Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sun, 24 May 2020 23:26:23 +0200 Subject: [PATCH 023/138] put debugData back Signed-off-by: Eugen Freiter --- .../hapjava/server/impl/http/impl/BinaryHandler.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/io/github/hapjava/server/impl/http/impl/BinaryHandler.java b/src/main/java/io/github/hapjava/server/impl/http/impl/BinaryHandler.java index 6bd5892f8..4f4c80788 100644 --- a/src/main/java/io/github/hapjava/server/impl/http/impl/BinaryHandler.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/BinaryHandler.java @@ -6,6 +6,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageCodec; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,6 +25,7 @@ public BinaryHandler(HomekitClientConnection connection) { @Override protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception { if (started) { + debugData("Sending data", msg, ctx); byte[] b = new byte[msg.readableBytes()]; msg.readBytes(b); out.writeBytes(connection.encryptResponse(b)); @@ -38,6 +40,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) t in.readBytes(b); byte[] decrypted = connection.decryptRequest(b); ByteBuf outBuf = Unpooled.copiedBuffer(decrypted); + debugData("Received data", outBuf, ctx); out.add(outBuf); started = true; } @@ -52,4 +55,13 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E } super.exceptionCaught(ctx, cause); } + + private void debugData(String msg, ByteBuf b, ChannelHandlerContext ctx) throws Exception { + if (logger.isDebugEnabled()) { + logger.debug( + String.format( + "%s [%s]:%n%s", + msg, ctx.channel().remoteAddress().toString(), b.toString(StandardCharsets.UTF_8))); + } + } } From 7ad8114ff661c5fb267841acdeb7a4ac6fefdc81 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2020 21:42:47 +0000 Subject: [PATCH 024/138] Bump junit from 4.12 to 4.13.1 Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bd88630bc..df479d90d 100644 --- a/pom.xml +++ b/pom.xml @@ -143,7 +143,7 @@ junit junit - 4.12 + 4.13.1 test From cc2d720b6b622e7c8954d26b913be5174d3aa265 Mon Sep 17 00:00:00 2001 From: Martin Hanes Date: Sat, 17 Oct 2020 11:17:53 +0200 Subject: [PATCH 025/138] fix the typo in CarbonDioxideSensorService --- .../hapjava/services/impl/CarbonDioxideSensorService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java b/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java index eaf4e44d6..b329e5f8f 100644 --- a/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java @@ -97,8 +97,8 @@ public void addOptionalCharacteristic(StatusLowBatteryCharacteristic statusLowBa addCharacteristic(statusLowBattery); } - public void addOptionalCharacteristic(CarbonDioxideLevelCharacteristic carbonMonoxideLevel) { - addCharacteristic(carbonMonoxideLevel); + public void addOptionalCharacteristic(CarbonDioxideLevelCharacteristic carbonDioxideLevel) { + addCharacteristic(carbonDioxideLevel); } public void addOptionalCharacteristic( From a6ce985c93723d22056f2e3c3c85074b382c732d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20V=C3=A1clavovi=C4=8D?= Date: Wed, 28 Oct 2020 11:14:31 +0100 Subject: [PATCH 026/138] add support for valid-values (for enum characteristics) --- .../accessories/HeaterCoolerAccessory.java | 18 ++++++ .../accessories/SecuritySystemAccessory.java | 18 ++++++ .../accessories/ThermostatAccessory.java | 18 ++++++ .../AccessoryFlagsCharacteristic.java | 2 +- .../CurrentAirPurifierCharacteristic.java | 2 +- .../TargetAirPurifierStateCharacteristic.java | 10 +--- .../airquality/AirQualityCharacteristic.java | 2 +- .../impl/base/EnumCharacteristic.java | 31 ++++++++-- .../battery/ChargingStateCharacteristic.java | 2 +- .../StatusLowBatteryCharacteristic.java | 2 +- .../CarbonDioxideDetectedCharacteristic.java | 2 +- .../CarbonMonoxideDetectedCharacteristic.java | 2 +- .../impl/common/ActiveCharacteristic.java | 7 +-- .../impl/common/InUseCharacteristic.java | 2 +- .../common/IsConfiguredCharacteristic.java | 7 +-- .../common/ProgramModeCharacteristic.java | 2 +- ...ProgrammableSwitchEventCharacteristic.java | 2 +- .../ServiceLabelNamespaceCharacteristic.java | 2 +- .../common/StatusFaultCharacteristic.java | 2 +- .../common/StatusTamperedCharacteristic.java | 2 +- .../ContactSensorStateCharacteristic.java | 2 +- .../fan/CurrentFanStateCharacteristic.java | 2 +- .../LockPhysicalControlsCharacteristic.java | 10 +--- .../fan/RotationDirectionCharacteristic.java | 10 +--- .../impl/fan/SwingModeCharacteristic.java | 10 +--- .../fan/TargetFanStateCharacteristic.java | 10 +--- .../CurrentDoorStateCharacteristic.java | 2 +- .../TargetDoorStateCharacteristic.java | 10 +--- ...urrentHeaterCoolerStateCharacteristic.java | 3 +- ...TargetHeaterCoolerStateCharacteristic.java | 11 +--- ...difierDehumidifierStateCharacteristic.java | 2 +- ...difierDehumidifierStateCharacteristic.java | 10 +--- .../LeakDetectedStateCharacteristic.java | 2 +- .../lock/LockCurrentStateCharacteristic.java | 2 +- .../lock/LockTargetStateCharacteristic.java | 10 +--- .../OccupancyDetectedCharacteristic.java | 2 +- ...rentSecuritySystemStateCharacteristic.java | 3 +- ...SecuritySystemAlarmTypeCharacteristic.java | 2 +- ...rgetSecuritySystemStateCharacteristic.java | 11 +--- .../slat/CurrentSlatStateCharacteristic.java | 2 +- .../impl/slat/SlatTypeCharacteristic.java | 2 +- .../SmokeDetectedCharacteristic.java | 2 +- ...rentHeatingCoolingStateCharacteristic.java | 3 +- ...rgetHeatingCoolingStateCharacteristic.java | 11 +--- .../TemperatureDisplayUnitCharacteristic.java | 10 +--- .../impl/valve/ValveTypeCharacteristic.java | 2 +- .../PositionStateCharacteristic.java | 2 +- .../services/impl/HeaterCoolerService.java | 60 +++++++++++-------- .../services/impl/SecuritySystemService.java | 2 + .../services/impl/ThermostatService.java | 58 +++++++++++------- 50 files changed, 199 insertions(+), 204 deletions(-) diff --git a/src/main/java/io/github/hapjava/accessories/HeaterCoolerAccessory.java b/src/main/java/io/github/hapjava/accessories/HeaterCoolerAccessory.java index d403b3839..d72af203a 100644 --- a/src/main/java/io/github/hapjava/accessories/HeaterCoolerAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/HeaterCoolerAccessory.java @@ -96,6 +96,24 @@ public interface HeaterCoolerAccessory extends HomekitAccessory { /** Unsubscribes from changes in the current temperature. */ void unsubscribeCurrentTemperature(); + /** + * Valid values for current state. + * + * @return array of valid current states + */ + default CurrentHeaterCoolerStateEnum[] getCurrentHeaterCoolerStateValidValues() { + return CurrentHeaterCoolerStateEnum.values(); + } + + /** + * Valid values for target state. + * + * @return array of valid target states + */ + default TargetHeaterCoolerStateEnum[] getTargetHeaterCoolerStateValidValues() { + return TargetHeaterCoolerStateEnum.values(); + } + @Override default Collection getServices() { return Collections.singleton(new HeaterCoolerService(this)); diff --git a/src/main/java/io/github/hapjava/accessories/SecuritySystemAccessory.java b/src/main/java/io/github/hapjava/accessories/SecuritySystemAccessory.java index 93f59fa1b..43c12acd2 100644 --- a/src/main/java/io/github/hapjava/accessories/SecuritySystemAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/SecuritySystemAccessory.java @@ -64,6 +64,24 @@ public interface SecuritySystemAccessory extends HomekitAccessory { */ void unsubscribeTargetSecuritySystemState(); + /** + * Valid values for current state. + * + * @return array of valid current states. + */ + default CurrentSecuritySystemStateEnum[] getCurrentSecuritySystemStateValidValues() { + return CurrentSecuritySystemStateEnum.values(); + } + + /** + * Valid values for target state. + * + * @return array of valid targe states. + */ + default TargetSecuritySystemStateEnum[] getTargetSecuritySystemStateValidValues() { + return TargetSecuritySystemStateEnum.values(); + } + @Override default Collection getServices() { return Collections.singleton(new SecuritySystemService(this)); diff --git a/src/main/java/io/github/hapjava/accessories/ThermostatAccessory.java b/src/main/java/io/github/hapjava/accessories/ThermostatAccessory.java index f55ed7a63..837993853 100644 --- a/src/main/java/io/github/hapjava/accessories/ThermostatAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/ThermostatAccessory.java @@ -189,6 +189,24 @@ default double getMinStepTargetTemperature() { /** Unsubscribes from changes in the target temperature. */ void unsubscribeTargetTemperature(); + /** + * Valid values for current state. + * + * @return array of valid current states. + */ + default CurrentHeatingCoolingStateEnum[] getCurrentHeatingCoolingStateValidValues() { + return CurrentHeatingCoolingStateEnum.values(); + } + + /** + * Valid values for target state. + * + * @return array of valid targe states. + */ + default TargetHeatingCoolingStateEnum[] getTargetHeatingCoolingStateValidValues() { + return TargetHeatingCoolingStateEnum.values(); + } + @Override default Collection getServices() { return Collections.singleton(new ThermostatService(this)); diff --git a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/AccessoryFlagsCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/AccessoryFlagsCharacteristic.java index 1e7885382..f04a4dd8e 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/AccessoryFlagsCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/accessoryinformation/AccessoryFlagsCharacteristic.java @@ -20,7 +20,7 @@ public AccessoryFlagsCharacteristic( super( "000000A6-0000-1000-8000-0026BB765291", "accessory flags", - 1, + AccessoryFlagsEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/CurrentAirPurifierCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/CurrentAirPurifierCharacteristic.java index 051f1b7ea..c5b506c40 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/CurrentAirPurifierCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/CurrentAirPurifierCharacteristic.java @@ -21,7 +21,7 @@ public CurrentAirPurifierCharacteristic( super( "000000A9-0000-1000-8000-0026BB765291", "current air purifier state", - 2, + CurrentAirPurifierStateEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/TargetAirPurifierStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/TargetAirPurifierStateCharacteristic.java index ecc762c6b..e8452d655 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/TargetAirPurifierStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airpurifier/TargetAirPurifierStateCharacteristic.java @@ -23,18 +23,10 @@ public TargetAirPurifierStateCharacteristic( super( "000000A8-0000-1000-8000-0026BB765291", "Air purifier state", - 1, + TargetAirPurifierStateEnum.values(), Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - @Override - protected void setValue(Integer value) throws Exception { - if (!setter.isPresent()) { - return; - } - setter.get().accept(TargetAirPurifierStateEnum.fromCode(value)); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityCharacteristic.java index ab9feec85..448b90383 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/AirQualityCharacteristic.java @@ -19,7 +19,7 @@ public AirQualityCharacteristic( super( "00000095-0000-1000-8000-0026BB765291", "air quality", - 5, + AirQualityEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java index 52eb1ccf9..f983efe16 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java @@ -3,10 +3,13 @@ import io.github.hapjava.characteristics.CharacteristicEnum; import io.github.hapjava.characteristics.ExceptionalConsumer; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Arrays; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.function.Supplier; +import javax.json.Json; +import javax.json.JsonArrayBuilder; import javax.json.JsonNumber; import javax.json.JsonObject; import javax.json.JsonObjectBuilder; @@ -22,7 +25,7 @@ public abstract class EnumCharacteristic extends BaseCharacteristic { - private final int maxValue; + private final T[] validValues; Optional>> getter; protected Optional> setter; @@ -32,7 +35,7 @@ public abstract class EnumCharacteristic * @param type a string containing a UUID that indicates the type of characteristic. Apple defines * a set of these, however implementors can create their own as well. * @param description a description of the characteristic to be passed to the consuming device. - * @param maxValue the number of enum items. + * @param validValues an array of valid values for enum. * @param getter getter to retrieve the value * @param setter setter to set value * @param subscriber subscriber to subscribe to changes @@ -41,25 +44,29 @@ public abstract class EnumCharacteristic public EnumCharacteristic( String type, String description, - int maxValue, + T[] validValues, Optional>> getter, Optional> setter, Optional> subscriber, Optional unsubscriber) { super( type, "int", description, getter.isPresent(), setter.isPresent(), subscriber, unsubscriber); - this.maxValue = maxValue; this.getter = getter; this.setter = setter; + this.validValues = validValues; } /** {@inheritDoc} */ @Override protected CompletableFuture makeBuilder(int iid) { + JsonArrayBuilder validValuesBuilder = Json.createArrayBuilder(); + if (validValues != null && validValues.length != 0) { + Arrays.stream(validValues).forEach((T value) -> validValuesBuilder.add(value.getCode())); + } return super.makeBuilder(iid) .thenApply( builder -> { - return builder.add("minValue", 0).add("maxValue", maxValue).add("minStep", 1); + return builder.add("valid-values", validValuesBuilder); }); } @@ -91,12 +98,24 @@ protected void setValue(Integer value) throws Exception { return; } - // TODO implement setter here? + // check if value is in valid values + if (validValues != null && value != null) { + for (T valid : validValues) { + if (valid.getCode() == value) { + setter.get().accept(valid); + return; + } + } + } } /** {@inheritDoc} */ @Override protected Integer getDefault() { + // as default return first item from valid values + if (validValues != null && validValues.length > 0) { + return validValues[0].getCode(); + } return 0; } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateCharacteristic.java index 7f662a651..f034e2c3c 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/battery/ChargingStateCharacteristic.java @@ -20,7 +20,7 @@ public ChargingStateCharacteristic( super( "0000008F-0000-1000-8000-0026BB765291", "Charging state", - 2, + ChargingStateEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryCharacteristic.java index 8fa55bbf1..fac7f3ead 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/battery/StatusLowBatteryCharacteristic.java @@ -20,7 +20,7 @@ public StatusLowBatteryCharacteristic( super( "00000079-0000-1000-8000-0026BB765291", "Status Low Battery", - 1, + StatusLowBatteryEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedCharacteristic.java index 443d1d457..ab327a40e 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideDetectedCharacteristic.java @@ -22,7 +22,7 @@ public CarbonDioxideDetectedCharacteristic( super( "00000092-0000-1000-8000-0026BB765291", "Carbon Dioxide Detected", - 1, + CarbonDioxideDetectedEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedCharacteristic.java index d9aa32a22..2ef603b6e 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideDetectedCharacteristic.java @@ -22,7 +22,7 @@ public CarbonMonoxideDetectedCharacteristic( super( "00000069-0000-1000-8000-0026BB765291", "Carbon Monoxide Detected", - 1, + CarbonMonoxideDetectedEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveCharacteristic.java index fdc932218..0d7fc2c1c 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveCharacteristic.java @@ -22,15 +22,10 @@ public ActiveCharacteristic( super( "000000B0-0000-1000-8000-0026BB765291", "Active", - 1, + ActiveEnum.values(), Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - @Override - protected void setValue(Integer value) throws Exception { - if (setter.isPresent()) setter.get().accept(ActiveEnum.fromCode(value)); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/InUseCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/InUseCharacteristic.java index 967d69f13..882c27a01 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/InUseCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/InUseCharacteristic.java @@ -19,7 +19,7 @@ public InUseCharacteristic( super( "000000D2-0000-1000-8000-0026BB765291", "In Use", - 1, + InUseEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredCharacteristic.java index c63c032c0..c016e87c9 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/IsConfiguredCharacteristic.java @@ -21,15 +21,10 @@ public IsConfiguredCharacteristic( super( "000000D6-0000-1000-8000-0026BB765291", "Is Configured", - 1, + IsConfiguredEnum.values(), Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - @Override - protected void setValue(Integer value) throws Exception { - if (setter.isPresent()) setter.get().accept(IsConfiguredEnum.fromCode(value)); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ProgramModeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ProgramModeCharacteristic.java index 6504049e7..1580ae392 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/ProgramModeCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ProgramModeCharacteristic.java @@ -19,7 +19,7 @@ public ProgramModeCharacteristic( super( "000000D1-0000-1000-8000-0026BB765291", "Program Mode", - 2, + ProgramModeEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEventCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEventCharacteristic.java index de32f0475..83dcbb2dc 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEventCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEventCharacteristic.java @@ -24,7 +24,7 @@ public ProgrammableSwitchEventCharacteristic( super( "00000073-0000-1000-8000-0026BB765291", "Switch Event", - 2, + ProgrammableSwitchEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceCharacteristic.java index 4d6f1e24d..9f8dd3248 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ServiceLabelNamespaceCharacteristic.java @@ -16,7 +16,7 @@ public ServiceLabelNamespaceCharacteristic( super( "000000CD-0000-1000-8000-0026BB765291", "service label namespace", - 1, + ServiceLabelNamespaceEnum.values(), Optional.of(getter), Optional.empty(), Optional.empty(), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultCharacteristic.java index 773d1c04d..5754e11a8 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusFaultCharacteristic.java @@ -17,7 +17,7 @@ public StatusFaultCharacteristic( super( "00000077-0000-1000-8000-0026BB765291", "Status Fault", - 1, + StatusFaultEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedCharacteristic.java index be0c4ae6b..501bbaedb 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/StatusTamperedCharacteristic.java @@ -20,7 +20,7 @@ public StatusTamperedCharacteristic( super( "0000007A-0000-1000-8000-0026BB765291", "Status Tampered", - 1, + StatusTamperedEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactSensorStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactSensorStateCharacteristic.java index 0dfb7ad9c..8ac3c1fde 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactSensorStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/contactsensor/ContactSensorStateCharacteristic.java @@ -22,7 +22,7 @@ public ContactSensorStateCharacteristic( super( "0000006A-0000-1000-8000-0026BB765291", "Contact Sensor", - 1, + ContactStateEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateCharacteristic.java index ec9e5c9dd..75f932e50 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/CurrentFanStateCharacteristic.java @@ -22,7 +22,7 @@ public CurrentFanStateCharacteristic( super( "000000AF-0000-1000-8000-0026BB765291", "Current Fan State", - 2, + CurrentFanStateEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsCharacteristic.java index 810aa8769..8ea6f35c6 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/LockPhysicalControlsCharacteristic.java @@ -21,18 +21,10 @@ public LockPhysicalControlsCharacteristic( super( "000000A7-0000-1000-8000-0026BB765291", "Physical Locks", - 1, + LockPhysicalControlsEnum.values(), Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - @Override - protected void setValue(Integer value) throws Exception { - if (!setter.isPresent()) { - return; - } - setter.get().accept(LockPhysicalControlsEnum.fromCode(value)); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationDirectionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationDirectionCharacteristic.java index 1a2ed8d1d..efa82681f 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationDirectionCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationDirectionCharacteristic.java @@ -21,18 +21,10 @@ public RotationDirectionCharacteristic( super( "00000028-0000-1000-8000-0026BB765291", "Rotation direction", - 1, + RotationDirectionEnum.values(), Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - @Override - protected void setValue(Integer value) throws Exception { - if (!setter.isPresent()) { - return; - } - setter.get().accept(RotationDirectionEnum.fromCode(value)); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/SwingModeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/SwingModeCharacteristic.java index 821a4a487..ba58dfd0f 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/fan/SwingModeCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/SwingModeCharacteristic.java @@ -21,18 +21,10 @@ public SwingModeCharacteristic( super( "000000B6-0000-1000-8000-0026BB765291", "Swing Mode", - 1, + SwingModeEnum.values(), Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - @Override - protected void setValue(Integer value) throws Exception { - if (!setter.isPresent()) { - return; - } - setter.get().accept(SwingModeEnum.fromCode(value)); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/TargetFanStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/TargetFanStateCharacteristic.java index e91b09732..1ab9ca305 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/fan/TargetFanStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/TargetFanStateCharacteristic.java @@ -21,18 +21,10 @@ public TargetFanStateCharacteristic( super( "000000BF-0000-1000-8000-0026BB765291", "Target Fan State", - 1, + TargetFanStateEnum.values(), Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - @Override - protected void setValue(Integer value) throws Exception { - if (!setter.isPresent()) { - return; - } - setter.get().accept(TargetFanStateEnum.fromCode(value)); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateCharacteristic.java index 9743c0a2a..e06937daa 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateCharacteristic.java @@ -19,7 +19,7 @@ public CurrentDoorStateCharacteristic( super( "0000000E-0000-1000-8000-0026BB765291", "Current Door State", - 4, + CurrentDoorStateEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/TargetDoorStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/TargetDoorStateCharacteristic.java index 3e6080eae..f5b490c6e 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/TargetDoorStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/TargetDoorStateCharacteristic.java @@ -21,18 +21,10 @@ public TargetDoorStateCharacteristic( super( "00000032-0000-1000-8000-0026BB765291", "Target Door State", - 1, + TargetDoorStateEnum.values(), Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - @Override - protected void setValue(Integer value) throws Exception { - if (!setter.isPresent()) { - return; - } - setter.get().accept(TargetDoorStateEnum.fromCode(value)); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateCharacteristic.java index 644e2ca2a..f7d48bae6 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/CurrentHeaterCoolerStateCharacteristic.java @@ -13,13 +13,14 @@ public class CurrentHeaterCoolerStateCharacteristic extends EnumCharacteristic implements EventableCharacteristic { public CurrentHeaterCoolerStateCharacteristic( + CurrentHeaterCoolerStateEnum[] validValues, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "000000B1-0000-1000-8000-0026BB765291", "current heater cooler state", - 3, + validValues, Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateCharacteristic.java index 0e360c735..a9a167d45 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/heatercooler/TargetHeaterCoolerStateCharacteristic.java @@ -14,6 +14,7 @@ public class TargetHeaterCoolerStateCharacteristic extends EnumCharacteristic implements EventableCharacteristic { public TargetHeaterCoolerStateCharacteristic( + TargetHeaterCoolerStateEnum[] validValues, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -21,18 +22,10 @@ public TargetHeaterCoolerStateCharacteristic( super( "000000B2-0000-1000-8000-0026BB765291", "target heater cooler state", - 2, + validValues, Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - @Override - protected void setValue(Integer value) throws Exception { - if (!setter.isPresent()) { - return; - } - setter.get().accept(TargetHeaterCoolerStateEnum.fromCode(value)); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/CurrentHumidifierDehumidifierStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/CurrentHumidifierDehumidifierStateCharacteristic.java index ba72a856b..229d0aa73 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/CurrentHumidifierDehumidifierStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/CurrentHumidifierDehumidifierStateCharacteristic.java @@ -20,7 +20,7 @@ public CurrentHumidifierDehumidifierStateCharacteristic( super( "000000B3-0000-1000-8000-0026BB765291", "current humidifier state", - 3, + CurrentHumidifierDehumidifierStateEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/TargetHumidifierDehumidifierStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/TargetHumidifierDehumidifierStateCharacteristic.java index 63de111d8..278e7443c 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/TargetHumidifierDehumidifierStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/TargetHumidifierDehumidifierStateCharacteristic.java @@ -22,18 +22,10 @@ public TargetHumidifierDehumidifierStateCharacteristic( super( "000000B4-0000-1000-8000-0026BB765291", "target humidifier state", - 2, + TargetHumidifierDehumidifierStateEnum.values(), Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - @Override - protected void setValue(Integer value) throws Exception { - if (!setter.isPresent()) { - return; - } - setter.get().accept(TargetHumidifierDehumidifierStateEnum.fromCode(value)); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateCharacteristic.java index ce9bc8120..ca0a2043e 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/leaksensor/LeakDetectedStateCharacteristic.java @@ -22,7 +22,7 @@ public LeakDetectedStateCharacteristic( super( "00000070-0000-1000-8000-0026BB765291", "Leak Detected State", - 3, + LeakDetectedStateEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lock/LockCurrentStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lock/LockCurrentStateCharacteristic.java index ad674779b..b6946313b 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lock/LockCurrentStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lock/LockCurrentStateCharacteristic.java @@ -17,7 +17,7 @@ public LockCurrentStateCharacteristic( super( "0000001D-0000-1000-8000-0026BB765291", "Current Lock State", - 3, + LockCurrentStateEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lock/LockTargetStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lock/LockTargetStateCharacteristic.java index f2efc26e0..c46a4eab5 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lock/LockTargetStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lock/LockTargetStateCharacteristic.java @@ -19,18 +19,10 @@ public LockTargetStateCharacteristic( super( "0000001E-0000-1000-8000-0026BB765291", "Target Lock State", - 1, + LockTargetStateEnum.values(), Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - @Override - protected void setValue(Integer value) throws Exception { - if (!setter.isPresent()) { - return; - } - setter.get().accept(LockTargetStateEnum.fromCode(value)); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/occupancysensor/OccupancyDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/occupancysensor/OccupancyDetectedCharacteristic.java index 97c7c70f5..0057b0968 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/occupancysensor/OccupancyDetectedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/occupancysensor/OccupancyDetectedCharacteristic.java @@ -17,7 +17,7 @@ public OccupancyDetectedCharacteristic( super( "00000071-0000-1000-8000-0026BB765291", "Occupancy Detected", - 1, + OccupancyDetectedEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/CurrentSecuritySystemStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/CurrentSecuritySystemStateCharacteristic.java index 779ab51c4..38ec2c3fd 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/CurrentSecuritySystemStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/CurrentSecuritySystemStateCharacteristic.java @@ -12,13 +12,14 @@ public class CurrentSecuritySystemStateCharacteristic extends EnumCharacteristic { public CurrentSecuritySystemStateCharacteristic( + CurrentSecuritySystemStateEnum[] validValues, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "00000066-0000-1000-8000-0026BB765291", "Current Security System State", - 4, + validValues, Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java index fa768f4fd..14c118399 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/SecuritySystemAlarmTypeCharacteristic.java @@ -21,7 +21,7 @@ public SecuritySystemAlarmTypeCharacteristic( super( "0000008E-0000-1000-8000-0026BB765291", "Alert Type", - 1, + SecuritySystemAlarmTypeEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/TargetSecuritySystemStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/TargetSecuritySystemStateCharacteristic.java index 5284d4f6e..5289ba380 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/TargetSecuritySystemStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/securitysystem/TargetSecuritySystemStateCharacteristic.java @@ -13,6 +13,7 @@ public class TargetSecuritySystemStateCharacteristic extends EnumCharacteristic { public TargetSecuritySystemStateCharacteristic( + TargetSecuritySystemStateEnum[] validValues, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -20,18 +21,10 @@ public TargetSecuritySystemStateCharacteristic( super( "00000067-0000-1000-8000-0026BB765291", "Target Security System State", - 3, + validValues, Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - @Override - protected void setValue(Integer value) throws Exception { - if (!setter.isPresent()) { - return; - } - setter.get().accept(TargetSecuritySystemStateEnum.fromCode(value)); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentSlatStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentSlatStateCharacteristic.java index 32e897d8b..84e5e1daa 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentSlatStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentSlatStateCharacteristic.java @@ -17,7 +17,7 @@ public CurrentSlatStateCharacteristic( super( "000000AA-0000-1000-8000-0026BB765291", "current slat state", - 2, + CurrentSlatStateEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/SlatTypeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/SlatTypeCharacteristic.java index b65dbdf2c..0d9f60608 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/slat/SlatTypeCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/SlatTypeCharacteristic.java @@ -14,7 +14,7 @@ public SlatTypeCharacteristic(Supplier> getter) super( "000000C0-0000-1000-8000-0026BB765291", "slat type", - 1, + SlatTypeEnum.values(), Optional.of(getter), Optional.empty(), Optional.empty(), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedCharacteristic.java index 76bf58ea9..9befabed0 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/smokesensor/SmokeDetectedCharacteristic.java @@ -17,7 +17,7 @@ public SmokeDetectedCharacteristic( super( "00000076-0000-1000-8000-0026BB765291", "Smoke Detected", - 1, + SmokeDetectedStateEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateCharacteristic.java index 87cf113a9..8c2600a69 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/CurrentHeatingCoolingStateCharacteristic.java @@ -14,13 +14,14 @@ public class CurrentHeatingCoolingStateCharacteristic extends EnumCharacteristic { public CurrentHeatingCoolingStateCharacteristic( + CurrentHeatingCoolingStateEnum[] validValues, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "0000000F-0000-1000-8000-0026BB765291", "Current heating cooling mode", - 2, + validValues, Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateCharacteristic.java index 6c5091451..844df0c67 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TargetHeatingCoolingStateCharacteristic.java @@ -13,6 +13,7 @@ public class TargetHeatingCoolingStateCharacteristic extends EnumCharacteristic { public TargetHeatingCoolingStateCharacteristic( + TargetHeatingCoolingStateEnum[] validValues, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -20,18 +21,10 @@ public TargetHeatingCoolingStateCharacteristic( super( "00000033-0000-1000-8000-0026BB765291", "Target heating cooling mode", - 3, + validValues, Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - @Override - protected void setValue(Integer value) throws Exception { - if (!setter.isPresent()) { - return; - } - setter.get().accept(TargetHeatingCoolingStateEnum.fromCode(value)); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitCharacteristic.java index 43d3d8c5d..02a23553f 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/thermostat/TemperatureDisplayUnitCharacteristic.java @@ -23,18 +23,10 @@ public TemperatureDisplayUnitCharacteristic( super( "00000036-0000-1000-8000-0026BB765291", "Temperature units", - 1, + TemperatureDisplayUnitEnum.values(), Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - @Override - protected void setValue(Integer value) throws Exception { - if (!setter.isPresent()) { - return; - } - setter.get().accept(TemperatureDisplayUnitEnum.fromCode(value)); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/valve/ValveTypeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/valve/ValveTypeCharacteristic.java index 4c25ce867..f98e468f4 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/valve/ValveTypeCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/valve/ValveTypeCharacteristic.java @@ -17,7 +17,7 @@ public ValveTypeCharacteristic( super( "000000D5-0000-1000-8000-0026BB765291", "Valve type", - 3, + ValveTypeEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateCharacteristic.java index 73141cb24..5b2488f52 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/PositionStateCharacteristic.java @@ -17,7 +17,7 @@ public PositionStateCharacteristic( super( "00000072-0000-1000-8000-0026BB765291", "Position state", - 2, + PositionStateEnum.values(), Optional.of(getter), Optional.empty(), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java b/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java index 299161ed8..5204ae006 100644 --- a/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java +++ b/src/main/java/io/github/hapjava/services/impl/HeaterCoolerService.java @@ -48,10 +48,12 @@ public HeaterCoolerService(HeaterCoolerAccessory accessory) { accessory::subscribeCurrentTemperature, accessory::unsubscribeCurrentTemperature), new CurrentHeaterCoolerStateCharacteristic( + accessory.getCurrentHeaterCoolerStateValidValues(), accessory::getCurrentHeaterCoolerState, accessory::subscribeCurrentHeaterCoolerState, accessory::unsubscribeCurrentHeaterCoolerState), new TargetHeaterCoolerStateCharacteristic( + accessory.getTargetHeaterCoolerStateValidValues(), accessory::getTargetHeaterCoolerState, accessory::setTargetHeaterCoolerState, accessory::subscribeTargetHeaterCoolerState, @@ -84,34 +86,40 @@ public HeaterCoolerService(HeaterCoolerAccessory accessory) { ((AccessoryWithPhysicalControlsLock) accessory)::unsubscribeLockControls)); } if (accessory instanceof AccessoryWithCoolingThresholdTemperature) { - new CoolingThresholdTemperatureCharacteristic( - ((AccessoryWithCoolingThresholdTemperature) accessory) - .getMinCoolingThresholdTemperature(), - ((AccessoryWithCoolingThresholdTemperature) accessory) - .getMaxCoolingThresholdTemperature(), - ((AccessoryWithCoolingThresholdTemperature) accessory) - .getStepCoolingThresholdTemperature(), - ((AccessoryWithCoolingThresholdTemperature) accessory)::getCoolingThresholdTemperature, - ((AccessoryWithCoolingThresholdTemperature) accessory)::setCoolingThresholdTemperature, - ((AccessoryWithCoolingThresholdTemperature) accessory) - ::subscribeCoolingThresholdTemperature, - ((AccessoryWithCoolingThresholdTemperature) accessory) - ::unsubscribeCoolingThresholdTemperature); + addOptionalCharacteristic( + new CoolingThresholdTemperatureCharacteristic( + ((AccessoryWithCoolingThresholdTemperature) accessory) + .getMinCoolingThresholdTemperature(), + ((AccessoryWithCoolingThresholdTemperature) accessory) + .getMaxCoolingThresholdTemperature(), + ((AccessoryWithCoolingThresholdTemperature) accessory) + .getStepCoolingThresholdTemperature(), + ((AccessoryWithCoolingThresholdTemperature) accessory) + ::getCoolingThresholdTemperature, + ((AccessoryWithCoolingThresholdTemperature) accessory) + ::setCoolingThresholdTemperature, + ((AccessoryWithCoolingThresholdTemperature) accessory) + ::subscribeCoolingThresholdTemperature, + ((AccessoryWithCoolingThresholdTemperature) accessory) + ::unsubscribeCoolingThresholdTemperature)); } if (accessory instanceof AccessoryWithHeatingThresholdTemperature) { - new HeatingThresholdTemperatureCharacteristic( - ((AccessoryWithHeatingThresholdTemperature) accessory) - .getMinHeatingThresholdTemperature(), - ((AccessoryWithHeatingThresholdTemperature) accessory) - .getMaxHeatingThresholdTemperature(), - ((AccessoryWithHeatingThresholdTemperature) accessory) - .getStepHeatingThresholdTemperature(), - ((AccessoryWithHeatingThresholdTemperature) accessory)::getHeatingThresholdTemperature, - ((AccessoryWithHeatingThresholdTemperature) accessory)::setHeatingThresholdTemperature, - ((AccessoryWithHeatingThresholdTemperature) accessory) - ::subscribeHeatingThresholdTemperature, - ((AccessoryWithHeatingThresholdTemperature) accessory) - ::unsubscribeHeatingThresholdTemperature); + addOptionalCharacteristic( + new HeatingThresholdTemperatureCharacteristic( + ((AccessoryWithHeatingThresholdTemperature) accessory) + .getMinHeatingThresholdTemperature(), + ((AccessoryWithHeatingThresholdTemperature) accessory) + .getMaxHeatingThresholdTemperature(), + ((AccessoryWithHeatingThresholdTemperature) accessory) + .getStepHeatingThresholdTemperature(), + ((AccessoryWithHeatingThresholdTemperature) accessory) + ::getHeatingThresholdTemperature, + ((AccessoryWithHeatingThresholdTemperature) accessory) + ::setHeatingThresholdTemperature, + ((AccessoryWithHeatingThresholdTemperature) accessory) + ::subscribeHeatingThresholdTemperature, + ((AccessoryWithHeatingThresholdTemperature) accessory) + ::unsubscribeHeatingThresholdTemperature)); } if (accessory instanceof AccessoryWithTemperatureDisplayUnits) { addOptionalCharacteristic( diff --git a/src/main/java/io/github/hapjava/services/impl/SecuritySystemService.java b/src/main/java/io/github/hapjava/services/impl/SecuritySystemService.java index f4cf60e00..f2bc8d02e 100644 --- a/src/main/java/io/github/hapjava/services/impl/SecuritySystemService.java +++ b/src/main/java/io/github/hapjava/services/impl/SecuritySystemService.java @@ -26,10 +26,12 @@ public SecuritySystemService( public SecuritySystemService(SecuritySystemAccessory accessory) { this( new CurrentSecuritySystemStateCharacteristic( + accessory.getCurrentSecuritySystemStateValidValues(), accessory::getCurrentSecuritySystemState, accessory::subscribeCurrentSecuritySystemState, accessory::unsubscribeCurrentSecuritySystemState), new TargetSecuritySystemStateCharacteristic( + accessory.getTargetSecuritySystemStateValidValues(), accessory::getTargetSecuritySystemState, accessory::setTargetSecuritySystemState, accessory::subscribeTargetSecuritySystemState, diff --git a/src/main/java/io/github/hapjava/services/impl/ThermostatService.java b/src/main/java/io/github/hapjava/services/impl/ThermostatService.java index 0806bb9b3..232d9c487 100644 --- a/src/main/java/io/github/hapjava/services/impl/ThermostatService.java +++ b/src/main/java/io/github/hapjava/services/impl/ThermostatService.java @@ -37,10 +37,12 @@ public ThermostatService( public ThermostatService(ThermostatAccessory accessory) { this( new CurrentHeatingCoolingStateCharacteristic( + accessory.getCurrentHeatingCoolingStateValidValues(), accessory::getCurrentState, accessory::subscribeCurrentState, accessory::unsubscribeCurrentState), new TargetHeatingCoolingStateCharacteristic( + accessory.getTargetHeatingCoolingStateValidValues(), accessory::getTargetState, accessory::setTargetState, accessory::subscribeTargetState, @@ -69,35 +71,45 @@ public ThermostatService(ThermostatAccessory accessory) { addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); } if (accessory instanceof AccessoryWithCoolingThresholdTemperature) { - new CoolingThresholdTemperatureCharacteristic( - ((AccessoryWithCoolingThresholdTemperature) accessory)::getCoolingThresholdTemperature, - ((AccessoryWithCoolingThresholdTemperature) accessory)::setCoolingThresholdTemperature, - ((AccessoryWithCoolingThresholdTemperature) accessory) - ::subscribeCoolingThresholdTemperature, - ((AccessoryWithCoolingThresholdTemperature) accessory) - ::unsubscribeCoolingThresholdTemperature); + addOptionalCharacteristic( + new CoolingThresholdTemperatureCharacteristic( + ((AccessoryWithCoolingThresholdTemperature) accessory) + ::getCoolingThresholdTemperature, + ((AccessoryWithCoolingThresholdTemperature) accessory) + ::setCoolingThresholdTemperature, + ((AccessoryWithCoolingThresholdTemperature) accessory) + ::subscribeCoolingThresholdTemperature, + ((AccessoryWithCoolingThresholdTemperature) accessory) + ::unsubscribeCoolingThresholdTemperature)); } if (accessory instanceof AccessoryWithHeatingThresholdTemperature) { - new HeatingThresholdTemperatureCharacteristic( - ((AccessoryWithHeatingThresholdTemperature) accessory)::getHeatingThresholdTemperature, - ((AccessoryWithHeatingThresholdTemperature) accessory)::setHeatingThresholdTemperature, - ((AccessoryWithHeatingThresholdTemperature) accessory) - ::subscribeHeatingThresholdTemperature, - ((AccessoryWithHeatingThresholdTemperature) accessory) - ::unsubscribeHeatingThresholdTemperature); + addOptionalCharacteristic( + new HeatingThresholdTemperatureCharacteristic( + ((AccessoryWithHeatingThresholdTemperature) accessory) + ::getHeatingThresholdTemperature, + ((AccessoryWithHeatingThresholdTemperature) accessory) + ::setHeatingThresholdTemperature, + ((AccessoryWithHeatingThresholdTemperature) accessory) + ::subscribeHeatingThresholdTemperature, + ((AccessoryWithHeatingThresholdTemperature) accessory) + ::unsubscribeHeatingThresholdTemperature)); } if (accessory instanceof AccessoryWithCurrentRelativeHumidity) { - new CurrentRelativeHumidityCharacteristic( - ((AccessoryWithCurrentRelativeHumidity) accessory)::getCurrentRelativeHumidity, - ((AccessoryWithCurrentRelativeHumidity) accessory)::subscribeCurrentRelativeHumidity, - ((AccessoryWithCurrentRelativeHumidity) accessory)::unsubscribeCurrentRelativeHumidity); + addOptionalCharacteristic( + new CurrentRelativeHumidityCharacteristic( + ((AccessoryWithCurrentRelativeHumidity) accessory)::getCurrentRelativeHumidity, + ((AccessoryWithCurrentRelativeHumidity) accessory)::subscribeCurrentRelativeHumidity, + ((AccessoryWithCurrentRelativeHumidity) accessory) + ::unsubscribeCurrentRelativeHumidity)); } if (accessory instanceof AccessoryWithTargetRelativeHumidity) { - new TargetRelativeHumidityCharacteristic( - ((AccessoryWithTargetRelativeHumidity) accessory)::getTargetRelativeHumidity, - ((AccessoryWithTargetRelativeHumidity) accessory)::setTargetRelativeHumidity, - ((AccessoryWithTargetRelativeHumidity) accessory)::subscribeTargetRelativeHumidity, - ((AccessoryWithTargetRelativeHumidity) accessory)::unsubscribeTargetRelativeHumidity); + addOptionalCharacteristic( + new TargetRelativeHumidityCharacteristic( + ((AccessoryWithTargetRelativeHumidity) accessory)::getTargetRelativeHumidity, + ((AccessoryWithTargetRelativeHumidity) accessory)::setTargetRelativeHumidity, + ((AccessoryWithTargetRelativeHumidity) accessory)::subscribeTargetRelativeHumidity, + ((AccessoryWithTargetRelativeHumidity) accessory) + ::unsubscribeTargetRelativeHumidity)); } } From 5e5fab83f253afd9e96f4f90cc982c1fc9d6696c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20V=C3=A1clavovi=C4=8D?= Date: Wed, 28 Oct 2020 11:38:42 +0100 Subject: [PATCH 027/138] CHANGES update --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 7680cec66..82f671c8c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,11 @@ * `server` package consists classes to run HomeKit server and handle communication * the process is following: client, e.g. openHAB bindings, extends accessory classes, e.g. `WindowCoveringAccessory` and implements all required methods. WindowCoveringAccessory is linked already to WindowCoveringService, that in turn is link to single characteristics. +## New and improved + +* Valid values are supported for enum characteristics instead of min and max values +* Supported valid states for Thermostat, SecuritySystem and HeaterCooler [#108] + # HAP-Java 1.1.5 ## Fixes From 2aa77698a67578065ee489715709bc2ca2e39820 Mon Sep 17 00:00:00 2001 From: Gert-Jan van der Heiden Date: Sat, 5 Dec 2020 21:01:23 +0100 Subject: [PATCH 028/138] [#119] added support for valid values for target HumidifierDehumidifierState --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 82f671c8c..3735d192a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,7 +11,7 @@ ## New and improved * Valid values are supported for enum characteristics instead of min and max values -* Supported valid states for Thermostat, SecuritySystem and HeaterCooler [#108] +* Supported valid states for Thermostat, SecuritySystem, HeaterCooler and HumidifierDehumidifier [#108] [#119](https://github.com/hap-java/HAP-Java/pull/119) # HAP-Java 1.1.5 From 5a58bfab104ae00eba313d919162d6f361c5fdee Mon Sep 17 00:00:00 2001 From: Gert-Jan van der Heiden Date: Sat, 5 Dec 2020 21:01:34 +0100 Subject: [PATCH 029/138] [#119] added support for valid values for target HumidifierDehumidifierState --- .../accessories/HumidifierDehumidifierAccessory.java | 10 ++++++++++ ...argetHumidifierDehumidifierStateCharacteristic.java | 3 ++- .../services/impl/HumidifierDehumidifierService.java | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/hapjava/accessories/HumidifierDehumidifierAccessory.java b/src/main/java/io/github/hapjava/accessories/HumidifierDehumidifierAccessory.java index bc723ce27..63a460526 100644 --- a/src/main/java/io/github/hapjava/accessories/HumidifierDehumidifierAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/HumidifierDehumidifierAccessory.java @@ -57,6 +57,16 @@ public interface HumidifierDehumidifierAccessory extends HomekitAccessory { CompletableFuture setTargetHumidifierDehumidifierState( TargetHumidifierDehumidifierStateEnum state); + /** + * Valid values for target state. + * + * @return array of valid target states. + */ + default TargetHumidifierDehumidifierStateEnum[] + getTargetHumidifierDehumidifierStateValidValues() { + return TargetHumidifierDehumidifierStateEnum.values(); + } + /** * Subscribes to changes in the humidifier/dehumidifier current state. * diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/TargetHumidifierDehumidifierStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/TargetHumidifierDehumidifierStateCharacteristic.java index 278e7443c..4b4a90a54 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/TargetHumidifierDehumidifierStateCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/TargetHumidifierDehumidifierStateCharacteristic.java @@ -15,6 +15,7 @@ public class TargetHumidifierDehumidifierStateCharacteristic implements EventableCharacteristic { public TargetHumidifierDehumidifierStateCharacteristic( + TargetHumidifierDehumidifierStateEnum[] validValues, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -22,7 +23,7 @@ public TargetHumidifierDehumidifierStateCharacteristic( super( "000000B4-0000-1000-8000-0026BB765291", "target humidifier state", - TargetHumidifierDehumidifierStateEnum.values(), + validValues, Optional.of(getter), Optional.of(setter), Optional.of(subscriber), diff --git a/src/main/java/io/github/hapjava/services/impl/HumidifierDehumidifierService.java b/src/main/java/io/github/hapjava/services/impl/HumidifierDehumidifierService.java index be57b7eb5..afd1dfdb2 100644 --- a/src/main/java/io/github/hapjava/services/impl/HumidifierDehumidifierService.java +++ b/src/main/java/io/github/hapjava/services/impl/HumidifierDehumidifierService.java @@ -54,6 +54,7 @@ public HumidifierDehumidifierService(HumidifierDehumidifierAccessory accessory) accessory::subscribeCurrentHumidifierDehumidifierState, accessory::unsubscribeCurrentHumidifierDehumidifierState), new TargetHumidifierDehumidifierStateCharacteristic( + accessory.getTargetHumidifierDehumidifierStateValidValues(), accessory::getTargetHumidifierDehumidifierState, accessory::setTargetHumidifierDehumidifierState, accessory::subscribeTargetHumidifierDehumidifierState, From b9d11b7c1e5ba9c3786a47b19cc9db114f03acc9 Mon Sep 17 00:00:00 2001 From: Gert-Jan van der Heiden Date: Sat, 5 Dec 2020 21:05:39 +0100 Subject: [PATCH 030/138] [#119] added support for valid values for target HumidifierDehumidifierState --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 3735d192a..f09698d52 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,7 +11,7 @@ ## New and improved * Valid values are supported for enum characteristics instead of min and max values -* Supported valid states for Thermostat, SecuritySystem, HeaterCooler and HumidifierDehumidifier [#108] [#119](https://github.com/hap-java/HAP-Java/pull/119) +* Supported valid states for Thermostat, SecuritySystem, HeaterCooler and HumidifierDehumidifier [#108] [#120](https://github.com/hap-java/HAP-Java/pull/119) # HAP-Java 1.1.5 From 32c515909ee776ff9afdad61a70391d6d957cd9f Mon Sep 17 00:00:00 2001 From: Gert-Jan van der Heiden Date: Sat, 5 Dec 2020 21:08:29 +0100 Subject: [PATCH 031/138] [#119] added support for valid values for target HumidifierDehumidifierState --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index f09698d52..398b3b93d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,7 +11,7 @@ ## New and improved * Valid values are supported for enum characteristics instead of min and max values -* Supported valid states for Thermostat, SecuritySystem, HeaterCooler and HumidifierDehumidifier [#108] [#120](https://github.com/hap-java/HAP-Java/pull/119) +* Supported valid states for Thermostat, SecuritySystem, HeaterCooler and HumidifierDehumidifier [#108] [#120](https://github.com/hap-java/HAP-Java/pull/120) # HAP-Java 1.1.5 From c345a4b98769190573ed653492c76c3cb30bd3c5 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Mon, 7 Dec 2020 23:30:55 +0100 Subject: [PATCH 032/138] add support for qr code Signed-off-by: Eugen Freiter --- .../hapjava/server/HomekitAuthInfo.java | 8 +++ .../hapjava/server/impl/HomekitRoot.java | 3 +- .../server/impl/crypto/HAPSetupCodeUtils.java | 54 +++++++++++++++++++ .../impl/jmdns/JmdnsHomekitAdvertiser.java | 9 +++- .../hapjava/server/impl/HomekitRootTest.java | 6 ++- 5 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 src/main/java/io/github/hapjava/server/impl/crypto/HAPSetupCodeUtils.java diff --git a/src/main/java/io/github/hapjava/server/HomekitAuthInfo.java b/src/main/java/io/github/hapjava/server/HomekitAuthInfo.java index a62b4e416..a7fff35fd 100644 --- a/src/main/java/io/github/hapjava/server/HomekitAuthInfo.java +++ b/src/main/java/io/github/hapjava/server/HomekitAuthInfo.java @@ -21,6 +21,14 @@ public interface HomekitAuthInfo { */ String getPin(); + /** + * A setup Id used for pairing the device using QR Code. It can be any alphanumeric combination of + * the length of 4. + * + * @return setup id + */ + String getSetupId(); + /** * A unique MAC address to be advertised with the HomeKit information. This does not have to be * the MAC address of the network interface. You can generate this using {@link diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java index c9bb97a8d..dd34844e5 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java @@ -116,7 +116,8 @@ public void start() { port -> { try { refreshAuthInfo(); - advertiser.advertise(label, authInfo.getMac(), port, configurationIndex); + advertiser.advertise( + label, authInfo.getMac(), port, configurationIndex, authInfo.getSetupId()); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/src/main/java/io/github/hapjava/server/impl/crypto/HAPSetupCodeUtils.java b/src/main/java/io/github/hapjava/server/impl/crypto/HAPSetupCodeUtils.java new file mode 100644 index 000000000..095e0c4c0 --- /dev/null +++ b/src/main/java/io/github/hapjava/server/impl/crypto/HAPSetupCodeUtils.java @@ -0,0 +1,54 @@ +package io.github.hapjava.server.impl.crypto; + +import java.util.Base64; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.SHA512Digest; + +public class HAPSetupCodeUtils { + + private static byte[] calculateHash(final String input, final Digest digest) { + byte[] inputAsBytes = input.getBytes(); + byte[] retValue = new byte[digest.getDigestSize()]; + digest.update(inputAsBytes, 0, inputAsBytes.length); + digest.doFinal(retValue, 0); + return retValue; + } + + /** + * generate SHA52 Hash for given string. The hash is used for mDNS advertisement. + * + * @param value value + * @return hash + */ + public static String generateSHA512Hash(final String value) { + final byte[] hash = calculateHash(value.toUpperCase(), new SHA512Digest()); + final byte[] hashTuncate = new byte[4]; + System.arraycopy(hash, 0, hashTuncate, 0, 4); + String hashStr = Base64.getEncoder().encodeToString(hashTuncate); + return hashStr; + } + + /** + * generate Setup URI which can be used fo QR Code generation. + * + * @param pin PIN number without "-" + * @param setupId alphanumeric string of the length 4 + * @param category accessory category + * @return setup UID + */ + public static String getSetupURI(final String pin, final String setupId, final int category) { + long code = + 0 << 43 // Version + | 0 << 39 // Reserved + | ((long) category) << 31 // Category + | 0 << 29 // BLE support + | 1 << 28 // IP support + | 0 << 27 // Paired / NFC + | Integer.valueOf(pin); // PIN + String payload = Long.toString(code, 36) + setupId; + while (payload.length() < 13) { + payload = '0' + payload; + } + return "X-HM://" + payload.toUpperCase(); + } +} diff --git a/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java index 81c4d4fed..9d87b6695 100644 --- a/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java +++ b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java @@ -1,5 +1,7 @@ package io.github.hapjava.server.impl.jmdns; +import static io.github.hapjava.server.impl.crypto.HAPSetupCodeUtils.generateSHA512Hash; + import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; @@ -21,6 +23,7 @@ public class JmdnsHomekitAdvertiser { private String label; private String mac; + private String setupId; private int port; private int configurationIndex; @@ -28,14 +31,15 @@ public JmdnsHomekitAdvertiser(InetAddress localAddress) throws UnknownHostExcept jmdns = JmDNS.create(localAddress); } - public synchronized void advertise(String label, String mac, int port, int configurationIndex) - throws Exception { + public synchronized void advertise( + String label, String mac, int port, int configurationIndex, String setupId) throws Exception { if (isAdvertising) { throw new IllegalStateException("HomeKit advertiser is already running"); } this.label = label; this.mac = mac; this.port = port; + this.setupId = setupId; this.configurationIndex = configurationIndex; logger.trace("Advertising accessory " + label); @@ -84,6 +88,7 @@ private void registerService() throws IOException { props.put("sf", discoverable ? "1" : "0"); props.put("id", mac); props.put("md", label); + props.put("sh", generateSHA512Hash(setupId + mac)); props.put("c#", Integer.toString(configurationIndex)); props.put("s#", "1"); props.put("ff", "0"); diff --git a/src/test/java/io/github/hapjava/server/impl/HomekitRootTest.java b/src/test/java/io/github/hapjava/server/impl/HomekitRootTest.java index aa9969a93..719fea9d1 100644 --- a/src/test/java/io/github/hapjava/server/impl/HomekitRootTest.java +++ b/src/test/java/io/github/hapjava/server/impl/HomekitRootTest.java @@ -26,6 +26,8 @@ public class HomekitRootTest { private HomekitAuthInfo authInfo; private static final int PORT = 12345; + private static final String SETUPID = "Gx12"; + private static final String LABEL = "Test Label"; @Before @@ -73,8 +75,10 @@ public void testWebHandlerStops() throws Exception { public void testAdvertiserStarts() throws Exception { final String mac = "00:00:00:00:00:00"; when(authInfo.getMac()).thenReturn(mac); + when(authInfo.getSetupId()).thenReturn(SETUPID); + root.start(); - verify(advertiser).advertise(eq(LABEL), eq(mac), eq(PORT), eq(1)); + verify(advertiser).advertise(eq(LABEL), eq(mac), eq(PORT), eq(1), eq(SETUPID)); } @Test From 548bfc58af8adcf4814d1aa965f55723c3d4af15 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Tue, 8 Dec 2020 09:47:02 +0100 Subject: [PATCH 033/138] add default implementation for setup Id Signed-off-by: Eugen Freiter --- .../hapjava/server/HomekitAuthInfo.java | 5 ++++- .../server/impl/crypto/HAPSetupCodeUtils.java | 20 +++++++++++++++++++ .../impl/jmdns/JmdnsHomekitAdvertiser.java | 1 + 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/hapjava/server/HomekitAuthInfo.java b/src/main/java/io/github/hapjava/server/HomekitAuthInfo.java index a7fff35fd..069bf7270 100644 --- a/src/main/java/io/github/hapjava/server/HomekitAuthInfo.java +++ b/src/main/java/io/github/hapjava/server/HomekitAuthInfo.java @@ -1,6 +1,7 @@ package io.github.hapjava.server; import io.github.hapjava.server.impl.HomekitServer; +import io.github.hapjava.server.impl.crypto.HAPSetupCodeUtils; import java.math.BigInteger; /** @@ -27,7 +28,9 @@ public interface HomekitAuthInfo { * * @return setup id */ - String getSetupId(); + default String getSetupId() { + return HAPSetupCodeUtils.generateSetupId(); + } /** * A unique MAC address to be advertised with the HomeKit information. This does not have to be diff --git a/src/main/java/io/github/hapjava/server/impl/crypto/HAPSetupCodeUtils.java b/src/main/java/io/github/hapjava/server/impl/crypto/HAPSetupCodeUtils.java index 095e0c4c0..a031bc264 100644 --- a/src/main/java/io/github/hapjava/server/impl/crypto/HAPSetupCodeUtils.java +++ b/src/main/java/io/github/hapjava/server/impl/crypto/HAPSetupCodeUtils.java @@ -5,6 +5,26 @@ import org.bouncycastle.crypto.digests.SHA512Digest; public class HAPSetupCodeUtils { + private static final String ALPHA_NUMERIC_STRING = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + + public static String randomAlphaNumeric(int count) { + StringBuilder builder = new StringBuilder(); + while (count-- != 0) { + int character = (int) (Math.random() * ALPHA_NUMERIC_STRING.length()); + builder.append(ALPHA_NUMERIC_STRING.charAt(character)); + } + return builder.toString(); + } + + public static String generateSetupId() { + String setupId = + "" + + ALPHA_NUMERIC_STRING.charAt((int) (Math.random() * ALPHA_NUMERIC_STRING.length())) + + ALPHA_NUMERIC_STRING.charAt((int) (Math.random() * ALPHA_NUMERIC_STRING.length())) + + ALPHA_NUMERIC_STRING.charAt((int) (Math.random() * ALPHA_NUMERIC_STRING.length())) + + ALPHA_NUMERIC_STRING.charAt((int) (Math.random() * ALPHA_NUMERIC_STRING.length())); + return setupId; + } private static byte[] calculateHash(final String input, final Digest digest) { byte[] inputAsBytes = input.getBytes(); diff --git a/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java index 9d87b6695..8e3daf616 100644 --- a/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java +++ b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java @@ -84,6 +84,7 @@ public synchronized void setConfigurationIndex(int revision) throws IOException private void registerService() throws IOException { logger.info("Registering " + SERVICE_TYPE + " on port " + port); + logger.trace("MAC:" + mac + " Setup Id:" + setupId); Map props = new HashMap<>(); props.put("sf", discoverable ? "1" : "0"); props.put("id", mac); From 0436d88a8c5b89aa00a1a86f93f90bdc82a6f004 Mon Sep 17 00:00:00 2001 From: Dennis Frommknecht Date: Fri, 16 Oct 2020 16:27:46 +0200 Subject: [PATCH 034/138] add support for linked services Some services require linked services to work, for example, the Television service which requires each input to be a linked service. With this change, support to link services is added, though it's not used in any existing service, as they don't need linking. This change also centralizes the generation of interface ids. They are now only generated in the registry. We were previously also generating them in the accessory controller, relying on an identical processing sequence. --- CHANGES.md | 1 + .../hapjava/server/impl/HomekitRegistry.java | 32 +++++++++++------ .../server/impl/json/AccessoryController.java | 36 +++++++++++++++---- .../io/github/hapjava/services/Service.java | 7 ++++ .../services/impl/AbstractServiceImpl.java | 10 ++++++ 5 files changed, 68 insertions(+), 18 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7680cec66..3da9222e2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,7 @@ * `services` package consists of services, which grouping characteristics. e.g. `WindowCoveringService` defines mandatory and optional characteristics for a window covering service as it is defined in HAP spec. * `server` package consists classes to run HomeKit server and handle communication * the process is following: client, e.g. openHAB bindings, extends accessory classes, e.g. `WindowCoveringAccessory` and implements all required methods. WindowCoveringAccessory is linked already to WindowCoveringService, that in turn is link to single characteristics. +* linked service support # HAP-Java 1.1.5 diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java b/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java index f655c1879..39ec31ff9 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java @@ -4,7 +4,12 @@ import io.github.hapjava.characteristics.Characteristic; import io.github.hapjava.services.Service; import io.github.hapjava.services.impl.AccessoryInformationService; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,7 +20,7 @@ public class HomekitRegistry { private final String label; private final Map accessories; - private final Map> services = new HashMap<>(); + private final Map> services = new HashMap<>(); private final Map> characteristics = new HashMap<>(); private boolean isAllowUnauthenticatedRequests = false; @@ -35,21 +40,26 @@ public synchronized void reset() { try { newServices = new ArrayList<>(2); newServices.add(new AccessoryInformationService(accessory)); - newServices.addAll(accessory.getServices()); + for (Service service : accessory.getServices()) { + newServices.add(service); + newServices.addAll(service.getLinkedServices()); + } } catch (Exception e) { logger.warn("Could not instantiate services for accessory " + accessory.getName(), e); - services.put(accessory, Collections.emptyList()); + services.put(accessory, Collections.emptyMap()); continue; } - Map newCharacteristics = new HashMap<>(); - services.put(accessory, newServices); + + Map newCharacteristicsByInterfaceId = new HashMap<>(); + Map newServicesByInterfaceId = new HashMap<>(); for (Service service : newServices) { - iid++; + newServicesByInterfaceId.put(++iid, service); for (Characteristic characteristic : service.getCharacteristics()) { - newCharacteristics.put(++iid, characteristic); + newCharacteristicsByInterfaceId.put(++iid, characteristic); } } - characteristics.put(accessory, newCharacteristics); + services.put(accessory, newServicesByInterfaceId); + characteristics.put(accessory, newCharacteristicsByInterfaceId); } } @@ -61,8 +71,8 @@ public Collection getAccessories() { return accessories.values(); } - public List getServices(Integer aid) { - return Collections.unmodifiableList(services.get(accessories.get(aid))); + public Map getServices(Integer aid) { + return Collections.unmodifiableMap(services.get(accessories.get(aid))); } public Map getCharacteristics(Integer aid) { diff --git a/src/main/java/io/github/hapjava/server/impl/json/AccessoryController.java b/src/main/java/io/github/hapjava/server/impl/json/AccessoryController.java index fb53c6315..98e6cd266 100644 --- a/src/main/java/io/github/hapjava/server/impl/json/AccessoryController.java +++ b/src/main/java/io/github/hapjava/server/impl/json/AccessoryController.java @@ -13,6 +13,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; import javax.json.Json; import javax.json.JsonArrayBuilder; import javax.json.JsonObject; @@ -31,12 +32,18 @@ public HttpResponse listing() throws Exception { Map>> accessoryServiceFutures = new HashMap<>(); for (HomekitAccessory accessory : registry.getAccessories()) { - int iid = 0; List> serviceFutures = new ArrayList<>(); - for (Service service : registry.getServices(accessory.getId())) { - serviceFutures.add(toJson(service, iid)); - iid += service.getCharacteristics().size() + 1; + + Map servicesByInterfaceId = registry.getServices(accessory.getId()); + + Map iidLookup = new HashMap<>(); + iidLookup.putAll(swapKeyAndValue(servicesByInterfaceId)); + iidLookup.putAll(swapKeyAndValue(registry.getCharacteristics(accessory.getId()))); + + for (Service service : servicesByInterfaceId.values()) { + serviceFutures.add(toJson(service, iidLookup)); } + accessoryServiceFutures.put(accessory.getId(), serviceFutures); } @@ -64,16 +71,18 @@ public HttpResponse listing() throws Exception { } } - private CompletableFuture toJson(Service service, int interfaceId) throws Exception { + private CompletableFuture toJson(Service service, Map iidLookup) + throws Exception { String shortType = service.getType().replaceAll("^0*([0-9a-fA-F]+)-0000-1000-8000-0026BB765291$", "$1"); JsonObjectBuilder builder = - Json.createObjectBuilder().add("iid", ++interfaceId).add("type", shortType); + Json.createObjectBuilder().add("iid", iidLookup.get(service)).add("type", shortType); List characteristics = service.getCharacteristics(); Collection> characteristicFutures = new ArrayList<>(characteristics.size()); for (Characteristic characteristic : characteristics) { - characteristicFutures.add(characteristic.toJson(++interfaceId)); + Integer iid = iidLookup.get(characteristic); + characteristicFutures.add(characteristic.toJson(iid)); } return CompletableFuture.allOf( @@ -85,7 +94,20 @@ private CompletableFuture toJson(Service service, int interfaceId) t .map(future -> future.join()) .forEach(c -> jsonCharacteristics.add(c)); builder.add("characteristics", jsonCharacteristics); + + if (!service.getLinkedServices().isEmpty()) { + JsonArrayBuilder jsonLinkedServices = Json.createArrayBuilder(); + service.getLinkedServices().stream() + .map(iidLookup::get) + .forEach(jsonLinkedServices::add); + builder.add("linked", jsonLinkedServices); + } + return builder.build(); }); } + + private Map swapKeyAndValue(Map map) { + return map.entrySet().stream().collect(Collectors.toMap(Entry::getValue, Entry::getKey)); + } } diff --git a/src/main/java/io/github/hapjava/services/Service.java b/src/main/java/io/github/hapjava/services/Service.java index aab8ef7e4..0fa65156e 100644 --- a/src/main/java/io/github/hapjava/services/Service.java +++ b/src/main/java/io/github/hapjava/services/Service.java @@ -29,4 +29,11 @@ public interface Service { * ########-####-####-####-############. */ String getType(); + + /** + * List of all the services to which the service links + * + * @return the list of linked services. + */ + List getLinkedServices(); } diff --git a/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java b/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java index 97f99906d..9db2a2a2d 100644 --- a/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java +++ b/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java @@ -12,6 +12,7 @@ abstract class AbstractServiceImpl implements Service { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final String type; private final List characteristics = new LinkedList<>(); + private final List linkedServices = new LinkedList<>(); /** @param type unique UUID of the service according to HAP specification. */ public AbstractServiceImpl(String type) { @@ -28,7 +29,16 @@ public String getType() { return type; } + @Override + public List getLinkedServices() { + return Collections.unmodifiableList(linkedServices); + } + public void addCharacteristic(Characteristic characteristic) { this.characteristics.add(characteristic); } + + public void addLinkedService(Service service) { + this.linkedServices.add(service); + } } From b0f69b1c0ffbb355822b640b91b50be283b1bb9b Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Tue, 8 Dec 2020 23:32:38 +0100 Subject: [PATCH 035/138] port of #68 PR on jmDNS to latest code base Signed-off-by: Eugen Freiter --- pom.xml | 5 +-- .../hapjava/server/impl/HomekitRoot.java | 6 +++ .../hapjava/server/impl/HomekitServer.java | 40 ++++++++++++++++++- .../HomekitStandaloneAccessoryServer.java | 11 +++++ .../impl/jmdns/JmdnsHomekitAdvertiser.java | 20 ++++++++-- 5 files changed, 73 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index bd88630bc..5faf6eafc 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,6 @@ UTF-8 4.1.42.Final - @@ -135,9 +134,9 @@ - javax.jmdns + org.jmdns jmdns - 3.4.1 + 3.5.6 diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java index c9bb97a8d..0e40067af 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java @@ -9,6 +9,7 @@ import io.github.hapjava.server.impl.jmdns.JmdnsHomekitAdvertiser; import java.io.IOException; import java.net.InetAddress; +import javax.jmdns.JmDNS; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,6 +54,11 @@ public class HomekitRoot { this.registry = new HomekitRegistry(label); } + HomekitRoot(String label, HomekitWebHandler webHandler, JmDNS jmdns, HomekitAuthInfo authInfo) + throws IOException { + this(label, webHandler, authInfo, new JmdnsHomekitAdvertiser(jmdns)); + } + /** * Add an accessory to be handled and advertised by this root. Any existing HomeKit connections * will be terminated to allow the clients to reconnect and see the updated accessory list. When diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitServer.java b/src/main/java/io/github/hapjava/server/impl/HomekitServer.java index f323fa60b..d348ca682 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitServer.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitServer.java @@ -8,6 +8,7 @@ import java.net.InetAddress; import java.security.InvalidAlgorithmParameterException; import java.util.concurrent.ExecutionException; +import javax.jmdns.JmDNS; /** * The main entry point for hap-java. Creating an instance of this class will listen for HomeKit @@ -28,6 +29,7 @@ public class HomekitServer { private final HomekitHttpServer http; private final InetAddress localAddress; + private final JmDNS jmdns; /** * Constructor. Contains an argument indicating the number of threads to use in the http server. @@ -41,9 +43,24 @@ public class HomekitServer { */ public HomekitServer(InetAddress localAddress, int port, int nThreads) throws IOException { this.localAddress = localAddress; + this.jmdns = null; http = new HomekitHttpServer(localAddress, port, nThreads); } + /** + * Constructor + * + * @param jmdns mdns service to register with + * @param port local port to bind to + * @param nThreads number of threads to use in the http server + * @throws IOException when the server cannot bind to the supplied port + */ + public HomekitServer(JmDNS jmdns, int port, int nThreads) throws IOException { + this.jmdns = jmdns; + this.localAddress = null; + http = new HomekitHttpServer(jmdns.getInetAddress(), port, nThreads); + } + /** * Constructor * @@ -55,6 +72,16 @@ public HomekitServer(InetAddress localAddress, int port) throws IOException { this(localAddress, port, Runtime.getRuntime().availableProcessors()); } + /** + * Constructor + * + * @param jmdns mdns service to register with + * @param port local port to bind to + * @throws IOException when the server cannot bind to the supplied port + */ + public HomekitServer(JmDNS jmdns, int port) throws IOException { + this(jmdns, port, Runtime.getRuntime().availableProcessors()); + } /** * Constructor * @@ -84,7 +111,11 @@ public void stop() { public HomekitStandaloneAccessoryServer createStandaloneAccessory( HomekitAuthInfo authInfo, HomekitAccessory accessory) throws IOException, ExecutionException, InterruptedException { - return new HomekitStandaloneAccessoryServer(accessory, http, localAddress, authInfo); + if (jmdns != null) { + return new HomekitStandaloneAccessoryServer(accessory, http, jmdns, authInfo); + } else { + return new HomekitStandaloneAccessoryServer(accessory, http, localAddress, authInfo); + } } /** @@ -114,7 +145,12 @@ public HomekitRoot createBridge( String firmwareRevision, String hardwareRevision) throws IOException { - HomekitRoot root = new HomekitRoot(label, http, localAddress, authInfo); + HomekitRoot root; + if (jmdns != null) { + root = new HomekitRoot(label, http, jmdns, authInfo); + } else { + root = new HomekitRoot(label, http, localAddress, authInfo); + } root.addAccessory( new HomekitBridge( label, serialNumber, model, manufacturer, firmwareRevision, hardwareRevision)); diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java b/src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java index e9b09145b..5eecfe45c 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java @@ -7,6 +7,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.concurrent.ExecutionException; +import javax.jmdns.JmDNS; /** * A server for exposing standalone HomeKit accessory (as opposed to a Bridge accessory which @@ -30,6 +31,16 @@ public class HomekitStandaloneAccessoryServer { root.addAccessory(accessory); } + HomekitStandaloneAccessoryServer( + HomekitAccessory accessory, + HomekitWebHandler webHandler, + JmDNS jmdns, + HomekitAuthInfo authInfo) + throws UnknownHostException, IOException, ExecutionException, InterruptedException { + root = new HomekitRoot(accessory.getName().get(), webHandler, jmdns, authInfo); + root.addAccessory(accessory); + } + /** Begins advertising and handling requests for this accessory. */ public void start() { root.start(); diff --git a/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java index 81c4d4fed..29864fce7 100644 --- a/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java +++ b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java @@ -24,6 +24,10 @@ public class JmdnsHomekitAdvertiser { private int port; private int configurationIndex; + public JmdnsHomekitAdvertiser(JmDNS jmdns) { + this.jmdns = jmdns; + } + public JmdnsHomekitAdvertiser(InetAddress localAddress) throws UnknownHostException, IOException { jmdns = JmDNS.create(localAddress); } @@ -53,7 +57,7 @@ public synchronized void advertise(String label, String mac, int port, int confi } public synchronized void stop() { - jmdns.unregisterAllServices(); + unregisterService(); } public synchronized void setDiscoverable(boolean discoverable) throws IOException { @@ -61,7 +65,7 @@ public synchronized void setDiscoverable(boolean discoverable) throws IOExceptio this.discoverable = discoverable; if (isAdvertising) { logger.trace("Re-creating service due to change in discoverability to " + discoverable); - jmdns.unregisterAllServices(); + unregisterService(); registerService(); } } @@ -72,14 +76,22 @@ public synchronized void setConfigurationIndex(int revision) throws IOException this.configurationIndex = revision; if (isAdvertising) { logger.trace("Re-creating service due to change in configuration index to " + revision); - jmdns.unregisterAllServices(); + unregisterService(); registerService(); } } } + private void unregisterService() { + jmdns.unregisterService(buildServiceInfo()); + } + private void registerService() throws IOException { logger.info("Registering " + SERVICE_TYPE + " on port " + port); + jmdns.registerService(buildServiceInfo()); + } + + private ServiceInfo buildServiceInfo() { Map props = new HashMap<>(); props.put("sf", discoverable ? "1" : "0"); props.put("id", mac); @@ -88,6 +100,6 @@ private void registerService() throws IOException { props.put("s#", "1"); props.put("ff", "0"); props.put("ci", "1"); - jmdns.registerService(ServiceInfo.create(SERVICE_TYPE, label, port, 1, 1, props)); + return ServiceInfo.create(SERVICE_TYPE, label, port, 1, 1, props); } } From c80f3e71d9bd375d505e45f4ba9753df9d27a926 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Wed, 9 Dec 2020 21:58:23 +0100 Subject: [PATCH 036/138] incorporate review feedback Signed-off-by: Eugen Freiter --- .../hapjava/server/impl/crypto/HAPSetupCodeUtils.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main/java/io/github/hapjava/server/impl/crypto/HAPSetupCodeUtils.java b/src/main/java/io/github/hapjava/server/impl/crypto/HAPSetupCodeUtils.java index a031bc264..ecf4d61de 100644 --- a/src/main/java/io/github/hapjava/server/impl/crypto/HAPSetupCodeUtils.java +++ b/src/main/java/io/github/hapjava/server/impl/crypto/HAPSetupCodeUtils.java @@ -17,13 +17,7 @@ public static String randomAlphaNumeric(int count) { } public static String generateSetupId() { - String setupId = - "" - + ALPHA_NUMERIC_STRING.charAt((int) (Math.random() * ALPHA_NUMERIC_STRING.length())) - + ALPHA_NUMERIC_STRING.charAt((int) (Math.random() * ALPHA_NUMERIC_STRING.length())) - + ALPHA_NUMERIC_STRING.charAt((int) (Math.random() * ALPHA_NUMERIC_STRING.length())) - + ALPHA_NUMERIC_STRING.charAt((int) (Math.random() * ALPHA_NUMERIC_STRING.length())); - return setupId; + return randomAlphaNumeric(4); } private static byte[] calculateHash(final String input, final Digest digest) { From 9de39dd32dabfadec8dd3f5f5c29df06679e2494 Mon Sep 17 00:00:00 2001 From: Gert-Jan van der Heiden Date: Thu, 10 Dec 2020 12:49:15 +0100 Subject: [PATCH 037/138] Add support for FilterMaintenanceAccessory [#124](https://github.com/hap-java/HAP-Java/pull/124) --- CHANGES.md | 1 + README.md | 4 ++-- .../accessories/AirPurifierAccessory.java | 17 ++++++++++++++++- .../impl/base/IntegerCharacteristic.java | 16 ++++++++++------ .../services/impl/AirPurifierService.java | 13 +++++++------ 5 files changed, 36 insertions(+), 15 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 58fae998c..ba6f5bf2b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,6 +13,7 @@ * Valid values are supported for enum characteristics instead of min and max values * Supported valid states for Thermostat, SecuritySystem, HeaterCooler and HumidifierDehumidifier [#108] [#120](https://github.com/hap-java/HAP-Java/pull/120) +* Support for FilterMaintenance. Can be used as a linked service for a Air Purifier [#124](https://github.com/hap-java/HAP-Java/pull/124) # HAP-Java 1.1.5 diff --git a/README.md b/README.md index f67ee2fd2..463f5d49b 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ After that, check out the [Sample](https://github.com/hap-java/HAP-Java/tree/sam Supported HomeKit Accessories ========= -Current implementation fully supports 37 HomeKit accessory/services. +Current implementation fully supports 38 HomeKit accessory/services. | HomeKit Accessory & Service type | Supported by Java-HAP | |--------------------|--------------------| @@ -41,7 +41,7 @@ Current implementation fully supports 37 HomeKit accessory/services. | Doorbell | :white_check_mark: | | Fan | :white_check_mark: | | Faucet | :white_check_mark: | -| Filter Maintenance | :x: | +| Filter Maintenance | :white_check_mark: | | Garage Door Opener | :white_check_mark: | | HAP Protocol Information | :white_check_mark: | | Heater Cooler | :white_check_mark: | diff --git a/src/main/java/io/github/hapjava/accessories/AirPurifierAccessory.java b/src/main/java/io/github/hapjava/accessories/AirPurifierAccessory.java index 3766f75a1..355c7c37e 100644 --- a/src/main/java/io/github/hapjava/accessories/AirPurifierAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/AirPurifierAccessory.java @@ -79,8 +79,23 @@ public interface AirPurifierAccessory extends HomekitAccessory { /** Unsubscribes from changes in the target state of the air purifier. */ void unsubscribeTargetState(); + /** + * If a filter maintenance service is needed as a linked service to this AirPurifier, this is the + * place. + * + * @return an instance of FilterMaintenanceAccessory, null if not needed. + */ + default FilterMaintenanceAccessory getFilterMaintenanceAccessory() { + return null; + }; + @Override default Collection getServices() { - return Collections.singleton(new AirPurifierService(this)); + AirPurifierService service = new AirPurifierService(this); + FilterMaintenanceAccessory fmAccessory = this.getFilterMaintenanceAccessory(); + if (fmAccessory != null) { + service.addLinkedService(fmAccessory.getPrimaryService()); + } + return Collections.singleton(service); } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java index e7a6d480b..be172f603 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java @@ -62,17 +62,21 @@ protected CompletableFuture makeBuilder(int iid) { return super.makeBuilder(iid) .thenApply( builder -> { - return builder - .add("minValue", minValue) - .add("maxValue", maxValue) - .add("minStep", 1) - .add("unit", unit); + builder.add("minValue", minValue).add("maxValue", maxValue).add("minStep", 1); + if (this.unit != null) { + builder.add("unit", unit); + } + return builder; }); } @Override protected CompletableFuture getValue() { - return getter.map(integerGetter -> integerGetter.get()).get(); + if (getter.isPresent()) { + return getter.map(integerGetter -> integerGetter.get()).get(); + } else { + return null; + } } @Override diff --git a/src/main/java/io/github/hapjava/services/impl/AirPurifierService.java b/src/main/java/io/github/hapjava/services/impl/AirPurifierService.java index 2346cdfc9..4918eab81 100644 --- a/src/main/java/io/github/hapjava/services/impl/AirPurifierService.java +++ b/src/main/java/io/github/hapjava/services/impl/AirPurifierService.java @@ -3,6 +3,7 @@ import io.github.hapjava.accessories.AirPurifierAccessory; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithPhysicalControlsLock; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithRotationSpeed; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithSwingMode; import io.github.hapjava.characteristics.impl.airpurifier.CurrentAirPurifierCharacteristic; import io.github.hapjava.characteristics.impl.airpurifier.TargetAirPurifierStateCharacteristic; @@ -53,13 +54,13 @@ public AirPurifierService(AirPurifierAccessory accessory) { ((AccessoryWithSwingMode) accessory)::subscribeSwingMode, ((AccessoryWithSwingMode) accessory)::unsubscribeSwingMode)); } - if (accessory instanceof AccessoryWithSwingMode) { + if (accessory instanceof AccessoryWithRotationSpeed) { addOptionalCharacteristic( - new SwingModeCharacteristic( - ((AccessoryWithSwingMode) accessory)::getSwingMode, - ((AccessoryWithSwingMode) accessory)::setSwingMode, - ((AccessoryWithSwingMode) accessory)::subscribeSwingMode, - ((AccessoryWithSwingMode) accessory)::unsubscribeSwingMode)); + new RotationSpeedCharacteristic( + ((AccessoryWithRotationSpeed) accessory)::getRotationSpeed, + ((AccessoryWithRotationSpeed) accessory)::setRotationSpeed, + ((AccessoryWithRotationSpeed) accessory)::subscribeRotationSpeed, + ((AccessoryWithRotationSpeed) accessory)::unsubscribeRotationSpeed)); } if (accessory instanceof AccessoryWithPhysicalControlsLock) { addOptionalCharacteristic( From ef70ea9ef1b2da1ce46073384b1d267d4becf55d Mon Sep 17 00:00:00 2001 From: Gert-Jan van der Heiden Date: Thu, 10 Dec 2020 13:05:14 +0100 Subject: [PATCH 038/138] Add support for FilterMaintenanceAccessory [#124](https://github.com/hap-java/HAP-Java/pull/124) --- CHANGES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index ba6f5bf2b..758ddf6f7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,7 +13,7 @@ * Valid values are supported for enum characteristics instead of min and max values * Supported valid states for Thermostat, SecuritySystem, HeaterCooler and HumidifierDehumidifier [#108] [#120](https://github.com/hap-java/HAP-Java/pull/120) -* Support for FilterMaintenance. Can be used as a linked service for a Air Purifier [#124](https://github.com/hap-java/HAP-Java/pull/124) +* Support for FilterMaintenance. Can be used as a linked service for an Air Purifier [#124](https://github.com/hap-java/HAP-Java/pull/124) # HAP-Java 1.1.5 @@ -28,6 +28,7 @@ * Fix various spec violations and optimize communications to improve performance [#65](https://github.com/hap-java/HAP-Java/pull/65) * Fix a pairing issue in which HAP-Java could listen on a different interface than that which it advertises [#67](https://github.com/hap-java/HAP-Java/pull/67) * Allow window covering to be used without optional characteristics. The inclusion of `HoldPositionCharacteristic` did terrible things, and we're still not sure why. Addressed [#56](https://github.com/hap-java/HAP-Java/pull/56) +* Air Purifier didn't support rotation speed characteristics. [#124](https://github.com/hap-java/HAP-Java/pull/124) ## New and improved From 10a9f43cb73c45d1fd63a1af73b72c618bddf062 Mon Sep 17 00:00:00 2001 From: Gert-Jan van der Heiden Date: Thu, 10 Dec 2020 13:59:46 +0100 Subject: [PATCH 039/138] [#124](https://github.com/hap-java/HAP-Java/pull/124 --- .../FilterMaintenanceAccessory.java | 42 ++++++++++++++ .../AccessoryWithFilterLifeLevel.java | 25 ++++++++ .../AccessoryWithResetFilterIndication.java | 14 +++++ .../FilterChangeIndicationCharacteristic.java | 27 +++++++++ .../FilterChangeIndicationEnum.java | 34 +++++++++++ .../FilterLifeLevelCharacteristic.java | 30 ++++++++++ .../ResetFilterIndicationCharacteristic.java | 21 +++++++ .../impl/FilterMaintenanceService.java | 57 +++++++++++++++++++ 8 files changed, 250 insertions(+) create mode 100644 src/main/java/io/github/hapjava/accessories/FilterMaintenanceAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFilterLifeLevel.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithResetFilterIndication.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterChangeIndicationCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterChangeIndicationEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/ResetFilterIndicationCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/services/impl/FilterMaintenanceService.java diff --git a/src/main/java/io/github/hapjava/accessories/FilterMaintenanceAccessory.java b/src/main/java/io/github/hapjava/accessories/FilterMaintenanceAccessory.java new file mode 100644 index 000000000..ff0ed80a9 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/FilterMaintenanceAccessory.java @@ -0,0 +1,42 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.filtermaintenance.FilterChangeIndicationEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.FilterMaintenanceService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** + * A Filter maintenance with mandatory characteristics. + * + *

The HomeKit app doesn't support a seperate FilterMaintenance, but as a linked service to + * AirPurifier. + * + * @see AirPurifierAccessory#getFilterMaintenanceAccessory() + */ +public interface FilterMaintenanceAccessory extends HomekitAccessory { + + /** + * The filter change indictaion. It's either yes or no. + * + * @return FilterChangeIndicationEnum + */ + CompletableFuture getFilterChangeIndication(); + + /** + * Subscribes to changes in the filter change indication. + * + * @param callback the function to call when the state changes. + */ + void subscribeFilterChangeIndication(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the filter change indication. */ + void unsubscribeFilterChangeIndication(); + + @Override + default Collection getServices() { + return Collections.singleton(new FilterMaintenanceService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFilterLifeLevel.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFilterLifeLevel.java new file mode 100644 index 000000000..2b94d4ec9 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithFilterLifeLevel.java @@ -0,0 +1,25 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** Accessory with filter level characteristics */ +public interface AccessoryWithFilterLifeLevel { + + /** + * what's the filter life level, percentage wise + * + * @return filter life level + */ + CompletableFuture getFilterLifeLevel(); + + /** + * Subscribes to changes in the filter life level. + * + * @param callback the function to call when the level changes. + */ + void subscribeFilterLifeLevel(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current filter life level. */ + void unsubscribeFilterLifeLevel(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithResetFilterIndication.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithResetFilterIndication.java new file mode 100644 index 000000000..a93f22192 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithResetFilterIndication.java @@ -0,0 +1,14 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import java.util.concurrent.CompletableFuture; + +/** Accessory with filter reset characteristics */ +public interface AccessoryWithResetFilterIndication { + + /** + * Request to reset the filter level + * + * @param indication always 1, by HomeKit protocol. (to be ignored) + */ + CompletableFuture resetFilterIndication(Integer indication) throws Exception; +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterChangeIndicationCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterChangeIndicationCharacteristic.java new file mode 100644 index 000000000..31ccbb25e --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterChangeIndicationCharacteristic.java @@ -0,0 +1,27 @@ +package io.github.hapjava.characteristics.impl.filtermaintenance; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class FilterChangeIndicationCharacteristic + extends EnumCharacteristic implements EventableCharacteristic { + + public FilterChangeIndicationCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000AC-0000-1000-8000-0026BB765291", + "filter change indication", + FilterChangeIndicationEnum.values(), + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterChangeIndicationEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterChangeIndicationEnum.java new file mode 100644 index 000000000..03d610afb --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterChangeIndicationEnum.java @@ -0,0 +1,34 @@ +package io.github.hapjava.characteristics.impl.filtermaintenance; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +public enum FilterChangeIndicationEnum implements CharacteristicEnum { + NO_CHANGE_NEEDED(0), + CHANGE_NEEDED(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(FilterChangeIndicationEnum.values()) + .collect(Collectors.toMap(FilterChangeIndicationEnum::getCode, t -> t)); + } + + public static FilterChangeIndicationEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + FilterChangeIndicationEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java new file mode 100644 index 000000000..29cdf8d6e --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java @@ -0,0 +1,30 @@ +package io.github.hapjava.characteristics.impl.filtermaintenance; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class FilterLifeLevelCharacteristic extends FloatCharacteristic + implements EventableCharacteristic { + + public FilterLifeLevelCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000AB-0000-1000-8000-0026BB765291", + "Filter Life Level", + 0, + 100, + 1, + "%", + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/ResetFilterIndicationCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/ResetFilterIndicationCharacteristic.java new file mode 100644 index 000000000..10d06bf96 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/ResetFilterIndicationCharacteristic.java @@ -0,0 +1,21 @@ +package io.github.hapjava.characteristics.impl.filtermaintenance; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; + +public class ResetFilterIndicationCharacteristic extends IntegerCharacteristic { + + public ResetFilterIndicationCharacteristic(ExceptionalConsumer setter) { + super( + "000000AD-0000-1000-8000-0026BB765291", + "Reset Filter Indication", + 1, + 1, + null, + Optional.empty(), + Optional.of(setter), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/FilterMaintenanceService.java b/src/main/java/io/github/hapjava/services/impl/FilterMaintenanceService.java new file mode 100644 index 000000000..10788cdea --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/FilterMaintenanceService.java @@ -0,0 +1,57 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.FilterMaintenanceAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithFilterLifeLevel; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithResetFilterIndication; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; +import io.github.hapjava.characteristics.impl.filtermaintenance.FilterChangeIndicationCharacteristic; +import io.github.hapjava.characteristics.impl.filtermaintenance.FilterLifeLevelCharacteristic; +import io.github.hapjava.characteristics.impl.filtermaintenance.ResetFilterIndicationCharacteristic; + +/** This service describes a filter maintenance. */ +public class FilterMaintenanceService extends AbstractServiceImpl { + + public FilterMaintenanceService(FilterChangeIndicationCharacteristic filerChange) { + super("000000BA-0000-1000-8000-0026BB765291"); + addCharacteristic(filerChange); + } + + public FilterMaintenanceService(FilterMaintenanceAccessory accessory) { + this( + new FilterChangeIndicationCharacteristic( + accessory::getFilterChangeIndication, + accessory::subscribeFilterChangeIndication, + accessory::unsubscribeFilterChangeIndication)); + + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + + if (accessory instanceof AccessoryWithFilterLifeLevel) { + addOptionalCharacteristic( + new FilterLifeLevelCharacteristic( + ((AccessoryWithFilterLifeLevel) accessory)::getFilterLifeLevel, + ((AccessoryWithFilterLifeLevel) accessory)::subscribeFilterLifeLevel, + ((AccessoryWithFilterLifeLevel) accessory)::unsubscribeFilterLifeLevel)); + } + + if (accessory instanceof AccessoryWithResetFilterIndication) { + addOptionalCharacteristic( + new ResetFilterIndicationCharacteristic( + ((AccessoryWithResetFilterIndication) accessory)::resetFilterIndication)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(FilterLifeLevelCharacteristic filterLifeLevel) { + addCharacteristic(filterLifeLevel); + } + + public void addOptionalCharacteristic(ResetFilterIndicationCharacteristic resetFilterIndication) { + addCharacteristic(resetFilterIndication); + } +} From a0e0bda11d2d7f78e49b26ee31a250d23b3effaf Mon Sep 17 00:00:00 2001 From: Gert-Jan van der Heiden Date: Thu, 10 Dec 2020 14:34:08 +0100 Subject: [PATCH 040/138] Add support for FilterMaintenanceAccessory [#124](https://github.com/hap-java/HAP-Java/pull/124) --- .../AccessoryWithResetFilterIndication.java | 2 ++ .../characteristics/impl/base/IntegerCharacteristic.java | 6 +----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithResetFilterIndication.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithResetFilterIndication.java index a93f22192..737ca03ec 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithResetFilterIndication.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithResetFilterIndication.java @@ -9,6 +9,8 @@ public interface AccessoryWithResetFilterIndication { * Request to reset the filter level * * @param indication always 1, by HomeKit protocol. (to be ignored) + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made */ CompletableFuture resetFilterIndication(Integer indication) throws Exception; } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java index be172f603..fcd804ccd 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java @@ -72,11 +72,7 @@ protected CompletableFuture makeBuilder(int iid) { @Override protected CompletableFuture getValue() { - if (getter.isPresent()) { - return getter.map(integerGetter -> integerGetter.get()).get(); - } else { - return null; - } + return getter.map(integerGetter -> integerGetter.get()).orElse(null); } @Override From 3c462a52c1e84ce4e2233944c5ad17cb6c417e9f Mon Sep 17 00:00:00 2001 From: Gert-Jan van der Heiden Date: Thu, 10 Dec 2020 15:24:29 +0100 Subject: [PATCH 041/138] Add support for FilterMaintenanceAccessory [#124](https://github.com/hap-java/HAP-Java/pull/124) --- .../accessories/AirPurifierAccessory.java | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/main/java/io/github/hapjava/accessories/AirPurifierAccessory.java b/src/main/java/io/github/hapjava/accessories/AirPurifierAccessory.java index 355c7c37e..3766f75a1 100644 --- a/src/main/java/io/github/hapjava/accessories/AirPurifierAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/AirPurifierAccessory.java @@ -79,23 +79,8 @@ public interface AirPurifierAccessory extends HomekitAccessory { /** Unsubscribes from changes in the target state of the air purifier. */ void unsubscribeTargetState(); - /** - * If a filter maintenance service is needed as a linked service to this AirPurifier, this is the - * place. - * - * @return an instance of FilterMaintenanceAccessory, null if not needed. - */ - default FilterMaintenanceAccessory getFilterMaintenanceAccessory() { - return null; - }; - @Override default Collection getServices() { - AirPurifierService service = new AirPurifierService(this); - FilterMaintenanceAccessory fmAccessory = this.getFilterMaintenanceAccessory(); - if (fmAccessory != null) { - service.addLinkedService(fmAccessory.getPrimaryService()); - } - return Collections.singleton(service); + return Collections.singleton(new AirPurifierService(this)); } } From 3858f17004f5c5f47f02f26b47adfa6c71e39faf Mon Sep 17 00:00:00 2001 From: Gert-Jan van der Heiden Date: Thu, 10 Dec 2020 15:30:03 +0100 Subject: [PATCH 042/138] Add support for FilterMaintenanceAccessory [#124](https://github.com/hap-java/HAP-Java/pull/124) typo in JavaDoc --- .../github/hapjava/accessories/FilterMaintenanceAccessory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/hapjava/accessories/FilterMaintenanceAccessory.java b/src/main/java/io/github/hapjava/accessories/FilterMaintenanceAccessory.java index ff0ed80a9..f288a05dd 100644 --- a/src/main/java/io/github/hapjava/accessories/FilterMaintenanceAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/FilterMaintenanceAccessory.java @@ -11,7 +11,7 @@ /** * A Filter maintenance with mandatory characteristics. * - *

The HomeKit app doesn't support a seperate FilterMaintenance, but as a linked service to + *

The HomeKit app doesn't support a separate FilterMaintenance, but as a linked service to * AirPurifier. * * @see AirPurifierAccessory#getFilterMaintenanceAccessory() From 8dda76904f464fc8e747658a2346bb827abdded7 Mon Sep 17 00:00:00 2001 From: Gert-Jan van der Heiden Date: Thu, 10 Dec 2020 15:32:33 +0100 Subject: [PATCH 043/138] Add support for FilterMaintenanceAccessory [#124](https://github.com/hap-java/HAP-Java/pull/124) JavaDoc error fixed --- .../github/hapjava/accessories/FilterMaintenanceAccessory.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/io/github/hapjava/accessories/FilterMaintenanceAccessory.java b/src/main/java/io/github/hapjava/accessories/FilterMaintenanceAccessory.java index f288a05dd..f1b2495a5 100644 --- a/src/main/java/io/github/hapjava/accessories/FilterMaintenanceAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/FilterMaintenanceAccessory.java @@ -14,7 +14,6 @@ *

The HomeKit app doesn't support a separate FilterMaintenance, but as a linked service to * AirPurifier. * - * @see AirPurifierAccessory#getFilterMaintenanceAccessory() */ public interface FilterMaintenanceAccessory extends HomekitAccessory { From 4db045bf4e8b665f2beeb21e9cda94ffb27a7dba Mon Sep 17 00:00:00 2001 From: Gert-Jan van der Heiden Date: Thu, 10 Dec 2020 15:33:22 +0100 Subject: [PATCH 044/138] Add support for FilterMaintenanceAccessory [#124](https://github.com/hap-java/HAP-Java/pull/124) JavaDoc error fixed --- .../github/hapjava/accessories/FilterMaintenanceAccessory.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/io/github/hapjava/accessories/FilterMaintenanceAccessory.java b/src/main/java/io/github/hapjava/accessories/FilterMaintenanceAccessory.java index f1b2495a5..2ebee5495 100644 --- a/src/main/java/io/github/hapjava/accessories/FilterMaintenanceAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/FilterMaintenanceAccessory.java @@ -13,7 +13,6 @@ * *

The HomeKit app doesn't support a separate FilterMaintenance, but as a linked service to * AirPurifier. - * */ public interface FilterMaintenanceAccessory extends HomekitAccessory { From a630dfd6f5fd48120f70401b91a8f6912e383bfd Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Thu, 10 Dec 2020 17:44:09 +0100 Subject: [PATCH 045/138] add linked service API Signed-off-by: Eugen Freiter --- src/main/java/io/github/hapjava/services/Service.java | 6 ++++++ .../github/hapjava/services/impl/AbstractServiceImpl.java | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/hapjava/services/Service.java b/src/main/java/io/github/hapjava/services/Service.java index 0fa65156e..87d1a48ba 100644 --- a/src/main/java/io/github/hapjava/services/Service.java +++ b/src/main/java/io/github/hapjava/services/Service.java @@ -36,4 +36,10 @@ public interface Service { * @return the list of linked services. */ List getLinkedServices(); + + /** + * Add linked services + * @param service linked service + */ + void addLinkedService(Service service); } diff --git a/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java b/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java index 9db2a2a2d..8c4e491b3 100644 --- a/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java +++ b/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java @@ -9,7 +9,6 @@ import org.slf4j.LoggerFactory; abstract class AbstractServiceImpl implements Service { - private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final String type; private final List characteristics = new LinkedList<>(); private final List linkedServices = new LinkedList<>(); @@ -38,6 +37,7 @@ public void addCharacteristic(Characteristic characteristic) { this.characteristics.add(characteristic); } + @Override public void addLinkedService(Service service) { this.linkedServices.add(service); } From 36f5ab0ad2b88c088aebcf2083f8bce83cdeedcf Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Thu, 10 Dec 2020 21:47:13 +0100 Subject: [PATCH 046/138] expose addLinkedService at Service interface Signed-off-by: Eugen Freiter --- src/main/java/io/github/hapjava/services/Service.java | 1 + .../io/github/hapjava/services/impl/AbstractServiceImpl.java | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/io/github/hapjava/services/Service.java b/src/main/java/io/github/hapjava/services/Service.java index 87d1a48ba..303cc5463 100644 --- a/src/main/java/io/github/hapjava/services/Service.java +++ b/src/main/java/io/github/hapjava/services/Service.java @@ -39,6 +39,7 @@ public interface Service { /** * Add linked services + * * @param service linked service */ void addLinkedService(Service service); diff --git a/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java b/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java index 8c4e491b3..25a44099d 100644 --- a/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java +++ b/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java @@ -5,8 +5,6 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; abstract class AbstractServiceImpl implements Service { private final String type; From 6a472be54b99bd32d42bdd2ee33a6a031871e199 Mon Sep 17 00:00:00 2001 From: Oliver Dotzauer Date: Sun, 3 Jan 2021 00:57:01 +0100 Subject: [PATCH 047/138] fix typo in enum value --- .../characteristics/impl/garagedoor/CurrentDoorStateEnum.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateEnum.java index 9d2b145ab..88474e6cc 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateEnum.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/garagedoor/CurrentDoorStateEnum.java @@ -15,7 +15,7 @@ public enum CurrentDoorStateEnum implements CharacteristicEnum { CLOSED(1), OPENING(2), CLOSING(3), - SOPPED(4); + STOPPED(4); private static final Map reverse; From 87bab73be807fec98b4f51b4a97326e3fd21045a Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sun, 24 Jan 2021 13:35:36 +0100 Subject: [PATCH 048/138] remove optional event parameters. it breaks some accessories in home app Signed-off-by: Eugen Freiter --- .../hapjava/characteristics/impl/base/BaseCharacteristic.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java index 73ce32df4..a90f1fadc 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java @@ -113,7 +113,6 @@ protected CompletableFuture makeBuilder(int instanceId) { .add("type", shortType) .add("perms", perms.build()) .add("format", format) - .add("ev", false) .add("description", description); if (isReadable) setJsonValue(builder, value); return builder; From c9e61c74a450b75ec50183f93edf00d9603b27d2 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Tue, 2 Feb 2021 13:43:17 +0100 Subject: [PATCH 049/138] extend support for min/max/step to further accessories Signed-off-by: Eugen Freiter --- .../accessories/LightSensorAccessory.java | 31 +++++++++ .../AccessoryWithCarbonDioxideLevel.java | 62 ++++++++++++++++++ .../AccessoryWithCarbonMonoxideLevel.java | 64 ++++++++++++++++++- .../AccessoryWithColorTemperature.java | 19 ++++++ .../AccessoryWithDuration.java | 19 ++++++ .../AccessoryWithNitrogenDioxideDensity.java | 31 +++++++++ .../AccessoryWithOzoneDensity.java | 28 ++++++++ .../AccessoryWithPM10Density.java | 28 ++++++++ .../AccessoryWithPM25Density.java | 28 ++++++++ .../AccessoryWithRemainingDuration.java | 19 ++++++ .../AccessoryWithSulphurDioxideDensity.java | 31 +++++++++ .../AccessoryWithVOCDensity.java | 28 ++++++++ .../NitrogenDioxideDensityCharacteristic.java | 19 +++++- .../OzoneDensityCharacteristic.java | 19 +++++- .../airquality/PM10DensityCharacteristic.java | 19 +++++- .../airquality/PM25DensityCharacteristic.java | 19 +++++- .../SulphurDioxideDensityCharacteristic.java | 19 +++++- .../airquality/VOCDensityCharacteristic.java | 19 +++++- .../CarbonDioxideLevelCharacteristic.java | 19 +++++- .../CarbonDioxidePeakLevelCharacteristic.java | 19 +++++- .../CarbonMonoxideLevelCharacteristic.java | 19 +++++- ...CarbonMonoxidePeakLevelCharacteristic.java | 19 +++++- .../ColorTemperatureCharacteristic.java | 16 ++++- ...urrentAmbientLightLevelCharacteristic.java | 19 +++++- .../RemainingDurationCharacteristic.java | 15 ++++- .../impl/valve/SetDurationCharacteristic.java | 16 ++++- .../services/impl/AirQualityService.java | 15 +++++ .../impl/CarbonDioxideSensorService.java | 6 ++ .../services/impl/LightSensorService.java | 3 + .../services/impl/LightbulbService.java | 2 + .../services/impl/ThermostatService.java | 12 ++++ .../hapjava/services/impl/ValveService.java | 4 ++ 32 files changed, 646 insertions(+), 40 deletions(-) diff --git a/src/main/java/io/github/hapjava/accessories/LightSensorAccessory.java b/src/main/java/io/github/hapjava/accessories/LightSensorAccessory.java index bcdab09c4..2087b1e99 100644 --- a/src/main/java/io/github/hapjava/accessories/LightSensorAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/LightSensorAccessory.java @@ -1,6 +1,7 @@ package io.github.hapjava.accessories; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.lightsensor.CurrentAmbientLightLevelCharacteristic; import io.github.hapjava.services.Service; import io.github.hapjava.services.impl.LightSensorService; import java.util.Collection; @@ -31,6 +32,36 @@ public interface LightSensorAccessory extends HomekitAccessory { /** Unsubscribes from changes in the current ambient light level. */ void unsubscribeCurrentAmbientLightLevel(); + /** + * return the min value for current ambient light level. overwrite if you want to change the + * default value. + * + * @return min current ambient light level + */ + default double getMinCurrentAmbientLightLevel() { + return CurrentAmbientLightLevelCharacteristic.DEFAULT_MIN_VALUE; + } + + /** + * return the max value for current ambient light level. overwrite if you want to change the + * default value. + * + * @return max current ambient light level + */ + default double getMaxCurrentAmbientLightLevel() { + return CurrentAmbientLightLevelCharacteristic.DEFAULT_MAX_VALUE; + } + + /** + * return the min step value for current ambient light level. overwrite if you want to change the + * default value. + * + * @return min step current ambient light level + */ + default double getMinStepCurrentAmbientLightLevel() { + return CurrentAmbientLightLevelCharacteristic.DEFAULT_STEP; + } + @Override default Collection getServices() { return Collections.singleton(new LightSensorService(this)); diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonDioxideLevel.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonDioxideLevel.java index bd4bb3afc..bd143b22f 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonDioxideLevel.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonDioxideLevel.java @@ -1,6 +1,8 @@ package io.github.hapjava.accessories.optionalcharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.carbondioxidesensor.CarbonDioxideLevelCharacteristic; +import io.github.hapjava.characteristics.impl.carbondioxidesensor.CarbonDioxidePeakLevelCharacteristic; import java.util.concurrent.CompletableFuture; /** Accessory with carbon dioxide level and peak level characteristic. */ @@ -20,6 +22,66 @@ public interface AccessoryWithCarbonDioxideLevel { */ CompletableFuture getCarbonDioxideLevel(); + /** + * return the min value for carbon dioxide level. overwrite if you want to change the default + * value. + * + * @return min carbon dioxide level + */ + default double getMinCarbonDioxideLevel() { + return CarbonDioxideLevelCharacteristic.DEFAULT_MIN_VALUE; + } + + /** + * return the max value for carbon dioxide level. overwrite if you want to change the default + * value. + * + * @return max carbon dioxide level + */ + default double getMaxCarbonDioxideLevel() { + return CarbonDioxideLevelCharacteristic.DEFAULT_MAX_VALUE; + } + + /** + * return the min step value for carbon dioxide level. overwrite if you want to change the default + * value. + * + * @return min step carbon dioxide level + */ + default double getMinStepCarbonDioxideLevel() { + return CarbonDioxideLevelCharacteristic.DEFAULT_STEP; + } + + /** + * return the min value for carbon dioxide peak level. overwrite if you want to change the default + * value. + * + * @return min carbon dioxide peak level + */ + default double getMinCarbonDioxidePeakLevel() { + return CarbonDioxidePeakLevelCharacteristic.DEFAULT_MIN_VALUE; + } + + /** + * return the max value for carbon dioxide peak level. overwrite if you want to change the default + * value. + * + * @return max carbon dioxide peak level + */ + default double getMaxCarbonDioxidePeakLevel() { + return CarbonDioxidePeakLevelCharacteristic.DEFAULT_MAX_VALUE; + } + + /** + * return the min step value for carbon dioxide peak level. overwrite if you want to change the + * default value. + * + * @return min step carbon dioxide peak level + */ + default double getMinStepCarbonDioxidePeakLevel() { + return CarbonDioxidePeakLevelCharacteristic.DEFAULT_STEP; + } + /** * Subscribes to changes in the carbon dioxide level. * diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonMonoxideLevel.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonMonoxideLevel.java index a3946c40b..cbccd9647 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonMonoxideLevel.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCarbonMonoxideLevel.java @@ -1,9 +1,11 @@ package io.github.hapjava.accessories.optionalcharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.carbonmonoxidesensor.CarbonMonoxideLevelCharacteristic; +import io.github.hapjava.characteristics.impl.carbonmonoxidesensor.CarbonMonoxidePeakLevelCharacteristic; import java.util.concurrent.CompletableFuture; -/** Accessory with carbon dioxide level and peak level characteristic. */ +/** Accessory with carbon monoxide level and peak level characteristic. */ public interface AccessoryWithCarbonMonoxideLevel { /** @@ -20,6 +22,66 @@ public interface AccessoryWithCarbonMonoxideLevel { */ CompletableFuture getCarbonMonoxideLevel(); + /** + * return the min value for carbon monoxide level. overwrite if you want to change the default + * value. + * + * @return min carbon monoxide level + */ + default double getMinCarbonMonoxideLevel() { + return CarbonMonoxideLevelCharacteristic.DEFAULT_MIN_VALUE; + } + + /** + * return the max value for carbon monoxide level. overwrite if you want to change the default + * value. + * + * @return max carbon monoxide level + */ + default double getMaxCarbonMonoxideLevel() { + return CarbonMonoxideLevelCharacteristic.DEFAULT_MAX_VALUE; + } + + /** + * return the min step value for carbon monoxide level. overwrite if you want to change the + * default value. + * + * @return min step carbon monoxide level + */ + default double getMinStepCarbonMonoxideLevel() { + return CarbonMonoxideLevelCharacteristic.DEFAULT_STEP; + } + + /** + * return the min value for carbon monoxide peak level. overwrite if you want to change the + * default value. + * + * @return min carbon monoxide peak level + */ + default double getMinCarbonMonoxidePeakLevel() { + return CarbonMonoxidePeakLevelCharacteristic.DEFAULT_MIN_VALUE; + } + + /** + * return the max value for carbon monoxide peak level. overwrite if you want to change the + * default value. + * + * @return max carbon monoxide peak level + */ + default double getMaxCarbonMonoxidePeakLevel() { + return CarbonMonoxidePeakLevelCharacteristic.DEFAULT_MAX_VALUE; + } + + /** + * return the min step value for carbon monoxide peak level. overwrite if you want to change the + * default value. + * + * @return min step carbon monoxide peak level + */ + default double getMinStepCarbonMonoxidePeakLevel() { + return CarbonMonoxidePeakLevelCharacteristic.DEFAULT_STEP; + } + /** * Subscribes to changes in the carbon monoxide level. * diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithColorTemperature.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithColorTemperature.java index fee36884d..ecf74ba63 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithColorTemperature.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithColorTemperature.java @@ -1,6 +1,7 @@ package io.github.hapjava.accessories.optionalcharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.lightbulb.ColorTemperatureCharacteristic; import java.util.concurrent.CompletableFuture; /** Accessory with color temperature. */ @@ -22,6 +23,24 @@ public interface AccessoryWithColorTemperature { */ CompletableFuture setColorTemperature(Integer value) throws Exception; + /** + * return the min value for color temperature. overwrite if you want to change the default value. + * + * @return min color temperature + */ + default int getMinColorTemperature() { + return ColorTemperatureCharacteristic.DEFAULT_MIN_VALUE; + } + + /** + * return the max value for color temperature. overwrite if you want to change the default value. + * + * @return max color temperature + */ + default int getMaxColorTemperature() { + return ColorTemperatureCharacteristic.DEFAULT_MAX_VALUE; + } + /** * Subscribes to changes in color temperature. * diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithDuration.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithDuration.java index e3d310dfd..a5cebf97c 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithDuration.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithDuration.java @@ -1,6 +1,7 @@ package io.github.hapjava.accessories.optionalcharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.valve.SetDurationCharacteristic; import java.util.concurrent.CompletableFuture; /** Accessory with duration characteristic. */ @@ -13,6 +14,24 @@ public interface AccessoryWithDuration { */ CompletableFuture getSetDuration(); + /** + * return the min value for duration. overwrite if you want to change the default value. + * + * @return min remaining duration + */ + default int getMinDuration() { + return SetDurationCharacteristic.DEFAULT_MIN_VALUE; + } + + /** + * return the max value for duration. overwrite if you want to change the default value. + * + * @return max duration + */ + default int getMaxDuration() { + return SetDurationCharacteristic.DEFAULT_MAX_VALUE; + } + /** * Sets the duration for which the service should run. * diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithNitrogenDioxideDensity.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithNitrogenDioxideDensity.java index 685edfb5c..6aead4f98 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithNitrogenDioxideDensity.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithNitrogenDioxideDensity.java @@ -1,6 +1,7 @@ package io.github.hapjava.accessories.optionalcharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.airquality.NitrogenDioxideDensityCharacteristic; import java.util.concurrent.CompletableFuture; /** Accessory with nitrogen dioxide density characteristic. */ @@ -13,6 +14,36 @@ public interface AccessoryWithNitrogenDioxideDensity { */ CompletableFuture getNitrogenDioxideDensity(); + /** + * return the min value for nitrogen dioxide density. overwrite if you want to change the default + * value. + * + * @return min nitrogen dioxide density + */ + default double getMinNitrogenDioxideDensity() { + return NitrogenDioxideDensityCharacteristic.DEFAULT_MIN_VALUE; + } + + /** + * return the max value for nitrogen dioxide density. overwrite if you want to change the default + * value. + * + * @return max nitrogen dioxide density + */ + default double getMaxNitrogenDioxideDensity() { + return NitrogenDioxideDensityCharacteristic.DEFAULT_MAX_VALUE; + } + + /** + * return the min step value for nitrogen dioxide density. overwrite if you want to change the + * default value. + * + * @return min step nitrogen dioxide density + */ + default double getMinStepNitrogenDioxideDensity() { + return NitrogenDioxideDensityCharacteristic.DEFAULT_STEP; + } + /** * Subscribes to changes in nitrogen dioxide density. * diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithOzoneDensity.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithOzoneDensity.java index 87bb8fa74..efba2a49f 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithOzoneDensity.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithOzoneDensity.java @@ -1,6 +1,7 @@ package io.github.hapjava.accessories.optionalcharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.airquality.OzoneDensityCharacteristic; import java.util.concurrent.CompletableFuture; /** Accessory with Ozone Density characteristic. */ @@ -13,6 +14,33 @@ public interface AccessoryWithOzoneDensity { */ CompletableFuture getOzoneDensity(); + /** + * return the min value for ozone density. overwrite if you want to change the default value. + * + * @return min ozone density + */ + default double getMinOzoneDensity() { + return OzoneDensityCharacteristic.DEFAULT_MIN_VALUE; + } + + /** + * return the max value for ozone density. overwrite if you want to change the default value. + * + * @return max ozone density + */ + default double getMaxOzoneDensity() { + return OzoneDensityCharacteristic.DEFAULT_MAX_VALUE; + } + + /** + * return the min step value for ozone density. overwrite if you want to change the default value. + * + * @return min step ozone density + */ + default double getMinStepOzoneDensity() { + return OzoneDensityCharacteristic.DEFAULT_STEP; + } + /** * Subscribes to changes in ozone density. * diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM10Density.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM10Density.java index 2bfad14ac..2ae3cded0 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM10Density.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM10Density.java @@ -1,6 +1,7 @@ package io.github.hapjava.accessories.optionalcharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.airquality.PM10DensityCharacteristic; import java.util.concurrent.CompletableFuture; /** Accessory with PM10 Density characteristic. */ @@ -13,6 +14,33 @@ public interface AccessoryWithPM10Density { */ CompletableFuture getPM10Density(); + /** + * return the min value for PM10 density. overwrite if you want to change the default value. + * + * @return min PM10 density + */ + default double getMinPM10Density() { + return PM10DensityCharacteristic.DEFAULT_MIN_VALUE; + } + + /** + * return the max value for PM10 density. overwrite if you want to change the default value. + * + * @return max PM10 density + */ + default double getMaxPM10Density() { + return PM10DensityCharacteristic.DEFAULT_MAX_VALUE; + } + + /** + * return the min step value for PM10 density. overwrite if you want to change the default value. + * + * @return min step PM10 density + */ + default double getMinStepPM10Density() { + return PM10DensityCharacteristic.DEFAULT_STEP; + } + /** * Subscribes to changes in PM10 density. * diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM25Density.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM25Density.java index 11ad2a12e..02488f52d 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM25Density.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPM25Density.java @@ -1,6 +1,7 @@ package io.github.hapjava.accessories.optionalcharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.airquality.PM25DensityCharacteristic; import java.util.concurrent.CompletableFuture; /** Accessory with PM25 Density characteristic. */ @@ -13,6 +14,33 @@ public interface AccessoryWithPM25Density { */ CompletableFuture getPM25Density(); + /** + * return the min value for PM25 density. overwrite if you want to change the default value. + * + * @return min PM25 density + */ + default double getMinPM25Density() { + return PM25DensityCharacteristic.DEFAULT_MIN_VALUE; + } + + /** + * return the max value for PM25 density. overwrite if you want to change the default value. + * + * @return max PM25 density + */ + default double getMaxPM25Density() { + return PM25DensityCharacteristic.DEFAULT_MAX_VALUE; + } + + /** + * return the min step value for PM25 density. overwrite if you want to change the default value. + * + * @return min step PM25 density + */ + default double getMinStepPM25Density() { + return PM25DensityCharacteristic.DEFAULT_STEP; + } + /** * Subscribes to changes in PM25 density. * diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRemainingDuration.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRemainingDuration.java index b949b413e..7859fb396 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRemainingDuration.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRemainingDuration.java @@ -1,6 +1,7 @@ package io.github.hapjava.accessories.optionalcharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.valve.RemainingDurationCharacteristic; import java.util.concurrent.CompletableFuture; /** @@ -17,6 +18,24 @@ public interface AccessoryWithRemainingDuration { */ CompletableFuture getRemainingDuration(); + /** + * return the min value for remaining duration. overwrite if you want to change the default value. + * + * @return min remaining duration + */ + default int getMinRemainingDuration() { + return RemainingDurationCharacteristic.DEFAULT_MIN_VALUE; + } + + /** + * return the max value for remaining duration. overwrite if you want to change the default value. + * + * @return max remaining duration + */ + default int getMaxRemainingDuration() { + return RemainingDurationCharacteristic.DEFAULT_MAX_VALUE; + } + /** * Subscribes to changes in the duration; note it is not necessary to emit a change every second, * homekit infers the countdown progress client side. diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSulphurDioxideDensity.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSulphurDioxideDensity.java index 94fad0be4..dbe0cc947 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSulphurDioxideDensity.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithSulphurDioxideDensity.java @@ -1,6 +1,7 @@ package io.github.hapjava.accessories.optionalcharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.airquality.SulphurDioxideDensityCharacteristic; import java.util.concurrent.CompletableFuture; /** Accessory with sulphur dioxide density characteristic. */ @@ -13,6 +14,36 @@ public interface AccessoryWithSulphurDioxideDensity { */ CompletableFuture getSulphurDioxideDensity(); + /** + * return the min value for sulphur dioxide density. overwrite if you want to change the default + * value. + * + * @return min sulphur dioxide density + */ + default double getMinSulphurDioxideDensity() { + return SulphurDioxideDensityCharacteristic.DEFAULT_MIN_VALUE; + } + + /** + * return the max value for sulphur dioxide density. overwrite if you want to change the default + * value. + * + * @return max sulphur dioxide density + */ + default double getMaxSulphurDioxideDensity() { + return SulphurDioxideDensityCharacteristic.DEFAULT_MAX_VALUE; + } + + /** + * return the min step value for sulphur dioxide density. overwrite if you want to change the + * default value. + * + * @return min step sulphur dioxide density + */ + default double getMinStepSulphurDioxideDensity() { + return SulphurDioxideDensityCharacteristic.DEFAULT_STEP; + } + /** * Subscribes to changes in sulphur dioxide density. * diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVOCDensity.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVOCDensity.java index b41cc43b7..f8a7bc46b 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVOCDensity.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVOCDensity.java @@ -1,6 +1,7 @@ package io.github.hapjava.accessories.optionalcharacteristic; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.airquality.VOCDensityCharacteristic; import java.util.concurrent.CompletableFuture; /** Accessory with VOC Density characteristic. */ @@ -13,6 +14,33 @@ public interface AccessoryWithVOCDensity { */ CompletableFuture getVOCDensity(); + /** + * return the min value for VOC density. overwrite if you want to change the default value. + * + * @return min VOC density + */ + default double getMinVOCDensity() { + return VOCDensityCharacteristic.DEFAULT_MIN_VALUE; + } + + /** + * return the max value for VOC density. overwrite if you want to change the default value. + * + * @return max VOC density + */ + default double getMaxVOCDensity() { + return VOCDensityCharacteristic.DEFAULT_MAX_VALUE; + } + + /** + * return the min step value for VOC density. overwrite if you want to change the default value. + * + * @return min step VOC density + */ + default double getMinStepVOCDensity() { + return VOCDensityCharacteristic.DEFAULT_STEP; + } + /** * Subscribes to changes in VOC density. * diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/NitrogenDioxideDensityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/NitrogenDioxideDensityCharacteristic.java index 432a85029..1e37e28b6 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airquality/NitrogenDioxideDensityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/NitrogenDioxideDensityCharacteristic.java @@ -9,21 +9,34 @@ /** This characteristic contains the current NO2 density in micrograms/m3. */ public class NitrogenDioxideDensityCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 1000; + public static final double DEFAULT_STEP = 1; public NitrogenDioxideDensityCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "000000C4-0000-1000-8000-0026BB765291", "nitrogen dioxide density", - 0, - 1000, - 1, + minValue, + maxValue, + minStep, "micrograms", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public NitrogenDioxideDensityCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/OzoneDensityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/OzoneDensityCharacteristic.java index 542dbce0b..577339288 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airquality/OzoneDensityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/OzoneDensityCharacteristic.java @@ -9,21 +9,34 @@ /** This characteristic contains the current ozone density in micrograms/m3. */ public class OzoneDensityCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 1000; + public static final double DEFAULT_STEP = 1; public OzoneDensityCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "000000C3-0000-1000-8000-0026BB765291", "ozone density", - 0, - 1000, - 1, + minValue, + maxValue, + minStep, "micrograms", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public OzoneDensityCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM10DensityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM10DensityCharacteristic.java index fdb0b9b49..a7b9c1245 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM10DensityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM10DensityCharacteristic.java @@ -11,21 +11,34 @@ * This characteristic contains the current PM10 micrometer particulate density in micrograms/m3. */ public class PM10DensityCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 1000; + public static final double DEFAULT_STEP = 1; public PM10DensityCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "000000C7-0000-1000-8000-0026BB765291", "PM10 density", - 0, - 1000, - 1, + minValue, + maxValue, + minStep, "micrograms", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public PM10DensityCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM25DensityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM25DensityCharacteristic.java index c54831553..1667934ef 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM25DensityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/PM25DensityCharacteristic.java @@ -11,21 +11,34 @@ * This characteristic contains the current PM2.5 micrometer particulate density in micrograms/m3. */ public class PM25DensityCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 1000; + public static final double DEFAULT_STEP = 1; public PM25DensityCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "000000C6-0000-1000-8000-0026BB765291", "PM2.5 density", - 0, - 1000, - 1, + minValue, + maxValue, + minStep, "micrograms", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public PM25DensityCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/SulphurDioxideDensityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/SulphurDioxideDensityCharacteristic.java index 03f668757..81df2c64b 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airquality/SulphurDioxideDensityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/SulphurDioxideDensityCharacteristic.java @@ -9,21 +9,34 @@ /** This characteristic contains the current SO2 density in micrograms/m3. */ public class SulphurDioxideDensityCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 1000; + public static final double DEFAULT_STEP = 1; public SulphurDioxideDensityCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "000000C5-0000-1000-8000-0026BB765291", "sulphur dioxide density", - 0, - 1000, - 1, + minValue, + maxValue, + minStep, "micrograms", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public SulphurDioxideDensityCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/airquality/VOCDensityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/airquality/VOCDensityCharacteristic.java index 76e062f45..a752161c9 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/airquality/VOCDensityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/airquality/VOCDensityCharacteristic.java @@ -12,21 +12,34 @@ * micrograms/m3. */ public class VOCDensityCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 1000; + public static final double DEFAULT_STEP = 1; public VOCDensityCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "000000C8-0000-1000-8000-0026BB765291", "VOC density", - 0, - 1000, - 1, + minValue, + maxValue, + minStep, "micrograms", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public VOCDensityCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideLevelCharacteristic.java index 7d1d8d440..b9b320564 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideLevelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxideLevelCharacteristic.java @@ -11,21 +11,34 @@ * This characteristic indicates the detected level of Carbon Dioxide in parts per million (ppm). */ public class CarbonDioxideLevelCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 100000; + public static final double DEFAULT_STEP = 1; public CarbonDioxideLevelCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "00000093-0000-1000-8000-0026BB765291", "Carbon Dioxide Level", - 0, - 100000, - 1, + minValue, + maxValue, + minStep, "ppm", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public CarbonDioxideLevelCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxidePeakLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxidePeakLevelCharacteristic.java index a2ad994fc..1a1793773 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxidePeakLevelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbondioxidesensor/CarbonDioxidePeakLevelCharacteristic.java @@ -9,21 +9,34 @@ /** This characteristic indicates the highest detected level (ppm) of carbon dioxide. */ public class CarbonDioxidePeakLevelCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 100000; + public static final double DEFAULT_STEP = 1; public CarbonDioxidePeakLevelCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "00000094-0000-1000-8000-0026BB765291", "Carbon Dioxide Level", - 0, - 100000, - 1, + minValue, + maxValue, + minStep, "ppm", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public CarbonDioxidePeakLevelCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideLevelCharacteristic.java index 554638a22..e35171b38 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideLevelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxideLevelCharacteristic.java @@ -9,21 +9,34 @@ /** This characteristic contains the Carbon Monoxide levels in parts per million (ppm). */ public class CarbonMonoxideLevelCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 100; + public static final double DEFAULT_STEP = 1; public CarbonMonoxideLevelCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "00000090-0000-1000-8000-0026BB765291", "Carbon Monoxide Level", - 0, - 100, - 1, + minValue, + maxValue, + minStep, "ppm", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public CarbonMonoxideLevelCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxidePeakLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxidePeakLevelCharacteristic.java index 3a714674a..892901866 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxidePeakLevelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/carbonmonoxidesensor/CarbonMonoxidePeakLevelCharacteristic.java @@ -9,21 +9,34 @@ /** This characteristic indicates the highest detected level (ppm) of Carbon Monoxide. */ public class CarbonMonoxidePeakLevelCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 100; + public static final double DEFAULT_STEP = 1; public CarbonMonoxidePeakLevelCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "00000091-0000-1000-8000-0026BB765291", "Carbon Monoxide Peak Level", - 0, - 100000, - 1, + minValue, + maxValue, + minStep, "ppm", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public CarbonMonoxidePeakLevelCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/ColorTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/ColorTemperatureCharacteristic.java index c7779233a..12886c7a1 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/ColorTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/ColorTemperatureCharacteristic.java @@ -12,8 +12,12 @@ /** This characteristic describes color temperature in Kelvin */ public class ColorTemperatureCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { + public static final int DEFAULT_MIN_VALUE = 50; + public static final int DEFAULT_MAX_VALUE = 400; public ColorTemperatureCharacteristic( + int minValue, + int maxValue, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -21,12 +25,20 @@ public ColorTemperatureCharacteristic( super( "000000CE-0000-1000-8000-0026BB765291", "color temperature", - 50, - 400, + minValue, + maxValue, "K", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public ColorTemperatureCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightsensor/CurrentAmbientLightLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightsensor/CurrentAmbientLightLevelCharacteristic.java index aee5e24d5..afec35ce6 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lightsensor/CurrentAmbientLightLevelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightsensor/CurrentAmbientLightLevelCharacteristic.java @@ -9,21 +9,34 @@ /** This characteristic indicates the current light level in Lux */ public class CurrentAmbientLightLevelCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0.0001; + public static final double DEFAULT_MAX_VALUE = 100000; + public static final double DEFAULT_STEP = 0.0001; public CurrentAmbientLightLevelCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "0000006B-0000-1000-8000-0026BB765291", "ambient light level", - 0.0001, - 100000, - 0.0001, + minValue, + maxValue, + minStep, "lux", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public CurrentAmbientLightLevelCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/valve/RemainingDurationCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/valve/RemainingDurationCharacteristic.java index 4866be230..828fafab1 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/valve/RemainingDurationCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/valve/RemainingDurationCharacteristic.java @@ -15,20 +15,31 @@ */ public class RemainingDurationCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { + public static final int DEFAULT_MIN_VALUE = 0; + public static final int DEFAULT_MAX_VALUE = 3600; public RemainingDurationCharacteristic( + int minValue, + int maxValue, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "000000D4-0000-1000-8000-0026BB765291", "remaining duration", - 0, - 3600, + minValue, + maxValue, "s", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public RemainingDurationCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/valve/SetDurationCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/valve/SetDurationCharacteristic.java index a372d0515..59cef5646 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/valve/SetDurationCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/valve/SetDurationCharacteristic.java @@ -12,8 +12,12 @@ /** This characteristic describes the duration, how long an accessory should be set to "InUse". */ public class SetDurationCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { + public static final int DEFAULT_MIN_VALUE = 0; + public static final int DEFAULT_MAX_VALUE = 3600; public SetDurationCharacteristic( + int minValue, + int maxValue, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -21,12 +25,20 @@ public SetDurationCharacteristic( super( "000000D3-0000-1000-8000-0026BB765291", "set duration", - 0, - 3600, + minValue, + maxValue, "s", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public SetDurationCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/services/impl/AirQualityService.java b/src/main/java/io/github/hapjava/services/impl/AirQualityService.java index c9ccd6d62..4aa74c88f 100644 --- a/src/main/java/io/github/hapjava/services/impl/AirQualityService.java +++ b/src/main/java/io/github/hapjava/services/impl/AirQualityService.java @@ -53,6 +53,9 @@ public AirQualityService(AirQualityAccessory accessory) { if (accessory instanceof AccessoryWithNitrogenDioxideDensity) { addOptionalCharacteristic( new NitrogenDioxideDensityCharacteristic( + ((AccessoryWithNitrogenDioxideDensity) accessory).getMinNitrogenDioxideDensity(), + ((AccessoryWithNitrogenDioxideDensity) accessory).getMaxNitrogenDioxideDensity(), + ((AccessoryWithNitrogenDioxideDensity) accessory).getMinStepNitrogenDioxideDensity(), ((AccessoryWithNitrogenDioxideDensity) accessory)::getNitrogenDioxideDensity, ((AccessoryWithNitrogenDioxideDensity) accessory)::subscribeNitrogenDioxideDensity, ((AccessoryWithNitrogenDioxideDensity) accessory) @@ -62,6 +65,9 @@ public AirQualityService(AirQualityAccessory accessory) { if (accessory instanceof AccessoryWithSulphurDioxideDensity) { addOptionalCharacteristic( new SulphurDioxideDensityCharacteristic( + ((AccessoryWithSulphurDioxideDensity) accessory).getMinSulphurDioxideDensity(), + ((AccessoryWithSulphurDioxideDensity) accessory).getMaxSulphurDioxideDensity(), + ((AccessoryWithSulphurDioxideDensity) accessory).getMinStepSulphurDioxideDensity(), ((AccessoryWithSulphurDioxideDensity) accessory)::getSulphurDioxideDensity, ((AccessoryWithSulphurDioxideDensity) accessory)::subscribeSulphurDioxideDensity, ((AccessoryWithSulphurDioxideDensity) accessory)::unsubscribeSulphurDioxideDensity)); @@ -69,6 +75,9 @@ public AirQualityService(AirQualityAccessory accessory) { if (accessory instanceof AccessoryWithPM25Density) { addOptionalCharacteristic( new PM25DensityCharacteristic( + ((AccessoryWithPM25Density) accessory).getMinPM25Density(), + ((AccessoryWithPM25Density) accessory).getMaxPM25Density(), + ((AccessoryWithPM25Density) accessory).getMinStepPM25Density(), ((AccessoryWithPM25Density) accessory)::getPM25Density, ((AccessoryWithPM25Density) accessory)::subscribePM25Density, ((AccessoryWithPM25Density) accessory)::unsubscribePM25Density)); @@ -76,6 +85,9 @@ public AirQualityService(AirQualityAccessory accessory) { if (accessory instanceof AccessoryWithPM10Density) { addOptionalCharacteristic( new PM10DensityCharacteristic( + ((AccessoryWithPM10Density) accessory).getMinPM10Density(), + ((AccessoryWithPM10Density) accessory).getMaxPM10Density(), + ((AccessoryWithPM10Density) accessory).getMinStepPM10Density(), ((AccessoryWithPM10Density) accessory)::getPM10Density, ((AccessoryWithPM10Density) accessory)::subscribePM10Density, ((AccessoryWithPM10Density) accessory)::unsubscribePM10Density)); @@ -83,6 +95,9 @@ public AirQualityService(AirQualityAccessory accessory) { if (accessory instanceof AccessoryWithVOCDensity) { addOptionalCharacteristic( new VOCDensityCharacteristic( + ((AccessoryWithVOCDensity) accessory).getMinVOCDensity(), + ((AccessoryWithVOCDensity) accessory).getMaxVOCDensity(), + ((AccessoryWithVOCDensity) accessory).getMinStepVOCDensity(), ((AccessoryWithVOCDensity) accessory)::getVOCDensity, ((AccessoryWithVOCDensity) accessory)::subscribeVOCDensity, ((AccessoryWithVOCDensity) accessory)::unsubscribeVOCDensity)); diff --git a/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java b/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java index b329e5f8f..b61e91b91 100644 --- a/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/CarbonDioxideSensorService.java @@ -34,11 +34,17 @@ public CarbonDioxideSensorService(CarbonDioxideSensorAccessory accessory) { if (accessory instanceof AccessoryWithCarbonDioxideLevel) { addOptionalCharacteristic( new CarbonDioxideLevelCharacteristic( + ((AccessoryWithCarbonDioxideLevel) accessory).getMinCarbonDioxideLevel(), + ((AccessoryWithCarbonDioxideLevel) accessory).getMaxCarbonDioxideLevel(), + ((AccessoryWithCarbonDioxideLevel) accessory).getMinStepCarbonDioxideLevel(), ((AccessoryWithCarbonDioxideLevel) accessory)::getCarbonDioxideLevel, ((AccessoryWithCarbonDioxideLevel) accessory)::subscribeCarbonDioxideLevel, ((AccessoryWithCarbonDioxideLevel) accessory)::unsubscribeCarbonDioxideLevel)); addOptionalCharacteristic( new CarbonDioxidePeakLevelCharacteristic( + ((AccessoryWithCarbonDioxideLevel) accessory).getMinCarbonDioxidePeakLevel(), + ((AccessoryWithCarbonDioxideLevel) accessory).getMaxCarbonDioxidePeakLevel(), + ((AccessoryWithCarbonDioxideLevel) accessory).getMinStepCarbonDioxidePeakLevel(), ((AccessoryWithCarbonDioxideLevel) accessory)::getCarbonDioxidePeakLevel, ((AccessoryWithCarbonDioxideLevel) accessory)::subscribeCarbonDioxidePeakLevel, ((AccessoryWithCarbonDioxideLevel) accessory)::unsubscribeCarbonDioxidePeakLevel)); diff --git a/src/main/java/io/github/hapjava/services/impl/LightSensorService.java b/src/main/java/io/github/hapjava/services/impl/LightSensorService.java index b40b4a43e..6aef8cd34 100644 --- a/src/main/java/io/github/hapjava/services/impl/LightSensorService.java +++ b/src/main/java/io/github/hapjava/services/impl/LightSensorService.java @@ -24,6 +24,9 @@ public LightSensorService(CurrentAmbientLightLevelCharacteristic lightLevel) { public LightSensorService(LightSensorAccessory accessory) { this( new CurrentAmbientLightLevelCharacteristic( + accessory.getMinCurrentAmbientLightLevel(), + accessory.getMaxCurrentAmbientLightLevel(), + accessory.getMinStepCurrentAmbientLightLevel(), accessory::getCurrentAmbientLightLevel, accessory::subscribeCurrentAmbientLightLevel, accessory::unsubscribeCurrentAmbientLightLevel)); diff --git a/src/main/java/io/github/hapjava/services/impl/LightbulbService.java b/src/main/java/io/github/hapjava/services/impl/LightbulbService.java index 387a2b092..b37ca4d2c 100644 --- a/src/main/java/io/github/hapjava/services/impl/LightbulbService.java +++ b/src/main/java/io/github/hapjava/services/impl/LightbulbService.java @@ -55,6 +55,8 @@ public LightbulbService(LightbulbAccessory accessory) { if (accessory instanceof AccessoryWithColorTemperature) { addOptionalCharacteristic( new ColorTemperatureCharacteristic( + ((AccessoryWithColorTemperature) accessory).getMinColorTemperature(), + ((AccessoryWithColorTemperature) accessory).getMaxColorTemperature(), ((AccessoryWithColorTemperature) accessory)::getColorTemperature, ((AccessoryWithColorTemperature) accessory)::setColorTemperature, ((AccessoryWithColorTemperature) accessory)::subscribeColorTemperature, diff --git a/src/main/java/io/github/hapjava/services/impl/ThermostatService.java b/src/main/java/io/github/hapjava/services/impl/ThermostatService.java index 232d9c487..c775a41d5 100644 --- a/src/main/java/io/github/hapjava/services/impl/ThermostatService.java +++ b/src/main/java/io/github/hapjava/services/impl/ThermostatService.java @@ -73,6 +73,12 @@ public ThermostatService(ThermostatAccessory accessory) { if (accessory instanceof AccessoryWithCoolingThresholdTemperature) { addOptionalCharacteristic( new CoolingThresholdTemperatureCharacteristic( + ((AccessoryWithCoolingThresholdTemperature) accessory) + .getMinCoolingThresholdTemperature(), + ((AccessoryWithCoolingThresholdTemperature) accessory) + .getMaxCoolingThresholdTemperature(), + ((AccessoryWithCoolingThresholdTemperature) accessory) + .getStepCoolingThresholdTemperature(), ((AccessoryWithCoolingThresholdTemperature) accessory) ::getCoolingThresholdTemperature, ((AccessoryWithCoolingThresholdTemperature) accessory) @@ -85,6 +91,12 @@ public ThermostatService(ThermostatAccessory accessory) { if (accessory instanceof AccessoryWithHeatingThresholdTemperature) { addOptionalCharacteristic( new HeatingThresholdTemperatureCharacteristic( + ((AccessoryWithHeatingThresholdTemperature) accessory) + .getMinHeatingThresholdTemperature(), + ((AccessoryWithHeatingThresholdTemperature) accessory) + .getMaxHeatingThresholdTemperature(), + ((AccessoryWithHeatingThresholdTemperature) accessory) + .getStepHeatingThresholdTemperature(), ((AccessoryWithHeatingThresholdTemperature) accessory) ::getHeatingThresholdTemperature, ((AccessoryWithHeatingThresholdTemperature) accessory) diff --git a/src/main/java/io/github/hapjava/services/impl/ValveService.java b/src/main/java/io/github/hapjava/services/impl/ValveService.java index b45c48ef4..519502a3c 100644 --- a/src/main/java/io/github/hapjava/services/impl/ValveService.java +++ b/src/main/java/io/github/hapjava/services/impl/ValveService.java @@ -58,6 +58,8 @@ public ValveService(ValveAccessory accessory) { if (accessory instanceof AccessoryWithDuration) { addOptionalCharacteristic( new SetDurationCharacteristic( + ((AccessoryWithDuration) accessory).getMinDuration(), + ((AccessoryWithDuration) accessory).getMaxDuration(), ((AccessoryWithDuration) accessory)::getSetDuration, ((AccessoryWithDuration) accessory)::setSetDuration, ((AccessoryWithDuration) accessory)::subscribeSetDuration, @@ -66,6 +68,8 @@ public ValveService(ValveAccessory accessory) { if (accessory instanceof AccessoryWithRemainingDuration) { addOptionalCharacteristic( new RemainingDurationCharacteristic( + ((AccessoryWithRemainingDuration) accessory).getMinRemainingDuration(), + ((AccessoryWithRemainingDuration) accessory).getMaxRemainingDuration(), ((AccessoryWithRemainingDuration) accessory)::getRemainingDuration, ((AccessoryWithRemainingDuration) accessory)::subscribeRemainingDuration, ((AccessoryWithRemainingDuration) accessory)::unsubscribeRemainingDuration)); From b090400364f77af87dc2a5082c405da276e3d7c4 Mon Sep 17 00:00:00 2001 From: Andy Lintner Date: Sun, 28 Mar 2021 11:35:24 -0400 Subject: [PATCH 050/138] Add initial workflow for CI Perform maven build --- .github/workflows/maven.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/maven.yml diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 000000000..e9a49816e --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,25 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Java CI with Maven + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: '11' + architecture: x64 + - name: Build with Maven + run: mvn -B package --file pom.xml From f0aea59cc88951f0b7fb9e3861ca7e01994a45f2 Mon Sep 17 00:00:00 2001 From: Andy Lintner Date: Sun, 28 Mar 2021 13:27:28 -0400 Subject: [PATCH 051/138] Setup for release Added release automation via Github workflow and upload to Maven Central for SNAPSHOTs and releases. --- .github/workflows/deploy.yml | 32 ++++++++++++++++++++++++++++ .github/workflows/maven.yml | 2 -- .github/workflows/release.yml | 39 +++++++++++++++++++++++++++++++++++ README.md | 4 ++++ RELEASING.md | 7 +++++++ pom.xml | 35 ++++++++++--------------------- 6 files changed, 93 insertions(+), 26 deletions(-) create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/release.yml create mode 100644 RELEASING.md diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..5fa648aae --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,32 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Java CI with Maven and Deploy + +on: + push: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 for Maven Central + uses: actions/setup-java@v1 + with: + java-version: '11' + architecture: x64 + server-id: ossrh + gpg-private-key: ${{ secrets.GPG_SECRET_KEY }} + gpg-passphrase: MAVEN_GPG_PASSPHRASE + server-username: MAVEN_USERNAME + server-password: MAVEN_CENTRAL_TOKEN + - name: Publish to Maven Central + run: mvn deploy + env: + MAVEN_USERNAME: hap-java-dev + MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index e9a49816e..a07bff253 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -4,8 +4,6 @@ name: Java CI with Maven on: - push: - branches: [ master ] pull_request: branches: [ master ] diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..0aa72794f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,39 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Stage Release on Master Branch + +on: + workflow_dispatch: + inputs: + releaseVersion: + description: Version to release + required: true + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: '11' + architecture: x64 + server-id: ossrh + gpg-private-key: ${{ secrets.GPG_SECRET_KEY }} + gpg-passphrase: MAVEN_GPG_PASSPHRASE + server-username: MAVEN_USERNAME + server-password: MAVEN_CENTRAL_TOKEN + - name: Setup git profile + run: | + git config user.name github-actions + git config user.email github-actions@github.com + - name: Maven release + run: mvn release:prepare release:perform -B -DreleaseVersion=${{ github.event.inputs.releaseVersion }} -Possrh + env: + MAVEN_USERNAME: hap-java-dev + MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} diff --git a/README.md b/README.md index 463f5d49b..9a9b3da86 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ HAP-Java ========= +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.github.hap-java/hap/badge.svg)](https://search.maven.org/search?q=g:io.github.hap-java%20a:hap) +[![MIT License](https://img.shields.io/github/license/hap-java/HAP-Java)](https://github.com/hap-java/HAP-Java/blob/master/LICENSE) +![Maven Build Status](https://github.com/andylintner/HAP-Java/actions/workflows/maven.yml/badge.svg) + HAP-Java is a Java implementation of the HomeKit Accessory Protocol. Using this library, you can create your own HomeKit Accessory or HomeKit Accessory Bridge. diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 000000000..a3001c9f3 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,7 @@ +# How to release HAP-Java + +These actions can only be performed by someone with maintainer level access to the repository. + +1. Run the [Stage Release on Master Branch](https://github.com/hap-java/HAP-Java/actions/workflows/release.yml) Action +2. After this completes, find the release on the [Releases page](https://github.com/hap-java/HAP-Java/releases) +3. Edit the release to include the changelog details diff --git a/pom.xml b/pom.xml index 5faf6eafc..91f687573 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git - scm:git:git@github.com:hap-java/HAP-Java.git + scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git HEAD @@ -284,11 +284,11 @@ ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ + https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ ossrh - https://oss.sonatype.org/content/repositories/snapshots + https://s01.oss.sonatype.org/content/repositories/snapshots @@ -325,20 +325,6 @@ ossrh - - gpg - ${env.GPG_KEYNAME} - ${env.GPG_PASSPHRASE} - false - deploy/pubring.gpg - deploy/secring.gpg - - - - performRelease - true - - @@ -354,6 +340,12 @@ + + + --pinentry-mode + loopback + + org.sonatype.plugins @@ -362,17 +354,12 @@ true ossrh - https://oss.sonatype.org/ + https://s01.oss.sonatype.org/ true - - org.apache.maven.plugins - maven-release-plugin - 2.5.3 - - + From 98efd976abfee5d2e63e45b3bbce52019788d03b Mon Sep 17 00:00:00 2001 From: Andy Lintner Date: Sun, 28 Mar 2021 16:44:59 -0400 Subject: [PATCH 052/138] Remove old deploy mechanism --- deploy/distribution.xml | 18 ------------------ deploy/publish.sh | 5 ----- deploy/pubring.gpg.enc | Bin 259376 -> 0 bytes deploy/secring.gpg.enc | Bin 5520 -> 0 bytes deploy/settings.xml | 9 --------- 5 files changed, 32 deletions(-) delete mode 100644 deploy/distribution.xml delete mode 100755 deploy/publish.sh delete mode 100644 deploy/pubring.gpg.enc delete mode 100644 deploy/secring.gpg.enc delete mode 100644 deploy/settings.xml diff --git a/deploy/distribution.xml b/deploy/distribution.xml deleted file mode 100644 index a2cf6d668..000000000 --- a/deploy/distribution.xml +++ /dev/null @@ -1,18 +0,0 @@ - - dir-with-dependencies - - dir - - false - - - / - true - false - ${artifact.groupId}.${artifact.artifactId}-${artifact.version}${dashClassifier?}.jar - runtime - - - \ No newline at end of file diff --git a/deploy/publish.sh b/deploy/publish.sh deleted file mode 100755 index 1fb261646..000000000 --- a/deploy/publish.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -if [[ ( $TRAVIS_PULL_REQUEST == "false" && $TRAVIS_BRANCH == "master" ) ]]; then - mvn deploy --settings deploy/settings.xml -DperformRelease=true -DskipTests=true - exit $? -fi diff --git a/deploy/pubring.gpg.enc b/deploy/pubring.gpg.enc deleted file mode 100644 index 1361f5a9fb491850de9a5c2846f6b0c7c223c876..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 259376 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 mnT0!|RXtTToH$Ds@dM*hnPCmqZD{@9Ky)@V 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%0Dh-b`Z{ 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 diff --git a/deploy/secring.gpg.enc b/deploy/secring.gpg.enc deleted file mode 100644 index 1f1c310495f57142662cc5d7b56285337976a8d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5520 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 SNZ<2A`-VN;JI65odIjy*9LmT5 diff --git a/deploy/settings.xml b/deploy/settings.xml deleted file mode 100644 index f08c008c0..000000000 --- a/deploy/settings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - ossrh - ${env.SONATYPE_USERNAME} - ${env.SONATYPE_PASSWORD} - - - From cd8c7937dd6921e037fd3f17899c3c23a0e194a5 Mon Sep 17 00:00:00 2001 From: Andy Lintner Date: Sat, 3 Apr 2021 12:40:53 -0400 Subject: [PATCH 053/138] Remove assembly plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don’t really need to build an assembly - most people will just consume this via maven, and if they don’t, they can do their own packaging. --- pom.xml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/pom.xml b/pom.xml index 91f687573..0aaa357b7 100644 --- a/pom.xml +++ b/pom.xml @@ -208,24 +208,6 @@ - - maven-assembly-plugin - 3.1.1 - - - deploy/distribution.xml - - - - - make-assembly - package - - single - - - - org.apache.maven.plugins maven-scm-publish-plugin From fc882b91e8d3318cd53b499c7cc9354745e44d2d Mon Sep 17 00:00:00 2001 From: Andy Lintner Date: Sat, 3 Apr 2021 12:47:53 -0400 Subject: [PATCH 054/138] Publish site on deploy --- .github/workflows/deploy.yml | 2 ++ pom.xml | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 5fa648aae..c8208d3af 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -30,3 +30,5 @@ jobs: MAVEN_USERNAME: hap-java-dev MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }} MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + - name: Publish site + run: mvn deploy-site diff --git a/pom.xml b/pom.xml index 0aaa357b7..56f7f239f 100644 --- a/pom.xml +++ b/pom.xml @@ -220,7 +220,7 @@ publish-scm - scm:git:git@github.com:hap-java/HAP-Java.git + scm:git:https://github.com/hap-java/HAP-Java.git gh-pages @@ -287,6 +287,8 @@ 3.0.1 io.github.hapjava.server.impl + 8 + false From 3c976abcbcefd9df62a813e7f819acdfa27b8b22 Mon Sep 17 00:00:00 2001 From: Andy Lintner Date: Sat, 3 Apr 2021 21:02:39 -0400 Subject: [PATCH 055/138] Fix site-deploy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turns out wagon-scm doesn’t pick up the github properties the way the release plugin does. This fixes the site deploy. Fixes #141 --- .github/workflows/deploy.yml | 6 +++++- README.md | 3 ++- pom.xml | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index c8208d3af..4b2a5e563 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -24,6 +24,10 @@ jobs: gpg-passphrase: MAVEN_GPG_PASSPHRASE server-username: MAVEN_USERNAME server-password: MAVEN_CENTRAL_TOKEN + - name: Setup git profile + run: | + git config --global user.name github-actions + git config --global user.email github-actions@github.com - name: Publish to Maven Central run: mvn deploy env: @@ -31,4 +35,4 @@ jobs: MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }} MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Publish site - run: mvn deploy-site + run: mvn -B site-deploy -Dusername=github-actions -Dpassword=${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 9a9b3da86..03fb0aaba 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,8 @@ Include HAP-Java in your project using maven: ``` -After that, check out the [Sample](https://github.com/hap-java/HAP-Java/tree/sample). +After that, check out the [Sample](https://github.com/hap-java/HAP-Java/tree/sample) and +read the [Javadoc](https://hap-java.github.io/HAP-Java/apidocs/index.html) Supported HomeKit Accessories ========= diff --git a/pom.xml b/pom.xml index 56f7f239f..8d5aca5be 100644 --- a/pom.xml +++ b/pom.xml @@ -211,7 +211,7 @@ org.apache.maven.plugins maven-scm-publish-plugin - 3.0.0 + 3.1.0 scm-publish From e4085bdd5237199e4c5a8b65ad583ea88faf3341 Mon Sep 17 00:00:00 2001 From: Andy Lintner Date: Sat, 3 Apr 2021 22:23:41 -0400 Subject: [PATCH 056/138] Fix for unregistering service when re-advertising MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we change the properties that we’re advertising over mDNS we unregister and re-register the service. However, we were calling unregister after changing the properties. This happened to work with our version of jmdns, which only compared the qualified name. jmdns has quite a few forks however, and it’s best to adhere to the implied contract when calling unregisterService. This is adapted from a fix proposed by J-N-K --- CHANGES.md | 4 ++ pom.xml | 11 +++- .../impl/jmdns/JmdnsHomekitAdvertiser.java | 13 +++- .../jmdns/JmdnsHomekitAdvertiserTest.java | 66 +++++++++++++++++++ 4 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 src/test/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiserTest.java diff --git a/CHANGES.md b/CHANGES.md index 758ddf6f7..bed3e3b48 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,10 @@ * Supported valid states for Thermostat, SecuritySystem, HeaterCooler and HumidifierDehumidifier [#108] [#120](https://github.com/hap-java/HAP-Java/pull/120) * Support for FilterMaintenance. Can be used as a linked service for an Air Purifier [#124](https://github.com/hap-java/HAP-Java/pull/124) +## Fixes + +* Fix for re-advertising service when using alternative jMDNS implementations. + # HAP-Java 1.1.5 ## Fixes diff --git a/pom.xml b/pom.xml index 8d5aca5be..0a358ad72 100644 --- a/pom.xml +++ b/pom.xml @@ -148,8 +148,15 @@ org.mockito - mockito-all - 1.10.19 + mockito-core + 3.8.0 + test + + + + org.assertj + assertj-core + 3.19.0 test diff --git a/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java index a178d2cfc..04016561e 100644 --- a/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java +++ b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java @@ -26,6 +26,7 @@ public class JmdnsHomekitAdvertiser { private String setupId; private int port; private int configurationIndex; + private ServiceInfo serviceInfo; public JmdnsHomekitAdvertiser(JmDNS jmdns) { this.jmdns = jmdns; @@ -87,12 +88,20 @@ public synchronized void setConfigurationIndex(int revision) throws IOException } private void unregisterService() { - jmdns.unregisterService(buildServiceInfo()); + if (serviceInfo != null) { + jmdns.unregisterService(serviceInfo); + serviceInfo = null; + } } private void registerService() throws IOException { logger.info("Registering " + SERVICE_TYPE + " on port " + port); - jmdns.registerService(buildServiceInfo()); + if (this.serviceInfo != null) { + throw new AssertionError( + "Registering an already registered service without unregistering first is not allowed"); + } + serviceInfo = buildServiceInfo(); + jmdns.registerService(serviceInfo); } private ServiceInfo buildServiceInfo() { diff --git a/src/test/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiserTest.java b/src/test/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiserTest.java new file mode 100644 index 000000000..1c04b2868 --- /dev/null +++ b/src/test/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiserTest.java @@ -0,0 +1,66 @@ +package io.github.hapjava.server.impl.jmdns; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import java.io.IOException; +import java.net.UnknownHostException; +import javax.jmdns.JmDNS; +import javax.jmdns.ServiceInfo; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +public class JmdnsHomekitAdvertiserTest { + + JmdnsHomekitAdvertiser subject; + JmDNS jmdns; + + @Before + public void setup() throws UnknownHostException, IOException { + jmdns = mock(JmDNS.class); + subject = new JmdnsHomekitAdvertiser(jmdns); + } + + @Test + public void testAdvertiseTwiceFails() throws Exception { + advertise(); + assertThatThrownBy(() -> advertise()).isNotNull(); + } + + /* + * Verify that the unregister call is for the initial registered service + * when changing discoverability causes advertising to be toggled. + */ + @Test + public void testSetDiscoverableAfterAdvertise() throws Exception { + subject.setDiscoverable(false); + advertise(); + subject.setDiscoverable(true); + assertThat(getArgumentFromUnregister().getPropertyString("sf")).isEqualTo("0"); + } + + /* + * Verify that the unregister call is for the initial registered service + * when changing the config index causes advertising to be toggled. + */ + @Test + public void testSetConfigurationIndex() throws Exception { + subject.setConfigurationIndex(1); + advertise(); + subject.setConfigurationIndex(2); + assertThat(getArgumentFromUnregister().getPropertyString("c#")).isEqualTo("1"); + } + + private ServiceInfo getArgumentFromUnregister() { + ArgumentCaptor serviceInfoCaptor = ArgumentCaptor.forClass(ServiceInfo.class); + verify(jmdns).unregisterService(serviceInfoCaptor.capture()); + return serviceInfoCaptor.getValue(); + } + + private void advertise() throws Exception { + subject.advertise("test", "00:00:00:00:00:00", 1234, 1, "1"); + } +} From c1ceec989e6f0803e6b4cae855222a4e21b9b8f9 Mon Sep 17 00:00:00 2001 From: Andy Lintner Date: Sat, 3 Apr 2021 22:48:02 -0400 Subject: [PATCH 057/138] Use the sonatype publisher for snapshots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this, the snapshot won’t auto-close. --- .github/workflows/deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4b2a5e563..d439198c7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -28,8 +28,8 @@ jobs: run: | git config --global user.name github-actions git config --global user.email github-actions@github.com - - name: Publish to Maven Central - run: mvn deploy + - name: Publish Snapshot to Maven Central + run: mvn deploy -B -Possrh env: MAVEN_USERNAME: hap-java-dev MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }} From 06e0165818f30b534f18bfaf0893ee7e20beb341 Mon Sep 17 00:00:00 2001 From: Andy Lintner Date: Sun, 4 Apr 2021 00:57:07 -0400 Subject: [PATCH 058/138] Add gpg keyname to signing config --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 8d5aca5be..56b04825a 100644 --- a/pom.xml +++ b/pom.xml @@ -325,6 +325,7 @@ + HAP-Java --pinentry-mode loopback From d4718f5d25e5202878628ac90d230ed3d7817106 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 8 May 2021 14:00:37 +0000 Subject: [PATCH 059/138] [maven-release-plugin] prepare release hap-2.0.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a3e89093a..5ce597061 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.0-snapshot + 2.0.0 jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - HEAD + hap-2.0.0 From f4a9872d303a1108e14e97fd54f9c3927f236400 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 8 May 2021 14:00:40 +0000 Subject: [PATCH 060/138] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5ce597061..bb760ace2 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.0 + 2.0.1-SNAPSHOT jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - hap-2.0.0 + HEAD From 39dcf16ef7f73317e0bd094d5937f36f32df3c07 Mon Sep 17 00:00:00 2001 From: Marius Volkhart Date: Thu, 27 Jan 2022 11:05:55 -0600 Subject: [PATCH 061/138] Fix: Log accessory names not futures (#151) The previous code logged the CompletableFuture, not the name of the accessory. Fixes #150 --- CHANGES.md | 4 ++++ .../java/io/github/hapjava/server/impl/HomekitRoot.java | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index bed3e3b48..1977e7fb7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +# HAP-Java 2.0.1 +## Fixes +* Log accessory names instead of futures. [#150](https://github.com/hap-java/HAP-Java/issues/150) + # HAP-Java 2.0.0 * major refactoring to support optional characteristics * structure and names adapted to HAP spec structure and naming. diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java index fba9d7b7b..e619fa1e7 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java @@ -83,7 +83,9 @@ public void addAccessory(HomekitAccessory accessory) { */ void addAccessorySkipRangeCheck(HomekitAccessory accessory) { this.registry.add(accessory); - logger.trace("Added accessory " + accessory.getName()); + if (logger.isTraceEnabled()) { + accessory.getName().thenAccept(name -> logger.trace("Added accessory {}", name)); + } if (started) { registry.reset(); webHandler.resetConnections(); @@ -99,7 +101,9 @@ void addAccessorySkipRangeCheck(HomekitAccessory accessory) { */ public void removeAccessory(HomekitAccessory accessory) { this.registry.remove(accessory); - logger.trace("Removed accessory " + accessory.getName()); + if (logger.isTraceEnabled()) { + accessory.getName().thenAccept(name -> logger.trace("Removed accessory {}", name)); + } if (started) { registry.reset(); webHandler.resetConnections(); From d81ea634858f9331fb26d64d3d84b4cf0616359a Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sat, 29 Jan 2022 15:43:33 +0100 Subject: [PATCH 062/138] upgrade netty Signed-off-by: Eugen Freiter --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bb760ace2..4d3a4dbbb 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ UTF-8 - 4.1.42.Final + 4.1.72.Final From 7a64a1b5d7c35f6976fea6dd6caefb4095967d5d Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sat, 29 Jan 2022 19:20:01 +0100 Subject: [PATCH 063/138] fix rotation speed data type Signed-off-by: Eugen Freiter --- .../AccessoryWithRotationSpeed.java | 4 +-- .../impl/fan/RotationSpeedCharacteristic.java | 34 +++++++++++++++---- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationSpeed.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationSpeed.java index 8b00e3350..ef39947ce 100644 --- a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationSpeed.java +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithRotationSpeed.java @@ -11,7 +11,7 @@ public interface AccessoryWithRotationSpeed { * * @return a future that will contain the speed, expressed as an integer between 0 and 100. */ - CompletableFuture getRotationSpeed(); + CompletableFuture getRotationSpeed(); /** * Sets the speed of the rotation @@ -20,7 +20,7 @@ public interface AccessoryWithRotationSpeed { * @return a future that completes when the change is made * @throws Exception when the change cannot be made */ - CompletableFuture setRotationSpeed(Integer speed) throws Exception; + CompletableFuture setRotationSpeed(Double speed) throws Exception; /** * Subscribes to changes in the rotation speed. diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationSpeedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationSpeedCharacteristic.java index 27c715530..6d61c78cc 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationSpeedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationSpeedCharacteristic.java @@ -3,30 +3,52 @@ import io.github.hapjava.characteristics.EventableCharacteristic; import io.github.hapjava.characteristics.ExceptionalConsumer; import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; -import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import io.github.hapjava.characteristics.impl.base.FloatCharacteristic; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.function.Supplier; /** This characteristic describes the rotation speed of a fan. */ -public class RotationSpeedCharacteristic extends IntegerCharacteristic +public class RotationSpeedCharacteristic extends FloatCharacteristic implements EventableCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 100; + public static final double DEFAULT_STEP = 25; public RotationSpeedCharacteristic( - Supplier> getter, - ExceptionalConsumer setter, + double minValue, + double maxValue, + double minStep, + Supplier> getter, + ExceptionalConsumer setter, Consumer subscriber, Runnable unsubscriber) { super( "00000029-0000-1000-8000-0026BB765291", "Rotation Speed", - 0, - 100, + minValue, + maxValue, + minStep, "%", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public RotationSpeedCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + this( + DEFAULT_MIN_VALUE, + DEFAULT_MAX_VALUE, + DEFAULT_STEP, + getter, + setter, + subscriber, + unsubscriber); + } } From 792099dc3e34c409eca51e397a768cb4921de518 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sat, 29 Jan 2022 19:22:53 +0100 Subject: [PATCH 064/138] fix rotation speed data type Signed-off-by: Eugen Freiter --- .../characteristics/impl/fan/RotationSpeedCharacteristic.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationSpeedCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationSpeedCharacteristic.java index 6d61c78cc..d7a1d6929 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationSpeedCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/fan/RotationSpeedCharacteristic.java @@ -14,7 +14,7 @@ public class RotationSpeedCharacteristic extends FloatCharacteristic implements EventableCharacteristic { public static final double DEFAULT_MIN_VALUE = 0; public static final double DEFAULT_MAX_VALUE = 100; - public static final double DEFAULT_STEP = 25; + public static final double DEFAULT_STEP = 1; public RotationSpeedCharacteristic( double minValue, From 80c636714dc1cc70ff91102e7f56ee4a87f073a0 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sat, 29 Jan 2022 19:26:26 +0100 Subject: [PATCH 065/138] update change.md Signed-off-by: Eugen Freiter --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 1977e7fb7..a3a55a290 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,7 @@ # HAP-Java 2.0.1 ## Fixes * Log accessory names instead of futures. [#150](https://github.com/hap-java/HAP-Java/issues/150) +* Fix rotation speed data type (BREAKING API CHANGE). According to HAP specification it must be float # HAP-Java 2.0.0 * major refactoring to support optional characteristics From d0d4f270fb969b20bd5bde0a4a510a59e58a8344 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Thu, 3 Feb 2022 22:42:25 +0100 Subject: [PATCH 066/138] add support for television service Signed-off-by: Eugen Freiter --- CHANGES.md | 25 ++++ .../accessories/InputSourceAccessory.java | 104 +++++++++++++ .../accessories/TelevisionAccessory.java | 123 ++++++++++++++++ .../TelevisionSpeakerAccessory.java | 43 ++++++ .../AccessoryWithActive.java | 38 +++++ .../AccessoryWithClosedCaptions.java | 37 +++++ .../AccessoryWithCurrentMediaState.java | 30 ++++ .../AccessoryWithIdentifier.java | 17 +++ .../AccessoryWithInputDeviceType.java | 29 ++++ .../AccessoryWithPictureMode.java | 39 +++++ .../AccessoryWithPowerMode.java | 16 ++ .../AccessoryWithTargetMediaState.java | 37 +++++ .../AccessoryWithTargetVisibilityState.java | 36 +++++ .../AccessoryWithVolumeControlType.java | 30 ++++ .../AccessoryWithVolumeSelector.java | 20 +++ .../impl/base/StringCharacteristic.java | 80 ++++++++++ .../ActiveIdentifierCharacteristic.java | 29 ++++ .../common/ConfiguredNameCharacteristic.java | 27 ++++ .../impl/common/IdentifierCharacteristic.java | 22 +++ .../impl/common/VersionCharacteristic.java | 2 +- .../CurrentVisibilityStateCharacteristic.java | 27 ++++ .../CurrentVisibilityStateEnum.java | 36 +++++ .../InputDeviceTypeCharacteristic.java | 26 ++++ .../impl/inputsource/InputDeviceTypeEnum.java | 38 +++++ .../InputSourceTypeCharacteristic.java | 26 ++++ .../impl/inputsource/InputSourceTypeEnum.java | 43 ++++++ .../TargetVisibilityStateCharacteristic.java | 29 ++++ .../TargetVisibilityStateEnum.java | 34 +++++ .../ClosedCaptionsCharacteristic.java | 31 ++++ .../impl/television/ClosedCaptionsEnum.java | 35 +++++ .../CurrentMediaStateCharacteristic.java | 28 ++++ .../television/CurrentMediaStateEnum.java | 37 +++++ .../television/PictureModeCharacteristic.java | 30 ++++ .../impl/television/PictureModeEnum.java | 44 ++++++ .../television/PowerModeCharacteristic.java | 20 +++ .../impl/television/PowerModeEnum.java | 35 +++++ .../television/RemoteKeyCharacteristic.java | 22 +++ .../impl/television/RemoteKeyEnum.java | 45 ++++++ .../SleepDiscoveryModeCharacteristic.java | 28 ++++ .../television/SleepDiscoveryModeEnum.java | 35 +++++ .../TargetMediaStateCharacteristic.java | 27 ++++ .../impl/television/TargetMediaStateEnum.java | 36 +++++ .../VolumeControlTypeCharacteristic.java | 28 ++++ .../VolumeControlTypeEnum.java | 36 +++++ .../VolumeSelectorCharacteristic.java | 22 +++ .../televisionspeaker/VolumeSelectorEnum.java | 34 +++++ .../services/impl/AbstractServiceImpl.java | 7 + .../services/impl/InputSourceService.java | 91 ++++++++++++ .../services/impl/TelevisionService.java | 139 ++++++++++++++++++ .../impl/TelevisionSpeakerService.java | 75 ++++++++++ 50 files changed, 1927 insertions(+), 1 deletion(-) create mode 100644 src/main/java/io/github/hapjava/accessories/InputSourceAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/TelevisionAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/TelevisionSpeakerAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithActive.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithClosedCaptions.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentMediaState.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithIdentifier.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithInputDeviceType.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPictureMode.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPowerMode.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetMediaState.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetVisibilityState.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolumeControlType.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolumeSelector.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/ActiveIdentifierCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/ConfiguredNameCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/IdentifierCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/inputsource/CurrentVisibilityStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/inputsource/CurrentVisibilityStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputDeviceTypeCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputDeviceTypeEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputSourceTypeCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputSourceTypeEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/inputsource/TargetVisibilityStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/inputsource/TargetVisibilityStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/ClosedCaptionsCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/ClosedCaptionsEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/CurrentMediaStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/CurrentMediaStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/PictureModeCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/PictureModeEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/PowerModeCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/PowerModeEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/SleepDiscoveryModeCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/SleepDiscoveryModeEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/TargetMediaStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/TargetMediaStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeControlTypeCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeControlTypeEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeSelectorCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeSelectorEnum.java create mode 100644 src/main/java/io/github/hapjava/services/impl/InputSourceService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/TelevisionService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/TelevisionSpeakerService.java diff --git a/CHANGES.md b/CHANGES.md index a3a55a290..bb5668235 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,31 @@ ## Fixes * Log accessory names instead of futures. [#150](https://github.com/hap-java/HAP-Java/issues/150) * Fix rotation speed data type (BREAKING API CHANGE). According to HAP specification it must be float +* Fix UUID of HAP Version characteristic + +## New +* New characteristics: + * Identifier + * Input Device Type + * Input Source Type + * Configured Name + * Current Visibility State + * Target Visibility State + * Sleep Discovery Mode + * Active Identifier + * Closed Captions + * Current Media State + * Target Media State + * Picture Mode + * Power Mode + * Remote Key + * Volume Control Type + * Volume Selector + +* New services + * Input Source + * Television + * Television Speaker # HAP-Java 2.0.0 * major refactoring to support optional characteristics diff --git a/src/main/java/io/github/hapjava/accessories/InputSourceAccessory.java b/src/main/java/io/github/hapjava/accessories/InputSourceAccessory.java new file mode 100644 index 000000000..cfa5ae3d6 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/InputSourceAccessory.java @@ -0,0 +1,104 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.common.IsConfiguredEnum; +import io.github.hapjava.characteristics.impl.inputsource.CurrentVisibilityStateEnum; +import io.github.hapjava.characteristics.impl.inputsource.InputSourceTypeEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.InputSourceService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** Input Source accessory. */ +public interface InputSourceAccessory extends HomekitAccessory { + + /** + * Retrieves configured name of input source. + * + * @return configured name of input source + */ + CompletableFuture getConfiguredName(); + + /** + * Sets the configured name. + * + * @param name configured name + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setConfiguredName(String name) throws Exception; + + /** + * Subscribes to changes in configured name. + * + * @param callback the function to call when the configured name changes. + */ + void subscribeConfiguredName(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the configured name. */ + void unsubscribeConfiguredName(); + + /** + * Retrieves the flag whether input source is configured. + * + * @return a future that will contain the flag . + */ + CompletableFuture isConfigured(); + /** + * set the flag whether input source is configured. + * + * @param state is configured state + * @return a future that completes when the change is made + */ + CompletableFuture setIsConfigured(IsConfiguredEnum state); + + /** + * Subscribes to changes in isConfigured. + * + * @param callback the function to call when the state changes. + */ + void subscribeIsConfigured(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in isConfigured. */ + void unsubscribeIsConfigured(); + + /** + * Retrieves the input source type. + * + * @return a future that will contain the input source type. + */ + CompletableFuture getInputSourceType(); + + /** + * Subscribes to changes in input source type. + * + * @param callback the function to call when the type changes. + */ + void subscribeInputSourceType(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the input source type. */ + void unsubscribeInputSourceType(); + + /** + * Retrieves the current visibility state. + * + * @return a future that will contain the current visibility state. + */ + CompletableFuture getCurrentVisibilityState(); + + /** + * Subscribes to changes in current visibility state. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentVisibilityState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current visibility state. */ + void unsubscribeCurrentVisibilityState(); + + @Override + default Collection getServices() { + return Collections.singleton(new InputSourceService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/TelevisionAccessory.java b/src/main/java/io/github/hapjava/accessories/TelevisionAccessory.java new file mode 100644 index 000000000..3a1a1c515 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/TelevisionAccessory.java @@ -0,0 +1,123 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.television.RemoteKeyEnum; +import io.github.hapjava.characteristics.impl.television.SleepDiscoveryModeEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.TelevisionService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** Television accessory. */ +public interface TelevisionAccessory extends HomekitAccessory { + + /** + * Retrieves the current active state of the TV. + * + * @return a future that will contain the state + */ + CompletableFuture isActive(); + + /** + * Sets the active state of the TV + * + * @param state the state to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setActive(boolean state) throws Exception; + + /** + * Subscribes to changes in the active state of the TV . + * + * @param callback the function to call when the active state changes. + */ + void subscribeActive(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the active state of the TV. */ + void unsubscribeActive(); + + /** + * Retrieves the active identifier + * + * @return a future that will contain the active identifier. + */ + CompletableFuture getActiveIdentifier(); + + /** + * Sets the active identifier + * + * @param identifier the active identifier + * @return a future that completes when the active identifier is changed + * @throws Exception when the active identifier cannot be set + */ + CompletableFuture setActiveIdentifier(Integer identifier) throws Exception; + + /** + * Subscribes to changes in the active identifier. + * + * @param callback the function to call when the active identifier changes. + */ + void subscribeActiveIdentifier(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the active identifier. */ + void unsubscribeActiveIdentifier(); + + /** + * Retrieves configured name. + * + * @return configured name + */ + CompletableFuture getConfiguredName(); + + /** + * Sets the mute status + * + * @param name configured name + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setConfiguredName(String name) throws Exception; + + /** + * Subscribes to changes in mute state. + * + * @param callback the function to call when the state changes. + */ + void subscribeConfiguredName(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the mute state. */ + void unsubscribeConfiguredName(); + + /** + * Sends the remote key. + * + * @param key remote key + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setRemoteKey(RemoteKeyEnum key) throws Exception; + + /** + * Retrieves the sleep discovery mode. + * + * @return a future that will contain the sleep discovery mode . + */ + CompletableFuture getSleepDiscoveryMode(); + + /** + * Subscribes to changes in sleep discovery mode. + * + * @param callback the function to call when the sleep discovery mode changes. + */ + void subscribeSleepDiscoveryMode(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the sleep discovery mode. */ + void unsubscribeSleepDiscoveryMode(); + + @Override + default Collection getServices() { + return Collections.singleton(new TelevisionService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/TelevisionSpeakerAccessory.java b/src/main/java/io/github/hapjava/accessories/TelevisionSpeakerAccessory.java new file mode 100644 index 000000000..28df1437d --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/TelevisionSpeakerAccessory.java @@ -0,0 +1,43 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.TelevisionSpeakerService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** Television speaker accessory. */ +public interface TelevisionSpeakerAccessory extends HomekitAccessory { + + /** + * Retrieves mute status. + * + * @return true if accessory is muted + */ + CompletableFuture isMuted(); + + /** + * Sets the mute status + * + * @param mute true if accessory should be muted + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setMute(boolean mute) throws Exception; + + /** + * Subscribes to changes in mute state. + * + * @param callback the function to call when the state changes. + */ + void subscribeMuteState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the mute state. */ + void unsubscribeMuteState(); + + @Override + default Collection getServices() { + return Collections.singleton(new TelevisionSpeakerService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithActive.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithActive.java new file mode 100644 index 000000000..4ad11be63 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithActive.java @@ -0,0 +1,38 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.common.ActiveEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with active characteristic {@link + * io.github.hapjava.characteristics.impl.common.ActiveCharacteristic}. + */ +public interface AccessoryWithActive { + + /** + * Retrieves the active state (see {@link + * io.github.hapjava.characteristics.impl.common.ActiveEnum} for supported values). + * + * @return a future that will contain the active state + */ + CompletableFuture getActive(); + + /** + * Set the active state (see {@link ActiveEnum} for supported values). + * + * @param active active state + * @return a future that completes when the change is made + */ + CompletableFuture setActive(ActiveEnum active); + + /** + * Subscribes to changes in the active state. + * + * @param callback the function to call when the active state changes. + */ + void subscribeActive(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the active state. */ + void unsubscribeActive(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithClosedCaptions.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithClosedCaptions.java new file mode 100644 index 000000000..c13da3289 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithClosedCaptions.java @@ -0,0 +1,37 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.television.ClosedCaptionsEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with closed captions characteristic {@link + * io.github.hapjava.characteristics.impl.television.ClosedCaptionsCharacteristic}. + */ +public interface AccessoryWithClosedCaptions { + + /** + * Retrieves the closed captions state (see {@link ClosedCaptionsEnum} for supported values). + * + * @return a future that will contain the closed captions + */ + CompletableFuture getClosedCaptions(); + + /** + * Set the closed captions state (see {@link ClosedCaptionsEnum} for supported values). + * + * @param closedCaptions closed captions + * @return a future that completes when the change is made + */ + CompletableFuture setClosedCaptions(ClosedCaptionsEnum closedCaptions); + + /** + * Subscribes to changes in the closed captions. + * + * @param callback the function to call when the closed captions changes. + */ + void subscribeClosedCaptions(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the closed captions. */ + void unsubscribeClosedCaptions(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentMediaState.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentMediaState.java new file mode 100644 index 000000000..22350d9b4 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentMediaState.java @@ -0,0 +1,30 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.television.CurrentMediaStateEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with current media state characteristic {@link + * io.github.hapjava.characteristics.impl.television.CurrentMediaStateCharacteristic}. + */ +public interface AccessoryWithCurrentMediaState { + + /** + * Retrieves the current media state (see {@link + * io.github.hapjava.characteristics.impl.television.CurrentMediaStateEnum} for supported values). + * + * @return a future that will contain the current media state + */ + CompletableFuture getCurrentMediaState(); + + /** + * Subscribes to changes in the current media state. + * + * @param callback the function to call when the current media state changes. + */ + void subscribeCurrentMediaState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current media state. */ + void unsubscribeCurrentMediaState(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithIdentifier.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithIdentifier.java new file mode 100644 index 000000000..b181d4d99 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithIdentifier.java @@ -0,0 +1,17 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with identifier characteristic {@link + * io.github.hapjava.characteristics.impl.common.IdentifierCharacteristic}. + */ +public interface AccessoryWithIdentifier { + + /** + * Retrieves the identifier of service. + * + * @return a future with the identifier + */ + CompletableFuture getIdentifier(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithInputDeviceType.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithInputDeviceType.java new file mode 100644 index 000000000..fca5f5df1 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithInputDeviceType.java @@ -0,0 +1,29 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.inputsource.InputDeviceTypeEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with input devices, e.g. television. {@link + * io.github.hapjava.characteristics.impl.inputsource.InputDeviceTypeCharacteristic} + */ +public interface AccessoryWithInputDeviceType { + + /** + * Retrieves the input device type. See {@link InputDeviceTypeEnum} for supported values. + * + * @return a future with the value + */ + CompletableFuture getInputDeviceType(); + + /** + * Subscribes to changes in input device type. + * + * @param callback the function when the input device type changes + */ + void subscribeInputDeviceType(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes */ + void unsubscribeInputDeviceType(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPictureMode.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPictureMode.java new file mode 100644 index 000000000..b369b98bf --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPictureMode.java @@ -0,0 +1,39 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.television.PictureModeEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with picture mode characteristic {@link + * io.github.hapjava.characteristics.impl.television.PictureModeCharacteristic}. + */ +public interface AccessoryWithPictureMode { + + /** + * Retrieves the picture mode (see {@link + * io.github.hapjava.characteristics.impl.television.PictureModeEnum} for supported values). + * + * @return a future that will contain the picture mode + */ + CompletableFuture getPictureMode(); + + /** + * Set the picture mode (see {@link + * io.github.hapjava.characteristics.impl.television.PictureModeEnum} for supported values). + * + * @param pictureMode picture mode + * @return a future that completes when the change is made + */ + CompletableFuture setPictureMode(PictureModeEnum pictureMode); + + /** + * Subscribes to changes in the picture mode. + * + * @param callback the function to call when the picture mode changes. + */ + void subscribePictureMode(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the picture mode. */ + void unsubscribePictureMode(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPowerMode.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPowerMode.java new file mode 100644 index 000000000..5abb7e6f1 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPowerMode.java @@ -0,0 +1,16 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.impl.television.PowerModeEnum; +import java.util.concurrent.CompletableFuture; + +/** An accessory with power mode. */ +public interface AccessoryWithPowerMode { + + /** + * Set the power mode. See {@link PowerModeEnum} for supported values + * + * @param mode target power mode + * @return a future that completes when the change is made + */ + CompletableFuture setPowerMode(PowerModeEnum mode); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetMediaState.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetMediaState.java new file mode 100644 index 000000000..e1f1a34bd --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetMediaState.java @@ -0,0 +1,37 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.television.TargetMediaStateEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with target media state characteristic {@link + * io.github.hapjava.characteristics.impl.television.TargetMediaStateCharacteristic}. + */ +public interface AccessoryWithTargetMediaState { + + /** + * Retrieves the target media state (see {@link TargetMediaStateEnum} for supported values). + * + * @return a future that will contain the target media state + */ + CompletableFuture getTargetMediaState(); + + /** + * Set the target media state (see {@link TargetMediaStateEnum} for supported values). + * + * @param targetMediaState target media state + * @return a future that completes when the change is made + */ + CompletableFuture setTargetMediaState(TargetMediaStateEnum targetMediaState); + + /** + * Subscribes to changes in the target media state. + * + * @param callback the function to call when the target media state changes. + */ + void subscribeTargetMediaState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the target media state. */ + void unsubscribeTargetMediaState(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetVisibilityState.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetVisibilityState.java new file mode 100644 index 000000000..dcadd1cc5 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetVisibilityState.java @@ -0,0 +1,36 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.inputsource.TargetVisibilityStateEnum; +import java.util.concurrent.CompletableFuture; + +/** accessory with target visibility state characteristics. */ +public interface AccessoryWithTargetVisibilityState { + + /** + * Retrieves the target visibility state. + * + * @return a future that will contain the target visibility state + */ + CompletableFuture getTargetVisibilityState(); + + /** + * Sets the target visibility state + * + * @param state the target visibility state to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setTargetVisibilityState(TargetVisibilityStateEnum state) + throws Exception; + + /** + * Subscribes to changes in t target visibility state. + * + * @param callback the function to call when the target visibility state changes. + */ + void subscribeTargetVisibilityState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the target visibility state. */ + void unsubscribeTargetVisibilityState(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolumeControlType.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolumeControlType.java new file mode 100644 index 000000000..21a6f6c66 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolumeControlType.java @@ -0,0 +1,30 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.televisionspeaker.VolumeControlTypeEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with volume control type {@link + * io.github.hapjava.characteristics.impl.televisionspeaker.VolumeControlTypeCharacteristic} + */ +public interface AccessoryWithVolumeControlType { + + /** + * Retrieves the current volume control type. see {@link VolumeControlTypeEnum} for possible + * values + * + * @return a future that will contain the type. + */ + CompletableFuture getVolumeControlType(); + + /** + * Subscribes to changes in the volume. + * + * @param callback the function to call when the state changes. + */ + void subscribeVolumeControlType(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the volume. */ + void unsubscribeVolumeControlType(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolumeSelector.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolumeSelector.java new file mode 100644 index 000000000..0c2a2890f --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolumeSelector.java @@ -0,0 +1,20 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.impl.televisionspeaker.VolumeSelectorEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with volume selector {@link + * io.github.hapjava.characteristics.impl.televisionspeaker.VolumeSelectorCharacteristic} + */ +public interface AccessoryWithVolumeSelector { + + /** + * Sets the volume selector + * + * @param value the volume selector + * @return a future that completes when the volume selector is changed + * @throws Exception when the volume selector cannot be set + */ + CompletableFuture setVolumeSelector(VolumeSelectorEnum value) throws Exception; +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java new file mode 100644 index 000000000..4373b06c9 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java @@ -0,0 +1,80 @@ +package io.github.hapjava.characteristics.impl.base; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; +import javax.json.JsonObjectBuilder; +import javax.json.JsonString; +import javax.json.JsonValue; + +/** + * A characteristic that provides an string value. + * + * @author Eugen Freiter + */ +public class StringCharacteristic extends BaseCharacteristic { + private final Optional>> getter; + private final Optional> setter; + /** + * Default constructor + * + * @param type a string containing a UUID that indicates the type of characteristic. Apple defines + * a set of these, however implementors can create their own as well. + * @param description a description of the characteristic to be passed to the consuming device. + * @param getter getter to retrieve the value + * @param setter setter for value + * @param subscriber subscriber to subscribe to changes + * @param unsubscriber unsubscriber to unsubscribe from chnages + */ + public StringCharacteristic( + String type, + String description, + Optional>> getter, + Optional> setter, + Optional> subscriber, + Optional unsubscriber) { + super( + type, + "string", + description, + getter.isPresent(), + setter.isPresent(), + subscriber, + unsubscriber); + this.getter = getter; + this.setter = setter; + } + + /** {@inheritDoc} */ + @Override + protected CompletableFuture makeBuilder(int iid) { + return super.makeBuilder(iid); + } + + /** {@inheritDoc} */ + @Override + public String convert(JsonValue jsonValue) { + return ((JsonString) jsonValue).getString(); + } + + /** {@inheritDoc} */ + @Override + public void setValue(String value) throws Exception { + setter.get().accept(value); + } + + /** {@inheritDoc} */ + @Override + protected CompletableFuture getValue() { + return getter.map(stringGetter -> stringGetter.get()).get(); + } + + /** {@inheritDoc} */ + @Override + protected String getDefault() { + return "Unknown"; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveIdentifierCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveIdentifierCharacteristic.java new file mode 100644 index 000000000..587c1b666 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveIdentifierCharacteristic.java @@ -0,0 +1,29 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class ActiveIdentifierCharacteristic extends IntegerCharacteristic { + + public ActiveIdentifierCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000E7-0000-1000-8000-0026BB765291", + "active identifier", + 0, + 1000, + "", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ConfiguredNameCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ConfiguredNameCharacteristic.java new file mode 100644 index 000000000..471d5a368 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ConfiguredNameCharacteristic.java @@ -0,0 +1,27 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.StringCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes a configured name. */ +public class ConfiguredNameCharacteristic extends StringCharacteristic { + + public ConfiguredNameCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000E3-0000-1000-8000-0026BB765291", + "configured name", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/IdentifierCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/IdentifierCharacteristic.java new file mode 100644 index 000000000..4b8f7613c --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/IdentifierCharacteristic.java @@ -0,0 +1,22 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +public class IdentifierCharacteristic extends IntegerCharacteristic { + + public IdentifierCharacteristic(Supplier> getter) { + super( + "000000E6-0000-1000-8000-0026BB765291", + "identifier", + 0, + 1000, + "", + Optional.of(getter), + Optional.empty(), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/VersionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/VersionCharacteristic.java index 038c46c5c..75c8aaf9e 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/VersionCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/VersionCharacteristic.java @@ -10,7 +10,7 @@ public class VersionCharacteristic extends StaticStringCharacteristic { public VersionCharacteristic(Supplier> getter) { super( - "00000023-0000-1000-8000-0026BB765291", + "00000037-0000-1000-8000-0026BB765291", "HAP version", Optional.of(getter), Optional.empty(), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/inputsource/CurrentVisibilityStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/CurrentVisibilityStateCharacteristic.java new file mode 100644 index 000000000..65c358a3f --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/CurrentVisibilityStateCharacteristic.java @@ -0,0 +1,27 @@ +package io.github.hapjava.characteristics.impl.inputsource; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes current visibility state. */ +public class CurrentVisibilityStateCharacteristic + extends EnumCharacteristic { + + public CurrentVisibilityStateCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000135-0000-1000-8000-0026BB765291", + "current visibility state", + CurrentVisibilityStateEnum.values(), + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/inputsource/CurrentVisibilityStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/CurrentVisibilityStateEnum.java new file mode 100644 index 000000000..2159393d8 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/CurrentVisibilityStateEnum.java @@ -0,0 +1,36 @@ +package io.github.hapjava.characteristics.impl.inputsource; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +public enum CurrentVisibilityStateEnum implements CharacteristicEnum { + SHOWN(0), + HIDDEN(1), + UNKNOWN_1(2), + UNKNOWN_2(3); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(CurrentVisibilityStateEnum.values()) + .collect(Collectors.toMap(CurrentVisibilityStateEnum::getCode, t -> t)); + } + + public static CurrentVisibilityStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + CurrentVisibilityStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputDeviceTypeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputDeviceTypeCharacteristic.java new file mode 100644 index 000000000..51a6fbde5 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputDeviceTypeCharacteristic.java @@ -0,0 +1,26 @@ +package io.github.hapjava.characteristics.impl.inputsource; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes input device type. */ +public class InputDeviceTypeCharacteristic extends EnumCharacteristic { + + public InputDeviceTypeCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000DC-0000-1000-8000-0026BB765291", + "input device type", + InputDeviceTypeEnum.values(), + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputDeviceTypeEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputDeviceTypeEnum.java new file mode 100644 index 000000000..5c7337174 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputDeviceTypeEnum.java @@ -0,0 +1,38 @@ +package io.github.hapjava.characteristics.impl.inputsource; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +public enum InputDeviceTypeEnum implements CharacteristicEnum { + OTHER(0), + TV(1), + RECORDING(2), + TUNER(3), + PLAYBACK(4), + AUDIO_SYSTEM(5); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(InputDeviceTypeEnum.values()) + .collect(Collectors.toMap(InputDeviceTypeEnum::getCode, t -> t)); + } + + public static InputDeviceTypeEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + InputDeviceTypeEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputSourceTypeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputSourceTypeCharacteristic.java new file mode 100644 index 000000000..91134891b --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputSourceTypeCharacteristic.java @@ -0,0 +1,26 @@ +package io.github.hapjava.characteristics.impl.inputsource; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes input source type. */ +public class InputSourceTypeCharacteristic extends EnumCharacteristic { + + public InputSourceTypeCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000DB-0000-1000-8000-0026BB765291", + "input source type", + InputSourceTypeEnum.values(), + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputSourceTypeEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputSourceTypeEnum.java new file mode 100644 index 000000000..94545ec18 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputSourceTypeEnum.java @@ -0,0 +1,43 @@ +package io.github.hapjava.characteristics.impl.inputsource; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +public enum InputSourceTypeEnum implements CharacteristicEnum { + OTHER(0), + HOME_SCREEN(1), + TUNER(2), + HDMI(3), + COMPOSITE_VIDEO(4), + S_VIDEO(5), + COMPONENT_VIDEO(6), + DVI(7), + AIRPLAY(8), + USB(9), + APPLICATION(10); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(InputSourceTypeEnum.values()) + .collect(Collectors.toMap(InputSourceTypeEnum::getCode, t -> t)); + } + + public static InputSourceTypeEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + InputSourceTypeEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/inputsource/TargetVisibilityStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/TargetVisibilityStateCharacteristic.java new file mode 100644 index 000000000..ea466d182 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/TargetVisibilityStateCharacteristic.java @@ -0,0 +1,29 @@ +package io.github.hapjava.characteristics.impl.inputsource; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes target visibility state. */ +public class TargetVisibilityStateCharacteristic + extends EnumCharacteristic { + + public TargetVisibilityStateCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000134-0000-1000-8000-0026BB765291", + "target visibility state", + TargetVisibilityStateEnum.values(), + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/inputsource/TargetVisibilityStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/TargetVisibilityStateEnum.java new file mode 100644 index 000000000..6a75b0c30 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/TargetVisibilityStateEnum.java @@ -0,0 +1,34 @@ +package io.github.hapjava.characteristics.impl.inputsource; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +public enum TargetVisibilityStateEnum implements CharacteristicEnum { + SHOWN(0), + HIDDEN(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(TargetVisibilityStateEnum.values()) + .collect(Collectors.toMap(TargetVisibilityStateEnum::getCode, t -> t)); + } + + public static TargetVisibilityStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + TargetVisibilityStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/ClosedCaptionsCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/television/ClosedCaptionsCharacteristic.java new file mode 100644 index 000000000..a963ca222 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/ClosedCaptionsCharacteristic.java @@ -0,0 +1,31 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Characteristic indicates whether the service provides closed captions. See {@link + * ClosedCaptionsEnum} for possible values. + */ +public class ClosedCaptionsCharacteristic extends EnumCharacteristic { + + public ClosedCaptionsCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000DD-0000-1000-8000-0026BB765291", + "closed captions", + ClosedCaptionsEnum.values(), + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/ClosedCaptionsEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/television/ClosedCaptionsEnum.java new file mode 100644 index 000000000..2eb994149 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/ClosedCaptionsEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Disabled” 1 ”Enabled” 2-255 ”Reserved” */ +public enum ClosedCaptionsEnum implements CharacteristicEnum { + DISABLED(0), + ENABLED(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(ClosedCaptionsEnum.values()) + .collect(Collectors.toMap(ClosedCaptionsEnum::getCode, t -> t)); + } + + public static ClosedCaptionsEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + ClosedCaptionsEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/CurrentMediaStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/television/CurrentMediaStateCharacteristic.java new file mode 100644 index 000000000..ca72c4ee9 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/CurrentMediaStateCharacteristic.java @@ -0,0 +1,28 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic indicates current media state. See {@link CurrentMediaStateEnum} for possible + * values. + */ +public class CurrentMediaStateCharacteristic extends EnumCharacteristic { + public CurrentMediaStateCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000E0-0000-1000-8000-0026BB765291", + "current media state", + CurrentMediaStateEnum.values(), + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/CurrentMediaStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/television/CurrentMediaStateEnum.java new file mode 100644 index 000000000..83836c4c6 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/CurrentMediaStateEnum.java @@ -0,0 +1,37 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Play” 1 ”Pause” 2 "Stop" 3 "Unknown" 4-255 ”Reserved” */ +public enum CurrentMediaStateEnum implements CharacteristicEnum { + PLAY(0), + PAUSE(1), + STOP(2), + UNKNOWN(3); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(CurrentMediaStateEnum.values()) + .collect(Collectors.toMap(CurrentMediaStateEnum::getCode, t -> t)); + } + + public static CurrentMediaStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + CurrentMediaStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/PictureModeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/television/PictureModeCharacteristic.java new file mode 100644 index 000000000..7efefa23a --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/PictureModeCharacteristic.java @@ -0,0 +1,30 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic retrieves / sets picture mode. See {@link PictureModeEnum} for possible + * values. + */ +public class PictureModeCharacteristic extends EnumCharacteristic { + public PictureModeCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000E2-0000-1000-8000-0026BB765291", + "picture mode", + PictureModeEnum.values(), + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/PictureModeEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/television/PictureModeEnum.java new file mode 100644 index 000000000..ac387cf65 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/PictureModeEnum.java @@ -0,0 +1,44 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 0 ”Other” 1 ”Standard” 2 "Calibrated" 3 "CalibratedDark" 4 "Vivid" 5 "Game" 6 "Computer" 7 + * "Custom" 8-255 ”Reserved” + */ +public enum PictureModeEnum implements CharacteristicEnum { + OTHER(0), + STANDARD(1), + CALIBRATED(2), + CALIBRATED_DARK(3), + VIVID(4), + GAME(5), + COMPUTER(6), + CUSTOM(7); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(PictureModeEnum.values()) + .collect(Collectors.toMap(PictureModeEnum::getCode, t -> t)); + } + + public static PictureModeEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + PictureModeEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/PowerModeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/television/PowerModeCharacteristic.java new file mode 100644 index 000000000..cebbe4124 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/PowerModeCharacteristic.java @@ -0,0 +1,20 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; + +/** characteristic sets power mode. See {@link PowerModeEnum} for possible values. */ +public class PowerModeCharacteristic extends EnumCharacteristic { + + public PowerModeCharacteristic(ExceptionalConsumer setter) { + super( + "000000DF-0000-1000-8000-0026BB765291", + "power mode selection", + PowerModeEnum.values(), + Optional.empty(), + Optional.of(setter), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/PowerModeEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/television/PowerModeEnum.java new file mode 100644 index 000000000..a171ba135 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/PowerModeEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Show” 1 ”Hide” 2-255 ”Reserved” */ +public enum PowerModeEnum implements CharacteristicEnum { + SHOW(0), + HIDE(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(PowerModeEnum.values()) + .collect(Collectors.toMap(PowerModeEnum::getCode, t -> t)); + } + + public static PowerModeEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + PowerModeEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyCharacteristic.java new file mode 100644 index 000000000..fe028d10d --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyCharacteristic.java @@ -0,0 +1,22 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; + +/** + * This characteristic sends information about pressed key on tv remote. See {@link RemoteKeyEnum} + * for possible values. + */ +public class RemoteKeyCharacteristic extends EnumCharacteristic { + public RemoteKeyCharacteristic(ExceptionalConsumer setter) { + super( + "000000E1-0000-1000-8000-0026BB765291", + "remote key", + RemoteKeyEnum.values(), + Optional.empty(), + Optional.of(setter), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyEnum.java new file mode 100644 index 000000000..443c23644 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyEnum.java @@ -0,0 +1,45 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +public enum RemoteKeyEnum implements CharacteristicEnum { + REWIND(0), + FAST_FORWARD(1), + NEXT_TRACK(2), + PREV_TRACK(3), + ARROW_UP(4), + ARROW_DOWN(5), + ARROW_LEFT(6), + ARROW_RIGHT(7), + SELECT(8), + BACK(9), + EXIT(10), + PLAY_PAUSE(11), + INFO(115); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(RemoteKeyEnum.values()) + .collect(Collectors.toMap(RemoteKeyEnum::getCode, t -> t)); + } + + public static RemoteKeyEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + RemoteKeyEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/SleepDiscoveryModeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/television/SleepDiscoveryModeCharacteristic.java new file mode 100644 index 000000000..2a7f41829 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/SleepDiscoveryModeCharacteristic.java @@ -0,0 +1,28 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic indicates whether the service can be discovered. See {@link + * SleepDiscoveryModeEnum} for possible values. + */ +public class SleepDiscoveryModeCharacteristic extends EnumCharacteristic { + public SleepDiscoveryModeCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000E8-0000-1000-8000-0026BB765291", + "sleep discovery mode", + SleepDiscoveryModeEnum.values(), + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/SleepDiscoveryModeEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/television/SleepDiscoveryModeEnum.java new file mode 100644 index 000000000..da1377b9a --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/SleepDiscoveryModeEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Not discoverable” 1 ”Always discoverable” 2-255 ”Reserved” */ +public enum SleepDiscoveryModeEnum implements CharacteristicEnum { + NOT_DISCOVERABLE(0), + ALWAYS_DISCOVERABLE(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(SleepDiscoveryModeEnum.values()) + .collect(Collectors.toMap(SleepDiscoveryModeEnum::getCode, t -> t)); + } + + public static SleepDiscoveryModeEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + SleepDiscoveryModeEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/TargetMediaStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/television/TargetMediaStateCharacteristic.java new file mode 100644 index 000000000..8909bb1ea --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/TargetMediaStateCharacteristic.java @@ -0,0 +1,27 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic sets media state. See {@link TargetMediaStateEnum} for possible values. */ +public class TargetMediaStateCharacteristic extends EnumCharacteristic { + public TargetMediaStateCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000137-0000-1000-8000-0026BB765291", + "current media state", + TargetMediaStateEnum.values(), + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/TargetMediaStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/television/TargetMediaStateEnum.java new file mode 100644 index 000000000..b6f6633ff --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/TargetMediaStateEnum.java @@ -0,0 +1,36 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Play” 1 ”Pause” 2 "Stop" 3-255 ”Reserved” */ +public enum TargetMediaStateEnum implements CharacteristicEnum { + PLAY(0), + PAUSE(1), + STOP(2); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(TargetMediaStateEnum.values()) + .collect(Collectors.toMap(TargetMediaStateEnum::getCode, t -> t)); + } + + public static TargetMediaStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + TargetMediaStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeControlTypeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeControlTypeCharacteristic.java new file mode 100644 index 000000000..c6d18a858 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeControlTypeCharacteristic.java @@ -0,0 +1,28 @@ +package io.github.hapjava.characteristics.impl.televisionspeaker; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic indicates volume control type. See {@link VolumeControlTypeEnum} for possible + * values. + */ +public class VolumeControlTypeCharacteristic extends EnumCharacteristic { + public VolumeControlTypeCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000E9-0000-1000-8000-0026BB765291", + "volume control type", + VolumeControlTypeEnum.values(), + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeControlTypeEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeControlTypeEnum.java new file mode 100644 index 000000000..dfeb24636 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeControlTypeEnum.java @@ -0,0 +1,36 @@ +package io.github.hapjava.characteristics.impl.televisionspeaker; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +public enum VolumeControlTypeEnum implements CharacteristicEnum { + NONE(0), + RELATIVE(1), + RELATIVE_WITH_CURRENT(2), + ABSOLUTE(3); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(VolumeControlTypeEnum.values()) + .collect(Collectors.toMap(VolumeControlTypeEnum::getCode, t -> t)); + } + + public static VolumeControlTypeEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + VolumeControlTypeEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeSelectorCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeSelectorCharacteristic.java new file mode 100644 index 000000000..8f2f84e7b --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeSelectorCharacteristic.java @@ -0,0 +1,22 @@ +package io.github.hapjava.characteristics.impl.televisionspeaker; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; + +/** + * This characteristic sends information about volume changes. See {@link VolumeSelectorEnum} for + * possible values. + */ +public class VolumeSelectorCharacteristic extends EnumCharacteristic { + public VolumeSelectorCharacteristic(ExceptionalConsumer setter) { + super( + "000000EA-0000-1000-8000-0026BB765291", + "volume selector", + VolumeSelectorEnum.values(), + Optional.empty(), + Optional.of(setter), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeSelectorEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeSelectorEnum.java new file mode 100644 index 000000000..63bc60e50 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeSelectorEnum.java @@ -0,0 +1,34 @@ +package io.github.hapjava.characteristics.impl.televisionspeaker; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +public enum VolumeSelectorEnum implements CharacteristicEnum { + INCREMENT(0), + DECREMENT(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(VolumeSelectorEnum.values()) + .collect(Collectors.toMap(VolumeSelectorEnum::getCode, t -> t)); + } + + public static VolumeSelectorEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + VolumeSelectorEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java b/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java index 25a44099d..2848d367e 100644 --- a/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java +++ b/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java @@ -5,14 +5,18 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; abstract class AbstractServiceImpl implements Service { private final String type; private final List characteristics = new LinkedList<>(); private final List linkedServices = new LinkedList<>(); + private static final Logger logger = LoggerFactory.getLogger(AbstractServiceImpl.class); /** @param type unique UUID of the service according to HAP specification. */ public AbstractServiceImpl(String type) { + logger.info("new service:" + type); this.type = type; } @@ -28,6 +32,7 @@ public String getType() { @Override public List getLinkedServices() { + logger.info(" all linked service" + this.linkedServices); return Collections.unmodifiableList(linkedServices); } @@ -37,6 +42,8 @@ public void addCharacteristic(Characteristic characteristic) { @Override public void addLinkedService(Service service) { + logger.info("add linked service" + service); this.linkedServices.add(service); + logger.info(" all linked service" + this.linkedServices); } } diff --git a/src/main/java/io/github/hapjava/services/impl/InputSourceService.java b/src/main/java/io/github/hapjava/services/impl/InputSourceService.java new file mode 100644 index 000000000..4af2736a2 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/InputSourceService.java @@ -0,0 +1,91 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.InputSourceAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithIdentifier; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithInputDeviceType; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithTargetVisibilityState; +import io.github.hapjava.characteristics.impl.common.ConfiguredNameCharacteristic; +import io.github.hapjava.characteristics.impl.common.IdentifierCharacteristic; +import io.github.hapjava.characteristics.impl.common.IsConfiguredCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; +import io.github.hapjava.characteristics.impl.inputsource.CurrentVisibilityStateCharacteristic; +import io.github.hapjava.characteristics.impl.inputsource.InputDeviceTypeCharacteristic; +import io.github.hapjava.characteristics.impl.inputsource.InputSourceTypeCharacteristic; +import io.github.hapjava.characteristics.impl.inputsource.TargetVisibilityStateCharacteristic; + +/** This service describes a input source. */ +public class InputSourceService extends AbstractServiceImpl { + + public InputSourceService( + ConfiguredNameCharacteristic configuredNameCharacteristic, + InputSourceTypeCharacteristic inputSourceTypeCharacteristic, + IsConfiguredCharacteristic isConfiguredCharacteristic, + CurrentVisibilityStateCharacteristic currentVisibilityStateCharacteristic) { + super("000000D9-0000-1000-8000-0026BB765291"); + addCharacteristic(configuredNameCharacteristic); + addCharacteristic(inputSourceTypeCharacteristic); + addCharacteristic(isConfiguredCharacteristic); + addCharacteristic(currentVisibilityStateCharacteristic); + } + + public InputSourceService(InputSourceAccessory accessory) { + this( + new ConfiguredNameCharacteristic( + accessory::getConfiguredName, + accessory::setConfiguredName, + accessory::subscribeConfiguredName, + accessory::unsubscribeConfiguredName), + new InputSourceTypeCharacteristic( + accessory::getInputSourceType, + accessory::subscribeInputSourceType, + accessory::unsubscribeInputSourceType), + new IsConfiguredCharacteristic( + accessory::isConfigured, + accessory::setIsConfigured, + accessory::subscribeIsConfigured, + accessory::unsubscribeIsConfigured), + new CurrentVisibilityStateCharacteristic( + accessory::getCurrentVisibilityState, + accessory::subscribeCurrentVisibilityState, + accessory::unsubscribeCurrentVisibilityState)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithIdentifier) { + addOptionalCharacteristic( + new IdentifierCharacteristic(((AccessoryWithIdentifier) accessory)::getIdentifier)); + } + if (accessory instanceof AccessoryWithInputDeviceType) { + addOptionalCharacteristic( + new InputDeviceTypeCharacteristic( + ((AccessoryWithInputDeviceType) accessory)::getInputDeviceType, + ((AccessoryWithInputDeviceType) accessory)::subscribeInputDeviceType, + ((AccessoryWithInputDeviceType) accessory)::unsubscribeInputDeviceType)); + } + if (accessory instanceof AccessoryWithTargetVisibilityState) { + addOptionalCharacteristic( + new TargetVisibilityStateCharacteristic( + ((AccessoryWithTargetVisibilityState) accessory)::getTargetVisibilityState, + ((AccessoryWithTargetVisibilityState) accessory)::setTargetVisibilityState, + ((AccessoryWithTargetVisibilityState) accessory)::subscribeTargetVisibilityState, + ((AccessoryWithTargetVisibilityState) accessory)::unsubscribeTargetVisibilityState)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(IdentifierCharacteristic identifier) { + addCharacteristic(identifier); + } + + public void addOptionalCharacteristic(InputDeviceTypeCharacteristic inputDeviceType) { + addCharacteristic(inputDeviceType); + } + + public void addOptionalCharacteristic(TargetVisibilityStateCharacteristic targetVisibilityState) { + addCharacteristic(targetVisibilityState); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/TelevisionService.java b/src/main/java/io/github/hapjava/services/impl/TelevisionService.java new file mode 100644 index 000000000..8846cffe1 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/TelevisionService.java @@ -0,0 +1,139 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.TelevisionAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithBrightness; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithClosedCaptions; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithCurrentMediaState; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithPictureMode; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithPowerMode; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithTargetMediaState; +import io.github.hapjava.characteristics.impl.common.ActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.ActiveEnum; +import io.github.hapjava.characteristics.impl.common.ActiveIdentifierCharacteristic; +import io.github.hapjava.characteristics.impl.common.ConfiguredNameCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; +import io.github.hapjava.characteristics.impl.lightbulb.BrightnessCharacteristic; +import io.github.hapjava.characteristics.impl.television.ClosedCaptionsCharacteristic; +import io.github.hapjava.characteristics.impl.television.CurrentMediaStateCharacteristic; +import io.github.hapjava.characteristics.impl.television.PictureModeCharacteristic; +import io.github.hapjava.characteristics.impl.television.PowerModeCharacteristic; +import io.github.hapjava.characteristics.impl.television.RemoteKeyCharacteristic; +import io.github.hapjava.characteristics.impl.television.SleepDiscoveryModeCharacteristic; +import io.github.hapjava.characteristics.impl.television.TargetMediaStateCharacteristic; + +/** This service describes a television. */ +public class TelevisionService extends AbstractServiceImpl { + + public TelevisionService( + ActiveCharacteristic activeCharacteristic, + ActiveIdentifierCharacteristic activeIdentifierCharacteristic, + ConfiguredNameCharacteristic configuredNameCharacteristic, + RemoteKeyCharacteristic remoteKeyCharacteristic, + SleepDiscoveryModeCharacteristic sleepDiscoveryModeCharacteristic) { + super("000000D8-0000-1000-8000-0026BB765291"); + addCharacteristic(activeCharacteristic); + addCharacteristic(activeIdentifierCharacteristic); + addCharacteristic(configuredNameCharacteristic); + addCharacteristic(remoteKeyCharacteristic); + addCharacteristic(sleepDiscoveryModeCharacteristic); + } + + public TelevisionService(TelevisionAccessory accessory) { + this( + new ActiveCharacteristic( + () -> accessory.isActive().thenApply(s -> s ? ActiveEnum.ACTIVE : ActiveEnum.INACTIVE), + (v) -> accessory.setActive(v == ActiveEnum.ACTIVE), + accessory::subscribeActive, + accessory::unsubscribeActive), + new ActiveIdentifierCharacteristic( + accessory::getActiveIdentifier, + accessory::setActiveIdentifier, + accessory::subscribeActiveIdentifier, + accessory::unsubscribeActiveIdentifier), + new ConfiguredNameCharacteristic( + accessory::getConfiguredName, + accessory::setConfiguredName, + accessory::subscribeConfiguredName, + accessory::unsubscribeConfiguredName), + new RemoteKeyCharacteristic(accessory::setRemoteKey), + new SleepDiscoveryModeCharacteristic( + accessory::getSleepDiscoveryMode, + accessory::subscribeSleepDiscoveryMode, + accessory::unsubscribeSleepDiscoveryMode)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithBrightness) { + addOptionalCharacteristic( + new BrightnessCharacteristic( + ((AccessoryWithBrightness) accessory)::getBrightness, + ((AccessoryWithBrightness) accessory)::setBrightness, + ((AccessoryWithBrightness) accessory)::subscribeBrightness, + ((AccessoryWithBrightness) accessory)::unsubscribeBrightness)); + } + if (accessory instanceof AccessoryWithPowerMode) { + addOptionalCharacteristic( + new PowerModeCharacteristic(((AccessoryWithPowerMode) accessory)::setPowerMode)); + } + if (accessory instanceof AccessoryWithClosedCaptions) { + addOptionalCharacteristic( + new ClosedCaptionsCharacteristic( + ((AccessoryWithClosedCaptions) accessory)::getClosedCaptions, + ((AccessoryWithClosedCaptions) accessory)::setClosedCaptions, + ((AccessoryWithClosedCaptions) accessory)::subscribeClosedCaptions, + ((AccessoryWithClosedCaptions) accessory)::unsubscribeClosedCaptions)); + } + if (accessory instanceof AccessoryWithCurrentMediaState) { + addOptionalCharacteristic( + new CurrentMediaStateCharacteristic( + ((AccessoryWithCurrentMediaState) accessory)::getCurrentMediaState, + ((AccessoryWithCurrentMediaState) accessory)::subscribeCurrentMediaState, + ((AccessoryWithCurrentMediaState) accessory)::unsubscribeCurrentMediaState)); + } + if (accessory instanceof AccessoryWithTargetMediaState) { + addOptionalCharacteristic( + new TargetMediaStateCharacteristic( + ((AccessoryWithTargetMediaState) accessory)::getTargetMediaState, + ((AccessoryWithTargetMediaState) accessory)::setTargetMediaState, + ((AccessoryWithTargetMediaState) accessory)::subscribeTargetMediaState, + ((AccessoryWithTargetMediaState) accessory)::unsubscribeTargetMediaState)); + } + if (accessory instanceof AccessoryWithPictureMode) { + addOptionalCharacteristic( + new PictureModeCharacteristic( + ((AccessoryWithPictureMode) accessory)::getPictureMode, + ((AccessoryWithPictureMode) accessory)::setPictureMode, + ((AccessoryWithPictureMode) accessory)::subscribePictureMode, + ((AccessoryWithPictureMode) accessory)::unsubscribePictureMode)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(PowerModeCharacteristic powerMode) { + addCharacteristic(powerMode); + } + + public void addOptionalCharacteristic(ClosedCaptionsCharacteristic closedCaptions) { + addCharacteristic(closedCaptions); + } + + public void addOptionalCharacteristic(CurrentMediaStateCharacteristic currentMediaState) { + addCharacteristic(currentMediaState); + } + + public void addOptionalCharacteristic(TargetMediaStateCharacteristic targetMediaState) { + addCharacteristic(targetMediaState); + } + + public void addOptionalCharacteristic(PictureModeCharacteristic pictureMode) { + addCharacteristic(pictureMode); + } + + public void addOptionalCharacteristic(BrightnessCharacteristic brightness) { + addCharacteristic(brightness); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/TelevisionSpeakerService.java b/src/main/java/io/github/hapjava/services/impl/TelevisionSpeakerService.java new file mode 100644 index 000000000..b53fb81bd --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/TelevisionSpeakerService.java @@ -0,0 +1,75 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.TelevisionSpeakerAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithActive; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithVolume; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithVolumeControlType; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithVolumeSelector; +import io.github.hapjava.characteristics.impl.audio.MuteCharacteristic; +import io.github.hapjava.characteristics.impl.audio.VolumeCharacteristic; +import io.github.hapjava.characteristics.impl.common.ActiveCharacteristic; +import io.github.hapjava.characteristics.impl.televisionspeaker.VolumeControlTypeCharacteristic; +import io.github.hapjava.characteristics.impl.televisionspeaker.VolumeSelectorCharacteristic; + +/** This service describes a television. */ +public class TelevisionSpeakerService extends AbstractServiceImpl { + + public TelevisionSpeakerService(MuteCharacteristic muteCharacteristic) { + super("00000113-0000-1000-8000-0026BB765291"); + addCharacteristic(muteCharacteristic); + } + + public TelevisionSpeakerService(TelevisionSpeakerAccessory accessory) { + this( + new MuteCharacteristic( + accessory::isMuted, + accessory::setMute, + accessory::subscribeMuteState, + accessory::unsubscribeMuteState)); + if (accessory instanceof AccessoryWithActive) { + addOptionalCharacteristic( + new ActiveCharacteristic( + ((AccessoryWithActive) accessory)::getActive, + ((AccessoryWithActive) accessory)::setActive, + ((AccessoryWithActive) accessory)::subscribeActive, + ((AccessoryWithActive) accessory)::unsubscribeActive)); + } + if (accessory instanceof AccessoryWithVolume) { + addOptionalCharacteristic( + new VolumeCharacteristic( + ((AccessoryWithVolume) accessory)::getVolume, + ((AccessoryWithVolume) accessory)::setVolume, + ((AccessoryWithVolume) accessory)::subscribeVolume, + ((AccessoryWithVolume) accessory)::unsubscribeVolume)); + } + if (accessory instanceof AccessoryWithVolumeSelector) { + addOptionalCharacteristic( + new VolumeSelectorCharacteristic( + ((AccessoryWithVolumeSelector) accessory)::setVolumeSelector)); + } + if (accessory instanceof AccessoryWithVolumeControlType) { + addOptionalCharacteristic( + new VolumeControlTypeCharacteristic( + ((AccessoryWithVolumeControlType) accessory)::getVolumeControlType, + ((AccessoryWithVolumeControlType) accessory)::subscribeVolumeControlType, + ((AccessoryWithVolumeControlType) accessory)::unsubscribeVolumeControlType)); + ; + } + } + + public void addOptionalCharacteristic(ActiveCharacteristic activeCharacteristic) { + addCharacteristic(activeCharacteristic); + } + + public void addOptionalCharacteristic(VolumeCharacteristic volume) { + addCharacteristic(volume); + } + + public void addOptionalCharacteristic(VolumeSelectorCharacteristic volumeSelector) { + addCharacteristic(volumeSelector); + } + + public void addOptionalCharacteristic(VolumeControlTypeCharacteristic volumeControlType) { + addCharacteristic(volumeControlType); + } +} From 18f6442f837881ab0bb4686f6f5533be87a95de9 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Thu, 3 Feb 2022 22:46:26 +0100 Subject: [PATCH 067/138] update readme Signed-off-by: Eugen Freiter --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 03fb0aaba..ba3cc6e7e 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ read the [Javadoc](https://hap-java.github.io/HAP-Java/apidocs/index.html) Supported HomeKit Accessories ========= -Current implementation fully supports 38 HomeKit accessory/services. +Current implementation fully supports 41 HomeKit accessory/services. | HomeKit Accessory & Service type | Supported by Java-HAP | |--------------------|--------------------| @@ -52,6 +52,7 @@ Current implementation fully supports 38 HomeKit accessory/services. | Heater Cooler | :white_check_mark: | | Humidifier Dehumidifier | :white_check_mark: | | Humidity Sensor | :white_check_mark: | +| Input Source | :white_check_mark: | | Irrigation System | :white_check_mark: | | Leak Sensor | :white_check_mark: | | Light Bulb | :white_check_mark: | @@ -72,6 +73,8 @@ Current implementation fully supports 38 HomeKit accessory/services. | Switch | :white_check_mark: | | Target Control | :x: | | Target Control Management | :x: | +| Television | :white_check_mark: | +| Television Speaker | :white_check_mark: | | Temperature Sensor | :white_check_mark: | | Thermostat | :white_check_mark: | | Valve | :white_check_mark: | From 4f1404d714fab411a4f9e9895dcb914d3a9db3bd Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Fri, 4 Feb 2022 14:02:39 +0100 Subject: [PATCH 068/138] add smart speaker Signed-off-by: Eugen Freiter --- .../accessories/SmartSpeakerAccessory.java | 62 ++++++++++++ .../accessories/TelevisionAccessory.java | 8 +- .../AccessoryWithAirPlayEnable.java | 37 ++++++++ .../AccessoryWithConfiguredName.java | 34 +++++++ .../AccessoryWithMute.java | 37 ++++++++ .../common/AirPlayEnableCharacteristic.java | 32 +++++++ .../hapjava/services/impl/DoorService.java | 14 +++ .../services/impl/SmartSpeakerService.java | 95 +++++++++++++++++++ 8 files changed, 315 insertions(+), 4 deletions(-) create mode 100644 src/main/java/io/github/hapjava/accessories/SmartSpeakerAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithAirPlayEnable.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithConfiguredName.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithMute.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/AirPlayEnableCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/services/impl/SmartSpeakerService.java diff --git a/src/main/java/io/github/hapjava/accessories/SmartSpeakerAccessory.java b/src/main/java/io/github/hapjava/accessories/SmartSpeakerAccessory.java new file mode 100644 index 000000000..9f70af2be --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/SmartSpeakerAccessory.java @@ -0,0 +1,62 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.television.CurrentMediaStateEnum; +import io.github.hapjava.characteristics.impl.television.TargetMediaStateEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.SmartSpeakerService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** Smart Speaker accessory. */ +public interface SmartSpeakerAccessory extends HomekitAccessory { + + /** + * Retrieves the current media state (see {@link + * io.github.hapjava.characteristics.impl.television.CurrentMediaStateEnum} for supported values). + * + * @return a future that will contain the current media state + */ + CompletableFuture getCurrentMediaState(); + + /** + * Subscribes to changes in the current media state. + * + * @param callback the function to call when the current media state changes. + */ + void subscribeCurrentMediaState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current media state. */ + void unsubscribeCurrentMediaState(); + + /** + * Retrieves the target media state (see {@link TargetMediaStateEnum} for supported values). + * + * @return a future that will contain the target media state + */ + CompletableFuture getTargetMediaState(); + + /** + * Set the target media state (see {@link TargetMediaStateEnum} for supported values). + * + * @param targetMediaState target media state + * @return a future that completes when the change is made + */ + CompletableFuture setTargetMediaState(TargetMediaStateEnum targetMediaState); + + /** + * Subscribes to changes in the target media state. + * + * @param callback the function to call when the target media state changes. + */ + void subscribeTargetMediaState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the target media state. */ + void unsubscribeTargetMediaState(); + + @Override + default Collection getServices() { + return Collections.singleton(new SmartSpeakerService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/TelevisionAccessory.java b/src/main/java/io/github/hapjava/accessories/TelevisionAccessory.java index 3a1a1c515..b51efe319 100644 --- a/src/main/java/io/github/hapjava/accessories/TelevisionAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/TelevisionAccessory.java @@ -72,7 +72,7 @@ public interface TelevisionAccessory extends HomekitAccessory { CompletableFuture getConfiguredName(); /** - * Sets the mute status + * Sets the configured name * * @param name configured name * @return a future that completes when the change is made @@ -81,13 +81,13 @@ public interface TelevisionAccessory extends HomekitAccessory { CompletableFuture setConfiguredName(String name) throws Exception; /** - * Subscribes to changes in mute state. + * Subscribes to changes in configured name. * - * @param callback the function to call when the state changes. + * @param callback the function to call when the configureed name changes. */ void subscribeConfiguredName(HomekitCharacteristicChangeCallback callback); - /** Unsubscribes from changes in the mute state. */ + /** Unsubscribes from changes in the configured name state. */ void unsubscribeConfiguredName(); /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithAirPlayEnable.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithAirPlayEnable.java new file mode 100644 index 000000000..76b90b43f --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithAirPlayEnable.java @@ -0,0 +1,37 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with AirPlay enable characteristic {@link + * io.github.hapjava.characteristics.impl.common.AirPlayEnableCharacteristic}. + */ +public interface AccessoryWithAirPlayEnable { + + /** + * Retrieves the AirPlay enable state + * + * @return a future that will contain the AirPlay enable state. + */ + CompletableFuture getAirPlayEnable(); + + /** + * Sets the AirPlay enable state + * + * @param state AirPlay enable state + * @return a future that completes when the AirPlay enable is changed + * @throws Exception when the AirPlay enable cannot be set + */ + CompletableFuture setAirPlayEnable(Integer state) throws Exception; + + /** + * Subscribes to changes in the AirPlay enable state. + * + * @param callback the function to call when the AirPlay enable state changes. + */ + void subscribeAirPlayEnable(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the AirPlay enable state. */ + void unsubscribeAirPlayEnable(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithConfiguredName.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithConfiguredName.java new file mode 100644 index 000000000..0c9528130 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithConfiguredName.java @@ -0,0 +1,34 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** Accessory with configured name. */ +public interface AccessoryWithConfiguredName { + + /** + * Retrieves configured name. + * + * @return configured name + */ + CompletableFuture getConfiguredName(); + + /** + * Sets the configured name + * + * @param name configured name + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setConfiguredName(String name) throws Exception; + + /** + * Subscribes to changes in configured name. + * + * @param callback the function to call when the configureed name changes. + */ + void subscribeConfiguredName(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the configured name state. */ + void unsubscribeConfiguredName(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithMute.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithMute.java new file mode 100644 index 000000000..a0bb08579 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithMute.java @@ -0,0 +1,37 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with mute characteristic {@link + * io.github.hapjava.characteristics.impl.audio.MuteCharacteristic}. + */ +public interface AccessoryWithMute { + + /** + * Retrieves mute status. + * + * @return true if accessory is muted + */ + CompletableFuture isMuted(); + + /** + * Sets the mute status + * + * @param mute true if accessory should be muted + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setMute(boolean mute) throws Exception; + + /** + * Subscribes to changes in mute state. + * + * @param callback the function to call when the state changes. + */ + void subscribeMuteState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the mute state. */ + void unsubscribeMuteState(); +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/AirPlayEnableCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/AirPlayEnableCharacteristic.java new file mode 100644 index 000000000..6158f13be --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/AirPlayEnableCharacteristic.java @@ -0,0 +1,32 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** characteristic to control air play. */ +public class AirPlayEnableCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + public AirPlayEnableCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000025B-0000-1000-8000-0026BB765291", + "AirPlay enable", + 0, + 1, + "", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/DoorService.java b/src/main/java/io/github/hapjava/services/impl/DoorService.java index c4c3b40b3..51f956f5f 100644 --- a/src/main/java/io/github/hapjava/services/impl/DoorService.java +++ b/src/main/java/io/github/hapjava/services/impl/DoorService.java @@ -2,8 +2,10 @@ import io.github.hapjava.accessories.DoorAccessory; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithHoldPosition; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithMute; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithObstructionDetection; +import io.github.hapjava.characteristics.impl.audio.MuteCharacteristic; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.ObstructionDetectedCharacteristic; import io.github.hapjava.characteristics.impl.windowcovering.CurrentPositionCharacteristic; @@ -53,6 +55,14 @@ public DoorService(DoorAccessory accessory) { ((AccessoryWithObstructionDetection) accessory)::subscribeObstructionDetected, ((AccessoryWithObstructionDetection) accessory)::unsubscribeObstructionDetected)); } + if (accessory instanceof AccessoryWithMute) { + addOptionalCharacteristic( + new MuteCharacteristic( + ((AccessoryWithMute) accessory)::isMuted, + ((AccessoryWithMute) accessory)::setMute, + ((AccessoryWithMute) accessory)::subscribeMuteState, + ((AccessoryWithMute) accessory)::unsubscribeMuteState)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { @@ -67,4 +77,8 @@ public void addOptionalCharacteristic( ObstructionDetectedCharacteristic obstructionDetectedCharacteristic) { addCharacteristic(obstructionDetectedCharacteristic); } + + public void addOptionalCharacteristic(MuteCharacteristic muteCharacteristic) { + addCharacteristic(muteCharacteristic); + } } diff --git a/src/main/java/io/github/hapjava/services/impl/SmartSpeakerService.java b/src/main/java/io/github/hapjava/services/impl/SmartSpeakerService.java new file mode 100644 index 000000000..e5f57eff4 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/SmartSpeakerService.java @@ -0,0 +1,95 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.SmartSpeakerAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithAirPlayEnable; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithConfiguredName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithMute; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithVolume; +import io.github.hapjava.characteristics.impl.audio.MuteCharacteristic; +import io.github.hapjava.characteristics.impl.audio.VolumeCharacteristic; +import io.github.hapjava.characteristics.impl.common.AirPlayEnableCharacteristic; +import io.github.hapjava.characteristics.impl.common.ConfiguredNameCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; +import io.github.hapjava.characteristics.impl.television.CurrentMediaStateCharacteristic; +import io.github.hapjava.characteristics.impl.television.TargetMediaStateCharacteristic; + +/** A smart speaker service can be used to control the audio output settings on a speaker device. */ +public class SmartSpeakerService extends AbstractServiceImpl { + + public SmartSpeakerService( + CurrentMediaStateCharacteristic currentMediaStateCharacteristic, + TargetMediaStateCharacteristic targetMediaStateCharacteristic) { + super("00000228-0000-1000-8000-0026BB765291"); + addCharacteristic(currentMediaStateCharacteristic); + addCharacteristic(targetMediaStateCharacteristic); + } + + public SmartSpeakerService(SmartSpeakerAccessory accessory) { + this( + new CurrentMediaStateCharacteristic( + accessory::getCurrentMediaState, + accessory::subscribeCurrentMediaState, + accessory::unsubscribeCurrentMediaState), + new TargetMediaStateCharacteristic( + accessory::getTargetMediaState, + accessory::setTargetMediaState, + accessory::subscribeTargetMediaState, + accessory::unsubscribeTargetMediaState)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithVolume) { + addOptionalCharacteristic( + new VolumeCharacteristic( + ((AccessoryWithVolume) accessory)::getVolume, + ((AccessoryWithVolume) accessory)::setVolume, + ((AccessoryWithVolume) accessory)::subscribeVolume, + ((AccessoryWithVolume) accessory)::unsubscribeVolume)); + } + if (accessory instanceof AccessoryWithConfiguredName) { + addOptionalCharacteristic( + new ConfiguredNameCharacteristic( + ((AccessoryWithConfiguredName) accessory)::getConfiguredName, + ((AccessoryWithConfiguredName) accessory)::setConfiguredName, + ((AccessoryWithConfiguredName) accessory)::subscribeConfiguredName, + ((AccessoryWithConfiguredName) accessory)::unsubscribeConfiguredName)); + } + if (accessory instanceof AccessoryWithMute) { + addOptionalCharacteristic( + new MuteCharacteristic( + ((AccessoryWithMute) accessory)::isMuted, + ((AccessoryWithMute) accessory)::setMute, + ((AccessoryWithMute) accessory)::subscribeMuteState, + ((AccessoryWithMute) accessory)::unsubscribeMuteState)); + } + if (accessory instanceof AccessoryWithAirPlayEnable) { + addOptionalCharacteristic( + new AirPlayEnableCharacteristic( + ((AccessoryWithAirPlayEnable) accessory)::getAirPlayEnable, + ((AccessoryWithAirPlayEnable) accessory)::setAirPlayEnable, + ((AccessoryWithAirPlayEnable) accessory)::subscribeAirPlayEnable, + ((AccessoryWithAirPlayEnable) accessory)::unsubscribeAirPlayEnable)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(VolumeCharacteristic volume) { + addCharacteristic(volume); + } + + public void addOptionalCharacteristic(ConfiguredNameCharacteristic configuredNameCharacteristic) { + addCharacteristic(configuredNameCharacteristic); + } + + public void addOptionalCharacteristic(MuteCharacteristic muteCharacteristic) { + addCharacteristic(muteCharacteristic); + } + + public void addOptionalCharacteristic(AirPlayEnableCharacteristic airPlayEnableCharacteristic) { + addCharacteristic(airPlayEnableCharacteristic); + } +} From 99e3d0f341ee616fdce977cd5deeb94c9f24884b Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sat, 5 Feb 2022 10:45:33 +0100 Subject: [PATCH 069/138] upate README Signed-off-by: Eugen Freiter --- CHANGES.md | 3 +++ README.md | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index bb5668235..9c56f2dc2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ * Log accessory names instead of futures. [#150](https://github.com/hap-java/HAP-Java/issues/150) * Fix rotation speed data type (BREAKING API CHANGE). According to HAP specification it must be float * Fix UUID of HAP Version characteristic +* Add Mute characteristic to Doorbell service ## New * New characteristics: @@ -22,11 +23,13 @@ * Remote Key * Volume Control Type * Volume Selector + * AirPlay Enable * New services * Input Source * Television * Television Speaker + * Smart Speaker # HAP-Java 2.0.0 * major refactoring to support optional characteristics diff --git a/README.md b/README.md index ba3cc6e7e..6d1f88458 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Include HAP-Java in your project using maven: io.github.hap-java hap - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT ``` @@ -28,7 +28,7 @@ read the [Javadoc](https://hap-java.github.io/HAP-Java/apidocs/index.html) Supported HomeKit Accessories ========= -Current implementation fully supports 41 HomeKit accessory/services. +Current implementation fully supports 42 HomeKit accessory/services. | HomeKit Accessory & Service type | Supported by Java-HAP | |--------------------|--------------------| @@ -67,6 +67,7 @@ Current implementation fully supports 41 HomeKit accessory/services. | Service Label | :white_check_mark: | | Siri | :x: | | Slat | :white_check_mark: | +| Smart Speaker | :white_check_mark: | | Smoke Sensor | :white_check_mark: | | Speaker | :white_check_mark: | | Stateless Programmable Switch | :white_check_mark: | From efa62a16ae8f158861f1d6150d4aecb9af73b5ae Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sat, 5 Feb 2022 11:03:38 +0100 Subject: [PATCH 070/138] close JsonWriters Signed-off-by: Eugen Freiter --- CHANGES.md | 1 + .../server/impl/json/AccessoryController.java | 7 ++++--- .../server/impl/json/CharacteristicsController.java | 8 ++++---- .../hapjava/server/impl/json/EventController.java | 12 +++++++----- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a3a55a290..42876c4c9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ ## Fixes * Log accessory names instead of futures. [#150](https://github.com/hap-java/HAP-Java/issues/150) * Fix rotation speed data type (BREAKING API CHANGE). According to HAP specification it must be float +* Close JsonWriters [#149](https://github.com/hap-java/HAP-Java/issues/149) # HAP-Java 2.0.0 * major refactoring to support optional characteristics diff --git a/src/main/java/io/github/hapjava/server/impl/json/AccessoryController.java b/src/main/java/io/github/hapjava/server/impl/json/AccessoryController.java index 98e6cd266..ef21422af 100644 --- a/src/main/java/io/github/hapjava/server/impl/json/AccessoryController.java +++ b/src/main/java/io/github/hapjava/server/impl/json/AccessoryController.java @@ -18,6 +18,7 @@ import javax.json.JsonArrayBuilder; import javax.json.JsonObject; import javax.json.JsonObjectBuilder; +import javax.json.JsonWriter; public class AccessoryController { @@ -64,9 +65,9 @@ public HttpResponse listing() throws Exception { .add("services", serviceArrayBuilders.get(accessory.getId()))); } - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - Json.createWriter(baos) - .write(Json.createObjectBuilder().add("accessories", accessories).build()); + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + JsonWriter jsonWriter = Json.createWriter(baos)) { + jsonWriter.write(Json.createObjectBuilder().add("accessories", accessories).build()); return new HapJsonResponse(baos.toByteArray()); } } diff --git a/src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java b/src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java index df5509dda..4d20a0c6b 100644 --- a/src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java +++ b/src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java @@ -58,10 +58,10 @@ public HttpResponse get(HttpRequest request) throws Exception { "Accessory " + aid + " has no characteristics or does not exist. Request: " + uri); } } - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - Json.createWriter(baos) - .write( - Json.createObjectBuilder().add("characteristics", characteristics.build()).build()); + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + JsonWriter jsonWriter = Json.createWriter(baos)) { + jsonWriter.write( + Json.createObjectBuilder().add("characteristics", characteristics.build()).build()); return new HapJsonResponse(baos.toByteArray()); } } diff --git a/src/main/java/io/github/hapjava/server/impl/json/EventController.java b/src/main/java/io/github/hapjava/server/impl/json/EventController.java index 80be265ae..41473f716 100644 --- a/src/main/java/io/github/hapjava/server/impl/json/EventController.java +++ b/src/main/java/io/github/hapjava/server/impl/json/EventController.java @@ -9,6 +9,7 @@ import javax.json.JsonArrayBuilder; import javax.json.JsonObject; import javax.json.JsonObjectBuilder; +import javax.json.JsonWriter; public class EventController { @@ -24,8 +25,9 @@ public HttpResponse getMessage(int accessoryId, int iid, EventableCharacteristic JsonObject data = Json.createObjectBuilder().add("characteristics", characteristics).build(); - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - Json.createWriter(baos).write(data); + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + JsonWriter jsonWriter = Json.createWriter(baos)) { + jsonWriter.write(data); byte[] dataBytes = baos.toByteArray(); return new EventResponse(dataBytes); @@ -44,9 +46,9 @@ public HttpResponse getMessage(ArrayList notifications) thr } JsonObject data = Json.createObjectBuilder().add("characteristics", characteristics).build(); - - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - Json.createWriter(baos).write(data); + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + JsonWriter jsonWriter = Json.createWriter(baos)) { + jsonWriter.write(data); byte[] dataBytes = baos.toByteArray(); return new EventResponse(dataBytes); From f241c8902117a6174b4e3ef3fe30b917b69fc80d Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Thu, 3 Feb 2022 22:42:25 +0100 Subject: [PATCH 071/138] add support for television service Signed-off-by: Eugen Freiter --- CHANGES.md | 25 ++++ .../accessories/InputSourceAccessory.java | 104 +++++++++++++ .../accessories/TelevisionAccessory.java | 123 ++++++++++++++++ .../TelevisionSpeakerAccessory.java | 43 ++++++ .../AccessoryWithActive.java | 38 +++++ .../AccessoryWithClosedCaptions.java | 37 +++++ .../AccessoryWithCurrentMediaState.java | 30 ++++ .../AccessoryWithIdentifier.java | 17 +++ .../AccessoryWithInputDeviceType.java | 29 ++++ .../AccessoryWithPictureMode.java | 39 +++++ .../AccessoryWithPowerMode.java | 16 ++ .../AccessoryWithTargetMediaState.java | 37 +++++ .../AccessoryWithTargetVisibilityState.java | 36 +++++ .../AccessoryWithVolumeControlType.java | 30 ++++ .../AccessoryWithVolumeSelector.java | 20 +++ .../impl/base/StringCharacteristic.java | 80 ++++++++++ .../ActiveIdentifierCharacteristic.java | 29 ++++ .../common/ConfiguredNameCharacteristic.java | 27 ++++ .../impl/common/IdentifierCharacteristic.java | 22 +++ .../impl/common/VersionCharacteristic.java | 2 +- .../CurrentVisibilityStateCharacteristic.java | 27 ++++ .../CurrentVisibilityStateEnum.java | 36 +++++ .../InputDeviceTypeCharacteristic.java | 26 ++++ .../impl/inputsource/InputDeviceTypeEnum.java | 38 +++++ .../InputSourceTypeCharacteristic.java | 26 ++++ .../impl/inputsource/InputSourceTypeEnum.java | 43 ++++++ .../TargetVisibilityStateCharacteristic.java | 29 ++++ .../TargetVisibilityStateEnum.java | 34 +++++ .../ClosedCaptionsCharacteristic.java | 31 ++++ .../impl/television/ClosedCaptionsEnum.java | 35 +++++ .../CurrentMediaStateCharacteristic.java | 28 ++++ .../television/CurrentMediaStateEnum.java | 37 +++++ .../television/PictureModeCharacteristic.java | 30 ++++ .../impl/television/PictureModeEnum.java | 44 ++++++ .../television/PowerModeCharacteristic.java | 20 +++ .../impl/television/PowerModeEnum.java | 35 +++++ .../television/RemoteKeyCharacteristic.java | 22 +++ .../impl/television/RemoteKeyEnum.java | 45 ++++++ .../SleepDiscoveryModeCharacteristic.java | 28 ++++ .../television/SleepDiscoveryModeEnum.java | 35 +++++ .../TargetMediaStateCharacteristic.java | 27 ++++ .../impl/television/TargetMediaStateEnum.java | 36 +++++ .../VolumeControlTypeCharacteristic.java | 28 ++++ .../VolumeControlTypeEnum.java | 36 +++++ .../VolumeSelectorCharacteristic.java | 22 +++ .../televisionspeaker/VolumeSelectorEnum.java | 34 +++++ .../services/impl/AbstractServiceImpl.java | 7 + .../services/impl/InputSourceService.java | 91 ++++++++++++ .../services/impl/TelevisionService.java | 139 ++++++++++++++++++ .../impl/TelevisionSpeakerService.java | 75 ++++++++++ 50 files changed, 1927 insertions(+), 1 deletion(-) create mode 100644 src/main/java/io/github/hapjava/accessories/InputSourceAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/TelevisionAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/TelevisionSpeakerAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithActive.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithClosedCaptions.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentMediaState.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithIdentifier.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithInputDeviceType.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPictureMode.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPowerMode.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetMediaState.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetVisibilityState.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolumeControlType.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolumeSelector.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/ActiveIdentifierCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/ConfiguredNameCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/IdentifierCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/inputsource/CurrentVisibilityStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/inputsource/CurrentVisibilityStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputDeviceTypeCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputDeviceTypeEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputSourceTypeCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputSourceTypeEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/inputsource/TargetVisibilityStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/inputsource/TargetVisibilityStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/ClosedCaptionsCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/ClosedCaptionsEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/CurrentMediaStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/CurrentMediaStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/PictureModeCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/PictureModeEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/PowerModeCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/PowerModeEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/SleepDiscoveryModeCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/SleepDiscoveryModeEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/TargetMediaStateCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/television/TargetMediaStateEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeControlTypeCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeControlTypeEnum.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeSelectorCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeSelectorEnum.java create mode 100644 src/main/java/io/github/hapjava/services/impl/InputSourceService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/TelevisionService.java create mode 100644 src/main/java/io/github/hapjava/services/impl/TelevisionSpeakerService.java diff --git a/CHANGES.md b/CHANGES.md index 42876c4c9..0f293c195 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,31 @@ * Log accessory names instead of futures. [#150](https://github.com/hap-java/HAP-Java/issues/150) * Fix rotation speed data type (BREAKING API CHANGE). According to HAP specification it must be float * Close JsonWriters [#149](https://github.com/hap-java/HAP-Java/issues/149) +* Fix UUID of HAP Version characteristic + +## New +* New characteristics: + * Identifier + * Input Device Type + * Input Source Type + * Configured Name + * Current Visibility State + * Target Visibility State + * Sleep Discovery Mode + * Active Identifier + * Closed Captions + * Current Media State + * Target Media State + * Picture Mode + * Power Mode + * Remote Key + * Volume Control Type + * Volume Selector + +* New services + * Input Source + * Television + * Television Speaker # HAP-Java 2.0.0 * major refactoring to support optional characteristics diff --git a/src/main/java/io/github/hapjava/accessories/InputSourceAccessory.java b/src/main/java/io/github/hapjava/accessories/InputSourceAccessory.java new file mode 100644 index 000000000..cfa5ae3d6 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/InputSourceAccessory.java @@ -0,0 +1,104 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.common.IsConfiguredEnum; +import io.github.hapjava.characteristics.impl.inputsource.CurrentVisibilityStateEnum; +import io.github.hapjava.characteristics.impl.inputsource.InputSourceTypeEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.InputSourceService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** Input Source accessory. */ +public interface InputSourceAccessory extends HomekitAccessory { + + /** + * Retrieves configured name of input source. + * + * @return configured name of input source + */ + CompletableFuture getConfiguredName(); + + /** + * Sets the configured name. + * + * @param name configured name + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setConfiguredName(String name) throws Exception; + + /** + * Subscribes to changes in configured name. + * + * @param callback the function to call when the configured name changes. + */ + void subscribeConfiguredName(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the configured name. */ + void unsubscribeConfiguredName(); + + /** + * Retrieves the flag whether input source is configured. + * + * @return a future that will contain the flag . + */ + CompletableFuture isConfigured(); + /** + * set the flag whether input source is configured. + * + * @param state is configured state + * @return a future that completes when the change is made + */ + CompletableFuture setIsConfigured(IsConfiguredEnum state); + + /** + * Subscribes to changes in isConfigured. + * + * @param callback the function to call when the state changes. + */ + void subscribeIsConfigured(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in isConfigured. */ + void unsubscribeIsConfigured(); + + /** + * Retrieves the input source type. + * + * @return a future that will contain the input source type. + */ + CompletableFuture getInputSourceType(); + + /** + * Subscribes to changes in input source type. + * + * @param callback the function to call when the type changes. + */ + void subscribeInputSourceType(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the input source type. */ + void unsubscribeInputSourceType(); + + /** + * Retrieves the current visibility state. + * + * @return a future that will contain the current visibility state. + */ + CompletableFuture getCurrentVisibilityState(); + + /** + * Subscribes to changes in current visibility state. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentVisibilityState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current visibility state. */ + void unsubscribeCurrentVisibilityState(); + + @Override + default Collection getServices() { + return Collections.singleton(new InputSourceService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/TelevisionAccessory.java b/src/main/java/io/github/hapjava/accessories/TelevisionAccessory.java new file mode 100644 index 000000000..3a1a1c515 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/TelevisionAccessory.java @@ -0,0 +1,123 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.television.RemoteKeyEnum; +import io.github.hapjava.characteristics.impl.television.SleepDiscoveryModeEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.TelevisionService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** Television accessory. */ +public interface TelevisionAccessory extends HomekitAccessory { + + /** + * Retrieves the current active state of the TV. + * + * @return a future that will contain the state + */ + CompletableFuture isActive(); + + /** + * Sets the active state of the TV + * + * @param state the state to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setActive(boolean state) throws Exception; + + /** + * Subscribes to changes in the active state of the TV . + * + * @param callback the function to call when the active state changes. + */ + void subscribeActive(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the active state of the TV. */ + void unsubscribeActive(); + + /** + * Retrieves the active identifier + * + * @return a future that will contain the active identifier. + */ + CompletableFuture getActiveIdentifier(); + + /** + * Sets the active identifier + * + * @param identifier the active identifier + * @return a future that completes when the active identifier is changed + * @throws Exception when the active identifier cannot be set + */ + CompletableFuture setActiveIdentifier(Integer identifier) throws Exception; + + /** + * Subscribes to changes in the active identifier. + * + * @param callback the function to call when the active identifier changes. + */ + void subscribeActiveIdentifier(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the active identifier. */ + void unsubscribeActiveIdentifier(); + + /** + * Retrieves configured name. + * + * @return configured name + */ + CompletableFuture getConfiguredName(); + + /** + * Sets the mute status + * + * @param name configured name + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setConfiguredName(String name) throws Exception; + + /** + * Subscribes to changes in mute state. + * + * @param callback the function to call when the state changes. + */ + void subscribeConfiguredName(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the mute state. */ + void unsubscribeConfiguredName(); + + /** + * Sends the remote key. + * + * @param key remote key + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setRemoteKey(RemoteKeyEnum key) throws Exception; + + /** + * Retrieves the sleep discovery mode. + * + * @return a future that will contain the sleep discovery mode . + */ + CompletableFuture getSleepDiscoveryMode(); + + /** + * Subscribes to changes in sleep discovery mode. + * + * @param callback the function to call when the sleep discovery mode changes. + */ + void subscribeSleepDiscoveryMode(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the sleep discovery mode. */ + void unsubscribeSleepDiscoveryMode(); + + @Override + default Collection getServices() { + return Collections.singleton(new TelevisionService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/TelevisionSpeakerAccessory.java b/src/main/java/io/github/hapjava/accessories/TelevisionSpeakerAccessory.java new file mode 100644 index 000000000..28df1437d --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/TelevisionSpeakerAccessory.java @@ -0,0 +1,43 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.TelevisionSpeakerService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** Television speaker accessory. */ +public interface TelevisionSpeakerAccessory extends HomekitAccessory { + + /** + * Retrieves mute status. + * + * @return true if accessory is muted + */ + CompletableFuture isMuted(); + + /** + * Sets the mute status + * + * @param mute true if accessory should be muted + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setMute(boolean mute) throws Exception; + + /** + * Subscribes to changes in mute state. + * + * @param callback the function to call when the state changes. + */ + void subscribeMuteState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the mute state. */ + void unsubscribeMuteState(); + + @Override + default Collection getServices() { + return Collections.singleton(new TelevisionSpeakerService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithActive.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithActive.java new file mode 100644 index 000000000..4ad11be63 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithActive.java @@ -0,0 +1,38 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.common.ActiveEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with active characteristic {@link + * io.github.hapjava.characteristics.impl.common.ActiveCharacteristic}. + */ +public interface AccessoryWithActive { + + /** + * Retrieves the active state (see {@link + * io.github.hapjava.characteristics.impl.common.ActiveEnum} for supported values). + * + * @return a future that will contain the active state + */ + CompletableFuture getActive(); + + /** + * Set the active state (see {@link ActiveEnum} for supported values). + * + * @param active active state + * @return a future that completes when the change is made + */ + CompletableFuture setActive(ActiveEnum active); + + /** + * Subscribes to changes in the active state. + * + * @param callback the function to call when the active state changes. + */ + void subscribeActive(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the active state. */ + void unsubscribeActive(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithClosedCaptions.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithClosedCaptions.java new file mode 100644 index 000000000..c13da3289 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithClosedCaptions.java @@ -0,0 +1,37 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.television.ClosedCaptionsEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with closed captions characteristic {@link + * io.github.hapjava.characteristics.impl.television.ClosedCaptionsCharacteristic}. + */ +public interface AccessoryWithClosedCaptions { + + /** + * Retrieves the closed captions state (see {@link ClosedCaptionsEnum} for supported values). + * + * @return a future that will contain the closed captions + */ + CompletableFuture getClosedCaptions(); + + /** + * Set the closed captions state (see {@link ClosedCaptionsEnum} for supported values). + * + * @param closedCaptions closed captions + * @return a future that completes when the change is made + */ + CompletableFuture setClosedCaptions(ClosedCaptionsEnum closedCaptions); + + /** + * Subscribes to changes in the closed captions. + * + * @param callback the function to call when the closed captions changes. + */ + void subscribeClosedCaptions(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the closed captions. */ + void unsubscribeClosedCaptions(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentMediaState.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentMediaState.java new file mode 100644 index 000000000..22350d9b4 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithCurrentMediaState.java @@ -0,0 +1,30 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.television.CurrentMediaStateEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with current media state characteristic {@link + * io.github.hapjava.characteristics.impl.television.CurrentMediaStateCharacteristic}. + */ +public interface AccessoryWithCurrentMediaState { + + /** + * Retrieves the current media state (see {@link + * io.github.hapjava.characteristics.impl.television.CurrentMediaStateEnum} for supported values). + * + * @return a future that will contain the current media state + */ + CompletableFuture getCurrentMediaState(); + + /** + * Subscribes to changes in the current media state. + * + * @param callback the function to call when the current media state changes. + */ + void subscribeCurrentMediaState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current media state. */ + void unsubscribeCurrentMediaState(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithIdentifier.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithIdentifier.java new file mode 100644 index 000000000..b181d4d99 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithIdentifier.java @@ -0,0 +1,17 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with identifier characteristic {@link + * io.github.hapjava.characteristics.impl.common.IdentifierCharacteristic}. + */ +public interface AccessoryWithIdentifier { + + /** + * Retrieves the identifier of service. + * + * @return a future with the identifier + */ + CompletableFuture getIdentifier(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithInputDeviceType.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithInputDeviceType.java new file mode 100644 index 000000000..fca5f5df1 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithInputDeviceType.java @@ -0,0 +1,29 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.inputsource.InputDeviceTypeEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with input devices, e.g. television. {@link + * io.github.hapjava.characteristics.impl.inputsource.InputDeviceTypeCharacteristic} + */ +public interface AccessoryWithInputDeviceType { + + /** + * Retrieves the input device type. See {@link InputDeviceTypeEnum} for supported values. + * + * @return a future with the value + */ + CompletableFuture getInputDeviceType(); + + /** + * Subscribes to changes in input device type. + * + * @param callback the function when the input device type changes + */ + void subscribeInputDeviceType(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes */ + void unsubscribeInputDeviceType(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPictureMode.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPictureMode.java new file mode 100644 index 000000000..b369b98bf --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPictureMode.java @@ -0,0 +1,39 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.television.PictureModeEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with picture mode characteristic {@link + * io.github.hapjava.characteristics.impl.television.PictureModeCharacteristic}. + */ +public interface AccessoryWithPictureMode { + + /** + * Retrieves the picture mode (see {@link + * io.github.hapjava.characteristics.impl.television.PictureModeEnum} for supported values). + * + * @return a future that will contain the picture mode + */ + CompletableFuture getPictureMode(); + + /** + * Set the picture mode (see {@link + * io.github.hapjava.characteristics.impl.television.PictureModeEnum} for supported values). + * + * @param pictureMode picture mode + * @return a future that completes when the change is made + */ + CompletableFuture setPictureMode(PictureModeEnum pictureMode); + + /** + * Subscribes to changes in the picture mode. + * + * @param callback the function to call when the picture mode changes. + */ + void subscribePictureMode(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the picture mode. */ + void unsubscribePictureMode(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPowerMode.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPowerMode.java new file mode 100644 index 000000000..5abb7e6f1 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithPowerMode.java @@ -0,0 +1,16 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.impl.television.PowerModeEnum; +import java.util.concurrent.CompletableFuture; + +/** An accessory with power mode. */ +public interface AccessoryWithPowerMode { + + /** + * Set the power mode. See {@link PowerModeEnum} for supported values + * + * @param mode target power mode + * @return a future that completes when the change is made + */ + CompletableFuture setPowerMode(PowerModeEnum mode); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetMediaState.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetMediaState.java new file mode 100644 index 000000000..e1f1a34bd --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetMediaState.java @@ -0,0 +1,37 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.television.TargetMediaStateEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with target media state characteristic {@link + * io.github.hapjava.characteristics.impl.television.TargetMediaStateCharacteristic}. + */ +public interface AccessoryWithTargetMediaState { + + /** + * Retrieves the target media state (see {@link TargetMediaStateEnum} for supported values). + * + * @return a future that will contain the target media state + */ + CompletableFuture getTargetMediaState(); + + /** + * Set the target media state (see {@link TargetMediaStateEnum} for supported values). + * + * @param targetMediaState target media state + * @return a future that completes when the change is made + */ + CompletableFuture setTargetMediaState(TargetMediaStateEnum targetMediaState); + + /** + * Subscribes to changes in the target media state. + * + * @param callback the function to call when the target media state changes. + */ + void subscribeTargetMediaState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the target media state. */ + void unsubscribeTargetMediaState(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetVisibilityState.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetVisibilityState.java new file mode 100644 index 000000000..dcadd1cc5 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithTargetVisibilityState.java @@ -0,0 +1,36 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.inputsource.TargetVisibilityStateEnum; +import java.util.concurrent.CompletableFuture; + +/** accessory with target visibility state characteristics. */ +public interface AccessoryWithTargetVisibilityState { + + /** + * Retrieves the target visibility state. + * + * @return a future that will contain the target visibility state + */ + CompletableFuture getTargetVisibilityState(); + + /** + * Sets the target visibility state + * + * @param state the target visibility state to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setTargetVisibilityState(TargetVisibilityStateEnum state) + throws Exception; + + /** + * Subscribes to changes in t target visibility state. + * + * @param callback the function to call when the target visibility state changes. + */ + void subscribeTargetVisibilityState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the target visibility state. */ + void unsubscribeTargetVisibilityState(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolumeControlType.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolumeControlType.java new file mode 100644 index 000000000..21a6f6c66 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolumeControlType.java @@ -0,0 +1,30 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.televisionspeaker.VolumeControlTypeEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with volume control type {@link + * io.github.hapjava.characteristics.impl.televisionspeaker.VolumeControlTypeCharacteristic} + */ +public interface AccessoryWithVolumeControlType { + + /** + * Retrieves the current volume control type. see {@link VolumeControlTypeEnum} for possible + * values + * + * @return a future that will contain the type. + */ + CompletableFuture getVolumeControlType(); + + /** + * Subscribes to changes in the volume. + * + * @param callback the function to call when the state changes. + */ + void subscribeVolumeControlType(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the volume. */ + void unsubscribeVolumeControlType(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolumeSelector.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolumeSelector.java new file mode 100644 index 000000000..0c2a2890f --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithVolumeSelector.java @@ -0,0 +1,20 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.impl.televisionspeaker.VolumeSelectorEnum; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with volume selector {@link + * io.github.hapjava.characteristics.impl.televisionspeaker.VolumeSelectorCharacteristic} + */ +public interface AccessoryWithVolumeSelector { + + /** + * Sets the volume selector + * + * @param value the volume selector + * @return a future that completes when the volume selector is changed + * @throws Exception when the volume selector cannot be set + */ + CompletableFuture setVolumeSelector(VolumeSelectorEnum value) throws Exception; +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java new file mode 100644 index 000000000..4373b06c9 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java @@ -0,0 +1,80 @@ +package io.github.hapjava.characteristics.impl.base; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; +import javax.json.JsonObjectBuilder; +import javax.json.JsonString; +import javax.json.JsonValue; + +/** + * A characteristic that provides an string value. + * + * @author Eugen Freiter + */ +public class StringCharacteristic extends BaseCharacteristic { + private final Optional>> getter; + private final Optional> setter; + /** + * Default constructor + * + * @param type a string containing a UUID that indicates the type of characteristic. Apple defines + * a set of these, however implementors can create their own as well. + * @param description a description of the characteristic to be passed to the consuming device. + * @param getter getter to retrieve the value + * @param setter setter for value + * @param subscriber subscriber to subscribe to changes + * @param unsubscriber unsubscriber to unsubscribe from chnages + */ + public StringCharacteristic( + String type, + String description, + Optional>> getter, + Optional> setter, + Optional> subscriber, + Optional unsubscriber) { + super( + type, + "string", + description, + getter.isPresent(), + setter.isPresent(), + subscriber, + unsubscriber); + this.getter = getter; + this.setter = setter; + } + + /** {@inheritDoc} */ + @Override + protected CompletableFuture makeBuilder(int iid) { + return super.makeBuilder(iid); + } + + /** {@inheritDoc} */ + @Override + public String convert(JsonValue jsonValue) { + return ((JsonString) jsonValue).getString(); + } + + /** {@inheritDoc} */ + @Override + public void setValue(String value) throws Exception { + setter.get().accept(value); + } + + /** {@inheritDoc} */ + @Override + protected CompletableFuture getValue() { + return getter.map(stringGetter -> stringGetter.get()).get(); + } + + /** {@inheritDoc} */ + @Override + protected String getDefault() { + return "Unknown"; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveIdentifierCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveIdentifierCharacteristic.java new file mode 100644 index 000000000..587c1b666 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ActiveIdentifierCharacteristic.java @@ -0,0 +1,29 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class ActiveIdentifierCharacteristic extends IntegerCharacteristic { + + public ActiveIdentifierCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000E7-0000-1000-8000-0026BB765291", + "active identifier", + 0, + 1000, + "", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ConfiguredNameCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ConfiguredNameCharacteristic.java new file mode 100644 index 000000000..471d5a368 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ConfiguredNameCharacteristic.java @@ -0,0 +1,27 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.StringCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes a configured name. */ +public class ConfiguredNameCharacteristic extends StringCharacteristic { + + public ConfiguredNameCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000E3-0000-1000-8000-0026BB765291", + "configured name", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/IdentifierCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/IdentifierCharacteristic.java new file mode 100644 index 000000000..4b8f7613c --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/IdentifierCharacteristic.java @@ -0,0 +1,22 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +public class IdentifierCharacteristic extends IntegerCharacteristic { + + public IdentifierCharacteristic(Supplier> getter) { + super( + "000000E6-0000-1000-8000-0026BB765291", + "identifier", + 0, + 1000, + "", + Optional.of(getter), + Optional.empty(), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/VersionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/VersionCharacteristic.java index 038c46c5c..75c8aaf9e 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/VersionCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/VersionCharacteristic.java @@ -10,7 +10,7 @@ public class VersionCharacteristic extends StaticStringCharacteristic { public VersionCharacteristic(Supplier> getter) { super( - "00000023-0000-1000-8000-0026BB765291", + "00000037-0000-1000-8000-0026BB765291", "HAP version", Optional.of(getter), Optional.empty(), diff --git a/src/main/java/io/github/hapjava/characteristics/impl/inputsource/CurrentVisibilityStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/CurrentVisibilityStateCharacteristic.java new file mode 100644 index 000000000..65c358a3f --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/CurrentVisibilityStateCharacteristic.java @@ -0,0 +1,27 @@ +package io.github.hapjava.characteristics.impl.inputsource; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes current visibility state. */ +public class CurrentVisibilityStateCharacteristic + extends EnumCharacteristic { + + public CurrentVisibilityStateCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000135-0000-1000-8000-0026BB765291", + "current visibility state", + CurrentVisibilityStateEnum.values(), + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/inputsource/CurrentVisibilityStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/CurrentVisibilityStateEnum.java new file mode 100644 index 000000000..2159393d8 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/CurrentVisibilityStateEnum.java @@ -0,0 +1,36 @@ +package io.github.hapjava.characteristics.impl.inputsource; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +public enum CurrentVisibilityStateEnum implements CharacteristicEnum { + SHOWN(0), + HIDDEN(1), + UNKNOWN_1(2), + UNKNOWN_2(3); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(CurrentVisibilityStateEnum.values()) + .collect(Collectors.toMap(CurrentVisibilityStateEnum::getCode, t -> t)); + } + + public static CurrentVisibilityStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + CurrentVisibilityStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputDeviceTypeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputDeviceTypeCharacteristic.java new file mode 100644 index 000000000..51a6fbde5 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputDeviceTypeCharacteristic.java @@ -0,0 +1,26 @@ +package io.github.hapjava.characteristics.impl.inputsource; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes input device type. */ +public class InputDeviceTypeCharacteristic extends EnumCharacteristic { + + public InputDeviceTypeCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000DC-0000-1000-8000-0026BB765291", + "input device type", + InputDeviceTypeEnum.values(), + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputDeviceTypeEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputDeviceTypeEnum.java new file mode 100644 index 000000000..5c7337174 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputDeviceTypeEnum.java @@ -0,0 +1,38 @@ +package io.github.hapjava.characteristics.impl.inputsource; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +public enum InputDeviceTypeEnum implements CharacteristicEnum { + OTHER(0), + TV(1), + RECORDING(2), + TUNER(3), + PLAYBACK(4), + AUDIO_SYSTEM(5); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(InputDeviceTypeEnum.values()) + .collect(Collectors.toMap(InputDeviceTypeEnum::getCode, t -> t)); + } + + public static InputDeviceTypeEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + InputDeviceTypeEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputSourceTypeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputSourceTypeCharacteristic.java new file mode 100644 index 000000000..91134891b --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputSourceTypeCharacteristic.java @@ -0,0 +1,26 @@ +package io.github.hapjava.characteristics.impl.inputsource; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes input source type. */ +public class InputSourceTypeCharacteristic extends EnumCharacteristic { + + public InputSourceTypeCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000DB-0000-1000-8000-0026BB765291", + "input source type", + InputSourceTypeEnum.values(), + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputSourceTypeEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputSourceTypeEnum.java new file mode 100644 index 000000000..94545ec18 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/InputSourceTypeEnum.java @@ -0,0 +1,43 @@ +package io.github.hapjava.characteristics.impl.inputsource; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +public enum InputSourceTypeEnum implements CharacteristicEnum { + OTHER(0), + HOME_SCREEN(1), + TUNER(2), + HDMI(3), + COMPOSITE_VIDEO(4), + S_VIDEO(5), + COMPONENT_VIDEO(6), + DVI(7), + AIRPLAY(8), + USB(9), + APPLICATION(10); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(InputSourceTypeEnum.values()) + .collect(Collectors.toMap(InputSourceTypeEnum::getCode, t -> t)); + } + + public static InputSourceTypeEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + InputSourceTypeEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/inputsource/TargetVisibilityStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/TargetVisibilityStateCharacteristic.java new file mode 100644 index 000000000..ea466d182 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/TargetVisibilityStateCharacteristic.java @@ -0,0 +1,29 @@ +package io.github.hapjava.characteristics.impl.inputsource; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic describes target visibility state. */ +public class TargetVisibilityStateCharacteristic + extends EnumCharacteristic { + + public TargetVisibilityStateCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000134-0000-1000-8000-0026BB765291", + "target visibility state", + TargetVisibilityStateEnum.values(), + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/inputsource/TargetVisibilityStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/TargetVisibilityStateEnum.java new file mode 100644 index 000000000..6a75b0c30 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/inputsource/TargetVisibilityStateEnum.java @@ -0,0 +1,34 @@ +package io.github.hapjava.characteristics.impl.inputsource; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +public enum TargetVisibilityStateEnum implements CharacteristicEnum { + SHOWN(0), + HIDDEN(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(TargetVisibilityStateEnum.values()) + .collect(Collectors.toMap(TargetVisibilityStateEnum::getCode, t -> t)); + } + + public static TargetVisibilityStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + TargetVisibilityStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/ClosedCaptionsCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/television/ClosedCaptionsCharacteristic.java new file mode 100644 index 000000000..a963ca222 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/ClosedCaptionsCharacteristic.java @@ -0,0 +1,31 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Characteristic indicates whether the service provides closed captions. See {@link + * ClosedCaptionsEnum} for possible values. + */ +public class ClosedCaptionsCharacteristic extends EnumCharacteristic { + + public ClosedCaptionsCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000DD-0000-1000-8000-0026BB765291", + "closed captions", + ClosedCaptionsEnum.values(), + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/ClosedCaptionsEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/television/ClosedCaptionsEnum.java new file mode 100644 index 000000000..2eb994149 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/ClosedCaptionsEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Disabled” 1 ”Enabled” 2-255 ”Reserved” */ +public enum ClosedCaptionsEnum implements CharacteristicEnum { + DISABLED(0), + ENABLED(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(ClosedCaptionsEnum.values()) + .collect(Collectors.toMap(ClosedCaptionsEnum::getCode, t -> t)); + } + + public static ClosedCaptionsEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + ClosedCaptionsEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/CurrentMediaStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/television/CurrentMediaStateCharacteristic.java new file mode 100644 index 000000000..ca72c4ee9 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/CurrentMediaStateCharacteristic.java @@ -0,0 +1,28 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic indicates current media state. See {@link CurrentMediaStateEnum} for possible + * values. + */ +public class CurrentMediaStateCharacteristic extends EnumCharacteristic { + public CurrentMediaStateCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000E0-0000-1000-8000-0026BB765291", + "current media state", + CurrentMediaStateEnum.values(), + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/CurrentMediaStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/television/CurrentMediaStateEnum.java new file mode 100644 index 000000000..83836c4c6 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/CurrentMediaStateEnum.java @@ -0,0 +1,37 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Play” 1 ”Pause” 2 "Stop" 3 "Unknown" 4-255 ”Reserved” */ +public enum CurrentMediaStateEnum implements CharacteristicEnum { + PLAY(0), + PAUSE(1), + STOP(2), + UNKNOWN(3); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(CurrentMediaStateEnum.values()) + .collect(Collectors.toMap(CurrentMediaStateEnum::getCode, t -> t)); + } + + public static CurrentMediaStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + CurrentMediaStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/PictureModeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/television/PictureModeCharacteristic.java new file mode 100644 index 000000000..7efefa23a --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/PictureModeCharacteristic.java @@ -0,0 +1,30 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic retrieves / sets picture mode. See {@link PictureModeEnum} for possible + * values. + */ +public class PictureModeCharacteristic extends EnumCharacteristic { + public PictureModeCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000E2-0000-1000-8000-0026BB765291", + "picture mode", + PictureModeEnum.values(), + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/PictureModeEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/television/PictureModeEnum.java new file mode 100644 index 000000000..ac387cf65 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/PictureModeEnum.java @@ -0,0 +1,44 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 0 ”Other” 1 ”Standard” 2 "Calibrated" 3 "CalibratedDark" 4 "Vivid" 5 "Game" 6 "Computer" 7 + * "Custom" 8-255 ”Reserved” + */ +public enum PictureModeEnum implements CharacteristicEnum { + OTHER(0), + STANDARD(1), + CALIBRATED(2), + CALIBRATED_DARK(3), + VIVID(4), + GAME(5), + COMPUTER(6), + CUSTOM(7); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(PictureModeEnum.values()) + .collect(Collectors.toMap(PictureModeEnum::getCode, t -> t)); + } + + public static PictureModeEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + PictureModeEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/PowerModeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/television/PowerModeCharacteristic.java new file mode 100644 index 000000000..cebbe4124 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/PowerModeCharacteristic.java @@ -0,0 +1,20 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; + +/** characteristic sets power mode. See {@link PowerModeEnum} for possible values. */ +public class PowerModeCharacteristic extends EnumCharacteristic { + + public PowerModeCharacteristic(ExceptionalConsumer setter) { + super( + "000000DF-0000-1000-8000-0026BB765291", + "power mode selection", + PowerModeEnum.values(), + Optional.empty(), + Optional.of(setter), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/PowerModeEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/television/PowerModeEnum.java new file mode 100644 index 000000000..a171ba135 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/PowerModeEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Show” 1 ”Hide” 2-255 ”Reserved” */ +public enum PowerModeEnum implements CharacteristicEnum { + SHOW(0), + HIDE(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(PowerModeEnum.values()) + .collect(Collectors.toMap(PowerModeEnum::getCode, t -> t)); + } + + public static PowerModeEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + PowerModeEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyCharacteristic.java new file mode 100644 index 000000000..fe028d10d --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyCharacteristic.java @@ -0,0 +1,22 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; + +/** + * This characteristic sends information about pressed key on tv remote. See {@link RemoteKeyEnum} + * for possible values. + */ +public class RemoteKeyCharacteristic extends EnumCharacteristic { + public RemoteKeyCharacteristic(ExceptionalConsumer setter) { + super( + "000000E1-0000-1000-8000-0026BB765291", + "remote key", + RemoteKeyEnum.values(), + Optional.empty(), + Optional.of(setter), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyEnum.java new file mode 100644 index 000000000..443c23644 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyEnum.java @@ -0,0 +1,45 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +public enum RemoteKeyEnum implements CharacteristicEnum { + REWIND(0), + FAST_FORWARD(1), + NEXT_TRACK(2), + PREV_TRACK(3), + ARROW_UP(4), + ARROW_DOWN(5), + ARROW_LEFT(6), + ARROW_RIGHT(7), + SELECT(8), + BACK(9), + EXIT(10), + PLAY_PAUSE(11), + INFO(115); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(RemoteKeyEnum.values()) + .collect(Collectors.toMap(RemoteKeyEnum::getCode, t -> t)); + } + + public static RemoteKeyEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + RemoteKeyEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/SleepDiscoveryModeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/television/SleepDiscoveryModeCharacteristic.java new file mode 100644 index 000000000..2a7f41829 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/SleepDiscoveryModeCharacteristic.java @@ -0,0 +1,28 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic indicates whether the service can be discovered. See {@link + * SleepDiscoveryModeEnum} for possible values. + */ +public class SleepDiscoveryModeCharacteristic extends EnumCharacteristic { + public SleepDiscoveryModeCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000E8-0000-1000-8000-0026BB765291", + "sleep discovery mode", + SleepDiscoveryModeEnum.values(), + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/SleepDiscoveryModeEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/television/SleepDiscoveryModeEnum.java new file mode 100644 index 000000000..da1377b9a --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/SleepDiscoveryModeEnum.java @@ -0,0 +1,35 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Not discoverable” 1 ”Always discoverable” 2-255 ”Reserved” */ +public enum SleepDiscoveryModeEnum implements CharacteristicEnum { + NOT_DISCOVERABLE(0), + ALWAYS_DISCOVERABLE(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(SleepDiscoveryModeEnum.values()) + .collect(Collectors.toMap(SleepDiscoveryModeEnum::getCode, t -> t)); + } + + public static SleepDiscoveryModeEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + SleepDiscoveryModeEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/TargetMediaStateCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/television/TargetMediaStateCharacteristic.java new file mode 100644 index 000000000..8909bb1ea --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/TargetMediaStateCharacteristic.java @@ -0,0 +1,27 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** This characteristic sets media state. See {@link TargetMediaStateEnum} for possible values. */ +public class TargetMediaStateCharacteristic extends EnumCharacteristic { + public TargetMediaStateCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "00000137-0000-1000-8000-0026BB765291", + "current media state", + TargetMediaStateEnum.values(), + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/TargetMediaStateEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/television/TargetMediaStateEnum.java new file mode 100644 index 000000000..b6f6633ff --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/TargetMediaStateEnum.java @@ -0,0 +1,36 @@ +package io.github.hapjava.characteristics.impl.television; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** 0 ”Play” 1 ”Pause” 2 "Stop" 3-255 ”Reserved” */ +public enum TargetMediaStateEnum implements CharacteristicEnum { + PLAY(0), + PAUSE(1), + STOP(2); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(TargetMediaStateEnum.values()) + .collect(Collectors.toMap(TargetMediaStateEnum::getCode, t -> t)); + } + + public static TargetMediaStateEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + TargetMediaStateEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeControlTypeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeControlTypeCharacteristic.java new file mode 100644 index 000000000..c6d18a858 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeControlTypeCharacteristic.java @@ -0,0 +1,28 @@ +package io.github.hapjava.characteristics.impl.televisionspeaker; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * This characteristic indicates volume control type. See {@link VolumeControlTypeEnum} for possible + * values. + */ +public class VolumeControlTypeCharacteristic extends EnumCharacteristic { + public VolumeControlTypeCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "000000E9-0000-1000-8000-0026BB765291", + "volume control type", + VolumeControlTypeEnum.values(), + Optional.of(getter), + Optional.empty(), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeControlTypeEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeControlTypeEnum.java new file mode 100644 index 000000000..dfeb24636 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeControlTypeEnum.java @@ -0,0 +1,36 @@ +package io.github.hapjava.characteristics.impl.televisionspeaker; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +public enum VolumeControlTypeEnum implements CharacteristicEnum { + NONE(0), + RELATIVE(1), + RELATIVE_WITH_CURRENT(2), + ABSOLUTE(3); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(VolumeControlTypeEnum.values()) + .collect(Collectors.toMap(VolumeControlTypeEnum::getCode, t -> t)); + } + + public static VolumeControlTypeEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + VolumeControlTypeEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeSelectorCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeSelectorCharacteristic.java new file mode 100644 index 000000000..8f2f84e7b --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeSelectorCharacteristic.java @@ -0,0 +1,22 @@ +package io.github.hapjava.characteristics.impl.televisionspeaker; + +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.impl.base.EnumCharacteristic; +import java.util.Optional; + +/** + * This characteristic sends information about volume changes. See {@link VolumeSelectorEnum} for + * possible values. + */ +public class VolumeSelectorCharacteristic extends EnumCharacteristic { + public VolumeSelectorCharacteristic(ExceptionalConsumer setter) { + super( + "000000EA-0000-1000-8000-0026BB765291", + "volume selector", + VolumeSelectorEnum.values(), + Optional.empty(), + Optional.of(setter), + Optional.empty(), + Optional.empty()); + } +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeSelectorEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeSelectorEnum.java new file mode 100644 index 000000000..63bc60e50 --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/televisionspeaker/VolumeSelectorEnum.java @@ -0,0 +1,34 @@ +package io.github.hapjava.characteristics.impl.televisionspeaker; + +import io.github.hapjava.characteristics.CharacteristicEnum; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +public enum VolumeSelectorEnum implements CharacteristicEnum { + INCREMENT(0), + DECREMENT(1); + + private static final Map reverse; + + static { + reverse = + Arrays.stream(VolumeSelectorEnum.values()) + .collect(Collectors.toMap(VolumeSelectorEnum::getCode, t -> t)); + } + + public static VolumeSelectorEnum fromCode(Integer code) { + return reverse.get(code); + } + + private final int code; + + VolumeSelectorEnum(int code) { + this.code = code; + } + + @Override + public int getCode() { + return code; + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java b/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java index 25a44099d..2848d367e 100644 --- a/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java +++ b/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java @@ -5,14 +5,18 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; abstract class AbstractServiceImpl implements Service { private final String type; private final List characteristics = new LinkedList<>(); private final List linkedServices = new LinkedList<>(); + private static final Logger logger = LoggerFactory.getLogger(AbstractServiceImpl.class); /** @param type unique UUID of the service according to HAP specification. */ public AbstractServiceImpl(String type) { + logger.info("new service:" + type); this.type = type; } @@ -28,6 +32,7 @@ public String getType() { @Override public List getLinkedServices() { + logger.info(" all linked service" + this.linkedServices); return Collections.unmodifiableList(linkedServices); } @@ -37,6 +42,8 @@ public void addCharacteristic(Characteristic characteristic) { @Override public void addLinkedService(Service service) { + logger.info("add linked service" + service); this.linkedServices.add(service); + logger.info(" all linked service" + this.linkedServices); } } diff --git a/src/main/java/io/github/hapjava/services/impl/InputSourceService.java b/src/main/java/io/github/hapjava/services/impl/InputSourceService.java new file mode 100644 index 000000000..4af2736a2 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/InputSourceService.java @@ -0,0 +1,91 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.InputSourceAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithIdentifier; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithInputDeviceType; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithTargetVisibilityState; +import io.github.hapjava.characteristics.impl.common.ConfiguredNameCharacteristic; +import io.github.hapjava.characteristics.impl.common.IdentifierCharacteristic; +import io.github.hapjava.characteristics.impl.common.IsConfiguredCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; +import io.github.hapjava.characteristics.impl.inputsource.CurrentVisibilityStateCharacteristic; +import io.github.hapjava.characteristics.impl.inputsource.InputDeviceTypeCharacteristic; +import io.github.hapjava.characteristics.impl.inputsource.InputSourceTypeCharacteristic; +import io.github.hapjava.characteristics.impl.inputsource.TargetVisibilityStateCharacteristic; + +/** This service describes a input source. */ +public class InputSourceService extends AbstractServiceImpl { + + public InputSourceService( + ConfiguredNameCharacteristic configuredNameCharacteristic, + InputSourceTypeCharacteristic inputSourceTypeCharacteristic, + IsConfiguredCharacteristic isConfiguredCharacteristic, + CurrentVisibilityStateCharacteristic currentVisibilityStateCharacteristic) { + super("000000D9-0000-1000-8000-0026BB765291"); + addCharacteristic(configuredNameCharacteristic); + addCharacteristic(inputSourceTypeCharacteristic); + addCharacteristic(isConfiguredCharacteristic); + addCharacteristic(currentVisibilityStateCharacteristic); + } + + public InputSourceService(InputSourceAccessory accessory) { + this( + new ConfiguredNameCharacteristic( + accessory::getConfiguredName, + accessory::setConfiguredName, + accessory::subscribeConfiguredName, + accessory::unsubscribeConfiguredName), + new InputSourceTypeCharacteristic( + accessory::getInputSourceType, + accessory::subscribeInputSourceType, + accessory::unsubscribeInputSourceType), + new IsConfiguredCharacteristic( + accessory::isConfigured, + accessory::setIsConfigured, + accessory::subscribeIsConfigured, + accessory::unsubscribeIsConfigured), + new CurrentVisibilityStateCharacteristic( + accessory::getCurrentVisibilityState, + accessory::subscribeCurrentVisibilityState, + accessory::unsubscribeCurrentVisibilityState)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithIdentifier) { + addOptionalCharacteristic( + new IdentifierCharacteristic(((AccessoryWithIdentifier) accessory)::getIdentifier)); + } + if (accessory instanceof AccessoryWithInputDeviceType) { + addOptionalCharacteristic( + new InputDeviceTypeCharacteristic( + ((AccessoryWithInputDeviceType) accessory)::getInputDeviceType, + ((AccessoryWithInputDeviceType) accessory)::subscribeInputDeviceType, + ((AccessoryWithInputDeviceType) accessory)::unsubscribeInputDeviceType)); + } + if (accessory instanceof AccessoryWithTargetVisibilityState) { + addOptionalCharacteristic( + new TargetVisibilityStateCharacteristic( + ((AccessoryWithTargetVisibilityState) accessory)::getTargetVisibilityState, + ((AccessoryWithTargetVisibilityState) accessory)::setTargetVisibilityState, + ((AccessoryWithTargetVisibilityState) accessory)::subscribeTargetVisibilityState, + ((AccessoryWithTargetVisibilityState) accessory)::unsubscribeTargetVisibilityState)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(IdentifierCharacteristic identifier) { + addCharacteristic(identifier); + } + + public void addOptionalCharacteristic(InputDeviceTypeCharacteristic inputDeviceType) { + addCharacteristic(inputDeviceType); + } + + public void addOptionalCharacteristic(TargetVisibilityStateCharacteristic targetVisibilityState) { + addCharacteristic(targetVisibilityState); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/TelevisionService.java b/src/main/java/io/github/hapjava/services/impl/TelevisionService.java new file mode 100644 index 000000000..8846cffe1 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/TelevisionService.java @@ -0,0 +1,139 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.TelevisionAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithBrightness; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithClosedCaptions; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithCurrentMediaState; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithPictureMode; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithPowerMode; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithTargetMediaState; +import io.github.hapjava.characteristics.impl.common.ActiveCharacteristic; +import io.github.hapjava.characteristics.impl.common.ActiveEnum; +import io.github.hapjava.characteristics.impl.common.ActiveIdentifierCharacteristic; +import io.github.hapjava.characteristics.impl.common.ConfiguredNameCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; +import io.github.hapjava.characteristics.impl.lightbulb.BrightnessCharacteristic; +import io.github.hapjava.characteristics.impl.television.ClosedCaptionsCharacteristic; +import io.github.hapjava.characteristics.impl.television.CurrentMediaStateCharacteristic; +import io.github.hapjava.characteristics.impl.television.PictureModeCharacteristic; +import io.github.hapjava.characteristics.impl.television.PowerModeCharacteristic; +import io.github.hapjava.characteristics.impl.television.RemoteKeyCharacteristic; +import io.github.hapjava.characteristics.impl.television.SleepDiscoveryModeCharacteristic; +import io.github.hapjava.characteristics.impl.television.TargetMediaStateCharacteristic; + +/** This service describes a television. */ +public class TelevisionService extends AbstractServiceImpl { + + public TelevisionService( + ActiveCharacteristic activeCharacteristic, + ActiveIdentifierCharacteristic activeIdentifierCharacteristic, + ConfiguredNameCharacteristic configuredNameCharacteristic, + RemoteKeyCharacteristic remoteKeyCharacteristic, + SleepDiscoveryModeCharacteristic sleepDiscoveryModeCharacteristic) { + super("000000D8-0000-1000-8000-0026BB765291"); + addCharacteristic(activeCharacteristic); + addCharacteristic(activeIdentifierCharacteristic); + addCharacteristic(configuredNameCharacteristic); + addCharacteristic(remoteKeyCharacteristic); + addCharacteristic(sleepDiscoveryModeCharacteristic); + } + + public TelevisionService(TelevisionAccessory accessory) { + this( + new ActiveCharacteristic( + () -> accessory.isActive().thenApply(s -> s ? ActiveEnum.ACTIVE : ActiveEnum.INACTIVE), + (v) -> accessory.setActive(v == ActiveEnum.ACTIVE), + accessory::subscribeActive, + accessory::unsubscribeActive), + new ActiveIdentifierCharacteristic( + accessory::getActiveIdentifier, + accessory::setActiveIdentifier, + accessory::subscribeActiveIdentifier, + accessory::unsubscribeActiveIdentifier), + new ConfiguredNameCharacteristic( + accessory::getConfiguredName, + accessory::setConfiguredName, + accessory::subscribeConfiguredName, + accessory::unsubscribeConfiguredName), + new RemoteKeyCharacteristic(accessory::setRemoteKey), + new SleepDiscoveryModeCharacteristic( + accessory::getSleepDiscoveryMode, + accessory::subscribeSleepDiscoveryMode, + accessory::unsubscribeSleepDiscoveryMode)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithBrightness) { + addOptionalCharacteristic( + new BrightnessCharacteristic( + ((AccessoryWithBrightness) accessory)::getBrightness, + ((AccessoryWithBrightness) accessory)::setBrightness, + ((AccessoryWithBrightness) accessory)::subscribeBrightness, + ((AccessoryWithBrightness) accessory)::unsubscribeBrightness)); + } + if (accessory instanceof AccessoryWithPowerMode) { + addOptionalCharacteristic( + new PowerModeCharacteristic(((AccessoryWithPowerMode) accessory)::setPowerMode)); + } + if (accessory instanceof AccessoryWithClosedCaptions) { + addOptionalCharacteristic( + new ClosedCaptionsCharacteristic( + ((AccessoryWithClosedCaptions) accessory)::getClosedCaptions, + ((AccessoryWithClosedCaptions) accessory)::setClosedCaptions, + ((AccessoryWithClosedCaptions) accessory)::subscribeClosedCaptions, + ((AccessoryWithClosedCaptions) accessory)::unsubscribeClosedCaptions)); + } + if (accessory instanceof AccessoryWithCurrentMediaState) { + addOptionalCharacteristic( + new CurrentMediaStateCharacteristic( + ((AccessoryWithCurrentMediaState) accessory)::getCurrentMediaState, + ((AccessoryWithCurrentMediaState) accessory)::subscribeCurrentMediaState, + ((AccessoryWithCurrentMediaState) accessory)::unsubscribeCurrentMediaState)); + } + if (accessory instanceof AccessoryWithTargetMediaState) { + addOptionalCharacteristic( + new TargetMediaStateCharacteristic( + ((AccessoryWithTargetMediaState) accessory)::getTargetMediaState, + ((AccessoryWithTargetMediaState) accessory)::setTargetMediaState, + ((AccessoryWithTargetMediaState) accessory)::subscribeTargetMediaState, + ((AccessoryWithTargetMediaState) accessory)::unsubscribeTargetMediaState)); + } + if (accessory instanceof AccessoryWithPictureMode) { + addOptionalCharacteristic( + new PictureModeCharacteristic( + ((AccessoryWithPictureMode) accessory)::getPictureMode, + ((AccessoryWithPictureMode) accessory)::setPictureMode, + ((AccessoryWithPictureMode) accessory)::subscribePictureMode, + ((AccessoryWithPictureMode) accessory)::unsubscribePictureMode)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(PowerModeCharacteristic powerMode) { + addCharacteristic(powerMode); + } + + public void addOptionalCharacteristic(ClosedCaptionsCharacteristic closedCaptions) { + addCharacteristic(closedCaptions); + } + + public void addOptionalCharacteristic(CurrentMediaStateCharacteristic currentMediaState) { + addCharacteristic(currentMediaState); + } + + public void addOptionalCharacteristic(TargetMediaStateCharacteristic targetMediaState) { + addCharacteristic(targetMediaState); + } + + public void addOptionalCharacteristic(PictureModeCharacteristic pictureMode) { + addCharacteristic(pictureMode); + } + + public void addOptionalCharacteristic(BrightnessCharacteristic brightness) { + addCharacteristic(brightness); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/TelevisionSpeakerService.java b/src/main/java/io/github/hapjava/services/impl/TelevisionSpeakerService.java new file mode 100644 index 000000000..b53fb81bd --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/TelevisionSpeakerService.java @@ -0,0 +1,75 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.TelevisionSpeakerAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithActive; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithVolume; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithVolumeControlType; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithVolumeSelector; +import io.github.hapjava.characteristics.impl.audio.MuteCharacteristic; +import io.github.hapjava.characteristics.impl.audio.VolumeCharacteristic; +import io.github.hapjava.characteristics.impl.common.ActiveCharacteristic; +import io.github.hapjava.characteristics.impl.televisionspeaker.VolumeControlTypeCharacteristic; +import io.github.hapjava.characteristics.impl.televisionspeaker.VolumeSelectorCharacteristic; + +/** This service describes a television. */ +public class TelevisionSpeakerService extends AbstractServiceImpl { + + public TelevisionSpeakerService(MuteCharacteristic muteCharacteristic) { + super("00000113-0000-1000-8000-0026BB765291"); + addCharacteristic(muteCharacteristic); + } + + public TelevisionSpeakerService(TelevisionSpeakerAccessory accessory) { + this( + new MuteCharacteristic( + accessory::isMuted, + accessory::setMute, + accessory::subscribeMuteState, + accessory::unsubscribeMuteState)); + if (accessory instanceof AccessoryWithActive) { + addOptionalCharacteristic( + new ActiveCharacteristic( + ((AccessoryWithActive) accessory)::getActive, + ((AccessoryWithActive) accessory)::setActive, + ((AccessoryWithActive) accessory)::subscribeActive, + ((AccessoryWithActive) accessory)::unsubscribeActive)); + } + if (accessory instanceof AccessoryWithVolume) { + addOptionalCharacteristic( + new VolumeCharacteristic( + ((AccessoryWithVolume) accessory)::getVolume, + ((AccessoryWithVolume) accessory)::setVolume, + ((AccessoryWithVolume) accessory)::subscribeVolume, + ((AccessoryWithVolume) accessory)::unsubscribeVolume)); + } + if (accessory instanceof AccessoryWithVolumeSelector) { + addOptionalCharacteristic( + new VolumeSelectorCharacteristic( + ((AccessoryWithVolumeSelector) accessory)::setVolumeSelector)); + } + if (accessory instanceof AccessoryWithVolumeControlType) { + addOptionalCharacteristic( + new VolumeControlTypeCharacteristic( + ((AccessoryWithVolumeControlType) accessory)::getVolumeControlType, + ((AccessoryWithVolumeControlType) accessory)::subscribeVolumeControlType, + ((AccessoryWithVolumeControlType) accessory)::unsubscribeVolumeControlType)); + ; + } + } + + public void addOptionalCharacteristic(ActiveCharacteristic activeCharacteristic) { + addCharacteristic(activeCharacteristic); + } + + public void addOptionalCharacteristic(VolumeCharacteristic volume) { + addCharacteristic(volume); + } + + public void addOptionalCharacteristic(VolumeSelectorCharacteristic volumeSelector) { + addCharacteristic(volumeSelector); + } + + public void addOptionalCharacteristic(VolumeControlTypeCharacteristic volumeControlType) { + addCharacteristic(volumeControlType); + } +} From 6f4166339808a88b9a6a9a08058b8b164d39e626 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Thu, 3 Feb 2022 22:46:26 +0100 Subject: [PATCH 072/138] update readme Signed-off-by: Eugen Freiter --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 03fb0aaba..ba3cc6e7e 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ read the [Javadoc](https://hap-java.github.io/HAP-Java/apidocs/index.html) Supported HomeKit Accessories ========= -Current implementation fully supports 38 HomeKit accessory/services. +Current implementation fully supports 41 HomeKit accessory/services. | HomeKit Accessory & Service type | Supported by Java-HAP | |--------------------|--------------------| @@ -52,6 +52,7 @@ Current implementation fully supports 38 HomeKit accessory/services. | Heater Cooler | :white_check_mark: | | Humidifier Dehumidifier | :white_check_mark: | | Humidity Sensor | :white_check_mark: | +| Input Source | :white_check_mark: | | Irrigation System | :white_check_mark: | | Leak Sensor | :white_check_mark: | | Light Bulb | :white_check_mark: | @@ -72,6 +73,8 @@ Current implementation fully supports 38 HomeKit accessory/services. | Switch | :white_check_mark: | | Target Control | :x: | | Target Control Management | :x: | +| Television | :white_check_mark: | +| Television Speaker | :white_check_mark: | | Temperature Sensor | :white_check_mark: | | Thermostat | :white_check_mark: | | Valve | :white_check_mark: | From 40e0338174b9d76a9181cba29eeafeceba15a31c Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Fri, 4 Feb 2022 14:02:39 +0100 Subject: [PATCH 073/138] add smart speaker Signed-off-by: Eugen Freiter --- .../accessories/SmartSpeakerAccessory.java | 62 ++++++++++++ .../accessories/TelevisionAccessory.java | 8 +- .../AccessoryWithAirPlayEnable.java | 37 ++++++++ .../AccessoryWithConfiguredName.java | 34 +++++++ .../AccessoryWithMute.java | 37 ++++++++ .../common/AirPlayEnableCharacteristic.java | 32 +++++++ .../hapjava/services/impl/DoorService.java | 14 +++ .../services/impl/SmartSpeakerService.java | 95 +++++++++++++++++++ 8 files changed, 315 insertions(+), 4 deletions(-) create mode 100644 src/main/java/io/github/hapjava/accessories/SmartSpeakerAccessory.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithAirPlayEnable.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithConfiguredName.java create mode 100644 src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithMute.java create mode 100644 src/main/java/io/github/hapjava/characteristics/impl/common/AirPlayEnableCharacteristic.java create mode 100644 src/main/java/io/github/hapjava/services/impl/SmartSpeakerService.java diff --git a/src/main/java/io/github/hapjava/accessories/SmartSpeakerAccessory.java b/src/main/java/io/github/hapjava/accessories/SmartSpeakerAccessory.java new file mode 100644 index 000000000..9f70af2be --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/SmartSpeakerAccessory.java @@ -0,0 +1,62 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.television.CurrentMediaStateEnum; +import io.github.hapjava.characteristics.impl.television.TargetMediaStateEnum; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.SmartSpeakerService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** Smart Speaker accessory. */ +public interface SmartSpeakerAccessory extends HomekitAccessory { + + /** + * Retrieves the current media state (see {@link + * io.github.hapjava.characteristics.impl.television.CurrentMediaStateEnum} for supported values). + * + * @return a future that will contain the current media state + */ + CompletableFuture getCurrentMediaState(); + + /** + * Subscribes to changes in the current media state. + * + * @param callback the function to call when the current media state changes. + */ + void subscribeCurrentMediaState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current media state. */ + void unsubscribeCurrentMediaState(); + + /** + * Retrieves the target media state (see {@link TargetMediaStateEnum} for supported values). + * + * @return a future that will contain the target media state + */ + CompletableFuture getTargetMediaState(); + + /** + * Set the target media state (see {@link TargetMediaStateEnum} for supported values). + * + * @param targetMediaState target media state + * @return a future that completes when the change is made + */ + CompletableFuture setTargetMediaState(TargetMediaStateEnum targetMediaState); + + /** + * Subscribes to changes in the target media state. + * + * @param callback the function to call when the target media state changes. + */ + void subscribeTargetMediaState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the target media state. */ + void unsubscribeTargetMediaState(); + + @Override + default Collection getServices() { + return Collections.singleton(new SmartSpeakerService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/accessories/TelevisionAccessory.java b/src/main/java/io/github/hapjava/accessories/TelevisionAccessory.java index 3a1a1c515..b51efe319 100644 --- a/src/main/java/io/github/hapjava/accessories/TelevisionAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/TelevisionAccessory.java @@ -72,7 +72,7 @@ public interface TelevisionAccessory extends HomekitAccessory { CompletableFuture getConfiguredName(); /** - * Sets the mute status + * Sets the configured name * * @param name configured name * @return a future that completes when the change is made @@ -81,13 +81,13 @@ public interface TelevisionAccessory extends HomekitAccessory { CompletableFuture setConfiguredName(String name) throws Exception; /** - * Subscribes to changes in mute state. + * Subscribes to changes in configured name. * - * @param callback the function to call when the state changes. + * @param callback the function to call when the configureed name changes. */ void subscribeConfiguredName(HomekitCharacteristicChangeCallback callback); - /** Unsubscribes from changes in the mute state. */ + /** Unsubscribes from changes in the configured name state. */ void unsubscribeConfiguredName(); /** diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithAirPlayEnable.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithAirPlayEnable.java new file mode 100644 index 000000000..76b90b43f --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithAirPlayEnable.java @@ -0,0 +1,37 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with AirPlay enable characteristic {@link + * io.github.hapjava.characteristics.impl.common.AirPlayEnableCharacteristic}. + */ +public interface AccessoryWithAirPlayEnable { + + /** + * Retrieves the AirPlay enable state + * + * @return a future that will contain the AirPlay enable state. + */ + CompletableFuture getAirPlayEnable(); + + /** + * Sets the AirPlay enable state + * + * @param state AirPlay enable state + * @return a future that completes when the AirPlay enable is changed + * @throws Exception when the AirPlay enable cannot be set + */ + CompletableFuture setAirPlayEnable(Integer state) throws Exception; + + /** + * Subscribes to changes in the AirPlay enable state. + * + * @param callback the function to call when the AirPlay enable state changes. + */ + void subscribeAirPlayEnable(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the AirPlay enable state. */ + void unsubscribeAirPlayEnable(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithConfiguredName.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithConfiguredName.java new file mode 100644 index 000000000..0c9528130 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithConfiguredName.java @@ -0,0 +1,34 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** Accessory with configured name. */ +public interface AccessoryWithConfiguredName { + + /** + * Retrieves configured name. + * + * @return configured name + */ + CompletableFuture getConfiguredName(); + + /** + * Sets the configured name + * + * @param name configured name + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setConfiguredName(String name) throws Exception; + + /** + * Subscribes to changes in configured name. + * + * @param callback the function to call when the configureed name changes. + */ + void subscribeConfiguredName(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the configured name state. */ + void unsubscribeConfiguredName(); +} diff --git a/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithMute.java b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithMute.java new file mode 100644 index 000000000..a0bb08579 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/optionalcharacteristic/AccessoryWithMute.java @@ -0,0 +1,37 @@ +package io.github.hapjava.accessories.optionalcharacteristic; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +/** + * Accessory with mute characteristic {@link + * io.github.hapjava.characteristics.impl.audio.MuteCharacteristic}. + */ +public interface AccessoryWithMute { + + /** + * Retrieves mute status. + * + * @return true if accessory is muted + */ + CompletableFuture isMuted(); + + /** + * Sets the mute status + * + * @param mute true if accessory should be muted + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setMute(boolean mute) throws Exception; + + /** + * Subscribes to changes in mute state. + * + * @param callback the function to call when the state changes. + */ + void subscribeMuteState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the mute state. */ + void unsubscribeMuteState(); +} diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/AirPlayEnableCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/AirPlayEnableCharacteristic.java new file mode 100644 index 000000000..6158f13be --- /dev/null +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/AirPlayEnableCharacteristic.java @@ -0,0 +1,32 @@ +package io.github.hapjava.characteristics.impl.common; + +import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.characteristics.ExceptionalConsumer; +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** characteristic to control air play. */ +public class AirPlayEnableCharacteristic extends IntegerCharacteristic + implements EventableCharacteristic { + + public AirPlayEnableCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + super( + "0000025B-0000-1000-8000-0026BB765291", + "AirPlay enable", + 0, + 1, + "", + Optional.of(getter), + Optional.of(setter), + Optional.of(subscriber), + Optional.of(unsubscriber)); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/DoorService.java b/src/main/java/io/github/hapjava/services/impl/DoorService.java index c4c3b40b3..51f956f5f 100644 --- a/src/main/java/io/github/hapjava/services/impl/DoorService.java +++ b/src/main/java/io/github/hapjava/services/impl/DoorService.java @@ -2,8 +2,10 @@ import io.github.hapjava.accessories.DoorAccessory; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithHoldPosition; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithMute; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithObstructionDetection; +import io.github.hapjava.characteristics.impl.audio.MuteCharacteristic; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.ObstructionDetectedCharacteristic; import io.github.hapjava.characteristics.impl.windowcovering.CurrentPositionCharacteristic; @@ -53,6 +55,14 @@ public DoorService(DoorAccessory accessory) { ((AccessoryWithObstructionDetection) accessory)::subscribeObstructionDetected, ((AccessoryWithObstructionDetection) accessory)::unsubscribeObstructionDetected)); } + if (accessory instanceof AccessoryWithMute) { + addOptionalCharacteristic( + new MuteCharacteristic( + ((AccessoryWithMute) accessory)::isMuted, + ((AccessoryWithMute) accessory)::setMute, + ((AccessoryWithMute) accessory)::subscribeMuteState, + ((AccessoryWithMute) accessory)::unsubscribeMuteState)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { @@ -67,4 +77,8 @@ public void addOptionalCharacteristic( ObstructionDetectedCharacteristic obstructionDetectedCharacteristic) { addCharacteristic(obstructionDetectedCharacteristic); } + + public void addOptionalCharacteristic(MuteCharacteristic muteCharacteristic) { + addCharacteristic(muteCharacteristic); + } } diff --git a/src/main/java/io/github/hapjava/services/impl/SmartSpeakerService.java b/src/main/java/io/github/hapjava/services/impl/SmartSpeakerService.java new file mode 100644 index 000000000..e5f57eff4 --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/SmartSpeakerService.java @@ -0,0 +1,95 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.SmartSpeakerAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithAirPlayEnable; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithConfiguredName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithMute; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithVolume; +import io.github.hapjava.characteristics.impl.audio.MuteCharacteristic; +import io.github.hapjava.characteristics.impl.audio.VolumeCharacteristic; +import io.github.hapjava.characteristics.impl.common.AirPlayEnableCharacteristic; +import io.github.hapjava.characteristics.impl.common.ConfiguredNameCharacteristic; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; +import io.github.hapjava.characteristics.impl.television.CurrentMediaStateCharacteristic; +import io.github.hapjava.characteristics.impl.television.TargetMediaStateCharacteristic; + +/** A smart speaker service can be used to control the audio output settings on a speaker device. */ +public class SmartSpeakerService extends AbstractServiceImpl { + + public SmartSpeakerService( + CurrentMediaStateCharacteristic currentMediaStateCharacteristic, + TargetMediaStateCharacteristic targetMediaStateCharacteristic) { + super("00000228-0000-1000-8000-0026BB765291"); + addCharacteristic(currentMediaStateCharacteristic); + addCharacteristic(targetMediaStateCharacteristic); + } + + public SmartSpeakerService(SmartSpeakerAccessory accessory) { + this( + new CurrentMediaStateCharacteristic( + accessory::getCurrentMediaState, + accessory::subscribeCurrentMediaState, + accessory::unsubscribeCurrentMediaState), + new TargetMediaStateCharacteristic( + accessory::getTargetMediaState, + accessory::setTargetMediaState, + accessory::subscribeTargetMediaState, + accessory::unsubscribeTargetMediaState)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + if (accessory instanceof AccessoryWithVolume) { + addOptionalCharacteristic( + new VolumeCharacteristic( + ((AccessoryWithVolume) accessory)::getVolume, + ((AccessoryWithVolume) accessory)::setVolume, + ((AccessoryWithVolume) accessory)::subscribeVolume, + ((AccessoryWithVolume) accessory)::unsubscribeVolume)); + } + if (accessory instanceof AccessoryWithConfiguredName) { + addOptionalCharacteristic( + new ConfiguredNameCharacteristic( + ((AccessoryWithConfiguredName) accessory)::getConfiguredName, + ((AccessoryWithConfiguredName) accessory)::setConfiguredName, + ((AccessoryWithConfiguredName) accessory)::subscribeConfiguredName, + ((AccessoryWithConfiguredName) accessory)::unsubscribeConfiguredName)); + } + if (accessory instanceof AccessoryWithMute) { + addOptionalCharacteristic( + new MuteCharacteristic( + ((AccessoryWithMute) accessory)::isMuted, + ((AccessoryWithMute) accessory)::setMute, + ((AccessoryWithMute) accessory)::subscribeMuteState, + ((AccessoryWithMute) accessory)::unsubscribeMuteState)); + } + if (accessory instanceof AccessoryWithAirPlayEnable) { + addOptionalCharacteristic( + new AirPlayEnableCharacteristic( + ((AccessoryWithAirPlayEnable) accessory)::getAirPlayEnable, + ((AccessoryWithAirPlayEnable) accessory)::setAirPlayEnable, + ((AccessoryWithAirPlayEnable) accessory)::subscribeAirPlayEnable, + ((AccessoryWithAirPlayEnable) accessory)::unsubscribeAirPlayEnable)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(VolumeCharacteristic volume) { + addCharacteristic(volume); + } + + public void addOptionalCharacteristic(ConfiguredNameCharacteristic configuredNameCharacteristic) { + addCharacteristic(configuredNameCharacteristic); + } + + public void addOptionalCharacteristic(MuteCharacteristic muteCharacteristic) { + addCharacteristic(muteCharacteristic); + } + + public void addOptionalCharacteristic(AirPlayEnableCharacteristic airPlayEnableCharacteristic) { + addCharacteristic(airPlayEnableCharacteristic); + } +} From c17a9d914e7446c6ec14e5c8d894df4691c5c2ee Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sat, 5 Feb 2022 10:45:33 +0100 Subject: [PATCH 074/138] upate README Signed-off-by: Eugen Freiter --- CHANGES.md | 3 +++ README.md | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 0f293c195..400e99ae2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,7 @@ * Fix rotation speed data type (BREAKING API CHANGE). According to HAP specification it must be float * Close JsonWriters [#149](https://github.com/hap-java/HAP-Java/issues/149) * Fix UUID of HAP Version characteristic +* Add Mute characteristic to Doorbell service ## New * New characteristics: @@ -23,11 +24,13 @@ * Remote Key * Volume Control Type * Volume Selector + * AirPlay Enable * New services * Input Source * Television * Television Speaker + * Smart Speaker # HAP-Java 2.0.0 * major refactoring to support optional characteristics diff --git a/README.md b/README.md index ba3cc6e7e..6d1f88458 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Include HAP-Java in your project using maven: io.github.hap-java hap - 2.0.0-SNAPSHOT + 2.0.1-SNAPSHOT ``` @@ -28,7 +28,7 @@ read the [Javadoc](https://hap-java.github.io/HAP-Java/apidocs/index.html) Supported HomeKit Accessories ========= -Current implementation fully supports 41 HomeKit accessory/services. +Current implementation fully supports 42 HomeKit accessory/services. | HomeKit Accessory & Service type | Supported by Java-HAP | |--------------------|--------------------| @@ -67,6 +67,7 @@ Current implementation fully supports 41 HomeKit accessory/services. | Service Label | :white_check_mark: | | Siri | :x: | | Slat | :white_check_mark: | +| Smart Speaker | :white_check_mark: | | Smoke Sensor | :white_check_mark: | | Speaker | :white_check_mark: | | Stateless Programmable Switch | :white_check_mark: | From 4d9da49cb5e5443bf6faf4711f77a91a21e51bdf Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Sun, 6 Feb 2022 14:05:04 +0100 Subject: [PATCH 075/138] remove unnecessary logging Signed-off-by: Eugen Freiter --- .../io/github/hapjava/services/impl/AbstractServiceImpl.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java b/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java index 2848d367e..4dfd9f5f6 100644 --- a/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java +++ b/src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java @@ -16,7 +16,6 @@ abstract class AbstractServiceImpl implements Service { /** @param type unique UUID of the service according to HAP specification. */ public AbstractServiceImpl(String type) { - logger.info("new service:" + type); this.type = type; } @@ -32,7 +31,6 @@ public String getType() { @Override public List getLinkedServices() { - logger.info(" all linked service" + this.linkedServices); return Collections.unmodifiableList(linkedServices); } @@ -42,8 +40,6 @@ public void addCharacteristic(Characteristic characteristic) { @Override public void addLinkedService(Service service) { - logger.info("add linked service" + service); this.linkedServices.add(service); - logger.info(" all linked service" + this.linkedServices); } } From 1f72143a2c88fb0a14d9b21ad3106d7feb288e7c Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Thu, 10 Feb 2022 11:46:55 +0100 Subject: [PATCH 076/138] add support for HomeKit categories Signed-off-by: Eugen Freiter --- .../server/HomekitAccessoryCategories.java | 40 ++++++++++++++++++ .../hapjava/server/impl/HomekitRoot.java | 41 ++++++++++++++++--- .../hapjava/server/impl/HomekitServer.java | 5 ++- .../impl/http/impl/AccessoryHandler.java | 6 +-- .../http/impl/DefaultHttpRequestImpl.java | 4 +- .../impl/http/impl/NettyResponseUtil.java | 7 ++-- .../impl/jmdns/JmdnsHomekitAdvertiser.java | 9 +++- .../hapjava/server/impl/HomekitRootTest.java | 6 ++- .../jmdns/JmdnsHomekitAdvertiserTest.java | 2 +- 9 files changed, 99 insertions(+), 21 deletions(-) create mode 100644 src/main/java/io/github/hapjava/server/HomekitAccessoryCategories.java diff --git a/src/main/java/io/github/hapjava/server/HomekitAccessoryCategories.java b/src/main/java/io/github/hapjava/server/HomekitAccessoryCategories.java new file mode 100644 index 000000000..05768a947 --- /dev/null +++ b/src/main/java/io/github/hapjava/server/HomekitAccessoryCategories.java @@ -0,0 +1,40 @@ +package io.github.hapjava.server; + +public final class HomekitAccessoryCategories { + public static final int OTHER = 1; + public static final int BRIDGES = 2; + public static final int FANS = 3; + public static final int GARAGE_DOOR_OPENERS = 4; + public static final int LIGHTING = 5; + public static final int LOCKS = 6; + public static final int OUTLETS = 7; + public static final int SWITCHES = 8; + public static final int THERMOSTATS = 9; + public static final int SENSORS = 10; + public static final int SECURITY_SYSTEMS = 11; + public static final int DOORS = 12; + public static final int WINDOWS = 13; + public static final int WINDOW_COVERINGS = 14; + public static final int PROGRAMMABLE_SWITCHES = 15; + public static final int RANGE_EXTENDER = 16; + public static final int IP_CAMERAS = 17; + public static final int VIDEO_DOORBELLS = 18; + public static final int AIR_PURIFIERS = 19; + public static final int HEATERS = 20; + public static final int AIR_CONDITIONERS = 21; + public static final int HUMIDIFIERS = 22; + public static final int DEHUMIDIFIERS = 23; + public static final int APPLE_TV = 24; + public static final int HOMEPOD = 25; + public static final int SPEAKER = 26; + public static final int AIRPORT = 27; + public static final int SPRINKLERS = 28; + public static final int FAUCETS = 29; + public static final int SHOWER_SYSTEMS = 30; + public static final int TELEVISION = 31; + public static final int REMOTES = 32; + public static final int ROUTER = 33; + public static final int AUDIO_RECEIVER = 34; + public static final int TV_SET_TOP_BOX = 35; + public static final int TV_STREAMING_STICK = 36; +} diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java index e619fa1e7..ca182f812 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java @@ -2,6 +2,7 @@ import io.github.hapjava.accessories.Bridge; import io.github.hapjava.accessories.HomekitAccessory; +import io.github.hapjava.server.HomekitAccessoryCategories; import io.github.hapjava.server.HomekitAuthInfo; import io.github.hapjava.server.HomekitWebHandler; import io.github.hapjava.server.impl.connections.HomekitClientConnectionFactoryImpl; @@ -25,24 +26,36 @@ public class HomekitRoot { private static final Logger logger = LoggerFactory.getLogger(HomekitRoot.class); - + private static final int DEFAULT_ACCESSORY_CATEGORY = HomekitAccessoryCategories.OTHER; private final JmdnsHomekitAdvertiser advertiser; private final HomekitWebHandler webHandler; private final HomekitAuthInfo authInfo; private final String label; + private final int category; private final HomekitRegistry registry; private final SubscriptionManager subscriptions = new SubscriptionManager(); private boolean started = false; private int configurationIndex = 1; HomekitRoot( - String label, HomekitWebHandler webHandler, InetAddress localhost, HomekitAuthInfo authInfo) + String label, HomekitWebHandler webHandler, InetAddress host, HomekitAuthInfo authInfo) + throws IOException { + this(label, DEFAULT_ACCESSORY_CATEGORY, webHandler, authInfo, new JmdnsHomekitAdvertiser(host)); + } + + HomekitRoot( + String label, + int category, + HomekitWebHandler webHandler, + InetAddress host, + HomekitAuthInfo authInfo) throws IOException { - this(label, webHandler, authInfo, new JmdnsHomekitAdvertiser(localhost)); + this(label, category, webHandler, authInfo, new JmdnsHomekitAdvertiser(host)); } HomekitRoot( String label, + int category, HomekitWebHandler webHandler, HomekitAuthInfo authInfo, JmdnsHomekitAdvertiser advertiser) @@ -51,14 +64,25 @@ public class HomekitRoot { this.webHandler = webHandler; this.authInfo = authInfo; this.label = label; + this.category = category; this.registry = new HomekitRegistry(label); } - HomekitRoot(String label, HomekitWebHandler webHandler, JmDNS jmdns, HomekitAuthInfo authInfo) + HomekitRoot( + String label, + int category, + HomekitWebHandler webHandler, + JmDNS jmdns, + HomekitAuthInfo authInfo) throws IOException { - this(label, webHandler, authInfo, new JmdnsHomekitAdvertiser(jmdns)); + this(label, category, webHandler, authInfo, new JmdnsHomekitAdvertiser(jmdns)); } + HomekitRoot(String label, HomekitWebHandler webHandler, JmDNS jmdns, HomekitAuthInfo authInfo) + throws IOException { + this( + label, DEFAULT_ACCESSORY_CATEGORY, webHandler, authInfo, new JmdnsHomekitAdvertiser(jmdns)); + } /** * Add an accessory to be handled and advertised by this root. Any existing HomeKit connections * will be terminated to allow the clients to reconnect and see the updated accessory list. When @@ -127,7 +151,12 @@ public void start() { try { refreshAuthInfo(); advertiser.advertise( - label, authInfo.getMac(), port, configurationIndex, authInfo.getSetupId()); + label, + category, + authInfo.getMac(), + port, + configurationIndex, + authInfo.getSetupId()); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitServer.java b/src/main/java/io/github/hapjava/server/impl/HomekitServer.java index d348ca682..af129a7a8 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitServer.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitServer.java @@ -139,6 +139,7 @@ public HomekitStandaloneAccessoryServer createStandaloneAccessory( public HomekitRoot createBridge( HomekitAuthInfo authInfo, String label, + int category, String manufacturer, String model, String serialNumber, @@ -147,9 +148,9 @@ public HomekitRoot createBridge( throws IOException { HomekitRoot root; if (jmdns != null) { - root = new HomekitRoot(label, http, jmdns, authInfo); + root = new HomekitRoot(label, category, http, jmdns, authInfo); } else { - root = new HomekitRoot(label, http, localAddress, authInfo); + root = new HomekitRoot(label, category, http, localAddress, authInfo); } root.addAccessory( new HomekitBridge( diff --git a/src/main/java/io/github/hapjava/server/impl/http/impl/AccessoryHandler.java b/src/main/java/io/github/hapjava/server/impl/http/impl/AccessoryHandler.java index 0e1bf1146..6b840c713 100644 --- a/src/main/java/io/github/hapjava/server/impl/http/impl/AccessoryHandler.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/AccessoryHandler.java @@ -72,9 +72,9 @@ private void sendResponse( HttpVersion.HTTP_1_1, status, Unpooled.copiedBuffer(responseBody.getBytes(StandardCharsets.UTF_8))); - response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/plain"); - response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes()); - response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); + response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain"); + response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); + response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); ctx.write(response); ctx.flush(); } diff --git a/src/main/java/io/github/hapjava/server/impl/http/impl/DefaultHttpRequestImpl.java b/src/main/java/io/github/hapjava/server/impl/http/impl/DefaultHttpRequestImpl.java index 48a3ceb35..a124705f1 100644 --- a/src/main/java/io/github/hapjava/server/impl/http/impl/DefaultHttpRequestImpl.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/DefaultHttpRequestImpl.java @@ -13,7 +13,7 @@ public DefaultHttpRequestImpl(io.netty.handler.codec.http.HttpRequest request) { @Override public String getUri() { - return request.getUri(); + return request.uri(); } @Override @@ -23,7 +23,7 @@ public byte[] getBody() { @Override public HttpMethod getMethod() { - io.netty.handler.codec.http.HttpMethod method = request.getMethod(); + io.netty.handler.codec.http.HttpMethod method = request.method(); if (method.equals(io.netty.handler.codec.http.HttpMethod.GET)) { return HttpMethod.GET; } else if (method.equals(io.netty.handler.codec.http.HttpMethod.POST)) { diff --git a/src/main/java/io/github/hapjava/server/impl/http/impl/NettyResponseUtil.java b/src/main/java/io/github/hapjava/server/impl/http/impl/NettyResponseUtil.java index fba52eef5..9e7963fb3 100644 --- a/src/main/java/io/github/hapjava/server/impl/http/impl/NettyResponseUtil.java +++ b/src/main/java/io/github/hapjava/server/impl/http/impl/NettyResponseUtil.java @@ -4,7 +4,8 @@ import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import java.util.Map.Entry; @@ -25,8 +26,8 @@ public static FullHttpResponse createResponse(HttpResponse homekitResponse) { for (Entry header : homekitResponse.getHeaders().entrySet()) { response.headers().add(header.getKey(), header.getValue()); } - response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes()); - response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); + response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); + response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); return response; } } diff --git a/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java index 04016561e..1535a490a 100644 --- a/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java +++ b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java @@ -27,6 +27,7 @@ public class JmdnsHomekitAdvertiser { private int port; private int configurationIndex; private ServiceInfo serviceInfo; + private int category; public JmdnsHomekitAdvertiser(JmDNS jmdns) { this.jmdns = jmdns; @@ -37,7 +38,8 @@ public JmdnsHomekitAdvertiser(InetAddress localAddress) throws UnknownHostExcept } public synchronized void advertise( - String label, String mac, int port, int configurationIndex, String setupId) throws Exception { + String label, int category, String mac, int port, int configurationIndex, String setupId) + throws Exception { if (isAdvertising) { throw new IllegalStateException("HomeKit advertiser is already running"); } @@ -45,6 +47,7 @@ public synchronized void advertise( this.mac = mac; this.port = port; this.setupId = setupId; + this.category = category; this.configurationIndex = configurationIndex; logger.trace("Advertising accessory " + label); @@ -114,7 +117,9 @@ private ServiceInfo buildServiceInfo() { props.put("c#", Integer.toString(configurationIndex)); props.put("s#", "1"); props.put("ff", "0"); - props.put("ci", "1"); + props.put("ci", Integer.toString(category)); + props.put("pv", "1.1"); + return ServiceInfo.create(SERVICE_TYPE, label, port, 1, 1, props); } } diff --git a/src/test/java/io/github/hapjava/server/impl/HomekitRootTest.java b/src/test/java/io/github/hapjava/server/impl/HomekitRootTest.java index 719fea9d1..3ac40288a 100644 --- a/src/test/java/io/github/hapjava/server/impl/HomekitRootTest.java +++ b/src/test/java/io/github/hapjava/server/impl/HomekitRootTest.java @@ -8,6 +8,7 @@ import static org.mockito.Mockito.when; import io.github.hapjava.accessories.HomekitAccessory; +import io.github.hapjava.server.HomekitAccessoryCategories; import io.github.hapjava.server.HomekitAuthInfo; import io.github.hapjava.server.HomekitWebHandler; import io.github.hapjava.server.impl.http.HomekitClientConnectionFactory; @@ -38,7 +39,8 @@ public void setup() throws Exception { when(webHandler.start(any())).thenReturn(CompletableFuture.completedFuture(PORT)); advertiser = mock(JmdnsHomekitAdvertiser.class); authInfo = mock(HomekitAuthInfo.class); - root = new HomekitRoot(LABEL, webHandler, authInfo, advertiser); + root = + new HomekitRoot(LABEL, HomekitAccessoryCategories.OTHER, webHandler, authInfo, advertiser); } @Test @@ -78,7 +80,7 @@ public void testAdvertiserStarts() throws Exception { when(authInfo.getSetupId()).thenReturn(SETUPID); root.start(); - verify(advertiser).advertise(eq(LABEL), eq(mac), eq(PORT), eq(1), eq(SETUPID)); + verify(advertiser).advertise(eq(LABEL), eq(1), eq(mac), eq(PORT), eq(1), eq(SETUPID)); } @Test diff --git a/src/test/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiserTest.java b/src/test/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiserTest.java index 1c04b2868..870bd739b 100644 --- a/src/test/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiserTest.java +++ b/src/test/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiserTest.java @@ -61,6 +61,6 @@ private ServiceInfo getArgumentFromUnregister() { } private void advertise() throws Exception { - subject.advertise("test", "00:00:00:00:00:00", 1234, 1, "1"); + subject.advertise("test", 1, "00:00:00:00:00:00", 1234, 1, "1"); } } From 234b785caed8dfc57e10c1c9083ae5680823a469 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 11 Feb 2022 13:54:56 +0000 Subject: [PATCH 077/138] [maven-release-plugin] prepare release hap-2.0.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6adc9e33f..73297653a 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.1-SNAPSHOT + 2.0.1 jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - HEAD + hap-2.0.1 From aca73d5ce78990f6f54f37b8fbe87308ec471052 Mon Sep 17 00:00:00 2001 From: eugen Date: Fri, 11 Feb 2022 16:06:12 +0100 Subject: [PATCH 078/138] revert changes done by release --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 73297653a..6adc9e33f 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.1 + 2.0.1-SNAPSHOT jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - hap-2.0.1 + HEAD From 1a664e45e4577433cc4e657cfd9c4110212bbac5 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 11 Feb 2022 15:14:32 +0000 Subject: [PATCH 079/138] [maven-release-plugin] prepare release hap-2.0.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6adc9e33f..73297653a 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.1-SNAPSHOT + 2.0.1 jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - HEAD + hap-2.0.1 From 7dc243be6ae2c9963ee2c7ca50c925e099a1ed95 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 11 Feb 2022 15:14:33 +0000 Subject: [PATCH 080/138] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 73297653a..ba85a561c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.1 + 2.0.2-SNAPSHOT jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - hap-2.0.1 + HEAD From b8fb0f9478b6ff9f0a38fa8a2cb263d1bb98b178 Mon Sep 17 00:00:00 2001 From: Eugen Freiter Date: Thu, 17 Feb 2022 14:12:03 +0100 Subject: [PATCH 081/138] mark primary service Signed-off-by: Eugen Freiter --- .../io/github/hapjava/accessories/HomekitAccessory.java | 2 +- .../hapjava/server/impl/json/AccessoryController.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/github/hapjava/accessories/HomekitAccessory.java b/src/main/java/io/github/hapjava/accessories/HomekitAccessory.java index f61fd223d..5e2bbe3b6 100644 --- a/src/main/java/io/github/hapjava/accessories/HomekitAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/HomekitAccessory.java @@ -86,6 +86,6 @@ default Collection getServices() { * @return primary service */ default Service getPrimaryService() { - return getServices().iterator().next(); + return getServices().isEmpty() ? null : getServices().iterator().next(); }; } diff --git a/src/main/java/io/github/hapjava/server/impl/json/AccessoryController.java b/src/main/java/io/github/hapjava/server/impl/json/AccessoryController.java index ef21422af..fdd223ec3 100644 --- a/src/main/java/io/github/hapjava/server/impl/json/AccessoryController.java +++ b/src/main/java/io/github/hapjava/server/impl/json/AccessoryController.java @@ -42,7 +42,7 @@ public HttpResponse listing() throws Exception { iidLookup.putAll(swapKeyAndValue(registry.getCharacteristics(accessory.getId()))); for (Service service : servicesByInterfaceId.values()) { - serviceFutures.add(toJson(service, iidLookup)); + serviceFutures.add(toJson(service, iidLookup, accessory.getPrimaryService() == service)); } accessoryServiceFutures.put(accessory.getId(), serviceFutures); @@ -72,8 +72,8 @@ public HttpResponse listing() throws Exception { } } - private CompletableFuture toJson(Service service, Map iidLookup) - throws Exception { + private CompletableFuture toJson( + Service service, Map iidLookup, boolean isPrimary) throws Exception { String shortType = service.getType().replaceAll("^0*([0-9a-fA-F]+)-0000-1000-8000-0026BB765291$", "$1"); JsonObjectBuilder builder = @@ -103,7 +103,7 @@ private CompletableFuture toJson(Service service, Map Date: Tue, 11 Oct 2022 15:19:58 -0600 Subject: [PATCH 082/138] avoid null pointer error if a characteristic goes missing like if a characteristic has been removed, but the iOS device hasn't updated its db yet, and tries to set it --- .../server/impl/json/CharacteristicsController.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java b/src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java index 4d20a0c6b..d061a11fc 100644 --- a/src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java +++ b/src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java @@ -51,7 +51,7 @@ public HttpResponse get(HttpRequest request) throws Exception { characteristics.add(characteristic.add("aid", aid).add("iid", iid).build()); } else { logger.warn( - "Accessory " + aid + " does not have characteristic " + iid + "Request: " + uri); + "Accessory " + aid + " does not have characteristic " + iid + ". Request: " + uri); } } else { logger.warn( @@ -77,7 +77,16 @@ public HttpResponse put(HttpRequest request, HomekitClientConnection connection) JsonObject jsonCharacteristic = (JsonObject) value; int aid = jsonCharacteristic.getInt("aid"); int iid = jsonCharacteristic.getInt("iid"); - Characteristic characteristic = registry.getCharacteristics(aid).get(iid); + Map accessory = registry.getCharacteristics(aid); + if (accessory.isEmpty()) { + logger.warn("Accessory {} has no characteristics or does not exist.", aid); + return new HapJsonNoContentResponse(); + } + Characteristic characteristic = accessory.get(iid); + if (characteristic == null) { + logger.warn("Accessory {} does not have characteristic {}.", aid, iid); + return new HapJsonNoContentResponse(); + } if (jsonCharacteristic.containsKey("value")) { characteristic.setValue(jsonCharacteristic.get("value")); From 7d5f01eb99d3c2fb72a4db622bad40647e3f4c53 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Tue, 11 Oct 2022 15:21:56 -0600 Subject: [PATCH 083/138] color temperature is in mireds, not Kelvin --- .../impl/lightbulb/ColorTemperatureCharacteristic.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/ColorTemperatureCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/ColorTemperatureCharacteristic.java index 12886c7a1..987ab5eee 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/ColorTemperatureCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/ColorTemperatureCharacteristic.java @@ -9,7 +9,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -/** This characteristic describes color temperature in Kelvin */ +/** This characteristic describes color temperature in mireds */ public class ColorTemperatureCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { public static final int DEFAULT_MIN_VALUE = 50; @@ -27,7 +27,7 @@ public ColorTemperatureCharacteristic( "color temperature", minValue, maxValue, - "K", + "mired", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), From 64972c8bf860a9d200c3b26a4f530cb10c160ced Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Tue, 11 Oct 2022 15:22:09 -0600 Subject: [PATCH 084/138] make BaseCharacteristic.getDefault public --- .../hapjava/characteristics/impl/base/BaseCharacteristic.java | 2 +- .../characteristics/impl/base/BooleanCharacteristic.java | 2 +- .../hapjava/characteristics/impl/base/EnumCharacteristic.java | 2 +- .../hapjava/characteristics/impl/base/FloatCharacteristic.java | 2 +- .../characteristics/impl/base/IntegerCharacteristic.java | 2 +- .../characteristics/impl/base/StaticStringCharacteristic.java | 2 +- .../hapjava/characteristics/impl/base/StringCharacteristic.java | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java index a90f1fadc..a0a302c29 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java @@ -181,7 +181,7 @@ public void unsubscribe() { * * @return a sensible default value. */ - protected abstract T getDefault(); + public abstract T getDefault(); /** * Writes the value key to the serialized characteristic diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java index 83d7f765a..3e07936a8 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java @@ -71,7 +71,7 @@ protected void setValue(Boolean value) throws Exception { /** {@inheritDoc} */ @Override - protected Boolean getDefault() { + public Boolean getDefault() { return false; } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java index f983efe16..df3287de5 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java @@ -111,7 +111,7 @@ protected void setValue(Integer value) throws Exception { /** {@inheritDoc} */ @Override - protected Integer getDefault() { + public Integer getDefault() { // as default return first item from valid values if (validValues != null && validValues.length > 0) { return validValues[0].getCode(); diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java index e15da4fde..3781cc14d 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java @@ -135,7 +135,7 @@ protected void setValue(Double value) throws Exception { /** {@inheritDoc} */ @Override - protected Double getDefault() { + public Double getDefault() { return minValue; } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java index fcd804ccd..3ea8d87bc 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java @@ -82,7 +82,7 @@ protected void setValue(Integer value) throws Exception { /** {@inheritDoc} */ @Override - protected Integer getDefault() { + public Integer getDefault() { return minValue; } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java index d5de2f9ce..ec851bbf7 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java @@ -65,7 +65,7 @@ protected CompletableFuture getValue() { /** {@inheritDoc} */ @Override - protected String getDefault() { + public String getDefault() { return "Unknown"; } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java index 4373b06c9..5282042d4 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java @@ -74,7 +74,7 @@ protected CompletableFuture getValue() { /** {@inheritDoc} */ @Override - protected String getDefault() { + public String getDefault() { return "Unknown"; } } From daa07eecee6a04a530ac4d33623822be28c760e1 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Tue, 11 Oct 2022 15:56:18 -0600 Subject: [PATCH 085/138] fix various small bugs where a BaseCharacteristic was given null for a callback --- CHANGES.md | 3 +++ .../impl/base/BaseCharacteristic.java | 19 +++++++++++++++---- .../impl/base/FloatCharacteristic.java | 2 +- .../impl/base/IntegerCharacteristic.java | 2 +- .../impl/base/StaticStringCharacteristic.java | 2 +- .../impl/base/StringCharacteristic.java | 4 ++-- 6 files changed, 23 insertions(+), 9 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 400e99ae2..debc2dfbf 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,6 @@ +# HAP-Java 2.0.2 + * Various minor stability issues for after an accessory has been removed. + # HAP-Java 2.0.1 ## Fixes * Log accessory names instead of futures. [#150](https://github.com/hap-java/HAP-Java/issues/150) diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java index a0a302c29..77b0bc452 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java @@ -125,15 +125,26 @@ public final void setValue(JsonValue jsonValue) { try { setValue(convert(jsonValue)); } catch (Exception e) { - logger.warn("Error while setting JSON value", e); + logger.warn( + "Error while setting JSON value {} for characteristic {}", + jsonValue, + getClass().getName(), + e); } } /** {@inheritDoc} */ @Override public void supplyValue(JsonObjectBuilder builder) { + CompletableFuture futureValue = getValue(); + + if (futureValue == null) { + setJsonValue(builder, getDefault()); + return; + } + try { - setJsonValue(builder, getValue().get()); + setJsonValue(builder, futureValue.get()); } catch (InterruptedException | ExecutionException e) { logger.warn("Error retrieving value", e); setJsonValue(builder, getDefault()); @@ -143,13 +154,13 @@ public void supplyValue(JsonObjectBuilder builder) { /** {@inheritDoc} */ @Override public void subscribe(HomekitCharacteristicChangeCallback callback) { - subscriber.get().accept(callback); + subscriber.ifPresent(s -> s.accept(callback)); } /** {@inheritDoc} */ @Override public void unsubscribe() { - unsubscriber.get().run(); + unsubscriber.ifPresent(u -> u.run()); } /** diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java index 3781cc14d..a8a98dc27 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java @@ -130,7 +130,7 @@ protected final CompletableFuture getValue() { @Override protected void setValue(Double value) throws Exception { - setter.get().accept(value); + if (setter.isPresent()) setter.get().accept(value); } /** {@inheritDoc} */ diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java index 3ea8d87bc..22a6dce83 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java @@ -77,7 +77,7 @@ protected CompletableFuture getValue() { @Override protected void setValue(Integer value) throws Exception { - setter.get().accept(value); + if (setter.isPresent()) setter.get().accept(value); } /** {@inheritDoc} */ diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java index ec851bbf7..576df7e0e 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java @@ -60,7 +60,7 @@ public void setValue(String value) throws Exception { /** {@inheritDoc} */ @Override protected CompletableFuture getValue() { - return getter.map(stringGetter -> stringGetter.get()).get(); + return getter.map(stringGetter -> stringGetter.get()).orElse(null); } /** {@inheritDoc} */ diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java index 5282042d4..65cf45a71 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java @@ -63,13 +63,13 @@ public String convert(JsonValue jsonValue) { /** {@inheritDoc} */ @Override public void setValue(String value) throws Exception { - setter.get().accept(value); + if (setter.isPresent()) setter.get().accept(value); } /** {@inheritDoc} */ @Override protected CompletableFuture getValue() { - return getter.map(stringGetter -> stringGetter.get()).get(); + return getter.map(stringGetter -> stringGetter.get()).orElse(null); } /** {@inheritDoc} */ From f28f6082b5148fa9c9ea3fec0886f10968756c7b Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 12 Oct 2022 14:35:31 +0000 Subject: [PATCH 086/138] [maven-release-plugin] prepare release hap-2.0.2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ba85a561c..79bdd1c69 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.2-SNAPSHOT + 2.0.2 jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - HEAD + hap-2.0.2 From 0ae78f684bef1c7051bebeb998c4326898bf92a9 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 12 Oct 2022 14:35:33 +0000 Subject: [PATCH 087/138] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 79bdd1c69..b682cf656 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.2 + 2.0.3-SNAPSHOT jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - hap-2.0.2 + HEAD From 781311e94c7967080119daec41d288a3a90b7691 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Wed, 12 Oct 2022 08:51:02 -0600 Subject: [PATCH 088/138] update git user for release action see https://github.com/actions/checkout/issues/13#issuecomment-724415212 also update the actions to non-deprecated versions --- .github/workflows/deploy.yml | 9 +++++---- .github/workflows/maven.yml | 5 +++-- .github/workflows/release.yml | 9 +++++---- RELEASING.md | 2 +- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d439198c7..ba402983e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -13,10 +13,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up JDK 11 for Maven Central - uses: actions/setup-java@v1 + uses: actions/setup-java@v2 with: + distribution: 'zulu' java-version: '11' architecture: x64 server-id: ossrh @@ -26,8 +27,8 @@ jobs: server-password: MAVEN_CENTRAL_TOKEN - name: Setup git profile run: | - git config --global user.name github-actions - git config --global user.email github-actions@github.com + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" - name: Publish Snapshot to Maven Central run: mvn deploy -B -Possrh env: diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index a07bff253..c7f0c8de8 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -13,10 +13,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up JDK 11 - uses: actions/setup-java@v1 + uses: actions/setup-java@v2 with: + distribution: 'zulu' java-version: '11' architecture: x64 - name: Build with Maven diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0aa72794f..3e1ae1875 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,10 +16,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up JDK 11 - uses: actions/setup-java@v1 + uses: actions/setup-java@v2 with: + distribution: 'zulu' java-version: '11' architecture: x64 server-id: ossrh @@ -29,8 +30,8 @@ jobs: server-password: MAVEN_CENTRAL_TOKEN - name: Setup git profile run: | - git config user.name github-actions - git config user.email github-actions@github.com + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" - name: Maven release run: mvn release:prepare release:perform -B -DreleaseVersion=${{ github.event.inputs.releaseVersion }} -Possrh env: diff --git a/RELEASING.md b/RELEASING.md index a3001c9f3..6dd790693 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -2,6 +2,6 @@ These actions can only be performed by someone with maintainer level access to the repository. -1. Run the [Stage Release on Master Branch](https://github.com/hap-java/HAP-Java/actions/workflows/release.yml) Action +1. Run the [Stage Release on Master Branch](https://github.com/hap-java/HAP-Java/actions/workflows/release.yml) Action with a version such as `2.0.2` 2. After this completes, find the release on the [Releases page](https://github.com/hap-java/HAP-Java/releases) 3. Edit the release to include the changelog details From 17f87f1ff5e1d5a43672950ac4f6aa3a2f935d15 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Fri, 7 Oct 2022 12:47:34 -0600 Subject: [PATCH 089/138] add back fanv1 service the Home app doesn't let you customize the icon for a FanV2 (i.e. to show a ceiling fan). --- .../accessories/BasicFanAccessory.java | 46 +++++++++++++ .../services/impl/BasicFanService.java | 65 +++++++++++++++++++ .../hapjava/services/impl/FanService.java | 7 +- 3 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 src/main/java/io/github/hapjava/accessories/BasicFanAccessory.java create mode 100644 src/main/java/io/github/hapjava/services/impl/BasicFanService.java diff --git a/src/main/java/io/github/hapjava/accessories/BasicFanAccessory.java b/src/main/java/io/github/hapjava/accessories/BasicFanAccessory.java new file mode 100644 index 000000000..98c4bf497 --- /dev/null +++ b/src/main/java/io/github/hapjava/accessories/BasicFanAccessory.java @@ -0,0 +1,46 @@ +package io.github.hapjava.accessories; + +import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback; +import io.github.hapjava.services.Service; +import io.github.hapjava.services.impl.BasicFanService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +/** + * A fan with mandatory characteristics. + * + * @author Andy Lintner + */ +public interface BasicFanAccessory extends HomekitAccessory { + /** + * Mandatory: Retrieves the current power state of the fan. + * + * @return a future that will contain the binary state + */ + CompletableFuture isOn(); + + /** + * Sets the active state of the fan + * + * @param state the binary state to set + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setOn(boolean state) throws Exception; + + /** + * Subscribes to changes in the active state of the fan. + * + * @param callback the function to call when the active state changes. + */ + void subscribeOn(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the active state of the fan. */ + void unsubscribeOn(); + + @Override + default Collection getServices() { + return Collections.singleton(new BasicFanService(this)); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/BasicFanService.java b/src/main/java/io/github/hapjava/services/impl/BasicFanService.java new file mode 100644 index 000000000..9a033dd0c --- /dev/null +++ b/src/main/java/io/github/hapjava/services/impl/BasicFanService.java @@ -0,0 +1,65 @@ +package io.github.hapjava.services.impl; + +import io.github.hapjava.accessories.BasicFanAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithRotationDirection; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithRotationSpeed; +import io.github.hapjava.characteristics.impl.common.NameCharacteristic; +import io.github.hapjava.characteristics.impl.common.OnCharacteristic; +import io.github.hapjava.characteristics.impl.fan.RotationDirectionCharacteristic; +import io.github.hapjava.characteristics.impl.fan.RotationSpeedCharacteristic; + +/** + * This service describes a fan. + * + *

In the R1 release of the HAP specification, this is simply described as Fan. It is no longer + * present in the R2 release. + */ +public class BasicFanService extends AbstractServiceImpl { + + public BasicFanService(OnCharacteristic on) { + super("00000040-0000-1000-8000-0026BB765291"); + addCharacteristic(on); + } + + public BasicFanService(BasicFanAccessory accessory) { + this( + new OnCharacteristic( + () -> accessory.isOn(), + (v) -> accessory.setOn(v), + accessory::subscribeOn, + accessory::unsubscribeOn)); + if (accessory instanceof AccessoryWithName) { + addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName)); + } + + if (accessory instanceof AccessoryWithRotationDirection) { + addOptionalCharacteristic( + new RotationDirectionCharacteristic( + ((AccessoryWithRotationDirection) accessory)::getRotationDirection, + ((AccessoryWithRotationDirection) accessory)::setRotationDirection, + ((AccessoryWithRotationDirection) accessory)::subscribeRotationDirection, + ((AccessoryWithRotationDirection) accessory)::unsubscribeRotationDirection)); + } + if (accessory instanceof AccessoryWithRotationSpeed) { + addOptionalCharacteristic( + new RotationSpeedCharacteristic( + ((AccessoryWithRotationSpeed) accessory)::getRotationSpeed, + ((AccessoryWithRotationSpeed) accessory)::setRotationSpeed, + ((AccessoryWithRotationSpeed) accessory)::subscribeRotationSpeed, + ((AccessoryWithRotationSpeed) accessory)::unsubscribeRotationSpeed)); + } + } + + public void addOptionalCharacteristic(NameCharacteristic name) { + addCharacteristic(name); + } + + public void addOptionalCharacteristic(RotationDirectionCharacteristic direction) { + addCharacteristic(direction); + } + + public void addOptionalCharacteristic(RotationSpeedCharacteristic speed) { + addCharacteristic(speed); + } +} diff --git a/src/main/java/io/github/hapjava/services/impl/FanService.java b/src/main/java/io/github/hapjava/services/impl/FanService.java index 24d2bc052..a8f3a7103 100644 --- a/src/main/java/io/github/hapjava/services/impl/FanService.java +++ b/src/main/java/io/github/hapjava/services/impl/FanService.java @@ -17,7 +17,12 @@ import io.github.hapjava.characteristics.impl.fan.SwingModeCharacteristic; import io.github.hapjava.characteristics.impl.fan.TargetFanStateCharacteristic; -/** This service describes a fan. */ +/** + * This service describes a fan. + * + *

In the R1 release of the HAP specification, this is described as Fan v2. In the R1 release of + * the HAP specification, this is simply described as Fan. + */ public class FanService extends AbstractServiceImpl { public FanService(ActiveCharacteristic active) { From 81ecb5cf402d10c11a6854ceba924dc9f04bab4b Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Tue, 18 Oct 2022 09:45:26 -0600 Subject: [PATCH 090/138] exclude characteristic description for well-known types they're optional, and they just take up extra data --- .../hapjava/characteristics/impl/base/BaseCharacteristic.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java index 77b0bc452..734a83cdc 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java @@ -112,8 +112,8 @@ protected CompletableFuture makeBuilder(int instanceId) { .add("iid", instanceId) .add("type", shortType) .add("perms", perms.build()) - .add("format", format) - .add("description", description); + .add("format", format); + if (shortType.length() == type.length()) builder.add("description", description); if (isReadable) setJsonValue(builder, value); return builder; }); From 91b5a3caac574d6b50f100bd6f541e660f256494 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Fri, 14 Oct 2022 13:14:19 -0600 Subject: [PATCH 091/138] don't reset connections just cause an accessory was added or removed the caller is expected to updated the configuration index, at which point iOS will shortly request the new accessory information. --- CHANGES.md | 3 +++ .../io/github/hapjava/server/impl/HomekitRoot.java | 2 -- .../io/github/hapjava/server/impl/HomekitRootTest.java | 10 ++++------ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index debc2dfbf..67f610bed 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,6 @@ +# HAP-Java 2.0.3 +* Avoid unnecessary forced disconnects. Library users should be updating the configuration index anyway. + # HAP-Java 2.0.2 * Various minor stability issues for after an accessory has been removed. diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java index ca182f812..df77afd07 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java @@ -112,7 +112,6 @@ void addAccessorySkipRangeCheck(HomekitAccessory accessory) { } if (started) { registry.reset(); - webHandler.resetConnections(); } } @@ -130,7 +129,6 @@ public void removeAccessory(HomekitAccessory accessory) { } if (started) { registry.reset(); - webHandler.resetConnections(); } } diff --git a/src/test/java/io/github/hapjava/server/impl/HomekitRootTest.java b/src/test/java/io/github/hapjava/server/impl/HomekitRootTest.java index 3ac40288a..81d2666e5 100644 --- a/src/test/java/io/github/hapjava/server/impl/HomekitRootTest.java +++ b/src/test/java/io/github/hapjava/server/impl/HomekitRootTest.java @@ -91,20 +91,18 @@ public void testAdvertiserStops() throws Exception { } @Test - public void testAddAccessoryResetsWeb() { + public void testAddAccessoryDoesntResetWeb() { root.start(); - verify(webHandler, never()).resetConnections(); root.addAccessory(accessory); - verify(webHandler).resetConnections(); + verify(webHandler, never()).resetConnections(); } @Test - public void testRemoveAccessoryResetsWeb() { + public void testRemoveAccessoryDoesntResetWeb() { root.addAccessory(accessory); root.start(); - verify(webHandler, never()).resetConnections(); root.removeAccessory(accessory); - verify(webHandler).resetConnections(); + verify(webHandler, never()).resetConnections(); } @Test(expected = IndexOutOfBoundsException.class) From 0226861d9e71eddde518ee530e90b08a9f3353e6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 18 Oct 2022 16:03:10 +0000 Subject: [PATCH 092/138] [maven-release-plugin] prepare release hap-2.0.3 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b682cf656..d58839503 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.3-SNAPSHOT + 2.0.3 jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - HEAD + hap-2.0.3 From de16264232b68dabc8646cc9d619a435ee759488 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 18 Oct 2022 16:03:11 +0000 Subject: [PATCH 093/138] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d58839503..7e7851c32 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.3 + 2.0.4-SNAPSHOT jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - hap-2.0.3 + HEAD From 0e4db0b6532fae8871684c7be8b821b75601394d Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Mon, 24 Oct 2022 13:47:10 -0600 Subject: [PATCH 094/138] keep subscriptions up to date when accessories are replaced but the connection is _not_ torn down also add the ability to modify accessories in a batch without having to regenerate all AIDs and IIDs (and other temporary data structures) at each intermediate step. You must use this mechanism if you plan on replacing an existing accessory with an equivalent one, but want to keep subscriptions intact. --- .../characteristics/Characteristic.java | 2 + .../impl/base/BaseCharacteristic.java | 6 + .../hapjava/server/impl/HomekitRegistry.java | 10 +- .../hapjava/server/impl/HomekitRoot.java | 44 ++-- .../impl/connections/SubscriptionManager.java | 231 +++++++++++++----- .../server/impl/json/EventController.java | 4 +- 6 files changed, 213 insertions(+), 84 deletions(-) diff --git a/src/main/java/io/github/hapjava/characteristics/Characteristic.java b/src/main/java/io/github/hapjava/characteristics/Characteristic.java index f73bf345b..d2e87030a 100644 --- a/src/main/java/io/github/hapjava/characteristics/Characteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/Characteristic.java @@ -18,6 +18,8 @@ * @author Andy Lintner */ public interface Characteristic { + /** @return The UUID type for this characteristic. */ + String getType(); /** * Adds an attribute to the passed JsonObjectBuilder named "value" with the current value of the diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java index 734a83cdc..1d2d55c5b 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java @@ -70,6 +70,12 @@ public BaseCharacteristic( this.unsubscriber = unsubscriber; } + @Override + /** {@inheritDoc} */ + public String getType() { + return type; + } + @Override /** {@inheritDoc} */ public final CompletableFuture toJson(int iid) { diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java b/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java index 39ec31ff9..cc6cd352e 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java @@ -2,6 +2,7 @@ import io.github.hapjava.accessories.HomekitAccessory; import io.github.hapjava.characteristics.Characteristic; +import io.github.hapjava.server.impl.connections.SubscriptionManager; import io.github.hapjava.services.Service; import io.github.hapjava.services.impl.AccessoryInformationService; import java.util.ArrayList; @@ -19,14 +20,16 @@ public class HomekitRegistry { private static final Logger logger = LoggerFactory.getLogger(HomekitRegistry.class); private final String label; + private final SubscriptionManager subscriptions; private final Map accessories; private final Map> services = new HashMap<>(); private final Map> characteristics = new HashMap<>(); private boolean isAllowUnauthenticatedRequests = false; - public HomekitRegistry(String label) { + public HomekitRegistry(String label, SubscriptionManager subscriptions) { this.label = label; + this.subscriptions = subscriptions; this.accessories = new ConcurrentHashMap<>(); reset(); } @@ -61,6 +64,7 @@ public synchronized void reset() { services.put(accessory, newServicesByInterfaceId); characteristics.put(accessory, newCharacteristicsByInterfaceId); } + subscriptions.resync(this); } public String getLabel() { @@ -87,8 +91,8 @@ public void add(HomekitAccessory accessory) { accessories.put(accessory.getId(), accessory); } - public void remove(HomekitAccessory accessory) { - accessories.remove(accessory.getId()); + public boolean remove(HomekitAccessory accessory) { + return accessories.remove(accessory.getId()) != null; } public boolean isAllowUnauthenticatedRequests() { diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java index df77afd07..a80f0723e 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java @@ -36,6 +36,8 @@ public class HomekitRoot { private final SubscriptionManager subscriptions = new SubscriptionManager(); private boolean started = false; private int configurationIndex = 1; + private int nestedBatches = 0; + private boolean madeChanges = false; HomekitRoot( String label, HomekitWebHandler webHandler, InetAddress host, HomekitAuthInfo authInfo) @@ -65,7 +67,7 @@ public class HomekitRoot { this.authInfo = authInfo; this.label = label; this.category = category; - this.registry = new HomekitRegistry(label); + this.registry = new HomekitRegistry(label, subscriptions); } HomekitRoot( @@ -83,11 +85,19 @@ public class HomekitRoot { this( label, DEFAULT_ACCESSORY_CATEGORY, webHandler, authInfo, new JmdnsHomekitAdvertiser(jmdns)); } + + public synchronized void batchUpdate() { + if (this.nestedBatches == 0) madeChanges = false; + ++this.nestedBatches; + } + + public synchronized void completeUpdateBatch() { + if (--this.nestedBatches == 0 && madeChanges) registry.reset(); + } + /** - * Add an accessory to be handled and advertised by this root. Any existing HomeKit connections - * will be terminated to allow the clients to reconnect and see the updated accessory list. When - * using this for a bridge, the ID of the accessory must be greater than 1, as that ID is reserved - * for the Bridge itself. + * Add an accessory to be handled and advertised by this root. When using this for a bridge, the + * ID of the accessory must be greater than 1, as that ID is reserved for the Bridge itself. * * @param accessory to advertise and handle. */ @@ -110,25 +120,28 @@ void addAccessorySkipRangeCheck(HomekitAccessory accessory) { if (logger.isTraceEnabled()) { accessory.getName().thenAccept(name -> logger.trace("Added accessory {}", name)); } - if (started) { + madeChanges = true; + if (started && nestedBatches == 0) { registry.reset(); } } /** - * Removes an accessory from being handled or advertised by this root. Any existing HomeKit - * connections will be terminated to allow the clients to reconnect and see the updated accessory - * list. + * Removes an accessory from being handled or advertised by this root. * * @param accessory accessory to cease advertising and handling */ public void removeAccessory(HomekitAccessory accessory) { - this.registry.remove(accessory); - if (logger.isTraceEnabled()) { - accessory.getName().thenAccept(name -> logger.trace("Removed accessory {}", name)); - } - if (started) { - registry.reset(); + if (this.registry.remove(accessory)) { + if (logger.isTraceEnabled()) { + accessory.getName().thenAccept(name -> logger.trace("Removed accessory {}", name)); + } + madeChanges = true; + if (started && nestedBatches == 0) { + registry.reset(); + } + } else { + accessory.getName().thenAccept(name -> logger.warn("Could not remove accessory {}", name)); } } @@ -140,6 +153,7 @@ public void removeAccessory(HomekitAccessory accessory) { */ public void start() { started = true; + madeChanges = false; registry.reset(); webHandler .start( diff --git a/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java b/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java index 25f68526c..72ac27ba8 100644 --- a/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java @@ -1,15 +1,18 @@ package io.github.hapjava.server.impl.connections; +import io.github.hapjava.characteristics.Characteristic; import io.github.hapjava.characteristics.EventableCharacteristic; +import io.github.hapjava.server.impl.HomekitRegistry; import io.github.hapjava.server.impl.http.HomekitClientConnection; import io.github.hapjava.server.impl.http.HttpResponse; import io.github.hapjava.server.impl.json.EventController; import java.util.ArrayList; -import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; +import java.util.List; +import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,12 +20,22 @@ public class SubscriptionManager { private static final Logger LOGGER = LoggerFactory.getLogger(SubscriptionManager.class); - private final ConcurrentMap> subscriptions = - new ConcurrentHashMap<>(); - private final ConcurrentMap> reverse = - new ConcurrentHashMap<>(); - private final ConcurrentMap> - pendingNotifications = new ConcurrentHashMap<>(); + private static class ConnectionsWithIds { + Set connections; + int aid, iid; + + ConnectionsWithIds(int aid, int iid) { + this.aid = aid; + this.iid = iid; + this.connections = new HashSet<>(); + } + } + + private final Map subscriptions = new HashMap<>(); + private final Map> reverse = + new HashMap<>(); + private final Map> pendingNotifications = + new HashMap<>(); private int nestedBatches = 0; public synchronized void addSubscription( @@ -31,64 +44,75 @@ public synchronized void addSubscription( EventableCharacteristic characteristic, HomekitClientConnection connection) { synchronized (this) { - if (!subscriptions.containsKey(characteristic)) { - subscriptions.putIfAbsent(characteristic, newSet()); - } - subscriptions.get(characteristic).add(connection); - if (subscriptions.get(characteristic).size() == 1) { - characteristic.subscribe( - () -> { - publish(aid, iid, characteristic); - }); + ConnectionsWithIds subscribers; + if (subscriptions.containsKey(characteristic)) { + subscribers = subscriptions.get(characteristic); + } else { + subscribers = new ConnectionsWithIds(aid, iid); + subscriptions.put(characteristic, subscribers); + subscribe(aid, iid, characteristic); } + subscribers.connections.add(connection); if (!reverse.containsKey(connection)) { - reverse.putIfAbsent(connection, newSet()); + reverse.put(connection, new HashSet<>()); } reverse.get(connection).add(characteristic); - LOGGER.trace( - "Added subscription to " + characteristic.getClass() + " for " + connection.hashCode()); + LOGGER.debug( + "Added subscription to {}:{} ({}) for {}", + aid, + iid, + characteristic.getClass().getSimpleName(), + connection.hashCode()); } } public synchronized void removeSubscription( EventableCharacteristic characteristic, HomekitClientConnection connection) { - Set subscriptions = this.subscriptions.get(characteristic); - if (subscriptions != null) { - subscriptions.remove(connection); - if (subscriptions.size() == 0) { + ConnectionsWithIds subscribers = subscriptions.get(characteristic); + if (subscribers != null) { + subscribers.connections.remove(connection); + if (subscribers.connections.isEmpty()) { + LOGGER.debug("Unsubscribing from characteristic as all subscriptions are closed"); characteristic.unsubscribe(); + subscriptions.remove(characteristic); + } + + // Remove pending notifications for this no-longer-subscribed characteristic + List connectionNotifications = pendingNotifications.get(connection); + if (connectionNotifications != null) { + connectionNotifications.removeIf(n -> n.aid == subscribers.aid && n.iid == subscribers.iid); + if (connectionNotifications.isEmpty()) pendingNotifications.remove(connection); } + + LOGGER.debug( + "Removed subscription from {}:{} ({}) for {}", + subscribers.aid, + subscribers.iid, + characteristic.getClass().getSimpleName(), + connection.hashCode()); } Set reverse = this.reverse.get(connection); if (reverse != null) { reverse.remove(characteristic); + if (reverse.isEmpty()) this.reverse.remove(connection); } - LOGGER.trace( - "Removed subscription to " + characteristic.getClass() + " for " + connection.hashCode()); } public synchronized void removeConnection(HomekitClientConnection connection) { - Set characteristics = reverse.remove(connection); + removeConnection(connection, reverse.remove(connection)); + } + + private void removeConnection( + HomekitClientConnection connection, Set characteristics) { pendingNotifications.remove(connection); if (characteristics != null) { for (EventableCharacteristic characteristic : characteristics) { - Set characteristicSubscriptions = - subscriptions.get(characteristic); - characteristicSubscriptions.remove(connection); - if (characteristicSubscriptions.isEmpty()) { - LOGGER.trace("Unsubscribing from characteristic as all subscriptions are closed"); - characteristic.unsubscribe(); - subscriptions.remove(characteristic); - } + removeSubscription(characteristic, connection); } } - LOGGER.trace("Removed connection {}", connection.hashCode()); - } - - private Set newSet() { - return Collections.newSetFromMap(new ConcurrentHashMap()); + LOGGER.debug("Removed connection {}", connection.hashCode()); } public synchronized void batchUpdate() { @@ -96,31 +120,35 @@ public synchronized void batchUpdate() { } public synchronized void completeUpdateBatch() { - if (--this.nestedBatches == 0 && !pendingNotifications.isEmpty()) { - LOGGER.trace("Publishing batched changes"); - for (ConcurrentMap.Entry> entry : - pendingNotifications.entrySet()) { - try { - HttpResponse message = new EventController().getMessage(entry.getValue()); - entry.getKey().outOfBand(message); - } catch (Exception e) { - LOGGER.warn("Failed to create new event message", e); - } + if (--this.nestedBatches == 0) flushUpdateBatch(); + } + + private void flushUpdateBatch() { + if (pendingNotifications.isEmpty()) return; + + LOGGER.trace("Publishing batched changes"); + for (Map.Entry> entry : + pendingNotifications.entrySet()) { + try { + HttpResponse message = new EventController().getMessage(entry.getValue()); + entry.getKey().outOfBand(message); + } catch (Exception e) { + LOGGER.warn("Failed to create new event message", e); } - pendingNotifications.clear(); } + pendingNotifications.clear(); } public synchronized void publish(int accessoryId, int iid, EventableCharacteristic changed) { - final Set subscribers = subscriptions.get(changed); - if ((subscribers == null) || (subscribers.isEmpty())) { - LOGGER.debug("No subscribers to characteristic {} at accessory {} ", changed, accessoryId); + final ConnectionsWithIds subscribers = subscriptions.get(changed); + if (subscribers == null || subscribers.connections.isEmpty()) { + LOGGER.trace("No subscribers to characteristic {} at accessory {} ", changed, accessoryId); return; // no subscribers } if (nestedBatches != 0) { LOGGER.trace("Batching change for accessory {} and characteristic {} " + accessoryId, iid); PendingNotification notification = new PendingNotification(accessoryId, iid, changed); - for (HomekitClientConnection connection : subscribers) { + for (HomekitClientConnection connection : subscribers.connections) { if (!pendingNotifications.containsKey(connection)) { pendingNotifications.put(connection, new ArrayList()); } @@ -132,7 +160,7 @@ public synchronized void publish(int accessoryId, int iid, EventableCharacterist try { HttpResponse message = new EventController().getMessage(accessoryId, iid, changed); LOGGER.trace("Publishing change for " + accessoryId); - for (HomekitClientConnection connection : subscribers) { + for (HomekitClientConnection connection : subscribers.connections) { connection.outOfBand(message); } } catch (Exception e) { @@ -140,15 +168,90 @@ public synchronized void publish(int accessoryId, int iid, EventableCharacterist } } + /** + * The accessory registry has changed; go through all subscriptions and link to any new/changed + * characteristics + */ + public synchronized void resync(HomekitRegistry registry) { + LOGGER.debug("Resyncing subscriptions"); + flushUpdateBatch(); + + Map newSubscriptions = new HashMap<>(); + Iterator> i = + subscriptions.entrySet().iterator(); + while (i.hasNext()) { + Map.Entry entry = i.next(); + EventableCharacteristic oldCharacteristic = entry.getKey(); + ConnectionsWithIds subscribers = entry.getValue(); + Characteristic newCharacteristic = + registry.getCharacteristics(subscribers.aid).get(subscribers.iid); + if (newCharacteristic == null || newCharacteristic.getType() != oldCharacteristic.getType()) { + // characteristic is gone or has completely changed; drop all subscriptions for it + LOGGER.debug( + "{}:{} ({}) has gone missing; dropping subscriptions.", + subscribers.aid, + subscribers.iid, + oldCharacteristic.getClass().getSimpleName()); + i.remove(); + for (HomekitClientConnection conn : subscribers.connections) { + removeSubscription(oldCharacteristic, conn); + } + } else if (newCharacteristic != oldCharacteristic) { + EventableCharacteristic newEventableCharacteristic = + (EventableCharacteristic) newCharacteristic; + LOGGER.debug( + "{}:{} has been replaced by a compatible characteristic; re-connecting subscriptions", + subscribers.aid, + subscribers.iid); + // characteristic has been replaced by another instance of the same thing; + // re-connect subscriptions + i.remove(); + oldCharacteristic.unsubscribe(); + subscribe(subscribers.aid, subscribers.iid, newEventableCharacteristic); + // we can't replace the key, and we can't add to the map while we're iterating, + // so save it off to a temporary map that we'll add them all at the end + newSubscriptions.put(newEventableCharacteristic, subscribers); + + for (HomekitClientConnection conn : subscribers.connections) { + Set subscribedCharacteristics = reverse.get(conn); + subscribedCharacteristics.remove(oldCharacteristic); + subscribedCharacteristics.add(newEventableCharacteristic); + + // and also update references for any pending notifications, so they'll get the proper + // value + List connectionPendingNotifications = pendingNotifications.get(conn); + if (connectionPendingNotifications != null) { + for (PendingNotification notification : connectionPendingNotifications) { + if (notification.characteristic == oldCharacteristic) { + notification.characteristic = newEventableCharacteristic; + } + } + } + } + } + } + subscriptions.putAll(newSubscriptions); + } + + private void subscribe(int aid, int iid, EventableCharacteristic characteristic) { + characteristic.subscribe( + () -> { + publish(aid, iid, characteristic); + }); + } + /** Remove all existing subscriptions */ - public void removeAll() { - LOGGER.trace("Removing {} reverse connections from subscription manager", reverse.size()); - Iterator i = reverse.keySet().iterator(); + public synchronized void removeAll() { + LOGGER.debug("Removing {} reverse connections from subscription manager", reverse.size()); + Iterator>> i = + reverse.entrySet().iterator(); while (i.hasNext()) { - HomekitClientConnection connection = i.next(); - LOGGER.trace("Removing connection {}", connection.hashCode()); - removeConnection(connection); + Map.Entry> entry = i.next(); + HomekitClientConnection connection = entry.getKey(); + LOGGER.debug("Removing connection {}", connection.hashCode()); + i.remove(); + removeConnection(connection, entry.getValue()); } - LOGGER.trace("Subscription sizes are {} and {}", reverse.size(), subscriptions.size()); + LOGGER.debug("Subscription sizes are {} and {}", reverse.size(), subscriptions.size()); } } diff --git a/src/main/java/io/github/hapjava/server/impl/json/EventController.java b/src/main/java/io/github/hapjava/server/impl/json/EventController.java index 41473f716..0c7c2bc7c 100644 --- a/src/main/java/io/github/hapjava/server/impl/json/EventController.java +++ b/src/main/java/io/github/hapjava/server/impl/json/EventController.java @@ -4,7 +4,7 @@ import io.github.hapjava.server.impl.connections.PendingNotification; import io.github.hapjava.server.impl.http.HttpResponse; import java.io.ByteArrayOutputStream; -import java.util.ArrayList; +import java.util.List; import javax.json.Json; import javax.json.JsonArrayBuilder; import javax.json.JsonObject; @@ -34,7 +34,7 @@ public HttpResponse getMessage(int accessoryId, int iid, EventableCharacteristic } } - public HttpResponse getMessage(ArrayList notifications) throws Exception { + public HttpResponse getMessage(List notifications) throws Exception { JsonArrayBuilder characteristics = Json.createArrayBuilder(); for (PendingNotification notification : notifications) { From a781e8518fb257bb9794b0ce47b5d00fb7cae64d Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Thu, 17 Nov 2022 14:04:46 -0700 Subject: [PATCH 095/138] address review comments --- .../hapjava/server/impl/HomekitRoot.java | 8 ++++++++ .../impl/connections/SubscriptionManager.java | 20 +++++++++---------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java index a80f0723e..1750a8d3e 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java @@ -86,11 +86,19 @@ public class HomekitRoot { label, DEFAULT_ACCESSORY_CATEGORY, webHandler, authInfo, new JmdnsHomekitAdvertiser(jmdns)); } + /** + * Begin a batch update of accessories. + * + *

After calling this, you can call addAccessory() and removeAccessory() multiple times without + * causing HAP-Java to re-publishing the metadata to HomeKit. You'll need to call + * completeUpdateBatch in order to publish all accumulated changes. + */ public synchronized void batchUpdate() { if (this.nestedBatches == 0) madeChanges = false; ++this.nestedBatches; } + /** Publish accumulated accessory changes since batchUpdate() was called. */ public synchronized void completeUpdateBatch() { if (--this.nestedBatches == 0 && madeChanges) registry.reset(); } diff --git a/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java b/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java index 72ac27ba8..9399295fb 100644 --- a/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/SubscriptionManager.java @@ -58,7 +58,7 @@ public synchronized void addSubscription( reverse.put(connection, new HashSet<>()); } reverse.get(connection).add(characteristic); - LOGGER.debug( + LOGGER.trace( "Added subscription to {}:{} ({}) for {}", aid, iid, @@ -73,7 +73,7 @@ public synchronized void removeSubscription( if (subscribers != null) { subscribers.connections.remove(connection); if (subscribers.connections.isEmpty()) { - LOGGER.debug("Unsubscribing from characteristic as all subscriptions are closed"); + LOGGER.trace("Unsubscribing from characteristic as all subscriptions are closed"); characteristic.unsubscribe(); subscriptions.remove(characteristic); } @@ -85,7 +85,7 @@ public synchronized void removeSubscription( if (connectionNotifications.isEmpty()) pendingNotifications.remove(connection); } - LOGGER.debug( + LOGGER.trace( "Removed subscription from {}:{} ({}) for {}", subscribers.aid, subscribers.iid, @@ -112,7 +112,7 @@ private void removeConnection( removeSubscription(characteristic, connection); } } - LOGGER.debug("Removed connection {}", connection.hashCode()); + LOGGER.trace("Removed connection {}", connection.hashCode()); } public synchronized void batchUpdate() { @@ -173,7 +173,7 @@ public synchronized void publish(int accessoryId, int iid, EventableCharacterist * characteristics */ public synchronized void resync(HomekitRegistry registry) { - LOGGER.debug("Resyncing subscriptions"); + LOGGER.trace("Resyncing subscriptions"); flushUpdateBatch(); Map newSubscriptions = new HashMap<>(); @@ -187,7 +187,7 @@ public synchronized void resync(HomekitRegistry registry) { registry.getCharacteristics(subscribers.aid).get(subscribers.iid); if (newCharacteristic == null || newCharacteristic.getType() != oldCharacteristic.getType()) { // characteristic is gone or has completely changed; drop all subscriptions for it - LOGGER.debug( + LOGGER.trace( "{}:{} ({}) has gone missing; dropping subscriptions.", subscribers.aid, subscribers.iid, @@ -199,7 +199,7 @@ public synchronized void resync(HomekitRegistry registry) { } else if (newCharacteristic != oldCharacteristic) { EventableCharacteristic newEventableCharacteristic = (EventableCharacteristic) newCharacteristic; - LOGGER.debug( + LOGGER.trace( "{}:{} has been replaced by a compatible characteristic; re-connecting subscriptions", subscribers.aid, subscribers.iid); @@ -242,16 +242,16 @@ private void subscribe(int aid, int iid, EventableCharacteristic characteristic) /** Remove all existing subscriptions */ public synchronized void removeAll() { - LOGGER.debug("Removing {} reverse connections from subscription manager", reverse.size()); + LOGGER.trace("Removing {} reverse connections from subscription manager", reverse.size()); Iterator>> i = reverse.entrySet().iterator(); while (i.hasNext()) { Map.Entry> entry = i.next(); HomekitClientConnection connection = entry.getKey(); - LOGGER.debug("Removing connection {}", connection.hashCode()); + LOGGER.trace("Removing connection {}", connection.hashCode()); i.remove(); removeConnection(connection, entry.getValue()); } - LOGGER.debug("Subscription sizes are {} and {}", reverse.size(), subscriptions.size()); + LOGGER.trace("Subscription sizes are {} and {}", reverse.size(), subscriptions.size()); } } From a559f7c5408aacc0f35f8d0515a9df733dec3797 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 23 Nov 2022 21:19:51 +0000 Subject: [PATCH 096/138] [maven-release-plugin] prepare release hap-2.0.4 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7e7851c32..e37ea6efc 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.4-SNAPSHOT + 2.0.4 jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - HEAD + hap-2.0.4 From 4038b55d5a38c450b7afacebc3ab9950b768363c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 23 Nov 2022 21:19:53 +0000 Subject: [PATCH 097/138] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e37ea6efc..81102f179 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.4 + 2.0.5-SNAPSHOT jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - hap-2.0.4 + HEAD From 2656dfdec98b67aa85a274571c1516345ec00a30 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Wed, 23 Nov 2022 14:39:22 -0700 Subject: [PATCH 098/138] update github actions to setup-java@v3 no breaking changes --- .github/workflows/deploy.yml | 2 +- .github/workflows/maven.yml | 2 +- .github/workflows/release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ba402983e..61753eab7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Set up JDK 11 for Maven Central - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'zulu' java-version: '11' diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index c7f0c8de8..6f597043c 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Set up JDK 11 - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'zulu' java-version: '11' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3e1ae1875..804e4b2bd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Set up JDK 11 - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'zulu' java-version: '11' From f04a4d6345b6dd94f7ed19874f98b539742c9126 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Fri, 16 Dec 2022 14:22:29 -0700 Subject: [PATCH 099/138] implement List Pairings request also keep track of admin permissions for users note that those permissions aren't actually enforced yet --- CHANGES.md | 3 ++ .../hapjava/server/HomekitAuthInfo.java | 38 ++++++++++++++++++- .../server/impl/connections/HttpSession.java | 2 +- .../server/impl/pairing/FinalPairHandler.java | 3 +- .../server/impl/pairing/MessageType.java | 7 +++- .../server/impl/pairing/PairingMethod.java | 24 ++++++++++++ .../impl/pairing/PairingUpdateController.java | 36 ++++++++++++++++-- .../impl/pairing/TypeLengthValueUtils.java | 10 +++++ 8 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 src/main/java/io/github/hapjava/server/impl/pairing/PairingMethod.java diff --git a/CHANGES.md b/CHANGES.md index 67f610bed..402d3fcec 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,6 @@ +# HAP-Java 2.0.5 +* Implement List-Pairings method. Compatibility with new Home infrastructure from iOS 16.2? + # HAP-Java 2.0.3 * Avoid unnecessary forced disconnects. Library users should be updating the configuration index anyway. diff --git a/src/main/java/io/github/hapjava/server/HomekitAuthInfo.java b/src/main/java/io/github/hapjava/server/HomekitAuthInfo.java index 069bf7270..6441962d7 100644 --- a/src/main/java/io/github/hapjava/server/HomekitAuthInfo.java +++ b/src/main/java/io/github/hapjava/server/HomekitAuthInfo.java @@ -3,6 +3,8 @@ import io.github.hapjava.server.impl.HomekitServer; import io.github.hapjava.server.impl.crypto.HAPSetupCodeUtils; import java.math.BigInteger; +import java.util.Collection; +import java.util.List; /** * Authentication info that must be provided when constructing a new {@link HomekitServer}. You will @@ -65,8 +67,23 @@ default String getSetupId() { * @param username the iOS device's username. The value will not be meaningful to anything but * iOS. * @param publicKey the iOS device's public key. + * @param isAdmin if the user is an admin, authorized to and/remove other users */ - void createUser(String username, byte[] publicKey); + default void createUser(String username, byte[] publicKey, boolean isAdmin) { + createUser(username, publicKey); + } + + /** + * Deprecated method to add a user, assuming all users are admins. + * + *

At least one of the createUser methods must be implemented. + * + * @param username the iOS device's username. + * @param publicKey the iOS device's public key. + */ + default void createUser(String username, byte[] publicKey) { + createUser(username, publicKey, true); + } /** * Called when an iOS device needs to remove an existing pairing. Subsequent calls to {@link @@ -76,6 +93,15 @@ default String getSetupId() { */ void removeUser(String username); + /** + * List all users which have been authenticated. + * + * @return the previously stored list of users. + */ + default Collection listUsers() { + return List.of(); + } + /** * Called when an already paired iOS device is re-connecting. The public key returned by this * method will be compared with the signature of the pair verification request to validate the @@ -86,6 +112,16 @@ default String getSetupId() { */ byte[] getUserPublicKey(String username); + /** + * Determine if the specified user is an admin. + * + * @param username the username of the iOS device to retrieve permissions for. + * @return the previously stored permissions. + */ + default boolean userIsAdmin(String username) { + return true; + } + /** * Called to check if a user has been created. The homekit accessory advertises whether the * accessory has already been paired. At this time, it's unclear whether multiple users can be diff --git a/src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java b/src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java index 36ac04085..08a49f64a 100644 --- a/src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java @@ -67,7 +67,7 @@ public HttpResponse handleRequest(HttpRequest request) throws IOException { public HttpResponse handleAuthenticatedRequest(HttpRequest request) throws IOException { advertiser.setDiscoverable( - false); // brigde is already bound and should not be discoverable anymore + false); // bridge is already bound and should not be discoverable anymore try { switch (request.getUri()) { case "/accessories": diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/FinalPairHandler.java b/src/main/java/io/github/hapjava/server/impl/pairing/FinalPairHandler.java index e5d9e371c..9d464147a 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/FinalPairHandler.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/FinalPairHandler.java @@ -65,7 +65,8 @@ private HttpResponse createUser(byte[] username, byte[] ltpk, byte[] proof) thro if (!new EdsaVerifier(ltpk).verify(completeData, proof)) { throw new Exception("Invalid signature"); } - authInfo.createUser(authInfo.getMac() + new String(username, StandardCharsets.UTF_8), ltpk); + authInfo.createUser( + authInfo.getMac() + new String(username, StandardCharsets.UTF_8), ltpk, true); return createResponse(); } diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/MessageType.java b/src/main/java/io/github/hapjava/server/impl/pairing/MessageType.java index ffdd0ba33..84991d130 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/MessageType.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/MessageType.java @@ -9,7 +9,12 @@ public enum MessageType { ENCRYPTED_DATA(5), STATE(6), ERROR(7), - SIGNATURE(10); + SIGNATURE(0x0a), + PERMISSIONS(0x0b), + FRAGMENT_DATA(0x0c), + FRAGMENT_LAST(0x0d), + FLAGS(0x13), + SEPARATOR(0xff); private final short key; diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/PairingMethod.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairingMethod.java new file mode 100644 index 000000000..07decf02d --- /dev/null +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairingMethod.java @@ -0,0 +1,24 @@ +package io.github.hapjava.server.impl.pairing; + +public enum PairingMethod { + PAIR_SETUP(0), + PAIR_SETUP_WITH_AUTH(1), + PAIR_VERIFY(2), + ADD_PAIRING(3), + REMOVE_PAIRING(4), + LIST_PAIRINGS(5); + + private final byte value; + + PairingMethod(byte value) { + this.value = value; + } + + PairingMethod(int value) { + this.value = (byte) value; + } + + public byte getValue() { + return value; + } +} diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/PairingUpdateController.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairingUpdateController.java index 3d52fe907..abc73dfc1 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/PairingUpdateController.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairingUpdateController.java @@ -7,6 +7,8 @@ import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.DecodeResult; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Iterator; public class PairingUpdateController { @@ -22,19 +24,45 @@ public HttpResponse handle(HttpRequest request) throws IOException { DecodeResult d = TypeLengthValueUtils.decode(request.getBody()); int method = d.getByte(MessageType.METHOD); - if (method == 3) { // Add pairing + if (method == PairingMethod.ADD_PAIRING.getValue()) { byte[] username = d.getBytes(MessageType.USERNAME); byte[] ltpk = d.getBytes(MessageType.PUBLIC_KEY); - authInfo.createUser(authInfo.getMac() + new String(username, StandardCharsets.UTF_8), ltpk); - } else if (method == 4) { // Remove pairing + byte permissions = d.getByte(MessageType.PERMISSIONS); + authInfo.createUser( + authInfo.getMac() + new String(username, StandardCharsets.UTF_8), ltpk, permissions == 1); + } else if (method == PairingMethod.REMOVE_PAIRING.getValue()) { byte[] username = d.getBytes(MessageType.USERNAME); authInfo.removeUser(authInfo.getMac() + new String(username, StandardCharsets.UTF_8)); if (!authInfo.hasUser()) { advertiser.setDiscoverable(true); } + } else if (method == PairingMethod.LIST_PAIRINGS.getValue()) { + TypeLengthValueUtils.Encoder e = TypeLengthValueUtils.getEncoder(); + + Collection usernames = authInfo.listUsers(); + boolean first = true; + Iterator iterator = usernames.iterator(); + while (iterator.hasNext()) { + String username = iterator.next(); + if (first) { + e.add(MessageType.STATE, (byte) 2); + first = false; + } else { + e.add(MessageType.SEPARATOR); + } + e.add(MessageType.USERNAME, username); + e.add(MessageType.PUBLIC_KEY, authInfo.getUserPublicKey(username)); + e.add(MessageType.PERMISSIONS, (short) (authInfo.userIsAdmin(username) ? 1 : 0)); + } + ; + + return new PairingResponse(e.toByteArray()); } else { throw new RuntimeException("Unrecognized method: " + method); } - return new PairingResponse(new byte[] {0x06, 0x01, 0x02}); + + TypeLengthValueUtils.Encoder e = TypeLengthValueUtils.getEncoder(); + e.add(MessageType.STATE, (byte) 2); + return new PairingResponse(e.toByteArray()); } } diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/TypeLengthValueUtils.java b/src/main/java/io/github/hapjava/server/impl/pairing/TypeLengthValueUtils.java index 396829d34..e36f81af8 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/TypeLengthValueUtils.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/TypeLengthValueUtils.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -37,6 +38,11 @@ private Encoder() { baos = new ByteArrayOutputStream(); } + public void add(MessageType type) { + baos.write(type.getKey()); + baos.write(0); + } + public void add(MessageType type, BigInteger i) throws IOException { add(type, ByteUtils.toByteArray(i)); } @@ -58,6 +64,10 @@ public void add(MessageType type, byte[] bytes) throws IOException { } } + public void add(MessageType type, String string) throws IOException { + add(type, string.getBytes(StandardCharsets.UTF_8)); + } + public byte[] toByteArray() { return baos.toByteArray(); } From 9c6358873e30fff25296d728cf242a842089c5c1 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Sat, 17 Dec 2022 20:12:32 -0700 Subject: [PATCH 100/138] be sure to remove MAC from username before returning to iOS --- .../server/impl/pairing/PairingUpdateController.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/PairingUpdateController.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairingUpdateController.java index abc73dfc1..d9cdf2f05 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/PairingUpdateController.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairingUpdateController.java @@ -42,6 +42,8 @@ public HttpResponse handle(HttpRequest request) throws IOException { Collection usernames = authInfo.listUsers(); boolean first = true; Iterator iterator = usernames.iterator(); + String mac = authInfo.getMac(); + while (iterator.hasNext()) { String username = iterator.next(); if (first) { @@ -50,9 +52,14 @@ public HttpResponse handle(HttpRequest request) throws IOException { } else { e.add(MessageType.SEPARATOR); } + byte[] publicKey = authInfo.getUserPublicKey(username); + boolean isAdmin = authInfo.userIsAdmin(username); + if (username.startsWith(mac)) { + username = username.substring(mac.length()); + } e.add(MessageType.USERNAME, username); - e.add(MessageType.PUBLIC_KEY, authInfo.getUserPublicKey(username)); - e.add(MessageType.PERMISSIONS, (short) (authInfo.userIsAdmin(username) ? 1 : 0)); + e.add(MessageType.PUBLIC_KEY, publicKey); + e.add(MessageType.PERMISSIONS, (short) (isAdmin ? 1 : 0)); } ; From d1d6fb09b4175cbabad181d2ee1f762b4111aaa9 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Mon, 19 Dec 2022 13:35:40 -0700 Subject: [PATCH 101/138] refactor pairing to match HAP spec more closely mostly renames, but significant change is that when we don't recognize a user, return an error code instead of throwing an exception and returning a 500 with no content. several responses are now generated with PairingResponse, rather than as a raw byte array, so that the reader can more easily follow what the response is. several more trace loggings showing the details of the pairing process have been added, for debug use also ensure that we check if the MAC has changed when refreshing auth info --- .../hapjava/server/impl/HomekitRoot.java | 1 + .../server/impl/connections/HttpSession.java | 28 ++++---- .../impl/jmdns/JmdnsHomekitAdvertiser.java | 11 +++ .../server/impl/pairing/ErrorCode.java | 26 +++++++ ...lPairHandler.java => ExchangeHandler.java} | 21 +++--- ...ringManager.java => PairSetupManager.java} | 27 ++++---- .../server/impl/pairing/PairSetupRequest.java | 67 +++++++++++++------ ...ionManager.java => PairVerifyManager.java} | 35 +++++----- ...ionRequest.java => PairVerifyRequest.java} | 35 +++++----- .../server/impl/pairing/PairingResponse.java | 21 ++++++ ...teController.java => PairingsManager.java} | 14 ++-- .../server/impl/pairing/SrpHandler.java | 16 ++--- .../hapjava/server/impl/pairing/Stage.java | 7 -- .../impl/pairing/TypeLengthValueUtils.java | 14 ++++ 14 files changed, 209 insertions(+), 114 deletions(-) create mode 100644 src/main/java/io/github/hapjava/server/impl/pairing/ErrorCode.java rename src/main/java/io/github/hapjava/server/impl/pairing/{FinalPairHandler.java => ExchangeHandler.java} (84%) rename src/main/java/io/github/hapjava/server/impl/pairing/{PairingManager.java => PairSetupManager.java} (64%) rename src/main/java/io/github/hapjava/server/impl/pairing/{PairVerificationManager.java => PairVerifyManager.java} (82%) rename src/main/java/io/github/hapjava/server/impl/pairing/{PairVerificationRequest.java => PairVerifyRequest.java} (61%) rename src/main/java/io/github/hapjava/server/impl/pairing/{PairingUpdateController.java => PairingsManager.java} (89%) delete mode 100644 src/main/java/io/github/hapjava/server/impl/pairing/Stage.java diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java index 1750a8d3e..36bfeefa2 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitRoot.java @@ -197,6 +197,7 @@ public void stop() { * @throws IOException if there is an error in the underlying protocol, such as a TCP error */ public void refreshAuthInfo() throws IOException { + advertiser.setMac(authInfo.getMac()); advertiser.setDiscoverable(!authInfo.hasUser()); } diff --git a/src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java b/src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java index 08a49f64a..f1c97c62c 100644 --- a/src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/HttpSession.java @@ -9,9 +9,9 @@ import io.github.hapjava.server.impl.jmdns.JmdnsHomekitAdvertiser; import io.github.hapjava.server.impl.json.AccessoryController; import io.github.hapjava.server.impl.json.CharacteristicsController; -import io.github.hapjava.server.impl.pairing.PairVerificationManager; -import io.github.hapjava.server.impl.pairing.PairingManager; -import io.github.hapjava.server.impl.pairing.PairingUpdateController; +import io.github.hapjava.server.impl.pairing.PairSetupManager; +import io.github.hapjava.server.impl.pairing.PairVerifyManager; +import io.github.hapjava.server.impl.pairing.PairingsManager; import io.github.hapjava.server.impl.responses.InternalServerErrorResponse; import io.github.hapjava.server.impl.responses.NotFoundResponse; import java.io.IOException; @@ -21,8 +21,8 @@ class HttpSession { - private volatile PairingManager pairingManager; - private volatile PairVerificationManager pairVerificationManager; + private volatile PairSetupManager pairSetupManager; + private volatile PairVerifyManager pairVerifyManager; private volatile AccessoryController accessoryController; private volatile CharacteristicsController characteristicsController; @@ -84,7 +84,7 @@ public HttpResponse handleAuthenticatedRequest(HttpRequest request) throws IOExc } case "/pairings": - return new PairingUpdateController(authInfo, advertiser).handle(request); + return new PairingsManager(authInfo, advertiser).handle(request); default: if (request.getUri().startsWith("/characteristics?")) { @@ -100,15 +100,15 @@ public HttpResponse handleAuthenticatedRequest(HttpRequest request) throws IOExc } private HttpResponse handlePairSetup(HttpRequest request) { - if (pairingManager == null) { + if (pairSetupManager == null) { synchronized (HttpSession.class) { - if (pairingManager == null) { - pairingManager = new PairingManager(authInfo, registry); + if (pairSetupManager == null) { + pairSetupManager = new PairSetupManager(authInfo, registry); } } } try { - return pairingManager.handle(request); + return pairSetupManager.handle(request); } catch (Exception e) { logger.warn("Exception encountered during pairing", e); return new InternalServerErrorResponse(e); @@ -116,15 +116,15 @@ private HttpResponse handlePairSetup(HttpRequest request) { } private HttpResponse handlePairVerify(HttpRequest request) { - if (pairVerificationManager == null) { + if (pairVerifyManager == null) { synchronized (HttpSession.class) { - if (pairVerificationManager == null) { - pairVerificationManager = new PairVerificationManager(authInfo, registry); + if (pairVerifyManager == null) { + pairVerifyManager = new PairVerifyManager(authInfo, registry); } } } try { - return pairVerificationManager.handle(request); + return pairVerifyManager.handle(request); } catch (Exception e) { logger.warn("Exception encountered while verifying pairing", e); return new InternalServerErrorResponse(e); diff --git a/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java index 1535a490a..9086acb3e 100644 --- a/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java +++ b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java @@ -79,6 +79,17 @@ public synchronized void setDiscoverable(boolean discoverable) throws IOExceptio } } + public synchronized void setMac(String mac) throws IOException { + if (this.mac != mac) { + this.mac = mac; + if (isAdvertising) { + logger.trace("Re-creating service due to change in mac to " + mac); + unregisterService(); + registerService(); + } + } + } + public synchronized void setConfigurationIndex(int revision) throws IOException { if (this.configurationIndex != revision) { this.configurationIndex = revision; diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/ErrorCode.java b/src/main/java/io/github/hapjava/server/impl/pairing/ErrorCode.java new file mode 100644 index 000000000..90112fc46 --- /dev/null +++ b/src/main/java/io/github/hapjava/server/impl/pairing/ErrorCode.java @@ -0,0 +1,26 @@ +package io.github.hapjava.server.impl.pairing; + +public enum ErrorCode { + OK(0), + UNKNOWN(1), + AUTHENTICATION(2), + BACKOFF(3), + MAX_PEERS(4), + MAX_TRIES(5), + UNAVAILABLE(6), + BUSY(7); + + private final short key; + + ErrorCode(short key) { + this.key = key; + } + + ErrorCode(int key) { + this.key = (short) key; + } + + public short getKey() { + return key; + } +} diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/FinalPairHandler.java b/src/main/java/io/github/hapjava/server/impl/pairing/ExchangeHandler.java similarity index 84% rename from src/main/java/io/github/hapjava/server/impl/pairing/FinalPairHandler.java rename to src/main/java/io/github/hapjava/server/impl/pairing/ExchangeHandler.java index 9d464147a..2310975fa 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/FinalPairHandler.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/ExchangeHandler.java @@ -6,22 +6,26 @@ import io.github.hapjava.server.impl.crypto.EdsaSigner; import io.github.hapjava.server.impl.crypto.EdsaVerifier; import io.github.hapjava.server.impl.http.HttpResponse; -import io.github.hapjava.server.impl.pairing.PairSetupRequest.Stage3Request; +import io.github.hapjava.server.impl.pairing.PairSetupRequest.ExchangeRequest; import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.DecodeResult; import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.Encoder; import java.nio.charset.StandardCharsets; import org.bouncycastle.crypto.digests.SHA512Digest; import org.bouncycastle.crypto.generators.HKDFBytesGenerator; import org.bouncycastle.crypto.params.HKDFParameters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -class FinalPairHandler { +class ExchangeHandler { private final byte[] k; private final HomekitAuthInfo authInfo; private byte[] hkdf_enc_key; - public FinalPairHandler(byte[] k, HomekitAuthInfo authInfo) { + private static final Logger LOGGER = LoggerFactory.getLogger(ExchangeHandler.class); + + public ExchangeHandler(byte[] k, HomekitAuthInfo authInfo) { this.k = k; this.authInfo = authInfo; } @@ -36,10 +40,10 @@ public HttpResponse handle(PairSetupRequest req) throws Exception { byte[] okm = hkdf_enc_key = new byte[32]; hkdf.generateBytes(okm, 0, 32); - return decrypt((Stage3Request) req, okm); + return decrypt((ExchangeRequest) req, okm); } - private HttpResponse decrypt(Stage3Request req, byte[] key) throws Exception { + private HttpResponse decrypt(ExchangeRequest req, byte[] key) throws Exception { ChachaDecoder chacha = new ChachaDecoder(key, "PS-Msg05".getBytes(StandardCharsets.UTF_8)); byte[] plaintext = chacha.decodeCiphertext(req.getAuthTagData(), req.getMessageData()); @@ -63,10 +67,11 @@ private HttpResponse createUser(byte[] username, byte[] ltpk, byte[] proof) thro byte[] completeData = ByteUtils.joinBytes(okm, username, ltpk); if (!new EdsaVerifier(ltpk).verify(completeData, proof)) { - throw new Exception("Invalid signature"); + return new PairingResponse(6, ErrorCode.AUTHENTICATION); } - authInfo.createUser( - authInfo.getMac() + new String(username, StandardCharsets.UTF_8), ltpk, true); + String stringUsername = new String(username, StandardCharsets.UTF_8); + LOGGER.trace("Creating initial user {}", stringUsername); + authInfo.createUser(authInfo.getMac() + stringUsername, ltpk, true); return createResponse(); } diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/PairingManager.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairSetupManager.java similarity index 64% rename from src/main/java/io/github/hapjava/server/impl/pairing/PairingManager.java rename to src/main/java/io/github/hapjava/server/impl/pairing/PairSetupManager.java index af6a6e01f..05b37b8ab 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/PairingManager.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairSetupManager.java @@ -9,48 +9,49 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class PairingManager { +public class PairSetupManager { - private static final Logger logger = LoggerFactory.getLogger(PairingManager.class); + private static final Logger logger = LoggerFactory.getLogger(PairSetupManager.class); private final HomekitAuthInfo authInfo; private final HomekitRegistry registry; private SrpHandler srpHandler; - public PairingManager(HomekitAuthInfo authInfo, HomekitRegistry registry) { + public PairSetupManager(HomekitAuthInfo authInfo, HomekitRegistry registry) { this.authInfo = authInfo; this.registry = registry; } public HttpResponse handle(HttpRequest httpRequest) throws Exception { PairSetupRequest req = PairSetupRequest.of(httpRequest.getBody()); + logger.trace("Handling pair-setup request {}", req); - if (req.getStage() == Stage.ONE) { - logger.trace("Starting pair for " + registry.getLabel()); + if (req.getState() == 1) { + logger.trace("Received SRP Start Request " + registry.getLabel()); srpHandler = new SrpHandler(authInfo.getPin(), authInfo.getSalt()); return srpHandler.handle(req); - } else if (req.getStage() == Stage.TWO) { - logger.trace("Entering second stage of pair for " + registry.getLabel()); + } else if (req.getState() == 3) { + logger.trace("Receive SRP Verify Request for " + registry.getLabel()); if (srpHandler == null) { - logger.warn("Received unexpected stage 2 request for " + registry.getLabel()); + logger.warn("Received unexpected SRP Verify Request for " + registry.getLabel()); return new UnauthorizedResponse(); } else { try { return srpHandler.handle(req); } catch (Exception e) { srpHandler = null; // You don't get to try again - need a new key - logger.warn("Exception encountered while processing pairing request", e); + logger.warn("Exception encountered while processing SRP Verify Request", e); return new UnauthorizedResponse(); } } - } else if (req.getStage() == Stage.THREE) { - logger.trace("Entering third stage of pair for " + registry.getLabel()); + } else if (req.getState() == 5) { + logger.trace("Received Exchange Request for " + registry.getLabel()); if (srpHandler == null) { - logger.warn("Received unexpected stage 3 request for " + registry.getLabel()); + logger.warn("Received unexpected Exchanged Request for " + registry.getLabel()); return new UnauthorizedResponse(); } else { - FinalPairHandler handler = new FinalPairHandler(srpHandler.getK(), authInfo); + ExchangeHandler handler = new ExchangeHandler(srpHandler.getK(), authInfo); try { return handler.handle(req); } catch (Exception e) { diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/PairSetupRequest.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairSetupRequest.java index bd0da4c81..4e7260fed 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/PairSetupRequest.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairSetupRequest.java @@ -2,46 +2,61 @@ import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.DecodeResult; import java.math.BigInteger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; abstract class PairSetupRequest { - - private static final short VALUE_STAGE_1 = 1; - private static final short VALUE_STAGE_2 = 3; - private static final short VALUE_STAGE_3 = 5; + private static final Logger logger = LoggerFactory.getLogger(PairSetupRequest.class); public static PairSetupRequest of(byte[] content) throws Exception { DecodeResult d = TypeLengthValueUtils.decode(content); + logger.trace("Decoded pair setup request: {}", d); short stage = d.getByte(MessageType.STATE); switch (stage) { - case VALUE_STAGE_1: - return new Stage1Request(); + case 1: + return new SRPStartRequest(d); - case VALUE_STAGE_2: - return new Stage2Request(d); + case 3: + return new SRPVerifyRequest(d); - case VALUE_STAGE_3: - return new Stage3Request(d); + case 5: + return new ExchangeRequest(d); default: throw new Exception("Unknown pair process stage: " + stage); } } - public abstract Stage getStage(); + // Raw integer. + // State of the pairing process. 1=M1, 2=M2, etc. + public abstract int getState(); + + public static class SRPStartRequest extends PairSetupRequest { + int flags; + + public SRPStartRequest(DecodeResult d) { + flags = 0; + if (d.hasMessage(MessageType.FLAGS)) { + flags = d.getInt(MessageType.FLAGS); + } + } - public static class Stage1Request extends PairSetupRequest { @Override - public Stage getStage() { - return Stage.ONE; + public int getState() { + return 1; + } + + public String toString() { + return ""; } } - public static class Stage2Request extends PairSetupRequest { + public static class SRPVerifyRequest extends PairSetupRequest { private final BigInteger a; private final BigInteger m1; - public Stage2Request(DecodeResult d) { + public SRPVerifyRequest(DecodeResult d) { a = d.getBigInt(MessageType.PUBLIC_KEY); m1 = d.getBigInt(MessageType.PROOF); } @@ -55,17 +70,21 @@ public BigInteger getM1() { } @Override - public Stage getStage() { - return Stage.TWO; + public int getState() { + return 3; + } + + public String toString() { + return ""; } } - static class Stage3Request extends PairSetupRequest { + static class ExchangeRequest extends PairSetupRequest { private final byte[] messageData; private final byte[] authTagData; - public Stage3Request(DecodeResult d) { + public ExchangeRequest(DecodeResult d) { messageData = new byte[d.getLength(MessageType.ENCRYPTED_DATA) - 16]; authTagData = new byte[16]; d.getBytes(MessageType.ENCRYPTED_DATA, messageData, 0); @@ -81,8 +100,12 @@ public byte[] getAuthTagData() { } @Override - public Stage getStage() { - return Stage.THREE; + public int getState() { + return 5; + } + + public String toString() { + return ""; } } } diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/PairVerificationManager.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairVerifyManager.java similarity index 82% rename from src/main/java/io/github/hapjava/server/impl/pairing/PairVerificationManager.java rename to src/main/java/io/github/hapjava/server/impl/pairing/PairVerifyManager.java index d43b2d210..44f25c6d1 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/PairVerificationManager.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairVerifyManager.java @@ -9,12 +9,11 @@ import io.github.hapjava.server.impl.crypto.EdsaVerifier; import io.github.hapjava.server.impl.http.HttpRequest; import io.github.hapjava.server.impl.http.HttpResponse; -import io.github.hapjava.server.impl.pairing.PairVerificationRequest.Stage1Request; -import io.github.hapjava.server.impl.pairing.PairVerificationRequest.Stage2Request; +import io.github.hapjava.server.impl.pairing.PairVerifyRequest.VerifyFinishRequest; +import io.github.hapjava.server.impl.pairing.PairVerifyRequest.VerifyStartRequest; import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.DecodeResult; import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.Encoder; import io.github.hapjava.server.impl.responses.NotFoundResponse; -import io.github.hapjava.server.impl.responses.OkResponse; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import org.bouncycastle.crypto.digests.SHA512Digest; @@ -23,9 +22,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class PairVerificationManager { +public class PairVerifyManager { - private static final Logger logger = LoggerFactory.getLogger(PairVerificationManager.class); + private static final Logger logger = LoggerFactory.getLogger(PairVerifyManager.class); private static volatile SecureRandom secureRandom; private final HomekitAuthInfo authInfo; @@ -36,26 +35,26 @@ public class PairVerificationManager { private byte[] publicKey; private byte[] sharedSecret; - public PairVerificationManager(HomekitAuthInfo authInfo, HomekitRegistry registry) { + public PairVerifyManager(HomekitAuthInfo authInfo, HomekitRegistry registry) { this.authInfo = authInfo; this.registry = registry; } public HttpResponse handle(HttpRequest rawRequest) throws Exception { - PairVerificationRequest request = PairVerificationRequest.of(rawRequest.getBody()); - switch (request.getStage()) { - case ONE: - return stage1((Stage1Request) request); + PairVerifyRequest request = PairVerifyRequest.of(rawRequest.getBody()); + switch (request.getState()) { + case 1: + return handleVerifyStartRequest((VerifyStartRequest) request); - case TWO: - return stage2((Stage2Request) request); + case 3: + return handleVerifyFinishRequest((VerifyFinishRequest) request); default: return new NotFoundResponse(); } } - private HttpResponse stage1(Stage1Request request) throws Exception { + private HttpResponse handleVerifyStartRequest(VerifyStartRequest request) throws Exception { logger.trace("Starting pair verification for " + registry.getLabel()); clientPublicKey = request.getClientPublicKey(); publicKey = new byte[32]; @@ -96,7 +95,7 @@ private HttpResponse stage1(Stage1Request request) throws Exception { return new PairingResponse(encoder.toByteArray()); } - private HttpResponse stage2(Stage2Request request) throws Exception { + private HttpResponse handleVerifyFinishRequest(VerifyFinishRequest request) throws Exception { ChachaDecoder chacha = new ChachaDecoder(hkdfKey, "PV-Msg03".getBytes(StandardCharsets.UTF_8)); byte[] plaintext = chacha.decodeCiphertext(request.getAuthTagData(), request.getMessageData()); @@ -110,7 +109,8 @@ private HttpResponse stage2(Stage2Request request) throws Exception { authInfo.getUserPublicKey( authInfo.getMac() + new String(clientUsername, StandardCharsets.UTF_8)); if (clientLtpk == null) { - throw new Exception("Unknown user: " + new String(clientUsername, StandardCharsets.UTF_8)); + logger.warn("Unknown user: {}", new String(clientUsername, StandardCharsets.UTF_8)); + return new PairingResponse(4, ErrorCode.AUTHENTICATION); } Encoder encoder = TypeLengthValueUtils.getEncoder(); @@ -122,9 +122,8 @@ private HttpResponse stage2(Stage2Request request) throws Exception { createKey("Control-Write-Encryption-Key"), createKey("Control-Read-Encryption-Key")); } else { - encoder.add(MessageType.ERROR, (short) 4); logger.warn("Invalid signature. Could not pair " + registry.getLabel()); - return new OkResponse(encoder.toByteArray()); + return new PairingResponse(4, ErrorCode.AUTHENTICATION); } } @@ -142,7 +141,7 @@ private byte[] createKey(String info) { private static SecureRandom getSecureRandom() { if (secureRandom == null) { - synchronized (PairVerificationManager.class) { + synchronized (PairVerifyManager.class) { if (secureRandom == null) { secureRandom = new SecureRandom(); } diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/PairVerificationRequest.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairVerifyRequest.java similarity index 61% rename from src/main/java/io/github/hapjava/server/impl/pairing/PairVerificationRequest.java rename to src/main/java/io/github/hapjava/server/impl/pairing/PairVerifyRequest.java index 3bb6627d9..7cf31585b 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/PairVerificationRequest.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairVerifyRequest.java @@ -2,33 +2,32 @@ import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.DecodeResult; -abstract class PairVerificationRequest { +abstract class PairVerifyRequest { - private static final short VALUE_STAGE_1 = 1; - private static final short VALUE_STAGE_2 = 3; - - static PairVerificationRequest of(byte[] content) throws Exception { + static PairVerifyRequest of(byte[] content) throws Exception { DecodeResult d = TypeLengthValueUtils.decode(content); short stage = d.getByte(MessageType.STATE); switch (stage) { - case VALUE_STAGE_1: - return new Stage1Request(d); + case 1: + return new VerifyStartRequest(d); - case VALUE_STAGE_2: - return new Stage2Request(d); + case 3: + return new VerifyFinishRequest(d); default: throw new Exception("Unknown pair process stage: " + stage); } } - abstract Stage getStage(); + // Raw integer. + // State of the pairing process. 1=M1, 2=M2, etc. + abstract int getState(); - static class Stage1Request extends PairVerificationRequest { + static class VerifyStartRequest extends PairVerifyRequest { private final byte[] clientPublicKey; - public Stage1Request(DecodeResult d) { + public VerifyStartRequest(DecodeResult d) { clientPublicKey = d.getBytes(MessageType.PUBLIC_KEY); } @@ -37,17 +36,17 @@ public byte[] getClientPublicKey() { } @Override - Stage getStage() { - return Stage.ONE; + int getState() { + return 1; } } - static class Stage2Request extends PairVerificationRequest { + static class VerifyFinishRequest extends PairVerifyRequest { private final byte[] messageData; private final byte[] authTagData; - public Stage2Request(DecodeResult d) { + public VerifyFinishRequest(DecodeResult d) { messageData = new byte[d.getLength(MessageType.ENCRYPTED_DATA) - 16]; authTagData = new byte[16]; d.getBytes(MessageType.ENCRYPTED_DATA, messageData, 0); @@ -63,8 +62,8 @@ public byte[] getAuthTagData() { } @Override - public Stage getStage() { - return Stage.TWO; + public int getState() { + return 3; } } } diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/PairingResponse.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairingResponse.java index af678a86c..defc5798f 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/PairingResponse.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairingResponse.java @@ -21,6 +21,27 @@ public PairingResponse(byte[] body) { super(body); } + public PairingResponse(int state) { + this(encodeSuccess(state)); + } + + public PairingResponse(int state, ErrorCode errorCode) { + this(encodeError(state, errorCode)); + } + + private static byte[] encodeSuccess(int state) { + TypeLengthValueUtils.Encoder encoder = TypeLengthValueUtils.getEncoder(); + encoder.add(MessageType.STATE, (byte) state); + return encoder.toByteArray(); + } + + private static byte[] encodeError(int state, ErrorCode errorCode) { + TypeLengthValueUtils.Encoder encoder = TypeLengthValueUtils.getEncoder(); + encoder.add(MessageType.STATE, (byte) state); + encoder.add(MessageType.ERROR, (byte) errorCode.getKey()); + return encoder.toByteArray(); + } + @Override public Map getHeaders() { return headers; diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/PairingUpdateController.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairingsManager.java similarity index 89% rename from src/main/java/io/github/hapjava/server/impl/pairing/PairingUpdateController.java rename to src/main/java/io/github/hapjava/server/impl/pairing/PairingsManager.java index d9cdf2f05..11eb4a709 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/PairingUpdateController.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairingsManager.java @@ -9,13 +9,17 @@ import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Iterator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class PairingUpdateController { +public class PairingsManager { private final HomekitAuthInfo authInfo; private final JmdnsHomekitAdvertiser advertiser; - public PairingUpdateController(HomekitAuthInfo authInfo, JmdnsHomekitAdvertiser advertiser) { + private static final Logger LOGGER = LoggerFactory.getLogger(PairingsManager.class); + + public PairingsManager(HomekitAuthInfo authInfo, JmdnsHomekitAdvertiser advertiser) { this.authInfo = authInfo; this.advertiser = advertiser; } @@ -24,6 +28,7 @@ public HttpResponse handle(HttpRequest request) throws IOException { DecodeResult d = TypeLengthValueUtils.decode(request.getBody()); int method = d.getByte(MessageType.METHOD); + if (method == PairingMethod.ADD_PAIRING.getValue()) { byte[] username = d.getBytes(MessageType.USERNAME); byte[] ltpk = d.getBytes(MessageType.PUBLIC_KEY); @@ -61,15 +66,12 @@ public HttpResponse handle(HttpRequest request) throws IOException { e.add(MessageType.PUBLIC_KEY, publicKey); e.add(MessageType.PERMISSIONS, (short) (isAdmin ? 1 : 0)); } - ; return new PairingResponse(e.toByteArray()); } else { throw new RuntimeException("Unrecognized method: " + method); } - TypeLengthValueUtils.Encoder e = TypeLengthValueUtils.getEncoder(); - e.add(MessageType.STATE, (byte) 2); - return new PairingResponse(e.toByteArray()); + return new PairingResponse(2); } } diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/SrpHandler.java b/src/main/java/io/github/hapjava/server/impl/pairing/SrpHandler.java index e02ccdb90..5c1c23353 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/SrpHandler.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/SrpHandler.java @@ -3,7 +3,7 @@ import com.nimbusds.srp6.*; import io.github.hapjava.server.impl.http.HttpResponse; import io.github.hapjava.server.impl.pairing.HomekitSRP6ServerSession.State; -import io.github.hapjava.server.impl.pairing.PairSetupRequest.Stage2Request; +import io.github.hapjava.server.impl.pairing.PairSetupRequest.SRPVerifyRequest; import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.Encoder; import io.github.hapjava.server.impl.responses.ConflictResponse; import io.github.hapjava.server.impl.responses.NotFoundResponse; @@ -39,19 +39,19 @@ public SrpHandler(String pin, BigInteger salt) { } public HttpResponse handle(PairSetupRequest request) throws Exception { - switch (request.getStage()) { - case ONE: - return step1(); + switch (request.getState()) { + case 1: + return handleSrpStartRequest(); - case TWO: - return step2((Stage2Request) request); + case 3: + return handleSrpVerifyRequest((SRPVerifyRequest) request); default: return new NotFoundResponse(); } } - private HttpResponse step1() throws Exception { + private HttpResponse handleSrpStartRequest() throws Exception { if (session.getState() != State.INIT) { logger.warn("Session is not in state INIT when receiving step1"); return new ConflictResponse(); @@ -68,7 +68,7 @@ private HttpResponse step1() throws Exception { return new PairingResponse(encoder.toByteArray()); } - private HttpResponse step2(Stage2Request request) throws Exception { + private HttpResponse handleSrpVerifyRequest(SRPVerifyRequest request) throws Exception { if (session.getState() != State.STEP_1) { logger.warn("Session is not in state Stage 1 when receiving step2"); return new ConflictResponse(); diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/Stage.java b/src/main/java/io/github/hapjava/server/impl/pairing/Stage.java deleted file mode 100644 index 19bac9a14..000000000 --- a/src/main/java/io/github/hapjava/server/impl/pairing/Stage.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.hapjava.server.impl.pairing; - -public enum Stage { - ONE, - TWO, - THREE -} diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/TypeLengthValueUtils.java b/src/main/java/io/github/hapjava/server/impl/pairing/TypeLengthValueUtils.java index e36f81af8..cbd76f118 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/TypeLengthValueUtils.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/TypeLengthValueUtils.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -78,10 +79,23 @@ public static final class DecodeResult { private DecodeResult() {} + public String toString() { + return result.toString(); + } + + public boolean hasMessage(MessageType type) { + return result.containsKey(type.getKey()); + } + public byte getByte(MessageType type) { return result.get(type.getKey())[0]; } + public int getInt(MessageType type) { + ByteBuffer wrapped = ByteBuffer.wrap(result.get(type.getKey())); + return wrapped.getInt(); + } + public BigInteger getBigInt(MessageType type) { return new BigInteger(1, result.get(type.getKey())); } From 036e05a82cd7c50b52c339200ec06af81c30d262 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Mon, 19 Dec 2022 15:19:42 -0700 Subject: [PATCH 102/138] log HTTP method with requests --- .../github/hapjava/server/impl/connections/ConnectionImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/hapjava/server/impl/connections/ConnectionImpl.java b/src/main/java/io/github/hapjava/server/impl/connections/ConnectionImpl.java index ab2ff4444..4869712e1 100644 --- a/src/main/java/io/github/hapjava/server/impl/connections/ConnectionImpl.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/ConnectionImpl.java @@ -59,7 +59,7 @@ private HttpResponse doHandleRequest(HttpRequest request) throws IOException { readKey = ((UpgradeResponse) response).getReadKey().array(); writeKey = ((UpgradeResponse) response).getWriteKey().array(); } - LOGGER.trace(response.getStatusCode() + " " + request.getUri()); + LOGGER.trace("{} {} {}", response.getStatusCode(), request.getMethod(), request.getUri()); return response; } From e7cd4835a503b039e08737200c35468d3316d5f7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 20 Dec 2022 14:56:06 +0000 Subject: [PATCH 103/138] [maven-release-plugin] prepare release hap-2.0.5 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 81102f179..473e00750 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.5-SNAPSHOT + 2.0.5 jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - HEAD + hap-2.0.5 From 331f75b77bea738ccf1836982b658d8cb18a432f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 20 Dec 2022 14:56:09 +0000 Subject: [PATCH 104/138] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 473e00750..42afd6b12 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.5 + 2.0.6-SNAPSHOT jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - hap-2.0.5 + HEAD From ec4cd274f6a9eab14d71ccad71457af0cbed1541 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Fri, 30 Dec 2022 10:26:17 -0700 Subject: [PATCH 105/138] allow library users to get the value of characteristics themselves so they can also generically get the value, regardless of how they decided to fulfill it, without having to also keep track themselves --- .../impl/base/BaseCharacteristic.java | 2 +- .../impl/base/BooleanCharacteristic.java | 2 +- .../impl/base/EnumCharacteristic.java | 13 ++++++++++++- .../impl/base/FloatCharacteristic.java | 2 +- .../impl/base/IntegerCharacteristic.java | 2 +- .../impl/base/StaticStringCharacteristic.java | 2 +- .../impl/base/StringCharacteristic.java | 2 +- 7 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java index 1d2d55c5b..9e57de098 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java @@ -190,7 +190,7 @@ public void unsubscribe() { * * @return a future that will complete with the current value. */ - protected abstract CompletableFuture getValue(); + public abstract CompletableFuture getValue(); /** * Supplies a default value for the characteristic to send to connected clients when the real diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java index 3e07936a8..5e7f16ad7 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java @@ -60,7 +60,7 @@ protected Boolean convert(JsonValue jsonValue) { } @Override - protected CompletableFuture getValue() { + public CompletableFuture getValue() { return getter.isPresent() ? getter.map(booleanGetter -> booleanGetter.get()).get() : null; } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java index df3287de5..881ef91e2 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java @@ -84,8 +84,19 @@ protected Integer convert(JsonValue jsonValue) { } } + /** + * @return the current value of this characteristic, or null if it has no value or can't be + * fetched + */ + public CompletableFuture getEnumValue() { + if (!getter.isPresent()) { + return null; + } + return getter.get().get(); + } + @Override - protected CompletableFuture getValue() { + public CompletableFuture getValue() { if (!getter.isPresent()) { return null; } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java index a8a98dc27..e38d310c7 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java @@ -94,7 +94,7 @@ protected Double convert(JsonValue jsonValue) { * the constructor. */ @Override - protected final CompletableFuture getValue() { + public final CompletableFuture getValue() { if (!getter.isPresent()) { return null; } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java index 22a6dce83..0c05fb4f7 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java @@ -71,7 +71,7 @@ protected CompletableFuture makeBuilder(int iid) { } @Override - protected CompletableFuture getValue() { + public CompletableFuture getValue() { return getter.map(integerGetter -> integerGetter.get()).orElse(null); } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java index 576df7e0e..fc5961d4d 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/StaticStringCharacteristic.java @@ -59,7 +59,7 @@ public void setValue(String value) throws Exception { /** {@inheritDoc} */ @Override - protected CompletableFuture getValue() { + public CompletableFuture getValue() { return getter.map(stringGetter -> stringGetter.get()).orElse(null); } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java index 65cf45a71..53196efb9 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java @@ -68,7 +68,7 @@ public void setValue(String value) throws Exception { /** {@inheritDoc} */ @Override - protected CompletableFuture getValue() { + public CompletableFuture getValue() { return getter.map(stringGetter -> stringGetter.get()).orElse(null); } From 2a5e1c013f17f59e261cc8662c241148338baff7 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Fri, 30 Dec 2022 10:31:46 -0700 Subject: [PATCH 106/138] don't add an extra AccessoryInformationService if the accessory already provided one --- .../github/hapjava/accessories/HomekitAccessory.java | 12 ++++++++++-- .../github/hapjava/server/impl/HomekitRegistry.java | 7 +++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/hapjava/accessories/HomekitAccessory.java b/src/main/java/io/github/hapjava/accessories/HomekitAccessory.java index 5e2bbe3b6..64c406492 100644 --- a/src/main/java/io/github/hapjava/accessories/HomekitAccessory.java +++ b/src/main/java/io/github/hapjava/accessories/HomekitAccessory.java @@ -4,6 +4,7 @@ import io.github.hapjava.services.impl.AccessoryInformationService; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.concurrent.CompletableFuture; /** @@ -81,11 +82,18 @@ default Collection getServices() { }; /** - * default implementation which is sufficient if accessory has only one services + * default implementation which is sufficient if accessory has only one service * * @return primary service */ default Service getPrimaryService() { - return getServices().isEmpty() ? null : getServices().iterator().next(); + Iterator iterator = getServices().iterator(); + while (iterator.hasNext()) { + Service service = iterator.next(); + if (!(service instanceof AccessoryInformationService)) { + return service; + } + } + return null; }; } diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java b/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java index cc6cd352e..3e3be9b6b 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java @@ -42,8 +42,11 @@ public synchronized void reset() { List newServices; try { newServices = new ArrayList<>(2); - newServices.add(new AccessoryInformationService(accessory)); - for (Service service : accessory.getServices()) { + Collection services = accessory.getServices(); + if (!services.stream().anyMatch(s -> s instanceof AccessoryInformationService)) { + newServices.add(new AccessoryInformationService(accessory)); + } + for (Service service : services) { newServices.add(service); newServices.addAll(service.getLinkedServices()); } From b40277150fb21378a92138aa24c915c0200d1285 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Fri, 30 Dec 2022 11:46:04 -0700 Subject: [PATCH 107/138] allow setting values directly via characteristics --- .../impl/base/BaseCharacteristic.java | 2 +- .../impl/base/BooleanCharacteristic.java | 2 +- .../impl/base/EnumCharacteristic.java | 12 ++++++++++-- .../impl/base/FloatCharacteristic.java | 2 +- .../impl/base/IntegerCharacteristic.java | 2 +- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java index 9e57de098..38d1d8e6d 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java @@ -183,7 +183,7 @@ public void unsubscribe() { * @param value the new value to set. * @throws Exception if the value cannot be set. */ - protected abstract void setValue(T value) throws Exception; + public abstract void setValue(T value) throws Exception; /** * Retrieves the current value of the characteristic. diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java index 5e7f16ad7..4b484eb85 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java @@ -65,7 +65,7 @@ public CompletableFuture getValue() { } @Override - protected void setValue(Boolean value) throws Exception { + public void setValue(Boolean value) throws Exception { if (setter.isPresent()) setter.get().accept(value); } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java index 881ef91e2..65e82747f 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java @@ -103,8 +103,16 @@ public CompletableFuture getValue() { return getter.get().get().thenApply(T::getCode); } + public void setValue(T value) throws Exception { + if (!setter.isPresent()) { + return; + } + + setter.get().accept(value); + } + @Override - protected void setValue(Integer value) throws Exception { + public void setValue(Integer value) throws Exception { if (!setter.isPresent()) { return; } @@ -113,7 +121,7 @@ protected void setValue(Integer value) throws Exception { if (validValues != null && value != null) { for (T valid : validValues) { if (valid.getCode() == value) { - setter.get().accept(valid); + setValue(valid); return; } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java index e38d310c7..f504322a0 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java @@ -129,7 +129,7 @@ public final CompletableFuture getValue() { } @Override - protected void setValue(Double value) throws Exception { + public void setValue(Double value) throws Exception { if (setter.isPresent()) setter.get().accept(value); } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java index 0c05fb4f7..6d1b7df6d 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java @@ -76,7 +76,7 @@ public CompletableFuture getValue() { } @Override - protected void setValue(Integer value) throws Exception { + public void setValue(Integer value) throws Exception { if (setter.isPresent()) setter.get().accept(value); } From ae4a8ecc43b2c72bb48ade78caa01e09394326fd Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Thu, 5 Jan 2023 18:08:41 -0700 Subject: [PATCH 108/138] check for AccessoryInformationService by UUID, not by class in case someone is using some form of decorator or replacement class, but is still exposing the proper type --- .../java/io/github/hapjava/server/impl/HomekitRegistry.java | 3 ++- .../hapjava/services/impl/AccessoryInformationService.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java b/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java index 3e3be9b6b..3c70cfb9e 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java @@ -43,7 +43,8 @@ public synchronized void reset() { try { newServices = new ArrayList<>(2); Collection services = accessory.getServices(); - if (!services.stream().anyMatch(s -> s instanceof AccessoryInformationService)) { + if (!services.stream() + .anyMatch(s -> s.getType().equals(AccessoryInformationService.TYPE))) { newServices.add(new AccessoryInformationService(accessory)); } for (Service service : services) { diff --git a/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java b/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java index 8aa7f3635..1fdb30d2a 100644 --- a/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java +++ b/src/main/java/io/github/hapjava/services/impl/AccessoryInformationService.java @@ -14,6 +14,7 @@ /** Accessory Information service. */ public class AccessoryInformationService extends AbstractServiceImpl { + public static final String TYPE = "0000003E-0000-1000-8000-0026BB765291"; public AccessoryInformationService( IdentifyCharacteristic identify, @@ -22,7 +23,7 @@ public AccessoryInformationService( NameCharacteristic name, SerialNumberCharacteristic serialNumber, FirmwareRevisionCharacteristic firmwareRevision) { - super("0000003E-0000-1000-8000-0026BB765291"); + super(TYPE); addCharacteristic(identify); addCharacteristic(manufacturer); addCharacteristic(model); From 77db50c29945843f9c559553093755acb5ddf5a3 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Mon, 9 Jan 2023 12:25:28 -0700 Subject: [PATCH 109/138] improve ProgrammableSwitchEvent * fix typo ProgrammableSwitchEnum * allow restricting valid values --- CHANGES.md | 5 +++++ .../impl/base/EnumCharacteristic.java | 13 +++++++++++-- .../impl/common/ProgrammableSwitchEnum.java | 2 +- .../ProgrammableSwitchEventCharacteristic.java | 10 +++++++++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 402d3fcec..ab4bc7e4f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,8 @@ +# HAP-Java 2.0.6 +* Several methods allowing library users to manipulate characteristics themselves +* Allow library users to provider their own implementation of AccessoryInformationService +* Fix ProgrammableSwitchEventEnum that has an incorrect value + # HAP-Java 2.0.5 * Implement List-Pairings method. Compatibility with new Home infrastructure from iOS 16.2? diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java index 65e82747f..0032b4301 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java @@ -26,7 +26,7 @@ public abstract class EnumCharacteristic extends BaseCharacteristic { private final T[] validValues; - Optional>> getter; + protected Optional>> getter; protected Optional> setter; /** @@ -100,7 +100,16 @@ public CompletableFuture getValue() { if (!getter.isPresent()) { return null; } - return getter.get().get().thenApply(T::getCode); + return getter + .get() + .get() + .thenApply( + e -> { + if (e == null) { + return null; + } + return e.getCode(); + }); } public void setValue(T value) throws Exception { diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEnum.java index 12f600f92..988472a0c 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEnum.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEnum.java @@ -9,7 +9,7 @@ public enum ProgrammableSwitchEnum implements CharacteristicEnum { SINGLE_PRESS(0), DOUBLE_PRESS(1), - LONG_PRESS(3); + LONG_PRESS(2); private static final Map reverse; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEventCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEventCharacteristic.java index 83dcbb2dc..b19430100 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEventCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/ProgrammableSwitchEventCharacteristic.java @@ -21,10 +21,18 @@ public ProgrammableSwitchEventCharacteristic( Supplier> getter, Consumer subscriber, Runnable unsubscriber) { + this(ProgrammableSwitchEnum.values(), getter, subscriber, unsubscriber); + } + + public ProgrammableSwitchEventCharacteristic( + ProgrammableSwitchEnum[] validValues, + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { super( "00000073-0000-1000-8000-0026BB765291", "Switch Event", - ProgrammableSwitchEnum.values(), + validValues, Optional.of(getter), Optional.empty(), Optional.of(subscriber), From 0039adb97b4f8240aaf8912ef1eb17d9d459259a Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Mon, 9 Jan 2023 12:25:09 -0700 Subject: [PATCH 110/138] more direct access to characteristic data --- .../impl/base/EnumCharacteristic.java | 4 ++++ .../impl/base/FloatCharacteristic.java | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java index 65e82747f..e3ba9ae46 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java @@ -137,4 +137,8 @@ public Integer getDefault() { } return 0; } + + public T[] getValidValues() { + return validValues; + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java index f504322a0..727cea645 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java @@ -138,4 +138,16 @@ public void setValue(Double value) throws Exception { public Double getDefault() { return minValue; } + + public double getMinValue() { + return minValue; + } + + public double getMaxValue() { + return maxValue; + } + + public double getMinStep() { + return minStep; + } } From 975485f2be3db04dac4302a374719802daa712e4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 13 Jan 2023 19:53:37 +0000 Subject: [PATCH 111/138] [maven-release-plugin] prepare release hap-2.0.6 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 42afd6b12..7a1762ffe 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.6-SNAPSHOT + 2.0.6 jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - HEAD + hap-2.0.6 From 2e75f47a3a3c9ba97e88c327062c1c0809b093db Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 13 Jan 2023 19:53:39 +0000 Subject: [PATCH 112/138] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7a1762ffe..29db614f1 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.6 + 2.0.7-SNAPSHOT jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - hap-2.0.6 + HEAD From b2d378f2b075202cde991b968f87e2eca5abd56c Mon Sep 17 00:00:00 2001 From: joenicorata <44491399+joenicorata@users.noreply.github.com> Date: Thu, 2 Mar 2023 13:57:45 -0600 Subject: [PATCH 113/138] TelevisionAccessory Related Fixes (#185) * RemoteKeyEnum code for Info button is incorrect. Release version shows code 115, actual code is 15. Tested on IOS 16 and 15. * Add support for accessory category on standalone server. previously category was implemented for a bridge, but not for standalone accessories. --- .../impl/television/RemoteKeyEnum.java | 2 +- .../hapjava/server/impl/HomekitServer.java | 9 ++++++++ .../HomekitStandaloneAccessoryServer.java | 22 +++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyEnum.java b/src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyEnum.java index 443c23644..d62298b84 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyEnum.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/television/RemoteKeyEnum.java @@ -18,7 +18,7 @@ public enum RemoteKeyEnum implements CharacteristicEnum { BACK(9), EXIT(10), PLAY_PAUSE(11), - INFO(115); + INFO(15); private static final Map reverse; diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitServer.java b/src/main/java/io/github/hapjava/server/impl/HomekitServer.java index af129a7a8..fc8ecaf76 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitServer.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitServer.java @@ -117,6 +117,15 @@ public HomekitStandaloneAccessoryServer createStandaloneAccessory( return new HomekitStandaloneAccessoryServer(accessory, http, localAddress, authInfo); } } + public HomekitStandaloneAccessoryServer createStandaloneAccessory( + HomekitAuthInfo authInfo, HomekitAccessory accessory, int category) + throws IOException, ExecutionException, InterruptedException { + if (jmdns != null) { + return new HomekitStandaloneAccessoryServer(accessory, http, jmdns, authInfo, category); + } else { + return new HomekitStandaloneAccessoryServer(accessory, http, localAddress, authInfo, category); + } + } /** * Creates a bridge accessory, capable of holding multiple child accessories. This has the diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java b/src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java index 5eecfe45c..99ff7b746 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java @@ -40,6 +40,28 @@ public class HomekitStandaloneAccessoryServer { root = new HomekitRoot(accessory.getName().get(), webHandler, jmdns, authInfo); root.addAccessory(accessory); } + + HomekitStandaloneAccessoryServer( + HomekitAccessory accessory, + HomekitWebHandler webHandler, + InetAddress localhost, + HomekitAuthInfo authInfo, + int category) + throws UnknownHostException, IOException, ExecutionException, InterruptedException { + root = new HomekitRoot(accessory.getName().get(), category, webHandler, localhost, authInfo); + root.addAccessory(accessory); + } + + HomekitStandaloneAccessoryServer( + HomekitAccessory accessory, + HomekitWebHandler webHandler, + JmDNS jmdns, + HomekitAuthInfo authInfo, + int category) + throws UnknownHostException, IOException, ExecutionException, InterruptedException { + root = new HomekitRoot(accessory.getName().get(), category, webHandler, jmdns, authInfo); + root.addAccessory(accessory); + } /** Begins advertising and handling requests for this accessory. */ public void start() { From 1b20ab04dc310756ee37ab74863d57b8c58e3948 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Thu, 2 Mar 2023 13:00:40 -0700 Subject: [PATCH 114/138] attempt to fix error in release github action --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 804e4b2bd..48b5ee7cf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,6 +10,9 @@ on: description: Version to release required: true +permissions: + contents: write + jobs: build: @@ -17,6 +20,7 @@ jobs: steps: - uses: actions/checkout@v3 + - uses: git-actions/set-user@v1 - name: Set up JDK 11 uses: actions/setup-java@v3 with: @@ -28,10 +32,6 @@ jobs: gpg-passphrase: MAVEN_GPG_PASSPHRASE server-username: MAVEN_USERNAME server-password: MAVEN_CENTRAL_TOKEN - - name: Setup git profile - run: | - git config --global user.name "github-actions[bot]" - git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" - name: Maven release run: mvn release:prepare release:perform -B -DreleaseVersion=${{ github.event.inputs.releaseVersion }} -Possrh env: From 50cd0d689adbffa165bb40f39a6d321cffb2aff9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 2 Mar 2023 20:03:15 +0000 Subject: [PATCH 115/138] [maven-release-plugin] prepare release hap-2.0.7 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 29db614f1..4a689cbdd 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.7-SNAPSHOT + 2.0.7 jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - HEAD + hap-2.0.7 From 26bf0903e8d23639b9e380078a442c86a23ac9b8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 2 Mar 2023 20:03:16 +0000 Subject: [PATCH 116/138] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4a689cbdd..ffd5798a3 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.7 + 2.0.8-SNAPSHOT jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - hap-2.0.7 + HEAD From ea14c5db7c10a2772a33da1e2d1ac161aecd2898 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Mon, 13 Oct 2025 17:58:36 -0600 Subject: [PATCH 117/138] Allow characteristic setters to get the username of the triggering connection --- CHANGES.md | 3 +++ .../characteristics/Characteristic.java | 10 +++++++++ .../characteristics/ExceptionalConsumer.java | 4 ++++ .../impl/base/BaseCharacteristic.java | 22 +++++++++++++++++-- .../impl/base/BooleanCharacteristic.java | 7 +++++- .../impl/base/EnumCharacteristic.java | 13 +++++++++-- .../impl/base/FloatCharacteristic.java | 7 +++++- .../impl/base/IntegerCharacteristic.java | 7 +++++- .../impl/base/StringCharacteristic.java | 8 ++++++- .../hapjava/server/impl/HomekitServer.java | 4 +++- .../HomekitStandaloneAccessoryServer.java | 2 +- .../impl/connections/ConnectionImpl.java | 7 ++++++ .../impl/http/HomekitClientConnection.java | 4 ++++ .../impl/json/CharacteristicsController.java | 2 +- .../impl/pairing/PairVerifyManager.java | 10 ++++----- .../server/impl/pairing/UpgradeResponse.java | 8 ++++++- 16 files changed, 101 insertions(+), 17 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ab4bc7e4f..a7e700425 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,6 @@ +# HAP-Java 2.0.7 +* Add overloads to characteristics so that the username can be passed through. + # HAP-Java 2.0.6 * Several methods allowing library users to manipulate characteristics themselves * Allow library users to provider their own implementation of AccessoryInformationService diff --git a/src/main/java/io/github/hapjava/characteristics/Characteristic.java b/src/main/java/io/github/hapjava/characteristics/Characteristic.java index d2e87030a..b2454e5f9 100644 --- a/src/main/java/io/github/hapjava/characteristics/Characteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/Characteristic.java @@ -44,4 +44,14 @@ public interface Characteristic { * @param jsonValue the JSON serialized value to set. */ void setValue(JsonValue jsonValue); + + /** + * Invoked by the remote client, this updates the current value of the characteristic. + * + * @param jsonValue the JSON serialized value to set. + * @param username the authenticated username making the request + */ + default void setValue(JsonValue jsonValue, String username) { + setValue(jsonValue); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/ExceptionalConsumer.java b/src/main/java/io/github/hapjava/characteristics/ExceptionalConsumer.java index 653d73c5f..1c625470d 100644 --- a/src/main/java/io/github/hapjava/characteristics/ExceptionalConsumer.java +++ b/src/main/java/io/github/hapjava/characteristics/ExceptionalConsumer.java @@ -2,4 +2,8 @@ public interface ExceptionalConsumer { void accept(T t) throws Exception; + + default void accept(T t, String username) throws Exception { + accept(t); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java index 38d1d8e6d..0808883bb 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/BaseCharacteristic.java @@ -128,13 +128,20 @@ protected CompletableFuture makeBuilder(int instanceId) { /** {@inheritDoc} */ @Override public final void setValue(JsonValue jsonValue) { + setValue(jsonValue, null); + } + + /** {@inheritDoc} */ + @Override + public final void setValue(JsonValue jsonValue, String username) { try { - setValue(convert(jsonValue)); + setValue(convert(jsonValue), username); } catch (Exception e) { logger.warn( - "Error while setting JSON value {} for characteristic {}", + "Error while setting JSON value {} for characteristic {} from user {}", jsonValue, getClass().getName(), + username, e); } } @@ -185,6 +192,17 @@ public void unsubscribe() { */ public abstract void setValue(T value) throws Exception; + /** + * Update the characteristic value using a new value supplied by the connected client. + * + * @param value the new value to set. + * @param username the authenticated username making the request + * @throws Exception if the value cannot be set. + */ + public void setValue(T value, String username) throws Exception { + setValue(value); + } + /** * Retrieves the current value of the characteristic. * diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java index 4b484eb85..b8c93a4e9 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/BooleanCharacteristic.java @@ -66,7 +66,12 @@ public CompletableFuture getValue() { @Override public void setValue(Boolean value) throws Exception { - if (setter.isPresent()) setter.get().accept(value); + setValue(value, null); + } + + @Override + public void setValue(Boolean value, String username) throws Exception { + if (setter.isPresent()) setter.get().accept(value, username); } /** {@inheritDoc} */ diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java index 4d8dfdb5f..a12cb0673 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/EnumCharacteristic.java @@ -113,15 +113,24 @@ public CompletableFuture getValue() { } public void setValue(T value) throws Exception { + setValue(value, null); + } + + public void setValue(T value, String username) throws Exception { if (!setter.isPresent()) { return; } - setter.get().accept(value); + setter.get().accept(value, username); } @Override public void setValue(Integer value) throws Exception { + setValue(value, null); + } + + @Override + public void setValue(Integer value, String username) throws Exception { if (!setter.isPresent()) { return; } @@ -130,7 +139,7 @@ public void setValue(Integer value) throws Exception { if (validValues != null && value != null) { for (T valid : validValues) { if (valid.getCode() == value) { - setValue(valid); + setValue(valid, username); return; } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java index 727cea645..f4b5fa562 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/FloatCharacteristic.java @@ -130,7 +130,12 @@ public final CompletableFuture getValue() { @Override public void setValue(Double value) throws Exception { - if (setter.isPresent()) setter.get().accept(value); + setValue(value, null); + } + + @Override + public void setValue(Double value, String username) throws Exception { + if (setter.isPresent()) setter.get().accept(value, username); } /** {@inheritDoc} */ diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java index 6d1b7df6d..96448eea8 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java @@ -77,7 +77,12 @@ public CompletableFuture getValue() { @Override public void setValue(Integer value) throws Exception { - if (setter.isPresent()) setter.get().accept(value); + setValue(value, null); + } + + @Override + public void setValue(Integer value, String username) throws Exception { + if (setter.isPresent()) setter.get().accept(value, username); } /** {@inheritDoc} */ diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java index 53196efb9..72ff0afd6 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/StringCharacteristic.java @@ -63,7 +63,13 @@ public String convert(JsonValue jsonValue) { /** {@inheritDoc} */ @Override public void setValue(String value) throws Exception { - if (setter.isPresent()) setter.get().accept(value); + setValue(value, null); + } + + /** {@inheritDoc} */ + @Override + public void setValue(String value, String username) throws Exception { + if (setter.isPresent()) setter.get().accept(value, username); } /** {@inheritDoc} */ diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitServer.java b/src/main/java/io/github/hapjava/server/impl/HomekitServer.java index fc8ecaf76..7728896a2 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitServer.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitServer.java @@ -117,13 +117,15 @@ public HomekitStandaloneAccessoryServer createStandaloneAccessory( return new HomekitStandaloneAccessoryServer(accessory, http, localAddress, authInfo); } } + public HomekitStandaloneAccessoryServer createStandaloneAccessory( HomekitAuthInfo authInfo, HomekitAccessory accessory, int category) throws IOException, ExecutionException, InterruptedException { if (jmdns != null) { return new HomekitStandaloneAccessoryServer(accessory, http, jmdns, authInfo, category); } else { - return new HomekitStandaloneAccessoryServer(accessory, http, localAddress, authInfo, category); + return new HomekitStandaloneAccessoryServer( + accessory, http, localAddress, authInfo, category); } } diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java b/src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java index 99ff7b746..4a5dbabee 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitStandaloneAccessoryServer.java @@ -40,7 +40,7 @@ public class HomekitStandaloneAccessoryServer { root = new HomekitRoot(accessory.getName().get(), webHandler, jmdns, authInfo); root.addAccessory(accessory); } - + HomekitStandaloneAccessoryServer( HomekitAccessory accessory, HomekitWebHandler webHandler, diff --git a/src/main/java/io/github/hapjava/server/impl/connections/ConnectionImpl.java b/src/main/java/io/github/hapjava/server/impl/connections/ConnectionImpl.java index 4869712e1..759506722 100644 --- a/src/main/java/io/github/hapjava/server/impl/connections/ConnectionImpl.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/ConnectionImpl.java @@ -27,6 +27,7 @@ class ConnectionImpl implements HomekitClientConnection { private int outboundBinaryMessageCount = 0; private byte[] readKey; private byte[] writeKey; + private String username; private boolean isUpgraded = false; private final Consumer outOfBandMessageCallback; private final SubscriptionManager subscriptions; @@ -58,6 +59,7 @@ private HttpResponse doHandleRequest(HttpRequest request) throws IOException { isUpgraded = true; readKey = ((UpgradeResponse) response).getReadKey().array(); writeKey = ((UpgradeResponse) response).getWriteKey().array(); + username = ((UpgradeResponse) response).getUsername(); } LOGGER.trace("{} {} {}", response.getStatusCode(), request.getMethod(), request.getUri()); return response; @@ -145,4 +147,9 @@ public void close() { public void outOfBand(HttpResponse message) { outOfBandMessageCallback.accept(message); } + + @Override + public String getUsername() { + return username; + } } diff --git a/src/main/java/io/github/hapjava/server/impl/http/HomekitClientConnection.java b/src/main/java/io/github/hapjava/server/impl/http/HomekitClientConnection.java index 58ef23c6e..2648808bb 100644 --- a/src/main/java/io/github/hapjava/server/impl/http/HomekitClientConnection.java +++ b/src/main/java/io/github/hapjava/server/impl/http/HomekitClientConnection.java @@ -13,4 +13,8 @@ public interface HomekitClientConnection { void close(); void outOfBand(HttpResponse message); + + default String getUsername() { + return null; + } } diff --git a/src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java b/src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java index d061a11fc..f5f8bb87c 100644 --- a/src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java +++ b/src/main/java/io/github/hapjava/server/impl/json/CharacteristicsController.java @@ -89,7 +89,7 @@ public HttpResponse put(HttpRequest request, HomekitClientConnection connection) } if (jsonCharacteristic.containsKey("value")) { - characteristic.setValue(jsonCharacteristic.get("value")); + characteristic.setValue(jsonCharacteristic.get("value"), connection.getUsername()); } if (jsonCharacteristic.containsKey("ev") && characteristic instanceof EventableCharacteristic) { diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/PairVerifyManager.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairVerifyManager.java index 44f25c6d1..4960c6f6a 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/PairVerifyManager.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairVerifyManager.java @@ -105,11 +105,10 @@ private HttpResponse handleVerifyFinishRequest(VerifyFinishRequest request) thro byte[] material = ByteUtils.joinBytes(clientPublicKey, clientUsername, publicKey); - byte[] clientLtpk = - authInfo.getUserPublicKey( - authInfo.getMac() + new String(clientUsername, StandardCharsets.UTF_8)); + String username = new String(clientUsername, StandardCharsets.UTF_8); + byte[] clientLtpk = authInfo.getUserPublicKey(authInfo.getMac() + username); if (clientLtpk == null) { - logger.warn("Unknown user: {}", new String(clientUsername, StandardCharsets.UTF_8)); + logger.warn("Unknown user: {}", username); return new PairingResponse(4, ErrorCode.AUTHENTICATION); } @@ -120,7 +119,8 @@ private HttpResponse handleVerifyFinishRequest(VerifyFinishRequest request) thro return new UpgradeResponse( encoder.toByteArray(), createKey("Control-Write-Encryption-Key"), - createKey("Control-Read-Encryption-Key")); + createKey("Control-Read-Encryption-Key"), + username); } else { logger.warn("Invalid signature. Could not pair " + registry.getLabel()); return new PairingResponse(4, ErrorCode.AUTHENTICATION); diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/UpgradeResponse.java b/src/main/java/io/github/hapjava/server/impl/pairing/UpgradeResponse.java index 87168aed1..121fc7fd8 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/UpgradeResponse.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/UpgradeResponse.java @@ -6,11 +6,13 @@ public class UpgradeResponse extends PairingResponse { private final byte[] readKey; private final byte[] writeKey; + private final String username; - UpgradeResponse(byte[] body, byte[] readKey, byte[] writeKey) { + UpgradeResponse(byte[] body, byte[] readKey, byte[] writeKey, String username) { super(body); this.readKey = readKey; this.writeKey = writeKey; + this.username = username; } @Override @@ -25,4 +27,8 @@ public ByteBuffer getReadKey() { public ByteBuffer getWriteKey() { return ByteBuffer.wrap(writeKey); } + + public String getUsername() { + return username; + } } From 4b40cd9421535d156f9ac7dd75842e95ddf3bbcc Mon Sep 17 00:00:00 2001 From: jrburk Date: Fri, 2 Jan 2026 19:38:59 +0100 Subject: [PATCH 118/138] Update to bouncy castle 1.8.3 (#193) * Update to bouncy castle 1.8.2 * bcprov 1.83 --------- Co-authored-by: Jochen Burkhardt Co-authored-by: Cody Cutrer --- CHANGES.md | 3 + pom.xml | 10 +- .../server/impl/crypto/ChachaDecoder.java | 98 ++++++++++++++----- .../server/impl/crypto/ChachaEncoder.java | 95 +++++++++++++----- .../server/impl/pairing/ExchangeHandler.java | 28 +++++- .../server/impl/pairing/PairSetupRequest.java | 34 ++++++- 6 files changed, 209 insertions(+), 59 deletions(-) mode change 100644 => 100755 src/main/java/io/github/hapjava/server/impl/crypto/ChachaDecoder.java mode change 100644 => 100755 src/main/java/io/github/hapjava/server/impl/crypto/ChachaEncoder.java mode change 100644 => 100755 src/main/java/io/github/hapjava/server/impl/pairing/ExchangeHandler.java mode change 100644 => 100755 src/main/java/io/github/hapjava/server/impl/pairing/PairSetupRequest.java diff --git a/CHANGES.md b/CHANGES.md index a7e700425..95f83e79f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,6 @@ +# HAP-Java 2.0.8 +* Updated bouncy castle to 1.83 + # HAP-Java 2.0.7 * Add overloads to characteristics so that the username can be passed through. diff --git a/pom.xml b/pom.xml index ffd5798a3..b25007f26 100644 --- a/pom.xml +++ b/pom.xml @@ -105,10 +105,16 @@ org.bouncycastle - bcprov-jdk15on - 1.51 + bcpkix-jdk18on + 1.83 + + org.bouncycastle + bctls-jdk18on + 1.82 + + net.vrallev.ecc ecc-25519-java diff --git a/src/main/java/io/github/hapjava/server/impl/crypto/ChachaDecoder.java b/src/main/java/io/github/hapjava/server/impl/crypto/ChachaDecoder.java old mode 100644 new mode 100755 index 4c74a469d..014f4b500 --- a/src/main/java/io/github/hapjava/server/impl/crypto/ChachaDecoder.java +++ b/src/main/java/io/github/hapjava/server/impl/crypto/ChachaDecoder.java @@ -1,54 +1,98 @@ package io.github.hapjava.server.impl.crypto; import java.io.IOException; -import org.bouncycastle.crypto.engines.ChaChaEngine; -import org.bouncycastle.crypto.generators.Poly1305KeyGenerator; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.modes.ChaCha20Poly1305; +import org.bouncycastle.crypto.params.AEADParameters; import org.bouncycastle.crypto.params.KeyParameter; -import org.bouncycastle.crypto.params.ParametersWithIV; -import org.bouncycastle.crypto.tls.AlertDescription; -import org.bouncycastle.crypto.tls.TlsFatalAlert; -import org.bouncycastle.util.Arrays; +import org.bouncycastle.tls.AlertDescription; +import org.bouncycastle.tls.TlsFatalAlert; public class ChachaDecoder { - private final ChaChaEngine decryptCipher; + private final ChaCha20Poly1305 cipher; + private final byte[] key; + private final byte[] nonce; public ChachaDecoder(byte[] key, byte[] nonce) throws IOException { + this.key = key; + // ChaCha20-Poly1305 requires exactly 12 bytes (96 bits) for nonce + this.nonce = ensureNonceSize(nonce); + this.cipher = new ChaCha20Poly1305(); + } - this.decryptCipher = new ChaChaEngine(20); + private byte[] ensureNonceSize(byte[] nonce) { + if (nonce == null) { + return new byte[12]; // Default to zero nonce if null + } - this.decryptCipher.init(false, new ParametersWithIV(new KeyParameter(key), nonce)); + // For HomeKit pairing messages, handle Apple's string-based nonces + if (nonce.length == 8) { + // Apple's HomeKit implementation uses a specific nonce format + // Based on RFC 7539 and Apple's implementation, the nonce should be: + // - 4 bytes constant (0x00000000) + // - 8 bytes nonce string + // This matches ChaCha20's 96-bit nonce requirement + byte[] adjustedNonce = new byte[12]; + // Put the 8-byte nonce at the END (bytes 4-11), not at the beginning + System.arraycopy(nonce, 0, adjustedNonce, 4, 8); + // First 4 bytes remain zero (counter initialization) + return adjustedNonce; + } + + if (nonce.length == 12) { + return nonce; // Already correct size + } + + // For other nonce lengths, pad or truncate to 12 bytes + byte[] adjustedNonce = new byte[12]; + if (nonce.length < 12) { + // Pad with zeros if too short - put nonce at beginning + System.arraycopy(nonce, 0, adjustedNonce, 0, nonce.length); + // Remaining bytes are already zero + } else { + // Truncate if too long - take first 12 bytes + System.arraycopy(nonce, 0, adjustedNonce, 0, 12); + } + + return adjustedNonce; } public byte[] decodeCiphertext(byte[] receivedMAC, byte[] additionalData, byte[] ciphertext) throws IOException { - KeyParameter macKey = initRecordMAC(decryptCipher); + try { + byte[] ciphertextWithTag = new byte[ciphertext.length + receivedMAC.length]; + System.arraycopy(ciphertext, 0, ciphertextWithTag, 0, ciphertext.length); + System.arraycopy(receivedMAC, 0, ciphertextWithTag, ciphertext.length, receivedMAC.length); + + ChaCha20Poly1305 cipher1 = new ChaCha20Poly1305(); + AEADParameters params = new AEADParameters(new KeyParameter(key), 128, nonce, additionalData); + cipher1.init(false, params); - byte[] calculatedMAC = PolyKeyCreator.create(macKey, additionalData, ciphertext); + byte[] output = new byte[cipher1.getOutputSize(ciphertextWithTag.length)]; + int len = cipher1.processBytes(ciphertextWithTag, 0, ciphertextWithTag.length, output, 0); + len += cipher1.doFinal(output, len); - if (!Arrays.constantTimeAreEqual(calculatedMAC, receivedMAC)) { + byte[] result = new byte[len]; + System.arraycopy(output, 0, result, 0, len); + + return result; + + } catch (InvalidCipherTextException e) { throw new TlsFatalAlert(AlertDescription.bad_record_mac); } + } - byte[] output = new byte[ciphertext.length]; - decryptCipher.processBytes(ciphertext, 0, ciphertext.length, output, 0); - - return output; + private static String bytesToHex(byte[] bytes) { + StringBuilder result = new StringBuilder(); + for (byte b : bytes) { + result.append(String.format("%02x", b)); + } + return result.toString(); } public byte[] decodeCiphertext(byte[] receivedMAC, byte[] ciphertext) throws IOException { return decodeCiphertext(receivedMAC, null, ciphertext); } - - private KeyParameter initRecordMAC(ChaChaEngine cipher) { - byte[] firstBlock = new byte[64]; - cipher.processBytes(firstBlock, 0, firstBlock.length, firstBlock, 0); - - // NOTE: The BC implementation puts 'r' after 'k' - System.arraycopy(firstBlock, 0, firstBlock, 32, 16); - KeyParameter macKey = new KeyParameter(firstBlock, 16, 32); - Poly1305KeyGenerator.clamp(macKey.getKey()); - return macKey; - } } diff --git a/src/main/java/io/github/hapjava/server/impl/crypto/ChachaEncoder.java b/src/main/java/io/github/hapjava/server/impl/crypto/ChachaEncoder.java old mode 100644 new mode 100755 index 3304e7d9a..889c9ca86 --- a/src/main/java/io/github/hapjava/server/impl/crypto/ChachaEncoder.java +++ b/src/main/java/io/github/hapjava/server/impl/crypto/ChachaEncoder.java @@ -1,20 +1,64 @@ package io.github.hapjava.server.impl.crypto; import java.io.IOException; -import org.bouncycastle.crypto.engines.ChaChaEngine; -import org.bouncycastle.crypto.generators.Poly1305KeyGenerator; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.modes.ChaCha20Poly1305; +import org.bouncycastle.crypto.params.AEADParameters; import org.bouncycastle.crypto.params.KeyParameter; -import org.bouncycastle.crypto.params.ParametersWithIV; public class ChachaEncoder { - private final ChaChaEngine encryptCipher; + private final ChaCha20Poly1305 cipher; + private final byte[] key; + private final byte[] nonce; public ChachaEncoder(byte[] key, byte[] nonce) throws IOException { + this.key = key; + // ChaCha20-Poly1305 requires exactly 12 bytes (96 bits) for nonce + this.nonce = ensureNonceSize(nonce); + this.cipher = new ChaCha20Poly1305(); + } + + private byte[] ensureNonceSize(byte[] nonce) { + if (nonce == null) { + return new byte[12]; // Default to zero nonce if null + } + + // For HomeKit pairing messages, handle Apple's string-based nonces + if (nonce.length == 8) { + // Apple's HomeKit implementation uses a specific nonce format + // Based on RFC 7539 and Apple's implementation, the nonce should be: + // - 4 bytes constant (0x00000000) + // - 8 bytes nonce string + // This matches ChaCha20's 96-bit nonce requirement and ChachaDecoder format + byte[] adjustedNonce = new byte[12]; + // Put the 8-byte nonce at the END (bytes 4-11), not at the beginning + System.arraycopy(nonce, 0, adjustedNonce, 4, 8); + // First 4 bytes remain zero (counter initialization) + return adjustedNonce; + } + + if (nonce.length == 12) { + return nonce; // Already correct size + } - this.encryptCipher = new ChaChaEngine(20); + byte[] adjustedNonce = new byte[12]; + if (nonce.length < 12) { + // Pad with zeros if too short + System.arraycopy(nonce, 0, adjustedNonce, 0, nonce.length); + } else { + // Truncate if too long + System.arraycopy(nonce, 0, adjustedNonce, 0, 12); + } + return adjustedNonce; + } - this.encryptCipher.init(true, new ParametersWithIV(new KeyParameter(key), nonce)); + private static String bytesToHex(byte[] bytes) { + StringBuilder result = new StringBuilder(); + for (byte b : bytes) { + result.append(String.format("%02x", b)); + } + return result.toString(); } public byte[] encodeCiphertext(byte[] plaintext) throws IOException { @@ -22,27 +66,28 @@ public byte[] encodeCiphertext(byte[] plaintext) throws IOException { } public byte[] encodeCiphertext(byte[] plaintext, byte[] additionalData) throws IOException { - KeyParameter macKey = initRecordMAC(encryptCipher); - - byte[] ciphertext = new byte[plaintext.length]; - encryptCipher.processBytes(plaintext, 0, plaintext.length, ciphertext, 0); + try { + // Use the nonce provided during construction + AEADParameters params = new AEADParameters(new KeyParameter(key), 128, nonce, additionalData); + cipher.init(true, params); - byte[] calculatedMAC = PolyKeyCreator.create(macKey, additionalData, ciphertext); - - byte[] ret = new byte[ciphertext.length + 16]; - System.arraycopy(ciphertext, 0, ret, 0, ciphertext.length); - System.arraycopy(calculatedMAC, 0, ret, ciphertext.length, 16); - return ret; - } + byte[] output = new byte[cipher.getOutputSize(plaintext.length)]; + int len = cipher.processBytes(plaintext, 0, plaintext.length, output, 0); + len += cipher.doFinal(output, len); - private KeyParameter initRecordMAC(ChaChaEngine cipher) { - byte[] firstBlock = new byte[64]; - cipher.processBytes(firstBlock, 0, firstBlock.length, firstBlock, 0); + // Split the result into ciphertext and MAC + byte[] ciphertext = new byte[plaintext.length]; + byte[] mac = new byte[16]; + System.arraycopy(output, 0, ciphertext, 0, plaintext.length); + System.arraycopy(output, plaintext.length, mac, 0, 16); - // NOTE: The BC implementation puts 'r' after 'k' - System.arraycopy(firstBlock, 0, firstBlock, 32, 16); - KeyParameter macKey = new KeyParameter(firstBlock, 16, 32); - Poly1305KeyGenerator.clamp(macKey.getKey()); - return macKey; + // Return combined ciphertext + MAC as expected by the original interface + byte[] ret = new byte[ciphertext.length + 16]; + System.arraycopy(ciphertext, 0, ret, 0, ciphertext.length); + System.arraycopy(mac, 0, ret, ciphertext.length, 16); + return ret; + } catch (InvalidCipherTextException e) { + throw new IOException("Encryption failed", e); + } } } diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/ExchangeHandler.java b/src/main/java/io/github/hapjava/server/impl/pairing/ExchangeHandler.java old mode 100644 new mode 100755 index 2310975fa..4b7c3eb11 --- a/src/main/java/io/github/hapjava/server/impl/pairing/ExchangeHandler.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/ExchangeHandler.java @@ -31,6 +31,8 @@ public ExchangeHandler(byte[] k, HomekitAuthInfo authInfo) { } public HttpResponse handle(PairSetupRequest req) throws Exception { + LOGGER.debug("ExchangeHandler: Starting M5 exchange with shared secret K: {}", bytesToHex(k)); + HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA512Digest()); hkdf.init( new HKDFParameters( @@ -40,12 +42,26 @@ public HttpResponse handle(PairSetupRequest req) throws Exception { byte[] okm = hkdf_enc_key = new byte[32]; hkdf.generateBytes(okm, 0, 32); + LOGGER.debug("ExchangeHandler: HKDF encryption key: {}", bytesToHex(okm)); + return decrypt((ExchangeRequest) req, okm); } private HttpResponse decrypt(ExchangeRequest req, byte[] key) throws Exception { - ChachaDecoder chacha = new ChachaDecoder(key, "PS-Msg05".getBytes(StandardCharsets.UTF_8)); - byte[] plaintext = chacha.decodeCiphertext(req.getAuthTagData(), req.getMessageData()); + LOGGER.debug("ExchangeHandler: Received AuthTag: {}", bytesToHex(req.getAuthTagData())); + LOGGER.debug("ExchangeHandler: Received MessageData: {}", bytesToHex(req.getMessageData())); + + try { + ChachaDecoder chacha = new ChachaDecoder(key, "PS-Msg05".getBytes(StandardCharsets.UTF_8)); + byte[] plaintext = chacha.decodeCiphertext(req.getAuthTagData(), req.getMessageData()); + return processDecryptedData(plaintext); + } catch (Exception e) { + LOGGER.error("ExchangeHandler: M5 decryption failed: {}", e.getMessage()); + throw new RuntimeException("HomeKit M5 message decryption failed", e); + } + } + + private HttpResponse processDecryptedData(byte[] plaintext) throws Exception { DecodeResult d = TypeLengthValueUtils.decode(plaintext); byte[] username = d.getBytes(MessageType.USERNAME); @@ -54,6 +70,14 @@ private HttpResponse decrypt(ExchangeRequest req, byte[] key) throws Exception { return createUser(username, ltpk, proof); } + private static String bytesToHex(byte[] bytes) { + StringBuilder result = new StringBuilder(); + for (byte b : bytes) { + result.append(String.format("%02x", b)); + } + return result.toString(); + } + private HttpResponse createUser(byte[] username, byte[] ltpk, byte[] proof) throws Exception { HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA512Digest()); hkdf.init( diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/PairSetupRequest.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairSetupRequest.java old mode 100644 new mode 100755 index 4e7260fed..6fa099be4 --- a/src/main/java/io/github/hapjava/server/impl/pairing/PairSetupRequest.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairSetupRequest.java @@ -85,10 +85,38 @@ static class ExchangeRequest extends PairSetupRequest { private final byte[] authTagData; public ExchangeRequest(DecodeResult d) { - messageData = new byte[d.getLength(MessageType.ENCRYPTED_DATA) - 16]; + // Get the complete encrypted data field + byte[] encryptedData = d.getBytes(MessageType.ENCRYPTED_DATA); + logger.debug("ExchangeRequest: Total encrypted data length: {}", encryptedData.length); + logger.debug("ExchangeRequest: Raw encrypted data: {}", bytesToHex(encryptedData)); + + // For HomeKit M5, the encrypted data contains ciphertext + 16-byte auth tag + // The auth tag is the LAST 16 bytes + if (encryptedData.length < 16) { + throw new RuntimeException( + "Encrypted data too short, expected at least 16 bytes for auth tag"); + } + + int ciphertextLength = encryptedData.length - 16; + messageData = new byte[ciphertextLength]; authTagData = new byte[16]; - d.getBytes(MessageType.ENCRYPTED_DATA, messageData, 0); - d.getBytes(MessageType.ENCRYPTED_DATA, authTagData, messageData.length); + + // Copy ciphertext (everything except last 16 bytes) + System.arraycopy(encryptedData, 0, messageData, 0, ciphertextLength); + // Copy auth tag (last 16 bytes) + System.arraycopy(encryptedData, ciphertextLength, authTagData, 0, 16); + + logger.debug("ExchangeRequest: Parsed ciphertext length: {}", messageData.length); + logger.debug("ExchangeRequest: Parsed ciphertext: {}", bytesToHex(messageData)); + logger.debug("ExchangeRequest: Parsed auth tag: {}", bytesToHex(authTagData)); + } + + private static String bytesToHex(byte[] bytes) { + StringBuilder result = new StringBuilder(); + for (byte b : bytes) { + result.append(String.format("%02x", b)); + } + return result.toString(); } public byte[] getMessageData() { From 9b7abd344300384e5b31fb964793bb471e76d628 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Fri, 2 Jan 2026 11:43:48 -0700 Subject: [PATCH 119/138] Update netty Closes #189 --- CHANGES.md | 1 + pom.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 95f83e79f..8da353402 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,6 @@ # HAP-Java 2.0.8 * Updated bouncy castle to 1.83 +* Updated netty to 4.1.126.Final # HAP-Java 2.0.7 * Add overloads to characteristics so that the username can be passed through. diff --git a/pom.xml b/pom.xml index b25007f26..3041f7396 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ UTF-8 - 4.1.72.Final + 4.1.126.Final From a62d34f498e8d589c951a7a8e66efacad05a35f8 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Fri, 2 Jan 2026 11:54:52 -0700 Subject: [PATCH 120/138] remove unused travis config --- .travis.yml | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f14d2e2b9..000000000 --- a/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: java -jdk: openjdk8 -after_success: -- openssl aes-256-cbc -pass pass:$ENCRYPTION_PASSWORD -in deploy/pubring.gpg.enc -out - deploy/pubring.gpg -d -- openssl aes-256-cbc -pass pass:$ENCRYPTION_PASSWORD -in deploy/secring.gpg.enc -out - deploy/secring.gpg -d -- deploy/publish.sh -env: - global: - - secure: f/Ka8GVA6NnyUktn+TtBhdjVuM1wQI24ANxKsinfm4ldjfC6jueONgt3bog40fQ+ud5gVtJBAhven/qRicx9hT8bnrNUBSbeSD74KfuaZ6ICUmFyG64mNgbfkbc/pRzSbUf5WVAHPtgCo/p5P2Kt5/4l6/+DKmxLEPQ6K8bMz3nQCv0VtsbuGWh+QRxewA3AYLXF/O3nmP5YbP/X081C+TLiQQoibaL9UUGGWmcdEdsupgNdivvPfoYSji+gw3Dj6dn/gFZC1Y3tdcciyA4WOcspCuhZ4OVHZrencHr7PvvCjwjTQQ9bOQZi3l2zcWMGtnZlwlREdzTjUDP6sc2sjDJYy4FL1I87LRAhkiCO+Bo/SznQxAlljtng5R7eqYplt7ByLDpA6GJZQbchn6O+3YqqC9bScgQlyGQiHpbNGAzY5RQRFmvOosYIFTJm6KxMNMO2jogwID72e/+iU/nn5VaUA1btTziTdshb4en2g6f2jLLKURFHKEcXKmZgEOUV1ajqTUh4e4I++drhM8Nq+7vSYpuXbmnVleNbqbq4fWmH6Y0JlMSTn9Hk/K2tq2zw+Jz1oFWeaE39JCkK5gf3IsyGoOSy+dXqI0f9LtG7bPV0dDQGaE3Dh8P1gsXIqTo1E8fD8C1CvZjFgTV4rHukRTk5nfiNxItyWqRIQiLfBqU= - - secure: wHq7ZQPVYVuHrxnQj5bE4KZscBWr9pYL6U1YO1n/P/obkUz4ni6wG9KgLSOk+v9zj69of/HNRtBuo794tup76QREwEYBdKZsBg7cf9Fe14M44WJjY0O9FXJnUbuRfRqTQ+WhbugwiYPVaNZkrBM3FNj/kt0VP/0CtFqB180vrzv9Gokh0ZUYEuqrA9DGV2OszCt27FUQKAGUxDvVdV7d+9D+2FugkeMRg4CLqpFFx5E0THvnQnoKGCLiMZKnv8DN5BGUedzXHFHZtldJNCve2XNYCVNp8xKlCslsR1bwNYsHNM1nuG4kPjWYQfGAIBzbratTpMUcUTIC4l3liG9PkRvg5MBxi2HEoB+FlH4r3X1Pzj5QBDvNP6/+hZjgJQhhKHEdgau1qcC/fVT6Rf/Hmc3EwPSYOKRobzxXJcSG2TDxijAQD4QO41ekMqQJ+FXHIMR7xL+hK//xZSPJJxvjaKgC081/L1GP5mNvXuuo5FjG+1dr4yCpRtpqAShvmcybGK9VHaRdh+MRG+Ec81PdXm2lqGAjHWkfZnDo79WeyEVkqcjTnv1+lgBrRC5SL1WLy3fErLGMJG1HjM41G2O8B6wVIm0nMM8ejoTocHNw3k1l1rSicM7s8wamk8hlU4QhF6wpfgY1LKs0hSkfqlaIdGLHldCsyKakTQSPSgq/TA0= - - secure: hknjOefKy9U3A9PLrrf+s3uUaD9Is2XnkKr4X4o5mT79iWBjHbNbGllK+o/lrh8oV+SNKs5l+mp+FVtB0JBJK1bPGb7r7nR8zJcCUNrfJlOYePAmRhlc6fyGcclPQ81LLrBvbgn1PXFOfcttjWjZH8uIVMc4uEgobYQeS1l6jPPL2w471PtBgajeF1MSpy1/pwnZEdAFVSAB8RMNnqR/s7KjwG+qhkFgLEr5nz3AhIj3JSXlBQRiUjDH+qlxIt8SOunQZVtt6nYAVDBGp89v6SYk6w2CrEri1aUu8/9I7OT/X9+jSD2ksVFvoWW1hXNHhf7FCWqhbQzO7fGOg90ZM/lmgOVbnA+dihtT60GfBfSET52bInqG1QjDnig+/kggVcupUjThVI7VFIl8SGJXfhQtlnwlCWG2OYq+JBXoy0xUpEdvUnbnJv/8i4uvFpGZ/cgqPazw3arrURL+Shd82l+aRFBhHAa+qTvWdmcxBF1TvkWIodfaEPa95PS4mmao709/2bo7rf+FwaTRFtikiW2Asw7xSNlWmRBl90cS+B62xFiie8H6OeeOFcSKTN07MfuYaCdWKfViHaBU/W7Dfp2Sc7isPEEuBiJYDHucH2dsw0qBk6DyWlnWMaxo297/9QGyopiQNocgG+efKd+x7/cENLKUEVUXKEiyD1aebXY= - - secure: r5CcVp0fSkOunI63uI0Y0LETKeWMGStDOJayKzHsSmUg5t0GlH/8Zd8YrItbppCW/WNOjZ2c3XHAnFf55N4hcOvaHIMLJQaPh4LrMEe1uhFXQFfF8RFte4Ek1KBD0l//MeZQZMqykyz+O/5q4dsAvOi6B617OW2bmpUaF8hs+x+/+6C7hWxYaOU/zHpRrb+LC4YpnHqwfVMgpUdKxo83uysMTPcJDjjBvn+MTiXQvxBBAz1k+3fLQxJSUur+pdc22D/i0VZFpkQ9UpQRosfu8hAjZo4NzTLt/PeKbLvIx4kPRBlJqsU89iRcBE8QKUi3Y/Kelif2e30SdDUhoDSHN66mRe2fx/QmsP29yM7myC3dtxRUedXYNw9nTyx+Gmj23nL/U4IzifNVizWZgoHNheAt0E7+Z0WVhGKTtrYyy/2ffn1DCa80A9jO5DS/nDHkqukaW0cwHFq2EbdKv1UQBacM0QFzgHTiH1qkOzAQ1dh5ekViAyUIQ8qR05M06yZzIEeCxY/p5x6fs/yzSoOI37t5t5V1qWW7QaJYDAGGQ/u+tNNieq9y2/w2umDPRHvcPS7jA6ElNlK/XIlwkJA8n9xOANRYVLI4AgaXm7dYBopK/bs88cMJ7l6rZt3TMJ78gafL1XA3E3qNoceuoSHYIdu5m3oBvKvqfgcrPt8QrKI= - - secure: cMqrDc1B+hsAQOguaJUWNDSb9Jdo51CiRBPXDP5dBhgf+oErOPED9obSmKiCPaDfaWjdZWENpBs5CPjm+drNF4xEPIoYKaLI4che16wxko+To6I7k+/aCDT+thzgi1qIoWTn7KTq0ZB73tRLDktyugND0fwetgKYMqBOn5laCazWQeLpdIq3r4zWG/dX30lOB4kdnsY2X4Yb+9OPZi9+ZEr8AAiI+FomEGCv22tKAUyA/SYqPA/XXg6XJ2hyDdkTfLrmuz+dJDasglIDj2Js4p/my0vsFtlvGIubdzfnbqCPmtFjECSfVmQz92947/Rb36zK5wrQH8UDsNWRqFmd/WDYbGX7Eg2hSrWpHGO8ury6yyZRvPL4R2XiOQ4/T1yTOosc57M/wG8+wLavKlULXiH+yDWwXJPTGbKbaXxfVxnu9t1VAoLx6f9dSUEd+NP5TdDVOyp0dyvjs/dAsTedCooMiuJw/Y2+bktmhr14I3XWs8+w1jFDLO6/GhH1QOoNf9wi3ywEKnYGfY5YXiFp8meBm7koZ5DvU21uxUG2OBrOYgYrt1KatmN9A0og7JR6QeHJ1yawT0wM3L14JbqOjktG2e3MNBCqNyP+hWDqD7RDRm1pITbTr5oz7gjXeAI1E00okQ3y2/SdbHEPOUu/tSc2YYsrPCfriRz1AHpedr0= From 5a8f9483c7ca7d299bd089f225ea5e9869de1e8a Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Fri, 2 Jan 2026 13:01:30 -0700 Subject: [PATCH 121/138] publish to GitHub Packages instead --- .github/workflows/deploy.yml | 18 +++++++----------- .github/workflows/release.yml | 16 +++++++--------- pom.xml | 8 ++++---- 3 files changed, 18 insertions(+), 24 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 61753eab7..4b1e82997 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -7,6 +7,9 @@ on: push: branches: [ master ] +permissions: + packages: write + jobs: build: @@ -14,26 +17,19 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up JDK 11 for Maven Central + - name: Set up JDK 11 uses: actions/setup-java@v3 with: distribution: 'zulu' java-version: '11' architecture: x64 - server-id: ossrh - gpg-private-key: ${{ secrets.GPG_SECRET_KEY }} - gpg-passphrase: MAVEN_GPG_PASSPHRASE - server-username: MAVEN_USERNAME - server-password: MAVEN_CENTRAL_TOKEN - name: Setup git profile run: | git config --global user.name "github-actions[bot]" git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" - - name: Publish Snapshot to Maven Central - run: mvn deploy -B -Possrh + - name: Publish Snapshot to GitHub Packages + run: mvn deploy -B env: - MAVEN_USERNAME: hap-java-dev - MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }} - MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Publish site run: mvn -B site-deploy -Dusername=github-actions -Dpassword=${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 48b5ee7cf..380426925 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,6 +12,7 @@ on: permissions: contents: write + packages: write jobs: build: @@ -27,14 +28,11 @@ jobs: distribution: 'zulu' java-version: '11' architecture: x64 - server-id: ossrh - gpg-private-key: ${{ secrets.GPG_SECRET_KEY }} - gpg-passphrase: MAVEN_GPG_PASSPHRASE - server-username: MAVEN_USERNAME - server-password: MAVEN_CENTRAL_TOKEN + - name: Setup git profile + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" - name: Maven release - run: mvn release:prepare release:perform -B -DreleaseVersion=${{ github.event.inputs.releaseVersion }} -Possrh + run: mvn release:prepare release:perform -B -DreleaseVersion=${{ github.event.inputs.releaseVersion }} env: - MAVEN_USERNAME: hap-java-dev - MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }} - MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/pom.xml b/pom.xml index 3041f7396..e1bf50876 100644 --- a/pom.xml +++ b/pom.xml @@ -278,12 +278,12 @@ https://hap-java.github.io/ - ossrh - https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ + github + https://maven.pkg.github.com/hap-java/HAP-Java - ossrh - https://s01.oss.sonatype.org/content/repositories/snapshots + github + https://maven.pkg.github.com/hap-java/HAP-Java From c23a813afefa27154378089362fceec0b94f96c8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 2 Jan 2026 20:05:27 +0000 Subject: [PATCH 122/138] [maven-release-plugin] prepare release hap-2.0.8 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e1bf50876..549efbad5 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.8-SNAPSHOT + 2.0.8 jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - HEAD + hap-2.0.8 From 92dd7476f19744221d4a150d64185a9b1d8685a7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 2 Jan 2026 20:05:28 +0000 Subject: [PATCH 123/138] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 549efbad5..5db91b7d5 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.8 + 2.0.9-SNAPSHOT jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - hap-2.0.8 + HEAD From b11d77f5fa9a21b94865261bc752f839a4e4fc39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jan 2026 17:10:03 -0700 Subject: [PATCH 124/138] Bump org.assertj:assertj-core from 3.19.0 to 3.27.7 (#195) Bumps [org.assertj:assertj-core](https://github.com/assertj/assertj) from 3.19.0 to 3.27.7. - [Release notes](https://github.com/assertj/assertj/releases) - [Commits](https://github.com/assertj/assertj/compare/assertj-core-3.19.0...assertj-build-3.27.7) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-version: 3.27.7 dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5db91b7d5..c292424af 100644 --- a/pom.xml +++ b/pom.xml @@ -162,7 +162,7 @@ org.assertj assertj-core - 3.19.0 + 3.27.7 test From dc610bc7885844ec73a2322848c7fc3a0cc703c0 Mon Sep 17 00:00:00 2001 From: udaw Date: Sun, 18 Oct 2026 22:15:15 +0200 Subject: [PATCH 125/138] Advertise uint32 format for valve duration characteristics (#202) SetDurationCharacteristic (D3) and RemainingDurationCharacteristic (D4) serialized their metadata with "format":"int", but the HAP spec defines both as UInt32. Add a protected IntegerCharacteristic constructor that takes the HAP format and use it for the two duration characteristics. All other integer characteristics keep advertising "int". Add regression tests for the D3/D4 formats and for an unchanged int-format characteristic. Co-Authored-By: Claude Opus 5.5 --- CHANGES.md | 3 + .../impl/base/IntegerCharacteristic.java | 49 +++++++++++++++- .../RemainingDurationCharacteristic.java | 1 + .../impl/valve/SetDurationCharacteristic.java | 1 + .../DurationCharacteristicFormatTest.java | 56 +++++++++++++++++++ 5 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 src/test/java/io/github/hapjava/characteristics/impl/valve/DurationCharacteristicFormatTest.java diff --git a/CHANGES.md b/CHANGES.md index 8da353402..4997cae9d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,6 @@ +# HAP-Java 2.0.9 +* Fix Set Duration and Remaining Duration characteristics to advertise format `uint32` instead of `int` [#202](https://github.com/hap-java/HAP-Java/issues/202) + # HAP-Java 2.0.8 * Updated bouncy castle to 1.83 * Updated netty to 4.1.126.Final diff --git a/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java index 96448eea8..6b6cd2982 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java @@ -47,8 +47,55 @@ public IntegerCharacteristic( Optional> setter, Optional> subscriber, Optional unsubscriber) { + this( + type, + "int", + description, + minValue, + maxValue, + unit, + getter, + setter, + subscriber, + unsubscriber); + } + + /** + * Constructor for characteristics whose HAP format differs from the default "int", e.g. "uint8", + * "uint16" or "uint32". Values are still handled as Java {@link Integer}, so the supported range + * must fit into a signed 32-bit integer. + * + * @param type a string containing a UUID that indicates the type of characteristic. Apple defines + * a set of these, however implementors can create their own as well. + * @param format the HAP format to advertise, e.g. "int" or "uint32". + * @param description a description of the characteristic to be passed to the consuming device. + * @param minValue the minimum supported value. + * @param maxValue the maximum supported value + * @param unit a description of the unit this characteristic supports. + * @param getter getter for value + * @param setter setter for value + * @param subscriber subscribers to changes + * @param unsubscriber unsubscribers to changes + */ + protected IntegerCharacteristic( + String type, + String format, + String description, + int minValue, + int maxValue, + String unit, + Optional>> getter, + Optional> setter, + Optional> subscriber, + Optional unsubscriber) { super( - type, "int", description, getter.isPresent(), setter.isPresent(), subscriber, unsubscriber); + type, + format, + description, + getter.isPresent(), + setter.isPresent(), + subscriber, + unsubscriber); this.minValue = minValue; this.maxValue = maxValue; this.unit = unit; diff --git a/src/main/java/io/github/hapjava/characteristics/impl/valve/RemainingDurationCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/valve/RemainingDurationCharacteristic.java index 828fafab1..29409027a 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/valve/RemainingDurationCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/valve/RemainingDurationCharacteristic.java @@ -26,6 +26,7 @@ public RemainingDurationCharacteristic( Runnable unsubscriber) { super( "000000D4-0000-1000-8000-0026BB765291", + "uint32", "remaining duration", minValue, maxValue, diff --git a/src/main/java/io/github/hapjava/characteristics/impl/valve/SetDurationCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/valve/SetDurationCharacteristic.java index 59cef5646..109a7bb6e 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/valve/SetDurationCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/valve/SetDurationCharacteristic.java @@ -24,6 +24,7 @@ public SetDurationCharacteristic( Runnable unsubscriber) { super( "000000D3-0000-1000-8000-0026BB765291", + "uint32", "set duration", minValue, maxValue, diff --git a/src/test/java/io/github/hapjava/characteristics/impl/valve/DurationCharacteristicFormatTest.java b/src/test/java/io/github/hapjava/characteristics/impl/valve/DurationCharacteristicFormatTest.java new file mode 100644 index 000000000..2e12b2daa --- /dev/null +++ b/src/test/java/io/github/hapjava/characteristics/impl/valve/DurationCharacteristicFormatTest.java @@ -0,0 +1,56 @@ +package io.github.hapjava.characteristics.impl.valve; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.hapjava.characteristics.impl.windowcovering.CurrentPositionCharacteristic; +import java.util.concurrent.CompletableFuture; +import javax.json.JsonObject; +import org.junit.Test; + +/** Regression tests for https://github.com/hap-java/HAP-Java/issues/202 */ +public class DurationCharacteristicFormatTest { + + @Test + public void setDurationAdvertisesUint32() { + SetDurationCharacteristic characteristic = + new SetDurationCharacteristic( + () -> CompletableFuture.completedFuture(300), value -> {}, callback -> {}, () -> {}); + + JsonObject json = characteristic.toJson(12).join(); + + assertThat(json.getString("type")).isEqualTo("D3"); + assertThat(json.getString("format")).isEqualTo("uint32"); + assertThat(json.getInt("value")).isEqualTo(300); + assertThat(json.getInt("minValue")).isEqualTo(0); + assertThat(json.getInt("maxValue")).isEqualTo(3600); + assertThat(json.getInt("minStep")).isEqualTo(1); + assertThat(json.getString("unit")).isEqualTo("s"); + } + + @Test + public void remainingDurationAdvertisesUint32() { + RemainingDurationCharacteristic characteristic = + new RemainingDurationCharacteristic( + () -> CompletableFuture.completedFuture(180), callback -> {}, () -> {}); + + JsonObject json = characteristic.toJson(13).join(); + + assertThat(json.getString("type")).isEqualTo("D4"); + assertThat(json.getString("format")).isEqualTo("uint32"); + assertThat(json.getInt("value")).isEqualTo(180); + assertThat(json.getInt("minValue")).isEqualTo(0); + assertThat(json.getInt("maxValue")).isEqualTo(3600); + assertThat(json.getString("unit")).isEqualTo("s"); + } + + @Test + public void otherIntegerCharacteristicsKeepIntFormat() { + CurrentPositionCharacteristic characteristic = + new CurrentPositionCharacteristic( + () -> CompletableFuture.completedFuture(50), callback -> {}, () -> {}); + + JsonObject json = characteristic.toJson(14).join(); + + assertThat(json.getString("format")).isEqualTo("int"); + } +} From d6d4dce3150c23bbe8d0246f0b8b3f73f7633dee Mon Sep 17 00:00:00 2001 From: udaw Date: Sun, 18 Oct 2026 22:31:43 +0200 Subject: [PATCH 126/138] Do not log pairing secrets in ExchangeHandler Remove DEBUG log statements that printed the SRP shared secret K, the HKDF-derived encryption key and the raw M5 auth tag / message data. No logic changes. Co-Authored-By: Claude Opus 5.5 --- CHANGES.md | 1 + .../hapjava/server/impl/pairing/ExchangeHandler.java | 7 ------- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4997cae9d..147cdd37e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,6 @@ # HAP-Java 2.0.9 * Fix Set Duration and Remaining Duration characteristics to advertise format `uint32` instead of `int` [#202](https://github.com/hap-java/HAP-Java/issues/202) +* Stop logging the SRP shared secret, derived key and M5 message data during pairing at DEBUG level # HAP-Java 2.0.8 * Updated bouncy castle to 1.83 diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/ExchangeHandler.java b/src/main/java/io/github/hapjava/server/impl/pairing/ExchangeHandler.java index 4b7c3eb11..6bae3ab84 100755 --- a/src/main/java/io/github/hapjava/server/impl/pairing/ExchangeHandler.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/ExchangeHandler.java @@ -31,8 +31,6 @@ public ExchangeHandler(byte[] k, HomekitAuthInfo authInfo) { } public HttpResponse handle(PairSetupRequest req) throws Exception { - LOGGER.debug("ExchangeHandler: Starting M5 exchange with shared secret K: {}", bytesToHex(k)); - HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA512Digest()); hkdf.init( new HKDFParameters( @@ -42,15 +40,10 @@ public HttpResponse handle(PairSetupRequest req) throws Exception { byte[] okm = hkdf_enc_key = new byte[32]; hkdf.generateBytes(okm, 0, 32); - LOGGER.debug("ExchangeHandler: HKDF encryption key: {}", bytesToHex(okm)); - return decrypt((ExchangeRequest) req, okm); } private HttpResponse decrypt(ExchangeRequest req, byte[] key) throws Exception { - LOGGER.debug("ExchangeHandler: Received AuthTag: {}", bytesToHex(req.getAuthTagData())); - LOGGER.debug("ExchangeHandler: Received MessageData: {}", bytesToHex(req.getMessageData())); - try { ChachaDecoder chacha = new ChachaDecoder(key, "PS-Msg05".getBytes(StandardCharsets.UTF_8)); byte[] plaintext = chacha.decodeCiphertext(req.getAuthTagData(), req.getMessageData()); From bf5955412a023304ffc0f8ffcd198538092329d8 Mon Sep 17 00:00:00 2001 From: udaw Date: Sun, 18 Oct 2026 22:32:47 +0200 Subject: [PATCH 127/138] Fix frame splitting in LengthPrefixedByteArrayProcessor - Read the length of each frame at the current position instead of always at the start of the read, so several frames of different length in one read are split correctly. - Clear the stored first length byte once it has been used, and do not take the fast path while such a byte is pending, so a read that ends after the first length byte is handled by step(). Adds tests that lock in the existing behaviour (single frame, several frames in one read, a frame split across two reads) plus regression tests for the two cases above. Co-Authored-By: Claude Opus 5.5 --- CHANGES.md | 1 + .../LengthPrefixedByteArrayProcessor.java | 5 +- .../LengthPrefixedByteArrayProcessorTest.java | 125 ++++++++++++++++++ 3 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 src/test/java/io/github/hapjava/server/impl/connections/LengthPrefixedByteArrayProcessorTest.java diff --git a/CHANGES.md b/CHANGES.md index 147cdd37e..a50fc7fae 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,7 @@ # HAP-Java 2.0.9 * Fix Set Duration and Remaining Duration characteristics to advertise format `uint32` instead of `int` [#202](https://github.com/hap-java/HAP-Java/issues/202) * Stop logging the SRP shared secret, derived key and M5 message data during pairing at DEBUG level +* Fix splitting of encrypted frames when several frames of different length arrive in one read, or when a read ends after the first length byte # HAP-Java 2.0.8 * Updated bouncy castle to 1.83 diff --git a/src/main/java/io/github/hapjava/server/impl/connections/LengthPrefixedByteArrayProcessor.java b/src/main/java/io/github/hapjava/server/impl/connections/LengthPrefixedByteArrayProcessor.java index 124acbb51..34ec3a41c 100644 --- a/src/main/java/io/github/hapjava/server/impl/connections/LengthPrefixedByteArrayProcessor.java +++ b/src/main/java/io/github/hapjava/server/impl/connections/LengthPrefixedByteArrayProcessor.java @@ -21,9 +21,9 @@ public synchronized Collection handle(byte[] data) { int pos = 0; LOGGER.trace( "Received message of length {}. Existing buffer is {}", data.length, buffer.size()); - if (buffer.size() == 0) { + if (buffer.size() == 0 && firstLengthByteBuffer == null) { while (data.length - pos > 18) { - int targetLength = (data[0] & 0xFF) + (data[1] & 0xFF) * 256 + 16 + 2; + int targetLength = (data[pos] & 0xFF) + (data[pos + 1] & 0xFF) * 256 + 16 + 2; LOGGER.trace("Attempting to read message of length {}", targetLength); if (data.length >= pos + targetLength) { byte[] b = new byte[targetLength - 2]; @@ -56,6 +56,7 @@ private void step(byte[] data, int pos, Collection results) { if (targetLength == 0) { if (firstLengthByteBuffer != null) { targetLength = (firstLengthByteBuffer & 0xFF) + (data[pos] & 0xFF) * 256 + 16; + firstLengthByteBuffer = null; pos += 1; LOGGER.trace( "Received the second byte after storing the first byte. New length is {}", diff --git a/src/test/java/io/github/hapjava/server/impl/connections/LengthPrefixedByteArrayProcessorTest.java b/src/test/java/io/github/hapjava/server/impl/connections/LengthPrefixedByteArrayProcessorTest.java new file mode 100644 index 000000000..9e839848d --- /dev/null +++ b/src/test/java/io/github/hapjava/server/impl/connections/LengthPrefixedByteArrayProcessorTest.java @@ -0,0 +1,125 @@ +package io.github.hapjava.server.impl.connections; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import org.junit.Test; + +public class LengthPrefixedByteArrayProcessorTest { + + private static final int TAG_LENGTH = 16; + + /** + * Builds a frame as it arrives on the wire: 2-byte little-endian payload length, payload, 16-byte + * tag. The payload and tag bytes start at 0x80 so that they never look like a short length. + */ + private static byte[] frame(int payloadLength, int seed) { + byte[] frame = new byte[2 + payloadLength + TAG_LENGTH]; + frame[0] = (byte) (payloadLength & 0xFF); + frame[1] = (byte) ((payloadLength >> 8) & 0xFF); + for (int i = 2; i < frame.length; i++) { + frame[i] = (byte) (0x80 + seed + i); + } + return frame; + } + + /** What the processor is expected to emit for a frame: payload + tag, without the length. */ + private static byte[] body(byte[] frame) { + return Arrays.copyOfRange(frame, 2, frame.length); + } + + private static byte[] concat(byte[]... parts) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + for (byte[] part : parts) { + out.write(part, 0, part.length); + } + return out.toByteArray(); + } + + private static List feed(LengthPrefixedByteArrayProcessor processor, byte[]... reads) { + List results = new ArrayList<>(); + for (byte[] read : reads) { + results.addAll(processor.handle(read)); + } + return results; + } + + private static List sizes(Collection results) { + List sizes = new ArrayList<>(); + for (byte[] result : results) { + sizes.add(result.length); + } + return sizes; + } + + private static void assertBodies(List results, byte[]... frames) { + List expectedSizes = new ArrayList<>(); + for (byte[] frame : frames) { + expectedSizes.add(frame.length - 2); + } + assertThat(sizes(results)).isEqualTo(expectedSizes); + for (int i = 0; i < frames.length; i++) { + assertThat(results.get(i)).as("frame %d", i).isEqualTo(body(frames[i])); + } + } + + @Test + public void singleFrameInOneRead() { + byte[] a = frame(100, 1); + assertBodies(feed(new LengthPrefixedByteArrayProcessor(), a), a); + } + + @Test + public void threeFramesInOneRead() { + byte[] a = frame(1024, 1); + byte[] b = frame(1024, 2); + byte[] c = frame(200, 3); + assertBodies(feed(new LengthPrefixedByteArrayProcessor(), concat(a, b, c)), a, b, c); + } + + @Test + public void frameSplitAcrossTwoReads() { + byte[] a = frame(1024, 1); + int[] offsets = {1, 2, 3, 10, 18, 19, 20, 500, a.length - 1}; + for (int offset : offsets) { + byte[] first = Arrays.copyOfRange(a, 0, offset); + byte[] second = Arrays.copyOfRange(a, offset, a.length); + List results = feed(new LengthPrefixedByteArrayProcessor(), first, second); + assertThat(sizes(results)).as("split at %d", offset).containsExactly(a.length - 2); + assertThat(results.get(0)).as("split at %d", offset).isEqualTo(body(a)); + } + } + + @Test + public void frameSplitWithNextFrameInSecondRead() { + byte[] a = frame(1024, 1); + byte[] b = frame(200, 2); + byte[] first = Arrays.copyOfRange(a, 0, 500); + byte[] second = concat(Arrays.copyOfRange(a, 500, a.length), b); + assertBodies(feed(new LengthPrefixedByteArrayProcessor(), first, second), a, b); + } + + @Test + public void shortFrameFollowedByLongerFrameInOneRead() { + byte[] a = frame(5, 1); + byte[] b = frame(100, 2); + List results = feed(new LengthPrefixedByteArrayProcessor(), concat(a, b)); + assertThat(sizes(results)).containsExactly(21, 116); + assertBodies(results, a, b); + } + + @Test + public void singleLengthByteThenRestThenAnotherFrame() { + byte[] a = frame(5, 1); + byte[] b = frame(40, 2); + byte[] first = Arrays.copyOfRange(a, 0, 1); + byte[] second = concat(Arrays.copyOfRange(a, 1, a.length), b); + List results = feed(new LengthPrefixedByteArrayProcessor(), first, second); + assertThat(sizes(results)).containsExactly(21, 56); + assertBodies(results, a, b); + } +} From c5d9dbd351cc28aa8c1b5de2a97c135d0060817f Mon Sep 17 00:00:00 2001 From: udaw Date: Sun, 18 Oct 2026 22:36:01 +0200 Subject: [PATCH 128/138] Grant contents: write to deploy workflow so site-deploy can push gh-pages The permissions block added in 5a8f9483 only granted packages: write, which drops the GITHUB_TOKEN's contents scope. maven-scm-publish-plugin then fails to push the site to gh-pages with a 403. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/deploy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4b1e82997..3206d8a70 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -8,6 +8,7 @@ on: branches: [ master ] permissions: + contents: write packages: write jobs: From 25ced5e44a2ce18ceb2e2b3b4d7099b923a6158c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 18 Oct 2026 20:44:28 +0000 Subject: [PATCH 129/138] [maven-release-plugin] prepare release hap-2.0.9 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c292424af..d476d8e49 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.9-SNAPSHOT + 2.0.9 jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - HEAD + hap-2.0.9 From bc039779085d7f967dbe4c25c47e73ca62c89d8d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 18 Oct 2026 20:44:31 +0000 Subject: [PATCH 130/138] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d476d8e49..a1a952779 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ hap hap-java Homekit Accessory Protocol for Java - 2.0.9 + 2.0.10-SNAPSHOT jar https://github.com/hap-java/HAP-Java @@ -26,7 +26,7 @@ scm:git:https://github.com/hap-java/HAP-Java.git scm:git:https://github.com/hap-java/HAP-Java.git https://github.com/hap-java/HAP-Java.git - hap-2.0.9 + HEAD From 25e9efe3b6e198aae42a5bb11c0abd9544e72773 Mon Sep 17 00:00:00 2001 From: udaw Date: Sun, 18 Oct 2026 22:54:39 +0200 Subject: [PATCH 131/138] Only run deploy in release:perform maven-release-plugin defaults release:perform to 'deploy site-deploy' because the pom has a distributionManagement/site element. The site-deploy runs in the release checkout without git credentials and fails with "could not read Username", marking every release run as failed after the artifacts were already published. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 380426925..53c2c8b9e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,6 +33,7 @@ jobs: git config --global user.name "github-actions[bot]" git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" - name: Maven release - run: mvn release:prepare release:perform -B -DreleaseVersion=${{ github.event.inputs.releaseVersion }} + # Only deploy; the site is published by the deploy workflow on pushes to master. + run: mvn release:prepare release:perform -B -DreleaseVersion=${{ github.event.inputs.releaseVersion }} -Dgoals=deploy env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 79ee9a1341af3f47cc8cf78f122618461951178c Mon Sep 17 00:00:00 2001 From: udaw Date: Sun, 18 Oct 2026 23:18:58 +0200 Subject: [PATCH 132/138] Remove redundant comment from release workflow Addresses review feedback on #206. --- .github/workflows/release.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 53c2c8b9e..29bfe0e59 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,7 +33,6 @@ jobs: git config --global user.name "github-actions[bot]" git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" - name: Maven release - # Only deploy; the site is published by the deploy workflow on pushes to master. run: mvn release:prepare release:perform -B -DreleaseVersion=${{ github.event.inputs.releaseVersion }} -Dgoals=deploy env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 3389210fa18420d5d75c821fb5a1548ed6748250 Mon Sep 17 00:00:00 2001 From: udaw Date: Sun, 18 Oct 2026 23:31:33 +0200 Subject: [PATCH 133/138] Add custom min/max constructors to remaining range characteristics (#162) Add DEFAULT_MIN_VALUE/DEFAULT_MAX_VALUE (and DEFAULT_STEP for float characteristics) and a constructor taking min/max (and step) to 19 characteristics that only supported fixed ranges, following the pattern of RotationSpeedCharacteristic. Existing constructors delegate with the previous values, so their serialized output is unchanged. --- CHANGES.md | 3 + .../impl/audio/VolumeCharacteristic.java | 16 +- .../battery/BatteryLevelCharacteristic.java | 15 +- .../impl/common/WaterLavelCharacteristic.java | 19 +- .../FilterLifeLevelCharacteristic.java | 19 +- ...tyDehumidifierThresholdCharacteristic.java | 27 +- ...dityHumidifierThresholdCharacteristic.java | 27 +- ...CurrentRelativeHumidityCharacteristic.java | 19 +- .../TargetRelativeHumidityCharacteristic.java | 27 +- .../lightbulb/BrightnessCharacteristic.java | 16 +- .../impl/lightbulb/HueCharacteristic.java | 28 +- .../lightbulb/SaturationCharacteristic.java | 27 +- .../slat/CurrentTiltAngleCharacteristic.java | 15 +- .../slat/TargetTiltAngleCharacteristic.java | 16 +- ...rentHorizontalTiltAngleCharacteristic.java | 15 +- .../CurrentPositionCharacteristic.java | 15 +- ...urrentVerticalTiltAngleCharacteristic.java | 15 +- ...rgetHorizontalTiltAngleCharacteristic.java | 16 +- .../TargetPositionCharacteristic.java | 16 +- ...TargetVerticalTiltAngleCharacteristic.java | 16 +- .../impl/CustomRangeCharacteristicsTest.java | 253 ++++++++++++++++++ 21 files changed, 574 insertions(+), 46 deletions(-) create mode 100644 src/test/java/io/github/hapjava/characteristics/impl/CustomRangeCharacteristicsTest.java diff --git a/CHANGES.md b/CHANGES.md index a50fc7fae..6dfc34b6d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,6 @@ +# HAP-Java 2.0.10 +* Add constructors with custom min/max (and step for float values) to the remaining range characteristics [#162](https://github.com/hap-java/HAP-Java/issues/162) + # HAP-Java 2.0.9 * Fix Set Duration and Remaining Duration characteristics to advertise format `uint32` instead of `int` [#202](https://github.com/hap-java/HAP-Java/issues/202) * Stop logging the SRP shared secret, derived key and M5 message data during pairing at DEBUG level diff --git a/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java index 2d1caedd7..2a4223d5c 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java @@ -11,8 +11,12 @@ /** Volume characteristic to control audio volume. */ public class VolumeCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { + public static final int DEFAULT_MIN_VALUE = 0; + public static final int DEFAULT_MAX_VALUE = 100; public VolumeCharacteristic( + int minValue, + int maxValue, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -20,12 +24,20 @@ public VolumeCharacteristic( super( "00000119-0000-1000-8000-0026BB765291", "volume", - 0, - 100, + minValue, + maxValue, "percentage", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public VolumeCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java index 0fa56ff40..ae7097ccd 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java @@ -11,20 +11,31 @@ /** This characteristic describes the current level of the battery. */ public class BatteryLevelCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { + public static final int DEFAULT_MIN_VALUE = 0; + public static final int DEFAULT_MAX_VALUE = 100; public BatteryLevelCharacteristic( + int minValue, + int maxValue, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "00000068-0000-1000-8000-0026BB765291", "battery level", - 0, - 100, + minValue, + maxValue, "%", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public BatteryLevelCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java index dda11b087..d9d29bac7 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java @@ -11,21 +11,34 @@ /** This characteristic describes the water level. */ public class WaterLavelCharacteristic extends FloatCharacteristic implements EventableCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 100; + public static final double DEFAULT_STEP = 1; public WaterLavelCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "000000B5-0000-1000-8000-0026BB765291", "water level", - 0, - 100, - 1, + minValue, + maxValue, + minStep, "%", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public WaterLavelCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java index 29cdf8d6e..6213769c9 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java @@ -10,21 +10,34 @@ public class FilterLifeLevelCharacteristic extends FloatCharacteristic implements EventableCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 100; + public static final double DEFAULT_STEP = 1; public FilterLifeLevelCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "000000AB-0000-1000-8000-0026BB765291", "Filter Life Level", - 0, - 100, - 1, + minValue, + maxValue, + minStep, "%", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public FilterLifeLevelCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java index b8a4ca569..84a31fe24 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java @@ -13,8 +13,14 @@ * turns on. */ public class HumidityDehumidifierThresholdCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 100; + public static final double DEFAULT_STEP = 1; public HumidityDehumidifierThresholdCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -22,13 +28,28 @@ public HumidityDehumidifierThresholdCharacteristic( super( "000000C9-0000-1000-8000-0026BB765291", "humidity threshold dehumidifier", - 0, - 100, - 1, + minValue, + maxValue, + minStep, "%", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public HumidityDehumidifierThresholdCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + this( + DEFAULT_MIN_VALUE, + DEFAULT_MAX_VALUE, + DEFAULT_STEP, + getter, + setter, + subscriber, + unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java index 2ee8adbf2..14b78bde1 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java @@ -13,8 +13,14 @@ * turns on. */ public class HumidityHumidifierThresholdCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 100; + public static final double DEFAULT_STEP = 1; public HumidityHumidifierThresholdCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -22,13 +28,28 @@ public HumidityHumidifierThresholdCharacteristic( super( "000000CA-0000-1000-8000-0026BB765291", "humidity threshold humidifier", - 0, - 100, - 1, + minValue, + maxValue, + minStep, "%", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public HumidityHumidifierThresholdCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + this( + DEFAULT_MIN_VALUE, + DEFAULT_MAX_VALUE, + DEFAULT_STEP, + getter, + setter, + subscriber, + unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java index be77125b6..7ce18fb46 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java @@ -9,21 +9,34 @@ /** This characteristic describes the current relative humidity. */ public class CurrentRelativeHumidityCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 100; + public static final double DEFAULT_STEP = 1; public CurrentRelativeHumidityCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "00000010-0000-1000-8000-0026BB765291", "Current Relative Humidity", - 0, - 100, - 1, + minValue, + maxValue, + minStep, "%", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public CurrentRelativeHumidityCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java index ab8ad7e29..600ca0f7c 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java @@ -10,8 +10,14 @@ /** This characteristic describes the target relative humidity. */ public class TargetRelativeHumidityCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 100; + public static final double DEFAULT_STEP = 1; public TargetRelativeHumidityCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -19,13 +25,28 @@ public TargetRelativeHumidityCharacteristic( super( "00000034-0000-1000-8000-0026BB765291", "Target Relative Humidity", - 0, - 100, - 1, + minValue, + maxValue, + minStep, "%", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public TargetRelativeHumidityCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + this( + DEFAULT_MIN_VALUE, + DEFAULT_MAX_VALUE, + DEFAULT_STEP, + getter, + setter, + subscriber, + unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java index 693e42efc..b68cb914b 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java @@ -15,8 +15,12 @@ */ public class BrightnessCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { + public static final int DEFAULT_MIN_VALUE = 0; + public static final int DEFAULT_MAX_VALUE = 100; public BrightnessCharacteristic( + int minValue, + int maxValue, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -24,12 +28,20 @@ public BrightnessCharacteristic( super( "00000008-0000-1000-8000-0026BB765291", "level of brightness", - 0, - 100, + minValue, + maxValue, "%", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public BrightnessCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java index 7a00816bc..f41a92319 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java @@ -10,7 +10,14 @@ /** This characteristic describes hue or color. */ public class HueCharacteristic extends FloatCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 360; + public static final double DEFAULT_STEP = 1; + public HueCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -18,13 +25,28 @@ public HueCharacteristic( super( "00000013-0000-1000-8000-0026BB765291", "hue or color", - 0, - 360, - 1, + minValue, + maxValue, + minStep, "arcdegrees", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public HueCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + this( + DEFAULT_MIN_VALUE, + DEFAULT_MAX_VALUE, + DEFAULT_STEP, + getter, + setter, + subscriber, + unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java index 90694ef98..5d2f7033b 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java @@ -12,8 +12,14 @@ /** This characteristic describes color saturation. */ public class SaturationCharacteristic extends FloatCharacteristic implements EventableCharacteristic { + public static final double DEFAULT_MIN_VALUE = 0; + public static final double DEFAULT_MAX_VALUE = 100; + public static final double DEFAULT_STEP = 1; public SaturationCharacteristic( + double minValue, + double maxValue, + double minStep, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -21,13 +27,28 @@ public SaturationCharacteristic( super( "0000002F-0000-1000-8000-0026BB765291", "color saturation", - 0, - 100, - 1, + minValue, + maxValue, + minStep, "%", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public SaturationCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + this( + DEFAULT_MIN_VALUE, + DEFAULT_MAX_VALUE, + DEFAULT_STEP, + getter, + setter, + subscriber, + unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java index 3a4868b2c..0f186281a 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java @@ -11,20 +11,31 @@ /** This characteristic describe current tilt angle (between -90 and 90) */ public class CurrentTiltAngleCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { + public static final int DEFAULT_MIN_VALUE = -90; + public static final int DEFAULT_MAX_VALUE = 90; public CurrentTiltAngleCharacteristic( + int minValue, + int maxValue, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "000000C1-0000-1000-8000-0026BB765291", "current tilt angle", - -90, - 90, + minValue, + maxValue, "arcdegrees", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public CurrentTiltAngleCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java index 2a327522d..44824f350 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java @@ -12,8 +12,12 @@ /** This characteristic describes the target angle of slats. */ public class TargetTiltAngleCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { + public static final int DEFAULT_MIN_VALUE = -90; + public static final int DEFAULT_MAX_VALUE = 90; public TargetTiltAngleCharacteristic( + int minValue, + int maxValue, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -21,12 +25,20 @@ public TargetTiltAngleCharacteristic( super( "000000C2-0000-1000-8000-0026BB765291", "target tilt angle", - -90, - 90, + minValue, + maxValue, "arcdegrees", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public TargetTiltAngleCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java index 1255d7eff..272a822f5 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentHorizontalTiltAngleCharacteristic.java @@ -11,20 +11,31 @@ /** This characteristic describes the current angle of horizontal slats. */ public class CurrentHorizontalTiltAngleCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { + public static final int DEFAULT_MIN_VALUE = -90; + public static final int DEFAULT_MAX_VALUE = 90; public CurrentHorizontalTiltAngleCharacteristic( + int minValue, + int maxValue, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "0000006C-0000-1000-8000-0026BB765291", "current horizontal tilt angle", - -90, - 90, + minValue, + maxValue, "arcdegrees", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public CurrentHorizontalTiltAngleCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java index d51614615..e9ed063c8 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java @@ -11,20 +11,31 @@ /** This characteristic describes the current position of accessories. */ public class CurrentPositionCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { + public static final int DEFAULT_MIN_VALUE = 0; + public static final int DEFAULT_MAX_VALUE = 100; public CurrentPositionCharacteristic( + int minValue, + int maxValue, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "0000006D-0000-1000-8000-0026BB765291", "current position", - 0, - 100, + minValue, + maxValue, "%", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public CurrentPositionCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java index c6258a2db..7d7168973 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentVerticalTiltAngleCharacteristic.java @@ -11,20 +11,31 @@ /** This characteristic describes the current angle of vertical slats for accessory. */ public class CurrentVerticalTiltAngleCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { + public static final int DEFAULT_MIN_VALUE = -90; + public static final int DEFAULT_MAX_VALUE = 90; public CurrentVerticalTiltAngleCharacteristic( + int minValue, + int maxValue, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "0000006E-0000-1000-8000-0026BB765291", "current vertical tilt angle", - -90, - 90, + minValue, + maxValue, "arcdegrees", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public CurrentVerticalTiltAngleCharacteristic( + Supplier> getter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java index 38c075760..17639ba6e 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetHorizontalTiltAngleCharacteristic.java @@ -12,8 +12,12 @@ /** This characteristic describes the target angle of horizontal slats for accessories. */ public class TargetHorizontalTiltAngleCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { + public static final int DEFAULT_MIN_VALUE = -90; + public static final int DEFAULT_MAX_VALUE = 90; public TargetHorizontalTiltAngleCharacteristic( + int minValue, + int maxValue, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -21,12 +25,20 @@ public TargetHorizontalTiltAngleCharacteristic( super( "0000007B-0000-1000-8000-0026BB765291", "target horizontal tilt angle", - -90, - 90, + minValue, + maxValue, "arcdegrees", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public TargetHorizontalTiltAngleCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java index 6398a0b37..82d06c4b2 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java @@ -12,8 +12,12 @@ /** This characteristic describes the target position of accessories like door, window, blinds. */ public class TargetPositionCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { + public static final int DEFAULT_MIN_VALUE = 0; + public static final int DEFAULT_MAX_VALUE = 100; public TargetPositionCharacteristic( + int minValue, + int maxValue, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -21,12 +25,20 @@ public TargetPositionCharacteristic( super( "0000007C-0000-1000-8000-0026BB765291", "target position", - 0, - 100, + minValue, + maxValue, "%", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public TargetPositionCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber); + } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java index 4d7c3ad71..03fd7a3f5 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetVerticalTiltAngleCharacteristic.java @@ -12,8 +12,12 @@ /** This characteristic describes the target angle of vertical slats. */ public class TargetVerticalTiltAngleCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { + public static final int DEFAULT_MIN_VALUE = -90; + public static final int DEFAULT_MAX_VALUE = 90; public TargetVerticalTiltAngleCharacteristic( + int minValue, + int maxValue, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -21,12 +25,20 @@ public TargetVerticalTiltAngleCharacteristic( super( "0000007D-0000-1000-8000-0026BB765291", "target vertical tilt angle", - -90, - 90, + minValue, + maxValue, "arcdegrees", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } + + public TargetVerticalTiltAngleCharacteristic( + Supplier> getter, + ExceptionalConsumer setter, + Consumer subscriber, + Runnable unsubscriber) { + this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber); + } } diff --git a/src/test/java/io/github/hapjava/characteristics/impl/CustomRangeCharacteristicsTest.java b/src/test/java/io/github/hapjava/characteristics/impl/CustomRangeCharacteristicsTest.java new file mode 100644 index 000000000..800719d6d --- /dev/null +++ b/src/test/java/io/github/hapjava/characteristics/impl/CustomRangeCharacteristicsTest.java @@ -0,0 +1,253 @@ +package io.github.hapjava.characteristics.impl; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.hapjava.characteristics.Characteristic; +import io.github.hapjava.characteristics.impl.audio.VolumeCharacteristic; +import io.github.hapjava.characteristics.impl.battery.BatteryLevelCharacteristic; +import io.github.hapjava.characteristics.impl.common.WaterLavelCharacteristic; +import io.github.hapjava.characteristics.impl.filtermaintenance.FilterLifeLevelCharacteristic; +import io.github.hapjava.characteristics.impl.humidifier.HumidityDehumidifierThresholdCharacteristic; +import io.github.hapjava.characteristics.impl.humidifier.HumidityHumidifierThresholdCharacteristic; +import io.github.hapjava.characteristics.impl.humiditysensor.CurrentRelativeHumidityCharacteristic; +import io.github.hapjava.characteristics.impl.humiditysensor.TargetRelativeHumidityCharacteristic; +import io.github.hapjava.characteristics.impl.lightbulb.BrightnessCharacteristic; +import io.github.hapjava.characteristics.impl.lightbulb.HueCharacteristic; +import io.github.hapjava.characteristics.impl.lightbulb.SaturationCharacteristic; +import io.github.hapjava.characteristics.impl.slat.CurrentTiltAngleCharacteristic; +import io.github.hapjava.characteristics.impl.slat.TargetTiltAngleCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.CurrentHorizontalTiltAngleCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.CurrentPositionCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.CurrentVerticalTiltAngleCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.TargetHorizontalTiltAngleCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.TargetPositionCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.TargetVerticalTiltAngleCharacteristic; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import javax.json.JsonObject; +import org.junit.Test; + +/** Custom min/max support, see https://github.com/hap-java/HAP-Java/issues/162 */ +public class CustomRangeCharacteristicsTest { + + /** Serialized output of the default constructors, captured before custom ranges were added. */ + private static final List DEFAULT_CONSTRUCTORS = + Arrays.asList( + Case.of( + "BatteryLevelCharacteristic", + new BatteryLevelCharacteristic( + () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"68\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"%\"}"), + Case.of( + "BrightnessCharacteristic", + new BrightnessCharacteristic( + () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"8\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"%\"}"), + Case.of( + "CurrentPositionCharacteristic", + new CurrentPositionCharacteristic( + () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"6D\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"%\"}"), + Case.of( + "TargetPositionCharacteristic", + new TargetPositionCharacteristic( + () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"7C\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"%\"}"), + Case.of( + "VolumeCharacteristic", + new VolumeCharacteristic( + () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"119\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"percentage\"}"), + Case.of( + "CurrentTiltAngleCharacteristic", + new CurrentTiltAngleCharacteristic( + () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"C1\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), + Case.of( + "TargetTiltAngleCharacteristic", + new TargetTiltAngleCharacteristic( + () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"C2\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), + Case.of( + "CurrentHorizontalTiltAngleCharacteristic", + new CurrentHorizontalTiltAngleCharacteristic( + () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"6C\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), + Case.of( + "TargetHorizontalTiltAngleCharacteristic", + new TargetHorizontalTiltAngleCharacteristic( + () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"7B\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), + Case.of( + "CurrentVerticalTiltAngleCharacteristic", + new CurrentVerticalTiltAngleCharacteristic( + () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"6E\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), + Case.of( + "TargetVerticalTiltAngleCharacteristic", + new TargetVerticalTiltAngleCharacteristic( + () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"7D\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), + Case.of( + "CurrentRelativeHumidityCharacteristic", + new CurrentRelativeHumidityCharacteristic( + () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"10\",\"perms\":[\"pr\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"), + Case.of( + "TargetRelativeHumidityCharacteristic", + new TargetRelativeHumidityCharacteristic( + () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"34\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"), + Case.of( + "FilterLifeLevelCharacteristic", + new FilterLifeLevelCharacteristic( + () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"AB\",\"perms\":[\"pr\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"), + Case.of( + "HueCharacteristic", + new HueCharacteristic( + () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"13\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":360.0,\"minStep\":1.0,\"unit\":\"arcdegrees\"}"), + Case.of( + "SaturationCharacteristic", + new SaturationCharacteristic( + () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"2F\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"), + Case.of( + "HumidityDehumidifierThresholdCharacteristic", + new HumidityDehumidifierThresholdCharacteristic( + () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"C9\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"), + Case.of( + "HumidityHumidifierThresholdCharacteristic", + new HumidityHumidifierThresholdCharacteristic( + () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"CA\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"), + Case.of( + "WaterLavelCharacteristic", + new WaterLavelCharacteristic( + () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"B5\",\"perms\":[\"pr\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}")); + + @Test + public void defaultConstructorsKeepTheirSerializedOutput() { + for (Case c : DEFAULT_CONSTRUCTORS) { + assertThat(c.characteristic.toJson(7).join().toString()).as(c.name).isEqualTo(c.expectedJson); + } + } + + @Test + public void integerCharacteristicsAdvertiseCustomRange() { + for (Characteristic c : + Arrays.asList( + new BatteryLevelCharacteristic( + 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), + new BrightnessCharacteristic( + 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), + new CurrentPositionCharacteristic( + 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), + new TargetPositionCharacteristic( + 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), + new VolumeCharacteristic( + 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), + new CurrentTiltAngleCharacteristic( + 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), + new TargetTiltAngleCharacteristic( + 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), + new CurrentHorizontalTiltAngleCharacteristic( + 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), + new TargetHorizontalTiltAngleCharacteristic( + 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), + new CurrentVerticalTiltAngleCharacteristic( + 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), + new TargetVerticalTiltAngleCharacteristic( + 10, + 20, + () -> CompletableFuture.completedFuture(50), + v -> {}, + cb -> {}, + () -> {}))) { + JsonObject json = c.toJson(7).join(); + String name = c.getClass().getSimpleName(); + assertThat(json.getInt("minValue")).as(name).isEqualTo(10); + assertThat(json.getInt("maxValue")).as(name).isEqualTo(20); + assertThat(json.getInt("minStep")).as(name).isEqualTo(1); + } + } + + @Test + public void floatCharacteristicsAdvertiseCustomRangeAndClampToIt() { + for (Characteristic c : + Arrays.asList( + new CurrentRelativeHumidityCharacteristic( + 10, 20, 0.5, () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}), + new TargetRelativeHumidityCharacteristic( + 10, + 20, + 0.5, + () -> CompletableFuture.completedFuture(50.0), + v -> {}, + cb -> {}, + () -> {}), + new FilterLifeLevelCharacteristic( + 10, 20, 0.5, () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}), + new HueCharacteristic( + 10, + 20, + 0.5, + () -> CompletableFuture.completedFuture(50.0), + v -> {}, + cb -> {}, + () -> {}), + new SaturationCharacteristic( + 10, + 20, + 0.5, + () -> CompletableFuture.completedFuture(50.0), + v -> {}, + cb -> {}, + () -> {}), + new HumidityDehumidifierThresholdCharacteristic( + 10, + 20, + 0.5, + () -> CompletableFuture.completedFuture(50.0), + v -> {}, + cb -> {}, + () -> {}), + new HumidityHumidifierThresholdCharacteristic( + 10, + 20, + 0.5, + () -> CompletableFuture.completedFuture(50.0), + v -> {}, + cb -> {}, + () -> {}), + new WaterLavelCharacteristic( + 10, 20, 0.5, () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}))) { + JsonObject json = c.toJson(7).join(); + String name = c.getClass().getSimpleName(); + assertThat(json.getJsonNumber("minValue").doubleValue()).as(name).isEqualTo(10.0); + assertThat(json.getJsonNumber("maxValue").doubleValue()).as(name).isEqualTo(20.0); + assertThat(json.getJsonNumber("minStep").doubleValue()).as(name).isEqualTo(0.5); + assertThat(json.getJsonNumber("value").doubleValue()).as(name).isEqualTo(20.0); + } + } + + private static class Case { + final String name; + final Characteristic characteristic; + final String expectedJson; + + private Case(String name, Characteristic characteristic, String expectedJson) { + this.name = name; + this.characteristic = characteristic; + this.expectedJson = expectedJson; + } + + static Case of(String name, Characteristic characteristic, String expectedJson) { + return new Case(name, characteristic, expectedJson); + } + } +} From 298f6dd525ce78247ffa9ae15c19559c233eb443 Mon Sep 17 00:00:00 2001 From: udaw Date: Mon, 19 Oct 2026 10:35:23 +0200 Subject: [PATCH 134/138] Add optional Mute characteristic to Doorbell service Mute is an optional characteristic of the Doorbell service. The 2.0.1 changelog lists it for the Doorbell service, but it was only wired into DoorService. DoorService is left unchanged to stay compatible. --- CHANGES.md | 3 ++ .../services/impl/DoorbellService.java | 14 +++++ .../services/impl/DoorbellServiceTest.java | 51 +++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 src/test/java/io/github/hapjava/services/impl/DoorbellServiceTest.java diff --git a/CHANGES.md b/CHANGES.md index a50fc7fae..2f8e24550 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,6 @@ +# HAP-Java 2.0.10 +* Add optional Mute characteristic to Doorbell service + # HAP-Java 2.0.9 * Fix Set Duration and Remaining Duration characteristics to advertise format `uint32` instead of `int` [#202](https://github.com/hap-java/HAP-Java/issues/202) * Stop logging the SRP shared secret, derived key and M5 message data during pairing at DEBUG level diff --git a/src/main/java/io/github/hapjava/services/impl/DoorbellService.java b/src/main/java/io/github/hapjava/services/impl/DoorbellService.java index fd8fc7004..06ee325d4 100644 --- a/src/main/java/io/github/hapjava/services/impl/DoorbellService.java +++ b/src/main/java/io/github/hapjava/services/impl/DoorbellService.java @@ -2,8 +2,10 @@ import io.github.hapjava.accessories.DoorbellAccessory; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithBrightness; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithMute; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName; import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithVolume; +import io.github.hapjava.characteristics.impl.audio.MuteCharacteristic; import io.github.hapjava.characteristics.impl.audio.VolumeCharacteristic; import io.github.hapjava.characteristics.impl.common.NameCharacteristic; import io.github.hapjava.characteristics.impl.common.ProgrammableSwitchEventCharacteristic; @@ -42,6 +44,14 @@ public DoorbellService(DoorbellAccessory accessory) { ((AccessoryWithVolume) accessory)::subscribeVolume, ((AccessoryWithVolume) accessory)::unsubscribeVolume)); } + if (accessory instanceof AccessoryWithMute) { + addOptionalCharacteristic( + new MuteCharacteristic( + ((AccessoryWithMute) accessory)::isMuted, + ((AccessoryWithMute) accessory)::setMute, + ((AccessoryWithMute) accessory)::subscribeMuteState, + ((AccessoryWithMute) accessory)::unsubscribeMuteState)); + } } public void addOptionalCharacteristic(NameCharacteristic name) { @@ -55,4 +65,8 @@ public void addOptionalCharacteristic(BrightnessCharacteristic brightness) { public void addOptionalCharacteristic(VolumeCharacteristic volume) { addCharacteristic(volume); } + + public void addOptionalCharacteristic(MuteCharacteristic mute) { + addCharacteristic(mute); + } } diff --git a/src/test/java/io/github/hapjava/services/impl/DoorbellServiceTest.java b/src/test/java/io/github/hapjava/services/impl/DoorbellServiceTest.java new file mode 100644 index 000000000..feb67b94c --- /dev/null +++ b/src/test/java/io/github/hapjava/services/impl/DoorbellServiceTest.java @@ -0,0 +1,51 @@ +package io.github.hapjava.services.impl; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; + +import io.github.hapjava.accessories.DoorbellAccessory; +import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithMute; +import io.github.hapjava.characteristics.Characteristic; +import io.github.hapjava.characteristics.impl.audio.MuteCharacteristic; +import io.github.hapjava.characteristics.impl.common.ProgrammableSwitchEventCharacteristic; +import java.util.concurrent.CompletableFuture; +import javax.json.JsonObject; +import javax.json.JsonValue; +import org.junit.Test; + +public class DoorbellServiceTest { + + @Test + public void doorbellWithoutOptionalsOnlyHasSwitchEvent() { + DoorbellService service = new DoorbellService(mock(DoorbellAccessory.class)); + + assertThat(service.getType()).isEqualTo("00000121-0000-1000-8000-0026BB765291"); + assertThat(service.getCharacteristics()) + .hasExactlyElementsOfTypes(ProgrammableSwitchEventCharacteristic.class); + } + + @Test + public void doorbellWithMuteHasMuteCharacteristic() throws Exception { + DoorbellAccessory accessory = + mock(DoorbellAccessory.class, withSettings().extraInterfaces(AccessoryWithMute.class)); + when(((AccessoryWithMute) accessory).isMuted()) + .thenReturn(CompletableFuture.completedFuture(true)); + + DoorbellService service = new DoorbellService(accessory); + + assertThat(service.getCharacteristics()) + .hasExactlyElementsOfTypes( + ProgrammableSwitchEventCharacteristic.class, MuteCharacteristic.class); + + Characteristic mute = service.getCharacteristics().get(1); + JsonObject json = mute.toJson(2).join(); + assertThat(json.getString("type")).isEqualTo("11A"); + assertThat(json.getBoolean("value")).isTrue(); + + mute.setValue(JsonValue.FALSE); + verify((AccessoryWithMute) accessory).setMute(false); + } +} From a2a6bffa2b9a636ddb7c0164d8801208a83410a1 Mon Sep 17 00:00:00 2001 From: udaw Date: Mon, 19 Oct 2026 17:03:53 +0200 Subject: [PATCH 135/138] Limit custom min/max to tilt angle characteristics Percentage characteristics and hue have fixed ranges by definition, so only the tilt angles keep the min/max constructors. --- CHANGES.md | 2 +- .../impl/audio/VolumeCharacteristic.java | 16 +- .../battery/BatteryLevelCharacteristic.java | 15 +- .../impl/common/WaterLavelCharacteristic.java | 19 +- .../FilterLifeLevelCharacteristic.java | 19 +- ...tyDehumidifierThresholdCharacteristic.java | 27 +- ...dityHumidifierThresholdCharacteristic.java | 27 +- ...CurrentRelativeHumidityCharacteristic.java | 19 +- .../TargetRelativeHumidityCharacteristic.java | 27 +- .../lightbulb/BrightnessCharacteristic.java | 16 +- .../impl/lightbulb/HueCharacteristic.java | 28 +- .../lightbulb/SaturationCharacteristic.java | 27 +- .../CurrentPositionCharacteristic.java | 15 +- .../TargetPositionCharacteristic.java | 16 +- .../impl/CustomRangeCharacteristicsTest.java | 253 ------------------ .../impl/CustomTiltAngleRangeTest.java | 92 +++++++ 16 files changed, 127 insertions(+), 491 deletions(-) delete mode 100644 src/test/java/io/github/hapjava/characteristics/impl/CustomRangeCharacteristicsTest.java create mode 100644 src/test/java/io/github/hapjava/characteristics/impl/CustomTiltAngleRangeTest.java diff --git a/CHANGES.md b/CHANGES.md index 6dfc34b6d..f6be4cdb6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,5 @@ # HAP-Java 2.0.10 -* Add constructors with custom min/max (and step for float values) to the remaining range characteristics [#162](https://github.com/hap-java/HAP-Java/issues/162) +* Add constructors with custom min/max to the tilt angle characteristics [#162](https://github.com/hap-java/HAP-Java/issues/162) # HAP-Java 2.0.9 * Fix Set Duration and Remaining Duration characteristics to advertise format `uint32` instead of `int` [#202](https://github.com/hap-java/HAP-Java/issues/202) diff --git a/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java index 2a4223d5c..2d1caedd7 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/audio/VolumeCharacteristic.java @@ -11,12 +11,8 @@ /** Volume characteristic to control audio volume. */ public class VolumeCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { - public static final int DEFAULT_MIN_VALUE = 0; - public static final int DEFAULT_MAX_VALUE = 100; public VolumeCharacteristic( - int minValue, - int maxValue, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -24,20 +20,12 @@ public VolumeCharacteristic( super( "00000119-0000-1000-8000-0026BB765291", "volume", - minValue, - maxValue, + 0, + 100, "percentage", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - public VolumeCharacteristic( - Supplier> getter, - ExceptionalConsumer setter, - Consumer subscriber, - Runnable unsubscriber) { - this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java index ae7097ccd..0fa56ff40 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/battery/BatteryLevelCharacteristic.java @@ -11,31 +11,20 @@ /** This characteristic describes the current level of the battery. */ public class BatteryLevelCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { - public static final int DEFAULT_MIN_VALUE = 0; - public static final int DEFAULT_MAX_VALUE = 100; public BatteryLevelCharacteristic( - int minValue, - int maxValue, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "00000068-0000-1000-8000-0026BB765291", "battery level", - minValue, - maxValue, + 0, + 100, "%", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } - - public BatteryLevelCharacteristic( - Supplier> getter, - Consumer subscriber, - Runnable unsubscriber) { - this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, subscriber, unsubscriber); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java index d9d29bac7..dda11b087 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/common/WaterLavelCharacteristic.java @@ -11,34 +11,21 @@ /** This characteristic describes the water level. */ public class WaterLavelCharacteristic extends FloatCharacteristic implements EventableCharacteristic { - public static final double DEFAULT_MIN_VALUE = 0; - public static final double DEFAULT_MAX_VALUE = 100; - public static final double DEFAULT_STEP = 1; public WaterLavelCharacteristic( - double minValue, - double maxValue, - double minStep, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "000000B5-0000-1000-8000-0026BB765291", "water level", - minValue, - maxValue, - minStep, + 0, + 100, + 1, "%", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } - - public WaterLavelCharacteristic( - Supplier> getter, - Consumer subscriber, - Runnable unsubscriber) { - this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java index 6213769c9..29cdf8d6e 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/filtermaintenance/FilterLifeLevelCharacteristic.java @@ -10,34 +10,21 @@ public class FilterLifeLevelCharacteristic extends FloatCharacteristic implements EventableCharacteristic { - public static final double DEFAULT_MIN_VALUE = 0; - public static final double DEFAULT_MAX_VALUE = 100; - public static final double DEFAULT_STEP = 1; public FilterLifeLevelCharacteristic( - double minValue, - double maxValue, - double minStep, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "000000AB-0000-1000-8000-0026BB765291", "Filter Life Level", - minValue, - maxValue, - minStep, + 0, + 100, + 1, "%", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } - - public FilterLifeLevelCharacteristic( - Supplier> getter, - Consumer subscriber, - Runnable unsubscriber) { - this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java index 84a31fe24..b8a4ca569 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityDehumidifierThresholdCharacteristic.java @@ -13,14 +13,8 @@ * turns on. */ public class HumidityDehumidifierThresholdCharacteristic extends FloatCharacteristic { - public static final double DEFAULT_MIN_VALUE = 0; - public static final double DEFAULT_MAX_VALUE = 100; - public static final double DEFAULT_STEP = 1; public HumidityDehumidifierThresholdCharacteristic( - double minValue, - double maxValue, - double minStep, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -28,28 +22,13 @@ public HumidityDehumidifierThresholdCharacteristic( super( "000000C9-0000-1000-8000-0026BB765291", "humidity threshold dehumidifier", - minValue, - maxValue, - minStep, + 0, + 100, + 1, "%", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - public HumidityDehumidifierThresholdCharacteristic( - Supplier> getter, - ExceptionalConsumer setter, - Consumer subscriber, - Runnable unsubscriber) { - this( - DEFAULT_MIN_VALUE, - DEFAULT_MAX_VALUE, - DEFAULT_STEP, - getter, - setter, - subscriber, - unsubscriber); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java index 14b78bde1..2ee8adbf2 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/humidifier/HumidityHumidifierThresholdCharacteristic.java @@ -13,14 +13,8 @@ * turns on. */ public class HumidityHumidifierThresholdCharacteristic extends FloatCharacteristic { - public static final double DEFAULT_MIN_VALUE = 0; - public static final double DEFAULT_MAX_VALUE = 100; - public static final double DEFAULT_STEP = 1; public HumidityHumidifierThresholdCharacteristic( - double minValue, - double maxValue, - double minStep, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -28,28 +22,13 @@ public HumidityHumidifierThresholdCharacteristic( super( "000000CA-0000-1000-8000-0026BB765291", "humidity threshold humidifier", - minValue, - maxValue, - minStep, + 0, + 100, + 1, "%", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - public HumidityHumidifierThresholdCharacteristic( - Supplier> getter, - ExceptionalConsumer setter, - Consumer subscriber, - Runnable unsubscriber) { - this( - DEFAULT_MIN_VALUE, - DEFAULT_MAX_VALUE, - DEFAULT_STEP, - getter, - setter, - subscriber, - unsubscriber); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java index 7ce18fb46..be77125b6 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/CurrentRelativeHumidityCharacteristic.java @@ -9,34 +9,21 @@ /** This characteristic describes the current relative humidity. */ public class CurrentRelativeHumidityCharacteristic extends FloatCharacteristic { - public static final double DEFAULT_MIN_VALUE = 0; - public static final double DEFAULT_MAX_VALUE = 100; - public static final double DEFAULT_STEP = 1; public CurrentRelativeHumidityCharacteristic( - double minValue, - double maxValue, - double minStep, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "00000010-0000-1000-8000-0026BB765291", "Current Relative Humidity", - minValue, - maxValue, - minStep, + 0, + 100, + 1, "%", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } - - public CurrentRelativeHumidityCharacteristic( - Supplier> getter, - Consumer subscriber, - Runnable unsubscriber) { - this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, DEFAULT_STEP, getter, subscriber, unsubscriber); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java index 600ca0f7c..ab8ad7e29 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/humiditysensor/TargetRelativeHumidityCharacteristic.java @@ -10,14 +10,8 @@ /** This characteristic describes the target relative humidity. */ public class TargetRelativeHumidityCharacteristic extends FloatCharacteristic { - public static final double DEFAULT_MIN_VALUE = 0; - public static final double DEFAULT_MAX_VALUE = 100; - public static final double DEFAULT_STEP = 1; public TargetRelativeHumidityCharacteristic( - double minValue, - double maxValue, - double minStep, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -25,28 +19,13 @@ public TargetRelativeHumidityCharacteristic( super( "00000034-0000-1000-8000-0026BB765291", "Target Relative Humidity", - minValue, - maxValue, - minStep, + 0, + 100, + 1, "%", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - public TargetRelativeHumidityCharacteristic( - Supplier> getter, - ExceptionalConsumer setter, - Consumer subscriber, - Runnable unsubscriber) { - this( - DEFAULT_MIN_VALUE, - DEFAULT_MAX_VALUE, - DEFAULT_STEP, - getter, - setter, - subscriber, - unsubscriber); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java index b68cb914b..693e42efc 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/BrightnessCharacteristic.java @@ -15,12 +15,8 @@ */ public class BrightnessCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { - public static final int DEFAULT_MIN_VALUE = 0; - public static final int DEFAULT_MAX_VALUE = 100; public BrightnessCharacteristic( - int minValue, - int maxValue, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -28,20 +24,12 @@ public BrightnessCharacteristic( super( "00000008-0000-1000-8000-0026BB765291", "level of brightness", - minValue, - maxValue, + 0, + 100, "%", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - public BrightnessCharacteristic( - Supplier> getter, - ExceptionalConsumer setter, - Consumer subscriber, - Runnable unsubscriber) { - this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java index f41a92319..7a00816bc 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/HueCharacteristic.java @@ -10,14 +10,7 @@ /** This characteristic describes hue or color. */ public class HueCharacteristic extends FloatCharacteristic { - public static final double DEFAULT_MIN_VALUE = 0; - public static final double DEFAULT_MAX_VALUE = 360; - public static final double DEFAULT_STEP = 1; - public HueCharacteristic( - double minValue, - double maxValue, - double minStep, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -25,28 +18,13 @@ public HueCharacteristic( super( "00000013-0000-1000-8000-0026BB765291", "hue or color", - minValue, - maxValue, - minStep, + 0, + 360, + 1, "arcdegrees", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - public HueCharacteristic( - Supplier> getter, - ExceptionalConsumer setter, - Consumer subscriber, - Runnable unsubscriber) { - this( - DEFAULT_MIN_VALUE, - DEFAULT_MAX_VALUE, - DEFAULT_STEP, - getter, - setter, - subscriber, - unsubscriber); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java index 5d2f7033b..90694ef98 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/lightbulb/SaturationCharacteristic.java @@ -12,14 +12,8 @@ /** This characteristic describes color saturation. */ public class SaturationCharacteristic extends FloatCharacteristic implements EventableCharacteristic { - public static final double DEFAULT_MIN_VALUE = 0; - public static final double DEFAULT_MAX_VALUE = 100; - public static final double DEFAULT_STEP = 1; public SaturationCharacteristic( - double minValue, - double maxValue, - double minStep, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -27,28 +21,13 @@ public SaturationCharacteristic( super( "0000002F-0000-1000-8000-0026BB765291", "color saturation", - minValue, - maxValue, - minStep, + 0, + 100, + 1, "%", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - public SaturationCharacteristic( - Supplier> getter, - ExceptionalConsumer setter, - Consumer subscriber, - Runnable unsubscriber) { - this( - DEFAULT_MIN_VALUE, - DEFAULT_MAX_VALUE, - DEFAULT_STEP, - getter, - setter, - subscriber, - unsubscriber); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java index e9ed063c8..d51614615 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/CurrentPositionCharacteristic.java @@ -11,31 +11,20 @@ /** This characteristic describes the current position of accessories. */ public class CurrentPositionCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { - public static final int DEFAULT_MIN_VALUE = 0; - public static final int DEFAULT_MAX_VALUE = 100; public CurrentPositionCharacteristic( - int minValue, - int maxValue, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "0000006D-0000-1000-8000-0026BB765291", "current position", - minValue, - maxValue, + 0, + 100, "%", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } - - public CurrentPositionCharacteristic( - Supplier> getter, - Consumer subscriber, - Runnable unsubscriber) { - this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, subscriber, unsubscriber); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java index 82d06c4b2..6398a0b37 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/windowcovering/TargetPositionCharacteristic.java @@ -12,12 +12,8 @@ /** This characteristic describes the target position of accessories like door, window, blinds. */ public class TargetPositionCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { - public static final int DEFAULT_MIN_VALUE = 0; - public static final int DEFAULT_MAX_VALUE = 100; public TargetPositionCharacteristic( - int minValue, - int maxValue, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -25,20 +21,12 @@ public TargetPositionCharacteristic( super( "0000007C-0000-1000-8000-0026BB765291", "target position", - minValue, - maxValue, + 0, + 100, "%", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - public TargetPositionCharacteristic( - Supplier> getter, - ExceptionalConsumer setter, - Consumer subscriber, - Runnable unsubscriber) { - this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber); - } } diff --git a/src/test/java/io/github/hapjava/characteristics/impl/CustomRangeCharacteristicsTest.java b/src/test/java/io/github/hapjava/characteristics/impl/CustomRangeCharacteristicsTest.java deleted file mode 100644 index 800719d6d..000000000 --- a/src/test/java/io/github/hapjava/characteristics/impl/CustomRangeCharacteristicsTest.java +++ /dev/null @@ -1,253 +0,0 @@ -package io.github.hapjava.characteristics.impl; - -import static org.assertj.core.api.Assertions.assertThat; - -import io.github.hapjava.characteristics.Characteristic; -import io.github.hapjava.characteristics.impl.audio.VolumeCharacteristic; -import io.github.hapjava.characteristics.impl.battery.BatteryLevelCharacteristic; -import io.github.hapjava.characteristics.impl.common.WaterLavelCharacteristic; -import io.github.hapjava.characteristics.impl.filtermaintenance.FilterLifeLevelCharacteristic; -import io.github.hapjava.characteristics.impl.humidifier.HumidityDehumidifierThresholdCharacteristic; -import io.github.hapjava.characteristics.impl.humidifier.HumidityHumidifierThresholdCharacteristic; -import io.github.hapjava.characteristics.impl.humiditysensor.CurrentRelativeHumidityCharacteristic; -import io.github.hapjava.characteristics.impl.humiditysensor.TargetRelativeHumidityCharacteristic; -import io.github.hapjava.characteristics.impl.lightbulb.BrightnessCharacteristic; -import io.github.hapjava.characteristics.impl.lightbulb.HueCharacteristic; -import io.github.hapjava.characteristics.impl.lightbulb.SaturationCharacteristic; -import io.github.hapjava.characteristics.impl.slat.CurrentTiltAngleCharacteristic; -import io.github.hapjava.characteristics.impl.slat.TargetTiltAngleCharacteristic; -import io.github.hapjava.characteristics.impl.windowcovering.CurrentHorizontalTiltAngleCharacteristic; -import io.github.hapjava.characteristics.impl.windowcovering.CurrentPositionCharacteristic; -import io.github.hapjava.characteristics.impl.windowcovering.CurrentVerticalTiltAngleCharacteristic; -import io.github.hapjava.characteristics.impl.windowcovering.TargetHorizontalTiltAngleCharacteristic; -import io.github.hapjava.characteristics.impl.windowcovering.TargetPositionCharacteristic; -import io.github.hapjava.characteristics.impl.windowcovering.TargetVerticalTiltAngleCharacteristic; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import javax.json.JsonObject; -import org.junit.Test; - -/** Custom min/max support, see https://github.com/hap-java/HAP-Java/issues/162 */ -public class CustomRangeCharacteristicsTest { - - /** Serialized output of the default constructors, captured before custom ranges were added. */ - private static final List DEFAULT_CONSTRUCTORS = - Arrays.asList( - Case.of( - "BatteryLevelCharacteristic", - new BatteryLevelCharacteristic( - () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"68\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"%\"}"), - Case.of( - "BrightnessCharacteristic", - new BrightnessCharacteristic( - () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"8\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"%\"}"), - Case.of( - "CurrentPositionCharacteristic", - new CurrentPositionCharacteristic( - () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"6D\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"%\"}"), - Case.of( - "TargetPositionCharacteristic", - new TargetPositionCharacteristic( - () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"7C\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"%\"}"), - Case.of( - "VolumeCharacteristic", - new VolumeCharacteristic( - () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"119\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":0,\"maxValue\":100,\"minStep\":1,\"unit\":\"percentage\"}"), - Case.of( - "CurrentTiltAngleCharacteristic", - new CurrentTiltAngleCharacteristic( - () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"C1\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), - Case.of( - "TargetTiltAngleCharacteristic", - new TargetTiltAngleCharacteristic( - () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"C2\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), - Case.of( - "CurrentHorizontalTiltAngleCharacteristic", - new CurrentHorizontalTiltAngleCharacteristic( - () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"6C\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), - Case.of( - "TargetHorizontalTiltAngleCharacteristic", - new TargetHorizontalTiltAngleCharacteristic( - () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"7B\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), - Case.of( - "CurrentVerticalTiltAngleCharacteristic", - new CurrentVerticalTiltAngleCharacteristic( - () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"6E\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), - Case.of( - "TargetVerticalTiltAngleCharacteristic", - new TargetVerticalTiltAngleCharacteristic( - () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"7D\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), - Case.of( - "CurrentRelativeHumidityCharacteristic", - new CurrentRelativeHumidityCharacteristic( - () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"10\",\"perms\":[\"pr\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"), - Case.of( - "TargetRelativeHumidityCharacteristic", - new TargetRelativeHumidityCharacteristic( - () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"34\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"), - Case.of( - "FilterLifeLevelCharacteristic", - new FilterLifeLevelCharacteristic( - () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"AB\",\"perms\":[\"pr\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"), - Case.of( - "HueCharacteristic", - new HueCharacteristic( - () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"13\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":360.0,\"minStep\":1.0,\"unit\":\"arcdegrees\"}"), - Case.of( - "SaturationCharacteristic", - new SaturationCharacteristic( - () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"2F\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"), - Case.of( - "HumidityDehumidifierThresholdCharacteristic", - new HumidityDehumidifierThresholdCharacteristic( - () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"C9\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"), - Case.of( - "HumidityHumidifierThresholdCharacteristic", - new HumidityHumidifierThresholdCharacteristic( - () -> CompletableFuture.completedFuture(50.0), v -> {}, cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"CA\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}"), - Case.of( - "WaterLavelCharacteristic", - new WaterLavelCharacteristic( - () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"B5\",\"perms\":[\"pr\",\"ev\"],\"format\":\"float\",\"value\":50.0,\"minValue\":0.0,\"maxValue\":100.0,\"minStep\":1.0,\"unit\":\"%\"}")); - - @Test - public void defaultConstructorsKeepTheirSerializedOutput() { - for (Case c : DEFAULT_CONSTRUCTORS) { - assertThat(c.characteristic.toJson(7).join().toString()).as(c.name).isEqualTo(c.expectedJson); - } - } - - @Test - public void integerCharacteristicsAdvertiseCustomRange() { - for (Characteristic c : - Arrays.asList( - new BatteryLevelCharacteristic( - 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), - new BrightnessCharacteristic( - 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), - new CurrentPositionCharacteristic( - 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), - new TargetPositionCharacteristic( - 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), - new VolumeCharacteristic( - 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), - new CurrentTiltAngleCharacteristic( - 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), - new TargetTiltAngleCharacteristic( - 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), - new CurrentHorizontalTiltAngleCharacteristic( - 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), - new TargetHorizontalTiltAngleCharacteristic( - 10, 20, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), - new CurrentVerticalTiltAngleCharacteristic( - 10, 20, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), - new TargetVerticalTiltAngleCharacteristic( - 10, - 20, - () -> CompletableFuture.completedFuture(50), - v -> {}, - cb -> {}, - () -> {}))) { - JsonObject json = c.toJson(7).join(); - String name = c.getClass().getSimpleName(); - assertThat(json.getInt("minValue")).as(name).isEqualTo(10); - assertThat(json.getInt("maxValue")).as(name).isEqualTo(20); - assertThat(json.getInt("minStep")).as(name).isEqualTo(1); - } - } - - @Test - public void floatCharacteristicsAdvertiseCustomRangeAndClampToIt() { - for (Characteristic c : - Arrays.asList( - new CurrentRelativeHumidityCharacteristic( - 10, 20, 0.5, () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}), - new TargetRelativeHumidityCharacteristic( - 10, - 20, - 0.5, - () -> CompletableFuture.completedFuture(50.0), - v -> {}, - cb -> {}, - () -> {}), - new FilterLifeLevelCharacteristic( - 10, 20, 0.5, () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}), - new HueCharacteristic( - 10, - 20, - 0.5, - () -> CompletableFuture.completedFuture(50.0), - v -> {}, - cb -> {}, - () -> {}), - new SaturationCharacteristic( - 10, - 20, - 0.5, - () -> CompletableFuture.completedFuture(50.0), - v -> {}, - cb -> {}, - () -> {}), - new HumidityDehumidifierThresholdCharacteristic( - 10, - 20, - 0.5, - () -> CompletableFuture.completedFuture(50.0), - v -> {}, - cb -> {}, - () -> {}), - new HumidityHumidifierThresholdCharacteristic( - 10, - 20, - 0.5, - () -> CompletableFuture.completedFuture(50.0), - v -> {}, - cb -> {}, - () -> {}), - new WaterLavelCharacteristic( - 10, 20, 0.5, () -> CompletableFuture.completedFuture(50.0), cb -> {}, () -> {}))) { - JsonObject json = c.toJson(7).join(); - String name = c.getClass().getSimpleName(); - assertThat(json.getJsonNumber("minValue").doubleValue()).as(name).isEqualTo(10.0); - assertThat(json.getJsonNumber("maxValue").doubleValue()).as(name).isEqualTo(20.0); - assertThat(json.getJsonNumber("minStep").doubleValue()).as(name).isEqualTo(0.5); - assertThat(json.getJsonNumber("value").doubleValue()).as(name).isEqualTo(20.0); - } - } - - private static class Case { - final String name; - final Characteristic characteristic; - final String expectedJson; - - private Case(String name, Characteristic characteristic, String expectedJson) { - this.name = name; - this.characteristic = characteristic; - this.expectedJson = expectedJson; - } - - static Case of(String name, Characteristic characteristic, String expectedJson) { - return new Case(name, characteristic, expectedJson); - } - } -} diff --git a/src/test/java/io/github/hapjava/characteristics/impl/CustomTiltAngleRangeTest.java b/src/test/java/io/github/hapjava/characteristics/impl/CustomTiltAngleRangeTest.java new file mode 100644 index 000000000..dcff74fc3 --- /dev/null +++ b/src/test/java/io/github/hapjava/characteristics/impl/CustomTiltAngleRangeTest.java @@ -0,0 +1,92 @@ +package io.github.hapjava.characteristics.impl; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.hapjava.characteristics.Characteristic; +import io.github.hapjava.characteristics.impl.slat.CurrentTiltAngleCharacteristic; +import io.github.hapjava.characteristics.impl.slat.TargetTiltAngleCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.CurrentHorizontalTiltAngleCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.CurrentVerticalTiltAngleCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.TargetHorizontalTiltAngleCharacteristic; +import io.github.hapjava.characteristics.impl.windowcovering.TargetVerticalTiltAngleCharacteristic; +import java.util.Arrays; +import java.util.concurrent.CompletableFuture; +import javax.json.JsonObject; +import org.junit.Test; + +/** Custom tilt angle ranges, see https://github.com/hap-java/HAP-Java/issues/162 */ +public class CustomTiltAngleRangeTest { + + @Test + public void defaultConstructorsKeepTheirSerializedOutput() { + // Serialized output captured before custom ranges were added. + for (Case c : + Arrays.asList( + Case.of( + new CurrentTiltAngleCharacteristic( + () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"C1\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), + Case.of( + new TargetTiltAngleCharacteristic( + () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"C2\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), + Case.of( + new CurrentHorizontalTiltAngleCharacteristic( + () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"6C\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), + Case.of( + new TargetHorizontalTiltAngleCharacteristic( + () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"7B\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), + Case.of( + new CurrentVerticalTiltAngleCharacteristic( + () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"6E\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), + Case.of( + new TargetVerticalTiltAngleCharacteristic( + () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), + "{\"iid\":7,\"type\":\"7D\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"))) { + assertThat(c.characteristic.toJson(7).join().toString()) + .as(c.characteristic.getClass().getSimpleName()) + .isEqualTo(c.expected); + } + } + + @Test + public void customRangeIsAdvertised() { + for (Characteristic c : + Arrays.asList( + new CurrentTiltAngleCharacteristic( + 0, 90, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), + new TargetTiltAngleCharacteristic( + 0, 90, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), + new CurrentHorizontalTiltAngleCharacteristic( + 0, 90, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), + new TargetHorizontalTiltAngleCharacteristic( + 0, 90, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), + new CurrentVerticalTiltAngleCharacteristic( + 0, 90, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), + new TargetVerticalTiltAngleCharacteristic( + 0, 90, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}))) { + JsonObject json = c.toJson(7).join(); + String name = c.getClass().getSimpleName(); + assertThat(json.getInt("minValue")).as(name).isEqualTo(0); + assertThat(json.getInt("maxValue")).as(name).isEqualTo(90); + assertThat(json.getInt("minStep")).as(name).isEqualTo(1); + } + } + + private static class Case { + final Characteristic characteristic; + final String expected; + + private Case(Characteristic characteristic, String expected) { + this.characteristic = characteristic; + this.expected = expected; + } + + static Case of(Characteristic characteristic, String expected) { + return new Case(characteristic, expected); + } + } +} From 3f844c2d3f81136507674a55166aa49a78e1e3ed Mon Sep 17 00:00:00 2001 From: Eugen Date: Tue, 3 Nov 2026 10:11:14 +0100 Subject: [PATCH 136/138] Drop slat tilt angle characteristics from custom min/max The Home app shows no control for the Slat service, so a custom range cannot be verified there. --- CHANGES.md | 2 +- .../slat/CurrentTiltAngleCharacteristic.java | 15 ++------------- .../impl/slat/TargetTiltAngleCharacteristic.java | 16 ++-------------- .../impl/CustomTiltAngleRangeTest.java | 14 -------------- 4 files changed, 5 insertions(+), 42 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f6be4cdb6..5bc1183a7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,5 @@ # HAP-Java 2.0.10 -* Add constructors with custom min/max to the tilt angle characteristics [#162](https://github.com/hap-java/HAP-Java/issues/162) +* Add constructors with custom min/max to the window covering tilt angle characteristics [#162](https://github.com/hap-java/HAP-Java/issues/162) # HAP-Java 2.0.9 * Fix Set Duration and Remaining Duration characteristics to advertise format `uint32` instead of `int` [#202](https://github.com/hap-java/HAP-Java/issues/202) diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java index 0f186281a..3a4868b2c 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/CurrentTiltAngleCharacteristic.java @@ -11,31 +11,20 @@ /** This characteristic describe current tilt angle (between -90 and 90) */ public class CurrentTiltAngleCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { - public static final int DEFAULT_MIN_VALUE = -90; - public static final int DEFAULT_MAX_VALUE = 90; public CurrentTiltAngleCharacteristic( - int minValue, - int maxValue, Supplier> getter, Consumer subscriber, Runnable unsubscriber) { super( "000000C1-0000-1000-8000-0026BB765291", "current tilt angle", - minValue, - maxValue, + -90, + 90, "arcdegrees", Optional.of(getter), Optional.empty(), Optional.of(subscriber), Optional.of(unsubscriber)); } - - public CurrentTiltAngleCharacteristic( - Supplier> getter, - Consumer subscriber, - Runnable unsubscriber) { - this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, subscriber, unsubscriber); - } } diff --git a/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java b/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java index 44824f350..2a327522d 100644 --- a/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java +++ b/src/main/java/io/github/hapjava/characteristics/impl/slat/TargetTiltAngleCharacteristic.java @@ -12,12 +12,8 @@ /** This characteristic describes the target angle of slats. */ public class TargetTiltAngleCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { - public static final int DEFAULT_MIN_VALUE = -90; - public static final int DEFAULT_MAX_VALUE = 90; public TargetTiltAngleCharacteristic( - int minValue, - int maxValue, Supplier> getter, ExceptionalConsumer setter, Consumer subscriber, @@ -25,20 +21,12 @@ public TargetTiltAngleCharacteristic( super( "000000C2-0000-1000-8000-0026BB765291", "target tilt angle", - minValue, - maxValue, + -90, + 90, "arcdegrees", Optional.of(getter), Optional.of(setter), Optional.of(subscriber), Optional.of(unsubscriber)); } - - public TargetTiltAngleCharacteristic( - Supplier> getter, - ExceptionalConsumer setter, - Consumer subscriber, - Runnable unsubscriber) { - this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, getter, setter, subscriber, unsubscriber); - } } diff --git a/src/test/java/io/github/hapjava/characteristics/impl/CustomTiltAngleRangeTest.java b/src/test/java/io/github/hapjava/characteristics/impl/CustomTiltAngleRangeTest.java index dcff74fc3..d3cd51756 100644 --- a/src/test/java/io/github/hapjava/characteristics/impl/CustomTiltAngleRangeTest.java +++ b/src/test/java/io/github/hapjava/characteristics/impl/CustomTiltAngleRangeTest.java @@ -3,8 +3,6 @@ import static org.assertj.core.api.Assertions.assertThat; import io.github.hapjava.characteristics.Characteristic; -import io.github.hapjava.characteristics.impl.slat.CurrentTiltAngleCharacteristic; -import io.github.hapjava.characteristics.impl.slat.TargetTiltAngleCharacteristic; import io.github.hapjava.characteristics.impl.windowcovering.CurrentHorizontalTiltAngleCharacteristic; import io.github.hapjava.characteristics.impl.windowcovering.CurrentVerticalTiltAngleCharacteristic; import io.github.hapjava.characteristics.impl.windowcovering.TargetHorizontalTiltAngleCharacteristic; @@ -22,14 +20,6 @@ public void defaultConstructorsKeepTheirSerializedOutput() { // Serialized output captured before custom ranges were added. for (Case c : Arrays.asList( - Case.of( - new CurrentTiltAngleCharacteristic( - () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"C1\",\"perms\":[\"pr\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), - Case.of( - new TargetTiltAngleCharacteristic( - () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), - "{\"iid\":7,\"type\":\"C2\",\"perms\":[\"pr\",\"pw\",\"ev\"],\"format\":\"int\",\"value\":50,\"minValue\":-90,\"maxValue\":90,\"minStep\":1,\"unit\":\"arcdegrees\"}"), Case.of( new CurrentHorizontalTiltAngleCharacteristic( () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), @@ -56,10 +46,6 @@ public void defaultConstructorsKeepTheirSerializedOutput() { public void customRangeIsAdvertised() { for (Characteristic c : Arrays.asList( - new CurrentTiltAngleCharacteristic( - 0, 90, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), - new TargetTiltAngleCharacteristic( - 0, 90, () -> CompletableFuture.completedFuture(50), v -> {}, cb -> {}, () -> {}), new CurrentHorizontalTiltAngleCharacteristic( 0, 90, () -> CompletableFuture.completedFuture(50), cb -> {}, () -> {}), new TargetHorizontalTiltAngleCharacteristic( From 11b8813cc3042a655827169a00803fe663ba5b3b Mon Sep 17 00:00:00 2001 From: Eugen Date: Sat, 7 Nov 2026 13:33:56 +0100 Subject: [PATCH 137/138] Close owned mDNS instance and remove shutdown hook on stop JmdnsHomekitAdvertiser created its own JmDNS when given an InetAddress but never closed it, and registered a shutdown hook on every advertise() that was never removed. Each stopped HomekitRoot therefore left a running JmDNS instance behind, kept reachable by its shutdown hook. stop() now removes the shutdown hook, closes the JmDNS only if the advertiser created it, and resets the advertising state so later changes to discoverability, MAC or configuration index do not re-register the service. --- CHANGES.md | 1 + .../impl/jmdns/JmdnsHomekitAdvertiser.java | 40 ++++++++++++++----- .../jmdns/JmdnsHomekitAdvertiserTest.java | 25 ++++++++++++ 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 2f8e24550..5fc06bed9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,6 @@ # HAP-Java 2.0.10 * Add optional Mute characteristic to Doorbell service +* Fix leak of mDNS instances and shutdown hooks when stopping `HomekitRoot` # HAP-Java 2.0.9 * Fix Set Duration and Remaining Duration characteristics to advertise format `uint32` instead of `int` [#202](https://github.com/hap-java/HAP-Java/issues/202) diff --git a/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java index 9086acb3e..1073f9b86 100644 --- a/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java +++ b/src/main/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiser.java @@ -17,6 +17,8 @@ public class JmdnsHomekitAdvertiser { private static final String SERVICE_TYPE = "_hap._tcp.local."; private final JmDNS jmdns; + private final boolean ownsJmdns; + private Thread shutdownHook; private boolean discoverable = true; private static final Logger logger = LoggerFactory.getLogger(JmdnsHomekitAdvertiser.class); private boolean isAdvertising = false; @@ -30,11 +32,16 @@ public class JmdnsHomekitAdvertiser { private int category; public JmdnsHomekitAdvertiser(JmDNS jmdns) { - this.jmdns = jmdns; + this(jmdns, false); } public JmdnsHomekitAdvertiser(InetAddress localAddress) throws UnknownHostException, IOException { - jmdns = JmDNS.create(localAddress); + this(JmDNS.create(localAddress), true); + } + + JmdnsHomekitAdvertiser(JmDNS jmdns, boolean ownsJmdns) { + this.jmdns = jmdns; + this.ownsJmdns = ownsJmdns; } public synchronized void advertise( @@ -54,18 +61,33 @@ public synchronized void advertise( registerService(); - Runtime.getRuntime() - .addShutdownHook( - new Thread( - () -> { - logger.trace("Stopping advertising in response to shutdown."); - jmdns.unregisterAllServices(); - })); + shutdownHook = + new Thread( + () -> { + logger.trace("Stopping advertising in response to shutdown."); + jmdns.unregisterAllServices(); + }); + Runtime.getRuntime().addShutdownHook(shutdownHook); isAdvertising = true; } public synchronized void stop() { unregisterService(); + isAdvertising = false; + if (shutdownHook != null) { + try { + Runtime.getRuntime().removeShutdownHook(shutdownHook); + } catch (IllegalStateException e) { + // already shutting down + } + } + if (ownsJmdns) { + try { + jmdns.close(); + } catch (IOException e) { + logger.warn("Failed to close mDNS", e); + } + } } public synchronized void setDiscoverable(boolean discoverable) throws IOException { diff --git a/src/test/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiserTest.java b/src/test/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiserTest.java index 870bd739b..731be498f 100644 --- a/src/test/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiserTest.java +++ b/src/test/java/io/github/hapjava/server/impl/jmdns/JmdnsHomekitAdvertiserTest.java @@ -2,7 +2,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import java.io.IOException; @@ -54,6 +56,29 @@ public void testSetConfigurationIndex() throws Exception { assertThat(getArgumentFromUnregister().getPropertyString("c#")).isEqualTo("1"); } + @Test + public void testStopClosesOwnedJmdns() throws Exception { + subject = new JmdnsHomekitAdvertiser(jmdns, true); + advertise(); + subject.stop(); + verify(jmdns).close(); + } + + @Test + public void testStopDoesNotCloseSuppliedJmdns() throws Exception { + advertise(); + subject.stop(); + verify(jmdns, never()).close(); + } + + @Test + public void testSetDiscoverableAfterStopDoesNotRegister() throws Exception { + advertise(); + subject.stop(); + subject.setDiscoverable(false); + verify(jmdns).registerService(any(ServiceInfo.class)); + } + private ServiceInfo getArgumentFromUnregister() { ArgumentCaptor serviceInfoCaptor = ArgumentCaptor.forClass(ServiceInfo.class); verify(jmdns).unregisterService(serviceInfoCaptor.capture()); From a162ea552137a9a7ca7b5caa7bc3f5ec78369a55 Mon Sep 17 00:00:00 2001 From: eugen Date: Sat, 14 Nov 2026 01:03:47 +0100 Subject: [PATCH 138/138] Update netty to 4.1.139.Final (#215) Co-authored-by: Eugen --- CHANGES.md | 1 + pom.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 1bbbbcf73..ab4bb1202 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ * Add constructors with custom min/max to the window covering tilt angle characteristics [#162](https://github.com/hap-java/HAP-Java/issues/162) * Add optional Mute characteristic to Doorbell service * Fix leak of mDNS instances and shutdown hooks when stopping `HomekitRoot` +* Updated netty to 4.1.139.Final # HAP-Java 2.0.9 * Fix Set Duration and Remaining Duration characteristics to advertise format `uint32` instead of `int` [#202](https://github.com/hap-java/HAP-Java/issues/202) diff --git a/pom.xml b/pom.xml index a1a952779..7bbbb9021 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ UTF-8 - 4.1.126.Final + 4.1.139.Final