]> git.basschouten.com Git - openhab-addons.git/blob
825c601ed0eda6b1ebe06a20cb03e269e097f93e
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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, RFXComDeviceConfiguration config, DeviceState deviceState)
146             throws RFXComUnsupportedChannelException {
147         switch (channelId) {
148             case CHANNEL_RAW_MESSAGE:
149                 return new StringType(HexUtils.bytesToHex(rawMessage));
150
151             case CHANNEL_RAW_PAYLOAD:
152                 byte[] payload = new byte[pulses.length * 2];
153                 ByteBuffer.wrap(payload).asShortBuffer().put(pulses);
154                 return new StringType(HexUtils.bytesToHex(payload));
155
156             case CHANNEL_PULSES:
157                 return new StringType(IntStream.range(0, pulses.length)
158                         .mapToObj(s -> Integer.toString(Short.toUnsignedInt(pulses[s])))
159                         .collect(Collectors.joining(" ")));
160
161             default:
162                 throw new RFXComUnsupportedChannelException("Nothing relevant for " + channelId);
163         }
164     }
165
166     @Override
167     public void setSubType(SubType subType) {
168         this.subType = subType;
169     }
170
171     @Override
172     public void setDeviceId(String deviceId) {
173         // Nothing to do here
174     }
175
176     @Override
177     public void convertFromState(String channelId, Type type)
178             throws RFXComUnsupportedChannelException, RFXComInvalidStateException {
179         switch (channelId) {
180             case CHANNEL_RAW_MESSAGE:
181             case CHANNEL_RAW_PAYLOAD:
182             case CHANNEL_PULSES:
183                 throw new RFXComUnsupportedChannelException("Cannot send on channel " + channelId);
184
185             case CHANNEL_COMMAND:
186                 if (type instanceof OnOffType) {
187                     if (type == OnOffType.ON) {
188                         this.pulses = config.onPulsesArray;
189                     } else {
190                         this.pulses = config.offPulsesArray;
191                     }
192                 } else if (type instanceof OpenClosedType) {
193                     if (type == OpenClosedType.OPEN) {
194                         this.pulses = config.openPulsesArray;
195                     } else {
196                         this.pulses = config.closedPulsesArray;
197                     }
198                 } else {
199                     throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type);
200                 }
201
202                 if (this.pulses == null) {
203                     throw new RFXComInvalidStateException(channelId, null,
204                             "No pulses provided in the device configuration for command" + type);
205                 }
206
207                 break;
208
209             default:
210                 throw new RFXComUnsupportedChannelException("Channel " + channelId + " is not relevant here");
211         }
212     }
213
214     @Override
215     public SubType convertSubType(String subType) throws RFXComUnsupportedValueException {
216         return ByteEnumUtil.convertSubType(SubType.class, subType);
217     }
218 }