]> git.basschouten.com Git - openhab-addons.git/blob
3e52c257e90d74739cc4b7bbbcbf5adbf686542a
[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.enocean.internal.handler;
14
15 import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*;
16
17 import java.util.Arrays;
18 import java.util.HashSet;
19 import java.util.Set;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
22 import java.util.function.Predicate;
23
24 import org.eclipse.jdt.annotation.NonNull;
25 import org.openhab.binding.enocean.internal.config.EnOceanActuatorConfig;
26 import org.openhab.binding.enocean.internal.config.EnOceanChannelRockerSwitchConfigBase.SwitchMode;
27 import org.openhab.binding.enocean.internal.config.EnOceanChannelRockerSwitchListenerConfig;
28 import org.openhab.binding.enocean.internal.config.EnOceanChannelVirtualRockerSwitchConfig;
29 import org.openhab.binding.enocean.internal.eep.EEP;
30 import org.openhab.binding.enocean.internal.eep.EEPFactory;
31 import org.openhab.binding.enocean.internal.eep.EEPType;
32 import org.openhab.binding.enocean.internal.messages.BasePacket;
33 import org.openhab.core.library.types.OnOffType;
34 import org.openhab.core.library.types.StopMoveType;
35 import org.openhab.core.library.types.StringType;
36 import org.openhab.core.library.types.UpDownType;
37 import org.openhab.core.thing.Channel;
38 import org.openhab.core.thing.ChannelUID;
39 import org.openhab.core.thing.CommonTriggerEvents;
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.ThingTypeUID;
44 import org.openhab.core.thing.link.ItemChannelLinkRegistry;
45 import org.openhab.core.thing.type.ChannelTypeUID;
46 import org.openhab.core.types.Command;
47 import org.openhab.core.types.RefreshType;
48 import org.openhab.core.util.HexUtils;
49
50 /**
51  *
52  * @author Daniel Weber - Initial contribution
53  *         This class defines base functionality for sending eep messages. This class extends EnOceanBaseSensorHandler
54  *         class as most actuator things send status or response messages, too.
55  */
56 public class EnOceanClassicDeviceHandler extends EnOceanBaseActuatorHandler {
57
58     // List of thing types which support sending of eep messages
59     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = new HashSet<>(
60             Arrays.asList(THING_TYPE_CLASSICDEVICE));
61
62     private StringType lastTriggerEvent = StringType.valueOf(CommonTriggerEvents.DIR1_PRESSED);
63     ScheduledFuture<?> releaseFuture = null;
64
65     public EnOceanClassicDeviceHandler(Thing thing, ItemChannelLinkRegistry itemChannelLinkRegistry) {
66         super(thing, itemChannelLinkRegistry);
67     }
68
69     @Override
70     void initializeConfig() {
71         super.initializeConfig();
72         ((EnOceanActuatorConfig) config).broadcastMessages = true;
73         ((EnOceanActuatorConfig) config).enoceanId = EMPTYENOCEANID;
74     }
75
76     @Override
77     public long getSenderIdToListenTo() {
78         return 0;
79     }
80
81     @Override
82     public void channelLinked(@NonNull ChannelUID channelUID) {
83         super.channelLinked(channelUID);
84
85         // if linked channel is a listening channel => put listener
86         Channel channel = getThing().getChannel(channelUID);
87         addListener(channel);
88     }
89
90     @Override
91     public void thingUpdated(Thing thing) {
92         super.thingUpdated(thing);
93
94         // it seems that there does not exist a channel update callback
95         // => remove all listeners and add them again
96         getBridgeHandler().removePacketListener(this);
97
98         this.getThing().getChannels().forEach(c -> {
99             if (isLinked(c.getUID()) && !addListener(c)) {
100                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Wrong channel configuration");
101             }
102         });
103     }
104
105     @Override
106     public void channelUnlinked(@NonNull ChannelUID channelUID) {
107         super.channelUnlinked(channelUID);
108
109         // if unlinked channel is listening channel => remove listener
110         Channel channel = getThing().getChannel(channelUID);
111         removeListener(channel);
112     }
113
114     protected boolean addListener(Channel channel) {
115         if (channel == null) {
116             return true;
117         }
118
119         ChannelTypeUID channelTypeUID = channel.getChannelTypeUID();
120         String id = channelTypeUID == null ? "" : channelTypeUID.getId();
121
122         if (id.startsWith(CHANNEL_ROCKERSWITCHLISTENER_START)) {
123             EnOceanChannelRockerSwitchListenerConfig config = channel.getConfiguration()
124                     .as(EnOceanChannelRockerSwitchListenerConfig.class);
125             try {
126                 getBridgeHandler().addPacketListener(this, Long.parseLong(config.enoceanId, 16));
127                 return true;
128             } catch (NumberFormatException e) {
129             }
130
131             return false;
132         }
133         return true;
134     }
135
136     protected void removeListener(Channel channel) {
137         if (channel == null) {
138             return;
139         }
140
141         ChannelTypeUID channelTypeUID = channel.getChannelTypeUID();
142         String id = channelTypeUID == null ? "" : channelTypeUID.getId();
143
144         if (id.startsWith(CHANNEL_ROCKERSWITCHLISTENER_START)) {
145             EnOceanChannelRockerSwitchListenerConfig config = channel.getConfiguration()
146                     .as(EnOceanChannelRockerSwitchListenerConfig.class);
147             try {
148                 getBridgeHandler().removePacketListener(this, Long.parseLong(config.enoceanId, 16));
149             } catch (NumberFormatException e) {
150             }
151         }
152     }
153
154     @Override
155     protected Predicate<Channel> channelFilter(EEPType eepType, byte[] senderId) {
156         return c -> {
157             ChannelTypeUID channelTypeUID = c.getChannelTypeUID();
158             String id = channelTypeUID == null ? "" : channelTypeUID.getId();
159
160             return id.startsWith(CHANNEL_ROCKERSWITCHLISTENER_START)
161                     && c.getConfiguration().as(EnOceanChannelRockerSwitchListenerConfig.class).enoceanId
162                             .equalsIgnoreCase(HexUtils.bytesToHex(senderId));
163         };
164     }
165
166     @SuppressWarnings("unlikely-arg-type")
167     private StringType convertToReleasedCommand(StringType command) {
168         return command.equals(CommonTriggerEvents.DIR1_PRESSED) ? StringType.valueOf(CommonTriggerEvents.DIR1_RELEASED)
169                 : StringType.valueOf(CommonTriggerEvents.DIR2_RELEASED);
170     }
171
172     private StringType convertToPressedCommand(Command command, SwitchMode switchMode) {
173         if (command instanceof StringType) {
174             return (StringType) command;
175         } else if (command instanceof OnOffType) {
176             switch (switchMode) {
177                 case RockerSwitch:
178                     return (command == OnOffType.ON) ? StringType.valueOf(CommonTriggerEvents.DIR1_PRESSED)
179                             : StringType.valueOf(CommonTriggerEvents.DIR2_PRESSED);
180                 case ToggleDir1:
181                     return StringType.valueOf(CommonTriggerEvents.DIR1_PRESSED);
182                 case ToggleDir2:
183                     return StringType.valueOf(CommonTriggerEvents.DIR2_PRESSED);
184                 default:
185                     return null;
186             }
187         } else if (command instanceof UpDownType) {
188             switch (switchMode) {
189                 case RockerSwitch:
190                     return (command == UpDownType.UP) ? StringType.valueOf(CommonTriggerEvents.DIR1_PRESSED)
191                             : StringType.valueOf(CommonTriggerEvents.DIR2_PRESSED);
192                 case ToggleDir1:
193                     return StringType.valueOf(CommonTriggerEvents.DIR1_PRESSED);
194                 case ToggleDir2:
195                     return StringType.valueOf(CommonTriggerEvents.DIR2_PRESSED);
196                 default:
197                     return null;
198             }
199         } else if (command instanceof StopMoveType) {
200             if (command == StopMoveType.STOP) {
201                 return lastTriggerEvent;
202             }
203         }
204
205         return null;
206     }
207
208     @Override
209     public void handleCommand(@NonNull ChannelUID channelUID, @NonNull Command command) {
210         // We must have a valid sendingEEPType and sender id to send commands
211         if (sendingEEPType == null || senderId == null || command == RefreshType.REFRESH) {
212             return;
213         }
214
215         String channelId = channelUID.getId();
216         Channel channel = getThing().getChannel(channelUID);
217         if (channel == null) {
218             return;
219         }
220
221         ChannelTypeUID channelTypeUID = channel.getChannelTypeUID();
222         String channelTypeId = (channelTypeUID != null) ? channelTypeUID.getId() : "";
223         if (channelTypeId.contains("Listener")) {
224             return;
225         }
226
227         EnOceanChannelVirtualRockerSwitchConfig channelConfig = channel.getConfiguration()
228                 .as(EnOceanChannelVirtualRockerSwitchConfig.class);
229
230         StringType result = convertToPressedCommand(command, channelConfig.getSwitchMode());
231
232         if (result != null) {
233             lastTriggerEvent = result;
234
235             EEP eep = EEPFactory.createEEP(sendingEEPType);
236             if (eep.setSenderId(senderId).setDestinationId(destinationId).convertFromCommand(channelId, channelTypeId,
237                     result, id -> this.getCurrentState(id), channel.getConfiguration()).hasData()) {
238                 BasePacket press = eep.setSuppressRepeating(getConfiguration().suppressRepeating).getERP1Message();
239
240                 getBridgeHandler().sendMessage(press, null);
241
242                 if (channelConfig.duration > 0) {
243                     releaseFuture = scheduler.schedule(() -> {
244                         if (eep.convertFromCommand(channelId, channelTypeId, convertToReleasedCommand(lastTriggerEvent),
245                                 id -> this.getCurrentState(id), channel.getConfiguration()).hasData()) {
246                             BasePacket release = eep.getERP1Message();
247                             getBridgeHandler().sendMessage(release, null);
248                         }
249                     }, channelConfig.duration, TimeUnit.MILLISECONDS);
250                 }
251             }
252         }
253     }
254
255     @Override
256     public void handleRemoval() {
257         if (releaseFuture != null && !releaseFuture.isDone()) {
258             releaseFuture.cancel(true);
259         }
260
261         releaseFuture = null;
262         super.handleRemoval();
263     }
264 }