]> git.basschouten.com Git - openhab-addons.git/blob
bbf7244eae5f809bd340b5d60602f75110974f94
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.nikobus.internal.handler;
14
15 import static org.openhab.binding.nikobus.internal.NikobusBindingConstants.*;
16 import static org.openhab.binding.nikobus.internal.protocol.SwitchModuleGroup.*;
17
18 import java.util.ArrayList;
19 import java.util.Collections;
20 import java.util.List;
21 import java.util.concurrent.Future;
22 import java.util.concurrent.TimeUnit;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.nikobus.internal.protocol.NikobusCommand;
27 import org.openhab.binding.nikobus.internal.protocol.SwitchModuleGroup;
28 import org.openhab.binding.nikobus.internal.utils.Utils;
29 import org.openhab.core.common.AbstractUID;
30 import org.openhab.core.library.types.OnOffType;
31 import org.openhab.core.thing.Bridge;
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.ThingTypeUID;
37 import org.openhab.core.thing.ThingUID;
38 import org.openhab.core.thing.binding.ThingHandler;
39 import org.openhab.core.types.Command;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 /**
44  * The {@link NikobusPushButtonHandler} is responsible for handling Nikobus push buttons.
45  *
46  * @author Boris Krivonog - Initial contribution
47  */
48 @NonNullByDefault
49 public class NikobusPushButtonHandler extends NikobusBaseThingHandler {
50     private static class ImpactedModule {
51         private final ThingUID thingUID;
52         private final SwitchModuleGroup group;
53
54         ImpactedModule(ThingUID thingUID, SwitchModuleGroup group) {
55             this.thingUID = thingUID;
56             this.group = group;
57         }
58
59         public ThingUID getThingUID() {
60             return thingUID;
61         }
62
63         public SwitchModuleGroup getGroup() {
64             return group;
65         }
66
67         @Override
68         public String toString() {
69             return "'" + thingUID + "'-" + group;
70         }
71     }
72
73     private static class ImpactedModuleUID extends AbstractUID {
74         ImpactedModuleUID(String uid) {
75             super(uid);
76         }
77
78         String getThingTypeId() {
79             return getSegment(0);
80         }
81
82         String getThingId() {
83             return getSegment(1);
84         }
85
86         SwitchModuleGroup getGroup() {
87             if (getSegment(2).equals("1")) {
88                 return FIRST;
89             }
90             if (getSegment(2).equals("2")) {
91                 return SECOND;
92             }
93             throw new IllegalArgumentException("Unexpected group found " + getSegment(2));
94         }
95
96         @Override
97         protected int getMinimalNumberOfSegments() {
98             return 3;
99         }
100     }
101
102     private static final String END_OF_TRANSMISSION = "\r#E1";
103     private final Logger logger = LoggerFactory.getLogger(NikobusPushButtonHandler.class);
104     private final List<ImpactedModule> impactedModules = Collections.synchronizedList(new ArrayList<>());
105     private @Nullable Future<?> requestUpdateFuture;
106
107     public NikobusPushButtonHandler(Thing thing) {
108         super(thing);
109     }
110
111     @Override
112     public void initialize() {
113         super.initialize();
114
115         if (thing.getStatus() == ThingStatus.OFFLINE) {
116             return;
117         }
118
119         impactedModules.clear();
120
121         Object impactedModulesObject = getConfig().get(CONFIG_IMPACTED_MODULES);
122         if (impactedModulesObject != null) {
123             try {
124                 Bridge bridge = getBridge();
125                 if (bridge == null) {
126                     throw new IllegalArgumentException("Bridge does not exist!");
127                 }
128
129                 ThingUID bridgeUID = thing.getBridgeUID();
130                 if (bridgeUID == null) {
131                     throw new IllegalArgumentException("Unable to read BridgeUID!");
132                 }
133
134                 String[] impactedModulesString = impactedModulesObject.toString().split(",");
135                 for (String impactedModuleString : impactedModulesString) {
136                     ImpactedModuleUID impactedModuleUID = new ImpactedModuleUID(impactedModuleString.trim());
137                     ThingTypeUID thingTypeUID = new ThingTypeUID(bridgeUID.getBindingId(),
138                             impactedModuleUID.getThingTypeId());
139                     ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, impactedModuleUID.getThingId());
140
141                     if (!bridge.getThings().stream().anyMatch(thing -> thing.getUID().equals(thingUID))) {
142                         throw new IllegalArgumentException(
143                                 "Impacted module " + thingUID + " not found for '" + impactedModuleString + "'");
144                     }
145
146                     impactedModules.add(new ImpactedModule(thingUID, impactedModuleUID.getGroup()));
147                 }
148             } catch (RuntimeException e) {
149                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
150                 return;
151             }
152
153             logger.debug("Impacted modules for {} = {}", thing.getUID(), impactedModules);
154         }
155
156         NikobusPcLinkHandler pcLink = getPcLink();
157         if (pcLink != null) {
158             pcLink.addListener(getAddress(), this::commandReceived);
159         }
160     }
161
162     @Override
163     public void dispose() {
164         super.dispose();
165
166         Utils.cancel(requestUpdateFuture);
167         requestUpdateFuture = null;
168
169         NikobusPcLinkHandler pcLink = getPcLink();
170         if (pcLink != null) {
171             pcLink.removeListener(getAddress());
172         }
173     }
174
175     @Override
176     public void handleCommand(ChannelUID channelUID, Command command) {
177         logger.debug("handleCommand '{}' '{}'", channelUID, command);
178
179         if (!CHANNEL_BUTTON.equals(channelUID.getId())) {
180             return;
181         }
182
183         // Whenever the button receives an ON command,
184         // we send a simulated button press to the Nikobus.
185         if (command == OnOffType.ON) {
186             NikobusPcLinkHandler pcLink = getPcLink();
187             if (pcLink != null) {
188                 pcLink.sendCommand(new NikobusCommand(getAddress() + END_OF_TRANSMISSION));
189             }
190         }
191     }
192
193     private void commandReceived() {
194         if (thing.getStatus() != ThingStatus.ONLINE) {
195             updateStatus(ThingStatus.ONLINE);
196         }
197
198         updateState(CHANNEL_BUTTON, OnOffType.ON);
199
200         if (!impactedModules.isEmpty()) {
201             Utils.cancel(requestUpdateFuture);
202             requestUpdateFuture = scheduler.schedule(this::update, 400, TimeUnit.MILLISECONDS);
203         }
204     }
205
206     private void update() {
207         for (ImpactedModule module : impactedModules) {
208             NikobusModuleHandler switchModule = getModuleWithId(module.getThingUID());
209             if (switchModule != null) {
210                 switchModule.requestStatus(module.getGroup());
211             }
212         }
213     }
214
215     private @Nullable NikobusModuleHandler getModuleWithId(ThingUID thingUID) {
216         Bridge bridge = getBridge();
217         if (bridge == null) {
218             return null;
219         }
220
221         Thing thing = bridge.getThing(thingUID);
222         if (thing == null) {
223             return null;
224         }
225
226         ThingHandler thingHandler = thing.getHandler();
227         if (thingHandler instanceof NikobusModuleHandler) {
228             return (NikobusModuleHandler) thingHandler;
229         }
230         return null;
231     }
232
233     @Override
234     protected String getAddress() {
235         return "#N" + super.getAddress();
236     }
237 }