]> git.basschouten.com Git - openhab-addons.git/blob
7cd25e2ee00f30c7cce664268ceeb4664850d194
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.io.homekit.internal.accessories;
14
15 import static org.openhab.io.homekit.internal.HomekitAccessoryType.*;
16 import static org.openhab.io.homekit.internal.HomekitCharacteristicType.*;
17
18 import java.lang.reflect.InvocationTargetException;
19 import java.util.AbstractMap.SimpleEntry;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Map.Entry;
27 import java.util.Objects;
28 import java.util.Optional;
29 import java.util.stream.Collectors;
30 import java.util.stream.Stream;
31
32 import org.eclipse.jdt.annotation.NonNullByDefault;
33 import org.eclipse.jdt.annotation.Nullable;
34 import org.openhab.core.items.GenericItem;
35 import org.openhab.core.items.GroupItem;
36 import org.openhab.core.items.Item;
37 import org.openhab.core.items.ItemRegistry;
38 import org.openhab.core.items.Metadata;
39 import org.openhab.core.items.MetadataKey;
40 import org.openhab.core.items.MetadataRegistry;
41 import org.openhab.io.homekit.internal.HomekitAccessoryType;
42 import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
43 import org.openhab.io.homekit.internal.HomekitCharacteristicType;
44 import org.openhab.io.homekit.internal.HomekitException;
45 import org.openhab.io.homekit.internal.HomekitOHItemProxy;
46 import org.openhab.io.homekit.internal.HomekitSettings;
47 import org.openhab.io.homekit.internal.HomekitTaggedItem;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 import io.github.hapjava.accessories.HomekitAccessory;
52 import io.github.hapjava.characteristics.Characteristic;
53 import io.github.hapjava.services.Service;
54
55 /**
56  * Creates a HomekitAccessory for a given HomekitTaggedItem.
57  *
58  * @author Andy Lintner - Initial contribution
59  * @author Eugen Freiter - refactoring for optional characteristics
60  */
61 @NonNullByDefault
62 public class HomekitAccessoryFactory {
63     private static final Logger logger = LoggerFactory.getLogger(HomekitAccessoryFactory.class);
64     public final static String METADATA_KEY = "homekit"; // prefix for HomeKit meta information in items.xml
65
66     /** List of mandatory attributes for each accessory type. **/
67     private final static Map<HomekitAccessoryType, HomekitCharacteristicType[]> MANDATORY_CHARACTERISTICS = new HashMap<HomekitAccessoryType, HomekitCharacteristicType[]>() {
68         {
69             put(LEAK_SENSOR, new HomekitCharacteristicType[] { LEAK_DETECTED_STATE });
70             put(MOTION_SENSOR, new HomekitCharacteristicType[] { MOTION_DETECTED_STATE });
71             put(OCCUPANCY_SENSOR, new HomekitCharacteristicType[] { OCCUPANCY_DETECTED_STATE });
72             put(CONTACT_SENSOR, new HomekitCharacteristicType[] { CONTACT_SENSOR_STATE });
73             put(SMOKE_SENSOR, new HomekitCharacteristicType[] { SMOKE_DETECTED_STATE });
74             put(HUMIDITY_SENSOR, new HomekitCharacteristicType[] { RELATIVE_HUMIDITY });
75             put(AIR_QUALITY_SENSOR, new HomekitCharacteristicType[] { AIR_QUALITY });
76             put(SWITCH, new HomekitCharacteristicType[] { ON_STATE });
77             put(CARBON_DIOXIDE_SENSOR, new HomekitCharacteristicType[] { CARBON_DIOXIDE_DETECTED_STATE });
78             put(CARBON_MONOXIDE_SENSOR, new HomekitCharacteristicType[] { CARBON_MONOXIDE_DETECTED_STATE });
79             put(WINDOW_COVERING, new HomekitCharacteristicType[] { TARGET_POSITION, CURRENT_POSITION, POSITION_STATE });
80             put(LIGHTBULB, new HomekitCharacteristicType[] { ON_STATE });
81             put(FAN, new HomekitCharacteristicType[] { ACTIVE_STATUS });
82             put(LIGHT_SENSOR, new HomekitCharacteristicType[] { LIGHT_LEVEL });
83             put(TEMPERATURE_SENSOR, new HomekitCharacteristicType[] { CURRENT_TEMPERATURE });
84             put(THERMOSTAT, new HomekitCharacteristicType[] { CURRENT_HEATING_COOLING_STATE,
85                     TARGET_HEATING_COOLING_STATE, CURRENT_TEMPERATURE, TARGET_TEMPERATURE });
86             put(LOCK, new HomekitCharacteristicType[] { LOCK_CURRENT_STATE, LOCK_TARGET_STATE });
87             put(VALVE, new HomekitCharacteristicType[] { ACTIVE_STATUS, INUSE_STATUS });
88             put(SECURITY_SYSTEM,
89                     new HomekitCharacteristicType[] { SECURITY_SYSTEM_CURRENT_STATE, SECURITY_SYSTEM_TARGET_STATE });
90             put(OUTLET, new HomekitCharacteristicType[] { ON_STATE, INUSE_STATUS });
91             put(SPEAKER, new HomekitCharacteristicType[] { MUTE });
92             put(SMART_SPEAKER, new HomekitCharacteristicType[] { CURRENT_MEDIA_STATE, TARGET_MEDIA_STATE });
93             put(GARAGE_DOOR_OPENER,
94                     new HomekitCharacteristicType[] { CURRENT_DOOR_STATE, TARGET_DOOR_STATE, OBSTRUCTION_STATUS });
95             put(HEATER_COOLER, new HomekitCharacteristicType[] { ACTIVE_STATUS, CURRENT_HEATER_COOLER_STATE,
96                     TARGET_HEATER_COOLER_STATE, CURRENT_TEMPERATURE });
97             put(WINDOW, new HomekitCharacteristicType[] { CURRENT_POSITION, TARGET_POSITION, POSITION_STATE });
98             put(DOOR, new HomekitCharacteristicType[] { CURRENT_POSITION, TARGET_POSITION, POSITION_STATE });
99             put(BATTERY, new HomekitCharacteristicType[] { BATTERY_LEVEL, BATTERY_LOW_STATUS });
100             put(FILTER_MAINTENANCE, new HomekitCharacteristicType[] { FILTER_CHANGE_INDICATION });
101             put(SLAT, new HomekitCharacteristicType[] { CURRENT_SLAT_STATE });
102             put(FAUCET, new HomekitCharacteristicType[] { ACTIVE_STATUS });
103             put(MICROPHONE, new HomekitCharacteristicType[] { MUTE });
104         }
105     };
106
107     /** List of service implementation for each accessory type. **/
108     private final static Map<HomekitAccessoryType, Class<? extends AbstractHomekitAccessoryImpl>> SERVICE_IMPL_MAP = new HashMap<HomekitAccessoryType, Class<? extends AbstractHomekitAccessoryImpl>>() {
109         {
110             put(LEAK_SENSOR, HomekitLeakSensorImpl.class);
111             put(MOTION_SENSOR, HomekitMotionSensorImpl.class);
112             put(OCCUPANCY_SENSOR, HomekitOccupancySensorImpl.class);
113             put(CONTACT_SENSOR, HomekitContactSensorImpl.class);
114             put(SMOKE_SENSOR, HomekitSmokeSensorImpl.class);
115             put(HUMIDITY_SENSOR, HomekitHumiditySensorImpl.class);
116             put(AIR_QUALITY_SENSOR, HomekitAirQualitySensorImpl.class);
117             put(SWITCH, HomekitSwitchImpl.class);
118             put(CARBON_DIOXIDE_SENSOR, HomekitCarbonDioxideSensorImpl.class);
119             put(CARBON_MONOXIDE_SENSOR, HomekitCarbonMonoxideSensorImpl.class);
120             put(WINDOW_COVERING, HomekitWindowCoveringImpl.class);
121             put(LIGHTBULB, HomekitLightbulbImpl.class);
122             put(FAN, HomekitFanImpl.class);
123             put(LIGHT_SENSOR, HomekitLightSensorImpl.class);
124             put(TEMPERATURE_SENSOR, HomekitTemperatureSensorImpl.class);
125             put(THERMOSTAT, HomekitThermostatImpl.class);
126             put(LOCK, HomekitLockImpl.class);
127             put(VALVE, HomekitValveImpl.class);
128             put(SECURITY_SYSTEM, HomekitSecuritySystemImpl.class);
129             put(OUTLET, HomekitOutletImpl.class);
130             put(SPEAKER, HomekitSpeakerImpl.class);
131             put(SMART_SPEAKER, HomekitSmartSpeakerImpl.class);
132             put(GARAGE_DOOR_OPENER, HomekitGarageDoorOpenerImpl.class);
133             put(DOOR, HomekitDoorImpl.class);
134             put(WINDOW, HomekitWindowImpl.class);
135             put(HEATER_COOLER, HomekitHeaterCoolerImpl.class);
136             put(BATTERY, HomekitBatteryImpl.class);
137             put(FILTER_MAINTENANCE, HomekitFilterMaintenanceImpl.class);
138             put(SLAT, HomekitSlatImpl.class);
139             put(FAUCET, HomekitFaucetImpl.class);
140             put(MICROPHONE, HomekitMicrophoneImpl.class);
141         }
142     };
143
144     private static List<HomekitCharacteristicType> getRequiredCharacteristics(HomekitTaggedItem taggedItem) {
145         if (taggedItem.getAccessoryType() == BATTERY) {
146             final String isChargeable = taggedItem.getConfiguration(HomekitBatteryImpl.BATTERY_TYPE, "false");
147             if ("true".equalsIgnoreCase(isChargeable) || "yes".equalsIgnoreCase(isChargeable)) {
148                 final List<HomekitCharacteristicType> characteristics = new ArrayList<>();
149                 characteristics.addAll(Arrays.asList(MANDATORY_CHARACTERISTICS.get(taggedItem.getAccessoryType())));
150                 characteristics.add(BATTERY_CHARGING_STATE);
151                 return characteristics;
152             }
153         }
154         return Arrays.asList(MANDATORY_CHARACTERISTICS.get(taggedItem.getAccessoryType()));
155     }
156
157     /**
158      * creates HomeKit accessory for an openhab item.
159      *
160      * @param taggedItem openhab item tagged as HomeKit item
161      * @param metadataRegistry openhab metadata registry required to get item meta information
162      * @param updater OH HomeKit update class that ensure the status sync between OH item and corresponding HomeKit
163      *            characteristic.
164      * @param settings OH settings
165      * @return HomeKit accessory
166      * @throws HomekitException exception in case HomeKit accessory could not be created, e.g. due missing mandatory
167      *             characteristic
168      */
169     @SuppressWarnings("null")
170     public static HomekitAccessory create(HomekitTaggedItem taggedItem, MetadataRegistry metadataRegistry,
171             HomekitAccessoryUpdater updater, HomekitSettings settings) throws HomekitException {
172         final HomekitAccessoryType accessoryType = taggedItem.getAccessoryType();
173         logger.trace("Constructing {} of accessory type {}", taggedItem.getName(), accessoryType.getTag());
174         final List<HomekitTaggedItem> foundCharacteristics = getMandatoryCharacteristicsFromItem(taggedItem,
175                 metadataRegistry);
176         final List<HomekitCharacteristicType> mandatoryCharacteristics = getRequiredCharacteristics(taggedItem);
177         if (foundCharacteristics.size() < mandatoryCharacteristics.size()) {
178             logger.warn("Accessory of type {} must have following characteristics {}. Found only {}",
179                     accessoryType.getTag(), mandatoryCharacteristics, foundCharacteristics);
180             throw new HomekitException("Missing mandatory characteristics");
181         }
182         AbstractHomekitAccessoryImpl accessoryImpl;
183         try {
184             final @Nullable Class<? extends AbstractHomekitAccessoryImpl> accessoryImplClass = SERVICE_IMPL_MAP
185                     .get(accessoryType);
186             if (accessoryImplClass != null) {
187                 accessoryImpl = accessoryImplClass.getConstructor(HomekitTaggedItem.class, List.class,
188                         HomekitAccessoryUpdater.class, HomekitSettings.class)
189                         .newInstance(taggedItem, foundCharacteristics, updater, settings);
190                 addOptionalCharacteristics(taggedItem, accessoryImpl, metadataRegistry);
191                 return accessoryImpl;
192             } else {
193                 logger.warn("Unsupported HomeKit type: {}", accessoryType.getTag());
194                 throw new HomekitException("Unsupported HomeKit type: " + accessoryType);
195             }
196         } catch (NoSuchMethodException | IllegalAccessException | InstantiationException
197                 | InvocationTargetException e) {
198             logger.warn("Cannot instantiate accessory implementation for accessory {}", accessoryType.getTag(), e);
199             throw new HomekitException("Cannot instantiate accessory implementation for accessory " + accessoryType);
200         }
201     }
202
203     /**
204      * return HomeKit accessory types for a OH item based on meta data
205      *
206      * @param item OH item
207      * @param metadataRegistry meta data registry
208      * @return list of HomeKit accessory types and characteristics.
209      */
210     public static List<Entry<HomekitAccessoryType, HomekitCharacteristicType>> getAccessoryTypes(Item item,
211             MetadataRegistry metadataRegistry) {
212         final List<Entry<HomekitAccessoryType, HomekitCharacteristicType>> accessories = new ArrayList<>();
213         final @Nullable Metadata metadata = metadataRegistry.get(new MetadataKey(METADATA_KEY, item.getUID()));
214         if (metadata != null) {
215             String[] tags = metadata.getValue().split(",");
216             for (String tag : tags) {
217                 final String[] meta = tag.split("\\.");
218                 Optional<HomekitAccessoryType> accessoryType = HomekitAccessoryType.valueOfTag(meta[0].trim());
219                 if (accessoryType.isPresent()) { // it accessory, check for characteristic
220                     HomekitAccessoryType type = accessoryType.get();
221                     if (meta.length > 1) {
222                         // it has characteristic as well
223                         accessories.add(new SimpleEntry<>(type,
224                                 HomekitCharacteristicType.valueOfTag(meta[1].trim()).orElse(EMPTY)));
225                     } else {// it has no characteristic
226                         accessories.add(new SimpleEntry<>(type, EMPTY));
227                     }
228                 } else { // it is no accessory, so, maybe it is a characteristic
229                     HomekitCharacteristicType.valueOfTag(meta[0].trim())
230                             .ifPresent(c -> accessories.add(new SimpleEntry<>(DUMMY, c)));
231                 }
232             }
233         }
234         return accessories;
235     }
236
237     public static @Nullable Map<String, Object> getItemConfiguration(Item item, MetadataRegistry metadataRegistry) {
238         final @Nullable Metadata metadata = metadataRegistry.get(new MetadataKey(METADATA_KEY, item.getUID()));
239         return metadata != null ? metadata.getConfiguration() : null;
240     }
241
242     /**
243      * return list of HomeKit relevant groups linked to an accessory
244      *
245      * @param item OH item
246      * @param itemRegistry item registry
247      * @param metadataRegistry metadata registry
248      * @return list of relevant group items
249      */
250     public static List<GroupItem> getAccessoryGroups(Item item, ItemRegistry itemRegistry,
251             MetadataRegistry metadataRegistry) {
252         return (item instanceof GroupItem) ? Collections.emptyList() : item.getGroupNames().stream().flatMap(name -> {
253             final @Nullable Item groupItem = itemRegistry.get(name);
254             if ((groupItem instanceof GroupItem) && ((GroupItem) groupItem).getBaseItem() == null) {
255                 return Stream.of((GroupItem) groupItem);
256             } else {
257                 return Stream.empty();
258             }
259         }).filter(groupItem -> !getAccessoryTypes(groupItem, metadataRegistry).isEmpty()).collect(Collectors.toList());
260     }
261
262     /**
263      * collect all mandatory characteristics for a given tagged item, e.g. collect all mandatory HomeKit items from a
264      * GroupItem
265      *
266      * @param taggedItem HomeKit tagged item
267      * @param metadataRegistry meta data registry
268      * @return list of mandatory
269      */
270     private static List<HomekitTaggedItem> getMandatoryCharacteristicsFromItem(HomekitTaggedItem taggedItem,
271             MetadataRegistry metadataRegistry) {
272         List<HomekitTaggedItem> collectedCharacteristics = new ArrayList<>();
273         if (taggedItem.isGroup()) {
274             for (Item item : ((GroupItem) taggedItem.getItem()).getAllMembers()) {
275                 addMandatoryCharacteristics(taggedItem, collectedCharacteristics, item, metadataRegistry);
276             }
277         } else {
278             addMandatoryCharacteristics(taggedItem, collectedCharacteristics, taggedItem.getItem(), metadataRegistry);
279         }
280         logger.trace("Mandatory characteristics: {}", collectedCharacteristics);
281         return collectedCharacteristics;
282     }
283
284     /**
285      * add mandatory HomeKit items for a given main item to a list of characteristics.
286      * Main item is use only to determine, which characteristics are mandatory.
287      * The characteristics are added to item.
288      * e.g. mainItem could be a group tagged as "thermostat" and item could be item linked to the group and marked as
289      * TargetTemperature
290      *
291      * @param mainItem main item
292      * @param characteristics list of characteristics
293      * @param item current item
294      * @param metadataRegistry meta date registry
295      */
296     private static void addMandatoryCharacteristics(HomekitTaggedItem mainItem, List<HomekitTaggedItem> characteristics,
297             Item item, MetadataRegistry metadataRegistry) {
298         // get list of mandatory characteristics
299         List<HomekitCharacteristicType> mandatoryCharacteristics = getRequiredCharacteristics(mainItem);
300         if (mandatoryCharacteristics.isEmpty()) {
301             // no mandatory characteristics linked to accessory type of mainItem. we are done
302             return;
303         }
304         // check whether we are adding characteristic to the main item, and if yes, use existing item proxy.
305         // if we are adding not to the main item (typical for groups), create new proxy item.
306         final HomekitOHItemProxy itemProxy = mainItem.getItem().equals(item) ? mainItem.getProxyItem()
307                 : new HomekitOHItemProxy(item);
308         // an item can have several tags, e.g. "ActiveStatus, InUse". we iterate here over all his tags
309         for (Entry<HomekitAccessoryType, HomekitCharacteristicType> accessory : getAccessoryTypes(item,
310                 metadataRegistry)) {
311             // if the item has only accessory tag, e.g. TemperatureSensor,
312             // then we will link all mandatory characteristic to this item,
313             // e.g. we will link CurrentTemperature in case of TemperatureSensor.
314             if (isRootAccessory(accessory)) {
315                 mandatoryCharacteristics.forEach(c -> characteristics.add(new HomekitTaggedItem(itemProxy,
316                         accessory.getKey(), c, mainItem.isGroup() ? (GroupItem) mainItem.getItem() : null,
317                         HomekitAccessoryFactory.getItemConfiguration(item, metadataRegistry))));
318             } else {
319                 // item has characteristic tag on it, so, adding it as that characteristic.
320
321                 final HomekitCharacteristicType characteristic = accessory.getValue();
322
323                 // check whether it is a mandatory characteristic. optional will be added later by another method.
324                 if (belongsToType(mainItem.getAccessoryType(), accessory)
325                         && isMandatoryCharacteristic(mainItem, characteristic)) {
326                     characteristics.add(new HomekitTaggedItem(itemProxy, accessory.getKey(), characteristic,
327                             mainItem.isGroup() ? (GroupItem) mainItem.getItem() : null,
328                             HomekitAccessoryFactory.getItemConfiguration(item, metadataRegistry)));
329                 }
330             }
331         }
332     }
333
334     /**
335      * add optional characteristic for given accessory.
336      *
337      * @param taggedItem main item
338      * @param accessory accessory
339      * @param metadataRegistry metadata registry
340      */
341     private static void addOptionalCharacteristics(HomekitTaggedItem taggedItem, AbstractHomekitAccessoryImpl accessory,
342             MetadataRegistry metadataRegistry) {
343         Map<HomekitCharacteristicType, GenericItem> characteristics = getOptionalCharacteristics(
344                 accessory.getRootAccessory(), metadataRegistry);
345         Service service = accessory.getPrimaryService();
346         HashMap<String, HomekitOHItemProxy> proxyItems = new HashMap<>();
347         proxyItems.put(taggedItem.getItem().getUID(), taggedItem.getProxyItem());
348         // an accessory can have multiple optional characteristics. iterate over them.
349         characteristics.forEach((type, item) -> {
350             try {
351                 // check whether a proxyItem already exists, if not create one.
352                 final HomekitOHItemProxy proxyItem = Objects
353                         .requireNonNull(proxyItems.computeIfAbsent(item.getUID(), k -> new HomekitOHItemProxy(item)));
354                 final HomekitTaggedItem optionalItem = new HomekitTaggedItem(proxyItem,
355                         accessory.getRootAccessory().getAccessoryType(), type,
356                         accessory.getRootAccessory().getRootDeviceGroupItem(),
357                         getItemConfiguration(item, metadataRegistry));
358                 final Characteristic characteristic = HomekitCharacteristicFactory.createCharacteristic(optionalItem,
359                         accessory.getUpdater());
360                 // find the corresponding add method at service and call it.
361                 service.getClass().getMethod("addOptionalCharacteristic", characteristic.getClass()).invoke(service,
362                         characteristic);
363                 accessory.addCharacteristic(optionalItem);
364             } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | HomekitException e) {
365                 logger.warn("Unsupported optional HomeKit characteristic: service type {}, characteristic type {}",
366                         service.getType(), type.getTag());
367             }
368         });
369     }
370
371     /**
372      * collect optional HomeKit characteristics for a OH item.
373      *
374      * @param taggedItem main OH item
375      * @param metadataRegistry OH metadata registry
376      * @return a map with characteristics and corresponding OH items
377      */
378     private static Map<HomekitCharacteristicType, GenericItem> getOptionalCharacteristics(HomekitTaggedItem taggedItem,
379             MetadataRegistry metadataRegistry) {
380         Map<HomekitCharacteristicType, GenericItem> characteristicItems = new HashMap<>();
381         if (taggedItem.isGroup()) {
382             GroupItem groupItem = (GroupItem) taggedItem.getItem();
383             groupItem.getMembers().forEach(item -> getAccessoryTypes(item, metadataRegistry).stream()
384                     .filter(c -> !isRootAccessory(c)).filter(c -> belongsToType(taggedItem.getAccessoryType(), c))
385                     .filter(c -> !isMandatoryCharacteristic(taggedItem, c.getValue()))
386                     .forEach(characteristic -> characteristicItems.put(characteristic.getValue(), (GenericItem) item)));
387         } else {
388             getAccessoryTypes(taggedItem.getItem(), metadataRegistry).stream().filter(c -> !isRootAccessory(c))
389                     .filter(c -> !isMandatoryCharacteristic(taggedItem, c.getValue()))
390                     .forEach(characteristic -> characteristicItems.put(characteristic.getValue(),
391                             (GenericItem) taggedItem.getItem()));
392         }
393         logger.trace("Optional characteristics for item {}: {}", taggedItem.getName(), characteristicItems.values());
394         return Collections.unmodifiableMap(characteristicItems);
395     }
396
397     /**
398      * return true is characteristic is a mandatory characteristic for the accessory.
399      *
400      * @param item item
401      * @param characteristic characteristic
402      * @return true if characteristic is mandatory, false if not mandatory
403      */
404     private static boolean isMandatoryCharacteristic(HomekitTaggedItem item, HomekitCharacteristicType characteristic) {
405         return MANDATORY_CHARACTERISTICS.containsKey(item.getAccessoryType())
406                 && getRequiredCharacteristics(item).contains(characteristic);
407     }
408
409     /**
410      * check whether accessory is root accessory, i.e. without characteristic tag.
411      *
412      * @param accessory accessory
413      * @return true if accessory has not characteristic.
414      */
415     private static boolean isRootAccessory(Entry<HomekitAccessoryType, HomekitCharacteristicType> accessory) {
416         return ((accessory.getValue() == null) || (accessory.getValue() == EMPTY));
417     }
418
419     /**
420      * check whether characteristic belongs to the specific accessory type.
421      * characteristic with no accessory type mentioned in metadata are considered as candidates for all types.
422      *
423      * @param accessoryType accessory type
424      * @param characteristic characteristic
425      * @return true if characteristic belongs to the accessory type.
426      */
427     private static boolean belongsToType(HomekitAccessoryType accessoryType,
428             Entry<HomekitAccessoryType, HomekitCharacteristicType> characteristic) {
429         return ((characteristic.getKey() == accessoryType) || (characteristic.getKey() == DUMMY));
430     }
431 }