]> git.basschouten.com Git - openhab-addons.git/blob
d39baf1dbf2522d20bccd5d2170cd1ff02408513
[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.satel.internal.command;
14
15 import org.eclipse.jdt.annotation.NonNullByDefault;
16 import org.eclipse.jdt.annotation.Nullable;
17 import org.openhab.binding.satel.internal.event.EventDispatcher;
18 import org.openhab.binding.satel.internal.protocol.SatelMessage;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21
22 /**
23  * Base class for all command classes.
24  *
25  * @author Krzysztof Goworek - Initial contribution
26  */
27 @NonNullByDefault
28 public abstract class SatelCommandBase extends SatelMessage implements SatelCommand {
29
30     private final Logger logger = LoggerFactory.getLogger(SatelCommandBase.class);
31
32     /**
33      * Used in extended (INT-RS v2.xx) command version.
34      */
35     protected static final byte[] EXTENDED_CMD_PAYLOAD = { 0x00 };
36
37     private static final byte COMMAND_RESULT_CODE = (byte) 0xef;
38
39     private volatile State state = State.NEW;
40
41     private boolean logResponseError = true;
42
43     private @Nullable SatelMessage response;
44
45     /**
46      * Creates new command basing on command code and extended command flag.
47      *
48      * @param commandCode command code
49      * @param extended if <code>true</code> command will be sent as extended (256 zones or outputs)
50      */
51     public SatelCommandBase(byte commandCode, boolean extended) {
52         this(commandCode, extended ? EXTENDED_CMD_PAYLOAD : EMPTY_PAYLOAD);
53     }
54
55     /**
56      * Creates new instance with specified command code and payload.
57      *
58      * @param command command code
59      * @param payload command payload
60      */
61     public SatelCommandBase(byte commandCode, byte[] payload) {
62         super(commandCode, payload);
63     }
64
65     @Override
66     public State getState() {
67         return state;
68     }
69
70     @Override
71     public void setState(State state) {
72         synchronized (this) {
73             this.state = state;
74             this.notifyAll();
75         }
76     }
77
78     @Override
79     public SatelMessage getRequest() {
80         return this;
81     }
82
83     @Override
84     public final boolean matches(@Nullable SatelMessage response) {
85         return response != null
86                 && (response.getCommand() == getCommand() || response.getCommand() == COMMAND_RESULT_CODE);
87     }
88
89     @Override
90     public final boolean handleResponse(EventDispatcher eventDispatcher, SatelMessage response) {
91         // if response is valid, store it for future use
92         if (response.getCommand() == COMMAND_RESULT_CODE) {
93             if (!hasCommandSucceeded(response)) {
94                 return false;
95             }
96         } else if (response.getCommand() != getCommand()) {
97             logger.debug("Response code does not match command {}: {}", String.format("%02X", getCommand()), response);
98             return false;
99         } else if (!isResponseValid(response)) {
100             return false;
101         }
102         this.response = response;
103         handleResponseInternal(eventDispatcher);
104         return true;
105     }
106
107     public void ignoreResponseError() {
108         this.logResponseError = false;
109     }
110
111     protected SatelMessage getResponse() {
112         final SatelMessage response = this.response;
113         if (response != null) {
114             return response;
115         } else {
116             throw new IllegalStateException("Response not yet received for command. " + this.toString());
117         }
118     }
119
120     /**
121      * Checks whether given response is valid for the command.
122      *
123      * @param response message to check
124      * @return <code>true</code> if given message is valid response for the command
125      */
126     protected abstract boolean isResponseValid(SatelMessage response);
127
128     /**
129      * Overriden in subclasses allows to execute action specific to given command (i.e. dispatch an event).
130      *
131      * @param eventDispatcher event dispatcher
132      */
133     protected void handleResponseInternal(final EventDispatcher eventDispatcher) {
134     }
135
136     protected static int bcdToInt(byte[] bytes, int offset, int size) {
137         int result = 0, digit;
138         int byteIdx = offset;
139         int digits = size * 2;
140         for (int i = 0; i < digits; ++i) {
141             if (i % 2 == 0) {
142                 digit = (bytes[byteIdx] >> 4) & 0x0f;
143             } else {
144                 digit = (bytes[byteIdx]) & 0x0f;
145                 byteIdx += 1;
146             }
147             result = result * 10 + digit;
148         }
149         return result;
150     }
151
152     protected boolean hasCommandSucceeded(SatelMessage response) {
153         // validate response message
154         if (response.getCommand() != COMMAND_RESULT_CODE) {
155             logger.debug("Invalid response code: {}. {}", String.format("%02X", response.getCommand()), getRequest());
156             return false;
157         }
158         if (response.getPayload().length != 1) {
159             logger.debug("Invalid payload length: {}. {}", response.getPayload().length, getRequest());
160             return false;
161         }
162
163         // check result code
164         byte responseCode = response.getPayload()[0];
165         String errorMsg;
166
167         switch (responseCode) {
168             case 0:
169                 // success
170                 return true;
171             case 0x01:
172                 errorMsg = "Requesting user code not found";
173                 break;
174             case 0x02:
175                 errorMsg = "No access";
176                 break;
177             case 0x03:
178                 errorMsg = "Selected user does not exist";
179                 break;
180             case 0x04:
181                 errorMsg = "Selected user already exists";
182                 break;
183             case 0x05:
184                 errorMsg = "Wrong code or code already exists";
185                 break;
186             case 0x06:
187                 errorMsg = "Telephone code already exists";
188                 break;
189             case 0x07:
190                 errorMsg = "Changed code is the same";
191                 break;
192             case 0x08:
193                 errorMsg = "Other error";
194                 break;
195             case 0x11:
196                 errorMsg = "Can not arm, but can use force arm";
197                 break;
198             case 0x12:
199                 errorMsg = "Can not arm";
200                 break;
201             case (byte) 0xff:
202                 logger.trace("Command accepted");
203                 return true;
204             default:
205                 if (responseCode >= 0x80 && responseCode <= 0x8f) {
206                     errorMsg = String.format("Other error: %02X", responseCode);
207                 } else {
208                     errorMsg = String.format("Unknown result code: %02X", responseCode);
209                 }
210         }
211
212         if (logResponseError) {
213             logger.info("{}. {}", errorMsg, getRequest());
214         }
215         return false;
216     }
217
218     /**
219      * Decodes firmware version and release date from command payload
220      *
221      * @param offset starting offset in payload
222      * @return decoded firmware version and release date as string
223      */
224     public String getVersion(int offset) {
225         // build version string
226         final byte[] payload = getResponse().getPayload();
227         return new String(payload, offset, 1) + "." + new String(payload, offset + 1, 2) + " "
228                 + new String(payload, offset + 3, 4) + "-" + new String(payload, offset + 7, 2) + "-"
229                 + new String(payload, offset + 9, 2);
230     }
231 }