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