2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.satel.internal.command;
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;
23 * Base class for all command classes.
25 * @author Krzysztof Goworek - Initial contribution
28 public abstract class SatelCommandBase extends SatelMessage implements SatelCommand {
30 private final Logger logger = LoggerFactory.getLogger(SatelCommandBase.class);
33 * Used in extended (INT-RS v2.xx) command version.
35 protected static final byte[] EXTENDED_CMD_PAYLOAD = { 0x00 };
37 private static final byte COMMAND_RESULT_CODE = (byte) 0xef;
39 private volatile State state = State.NEW;
41 private boolean logResponseError = true;
43 private @Nullable SatelMessage response;
46 * Creates new command basing on command code and extended command flag.
48 * @param commandCode command code
49 * @param extended if <code>true</code> command will be sent as extended (256 zones or outputs)
51 public SatelCommandBase(byte commandCode, boolean extended) {
52 this(commandCode, extended ? EXTENDED_CMD_PAYLOAD : EMPTY_PAYLOAD);
56 * Creates new instance with specified command code and payload.
58 * @param command command code
59 * @param payload command payload
61 public SatelCommandBase(byte commandCode, byte[] payload) {
62 super(commandCode, payload);
66 public State getState() {
71 public void setState(State state) {
79 public SatelMessage getRequest() {
84 public final boolean matches(@Nullable SatelMessage response) {
85 return response != null
86 && (response.getCommand() == getCommand() || response.getCommand() == COMMAND_RESULT_CODE);
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)) {
96 } else if (response.getCommand() != getCommand()) {
97 logger.debug("Response code does not match command {}: {}", String.format("%02X", getCommand()), response);
99 } else if (!isResponseValid(response)) {
102 this.response = response;
103 handleResponseInternal(eventDispatcher);
107 public void ignoreResponseError() {
108 this.logResponseError = false;
111 protected SatelMessage getResponse() {
112 final SatelMessage response = this.response;
113 if (response != null) {
116 throw new IllegalStateException("Response not yet received for command. " + this.toString());
121 * Checks whether given response is valid for the command.
123 * @param response message to check
124 * @return <code>true</code> if given message is valid response for the command
126 protected abstract boolean isResponseValid(SatelMessage response);
129 * Overriden in subclasses allows to execute action specific to given command (i.e. dispatch an event).
131 * @param eventDispatcher event dispatcher
133 protected void handleResponseInternal(final EventDispatcher eventDispatcher) {
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) {
142 digit = (bytes[byteIdx] >> 4) & 0x0f;
144 digit = (bytes[byteIdx]) & 0x0f;
147 result = result * 10 + digit;
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());
158 if (response.getPayload().length != 1) {
159 logger.debug("Invalid payload length: {}. {}", response.getPayload().length, getRequest());
164 byte responseCode = response.getPayload()[0];
167 switch (responseCode) {
172 errorMsg = "Requesting user code not found";
175 errorMsg = "No access";
178 errorMsg = "Selected user does not exist";
181 errorMsg = "Selected user already exists";
184 errorMsg = "Wrong code or code already exists";
187 errorMsg = "Telephone code already exists";
190 errorMsg = "Changed code is the same";
193 errorMsg = "Other error";
196 errorMsg = "Can not arm, but can use force arm";
199 errorMsg = "Can not arm";
202 logger.trace("Command accepted");
205 if (responseCode >= 0x80 && responseCode <= 0x8f) {
206 errorMsg = String.format("Other error: %02X", responseCode);
208 errorMsg = String.format("Unknown result code: %02X", responseCode);
212 if (logResponseError) {
213 logger.info("{}. {}", errorMsg, getRequest());
219 * Decodes firmware version and release date from command payload
221 * @param offset starting offset in payload
222 * @return decoded firmware version and release date as string
224 public String getVersion(int offset) {
225 // build version string
226 final byte[] payload = getResponse().getPayload();
227 String verStr = 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);