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.rfxcom.internal.handler;
15 import static org.openhab.binding.rfxcom.internal.RFXComBindingConstants.*;
18 import java.util.concurrent.ConcurrentHashMap;
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.openhab.binding.rfxcom.internal.DeviceMessageListener;
22 import org.openhab.binding.rfxcom.internal.RFXComBindingConstants;
23 import org.openhab.binding.rfxcom.internal.config.RFXComDeviceConfiguration;
24 import org.openhab.binding.rfxcom.internal.config.RFXComGenericDeviceConfiguration;
25 import org.openhab.binding.rfxcom.internal.exceptions.RFXComException;
26 import org.openhab.binding.rfxcom.internal.exceptions.RFXComInvalidParameterException;
27 import org.openhab.binding.rfxcom.internal.exceptions.RFXComInvalidStateException;
28 import org.openhab.binding.rfxcom.internal.exceptions.RFXComMessageNotImplementedException;
29 import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedChannelException;
30 import org.openhab.binding.rfxcom.internal.messages.RFXComBaseMessage.PacketType;
31 import org.openhab.binding.rfxcom.internal.messages.RFXComDeviceMessage;
32 import org.openhab.binding.rfxcom.internal.messages.RFXComMessage;
33 import org.openhab.binding.rfxcom.internal.messages.RFXComMessageFactory;
34 import org.openhab.binding.rfxcom.internal.messages.RFXComMessageFactoryImpl;
35 import org.openhab.core.library.types.DecimalType;
36 import org.openhab.core.library.types.OnOffType;
37 import org.openhab.core.thing.Bridge;
38 import org.openhab.core.thing.Channel;
39 import org.openhab.core.thing.ChannelUID;
40 import org.openhab.core.thing.Thing;
41 import org.openhab.core.thing.ThingStatus;
42 import org.openhab.core.thing.ThingStatusDetail;
43 import org.openhab.core.thing.ThingStatusInfo;
44 import org.openhab.core.thing.ThingUID;
45 import org.openhab.core.thing.binding.BaseThingHandler;
46 import org.openhab.core.thing.binding.ThingHandler;
47 import org.openhab.core.types.Command;
48 import org.openhab.core.types.RefreshType;
49 import org.openhab.core.types.State;
50 import org.openhab.core.types.Type;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
55 * The {@link RFXComHandler} is responsible for handling commands, which are
56 * sent to one of the channels.
58 * @author Pauli Anttila - Initial contribution
60 public class RFXComHandler extends BaseThingHandler implements DeviceMessageListener, DeviceState {
61 private static final int LOW_BATTERY_LEVEL = 1;
63 private final Logger logger = LoggerFactory.getLogger(RFXComHandler.class);
65 private final Map<String, Type> stateMap = new ConcurrentHashMap<>();
67 private RFXComBridgeHandler bridgeHandler;
69 private Class<? extends RFXComDeviceConfiguration> configType;
70 private RFXComDeviceConfiguration config;
72 private RFXComMessageFactory messageFactory;
74 public RFXComHandler(@NonNull Thing thing) {
75 this(thing, RFXComMessageFactoryImpl.INSTANCE);
78 public RFXComHandler(@NonNull Thing thing, RFXComMessageFactory messageFactory) {
80 this.messageFactory = messageFactory;
82 configType = RFXComBindingConstants.THING_TYPE_UID_CONFIGURATION_CLASS_MAP.getOrDefault(thing.getThingTypeUID(),
83 RFXComGenericDeviceConfiguration.class);
87 public void handleCommand(ChannelUID channelUID, Command command) {
88 logger.debug("Received channel: {}, command: {}", channelUID, command);
90 if (bridgeHandler != null) {
91 if (command instanceof RefreshType) {
92 logger.trace("Received unsupported Refresh command");
95 PacketType packetType = RFXComMessageFactoryImpl
96 .convertPacketType(getThing().getThingTypeUID().getId().toUpperCase());
98 RFXComMessage msg = messageFactory.createMessage(packetType, config, channelUID, command);
100 bridgeHandler.sendMessage(msg);
101 } catch (RFXComMessageNotImplementedException e) {
102 logger.error("Message not supported", e);
103 } catch (RFXComUnsupportedChannelException e) {
104 logger.error("Channel not supported", e);
105 } catch (RFXComInvalidStateException e) {
106 logger.error("Invalid state supplied for channel", e);
107 } catch (RFXComException e) {
108 logger.error("Transmitting error", e);
115 public void initialize() {
116 logger.debug("Initializing thing {}", getThing().getUID());
118 Bridge bridge = getBridge();
120 if (bridge == null) {
121 initializeBridge(null, null);
123 initializeBridge(bridge.getHandler(), bridge.getStatus());
130 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
131 logger.debug("bridgeStatusChanged {} for thing {}", bridgeStatusInfo, getThing().getUID());
133 Bridge bridge = getBridge();
135 if (bridge == null) {
136 initializeBridge(null, bridgeStatusInfo.getStatus());
138 initializeBridge(bridge.getHandler(), bridgeStatusInfo.getStatus());
142 private void initializeBridge(ThingHandler thingHandler, ThingStatus bridgeStatus) {
143 logger.debug("initializeBridge {} for thing {}", bridgeStatus, getThing().getUID());
146 config = getConfigAs(configType);
147 config.parseAndValidate();
148 if (thingHandler != null && bridgeStatus != null) {
149 bridgeHandler = (RFXComBridgeHandler) thingHandler;
150 bridgeHandler.registerDeviceStatusListener(this);
152 if (bridgeStatus == ThingStatus.ONLINE) {
153 updateStatus(ThingStatus.ONLINE);
155 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
158 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
160 } catch (RFXComInvalidParameterException e) {
161 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
166 public void dispose() {
167 logger.debug("Thing {} disposed.", getThing().getUID());
168 if (bridgeHandler != null) {
169 bridgeHandler.unregisterDeviceStatusListener(this);
171 bridgeHandler = null;
176 public void onDeviceMessageReceived(ThingUID bridge, RFXComDeviceMessage message) {
178 if (config.matchesMessage(message)) {
179 String receivedId = PACKET_TYPE_THING_TYPE_UID_MAP.get(message.getPacketType()).getId();
180 if (receivedId.equals(getThing().getThingTypeUID().getId())) {
181 logger.debug("Message from bridge [{}] matches thing [{}] message: {}", bridge,
182 getThing().getUID().toString(), message);
183 updateStatus(ThingStatus.ONLINE);
185 for (Channel channel : getThing().getChannels()) {
186 ChannelUID uid = channel.getUID();
187 String channelId = uid.getId();
191 case CHANNEL_COMMAND:
192 case CHANNEL_CHIME_SOUND:
194 postNullableCommand(uid, message.convertToCommand(channelId, config, this));
197 case CHANNEL_LOW_BATTERY:
198 updateNullableState(uid,
199 isLowBattery(message.convertToState(CHANNEL_BATTERY_LEVEL, config, this)));
203 updateNullableState(uid, message.convertToState(channelId, config, this));
206 } catch (RFXComInvalidStateException e) {
207 logger.trace("{} not configured for {}", channelId, message);
208 } catch (RFXComUnsupportedChannelException e) {
209 logger.trace("{} does not handle {}", channelId, message);
214 } catch (Exception e) {
215 logger.error("Error occurred during message receiving", e);
219 private void updateNullableState(ChannelUID uid, State state) {
224 stateMap.put(uid.getId(), state);
225 updateState(uid, state);
228 private void postNullableCommand(ChannelUID uid, Command command) {
229 if (command == null) {
233 stateMap.put(uid.getId(), command);
234 postCommand(uid, command);
238 public Type getLastState(String channelId) {
239 return stateMap.get(channelId);
243 * Check if battery level is below low battery threshold level.
245 * @param batteryLevel Internal battery level
248 private State isLowBattery(State batteryLevel) {
249 int level = ((DecimalType) batteryLevel).intValue();
250 if (level <= LOW_BATTERY_LEVEL) {
253 return OnOffType.OFF;