2 * Copyright (c) 2010-2023 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.binding.homematic.internal.handler;
15 import static org.openhab.binding.homematic.internal.HomematicBindingConstants.*;
16 import static org.openhab.binding.homematic.internal.misc.HomematicConstants.*;
18 import java.io.IOException;
19 import java.math.BigDecimal;
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.Iterator;
23 import java.util.List;
25 import java.util.Map.Entry;
26 import java.util.Objects;
27 import java.util.concurrent.Future;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.openhab.binding.homematic.internal.common.HomematicConfig;
31 import org.openhab.binding.homematic.internal.communicator.HomematicGateway;
32 import org.openhab.binding.homematic.internal.converter.ConverterException;
33 import org.openhab.binding.homematic.internal.converter.ConverterFactory;
34 import org.openhab.binding.homematic.internal.converter.ConverterTypeException;
35 import org.openhab.binding.homematic.internal.converter.TypeConverter;
36 import org.openhab.binding.homematic.internal.misc.HomematicClientException;
37 import org.openhab.binding.homematic.internal.misc.HomematicConstants;
38 import org.openhab.binding.homematic.internal.model.HmChannel;
39 import org.openhab.binding.homematic.internal.model.HmDatapoint;
40 import org.openhab.binding.homematic.internal.model.HmDatapointConfig;
41 import org.openhab.binding.homematic.internal.model.HmDatapointInfo;
42 import org.openhab.binding.homematic.internal.model.HmDevice;
43 import org.openhab.binding.homematic.internal.model.HmParamsetType;
44 import org.openhab.binding.homematic.internal.type.HomematicChannelTypeProvider;
45 import org.openhab.binding.homematic.internal.type.HomematicTypeGeneratorImpl;
46 import org.openhab.binding.homematic.internal.type.MetadataUtils;
47 import org.openhab.binding.homematic.internal.type.UidUtils;
48 import org.openhab.core.config.core.Configuration;
49 import org.openhab.core.config.core.validation.ConfigValidationException;
50 import org.openhab.core.library.types.OnOffType;
51 import org.openhab.core.library.types.StopMoveType;
52 import org.openhab.core.thing.Bridge;
53 import org.openhab.core.thing.Channel;
54 import org.openhab.core.thing.ChannelUID;
55 import org.openhab.core.thing.Thing;
56 import org.openhab.core.thing.ThingStatus;
57 import org.openhab.core.thing.ThingStatusDetail;
58 import org.openhab.core.thing.binding.BaseThingHandler;
59 import org.openhab.core.thing.binding.ThingHandler;
60 import org.openhab.core.thing.binding.builder.ChannelBuilder;
61 import org.openhab.core.thing.type.ChannelType;
62 import org.openhab.core.thing.type.ChannelTypeUID;
63 import org.openhab.core.types.Command;
64 import org.openhab.core.types.RefreshType;
65 import org.openhab.core.types.State;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
70 * The {@link HomematicThingHandler} is responsible for handling commands, which are sent to one of the channels.
72 * @author Gerhard Riegler - Initial contribution
74 public class HomematicThingHandler extends BaseThingHandler {
75 private final Logger logger = LoggerFactory.getLogger(HomematicThingHandler.class);
76 private final HomematicChannelTypeProvider channelTypeProvider;
77 private Future<?> initFuture;
78 private final Object initLock = new Object();
79 private volatile boolean deviceDeletionPending = false;
81 public HomematicThingHandler(Thing thing, HomematicChannelTypeProvider channelTypeProvider) {
83 this.channelTypeProvider = channelTypeProvider;
87 public void initialize() {
88 if (initFuture != null) {
92 initFuture = scheduler.submit(() -> {
95 synchronized (initLock) {
96 doInitializeInBackground();
98 } catch (HomematicClientException ex) {
99 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, ex.getMessage());
100 } catch (IOException ex) {
101 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, ex.getMessage());
102 } catch (GatewayNotAvailableException ex) {
103 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, ex.getMessage());
104 } catch (Exception ex) {
105 logger.error("{}", ex.getMessage(), ex);
106 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, ex.getMessage());
111 private void doInitializeInBackground() throws GatewayNotAvailableException, HomematicClientException, IOException {
112 HomematicGateway gateway = getHomematicGateway();
113 HmDevice device = gateway.getDevice(UidUtils.getHomematicAddress(getThing()));
114 HmChannel channelZero = device.getChannel(0);
115 loadHomematicChannelValues(channelZero);
116 updateStatus(device);
117 logger.debug("Initializing thing '{}' from gateway '{}'", getThing().getUID(), gateway.getId());
120 Map<String, String> properties = editProperties();
121 setProperty(properties, channelZero, PROPERTY_BATTERY_TYPE, VIRTUAL_DATAPOINT_NAME_BATTERY_TYPE);
122 setProperty(properties, channelZero, Thing.PROPERTY_FIRMWARE_VERSION, VIRTUAL_DATAPOINT_NAME_FIRMWARE);
123 setProperty(properties, channelZero, Thing.PROPERTY_SERIAL_NUMBER, device.getAddress());
124 setProperty(properties, channelZero, PROPERTY_AES_KEY, DATAPOINT_NAME_AES_KEY);
125 updateProperties(properties);
127 // update data point list for reconfigurable channels
128 for (HmChannel channel : device.getChannels()) {
129 if (channel.isReconfigurable()) {
130 loadHomematicChannelValues(channel);
131 if (channel.checkForChannelFunctionChange()) {
132 gateway.updateChannelValueDatapoints(channel);
137 // update configurations
138 Configuration config = editConfiguration();
139 for (HmChannel channel : device.getChannels()) {
140 loadHomematicChannelValues(channel);
141 for (HmDatapoint dp : channel.getDatapoints()) {
142 if (dp.getParamsetType() == HmParamsetType.MASTER) {
143 config.put(MetadataUtils.getParameterName(dp), getValueForConfiguration(dp));
147 updateConfiguration(config);
149 boolean channelsChanged = false;
151 // update thing channel list for reconfigurable channels (relies on the new value of the
152 // CHANNEL_FUNCTION datapoint fetched during configuration update)
153 List<Channel> thingChannels = new ArrayList<>(getThing().getChannels());
155 if (thingChannels.isEmpty()) {
156 for (HmChannel channel : device.getChannels()) {
157 for (HmDatapoint dp : channel.getDatapoints()) {
158 if (HomematicTypeGeneratorImpl.isIgnoredDatapoint(dp)
159 || dp.getParamsetType() != HmParamsetType.VALUES) {
162 ChannelUID channelUID = UidUtils.generateChannelUID(dp, getThing().getUID());
163 if (containsChannel(thingChannels, channelUID)) {
164 // Channel is already present
168 ChannelTypeUID channelTypeUID = UidUtils.generateChannelTypeUID(dp);
169 ChannelType channelType = channelTypeProvider.getInternalChannelType(channelTypeUID);
170 if (channelType == null) {
171 channelType = HomematicTypeGeneratorImpl.createChannelType(dp, channelTypeUID);
172 channelTypeProvider.addChannelType(channelType);
175 Channel thingChannel = ChannelBuilder.create(channelUID, MetadataUtils.getItemType(dp))
176 .withLabel(MetadataUtils.getLabel(dp))
177 .withDescription(MetadataUtils.getDatapointDescription(dp)).withType(channelType.getUID())
179 thingChannels.add(thingChannel);
182 "Updated value datapoints for channel {} of device '{}' (function {}), now has {} datapoints",
183 channel, channel.getDevice().getAddress(), channel.getCurrentFunction(),
184 channel.getDatapoints().size());
187 channelsChanged = true;
190 if (updateDynamicChannelList(device, thingChannels)) {
191 channelsChanged = true;
194 if (channelsChanged) {
195 updateThing(editThing().withChannels(thingChannels).build());
198 thingChannels.forEach(channel -> {
199 if (isLinked(channel.getUID())) {
200 channelLinked(channel.getUID());
206 * Update the given thing channel list to reflect the device's current datapoint set
208 * @return true if the list was modified, false if it was not modified
210 private boolean updateDynamicChannelList(HmDevice device, List<Channel> thingChannels) {
211 boolean changed = false;
212 for (HmChannel channel : device.getChannels()) {
213 if (!channel.isReconfigurable()) {
216 final String expectedFunction = channel
217 .getDatapoint(HmParamsetType.MASTER, HomematicConstants.DATAPOINT_NAME_CHANNEL_FUNCTION)
219 final String propertyName = String.format(PROPERTY_DYNAMIC_FUNCTION_FORMAT, channel.getNumber());
221 // remove thing channels that were configured for a different function
222 Iterator<Channel> channelIter = thingChannels.iterator();
223 while (channelIter.hasNext()) {
224 Map<String, String> properties = channelIter.next().getProperties();
225 String function = properties.get(propertyName);
226 if (function != null && !function.equals(expectedFunction)) {
227 channelIter.remove();
231 for (HmDatapoint dp : channel.getDatapoints()) {
232 if (HomematicTypeGeneratorImpl.isIgnoredDatapoint(dp)
233 || dp.getParamsetType() != HmParamsetType.VALUES) {
236 ChannelUID channelUID = UidUtils.generateChannelUID(dp, getThing().getUID());
237 if (containsChannel(thingChannels, channelUID)) {
238 // Channel is already present -> channel configuration likely hasn't changed
242 Map<String, String> channelProps = new HashMap<>();
243 channelProps.put(propertyName, expectedFunction);
245 ChannelTypeUID channelTypeUID = UidUtils.generateChannelTypeUID(dp);
246 ChannelType channelType = channelTypeProvider.getInternalChannelType(channelTypeUID);
247 if (channelType == null) {
248 channelType = HomematicTypeGeneratorImpl.createChannelType(dp, channelTypeUID);
249 channelTypeProvider.addChannelType(channelType);
252 Channel thingChannel = ChannelBuilder.create(channelUID, MetadataUtils.getItemType(dp))
253 .withProperties(channelProps).withLabel(MetadataUtils.getLabel(dp))
254 .withDescription(MetadataUtils.getDatapointDescription(dp)).withType(channelType.getUID())
256 thingChannels.add(thingChannel);
265 * Checks whether the given list includes a channel with the given UID
267 private static boolean containsChannel(List<Channel> channels, ChannelUID channelUID) {
268 for (Channel channel : channels) {
269 ChannelUID uid = channel.getUID();
270 if (Objects.equals(channelUID.getGroupId(), uid.getGroupId())
271 && Objects.equals(channelUID.getId(), uid.getId())) {
279 * Sets a thing property with a datapoint value.
281 private void setProperty(Map<String, String> properties, HmChannel channelZero, String propertyName,
282 String datapointName) {
283 HmDatapoint dp = channelZero
284 .getDatapoint(new HmDatapointInfo(HmParamsetType.VALUES, channelZero, datapointName));
286 properties.put(propertyName, Objects.toString(dp.getValue(), ""));
291 public void channelLinked(ChannelUID channelUID) {
292 handleRefresh(channelUID);
296 * Updates the state of the given channel.
298 protected void handleRefresh(ChannelUID channelUID) {
300 if (thing.getStatus() == ThingStatus.ONLINE) {
301 logger.debug("Updating channel '{}' from thing id '{}'", channelUID, getThing().getUID().getId());
302 updateChannelState(channelUID);
304 } catch (Exception ex) {
305 logger.warn("{}", ex.getMessage());
310 public void handleCommand(ChannelUID channelUID, Command command) {
311 logger.debug("Received command '{}' for channel '{}'", command, channelUID);
312 HmDatapoint dp = null;
314 HomematicGateway gateway = getHomematicGateway();
315 HmDatapointInfo dpInfo = UidUtils.createHmDatapointInfo(channelUID);
316 if (RefreshType.REFRESH == command) {
317 logger.debug("Refreshing {}", dpInfo);
318 dpInfo = new HmDatapointInfo(dpInfo.getAddress(), HmParamsetType.VALUES, 0,
319 VIRTUAL_DATAPOINT_NAME_RELOAD_FROM_GATEWAY);
320 dp = gateway.getDatapoint(dpInfo);
321 sendDatapoint(dp, new HmDatapointConfig(), Boolean.TRUE);
323 Channel channel = getThing().getChannel(channelUID.getId());
324 if (channel == null) {
325 logger.warn("Channel '{}' not found in thing '{}' on gateway '{}'", channelUID, getThing().getUID(),
328 if (StopMoveType.STOP == command && DATAPOINT_NAME_LEVEL.equals(dpInfo.getName())) {
329 // special case with stop type (rollershutter)
330 dpInfo.setName(DATAPOINT_NAME_STOP);
331 HmDatapoint stopDp = gateway.getDatapoint(dpInfo);
332 ChannelUID stopChannelUID = UidUtils.generateChannelUID(stopDp, getThing().getUID());
333 handleCommand(stopChannelUID, OnOffType.ON);
335 dp = gateway.getDatapoint(dpInfo);
336 TypeConverter<?> converter = ConverterFactory.createConverter(channel.getAcceptedItemType());
337 Object newValue = converter.convertToBinding(command, dp);
338 HmDatapointConfig config = getChannelConfig(channel, dp);
339 sendDatapoint(dp, config, newValue);
343 } catch (HomematicClientException | GatewayNotAvailableException ex) {
344 logger.warn("{}", ex.getMessage());
345 } catch (IOException ex) {
346 if (dp != null && dp.getChannel().getDevice().isOffline()) {
347 logger.warn("Device '{}' is OFFLINE, can't send command '{}' for channel '{}'",
348 dp.getChannel().getDevice().getAddress(), command, channelUID);
349 logger.trace("{}", ex.getMessage(), ex);
351 logger.error("{}", ex.getMessage(), ex);
353 } catch (ConverterTypeException ex) {
354 logger.warn("{}, please check the item type and the commands in your scripts", ex.getMessage());
355 } catch (Exception ex) {
356 logger.error("{}", ex.getMessage(), ex);
360 private void sendDatapoint(HmDatapoint dp, HmDatapointConfig config, Object newValue)
361 throws IOException, HomematicClientException, GatewayNotAvailableException {
362 String rxMode = getRxModeForDatapointTransmission(dp.getName(), dp.getValue(), newValue);
363 getHomematicGateway().sendDatapoint(dp, config, newValue, rxMode);
367 * Returns the rx mode that shall be used for transmitting a new value of a datapoint to the device. The
368 * HomematicThingHandler always uses the default rx mode; custom thing handlers can override this method to
369 * adjust the rx mode.
371 * @param datapointName The datapoint that will be updated on the device
372 * @param currentValue The current value of the datapoint
373 * @param newValue The value that will be sent to the device
374 * @return The rxMode ({@link org.openhab.binding.homematic.internal.HomematicBindingConstants#RX_BURST_MODE
375 * "BURST"} for burst mode,
376 * {@link org.openhab.binding.homematic.internal.HomematicBindingConstants#RX_WAKEUP_MODE "WAKEUP"} for
377 * wakeup mode, or null for the default mode)
379 protected String getRxModeForDatapointTransmission(String datapointName, Object currentValue, Object newValue) {
384 * Evaluates the channel and datapoint for this channelUID and updates the state of the channel.
386 private void updateChannelState(ChannelUID channelUID)
387 throws GatewayNotAvailableException, HomematicClientException, IOException, ConverterException {
388 HomematicGateway gateway = getHomematicGateway();
389 HmDatapointInfo dpInfo = UidUtils.createHmDatapointInfo(channelUID);
390 HmDatapoint dp = gateway.getDatapoint(dpInfo);
391 Channel channel = getThing().getChannel(channelUID.getId());
392 updateChannelState(dp, channel);
396 * Sets the configuration or evaluates the channel for this datapoint and updates the state of the channel.
398 protected void updateDatapointState(HmDatapoint dp) {
400 updateStatus(dp.getChannel().getDevice());
402 if (dp.getParamsetType() == HmParamsetType.MASTER) {
403 // update configuration
404 Configuration config = editConfiguration();
405 config.put(MetadataUtils.getParameterName(dp), getValueForConfiguration(dp));
406 updateConfiguration(config);
407 } else if (!HomematicTypeGeneratorImpl.isIgnoredDatapoint(dp)) {
409 ChannelUID channelUID = UidUtils.generateChannelUID(dp, thing.getUID());
410 Channel channel = thing.getChannel(channelUID.getId());
411 if (channel != null) {
412 updateChannelState(dp, channel);
414 logger.warn("Channel not found for datapoint '{}'", new HmDatapointInfo(dp));
417 } catch (GatewayNotAvailableException ex) {
419 } catch (Exception ex) {
420 logger.error("{}", ex.getMessage(), ex);
424 private @Nullable Object getValueForConfiguration(HmDatapoint dp) {
425 if (dp.getValue() == null) {
428 if (dp.isEnumType()) {
429 return dp.getOptionValue();
431 if (dp.isNumberType()) {
432 // For number datapoints that are only used depending on the value of other datapoints,
433 // the CCU may return invalid (out of range) values if the datapoint currently is not in use.
434 // Make sure to not invalidate the whole configuration by returning the datapoint's default
435 // value in that case.
436 final boolean minValid, maxValid;
437 if (dp.isFloatType()) {
438 Double numValue = dp.getDoubleValue();
439 minValid = dp.getMinValue() == null || numValue >= dp.getMinValue().doubleValue();
440 maxValid = dp.getMaxValue() == null || numValue <= dp.getMaxValue().doubleValue();
442 Integer numValue = dp.getIntegerValue();
443 minValid = dp.getMinValue() == null || numValue >= dp.getMinValue().intValue();
444 maxValid = dp.getMaxValue() == null || numValue <= dp.getMaxValue().intValue();
446 if (minValid && maxValid) {
447 return dp.getValue();
449 logger.warn("Value for datapoint {} is outside of valid range, using default value for config.", dp);
450 return dp.getDefaultValue();
452 return dp.getValue();
456 * Converts the value of the datapoint to a State, updates the channel and also sets the thing status if necessary.
458 private void updateChannelState(final HmDatapoint dp, Channel channel)
459 throws IOException, GatewayNotAvailableException, ConverterException {
460 if (dp.isTrigger()) {
461 final Object value = dp.getValue();
462 if (value != null && !value.equals(dp.getPreviousValue())) {
463 triggerChannel(channel.getUID(), value.toString());
465 } else if (isLinked(channel)) {
466 loadHomematicChannelValues(dp.getChannel());
468 TypeConverter<?> converter = ConverterFactory.createConverter(channel.getAcceptedItemType());
469 State state = converter.convertFromBinding(dp);
471 updateState(channel.getUID(), state);
473 logger.debug("Failed to get converted state from datapoint '{}'", dp.getName());
479 * Loads all values for the given Homematic channel if it is not initialized.
481 private void loadHomematicChannelValues(HmChannel hmChannel) throws GatewayNotAvailableException, IOException {
482 if (!hmChannel.isInitialized()) {
483 synchronized (this) {
484 if (!hmChannel.isInitialized()) {
486 getHomematicGateway().loadChannelValues(hmChannel);
487 } catch (IOException ex) {
488 if (hmChannel.getDevice().isOffline()) {
489 logger.warn("Device '{}' is OFFLINE, can't update channel '{}'",
490 hmChannel.getDevice().getAddress(), hmChannel.getNumber());
501 * Updates the thing status based on device status.
503 private void updateStatus(HmDevice device) throws GatewayNotAvailableException, IOException {
504 loadHomematicChannelValues(device.getChannel(0));
506 ThingStatus oldStatus = thing.getStatus();
507 if (oldStatus == ThingStatus.UNINITIALIZED) {
510 ThingStatus newStatus = ThingStatus.ONLINE;
511 ThingStatusDetail newDetail = ThingStatusDetail.NONE;
513 if ((getBridge() != null) && (getBridge().getStatus() == ThingStatus.OFFLINE)) {
514 newStatus = ThingStatus.OFFLINE;
515 newDetail = ThingStatusDetail.BRIDGE_OFFLINE;
516 } else if (device.isFirmwareUpdating()) {
517 newStatus = ThingStatus.OFFLINE;
518 newDetail = ThingStatusDetail.FIRMWARE_UPDATING;
519 } else if (device.isUnreach()) {
520 newStatus = ThingStatus.OFFLINE;
521 newDetail = ThingStatusDetail.COMMUNICATION_ERROR;
522 } else if (device.isConfigPending() || device.isUpdatePending()) {
523 newDetail = ThingStatusDetail.CONFIGURATION_PENDING;
526 if (thing.getStatus() != newStatus || thing.getStatusInfo().getStatusDetail() != newDetail) {
527 updateStatus(newStatus, newDetail);
529 if (oldStatus == ThingStatus.OFFLINE && newStatus == ThingStatus.ONLINE) {
535 * Returns true, if the channel is linked at least to one item.
537 private boolean isLinked(Channel channel) {
538 return channel != null && super.isLinked(channel.getUID().getId());
542 * Returns the channel config for the given datapoint.
544 protected HmDatapointConfig getChannelConfig(HmDatapoint dp) {
545 ChannelUID channelUid = UidUtils.generateChannelUID(dp, getThing().getUID());
546 Channel channel = getThing().getChannel(channelUid.getId());
547 return channel != null ? getChannelConfig(channel, dp) : new HmDatapointConfig();
551 * Returns the config for a channel.
553 private HmDatapointConfig getChannelConfig(Channel channel, HmDatapoint dp) {
554 return channel.getConfiguration().as(HmDatapointConfig.class);
558 * Returns the Homematic gateway if the bridge is available.
560 private HomematicGateway getHomematicGateway() throws GatewayNotAvailableException {
561 final Bridge bridge = getBridge();
562 if (bridge != null) {
563 HomematicBridgeHandler bridgeHandler = (HomematicBridgeHandler) bridge.getHandler();
564 if (bridgeHandler != null && bridgeHandler.getGateway() != null) {
565 return bridgeHandler.getGateway();
569 throw new GatewayNotAvailableException("HomematicGateway not yet available!");
573 public void handleConfigurationUpdate(Map<String, Object> configurationParameters)
574 throws ConfigValidationException {
575 super.handleConfigurationUpdate(configurationParameters);
578 HomematicGateway gateway = getHomematicGateway();
579 HmDevice device = gateway.getDevice(UidUtils.getHomematicAddress(getThing()));
581 for (Entry<String, Object> configurationParameter : configurationParameters.entrySet()) {
582 String key = configurationParameter.getKey();
583 Object newValue = configurationParameter.getValue();
585 if (key.startsWith("HMP_")) {
586 key = key.substring(4);
587 int sepPos = key.indexOf("_");
588 Integer channelNumber = Integer.valueOf(key.substring(0, sepPos));
589 String dpName = key.substring(sepPos + 1);
591 HmDatapointInfo dpInfo = new HmDatapointInfo(device.getAddress(), HmParamsetType.MASTER,
592 channelNumber, dpName);
593 HmDatapoint dp = device.getChannel(channelNumber).getDatapoint(dpInfo);
597 if (newValue != null) {
598 if (newValue instanceof BigDecimal decimal) {
599 if (dp.isIntegerType()) {
600 newValue = decimal.intValue();
601 } else if (dp.isFloatType()) {
602 newValue = decimal.doubleValue();
604 } else if (newValue instanceof String string && dp.isEnumType()) {
605 newValue = dp.getOptionIndex(string);
607 if (!Objects.equals(dp.getValue(), newValue)) {
608 sendDatapoint(dp, new HmDatapointConfig(), newValue);
611 } catch (IOException ex) {
612 logger.error("Error setting thing property {}: {}", dpInfo, ex.getMessage());
615 logger.error("Can't find datapoint for thing property {}", dpInfo);
619 gateway.triggerDeviceValuesReload(device);
620 } catch (HomematicClientException | GatewayNotAvailableException ex) {
621 logger.error("Error setting thing properties: {}", ex.getMessage(), ex);
625 @SuppressWarnings("null")
627 public synchronized void handleRemoval() {
629 final ThingHandler handler;
631 if ((bridge = getBridge()) == null || (handler = bridge.getHandler()) == null) {
632 super.handleRemoval();
636 final HomematicConfig config = bridge.getConfiguration().as(HomematicConfig.class);
637 final boolean factoryResetOnDeletion = config.isFactoryResetOnDeletion();
638 final boolean unpairOnDeletion = factoryResetOnDeletion || config.isUnpairOnDeletion();
640 if (unpairOnDeletion) {
641 deviceDeletionPending = true;
642 ((HomematicBridgeHandler) handler).deleteFromGateway(UidUtils.getHomematicAddress(thing),
643 factoryResetOnDeletion, false, true);
645 super.handleRemoval();
650 * Called by the bridgeHandler when this device has been removed from the gateway.
652 public synchronized void deviceRemoved() {
653 deviceDeletionPending = false;
654 if (getThing().getStatus() == ThingStatus.REMOVING) {
655 // thing removal was initiated
656 updateStatus(ThingStatus.REMOVED);
658 // device removal was initiated on homematic side, thing is not removed
659 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.GONE);
664 * Called by the bridgeHandler when the device for this thing has been added to the gateway.
665 * This is used to reconnect a device that was previously unpaired.
667 * @param device The device that has been added to the gateway
669 public void deviceLoaded(HmDevice device) {
671 updateStatus(device);
672 } catch (GatewayNotAvailableException ex) {
674 } catch (IOException ex) {
675 logger.warn("Could not reinitialize the device '{}': {}", device.getAddress(), ex.getMessage(), ex);
680 * Returns whether the device deletion is pending.
682 * @return true, if the deletion of this device on its gateway has been triggered but has not yet completed
684 public synchronized boolean isDeletionPending() {
685 return deviceDeletionPending;