]> git.basschouten.com Git - openhab-addons.git/blob
45dc66d9bba3b6b9f48f25bc371a775baedd9784
[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.max.internal.command;
14
15 import java.io.ByteArrayOutputStream;
16 import java.io.IOException;
17 import java.nio.charset.StandardCharsets;
18 import java.util.ArrayList;
19 import java.util.Base64;
20 import java.util.List;
21 import java.util.Set;
22 import java.util.TreeSet;
23
24 import org.apache.commons.lang3.StringUtils;
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.openhab.binding.max.internal.Utils;
27 import org.openhab.binding.max.internal.device.Device;
28 import org.openhab.binding.max.internal.device.RoomInformation;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 /**
33  * The {@link MCommand} Creates the MAX! Cube the room & device name information update message.
34  *
35  * @author Marcel Verpaalen - Initial Contribution
36  */
37 @NonNullByDefault
38 public class MCommand extends CubeCommand {
39     private final Logger logger = LoggerFactory.getLogger(MCommand.class);
40
41     private static final byte MAGIC_NR = 86;
42     private static final byte M_VERSION = 2;
43     private static final int MAX_NAME_LENGTH = 32;
44     private static final int MAX_GROUP_COUNT = 20;
45     private static final int MAX_DEVICES_COUNT = 140;
46     private static final int MAX_MSG_LENGTH = 1900;
47
48     private final List<Device> devices;
49     public final List<RoomInformation> rooms;
50
51     public MCommand(List<Device> devices) {
52         this(devices, new ArrayList<>());
53     }
54
55     public MCommand(List<Device> devices, List<RoomInformation> rooms) {
56         this.devices = new ArrayList<>(devices);
57         this.rooms = new ArrayList<>(rooms);
58         roombuilder();
59     }
60
61     public void listRooms() {
62         for (RoomInformation room : rooms) {
63             logger.debug("M Command room info: {}", room);
64         }
65     }
66
67     public List<RoomInformation> getRooms() {
68         return rooms;
69     }
70
71     private void roombuilder() {
72         for (Device di : devices) {
73             boolean foundRoom = false;
74
75             for (RoomInformation room : rooms) {
76                 if (room.getPosition() == di.getRoomId()) {
77                     foundRoom = true;
78                 }
79             }
80             // Add new rooms based on device information.
81             // TODO check if it is allowed to have any device creating a room, or should it be a thermostat
82             if (!foundRoom && di.getRoomId() != 0 && rooms.size() < MAX_GROUP_COUNT) {
83                 RoomInformation room = new RoomInformation(di.getRoomId(), di.getRoomName(), di.getRFAddress());
84                 rooms.add(room);
85             }
86         }
87     }
88
89     public byte[] concatenateByteArrays(List<byte[]> blocks) {
90         ByteArrayOutputStream os = new ByteArrayOutputStream();
91         for (byte[] b : blocks) {
92             os.write(b, 0, b.length);
93         }
94         return os.toByteArray();
95     }
96
97     @Override
98     public String getCommandString() {
99         int deviceCount = 0;
100         int roomCount = 0;
101
102         ByteArrayOutputStream message = new ByteArrayOutputStream();
103
104         try {
105             byte[] header = { MAGIC_NR, M_VERSION, (byte) rooms.size() };
106             message.write(header);
107
108             Set<Integer> sortedRooms = new TreeSet<>();
109             for (RoomInformation room : rooms) {
110                 sortedRooms.add(room.getPosition());
111             }
112
113             for (Integer roomPos : sortedRooms) {
114                 for (RoomInformation room : rooms) {
115                     if (room.getPosition() == roomPos) {
116                         if (roomCount < MAX_GROUP_COUNT) {
117                             byte[] roomName = StringUtils.abbreviate(room.getName(), MAX_NAME_LENGTH)
118                                     .getBytes(StandardCharsets.UTF_8);
119                             byte[] nameLength = new byte[] { (byte) roomName.length };
120                             byte[] rfAddress = Utils.hexStringToByteArray(room.getRFAddress());
121                             message.write(roomPos.byteValue());
122                             message.write(nameLength);
123                             message.write(roomName);
124                             message.write(rfAddress);
125                         } else {
126                             logger.warn("{} exceeds max number of rooms ({}). Ignored", room, MAX_GROUP_COUNT);
127                         }
128                         roomCount++;
129                     }
130                 }
131             }
132             for (Device di : devices) {
133                 if (deviceCount < MAX_DEVICES_COUNT) {
134                     deviceCount++;
135                 } else {
136                     logger.warn("{} exceeds max number of devices ({}). Ignored", di, MAX_DEVICES_COUNT);
137                 }
138             }
139             message.write((byte) deviceCount);
140             for (Device di : devices) {
141                 if (deviceCount > 0) {
142                     byte[] deviceType = { (byte) di.getType().getValue() };
143                     byte[] rfAddress = Utils.hexStringToByteArray(di.getRFAddress());
144                     byte[] deviceName = StringUtils.abbreviate(di.getName(), MAX_NAME_LENGTH)
145                             .getBytes(StandardCharsets.UTF_8);
146                     byte[] nameLength = { (byte) deviceName.length };
147                     byte[] serialNumber = di.getSerialNumber().getBytes(StandardCharsets.UTF_8);
148                     byte[] roomId = { (byte) di.getRoomId() };
149
150                     message.write(deviceType);
151                     message.write(rfAddress);
152                     message.write(serialNumber);
153                     message.write(nameLength);
154                     message.write(deviceName);
155                     message.write(roomId);
156                 } else {
157                     logger.warn("{} exceeds max number of devices ({}). Ignored", di, MAX_DEVICES_COUNT);
158                 }
159                 deviceCount--;
160             }
161
162             byte[] dst = { 0x01 };
163             message.write(dst);
164         } catch (IOException e) {
165             logger.debug("Error while generating m: command: {}", e.getMessage(), e);
166         }
167
168         final String encodedString = Base64.getEncoder().encodeToString(message.toByteArray());
169         final StringBuilder commandStringBuilder = new StringBuilder();
170         int parts = (int) Math.round(encodedString.length() / MAX_MSG_LENGTH + 0.5);
171         for (int i = 0; i < parts; i++) {
172             String partString = StringUtils.abbreviate(encodedString.substring((i) * MAX_MSG_LENGTH), MAX_MSG_LENGTH);
173             commandStringBuilder.append("m:").append(String.format("%02d", i)).append(",").append(partString)
174                     .append('\r').append('\n');
175         }
176
177         return commandStringBuilder.toString();
178     }
179
180     @Override
181     public String getReturnStrings() {
182         return "A:";
183     }
184 }