]> git.basschouten.com Git - openhab-addons.git/blob
ffabbd4b7a70cf5d242215ffc9291a64df6668c5
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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, config, channelUID, command);
99
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);
109                 }
110             }
111         }
112     }
113
114     @Override
115     public void initialize() {
116         logger.debug("Initializing thing {}", getThing().getUID());
117
118         Bridge bridge = getBridge();
119
120         if (bridge == null) {
121             initializeBridge(null, null);
122         } else {
123             initializeBridge(bridge.getHandler(), bridge.getStatus());
124         }
125
126         stateMap.clear();
127     }
128
129     @Override
130     public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
131         logger.debug("bridgeStatusChanged {} for thing {}", bridgeStatusInfo, getThing().getUID());
132
133         Bridge bridge = getBridge();
134
135         if (bridge == null) {
136             initializeBridge(null, bridgeStatusInfo.getStatus());
137         } else {
138             initializeBridge(bridge.getHandler(), bridgeStatusInfo.getStatus());
139         }
140     }
141
142     private void initializeBridge(ThingHandler thingHandler, ThingStatus bridgeStatus) {
143         logger.debug("initializeBridge {} for thing {}", bridgeStatus, getThing().getUID());
144
145         try {
146             config = getConfigAs(configType);
147             config.parseAndValidate();
148             if (thingHandler != null && bridgeStatus != null) {
149                 bridgeHandler = (RFXComBridgeHandler) thingHandler;
150                 bridgeHandler.registerDeviceStatusListener(this);
151
152                 if (bridgeStatus == ThingStatus.ONLINE) {
153                     updateStatus(ThingStatus.ONLINE);
154                 } else {
155                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
156                 }
157             } else {
158                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
159             }
160         } catch (RFXComInvalidParameterException e) {
161             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
162         }
163     }
164
165     @Override
166     public void dispose() {
167         logger.debug("Thing {} disposed.", getThing().getUID());
168         if (bridgeHandler != null) {
169             bridgeHandler.unregisterDeviceStatusListener(this);
170         }
171         bridgeHandler = null;
172         super.dispose();
173     }
174
175     @Override
176     public void onDeviceMessageReceived(ThingUID bridge, RFXComDeviceMessage message) {
177         try {
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);
184
185                     for (Channel channel : getThing().getChannels()) {
186                         ChannelUID uid = channel.getUID();
187                         String channelId = uid.getId();
188
189                         try {
190                             switch (channelId) {
191                                 case CHANNEL_COMMAND:
192                                 case CHANNEL_CHIME_SOUND:
193                                 case CHANNEL_MOOD:
194                                     postNullableCommand(uid, message.convertToCommand(channelId, config, this));
195                                     break;
196
197                                 case CHANNEL_LOW_BATTERY:
198                                     updateNullableState(uid,
199                                             isLowBattery(message.convertToState(CHANNEL_BATTERY_LEVEL, config, this)));
200                                     break;
201
202                                 default:
203                                     updateNullableState(uid, message.convertToState(channelId, config, this));
204                                     break;
205                             }
206                         } catch (RFXComInvalidStateException e) {
207                             logger.trace("{} not configured for {}", channelId, message);
208                         } catch (RFXComUnsupportedChannelException e) {
209                             logger.trace("{} does not handle {}", channelId, message);
210                         }
211                     }
212                 }
213             }
214         } catch (Exception e) {
215             logger.error("Error occurred during message receiving", e);
216         }
217     }
218
219     private void updateNullableState(ChannelUID uid, State state) {
220         if (state == null) {
221             return;
222         }
223
224         stateMap.put(uid.getId(), state);
225         updateState(uid, state);
226     }
227
228     private void postNullableCommand(ChannelUID uid, Command command) {
229         if (command == null) {
230             return;
231         }
232
233         stateMap.put(uid.getId(), command);
234         postCommand(uid, command);
235     }
236
237     @Override
238     public Type getLastState(String channelId) {
239         return stateMap.get(channelId);
240     }
241
242     /**
243      * Check if battery level is below low battery threshold level.
244      *
245      * @param batteryLevel Internal battery level
246      * @return OnOffType
247      */
248     private State isLowBattery(State batteryLevel) {
249         int level = ((DecimalType) batteryLevel).intValue();
250         if (level <= LOW_BATTERY_LEVEL) {
251             return OnOffType.ON;
252         } else {
253             return OnOffType.OFF;
254         }
255     }
256 }