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.mynice.internal.xml;
15 import java.security.MessageDigest;
16 import java.security.NoSuchAlgorithmException;
17 import java.util.Base64;
18 import java.util.Base64.Encoder;
19 import java.util.UUID;
21 import javax.xml.bind.DatatypeConverter;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.openhab.binding.mynice.internal.xml.dto.CommandType;
25 import org.openhab.binding.mynice.internal.xml.dto.T4Command;
28 * The {@link RequestBuilder} is responsible for building a string request from the CommandType
30 * @author Gaƫl L'hopital - Initial contribution
33 public class RequestBuilder {
34 private static final Encoder BASE64_ENCODER = Base64.getEncoder();
36 public static final String USERNAME = "%un%";
37 public static final String CLIENT_CHALLENGE = "%cc%";
38 private static final String START_REQUEST = "<Request id=\"%s\" source=\"openhab\" target=\"%s\" gw=\"gwID\" protocolType=\"NHK\" protocolVersion=\"1.0\" type=\"%s\">\r\n";
39 private static final String END_REQUEST = "%s%s</Request>";
40 private static final String DOOR_ACTION = "<DoorAction>%s</DoorAction>";
41 private static final String T4_ACTION = "<T4Action>%s</T4Action>";
42 private static final String SIGN = "<Sign>%s</Sign>";
44 private final String clientChallenge = UUID.randomUUID().toString().substring(0, 8);
45 private final byte[] clientChallengeArr = invertArray(DatatypeConverter.parseHexBinary(clientChallenge));
46 private final MessageDigest digest;
47 private final String it4WifiMac;
48 private final String username;
50 private int sessionId = 0;
51 private int commandSequence = 0;
52 private byte[] sessionPassword = {};
54 public RequestBuilder(String it4WifiMac, String username) {
56 this.digest = MessageDigest.getInstance("SHA-256");
57 this.it4WifiMac = it4WifiMac;
58 this.username = username;
59 } catch (NoSuchAlgorithmException e) {
60 throw new IllegalArgumentException(e);
64 private String buildSign(CommandType command, String message) {
65 if (command.signNeeded) {
66 byte[] msgHash = sha256(message.getBytes());
67 byte[] sign = sha256(msgHash, sessionPassword);
68 return String.format(SIGN, BASE64_ENCODER.encodeToString(sign));
73 public String buildMessage(String id, String command) {
74 return buildMessage(CommandType.CHANGE, id, String.format(DOOR_ACTION, command.toLowerCase()));
77 public String buildMessage(String id, T4Command t4) {
78 return buildMessage(CommandType.CHANGE, id, String.format(T4_ACTION, t4.name()));
81 public String buildMessage(CommandType command, Object... bodyParms) {
82 String startRequest = String.format(START_REQUEST, getCommandId(), it4WifiMac, command);
83 String body = startRequest + getBody(command, bodyParms);
84 String sign = buildSign(command, body);
85 return String.format(END_REQUEST, body, sign);
88 public String getBody(CommandType command, Object... bodyParms) {
89 String result = command.body;
90 if (result.length() != 0) {
91 result = result.replace(USERNAME, username);
92 result = result.replace(CLIENT_CHALLENGE, clientChallenge);
93 result = String.format(result, bodyParms);
98 public int getCommandId() {
99 return (commandSequence++ << 8) | sessionId;
102 public void setChallenges(String serverChallenge, int sessionId, String password) {
103 byte[] serverChallengeArr = invertArray(DatatypeConverter.parseHexBinary(serverChallenge));
104 byte[] pairingPassword = Base64.getDecoder().decode(password);
105 this.sessionPassword = sha256(pairingPassword, serverChallengeArr, clientChallengeArr);
106 this.sessionId = sessionId & 255;
109 private byte[] sha256(byte[]... values) {
110 for (byte[] data : values) {
113 return digest.digest();
116 private static byte[] invertArray(byte[] data) {
117 byte[] result = new byte[data.length];
118 int i = data.length - 1;