]> git.basschouten.com Git - openhab-addons.git/blob
7c5f25aaf602c4ee0ea037b0702cd4377c2ec909
[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.lcn.internal;
14
15 import java.util.Collection;
16 import java.util.Optional;
17 import java.util.Set;
18 import java.util.function.Consumer;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.lcn.internal.common.LcnAddr;
23 import org.openhab.binding.lcn.internal.common.LcnAddrMod;
24 import org.openhab.binding.lcn.internal.common.LcnDefs;
25 import org.openhab.binding.lcn.internal.common.LcnDefs.OutputPortDimMode;
26 import org.openhab.binding.lcn.internal.common.LcnException;
27 import org.openhab.binding.lcn.internal.connection.Connection;
28 import org.openhab.binding.lcn.internal.connection.ConnectionCallback;
29 import org.openhab.binding.lcn.internal.connection.ConnectionSettings;
30 import org.openhab.binding.lcn.internal.connection.ModInfo;
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.binding.BaseBridgeHandler;
37 import org.openhab.core.thing.binding.ThingHandler;
38 import org.openhab.core.thing.binding.ThingHandlerService;
39 import org.openhab.core.types.Command;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 /**
44  * The {@link PckGatewayHandler} is responsible for the communication via a PCK gateway.
45  *
46  * @author Fabian Wolter - Initial contribution
47  */
48 @NonNullByDefault
49 public class PckGatewayHandler extends BaseBridgeHandler {
50     private final Logger logger = LoggerFactory.getLogger(PckGatewayHandler.class);
51     private @Nullable Connection connection;
52     private Optional<Consumer<String>> pckListener = Optional.empty();
53     private @Nullable PckGatewayConfiguration config;
54
55     public PckGatewayHandler(Bridge bridge) {
56         super(bridge);
57     }
58
59     @Override
60     public void handleCommand(ChannelUID channelUID, Command command) {
61         // nothing
62     }
63
64     @Override
65     public synchronized void initialize() {
66         PckGatewayConfiguration localConfig = config = getConfigAs(PckGatewayConfiguration.class);
67
68         String errorMessage = "Could not connect to LCN-PCHK/VISU: " + localConfig.getHostname() + ": ";
69
70         try {
71             OutputPortDimMode dimMode;
72             String mode = localConfig.getMode();
73             if (LcnDefs.OutputPortDimMode.NATIVE50.name().equalsIgnoreCase(mode)) {
74                 dimMode = LcnDefs.OutputPortDimMode.NATIVE50;
75             } else if (LcnDefs.OutputPortDimMode.NATIVE200.name().equalsIgnoreCase(mode)) {
76                 dimMode = LcnDefs.OutputPortDimMode.NATIVE200;
77             } else {
78                 throw new LcnException("DimMode " + mode + " is not supported");
79             }
80
81             ConnectionSettings settings = new ConnectionSettings("0", localConfig.getHostname(), localConfig.getPort(),
82                     localConfig.getUsername(), localConfig.getPassword(), dimMode, LcnDefs.OutputPortStatusMode.PERCENT,
83                     localConfig.getTimeoutMs());
84
85             connection = new Connection(settings, scheduler, new ConnectionCallback() {
86                 @Override
87                 public void onOnline() {
88                     updateStatus(ThingStatus.ONLINE);
89                 }
90
91                 @Override
92                 public void onOffline(@Nullable String errorMessage) {
93                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, errorMessage + ".");
94                 }
95
96                 @Override
97                 public void onPckMessageReceived(String message) {
98                     pckListener.ifPresent(l -> l.accept(message));
99                     getThing().getThings().stream().filter(t -> t.getStatus() == ThingStatus.ONLINE).map(t -> {
100                         LcnModuleHandler handler = (LcnModuleHandler) t.getHandler();
101                         if (handler == null) {
102                             logger.warn("Failed to process PCK message: Handler not set");
103                         }
104                         return handler;
105                     }).filter(h -> h != null).forEach(h -> h.handleStatusMessage(message));
106                 }
107             });
108
109             updateStatus(ThingStatus.UNKNOWN);
110         } catch (LcnException e) {
111             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMessage + e.getMessage());
112         }
113     }
114
115     @Override
116     public Collection<Class<? extends ThingHandlerService>> getServices() {
117         return Set.of(LcnModuleDiscoveryService.class);
118     }
119
120     @Override
121     public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) {
122         if (childThing.getThingTypeUID().equals(LcnBindingConstants.THING_TYPE_MODULE)
123                 || childThing.getThingTypeUID().equals(LcnBindingConstants.THING_TYPE_GROUP)) {
124             try {
125                 LcnAddr addr = getLcnAddrFromThing(childThing);
126                 Connection localConnection = connection;
127                 if (localConnection != null) {
128                     localConnection.removeLcnModule(addr);
129                 }
130             } catch (LcnException e) {
131                 logger.warn("Failed to read configuration: {}", e.getMessage());
132             }
133         }
134     }
135
136     private LcnAddr getLcnAddrFromThing(Thing childThing) throws LcnException {
137         LcnModuleHandler lcnModuleHandler = (LcnModuleHandler) childThing.getHandler();
138         if (lcnModuleHandler != null) {
139             return lcnModuleHandler.getCommandAddress();
140         } else {
141             throw new LcnException("Could not get module handler");
142         }
143     }
144
145     /**
146      * Enqueues a PCK (String) command to be sent to an LCN module.
147      *
148      * @param addr the modules address
149      * @param wantsAck true, if the module shall send an ACK upon successful processing
150      * @param pck the command to send
151      */
152     public void queue(LcnAddr addr, boolean wantsAck, String pck) {
153         Connection localConnection = connection;
154         if (localConnection != null) {
155             localConnection.queue(addr, wantsAck, pck);
156         } else {
157             logger.warn("Dropped PCK command: {}", pck);
158         }
159     }
160
161     /**
162      * Enqueues a PCK (ByteBuffer) command to be sent to an LCN module.
163      *
164      * @param addr the modules address
165      * @param wantsAck true, if the module shall send an ACK upon successful processing
166      * @param pck the command to send
167      */
168     public void queue(LcnAddr addr, boolean wantsAck, byte[] pck) {
169         Connection localConnection = connection;
170         if (localConnection != null) {
171             localConnection.queue(addr, wantsAck, pck);
172         } else {
173             logger.warn("Dropped PCK command of length: {}", pck.length);
174         }
175     }
176
177     /**
178      * Sends a broadcast message to all LCN modules: All LCN modules are requested to answer with an Ack.
179      */
180     void sendModuleDiscoveryCommand() {
181         Connection localConnection = connection;
182         if (localConnection != null) {
183             localConnection.sendModuleDiscoveryCommand();
184         }
185     }
186
187     /**
188      * Send a request to an LCN module to respond with its serial number and firmware version.
189      *
190      * @param addr the module's address
191      */
192     void sendSerialNumberRequest(LcnAddrMod addr) {
193         Connection localConnection = connection;
194         if (localConnection != null) {
195             localConnection.sendSerialNumberRequest(addr);
196         }
197     }
198
199     /**
200      * Send a request to an LCN module to respond with its configured name.
201      *
202      * @param addr the module's address
203      */
204     void sendModuleNameRequest(LcnAddrMod addr) {
205         Connection localConnection = connection;
206         if (localConnection != null) {
207             localConnection.sendModuleNameRequest(addr);
208         }
209     }
210
211     /**
212      * Returns the ModInfo to a given module. Will be created if it doesn't exist,yet.
213      *
214      * @param addr the module's address
215      * @return the ModInfo
216      * @throws LcnException when this handler is not initialized, yet
217      */
218     ModInfo getModInfo(LcnAddrMod addr) throws LcnException {
219         Connection localConnection = connection;
220         if (localConnection != null) {
221             return localConnection.updateModuleData(addr);
222         } else {
223             throw new LcnException("Connection is null");
224         }
225     }
226
227     /**
228      * Registers a listener to receive all PCK messages from this PCK gateway.
229      *
230      * @param listener the listener to add
231      */
232     void registerPckListener(Consumer<String> listener) {
233         this.pckListener = Optional.of(listener);
234     }
235
236     /**
237      * Removes all listeners for PCK messages from this PCK gateway.
238      */
239     void removeAllPckListeners() {
240         this.pckListener = Optional.empty();
241     }
242
243     /**
244      * Gets the Connection for this handler.
245      *
246      * @return the Connection
247      */
248     @Nullable
249     public Connection getConnection() {
250         return connection;
251     }
252
253     /**
254      * Gets the local segment ID. When no segments are used, the value is 0.
255      *
256      * @return the local segment ID
257      */
258     public int getLocalSegmentId() {
259         Connection localConnection = connection;
260         if (localConnection != null) {
261             return localConnection.getLocalSegId();
262         } else {
263             return 0;
264         }
265     }
266
267     /**
268      * Translates the given physical segment ID (0 or 4 if local segment) to the logical segment ID (local segment ID).
269      *
270      * @param physicalSegmentId the segment ID to convert
271      * @return the converted segment ID
272      */
273     public int toLogicalSegmentId(int physicalSegmentId) {
274         int localSegmentId = getLocalSegmentId();
275         if ((physicalSegmentId == 0 || physicalSegmentId == 4) && localSegmentId != -1) {
276             // PCK message came from local segment
277             // physicalSegmentId == 0 => Module is programmed to send status messages to local segment only
278             // physicalSegmentId == 4 => Module is programmed to send status messages globally (to all segments)
279             // or segment coupler scan did not finish, yet (-1). Assume local segment, then.
280             return localSegmentId;
281         } else {
282             return physicalSegmentId;
283         }
284     }
285
286     @Override
287     public void dispose() {
288         Connection localConnection = connection;
289         if (localConnection != null) {
290             localConnection.shutdown();
291         }
292     }
293
294     /**
295      * Gets the configured connection timeout for the PCK gateway.
296      *
297      * @return the timeout in ms
298      */
299     public long getTimeoutMs() {
300         PckGatewayConfiguration localConfig = config;
301         return localConfig != null ? localConfig.getTimeoutMs() : 3500;
302     }
303 }