2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.io.homekit.internal.accessories;
15 import static org.openhab.io.homekit.internal.HomekitCharacteristicType.*;
17 import java.math.BigDecimal;
18 import java.util.HashMap;
20 import java.util.Optional;
21 import java.util.concurrent.CompletableFuture;
22 import java.util.concurrent.ExecutionException;
23 import java.util.function.Function;
24 import java.util.function.Supplier;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.openhab.io.homekit.internal.HomekitCharacteristicType;
29 import io.github.hapjava.characteristics.Characteristic;
30 import io.github.hapjava.characteristics.impl.accessoryinformation.FirmwareRevisionCharacteristic;
31 import io.github.hapjava.characteristics.impl.accessoryinformation.HardwareRevisionCharacteristic;
32 import io.github.hapjava.characteristics.impl.accessoryinformation.ManufacturerCharacteristic;
33 import io.github.hapjava.characteristics.impl.accessoryinformation.ModelCharacteristic;
34 import io.github.hapjava.characteristics.impl.accessoryinformation.SerialNumberCharacteristic;
35 import io.github.hapjava.characteristics.impl.airquality.AirQualityCharacteristic;
36 import io.github.hapjava.characteristics.impl.airquality.AirQualityEnum;
37 import io.github.hapjava.characteristics.impl.common.ActiveCharacteristic;
38 import io.github.hapjava.characteristics.impl.common.ActiveEnum;
39 import io.github.hapjava.characteristics.impl.common.ActiveIdentifierCharacteristic;
40 import io.github.hapjava.characteristics.impl.common.ConfiguredNameCharacteristic;
41 import io.github.hapjava.characteristics.impl.common.IdentifierCharacteristic;
42 import io.github.hapjava.characteristics.impl.common.IsConfiguredCharacteristic;
43 import io.github.hapjava.characteristics.impl.common.IsConfiguredEnum;
44 import io.github.hapjava.characteristics.impl.common.NameCharacteristic;
45 import io.github.hapjava.characteristics.impl.common.ServiceLabelIndexCharacteristic;
46 import io.github.hapjava.characteristics.impl.heatercooler.CurrentHeaterCoolerStateCharacteristic;
47 import io.github.hapjava.characteristics.impl.heatercooler.CurrentHeaterCoolerStateEnum;
48 import io.github.hapjava.characteristics.impl.heatercooler.TargetHeaterCoolerStateCharacteristic;
49 import io.github.hapjava.characteristics.impl.heatercooler.TargetHeaterCoolerStateEnum;
50 import io.github.hapjava.characteristics.impl.inputsource.CurrentVisibilityStateCharacteristic;
51 import io.github.hapjava.characteristics.impl.inputsource.CurrentVisibilityStateEnum;
52 import io.github.hapjava.characteristics.impl.inputsource.InputDeviceTypeCharacteristic;
53 import io.github.hapjava.characteristics.impl.inputsource.InputDeviceTypeEnum;
54 import io.github.hapjava.characteristics.impl.inputsource.InputSourceTypeCharacteristic;
55 import io.github.hapjava.characteristics.impl.inputsource.InputSourceTypeEnum;
56 import io.github.hapjava.characteristics.impl.television.ClosedCaptionsCharacteristic;
57 import io.github.hapjava.characteristics.impl.television.ClosedCaptionsEnum;
58 import io.github.hapjava.characteristics.impl.television.PictureModeCharacteristic;
59 import io.github.hapjava.characteristics.impl.television.PictureModeEnum;
60 import io.github.hapjava.characteristics.impl.television.SleepDiscoveryModeCharacteristic;
61 import io.github.hapjava.characteristics.impl.television.SleepDiscoveryModeEnum;
62 import io.github.hapjava.characteristics.impl.televisionspeaker.VolumeControlTypeCharacteristic;
63 import io.github.hapjava.characteristics.impl.televisionspeaker.VolumeControlTypeEnum;
64 import io.github.hapjava.characteristics.impl.thermostat.CurrentHeatingCoolingStateCharacteristic;
65 import io.github.hapjava.characteristics.impl.thermostat.CurrentHeatingCoolingStateEnum;
66 import io.github.hapjava.characteristics.impl.thermostat.TargetHeatingCoolingStateCharacteristic;
67 import io.github.hapjava.characteristics.impl.thermostat.TargetHeatingCoolingStateEnum;
68 import io.github.hapjava.characteristics.impl.thermostat.TemperatureDisplayUnitCharacteristic;
69 import io.github.hapjava.characteristics.impl.thermostat.TemperatureDisplayUnitEnum;
72 * Creates an optional characteristics from metadata
74 * @author Cody Cutrer - Initial contribution
77 public class HomekitMetadataCharacteristicFactory {
79 // List of optional characteristics that can be set via metadata, and the corresponding method to create them.
80 private static final Map<HomekitCharacteristicType, Function<Object, Characteristic>> OPTIONAL = new HashMap<>() {
82 put(ACTIVE_IDENTIFIER, HomekitMetadataCharacteristicFactory::createActiveIdentifierCharacteristic);
83 put(ACTIVE_STATUS, HomekitMetadataCharacteristicFactory::createActiveStatusCharacteristic);
84 put(AIR_QUALITY, HomekitMetadataCharacteristicFactory::createAirQualityCharacteristic);
85 put(CLOSED_CAPTIONS, HomekitMetadataCharacteristicFactory::createClosedCaptionsCharacteristic);
86 put(CONFIGURED, HomekitMetadataCharacteristicFactory::createIsConfiguredCharacteristic);
87 put(CONFIGURED_NAME, HomekitMetadataCharacteristicFactory::createConfiguredNameCharacteristic);
88 put(CURRENT_HEATER_COOLER_STATE,
89 HomekitMetadataCharacteristicFactory::createCurrentHeaterCoolerStateCharacteristic);
90 put(CURRENT_HEATING_COOLING_STATE,
91 HomekitMetadataCharacteristicFactory::createCurrentHeatingCoolingStateCharacteristic);
92 put(CURRENT_VISIBILITY, HomekitMetadataCharacteristicFactory::createCurrentVisibilityCharacteristic);
93 put(FIRMWARE_REVISION, HomekitMetadataCharacteristicFactory::createFirmwareRevisionCharacteristic);
94 put(HARDWARE_REVISION, HomekitMetadataCharacteristicFactory::createHardwareRevisionCharacteristic);
95 put(IDENTIFIER, HomekitMetadataCharacteristicFactory::createIdentifierCharacteristic);
96 put(INPUT_DEVICE_TYPE, HomekitMetadataCharacteristicFactory::createInputDeviceTypeCharacteristic);
97 put(INPUT_SOURCE_TYPE, HomekitMetadataCharacteristicFactory::createInputSourceTypeCharacteristic);
98 put(MANUFACTURER, HomekitMetadataCharacteristicFactory::createManufacturerCharacteristic);
99 put(MODEL, HomekitMetadataCharacteristicFactory::createModelCharacteristic);
100 put(NAME, HomekitMetadataCharacteristicFactory::createNameCharacteristic);
101 put(PICTURE_MODE, HomekitMetadataCharacteristicFactory::createPictureModeCharacteristic);
102 put(SERIAL_NUMBER, HomekitMetadataCharacteristicFactory::createSerialNumberCharacteristic);
103 put(SERVICE_INDEX, HomekitMetadataCharacteristicFactory::createServiceIndexCharacteristic);
104 put(SLEEP_DISCOVERY_MODE, HomekitMetadataCharacteristicFactory::createSleepDiscoveryModeCharacteristic);
105 put(TARGET_HEATER_COOLER_STATE,
106 HomekitMetadataCharacteristicFactory::createTargetHeaterCoolerStateCharacteristic);
107 put(TARGET_HEATING_COOLING_STATE,
108 HomekitMetadataCharacteristicFactory::createTargetHeatingCoolingStateCharacteristic);
109 put(TEMPERATURE_UNIT, HomekitMetadataCharacteristicFactory::createTemperatureDisplayUnitCharacteristic);
110 put(VOLUME_CONTROL_TYPE, HomekitMetadataCharacteristicFactory::createVolumeControlTypeCharacteristic);
114 public static Optional<Characteristic> createCharacteristic(String characteristic, Object value) {
115 var type = HomekitCharacteristicType.valueOfTag(characteristic);
116 if (type.isEmpty()) {
117 return Optional.empty();
119 return createCharacteristic(type.get(), value);
122 public static Optional<Characteristic> createCharacteristic(HomekitCharacteristicType type, Object value) {
123 if (!OPTIONAL.containsKey(type)) {
124 return Optional.empty();
126 return Optional.of(OPTIONAL.get(type).apply(value));
129 private static Supplier<CompletableFuture<Integer>> getInteger(Object value) {
131 if (value instanceof BigDecimal valueAsBigDecimal) {
132 intValue = valueAsBigDecimal.intValue();
133 } else if (value instanceof Float) {
134 intValue = ((Float) value).intValue();
135 } else if (value instanceof Integer) {
136 intValue = (Integer) value;
137 } else if (value instanceof Long) {
138 intValue = ((Long) value).intValue();
140 intValue = Integer.valueOf(value.toString());
142 return () -> CompletableFuture.completedFuture(intValue);
145 private static Supplier<CompletableFuture<String>> getString(Object value) {
146 return () -> CompletableFuture.completedFuture(value.toString());
149 private static <T extends Enum<T>> Supplier<CompletableFuture<T>> getEnum(Object value, Class<T> klazz) {
150 T enumValue = Enum.valueOf(klazz, value.toString());
151 return () -> CompletableFuture.completedFuture(enumValue);
154 private static <T extends Enum<T>> Supplier<CompletableFuture<T>> getEnum(Object value, Class<T> klazz, T trueValue,
156 if (value.equals(true) || value.equals("true")) {
157 return () -> CompletableFuture.completedFuture(trueValue);
158 } else if (value.equals(false) || value.equals("false")) {
159 return () -> CompletableFuture.completedFuture(falseValue);
161 return getEnum(value, klazz);
164 private static Characteristic createActiveIdentifierCharacteristic(Object value) {
165 return new ActiveIdentifierCharacteristic(getInteger(value), v -> {
171 private static Characteristic createActiveStatusCharacteristic(Object value) {
172 return new ActiveCharacteristic(getEnum(value, ActiveEnum.class, ActiveEnum.ACTIVE, ActiveEnum.INACTIVE), v -> {
178 private static Characteristic createAirQualityCharacteristic(Object value) {
179 return new AirQualityCharacteristic(getEnum(value, AirQualityEnum.class), v -> {
184 private static Characteristic createClosedCaptionsCharacteristic(Object value) {
185 return new ClosedCaptionsCharacteristic(
186 getEnum(value, ClosedCaptionsEnum.class, ClosedCaptionsEnum.ENABLED, ClosedCaptionsEnum.DISABLED),
193 private static Characteristic createIsConfiguredCharacteristic(Object value) {
194 return new IsConfiguredCharacteristic(
195 getEnum(value, IsConfiguredEnum.class, IsConfiguredEnum.CONFIGURED, IsConfiguredEnum.NOT_CONFIGURED),
202 private static Characteristic createConfiguredNameCharacteristic(Object value) {
203 return new ConfiguredNameCharacteristic(getString(value), v -> {
209 private static Characteristic createCurrentVisibilityCharacteristic(Object value) {
210 return new CurrentVisibilityStateCharacteristic(getEnum(value, CurrentVisibilityStateEnum.class,
211 CurrentVisibilityStateEnum.SHOWN, CurrentVisibilityStateEnum.HIDDEN), v -> {
216 private static Characteristic createCurrentHeaterCoolerStateCharacteristic(Object value) {
217 var enumSupplier = getEnum(value, CurrentHeaterCoolerStateEnum.class);
218 CurrentHeaterCoolerStateEnum enumValue;
220 enumValue = enumSupplier.get().get();
221 } catch (InterruptedException | ExecutionException e) {
222 enumValue = CurrentHeaterCoolerStateEnum.INACTIVE;
224 return new CurrentHeaterCoolerStateCharacteristic(new CurrentHeaterCoolerStateEnum[] { enumValue },
230 private static Characteristic createCurrentHeatingCoolingStateCharacteristic(Object value) {
231 var enumSupplier = getEnum(value, CurrentHeatingCoolingStateEnum.class);
232 CurrentHeatingCoolingStateEnum enumValue;
234 enumValue = enumSupplier.get().get();
235 } catch (InterruptedException | ExecutionException e) {
236 enumValue = CurrentHeatingCoolingStateEnum.OFF;
238 return new CurrentHeatingCoolingStateCharacteristic(new CurrentHeatingCoolingStateEnum[] { enumValue },
244 private static Characteristic createFirmwareRevisionCharacteristic(Object value) {
245 return new FirmwareRevisionCharacteristic(getString(value));
248 private static Characteristic createHardwareRevisionCharacteristic(Object value) {
249 return new HardwareRevisionCharacteristic(getString(value));
252 private static Characteristic createIdentifierCharacteristic(Object value) {
253 return new IdentifierCharacteristic(getInteger(value));
256 private static Characteristic createInputDeviceTypeCharacteristic(Object value) {
257 return new InputDeviceTypeCharacteristic(getEnum(value, InputDeviceTypeEnum.class), v -> {
262 private static Characteristic createInputSourceTypeCharacteristic(Object value) {
263 return new InputSourceTypeCharacteristic(getEnum(value, InputSourceTypeEnum.class), v -> {
268 private static Characteristic createManufacturerCharacteristic(Object value) {
269 return new ManufacturerCharacteristic(getString(value));
272 private static Characteristic createModelCharacteristic(Object value) {
273 return new ModelCharacteristic(getString(value));
276 private static Characteristic createNameCharacteristic(Object value) {
277 return new NameCharacteristic(getString(value));
280 private static Characteristic createPictureModeCharacteristic(Object value) {
281 return new PictureModeCharacteristic(getEnum(value, PictureModeEnum.class), v -> {
287 private static Characteristic createSerialNumberCharacteristic(Object value) {
288 return new SerialNumberCharacteristic(getString(value));
291 private static Characteristic createServiceIndexCharacteristic(Object value) {
292 return new ServiceLabelIndexCharacteristic(getInteger(value));
295 private static Characteristic createSleepDiscoveryModeCharacteristic(Object value) {
296 return new SleepDiscoveryModeCharacteristic(getEnum(value, SleepDiscoveryModeEnum.class,
297 SleepDiscoveryModeEnum.ALWAYS_DISCOVERABLE, SleepDiscoveryModeEnum.NOT_DISCOVERABLE), v -> {
302 private static Characteristic createTargetHeaterCoolerStateCharacteristic(Object value) {
303 var enumSupplier = getEnum(value, TargetHeaterCoolerStateEnum.class);
304 TargetHeaterCoolerStateEnum enumValue;
306 enumValue = enumSupplier.get().get();
307 } catch (InterruptedException | ExecutionException e) {
308 enumValue = TargetHeaterCoolerStateEnum.AUTO;
311 return new TargetHeaterCoolerStateCharacteristic(new TargetHeaterCoolerStateEnum[] { enumValue }, enumSupplier,
318 private static Characteristic createTargetHeatingCoolingStateCharacteristic(Object value) {
319 var enumSupplier = getEnum(value, TargetHeatingCoolingStateEnum.class);
320 TargetHeatingCoolingStateEnum enumValue;
322 enumValue = enumSupplier.get().get();
323 } catch (InterruptedException | ExecutionException e) {
324 enumValue = TargetHeatingCoolingStateEnum.OFF;
327 return new TargetHeatingCoolingStateCharacteristic(new TargetHeatingCoolingStateEnum[] { enumValue },
334 private static Characteristic createTemperatureDisplayUnitCharacteristic(Object value) {
335 var enumSupplier = getEnum(value, TemperatureDisplayUnitEnum.class);
336 TemperatureDisplayUnitEnum enumValue;
338 enumValue = enumSupplier.get().get();
339 } catch (InterruptedException | ExecutionException e) {
340 enumValue = HomekitCharacteristicFactory.useFahrenheit() ? TemperatureDisplayUnitEnum.FAHRENHEIT
341 : TemperatureDisplayUnitEnum.CELSIUS;
344 return new TemperatureDisplayUnitCharacteristic(enumSupplier, v -> {
350 private static Characteristic createVolumeControlTypeCharacteristic(Object value) {
351 return new VolumeControlTypeCharacteristic(getEnum(value, VolumeControlTypeEnum.class), v -> {