2 * Copyright (c) 2010-2020 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.config.RFXComDeviceConfiguration;
23 import org.openhab.binding.rfxcom.internal.exceptions.RFXComException;
24 import org.openhab.binding.rfxcom.internal.exceptions.RFXComMessageNotImplementedException;
25 import org.openhab.binding.rfxcom.internal.messages.RFXComBaseMessage.PacketType;
26 import org.openhab.binding.rfxcom.internal.messages.RFXComDeviceMessage;
27 import org.openhab.binding.rfxcom.internal.messages.RFXComMessage;
28 import org.openhab.binding.rfxcom.internal.messages.RFXComMessageFactory;
29 import org.openhab.core.library.types.DecimalType;
30 import org.openhab.core.library.types.OnOffType;
31 import org.openhab.core.thing.Channel;
32 import org.openhab.core.thing.ChannelUID;
33 import org.openhab.core.thing.Thing;
34 import org.openhab.core.thing.ThingStatus;
35 import org.openhab.core.thing.ThingStatusDetail;
36 import org.openhab.core.thing.ThingStatusInfo;
37 import org.openhab.core.thing.ThingUID;
38 import org.openhab.core.thing.binding.BaseThingHandler;
39 import org.openhab.core.thing.binding.ThingHandler;
40 import org.openhab.core.types.Command;
41 import org.openhab.core.types.RefreshType;
42 import org.openhab.core.types.State;
43 import org.openhab.core.types.Type;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 * The {@link RFXComHandler} is responsible for handling commands, which are
49 * sent to one of the channels.
51 * @author Pauli Anttila - Initial contribution
53 public class RFXComHandler extends BaseThingHandler implements DeviceMessageListener, DeviceState {
54 private static final int LOW_BATTERY_LEVEL = 1;
56 private final Logger logger = LoggerFactory.getLogger(RFXComHandler.class);
58 private final Map<String, Type> stateMap = new ConcurrentHashMap<>();
60 private RFXComBridgeHandler bridgeHandler;
61 private RFXComDeviceConfiguration config;
63 public RFXComHandler(@NonNull Thing thing) {
68 public void handleCommand(ChannelUID channelUID, Command command) {
69 logger.debug("Received channel: {}, command: {}", channelUID, command);
71 if (bridgeHandler != null) {
72 if (command instanceof RefreshType) {
73 logger.trace("Received unsupported Refresh command");
76 PacketType packetType = RFXComMessageFactory
77 .convertPacketType(getThing().getThingTypeUID().getId().toUpperCase());
79 RFXComMessage msg = RFXComMessageFactory.createMessage(packetType);
81 msg.setConfig(config);
82 msg.convertFromState(channelUID.getId(), command);
84 bridgeHandler.sendMessage(msg);
85 } catch (RFXComMessageNotImplementedException e) {
86 logger.error("Message not supported", e);
87 } catch (RFXComException e) {
88 logger.error("Transmitting error", e);
95 public void initialize() {
96 logger.debug("Initializing thing {}", getThing().getUID());
97 initializeBridge((getBridge() == null) ? null : getBridge().getHandler(),
98 (getBridge() == null) ? null : getBridge().getStatus());
104 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
105 logger.debug("bridgeStatusChanged {} for thing {}", bridgeStatusInfo, getThing().getUID());
106 initializeBridge((getBridge() == null) ? null : getBridge().getHandler(), bridgeStatusInfo.getStatus());
109 private void initializeBridge(ThingHandler thingHandler, ThingStatus bridgeStatus) {
110 logger.debug("initializeBridge {} for thing {}", bridgeStatus, getThing().getUID());
112 config = getConfigAs(RFXComDeviceConfiguration.class);
113 if (config.deviceId == null || config.subType == null) {
114 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
115 "RFXCOM device missing deviceId or subType");
116 } else if (thingHandler != null && bridgeStatus != null) {
117 bridgeHandler = (RFXComBridgeHandler) thingHandler;
118 bridgeHandler.registerDeviceStatusListener(this);
120 if (bridgeStatus == ThingStatus.ONLINE) {
121 updateStatus(ThingStatus.ONLINE);
123 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
126 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
131 public void dispose() {
132 logger.debug("Thing {} disposed.", getThing().getUID());
133 if (bridgeHandler != null) {
134 bridgeHandler.unregisterDeviceStatusListener(this);
136 bridgeHandler = null;
141 public void onDeviceMessageReceived(ThingUID bridge, RFXComDeviceMessage message) {
143 String id = message.getDeviceId();
144 if (config.deviceId.equals(id)) {
145 String receivedId = PACKET_TYPE_THING_TYPE_UID_MAP.get(message.getPacketType()).getId();
146 logger.debug("Received message from bridge: {} message: {}", bridge, message);
148 if (receivedId.equals(getThing().getThingTypeUID().getId())) {
149 updateStatus(ThingStatus.ONLINE);
151 for (Channel channel : getThing().getChannels()) {
152 ChannelUID uid = channel.getUID();
153 String channelId = uid.getId();
157 case CHANNEL_COMMAND:
158 case CHANNEL_CHIME_SOUND:
160 postNullableCommand(uid, message.convertToCommand(channelId, this));
163 case CHANNEL_LOW_BATTERY:
164 updateNullableState(uid,
165 isLowBattery(message.convertToState(CHANNEL_BATTERY_LEVEL, this)));
169 updateNullableState(uid, message.convertToState(channelId, this));
172 } catch (RFXComException e) {
173 logger.trace("{} does not handle {}", channelId, message);
178 } catch (Exception e) {
179 logger.error("Error occurred during message receiving", e);
183 private void updateNullableState(ChannelUID uid, State state) {
188 stateMap.put(uid.getId(), state);
189 updateState(uid, state);
192 private void postNullableCommand(ChannelUID uid, Command command) {
193 if (command == null) {
197 stateMap.put(uid.getId(), command);
198 postCommand(uid, command);
202 public Type getLastState(String channelId) {
203 return stateMap.get(channelId);
207 * Check if battery level is below low battery threshold level.
209 * @param batteryLevel Internal battery level
212 private State isLowBattery(State batteryLevel) {
213 int level = ((DecimalType) batteryLevel).intValue();
214 if (level <= LOW_BATTERY_LEVEL) {
217 return OnOffType.OFF;