]> git.basschouten.com Git - openhab-addons.git/blob
992015902859b35a9b506427ceaeba4ca4957d6d
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.binding.rfxcom.internal.handler;
14
15 import static org.openhab.binding.rfxcom.internal.RFXComBindingConstants.*;
16
17 import java.util.Map;
18 import java.util.concurrent.ConcurrentHashMap;
19
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;
53
54 /**
55  * The {@link RFXComHandler} is responsible for handling commands, which are
56  * sent to one of the channels.
57  *
58  * @author Pauli Anttila - Initial contribution
59  */
60 public class RFXComHandler extends BaseThingHandler implements DeviceMessageListener, DeviceState {
61     private static final int LOW_BATTERY_LEVEL = 1;
62
63     private final Logger logger = LoggerFactory.getLogger(RFXComHandler.class);
64
65     private final Map<String, Type> stateMap = new ConcurrentHashMap<>();
66
67     private RFXComBridgeHandler bridgeHandler;
68
69     private Class<? extends RFXComDeviceConfiguration> configType;
70     private RFXComDeviceConfiguration config;
71
72     private RFXComMessageFactory messageFactory;
73
74     public RFXComHandler(@NonNull Thing thing) {
75         this(thing, RFXComMessageFactoryImpl.INSTANCE);
76     }
77
78     public RFXComHandler(@NonNull Thing thing, RFXComMessageFactory messageFactory) {
79         super(thing);
80         this.messageFactory = messageFactory;
81
82         configType = RFXComBindingConstants.THING_TYPE_UID_CONFIGURATION_CLASS_MAP.getOrDefault(thing.getThingTypeUID(),
83                 RFXComGenericDeviceConfiguration.class);
84     }
85
86     @Override
87     public void handleCommand(ChannelUID channelUID, Command command) {
88         logger.debug("Received channel: {}, command: {}", channelUID, command);
89
90         if (bridgeHandler != null) {
91             if (command instanceof RefreshType) {
92                 logger.trace("Received unsupported Refresh command");
93             } else {
94                 try {
95                     PacketType packetType = RFXComMessageFactoryImpl
96                             .convertPacketType(getThing().getThingTypeUID().getId().toUpperCase());
97
98                     RFXComMessage msg = messageFactory.createMessage(packetType);
99
100                     msg.setConfig(config);
101                     msg.convertFromState(channelUID.getId(), command);
102
103                     bridgeHandler.sendMessage(msg);
104                 } catch (RFXComMessageNotImplementedException e) {
105                     logger.error("Message not supported", e);
106                 } catch (RFXComUnsupportedChannelException e) {
107                     logger.error("Channel not supported", e);
108                 } catch (RFXComInvalidStateException e) {
109                     logger.error("Invalid state supplied for channel", e);
110                 } catch (RFXComException e) {
111                     logger.error("Transmitting error", e);
112                 }
113             }
114         }
115     }
116
117     @Override
118     public void initialize() {
119         logger.debug("Initializing thing {}", getThing().getUID());
120
121         Bridge bridge = getBridge();
122
123         if (bridge == null) {
124             initializeBridge(null, null);
125         } else {
126             initializeBridge(bridge.getHandler(), bridge.getStatus());
127         }
128
129         stateMap.clear();
130     }
131
132     @Override
133     public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
134         logger.debug("bridgeStatusChanged {} for thing {}", bridgeStatusInfo, getThing().getUID());
135
136         Bridge bridge = getBridge();
137
138         if (bridge == null) {
139             initializeBridge(null, bridgeStatusInfo.getStatus());
140         } else {
141             initializeBridge(bridge.getHandler(), bridgeStatusInfo.getStatus());
142         }
143     }
144
145     private void initializeBridge(ThingHandler thingHandler, ThingStatus bridgeStatus) {
146         logger.debug("initializeBridge {} for thing {}", bridgeStatus, getThing().getUID());
147
148         try {
149             config = getConfigAs(configType);
150             config.parseAndValidate();
151             if (thingHandler != null && bridgeStatus != null) {
152                 bridgeHandler = (RFXComBridgeHandler) thingHandler;
153                 bridgeHandler.registerDeviceStatusListener(this);
154
155                 if (bridgeStatus == ThingStatus.ONLINE) {
156                     updateStatus(ThingStatus.ONLINE);
157                 } else {
158                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
159                 }
160             } else {
161                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
162             }
163         } catch (RFXComInvalidParameterException e) {
164             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
165         }
166     }
167
168     @Override
169     public void dispose() {
170         logger.debug("Thing {} disposed.", getThing().getUID());
171         if (bridgeHandler != null) {
172             bridgeHandler.unregisterDeviceStatusListener(this);
173         }
174         bridgeHandler = null;
175         super.dispose();
176     }
177
178     @Override
179     public void onDeviceMessageReceived(ThingUID bridge, RFXComDeviceMessage message) {
180         try {
181             if (config.matchesMessage(message)) {
182                 String receivedId = PACKET_TYPE_THING_TYPE_UID_MAP.get(message.getPacketType()).getId();
183                 logger.debug("Received message from bridge: {} message: {}", bridge, message);
184
185                 if (receivedId.equals(getThing().getThingTypeUID().getId())) {
186                     updateStatus(ThingStatus.ONLINE);
187
188                     for (Channel channel : getThing().getChannels()) {
189                         ChannelUID uid = channel.getUID();
190                         String channelId = uid.getId();
191
192                         try {
193                             switch (channelId) {
194                                 case CHANNEL_COMMAND:
195                                 case CHANNEL_CHIME_SOUND:
196                                 case CHANNEL_MOOD:
197                                     postNullableCommand(uid, message.convertToCommand(channelId, this));
198                                     break;
199
200                                 case CHANNEL_LOW_BATTERY:
201                                     updateNullableState(uid,
202                                             isLowBattery(message.convertToState(CHANNEL_BATTERY_LEVEL, this)));
203                                     break;
204
205                                 default:
206                                     updateNullableState(uid, message.convertToState(channelId, this));
207                                     break;
208                             }
209                         } catch (RFXComException e) {
210                             logger.trace("{} does not handle {}", channelId, message);
211                         }
212                     }
213                 }
214             }
215         } catch (Exception e) {
216             logger.error("Error occurred during message receiving", e);
217         }
218     }
219
220     private void updateNullableState(ChannelUID uid, State state) {
221         if (state == null) {
222             return;
223         }
224
225         stateMap.put(uid.getId(), state);
226         updateState(uid, state);
227     }
228
229     private void postNullableCommand(ChannelUID uid, Command command) {
230         if (command == null) {
231             return;
232         }
233
234         stateMap.put(uid.getId(), command);
235         postCommand(uid, command);
236     }
237
238     @Override
239     public Type getLastState(String channelId) {
240         return stateMap.get(channelId);
241     }
242
243     /**
244      * Check if battery level is below low battery threshold level.
245      *
246      * @param batteryLevel Internal battery level
247      * @return OnOffType
248      */
249     private State isLowBattery(State batteryLevel) {
250         int level = ((DecimalType) batteryLevel).intValue();
251         if (level <= LOW_BATTERY_LEVEL) {
252             return OnOffType.ON;
253         } else {
254             return OnOffType.OFF;
255         }
256     }
257 }