]> git.basschouten.com Git - openhab-addons.git/blob
1aa27620be3bfe0018612211a43a7594e8add1d2
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.rfxcom.internal.messages;
14
15 import static org.openhab.binding.rfxcom.internal.RFXComBindingConstants.*;
16 import static org.openhab.binding.rfxcom.internal.messages.RFXComBaseMessage.PacketType.RAW;
17
18 import java.nio.ByteBuffer;
19 import java.util.stream.Collectors;
20 import java.util.stream.IntStream;
21
22 import org.openhab.binding.rfxcom.internal.config.RFXComDeviceConfiguration;
23 import org.openhab.binding.rfxcom.internal.config.RFXComRawDeviceConfiguration;
24 import org.openhab.binding.rfxcom.internal.exceptions.RFXComException;
25 import org.openhab.binding.rfxcom.internal.exceptions.RFXComInvalidStateException;
26 import org.openhab.binding.rfxcom.internal.exceptions.RFXComMessageTooLongException;
27 import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedChannelException;
28 import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedValueException;
29 import org.openhab.binding.rfxcom.internal.handler.DeviceState;
30 import org.openhab.core.library.types.OnOffType;
31 import org.openhab.core.library.types.OpenClosedType;
32 import org.openhab.core.library.types.StringType;
33 import org.openhab.core.types.State;
34 import org.openhab.core.types.Type;
35 import org.openhab.core.util.HexUtils;
36
37 /**
38  * RFXCOM data class for raw messages.
39  *
40  * @author James Hewitt-Thomas - New addition to the PRO RFXCom firmware
41  */
42 public class RFXComRawMessage extends RFXComDeviceMessageImpl<RFXComRawMessage.SubType> {
43
44     public enum SubType implements ByteEnumWrapper {
45         RAW_PACKET1(0x00),
46         RAW_PACKET2(0x01),
47         RAW_PACKET3(0x02),
48         RAW_PACKET4(0x03),
49
50         UNKNOWN(0xFF);
51
52         private final int subType;
53
54         SubType(int subType) {
55             this.subType = subType;
56         }
57
58         @Override
59         public byte toByte() {
60             return (byte) subType;
61         }
62
63         public static SubType fromByte(int input) {
64             for (SubType c : SubType.values()) {
65                 if (c.subType == input) {
66                     return c;
67                 }
68             }
69
70             return SubType.UNKNOWN;
71         }
72     }
73
74     public SubType subType;
75     public byte repeat;
76     public short[] pulses;
77
78     private RFXComRawDeviceConfiguration config;
79
80     public RFXComRawMessage() {
81         super(RAW);
82         pulses = new short[0];
83     }
84
85     public RFXComRawMessage(byte[] message) throws RFXComException {
86         encodeMessage(message);
87     }
88
89     @Override
90     public String toString() {
91         String str = super.toString();
92
93         str += ", Sub type = " + subType;
94
95         return str;
96     }
97
98     @Override
99     public void encodeMessage(byte[] message) throws RFXComException {
100         super.encodeMessage(message);
101
102         final int pulsesByteLen = rawMessage.length - 5;
103         if (pulsesByteLen % 4 != 0) {
104             throw new RFXComException("Incorrect byte length for pulses - must be divisible by 4");
105         }
106
107         subType = SubType.fromByte(super.subType);
108         repeat = rawMessage[4];
109         pulses = new short[pulsesByteLen / 2];
110         ByteBuffer.wrap(rawMessage, 5, rawMessage.length - 5).asShortBuffer().get(pulses);
111     }
112
113     @Override
114     public byte[] decodeMessage() throws RFXComException {
115         if (pulses.length > 124) {
116             throw new RFXComMessageTooLongException("Longest payload according to RFXtrx SDK is 124 shorts.");
117         }
118
119         final int pulsesByteLen = pulses.length * 2;
120         byte[] data = new byte[5 + pulsesByteLen];
121
122         data[0] = (byte) (data.length - 1);
123         data[1] = RAW.toByte();
124         data[2] = subType.toByte();
125         data[3] = seqNbr;
126         data[4] = (byte) config.repeat;
127
128         ByteBuffer.wrap(data, 5, pulsesByteLen).asShortBuffer().put(pulses);
129
130         return data;
131     }
132
133     @Override
134     public String getDeviceId() {
135         return "RAW";
136     }
137
138     @Override
139     public void setConfig(RFXComDeviceConfiguration config) throws RFXComException {
140         super.setConfig(config);
141         this.config = (RFXComRawDeviceConfiguration) config;
142     }
143
144     @Override
145     public State convertToState(String channelId, DeviceState deviceState) throws RFXComUnsupportedChannelException {
146         switch (channelId) {
147             case CHANNEL_RAW_MESSAGE:
148                 return new StringType(HexUtils.bytesToHex(rawMessage));
149
150             case CHANNEL_RAW_PAYLOAD:
151                 byte[] payload = new byte[pulses.length * 2];
152                 ByteBuffer.wrap(payload).asShortBuffer().put(pulses);
153                 return new StringType(HexUtils.bytesToHex(payload));
154
155             case CHANNEL_PULSES:
156                 return new StringType(IntStream.range(0, pulses.length)
157                         .mapToObj(s -> Integer.toString(Short.toUnsignedInt(pulses[s])))
158                         .collect(Collectors.joining(" ")));
159
160             default:
161                 throw new RFXComUnsupportedChannelException("Nothing relevant for " + channelId);
162         }
163     }
164
165     @Override
166     public void setSubType(SubType subType) {
167         this.subType = subType;
168     }
169
170     @Override
171     public void setDeviceId(String deviceId) {
172         // Nothing to do here
173     }
174
175     @Override
176     public void convertFromState(String channelId, Type type)
177             throws RFXComUnsupportedChannelException, RFXComInvalidStateException {
178         switch (channelId) {
179             case CHANNEL_RAW_MESSAGE:
180             case CHANNEL_RAW_PAYLOAD:
181             case CHANNEL_PULSES:
182                 throw new RFXComUnsupportedChannelException("Cannot send on channel " + channelId);
183
184             case CHANNEL_COMMAND:
185                 if (type instanceof OnOffType) {
186                     if (type == OnOffType.ON) {
187                         this.pulses = config.onPulsesArray;
188                     } else {
189                         this.pulses = config.offPulsesArray;
190                     }
191                 } else if (type instanceof OpenClosedType) {
192                     if (type == OpenClosedType.OPEN) {
193                         this.pulses = config.openPulsesArray;
194                     } else {
195                         this.pulses = config.closedPulsesArray;
196                     }
197                 } else {
198                     throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
199                 }
200
201                 if (this.pulses == null) {
202                     throw new RFXComInvalidStateException(channelId, null,
203                             "No pulses provided in the device configuration for command" + type);
204                 }
205
206                 break;
207
208             default:
209                 throw new RFXComUnsupportedChannelException("Channel " + channelId + " is not relevant here");
210         }
211     }
212
213     @Override
214     public SubType convertSubType(String subType) throws RFXComUnsupportedValueException {
215         return ByteEnumUtil.convertSubType(SubType.class, subType);
216     }
217 }