]> git.basschouten.com Git - openhab-addons.git/blob
52fcbbcaa469e2209fb6c2e7e615fc03072c66b0
[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.velux.internal.bridge.slip;
14
15 import java.util.Random;
16
17 import org.eclipse.jdt.annotation.NonNullByDefault;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.openhab.binding.velux.internal.bridge.common.RunProductCommand;
20 import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response;
21 import org.openhab.binding.velux.internal.bridge.slip.utils.Packet;
22 import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command;
23 import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber;
24 import org.openhab.binding.velux.internal.things.VeluxProduct;
25 import org.openhab.binding.velux.internal.things.VeluxProduct.DataSource;
26 import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex;
27 import org.openhab.binding.velux.internal.things.VeluxProduct.ProductState;
28 import org.openhab.binding.velux.internal.things.VeluxProductName;
29 import org.openhab.binding.velux.internal.things.VeluxProductPosition;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 /**
34  * Protocol specific bridge communication supported by the Velux bridge:
35  * <B>Send Command to Actuator</B>
36  * <P>
37  * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class
38  * itself.
39  * <P>
40  * As 3rd level class it defines informations how to send query and receive answer through the
41  * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider}
42  * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol
43  * SlipBridgeCommunicationProtocol}.
44  * <P>
45  * Methods in addition to the mentioned interface:
46  * <UL>
47  * <LI>{@link #setNodeIdAndParameters} to define the node and intended parameter value.</LI>
48  * </UL>
49  *
50  * @see RunProductCommand
51  * @see SlipBridgeCommunicationProtocol
52  *
53  * @author Guenther Schreiner - Initial contribution.
54  */
55 @NonNullByDefault
56 public class SCrunProductCommand extends RunProductCommand implements SlipBridgeCommunicationProtocol {
57     private final Logger logger = LoggerFactory.getLogger(SCrunProductCommand.class);
58
59     private static final String DESCRIPTION = "Send Command to Actuator";
60     private static final Command COMMAND = Command.GW_COMMAND_SEND_REQ;
61
62     /*
63      * ===========================================================
64      * Message Content Parameters
65      */
66
67     private int reqSessionID = 0;
68     private int reqCommandOriginator = 8; // SAAC
69     private int reqPriorityLevel = 5; // Comfort Level 2
70     private int reqParameterActive = 0; // Main Parameter
71     private int reqFPI1 = 0; // Functional Parameter Indicator 1 set of bits
72     private int reqFPI2 = 0; // Functional Parameter Indicator 2 set of bits
73     private int reqMainParameter = 0; // for FunctionalParameterValueArray
74     private int reqIndexArrayCount = 1; // One node will be addressed
75     private int reqIndexArray01 = 1; // This is the node
76     private int reqPriorityLevelLock = 0; // Do not set a new lock on priority level
77     private int reqPL03 = 0; // unused
78     private int reqPL47 = 0; // unused
79     private int reqLockTime = 0; // 30 seconds
80     private @Nullable FunctionalParameters reqFunctionalParameters = null;
81
82     /*
83      * ===========================================================
84      * Message Objects
85      */
86
87     private byte[] requestData = new byte[0];
88
89     /*
90      * ===========================================================
91      * Result Objects
92      */
93
94     private boolean success = false;
95     private boolean finished = false;
96
97     private VeluxProduct product = VeluxProduct.UNKNOWN;
98
99     /*
100      * ===========================================================
101      * Constructor Method
102      */
103
104     public SCrunProductCommand() {
105         logger.debug("SCgetProduct(Constructor) called.");
106         Random rand = new Random();
107         reqSessionID = rand.nextInt(0x0fff);
108         logger.debug("SCgetProduct(): starting sessions with the random number {}.", reqSessionID);
109     }
110
111     /*
112      * ===========================================================
113      * Methods required for interface {@link BridgeCommunicationProtocol}.
114      */
115
116     @Override
117     public String name() {
118         return DESCRIPTION;
119     }
120
121     @Override
122     public CommandNumber getRequestCommand() {
123         success = false;
124         finished = false;
125         logger.debug("getRequestCommand() returns {}.", COMMAND.getCommand());
126         return COMMAND.getCommand();
127     }
128
129     @Override
130     public byte[] getRequestDataAsArrayOfBytes() {
131         Packet request = new Packet(new byte[66]);
132         reqSessionID = (reqSessionID + 1) & 0xffff;
133
134         request.setTwoByteValue(0, reqSessionID);
135         request.setOneByteValue(2, reqCommandOriginator);
136         request.setOneByteValue(3, reqPriorityLevel);
137         request.setOneByteValue(4, reqParameterActive);
138
139         FunctionalParameters reqFunctionalParameters = this.reqFunctionalParameters;
140         reqFPI1 = reqFunctionalParameters != null ? reqFunctionalParameters.writeArray(request, 9) : 0;
141
142         request.setOneByteValue(5, reqFPI1);
143         request.setOneByteValue(6, reqFPI2);
144         request.setTwoByteValue(7, reqMainParameter);
145         request.setOneByteValue(41, reqIndexArrayCount);
146         request.setOneByteValue(42, reqIndexArray01);
147         request.setOneByteValue(62, reqPriorityLevelLock);
148         request.setOneByteValue(63, reqPL03);
149         request.setOneByteValue(64, reqPL47);
150         request.setOneByteValue(65, reqLockTime);
151
152         requestData = request.toByteArray();
153
154         if (logger.isTraceEnabled()) {
155             logger.trace("getRequestDataAsArrayOfBytes(): ntfSessionID={}.", hex(reqSessionID));
156             logger.trace("getRequestDataAsArrayOfBytes(): reqCommandOriginator={}.", hex(reqCommandOriginator));
157             logger.trace("getRequestDataAsArrayOfBytes(): reqPriorityLevel={}.", hex(reqPriorityLevel));
158             logger.trace("getRequestDataAsArrayOfBytes(): reqParameterActive={}.", hex(reqParameterActive));
159             logger.trace("getRequestDataAsArrayOfBytes(): reqFPI1={}.", bin(reqFPI1));
160             logger.trace("getRequestDataAsArrayOfBytes(): reqFPI2={}.", bin(reqFPI2));
161             logger.trace("getRequestDataAsArrayOfBytes(): reqMainParameter={}.", hex(reqMainParameter));
162             logger.trace("getRequestDataAsArrayOfBytes(): reqFunctionalParameters={}.", reqFunctionalParameters);
163             logger.trace("getRequestDataAsArrayOfBytes(): reqIndexArrayCount={}.", hex(reqIndexArrayCount));
164             logger.trace("getRequestDataAsArrayOfBytes(): reqIndexArray01={} (reqNodeId={}).", reqIndexArray01,
165                     reqIndexArray01);
166             logger.trace("getRequestDataAsArrayOfBytes(): reqPriorityLevelLock={}.", hex(reqPriorityLevelLock));
167             logger.trace("getRequestDataAsArrayOfBytes(): reqPL03={}.", hex(reqPL03));
168             logger.trace("getRequestDataAsArrayOfBytes(): reqPL47={}.", hex(reqPL47));
169             logger.trace("getRequestDataAsArrayOfBytes(): reqLockTime={}.", hex(reqLockTime));
170
171             logger.trace("getRequestDataAsArrayOfBytes() data is {}.", new Packet(requestData).toString());
172         }
173         return requestData;
174     }
175
176     private String hex(int i) {
177         return Integer.toHexString(i);
178     }
179
180     private String bin(int i) {
181         return Integer.toBinaryString(i);
182     }
183
184     @Override
185     public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) {
186         KLF200Response.introLogging(logger, responseCommand, thisResponseData);
187         success = false;
188         finished = false;
189         Packet responseData = new Packet(thisResponseData);
190         switch (Command.get(responseCommand)) {
191             case GW_COMMAND_SEND_CFM:
192                 if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 3)) {
193                     finished = true;
194                     break;
195                 }
196                 int cfmSessionID = responseData.getTwoByteValue(0);
197                 int cfmStatus = responseData.getOneByteValue(2);
198                 switch (cfmStatus) {
199                     case 0:
200                         logger.info("setResponse(): returned status: Error – Command rejected.");
201                         finished = true;
202                         break;
203                     case 1:
204                         logger.debug("setResponse(): returned status: OK - Command is accepted.");
205                         if (!KLF200Response.check4matchingSessionID(logger, cfmSessionID, reqSessionID)) {
206                             finished = true;
207                         } else if (!isSequentialEnforced) {
208                             logger.trace(
209                                     "setResponse(): skipping wait for more packets as sequential processing is not enforced.");
210                             finished = true;
211                             success = true;
212                         }
213                         break;
214                     default:
215                         logger.warn("setResponse(): returned status={} (not defined).", cfmStatus);
216                         finished = true;
217                         break;
218                 }
219                 break;
220
221             case GW_COMMAND_RUN_STATUS_NTF:
222                 if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 13)) {
223                     finished = true;
224                     break;
225                 }
226                 int ntfSessionID = responseData.getTwoByteValue(0);
227                 int ntfStatusiD = responseData.getOneByteValue(2);
228                 int ntfIndex = responseData.getOneByteValue(3);
229                 int ntfNodeParameter = responseData.getOneByteValue(4);
230                 int ntfParameterValue = responseData.getTwoByteValue(5);
231                 int ntfRunStatus = responseData.getOneByteValue(7);
232                 int ntfStatusReply = responseData.getOneByteValue(8);
233                 int ntfInformationCode = responseData.getFourByteValue(9);
234
235                 if (logger.isTraceEnabled()) {
236                     logger.trace("setResponse(): ntfSessionID={} (requested {}).", ntfSessionID, reqSessionID);
237                     logger.trace("setResponse(): ntfStatusiD={}.", ntfStatusiD);
238                     logger.trace("setResponse(): ntfIndex={}.", ntfIndex);
239                     logger.trace("setResponse(): ntfNodeParameter={}.", ntfNodeParameter);
240                     logger.trace("setResponse(): ntfParameterValue={}.", String.format("0x%04X", ntfParameterValue));
241                     logger.trace("setResponse(): ntfRunStatus={}.", ntfRunStatus);
242                     logger.trace("setResponse(): ntfStatusReply={}.", ntfStatusReply);
243                     logger.trace("setResponse(): ntfInformationCode={}.", ntfInformationCode);
244                 }
245
246                 if (!KLF200Response.check4matchingSessionID(logger, ntfSessionID, reqSessionID)) {
247                     finished = true;
248                 }
249                 switch (ntfRunStatus) {
250                     case 0:
251                         logger.debug("setResponse(): returned ntfRunStatus: EXECUTION_COMPLETED.");
252                         success = true;
253                         break;
254                     case 1:
255                         logger.info("setResponse(): returned ntfRunStatus: EXECUTION_FAILED.");
256                         finished = true;
257                         break;
258                     case 2:
259                         logger.debug("setResponse(): returned ntfRunStatus: EXECUTION_ACTIVE.");
260                         break;
261                     default:
262                         logger.warn("setResponse(): returned ntfRunStatus={} (not defined).", ntfRunStatus);
263                         finished = true;
264                         break;
265                 }
266                 if (!isSequentialEnforced) {
267                     logger.trace(
268                             "setResponse(): skipping wait for more packets as sequential processing is not enforced.");
269                     success = true;
270                     finished = true;
271                 }
272                 break;
273
274             case GW_COMMAND_REMAINING_TIME_NTF:
275                 if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 6)) {
276                     finished = true;
277                     break;
278                 }
279                 int timeNtfSessionID = responseData.getTwoByteValue(0);
280                 int timeNtfIndex = responseData.getOneByteValue(2);
281                 int timeNtfNodeParameter = responseData.getOneByteValue(3);
282                 int timeNtfSeconds = responseData.getTwoByteValue(4);
283
284                 if (!KLF200Response.check4matchingSessionID(logger, timeNtfSessionID, reqSessionID)) {
285                     finished = true;
286                 }
287
288                 if (logger.isDebugEnabled()) {
289                     logger.debug("setResponse(): timeNtfSessionID={}.", timeNtfSessionID);
290                     logger.debug("setResponse(): timeNtfIndex={}.", timeNtfIndex);
291                     logger.debug("setResponse(): timeNtfNodeParameter={}.", timeNtfNodeParameter);
292                     logger.debug("setResponse(): timeNtfSeconds={}.", timeNtfSeconds);
293                 }
294
295                 if (!isSequentialEnforced) {
296                     logger.trace(
297                             "setResponse(): skipping wait for more packets as sequential processing is not enforced.");
298                     success = true;
299                     finished = true;
300                 }
301                 break;
302
303             case GW_SESSION_FINISHED_NTF:
304                 finished = true;
305                 if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 2)) {
306                     break;
307                 }
308                 int finishedNtfSessionID = responseData.getTwoByteValue(0);
309                 if (!KLF200Response.check4matchingSessionID(logger, finishedNtfSessionID, reqSessionID)) {
310                     break;
311                 }
312                 logger.debug("setResponse(): finishedNtfSessionID={}.", finishedNtfSessionID);
313                 success = true;
314                 break;
315
316             default:
317                 KLF200Response.errorLogging(logger, responseCommand);
318                 finished = true;
319         }
320         KLF200Response.outroLogging(logger, success, finished);
321     }
322
323     @Override
324     public boolean isCommunicationFinished() {
325         return finished;
326     }
327
328     @Override
329     public boolean isCommunicationSuccessful() {
330         return success;
331     }
332
333     /*
334      * ===========================================================
335      * Methods in addition to the interface {@link BridgeCommunicationProtocol}
336      * and the abstract class {@link RunProductCommand}
337      */
338
339     @Override
340     public boolean setNodeIdAndParameters(int nodeId, @Nullable VeluxProductPosition mainParameter,
341             @Nullable FunctionalParameters functionalParameters) {
342         logger.debug("setNodeIdAndParameters({}) called.", nodeId);
343
344         if ((mainParameter != null) || (functionalParameters != null)) {
345             reqIndexArray01 = nodeId;
346
347             reqMainParameter = (mainParameter == null) ? VeluxProductPosition.VPP_VELUX_STOP
348                     : mainParameter.getPositionAsVeluxType();
349
350             int setMainParameter = VeluxProductPosition.isValid(reqMainParameter) ? reqMainParameter
351                     : VeluxProductPosition.VPP_VELUX_IGNORE;
352
353             reqFunctionalParameters = functionalParameters;
354
355             // create notification product that clones the new command positions
356             product = new VeluxProduct(VeluxProductName.UNKNOWN, new ProductBridgeIndex(reqIndexArray01),
357                     ProductState.EXECUTING.value, setMainParameter, setMainParameter, reqFunctionalParameters, COMMAND)
358                     .overrideDataSource(DataSource.BINDING);
359
360             return true;
361         }
362         product = VeluxProduct.UNKNOWN;
363         return false;
364     }
365
366     public VeluxProduct getProduct() {
367         logger.trace("getProduct(): returning {}.", product);
368         return product;
369     }
370 }